Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
59b8514
refactor: export the rules layers' guarded file reads
REPPL Oct 11, 2026
c8d1ab0
feat: keep memory notes with the memory verb
REPPL Oct 11, 2026
dd18569
fix: name a refused root before a memory verb's own refusal
REPPL Oct 11, 2026
efe024a
fix: re-read memory notes under the lock through the opened directory
REPPL Oct 11, 2026
785f351
fix: keep the reason a memory write failed
REPPL Oct 11, 2026
089b8f7
fix: exit 1 for a memory file that does not parse
REPPL Oct 11, 2026
1276983
docs: say the v0.14.0 guide's memory name returns with a new job
REPPL Oct 11, 2026
b8d31ba
docs: record the limits memory notes step 1 chose
REPPL Oct 11, 2026
0ed2d61
fix: keep memory notes inside the test and reach gates
REPPL Oct 11, 2026
2a30bef
fix: pin the example receipt to the manifest naming the memory chapter
REPPL Oct 11, 2026
84f8042
Merge remote-tracking branch 'origin/main' into feat/memory-notes-store
REPPL Oct 12, 2026
e6d2a7d
feat: export the loader's term rule and keep a recalled ledger
REPPL Oct 12, 2026
59b69f3
feat: recall memory notes through the prompt hook and rules --prompt
REPPL Oct 12, 2026
9ae1377
feat: count a memory note's id as a record handle in running text
REPPL Oct 12, 2026
fa6abde
fix: keep the recall's note items inside its package
REPPL Oct 12, 2026
d9d8b23
fix: look up the root commit once per prompt, and only for a matched …
REPPL Oct 12, 2026
0a0fb14
fix: name a root-commit lookup git did not answer in time
REPPL Oct 12, 2026
bff58f9
refactor: read both memory layers through one reader
REPPL Oct 12, 2026
a8fba1b
test: prove the rules and the notes share one turn's budget
REPPL Oct 12, 2026
295bcde
fix: cap the prompt rules --prompt reads as the hook caps it
REPPL Oct 12, 2026
bf9547e
fix: keep the recall's untied matching inside its package
REPPL Oct 12, 2026
2a5bb1a
Merge main into feat/memory-recall-notes
REPPL Oct 14, 2026
a472778
fix: name each recalled note's layer and fence its body
REPPL Oct 14, 2026
8dad01d
fix: bound the recall's own root-commit lookup
REPPL Oct 14, 2026
ca44366
fix: keep the rules when the memory recall panics
REPPL Oct 14, 2026
ed0e9d0
fix: bound the live drain's root-commit lookup
REPPL Oct 14, 2026
bffda6f
fix: name a root-lookup timeout in rules --prompt
REPPL Oct 14, 2026
55776c5
chore: capture three follow-ups from the recall provenance round
REPPL Oct 14, 2026
643ad26
docs: say a recalled note is re-sent once when the layer joins its si…
REPPL Oct 14, 2026
aafc5dd
chore: capture the root-commit lookup's cost on very large repositories
REPPL Oct 14, 2026
ad4cd88
Merge main into feat/memory-recall-notes
REPPL Oct 14, 2026
b9cfbec
fix: keep the recall's layer label inside its package
REPPL Oct 14, 2026
4933ca6
Merge main into feat/memory-recall-notes
REPPL Oct 15, 2026
36fda1c
Merge main into feat/memory-recall-notes
REPPL Oct 15, 2026
55ebd1b
Merge main into feat/memory-recall-notes
REPPL Oct 15, 2026
8ff18b2
Merge main into feat/memory-recall-notes
REPPL Oct 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .abcd/development/brief/04-surfaces/37-memory.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# `/abcd:memory` — Keep Memory Notes

`/abcd:memory` keeps memory notes: short lessons an agent writes so that a later
session does not learn them again (itd-2610091918433290,
spc-2610111627213138). It is an agents' command: its page is hidden from a
person's menu, and the binary's help lists the verb with the agents. Recall of a note through the prompt hook is the same
spec's later step; this chapter covers the store and the verb.

## Sub-verbs

> _Machine-checked (`surface_coverage`, spc-27): each row records the verb's
> adr-40 bucket (`lint` / `review` / `audit` / `gate`, or `—` for a
> non-assessment verb) and its existence (`shipped` / `staged`). The existence
> fact is verified against the committed command-tree snapshot in both
> directions. The bucket cell is checked for membership of the closed adr-40
> vocabulary only._

| Verb | Bucket | Status |
|---|---|---|
| `add` | — | shipped |
| `list` | — | shipped |
| `remove` | — | shipped |

## Two layers

A `memory.json` sits beside each `rules.json` layer. The machine's
`~/.abcd.noindex/memory.json` holds the person's own notes; a repository's
`.abcd/memory.json` holds the notes a person promoted into the project. Both
are read with the checks the two rules layers get: the machine file only as a
regular file this account owns that no one else can write, within 256 KiB and
never behind a symlinked `~/.abcd.noindex`; the project's file through the
repository layer's guarded read, from the root the rules loader resolves. A
file that fails a check or does not parse is named with its reason, in tilde
form, and never silently skipped. An absent file costs nothing and nothing is
created by a read.

Each note carries an id minted in the `mem` family, a one-line title of at
most 120 characters, a body of at most 2 KiB, one to twelve recall words or
short phrases, and its stamps: who wrote it (the `Assisted-by:` form
`<Vendor>:<model-version>`), when (the clock's UTC time, never the caller's),
in which session (the host's own id, or null where the host supplies none,
shown as "session unknown"), and its origin (`agent`, or `host-memory-move`
for a lesson moved by hand from the host's own memory files). A personal note
is tied to the repository it was written in by that repository's root commit,
or carries a null root and is recalled in every repository. The project's file
carries no root.

## Writing

Agents write only the machine layer, and only through one gate, before
anything is written:

- the shared scanner, through the memory-page writer's own redactor, refuses
on a degraded scanner and on any span the page writer would have to rewrite
(a secret, a token, an identity or network span, the caller's home path) in
any field;
- the private-names banlist refuses a name this checkout's private layer holds
and, in a linked worktree, the primary checkout's; a store that cannot be
read counts as a match.

The refusal names the field and the kind of span, never the span, and the
agent rewrites the note in its own words: the gate never redacts and writes.
A session id shaped like a link is refused, so no session URL enters a note. A
note written to be recalled everywhere has been judged against the writing
checkout's private names only, since there is no machine-wide banlist layer,
and the write says so. A write with no session id says, on that write, that
the session is recorded as unknown.

A write creates the abcd home if it is missing (refusing a symlinked one),
takes an exclusive lock on that directory, re-reads the file under the lock,
and replaces it atomically at mode 0600, so two agents writing at once both
land. A machine file the read refuses is never overwritten.

Adding takes no path: the text arrives on stdin, so the verb never opens the
host's memory files, on this verb or any other. The page tells the agent to
move a lesson from them only at the person's word and never to edit or delete
the original.

## Reviewing

Until the project's review on the dashboard ships (itd-2610111655362931),
listing and removing are the review. Listing shows every note in both layers
with its body, recall words, stamps and reach (this repository, everywhere,
another repository, or the project's file). Removing takes any personal note
out of the machine layer, whoever wrote it, run by a person or by an agent at
a person's request. A note in the project's file is refused, naming the file:
it is removed by editing that file in a reviewed change. There is no promote
operation.

## Exit codes

`0` done; `1` an I/O failure or a machine file the read refuses, with nothing
written; `2` refused (a field past its limit, the gate, no root commit for a
tied note, an absent note, a note in the project's file), with nothing written.
The JSON output holds on every path: a refusal is the `{"abcd":"error",…}`
envelope on stdout.

<!-- surface-appendix:begin — generated from the command tree by `go generate ./internal/surface/cli`; never edit by hand -->

## Appendix: the shipped surface

_Generated from the command tree; a drift test fails `go test` when this appendix and the tree disagree. It lists flags and sub-verbs only. What each flag means is in the [CLI reference](../../../../docs/reference/cli/commands.md), and exit codes, output fields and behaviour are the prose's to state._

### `abcd memory`

Sub-verbs: `abcd memory add`, `abcd memory list`, `abcd memory remove`.

Flags: none.

### `abcd memory add`

Sub-verbs: none.

| Flag | Type |
|---|---|
| `--by` | string |
| `--everywhere` | bool |
| `--moved-from-host` | bool |
| `--recall` | stringArray |
| `--session` | string |
| `--title` | string |

### `abcd memory list`

Sub-verbs: none.

Flags: none.

### `abcd memory remove`

Sub-verbs: none.

Flags: none.

<!-- surface-appendix:end -->
5 changes: 3 additions & 2 deletions .abcd/development/brief/04-surfaces/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ are wiring rather than user-facing surface are listed separately under
| 35 | `/abcd:drain` | shipped | Work the open ledger unattended: preview with `--dry-run` which open issues a machine may fix alone and in what order, then run it one lane at a time, routing every hand-back by its kind | [`35-drain.md`](35-drain.md) |
| 36 | `/abcd:dashboard` | shipped | Open the project from a phone or another computer on your own Tailscale network, through a server that answers only the devices Tailscale names | [`36-dashboard.md`](36-dashboard.md) |
| 37 | `/abcd:embargo` | shipped | Keep a privately reported security advisory off the public ledger until its fix ships, then close or publish it on your approval of exactly what it shows | [`37-embargo.md`](37-embargo.md) |
| 37 | `/abcd:memory` | shipped | Keep short lessons an agent would otherwise relearn, stamped with who wrote them, when and in which session, and refused when they would hold a secret, a private name or a network address | [`37-memory.md`](37-memory.md) |
| 37 | `/abcd:reserve` | shipped | Reserve an issue or intent on the shared remote before starting it, so another person, computer or hosted session sees it is taken | [`37-reserve.md`](37-reserve.md) |

## How much of this table a machine keeps honest
Expand Down Expand Up @@ -145,7 +146,7 @@ the command's sentence (the section below).
| people | checks | `lab`, `reading` |
| people | portability | `disembark`, `embark` |
| people | release | `launch` |
| agents and hosts | — | `banlist`, `docs`, `drain`, `guard`, `guard hook`, `history`, `ideate record`, `identity`, `implement`, `inbox`, `intent audit ingest`, `intent consistency ingest`, `intent prepass`, `lint`, `mode`, `peers`, `reflect`, `report`, `reserve`, `rules`, `scribe`, `site`, `spec`, `statusline` |
| agents and hosts | — | `banlist`, `docs`, `drain`, `guard`, `guard hook`, `history`, `ideate record`, `identity`, `implement`, `inbox`, `intent audit ingest`, `intent consistency ingest`, `intent prepass`, `lint`, `memory`, `mode`, `peers`, `reflect`, `report`, `reserve`, `rules`, `scribe`, `site`, `spec`, `statusline` |

The placement is presentation. No verb is hidden, renamed, moved or nested by
it, every verb runs the same whichever block lists it, and the group titles
Expand Down Expand Up @@ -342,7 +343,7 @@ documents is then an unknown command (iss-161). One file per verb, directly unde
<!-- index: commands -->
`abcd`, `ahoy`, `banlist`, `build`, `capture`, `dashboard`, `decide`, `disembark`, `docs`, `drain`,
`embargo`, `embark`, `guard`, `history`, `ideate`, `identity`, `implement`, `inbox`,
`intent`, `lab`, `launch`, `library`, `lint`, `mode`, `peers`,
`intent`, `lab`, `launch`, `library`, `lint`, `memory`, `mode`, `peers`,
`reading`, `reflect`, `report`, `reserve`, `scribe`, `site`, `update`.
<!-- /index -->

Expand Down
7 changes: 4 additions & 3 deletions .abcd/development/release-gate/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
".abcd/development/brief/04-surfaces/35-drain.md",
".abcd/development/brief/04-surfaces/36-dashboard.md",
".abcd/development/brief/04-surfaces/37-embargo.md",
".abcd/development/brief/04-surfaces/37-memory.md",
".abcd/development/brief/04-surfaces/37-reserve.md",
".abcd/development/brief/04-surfaces/README.md",
".abcd/development/brief/02-constraints/04-naming.md",
Expand Down Expand Up @@ -85,10 +86,10 @@
"probe": "list skills/ (abcd ships zero skills; the directory is empty or absent)"
}
],
"checkerCount": 43,
"promptHash": "sha256:58c2afdf85d69ff89b535ab77f3b5f11cc077b9fc1831b3dbb75a283bdcc1ec0",
"checkerCount": 44,
"promptHash": "sha256:74469080414a662a08948e3db4f112cf644ab76890f4e53d8b8a814b0230bd1d",
"prompt": {
"context": "Repo root: the current working directory — use repo-relative paths\nthroughout, never absolute local paths. Ground truth is the SHIPPED surface,\nverified empirically: build the binary (make build produces bin/abcd-<goos>-<arch>)\nand run it (`abcd --help` and `abcd <verb> --help`), and list commands/, agents/\nand skills/. abcd currently ships ZERO skills — the whole /abcd: surface is\ncommands under commands/ (abcd, ahoy, banlist, build, capture, dashboard, decide, disembark, docs, drain, embargo, embark, guard,\nhistory, ideate, identity, implement, inbox, intent, lab, launch, library, lint, mode, peers,\nreading, reflect, report, reserve, scribe, site, update)\nand agent prompts under agents/ (cold-reading-comparative, cold-reading-detection, cold-reading-entailment,\ncold-reading-widening, docs-currency-reviewer, graveyard-interpreter,\nintent-auditor, lifeboat-reviewer, planning-interviewer, press-release-composer,\nprinciple-distiller, question-drafter, reflection-composer, release-changelog-composer, ruthless-reviewer, scribe,\nsecurity-reviewer, sota-researcher);\nskills/ is empty or absent. The brief chapters that carry surface claims are\npinned in this manifest's briefDocs: .abcd/development/brief/04-surfaces/*.md\n(the index README included) and the 02-constraints, 05-internals and 06-delivery\nchapters that count or enumerate verbs, sub-verbs, agents, hooks and the plugin\ntree. That list says where to LOOK, not what may be REPORTED: a surface claim\nin any other brief chapter is in scope, and a real surface whose only\ndocumented home lies outside the pinned list is reported with that location.\nReport DISCREPANCIES ONLY — where record and reality disagree, or one side is\nmissing. A brief row explicitly marked staged (its Status column is \"staged\")\n/ probe-only / later-phase is NOT a discrepancy; an unmarked claim about a\nsurface that does not exist IS. Do not fix anything.",
"context": "Repo root: the current working directory — use repo-relative paths\nthroughout, never absolute local paths. Ground truth is the SHIPPED surface,\nverified empirically: build the binary (make build produces bin/abcd-<goos>-<arch>)\nand run it (`abcd --help` and `abcd <verb> --help`), and list commands/, agents/\nand skills/. abcd currently ships ZERO skills — the whole /abcd: surface is\ncommands under commands/ (abcd, ahoy, banlist, build, capture, dashboard, decide, disembark, docs, drain, embargo, embark, guard,\nhistory, ideate, identity, implement, inbox, intent, lab, launch, library, lint, memory, mode, peers,\nreading, reflect, report, reserve, scribe, site, update)\nand agent prompts under agents/ (cold-reading-comparative, cold-reading-detection, cold-reading-entailment,\ncold-reading-widening, docs-currency-reviewer, graveyard-interpreter,\nintent-auditor, lifeboat-reviewer, planning-interviewer, press-release-composer,\nprinciple-distiller, question-drafter, reflection-composer, release-changelog-composer, ruthless-reviewer, scribe,\nsecurity-reviewer, sota-researcher);\nskills/ is empty or absent. The brief chapters that carry surface claims are\npinned in this manifest's briefDocs: .abcd/development/brief/04-surfaces/*.md\n(the index README included) and the 02-constraints, 05-internals and 06-delivery\nchapters that count or enumerate verbs, sub-verbs, agents, hooks and the plugin\ntree. That list says where to LOOK, not what may be REPORTED: a surface claim\nin any other brief chapter is in scope, and a real surface whose only\ndocumented home lies outside the pinned list is reported with that location.\nReport DISCREPANCIES ONLY — where record and reality disagree, or one side is\nmissing. A brief row explicitly marked staged (its Status column is \"staged\")\n/ probe-only / later-phase is NOT a discrepancy; an unmarked claim about a\nsurface that does not exist IS. Do not fix anything.",
"directionA": "Direction A. Read ${doc} fully. Extract every checkable claim\nabout the shipped surface (verbs, sub-verbs, flags, skill names, counts,\nfile layouts, \"abcd ships N ...\" statements) and verify each against\nreality. Return item=\"${doc}\" and the discrepancy list.",
"directionB": "Direction B. The real surface \"${s.name}\" (${s.kind}) exists:\ninspect it (${s.probe}). Search the brief's surface chapters for its\ndocumented home (grep .abcd/development/brief/). If no brief row documents\nit — or the brief documents it under a wrong name/shape — that is a\ndiscrepancy. Return item=\"${s.name}\" and the discrepancy list (empty if\nproperly documented)."
}
Expand Down
2 changes: 1 addition & 1 deletion .abcd/development/release-gate/receipt.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"verificationResult": "PROMOTE",
"judgeModel": "claude-opus-4-8",
"tier": "full",
"manifestHash": "sha256:6607746c5fe41e6eaad90a390b71f1583c6a213ba9f6f395aa557ffffa5c6cc0",
"manifestHash": "sha256:7c24686a047353fd41b487afe1d514ddb6df86cf26640c168bde258af6b5ae1e",
"policy": {
"detector": "iss35-brief-surface-crosscheck",
"version": "1",
Expand Down
79 changes: 78 additions & 1 deletion .abcd/development/release/surface.json
Original file line number Diff line number Diff line change
Expand Up @@ -3214,6 +3214,75 @@
}
]
},
{
"path": "abcd memory",
"hidden": false,
"group": "agents",
"block": "agents",
"sentence": "Count the memory notes in both layers and name any layer it cannot read: Writes nothing; refuses an unknown sub-verb.",
"flags": []
},
{
"path": "abcd memory add",
"hidden": false,
"sentence": "Write one memory note, its body read from stdin: Writes ~/.abcd.noindex/memory.json only; refuses a secret, a network address or a private name.",
"flags": [
{
"name": "by",
"shorthand": "",
"type": "string",
"required": false,
"hidden": false
},
{
"name": "everywhere",
"shorthand": "",
"type": "bool",
"required": false,
"hidden": false
},
{
"name": "moved-from-host",
"shorthand": "",
"type": "bool",
"required": false,
"hidden": false
},
{
"name": "recall",
"shorthand": "",
"type": "stringArray",
"required": false,
"hidden": false
},
{
"name": "session",
"shorthand": "",
"type": "string",
"required": false,
"hidden": false
},
{
"name": "title",
"shorthand": "",
"type": "string",
"required": false,
"hidden": false
}
]
},
{
"path": "abcd memory list",
"hidden": false,
"sentence": "List every memory note with its body, recall words, stamps and reach: Writes nothing; refuses any argument.",
"flags": []
},
{
"path": "abcd memory remove",
"hidden": false,
"sentence": "Remove one personal memory note, whoever wrote it: Writes ~/.abcd.noindex/memory.json only; refuses a note in the project's file.",
"flags": []
},
{
"path": "abcd mode",
"hidden": false,
Expand Down Expand Up @@ -3417,7 +3486,15 @@
"group": "agents",
"block": "agents",
"sentence": "Render the active rule set, or the one domain named: Writes nothing; refuses an unknown domain.",
"flags": []
"flags": [
{
"name": "prompt",
"shorthand": "",
"type": "string",
"required": false,
"hidden": false
}
]
},
{
"path": "abcd scribe",
Expand Down
Loading
Loading