From 4a808f0c5813af94e6ffe926d1e30a9de0af7ba4 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Wed, 9 Sep 2026 03:19:04 +0000 Subject: [PATCH] docs(memorable): vendor environment allowlist and stderr policy; two follow-ups Co-Authored-By: Claude Fable 5.1 --- TODOS.md | 35 +++++++++++++++++++++++++++++++ docs/memorable-workflow-memory.md | 10 +++++++++ 2 files changed, 45 insertions(+) diff --git a/TODOS.md b/TODOS.md index 2f0c498ee..762197d21 100644 --- a/TODOS.md +++ b/TODOS.md @@ -626,6 +626,41 @@ identity-based answer. **Effort:** S. **Priority:** P3. **Depends on:** none. +### P3: one state-root rule for the bridge's four stores + +**What:** The hook, `bin/gstack-config` and `bin/gstack-memorable` resolve +their root as `GSTACK_STATE_ROOT` > `GSTACK_HOME` > `GSTACK_STATE_DIR`; the +egress ledger (`lib/egress-receipt.ts`) honors `GSTACK_HOME` > +`GSTACK_STATE_DIR`; the trust-policy store (`lib/gbrain-repo-policy-client.ts`) +only `GSTACK_HOME`; `bin/gstack-uninstall` deletes only +`${GSTACK_STATE_DIR:-$HOME/.gstack}`. Extract one shared rule (a +`lib/state-root.ts` plus its bash twin) and use it everywhere. + +**Why:** With `GSTACK_STATE_ROOT` set, the gate lives under one directory and +the receipts under another; the tests pin all three variables to one temp dir, +so the drift is invisible to them. Found by the /ship red team. + +**Context:** Uninstall already flips `memorable_recall` off unconditionally +(through gstack-config's own resolution) so no config can say `on` after the +hook is gone; the remaining drift is observability, not consent. + +**Effort:** S (human ~3 h / CC+gstack ~20 min). **Priority:** P3. +**Depends on:** none. + +### P3: shared hook logging helper + +**What:** `stateRoot()` and the `hook-errors.log` appender now exist in five +hooks (`question-log`, `question-preference`, `auq-error-fallback`, +`timeline-stop`, `memorable-user-prompt`), with drifting env-var precedence. +Extract `hosts/claude/hooks/hook-log.ts` (root resolution, 0600 append, the +rate limiter the memorable hook added) and migrate the five. + +**Why:** One place to fix precedence and file modes; the memorable hook's +rate limiter belongs to every hook that can fail on every prompt. + +**Effort:** S (human ~2 h / CC+gstack ~15 min). **Priority:** P3. +**Depends on:** the state-root rule above. + ## Aside integration follow-ups (filed via /plan-ceo-review + /plan-eng-review on the third-party-actions Aside plan) ### QA logged-in-evidence path via Aside (Phase 2) diff --git a/docs/memorable-workflow-memory.md b/docs/memorable-workflow-memory.md index 61e64bb68..e35ab30e2 100644 --- a/docs/memorable-workflow-memory.md +++ b/docs/memorable-workflow-memory.md @@ -68,6 +68,16 @@ allowlisted argument fields and a 200-character task line. Those are the vendor's claims. The bridge is tested against memorable-cli 0.5.18's hook contract; other versions are the vendor's compatibility claim. +The binary's environment is an allowlist, not your session's: `PATH`, `HOME`, +user and shell names, locale (`LANG`, `LC_*`), temp directories, the standard +proxy and TLS variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, +`SSL_CERT_FILE`, `SSL_CERT_DIR`, `NODE_EXTRA_CA_CERTS`), the `XDG_*` +directories, and every `MEMORABLE*` variable. No `ANTHROPIC_API_KEY`, no +`GSTACK_*`, no `CLAUDE_*` reaches it. Its stderr is kept out of +`hook-errors.log` whenever the redaction engine finds a credential or PII +shape in it, so a vendor that echoes its input on an error cannot copy your +prompt into a log. + The hook skips the hand-off entirely, with one rate-limited line in `~/.gstack/hook-errors.log`, when: