Merge remote-tracking branch 'origin/main' into garrytan/trunk-land-skill

# Conflicts:
#	CHANGELOG.md
#	VERSION
#	package.json
This commit is contained in:
Garry Tan
2026-06-09 11:20:15 -07:00
123 changed files with 4624 additions and 319 deletions
+247
View File
@@ -44,6 +44,253 @@ If you only want to merge, run `/land` and stop. Got ten PRs green and ready? Ru
#### For contributors
- `lib/merge.ts` holds the pure regime logic (detection precedence, submit planning, landing classification, handoff schema + validation); `test/gstack-merge.test.ts` (30) and `test/gstack-merge-cli.test.ts` (11) pin it. A generated-doc scrub test fails CI if `/land`'s SKILL.md ever grows deploy/canary machinery. The merge SHA → revert handoff and the never-blind-retry invariant (cli/cli#3442, cli/cli#13380) moved into `/land` with their tests.
## [1.57.7.0] - 2026-06-08
## **Every plan review now ends by telling you, in one line, whether anything is still unresolved.**
## **The GSTACK REVIEW REPORT closes with the open decisions, or "NO UNRESOLVED DECISIONS" in plain sight, before you approve.**
When a plan-review skill (/plan-ceo-review, /plan-eng-review, /plan-design-review,
/plan-devex-review, and /codex) finishes and hands you the plan to approve, its report
now ends with a mandatory unresolved-decisions verdict. If decisions are still open, it
lists each one and what breaks if you ship it deferred. If nothing is open, it prints the
exact line NO UNRESOLVED DECISIONS. A token-reduction pass had made this line optional, so
a clean plan and a plan hiding an open question rendered the same. Now the line is never
omitted, it is always the last thing you read before the approval prompt, and the approval
gate refuses to let the plan through without it.
### What changed, before and after
| At plan-approval time | Before | After |
|---|---|---|
| Clean plan | usually no unresolved line | `NO UNRESOLVED DECISIONS` as the final line |
| Plan with open decisions | unresolved line optional, often dropped | `**UNRESOLVED DECISIONS:**` + one bullet per open item |
| Approval gate (ExitPlanMode) | checked the line "if applicable" | blocks unless the unresolved status is the final line |
| /plan-devex-review review log | never written, gate uncheckable | written, so the dashboard and report see its data |
The unresolved count across reviews is computed without double-counting the review that
just ran, using the same 7-day freshness window as the Review Readiness Dashboard.
### What this means for you
Every approve-plan moment now carries an explicit verdict on open questions, so a missed
ambiguity cannot slip through looking like a clean plan. If you run the plan-review skills
or /autoplan, you will see the unresolved status as the closing line of every report.
Nothing to configure. Upgrade and your next plan review shows it.
### Itemized changes
#### Added
- **Mandatory unresolved-decisions status in the GSTACK REVIEW REPORT.** Generated into
all six report consumers (/plan-ceo-review, /plan-eng-review, /plan-design-review,
/plan-devex-review, /codex, /devex-review) from `scripts/resolvers/review.ts`. The report
always ends with either the exact unbolded sentinel `NO UNRESOLVED DECISIONS` or a
`**UNRESOLVED DECISIONS:**` bullet block listing each open item; never omitted, always
the final line.
- **Blocking approval gate.** The EXIT PLAN MODE GATE now refuses ExitPlanMode unless the
report's final non-whitespace line is the unresolved status (no "if applicable" escape).
- Static and E2E tests pinning the mandatory status across every report consumer and
gate-bearing skill, so a future compression pass cannot silently drop it again.
#### Fixed
- **/plan-devex-review never logged a review entry.** It carried the approval gate but
never called `gstack-review-log`, so the gate's "review log was called" check was
structurally unsatisfiable and its data was invisible to the Review Readiness Dashboard
and the report. It now logs with the correct timestamp and DX fields.
#### For contributors
- Rebased the parity-suite size baseline v1.53.0.0 to v1.57.7.0 (captures current union
sizes; keeps the per-skill 1.05 ratio so future bloat is still caught). Regenerated the
three ship golden fixtures left stale by #1909. The frozen v1.44.1 integrity anchor and
the v1.47 size-budget baseline are untouched.
## [1.57.6.0] - 2026-06-07
## **Eight community-filed bugs fixed in one wave, four of them security guards that were quietly failing open.**
## **Your redaction gate now catches modern OpenAI keys, and `/ship`'s adversarial review stops choking on your own security tests.**
This is a fix wave. The throughline: guards that reported success while doing nothing.
The secret-redaction gate that every `/spec`, `/ship`, `/cso`, and `/document-*` run
passes through was blind to modern `sk-proj-`/`sk-svcacct-`/`sk-admin-` OpenAI keys and
silently dropped its size cap on a bad flag. The cross-project learnings trust gate was
an allowlist on paper and a denylist in code, so untrusted rows leaked between projects.
The destructive-action classifier waved through "rotate the database password." Each one
looked like it was protecting you. None of them were. All four now fail closed, with
tests that pin the exact case that used to slip by. Three more fixes clear silent
crashes and skipped reviewers, and `/ship`'s adversarial pass no longer trips Anthropic's
usage policy when it reads your repo's own attack-payload fixtures.
### The numbers that matter
Reproduce with `bun test test/redact-engine.test.ts test/gstack-learnings-search.test.ts test/one-way-doors.test.ts test/diff-scope.test.ts test/brain-cache-roundtrip.test.ts`.
| Guard / path | Before | After |
|---|---|---|
| `sk-proj-`/`sk-svcacct-`/`sk-admin-` OpenAI keys | zero findings (HIGH fails open) | blocked, with prose false-positive guards |
| `gstack-redact --max-bytes <garbage>` | NaN silently disables the size cap | rejected at the CLI; engine backstop holds |
| Cross-project learnings with no `trusted` field | imported (denylist bug) | excluded (true allowlist) |
| "rotate the database password" | classified two-way (auto-approvable) | classified one-way (always asks) |
| `.mjs/.cjs/.mts/.cts`-only PRs | backend reviewer skipped | backend reviewer runs |
| `_meta.json` missing `last_refresh` | brain-cache crashes (TypeError) | degrades to a cold cache |
| Safety-skill hooks on Claude Code 2.1.162 | every Edit/Write errored | hooks resolve and run |
| `/ship` adversarial review over security fixtures | denied by usage policy | runs, fixtures read in summary mode |
The redaction one is the sharpest: a project/service-account/admin OpenAI key pasted
into a spec or PR body used to sail straight through the gate. Now it blocks, and the
calibration is pinned so hyphenated prose like "the sk-learning-rate schedule" does not
false-positive and wedge your ship.
### What this means for you
If you rely on the redaction guard or the cross-project learnings gate, they now do what
the docs always said. If you run `/ship` on a repo that tests its own security guards,
adversarial review stops dying on contact with your fixtures. And if you are on Claude
Code 2.1.162, `/guard`, `/freeze`, and `/careful` work again instead of erroring on every
edit. Upgrade and re-run anything that touched these paths.
### Itemized changes
#### Fixed
- **Redaction misses modern OpenAI keys (#1868).** `openai.key` (HIGH/block) used a
contiguous-alphanumeric pattern that stopped at the first `-`/`_`, so base64url-bodied
`sk-proj-`/`sk-svcacct-`/`sk-admin-` keys produced no finding and failed open through
every redaction sink. Replaced with explicit bare-vs-prefixed alternation; added
positive and false-positive tests. Reported by @jbetala7.
- **Redaction size cap fails open on a bad flag (#1824).** A malformed `--max-bytes`
parsed to `NaN`, and `byteLen > NaN` is always false, silently disabling the
fail-closed oversize guard; a negative value blocked everything. The CLI now rejects
non-integer / non-positive values, and the engine falls back to the default cap as a
backstop. Reported by @jbetala7.
- **Cross-project learnings trust gate leaked (#1745).** `gstack-learnings-search
--cross-project` is documented as an allowlist but was coded as `trusted === false`,
admitting any row missing the `trusted` field. Flipped to `trusted !== true`. Reported
by @jbetala7.
- **Destructive-action classifier missed "rotate ... password" (#1839).** The `rotate`
keyword pattern omitted `password` while its `revoke`/`reset` siblings included it, so
the most common credential-rotation phrasing classified as a reversible two-way
question. Added `password` to the alternation.
- **Review Army skipped backend reviewer on ESM/CJS PRs (#1810).** `gstack-diff-scope`
matched only `*.ts|*.js`; a PR touching only `.mjs/.cjs/.mts/.cts` reported no backend
scope. Added the four module extensions. Reported by @jbetala7.
- **Brain-cache crash on a partial `_meta.json` (#1879).** `loadMeta` returned parsed
JSON verbatim; a file missing `last_refresh` crashed three consumers with a TypeError.
Added an object-shape guard and map normalization; missing schema/endpoint identity now
forces a safe rebuild rather than trusting a stale file. Reported by @jbetala7.
- **Safety-skill hooks broken on Claude Code 2.1.162 (#1871).** `guard`, `freeze`, and
`careful` frontmatter hooks used `${CLAUDE_SKILL_DIR}`, which CC 2.1.162 no longer
populates, so every Edit/Write/Bash errored. Anchored the hook commands to the
installed checkout path. Reported by @omariani-howdy.
- **`/ship` adversarial review denied on own security fixtures (#1899).** The Claude
adversarial subagent reasoned "like an attacker" over the full diff; when the diff
included the repo's own attack-payload regression fixtures, Anthropic's real-time
usage-policy safeguards denied the call. The subagent now carries authorized-defensive
-testing framing and reads fixture/test files in summary mode (no raw payload bytes),
stating so explicitly. Reported by @bmajewski.
#### For contributors
- `#1882` (skills hardcode `~/.claude/skills/gstack/`, breaking non-`gstack` install
dirs) is filed as the top item in `TODOS.md`. It was scoped out of this wave once it
proved to be a host-config/preamble change touching all 52 skills, distinct from the
`#1871` hook fix it was originally paired with.
## [1.57.5.0] - 2026-06-07
## **Your agent now keeps its decisions, not just its code.**
## **The durable calls you make, and the "why" behind them, are captured, curated, and resurfaced across sessions, with no daemon to run.**
Every session you and the agent settle real decisions: pick an architecture, cut a scope, choose a tool, reverse an earlier call. Until now that reasoning lived only in a transcript that scrolls away, so the next session re-litigates settled questions or loses the "why." This release adds an institutional decision memory. Durable decisions land in an append-only, event-sourced store, the scope-relevant ones surface automatically at session start, and you can search them any time. It is file-only and works with gbrain off; when gbrain is up you can add semantic recall on top. The planning and ship skills capture their own key calls so the high-value decisions get recorded without anyone remembering to. Separately, `/sync-gbrain` learned to build the cross-reference call graph and to heal a crashed daemon's stale lock instead of wedging every sync.
### The numbers that matter
No speed benchmark here, the win is capability and reliability. These are the real shape of the release (`git diff 1.57.0.0..HEAD`, `bun test`):
| Metric | Value |
|--------|-------|
| New commands | 2 (`gstack-decision-log`, `gstack-decision-search`) |
| Session-start read cost | O(active) bounded snapshot, not a full-history scan |
| Works with gbrain OFF | Yes, every capture/curate/resurface path is files + bins only |
| New source | ~2,550 lines across 26 files |
| New tests | 117 across the decision store + gbrain stages |
Resurfaced decision text is treated as data, not instructions (datamarked at the render boundary), secrets are blocked on write, and `redact` expunges a decision from every read path. The whole loop degrades cleanly: turn gbrain off and you still capture, curate, and resurface.
### What this means for you
Start a session tomorrow and the agent already knows what you settled and why, instead of asking again or quietly reversing it. Log a call with `gstack-decision-log`, reverse one with `--supersede`, pull the relevant history with `gstack-decision-search`. CEO, eng, spec, and ship reviews record their decisions for you. Run `/sync-gbrain` and a crashed autopilot no longer blocks your next sync.
### Itemized changes
#### Added
- **Cross-session decision memory.** An event-sourced (`decide`/`supersede`/`redact`) store at `~/.gstack/projects/<slug>/decisions.jsonl`. "Active" is computed, never a mutable flag, so the history stays honest and tolerant of dangling references.
- **`gstack-decision-log`** — capture a durable decision, reverse one (`--supersede <id>`), expunge an accidental secret (`--redact <id>`), or rewrite the log to its active set (`--compact`). Non-interactive, injection-sanitized, blocks HIGH and MEDIUM secrets on write.
- **`gstack-decision-search`** — read active decisions, scope-filtered to the current branch/issue, with `--recent N`, `--scope`, `--query`, `--all`, `--json`. Add `--semantic` (with `--query`) to append related hits from gbrain memory when it is up; it degrades silently to the reliable file results when gbrain is off.
- **Session-start resurfacing.** Context Recovery shows the scope-relevant active decisions at the top of a session, from a bounded snapshot so it stays fast as the log grows.
- **Skill capture.** `/plan-ceo-review`, `/plan-eng-review`, `/spec`, and `/ship` record their structured decisions (accepted scope, architecture verdict, filed spec, version bump) automatically.
- **A `## Cross-session decision memory` section in CLAUDE.md** documenting when and how to capture and resurface.
- **`/sync-gbrain` call-graph build (`--dream`).** Builds the symbol cross-reference graph behind a lock-free gate, with an honest outcome guard that reports a degraded no-op as WARN rather than a false success.
#### Changed
- Decision text that resurfaces into agent context is datamarked (code fences, `---` banners, `<|role|>`/`</system>` tags, chat turn-prefixes, and Unicode line terminators are neutralized) so stored text can never masquerade as instructions.
- `/sync-gbrain` pin guidance is accurate for current gbrain, and the worktree-scoped `.gbrain-source` pin routes code queries correctly.
#### Fixed
- `/sync-gbrain` no longer wedges forever on a crashed autopilot daemon's stale lock: it reads the holder pid, confirms liveness, and ignores a dead one (it stays conservative when it cannot tell).
#### For contributors
- New shared `lib/jsonl-store.ts` (injection-reject + atomic single-line append + tolerant read) backs both the learnings and decision stores, so the sanitization path is audited in one place.
- `lib/bin-context.ts` shares slug/branch/flag plumbing across the decision bins.
## [1.57.4.0] - 2026-06-08
## **The completeness principle is now Boil the Ocean, matching the post it came from.**
## **One name across the ETHOS file, every skill, and the developer-profile dial.**
The principle that tells gstack to do the complete thing was called "Boil the Lake" in
`ETHOS.md` and in every generated skill, with the ocean cast as the anti-pattern. The
developer-profile system and the completeness intro link already used "boil the ocean"
as the good, ship-the-whole-thing pole. So the same idea carried two opposite framings
depending on where you read it. This renames the principle to Boil the Ocean everywhere
and reframes the metaphor: the ocean is the complete destination, and lakes are the
boilable units you ship on the way there. The guidance is identical. Only the name and
the framing prose changed.
### The numbers that matter
Reproduce with `git diff v1.57.3.0..HEAD --stat`.
| Property | Before | After |
|---|---|---|
| Principle name in ETHOS + every skill | "Boil the Lake" | "Boil the Ocean" |
| Name vs. the `scope_appetite` dial ("boil the ocean" = complete) | split | unified |
| Files updated | — | 63 (ETHOS, CLAUDE, README, resolvers, templates, generated SKILL.md) |
| Runtime behavior change | — | none, text only |
The one number that matters is zero: no behavior changed. A reviewer reading `ETHOS.md`
no longer hits "ocean" as the thing to avoid in one section and the thing to aim for in
the next.
### What this means for you
You get the same complete-the-work recommendations, now under the name from Garry's
"Boil the Oceans" post. The metaphor reads straight through: the ocean is the goal,
lakes are how you get there one boil at a time, and only genuinely unrelated
multi-quarter migrations sit outside scope. Nothing to do on your end.
### Itemized changes
#### Changed
- `ETHOS.md` section 1 is renamed to "Boil the Ocean" and reframed so the ocean is the
complete destination and lakes are the boilable first units, not the ceiling.
- The "Completeness Principle" header injected into every tier-2+ skill now reads
"Boil the Ocean," with prose to match.
- `CLAUDE.md` and `README.md` references updated to the new name.
#### For contributors
- Source of the rename lives in the preamble resolvers
(`generate-completeness-section.ts`, the `composition.ts` skip-list, and
`generate-lake-intro.ts`); all SKILL.md files are regenerated from them.
- Unit assertions (`skill-validation`, `terse-build`) and the three ship golden
fixtures updated to the new header.
## [1.57.3.0] - 2026-06-07
## **Every PR `/ship` opens gets the version stamped into its title, fork and agent PRs included.**