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).
- License 许可
- MIT
- 最近更新
- 2026-08-16
- Stars
- ★ 3
- Forks
- 0
01 前言 Front Matter
- name
- goal-run
- description
- 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).
02 资源文件 2
- 参考文档schemas/goal-halt-report.schema.json4.8 KB
- 参考文档schemas/goal-run-log.schema.json3.6 KB
03 SKILL.md 正文
<!-- markdownlint-disable-file MD041 MD003 -->
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.
04 同仓库更多 Skills
webapp-testing
Playwright end-to-end testing guidance for web apps, with practical patterns and setup steps. Use when creating or improving browser-based E2E coverage, test stability, and CI execution. DO NOT USE FOR: React component tests (use ui-testing), canvas game interaction (use browser-canvas-testing), TDD workflow (use test-driven-development), debugging failures (use systematic-debugging), or randomized property tests (use property-based-testing).
verification-before-completion
Evidence-based verification checklist before marking work complete. Use before PRs, releases, marking tickets done, or any "I'm finished" declaration; also when naming a baseline commit for a differential suite claim, when designing a mutation campaign or red-run evidence, when making an absence claim, a universal claim, or a one-time demonstration offered as a standing guarantee, when attributing a measurement or claiming a sweep or verification loop is complete, and when stating what a check or a CI suite will do about a change. DO NOT USE FOR: post-merge cleanup or archival (use post-pr-review) or processing GitHub review comments (use code-review-intake).
validation-methodology
Reusable validation and review methodology for staged validation, failure triage, and prosecution-depth setup. Use when running validation ladders, triaging failures, or executing adversarial review passes. DO NOT USE FOR: CE Gate orchestration, specialist dispatch ownership, or step execution flow (keep those in Code-Conductor).
upstream-onboarding
Shared opening-phase protocol for upstream agents (Experience-Owner, Solution-Designer, Issue-Planner) and Code-Conductor when invoked on an existing GitHub issue. Renders a scaled context brief and runs a standards check on inherited work at each phase boundary. Use when a user-invocable agent receives a request referencing an existing GitHub issue and must load solution-authoring and upstream-onboarding at the opening phase. DO NOT USE FOR: subagent dispatches (which already operate within an assessed session context); post-merge review (use post-pr-review); research or non-tree subagents.
ui-testing
Resilient React component testing strategies focusing on user behavior. Use when writing or reviewing component-level React tests, fixing flaky component tests, or establishing React testing patterns. DO NOT USE FOR: Playwright E2E tests (use webapp-testing), canvas game interaction (use browser-canvas-testing), TDD workflow (use test-driven-development), debugging failures (use systematic-debugging), UI component design (use frontend-design), or randomized property tests (use property-based-testing).
ui-iteration
Reusable screenshot-driven UI polish workflow for iterative visual refinement. Use when improving layout, hierarchy, spacing, feedback, or Tailwind-based presentation through repeated review-and-adjust passes. DO NOT USE FOR: functional bug fixing, accessibility-only audits, or major redesign strategy work (use Code-Smith or frontend-design).