wendkeep
v0.90.0
Published
Vault-first persistent memory for AI coding agents, with an optional profile-aware governance runtime: OFF, FLOW, GUIDE, GOVERN, or ASSURE. Local-first and agent-agnostic (Claude Code, Codex, Cursor…).
Maintainers
Readme
wendkeep
Português · English
Your AI coding agent forgets every session. wendkeep makes it remember — in the Obsidian vault you already use.
In the graph: 🔵 session · 🟣 decision · 🔴 bug · 🟢 learning · 🟡 change — every note, backlinked.
Persistent memory for AI coding agents, built on your Obsidian vault. Every Claude Code and Codex session is captured turn by turn into local Markdown — init wires both (Codex asks you to approve its hooks once; import backfills past sessions either way) — with token/cost tracking and automatically extracted decisions, bugs, and learnings. That always-on plane is Keep Core. On top of it, Wend Runtime provides a native, zero-dependency lifecycle (spec → change → TDD → sensor-gated archive), selected through the OFF, FLOW, GUIDE, GOVERN, and ASSURE Operating Profiles. 100% local, open-core.
When projecting sessions, trailing internal metadata is removed only from assistant responses; user reports remain intact. XML-like tags are written as escaped text so Obsidian Reading view does not interpret them as HTML.
The runtime is separated into thirteen physical boundaries — cli, commit, contracts,
evidence, harness, integrations, mcp, migrations, observer, pi, sync, vault, and worktrees — without
fragmenting installation. The root package publishes exactly wendkeep/commit,
wendkeep/contracts, wendkeep/evidence, wendkeep/harness, wendkeep/mcp,
wendkeep/migrations, wendkeep/observer, wendkeep/sync, wendkeep/vault, and
wendkeep/worktrees. CLI, Pi, and Integrations remain private, and no @wendkeep/* workspace is
an independent package. Historical facades preserve identity throughout 0.x. See the
modular architecture,
compatibility, migrations, and
support policy.
In the 0.66 Integrations Kernel phase, packages/integrations/src/ becomes the canonical
authority for the hook catalog and projection, the envelope/provider, transcript content and usage
filters and parsers, and session identity. These rules are pure: stdin/stdout, environment,
filesystem, Vault, and registry effects remain in the historical facades. MCP and Integrations are
sibling adapters with no dependency between them, and the direction remains
cli/mcp/integrations/pi → Harness → Vault. Hooks, sessions, paths, configuration, and schemas
remain equivalent; the private @wendkeep/integrations workspace stays inside the single
published wendkeep package without a public subpath. Consumers use the supported historical
facades, while deep packages/ imports remain blocked.
The native MCP workspace now serves semantic project, context, memory, change, spec, task,
handoff, evidence, and Observer tools through wendkeep mcp serve. A versioned, verified catalog
declares every effect/capability; known reads skip the mutation gate, while writes require a
capability, session, active context, lease, and reason. Stable schemas, pagination, byte budgets,
timeouts, cancellation, redaction, and payload-free local audit bound the surface. init preserves
the .mcp.json merge while invoking the installed package through
npx --no-install wendkeep mcp serve—with no dynamic @latest dependency or arbitrary filesystem
access. Stdio may start outside a bound project; without --vault, each call resolves and audits
only its declared project_root. The workspace remains private inside the single wendkeep package.
In the 0.64 CLI Runtime phase, packages/cli/src/index.mjs owns help, version reporting, Vault
selection, error presentation, and lazy dispatch. bin/wendkeep.mjs is reduced to the shebang and
a runCli() invocation. The tarball remains a single artifact and proves both aliases in an
isolated consumer; there is no public wendkeep/cli subpath in this phase.
In the 0.63 Harness FLOW Store phase, packages/vault/src/locale.mjs becomes the canonical
source for Vault locale and taxonomy, while packages/harness/src/flow-store.mjs becomes the
canonical durable FLOW store. The historical hooks/locale.mjs and
hooks/vault-runtime-store.mjs facades preserve export identity. Harness depends only on Vault's
public index — never the reverse — with no migration of paths, schemas, or locks; the tarball
remains a single artifact published by the root wendkeep package.
Under multiprocess contention, transient release of the public lock and its owner/lease metadata
is revalidated with a bounded budget/deadline, including final cleanup; junctions, reparse points,
dangling locks, and non-transient errors are still rejected before any write.
npm i -D wendkeep && npx wendkeep init # captures from the next session on
npx wendkeep import # backfill past Claude + Codex sessions▶ Interactive demo: docs/index.html — a self-contained page with the live knowledge‑graph hero. It lives in the GitHub repo (the npm tarball ships only the runtime), so clone or download docs/ to open it locally or serve it on any static host. The image above is a static render of it.
From one real production vault (
npx wendkeep stats): 308 sessions · 1,696 prompts · $4,836 captured across 46 active days (Jan–Jul 2026) · 15 models — every one a note in the graph.
Extracted from a system in daily production use: the capture engine, cost tracking and graph wiring are battle‑tested; the cross‑platform installer (
wendkeep init) and the native change loop are the newer parts. Seedocs/for the project's strategy and decision log.
The problem: the context dies when the window closes
Decisions, dead ends, the reason you chose X over Y — gone next session. The pieces to fix that exist but are scattered (qmd‑sessions, memsearch, Nexus, hand‑written hooks). wendkeep ships local capture plus an optional Docker Observer that keeps complete memory browsable without depending on Obsidian.
| | |
|---|---|
| Capture — every turn, on disk | SessionStart / Stop hooks write each session to a dated Markdown note: prompts, iterations, files touched, wikilinks. |
| Derive — decisions, bugs, learnings | Pulled from the transcript into their own notes, backlinked to the session. Your history becomes navigable, not archival. |
| Recall — injected back | Canonical CORE + operational SHARED_MEMORY enter on startup, /clear, and /compact; on every prompt, the local chunk index selects a few passages with source, authority, and validity under an explicit budget. |
| Cost — what it all cost | Per‑model, cache‑aware token pricing per session — plus cost --trend with a run‑rate projection across the whole vault; research previews without a final rate remain unestimated. |
| Multi‑agent — one vault, both agents | init wires the session hooks into .claude/settings.json and .codex/hooks.json, and every note is tagged with the agent that wrote it: Claude Code is detected from its environment, anything else is recorded as Codex. One shared graph, whichever agent you are in. |
| Local‑first — no cloud, no account | Everything is plain Markdown on your disk. The native MCP queries local semantic state and gates writes by capability/context/lease. |
| Local Observer — many projects, one view | wendkeep observer keeps documents, FTS5 chunks, sessions, agents, tokens, costs, calls, and transcripts in SQLite. Identities and foreign keys are project-scoped; each event is atomic. Hooks publish only what changed; idempotent gzip batches retry transient socket resets once; observer reconcile --url ignores the incremental cursor to regenerate the complete projection while preserving local/remote revision baselines. |
During historical migration, the Observer preserves differences between frontmatter totals and the
ledger as explicit reconciliation rows, and disambiguates duplicate session_id values per file
without inventing calls.
Requirements
The Codex scope guard treats commit, push, pull, merge, publish, and destructive
operations as independent capabilities, including inside compound commands.
- Node.js ≥ 18
- An AI coding agent with hooks.
initwires Claude Code and Codex automatically — Codex gets twelve compatible hooks, including per-prompt recall and the scopedPreToolUseguard, and enumerates them untrusted, so approve its “Hooks need review” prompt once at startup (see Notes & roadmap) - Obsidian (to view the graph) — optional but the point
Install & set up
# in your project
npm install --save-dev wendkeep # or: npm install -g wendkeep
npx wendkeep initwendkeep init is interactive and idempotent. It will:
Create the vault folder taxonomy and a templated
README.md(default vault:<project>/.<project-name>-vault, e.g..MyApp-vault; override with--vault).Write a provider-neutral
.wendkeep.jsonbinding at the project root and a matching.brain/PROJECT.jsonmarker in the vault, then merge the session hooks into.claude/settings.json. The binding is provider-neutral by design: any agent resolves the same vault from its sessioncwd, with no machine-global environment variable. Older registrations already in.claude/settings.jsonare adopted automatically.Wire the Codex hooks in
.codex/hooks.json— twelve compatible entries:brain-inject+session-start+observer-publishonSessionStart,session-ensure+evidence-context+change-contextonUserPromptSubmit,session-stop+observer-publish+change-nagonStop,subagent-stop+observer-publishonSubagentStop, andchange-guardonPreToolUseforBash,exec_command,apply_patch, and mutable MCP tools, always in thenpx wendkeep hook <name>form. For the Observer,SessionStartonly drains the outbox,Stopenqueues the changed session, andSubagentStopenqueues only the affected transcript; full scanning is explicit throughobserver reconcile. When the host omitswork_session_id,session-startandsession-ensurederive it from the canonicalsession_id, preserving an explicit handoff and the already registered value first. The guard accepts object, raw-string, and argv Codex payloads; before a mutation it compares the session with the project, Git root, remote, branch, and worktree, denying missing or divergent targets. A rawgit checkout/switchbranch transition is denied before it can strand the session; usewendkeep context switch <branch> [--create], which moves Git and the causal scope together in the same worktree with an audited revision and rollback. If a divergence is already quarantined,context status --session <id>inventories sanitizedreserved/observedcandidates;context recover --session <id> --select <reserved|observed> --revision <n> --reason <text>requires an explicit choice, CAS, and current-checkout proof, failing closed before clearing the conflict if revalidation changes.doctordiagnoses orphaned active contexts, removed worktrees, and expiredrequest-stopleases without writing;context repair --key <key> --revision <n> --reason <text> --session <id>revalidates under lock, closes only the ownerless/removed context or expires only its lease, while preserving the record and all historical memory. The change lifecycle usesactive_contexts, identified byrepository_id+worktree_id+work_session_id; two matching sessions fail with ambiguity instead of selecting silently,CURRENT_CHANGE.mdis only a derived projection for one unambiguous context, and migration never invents a worktree or session identity. The other four stay out because Codex offers no equivalent payload, tool, or event:change-warn(no reliabletool_input.file_path),plan-capture(noExitPlanMode),decision-capture(AskUserQuestionis Claude-only), andtask-log(TaskCompletedis not in Codex's event enum). Codex scope blocks usepermissionDecision: "deny";askis never emitted inPreToolUse. The merge remains non-destructive, preserves third-party hooks, and migrates legacytimeouttotimeoutSec. Codex enumerates every hook as untrusted and runs none until you approve the “Hooks need review” prompt at startup —initcannot pre-approve them. Onceactive_contextsis initialized,brain-injectandchange-contextmark only the causal context's change as current; the backlog remains global, and an empty or ambiguous store never revivesCURRENT_CHANGE.md.Add the native semantic
wendkeep-vaultMCP server to.mcp.json. It offers bounded reads and capability-gated writes without arbitrary filesystem access or an@latestdownload. Skip with--no-mcp. (--no-mcpskips only wendkeep's own MCP; companion MCPs still follow--companions.)Offer to pin companion plugins/MCP (multi-choice; none pre-checked — wendkeep is a neutral harness and presumes no third-party plugin). Each is wired the most agent-agnostic way it supports:
context-mode— context optimizer + FTS5 memory, wired as a Claude Code plugin. It ships its own MCP server, so wendkeep deliberately adds no.mcp.jsonentry (registering both cold-started two servers at once). On non-Claude agents, add the MCP by hand:npx -y context-mode.understand-anything— project domain graph, via aunderstand-injectSessionStart hook that injects the graph when generated.caveman— token-compression mode; runs its own cross-agent installer on non-Claude agents.dotcontext— legacy, not recommended, and hidden from the picker. wendkeep's native a2 loop (change/verify/ gate) already does its job, so installing it duplicates the harness. Reachable only via an explicit--companions dotcontextfor anyone already invested (tune with--dotcontext-mcp/--dotcontext-hooks).
Control with
--companions <csv>or--no-companions. The Claude Code plugin layer (extraKnownMarketplaces+enabledPlugins) is wired as a bonus where the companion has one.Install a color system into the vault's
.obsidian/: a CSS snippet that accents notes by type (session/decision/bug/learning, via thecssclassesthe hooks emit) plus graph color groups by folder. Non-destructive merge intoappearance.json/graph.json; skip with--no-colors. Re-apply it any time on an existing vault withwendkeep theme sync— Obsidian ownsgraph.jsonand can drop the color groups (a grey graph); the re-sync restores them without a full re-init.Seed Shared Project Memory v2 without overwriting existing artifacts:
.brain/CORE.md,.brain/SHARED_MEMORY.md,.brain/MEMORY_EVENTS.jsonl,.brain/MEMORY_CANDIDATES.jsonl, and.brain/COMPACTION_PROTOCOL.md. The durable outbox appears under.brain/memory-outbox/;EVIDENCE_INDEX.jsonlis rebuilt locally from chunks whileDIGEST.md/index.jsonlremain compatible. Everything stays in the vault.Seed the definitions + skills layer:
.brain/agents/+.brain/skills/(versioned source of truth), including the native process skillswk-workflow/wk-tdd/wk-debugging/wk-brainstorming/wk-planning/wk-verify(some ship templates — e.g.wk-verify'sverdict-template.json+ reviewer prompt).initrunswendkeep sync-defsfor you, delivering the skills to.claude/skills/and.agents/skills/, and the agent definitions (.brain/agents/*.toml) to.codex/agents/, plus a managed section inAGENTS.mdthat indexes the skills for Codex;sync-defs --checkdetects stale copies (re-runsync-defsafter editing.brain).Seed the change/spec lifecycle: the
07-Specs/+08-Mudanças/folders and a nativewendkeep.sensors.json— critical memory validation/health sensors plus one for each oftypecheck/test/lint/buildfound in yourpackage.json.memory-healthblocks delivery on corruption or projection divergence; semantic conflicts degrade only the affected keys and await curation. Pending outbox events and ordinary candidates are warnings. Add sensors withwendkeep sensors add. Driveswendkeep change/wendkeep verify— see Change lifecycle below.
npx wendkeep init --vault "~/vaults/work" --project . --yes # non-interactive (no companions unless you ask)
npx wendkeep init --companions "context-mode,understand-anything" --yes
npx wendkeep init --no-companions --no-mcp --yes # zero companions, no wendkeep MCPinit options
| Flag | What it does |
|---|---|
| --vault <path> | Vault folder. Default <project>/.<project-name>-vault; interactive init asks. Point it at an existing vault to install into it. |
| --project <path> | Project root to wire (default: current directory). |
| --locale <pt-BR\|en> | Vault language — folder names, scaffold, skills. Interactive init asks; locked at init. |
| --companions <csv> | Companions to pin: context-mode,caveman,understand-anything (default: none — opt in explicitly; dotcontext is legacy). |
| --no-companions | Pin no companions. |
| --no-mcp | Skip wendkeep's own vault MCP (wendkeep-vault). Companion MCPs still follow --companions. |
| --no-colors | Skip the Obsidian color system (.obsidian snippet + graph groups). |
| --vscode-worktree-tasks | Create a local, Git-excluded .vscode/tasks.json for creating/listing/opening/finishing worktrees; never overwrites an existing file. Also accepted by sync. |
| --git-commit-hooks | Opt in to copying commit hooks into .githooks/ and setting local core.hooksPath; custom hooks are preserved without --force. |
| --yes, -y | Non-interactive; accept defaults (skips the language / vault / companion prompts). |
| --force | Overwrite existing wendkeep config blocks. |
Then open the vault in Obsidian, send a test prompt in your agent, and confirm a note appears under 02-Sessões/… (or 02-Sessions/… for an en vault).
Project isolation
Each project owns a .wendkeep.json containing a stable projectId and its vault path.
Relative paths (for example .NutriGymBrain) are resolved from the project root; absolute
paths are also supported. Hooks search upward from the agent's cwd, so nested packages use
the nearest binding. The vault carries the same identity in .brain/PROJECT.json; a mismatch
is rejected before any session is written. If no binding exists, hooks fail closed and never
create the historical ~/wendkeep-vault fallback.
In linked worktrees, profile use and profile status resolve the main worktree's canonical
binding through the shared Git registry; the persistent selection applies project-wide without
rewriting the current worktree's versioned .wendkeep.json.
OBSIDIAN_VAULT_PATH remains only as legacy/manual CLI compatibility. It is not used to
route automatic Codex or Claude hooks and a project-local binding overrides an inherited
machine value.
Updating
The hooks live inside the installed package, so upgrading means installing the new version
and re-wiring the project. sync does the three steps (init → sync-defs → doctor) in
one command:
npm install --save-dev wendkeep@latest && npx --no-install wendkeep sync --project . --yessync lets its own init stage validate or rebuild the binding before resolving the Vault for
later stages. An invalid .wendkeep.json stops at init without falling back to an inherited global Vault.
The install stays outside sync on purpose: a running process cannot replace itself and
keep going — the code in memory would still be the old one.
In WendKeep's own development checkout, do not install wendkeep in devDependencies. Use
node ./bin/wendkeep.mjs sync --project . --yes: the installer recognizes the self-checkout and
keeps hooks on the working tree without duplicating consumer npx commands.
In a pnpm monorepo the install command differs (npm in a pnpm repo fails with
Cannot read properties of null (reading 'matches')). Resolve the published version first and
reuse exactly the value returned:
$version = pnpm view wendkeep version
pnpm add -D -w "wendkeep@$version" --config.minimumReleaseAge=0
pnpm install --update-checksums --config.minimumReleaseAge=0
pnpm exec wendkeep sync --project . --yesDo not ask pnpm for
wendkeep@latest. pnpm 11 ignores packages published in the last 24h by default (minimumReleaseAge, a supply-chain guard) — and it does not complain: it installs the previous version, exits 0, and the only hint is a quiet(X.Y.Z is available)in the output. You end up on the old version thinking you upgraded. Check withpnpm exec wendkeep --version.Do not edit only the version or integrity in
pnpm-lock.yaml.pnpm addandpnpm install --update-checksumsmust recalculate the complete entry. If the lock is already inconsistent andERR_PNPM_TARBALL_INTEGRITYappears, prune the local store and repeat:pnpm store prune pnpm install --update-checksums --config.minimumReleaseAge=0After installing, record the exception in
pnpm-workspace.yaml— pnpm does not write that line for you:minimumReleaseAgeExclude: - wendkeep@<the version returned by pnpm view>Without it, CI's
pnpm installfails withERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATIONuntil the version turns 24h old.
Restart Codex and Claude Code afterwards — the generated skills are read at startup.
sync reseeds the wk-* skills from the installed version's seeds. That is not an
extra: they are package artifacts, and merely copying .brain/skills would propagate the
previous version's content while stamping the new version into the metadata — doctor would
stop reporting defs stale without a single skill having been updated. If you hand-edited a
wk-*, that edit is overwritten; your own customisation belongs in a skill of your own,
which the reseed never touches.
Features by group
The README is the map; the guides provide syntax, options, exit codes, examples, and diagnosis.
| Group | Use it for | Detailed guide |
|---|---|---|
| Installation and updates | init, sync, companions, and the first project↔vault binding | Installation and first use |
| Managed worktrees | worktree create/list/status/open/finish/cleanup/remove/prune, merge proof, preflight, crash-safe cleanup/common gate, and receipts | Managed worktrees |
| Active context | active_contexts by repository_id/worktree_id/work_session_id, causal transition, quarantine, and explicit recovery | Active context |
| Portable state | portable status/export/import/diff, authored/runtime boundary, redaction, and the active-work snapshot | Portable state |
| Local-first sync | sync status/push/pull/conflicts/resolve, revision/CAS, outbox, leases, and explicit conflicts | Sync protocol |
| Native MCP | semantic tools, effects/capabilities, stdio, schemas, pagination, budgets, audit, and client configuration | Native MCP |
| Evidence recall and embeddings | cursor/byte budgets, bounded lexical/FTS sidecars, and an optional local plugin contract that is off by default | Optional embedding plugin |
| Host capabilities | versioned lifecycle/effect matrix, degraded mode, human waivers, and evidence/Observer coverage | Host capabilities |
| Ecosystem bridges | optional Spec Kit/Superpowers adapters, canonical baseline, single ownership, drift, and Git/CI/Envelope proof with an index/worktree-identical manifest | Ecosystem bridges |
| Operating profiles | profile, flow, always-on Keep Core, and Wend Runtime governance | Operating profiles |
| Changes and verification | change, specs, sensors, TDD, evidence, Task Contracts, and archive | Changes and verification |
| Shared memory | CORE, SHARED, status, validation, repair, and curation | Memory |
| Sessions and import | causal hooks, observability reconciliation, and Claude/Codex backfill | Sessions and import |
| Notes and knowledge | BUG/APR/ADR, repairs, renumbering, lessons, and dashboard | Notes and knowledge |
| Costs and observability | safe dry-run, tri-state, aggregation, trends, and historical rebuild | Costs and observability |
| Maintenance and diagnostics | doctor, frontier/manifest freshness, drift, version, and help | Maintenance and diagnostics |
| Evidence-based commits | wendkeep commit, canonically ordered derived tasks, tests only from execution bound to the exact SHA, opt-in Git hooks, privacy, and remote range gate | Commits |
| Local Observer | observer serve, scoped/expiring hash-only bootstrap, incremental publishing with canonical timestamps, reconcile, policy, stable structural identities, content-bound hashes, deletion-safe capture, required encryption, retention, purge, and multi-project index | Local Observer · Security |
Operations that deserve step-by-step guidance: verify and exits 0/1/2, causal TDD attestation, legacy-memory migration, and safe retroactive import.
Operating Profiles — Keep Core is always active
Keep Core is always active: the Vault, sessions, identity, CORE/SHARED, lessons, costs,
and persistence do not turn off with the harness. Wend Runtime controls only the automatic
governance layer. Even under OFF, explicit WendKeep commands remain available; invoking one is a
deliberate opt-in and runs that command's own validations:
| Profile | Route | Use |
|---|---|---|
| OFF | LLM-native harness | No Wend router, skill gate, or gates; explicit selection only. |
| FLOW | E → V | Microcontract with Git baseline, allowlist, sensor, and receipt, without a change. |
| GUIDE | P → E → V | Compact change; no automatic design/spec/ADR when contract impact is none. |
| GOVERN | P → R → E → V | Current a2 loop and compatible fallback. |
| ASSURE | P → R → E → V → C | Governance with confirmation and handoff. |
A route is a sequence of work stages, not a list of command names:
P= Plan/Propose — understand the request, bound the scope, and record the approach when a change is needed.R= Review — inspect the proposal/design before execution; this is the formal a2-loop review.E= Execute — edit the permitted code or artifacts.V= Validate — run tests, sensors, and checks and record evidence.C= Confirm/hand off — obtain explicit confirmation and complete the handoff.
Thus, P → R → E → V means “plan/propose, review, execute, and validate”. FLOW starts at the
execution/validation microcontract; OFF applies no automatic Wend route and returns process
ownership to the native LLM harness.
Who selects the profile, and for how long?
The LLM harness (Codex, Claude, or another agent) classifies the current implementation and can
record a temporary choice with wendkeep profile route. Wend Runtime does not interpret prompt
text or use diff size, heuristics, or environment variables: it validates the choice, applies the
route in hooks, and expires the lease when the request ends. With no recorded route, the configured
base profile remains effective.
Resolution follows this order:
- a valid lease for the current request in
SESSION_REGISTRY.json; - a persistent session override in that registry;
harness.profilein the project's.wendkeep.json;GOVERNwhen no valid setting exists.
Without --session, profile use changes the project default for conversations/hooks that do
not have a session override. With --session <id>, it changes only that session and leaves the
project default untouched. Therefore, profile use OFF without --session is not an isolated
test: it writes the project binding and can be shared if .wendkeep.json is committed.
profile route is different: it requires --session and --reason, accepts only FLOW, GUIDE,
GOVERN, or ASSURE, does not rewrite the project/persistent override, and applies only to the
current causal prompt. An accepted Stop consumes the lease; if the process dies first, the next
prompt advances the sequence and makes the old lease ineffective. OFF is never selected
automatically.
npx wendkeep profile status
npx wendkeep profile use GUIDE # project default
npx wendkeep profile use FLOW --session <id> # one session only
npx wendkeep profile route FLOW --session <id> --reason "local fix" # current request
npx wendkeep profile status --session <id> # session-effective profileIn human status --session output, base=<profile>/<source> and lease=<state> accompany the
effective profile; --json exposes the same data as base_profile, base_source, and
task_lease. profile route only accepts a session after UserPromptSubmit has recorded a
positive causal turn and sequence that agree in the registry.
Which profile fits a simple request?
“Small” describes size, not risk. The harness uses this matrix to choose and record a temporary route; semantic inference remains in the agent, not Wend Runtime:
| Situation | Work kind | Suggested profile | New change |
|---|---|---|---|
| Question, inspection, or diagnosis with no mutation | inspection | No transition | No |
| Local, reversible fix with an allowlist and no contract/spec change | maintenance | FLOW (E → V) | No |
| Small behavior change without formal review | implementation | GUIDE (P → E → V) | Yes, compact |
| Public contract, security, schema, dependency, CI/release workflow, or policy | implementation | GOVERN/ASSURE | Yes |
| Merge, push, tag, or publication of approved behavior | delivery | ASSURE | No |
| Operational recovery without code/config correction | recovery | FLOW/ASSURE | No |
Work kind, profile, contract impact, and operational risk are independent dimensions. A
delivery records authorized capabilities and an append-only receipt without creating a change,
spec, or ADR. If delivery requires a code/config edit, it pauses and work returns to
implementation:
In multi-context Vaults, active_contexts[].delivery_id is authoritative and --session <id>
selects the caller explicitly. CURRENT_DELIVERY is only a derived projection when there is one
single, unambiguous context; hooks consult only the causal delivery, and an ID from another context
fails with WENDKEEP_DELIVERY_CONTEXT_MISMATCH.
operating_profile_task belongs to the causal work session's active context. profile route and
profile status --session <id>, hooks, and Stop read/consume only that lease; a temporary route
never crosses into a sibling. Legacy session fallback exists only without active_contexts;
an initialized registry never copies a global authorization without proven identity.
npx wendkeep delivery start release-0-74-0 --allow git:merge --allow git:push --allow publish --source-change <slug> --source-commit <sha> --session <id>
npx wendkeep delivery status release-0-74-0 --session <id>
npx wendkeep delivery finish release-0-74-0 --target origin/main --ci-url <url> --version 0.74.0 --npm-integrity <sha512> --release-url <url> --session <id>For git:merge and git:push capabilities, delivery finish requires
--target <remote>/<branch> (for example, --target origin/main). delivery start binds the
origin remote to the expected repository; during finish, the target is resolved again with
git ls-remote. If the target cannot be resolved or the origin/repository binding diverges, the
delivery is blocked before provenance adapters run.
The provenance gate re-derives authority against the current subject before archive, delivery,
release, or cleanup. Its single taxonomy is verified, reported, legacy-unbound, stale,
conflict, and unproven; only verified authorizes required proof. Evidence, a verdict, or a
receipt captured before amend/rebase, from another branch/worktree/session, or backed only by a
reported/offline external claim fails closed with objective recovery. New receipts use schema v2,
previous_hash, receipt_hash, and a separate checkpoint to detect tampering and truncation.
Stable codes are WENDKEEP_PROVENANCE_GATE_BLOCKED, WENDKEEP_RECEIPT_LEDGER_BUSY,
WENDKEEP_RECEIPT_LEDGER_CONFLICT, WENDKEEP_RECEIPT_LEDGER_CORRUPT, and
WENDKEEP_RECEIPT_LEDGER_TRUNCATED. Safe recovery reads state, reasonCodes, diagnostics,
and repair.command in --json output, runs npx --no-install wendkeep verify --deep --json or
the indicated status command, and recaptures proof; it never prints raw stderr, tokens, private URLs,
or Vault paths, and never edits the ledger/checkpoint.
If the harness does not record a lease, a small fix remains under the configured profile —
GOVERN by default. OFF does not mean “simple task”: it is a persistent human choice that hands
governance to the native harness. The LLM may temporarily elevate an OFF base to a Wend route,
but it can never select OFF on its own.
A corrupt binding never selects OFF: with one unambiguous explicit or legacy Vault, Keep Core
remains active under GOVERN, the error stays visible, and mutation guards fail closed. Additional
roots that FLOW must protect can be declared as project-relative paths under
harness.flow.protectedRoots in .wendkeep.json; any change below them requires promotion.
Invalid local config, marker, or identity never silently falls back to a parent/global Vault.
wendkeep profile status/use/route makes the choice observable; wendkeep flow
start/finish/promote handles local adjustments without manufacturing an ADR and fails closed on
physical escapes, Git metadata/hidden flags, mutating sensors, protected surfaces, or incomplete
session projection. Bounded no-follow discovery sees empty/ignored protected aliases; Vault writes
and owner+lease locks validate physical topology. Concurrent promotion elects one owner and supports
retry through --change-slug. Read the complete
Operating profiles guide.
wendkeep doctor separates structural errors, workflow attention, repairable debt, and semantic
ambiguity. Use --scope core for Keep Core health, --scope runtime for governance, and
--strict in CI/release; wendkeep sync validates Core only so work in progress does not become
an installation failure.
To resume a change in another clone without tracking transcripts, tokens, paths, leases, or outboxes,
wendkeep portable export creates .wendkeep/portable/state.json with sanitized authored
CORE/ADRs/specs/deltas and a compact active-work projection. portable import validates project,
hashes, revisions, and paths before writing; stale or conflicting state never silently replaces local
state. Git tracking is opt-in and the command never adds files automatically.
Shared Project Memory v2
Hot memory now separates human authorship, operational state, and evidence:
CORE.mdis canonical. It is the short, hand-curated nucleus for durable preferences, active patterns, and open issues; no projector may infer or overwrite it.SHARED_MEMORY.mdis bounded generated operational state. TheStophook turns the session handoff into sanitized events; the projector deterministically reduces the ledger and publishes a verifiable revision, cursor, and hash. Admission prioritizes critical operational state and never publishes beyond 48 lines/6 KiB; omitted events remain in the append-only authority and surface only as verifiable counts. Facts areverifiedonly with local evidence; unsupported reports remainreported, and disagreements become candidates for human judgment.MEMORY_EVENTS.jsonlis the append-only authority.Stopmakes events durable in the outbox before acknowledging the attempt; the projector runs outside the registry lock and retries reuse the same IDs. Repeating an identicalevent_id/payload is a no-op; reusing the ID with different bytes is observable corruption.MEMORY_CANDIDATES.jsonlis the curation queue. Conflicts and legacy content are never silently promoted.promoteandrejectrecord the decision as a new event; promotion preserves the selected event's JSON type, session, activation/epoch, and source turn.- Registers are scoped.
git.local-head, handoffs, verdicts, and change status carry project, work-session, change, branch, or worktree scope. Two branches do not create a global conflict; only events in the same scope and causal lineage may advance automatically. Onceactive_contextsis initialized,Stopderiveswork_session_id,repository_id,worktree_id, branch, and change from the causal active context; a divergent handoff fails before CAS, note, outbox, or ledger mutation. Legacy compatibility exists only without the contextual store. EVIDENCE_INDEX.jsonlis local recall. Markdown is chunked by headings and blocks without requiring the Observer. Ranking combines BM25, exact phrases, field weights, authority, validity, bounded recency, and source diversity. The automaticUserPromptSubmithook is read-only: it never migratesCURRENT_CHANGE.mdor mutates the registry; it excludes passages owned by an active sibling session or change while preserving global and historical evidence. Explicit/brain-recallremains global.
Artifacts stay under .brain/ only. Sanitization strips secrets, tokens, local paths, transcripts, and harness payloads both before persistence and before injection. Events carry a project_id, and one vault never accepts another project's events.
Lifecycle in brief: each SessionStart opens an epoch that spans multiple Stop events;
UserPromptSubmit advances the native turn and recovers exactly one closed legacy activation.
Codex uses session_id/turn_id plus transcript order, with no artificial causal fields. See
sessions and hooks and
memory.
Injection and budgets
CORE.md is the only manual layer: it accepts up to 40 lines, warns from 35, keeps a 4 KiB ceiling, and caps each line at 320 characters. SHARED_MEMORY.md is generated exclusively by the projector and ledger; never edit it to repair state. brain-inject delivers the same revision/hash on startup, /clear, and /compact SessionStart events, always placing CORE and SHARED before change context. The full envelope is capped at 24 KiB; SHARED reserves up to 48 lines/6 KiB. Under pressure, lessons are removed first, then non-current changes. CORE is never sliced and SHARED admits deterministic whole events rather than prefix-slicing; a missing, invalid, or over-budget layer becomes a visible, repairable <wk_memory_error>.
memory status --gate and validate-memory --vault also check semantic coverage: they report a code, counts, and active/projected/missing keys. An empty v2 bundle is neutral; a bounded omission that exactly reproduces ledger selection is an operational warning. An arbitrarily missing event, false count, placeholders as the only content, or an unresolved decision link remains explicitly degraded/blocking without printing memory values.
DIGEST.md is no longer the operational handoff: it remains the /brain-recall bridge and legacy-vault fallback. A vault without SHARED receives CORE+DIGEST with a deprecation warning; migrate during the compatibility window:
npx --no-install wendkeep memory status --gate --vault .MyApp-vault
npx --no-install wendkeep memory migrate --vault .MyApp-vault # preview, zero writes
npx --no-install wendkeep memory migrate --apply --vault .MyApp-vault # backup + candidates + v2 bundle
npx --no-install wendkeep memory rescope --vault .MyApp-vault # preview without values
npx --no-install wendkeep memory rescope --apply --vault .MyApp-vault # append-only and idempotentHealth and recovery
Use npx --no-install wendkeep memory status --gate --vault <vault> in CI and before verify/archive. Revision 0
immediately after valid migration is healthy. The gate correlates last_memory_attempt, outbox,
ledger, SHARED, and checkpoint: degraded with a durable outbox is a warning; an ambiguous attempt,
lost publication, or mismatched checkpoint blocks. See migration
and diagnostics.
If status blocks, preserve the evidence and run npx --no-install wendkeep memory repair --vault <vault> to back up
the corrupt ledger, retain valid lines, and re-project. Repair remains structural: its only narrow
acknowledgement exception covers attempts entirely represented by the outbox consumed by that same
run; it does not scan or reclassify historical attempts. Valid pre-0.59 causal checkpoints and
exactly re-derived assert-only historical prefixes are
CAS-migrated on both the attempt and memory_checkpoint to the correct physical boundary with
backup/audit; divergent mirrors fail closed. A demonstrably superseded
ambiguity uses memory reconcile <session> --by-session <successor>
--reason <reason> as a dry run and requires --apply; the decision is backed up and audited
without rewriting ledger, CORE, or notes. Run status --gate again afterwards. memory rescope
append-only separates legacy handoffs with proven identity even when they already participate in
candidates; it never selects a winner. Actionable conflicts require explicit, durable curation.
Start with memory curate --vault <vault>: the menu shows actionable conflicts only, groups them
under friendly names, shows sanitized previews, and confirms every write with default no.
memory curate --all includes proven historical handoffs and lets H batch-close them after
confirmation. In a non-TTY environment, use
memory candidates --active --vault <vault> to list only safe IDs and metadata in read-only mode —
it does not expose memory values or content. After human review,
memory promote <id> --event <event-id> selects one event from the candidate, while
memory reject <id> keeps the current value. memory repair does not choose a winner. The decision is idempotent, and a
new promotion accepts a later Stop from the same session/activation without recreating a conflict.
A promotion written by 0.66.1 remains historical: if the next Stop forms a new candidate, update
to 0.66.3 and run memory repair. During replay, a transient candidate is re-evaluated against the
final modern source and, when causal supersession is proven, re-anchored to it; explicit promotion
uses that anchor and crosses only the physical predecessors required by replay. The same
session/activation/epoch and a higher turn advances; a lower turn is superseded. Repair migrates the checkpoint and mirror only when it proves the exact previous
replay, attempt identity, and absence of a real conflict; it creates a backup and audit without
appending or rewriting events. Ambiguity stays queued for explicit curation. Do not publish or
install 0.66.2; use 0.66.3 or later. Decisions survive repair/replay;
blocked_by_core cannot override CORE. Doctor only diagnoses, now with human-readable output and
the guided next action; its health hook preserves JSON for automation. A missing Vault or unsafe
boundary/registry also yields blocked memory, a safe command with the resolved path, and structured
JSON—never a false “intact bundle” or a stack trace. When status/doctor reports projected
acknowledgement pending on 0.66.4 or later, first run the targeted dry run
memory recover-attempt <session> --vault <vault>, then authorize --apply; it changes only
registry/checkpoint. See syntax, preconditions, and fail-closed behavior in
memory and curation.
Session notes use one live ## Agentes, tokens e custos snapshot. Main-agent and subagent hooks recompose it atomically, with costs, token dimensions, reasoning tokens and effort per model/source. On Codex, subagent prompts register their rollout for observability without advancing the main agent's sequence; SubagentStop reads the child from agent_transcript_path and persists its signal only when parent_thread_id matches a validated session root. The main Stop uses the registry's causal turn_id mapping before falling back to local transcript order. Every terminal attempt writes a sanitized, idempotent receipt to .brain/SESSION_ITERATION_OUTCOMES.jsonl, distinguishing insertion, duplicate, skipped, aborted, busy-lock, failure, and observability status; the cursor advances only after note confirmation. subagent_notification is not a human prompt, turn_aborted is explicit, and custom tool output is not double-counted. To recover missing markers while a conversation is open, hook session-backfill is a dry-run by default and never writes a Codex turn without task_complete. Every hook that rewrites a session note takes a per-file lock and writes through a temp file + rename, so the SubagentStop fan-out (one hook run per subagent) can never leave a note half-written; a note whose frontmatter reads back damaged is left untouched rather than patched.
On definitive close, the activation and session become done in SESSION_REGISTRY.json only
after memory/observability publication; CURRENT_SESSION.md is a derived view, not the identity
authority, and does not list finalized sessions.
Retroactive memory (import) — install today, remember yesterday
Install wendkeep into an existing project and it only remembers sessions from now on. wendkeep import fixes that: one command backfills your project's past Claude & Codex sessions into the vault — deduped, dated, with cost — so the graph starts full, not empty. It rebuilds each transcript as a full session note in its real date folder — frontmatter (tagged with the transcript's real provider), one iteration block per turn, cost + subagent telemetry, derived decision/bug/learning notes, finalized closing. An offline replay of the live capture flow, so an imported note is indistinguishable from a captured one.
wendkeep import --vault .myproject-vault --dry-run # preview what would be imported (both agents)
wendkeep import --vault .myproject-vault # write the notes
wendkeep import --vault .myproject-vault --source codex # just Codex- Both agents by default (
--source all). Claude sessions come from~/.claude/projects/<slug>/; Codex rollouts from~/.codex/sessions/**, scoped to this project by thecwdrecorded in each session (case- and separator-insensitive, subdirs included). Narrow with--source claude/--source codex. - Every note records its
session_idandproviderin frontmatter (live capture and import alike). Backfill older notes withwendkeep import --stamp-ids(fills the id from the registry; idempotent). - Deduped by
session_idagainst the vault'sSESSION_REGISTRYand existing notes' frontmatter — only sessions not already present are imported, and it never overwrites an existing note. Re-running is a no-op. --from <dir>/--codex-from <dir>point at the transcript folders explicitly (use if the auto-derived path misses). Also:--since <date>,--limit <n>,--rescan-decisions,--json.- Once imported,
wendkeep costaggregates your entire history — retroactively, across both agents.
Derived notes — numbered like ADRs (note new, renumber-*)
Decisions, bugs and learnings are derived notes: they live in the month folder of their tree (<folder>/<year>/<MM-MON>/) and carry a sequential id — ADR-0001, BUG-0001, APR-0001. One glance tells you what a note is and where it sits in the project's history. No day-level subfolders: a DIA N folder holding one note is noise, and it hides the note from folder-wide search.
Creating one (never write the file by hand — the command owns the number, the folder and the frontmatter):
wendkeep note new --type bug "login 500s when the token expires mid-refresh"
# → 05-Bugs/2026/07-JUL/BUG-0007-login-500s-when-the-token-expires-mid-refresh.md
wendkeep note new --type learning "a regex without /g only ever returns the first match"
# → 06-Aprendizados/2026/07-JUL/APR-0003-a-regex-without-g-only-ever-returns-the-first-match.mdIt prints the created path, numbers from the current max (recursive scan), files it in the month folder for today (--date YYYY-MM-DD to override), and links the active session in source: so the graph stays connected. Agents get this rule injected at SessionStart — they call the command instead of guessing a filename.
Reconnecting legacy notes. Derived notes created by older versions carry no source: session and sit as islands in the graph. wendkeep note relink backfills them: each orphan inherits the modal source session of its type/month cohort (the session its non-orphan siblings already point to). Dry-run by default; --apply writes; notes with no sibling to infer from are skipped and reported.
Migrating an existing vault. Notes created before 0.41.0 have date-prefixed names (2026-07-16-bug-<slug>.md) and may sit in legacy DIA N subfolders. One command per tree renumbers them chronologically, moves them up into the month folder, and rewrites every wikilink across the vault:
# Bugs — 05-Bugs → BUG-NNNN
wendkeep renumber-bugs # preview: prints every from → to, writes nothing
wendkeep renumber-bugs --apply # migrate
# Learnings — 06-Aprendizados → APR-NNNN
wendkeep renumber-learnings # preview
wendkeep renumber-learnings --apply # migrate
# Decisions — 04-Decisões → ADR-NNNN (since 0.30.0)
wendkeep renumber-decisions # preview
wendkeep renumber-decisions --apply # migrate- Preview is the default. Nothing is written until
--apply— read thefrom → tolist first; that is where a mangled slug shows up, before it touches your files. - One tree at a time, on purpose. There is no
renumber-all: each folder is migrated and reviewed on its own. - Order is chronological, derived from the note's date (frontmatter → filename prefix → folder), so
BUG-0001is genuinely the oldest bug — not the first one the scanner happened to read. - Wikilinks are rewritten vault-wide (full-path and basename forms, aliases preserved), the body's
type/bug:/apr:/H1 are normalized, and emptiedDIAfolders are removed. Idempotent: a second--applyrenames nothing. Close Obsidian while migrating, and commit the vault first if it is under git.
Change lifecycle — the a2 loop (spec‑driven, native)
Beyond capturing sessions, wendkeep is a harness: a native, zero‑dependency loop that keeps intent (specs), work (changes) and proof (sensors) together in the vault, wikilinked into the Obsidian graph.
explore → propose → apply (TDD) → verify → archive- Propose —
wendkeep change new <slug>scaffolds08-Mudanças/<slug>/(proposta.md,design.md,tarefas.md;--simpleskips the design).--guidecreates the compact GUIDE contract and omits automatic design/spec/ADR whencontract_impact:none. It becomes the global current change. When the change declaresspec_impact: required, you author the delta yourself atspecs/<capability>/spec.md— there is no placeholder to delete. Multiple changes may remain open:change list/statusand the hooks show every pending one, while commands without--changeact on the current one alone.change use <slug>changes focus andchange continue <archived> <new>creates an auditable continuation. - Apply — implement each
tarefas.mdtask. Mark machine proof with one or more[sensor:<id>]tags on the same task: every distinct ID enters the gate once, in declaration order. Also mark satisfied requirements with one or more[req:<ID>]tags. For causal TDD, add[tdd]and recordwendkeep tdd red|green; the TDD attestation guide covers profiles, waivers, and exit codes. - Verify —
wendkeep verifyruns declared sensors and writes an Evidence Envelope v2 toevidencia.json, SHA-256-bound to project/repository/worktree/session, HEAD, index tree, normalized worktree digest, tasks, effective spec, TDD attestations, and sensor configuration. Each sensor records a sanitized command, execution window, duration, exit code, output digest, and a sanitized tail bounded to 2,000 characters;artifact_resultsbinds explicit digests for versioned files without using stdout as proof. If HEAD changes during the run, no new envelope is published.change statusreportsbound,stale,context-mismatch, orlegacy-unbound; v1 evidence remains readable but cannot satisfy v2 authority. The public schema isschema/wendkeep.evidence-envelope-v2.schema.json.verify --deepbinds the package, attestations, and verdict to the currentenvelope_id. - Archive —
wendkeep change archive <slug>gates on the evidence (blocks unless every declared critical sensor is green), promotes each applicable spec delta (ADDED/MODIFIED/REMOVED) into the living07-Specs/<capability>.mdand moves the change to_arquivo/. GOVERN/ASSURE mint an ADR in04-Decisões/; compact GUIDE with no contract impact does not mint one automatically.
The gate blocks unless the scaffold is filled, no task is open, evidence is fresh, and every declared requirement is covered.
--forcewaives exactly one of those — the open-task check — and is the human's call, never the agent's. An unfilled scaffold, a red critical sensor, stale evidence, an orphan requirement or a missing verdict block regardless.
After the fix, archive requires a final recapture with
wendkeep verify --deep --change <slug>. The package is complete and canonical; the verdict must
also be complete and canonical, with both bound to the same checkout, change, tasks, spec, and sensors. Before any mutation, the command
writes an authorization receipt to the separate change-archive-receipts-v2 ledger; only after
that receipt is valid may it promote the spec/ADR or move the change. change archive --json
returns the serializable state, reason_codes, diagnostics, and repair fields. Corruption
or truncation in any ledger fails closed before mutation. --force does not bypass provenance,
integrity, package, verdict, corruption, or truncation; the exact recovery is the recapture above.
Archive sealing uses the runtime lock and a private ASCII transaction at
.brain/runtime/archive-transactions/<uuid>/{original,authorized}: it atomically renames the live
change to original, checks the digest, and promotes only authorized. On a seal or divergence
failure, it removes the snapshot and restores original without partial promotion. Multi-spec
promotion is one atomic unit: it captures before-images/digests, rolls back before/after writes,
and permits retry only after reconciliation and fresh verification. The post-release finalizer
validates original/destination digests, but the completed journal keeps the original retained;
no destructive cleanup is automatic. A failure leaves published-recovery-required. Text and --json
keep the same sanitized diagnostic (code, operation, state, blocker, expected, observed, recovery).
The archive uses a directory lock with a token-specific marker and lease: acquisition prepares a
sibling .pending directory and publishes it by atomic rename, uses no hardlink, and allows at most
3 topology attempts. A live owner returns WENDKEEP_ARCHIVE_BUSY, a dead owner is safely reaped,
an invalid marker returns WENDKEEP_ARCHIVE_LOCK_UNAVAILABLE, and ownership loss returns
WENDKEEP_ARCHIVE_LOCK_OWNERSHIP_LOST. The transaction keeps archive-transaction.json with
phases prepared → isolated → copied → sealed → published → promotion-prepared →
promotion-applied → completed or recovery-required. A pending journal blocks a new archive for
the same slug before the gate. On
a collision/post-publication failure, original is retained and the state is
published-recovery-required; operation_id and transaction_phase are sanitized. Inspect it
with wendkeep change archive recover <operation-id> --change <slug> [--spec-action rollback|resume] [--json]:
without --spec-action, this is a read-only, fail-closed, idempotent operation that returns
sanitized actions without promoting or deleting. rollback restores before-images and resume
converges after-images for a promotion-prepared promotion, while retaining the journal for
reconciliation. When an operation ID exists, repair.command points to that recovery; do not treat
command:null as the normal flow.
wendkeep init seeds process skills into the vault's .brain/skills and delivers identical copies to .claude/skills/ and .agents/skills/; Codex gets the agent definitions (.brain/agents/*.toml → .codex/agents/) plus a managed section in AGENTS.md that indexes the skills. Every skill carries source hash/version metadata; doctor warns when reseed + agent restart is required.
The loop in five minutes
npx wendkeep init --yes # vault + hooks + sensors + skills
npx wendkeep change new dark-mode # proposta/design/tarefas — change is now activeEdit tarefas.md — tag proof and requirement per task:
- [ ] 1.1 toggle persists across sessions [req:UI-1] [sensor:tests] [tdd]Declare the capability in proposta.md (specs: [ui]) and author its delta only in
08-Mudanças/<slug>/specs/ui/spec.md. 07-Specs is generated/read-only. Then:
npx wendkeep change status # every open change + its pending tasks
npx wendkeep change list # same backlog, plus the archived ones
npx wendkeep change status dark-mode # one screen for one change: specs / tasks / sensors / verdict
npx wendkeep spec effective --change dark-mode # living contract + this change's delta
npx wendkeep change done 1.1 # tick a task from the CLI
npx wendkeep tdd red 1.1 --requirement UI-1 --test tests/ui.test.mjs --command "npm test"
# implement; then observe GREEN in the same causal context
npx wendkeep tdd green 1.1 --command "npm test"
npx wendkeep verify # run the declared sensors -> evidencia.json
npx wendkeep verify --deep # assemble the verification package
# the wk-verify skill (fresh, read-only pass) writes verdict.json
npx wendkeep change diff # preview what will land in 07-Specs
npx wendkeep change archive dark-mode # gate: sensors + verdict + no open tasksThe archive promotes the delta into generated 07-Specs/ui.md, mints an ADR, and the
Obsidian graph now links session ↔ change ↔ requirement ↔ decision. Every generated
artifact (design/tarefas) is born linking its change's proposta hub, and hand-authored
spec.md deltas are auto-linked on verify/archive — no change artifact is a graph island.
To backfill older changes that predate this, run wendkeep change backlink --apply (dry-run
without --apply). A change that names no [req:] still runs verify --deep, but skips the
wk-verify reading pass: the command writes a trivial verdict on its own and the sensor gate
is the real proof.
How it works
agent session ──hooks──▶ wendkeep ──▶ Markdown in vault ──▶ .brain memory + Obsidian graph
(Claude/Codex) (Node) (02-Sessões/…) (CORE+SHARED, ledger, backlinks)The agent's settings.json points each hook at npx wendkeep hook …; in Claude Code the change-lifecycle hooks instead run the installed script directly (node on ${CLAUDE_PROJECT_DIR}/node_modules/wendkeep/hooks/<name>.mjs) when the package is present locally, skipping an npx resolve on every event. .codex/hooks.json mirrors the same groups with PascalCase event keys, but always uses the npx form (${CLAUDE_PROJECT_DIR} does not exist in Codex) and spells its timeout timeoutSec — a plain timeout is neither a field nor an error there, it silently falls through to a 600s default, so init migrates that legacy key in place. On Stop, wendkeep parses the session transcript, appends the turn, updates token/cost data, emits derived notes, and publishes the sanitized handoff to the memory outbox. On SessionStart — startup, /clear, and /compact — brain-inject injects CORE + SHARED, every open change with its pending tasks, the causal current-change marker, project lessons, and a <wk_process> router. The inventory remains global, but CURRENT and the sentinel hash come from the session's active context; active_contexts: {} fails closed instead of reviving CURRENT_CHANGE.md. Claude, Codex, or another agent can therefore resume work started elsewhere without hiding the rest of the backlog.
The archive gate blocks unless: the change scaffold is filled (G0), no task is open (G1), every declared critical sensor is green (with fresh evidence), and a verdict.json is present and current. --force waives G1 only — G0 is inescapable by design (a placeholder change forced through once minted a fake ADR), and no flag turns a red sensor or a missing verdict green. The agent is instructed never to use it on its own.
Notes & roadmap
- Vault folder names default to Portuguese (
02-Sessões,04-Decisões, …). Passwendkeep init --locale enfor an English vault (02-Sessions,04-Decisions, English scaffold/skills). The locale is a vault property, locked at init; parsers are bilingual so mixed content never breaks. - Default recall remains local lexical/FTS. Embeddings require an explicitly supplied trusted local plugin with forbidden network access, budgets, and lexical fallback; Core ships no model or provider.
- Transcript formats are agent‑internal and can change between agent versions; parsing is isolated but may need updates.
- Installer wires both agents:
.claude/settings.json+.mcp.jsonfor Claude Code,.codex/hooks.jsonfor Codex. Eight Codex hooks are compatible;change-guardvalidates the project lease inPreToolUseand denies mutations outside it. The four hooks without an equivalent remain Claude-only:change-warn,plan-capture,decision-capture, andtask-log. - Codex hooks start untrusted. They are
