Its env-less contract must be env-less: any ambient override leaking into a
shared-process shard flips the run into override mode, which correctly skips
the cache write the test asserts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Found while closing the wave's eval gate: a test exporting
GSTACK_PROJECT_SLUG from the repo root persisted the override into the cwd
slug cache, silently rebinding the ENTIRE repo's session state (evals,
decisions, timelines) to the test's slug for every later env-less run. The
escape hatch is per-invocation by contract — it no longer writes the cache.
The cache dir also hardcoded $HOME while lib/bin-context.ts's native port
(#2561) reads it GSTACK_HOME-aware, so temp-home test runs littered the real
~/.gstack (observed: 2,528 stale temp-cwd entries, swept). Writer and reader
now key the same GSTACK_HOME-aware cache; regression tests pin both
behaviors.
Also raises the cso --diff eval budget (240s/25t → 360s/40t):
transcript-verified, the wave's legitimately-grown audit session completes
the report and dies in closing telemetry at ~215s under the old budget; the
full-audit sibling already runs at 300s.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(gstack-slug): sanitize cached slug before eval
The compute and fallback paths filter slug output to [a-zA-Z0-9._-], but a
value read straight from ~/.gstack/slug-cache was echoed into eval output
unsanitized. A locally-planted cache file could inject shell into
eval "$(gstack-slug)". Re-sanitize on every path so the invariant the file
header promises actually holds, and heal a poisoned cache on the next write.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(telemetry): accurate consent copy + JSON-safe repo basename
The telemetry consent prompt promised "no repo names" while the preamble
epilogue records the repo basename in the local skill-usage.jsonl. It is
already stripped before any remote upload, so it never left the machine, but
the copy was unqualified. Reword it to state repo name is local-only and
stripped before upload.
Also sanitize the basename to [a-zA-Z0-9._-] before it goes into the
hand-built JSON, so a repo directory name containing quotes or newlines can
neither break the JSON nor leak a fragment past the regex stripper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(docs): regenerate SKILL.md + ship goldens for telemetry change
Generated output of the preceding resolver change: the corrected consent copy
and sanitized repo basename now appear in every skill preamble. Golden ship
fixtures refreshed to match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(telemetry): enforce no-repo-identity-egress invariant
Pins the contract that repo/branch identity in the synced skill-usage.jsonl is
stripped before the remote POST. Three checks: a floor (the three known fields),
coverage (every repo/branch field a producer writes into skill-usage.jsonl is
stripped, so a future producer rename can't silently leak), and behavior (runs
the actual sed strip expressions over a sample event). Scoped to the synced
file, so the local-only timeline branch field is correctly excluded.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(gstack-slug): regression test for cached-slug eval injection
Proves a poisoned ~/.gstack/slug-cache file cannot inject shell metacharacters
into gstack-slug output (the value consumed by eval). Verified red when the
cache-read sanitization is removed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore: bump version and changelog (v1.55.1.0)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>