agentleFS
OneDro1dOneDro1d/dark-factory/skills/agent-notepad/SKILL.md

agent-notepad

Skill4 starsChanged 2 months agoApache-2.0View on GitHub

Persistent, per-objective working memory for coding agents — one standalone git "notepad" repo per objective that survives context compaction, spans multiple code repos from a single session, keeps an append-only journal, and mirrors into an episodic memory index. Ships as a Claude Code plugin (hooks + skills + notepad template + a memory adapter). Evolves and supersedes handoff-auto. Use when setting up objective-scoped agent memory, running several long-lived concurrent agent sessions, surviving auto-compaction, enabling `/clear` instead of `/compact`, driving several code repos from one place without `cd`, or installing/uninstalling the notepad hooks. Triggers on "agent notepad", "working memory notepad", "per-objective memory", "scope-init", "survive compaction", "cross-repo agent memory", "supersede handoff-auto".

  • Commits and pushes

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    NOT caught, so never type a credential into a command.

    line 89
  2. Very specific:

    No new DB/service (files are truth) · no live context-% trigger · no automatic /clear (habit) · no shared mutable files · no cross-*group* …

    line 198

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

agent-notepad — persistent, per-objective working memory

What this is

A notepad is one standalone git repo per objective (<group>-<objective>, e.g. proj-arbbot), holding the agent's working memory for that objective. It survives context compaction, keeps history (append-only journal, not a rewrite), spans several code repos from a single session, and syncs across machines. It is the short-term, auto-loaded tier that complements a curated long-term store — and it evolves and supersedes handoff-auto: the same continuity machinery, now objective-scoped instead of cwd-scoped, with history and cross-repo reach. ⚠️ handoff-auto is deliberately UNBACKTICKED and unlinked here: it was REMOVED from this repo on 2026-09-04, and a backticked name reads to tier-check.py as a reference to a component this tier ships — which is what the gate caught. The plugin installer still UNWIRES its old hook entries from an existing settings.json; that is for machines installed before the removal, and it stays.

Product = a Claude Code plugin: hooks + skills + a notepad template + a small Python memory adapter. No binary, no daemon. Full rationale in DESIGN.md.

Why it beats naive auto-handoff

handoff-auto keys by cwd → one rewritten file → parallel sessions clobber, no history, single-repo. A notepad gives zero contention (separate folder + cwd + git repo per objective), append-only history, and cross-repo context driven from one place via absolute paths (never cd). The episodic memory index is exercised at both ends by hooks (write-mirror on Stop, query on digest build), so "memory is actually used" is enforced, not left to agent discretion.

The four layers

LayerAnchored toWhere
Working memory (Notes)the taskthe notepad repo (NOTES.md + journal) — this skill
Per-repo context storecode (file:line)<code-repo>/.claude/context/ — df-context-store
Episodic indexjournals, by prefixthe memory index (MemPalace ref impl) — mirror + digest
Curateddistilled cross-projectthe long-term store (Engram ref) — existing

Notepad layout

proj-arbbot/
  CLAUDE.md            # orientation: objective, repos-in-scope, read-first/dispatch rules
  NOTES.md             # compact working memory, auto-loaded (≤150 lines, redacted)
  SCOPE.md             # charter: objective, done-criteria, repo subset
  DIGEST.md            # standing caveats, hand-maintained, COMMITTED, auto-loaded
  repos.manifest.json  # the CODE repos this notepad drives
  sessions/
    index.json         # session metadata index
    <ISO8601>_<id>.jsonl   # append-only journal, one file per session
  handoffs/            # deliberate structured handoff docs (/handoff → forces push)
  .claude/settings.json  # notepad-scoped hooks incl. the commit gate

The hooks (what runs when)

  • SessionStart — best-effort git pull, then FILE-READS-ONLY inject NOTES.md + DIGEST.md + repos.manifest.json (~1–3 s). Outside a notepad, degrades to handoff-auto behavior. The payload has no size ceiling, and this is load-bearing. It is piped to jq -Rs, never passed as an argv element. Until 2026-09-04 it used jq -n --arg, and Linux caps one argv element at 128 KB (MAX_ARG_STRLEN) while macOS caps only the ~1 MB total — so a 259 KB NOTES.md restored fine on the maintainer's laptop and injected zero bytes on every Linux box in the fleet, with the hook still exiting 0. ⚠️ A restore that emits nothing is indistinguishable from a notepad with nothing to say. That is why the encode failure path now injects a WARNING naming NOTES.md and DIGEST.md instead of staying silent: the hook contract is exit 0 always, so the payload is the only channel that reaches the session — stderr is read by nobody. ⚠️ Do not "fix" a large NOTES.md by capping the payload here. Bloat is a real and separate problem; capping would restore the silent-truncation failure this removed. It also installs the notepad's credential pre-commit when ~/.claude/hooks/secret-guard.py is installed (secret-guard.py --install-precommit <notepad>; idempotent, silent, skipped under AGENT_NOTEPAD_DRY_RUN=1). The pre-commit re-redacts staged sessions/*.jsonl and refuses any other staged credential, naming file, line and rule, never the value. An existing pre-commit is kept as pre-commit.local and still runs after it.
  • Stop — append deterministic journal entries (files touched, commands, a stop marker), upsert sessions/index.json, mirror the journal into the memory index, best-effort git push. ⛔ Every journal entry passes through lib/redact.sh before it is written — one entry per whole command, so a multi-line key block is seen intact. The journal is committed and pushed, and it used to be written unredacted: a credential typed into a command reached a committed journal (2026-09). If the redactor fails to load, the entry text is withheld, never written raw. ⚠️ Redaction is pattern-based. A bare high-entropy secret with no prefix and no keyword is NOT caught, so never type a credential into a command; pass it through an env var or a file.
  • UserPromptSubmit — soft nudge to keep NOTES.md current (backed by the PreCompact floor).
  • PreCompact — deterministic floor: snapshot recent intent into PRECOMPACT.md (gitignored, overwritten each time) + a journal entry before compaction. It used to be appended to the NOTES.md tail, which is exactly the part the restore cuts first.
  • SessionStart, source=compact (since 2026-09-18) — a CONTINUING session gets the working documents, not the cold-start orientation (the compaction summary carries that): the PRECOMPACT.md floor, the newest handoff whole (up to ~7 KB), and NOTES.md. It is split over two wirings of the same script (session-start.sh and session-start.sh --part notes, the second on matcher compact only), because the harness caps each hook at ~10 KiB (re-measured on Claude Code 2.1.276: 9,900 bytes whole, 12,000 externalised; two hooks at 9,900 both whole). Part 2 continues NOTES.md from the exact byte part 1 stopped. With part 1 alone the restore is still complete and says what it did not carry. Pair: Tier 1 hooks/context-budget.py now says checkpoint, then continue instead of hand off and /clear.
  • SessionStart, source=startup|resume|clear — the COLD restore: the orientation block, the newest handoff (≤4,096 bytes), DIGEST.md, the manifest digest, then NOTES.md LAST with whatever the budget left — a reserved floor of 1,200 bytes. ⛔ SPLIT IN TWO SINCE 2026-09-20, and until then this path was the starved one. The compaction restore got its second hook in 2026-09-18 and the cold path did not, so on a real 28,359-byte NOTES.md a compaction restored 45.6% and a /clear restored 4.6% — and /clear is the path this skill itself recommends for a full window. The second wiring is session-start.sh --part cold-notes on matcher startup|resume|clear. ⚠️ It starts at the floor part 1 GUARANTEES, not at where part 1 actually stopped. The two hooks run in PARALLEL, so part 2 cannot observe part 1's cut; part 1's slice is ≥ the 1,200-byte reserve and sometimes much more. So the first bytes of part 2 may REPEAT part 1, and that is the deliberate choice: overlap costs a kilobyte, a gap is NOTES.md that no hook delivered and nothing announced. ⚠️ It is a new --part NAME rather than a widened matcher on --part notes, and that is load-bearing. wire-settings.py merges hooks add-only, keyed by <file> --part <name>: a matcher changed on an entry that is already wired is never applied, and the entry still reads as wired. A widened matcher would have shipped, pinned, installed and done nothing. ⚠️ This wiring has THREE homes — plugin/install.sh, plugin/.claude-plugin/plugin.json and the kit's starter-kit/instance/boot-kit/settings.template.json. A machine gets whichever route installed it, so an entry added to one and forgotten in another is a hook that runs for part of the fleet, with every "is it wired?" check green on both.
  • PreToolUse(Bash) — the commit gate (ships in the notepad's .claude/settings.json, arms only in notepad sessions): blocks agent git -C <code-repo> commits that drift from that repo's df-context-store.

Install

# from the plugin dir; installs to the STABLE path ~/.claude/hooks/agent-notepad/,
# merges the four user-level Notes hooks into ~/.claude/settings.json (idempotent),
# installs this skill, and UNWIRES handoff-auto (files kept — reversible).
plugin/install.sh                 # targets $HOME
plugin/install.sh --target DIR    # targets DIR (used by the test harness against a temp HOME)

The installer backs up settings.json before editing it. To reverse: restore the backup and re-wire handoff-auto. As a Claude Code plugin, the four Notes hooks are declared in plugin/.claude-plugin/plugin.json via ${CLAUDE_PLUGIN_ROOT}.

A day in the life

  1. /scope-init proj-arbbot — creates the notepad repo, interviews for objective + in-scope code repos, warm-starts NOTES.md, derives the memory wing (proj).
  2. SessionStart auto-loads NOTES.md + DIGEST.md; you resume from state instead of re-deriving it. You work across the manifest's repos via absolute paths.
  3. As you go, NOTES.md stays fresh (nudged each turn); durable code-anchored learnings go to each repo's FINDINGS.md/DECISIONS.md (df-context-store), not here.
  4. You git -C /abs/code-repo commit — the commit gate checks it against that repo's store; a drifting commit is blocked with a fix hint, a compliant one passes.
  5. On Stop, the journal appends + mirrors into the memory index; a background digest build queries the proj wing so a sibling objective's recent activity shows up in DIGEST.md.
  6. Context fills → PreCompact writes the floor. You /clear instead of /compact; the next session rehydrates goal + next-action from NOTES.md alone.
  7. At a real milestone, /handoff writes handoffs/<date>-<topic>.md and forces a push.

Routing (where does this note go?)

Ephemeral task progress → NOTES.md. Durable + code-anchored + single-repo → that repo's FINDINGS/DECISIONS. Cross-scope episodic (same prefix) → the memory index (mirror + digest). Deliberate handoff → handoffs/ + remote. Distilled/canonical → the curated store.

Publishing a handoff commits the POINTER with the DOCUMENT

lib/publish-handoff.sh stages NOTES.md and DIGEST.md alongside the handoff file, so the refreshed Notes land in the same commit. This is not tidiness. SessionStart injects NOTES.md and only a pointer to the newest handoff — so a cold reader told the Notes are current has no reason to open the handoff at all. Committing the document without the pointer keeps the artifact and loses the only route to it, which is exactly what this tier exists to prevent. Measured twice on 2026-09-04, on two machines, as M NOTES.md left in the working tree after the publisher had exited 0.

⚠️ It stages those two files and nothing else — deliberately not add -A. A notepad also holds a manifest, a charter and session journals that other machinery writes on its own schedule; sweeping them in publishes a half-written record from a different tier under this one's commit message.

⛔ It refuses a call it does not understand, before writing anything. An empty or whitespace-only body, a body file that does not exist, or any argument beyond <notepad-root> <topic> [body-file] exits 2 with no file and no commit. Measured 2026-09-19: --body-file <f> was read as a body-file name, the body fell back to an empty stdin, and a header-only handoff was committed and pushed with exit 0 — indistinguishable from success.

⚠️ The commit is not best-effort; only the push is. A flaky remote must not block a local checkpoint, but a rejected commit reported as success loses the checkpoint entirely — so a non-zero commit (other than "nothing to commit") is surfaced and returns non-zero.

Relationship to handoff-auto

Evolution, not coexistence: the handoff-auto machinery becomes the Notes tier; its hooks are extended and the commit gate is added. The installer unwires handoff-auto (leaving its files in place, reversible). Outside a notepad, behavior degrades to today's.

Non-goals (v1)

No new DB/service (files are truth) · no live context-% trigger · no automatic /clear (habit) · no shared mutable files · no cross-group auto-sharing · manual human code-commits are not governed (agent commits only).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.