From 93d0a20dd297640e5f8113300eca25fa69bbab46 Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 3 Nov 2026 02:49:07 +0000 Subject: [PATCH] fix: Done-means honors loadout section exclusions and the skill's own stop rules (Step 11 voices) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- investigate/SKILL.md | 2 +- review/SKILL.md | 2 +- scripts/resolvers/contract-done-means.ts | 16 ++++++++++------ test/fixtures/skill-md-size-allowlist.json | 18 +++++++++--------- test/helpers/carve-guards.ts | 4 ++-- test/skill-descriptions.test.ts | 9 +++++---- 6 files changed, 28 insertions(+), 23 deletions(-) diff --git a/investigate/SKILL.md b/investigate/SKILL.md index 3fd0c0cf5..20f6455a7 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -85,7 +85,7 @@ gbrain: Finish only when each of these exists (contract v1): -- `debug-report`: a `DEBUG REPORT` with Symptom, Root cause, Fix, Evidence, Regression test and Status filled in. +- `debug-report`: a `DEBUG REPORT` with Symptom, Root cause, Fix, Evidence, Regression test and Status filled in (`none: ` where the skill's own stop rule leaves one empty). - `check-after-last-edit`: a test or repro command run after your last edit. ## Shared preamble (load first) diff --git a/review/SKILL.md b/review/SKILL.md index f2a3e2828..dbd2e97e0 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -44,7 +44,7 @@ Finish only when each of these exists (contract v1): - `specialists-dispatched`: the specialist selection printed (`Dispatching N specialists:` ...), then one Agent dispatch per specialist it names (none when a small diff skips them). - `adversarial-pass`: an Agent dispatch for the adversarial pass, its prompt including "Think like an attacker and a chaos engineer". -- `review-report`: a `Pre-Landing Review` with Gate edits filled in. +- `review-report`: a `Pre-Landing Review` with Gate edits filled in (`none: ` where the skill's own stop rule leaves one empty). ## Shared preamble (load first) diff --git a/scripts/resolvers/contract-done-means.ts b/scripts/resolvers/contract-done-means.ts index 50fe40854..bc9743ef0 100644 --- a/scripts/resolvers/contract-done-means.ts +++ b/scripts/resolvers/contract-done-means.ts @@ -32,10 +32,10 @@ const KIND_FIELDS: Record = { agent_dispatch: ['match'], }; -// The resolver that renders the step each kind checks today. A host that suppresses it never shows that step, so its items are left out of the list there. -const BACKED_BY: Partial> = { - selected_specialists_dispatched: 'REVIEW_ARMY', - agent_dispatch: 'ADVERSARIAL_STEP', +// The resolver and section that render the step each kind checks today. A host that suppresses the resolver, or a loadout that excludes the section, never shows that step, so its items are left out of the list there. +const BACKED_BY: Partial> = { + selected_specialists_dispatched: { resolver: 'REVIEW_ARMY', section: 'review-army' }, + agent_dispatch: { resolver: 'ADVERSARIAL_STEP', section: 'adversarial' }, }; const PLACEHOLDER = /^\[.*\]$|^<.*>$|^\.\.\.$|^(todo|tbd|n\/a)$/i; @@ -106,7 +106,7 @@ const list = (xs: string[]) => xs.length < 2 ? xs.join('') : xs.slice(0, -1).joi function plainWords(item: ContractItem): string { switch (item.kind) { - case 'report_labels': return 'a ' + code(item.heading) + ' with ' + list(item.labels) + ' filled in'; + case 'report_labels': return 'a ' + code(item.heading) + ' with ' + list(item.labels) + ' filled in (' + code('none: ') + ' where the skill\'s own stop rule leaves one empty)'; case 'check_after_last_edit': return 'a test or repro command run after your last edit'; case 'selected_specialists_dispatched': return 'the specialist selection printed (' + code(item.marker) + ' ...), then one Agent dispatch per specialist it names (none when a small diff skips them)'; case 'agent_dispatch': return 'an Agent dispatch for the ' + item.id.replace(/-/g, ' ') + ', its prompt including "' + item.match + '"'; @@ -131,5 +131,9 @@ export function generateContractDoneMeans(ctx: TemplateContext): string { const where = path.relative(ROOT, ctx.tmplPath).split(path.sep).join('/'); const contract = contractFromTemplate(fs.readFileSync(ctx.tmplPath, 'utf-8'), where, { required: true })!; const suppressed = new Set(getHostConfig(ctx.host).suppressedResolvers ?? []); - return renderDoneMeans({ ...contract, items: contract.items.filter(item => !suppressed.has(BACKED_BY[item.kind] ?? '')) }); + const shown = (item: ContractItem) => { + const backing = BACKED_BY[item.kind]; + return !backing || (!suppressed.has(backing.resolver) && !ctx.modules?.excluded.has('section:' + ctx.skillName + '/' + backing.section)); + }; + return renderDoneMeans({ ...contract, items: contract.items.filter(shown) }); } diff --git a/test/fixtures/skill-md-size-allowlist.json b/test/fixtures/skill-md-size-allowlist.json index e3cf5f6d4..46678fc24 100644 --- a/test/fixtures/skill-md-size-allowlist.json +++ b/test/fixtures/skill-md-size-allowlist.json @@ -6,52 +6,52 @@ "agy/gstack-plan-design-review": 109107, "agy/gstack-plan-devex-review": 114948, "agy/gstack-plan-eng-review": 143581, - "agy/gstack-review": 104423, + "agy/gstack-review": 104488, "codex/gstack-autoplan": 108400, "codex/gstack-plan-design-review": 110464, "codex/gstack-plan-devex-review": 116456, "codex/gstack-plan-eng-review": 144746, - "codex/gstack-review": 103946, + "codex/gstack-review": 104011, "copilot/gstack-autoplan": 109100, "copilot/gstack-plan-design-review": 109373, "copilot/gstack-plan-devex-review": 114410, "copilot/gstack-plan-eng-review": 143746, - "copilot/gstack-review": 104004, + "copilot/gstack-review": 104069, "cursor/gstack-autoplan": 108332, "cursor/gstack-plan-design-review": 107915, "cursor/gstack-plan-devex-review": 113680, "cursor/gstack-plan-eng-review": 142336, - "cursor/gstack-review": 122476, + "cursor/gstack-review": 122541, "factory/gstack-autoplan": 110610, "factory/gstack-plan-design-review": 111013, "factory/gstack-plan-devex-review": 117043, "factory/gstack-plan-eng-review": 145444, - "factory/gstack-review": 126249, + "factory/gstack-review": 126314, "gbrain/gstack-autoplan": 108739, "gbrain/gstack-plan-eng-review": 124963, "gemini/gstack-autoplan": 108890, "gemini/gstack-plan-design-review": 109201, "gemini/gstack-plan-devex-review": 114238, "gemini/gstack-plan-eng-review": 143579, - "gemini/gstack-review": 103826, + "gemini/gstack-review": 103891, "hermes/gstack-autoplan": 108742, "hermes/gstack-plan-eng-review": 124999, "kiro/gstack-autoplan": 108106, "kiro/gstack-plan-design-review": 107761, "kiro/gstack-plan-devex-review": 113532, "kiro/gstack-plan-eng-review": 142198, - "kiro/gstack-review": 122300, + "kiro/gstack-review": 122365, "openclaw/gstack-autoplan": 108562, "openclaw/gstack-plan-eng-review": 123478, "opencode/gstack-autoplan": 109081, "opencode/gstack-plan-design-review": 109229, "opencode/gstack-plan-devex-review": 114288, "opencode/gstack-plan-eng-review": 143605, - "opencode/gstack-review": 123098, + "opencode/gstack-review": 123163, "slate/gstack-autoplan": 108219, "slate/gstack-plan-design-review": 107838, "slate/gstack-plan-devex-review": 113606, "slate/gstack-plan-eng-review": 142267, - "slate/gstack-review": 122388 + "slate/gstack-review": 122453 } } diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index 7d6e1ad38..aa6b49cd5 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -490,10 +490,10 @@ do not launch the downstream skill or open a browser.`, gateAfterStop: undefined, // operational multi-STOP skill, like ship }, behavioral: 'plan', - maxSkeletonBytes: 59_709, // GST-1088 lane B (raised from 59,073 to measured, no headroom): the contract: frontmatter block and the F1 "Done means" list rendered from it at the top of the body, plus widened triggers; F1 is a gate-rev-2 recommendation on the plan Garry approved with "do it" on gate rev 1 (2026-11-03), contingent on his gate-rev-2 answer, and lane C's diet is expected to bring /review back under 59,073; measured 59,709. Before: GSTA-96 hoist (2026-10-26): lowered from 80_900 by the shared preamble that moved to sections/preamble.md, keeping the pre-hoist headroom of 319 bytes; measured 58,754. Before: Shared-code identity/skip/action rules + critical-severity validation; measured 74,493 (2026-09-17). + v1.91.12.0 merge of #2999 (review clarity repairs: await reads, research alongside dispatch, /review deadline and setup authority, findings sources) with #3002 (guarded state-root lines, plan-check checkpoints); each fit alone; measured 74,881 (2026-10-01). + Oct 8 wave: shared voice directive bans "load-bearing" and adds the reply-language rule, offset by tightening the bounded closer (net +17 bytes per tier-2+ skill); measured 74,884 (2026-10-08). + gate integrity (HONEST_WORK_GATE_INTEGRITY W2): Step 3.5 Diff scans with the shared GATE_SCAN_BLOCK, the Step 4 carry, the four-option `review-gate-disposition` question in 5c, gate record fields in 5.8 and the Gate edits report line; measured 80,054 (2026-10-10). + Oct 10 multi-agent wave PR D2 (sideways review): Step 4's enum-only paragraph becomes the sideways sweep with the forced-CRITICAL list, plus the flake-fix number sentence in the Gate Integrity paragraph; measured skeleton 80,812 (2026-10-10). + maxSkeletonBytes: 59_774, // GST-1088 lane B (raised from 59,073 to measured, no headroom): the contract: frontmatter block and the F1 "Done means" list rendered from it at the top of the body, plus widened triggers; F1 is a gate-rev-2 recommendation on the plan Garry approved with "do it" on gate rev 1 (2026-11-03), contingent on his gate-rev-2 answer, and lane C's diet is expected to bring /review back under 59,073; measured 59,774. Before: GSTA-96 hoist (2026-10-26): lowered from 80_900 by the shared preamble that moved to sections/preamble.md, keeping the pre-hoist headroom of 319 bytes; measured 58,754. Before: Shared-code identity/skip/action rules + critical-severity validation; measured 74,493 (2026-09-17). + v1.91.12.0 merge of #2999 (review clarity repairs: await reads, research alongside dispatch, /review deadline and setup authority, findings sources) with #3002 (guarded state-root lines, plan-check checkpoints); each fit alone; measured 74,881 (2026-10-01). + Oct 8 wave: shared voice directive bans "load-bearing" and adds the reply-language rule, offset by tightening the bounded closer (net +17 bytes per tier-2+ skill); measured 74,884 (2026-10-08). + gate integrity (HONEST_WORK_GATE_INTEGRITY W2): Step 3.5 Diff scans with the shared GATE_SCAN_BLOCK, the Step 4 carry, the four-option `review-gate-disposition` question in 5c, gate record fields in 5.8 and the Gate edits report line; measured 80,054 (2026-10-10). + Oct 10 multi-agent wave PR D2 (sideways review): Step 4's enum-only paragraph becomes the sideways sweep with the forced-CRITICAL list, plus the flake-fix number sentence in the Gate Integrity paragraph; measured skeleton 80,812 (2026-10-10). minUnionBytes: 89_000, // Phase 4 wave 1; measured union 93,357 mustContain: ['confidence', 'P1', 'P2', 'Review Army', 'adversarial'], - maxSizeRatio: 1.3255, // + GST-1088 lane B (raised from 1.3180 to measured, no headroom): the contract: frontmatter block and the F1 "Done means" list, contingent on Garry's gate-rev-2 answer; lane C's diet is expected to bring /review back under 1.3180; measured 143,842 / 108,523 = 1.3255. Before: + GSTA-167 variant G part 1 (plan approved by Garry 2026-10-30, autoplan-20261030161754 "Do it", which names about +0.3k prompt tokens per voice): the native adversarial and outside-voice prompts carry the blocking floor and the Severity and Blocks lines, +537 bytes; measured 1.3180 (2026-10-30). Before: 1.3136. GSTA-96 hoist merged with GSTA-92 phase 2 (2026-10-26): both owner-approved raises together, measured 142,550 / 108,523 = 1.3135. Before: Shared-code feature + critical-severity validation: 128,042 union bytes / 108,523 baseline = 1.1799; preserves content floors. + v1.91.12.0 merge of #2999 (above, plus plan-completion fallback intent and specialist checklist-by-path) with #3002; measured 1.1843 (2026-10-01). + v1.91.19.0 wave: verdict-form Codex callers with sandbox preflight (INV-1/B1), P0/P1 structured gate (B1b), bound-plan discovery and data-not-instructions guard (F1/D3), visible LEARNINGS failures (B5); measured 129,941 / 108,523 = 1.1974 (2026-10-03). + v1.91.18.0 (#3023) merged into the v1.91.19.0 wave; measured 130,025 / 108,523 = 1.1981 (2026-10-04). + Oct 6 fix wave: quota_exhausted preflight branch (#3051); measured 130,359 / 108,523 = 1.2012 (2026-10-06). + Oct 6 fix wave A3/CEO-12: free text reaches commands only through agent-written mktemp files (mktemp block, write rule, path rebuild and refusal at each posting, prompt and brief site); measured 131,314 / 108,523 = 1.2100 (2026-10-06). Combined Oct 6 wave head (A and B): measured 131,966 / 108,523 = 1.2160 (2026-10-06). + Oct 7 wave B3: the Codex probe runs as a command (select-model capture lines, check-sandbox/probe-model/run-with-timeout subcommands) instead of being sourced; measured 132,199 / 108,523 = 1.2182 (2026-10-07). + Oct 7 wave CEO-28/DX-11: review prompts ask for a severity per finding and design proposals use the proposal gate; measured 132,258 / 108,523 = 1.2187 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; measured 132,233 / 108,523 = 1.2185 (2026-10-07). Merged with the guard area: measured 132,522 / 108,523 = 1.2211 (2026-10-07). + Oct 8 wave: the defensive-security framing and fixture summary mode reach the security specialist and the Red Team (stated once in the dispatch, referenced by the Red Team); measured 133,902 / 108,523 = 1.2339 (2026-10-08). + gate integrity W2 (above); measured 1.282 (2026-10-10). + Oct 10 multi-agent wave PR D2 (sideways review): sideways sweep and flake-fix number in Step 4; `Diff read` and `Undeclared behavior changes` in the review-mode Scope Check (plan-completion section); measured 140,497 / 108,523 = 1.2946 (2026-10-10). + harness lane E (2026-10-26): the lead-judgment stage in the finding merge and the `cause` anchor in the specialist prompts, shared with /ship through scripts/resolvers/review-army.ts; measured 141,949 / 108,523 = 1.3080. + maxSizeRatio: 1.3261, // + GST-1088 lane B (raised from 1.3180 to measured, no headroom): the contract: frontmatter block and the F1 "Done means" list, contingent on Garry's gate-rev-2 answer; lane C's diet is expected to bring /review back under 1.3180; measured 143,907 / 108,523 = 1.3261. Before: + GSTA-167 variant G part 1 (plan approved by Garry 2026-10-30, autoplan-20261030161754 "Do it", which names about +0.3k prompt tokens per voice): the native adversarial and outside-voice prompts carry the blocking floor and the Severity and Blocks lines, +537 bytes; measured 1.3180 (2026-10-30). Before: 1.3136. GSTA-96 hoist merged with GSTA-92 phase 2 (2026-10-26): both owner-approved raises together, measured 142,550 / 108,523 = 1.3135. Before: Shared-code feature + critical-severity validation: 128,042 union bytes / 108,523 baseline = 1.1799; preserves content floors. + v1.91.12.0 merge of #2999 (above, plus plan-completion fallback intent and specialist checklist-by-path) with #3002; measured 1.1843 (2026-10-01). + v1.91.19.0 wave: verdict-form Codex callers with sandbox preflight (INV-1/B1), P0/P1 structured gate (B1b), bound-plan discovery and data-not-instructions guard (F1/D3), visible LEARNINGS failures (B5); measured 129,941 / 108,523 = 1.1974 (2026-10-03). + v1.91.18.0 (#3023) merged into the v1.91.19.0 wave; measured 130,025 / 108,523 = 1.1981 (2026-10-04). + Oct 6 fix wave: quota_exhausted preflight branch (#3051); measured 130,359 / 108,523 = 1.2012 (2026-10-06). + Oct 6 fix wave A3/CEO-12: free text reaches commands only through agent-written mktemp files (mktemp block, write rule, path rebuild and refusal at each posting, prompt and brief site); measured 131,314 / 108,523 = 1.2100 (2026-10-06). Combined Oct 6 wave head (A and B): measured 131,966 / 108,523 = 1.2160 (2026-10-06). + Oct 7 wave B3: the Codex probe runs as a command (select-model capture lines, check-sandbox/probe-model/run-with-timeout subcommands) instead of being sourced; measured 132,199 / 108,523 = 1.2182 (2026-10-07). + Oct 7 wave CEO-28/DX-11: review prompts ask for a severity per finding and design proposals use the proposal gate; measured 132,258 / 108,523 = 1.2187 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; measured 132,233 / 108,523 = 1.2185 (2026-10-07). Merged with the guard area: measured 132,522 / 108,523 = 1.2211 (2026-10-07). + Oct 8 wave: the defensive-security framing and fixture summary mode reach the security specialist and the Red Team (stated once in the dispatch, referenced by the Red Team); measured 133,902 / 108,523 = 1.2339 (2026-10-08). + gate integrity W2 (above); measured 1.282 (2026-10-10). + Oct 10 multi-agent wave PR D2 (sideways review): sideways sweep and flake-fix number in Step 4; `Diff read` and `Undeclared behavior changes` in the review-mode Scope Check (plan-completion section); measured 140,497 / 108,523 = 1.2946 (2026-10-10). + harness lane E (2026-10-26): the lead-judgment stage in the finding merge and the `cause` anchor in the specialist prompts, shared with /ship through scripts/resolvers/review-army.ts; measured 141,949 / 108,523 = 1.3080. }, codex: { skill: 'codex', diff --git a/test/skill-descriptions.test.ts b/test/skill-descriptions.test.ts index d40c43f92..98523b115 100644 --- a/test/skill-descriptions.test.ts +++ b/test/skill-descriptions.test.ts @@ -157,7 +157,7 @@ describe('contract-done-means renderer', () => { test('names each item kind in plain words', () => { expect(out).toContain('## Done means'); - expect(out).toContain('- ' + BT + 'debug-report' + BT + ': a ' + BT + 'DEBUG REPORT' + BT + ' with Symptom, Root cause and Status filled in.'); + expect(out).toContain('- ' + BT + 'debug-report' + BT + ': a ' + BT + 'DEBUG REPORT' + BT + ' with Symptom, Root cause and Status filled in (' + BT + 'none: ' + BT + " where the skill's own stop rule leaves one empty)."); expect(out).toContain('- ' + BT + 'check-after-last-edit' + BT + ': a test or repro command run after your last edit.'); expect(out).toContain('- ' + BT + 'specialists-dispatched' + BT + ': the specialist selection printed (' + BT + 'Dispatching N specialists:' + BT + ' ...), then one Agent dispatch per specialist it names (none when a small diff skips them).'); expect(out).toContain('- ' + BT + 'adversarial-pass' + BT + ': an Agent dispatch for the adversarial pass, its prompt including "chaos engineer".'); @@ -205,9 +205,10 @@ describe('contract-done-means renderer', () => { expect(renderDoneMeans(all, false)).not.toContain('gstack-skill-contract'); }); - test('a host that suppresses the step an item checks leaves that item out', () => { - const render = (host: string) => generateContractDoneMeans({ skillName: 'review', tmplPath: join(ROOT, 'review', 'SKILL.md.tmpl'), host, paths: HOST_PATHS[host] } as Parameters[0]); - const ids = (host: string) => [...render(host).matchAll(/^- \u0060([a-z-]+)\u0060:/gm)].map(m => m[1]); + test('a host that suppresses, or a loadout that excludes, the step an item checks leaves that item out', () => { + const render = (host: string, excluded: string[] = []) => generateContractDoneMeans({ skillName: 'review', tmplPath: join(ROOT, 'review', 'SKILL.md.tmpl'), host, paths: HOST_PATHS[host], modules: excluded.length ? { name: 'test', excluded: new Set(excluded) } : undefined } as unknown as Parameters[0]); + const ids = (host: string, excluded: string[] = []) => [...render(host, excluded).matchAll(/^- \u0060([a-z-]+)\u0060:/gm)].map(m => m[1]); + expect(ids('claude', ['section:review/review-army', 'section:review/adversarial'])).toEqual(['review-report']); expect(ids('claude')).toEqual(['specialists-dispatched', 'adversarial-pass', 'review-report']); expect(ids('codex')).toEqual(['adversarial-pass', 'review-report']); expect(ids('hermes')).toEqual(['review-report']);