Goal run
Methodology home for the goal-run harness (issue #874): the stage-machine contract, halt-reason precedence, launch arms, worktree lifecycle, the label-to-ledger join query for escape-rate segmentation, and the untrusted-content discipline for a single vendor-goal-loop harness run. Use when extending, halting, resuming, or documenting a /goal-run invocation, or when writing the escape-rate query that segments goal-run PRs from Code-Conductor-orchestrated PRs. DO NOT USE FOR: the live /goal-run execution prose itself (use agents/Goal-Run.agent.md, the shared body this skill is referenced from) or the goal-contract plan-authoring schema (use skills/plan-authoring's goal-contract schema, decision 872-D2).From its SKILL.md
npx -y skills add Grimblaz/agent-orchestra --skill goal-runAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
19.3 KB, as published. Nobody here has run it
Goal-Run Harness
Reference methodology for the goal-run harness: a launcher/resumer that walks a single GitHub issue's approved goal-contract (a structured, machine-checkable target the vendor's own /goal loop runs against) through to a reviewed, classed pull request, without a human answering anything mid-run. This file documents the mechanics; the runnable prose lives in agents/Goal-Run.agent.md. For the invocation entry point and the reader-facing dual-flow orientation (conducted vs. goal-run, launch, resume, halt-reason reading guide, honest budget expectations), see CLAUDE.md § Orchestration and HOW-IT-WORKS.md § Goal-run: the unattended pipeline.
When to Use
- When extending, debugging, or reviewing a change to the goal-run harness's stage machine, halt model, worktree lifecycle, or untrusted-content barriers.
- When writing or reviewing the
agents/Goal-Run.agent.md/agents/goal-run.mdshared body and shell, or the customer-experience (CE) surface-class doc (skills/customer-experience/references/goal-run-surface-classes.md, 874-D8) and dual-flow guide that reference this skill. - When building the escape-rate segmentation query described in
## Label-To-Ledger Join. - When a reader needs to know what a
goal-run-stage-{issue},goal-run-inflight-{issue}, orgoal-halt-report-{issue}marker means.
DO NOT USE FOR
- Running
/goal-runitself — that isagents/Goal-Run.agent.md(shared body) andagents/goal-run.md(Claude Code shell). This skill is the reference the body cites, not a substitute for it. - The goal-contract's own shape (targets, halt conditions, evidence obligations) — that schema lives under
skills/plan-authoring/schemas/goal-contract.schema.json(872-D2). - General adversarial-review, CE Gate (Customer Experience Gate — the validation step confirming an implementation achieves the intended customer experience), or PR-persistence mechanics — the Post-Loop Chain (below) reuses
adversarial-review,customer-experience, andpersist-changesunchanged; load those skills directly for their own methodology.
Purpose
A goal-run invocation must survive a crash or a second concurrent launch attempt without losing track of where it is or double-provisioning a worktree. Every stage transition, halt, and terminal check is therefore read from durable GitHub artifacts — never from what a conversation remembers saying — and every function that makes one of these decisions is a tested PowerShell primitive under .github/scripts/lib/goal-run-*-core.ps1, not inline arithmetic in the agent prose.
Stage-Machine Contract
The stage-machine's WRITTEN marker vocabulary — the value actually persisted to the goal-run-stage-{Issue} comment — is a fixed four-value sequence ($script:GoalRunStageOrder in goal-run-stage-core.ps1):
pre-loop -> loop-launched -> loop-released -> chain-dispatched
pre-loop is the implicit starting state — no marker is ever posted for it on its own, and (M17 fix) the stage-marker writer's own ValidateSet excludes it, reconciling it against the reader's ExplicitStageMarker ValidateSet (Resolve-GoalRunResumeStage), which never accepted it either. A single <!-- goal-run-stage-{issue} --> issue comment is upserted in place (never appended-to) and always reflects the latest completed stage. This is deliberately the coarse top-level enum a resumed invocation switches on; finer-grained per-attempt history (why a run deviated, what it checkpointed) lives in the typed run log and the mutex marker, not here. Since the M10 fix, this marker also carries the provisioned worktree path (-WorktreePath on Set-GoalRunStageMarker), so a resuming invocation reads it directly off the marker instead of an undefined "most recent worktree" filesystem search.
Resolve-GoalRunResumeStage's returned .ResumeStage vocabulary is a superset of the four written values above: it adds a fifth, resolver-only value, loop-interrupted, that is never itself written to the stage marker — the function derives it purely from the durable signals below when an earlier session's loop-launched marker never reached loop-released. Resolve-GoalRunResumeStage decides where a resumed invocation re-enters, in this precedence (highest wins):
- Contract hash not verified against the launch-pinned value ->
blocked(not a stage to execute — report and stop; the contract needs plan-side remediation). - Terminal emissions already verified on a known PR ->
complete(nothing left to do). - Explicit stage marker is
chain-dispatchedorloop-released-> resume atchain-dispatched(the loop already released; only the chain remains). - Explicit stage marker is
loop-launched-> resolve toloop-interrupted, not literallyloop-launched: a prior session launched the vendor loop and this resuming session has no live transcript to read a release verdict from, so the loop's true outcome must be reconstructed from durable state (seeagents/Goal-Run.agent.md's### loop-interrupted (resume without a transcript)section). - No explicit marker, but the typed run log has a
checkpoint/deviation/experience-observationentry -> alsoloop-interrupted, for the same reason — the loop ran even though no explicit marker was ever written (e.g. a crash between launch and the next marker). goal-run-active.jsonexists but no run-log evidence ->loop-launched— the worktree was provisioned but the loop itself was never launched.- A mutex marker exists but nothing else ->
pre-loop(crash midpre-loop). - Nothing present ->
pre-loop(fresh launch).
blocked and complete are terminal reports, not stages a resumer executes — report and stop. loop-interrupted, unlike those two, IS a stage a resumer executes: its stage-machine section re-validates the committed worktree state directly, then either discovers the interrupted loop's work was already satisfied, relaunches the loop in the same worktree if it is genuinely dead, or reports a still-live run under a different session — it never just reports and stops.
Halt Model And Precedence
A halt report is a closed, five-value-enum object validated against skills/goal-run/schemas/goal-halt-report.schema.json (additionalProperties: false — every field the harness or an executor can populate is named and typed). The halt_reason enum is reused verbatim from the goal-contract schema's halt_conditions (872-D2): unachievable-target, invariant-conflict, budget-exhausted, gate-input-needed, chain-stage-failure.
When more than one halt condition is true at the same evaluation point, Resolve-GoalRunHaltPrecedence (goal-run-chain-core.ps1) picks the single winner by this total, non-negotiable order (highest wins):
invariant-conflict > unachievable-target > gate-input-needed > budget-exhausted > chain-stage-failure
This ordered list is the entire precedence rule; nothing else in the file overrides it. Resolve-GoalRunHaltPrecedence does not itself decide whether a condition is true — each producer's own detection lives elsewhere (the launch-pinned contract-hash mismatch and an executor's own halt-claim for invariant-conflict; the re-validation disposition, the fix-cycle cap, or a stage crash for chain-stage-failure; the wall-clock backstop for budget-exhausted; the chain-gate demand for gate-input-needed) — callers pass in the already-decided booleans.
A single-condition halt (nothing else co-occurring, e.g. the loop-released bounded-retry exhaustion) skips Resolve-GoalRunHaltPrecedence entirely and goes straight to the report builder — the precedence function only matters when two or more producers are simultaneously true.
Never hand-build a halt comment. New-GoalRunChainHaltReport composes the schema-shaped object (running every free-text field through secret redaction, below); Invoke-GoalRunHaltEmit (goal-run-halt-core.ps1) schema-validates and refuses to post an invalid report.
Launch Arms
Arm I (in-session) — the only arm implemented in this PR
/goal-run {issue} is both launcher and resumer. The harness verifies the contract hash, posts a mutex marker (goal-run-inflight-{issue}) before provisioning any worktree so a running worktree with no live marker can never exist, tiebreaks a concurrent launch race by lowest comment id, then sets the goal on itself and becomes the executor inside the freshly provisioned worktree. When the vendor /goal loop completes and control genuinely returns to the harness session, it reads the now-flushed goal_status verdict and enters the Post-Loop Chain (re-validate, CE Gate, adversarial review, capped fix cycles, PR creation with classing — see agents/Goal-Run.agent.md ## Post-Loop Chain for the full stage-by-stage prose).
The loop-to-chain seam is deliberately built from durable artifacts only: New-GoalRunExecutorSessionHandle and Invoke-GoalRunLaunchChain take an issue number, repo root, contract hash, worktree path, and a session handle — never live conversation context — so a future arm can populate the same shapes without rewriting this transition.
Arm H (headless) — PR-2, not yet implemented
A future PR will let the harness supervise an externally polled claude -p process instead of running in-session. The loop-to-chain seam above is already shaped to accept this without a rewrite. No further design decisions have been made yet; do not invent Arm H behavior beyond this pointer.
Arm M (manual hand-off) — PR-2, not yet implemented
A future PR will define a manual two-block rendering surface for a human to hand off a run. This PR renders no Arm M output and accepts no scope_boundaries prompt field. No further design decisions have been made yet; do not invent Arm M behavior beyond this pointer.
Worktree Lifecycle
All worktree mechanics live in goal-run-worktree-core.ps1; its own header comment is the authoritative function-by-function summary — this section is a pointer, not a re-derivation.
- Provision —
New-GoalRunWorktreerefuses a dirty tree first, then creates a named branchgoal-run/issue-{N}-{token}({token}is a GUID-style collision-proof suffix, so two concurrent runs for the same issue never collide) at a short root outside the checkout, settingcore.longpathsatworktree add. - State file —
New-GoalRunActiveStatewritesgoal-run-active.jsonat the worktree root: ceilings, baseline, arm, executor session id, the launch-pinnedcontract_hash,launched_at, aheartbeat_attimestamp (seeded equal tolaunched_at), andteardown_deferred(defaultfalse).Get-GoalRunActiveStatereads it back and returns$nullon a missing file rather than throwing.Update-GoalRunActiveStateHeartbeatrefreshesheartbeat_at— wired live (M5 fix) viaInvoke-GoalRunChainStageBoundaryHousekeeping(goal-run-budget-core.ps1) at every chain-stage boundary and at the loop-release point; before that fix the function existed and was tested but had zero live callers. - Run log —
goal-run-log.jsonlat the worktree root (alongsidegoal-run-active.json), one JSON object per line, each independently validated againstschemas/goal-run-log.schema.jsonbefore it is appended.Add-GoalRunLogEntry(goal-run-log-core.ps1, M9 fix) is the schema-validating writer;Test-GoalRunLogHasCheckpointis the readerResolve-GoalRunResumeStage's-RunLogHasCheckpointparameter is actually fed from (Invocation Contract step 3). Acheckpointentry is written at every chain-stage boundary via the sameInvoke-GoalRunChainStageBoundaryHousekeepingcall named above. - Teardown —
Remove-GoalRunWorktreeretriesgit worktree removewith backoff on every exit path and diagnoses the outcome honestly across the same six-value vocabulary used by the session-startup cleanup detector:removed,removed-partial-root-held,removed-partial-content-remains,stale-registration,failed,verification-indeterminate. On retry exhaustion it setsteardown_deferred: trueon the state file (best-effort) rather than throwing. It never force-removes a locked worktree on aprunableporcelain flag alone — only onTest-Path-verified absence of the directory, because real git never setsprunableon a locked worktree.
Label-To-Ledger Join (874-D7)
Every goal-run PR carries two machine-readable markers of its own provenance, both written at PR-creation time by Invoke-GoalRunClassEmission (goal-run-chain-core.ps1):
- The
goal-runGitHub PR label, applied byAdd-GoalRunPrLabel. - A
goal_run_classscalar field (default valuegoal-run) plus agoal_run_issuefield, folded into the PR body's<!-- pipeline-metrics -->block through the existingInvoke-PipelineMetricsV4Emitprimitive. Both fields are additive-safe: a reader that does not know about them silently ignores them.
The join key is goal_run_class (or, equivalently, the goal-run label) on the PR side. A future escape-rate query segments the frame-credit-ledger's per-PR review-defect counts by joining on that key: PRs where goal_run_class == 'goal-run' (or carrying the goal-run label) are goal-run-produced; every other PR in the ledger is Code-Conductor-orchestrated. No query tool exists yet — this section documents the join key and the intended analysis shape (per-PR defect count grouped by goal-run vs. non-goal-run) so a future maintainer can write it directly against the ledger and gh pr list --label goal-run without re-deriving which field to key on.
Untrusted-Content Discipline (M4/M5/M12)
Three separate guardrails stand between content the harness does not fully trust — a transcript event, a live issue comment that could have been edited after launch, or executor-supplied evidence text — and any durable artifact the harness writes:
- Launch-pinned contract-hash check (step 5, wired live by the M1 fix) —
Test-GoalRunContractHashPinned(goal-run-prompt-core.ps1) compares the live issue-body contract hash against the hash pinned at launch time, not the contract's own self-reportedcontract_hashfield — this catches a post-approval edit that kept its own field internally consistent. A mismatch is the highest-precedence halt producer,invariant-conflict. Three live call sites actually run this check: the vendor/goalloop predicate (goal-run-predicate.ps1 / goal-run-predicate-core.ps1, on every loop iteration, viaResolve-GoalRunLoopPredicate), chain re-validation (Invoke-GoalRunChainRevalidate's-LaunchPinnedHashparameter, goal-run-chain-core.ps1), and resume-time verification (theagents/Goal-Run.agent.mdInvocation Contract step 1). Before the M1 fix, the function existed and was tested but had zero live callers. - Transcript allow-list + secret-redaction pass (step 1) —
Select-GoalRunAllowedFields(goal-run-transcript-core.ps1) copies only explicitly named keys out of a raw transcript event and also rejects a nested dictionary or non-string enumerable found under an allow-listed key name, so a poisoned event cannot smuggle structured content in under a trusted key.Get-GoalRunRedactedTextthen runs a fixed set of secret-shaped regexes (GitHub/AWS/Slack tokens, PEM private-key blocks, bearer tokens, generickey=valuesecret assignments) over any string headed for a durable artifact, replacing each match with a[REDACTED:<pattern-name>]placeholder. - Executor-evidence inert-render discipline (step 1) —
ConvertTo-GoalRunInertEvidenceText(goal-run-halt-core.ps1) strips marker-delimiter substrings out ofevidence[]/plan_remediationtext before a halt report is posted, so executor-authored text cannot smuggle a live<!-- ... -->marker literal into a durable comment.Invoke-GoalRunHaltEmitapplies this and schema-validates before ever posting — it is never bypassed by constructing a raw comment string.
Any transcript-derived or executor-supplied string reaching a halt report or a PR body fragment must already have passed through the allow-list extractor and the redaction pass; the halt-report schema's closed shape (additionalProperties: false) cannot itself enforce that upstream discipline, only the field shape.
Gotchas
| Trigger | Gotcha | Fix |
|---|---|---|
Polling stream-json stdout for the loop's release verdict | goal_status is transcript-only and never reaches stream-json stdout on either platform (goal-loop-capability-probe finding) — a harness watching stdout is blind to release | Only read the verdict after control genuinely returns to the harness session, via Resolve-GoalRunControlReturn's bounded transcript retry |
Calling Resolve-GoalRunHaltPrecedence on every halt | The function only matters when two or more of the five producers are true at once | A single-condition halt (e.g. loop-released retry exhaustion) goes straight to the report builder |
Assuming a chain-dispatched marker on resume means work remains | A PR that already carries both the goal-run label and the classing metrics is genuinely complete | Call Test-GoalRunPrEmissionsVerified against the run's known PR before assuming any work remains |
Release-Hygiene Note
This file and its schemas/*.json siblings sit under skills/*, one of the bare glob patterns Get-FVPluginEntryPointPatterns (frame-predicate-core.ps1) classifies as a cache-keyed plugin entry point — any path under skills/** counts, not just files with a sibling SKILL.md. Claude Code's plugin cache is keyed by the version in .claude-plugin/plugin.json; a same-version reinstall keeps serving whatever entry-point snapshot that version last had. This file is a new entry point added after this PR's step-4 version bump already landed, so it requires its own coalesced bump-version.ps1 bump — see this step's dispatch report for the resulting version.