mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-01 22:45:02 +01:00
v1.91.277.0 feat(preamble): office-hours stops copying the shared AskUserQuestion subsections (GSTA-96) (#3453)
* fix(preamble): completeness means complete coverage with minimal code (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(preamble): tier 2+ skills load the shared preamble once per session (GSTA-96) Every tier 2+ SKILL.md inlined the same ~23 KB preamble. It now renders once per host into sections/preamble.md beside each tier 2+ SKILL.md; the SKILL.md keeps a pointer that asks for the Read only when the file's id is not already in context. Only sections byte-identical to the shared render move, so every skill keeps exactly its pre-hoist paragraphs (verified across all 41 skills). Parity caps for ten skills raised to their measured unions (owner-approved, Garry 2026-10-25: "do it"); 63 size pins lowered and 26 deleted. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(paid-select): a generated sections/preamble.md takes its skill template's identity in the PR planner (GSTA-96) screen-summary failed on #3434 with 'PR profile requires full validation' for nine skills whose only paid coverage is periodic: their new sections/preamble.md had no dependency identity, while the SKILL.md beside it inherits its template's. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(section-manifest): the shared sections/preamble.md renders from the hoist resolver, not a section template (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (CHANGELOG says 3 characters shorter; model-overlays guards the coverage half) The completeness section shrank by 3 characters (5 bytes), matching the 89 pins lowered by 3. The default-copy test pinned only the minimal-code half; an expectMentions check now fails if the full-coverage or never-excuses-a-gap clause is dropped. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (model-overlays binds smaller diff to equal coverage) The default-copy check now also fails if 'on equal coverage' is dropped from the smaller-diff rule (which would tell models to prefer the smaller diff unconditionally) or if the no-impossible-state-checks clause is removed. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG lists the model-overlays coverage checks The [Unreleased] Tests bullet now says the default-copy test checks both halves of the rule (full coverage of the requirement; smaller diff only at equal coverage; no impossible-state checks; never excuses a gap), matching88e3cc0and44eacaf. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: plan-ceo-review and ETHOS say completeness means coverage, not more code Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (CEO bullet bounds code by accepted scope; tests bind each completeness rule) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: cover the shared-preamble hoist (ship Step 7) Adds test/preamble-hoist.test.ts (tier 1 unchanged; tier 2+ SKILL.md plus sections/preamble.md reproduces the pre-hoist preamble; pointer position and id) and two gen-skill-docs-checks cases (one shared preamble per host, beside every pointer and named by its id; preamble artifact kind). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (ship Step 9.4) - setup: follow a symlinked sections/ dir in the carved-pointer status check - touchfiles: select preamble-hoist.ts alongside every preamble.ts entry - section-manifest: exempt sections/preamble.md only for skills whose SKILL.md carries the shared-preamble pointer - skill-size-budget: growth ceiling measures SKILL.md plus its shared preamble - host-conformance: tolerate a missing sections/ dir, as setup does - skill-validation: read bin/gstack-skill-start directly - skill-fixture: error messages and docblocks name the actual boundary (harness-version non-behavioral note updated) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: describe the size-budget growth check's startup measure (ship Step 9.4, pass 2) The growth ceiling now measures SKILL.md plus the shared preamble, the same measure as the shrink floor; the docblock said SKILL.md only. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG says simplification.md quotes the old rule and CLAUDE.md restates the new one Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG lists the hoist tests and the growth-ceiling measure (ship Step 9.4) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: cover the symlinked sections/ status check and the ETHOS house-rule quote (ship Step 7) host-conformance: --status still finds a broken bare sections/ pointer when a carved Claude skill's installed sections/ is a directory symlink (fails at the pre-repair0e5d11fd9). gen-skill-docs: the house rule review/specialists/ simplification.md quotes from ETHOS.md still appears in ETHOS.md. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: describe the shared preamble file (ship Step 14.5 docs audit) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: the shared-preamble skip needs the skill's own Read; digest and first-run intro say complete coverage, minimal code (GSTA-96) The pointer skipped the shared file when any text carrying its id was in context, so a page or file with that heading could stand in for the anti-spoofing rules (security, red team and adversarial review, ship run 2). It now requires the skill's own earlier Read. The digest's Boil the Ocean bullet and the first-run intro still said 'AI makes completeness cheap, so do the complete thing' (docs audit blocker); both now carry Garry's 'complete coverage, minimal code' (2026-10-25: '1/ yes fix this'). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: pin the shared-preamble provenance skip and the new Boil the Ocean wording (ship Step 7) preamble-hoist: on every host, tier and render option the pointer allows the skip only when the skill's own Read is in context. agents-digest and gstack-skill-start: the digest bullet and the lake intro say complete coverage, minimal code, never the retired phrasing. Each fails at9af36d010. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: describe the provenance skip where the docs still described the id-only one (ship Step 9.4) The preamble-hoist.ts docblock and ARCHITECTURE.md still said the shared preamble is re-read only when its id is absent; since2acf1a44athe pointer requires the skill's own earlier Read of the file. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG lists the provenance-pointer and Boil the Ocean wording tests (ship Step 13) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * revert(digest): keep the agents digest at main's bytes; an unstamped head cannot change a release output (GSTA-96) CI 'Check VERSION is not stale vs queue' classified #3434 as stamped because it touched agents-digest/gstack-AGENTS.md (a release_outputs path), while regenerating only the generator fails the digest freshness test. The first-run intro rewording stays. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(preamble): office-hours' Q<N> form is its own subsection, so it stops copying the shared AskUserQuestion subsections (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: pin the added-subsection note and readSkillEffective's changed-plus-added order (ship Step 7, GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: variantSubsections and readSkillEffective docblocks name added subsections; office-hours cap history ends 'Before: 1.2279' (ship Step 9.4) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG says the Q<N> lead-in gained the Layout line text instead of claiming no wording changed (ship Step 11) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG gives office-hours' measured SKILL.md drop, 6,333 bytes (ship Step 13) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: CEO completeness posture check covers the accepted-scope bound Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: CHANGELOG says plan-ceo-review grows about 110 characters and each skill loses 3 from the section (cherry-picked from #3425's 25eec73; the digest/lake-intro line is dropped because #3434 rewords the intro and its own bullet names the digest deferral) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: voice judge reads the hoisted preamble; register ETHOS.md on its reader cases Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: refresh the claude ship golden the main merge took from main (ship Step 5 preflight) The1a4dcaf07merge resolved test/fixtures/golden/claude-ship-SKILL.md to main's pre-hoist bytes. The golden is a copy of ship/SKILL.md; regenerated with bun scripts/gen-ship-goldens.ts (pregate regen: REGEN_STALE). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: pr-dependencies' unplaceable-file example uses SECURITY.md now that ETHOS.md is mapped6183ca8aeregistered ETHOS.md on its reader cases in touchfiles-data.ts, so a PR touching only ETHOS.md plans in pr mode and no longer restores the full gate. The fail-closed test and the real-unknown label check now use SECURITY.md, which is tracked and unmapped; the assertions are unchanged. CHANGELOG names the third test file. (CI free shard 7 at 7c21720ac; remote pregate shard 6.) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: host path-leak checks read the moved shared preamble too; name SHARED_PREAMBLE_FILE (ship review pass 2) The external-host path-leak assertions in gen-skill-docs.test.ts (Codex, Factory) and host-config.test.ts (Copilot, Antigravity) read SKILL.md alone, so a Claude path that moved into sections/preamble.md with the hoist went unchecked. They now read the skill's startup text through readSkillStartup. workflow-judge-input and host-conformance use the SHARED_PREAMBLE_FILE constant instead of a bare 'preamble.md' literal, and preamble.ts's header describes the hoisted composition. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG token savings name the per-skill skip and the autoplan follow-up (ship review pass 2) The pointer's skip is bound to the skill's own copy of sections/preamble.md, so "every later one saves about 5,800" holds per skill, not per session, and /autoplan's skip list and the inline /office-hours loader do not yet skip the pointer section. The entry now says so instead of claiming the full /autoplan saving. Adding the skip entry grows size-pinned skills, so it is a follow-up. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: corpus budget measures startup bytes; carve-safety counts shared preamble as uncarved (ship review pass 2) skill-size-budget's growth ceiling and shrink floor already measured SKILL.md plus sections/preamble.md, but the total-corpus check still summed hoisted SKILL.md bytes against the pre-hoist baseline, hiding about 0.9 MB of headroom. It now sums the same startup bytes (3,114,885 -> 2,590,484, x0.832); the unreachable SKILL.md-missing fallback is gone. auq-format-always-loaded's carve-safety guard treated any sections/ dir as carved, so every skill with only the shared preamble ran it and its sanity check passed on preamble.md. It now uses setup --status's rule: a section other than the shared preamble. Those skills keep the per-skill checks. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: remaining SKILL.md-only negatives read the shared preamble too (ship review pass 3) The parameterized external-host path-leak smoke test, the two Codex .claude path checks and both vestigial-handshake negatives read SKILL.md alone, so text that moved into sections/preamble.md went unchecked. They now read readSkillStartup. preamble.ts's header no longer says tier 1 renders through generatePreamble(). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: ratchet skeleton caps and eager context ceilings down after the hoist (ship review pass 3) The hoist moved about 22 KB out of every tier 2+ SKILL.md, which left each hoisted maxSkeletonBytes cap and each eagerPerInvocation ceiling roughly 22 KB / 5-6K tokens slack. The 20 hoisted skeleton caps now sit at the post-hoist skeleton plus their pre-hoist headroom, and 42 context-budget ceilings take the recaptured (lower) values. Nothing was raised: the four ceilings the recapture would have raised keep their old values. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: preamble-hoist's section splitter closes a fence only on a bare delimiter (ship Step 11) Inside an open fence, a line such as "```bash" is content in CommonMark, but splitSections treated it as the closing fence, so a later "## " line in the same block could split a section mid-code-block and hoist half of it. A closing delimiter now needs nothing but whitespace after it. Every host's render is unchanged (gen-skill-docs --dry-run FRESH on all twelve hosts). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: skill-startup.ts is a global, pinned harness file like skill-fixture.ts (ship review pass 3) skill-fixture.ts is a global touchfile pinned in scripts/harness-version.json because it reshapes the skill content most E2E suites read, and it now builds that content through test/helpers/skill-startup.ts. That helper was in neither list, so a later edit to it would change fixture inputs without the harness-version guard and select only 27 keys. It is now global and pinned, recorded non-behavioral (its fixtures are byte-identical to the pre-hoist output). ARCHITECTURE.md and the CHANGELOG stop calling the pointer id a content id: it hashes the shared text before host rewrites. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG matches the merged skip lists and the baseline diff (ship review, run 4) The headline still called the autoplan and inline /office-hours skip entries a follow-up, but the main merge added them; it now says the loaded skills skip their shared Read. The #3451 sentence is dropped, because that fix shipped in v1.91.237.0 and is recorded there. The typecheck baseline line says the TS2769 entry was re-keyed for the 'preamble' artifact kind with its count unchanged, not fixed and lowered. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: pin the pointer entry in the loader skip lists; drop a carve-safety tautology (ship review, run 4) A new INVOKE_SKILL test checks that plan-ceo-review's inline /office-hours skip list and autoplan's section skip list both name the shared-preamble pointer heading, and that office-hours and the four plan reviews carry it. The carve-safety "sanity" assertion repeated the predicate discoverSkills() now uses to set sectionsDir, so it could never fail; it is removed. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: gstack-doctor finds the Codex model patch in the hoisted shared preamble (ship Step 11, run 4) The doctor's patch row grepped only the Codex review SKILL.md for the "Model-Specific Behavioral Patch (" heading. The hoist moves that section into sections/preamble.md, so the required row (for ship, review, the plan reviews, office-hours, investigate and more) would fail on every correct post-hoist Codex install and block install.sh's "gstack: ok". It now checks the SKILL.md and then its shared preamble and names the file it found. A new doctor test covers a hoisted render. preamble-hoist's variantSubsections also returns null when no subsection differs, instead of emitting a replacing heading with no subsections. Every host's render is unchanged. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(docsync): fixtureDocs installs the generated sections/preamble.md (hunk taken from #3472) A generated skill that loads the shared preamble names sections/preamble.md as a required asset before skill start, and a spawned document-release child blocks on its absence, so docsync-spawned stayed red on every #3434 screen. fixtureDocs now copies that file when the generated skill has it. This single hunk is taken unchanged from gstack#3472 (head2e3f7ba1, GSTA-107), which trains ahead of #3434, so the train merge sees identical lines. Nothing else from #3472 is included. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG notes the docsync fixture hunk taken from #3472 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: pin readSkillEffective's added-subsection change in harness-version.json as non-behavioral (CI quick-bun-default) skill-startup.ts changed in this PR so readSkillEffective appends a variant's added ### subsections. Its output is byte-identical to the pinned helper for every tier 2+ render without added subsections (735 across all hosts) and for office-hours' pre-change SKILL.md, so no pass-rate series resets. CHANGELOG Tests line updated. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG and the office-hours cap note give the post-merge measure (6,328 bytes; union 160,952 -> 154,624) (ship Step 11, #3453) The 6,333-byte drop and the 160,989 -> 154,653 union were measured at5db328f2b, before main was merged; against this PR's baseb727a6d3cthe numbers are 6,328 bytes and 160,952 -> 154,624. The cap stays 1.1796. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: round the post-merge office-hours ratio correctly (154,624 / 131,110 = 1.1793) (ship review pass 2, #3453) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG names the post-merge context-budget ratchet (captain hoisted) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): correct #3434's post-merge allowlist count and the two context-budget rows above main (GSTA-96, per GStack Minder) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): restate the corrections to #3434's post-merge bullets after its v1.91.265.0 merge (GSTA-96, per GStack Minder) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(auq): planted skill fixtures resolve the shared preamble like an install (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(plan-format): mode menu delivers its question without output-harvesting wording (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): record the v1.91.270.0 merge values (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): office-hours context-budget ceiling after the v1.91.270.0 merge (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(auq): parallel-capture contract expects the planted skill with its sections path mapped (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): correct the v1.91.270.0 recapture wording for hoisted rows (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): record the v1.91.273.0 merge re-measurements (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): restore the released entries the previous commit truncated (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): end two sentences the prose gate flags (GSTA-96) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
3e4240a76f
commit
6ff98c6616
13 files changed
+132
-95
No files matched your search
@@ -1,5 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
## [1.91.277.0] - 2026-10-27
|
||||
|
||||
**office-hours stops carrying its own copy of the shared AskUserQuestion subsections.** After the shared-preamble hoist, office-hours still inlined whole copies of the shared `### When AskUserQuestion is unavailable or a call fails` and `### Format` subsections, because each held one office-hours addition: the `Q<N>` open-question form. A session's first office-hours invocation read those subsections twice, once from the shared file and once from SKILL.md. The `Q<N>` form and its routing sentence now render as office-hours' own `### Open-ended questions` subsection at the end of the AskUserQuestion section, so the shared subsections apply as they are and office-hours' SKILL.md drops 6,328 bytes (its parity union goes from 160,952 to 154,624, under the pre-hoist 1.18 cap).
|
||||
|
||||
### Changed
|
||||
- **`scripts/resolvers/preamble/generate-ask-user-format.ts`**: office-hours' `Q<N>` form and "a free-text reply answers the most recent unanswered `Q<N>`" move into one `### Open-ended questions` subsection appended to the AskUserQuestion section. The form's template and Q-numbering text are unchanged; its lead-in now also carries what the Layout line used to say, "use this form instead of a `D<N>` brief" and the free-text routing rule. Other skills render byte-identically.
|
||||
- **`scripts/resolvers/preamble-hoist.ts`**: a skill variant may add subsections after the shared ones. It keeps only its changed and added subsections, and its note says "Only these subsections replace the shared ones or, under a new heading, add to its end". Variants without additions keep the existing note.
|
||||
- **plan-eng-review keeps its versions** of the shared Tool resolution, fallback, Format, Self-check, Voice, plan-mode invocation and Plan Status Footer text, because each differs on purpose (the Scope gate, D-numbering after the target menu, the em-dash rule for composed prose, the selected-report footer, its own self-check). Its cap is unchanged.
|
||||
|
||||
### Tests
|
||||
- `test/helpers/skill-startup.ts` applies added subsections when it builds the effective text, so the hoisted-equals-inline check covers office-hours' new layout. Its `scripts/harness-version.json` pin moves to the new blob, recorded non-behavioral at HARNESS_VERSION 6 with no pass-rate reset: `readSkillStartup`, `readSharedSections` and `readSkillEffective` return the same bytes as the pinned helper for every tier 2+ render without added subsections (735 across all hosts) and for office-hours' SKILL.md before this change, so office-hours' fixture input changes only because its prompt did.
|
||||
- `test/preamble-hoist.test.ts` checks, on every host and tier, that a cut-down variant carries the "add to its end" note exactly when it adds a subsection, and that `readSkillEffective` puts a changed subsection in place and appends added ones in order.
|
||||
- `test/helpers/carve-guards.ts`: office-hours' parity cap goes down from 1.2279 to 1.1796, its union as measured before the main merge (154,653 bytes; 154,624 now, 1.1793). After the v1.91.270.0 merge, `test/fixtures/context-budget.json` is recaptured and office-hours' ceiling goes down from 20,792 to 19,139. No cap or pin goes up.
|
||||
|
||||
### Gate integrity
|
||||
- Corrections to the shared-preamble entry below: its post-v1.91.260.0 bullet should say the size allowlist carries 31 pin raises from #3433 and #3481 (23 and 8), not 30, and that `alwaysOnTotal` sits above main's unchanged ceiling (7,458) by the measured growth, so "all stay below main's pre-hoist values" holds for the per-skill rows only (`gstack` is back at main's 5,326 since the v1.91.265.0 merge). Its post-v1.91.265.0 bullet's "measured 7,542" for `alwaysOnTotal` is the raw bill; the ratchet's bill, which excludes test fixtures and aliases, measures 7,398.5 against the 7,542 ceiling. Its post-v1.91.270.0 bullet's "every row that rises matches main's own recapture" holds for the non-hoisted rows only (`alwaysOnTotal`, `capy/skill/gstack`, `diagram`, `gstack`, `gstack-upgrade`); the hoisted rows that rose (`eval-plan` 4,887, `landing-report`, `plan-tune`, `retro`, `ship`) are that branch's own recapture with main's growth included, and main measures them unhoisted (`eval-plan` 10,581).
|
||||
|
||||
## [1.91.276.0] - 2026-10-27
|
||||
|
||||
**Tier 2+ skills load the shared preamble once per session instead of once per invocation.** Every tier 2+ SKILL.md carried the same ~23 KB of preamble (AskUserQuestion format, voice, writing style, completeness, completion protocol and more), so each skill invocation re-read text the conversation already held. That text now renders once per host into `sections/preamble.md` beside each tier 2+ SKILL.md. The SKILL.md keeps a short "Shared preamble (load first)" pointer naming the file and its id (a hash of the shared text before host rewrites), and asks for the Read unless the skill's own earlier Read of that file, headed by that id, is still in context, so other text that merely carries the id cannot stand in for the shared rules. Measured with Anthropic token counting: review's SKILL.md drops from 21,884 to 16,103 tokens, ship from 22,688 to 16,907, office-hours from 27,100 to 23,085 and design-review from 39,040 to 33,261. The shared file is 5,953 tokens, so a skill's first invocation in a session costs about 170 tokens more than before (about 1,900 for office-hours, about 3,800 for plan-eng-review) and every later invocation of that skill saves about 5,800 (4,000 for office-hours, 2,200 for plan-eng-review). The skip is tied to the skill's own copy of the file, so the first invocation of each different skill pays the Read again. A /autoplan run's four review-skill loads carry 17,941 fewer SKILL.md tokens (191,871 → 173,930), and /autoplan and the inline /office-hours loader skip each loaded skill's "Shared preamble (load first)" section, because the parent already holds the shared preamble.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# gstack digest v1.91.276.0 — regenerate/re-copy after upgrading gstack
|
||||
# gstack digest v1.91.277.0 — regenerate/re-copy after upgrading gstack
|
||||
|
||||
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
|
||||
for agent hosts without a full skill install. The full skills add workflows,
|
||||
|
||||
+3
-63
@@ -82,38 +82,11 @@ If `SKILL_PREFIX` is `true` use `/gstack-*` names; paths stay `~/.claude/skills/
|
||||
|
||||
## AskUserQuestion Format
|
||||
|
||||
Only these subsections replace the shared ones; the rest of this section is shared.
|
||||
Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.
|
||||
|
||||
### When AskUserQuestion is unavailable or a call fails
|
||||
### Open-ended questions
|
||||
|
||||
Tell these apart:
|
||||
|
||||
1. **Auto-decide denial (NOT a failure).** The result contains `[plan-tune auto-decide] <id> → <option>` — the preference hook as designed. Proceed with that option. Do NOT retry, do NOT fall back to prose.
|
||||
2. **Genuine failure** — no variant in your tool list, OR the variant is present but the call returns an error / missing result (MCP transport error, empty result, host bug such as Conductor's flaky MCP variant above).
|
||||
- If it was present and **errored** (not absent), retry the SAME call **once** — only if no answer could have surfaced (a missing-result error can arrive after the user saw the question; retrying would double-prompt, so if it may have reached them, treat it as pending and don't retry).
|
||||
- Then branch on `SESSION_KIND` (echoed by the preamble; empty/absent ⇒ `interactive`):
|
||||
- `spawned` → defer to the **Spawned session** block: auto-choose the recommended option. Never prose, never BLOCKED.
|
||||
- `unattended` → auto-choose the recommended option; a consent, destructive or irreversible option, unrecommended question or approval gate stays pending.
|
||||
- `headless` → `BLOCKED — AskUserQuestion unavailable`; stop and wait (no human can answer).
|
||||
- `interactive` → **prose fallback** (below).
|
||||
|
||||
**Prose fallback — render the decision brief as a markdown message, not a tool call.** Same information as the tool format below in paragraphs, not ✅/❌ bullets. It MUST surface this triad:
|
||||
|
||||
1. **A clear ELI10 of the issue itself** — plain English on what's being decided and why it matters (the question, not per-choice), naming the stakes. Lead with it.
|
||||
2. **Completeness scores per choice** — explicit on EACH choice, per the Completeness rule in the Format section below; never drop the score.
|
||||
3. **The recommendation and why** — the `Recommendation: <choice> because <reason>` line plus the `(recommended)` marker on that choice.
|
||||
|
||||
Layout: a `D<N>` title; an explicit reply line listing the offered selectors; the issue ELI10; the Recommendation line; ONE paragraph per choice with its `(recommended)` marker, `Completeness: X/10`, and 2-4 sentences of reasoning (never a bare bullet list); a closing `Net:` line. With `QUESTION_TUNING: true`, append the checked `<gstack-qid:{question_id}>` to the explicit reply line. Split chains / 5+ options: one prose block per per-option call, in order. Before an interactive prose question, finish the tool calls that do not depend on its answer; then send the complete brief as the turn's final message and STOP and wait for the typed answer. An open-ended question with no options list uses the `Q<N>` form below instead of a `D<N>` brief; a free-text reply answers the most recent unanswered `Q<N>`. Do not publish an earlier copy during tool work or follow it with tools or a waiting message. In plan mode this satisfies end-of-turn like a tool call.
|
||||
|
||||
**Continuation — mapping a typed reply back to a brief.** Each brief carries a stable label (`D<N>`, or `D<N>.k` in a split chain) the user references (e.g. "3.2: B"). A bare letter maps to the most-recent UNANSWERED brief; if more than one is open (a split chain), do NOT guess — ask which `D<N>.k` it answers. Never apply a bare letter ambiguously across a chain.
|
||||
|
||||
**One-way / destructive confirmations in prose.** A one-way door (irreversible or destructive: delete, force-push, drop, overwrite) makes prose a WEAKER gate than the tool, so strengthen it: require an explicit typed confirmation (the exact option letter or word), state plainly what is irreversible, and NEVER proceed on a vague, partial or ambiguous reply — re-ask. Silence or "ok"/"sure" without the explicit choice is not-yet-confirmed.
|
||||
|
||||
### Format
|
||||
|
||||
Every AskUserQuestion is a decision brief and must be sent as tool_use, not prose — unless the documented failure fallback above applies (interactive session + the call is unavailable/erroring), when the prose fallback is the correct output.
|
||||
|
||||
**Open-question prose form (`Q<N>`)** — for open-ended questions with no fixed option set (the Phase 2A/2B diagnostic questions) when you are in prose:
|
||||
**Open-question prose form (`Q<N>`)** — for open-ended questions with no fixed option set (the Phase 2A/2B diagnostic questions) when you are in prose, use this form instead of a `D<N>` brief; a free-text reply answers the most recent unanswered `Q<N>`:
|
||||
|
||||
```text
|
||||
Q<N> — <question, verbatim>
|
||||
@@ -124,39 +97,6 @@ Reply in your own words — I'll wait.
|
||||
|
||||
Q-numbering starts at `Q1` per invocation, independent of D-numbering. Questions with discrete options always use `D<N>`.
|
||||
|
||||
```
|
||||
D<N> — <one-line question title>
|
||||
Project/branch/task: <1 short grounding sentence using _BRANCH>
|
||||
ELI10: <plain English a 16-year-old could follow, 2-4 sentences, name the stakes>
|
||||
Stakes if we pick wrong: <one sentence on what breaks, what user sees, what's lost>
|
||||
Recommendation: <choice> because <one-line reason>
|
||||
Completeness: A=X/10, B=Y/10 (or: Note: options differ in kind, not coverage — no completeness score)
|
||||
Pros / cons:
|
||||
A) <option label> (recommended)
|
||||
✅ <pro — concrete, observable, ≥40 chars>
|
||||
❌ <con — honest, ≥40 chars>
|
||||
B) <option label>
|
||||
✅ <pro>
|
||||
❌ <con>
|
||||
Net: <one-line synthesis of what you're actually trading off>
|
||||
```
|
||||
|
||||
D-numbering: first question in a skill invocation is `D1`; increment yourself. This is a model-level instruction, not a runtime counter.
|
||||
|
||||
ELI10 is always present, in plain English, not function names; Recommendation is ALWAYS present. Keep the `(recommended)` label; AUTO_DECIDE depends on it.
|
||||
|
||||
Completeness: use `Completeness: N/10` only when options differ in coverage. 10 = complete, 7 = happy path, 3 = shortcut. If options differ in kind, write: `Note: options differ in kind, not coverage — no completeness score.`
|
||||
|
||||
Accepted shortcuts leave a trail: when the user selects an option that is BOTH Completeness ≤ 7 AND a durable-scope call (architecture or scope-cut, never a turn-level choice), log it via `gstack-decision-log` with the ceiling and the upgrade trigger in the rationale, and, while implementing that option (same edit, no follow-up question), mark each cut corner in code with `gstack-shortcut(dec-<id>): <ceiling>, upgrade when <trigger>` in the language's comment syntax. Never agent-initiated: the marker exists only downstream of the user's explicit choice. /retro harvests these into a debt ledger, joined on the decision id.
|
||||
|
||||
`Pros / cons:` in question text; descriptions use literal ✅/❌ bullets, not Pro:/Con:. Each real option: ≥2 pros and ≥1 con, ≥40 chars each. One-way/destructive escape: `✅ No cons — this is a hard-stop choice`.
|
||||
|
||||
Neutral posture: `Recommendation: <default> — this is a taste call, no strong preference either way`; `(recommended)` STAYS on the default option for AUTO_DECIDE.
|
||||
|
||||
Effort both-scales: when an option involves effort, label both human-team and CC+gstack time, e.g. `(human: ~2 days / CC: ~15 min)`, so AI compression is visible at decision time.
|
||||
|
||||
`Net:` line closes question text. Per-skill instructions may add stricter rules.
|
||||
|
||||
## Question Tuning (skip entirely if `QUESTION_TUNING: false`)
|
||||
|
||||
Before each decision brief (AskUserQuestion or Conductor/fallback prose), choose `question_id` from `~/.claude/skills/gstack/scripts/question-registry.ts` or `{skill}-{slug}`, then run `~/.claude/skills/gstack/bin/gstack-question-preference --check "<id>"`; for an unregistered id, write the question summary to `.gstack/tmp/qt.txt` (file-write tool) and append `--summary-file .gstack/tmp/qt.txt` (one-way keyword check). `AUTO_DECIDE` means choose the recommended option and say "Auto-decided [summary] → [option] (your preference). Change with /plan-tune." `ASK_NORMALLY` means ask.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gstack",
|
||||
"version": "1.91.276",
|
||||
"version": "1.91.277",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"test/helpers/session-ledger.ts": {"blob":"a10bf28bf100f135b93c73818670dd147253cdd3","since":6,"nonBehavioral":"credential rejections are classed infra instead of assertion: no case's pass or fail changes; only failed trials whose provider rejected the run's key move class, so no rerun spends on a rejected key"},
|
||||
"test/helpers/session-runner.ts": {"blob":"cc65a66adafa9c053e3da3c6f17b5ac86c0eade7","since":6,"nonBehavioral":"Claude-host verdicts are unchanged: the Codex host (EVALS_HOST=codex, codex-host-runner) runs only when selected and records its own per-model series; the Codex runner refactor (execCodex extraction, onSpawn hook) keeps its events and verdicts and adds token cost accounting; host fields on the plan/report are absent for claude"},
|
||||
"test/helpers/skill-fixture.ts": {"blob":"8a654dae6b0a396d839b02179119ae3e3bb13f69","since":6,"nonBehavioral":"extractSkillBody and extractSkillSections follow the shared-preamble hoist (pointer plus sections/preamble.md); every fixture they build is byte-identical to the pre-hoist output (compared for 8 body and 4 section extractions), so no case's input or verdict changes; the /ship review then reworded only comments and two error messages, so no fixture byte changes"},
|
||||
"test/helpers/skill-startup.ts": {"blob":"f2096462850a33e0913cad4cca2cf3c75eedb0e3","since":6,"nonBehavioral":"new in the shared-preamble hoist: skill-fixture.ts's extraction splices sections/preamble.md through readSharedSections and readSkillEffective, and every fixture it builds is byte-identical to the pre-hoist output (skill-fixture.ts's entry), so pinning it changes no case's input or verdict"},
|
||||
"test/helpers/skill-startup.ts": {"blob":"a8d21e3817c3f35a1d06af325c92fde7d420e377","since":6,"nonBehavioral":"readSkillEffective also appends a variant's added ### subsections (office-hours' Open-ended questions); readSkillStartup, readSharedSections and readSkillEffective return bytes identical to the pinned helper for every tier 2+ render without added subsections (735 across all hosts) and for office-hours' SKILL.md before this PR, so office-hours' fixture input changes only because its prompt changed in this PR, not because the helper did"},
|
||||
"test/helpers/test-selection.ts": {"blob":"51655f89f66be7a5ec487e2abfe4744300ebd5f6","since":6},
|
||||
"test/helpers/touchfiles.ts": {"blob":"15f369056d41b9dc42dcd86f81f60c878074704c","since":6}
|
||||
}
|
||||
|
||||
@@ -64,17 +64,22 @@ const headingOf = (section: string) => section.split('\n', 1)[0]!;
|
||||
|
||||
/**
|
||||
* A skill-specific variant of a shared section, cut down to the `### `
|
||||
* subsections that differ, when the two share their heading, intro and
|
||||
* subsection headings. Otherwise the whole section stays inline.
|
||||
* subsections that differ plus any it adds after the shared ones, when it
|
||||
* keeps the shared heading, intro and subsection headings in order.
|
||||
* Otherwise the whole section stays inline.
|
||||
*/
|
||||
function variantSubsections(own: string, shared: string): string | null {
|
||||
const ownParts = splitSections(own, '### ');
|
||||
const sharedParts = splitSections(shared, '### ');
|
||||
if (ownParts.length < 2 || ownParts.length !== sharedParts.length || ownParts[0] !== sharedParts[0]) return null;
|
||||
if (ownParts.some((part, i) => headingOf(part) !== headingOf(sharedParts[i]!))) return null;
|
||||
const changed = ownParts.slice(1).filter((part, i) => part !== sharedParts[i + 1]);
|
||||
if (changed.length === 0) return null;
|
||||
return [`${headingOf(own)}\n\nOnly these subsections replace the shared ones; the rest of this section is shared.`, ...changed].join('\n\n');
|
||||
if (ownParts.length < 2 || ownParts.length < sharedParts.length || ownParts[0] !== sharedParts[0]) return null;
|
||||
if (sharedParts.some((part, i) => headingOf(part) !== headingOf(ownParts[i]!))) return null;
|
||||
const changed = ownParts.slice(1, sharedParts.length).filter((part, i) => part !== sharedParts[i + 1]);
|
||||
const added = ownParts.slice(sharedParts.length);
|
||||
if (changed.length === 0 && added.length === 0) return null;
|
||||
const note = added.length
|
||||
? 'Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.'
|
||||
: 'Only these subsections replace the shared ones; the rest of this section is shared.';
|
||||
return [`${headingOf(own)}\n\n${note}`, ...changed, ...added].join('\n\n');
|
||||
}
|
||||
|
||||
/** Where the installed SKILL.md finds its copy of the shared file. */
|
||||
|
||||
@@ -36,7 +36,7 @@ Tell these apart:
|
||||
2. **Completeness scores per choice** — explicit on EACH choice, per the Completeness rule in the Format section below; never drop the score.
|
||||
3. **The recommendation and why** — the \`Recommendation: <choice> because <reason>\` line plus the \`(recommended)\` marker on that choice.
|
||||
|
||||
Layout: a \`D<N>\` title; an explicit reply line listing the offered selectors; the issue ELI10; the Recommendation line; ONE paragraph per choice with its \`(recommended)\` marker, \`Completeness: X/10\`, and 2-4 sentences of reasoning (never a bare bullet list); a closing \`Net:\` line. With \`QUESTION_TUNING: true\`, append the checked \`<gstack-qid:{question_id}>\` to the explicit reply line. Split chains / 5+ options: one prose block per per-option call, in order. Before an interactive prose question, finish the tool calls that do not depend on its answer; then send the complete brief as the turn's final message and STOP and wait for the typed answer.${openQuestions ? ' An open-ended question with no options list uses the `Q<N>` form below instead of a `D<N>` brief; a free-text reply answers the most recent unanswered `Q<N>`.' : ''} Do not publish an earlier copy during tool work or follow it with tools or a waiting message. In plan mode this satisfies end-of-turn like a tool call.
|
||||
Layout: a \`D<N>\` title; an explicit reply line listing the offered selectors; the issue ELI10; the Recommendation line; ONE paragraph per choice with its \`(recommended)\` marker, \`Completeness: X/10\`, and 2-4 sentences of reasoning (never a bare bullet list); a closing \`Net:\` line. With \`QUESTION_TUNING: true\`, append the checked \`<gstack-qid:{question_id}>\` to the explicit reply line. Split chains / 5+ options: one prose block per per-option call, in order. Before an interactive prose question, finish the tool calls that do not depend on its answer; then send the complete brief as the turn's final message and STOP and wait for the typed answer. Do not publish an earlier copy during tool work or follow it with tools or a waiting message. In plan mode this satisfies end-of-turn like a tool call.
|
||||
|
||||
**Continuation — mapping a typed reply back to a brief.** Each brief carries a stable label (\`D<N>\`, or \`D<N>.k\` in a split chain) the user references (e.g. "3.2: B"). A bare letter maps to the most-recent UNANSWERED brief; if more than one is open (a split chain), do NOT guess — ask which \`D<N>.k\` it answers. Never apply a bare letter ambiguously across a chain.
|
||||
|
||||
@@ -45,18 +45,7 @@ Layout: a \`D<N>\` title; an explicit reply line listing the offered selectors;
|
||||
### Format
|
||||
|
||||
Every AskUserQuestion is a decision brief and must be sent as tool_use, not prose — unless the documented failure fallback above applies (interactive session + the call is unavailable/erroring), when the prose fallback is the correct output.
|
||||
${openQuestions ? `
|
||||
**Open-question prose form (\`Q<N>\`)** — for open-ended questions with no fixed option set (the Phase 2A/2B diagnostic questions) when you are in prose:
|
||||
|
||||
\`\`\`text
|
||||
Q<N> — <question, verbatim>
|
||||
Why I'm asking: <1-2 sentences: stakes, what a weak answer would mean>
|
||||
What a strong answer sounds like: <the section's "push until you hear" line>
|
||||
Reply in your own words — I'll wait.
|
||||
\`\`\`
|
||||
|
||||
Q-numbering starts at \`Q1\` per invocation, independent of D-numbering. Questions with discrete options always use \`D<N>\`.
|
||||
` : ''}
|
||||
\`\`\`
|
||||
D<N> — <one-line question title>
|
||||
Project/branch/task: <1 short grounding sentence using _BRANCH>
|
||||
@@ -137,5 +126,18 @@ ${planReview ? `Before emitting a tool or prose decision brief, verify:
|
||||
- [ ] If you had 5+ options, you split (or batched into ≤4-groups) — did NOT drop any
|
||||
- [ ] If you split, you checked dependencies between options before firing the chain
|
||||
- [ ] If a per-option Hold fires, you stopped the chain immediately (didn't queue)`}
|
||||
`;
|
||||
${openQuestions ? `
|
||||
### Open-ended questions
|
||||
|
||||
**Open-question prose form (\`Q<N>\`)** — for open-ended questions with no fixed option set (the Phase 2A/2B diagnostic questions) when you are in prose, use this form instead of a \`D<N>\` brief; a free-text reply answers the most recent unanswered \`Q<N>\`:
|
||||
|
||||
\`\`\`text
|
||||
Q<N> — <question, verbatim>
|
||||
Why I'm asking: <1-2 sentences: stakes, what a weak answer would mean>
|
||||
What a strong answer sounds like: <the section's "push until you hear" line>
|
||||
Reply in your own words — I'll wait.
|
||||
\`\`\`
|
||||
|
||||
Q-numbering starts at \`Q1\` per invocation, independent of D-numbering. Questions with discrete options always use \`D<N>\`.
|
||||
` : ''}`;
|
||||
}
|
||||
@@ -95,9 +95,12 @@ for (const source of sources) describe(`CEO clarity routing — ${source.label}`
|
||||
});
|
||||
|
||||
describe('CEO completeness posture', () => {
|
||||
// Value: protects=CEO Philosophy bullet bounds extra code by the accepted scope, so SCOPE/SELECTIVE EXPANSION still build accepted items;
|
||||
// fails_when=the bullet says "no code beyond the requirement" again, contradicting EXPANSION; why_new=the rule group lacked 'accepted scope';
|
||||
// seam=none; no_claim=model behavior in a real CEO review (keyword check on the template only)
|
||||
test('the Philosophy bullet measures completeness in coverage, not code', () => {
|
||||
const philosophy = readFileSync(resolve(root, 'plan-ceo-review/SKILL.md.tmpl'), 'utf8').split('## Philosophy')[1]!.split('## Prime Directives')[0]!;
|
||||
expectMentions(philosophy, [['whole requirement', '90% shortcut', 'no code beyond', 'equal coverage', 'smaller diff']], 'philosophy');
|
||||
expectMentions(philosophy, [['whole requirement', '90% shortcut', 'no code beyond', 'accepted scope', 'equal coverage', 'smaller diff']], 'philosophy');
|
||||
expectAbsent(philosophy, ['COMPLETENESS IS CHEAP', '~150 LOC'], 'philosophy');
|
||||
});
|
||||
});
|
||||
Vendored
+1
-1
@@ -41,7 +41,7 @@
|
||||
"landing-report": 3269,
|
||||
"learn": 2931,
|
||||
"make-pdf": 5204,
|
||||
"office-hours": 20792,
|
||||
"office-hours": 19139,
|
||||
"open-gstack-browser": 4872,
|
||||
"openclaw/skills/gstack-openclaw-ceo-review": 2764,
|
||||
"openclaw/skills/gstack-openclaw-investigate": 1429,
|
||||
|
||||
@@ -223,7 +223,7 @@ export const CARVE_GUARDS: Record<string, CarveGuard> = {
|
||||
// 1.08 → 1.10: the scope-gate exceptions block (+ its adversarial-review
|
||||
// hardening: host-anchored mode signal, precedence, passing-mention
|
||||
// guards) and the plan-mode preamble reword land the union at 1.092.
|
||||
maxSizeRatio: 1.3532, // GSTA-96 hoist merged with v1.91.273.0 (#3500, 2026-10-27): the union now includes the shared sections/preamble.md (the hoist's owner-approved raise, Garry 2026-10-25 "do it") on top of main's 1.2400; measured 168,601 / 124,597 = 1.3532 on the merged tree. Before: pstack item 9 (Garry 2026-10-26 "do it"): the design-twice pass (two shapes, eight red flags, ~2.4 KB) joins Section 1 of the lazy review section; measured 154,366 / 124,597 = 1.2389 (2026-10-26). Earlier history: + clarity rules for saved decisions/setup gates + the Aside probe's failure reason; measured 1.1504. + test value bar and Tests to Retire in the lazy Test review section (~2.6KB); measured 1.168 + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.173 (2026-09-30). + v1.91.12.0 merge of #2999 (headless rule: a disallowed question tool never qualifies) with #3002; measured 1.1741 (2026-10-01). + 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 146,905 / 124,597 = 1.1790 (2026-10-06). Combined Oct 6 wave head (A and B): measured 147,524 / 124,597 = 1.1840 (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 147,693 / 124,597 = 1.1854 (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 147,752 / 124,597 = 1.1858 (2026-10-07). + fix wave PR D: the prerequisite offer is skipped in spawned/headless sessions (#1958); measured 147,864 / 124,597 = 1.1867 (2026-10-07). + Oct 10 multi-agent wave PR D1 (reality rows): the Scope Challenge reality-rows block, the deep incident table (18 rows, each tied to the incident that earned it), the Completion summary line and the checklist row; measured 151,423 / 124,597 = 1.2153 (2026-10-10). + #280: the Design Doc Check finds the CEO plan (`gstack-ceo-plan-find`) and names it in the data rule, +325 bytes on main's 151,630; measured 151,955 / 124,597 = 1.2196 (2026-10-12). Fix wave PR 4 (owner-approved raise 1.2170 → 1.2196): #3182's timer-bounded native fallback (net −17 bytes) plus #280's cross-branch rule, offset by tightening the Design Doc Check; measured 151,950 / 124,597 = 1.2195 (2026-10-15).
|
||||
maxSizeRatio: 1.3532, // GSTA-96 hoist merged with v1.91.273.0 (#3500, 2026-10-27): the union now includes the shared sections/preamble.md (the hoist's owner-approved raise, Garry 2026-10-25 "do it") on top of main's 1.2400; measured 168,601 / 124,597 = 1.3532 on the merged tree. Its own versions of shared sections stay after the GSTA-96 dedupe (2026-10-26) because each differs on purpose: the Scope gate in Tool resolution, D-numbering after the target menu, the em-dash rule for composed prose, the selected-report footer, its own self-check and plan-mode invocation. Before: pstack item 9 (Garry 2026-10-26 "do it"): the design-twice pass (two shapes, eight red flags, ~2.4 KB) joins Section 1 of the lazy review section; measured 154,366 / 124,597 = 1.2389 (2026-10-26). Earlier history: + clarity rules for saved decisions/setup gates + the Aside probe's failure reason; measured 1.1504. + test value bar and Tests to Retire in the lazy Test review section (~2.6KB); measured 1.168 + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.173 (2026-09-30). + v1.91.12.0 merge of #2999 (headless rule: a disallowed question tool never qualifies) with #3002; measured 1.1741 (2026-10-01). + 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 146,905 / 124,597 = 1.1790 (2026-10-06). Combined Oct 6 wave head (A and B): measured 147,524 / 124,597 = 1.1840 (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 147,693 / 124,597 = 1.1854 (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 147,752 / 124,597 = 1.1858 (2026-10-07). + fix wave PR D: the prerequisite offer is skipped in spawned/headless sessions (#1958); measured 147,864 / 124,597 = 1.1867 (2026-10-07). + Oct 10 multi-agent wave PR D1 (reality rows): the Scope Challenge reality-rows block, the deep incident table (18 rows, each tied to the incident that earned it), the Completion summary line and the checklist row; measured 151,423 / 124,597 = 1.2153 (2026-10-10). + #280: the Design Doc Check finds the CEO plan (`gstack-ceo-plan-find`) and names it in the data rule, +325 bytes on main's 151,630; measured 151,955 / 124,597 = 1.2196 (2026-10-12). Fix wave PR 4 (owner-approved raise 1.2170 → 1.2196): #3182's timer-bounded native fallback (net −17 bytes) plus #280's cross-branch rule, offset by tightening the Design Doc Check; measured 151,950 / 124,597 = 1.2195 (2026-10-15).
|
||||
},
|
||||
'plan-design-review': {
|
||||
skill: 'plan-design-review',
|
||||
@@ -353,7 +353,7 @@ do not launch the downstream skill or open a browser.`,
|
||||
maxSkeletonBytes: 80_533, // GSTA-96 hoist (2026-10-26): lowered from 95_900 by the shared preamble that moved to sections/preamble.md, keeping the pre-hoist headroom of 952 bytes; measured 79,581. Before: Office-hours + sketch outside voices include host guards and completion checks. + v1.91.19.0 wave: Q<N> open-question prose form (#2729, approved Q3), DESIGN_READY --version probe (B4), Aside ~/.local/bin fallback (E7), verdict-form outside voice (INV-1), plus v1.91.17.0's round accounting (#3022) merged in; measured 89,684 (2026-10-03). + 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); skeleton measured 92,590 (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; skeleton measured 92,869 (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; skeleton measured 92,930 (2026-10-07). + Oct 7 wave guard (CEO-20): conditional `run_in_background` request plus the background-run recovery at each foreground dispatch site; skeleton measured 92,756 (2026-10-07). Merged with the guard area: measured skeleton 93,099 (2026-10-07). + fix wave PR D: the Phase 4.5 builder-profile read before the session log (#2801) and the pre-telemetry design-doc check (#1049); measured skeleton 94,877 (2026-10-07). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10).
|
||||
minUnionBytes: 115_800, // Phase 4 wave 4; measured union 118,175
|
||||
mustContain: ['design doc', 'problem statement'],
|
||||
maxSizeRatio: 1.2279, // + 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 149,758 / 131,110 = 1.1422 (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 150,037 / 131,110 = 1.1444 (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 150,098 / 131,110 = 1.1448 (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 150,011 / 131,110 = 1.1442 (2026-10-07). Merged with the guard area: measured 150,354 / 131,110 = 1.1468 (2026-10-07). + fix wave PR D: builder-profile read before the session log with Phase 6 using the carried values (#2801), the inline design doc before approval (#879) and the pre-telemetry design-doc check (#1049); measured 153,206 / 131,110 = 1.1685 (2026-10-07). + empty LAST_ASSIGNMENT/LAST_PROJECT guards in the welcome_back and regular greetings (backfilled profiles); measured 153,469 / 131,110 = 1.1705 (2026-10-08). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10). + GSTA-96 shared-preamble hoist (owner-approved raise, Garry 2026-10-25: "do it"): the union now holds sections/preamble.md, so a fresh invocation reads the pointer plus the shared file and its own copies of shared sections twice until their dedupe; measured 160,989 / 131,110 = 1.2279 (2026-10-25). Before: 1.18.
|
||||
maxSizeRatio: 1.1796, // + 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 149,758 / 131,110 = 1.1422 (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 150,037 / 131,110 = 1.1444 (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 150,098 / 131,110 = 1.1448 (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 150,011 / 131,110 = 1.1442 (2026-10-07). Merged with the guard area: measured 150,354 / 131,110 = 1.1468 (2026-10-07). + fix wave PR D: builder-profile read before the session log with Phase 6 using the carried values (#2801), the inline design doc before approval (#879) and the pre-telemetry design-doc check (#1049); measured 153,206 / 131,110 = 1.1685 (2026-10-07). + empty LAST_ASSIGNMENT/LAST_PROJECT guards in the welcome_back and regular greetings (backfilled profiles); measured 153,469 / 131,110 = 1.1705 (2026-10-08). + Honest Work wave merged (W2/W3 gate integrity in review/ship, W4 kernel + DONE line in every tier-2+ skill, W5/W7 in spec/ship): re-measured on the integrated tree (2026-10-10). + GSTA-96 shared-preamble hoist (owner-approved raise, Garry 2026-10-25: "do it"): the union now holds sections/preamble.md, so a fresh invocation reads the pointer plus the shared file and its own copies of shared sections; measured 160,989 / 131,110 = 1.2279 (2026-10-25). Before: 1.18. - GSTA-96 dedupe: the Q<N> open-question form is office-hours' own appended subsection, so its copies of the shared Format and fallback subsections go; measured 154,653 / 131,110 = 1.1796 (2026-10-26), 154,624 = 1.1793 after the main merge. Before: 1.2279.
|
||||
},
|
||||
'document-release': {
|
||||
skill: 'document-release',
|
||||
|
||||
@@ -23,7 +23,10 @@ export function readSkillStartup(skillMd: string): string {
|
||||
return `${skill.slice(0, at)}${shared}\n${skill.slice(at)}`;
|
||||
}
|
||||
|
||||
const VARIANT_NOTE = 'Only these subsections replace the shared ones; the rest of this section is shared.';
|
||||
const VARIANT_NOTES = [
|
||||
'Only these subsections replace the shared ones; the rest of this section is shared.',
|
||||
'Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.',
|
||||
];
|
||||
|
||||
/** Split markdown at one heading level outside code fences; parts are trimmed. */
|
||||
function splitAt(text: string, marker: string): string[] {
|
||||
@@ -59,7 +62,7 @@ export function readSharedSections(skillMd: string): string[] {
|
||||
* The text a tier 2+ skill effectively follows, laid out as one file: the
|
||||
* pointer section is replaced by the shared sections, each with this skill's
|
||||
* same-heading section (or, for a cut-down variant, its replacement
|
||||
* subsections) applied, and those overriding sections leave their own place.
|
||||
* and added subsections) applied, and those overriding sections leave their own place.
|
||||
* Every preamble section then appears once with the text the skill used to
|
||||
* inline; only the grouping differs. SKILL.md without a pointer is returned as is.
|
||||
*/
|
||||
@@ -80,9 +83,12 @@ export function readSkillEffective(skillMd: string): string {
|
||||
const effective = shared.map(section => {
|
||||
const override = overrides.get(headingOf(section));
|
||||
if (!override) return section;
|
||||
if (!override.includes(VARIANT_NOTE)) return override;
|
||||
if (!VARIANT_NOTES.some(note => override.includes(note))) return override;
|
||||
const replacements = new Map(splitAt(override, '### ').slice(1).map(part => [headingOf(part), part]));
|
||||
return splitAt(section, '### ').map(part => replacements.get(headingOf(part)) ?? part).join('\n\n');
|
||||
const parts = splitAt(section, '### ');
|
||||
const sharedHeads = new Set(parts.map(headingOf));
|
||||
const added = [...replacements.values()].filter(part => !sharedHeads.has(headingOf(part)));
|
||||
return [...parts.map(part => replacements.get(headingOf(part)) ?? part), ...added].join('\n\n');
|
||||
});
|
||||
return `${skill.slice(0, pointer + 1)}${effective.join('\n\n')}\n\n${kept.join('')}`;
|
||||
}
|
||||
|
||||
@@ -112,4 +112,68 @@ describe('shared-preamble hoist', () => {
|
||||
expectAbsent(pointer, ['whose first heading names'], label(ctx));
|
||||
}
|
||||
});
|
||||
|
||||
// Generated by /ship Step 7 coverage audit (GSTA-96, PR #3453).
|
||||
// Value: protects=a cut-down preamble variant that appends a subsection says so in its note, and a replace-only variant keeps the replace-only note;
|
||||
// fails_when=the note collapses to "Only these subsections replace the shared ones" while office-hours appends ### Open-ended questions;
|
||||
// why_new=the hoisted-equals-inline case passes for either note, since readSkillEffective accepts both; goldens cover ship only;
|
||||
// seam=none; no_claim=that a model treats an added subsection differently (resolver output only)
|
||||
test('a cut-down variant names an added subsection in its note, and only then', () => {
|
||||
const REPLACE = 'Only these subsections replace the shared ones; the rest of this section is shared.';
|
||||
const ADD = 'Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.';
|
||||
const subheadings = (section: string) => {
|
||||
const heads: string[] = [];
|
||||
let fence: string | null = null;
|
||||
for (const line of section.split('\n')) {
|
||||
const delimiter = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
|
||||
if (delimiter) {
|
||||
if (!fence) fence = delimiter;
|
||||
else if (delimiter[0] === fence[0] && delimiter.length >= fence.length) fence = null;
|
||||
} else if (!fence && line.startsWith('### ')) heads.push(line);
|
||||
}
|
||||
return heads;
|
||||
};
|
||||
let adding = 0;
|
||||
for (const ctx of contexts()) {
|
||||
if (!hoistsPreamble(ctx)) continue;
|
||||
const shared = new Map(sections(generateSharedPreambleFile(ctx)).map(section => [section.split('\n', 1)[0]!, section]));
|
||||
for (const section of sections(generateSkillPreamble(ctx))) {
|
||||
const note = section.includes(ADD) ? ADD : section.includes(REPLACE) ? REPLACE : null;
|
||||
if (!note) continue;
|
||||
const base = shared.get(section.split('\n', 1)[0]!);
|
||||
expect(base, label(ctx)).toBeDefined();
|
||||
const sharedHeads = new Set(subheadings(base!));
|
||||
const adds = subheadings(section).some(heading => !sharedHeads.has(heading));
|
||||
expect(note, label(ctx)).toBe(adds ? ADD : REPLACE);
|
||||
if (adds) adding++;
|
||||
}
|
||||
}
|
||||
expect(adding).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
// Generated by /ship Step 7 coverage audit (GSTA-96, PR #3453).
|
||||
// Value: protects=readSkillEffective rebuilds a variant section as shared A, changed B, then added C and D, in that order, and keeps the skill's own sections after it;
|
||||
// fails_when=added subsections are dropped, put before the shared ones, reordered, or a changed subsection leaves the original in place;
|
||||
// why_new=the generated-skill case has only office-hours' single added subsection and no variant that both changes and adds;
|
||||
// seam=none; no_claim=that the generator emits this shape (the resolver cases above cover real renders)
|
||||
test('readSkillEffective applies a changed subsection and appends added ones in order', () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-preamble-effective-'));
|
||||
try {
|
||||
const note = 'Only these subsections replace the shared ones or, under a new heading, add to its end; the rest of this section is shared.';
|
||||
fs.mkdirSync(path.join(root, 'sections'));
|
||||
fs.writeFileSync(path.join(root, 'sections', SHARED_PREAMBLE_FILE),
|
||||
'# gstack shared preamble 0123456789ab\n\n## Rules\n\nShared intro.\n\n### A\n\nShared A.\n\n### B\n\nShared B.\n');
|
||||
fs.writeFileSync(path.join(root, 'SKILL.md'), [
|
||||
'# fixture', '', SHARED_POINTER_HEADING, '', 'Pointer text.', '',
|
||||
'## Rules', '', note, '', '### B', '', 'Changed B.', '', '### C', '', 'Added C.', '', '### D', '', 'Added D.', '',
|
||||
'## Own', '', 'Own body.', '',
|
||||
].join('\n'));
|
||||
expect(readSkillEffective(path.join(root, 'SKILL.md'))).toBe([
|
||||
'# fixture', '', '## Rules', '', 'Shared intro.', '', '### A', '', 'Shared A.', '', '### B', '', 'Changed B.', '',
|
||||
'### C', '', 'Added C.', '', '### D', '', 'Added D.', '', '## Own', '', 'Own body.', '',
|
||||
].join('\n'));
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user