diff --git a/README.md b/README.md index 3cfc7244a..ff9c0a878 100644 --- a/README.md +++ b/README.md @@ -332,6 +332,9 @@ 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. diff --git a/docs/memorable-workflow-memory.md b/docs/memorable-workflow-memory.md new file mode 100644 index 000000000..6c23f1247 --- /dev/null +++ b/docs/memorable-workflow-memory.md @@ -0,0 +1,125 @@ +# Workflow memory with Memorable (optional, third party) + +The third time you ask Claude Code to do the same shape of work, it starts +from nothing again. It re-reads the same files, re-runs the same searches, and +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. + +## 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. + +## 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. + +## Exactly what leaves your machine + +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. + +| 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. It sends the finished session's tool calls and their arguments to Memorable's extraction API under `memorable enable`. | + +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. + +## What gstack pin-tests, and what is Memorable's claim + +gstack tests the gating and the wiring, in `test/gstack-memorable.test.ts`: + +- `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. + +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. + +## 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 +``` + +`enable` runs `memorable enable` to record capture consent, then registers the +hook. Check it with: + +```bash +bin/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. + +`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. + +## Turning it off + +```bash +bin/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. + +## Under the hood + +`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. + +`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. + +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. + +## 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.