start-marathon
Start marathon preparation from intake through reviewed implementation plans, honest contracts, disjoint lanes, MARATHON.yaml, preflight and dry-run. Use for "start marathon", "marathon", a marathon q
SKILL.mdskills/2-daily/start-marathon/SKILL.md
GitHubStart marathon
Prepare an honest, runnable marathon plan without firing work. Treat PROJECT/** as the execution
record, GitHub as the live signal stream, and deterministic preflight output as stronger than prose.
Routing
- Primary — start or substantially revise an arc: follow Steps 0–6, including intake review, contract review, implementation plan drafting and independent plan QA, lane computation, plan and YAML preparation, preflight, and dry-run. Choose real work with a per-item machine-checkable pass condition. At most one long-horizon marathon may be in flight; check the RELEASES roadmap's Immediate next-up and active marathon rows before preparing a competing arc.
- Secondary — another session prepared the docs: verify the existing issue, doc, review receipt,
contract, and current commit. Run focused smoke checks, direct preflight, planner
--check, andmarathon.sh --plan <file> --dry-run. Repair a concrete stale input and recheck; do not rewrite a sound plan or duplicate its issue/ledger row. If required inputs are absent, enter the primary path at the first missing step. - Explicit firing:
start-marathononly prepares the exact command and evidence. The existing/pre-marathoncommand may fire after the operator confirms the dry-run-approved plan and order. A bare “marathon” is never implicit authorization to dispatch.
Recite this — verbatim, as the first thing in your first response
Start-Marathon Discipline:
- Resolve the harness and arm the guard (Step 0). Run the locator block first — it exports
$HARNESSand is the relay-xyz guard's proof-of-load; with the guard enabled and no prior proof-of-load, planner/preflight calls are cancelled with exit 2.- Inventory and reconcile (Steps 1–2). List every open issue and every
GH-*.mdcapture; give each exactly one classification from live GitHub state, never from stale local text.- Capture the intake that is missing (Step 3). For each in-scope open issue with no capture doc, render it with the existing writer and park its ledger row; list what was written.
- Prepare and review (Steps 4–5). Ground each contract and implementation plan in current code, obtain independent plan QA, compute disjoint lanes, prepare YAML, and run direct preflight plus the actual
marathon.sh --dry-run. Record exits and repair bounded defects.- Report with decisions (Step 6). Report the classification, reviewed plans, exact verdicts, collision map, waves, and runnable command; state any blocker. Firing and closing stay behind the operator's exact-plan confirmation.
Overall Goal:
/start-marathon, an ambiguous “marathon,” or legacy/marathon-triageyields a reviewed, preflighted, dry-run-approved marathon proposal without dispatch.
Then begin work.
Guardrails
- Read
ROUTER.md,AGENTS.md,GUIDING-PRINCIPLES.md, the RELEASES DB (releases roadmap list), andPROJECT/PDDA.mdfirst. - The default includes ordinary readiness computation.
marathon_plan.py --dry-run --deeppublishes no plan file (--dry-runprints the report only;--deepdelegates toswarm-preflight.sh --dry-runper ready item) andswarm-preflight.sh --dry-runpublishes no packet — but neither is side-effect-free: preflight runsgit fetch --pruneon the target and adds then removes a detached temporary worktree attarget.ref(utils/py/swarm_preflight.py:1297,:1355-1361,:1378-1380) before its dry-run exit. That is Git metadata and a transient worktree, not a published artifact. Run them; do not ask whether to. If the operator explicitly asks for a strictly read-only audit, that override covers the metadata effects too: skip Step 3's writes and report the captures you would have written, and either obtain readiness evidence from an authorised disposable clone or report it as unavailable — a skipped preflight is not a verdict. - Writing a
1-INBOXcapture doc and parking its ledger row through the writer is intake. Create the umbrella issue and a derived full clone for the selected arc during preparation; both are needed to make a YAML plan and dry-run concrete. Report their identifiers. - The request authorizes reversible preparation: promote a selected capture under PDDA, write its reviewed plan, create a task branch in a fresh full clone where the repo SOP requires it, and write the planner file and YAML. Do not close issues or fire until the exact plan/order is confirmed. Preserve an existing plan and receipt unless evidence requires a revision.
- Incidental findings outside the marathon task go into root
PARKED/under its README contract. Do not expand the arc or file an issue merely to park a finding. During triage, promote selected items through issue-firstPROJECT/1-INBOX/GH-*.mdcapture and RELEASES roadmap registration; leave a promotion pointer in PARKED. A blocker to the selected marathon stays in its active plan. - Recovery is bounded: diagnose a concrete failure with
workhorse, then useunstuckif a session stalls or repeats a step without new evidence. Re-run the affected check once after a material correction. If the same condition persists, report the blocker; do not bypass a deterministic verdict, exceedLANE_MAX_ATTEMPTS, re-fire a parked lane, or fabricate readiness. - Never override a deterministic PDDA or preflight finding with narrative judgment.
- Use the repo's standing target branch policy. Do not invent a branch or silently substitute a builder.
- If GitHub is unavailable, mark live-state evidence
UNKNOWN; do not infer it from stale local text. - Orchestrator vs. review separation (GH-784). The orchestrator cannot self-satisfy the review
contract or attest review solely by observing test suites; it must mechanically invoke an
independent peer/Codex QA turn before sign-off. Wave completion requires on-disk receipts under
relay-system/and all wave checklist items checked.
Drive loop — how an agent runs this skill end to end
Follow the steps in order; each has an exit contract and a next action. Do not stop at the first non-zero exit — classify it. Every run ends in one of two report shapes (Step 6): a complete report, or a blocked report that names the command, its exit code, the evidence that could not be established and the next action. Asking the operator whether to run a step is neither.
| Step | Command / action | Exit → action |
|---|---|---|
| 0 | locator block below | locator not found → blocked report (install the skill or set XYZ_HARNESS); else continue |
| 1 | gh issue list + find PROJECT/1-INBOX PROJECT/2-WORKING |
gh failure → live state UNKNOWN, continue with local docs |
| 2 | one classification per issue and per doc | — |
| 3 | render + park each missing capture | writer refusal → record it per issue, continue |
| 4 | reconcile contracts; draft/review implementation plans; run marathon_plan.py --dry-run --deep |
0 clean → continue · 2 usage → fix and retry once · 3 ledger error → block · 4 drift / 5 held → classify and repair selected items · 6 unavailable GitHub → mark UNKNOWN; other codes → block |
| 5 | direct swarm-preflight.sh --dry-run per candidate; audit collisions; prepare YAML; run marathon.sh --plan <file> --dry-run |
Preflight: 0 READY, 3 NEEDS-CONTRACT, 4 CONTRACT-STALE, 5–7 BLOCKED; usage 2 → correct once. Dry-run failure → repair and retry once, then block |
| 6 | report | the Done rule below decides whether it may claim completion; otherwise it is a blocked report |
| 7 | firing boundary (operator-confirmed only) | recheck umbrella, ledger, clone and exact approved plan/order; then hand to /pre-marathon |
Done rule: claim a complete preparation only when (a) each in-scope issue and capture has one
classification and written captures have ledger rows; (b) selected items have current, reviewed
implementation plans and valid contracts; (c) every selected candidate has a direct preflight exit
and verdict; (d) the planner report or --check records waves, held items and drift; (e) audited
write-sets are disjoint within each wave; and (f) the exact YAML and marathon.sh --dry-run exit 0
are recorded. A failed or unavailable check yields a blocked report with command, exit, missing
evidence and next action, not fabricated readiness.
Permission-classifier blocks: if a harness permission layer cancels a planner or preflight call,
first check that Step 0 actually ran in this session (the relay-xyz guard — STOP message means it
did not); then retry the identical command once before escalating.
Workflow
0. Resolve the harness root — this is also the guard's proof-of-load
Run this block as the first Bash call of the session. Besides exporting $HARNESS, a Bash call
that runs find-harness.sh is what the relay-xyz guard hook
(relay-automation/hooks/relay-xyz-guard.sh) accepts as proof the skill stack is loaded.
marathon-plan and swarm-preflight are Tier-A entrypoints (Bash shims and Python twins alike).
With the guard enabled and no prior proof-of-load in this session, calls to them are blocked with
exit 2 and the message relay-xyz guard — STOP (the hook is fail-open and session-scoped; a session
that already loaded relay-xyz is not blocked). The remedy is to run this block, not to ask the
operator.
L=""
for candidate in "${XYZ_HARNESS:+$XYZ_HARNESS/skills/1-hourly/relay-xyz/find-harness.sh}" \
"$HOME/.claude/skills/relay-xyz/find-harness.sh" \
"$HOME/.codex/skills/relay-xyz/find-harness.sh" \
"$HOME/.gemini/config/skills/relay-xyz/find-harness.sh" \
"$HOME/.gemini/antigravity/skills/relay-xyz/find-harness.sh" \
"$HOME/.gemini/antigravity-cli/skills/relay-xyz/find-harness.sh" \
"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/skills/relay-xyz/find-harness.sh" \
"$(git rev-parse --show-toplevel 2>/dev/null)/skills/1-hourly/relay-xyz/find-harness.sh"; do
[ -n "$candidate" ] && [ -f "$candidate" ] && { L="$candidate"; break; }
done
[ -n "$L" ] || { echo "relay-xyz: locator not found — install the skill or set XYZ_HARNESS" >&2; exit 1; }
eval "$("$L" --env)"
Reference every script below as $HARNESS/utils/swarm-preflight.sh,
$HARNESS/utils/py/marathon_plan.py and $HARNESS/utils/hq/hq-lib.sh — not bare utils/... paths,
which resolve to nothing (or to an unrelated utils/ directory) in a vendored .xyz/ install.
1. Inventory intake and active work
List open issues and all issue capture docs in deterministic order:
gh issue list --state open --limit 200 --json number,title,labels \
--jq 'sort_by(.number) | .[] | "\(.number)\t\(.title)\t[\(.labels|map(.name)|join(","))]"'
find PROJECT/1-INBOX PROJECT/2-WORKING -maxdepth 1 -type f \
-name 'GH-[0-9]*.md' -print | LC_ALL=C sort -V
Read the ledger's parked pointers (releases roadmap list) and each candidate's frontmatter, status table, acceptance criteria, and
Swarm Preflight Contract. Do not treat a title match as a contract.
2. Reconcile each issue document
Query each issue number with one batched gh issue list or gh issue view. Assign exactly one
classification:
| Classification | Meaning | Recommendation |
|---|---|---|
STALE-CLOSED |
Capture is active but the issue is closed | Reconcile outcome; move to 3-COMPLETED only if shipped, otherwise 4-MISC |
READY |
Open, promoted, valid contract, dry-run exit 0 | Candidate for ranking |
CONTRACT-STALE |
Preflight exit 4 says the fix already landed | Verify delivery evidence; propose reconciliation |
NEEDS-PROMOTE |
Open, contract exists, still in 1-INBOX |
Propose promotion and full active-doc contract |
NEEDS-CONTRACT |
Open, no valid preflight JSON contract | Propose bounded acceptance and write-set contract |
NEEDS-CAPTURE |
Open, in scope, and no GH-<n>-*.md exists in 1-INBOX or 2-WORKING |
Write the capture now (Step 3), then classify as NEEDS-CONTRACT |
BLOCKED |
Preflight exits 5, 6, or 7 | Report the exact blocker; do not queue |
NOT-A-WORK-ITEM |
Feedback, report, duplicate, deferred, or meta-only | Exclude and explain |
UNKNOWN |
GitHub or required evidence unavailable | Exclude until verified |
A contract exists only when valid JSON appears under a heading matching Preflight Contract and
satisfies $HARNESS/utils/swarm-preflight.sh's current schema (Step 0). Run the script rather than
hand-validating it.
3. Capture the intake that is missing — with the existing writers
For every NEEDS-CAPTURE issue, produce the same two artifacts hq park --create produces for a
new issue, using the same functions it uses. Never hand-author frontmatter, never add a second
write path:
source "$HARNESS/utils/hq/hq-lib.sh" # hq_render_capture, hq_roadmap_line, hq_slug
NUM=<n>; URL="https://github.com/<org>/<repo>/issues/$NUM"
TITLE="$(gh issue view "$NUM" --json title --jq .title)"
BODY="$(gh issue view "$NUM" --json body --jq .body)"
CREATED="$(gh issue view "$NUM" --json createdAt --jq '.createdAt[0:10]')"
DOC="PROJECT/1-INBOX/GH-${NUM}-$(hq_slug "$TITLE" | tr '[:lower:]' '[:upper:]').md"
# <num> <src> <title> <created> <doc_type> <project> <repo> <request> — ratings default to
# ratings_provisional: true, which marathon-plan already parks out of active waves.
hq_render_capture "$NUM" "$URL" "$TITLE" "$CREATED" bugfix <project> <org>/<repo> "$BODY" > "$DOC"
python3 "$HARNESS/utils/py/releases_app.py" roadmap add \
--issue-num "$NUM" --issue-url "$URL" --title "$TITLE" --created "$CREATED" --doc-path "$DOC" \
--raw-text "$(hq_roadmap_line "$NUM" "$TITLE" "$CREATED" "$DOC" "$(basename "$DOC")" "$URL")"
python3 "$HARNESS/utils/py/releases_app.py" roadmap list | grep "GH-$NUM" # read the row back
Use feedback instead of bugfix for a non-defect capture. A doc written here is a capture, not
an active-work doc: it carries no ## Status table until promotion. Record each (issue, doc path, ledger gid) for the report. If roadmap add fails, the intake is half-complete (doc exists, no
row) — report it as such with the writer's error, never as success. NOT-A-WORK-ITEM issues get no capture and are listed with
the reason.
4. Review contracts, draft plans, then compute
For each selected member, read the issue's current body/comments, the active doc, actual code entry
points and writes, and relevant tests. Reconcile acceptance, dependencies, target branch, fix probes,
artifacts, artifacts_new, and lanes against that evidence. Use recon for unfamiliar stateful
code and swe for plan quality. Revise a stale contract in the canonical doc and rerun preflight;
a syntactically valid JSON contract with dishonest paths is still blocked.
For work beyond a simple edit, finish the implementation plan in its canonical PROJECT/2-WORKING
doc: goal, scope, current-state map, blast radius, one ordered phase list, explicit write-sets and
dependencies, per-phase acceptance and QA, rollback for Costly changes, and final integration gate.
Use Phase 0 for uncertainty that prevents a truthful contract; write its findings back before later
phases. Review an existing plan for freshness before rewriting it. Run independent plan QA with
relay-xyz (Agy or Codex per operator choice); resolve findings and record the review artifact and
verdict. A self-reviewed plan is not complete QA. Missing runtime capability becomes a separate
parked dependency and holds its lane.
Check RELEASES ratings and the PDDA risk gate. ratings_provisional: true and risk > 2 prevent
automatic selection. #443 owns the fuller Product Release System (PRS; definition)
freshness gate; until it lands, inspect rating and
issue update timestamps and hold uncertain ratings rather than claiming planner verification.
python3 "$HARNESS/utils/py/marathon_plan.py" --dry-run --deep
--dry-run prints the report and writes no MARATHON-PLAN-*.md; --deep runs
swarm-preflight.sh --dry-run for every ready item and folds the verdicts in. Quote the waves, the
held items and any drift lines in the report. Handle the exit code per the drive loop table; 3
(ledger unparseable) turns the run into a blocked report, as does any other unmet Done-rule
After contract and candidate review, if a current MARATHON-PLAN-*.md already exists, run the
planner with --check first: --check reports whether it is in sync, reporting drift without
overwriting it. Write the canonical plan with the planner without --dry-run only when the file
is absent, inspect its diff, and run --check. Revise an existing plan only when concrete evidence
or operator review explicitly justifies the revision; drift is a finding for the report, not an
automatic reason to regenerate or overwrite an in-flight or reviewed plan. This is reversible
preparation authorized by a start request; a mismatch is drift, not success.
Wave Lifecycle Contract & /start-task Step-8 Parity (GH-784)
Marathon batch execution must strictly enforce the /start-task double-relay protocol per wave
rather than collapsing execution into a single build-and-push loop:
- Wave Plan QA (Step 6 parity): Before coding a wave, conduct independent plan QA via
relay-xyz(Codex reviewer default). Commit review inputs so isolated reviewers can see them. - Wave Build & Proof-of-Done: Implement the wave's changes within its declared allowlist. Execute the wave's Proof of Done test suite green (runnable command + test exit 0).
- Wave Post-Build Codex QA Relay (Step 8 parity): Before pushing feature branches or opening
PRs, execute an independent Codex review turn against the committed wave diff and test receipts.
Record the on-disk receipt under
relay-system/<YYYY-MM-DD>/<label>.codex.md. - Adjudication: Adjudicate and resolve all findings from the Codex QA turn and CodeRabbit / Peer Review.
- Orchestrator vs. Review Protocol Separation: The Orchestrator (the Claude dispatch/tool driver) cannot self-satisfy the review contract or attest review solely by observing passing test suites. It must mechanically invoke an independent peer/Codex QA turn before sign-off.
Acceptance & Quality Checklist Contract
Every marathon plan (PROJECT/2-WORKING/MARATHON-PLAN-*.md) must mandate an explicit per-wave
verification section:
## Acceptance & Quality Checklist
### Wave 1
- [ ] Wave 1 Proof of Done Test Suite Green (runnable command + test exit 0)
- [ ] Wave 1 Post-Build Codex QA Relay executed (receipt recorded under `relay-system/<YYYY-MM-DD>/<label>.codex.md`)
- [ ] Wave 1 CodeRabbit / Peer Review findings adjudicated
Mechanical check: set TARGET_ROOT to the absolute root of the repository whose plan is being
executed, CANONICAL_PLAN to its absolute plan path, and WAVE to the positive wave number being
admitted. Bind both commands to that consumer root:
python3 "$HARNESS/utils/pdda/check_marathon_qa.py" --root "$TARGET_ROOT" --pre-pr --wave "$WAVE" --doc "$CANONICAL_PLAN"
# or via pdda.sh (also binds the activity log to the consumer):
PDDA_REPO_ROOT="$TARGET_ROOT" "$HARNESS/utils/pdda/pdda.sh" marathon-qa --root "$TARGET_ROOT" --pre-pr --wave "$WAVE" --doc "$CANONICAL_PLAN"
Use a positive wave number and one explicit plan. Every declared wave must retain its mandatory
checklist structure; only the selected wave must be complete for this PR. Future waves remain
honestly pending. For final closeout, omit --wave from the same command to require all waves.
Completed docs always require all waves, even if a selector is supplied.
Checked receipt citations must resolve on disk under the consumer root. Codex receipts for checked
or required items must have a first STATUS: header of Approved or Closed; ordinary Markdown
links and backticked paths are supported. Empty, missing, nonterminal and placeholder receipts
fail. This checks recorded terminal status, not independent authorship or exact reviewed SHA;
the independent review and head verification above remain required. Routine aggregate scans warn
on pending items in active plans.
5. Preflight, form lanes, and dry-run the actual marathon
Run preflight directly for every candidate that is not NOT-A-WORK-ITEM or UNKNOWN, including
the ready items --deep already touched — deep delegation discards preflight output and only
handles exits 4/5/6/7, so it cannot supply a candidate's recorded exit and verdict. Use paths for
inbox docs and issue numbers for promoted docs:
"$HARNESS/utils/swarm-preflight.sh" --project-doc PROJECT/1-INBOX/GH-<n>-<slug>.md --dry-run
"$HARNESS/utils/swarm-preflight.sh" --gh-issue <n> --dry-run
Record the exact exit and verdict: ready 0, usage 2, invalid contract 3, already landed 4,
not ready 5, blocked target 6, or ambiguous 7. Every code maps to a classification in Step 2;
none is a reason to stop.
Audit artifacts, artifacts_new, and lanes against the issue's actual scope. Flag placeholder,
missing, over-broad, or unrelated write-sets; a ready exit does not make a dishonest write-set safe.
Ranking and waves: apply the PDDA selection rule — gate on risk <= 2, then rank by lowest
effort + complexity, then fewest phases. Do not store a new composite score. Place lanes together
only when their declared and audited write-sets are disjoint and all zone caps hold. Shared ledgers
such as the RELEASES DB and CHANGELOG.md collide. Kernel paths obey the repo's
one-kernel-lane-per-wave cap.
For the selected arc, create or reuse the umbrella issue and RELEASES marathon row before preparing
executable YAML. Keep one PROJECT/2-WORKING/<arc>/MARATHON.yaml and phase briefs. Use the current
checked-in YAML examples and marathon.sh parser for supported keys: name, ordered phases,
unique id, brief, exact artifact, builder/reviewer, depends_on where needed, and round/time
bounds. Record member issues and umbrella identity in comments and briefs. Preserve sound existing
YAML. Audit real writes, generated files and shared ledgers before declaring lanes disjoint.
Run focused smoke checks for changed plan/brief paths, then run the no-dispatch command from a disposable full clone on the intended task branch:
"$HARNESS/relay-automation/marathon.sh" --plan PROJECT/2-WORKING/<arc>/MARATHON.yaml --dry-run
Inspect its complete output and exit, including worker availability, branch, dependencies,
collisions, paths and gates. The secondary route runs the same command after existing-doc review
and marathon_plan.py --check. A per-phase marathon-drive.sh --dry-run is not a substitute for
the full YAML dry-run. A failed dry-run holds the plan. An active marathon or parked lane cannot be
overridden with --force; replan through the standing queue.
6. Report
Return one of the two shapes. A complete report (the Done rule holds) contains:
Classification table with issue, doc, live state, contract state, and reason — one row per open issue and per
GH-*.mddoc.Captures written in Step 3: issue, doc path, ledger gid; and the issues excluded with reasons.
Reviewed implementation plans and QA receipts, the planner output (waves, held, drift), and each selected candidate's direct preflight exit and verdict.
Collision map, YAML path, focused smoke result, and the exact full-plan dry-run command and exit.
Decisions needed — one default recommendation per item, not a flat symmetric list of options the operator has to weigh unaided. For each item that needs a call, emit:
RECOMMEND: <the single default action — commit captures | archive | close | revise contract | unblock | confirm exact plan and fire | hold> BECAUSE: <the evidence behind it — live state, preflight verdict, rating, collision risk> UNLESS: <the specific condition under which the operator should override the default>The operator starts from the recommendation and only overrides when the
UNLESSclause holds — never from a blank menu. Reserve a bare options list only for genuinely balanced calls where no default is defensible, and say so explicitly.
A blocked report contains everything above that was established, plus one line per unmet requirement: the command run, its exit code, the evidence that could not be established, and the next action. It never contains fabricated waves or verdicts.
Keep the default report inline. If the operator requests a persisted report, write a dated
PROJECT/1-INBOX/START-MARATHON-YYYY-MM-DD.md with doc_type: report, source/provenance, and
roadmap_exempt: true. If promoted to 2-WORKING, add the full PDDA frontmatter, exact status table,
and the ledger pointer. Never execute the marathon from this skill.
7. Firing boundary — umbrella identity and derived full clone
Create or reconcile umbrella identity during preparation. Before firing, verify the selected plan, ledger row, clone, dry-run evidence and order still match the operator's confirmation. Dispatch only after that exact confirmation.
7a. Every marathon has an umbrella tracking issue
A marathon without a GitHub umbrella issue does not start. The umbrella is the marathon's identity: waves, clone folder, ledger row and closeout all key off its number.
Today this is under-enforced and the gap is measurable: releases_app.py marathon add requires
--tracking-issue (utils/py/releases_app.py:4901) and marathons.tracking_ref_id is NOT NULL
(:479) — but the executor never reads either. marathon_drive.py has no --tracking-issue flag
and the MARATHON.yaml schema has no field for one, so the requirement binds only if someone
chooses to create the ledger row. Most runs have not: at least eight marathons are visible in
committed transcripts and marathon-system/, against two rows in the marathons table.
Procedure, before the selected marathon fires:
- Open the umbrella issue. Title it for the arc, not the first item. Body lists the candidate member issues, the wave sketch, and the acceptance rule for the marathon as a whole.
- Register it in the ledger immediately:
Offline,python3 "$HARNESS/utils/py/releases_app.py" marathon add \ --tracking-issue https://github.com/<org>/<repo>/issues/<n> --status plannedTMP-XXXXXXis an accepted placeholder — but reconcile it before the marathon closes, or the ledger row permanently names an issue that does not exist. The token is shape-checked only (check_tracking_token,:1675-1694); GitHub is never queried, so a typo in the URL is accepted silently. - Dial every member issue into the same release, and link them to this marathon.
Carry the umbrella number into every downstream artifact: the clone folder name (7b), the plan doc, each phase brief, and the closeout. If you cannot name the umbrella issue, the marathon is not ready to fire — the triage report is where that arc gets decided.
7b. Marathons run in a full clone, deterministically named
Two rules, both currently unenforced by code. State them explicitly in the plan so a reviewer can check them.
A full clone, never a linked worktree and never the primary checkout. The mechanism that makes
this necessary is real but indirect: validate.sh:16-53 refuses to run inside a linked worktree
(GH-45, exit 2), and driver_lock_path_for_repo (relay-automation/driver-lock-lib.sh:20-35)
resolves a linked worktree's lock to its parent's .git/relay-driver.lock, so a worktree
cannot run a second driver concurrently. Nothing refuses a marathon launched from the primary
checkout — test/gh35-test-tiers.sh:367-370 proves the primary checkout runs the gate normally —
so this rule is on the operator, not the harness.
Clone folder name is derived, not chosen:
marathon-gh-<umbrella-issue-number>-<short-description>
<short-description> is lowercase, hyphen-separated, three words or fewer, describing the arc —
not a wave label, not a phase number. One clone per marathon; a second attempt at the same arc
reuses the name with a -r2 suffix rather than inventing a new slug.
CLONE="$HOME/marathon-clones/marathon-gh-${UMBRELLA}-${SLUG}"
git clone <remote> "$CLONE"
This replaces the current free-form convention, which has drifted badly and is the reason a
salvage operation once could not find its own artifacts: live folders are gh271-waveA,
gh396-phase0 and gh405-mock-board — a wave label, a phase number and a feature name, three
different meanings under one shape — while committed transcripts also show gh-8-… and gh-115-…
with a different separator, plus a gh-115-clean retry folder with no stated relationship to its
original.