agent-afk 5.126.0 → 5.127.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -34,5 +34,5 @@ export declare class XaiProvider implements ModelProvider {
34
34
  export { resolveXaiAuth, formatXaiAuthDiagnostic, formatXaiHttpAuthError, type XaiAuthResolution, type XaiAuthSource, } from './auth.js';
35
35
  export { resolveXaiEndpoint, DEFAULT_XAI_API_BASE_URL, DEFAULT_XAI_OAUTH_BASE_URL, type XaiAuthMode, } from './endpoints.js';
36
36
  export { readXaiTokens, writeXaiTokens, clearXaiTokens, getXaiAuthPath, } from './auth-store.js';
37
- export { startDeviceCodeFlow, pollDeviceCodeToken, buildPkceAuthorizeUrl, exchangeAuthorizationCode, generateCodeVerifier, generateOAuthState, refreshXaiTokens, ensureFreshAccessToken, discoverXaiOidc, } from './oauth.js';
37
+ export { startDeviceCodeFlow, pollDeviceCodeToken, buildPkceAuthorizeUrl, exchangeAuthorizationCode, generateCodeVerifier, generateOAuthState, refreshXaiTokens, ensureFreshAccessToken, discoverXaiOidc, discoverXaiOidcCached, clearOidcCache, } from './oauth.js';
38
38
  export { deriveXaiCallCostUsd, isGrokModelId, XAI_MODEL_PRICING, } from './pricing.js';
@@ -13,8 +13,11 @@ export interface OAuthHttpDeps {
13
13
  clientId?: string;
14
14
  scopes?: string;
15
15
  nowSeconds?: () => number;
16
+ nowMs?: () => number;
16
17
  }
17
18
  export declare function asNonEmptyString(v: unknown): string | undefined;
18
19
  export declare function safeText(res: Response): Promise<string>;
20
+ export declare function clearOidcCache(): void;
19
21
  export declare function discoverXaiOidc(deps?: OAuthHttpDeps): Promise<XaiOidcDiscovery>;
22
+ export declare function discoverXaiOidcCached(deps?: OAuthHttpDeps): Promise<XaiOidcDiscovery>;
20
23
  export declare function tokenResponseToBundle(json: Record<string, unknown>, nowSeconds?: () => number, fallbackRefresh?: string): XaiTokenBundle | null;
@@ -1,7 +1,7 @@
1
1
  import { type XaiAuthStoreDeps, type XaiTokenBundle } from './auth-store.js';
2
2
  import { type OAuthHttpDeps } from './oauth-http.js';
3
3
  export type { FetchFn, OAuthHttpDeps, XaiOidcDiscovery } from './oauth-http.js';
4
- export { discoverXaiOidc, tokenResponseToBundle } from './oauth-http.js';
4
+ export { clearOidcCache, discoverXaiOidc, discoverXaiOidcCached, tokenResponseToBundle } from './oauth-http.js';
5
5
  export { startDeviceCodeFlow, pollDeviceCodeToken, type DeviceCodeStart, type DevicePollResult, } from './oauth-device.js';
6
6
  export { buildPkceAuthorizeUrl, codeChallengeS256, exchangeAuthorizationCode, generateCodeVerifier, generateOAuthState, type PkceAuthorizeParams, } from './oauth-pkce.js';
7
7
  export declare function refreshXaiTokens(refreshToken: string, deps?: OAuthHttpDeps & {
@@ -5,7 +5,7 @@ export interface EmitForkStartedArgs {
5
5
  id: string;
6
6
  parentSessionId: string | undefined;
7
7
  rootId: string;
8
- effectiveChildModel: AgentModelInput | undefined;
8
+ effectiveChildModel: AgentModelInput;
9
9
  childConfig: AgentConfig;
10
10
  promptHead: string | undefined;
11
11
  effectiveAgentType: string | undefined;
@@ -4,9 +4,9 @@ import { type ReadScopeInputs } from './subagent-read-scope.js';
4
4
  import { type SubagentHandle } from './subagent/handle.js';
5
5
  import type { SubagentStatus, SubagentResult, SubagentTrace } from './subagent/result.js';
6
6
  export type { SubagentStatus, SubagentResult, SubagentTrace, SubagentHandle };
7
- import { DENY_ELICITATION, SUBAGENT_DEFAULT_MAX_TOOL_USE_ITERATIONS, SUBAGENT_DEFAULT_TIMEOUT_MS, SUBAGENT_DEFAULT_IDLE_TIMEOUT_MS, SUBAGENT_BACKGROUND_TIMEOUT_MS, resolveSubagentTimeoutMs, resolveSubagentIdleTimeoutMs } from './subagent/constants.js';
7
+ import { DENY_ELICITATION, SUBAGENT_DEFAULT_MAX_TOOL_USE_ITERATIONS, SUBAGENT_DEFAULT_TIMEOUT_MS, SUBAGENT_DEFAULT_IDLE_TIMEOUT_MS, SUBAGENT_BACKGROUND_TIMEOUT_MS, SUBAGENT_DRAIN_TIMEOUT_MS, resolveSubagentTimeoutMs, resolveSubagentIdleTimeoutMs } from './subagent/constants.js';
8
8
  import type { ForkParent, ForkSubagentOptions, SubagentManagerOptions } from './subagent/fork-types.js';
9
- export { DENY_ELICITATION, SUBAGENT_DEFAULT_MAX_TOOL_USE_ITERATIONS, SUBAGENT_DEFAULT_TIMEOUT_MS, SUBAGENT_DEFAULT_IDLE_TIMEOUT_MS, SUBAGENT_BACKGROUND_TIMEOUT_MS, resolveSubagentTimeoutMs, resolveSubagentIdleTimeoutMs, };
9
+ export { DENY_ELICITATION, SUBAGENT_DEFAULT_MAX_TOOL_USE_ITERATIONS, SUBAGENT_DEFAULT_TIMEOUT_MS, SUBAGENT_DEFAULT_IDLE_TIMEOUT_MS, SUBAGENT_BACKGROUND_TIMEOUT_MS, SUBAGENT_DRAIN_TIMEOUT_MS, resolveSubagentTimeoutMs, resolveSubagentIdleTimeoutMs, };
10
10
  export type { ForkParent, ForkSubagentOptions, SubagentManagerOptions };
11
11
  export declare class SubagentManager {
12
12
  private readonly active;
@@ -8,6 +8,7 @@ export interface RunBackgroundBranchArgs {
8
8
  prompt: string;
9
9
  model: string | undefined;
10
10
  parentSessionId: string | undefined;
11
+ onSettled?: (isError: boolean) => void;
11
12
  }
12
13
  export declare function runBackgroundBranch(args: RunBackgroundBranchArgs): Promise<ToolResult>;
13
14
  export {};
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: false-completion-gate
3
+ description: "Fires when a state-mutating task is about to be declared Done (success summary or completion verdict) — to catch silent success, the highest-frequency invisible agent failure where confident 'done' language hides work that never landed. Decomposes the completion into receipt-checkable assertions, then dispatches a reconciliation auditor that cross-references each claim against the session's tool-call LEDGER (did the producing action fire?) and a fresh postcondition READ-BACK of the named artifact (does the file/test/commit exist and match?), gating the Done behind a BACKED/UNBACKED/UNVERIFIABLE verdict plus a bounded repair loop. Distinct from shadow-verify, which RE-DERIVES an investigation finding's correctness — this reconciles a COMPLETION against execution receipts, never re-deriving the claim. Use before any Done/success in implementation, fix, refactor, migration, or multi-file write work. Skip text-terminal sessions (explanation, Q&A) and work that already failed loudly."
4
+ failure_modes:
5
+ - false completeness
6
+ - confident fabrication
7
+ - tool thrash
8
+ ---
9
+
10
+ ## Sub-agent contract
11
+ /contract
12
+
13
+ This skill fires when a session (or a returning sub-agent) is about to declare a state-mutating task **complete** — a Done terminal state, a success summary, or a passing completion verdict. The core invariant: **a completion claim must be backed by execution receipts and a fresh artifact read-back, never by the assertive language of the claim itself.** Claim and evidence must be structurally separated — the base agent fuses them, narrating "done" straight from in-context memory of a tool result it never re-observed. That fusion is exactly how "silent success" propagates a false Done into downstream steps that then compound on a foundation that was never real.
14
+
15
+ **Phase 1 — Claim decomposition.** Before emitting the Done, decompose the pending completion into a checklist of concrete, receipt-checkable assertions. Each assertion names: (a) the **deliverable** ("feature X implemented", "tests green", "branch pushed"), (b) the **producing action** that must have fired to make it true (an Edit/Write to a specific path, a specific test command, a `git push`), and (c) the **durable location** that would prove it (file path + expected content, test-output line, commit SHA). Add a **goal-substitution assertion**: if the original goal was diagnostic (interrogative — "why does X", "what causes Y") but the deliverables are all implementation, the diagnostic answer is itself a required assertion — its absence is an UNBACKED completion, because the question was silently swapped for a patch.
16
+
17
+ **Phase 2 — Receipts reconciliation.** Dispatch one read-only reconciliation auditor (`subagent_type: "awa-private:research-agent"` — locked to Read/Grep/Glob; add a Bash-capable type with `isolation: "worktree"` only if a postcondition needs a command rerun, e.g. re-running the test). It receives ONLY the assertion checklist + the user's original goal — never the orchestrator's success narrative. For each assertion it independently establishes: **ledger_match** — did the producing action actually appear in this session's tool-call history? **postcondition** — read the durable artifact from source *now* (file content, fresh test output, `git log`/`git status`) and check it matches the claim. **durable_location** — confirm evidence is a real location, never transcript-only. The orchestrator hands the auditor this session's tool-call history as the ledger; if that history is unavailable or truncated (a hand-off, a compacted context), the auditor marks the assertion `UNVERIFIABLE` rather than assuming the action fired — the gate fails closed, never open. Returns a receipts table: `{assertion, ledger_match: yes|no, postcondition: pass|fail|unverifiable, durable_location, verdict: BACKED|UNBACKED|UNVERIFIABLE}`.
18
+
19
+ **Gate verdict (merge):**
20
+ - All assertions **BACKED** → `VERIFIED`: emit the Done, attaching the receipts table as the evidence block (durable locations, not prose).
21
+ - Any **UNBACKED** (no producing action in the ledger, or read-back contradicts the claim) → `FALSE-COMPLETION`: do **not** emit Done. Surface the exact unbacked assertion(s) and what the read-back actually showed.
22
+ - Any **UNVERIFIABLE** (external side-effect with no fetchable receipt) → never pass as a confident Done; surface tagged `[needs-human-review]`.
23
+
24
+ **Phase 3 — Bounded repair.** On `FALSE-COMPLETION`, route only the unbacked assertions to a targeted repair pass — re-execute the missing producing action or fix the failing postcondition — then re-run Phase 2 on just those assertions. Cap at **2 repair cycles**. If an assertion is still UNBACKED after 2 cycles, emit a **Blocked** terminal state naming the exact unbacked assertion and the missing receipt — never a Done. The asymmetry is safe by construction: the gate can only ever downgrade a false Done to an honest Blocked/needs-review; it cannot manufacture a completion that wasn't real.
25
+
26
+ **When to invoke:** before any Done / success summary / completion verdict in state-mutating work (implementation, bug fix, refactor, migration, multi-file write, deployment) — especially when the belief that it worked rests on in-context memory of a tool result rather than a fresh read-back, or when the run spanned many steps and the early "success" was never re-observed.
27
+
28
+ **Skip when:** the session is text-terminal (a pure explanation, architecture walkthrough, or Q&A that mutates no artifact — there are no receipts to reconcile); the work already failed loudly (no false-completion risk); or an orchestrator that already verifies its own completion (`ship`, `mint`, `heal`) is driving — invoke once at the outer Done, not per inner step.
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: fix-pr
3
+ description: "One-verb pipeline for the operator's highest-frequency manual loop: fetch a PR's unresolved reviewer feedback (inline review comments, review-summary bodies, and issue-level conversation comments) and failing CI checks, fix them in an isolated managed worktree via a budget-bounded subagent, verify with the project's test gates, and push the fix back to the PR branch. Replaces the retyped recipe 'send a subagent in a worktree to fix <PR feedback>, then push.' Use when a PR has review comments or red CI that needs addressing — e.g. 'fix pr 286', 'address the review on #215', 'CI is red on the worktree-sweep PR'. Never force-pushes, never touches main, fails closed on missing gh auth or un-pushable fork PRs."
4
+ argument-hint: "<PR-number-or-URL> [--repo <path>] [--no-push] [--re-review]"
5
+ surface: "afk"
6
+ failure_modes:
7
+ - push to wrong branch
8
+ - nested /review max_depth self-collision
9
+ - silent partial fix (some comments addressed, done claimed for all)
10
+ - unmanaged worktree leak
11
+ ---
12
+
13
+ ## Sub-agent contract
14
+ /contract
15
+
16
+ `fix-pr` turns "review feedback / red CI on PR N" into a pushed fix commit with test evidence, using worktree isolation so the operator's working tree is never disturbed. It is the composition the operator previously chained by hand: `/resolve`-style feedback interpretation + managed worktree + budget-bounded fix subagent + test gate + push.
17
+
18
+ **Skip when:** the fix is a one-line suggestion the operator pointed at directly (apply inline); the PR is already green with all threads resolved (report and stop); or the work is local-only and unpushed (use `/ship`).
19
+
20
+ ---
21
+
22
+ ### Phase 0 — Input gate & preflight (inline, fail closed)
23
+
24
+ Parse `$ARGUMENTS`:
25
+ - **`pr`** — PR number or URL (required). If absent, stop: "fix-pr requires a PR number or URL."
26
+ - **`repo`** — repo path from `--repo`; default: current working directory's git root.
27
+ - **`no_push`** — from `--no-push`: produce the fix in a kept worktree + diff summary, no remote mutation.
28
+ - **`re_review`** — from `--re-review`: after pushing, re-trigger `/review` (top-level only — see Phase 6 guard).
29
+
30
+ Preflight (all inline bash; any failure → **Blocked**, do not improvise):
31
+ 1. `gh auth status` — must be authenticated. Fail closed if not.
32
+ 2. `gh pr view <pr> --json state,headRefName,headRepositoryOwner,isCrossRepository,mergeable,url` — PR must be OPEN.
33
+ 3. **Fork guard:** if `isCrossRepository` is true and the authenticated account cannot push to the head repo, emit **Blocked** naming the fork and stop. Never attempt workarounds.
34
+ 4. Record `head_branch` — this is the ONLY branch this skill will ever push to.
35
+
36
+ ---
37
+
38
+ ### Phase 1 — Feedback harvest (inline)
39
+
40
+ Reviewer feedback lives in **three distinct GitHub stores** — miss any one and the fix is silently partial. Harvest all three, then the gates:
41
+
42
+ 1. **Inline review comments:** `gh api repos/{owner}/{repo}/pulls/<pr>/comments` — comments anchored to a diff line (each carries `path`/`line`/`diff_hunk`).
43
+ 2. **Review summary bodies:** `gh pr view <pr> --json reviews` — the top-level body of each APPROVE / REQUEST_CHANGES / COMMENT review submission.
44
+ 3. **Issue-level conversation comments:** `gh pr view <pr> --json comments` (equivalently `gh api repos/{owner}/{repo}/issues/<pr>/comments`). A PR is also an issue, and its plain conversation comments ("also update the docs", "rename this before merge") live on the **issues** endpoint — the `pulls/<pr>/comments` endpoint does NOT return them. Skipping this source is the known gap: reviewers who leave feedback as normal PR comments are otherwise dropped entirely, and a PR can have actionable conversation comments with zero inline review comments.
45
+
46
+ Then the gates:
47
+ 4. **Failing CI checks:** `gh pr checks <pr>` — for each failing check, pull the tail of its log (`gh run view --log-failed` when available).
48
+ 5. **PR description acceptance criteria** if present.
49
+
50
+ **Noise filter (apply to sources 1–3 before building the spec):** drop bot/automation chatter (vercel, github-actions, codecov, dependabot, deploy-preview posts) and non-actionable social comments ("LGTM", "thanks", 👍). Keep only unresolved, actionable requests.
51
+
52
+ Routing rule:
53
+ - Any actionable reviewer feedback (inline comments, review bodies, or conversation comments) exists → it is the primary spec; failing checks are secondary gates.
54
+ - **No actionable feedback but CI is red → the failing checks ARE the spec** (acceptance criterion 3).
55
+ - Neither → report "PR is green with no unresolved feedback" and stop (Done, no mutation).
56
+
57
+ Consolidate into a numbered fix spec: each item = source (comment URL or check name), file/line if known, and the requested change. This numbered list is the completeness contract — every item must be addressed or explicitly declared out-of-scope in the terminal report.
58
+
59
+ ---
60
+
61
+ ### Phase 2 — Isolated worktree (inline, managed only)
62
+
63
+ Create the worktree via the **`worktree` tool** (`action: create`, `name: pr<pr>-fix`, `base: <head_branch>` after `git fetch`). **NEVER raw `git worktree add`** — unmanaged trees lack sweep metadata and leak (the 108-worktree/14GB sprawl was this failure mode).
64
+
65
+ If a managed worktree for this PR already exists, reuse it only if clean; otherwise create a fresh one with a suffixed name.
66
+
67
+ ---
68
+
69
+ ### Phase 3 — Fix dispatch (one subagent, budget-bounded)
70
+
71
+ Size the dispatch per `/right-size-delegation` if available; defaults otherwise:
72
+
73
+ Dispatch ONE implementation subagent (`agent` tool, `cwd: <worktree path>`, `max_turns: 25`, model right-sized to the diff — `sonnet` default, `haiku` never for code fixes):
74
+ - inputs: the numbered fix spec, `head_branch`, repo test/lint commands (inferred from package.json / Makefile / pyproject.toml and passed explicitly).
75
+ - goal: address every numbered item with minimal diffs; run the narrowest relevant tests per item; commit locally with a message referencing the PR (`fix(pr-<pr>): address review feedback`).
76
+ - non_goals: do NOT push, do NOT touch branches other than the checked-out one, do NOT invoke /review or any skill, do NOT expand scope beyond the numbered items.
77
+ - deliverable: per-item status table (`fixed` | `out-of-scope <reason>`), unified diff summary, targeted test output, local commit SHA.
78
+
79
+ ---
80
+
81
+ ### Phase 4 — Verification gate (inline in the worktree)
82
+
83
+ Run the project's full test/lint gates in the worktree yourself — do not trust the subagent's report alone.
84
+
85
+ - All green → Phase 5.
86
+ - Failures → iterate: re-dispatch the fix subagent with the failure output as an updated spec (or hand off to `/heal` semantics if that skill is loadable), **≤2 iterations**. Cap reached → keep the worktree, emit **Blocked** naming the branch, the worktree path, the surviving failures, and the per-item status table.
87
+
88
+ **Completeness check:** every numbered spec item must be `fixed` or explicitly `out-of-scope` with a reason. A partially addressed spec is never reported as Done.
89
+
90
+ ---
91
+
92
+ ### Phase 5 — Push (guarded)
93
+
94
+ - `no_push` set → `worktree keep` (reason: "fix-pr --no-push review pending"), emit the diff summary + per-item table, stop (Done, no remote mutation).
95
+ - Otherwise: `git push origin <head_branch>` from the worktree. **Plain push only — never `--force`, never `--force-with-lease`, never any other ref.** If push is rejected (non-fast-forward because the PR moved), fetch + rebase the fix commits onto the new head, re-run Phase 4 gates, push again. If still rejected → Blocked.
96
+
97
+ ---
98
+
99
+ ### Phase 6 — Optional re-review (top-level guard)
100
+
101
+ If `re_review`: invoke `/review` **directly from this top-level session only — NEVER from inside a subagent** (known max_depth self-collision: 100+ `delegation.skipped reason:"max_depth" requested_name:"review"` entries in routing-decisions.jsonl). If the current session is itself a subagent (check `get_runtime_state` depth), skip re-review and note it in the terminal report instead.
102
+
103
+ ---
104
+
105
+ ### Phase 7 — Cleanup & terminal state
106
+
107
+ - Success: `worktree remove` (branch ref is preserved automatically).
108
+ - Failure/Blocked: keep the worktree, name its path and branch in the report.
109
+
110
+ **Done** must cite: pushed commit SHA(s), the PR URL, the per-item fix table, and test-gate output location.
111
+ **Blocked** must cite: exact unblock condition (auth, fork perms, surviving test failures), worktree path, and everything already fixed.
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: polish
3
+ description: "Iteratively refines any artifact (prose, spec, prompt, API design, SKILL.md, code snippet) by running a decoupled evaluator → generator loop until explicit criteria are met or an iteration cap is reached. Locks criteria in Wave 1, isolates the evaluator from generation history to prevent sycophancy, and emits the best version with any remaining gaps flagged at cap."
4
+ ---
5
+
6
+ ## Sub-agent contract
7
+ /contract
8
+
9
+ `polish` is a three-wave evaluator-optimizer loop that applies to any artifact at any workflow stage. Wave 1 locks concrete, testable criteria. Waves 2 and 3 alternate (evaluator → generator) until all criteria pass or the iteration cap is reached. The evaluator is always spawned stateless — it never sees prior generation history — eliminating the sycophancy failure mode that plagues shared-context review loops.
10
+
11
+ The orchestrator coordinates but never edits the artifact directly. All revisions are produced by the Wave 3 generator, which receives only the `blocking_gaps` list from the evaluator (not its full reasoning). This constraint prevents scope-creep revisions and keeps each iteration focused on the delta between current state and the locked criteria.
12
+
13
+ On cap exhaust the orchestrator emits the best-scoring version plus a structured `remaining_gaps` report so the caller can decide whether to extend the cap, escalate to a human, or ship with known gaps documented.
14
+
15
+ ---
16
+
17
+ ## Inputs
18
+
19
+ | Field | Required | Description |
20
+ |---|---|---|
21
+ | `artifact` | yes | The text, spec, prompt, design doc, or code snippet to refine |
22
+ | `goal` | yes | Natural-language quality intent ("make this safe for external stakeholders") |
23
+ | `criteria` | no | Pre-supplied testable criteria — skips Wave 1 extraction if provided |
24
+ | `threshold` | no | `pass_all` (default) or `pass_N` where N is an integer |
25
+ | `cap` | no | Max refinement iterations (default: 4, max: 8) |
26
+
27
+ ---
28
+
29
+ ## Wave 1 — Criteria Extraction
30
+
31
+ **Trigger:** Always, unless `criteria` were supplied by the caller.
32
+
33
+ **Agent:** Single subagent. Reads `artifact` + `goal`. Produces:
34
+
35
+ ```json
36
+ {
37
+ "criteria": [
38
+ { "id": "C1", "text": "<concrete, binary-testable criterion>" },
39
+ ...
40
+ ],
41
+ "threshold": "pass_all | pass_N",
42
+ "cap": 4
43
+ }
44
+ ```
45
+
46
+ **Rules:**
47
+ - Criteria must be falsifiable — "no jargon visible to end-users", not "improve clarity".
48
+ - Maximum 8 criteria. If the goal implies more, merge related ones.
49
+ - Criteria are **locked** after Wave 1. The evaluator may not add or modify them.
50
+
51
+ **Exit:** Emit `criteria.json`. Proceed to Wave 2.
52
+
53
+ ---
54
+
55
+ ## Wave 2 — Evaluation (stateless)
56
+
57
+ **Agent:** Single subagent, spawned fresh with **no generation history**. Receives:
58
+ - Current artifact (text only)
59
+ - Locked `criteria.json`
60
+
61
+ **Produces:**
62
+
63
+ ```json
64
+ {
65
+ "scores": [
66
+ { "criterion_id": "C1", "pass": true, "gap": "" },
67
+ { "criterion_id": "C2", "pass": false, "gap": "<specific, actionable description of what is missing or wrong>" }
68
+ ],
69
+ "overall": "pass | fail",
70
+ "blocking_gaps": ["C2: <gap text>", ...]
71
+ }
72
+ ```
73
+
74
+ **Rules:**
75
+ - If `overall: pass` (threshold met) → skip Wave 3, emit final artifact, terminate loop.
76
+ - If iteration count == cap → skip Wave 3, emit best version + `remaining_gaps`, terminate loop.
77
+ - Gap text must be specific enough for Wave 3 to act on without re-reading evaluator reasoning.
78
+
79
+ ---
80
+
81
+ ## Wave 3 — Revision
82
+
83
+ **Agent:** Single subagent. Receives:
84
+ - Current artifact
85
+ - `blocking_gaps` list (text only — NOT the evaluator's full `scores` object)
86
+ - Original `goal` (for orientation only, not as new criteria)
87
+
88
+ **Produces:** Revised artifact (full text, not a diff).
89
+
90
+ **Rules:**
91
+ - Address only the `blocking_gaps`. Do not make unrequested changes.
92
+ - Do not invent new criteria or second-guess passing scores.
93
+ - Output is the next artifact fed into Wave 2.
94
+
95
+ ---
96
+
97
+ ## Loop Control
98
+
99
+ ```
100
+ iteration = 0
101
+ artifact = <input>
102
+
103
+ Wave 1 → criteria.json
104
+
105
+ loop:
106
+ iteration += 1
107
+ result = Wave 2(artifact, criteria.json)
108
+ if result.overall == "pass":
109
+ emit artifact, result.scores, iteration_count
110
+ DONE
111
+ if iteration == cap:
112
+ emit artifact, result.remaining_gaps, "CAP_REACHED"
113
+ DONE
114
+ artifact = Wave 3(artifact, result.blocking_gaps)
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Outputs
120
+
121
+ | State | Emitted |
122
+ |---|---|
123
+ | Converged | Final artifact + `{status: "CONVERGED", iterations: N, scores: [...]}` |
124
+ | Cap reached | Best artifact + `{status: "CAP_REACHED", iterations: cap, remaining_gaps: [...]}` |
125
+
126
+ `CAP_REACHED` is not a failure — it is an explicit signal for the caller to extend, escalate, or ship with documented gaps. The orchestrator never silently discards gap information.
127
+
128
+ ---
129
+
130
+ ## Composition
131
+
132
+ `polish` is designed to run *after* a generative skill and *before* `ship`:
133
+
134
+ ```
135
+ spec → forge → polish → ship
136
+ research → mint → polish → ship
137
+ <any draft> → polish → ship
138
+ ```
139
+
140
+ It may also be called standalone on an existing artifact with explicit `criteria` supplied.