brain-page
This skill is the operating manual for working with a project's brain under the Open Project Brain Standard. The protocol overview lives in the project's root BRAIN.md; here we expand the category taxonomy and the exact command for every operation.
The model is simple: everything goes through the brain CLI.
- Read =
brainread subcommands (brain-dir/list-pages/read-page <id>/read-root <slug>) — location-independent, no need to know where the brain lives. - Write =
brainwrite subcommands. Every mutation (create / update / append / archive / tag / root-page rewrite / reindex) is correct-by-construction, so frontmatter can never be mis-shaped and a compiled_truth rewrite can never silently skip its timeline entry.
NEVER hand-edit any file under the brain directory. All reads and writes MUST go through the
brainCLI. Manual edits are unsupported and illegitimate. Correctness is guaranteed by construction inside the CLI — there is no validator, and nothing at the file layer can catch or undo a bad manual edit, so a hand edit silently breaks the brain's invariants. Always reach for abrainsubcommand instead of an editor.
Invoking the CLI
The CLI ships inside this skill bundle at bin/brain.mjs. It is zero-dependency Node (ESM) — run it directly:
node <this-skill-bundle>/bin/brain.mjs <subcommand> [flags]
Resolve <this-skill-bundle> to the directory this SKILL.md lives in. In the brain.md source repository itself that path is skills/brain-page/bin/brain.mjs; when the skill is installed globally it is wherever setup linked it (e.g. ~/.claude/skills/brain-page/bin/brain.mjs). Run all commands from the project root.
The CLI resolves the brain directory itself: it reads brainRoot from ./.mindmux/preferences.json when present (absolute or relative to the project root), otherwise falls back to ./brain. A missing file, broken JSON, or absent field all fall back silently. Run brain brain-dir to see the resolved directory and its source.
Run node <bundle>/bin/brain.mjs help for the full flag reference.
Read operations
Define a shell function (do not use BRAIN="node <bundle>/bin/brain.mjs"; $BRAIN … — VAR="node x"; $VAR only word-splits in bash; zsh, macOS's default shell, treats node /…/brain.mjs as a single command name and fails with exit 127. A function is portable across bash and zsh, so keep this form):
brain() { node <bundle>/bin/brain.mjs "$@"; }
brain init # ensure BRAIN.md, scaffold empty brain, default-wire CLAUDE.md + AGENTS.md
brain wire # default wire both agent config files (optional --agent subset / all)
brain install-hooks # opt-in Claude Code SessionStart snapshot (project-local .claude/settings.json)
brain uninstall-hooks # remove that SessionStart hook
brain install-hooks --agent codex # Codex project-local hooks.json; startup/resume/compact
brain uninstall-hooks --agent codex # remove only the Codex hook
brain brain-dir # print the resolved brain directory + its source (brainRoot / default)
brain list-pages # list every page: id / title / category / status
brain read-page <id> # print brain/pages/<id>.md
brain read-root <slug> # print a root page brain/<slug>.md
Session-companion discipline (while coding)
- Start of a task: load relevant brain context (
list-pages/read-page/read-root). - When a decision/constraint settles: capture it immediately via the CLI — do not wait to be asked.
- Pure implementation with no new decision: do not write to the brain.
- When overturning a prior conclusion:
update-truthand/orappend-timeline --kind reversal(orarchive-page). - Never hand-edit brain files.
The five page categories
Each page's category must be one of:
| category | boundary (what to write) | typical compiled_truth structure |
|---|---|---|
project |
The state and intent of a self-contained piece of work / sub-project / module — the part that can't be read straight from the code | goal, scope, current status, key constraints |
concept |
A concept / term / mechanism that needs a shared, lasting understanding | definition, why it's this way, boundaries and counter-examples |
decision |
An established judgment and its reasoning (the most common) | what was decided, alternatives, rationale, blast radius |
person |
A relevant person / role, their preferences and responsibilities | who they are, what they care about, collaboration conventions |
reference |
An external resource / object of analysis worth keeping | what it is, key takeaways, links, implications for this project |
When in doubt, most knowledge lands in decision or concept.
Page id conventions
- Use kebab-case for
id, semantically clear, e.g.markdown-over-sqlite,auth-flow. - The
idmust equal the filename (brain/pages/<id>.md, without the extension). The CLI enforces this. - Once an id is referenced via
[[ ]]it should stay stable; renaming means updating every reference and re-runninglint-links.
Write operations (every one is a CLI subcommand)
Assume the brain shell function defined above (brain() { node <bundle>/bin/brain.mjs "$@"; }) and that you are in the project root.
Create a page
brain create-page --id <kebab-id> --category <category> --title "<one-line title>" \
[--tags a,b] [--status active] [--source "<where this came from>"]
Generates brain/pages/<id>.md from the template (frontmatter + <!-- compiled_truth --> + a visible ## Timeline seeded with one kind: decision creation entry), then reindexes. Fill in the real compiled_truth afterwards via update-truth.
Rewrite compiled_truth (atomic with its timeline entry)
echo "<new compiled_truth markdown>" | brain update-truth --id <id> \
--summary "<what changed and why>" [--source "<source>"]
Reads the new compiled_truth from stdin, rewrites the compiled_truth section (canonical marker: <!-- compiled_truth -->), and in the same atomic write appends a kind: decision entry to the visible ## Timeline section and bumps updated. Changing the understanding and recording why are inseparable — you cannot do one without the other.
Append a timeline entry (append-only)
brain append-timeline --id <id> --kind <decision|evidence|reversal|note> \
--summary "<one line>" [--source "<source>"] [--affects a,b]
Appends to the end of the timeline only; existing entries are never touched.
Archive a page
brain archive-page --id <id> [--reversal-summary "<why it was overturned>"]
Sets status: archived, optionally appends a kind: reversal entry, then reindexes.
Change tags
brain set-tags --id <id> --tags a,b,c
Rewrite a root page
echo "<root page body markdown>" | brain update-root <slug>
<slug> must be one of the six fixed root pages: background / architecture / flow / mindmap / stack / roadmap. The CLI validates the slug, rewrites the whole brain/<slug>.md, regenerates the frontmatter, and guarantees the canonical H1 heading is present. Root pages have no timeline — their history lives in git. Lean on ```mermaid blocks (graph / sequenceDiagram / mindmap / gantt) to keep them visual.
Index / checks
brain reindex # rebuild brain/index.md (also run automatically by the write commands above)
brain lint-links # verify current [[page-id]] links resolve
lint-links treats Page compiled_truth as the current knowledge graph and root page bodies as current root knowledge. It intentionally does not lint Page timeline entries, because timeline is append-only provenance and may contain historical syntax examples or obsolete references.
Why there is no validator
There is deliberately no validate command. Because every write goes through this CLI, the failure modes a validator used to guard are structurally impossible: frontmatter is always CLI-generated, and update-truth rewrites compiled_truth and appends its timeline entry in one atomic write. The guarantee holds only as long as you never hand-edit a brain file — there is nothing to catch a manual edit afterwards. reindex and lint-links remain as optional hygiene, not load-bearing gates.
Cross-page references
- Always reference another page with
[[page-id]](the bare id, without brackets, is for filenames / CLI flags). - After adding references, run
lint-linksto confirm nothing is broken. - Do not wrap root-page slugs, file paths, ordinary words, or uncertain entities in
[[ ]].
Codex context recovery
Hook support targets Codex CLI 0.153.4+, Node 18+, and POSIX sh/awk. Trust the
project and review the installed hook with /hooks; installation does not enable
features or grant trust. Uninstall before moving a project, then reinstall.
Codex snapshots contain at most 8 KiB of complete listing rows; run list-pages
for the full index and read-page <id> for details. Hook failures are non-blocking.
With Astra native notes/history available, keep relevant page IDs and unresolved
task state in notes, use history for earlier task evidence, and re-read current
brain pages after context rollover. Do not copy task history into the brain.
Experimental context management is separately enabled with
features.context_management.experimental_mode = true and a new task; check
current eligibility.
Automatic experimental-rollover delivery is not yet runtime-verified; explicit CLI
reads remain the fallback. Hooks never write native notes or brain pages.