mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-09 06:28:59 +02:00
docs(memorable): accurate bridge guide; README row, Docs table, privacy pointer; PROJECT_STRUCTURE
The README section becomes one row in the Standalone binaries table (the shape every other binary uses) plus a Docs-table row and one Privacy bullet saying that optional third-party bridges are off by default and receipted. The guide now separates the two consents, says what gstack hands to the vendor binary and what the receipt can and cannot attest, attributes every statement about the vendor's network activity to the vendor, describes the hook manager accurately (identity via KNOWN_HOOKS, list-items vs list-sources, rollback is whole-file), states the Claude Code hot-reload behaviour, adds a troubleshooting runbook, and keeps the contributors' credit in the CHANGELOG idiom. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
0eaff4b376
commit
a3532b67d0
@@ -266,6 +266,7 @@ Beyond the slash-command skills, gstack ships standalone CLIs for workflows that
|
||||
| `gstack-context-bill` | **Token bill-of-materials** — read-only, offline audit of what an installed skills tree costs in tokens: always-on frontmatter every session pays vs per-invocation SKILL.md + forced references. `--diff` compares two trees, `--budget` enforces a ceiling, `--exact` opts into Anthropic `count_tokens` (sends file text off-machine; writes an egress receipt first, degrades to the offline estimate if the receipt can't be written). |
|
||||
| `gstack-code-intelligence` | **Code-intelligence provider picker** — wraps GBrain, Sourcebot, and Graphify behind one interface: `options`/`status` to see what's available, `select` to pick one, `index`/`search` to use it, `suggest` to check whether the one-time indexing offer should fire here. The offer triggers on large repos (1,000+ tracked files; a decline is persisted). Non-local providers refuse to index *or search* until you record per-repo consent (`consent <repo> yes\|no` — the query text is repo-derived content), the per-repo trust policy's deny and read-only tiers veto write-class operations regardless of consent, and every off-machine send writes an egress receipt. Fully optional — with nothing selected, gstack falls back to grep. |
|
||||
| `gstack-verify-gate` | **Verification stop hook (opt-in)** — blocks a Claude Code turn from ending until the project's declared verify command passes (after 3 blocked re-entries it yields with a loud still-RED warning instead of looping forever). Declare it on one line in CLAUDE.md: `<!-- gstack:verify: bun test -->`. Hooks bypass the permission system, so a declared command never runs until you trust it once per repo (`gstack-verify-gate --trust`); editing the command invalidates trust until re-granted, and every grant is audit-logged. `./setup` never registers it for you — opt in with `gstack-settings-hook add-event --event Stop --command ~/.claude/skills/gstack/bin/gstack-verify-gate --source verify-gate`, remove with `gstack-settings-hook remove-source --source verify-gate`. |
|
||||
| `gstack-memorable` | **Memorable recall bridge (opt-in, third party, Claude Code only)** — connects Claude Code to the external [Memorable](https://memorable.sh) CLI *through gstack* instead of the vendor's own installer, so the hook gets gstack's guarantees: an explicit consent key (`memorable_recall`, off by default, listed by `gstack-egress grants`), a fail-closed egress receipt for every prompt handed over (`gstack-egress list --sink memorable-recall`), a HIGH-tier secret pre-scan, a trust envelope and 8 KiB cap on whatever comes back, an allowlisted environment and process-group containment for the vendor process, and clean removal. `enable` registers the hook at the stable install with a 5 s timeout and never runs the vendor's own consent command; `disable` revokes the gate first and removes the entry by identity even after Claude Code strips the tag; `status` is read-only. gstack never installs Memorable, and what its binary sends is the vendor's claim, not gstack's. Not available on Windows yet. [Full guide](docs/memorable-workflow-memory.md). |
|
||||
| `gstack-wtree` | **Working-tree fingerprint** — prints a content hash of what's actually on disk (temp index seeded from the stat cache, ~40x cheaper than a full re-hash; untracked source counts, gitignored scratch doesn't). Identical content fingerprints identically through commits, rebases, amends, and squashes — it's what binds reviews and test evidence to content instead of commit SHAs. |
|
||||
| `gstack-evidence` | **Verification-evidence ledger** — `run --label <lane> -- <cmd>` transparently wraps any test command (the child's exit code always passes through) and records what ran against which working-tree fingerprint; `check` grades each label FRESH/STALE/MISSING with `--expect-cmd`, `--max-age`, and `--allow-paths` binding. /ship and /land-and-deploy cite fresh evidence instead of re-running suites. Per-run logs are 0600, capped at 2MB, pruned after 30 days; the ledger and logs stay machine-local by design. |
|
||||
| `gstack-issue-guard` | **Tracker-text trust envelope** — fetches GitHub issue/PR text (`issue <n>`, `pr-body`, `pr-comments`, or `--stdin`) and wraps it in a labeled envelope so agents treat it as data: injection-shaped lines get labeled even through fullwidth and invisible-character evasion, and forged envelope banners are defused. Every tracker-text ingress in gstack routes through it, enforced by a CI scanner. |
|
||||
@@ -292,49 +293,6 @@ prune-stale --repoint` removes dead gstack hook entries, re-points stale ones
|
||||
at the stable install, and collapses duplicates, printing one line (and
|
||||
writing a backup beside the file) only when it changed something.
|
||||
|
||||
### Optional Memorable workflow memory (Claude Code only)
|
||||
|
||||
gstack can connect Claude Code to the external `memorable` CLI for workflow
|
||||
capture and injection. It is off by default, and gstack does not install or
|
||||
bundle Memorable. When enabled, its hooks capture all Claude Code prompts, not
|
||||
only gstack commands, and inject relevant workflow guidance into later prompts.
|
||||
Review Memorable's storage and privacy settings before enabling it; Memorable,
|
||||
not gstack, owns the captured data and any network access.
|
||||
|
||||
```bash
|
||||
bin/gstack-memorable enable
|
||||
bin/gstack-memorable status
|
||||
bin/gstack-memorable disable
|
||||
```
|
||||
|
||||
The hooks fail open: if Memorable is missing or errors, Claude continues
|
||||
normally. This is recalled procedural guidance, not deterministic replay, and
|
||||
it is unrelated to Aside or browser automation.
|
||||
|
||||
**Exactly what leaves the machine, per command this bridge can trigger.** The
|
||||
hook makes no network call of its own; every row below is the third-party CLI
|
||||
acting under its own consent, which is why there is no gstack egress receipt to
|
||||
read. `gstack-egress` will not show these.
|
||||
|
||||
| Command | What leaves the machine |
|
||||
|---|---|
|
||||
| `command -v memorable`, `gstack-memorable status` | Nothing. Both are local reads. |
|
||||
| `gstack-memorable enable` | Nothing from gstack. It runs `memorable enable`, which records consent on your machine. |
|
||||
| the hook, on every prompt | The prompt text you typed, to Memorable's embed endpoint, when the local lexical match misses. Nothing else at prompt time. |
|
||||
| capture, at session end | Memorable's own hook, not this bridge and not gstack's consent. It sends the finished session's tool calls and their arguments to Memorable's extraction API under `memorable enable`. Turn it off with `memorable disable`. |
|
||||
|
||||
**What gstack pin-tests, and what is Memorable's own claim.** gstack tests the
|
||||
gating and the wiring: that `enable` refuses when Memorable already registered
|
||||
the hook itself, that `disable` removes only gstack's entry and never a foreign
|
||||
one, that the hook exits zero and silent when the binary is missing, and that
|
||||
`status` writes nothing. Everything past the process boundary is Memorable's
|
||||
claim and not ours: what it stores, where it stores it, what it sends, and what
|
||||
`memorable disable` and `memorable forget` actually erase. The CLI is a
|
||||
closed-source npm package from a third party.
|
||||
|
||||
Full guide, including what to do when Memorable already registered the hook
|
||||
itself: [docs/memorable-workflow-memory.md](docs/memorable-workflow-memory.md).
|
||||
|
||||
### Continuous checkpoint mode (opt-in, local by default)
|
||||
|
||||
Set `gstack-config set checkpoint_mode continuous` and skills auto-commit your work as you go with a `WIP:` prefix plus a structured `[gstack-context]` body (decisions, remaining work, failed approaches). Survives crashes and context switches. `/context-restore` reads those commits to reconstruct session state. `/ship` filter-squashes WIP commits before the PR (preserving non-WIP commits) so bisect stays clean. Push is opt-in via `checkpoint_push=true` — default is local-only so you don't trigger CI on every WIP commit.
|
||||
@@ -552,6 +510,7 @@ Other references: [docs/gbrain-sync.md](docs/gbrain-sync.md) (sync-specific guid
|
||||
| [Architecture](ARCHITECTURE.md) | Design decisions and system internals |
|
||||
| [Browser](BROWSER.md) | How gstack drives Aside first (the contract, the cookbook, rendering, research), when the fallback engine kicks in, and the fallback's full `$B` command reference |
|
||||
| [Contributing](CONTRIBUTING.md) | Dev setup, testing, contributor mode, and dev mode |
|
||||
| [Memorable recall bridge](docs/memorable-workflow-memory.md) | Opt-in third-party workflow memory through gstack: two consents, what gstack hands over and can attest, removal, troubleshooting |
|
||||
| [Changelog](CHANGELOG.md) | What's new in every version |
|
||||
|
||||
## Privacy & Telemetry
|
||||
@@ -564,6 +523,7 @@ gstack includes **opt-in** usage telemetry to help improve the project. Here's e
|
||||
- **What's never sent:** code, file paths, repo names, branch names, prompts, or any user-generated content.
|
||||
- **Change anytime:** `gstack-config set telemetry off` disables everything instantly.
|
||||
- **Every off-machine send is receipted.** Any gstack-initiated network send — telemetry included — writes a hash-chained, tamper-evident receipt to `~/.gstack/security/egress.jsonl` before the send; sensitive sinks refuse to send at all if the receipt can't be written. Audit with `gstack-egress list`, verify the chain with `gstack-egress verify` (exit 3 on tamper), see the standing consent settings with `gstack-egress grants`. The ledger records attempted sends so accidents are auditable — it's an audit trail, not a network firewall.
|
||||
- **Optional third-party bridges are off by default and receipted too.** The one that exists today, the [Memorable recall bridge](docs/memorable-workflow-memory.md), hands your prompt to a locally installed vendor binary only after you run `gstack-memorable enable`; every hand-off writes a receipt first, the consent shows up in `gstack-egress grants`, and what the vendor then sends is documented as the vendor's claim.
|
||||
|
||||
Data is stored in [Supabase](https://supabase.com) (open source Firebase alternative). The schema is in [`supabase/migrations/`](supabase/migrations/) — you can verify exactly what's collected. The Supabase publishable key in the repo is a public key (like a Firebase API key) — row-level security policies deny all direct access. Telemetry flows through validated edge functions that enforce schema checks, event type allowlists, and field length limits.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ gstack/
|
||||
│ └── dist/ # Compiled binary
|
||||
├── hosts/ # Typed host configs (one per AI agent)
|
||||
│ ├── claude.ts # Primary host config
|
||||
│ ├── claude/hooks/ # Claude Code lifecycle hooks (AUQ capture + enforcement, spawned-session directive, timeline stop)
|
||||
│ ├── claude/hooks/ # Claude Code lifecycle hooks (AUQ capture + enforcement, spawned-session directive, timeline stop, Memorable recall bridge (opt-in))
|
||||
│ ├── codex.ts, factory.ts, kiro.ts # Existing hosts
|
||||
│ ├── opencode.ts, slate.ts, cursor.ts, openclaw.ts # IDE hosts
|
||||
│ ├── hermes.ts, gbrain.ts # Agent runtime hosts
|
||||
@@ -63,7 +63,7 @@ gstack/
|
||||
├── freeze/ # /freeze skill; bin/check-freeze.sh (PreToolUse edit-boundary hook; sources careful/bin/hook-extract.sh, fails closed)
|
||||
├── guard/, unfreeze/ # /guard (careful + freeze in one), /unfreeze
|
||||
├── gstack-upgrade/ # /gstack-upgrade skill + migrations/ (run after ./setup during an upgrade)
|
||||
├── bin/ # CLI utilities (gstack-render.ts = render a local HTML file through Aside or the engine, gstack-repo-mode, gstack-slug, gstack-config, gstack-wtree, gstack-evidence, gstack-issue-guard, gstack-relink, etc.)
|
||||
├── bin/ # CLI utilities (gstack-render.ts = render a local HTML file through Aside or the engine, gstack-repo-mode, gstack-slug, gstack-config, gstack-wtree, gstack-evidence, gstack-issue-guard, gstack-relink, gstack-memorable, etc.)
|
||||
├── document-release/ # /document-release skill (post-ship doc updates + Diataxis coverage map)
|
||||
├── document-generate/ # /document-generate skill (Diataxis doc generator: tutorial/how-to/reference/explanation)
|
||||
├── cso/ # /cso skill (OWASP Top 10 + STRIDE security audit)
|
||||
|
||||
@@ -6,120 +6,195 @@ arrives at the fix it already wrote last month. **Memorable** is a third-party
|
||||
CLI that records how a task was done and hands that back the next time you ask
|
||||
for something close to it.
|
||||
|
||||
gstack does not install it, bundle it, or depend on it. This is a bridge: two
|
||||
commands that wire Memorable's `UserPromptSubmit` hook into gstack's own hook
|
||||
manager, so the entry is registered, listed and removed the same way every
|
||||
other gstack hook is. It is off until you turn it on, and Claude Code is the
|
||||
only host it works with.
|
||||
gstack does not install it, bundle it, or depend on it. What gstack adds is a
|
||||
**bridge**: Memorable's `UserPromptSubmit` hook registered through gstack's own
|
||||
hook manager, wrapped in the guarantees gstack gives every other off-machine
|
||||
sink. It is off until you turn it on, Claude Code is the only host it works
|
||||
with, and it is not available on Windows yet.
|
||||
|
||||
## What you get
|
||||
|
||||
- The prompt hook asks Memorable, before each turn, whether a past session
|
||||
already solved something close to this, and injects that procedure if so.
|
||||
- Registration through `gstack-settings-hook`, so `list-sources`,
|
||||
`prune-stale` and `rollback` all see it.
|
||||
- An off switch that removes gstack's entry and nothing else.
|
||||
- No daemon, no dependency, and nothing at all if the binary is absent.
|
||||
- Before each prompt, the hook asks Memorable whether a past session already
|
||||
solved something close to this and, if so, injects that procedure as
|
||||
clearly labelled reference data.
|
||||
- An explicit gstack-side consent key, `memorable_recall`, off by default and
|
||||
listed by `gstack-egress grants` with its revoke command.
|
||||
- A receipt for every prompt handed to the vendor binary, before the hand-off
|
||||
(`gstack-egress list --sink memorable-recall`). No receipt, no hand-off.
|
||||
- A HIGH-tier secret pre-scan: a prompt carrying a live-shaped credential is
|
||||
never handed over.
|
||||
- A trust envelope and an 8 KiB cap on whatever comes back, and the vendor can
|
||||
never block a prompt or speak as gstack.
|
||||
- The vendor runs in an allowlisted environment (no API keys from your
|
||||
session) inside its own process group, and is killed with that group if it
|
||||
overruns the budget.
|
||||
- Registration at the stable install, healing on every `./setup`, survival of
|
||||
`./setup --no-team`, removal by `gstack-uninstall`, and an off switch that
|
||||
works even after Claude Code has rewritten `settings.json`.
|
||||
|
||||
## What this is not
|
||||
|
||||
- Not deterministic replay. It is recalled guidance the model may ignore.
|
||||
- Not related to Aside or to browser automation.
|
||||
- Not a gstack feature with gstack's guarantees. Everything past the process
|
||||
boundary belongs to a closed-source npm package from another vendor.
|
||||
- Not a gstack feature with gstack's guarantees past the process boundary.
|
||||
Everything the `memorable` binary does after gstack hands it a prompt belongs
|
||||
to a closed-source npm package from another vendor.
|
||||
|
||||
## Exactly what leaves your machine
|
||||
## Two consents, neither implies the other
|
||||
|
||||
The hook makes no network call of its own. Every row below is the Memorable
|
||||
CLI acting under its own consent, which is why **`gstack-egress` will not show
|
||||
any of it**: gstack issues no request here, so there is no receipt to write.
|
||||
| Consent | Who sets it | What it controls |
|
||||
|---|---|---|
|
||||
| `memorable_recall` (gstack) | `gstack-memorable enable` / `disable` | whether gstack's hook hands prompts to the vendor binary at all |
|
||||
| Memorable's own consent | `memorable enable` / `disable` / `forget`, run by you | whether Memorable stores procedures and, per its docs, sends session traces to its extraction API |
|
||||
|
||||
| Command | What leaves the machine |
|
||||
gstack never runs the vendor's consent commands and never reads their state.
|
||||
`gstack-memorable enable` prints them so you can run or inspect them yourself.
|
||||
Turning the bridge off turns off gstack's hand-off; it does not change what
|
||||
Memorable is allowed to do with what it already has.
|
||||
|
||||
## What gstack hands over, and what it can attest
|
||||
|
||||
| Command | What gstack hands to the vendor binary |
|
||||
|---|---|
|
||||
| `command -v memorable`, `gstack-memorable status` | Nothing. Both are local reads. |
|
||||
| `gstack-memorable enable` | Nothing from gstack. It runs `memorable enable`, which records consent on your machine. |
|
||||
| the hook, on every prompt | The prompt text you typed, to Memorable's embed endpoint, when the local lexical match misses. Nothing else at prompt time. |
|
||||
| capture, at session end | Memorable's own hook, not this bridge. It sends the finished session's tool calls and their arguments to Memorable's extraction API under `memorable enable`. |
|
||||
| `gstack-memorable status`, `enable`, `disable` | Nothing. They check that the binary exists; they never execute it. |
|
||||
| the hook, on every prompt (gate on) | Claude Code's `UserPromptSubmit` JSON: `session_id`, `cwd`, `transcript_path`, `prompt`. The binary runs with your privileges, so it can read anything you can, including the transcript that path names. |
|
||||
|
||||
Two things follow from that table and are worth saying out loud. The hook sees
|
||||
**every** Claude Code prompt, not only the ones that came from a gstack skill.
|
||||
And capture is a separate consent from this bridge: turning the bridge off does
|
||||
not turn capture off. `memorable disable` does.
|
||||
The receipt attests exactly those bytes (count and sha256) and names the
|
||||
recipient gstack actually ran: `local:<path to the memorable executable>`. It
|
||||
does not and cannot attest the vendor's network activity. Memorable's
|
||||
documentation states that recall embeds a scrubbed task line through its API
|
||||
only when the local lexical match misses, and that capture uploads tool names,
|
||||
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.
|
||||
|
||||
## What gstack pin-tests, and what is Memorable's claim
|
||||
The hook skips the hand-off entirely, with one rate-limited line in
|
||||
`~/.gstack/hook-errors.log`, when:
|
||||
|
||||
gstack tests the gating and the wiring, in `test/gstack-memorable.test.ts`:
|
||||
- the gate is off, `MEMORABLE=0` is set, or the vendor binary is missing;
|
||||
- the prompt carries a HIGH-tier credential shape (checked on the raw bytes
|
||||
and on the decoded string values, so a JSON-escaped key does not slip by),
|
||||
or is larger than 1 MiB;
|
||||
- the repo's per-remote trust policy is `deny` or `read-only` (judged by the
|
||||
session's working directory, so a session that touches other repositories
|
||||
is not covered);
|
||||
- the receipt cannot be written, or fewer than 500 ms remain of the hook's
|
||||
4.5 s budget.
|
||||
|
||||
- `enable` refuses when Memorable already registered the hook itself, and
|
||||
touches neither consent nor the settings file when it refuses.
|
||||
- `disable` removes only gstack's entry and never a foreign one.
|
||||
- The hook exits zero and silent when the binary is missing.
|
||||
- `status` writes nothing.
|
||||
A receipt whose outcome is missing means the host killed the hook or the clock
|
||||
ran out. Read it as unknown, never as success. An outcome of `output-written`
|
||||
means gstack wrote enveloped context to Claude Code's stdin; whether Claude used
|
||||
it is not something a hook can know.
|
||||
|
||||
Everything else is Memorable's claim and not ours: what it stores, where it
|
||||
stores it, what it sends, and what `memorable disable` and `memorable forget`
|
||||
actually erase.
|
||||
## What gstack tests, and what is Memorable's claim
|
||||
|
||||
gstack tests the bridge in `test/gstack-memorable.test.ts`,
|
||||
`test/memorable-user-prompt-hook.test.ts` and
|
||||
`test/gstack-settings-hook-schema-aware.test.ts`, against a fake vendor: every
|
||||
refusal above, the receipt-before-hand-off order, the envelope and cap, the
|
||||
environment allowlist, the process-group kill, the identity-based removal
|
||||
after Claude Code strips the tag, the sweep exclusion, the uninstall arm, and
|
||||
the exit codes. Everything past the process boundary, what Memorable stores,
|
||||
where, what it sends, and what `memorable disable` and `memorable forget`
|
||||
erase, is Memorable's claim and not ours.
|
||||
|
||||
## Turning it on (three steps, the middle one is yours)
|
||||
|
||||
```bash
|
||||
npm i -g memorable-cli # the CLI, from npm, not from gstack
|
||||
memorable login # opens a browser; only you can do this
|
||||
bin/gstack-memorable enable
|
||||
npm i -g memorable-cli # the CLI, from npm, not from gstack
|
||||
memorable login && memorable enable # the vendor's account and the vendor's consent; only you can do these
|
||||
gstack-memorable enable # gstack's gate + the hook, at the stable install
|
||||
```
|
||||
|
||||
`enable` runs `memorable enable` to record capture consent, then registers the
|
||||
hook. Check it with:
|
||||
`enable` refuses, and changes nothing, when the vendor binary is missing, when
|
||||
Memorable already registered its own hook (see below), when the stable install
|
||||
does not carry this bridge (run `./setup` first), or when `settings.json`
|
||||
cannot be read. Claude Code picks up the new hook within a few seconds on
|
||||
current versions; if it does not fire, restart the session. Verify with:
|
||||
|
||||
```bash
|
||||
bin/gstack-memorable status
|
||||
gstack-memorable status
|
||||
```
|
||||
|
||||
## If Memorable already registered the hook
|
||||
|
||||
`memorable start`, `memorable setup` and `memorable install-hooks` each
|
||||
register the same `UserPromptSubmit` hook under Memorable's own name, outside
|
||||
gstack's table. That is the documented way to install the CLI, so on most
|
||||
machines the hook is already there before gstack is asked.
|
||||
`memorable install-hooks` and `memorable start` register the same
|
||||
`UserPromptSubmit` hook under Memorable's own name, outside gstack's table
|
||||
(`memorable enable` does not; only those two do). `enable` refuses in that case
|
||||
rather than adding a second entry: two entries run the hook twice on every
|
||||
prompt, injecting twice and capturing twice against your allowance.
|
||||
|
||||
`enable` refuses in that case rather than adding a second entry. Two entries
|
||||
run the same command twice on every prompt: the context is injected twice, and
|
||||
the session is captured twice against your extraction allowance.
|
||||
|
||||
To hand it to gstack instead, delete that entry from
|
||||
`~/.claude/settings.json` and run `enable` again. Memorable has no command that
|
||||
removes its own hook; `uninstall-hooks` is not a command in 0.5.18.
|
||||
To hand it to gstack instead, delete that entry from `~/.claude/settings.json`
|
||||
and run `enable` again. Memorable has no command that removes its own hook.
|
||||
|
||||
## Turning it off
|
||||
|
||||
```bash
|
||||
bin/gstack-memorable disable
|
||||
gstack-memorable disable
|
||||
```
|
||||
|
||||
That removes gstack's hook entry and runs `memorable disable`, which stops
|
||||
capture. It does not delete anything Memorable has already stored. For that,
|
||||
use `memorable forget`, or remove the binary.
|
||||
The gate goes off first, so the very next prompt is off even before the entry
|
||||
is gone; a prompt already past the gate completes. Then gstack's entry is
|
||||
removed by identity (tag or no tag), both results are verified, and any
|
||||
partial failure is reported with a non-zero exit. Memorable's own consent and
|
||||
whatever it stored are untouched: `memorable disable` stops capture,
|
||||
`memorable forget` denies everything, and its own docs say what each erases.
|
||||
|
||||
## Under the hood
|
||||
The entry also comes out with `gstack-uninstall` (named in its summary) and
|
||||
survives `./setup --no-team`, which only tears down team-mode hooks.
|
||||
|
||||
`bin/gstack-memorable` is the front door. It resolves the CLI from
|
||||
`MEMORABLE_BIN`, then `~/.memorable/bin/memorable`, then `PATH`, and refuses
|
||||
with a message naming the variable if it finds none.
|
||||
## Troubleshooting
|
||||
|
||||
`hosts/claude/hooks/memorable-user-prompt-hook` is what actually gets
|
||||
registered. It resolves the same three ways, execs `memorable hook
|
||||
user-prompt`, and exits zero on every failure path. A missing or broken
|
||||
integration must never interrupt Claude Code, so the hook has no way to fail
|
||||
loudly: if it cannot find the binary it prints nothing and returns success.
|
||||
- **The hook never fires.** `gstack-memorable status` should show the gate
|
||||
`on` and "registered by gstack". If it shows a mismatch line, follow it.
|
||||
If everything looks right, restart Claude Code once.
|
||||
- **Nothing is ever recalled.** The hand-offs are happening if
|
||||
`gstack-egress list --sink memorable-recall` shows receipts with
|
||||
`output-written` or `injected=no` outcomes. `injected=no` means the vendor
|
||||
returned nothing: check `memorable status` and `memorable doctor` for
|
||||
login, consent and stored procedures.
|
||||
- **Outcomes say `timeout`.** The vendor took longer than the budget allowed
|
||||
(roughly 4 seconds after gstack's own work). That is usually network.
|
||||
- **`hook-errors.log` names a refusal.** `refused:redaction-high` means a
|
||||
credential shape was in the prompt; `trust policy ... deny or read-only`
|
||||
means the repo is protected; `receipt-unwritable` means
|
||||
`~/.gstack/security` is not writable.
|
||||
- **`enable` says the stable install predates this bridge.** Run `./setup`
|
||||
(or `/gstack-upgrade`) so the hook registered at `~/.claude/skills/gstack`
|
||||
is the code that will run.
|
||||
|
||||
Registration itself goes through `bin/gstack-settings-hook ensure-event`, and
|
||||
the entry is in that file's `KNOWN_HOOKS` table, so it is identified by its
|
||||
command rather than by a tag. Claude Code rewrites `settings.json` and does not
|
||||
preserve private tags; the same reason the rest of the table matches on
|
||||
command.
|
||||
## Under the hood, accurately
|
||||
|
||||
`bin/gstack-memorable` is the front door. It resolves the vendor CLI from
|
||||
`GSTACK_MEMORABLE_BIN`, then `MEMORABLE_BIN`, then `~/.memorable/bin/memorable`,
|
||||
then `PATH`, and refuses with a message naming those when it finds none. It
|
||||
never executes the binary. It takes a lock for the duration of `enable` and
|
||||
`disable`, captures the prior state first, and on a failure restores that
|
||||
state rather than an assumed one.
|
||||
|
||||
`hosts/claude/hooks/memorable-user-prompt-hook` is the registered command: a
|
||||
fail-open bash shim over `memorable-user-prompt-hook.ts`, which runs the
|
||||
pipeline described above and always exits 0.
|
||||
|
||||
Registration goes through `gstack-settings-hook ensure-event` with a 5 s
|
||||
timeout, and the hook has a row in that file's `KNOWN_HOOKS` table, so it is
|
||||
identified by its command, never by a tag. Consequences:
|
||||
|
||||
- `gstack-settings-hook list-items --event UserPromptSubmit --owned-by
|
||||
gstack-memorable` shows it whether or not the tag survived;
|
||||
`list-sources` shows only tagged entries, so it may not.
|
||||
- `prune-stale --repoint` (run by every `./setup`) heals a stale path.
|
||||
- `./setup --no-team` excludes it from its sweep; `gstack-uninstall` removes it.
|
||||
- `gstack-settings-hook rollback` is a whole-file restore of the last
|
||||
mutation, not a per-hook undo.
|
||||
|
||||
Windows: not yet. There is no process group to contain the vendor there, so
|
||||
`enable` refuses and the hook exits 0. Tracked in TODOS.md.
|
||||
|
||||
## Credits
|
||||
|
||||
The integration and its hook contract are by
|
||||
[Advaiyt Sane](https://github.com/AdvaiytSane) and
|
||||
[Nikhil Krishnaswamy](https://github.com/NIkhil-cmd-cmd) at Memorable.
|
||||
[Advaiyt Sane](https://github.com/AdvaiytSane) (@AdvaiytSane) and
|
||||
[Nikhil Krishnaswamy](https://github.com/NIkhil-cmd-cmd) (@NIkhil-cmd-cmd)
|
||||
at Memorable (#2831). The consent, receipt, envelope and containment layers
|
||||
were added in review.
|
||||
|
||||
Reference in New Issue
Block a user