mirror of
https://github.com/garrytan/gstack.git
synced 2026-07-10 01:48:38 +02:00
Merge remote-tracking branch 'origin/main' into garrytan/trunk-land-skill
# Conflicts: # CHANGELOG.md # VERSION # package.json
This commit is contained in:
+247
@@ -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.**
|
||||
|
||||
Reference in New Issue
Block a user