GST-1088 lane C review fix round: drop the adversarial contract marker (match alone gates it), shared-preamble cut check, changelog at post-#3909 sizes, pins at measured (review 56,716, ratio 1.2979)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-11-03 16:00:03 +00:00
1 parent 27052ffd77
commit 8cd564a729
8 files changed
+18 -19

No files matched your search

+2 -2
View File
@@ -1,7 +1,7 @@
**/investigate loads about 3.4 KB less and /review about 3.3 KB less, and a /review blocker now needs proof (GST-1088 lane C, the candidate).** Opus 5.5 read these skills in full and still skipped their required steps (Run D, gstack-evals#140). The forced full /review caught 67.2% of blocking bugs but raised 2.71 false blockers per PR per pass (gstack-evals#120). This change cuts pieces of the two skills that nothing showed were doing work, and it makes every blocking /review finding prove itself. It merges only after the GST-1089 no-loss replay passes.
**/investigate loads about 3.4 KB less and /review about 3.2 KB less, and a /review blocker now needs proof (GST-1088 lane C, the candidate).** Opus 5.5 read these skills in full and still skipped their required steps (Run D, gstack-evals#140). The forced full /review caught 67.2% of blocking bugs but raised 2.71 false blockers per PR per pass (gstack-evals#120). This change cuts pieces of the two skills that nothing showed were doing work, and it makes every blocking /review finding prove itself. It merges only after the GST-1089 no-loss replay passes.
### Changed
- **Cuts, every loadout and every host**: /investigate drops its learnings search, Capture Learnings and the investigation-typed learnings log. /review drops its learnings search, Capture Learnings, the repo-ownership preamble section and the Step 3.4 VERSION queue line. Always-loaded bytes: /investigate 20,243 to 16,839, /review 58,815 to 55,517 (Claude SKILL.md, frontmatter included). Telemetry (`gstack-skill-end`) stays, because it writes the timeline's `completed` event with the real outcome. No other skill changes on any host.
- **Cuts, every loadout and every host**: /investigate drops its learnings search, Capture Learnings and the investigation-typed learnings log. /review drops its learnings search, Capture Learnings, the repo-ownership preamble section and the Step 3.4 VERSION queue line. Always-loaded bytes: /investigate 21,242 to 17,838, /review 60,014 to 56,716 (Claude SKILL.md, frontmatter included, against v1.94.24.0). Telemetry (`gstack-skill-end`) stays, because it writes the timeline's `completed` event with the real outcome. No other skill changes on any host.
- **/review blocker proof**: a CRITICAL finding names its floor reason (fails a test, breaks production, loses data or secrets, or one of the forced-CRITICAL classes) and carries a repro, a failing test, or a cited line with its consequence. /review checks a forwarded finding and adds both itself. A CRITICAL nobody proves is INFORMATIONAL. This applies in the critical pass and in the pre-emit verification gate.
- **/review markers**: specialist selection always prints one `Dispatching N specialists:` line, small diffs included (`Dispatching 0 specialists: (small diff)`), and the native adversarial pass prints `Dispatching adversarial pass: native` before its Agent call. /ship's text is unchanged.
+1 -1
View File
@@ -70,6 +70,6 @@
],
"pins": {
"investigate": 17838,
"review": 56769
"review": 56716
}
}
-1
View File
@@ -29,7 +29,6 @@ contract:
- id: adversarial-pass
kind: agent_dispatch
match: Think like an attacker and a chaos engineer
marker: "Dispatching adversarial pass: native"
- id: review-report
kind: report_labels
heading: Pre-Landing Review
-1
View File
@@ -35,7 +35,6 @@ contract:
- id: adversarial-pass
kind: agent_dispatch
match: Think like an attacker and a chaos engineer
marker: "Dispatching adversarial pass: native"
- id: review-report
kind: report_labels
heading: Pre-Landing Review
+2 -10
View File
@@ -18,7 +18,7 @@ export type ContractItem =
| { id: string; kind: 'report_labels'; heading: string; labels: string[] }
| { id: string; kind: 'check_after_last_edit' }
| { id: string; kind: 'selected_specialists_dispatched'; marker: string }
| { id: string; kind: 'agent_dispatch'; match: string; marker?: string };
| { id: string; kind: 'agent_dispatch'; match: string };
export interface SkillContract {
version: 1;
@@ -32,11 +32,6 @@ const KIND_FIELDS: Record<ContractItem['kind'], string[]> = {
agent_dispatch: ['match'],
};
// Fields a kind may carry; when present each must be non-empty text. bin/gstack-skill-contract checks agent_dispatch's marker before its match.
const OPTIONAL_FIELDS: Partial<Record<ContractItem['kind'], string[]>> = {
agent_dispatch: ['marker'],
};
// 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<Record<ContractItem['kind'], { resolver: string; section: string }>> = {
selected_specialists_dispatched: { resolver: 'REVIEW_ARMY', section: 'review-army' },
@@ -74,14 +69,11 @@ export function parseSkillContract(value: unknown, where: string): SkillContract
if (seen.has(raw.id)) fail(where, at + ' duplicate id "' + raw.id + '"');
seen.add(raw.id);
for (const key of Object.keys(raw)) {
if (key !== 'id' && key !== 'kind' && !KIND_FIELDS[kind].includes(key) && !OPTIONAL_FIELDS[kind]?.includes(key)) fail(where, at + ' (' + kind + ') has unknown key "' + key + '"');
if (key !== 'id' && key !== 'kind' && !KIND_FIELDS[kind].includes(key)) fail(where, at + ' (' + kind + ') has unknown key "' + key + '"');
}
for (const field of KIND_FIELDS[kind]) {
if (field !== 'labels' && !isText(raw[field])) fail(where, at + ' ' + field + ' must be a non-empty string');
}
for (const field of OPTIONAL_FIELDS[kind] ?? []) {
if (field in raw && !isText(raw[field])) fail(where, at + ' ' + field + ' must be a non-empty string');
}
if (kind === 'report_labels') {
if (!Array.isArray(raw.labels) || raw.labels.length === 0) fail(where, at + ' labels must be a non-empty list');
for (const label of raw.labels as unknown[]) {
+11
View File
@@ -142,6 +142,17 @@ describe('cut ledger against every host render', () => {
if (violations.length) throw new Error(`${violations.length} cut ledger violation(s):\n ${violations.join('\n ')}`);
});
test('a preamble cut never comes back through the shared preamble file, on any host', () => {
const leaks: string[] = [];
for (const host of ALL_HOST_CONFIGS) for (const cut of ledger.cuts.filter(c => c.preamble_piece)) {
const file = host.name === 'claude'
? path.join(ROOT, cut.skill, 'sections', SHARED_PREAMBLE_FILE)
: path.join(outDir, host.hostSubdir, 'skills', `gstack-${cut.skill}`, 'sections', SHARED_PREAMBLE_FILE);
if (fs.existsSync(file) && fs.readFileSync(file, 'utf-8').includes(cut.match)) leaks.push(`${host.name}: ${cut.id} "${cut.match}" in ${path.relative(outDir, file)}`);
}
expect(leaks).toEqual([]);
});
test('review-only GST-1088 text renders in /review alone, on every host', () => {
const leaks = Object.entries(renders).filter(([key]) => !key.endsWith('/review'))
.flatMap(([key, text]) => REVIEW_ONLY.filter(s => text.includes(s)).map(s => `${key}: "${s}"`));
+2 -2
View File
@@ -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: 56_769, // GST-1088 lane C diet (R56 cut ledger modules/cuts.json: learnings search and capture, repo ownership, queue status; plus the blocker-proof rule, the two dispatch markers and the adversarial-pass contract marker): lowered from 60,014 to measured, no headroom; measured 56,769 with #3909 merged (2026-11-03). Before: GST-1088 lane B (#3909, raised from 59,774 to measured, no headroom; owner ruling via GStack Minder 2026-11-03): CONTRACT_CHECKER_SHIPPED=true now that bin/gstack-skill-contract ships (#3912) renders the gstack-skill-contract check/waive sentence under the Done means list (renders/GSTA-256/plan-v3.md line 74, r2-d6 "Done-means list"), +240 bytes; measured 60,014. Before: 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; GST-1088 gate rev 2 (Garry, 2026-11-03, "do it") raised them for the Done-means list, and this lane's diet brings them back to 56,769; 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).
maxSkeletonBytes: 56_716, // GST-1088 lane C diet (R56 cut ledger modules/cuts.json: learnings search and capture, repo ownership, queue status; plus the blocker-proof rule, and the two dispatch markers): lowered from 60,014 to measured, no headroom; measured 56,716 with #3909 merged (2026-11-03). Before: GST-1088 lane B (#3909, raised from 59,774 to measured, no headroom; owner ruling via GStack Minder 2026-11-03): CONTRACT_CHECKER_SHIPPED=true now that bin/gstack-skill-contract ships (#3912) renders the gstack-skill-contract check/waive sentence under the Done means list (renders/GSTA-256/plan-v3.md line 74, r2-d6 "Done-means list"), +240 bytes; measured 60,014. Before: 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; GST-1088 gate rev 2 (Garry, 2026-11-03, "do it") raised them for the Done-means list, and this lane's diet brings them back to 56,716; 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.2984, // GST-1088 lane C diet (same cuts and additions as maxSkeletonBytes): lowered from 1.3283 to measured, rounded up; measured 140,902 / 108,523 = 1.29836 with #3909 merged (2026-11-03). Before: + GST-1088 lane B (#3909, raised from 1.3261 to measured, no headroom; owner ruling via GStack Minder 2026-11-03): the gstack-skill-contract check/waive sentence that CONTRACT_CHECKER_SHIPPED=true renders (renders/GSTA-256/plan-v3.md line 74, r2-d6 "Done-means list"), +240 bytes; measured 144,147 / 108,523 = 1.32826, rounded up. Before: + GST-1088 lane B (raised from 1.3180 to measured, no headroom): the contract: frontmatter block and the F1 "Done means" list; GST-1088 gate rev 2 (Garry, 2026-11-03, "do it") raised them for the Done-means list, and this lane's diet brings them back to 1.2984; 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.
maxSizeRatio: 1.2979, // GST-1088 lane C diet (same cuts and additions as maxSkeletonBytes): lowered from 1.3283 to measured, rounded up; measured 140,849 / 108,523 = 1.29788 with #3909 merged (2026-11-03). Before: + GST-1088 lane B (#3909, raised from 1.3261 to measured, no headroom; owner ruling via GStack Minder 2026-11-03): the gstack-skill-contract check/waive sentence that CONTRACT_CHECKER_SHIPPED=true renders (renders/GSTA-256/plan-v3.md line 74, r2-d6 "Done-means list"), +240 bytes; measured 144,147 / 108,523 = 1.32826, rounded up. Before: + GST-1088 lane B (raised from 1.3180 to measured, no headroom): the contract: frontmatter block and the F1 "Done means" list; GST-1088 gate rev 2 (Garry, 2026-11-03, "do it") raised them for the Done-means list, and this lane's diet brings them back to 1.2979; 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',
-2
View File
@@ -184,8 +184,6 @@ describe('contract-done-means renderer', () => {
['heading missing', one({ id: 'a', kind: 'report_labels', labels: ['Symptom'] }), /heading must be a non-empty string/],
['marker missing', one({ id: 'a', kind: 'selected_specialists_dispatched' }), /marker must be a non-empty string/],
['match missing', one({ id: 'a', kind: 'agent_dispatch' }), /match must be a non-empty string/],
['empty agent_dispatch marker', one({ id: 'a', kind: 'agent_dispatch', match: 'x', marker: ' ' }), /marker must be a non-empty string/],
['marker on a kind without one', one({ id: 'a', kind: 'report_labels', heading: 'R', labels: ['L'], marker: 'x' }), /unknown key "marker"/],
['field from another kind', one({ id: 'a', kind: 'check_after_last_edit', match: 'x' }), /unknown key "match"/],
];
for (const [name, value, want] of MALFORMED) {