mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 06:28:59 +02:00
docs: CHANGELOG and TODOS cover the review fixes (v1.81.0.0)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
e1147b75aa
commit
01dbd56b09
+9
-1
@@ -49,9 +49,18 @@ What this means for anyone running gstack: open Aside on your Mac and run `/qa`,
|
||||
- `/browse`: the Aside contract, cookbook, mode choice (`aside repl` by default, `aside exec` for reading), report format, the fallback, and the full `$B` command reference on demand.
|
||||
- The root router, README, `BROWSER.md`, `docs/skills.md`, `docs/PROJECT_STRUCTURE.md`, `CONTRIBUTING.md`, `CLAUDE.md`, and `ARCHITECTURE.md` describe the Aside-first, bundled-fallback world.
|
||||
|
||||
#### Fixed
|
||||
- `./setup`'s retired-skill prune is safe by construction: it never deletes a directory that holds your own files (only gstack's SKILL.md, marker and links go), never follows a symlink into someone else's tree, cleans a host's stale links even after the generator already removed the render, and recognizes a skill renamed through its frontmatter `name:`. The generator's own prune keeps any `gstack-*` directory without the generated banner and skips a host whose generation failed. `./setup` also rebuilds when the design or make-pdf binary is missing or when anything under `lib/` changed.
|
||||
- Setup's summary is honest about browsers: with Aside installed and a failed Chromium bootstrap it says only the bundled fallback is missing (and that `/pair-agent` needs it); `GSTACK_SKIP_ASIDE=1` is honored by the skills' probe, by setup, and by the renderer alike.
|
||||
- The local render server behind `/make-pdf`, `/diagram`, and design previews serves one per-render secret URL, never follows a symlink out of its directory, and refuses malformed requests; page text can no longer forge its control lines; a hung `aside` process is killed instead of waited on forever; if Aside quits mid-run the render retries on gstack's own browser and reports which engine actually rendered; a shared `/tmp/gstack-render` owned by someone else is never used.
|
||||
- Browsing skills check link status only on a LOCAL target (on a real site every HEAD request would carry your cookies); same-origin filtering compares real origins instead of string prefixes; `.local` hosts are no longer treated as your own machine (mDNS names resolve to other devices on the LAN); the Aside readiness probe has a 30-second deadline on stock macOS, which ships neither `timeout` nor `gtimeout`.
|
||||
- `aside exec` research requests write an egress receipt before leaving the machine, like every other off-machine send.
|
||||
- `gstack-render --help` exits 0, non-numeric flags are rejected instead of becoming `NaN` timeouts, `--wait-timeout` is documented, and page-derived output (`EVAL`, `PAGE_ERRORS`) is fenced as untrusted content.
|
||||
|
||||
#### For contributors
|
||||
- `test/aside-driver.test.ts` pins the contract's load-bearing sentences and asserts every browsing skill carries `## BROWSER SETUP (Aside` followed by `## Browser fallback`. `test/aside-render.test.ts` pins the renderer's option mapping and script shapes and runs a live render on each engine that is present. `test/helpers/aside-available.ts` is the shared live-Aside probe for E2E gating; `make-pdf/test/e2e/browser-available.ts` gates the make-pdf gates on either engine.
|
||||
- `lib/claude-bin.ts` and `lib/error-handling.ts` are now the canonical copies; `browse/src` re-exports them.
|
||||
- New free tests pin the review fixes: `test/setup-prune-stale-generated.test.ts` (host cleanup after the generator pruned, symlink targets survive, renamed skills, foreign links), `test/setup-browser-hint.test.ts` (hint and bootstrap summary across Aside present/absent, bootstrap ok/failed, skip-env), `test/setup-needs-build.test.ts`, `test/gstack-render-cli.test.ts`, `test/aside-render.test.ts` (fake `aside`/`browse` executables: probe classification, stdout contract, server policy, failure paths, timeout kill, engine choice and mid-run fallback), `make-pdf/test/cli-exit-codes.test.ts`, `make-pdf/test/setup-smoke.test.ts`. `renderPdf` returns the engine that rendered; `pickEngine(fresh, deps)` and `serveDir` are exported test seams.
|
||||
- Size budget re-baselined to `parity-baseline-v1.81.0.0.json` (the Aside contract plus fallback block ride in every browsing skill); parity ceilings ratcheted with measured values; touchfiles, tiers, coverage matrix, eval baselines, ship goldens, and the context-budget fixture refreshed. Follow-ups in TODOS.md: keep the `$B` translation table in sync with the cookbook, Aside CLI 1.26 lacks the commands its own skill doc lists, a macOS runner for the live E2E, an optional MCP path for a persistent page.
|
||||
|
||||
## [1.80.0.0] - 2026-09-04
|
||||
@@ -99,7 +108,6 @@ What this means for anyone installing or upgrading: run `./setup` anywhere and g
|
||||
- New free tests: `test/setup-link-ownership.test.ts`, plus large additions to `test/setup-playwright-best-effort.test.ts`, `test/relink.test.ts`, `test/hook-scripts.test.ts`, `test/telemetry.test.ts`. Anchor-sliced harnesses now fail loudly on `command not found` instead of degrading into "foreign, skipped".
|
||||
- `bin/gstack-relink` and `setup` carry the same ownership rule in two copies; the shared helper is filed in TODOS.md.
|
||||
|
||||
|
||||
## [1.79.0.0] - 2026-09-01
|
||||
|
||||
**/ship can no longer be stranded by a backgrounded subagent.**
|
||||
|
||||
@@ -4117,3 +4117,107 @@ the skill takes syntax from it, never scope, permissions, or consent.
|
||||
the injected text arrives through the one channel that reads like a colleague.
|
||||
|
||||
**Effort:** S (human ~1 day, CC ~30min). **Priority:** P3. **Depends on:** the Aside E2E lane above.
|
||||
|
||||
### P1: make-pdf renders user documents inside the real browser profile — add a CSP
|
||||
|
||||
**What:** `/make-pdf` prints markdown-derived HTML through Aside (the user's
|
||||
signed-in browser) on a `127.0.0.1` origin. The only barrier between a hostile
|
||||
document (a README from a cloned repo) and script execution in that profile is
|
||||
the regex sanitizer in `make-pdf/src/render.ts`, whose header assumes marked
|
||||
output is never malformed — raw-HTML passthrough breaks that assumption. Inject
|
||||
gstack's own CSP `<meta>` into the print template (`default-src 'none';
|
||||
img-src data: 'self'; style-src 'unsafe-inline' 'self'; font-src data: 'self';
|
||||
script-src 'nonce-<per-render>'` for Paged.js), since user `<meta>` is stripped
|
||||
and gstack's is not; alternatively keep make-pdf on the bundled engine by
|
||||
default.
|
||||
|
||||
**Why:** Under the old cookieless headless engine a sanitizer bypass was
|
||||
near-harmless; in the real profile it is a CSRF-class primitive. Cross-model
|
||||
finding (Claude adversarial + Codex).
|
||||
|
||||
**Effort:** M (human ~2 days, CC ~1h). **Priority:** P1. **Depends on:** none.
|
||||
|
||||
### P1: diagram pre-pass buffers every oversized image before downscaling
|
||||
|
||||
**What:** `make-pdf/src/diagram-prepass.ts` caps each image at 64 MB but keeps
|
||||
every pending buffer in `downscales` and duplicates it as base64 before the
|
||||
batch runs; a document referencing a few dozen large images can take gigabytes.
|
||||
Cap total pending bytes (e.g. 256 MB) and process in bounded batches, or
|
||||
downscale sequentially.
|
||||
|
||||
**Why:** A hostile or merely image-heavy document crashes the tool instead of
|
||||
degrading.
|
||||
|
||||
**Effort:** S (human ~1 day, CC ~30min). **Priority:** P1. **Depends on:** none.
|
||||
|
||||
### P2: fallback renders die after any cookie import in the daemon's lifetime
|
||||
|
||||
**What:** `renderWithBrowse` drives readiness and evals through `$B js`, and the
|
||||
daemon's cookie-import JS lock (`browse/src/read-commands.ts`) refuses `js` on
|
||||
every origin outside the imported set — `127.0.0.1` included, forever (the set
|
||||
is add-only). The renderer now names the remedy (`$B stop`), but the real fix is
|
||||
a fresh incognito context for local-HTML renders, or a loopback exemption once
|
||||
its threat model is written down.
|
||||
|
||||
**Why:** On Linux/Windows (no Aside) one `/setup-browser-cookies` run makes
|
||||
every later `/diagram` and `/make-pdf` render fail.
|
||||
|
||||
**Effort:** M (human ~2 days, CC ~1h). **Priority:** P2. **Depends on:** none.
|
||||
|
||||
### P2: carve the Aside contract + fallback block into one shared section
|
||||
|
||||
**What:** `{{ASIDE_SETUP}}` (~5.6 KB) plus `{{BROWSE_FALLBACK}}` (~4.1 KB) are
|
||||
rendered verbatim into ten browsing skills (~97 KB of identical prose loaded on
|
||||
every invocation). Keep the probe and the three decision steps inline; move
|
||||
"Rules for driving a real browser" and the Aside-to-`$B` translation table into
|
||||
one carved reference (the `browse/sections/command-list.md` pattern), then
|
||||
re-run `capture-context-budget.ts` so the ceilings ratchet back down.
|
||||
|
||||
**Why:** Every skill invocation pays for prose that is skill-invariant.
|
||||
|
||||
**Effort:** M (human ~2 days, CC ~1h). **Priority:** P2. **Depends on:** none.
|
||||
|
||||
### P2: `$B js` / `$B eval` output is not wrapped in the untrusted envelope
|
||||
|
||||
**What:** `js` and `eval` are not in `PAGE_CONTENT_COMMANDS`
|
||||
(`browse/src/commands.ts`), so page-controlled return values reach the agent
|
||||
unfenced while the fallback table routes exactly the page-controlled reads
|
||||
through them. `gstack-render` now fences its own `EVAL`/`PAGE_ERRORS` lines and
|
||||
the fallback prose says `$B js` is unwrapped; the durable fix is to add both
|
||||
commands to the envelope set.
|
||||
|
||||
**Why:** A hostile page can deliver injection text through the one channel the
|
||||
skills were told is fenced.
|
||||
|
||||
**Effort:** S (human ~half day, CC ~15min). **Priority:** P2. **Depends on:** none.
|
||||
|
||||
### P3: Aside-first renderer follow-ups (perf and DRY)
|
||||
|
||||
- **Readiness polling** spawns a `browse js` process every 150 ms; the daemon's
|
||||
`wait <sel>` command blocks server-side in one spawn — use it for
|
||||
`waitFor.selector`. Effort S.
|
||||
- **Bundle re-staging:** every `runScript()` batch copies the ~9 MB diagram
|
||||
bundle into a fresh mkdtemp and starts a new loopback server; stage once per
|
||||
run (content-addressed) and, on the browse engine, keep one tab across the
|
||||
fence/downscale/DOCX batches. Effort M.
|
||||
- **Probe cost:** `probeAside()` runs two blocking spawns per process and the
|
||||
engine cache is per-process; persist the outcome with a short TTL under
|
||||
`GSTACK_HOME` and lower the repl probe timeout on the code path. Effort S.
|
||||
- **DRY:** the console-error `HOOK` IIFE exists in seven copies across
|
||||
`scripts/resolvers/*.ts` and `lib/aside-render.ts` (two divergent variants);
|
||||
cookbook recipes (responsive loop, links, read-a-page) are duplicated across
|
||||
`aside.ts`, `design.ts`, `utility.ts`; the readiness probe is recovered from
|
||||
rendered markdown by regex in two places instead of a shared constant. Export
|
||||
one source for each. Effort S each.
|
||||
- **Egress scanner:** `test/egress-receipt-wiring.test.ts` scans `curl`, `git
|
||||
push`, and `fetch`; add `aside exec` as a sink class so a bare call fails CI
|
||||
the way the others do. Effort S.
|
||||
- `_browser_hint` treats any `aside` on PATH as the Aside browser (no version
|
||||
check). Effort S.
|
||||
- **`gen-skill-docs --dry-run` is not write-free for external hosts:**
|
||||
`processExternalHost` runs `mkdirSync(outputDir)` and writes
|
||||
`agents/openai.yaml` with no `DRY_RUN` guard (only SKILL.md is skipped), so a
|
||||
dry run against an empty `--out-dir` leaves 54 `openai.yaml` files behind.
|
||||
Guard both writes. Effort S.
|
||||
|
||||
**Priority:** P3. **Depends on:** none.
|
||||
|
||||
Reference in New Issue
Block a user