aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/design/2026-06-02-flush-promotion.org2
-rw-r--r--docs/design/2026-06-02-pattern-catalog-spec.org2
-rw-r--r--docs/design/2026-06-17-ntfy-agent-comms-proposal.org2
-rw-r--r--docs/design/2026-06-18-triage-intake-phone-push-workflow.org2
-rw-r--r--docs/design/2026-07-13-runtime-portability-inventories.org84
-rw-r--r--docs/design/2026-07-14-sentry-workflow-proposal.org150
-rw-r--r--docs/design/2026-07-15-subproject-pattern-proposal.org22
-rw-r--r--docs/design/2026-07-15-subprojects-convention-home-instance.org282
-rw-r--r--docs/design/2026-07-16-polyglot-bundle-collision.txt30
-rw-r--r--docs/design/task-review.org2
-rw-r--r--docs/specs/2026-06-16-autonomous-batch-execution-spec.org2
-rw-r--r--docs/specs/2026-06-16-encourage-kb-contribution-spec.org2
-rw-r--r--docs/specs/2026-07-01-docs-lifecycle-spec.org2
-rw-r--r--docs/specs/2026-07-14-sentry-workflow-spec.org275
-rw-r--r--docs/specs/agent-knowledge-base-spec.org2
-rw-r--r--docs/specs/inbox-workflow-consolidation-spec.org2
16 files changed, 853 insertions, 10 deletions
diff --git a/docs/design/2026-06-02-flush-promotion.org b/docs/design/2026-06-02-flush-promotion.org
index 9d9d8a3..ff4e579 100644
--- a/docs/design/2026-06-02-flush-promotion.org
+++ b/docs/design/2026-06-02-flush-promotion.org
@@ -1,5 +1,5 @@
#+TITLE: Flush Promotion — Handoff Bundle (from work)
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-02
* Provenance
diff --git a/docs/design/2026-06-02-pattern-catalog-spec.org b/docs/design/2026-06-02-pattern-catalog-spec.org
index e74b8ae..6bb105a 100644
--- a/docs/design/2026-06-02-pattern-catalog-spec.org
+++ b/docs/design/2026-06-02-pattern-catalog-spec.org
@@ -1,5 +1,5 @@
#+TITLE: Cross-Project Pattern Catalog — Spec
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-02
One-page spec for capturing reusable design patterns so they travel from one project to the next instead of being re-derived. Drafted for Craig's spec-review; the five open questions from the [[file:../../todo.org][todo.org]] task carry a recommended call each, marked DECISION.
diff --git a/docs/design/2026-06-17-ntfy-agent-comms-proposal.org b/docs/design/2026-06-17-ntfy-agent-comms-proposal.org
index ce17138..0961b47 100644
--- a/docs/design/2026-06-17-ntfy-agent-comms-proposal.org
+++ b/docs/design/2026-06-17-ntfy-agent-comms-proposal.org
@@ -1,5 +1,5 @@
#+TITLE: Proposal — Promote the ntfy phone channel into a general agent-comms tool
-#+AUTHOR: Craig Jennings & Claude (home project)
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-17
* Why this is in rulesets' inbox
diff --git a/docs/design/2026-06-18-triage-intake-phone-push-workflow.org b/docs/design/2026-06-18-triage-intake-phone-push-workflow.org
index cd830fb..a0bb416 100644
--- a/docs/design/2026-06-18-triage-intake-phone-push-workflow.org
+++ b/docs/design/2026-06-18-triage-intake-phone-push-workflow.org
@@ -1,5 +1,5 @@
#+TITLE: Triage Intake Workflow (Engine)
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-05-01
* Summary
diff --git a/docs/design/2026-07-13-runtime-portability-inventories.org b/docs/design/2026-07-13-runtime-portability-inventories.org
new file mode 100644
index 0000000..b9e8244
--- /dev/null
+++ b/docs/design/2026-07-13-runtime-portability-inventories.org
@@ -0,0 +1,84 @@
+#+TITLE: Runtime Portability — Hook, MCP, and Memory Inventories
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-13
+
+Decision-prep for the generic-agent-runtime arc (spec: [[file:2026-05-28-generic-agent-runtime-spec.org]]). Three of the eight gap child tasks are inventories; this doc is their deliverable. Each section ends with a verdict and any decision left for Craig.
+
+* Hook parity inventory
+
+What's wired today (global =.claude/settings.json=, symlinked to =~/.claude/settings.json=):
+
+- PreToolUse AskUserQuestion hard-deny — blocks the popup-choice tool, forcing inline numbered options per =interaction.md=. Exists because prose alone failed.
+- PreCompact =precompact-priorities.sh= — saves priorities before context compaction.
+- SessionStart =session-title.sh= (cosmetic title) and =session-clear-resume.sh= (resume-after-/clear plumbing).
+- Stop =ai-wrap-teardown.sh= — sentinel-gated session teardown.
+
+Shipped but unwired (reference copies in =hooks/= + =settings-snippet.json=): =git-commit-confirm.py=, =gh-pr-create-confirm.py=, =destructive-bash-confirm.py=. Not active under Claude either, so they impose no parity requirement today.
+
+Language bundles wire PostToolUse validators per edit (=validate-bash.sh=, =validate-el.sh=, =validate-go.sh=, plus python/typescript equivalents) — and every bundle also ships the same validation as a =githooks/pre-commit=.
+
+Per-hook portability verdicts:
+
+- AskUserQuestion deny: moot off Claude. The popup tool is Claude Code's; Codex-style and local harnesses have no equivalent tool to deny. No port needed.
+- PostToolUse validators: covered off Claude. The bundles' git pre-commit hooks run the same validators, so enforcement survives at commit time instead of edit time. Acceptable downgrade, no work needed.
+- SessionStart clear-resume: not portable as-is; /clear semantics are Claude's. This is the same surface as the "session plumbing per runtime" child task — resolve it there, not as a hook port.
+- session-title: cosmetic, skip on other runtimes.
+- PreCompact priority-save: genuine gap. Codex-style harnesses compact without a hook point. Downgrade is prose (the session-context write triggers in protocols.org already say "save before compaction"), which is weaker but is also the pre-2026 Claude behavior. Accept prose, or shorten sessions on those runtimes.
+- Stop wrap-teardown: partial gap. Codex has a turn-completion notify command that could run the same sentinel-gated script; local harnesses vary. Port the script trigger where a hook point exists; where none does, teardown becomes a manual step in the wrap phrase.
+
+Verdict: only PreCompact and Stop carry real porting work, and both have acceptable degraded modes. Nothing here blocks a pilot.
+
+* MCP portability check
+
+Locally-configured servers (user scope, =~/.claude.json=): linear, notion, figma, slack-deepsat, google-calendar, google-docs-personal, google-docs-work, drawio, google-keep. No project-scope servers anywhere. All are ordinary MCP server definitions, portable to any MCP-speaking harness (Codex CLI reads them from its config; most local harnesses that matter speak MCP now). Port = translate the server blocks + carry the auth material. Mechanical.
+
+claude.ai-managed connectors (session-injected, not in local config): Gmail, and the claude.ai variants of Calendar/Drive. These do not travel. Overlap analysis: calendar and docs are already covered by the locally-configured servers; Gmail's workflow-preferred path is already =cmail-action= (local CLI), so the managed Gmail connector is convenience, not dependency.
+
+Named gap: signal-mcp. protocols.org "Paging Craig" names it as the only supported away-from-desk page path, and it is not in any local config — it lives claude.ai-side. Off Claude there is currently no page channel at all. This lands squarely on the open Signal-pager task ([#C], todo.org): the signal-cli runbook that task produces would be the runtime-neutral paging path. Recommend noting runtime-portability as an added motivation on that task.
+
+Verdict: portable except paging; paging's fix is already a filed task.
+
+* Memory story for non-Claude agents
+
+Claude auto-memory (=~/.claude/projects/<enc>/memory/=, MEMORY.md index) is harness-owned: written by Claude's memory tooling, loaded by its session start. A non-Claude agent should neither read nor write it.
+
+The designed cross-agent store already exists: the org-roam KB (=knowledge-base.md= — reading is plain rg over files, writing is one node per fact, both runtime-neutral). Project state lives in file artifacts every agent reads anyway: todo.org, notes.org, session anchors, docs/.
+
+One wording gap: knowledge-base.md's "Capture, then promote" section names harness memory as the capture layer. For a non-Claude agent the capture layer is the session log itself. Recommended one-sentence addition there: an agent without harness memory captures into its session log and promotes from it at wrap-up. Shared-asset edit, needs approval.
+
+Verdict: no build. One approved sentence in knowledge-base.md closes it.
+
+* Skill and command parity
+
+What rulesets ships: 11 skills (SKILL.md, model-invokable — add-tests, debug, five-whys, flush, frontend-design, pairwise-tests, playwright-js, playwright-py, review-code, root-cause-trace, voice) and 18 commands (plain .md prompts under =.claude/commands/=, user-invoked). Every body is markdown instructions; nothing executable lives in the registration layer.
+
+The load-bearing observation: a =/name= reference is just a pointer to a file on disk. Any harness that reads files can execute all 29 artifacts through one resolution rule in its bootstrap entry file: "a skill or command reference (=/voice=, =/review-code=, =/brainstorm=) resolves to =~/.claude/skills/<name>/SKILL.md= or =~/.claude/commands/<name>.md= — read the file and follow it." That single sentence, emitted by the instruction-bootstrap install target, makes the whole library portable with zero per-skill work. commits.md's existing "/voice unavailable — walk the patterns inline" fallback generalizes the same way.
+
+What that rule does not carry:
+
+- Auto-invocation. Claude Code triggers skills from their descriptions; other harnesses won't. The high-value auto-triggers (voice and review-code inside the publish flow) don't actually need it — commits.md invokes them by name at fixed flow points, and the resolution rule covers a by-name invocation. The convenience triggers (debug, frontend-design firing on topic match) degrade to on-request. Acceptable.
+- Native slash registration. Codex-style harnesses have their own custom-prompt mechanism; the 18 commands could also be registered there for ergonomics (an optional install nicety, not a requirement — the resolution rule already works).
+- flush. Its mechanics are Claude Code's (/clear, the self-inject resume hook). Explicitly not ported; the session-plumbing child owns that surface.
+- Harness built-ins (code-review ultra, plan mode, artifacts). Not rulesets' to port; flows that name them need per-runtime alternatives or graceful absence, same as today when they're unavailable.
+
+Verdict: no per-skill porting matrix needed. One resolution sentence in the bootstrap entry file (instruction-bootstrap child), optional native registration as an install nicety, flush explicitly excluded.
+
+* Session plumbing per runtime
+
+Four pieces, examined:
+
+- =session-context-path= is already runtime-aware by design — the =AI_AGENT_ID= shape carries a runtime segment (=host.project.runtime.epoch=) and the resolver is pure bash. Nothing to do.
+- The anchor cycle (suspend entry, startup's interrupted-session recovery, wrap-up's archive rename) is plain files driven by workflow prose. Any agent that reads protocols.org executes it identically. Nothing to do.
+- =self-inject.sh= is harness-agnostic: it types arbitrary strings into a tmux pane. The Claude-bound part is only the *payload* — "/clear" plus a resume line that leans on the SessionStart hook. A Codex auto-flush is a payload variant: the clear command differs (confirm the exact one when wiring) and the second injected line must itself carry the resume instruction ("read .ai/session-context.org and resume") since no hook fires. Arguably simpler than the Claude path.
+- =session-clear-resume.sh= (the SessionStart hook) is Claude-only, and the payload variant above makes it unnecessary off Claude.
+
+Verdict: no build. The flush *skill* stays Claude-official; a codex auto-flush is a documented payload variant to wire the day a codex session actually wants it, not before.
+
+* Decisions for Craig
+
+All four approved by Craig, 2026-07-13:
+
+1. PreCompact downgrade off Claude: prose-only "save before compaction" accepted.
+2. Stop-teardown off Claude: port via Codex notify where available, manual teardown elsewhere.
+3. Runtime-portability motivation note added to the Signal-pager task.
+4. knowledge-base.md capture-layer sentence added (non-Claude runtimes capture into the session log and promote from there).
diff --git a/docs/design/2026-07-14-sentry-workflow-proposal.org b/docs/design/2026-07-14-sentry-workflow-proposal.org
new file mode 100644
index 0000000..d29ceb8
--- /dev/null
+++ b/docs/design/2026-07-14-sentry-workflow-proposal.org
@@ -0,0 +1,150 @@
+#+TITLE: Sentry Workflow — Proposal for rulesets
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-13
+
+* Status
+Proposal from the work project to the rulesets session, for implementation as a
+general (shared-layer) workflow: =.ai/workflows/sentry.org= plus a small
+supporting script or two under =.ai/scripts/=.
+
+* Problem
+
+An agent session left running overnight can keep a project clean and healthy on
+a cadence, but the current pieces don't coordinate. Two gaps:
+
+1. *No agent-vs-agent collision guard on shared public files.* =capture-guard=
+ only detects Emacs org-capture buffers (the human case) on one target file,
+ and it's advisory, not a lock. There is no flock anywhere in =.ai/scripts/=.
+ Multiple sessions (several projects, sometimes both daily drivers) can
+ read-modify-write =~/org/roam/inbox.org= and the =agents/= KB nodes at once.
+ The only coordination is the git pull/commit/push discipline in
+ knowledge-base.md, which is optimistic — a pull/push race forks a
+ =*sync-conflict*= file, and the KB search already globs those out, so we know
+ the forks happen and get dropped.
+
+2. *No single serialized runner.* Firing inbox-zero and triage-intake as
+ separate hourly crons means they can overlap each other and other agents.
+
+Sentry is one supervisor loop that runs a fixed sequence of hygiene passes
+serially, holds locks so it never collides with itself or other agents, and
+checkpoints aggressively for crash recovery.
+
+* What sentry is
+
+A single workflow, driven by one interactive =/loop= (one cron), that on each
+fire walks an ordered list of passes inside a single turn. It replaces the
+per-task crons. Design invariants:
+
+- *Serial.* Passes run one at a time, in order. They never overlap each other.
+- *Single-runner lock.* Sentry takes a project-local flock
+ (=.ai/.sentry.lock=) at entry. If a prior fire is still running, this fire
+ skips instead of doubling up.
+- *Shared-file lock.* Any pass that writes a public roam file first acquires a
+ roam-write lock (flock on =~/org/roam/.roam-write.lock=) held across the whole
+ read-modify-write-commit-push cycle, so it serializes against other agents on
+ the same host. =capture-guard= still runs underneath for the human case. (The
+ roam-write lock is new; it's the general guard the dropped KB-guard note would
+ have proposed, folded in here.)
+- *Non-destructive by default.* Read-only and idempotent-filing passes run
+ fully. Anything destructive or judgment-heavy (triage trash/mark-read, task
+ regrades, spec flips) queues its proposal into the digest for morning
+ approval, never fires unattended.
+- *Idempotent + isolated.* Every pass is safe to repeat. A pass that errors
+ doesn't abort the rest; its failure is a loud banner in the digest.
+
+* Crash-recovery spine (applies to every pass)
+
+Crash recovery has been a live consideration. Two enforced steps wrap each pass:
+
+1. *Session-context between passes.* Before the next pass starts, the current
+ pass appends a dated entry to =.ai/session-context.org= recording what it
+ did. A crash mid-sequence leaves a readable "got this far" trail, so the next
+ session resumes without re-deriving state.
+2. *Local commit, no push, on any disk change.* Every pass that writes to disk
+ gets its own =chore(sentry): <pass> — <what changed>= commit on the current
+ branch, unpushed. A crash loses at most one pass. Craig reviews the stack in
+ the morning and pushes deliberately. Conventional messages, no AI
+ attribution. (Open decision: commit on the current branch vs a dedicated
+ =sentry/<date>= branch — see Open decisions.)
+
+The unit per pass is therefore: do the work → log to session-context → commit.
+That triplet is the recovery boundary.
+
+* Passes (v1 — all ten, plus the first-run KB promotion)
+
+Ordered so read-only/pull passes precede writes, and the heaviest external
+sweep sits mid-run:
+
+1. *Roam pull* — =git -C ~/org/roam pull --ff-only= so later reads are fresh.
+ Read-only.
+2. *Inbox zero* — inbox.org roam mode. Route roam-inbox items this project
+ owns. Acquires the roam-write lock for the inbox edit.
+3. *Triage intake* — triage-intake.org. External-accounts sweep, classify,
+ file Action tasks. Destructive actions (trash/mark-read) queue for morning;
+ =mbsync -a= and the sentinel advance run.
+4. *Todo cleanup* — clean-todo.org + todo-cleanup.el + lint-org.el. Convert
+ level-3 done subtasks to dated logs, archive done subtrees, flag lint.
+5. *Task audit* — task-audit.org. Re-check =:solo:=/=:quick:= tags,
+ priority-scheme conformance, bug severity×frequency grades, stale =DOING=
+ specs with closed parents, blocked/blocker reciprocity. Regrades queue for
+ morning.
+6. *Working-files hygiene* — flag =working/<slug>/= dirs whose task is DONE but
+ not filed to =assets/=. Report only; filing is a morning decision.
+7. *Spec status board* — the docs-lifecycle grep. Flag =DOING= specs with
+ closed parents, or specs stuck in =DRAFT=.
+8. *Link integrity* — lint-org broken =file:= links across todo/notes/prep.
+9. *Git health* — uncommitted drift, unpushed commits, stale local branches,
+ whether main is behind origin.
+10. *Prep + symlink freshness* — verify tomorrow's prep and both symlinks
+ resolve.
+11. *KB lesson promotion (first run of the session, and thereafter as new
+ lessons accrue)* — promote recent durable lessons out of the fast capture
+ layer. CLASSIFICATION-GATED:
+ - Personal project → write to the roam KB as =agents/= nodes (pull, lock,
+ write, commit, push), per knowledge-base.md.
+ - Work / denylisted project → NEVER touch roam. Promote to the project's own
+ durable store (e.g. =deepsat/knowledge.org=) instead, and emit the
+ one-line refusal note from the knowledge-base.md refusal contract so
+ nothing is lost silently.
+
+* Digest output
+
+One consolidated, timestamped summary per fire: run time first, then per-pass
+one-liners (what it found / did), the morning-approval queue (proposed
+trash/mark-read, regrades, files-to-move), and any failures as loud banners at
+the top. Delta-only where a pass supports it (triage): a pass with nothing to
+report is one line.
+
+* Cadence
+
+One hourly sentry to start (passes short-circuit fast when there's nothing to
+do). A slower evening interval (every 2–3 hours) is a config knob if hourly
+proves noisy.
+
+* Open decisions for rulesets
+
+1. *Commit target* — per-pass commits on the current branch (simplest, matches
+ "commit but don't push") vs a dedicated =sentry/<date>= branch (keeps the
+ working branch clean, costs a branch dance). Proposal leans current branch.
+2. *Roam-write lock scope* — host-local flock only (solves same-host
+ multi-agent, the common case) vs also hardening roam-sync to surface
+ cross-host conflicts loudly. Proposal: ship the flock now, note the
+ cross-host limit, treat roam-sync conflict-surfacing as a separate follow-up.
+3. *Interval default* — hourly vs evening-only. Proposal: hourly, config knob.
+4. *Which passes gate on a green tree* — e.g. skip todo-cleanup commits if the
+ project suite is red, matching inbox monitor-mode's clean-tree gate.
+
+* Cover note from sender (work, 2026-07-14 00:02)
+
+Intro for the sentry-proposal.org file just sent. This is a NEW general workflow proposal (.ai/workflows/sentry.org + supporting scripts under .ai/scripts/), not an edit to an existing synced file.
+
+What it is: one serialized supervisor loop that runs a project's evening hygiene passes on a cadence, with locks so it never collides with itself or with other agents. It replaces firing inbox-zero + triage-intake as separate crons.
+
+Why now: an agent left running overnight can keep a project clean, but the current pieces don't coordinate. capture-guard only handles the Emacs-capture case on one file and holds no lock; there is no flock anywhere in .ai/scripts/; and separate crons let inbox-zero and triage overlap each other and other agents on shared roam files, which already fork *sync-conflict* files that the KB search globs out.
+
+Three design points worth your judgment:
+1. Crash-recovery spine on every pass (crash recovery has been a live concern): enforce a session-context.org update BETWEEN passes, and a local commit (no push) after any disk change, so a crash loses at most one pass.
+2. A new roam-write flock (~/org/roam/.roam-write.lock) as the shared-file guard. This absorbs a separate 'guard all public KB files' note we considered and dropped. The lock is the better home for that concern than a bespoke per-file guard.
+3. The KB lesson-promotion pass is classification-gated: personal projects promote to roam agents/ nodes; a denylisted work project promotes to its own local store (deepsat/knowledge.org) and never touches roam, per knowledge-base.md's refusal contract.
+
+Companion files to reconcile: knowledge-base.md (the gated promotion + refusal contract), inbox.org (roam mode + the new lock), triage-intake.org (runs as a sentry pass; keep its own triggers), capture-guard (becomes one layer under the roam-write lock, not the whole guard), and clean-todo.org / task-audit.org / lint-org.el (invoked as passes). Open decisions for you are listed at the bottom of the proposal file: commit target, lock scope, interval default, and which passes gate on a green tree.
diff --git a/docs/design/2026-07-15-subproject-pattern-proposal.org b/docs/design/2026-07-15-subproject-pattern-proposal.org
new file mode 100644
index 0000000..61ade18
--- /dev/null
+++ b/docs/design/2026-07-15-subproject-pattern-proposal.org
@@ -0,0 +1,22 @@
+#+TITLE: Proposal: promote the "subproject" pattern into the claude-r
+#+SOURCE: from home
+#+DATE: 2026-07-15 23:24:07 -0500
+
+Proposal: promote the "subproject" pattern into the claude-rules layer.
+
+WHAT WE DID (in home, 2026-07-15)
+We named and built a pattern for what happens when a standalone project is consolidated into another project. home absorbed nine former projects on 2026-06-11 (danneel, finances, jr-estate, kit, health, documents, clipper, elibrary, philosophy). We now call each folded-in unit a "subproject": a former standalone project that lives as a subdirectory, shares the parent's .ai/ scope (one session, one toolchain), but keeps its own files, assets, and history self-contained under its subdir.
+
+The pattern has four parts:
+1. Vocabulary — parent project, subproject, consolidation (the process), subproject archiving. Terminology so we can talk precisely about where a subproject's documents/assets live and archive them cleanly.
+2. Subproject brief — a read-first orientation doc at <subproject>/<subproject>-brief.org (situation, current state, reading list, and for deep ones cast/timeline/key-facts-with-verify-flags). The parent's notes.org holds a thin index + a read-first rule + pointers to urgent items only, never a copy of subproject substance ("one fact, one home").
+3. Lifecycle criteria — when to CREATE a subproject (consolidate: domain fit, low independent cadence, shared-scope safety re: privacy/remote posture, no independent collaborators), and when/how to ARCHIVE one cleanly (terminal state -> move the self-contained subdir to <parent>/archive/, reconcile pointers/links/tags, manifest note).
+4. Instrumentation — a use/miss log, metrics weighted toward Craig-caught errors over self-report, review folded into task-audit, and a falsifiable kill criterion.
+
+WHY IT'S GENERAL (not just a home thing)
+Consolidating a project into another drops what the former project's own startup used to auto-load (its full depth), so sessions start cold and re-read files. That failure recurs whenever ANY project folds into another. The pattern also gives consolidation and archiving a named, repeatable shape, and the self-contained-subdir rule is what makes archiving a move-plus-pointer-reconcile instead of an excavation.
+
+THE PROPOSAL
+Promote this to a rulesets claude-rule (e.g. claude-rules/subprojects.md): the vocabulary, the brief convention, the parent-vs-subproject content criterion, and the create/archive criteria. Any parent/hub project would then inherit it. home's .ai/subprojects.org (attached next) becomes the local instance/index under the general rule. It's a companion to working-files.md (in-progress artifact layout) and docs-lifecycle.md (formal-doc lifecycle) — same family, different scope: this one governs whole-subproject orientation and lifecycle.
+
+Attached: the home instance .ai/subprojects.org as the reference draft to adapt. Apply your value gate; this is a proposal, not a synced-file edit.
diff --git a/docs/design/2026-07-15-subprojects-convention-home-instance.org b/docs/design/2026-07-15-subprojects-convention-home-instance.org
new file mode 100644
index 0000000..a1c4397
--- /dev/null
+++ b/docs/design/2026-07-15-subprojects-convention-home-instance.org
@@ -0,0 +1,282 @@
+#+TITLE: Subprojects — Convention
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-15
+
+# The reference spec for the subproject pattern: what a subproject is, when to
+# create one (consolidation), how to keep it legible (the brief + content
+# split), and how to archive one cleanly. Not read every session — the
+# always-on parts (the subproject index + the read-first rule) live in
+# notes.org and point here. Read this when consolidating a project, creating
+# or maintaining a subproject brief, or archiving a subproject.
+
+* Vocabulary (say it the same way every time)
+
+The terminology is the point — it's what lets us talk precisely about where a
+subproject's documents and assets live, and archive them cleanly.
+
+- *Parent project* (a.k.a. hub) — a top-level project that owns an =.ai/=
+ scope: its own protocols, session cadence, memory, inbox, todo. home, work,
+ rulesets, .emacs.d are parent projects. Reached by launching an agent
+ session in its directory.
+- *Subproject* — a formerly-standalone project that has been *consolidated*
+ into a parent and now lives as a subdirectory of it (=danneel/=,
+ =finances/=, =kit/= …). A subproject shares the parent's =.ai/= tooling and
+ session — it is not a separate agent scope — but keeps its own working
+ files, assets, notes, and history self-contained under its one subdirectory.
+- *Consolidation* — the process of turning a standalone project into a
+ subproject of a parent. (home's nine subprojects were consolidated on
+ 2026-06-11.) The inverse — promoting a subproject back to a standalone
+ project — is rare but allowed; it's just consolidation run backward.
+- *Subproject archiving* — retiring a completed or abandoned subproject
+ cleanly. Because a subproject is self-contained under one subdirectory,
+ archiving is a move plus a pointer reconcile, not an excavation.
+- *Subproject brief* — the read-first orientation doc for one subproject, at
+ =<subproject>/<subproject>-brief.org=.
+
+The cross-project boundary rule (=claude-rules/cross-project.md=) governs
+*parent* projects, which are separate =.ai/= scopes. It does *not* apply
+between a parent and its subprojects — they share one scope.
+
+* Why the pattern exists
+
+As a standalone project, each of these units auto-loaded its own notes.org —
+its full depth — at every startup, so recall felt instant. Consolidated into a
+parent, a subproject is one compressed section competing with the others, and
+the parent's startup surfaces hub-wide state, not any one subproject's depth.
+So a session starts cold on a given subproject and re-reads several files on
+demand. This convention restores fast, accurate orientation without
+re-bloating what loads every session — and gives consolidation and archiving a
+named, repeatable shape.
+
+* Document & asset location (what makes clean archiving possible)
+
+Everything for a subproject lives under =<subproject>/= — its =notes.org=,
+=assets/=, working files, event log, briefs. Links *within* a subproject are
+relative to the subproject dir, so the whole tree can move without breaking
+them. The parent's own top-level =assets/= is for parent-level material only,
+never a subproject's. This self-containment is exactly what lets archiving be
+a single =mv= plus a pointer reconcile. Keep it strict: a subproject's asset
+in the parent's shared =assets/= is a future broken link.
+
+* The two layers (parent vs subproject)
+
+- *Always-on layer* — the parent's notes.org. Read at every startup, so every
+ line is a tax paid whether or not it's relevant today. Holds only what must
+ be seen session-independently.
+- *On-demand layer* — the subproject brief + the subproject's notes.org.
+ Loaded only when working that subproject. Holds the depth.
+
+The whole design is: sort content by *when it's needed*, keep the always-on
+layer thin, put the depth one open away.
+
+* Creating a subproject (consolidation criteria)
+
+Consolidate a standalone project into a parent as a subproject when *all* hold:
+
+1. *Domain fit* — the project is a facet of a domain the parent already owns
+ (danneel is home life-admin; finances is home).
+2. *Low independent cadence* — it no longer needs its own session rhythm.
+ You'd rather reach it from the parent than launch it standalone, and its
+ standalone startup/sync/session overhead now outweighs the isolation.
+3. *Shared-scope safety* — it does not need a git/remote/visibility posture
+ distinct from the parent. A public code project, a team repo, or anything
+ with its own CI or gitignore-privacy posture *stays standalone* — folding
+ it would break the parent's privacy model (see
+ =claude-rules/git-hosting-privacy-model= and the gitignore-vs-track
+ decision). Personal/documentation units fold; code-with-a-public-remote
+ does not.
+4. *No independent collaborators* depend on it as a standalone deliverable.
+
+Keep it standalone when any of those fail: active independent development, its
+own team or remote, a distinct visibility/security posture, or activity high
+enough that competing in the parent's hub would bury it.
+
+*The consolidation act itself* (mechanics, mirroring home's 2026-06-11 fold):
+move the project tree under the parent as =<parent>/<subproject>/=; import its
+tasks into the parent's todo with an area =:tag:= and a =:MIGRATED_FROM:=
+property; fold its live reminders/decisions into the parent's notes as
+pointers (see the content split); build the subproject brief; record the fold
+in a consolidation manifest (=docs/consolidation-manifest-<subproject>.org=).
+Retire the source to =~/projects/.retired/<name>= once parity is verified.
+
+* The subproject brief
+
+** Path (fixed — discovery is mechanical)
+
+=<subproject>/<subproject>-brief.org= (=danneel/danneel-brief.org=,
+=finances/finances-brief.org=). The subproject name is the directory name, so
+the brief name follows for free. One predictable name means the read-first
+rule never has to hunt; put descriptive richness *inside*, not in the filename.
+
+** Spine (scales from light to deep)
+
+Required in every brief, even a fifteen-line one:
+- *One-paragraph situation* — what this subproject is, in plain language.
+- *Current state / open threads* — the live stuff. The only high-churn
+ section; everything else is stable.
+- *Reading list* — the subproject's deeper files and what each holds. The
+ brief *points into* notes.org and the rest; it does not copy them.
+
+Add only where the subproject warrants it (danneel, jr-estate, finances earn
+all of these; elibrary or documents may earn none):
+- Cast / contacts.
+- Timeline (for matters with history).
+- Key facts / numbers, with explicit *verify* flags anywhere the underlying
+ record disagrees with itself. A brief that asserts a wrong number is worse
+ than no brief — flag, don't guess.
+
+** Read-first rule (the behavior)
+
+When a session's work targets a subproject — a file under =<subproject>/=, its
+=:tag:=, or Craig naming the topic — open =<subproject>/<subproject>-brief.org=
+*first*, before touching anything else in it. If the subproject has no brief
+yet, creating it is the first step of the work (lazy backfill), and the same
+pass reconciles that subproject's parent-vs-subproject content.
+
+* Parent-vs-subproject content criterion
+
+The test for any piece of content: *if next session I'm working a different
+subproject, do I still need to see this?* Three bins:
+
+1. *Yes, and it's a fact or policy* → hub-level → the parent's notes.org.
+ Machine names, mail policy, calendar access, the hub map, the subproject
+ index.
+2. *Yes, but only because it's urgent* — a deadline or blocking status that
+ would be missed if it only lived where I might not look this week → the
+ parent holds a *one-line pointer* (item + date + "see the subproject
+ brief"); the substance stays in the subproject.
+3. *No* → the subproject. Working depth: contacts, contract facts, case
+ theory, evidence, document index, and any reminder that isn't time-critical.
+
+One hard rule over both axes: *one fact, one home.* The parent never copies
+subproject substance — it points. The parent holds the trigger; the subproject
+holds the truth. Every parent↔subproject duplicate is a second surface that
+goes stale.
+
+* Archiving a subproject (archiving criteria + clean process)
+
+Archive a subproject when it reaches a *terminal* state:
+- *Complete* — the work is done and closed (a settlement signed, a probate
+ closed, a trip finished): no live threads, no open tasks, nothing
+ time-sensitive in the parent still pointing at it; or
+- *Abandoned / superseded* — dropped, or replaced by other work.
+
+Clean archive process (self-containment is what makes each step a one-liner):
+1. Close or settle its tasks in the parent's todo (DONE / CANCELLED), per the
+ todo completion rules.
+2. Move the whole subproject tree to the parent's archive:
+ =<parent>/archive/<subproject>/= (keep it intact — archiving is reversible,
+ not deletion).
+3. Update inbound =file:= links that pointed into it (grep first, per the
+ keep-links-current rule).
+4. Move or retire its brief with it, and drop its row from the active
+ subproject index (or mark it =archived= there).
+5. Strip its pointers/reminders from the parent's always-on layer, and retire
+ its =:tag:= from active use.
+6. Record the archive in a manifest note
+ (=docs/archive-manifest-<subproject>.org=): why, when, where it moved,
+ what links were repointed. Mirrors the consolidation manifest.
+
+The =:LAST_UPDATED:= on a brief plus "no open tasks under the tag" is the
+signal a subproject is a candidate for archiving — surfaced at the review
+cadence, never auto-archived.
+
+* Freshness
+
+- *Wrap-up hook*: at wrap-up, for each subproject touched this session, refresh
+ its brief's Current-state section and bump its =:LAST_UPDATED:= date. Stable
+ sections rarely change, so this is cheap.
+- *Staleness nudge*: a brief whose =:LAST_UPDATED:= is old while its subproject
+ was recently active is worth surfacing at startup.
+
+* Self-improvement loop
+
+Every real use emits a signal, and the signal is *captured*, not silently
+acted on:
+- Brief answers the question → *hit*.
+- Brief is missing something, stale, or wrong → *miss*, logged in
+ [[file:subprojects-log.org][subprojects-log.org]].
+- Each miss drives *two* updates: *local* (fix that brief) and, if the same
+ kind of miss recurs across subprojects, *structural* (fix this spine/spec,
+ not just the one file). The structural half is the actual self-improvement —
+ grow-and-refine from real use rather than rewriting from scratch (the ACE
+ idea behind the =codify= skill).
+
+* Metrics
+
+Split by who can trust them.
+
+*Process signals (I observe, cheaply):*
+- *Orientation cost* — files/tool-calls before my first correct action in a
+ subproject. Target: ~1 (the brief), occasionally a second deep dive.
+- *Brief hit rate* — fraction of subproject questions answered from the brief
+ alone. Should climb.
+- *Freshness gap* — days between =:LAST_UPDATED:= and last subproject activity.
+ Target ~0.
+
+*Outcome signals (Craig is ground truth — weight these over the above):*
+- *Craig-caught errors* — the real quality bar. Target: trend to zero. If
+ briefs work, Craig stops catching me.
+- *Felt speed* — did dropping into the subproject feel fast and right.
+
+*Honesty caveat.* Most process signals are self-observed, and self-report is
+biased toward "hit." So the external signals (Craig-caught errors, felt speed)
+outrank mine, and the log is auditable so Craig can spot-check my self-grades.
+The instrumentation must stay lighter than the briefs, or it rots like an
+unmaintained brief. Small N, no clean A/B — this is directional evidence, not
+proof.
+
+* Review cadence
+
+Fold a "subprojects health" check into the existing task-audit cadence: read
+the log, look at the hit-rate / correction / freshness trends, flag any
+subproject that's gone terminal (archive candidate), and check the
+parent-vs-subproject criterion — anywhere the parent holds a copy where it
+should hold a pointer. Decide continue / adjust / kill for the pattern itself.
+
+* Kill criteria (falsifiable)
+
+After ~6-8 subproject-sessions across ≥3 subprojects, drop or rework the
+pattern if:
+- the correction rate isn't falling, or Craig is still catching errors; or
+- the freshness gap keeps reopening despite the wrap-up hook; or
+- maintenance costs more time than the orientation it saves.
+
+Success is the mirror: orientation down to ~1 read, Craig catching ~nothing,
+briefs current, and I'm right fast in any subproject cold.
+
+* Rollout
+
+- Define the convention (this file) + the index scaffold in notes.org. Done
+ 2026-07-15.
+- *Lazy backfill*: the next session that touches a subproject with no brief
+ creates the brief as its first step — cost paid exactly when it's used.
+- *Reconcile in the same pass*: building a subproject's brief includes
+ reconciling its parent-vs-subproject content — push substance down, leave
+ pointers up, drop dead reminders.
+- *Promotion*: this pattern is home-instanced but general. Consolidation into
+ subprojects will recur whenever any project folds into another, so this is a
+ candidate to promote into the rulesets rules layer (proposed to rulesets
+ 2026-07-15).
+
+* Adoption status
+
+| Subproject | Brief exists | Reconciled | Notes |
+|------------+--------------+------------+---------------------------------------|
+| danneel | yes | yes | First worked instance, 2026-07-15. |
+|------------+--------------+------------+---------------------------------------|
+| clipper | no | no | Lazy backfill on next touch. |
+|------------+--------------+------------+---------------------------------------|
+| documents | no | no | Lazy backfill on next touch. |
+|------------+--------------+------------+---------------------------------------|
+| elibrary | no | no | No notes.org yet; lightest subproject.|
+|------------+--------------+------------+---------------------------------------|
+| finances | no | no | Deep; earns the full spine. |
+|------------+--------------+------------+---------------------------------------|
+| health | no | no | Has its own notes.org. |
+|------------+--------------+------------+---------------------------------------|
+| jr-estate | no | no | Deep; earns the full spine. |
+|------------+--------------+------------+---------------------------------------|
+| kit | no | no | Lazy backfill on next touch. |
+|------------+--------------+------------+---------------------------------------|
+| philosophy | no | no | Lazy backfill on next touch. |
diff --git a/docs/design/2026-07-16-polyglot-bundle-collision.txt b/docs/design/2026-07-16-polyglot-bundle-collision.txt
new file mode 100644
index 0000000..b7e1257
--- /dev/null
+++ b/docs/design/2026-07-16-polyglot-bundle-collision.txt
@@ -0,0 +1,30 @@
+Proposal: language bundles collide on coverage-makefile.txt in a polyglot project, and the second install loses silently.
+
+Context: scaffolded a new project (clock-panel) on 2026-07-16 with both the python and typescript bundles installed into the same project.
+
+What happened. The second install printed:
+
+ [skip] coverage-makefile.txt already exists (use FORCE=1 to overwrite)
+
+Both bundles ship a file at that same path. Python installed first and won, so the project ended up with Python's coverage fragment only. The TypeScript one (c8, Istanbul json-summary) never landed. The line reads like routine idempotence, not a dropped deliverable, so it's easy to skim past. I only caught it by checking which targets were actually in the file.
+
+The workaround is bad. FORCE=1 gets the TypeScript fragment but overwrites the Python one, so you can't have both without renaming by hand between installs. FORCE=1 also re-seeds CLAUDE.md, which is fine on a fresh project and destructive on a customized one. What I did in clock-panel: install python, mv coverage-makefile.txt coverage-makefile-python.txt, install typescript FORCE=1, mv coverage-makefile.txt coverage-makefile-typescript.txt. Both fragments now coexist under language-suffixed names.
+
+The deeper problem, which the filename collision only hints at: both fragments define targets with the SAME names.
+
+ coverage:
+ coverage-summary:
+
+So even with both files present, a polyglot project can't copy both into one Makefile. It gets duplicate targets. The fragments assume they're the only language in the project. Renaming the files doesn't fix that, it just makes both sets of instructions visible while leaving the conflict for whoever pastes them.
+
+Options, in the order I'd weigh them:
+
+Suffix the shipped filename per bundle (coverage-makefile-python.txt, coverage-makefile-typescript.txt) so installs never collide. Cheap, and it makes the multi-bundle case work by default. It leaves the target-name conflict.
+
+Namespace the targets too (coverage-python / coverage-typescript, with a coverage target that runs both). Fixes the real problem for polyglot projects. Bigger change, and it makes single-language projects type a longer target name unless there's an alias.
+
+At minimum, make the skip line loud when the skipped file came from a DIFFERENT bundle than the one that wrote it. A skip that means "already yours" and a skip that means "another language's file is here and yours is being dropped" currently look identical.
+
+Worth deciding whether polyglot projects are a supported case at all. If they are, the bundles need a collision story. If they aren't, install-lang could say so when it detects a second bundle going into a project that already has one, rather than half-installing.
+
+Also flagged separately in a note from home today: the same install-lang run is where I'd look for other per-bundle files that could collide on a shared filename. I only checked coverage-makefile.txt.
diff --git a/docs/design/task-review.org b/docs/design/task-review.org
index 6c6dac7..c9ae023 100644
--- a/docs/design/task-review.org
+++ b/docs/design/task-review.org
@@ -1,5 +1,5 @@
#+TITLE: Design: Daily Task-Review Habit
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-05-16
#+OPTIONS: toc:nil num:nil
diff --git a/docs/specs/2026-06-16-autonomous-batch-execution-spec.org b/docs/specs/2026-06-16-autonomous-batch-execution-spec.org
index 23fc574..fe3458b 100644
--- a/docs/specs/2026-06-16-autonomous-batch-execution-spec.org
+++ b/docs/specs/2026-06-16-autonomous-batch-execution-spec.org
@@ -1,5 +1,5 @@
#+TITLE: Autonomous-Batch Task Execution — Spec
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-16
#+TODO: TODO | DONE
#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
diff --git a/docs/specs/2026-06-16-encourage-kb-contribution-spec.org b/docs/specs/2026-06-16-encourage-kb-contribution-spec.org
index cfbfe79..3d08ed5 100644
--- a/docs/specs/2026-06-16-encourage-kb-contribution-spec.org
+++ b/docs/specs/2026-06-16-encourage-kb-contribution-spec.org
@@ -1,5 +1,5 @@
#+TITLE: Encourage Org-Roam KB Contribution Across Workflows — Spec
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-16
#+TODO: TODO | DONE
#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
diff --git a/docs/specs/2026-07-01-docs-lifecycle-spec.org b/docs/specs/2026-07-01-docs-lifecycle-spec.org
index f5bfe63..91c1603 100644
--- a/docs/specs/2026-07-01-docs-lifecycle-spec.org
+++ b/docs/specs/2026-07-01-docs-lifecycle-spec.org
@@ -1,5 +1,5 @@
#+TITLE: Docs Lifecycle — Spec
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-07-01
#+TODO: TODO | DONE
#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
diff --git a/docs/specs/2026-07-14-sentry-workflow-spec.org b/docs/specs/2026-07-14-sentry-workflow-spec.org
new file mode 100644
index 0000000..fe1c155
--- /dev/null
+++ b/docs/specs/2026-07-14-sentry-workflow-spec.org
@@ -0,0 +1,275 @@
+#+TITLE: Sentry Workflow — Spec
+#+AUTHOR: Craig Jennings
+#+DATE: 2026-07-14
+#+TODO: TODO | DONE
+#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
+
+* DRAFT sentry workflow
+:PROPERTIES:
+:ID: f6c51f27-d7a2-4b63-9ff9-5ba005a66dfb
+:END:
+- 2026-07-14 Tue @ 02:03:28 -0500 — all 12 review findings dispositioned live with Craig and folded into the design; decisions now 10/10; still DRAFT pending Craig's deep read.
+- 2026-07-14 Tue @ 00:52:03 -0500 — drafted, all nine design decisions resolved live with Craig during the authoring session.
+
+* Metadata
+| Status | draft |
+|----------+------------------------------------------|
+| Owner | Craig Jennings |
+|----------+------------------------------------------|
+| Reviewer | (spec-review, next session) |
+|----------+------------------------------------------|
+| Related | [[file:../../todo.org][todo.org — sentry build task]] |
+|----------+------------------------------------------|
+
+* Summary
+
+Sentry is one supervisor workflow that runs a project's hygiene passes serially on a cadence, holds locks so it never collides with itself or other agents, commits its work to a host-suffixed side branch for morning review, and checkpoints between passes so a crash loses at most one pass. It replaces the idea of firing inbox-zero and triage-intake as separate uncoordinated crons.
+
+* Problem / Context
+
+An agent session left running overnight can keep a project clean, but the pieces don't coordinate. There is no lock anywhere in .ai/scripts/ (verified 2026-07-14): capture-guard covers only the Emacs org-capture case on one file, and it's advisory. Multiple sessions (several projects, sometimes both daily drivers) can read-modify-write ~/org/roam/inbox.org and the agents/ KB nodes at once. The only coordination is git optimism, and we know it loses: the KB search already globs out *sync-conflict* files because pull/push races fork them.
+
+Separate crons compound it: inbox-zero and triage-intake fired independently can overlap each other and any interactive session on the same shared files.
+
+Proposal origin: the work project, 2026-07-13 (preserved at [[file:../design/2026-07-14-sentry-workflow-proposal.org][docs/design/2026-07-14-sentry-workflow-proposal.org]]). This spec supersedes the proposal where they differ: the skeptical review found four conflicts with existing discipline, each resolved as a Decision below.
+
+* Goals and Non-Goals
+
+** Goals
+- One serialized runner per project: passes never overlap each other, and a fire that finds a prior fire still running skips.
+- A shared-file lock that actually serializes same-host agents writing public roam files.
+- Crash-lossless to one-pass granularity: session-context entry plus a commit after every disk-writing pass.
+- Main stays clean: overnight commits land on a host-suffixed sentry branch so cross-machine pulls and the template sync keep working.
+- Nothing destructive fires unattended: trash/mark-read, regrades, and file moves queue for morning approval.
+- Portable across projects with zero configuration: passes detect their targets and skip where not applicable.
+
+** Non-Goals
+- No report-only or degraded pass mode. A pass runs fully or it doesn't run (Craig's explicit direction). The morning-approval queue is a permanent division of labor, not a degraded mode.
+- No cross-host locking. The lock is host-local; cross-host races stay with roam-sync's abort-loudly rebase.
+- No unattended /schedule contract. Sentry runs inside a live session Craig starts, driven by /loop, the same boundary inbox.org draws for auto inbox zero.
+- Sentry never runs git against the roam repo. roam-sync remains that repo's only committer.
+
+** Scope tiers
+- v1: the sentry.org engine (ten mechanical passes, entry gates, branch mechanics, digest, the stop-sentry operation), the agent-lock helper script with bats tests, the roam-write lock adopted by every roam write path, companion-file reconciliations (knowledge-base.md write recipe, inbox.org core §5, roam-sync.sh header, triage-intake.org note, wrap-it-up.org active-sentry guard), INDEX.org entry.
+- Out of scope: changes to roam-sync.sh behavior; changes to the wrap-up teardown feature.
+- vNext (logged to todo.org): cross-host roam conflict surfacing; the KB lesson-promotion pass (blocked on the lesson-detection heuristic task); a fully-unattended /schedule variant if the interactive shape proves too narrow.
+
+* Design
+
+** For Craig (the user altitude)
+
+You type the trigger in a project session ("run sentry", or the /loop line it expands to). Sentry first checks its entry ticket: the project's notes.org must carry :COMMIT_AUTONOMY: yes, or sentry declines to start and names the marker. Then the entry gates run while you're still at the terminal: a dirty tracked tree stops and describes what's dirty, file by file, and offers numbered options (finish the job, stash it, or name changes to roll back); a red test suite stops the same way with an offer to investigate. The loop starts only once both are answered.
+
+Launching is a handoff — the launch contract: sentry owns the repo until the morning merge. Working in that repo mid-night means saying "stop sentry" first (it cancels the loop and walks the branch and queue disposition), and open Emacs buffers on project files want a revert after launch and again after the merge. "Wrap it up" while sentry is active refuses and points at "stop sentry".
+
+Each fire (hourly by default) walks the pass list serially and appends one timestamped digest to the session log: failures as banners on top, then one line per pass, then the morning-approval queue. A pass with nothing to do is one line. Anything destructive or judgment-shaped waits in that queue for you.
+
+In the morning you review the sentry/<date>-<host> branch (a stack of small unpushed chore(sentry) commits), squash-merge it into your working branch, delete it, and answer the approval queue. If a night's branch is still unmerged when sentry next starts, that fire skips and says so.
+
+** For the implementer
+
+*The engine.* A synced template workflow, .ai/workflows/sentry.org, driven by /loop <interval> (default hourly). Each fire acquires the single-runner lock, verifies branch state, walks the pass list, writes the digest, commits the spine residue, and releases the lock. The runner re-touches the lock between passes (the heartbeat), so a live run's lock is never older than one pass. Every pass follows one contract: probe, work, session-context entry, then a commit when the pass wrote to disk. The probe asks whether the pass's target exists in this project; when it doesn't, the pass is one skip line and nothing more. The session-context entry precedes the commit so a crash between them still leaves the trail.
+
+*Branch mechanics.* At loop start (after the entry gates and a fetch-and-ff-only reconcile of the project branch — a diverged branch joins the interactive gate), sentry creates sentry/YYYY-MM-DD-<host> from HEAD and checks it out; the host suffix prevents a same-date collision between the daily drivers. Commits are conventional, one per writing pass: chore(sentry): <pass> — <what changed>. Nothing pushes. At entry, an existing unmerged sentry/* branch means skip-and-note. No second branch stacks on the first. Morning teardown (review, squash-merge, delete) is Craig's, documented in the workflow, never automated.
+
+*Locks.* One helper script, .ai/scripts/agent-lock, serves both locks. flock is unusable here: every Bash call an agent makes is its own short-lived shell, so a flock dies with the call that acquired it. Instead: atomic mkdir as acquire (create-and-win or fail-and-lose), a metadata file inside recording PID, hostname, and ISO timestamp, and age-based staleness reclaim so a crashed run's lock expires instead of wedging every later fire. Subcommands: acquire <name> [--ttl], release <name>, status <name>. Lock homes: /run/user/<uid>/agent-locks/<name>/ for both locks — the helper owns the path scheme, callers pass names — with ~/.cache/agent-locks/ as the fallback where no runtime dir exists. tmpfs makes a lock host-local by construction, keeps it out of every repo (a lock inside ~/org/roam would ride roam-sync's git add -A to the other machine as a phantom hold), and clears it on reboot. Contention is a bounded wait (~30s), then defer-and-note. With the heartbeat keeping a live lock young, TTLs size to the longest single pass; every reclaim surfaces in the digest.
+
+*Roam writes.* A pass touching a public roam file acquires the roam-write lock, runs capture-guard --wait (the human-capture layer stays underneath), edits the working tree, triggers roam-sync with systemctl --user start roam-sync.service, and releases the lock. Sentry never runs a git write against ~/org/roam, and pass 1's ff-only pull is its only git read of that repo. The 06-24 one-git-owner rule holds. The lock spans only edit-plus-trigger; roam-sync itself is serialized by systemd (a oneshot unit never runs concurrently with itself).
+
+*Unattended safety.* With no one at the terminal, any unsafe state (unexpected dirty tree, red suite, lost lock, unmerged prior branch) makes the affected scope (the pass, or the whole fire, whichever the state poisons) skip with one digest line. The next fire retries; if the state persists to morning, the interactive entry gate handles it with Craig present. Skips are never silent and never partial: no pass runs in a reduced form. Two refinements keep the machinery honest: the dirty check excludes the spine set (session-context files, path resolved via session-context-path) so sentry's own bookkeeping can't trip it, and after the second consecutive fire skipped on an unmerged prior branch, sentry sends one persistent desktop notification naming the project and branch, then repeats at most daily — a multi-day stall never stays silent.
+
+*Pass list (v1), in order, each with its detection probe:*
+
+1. Roam pull — git -C ~/org/roam pull --ff-only, skipped when the tree is dirty (roam-sync owns that case) or the clone is absent. Read-only; the one narrow exception to "don't touch roam git," pull-only and ff-only, so later reads are fresh.
+2. Inbox zero — inbox.org roam mode, run under the engine's no-approvals contract (quick+solo+agreed items execute; shared-asset proposals park; everything lands in the morning queue). Probe: roam clone or project inbox/ exists.
+3. Triage intake — triage-intake.org. Probe: triage plugins present. Destructive actions queue.
+4. Todo cleanup — clean-todo.org mechanics. Probe: root todo.org.
+5. Task audit — task-audit.org. Probe: root todo.org. Regrades queue.
+6. Working-files hygiene — flag working/<slug>/ dirs whose task is closed. Probe: working/ exists. Filing queues.
+7. Spec status board — the docs-lifecycle grep. Probe: docs/specs/ exists.
+8. Link integrity — broken file: links in the project's org files. Probe: lint-org.el present.
+9. Git health — drift, unpushed commits, stale branches, main-behind-origin. Probe: .git.
+10. Prep + symlink freshness. Probe: the prep dir / symlinks exist (work and home only, in practice).
+(KB lesson promotion, the proposal's eleventh pass, is deferred to vNext — see the KB finding and the filed lesson-detection-heuristic task. v1 ships the ten mechanical passes above.)
+
+*Digest.* Appended per fire to the session-context.org Session Log (which the spine already writes), so it survives a crash, rides the session archive, and is on screen in the running session. The morning-approval queue accumulates under one heading in the same file — each item carries what, why, and the exact command or edit that fires on approval.
+
+* Alternatives Considered
+
+** Separate crons per task (status quo direction)
+- Good, because each piece stays independently simple.
+- Bad, because nothing serializes them: the observed sync-conflict forks are this cost, already paid.
+- Bad, because N crons means N session lifecycles to manage instead of one.
+
+** flock for both locks (as proposed)
+- Good, because it's the standard tool and kernel-enforced.
+- Bad, because it binds to a living process; agent tool calls are short-lived shells and /loop turns share none, so the lock evaporates on return. Disqualifying.
+
+** Per-pass commits on the current branch (as proposed)
+- Good, because commits sit where the edits apply; no morning merge step.
+- Bad, because unpushed commits on main on two daily drivers diverge main by morning, the exact state startup's fast-forward refuses and the template-sync guard blocks on. Disqualifying with two machines.
+
+** Degraded report-only mode for unsafe states
+- Good, because some findings still land when committing is unsafe.
+- Bad, because it blurs the contract: a pass that half-ran reads as having run. Craig rejected it outright; skip-and-note is the replacement.
+
+** Per-project pass manifest instead of detection
+- Good, because explicit control over what runs where.
+- Bad, because it's one more thing to maintain and forget; detection activates a pass the day its target appears. Rejected for v1.
+
+* Decisions [10/10]
+
+** DONE Commit target: host-suffixed sentry branch
+- Context: overnight commits must survive two daily drivers that both pull main; unpushed commits on main diverge it across machines, breaking startup fast-forward and the template-sync guard.
+- Decision: We will commit each writing pass to sentry/YYYY-MM-DD-<host>, created from HEAD at loop start, never pushed. Morning flow: review, squash-merge, delete. An unmerged prior sentry branch at entry skips the fire.
+- Consequences: easier — main's ref stays clean for cross-machine sync; a bad night is discarded by deleting one branch. Harder — a daily merge step; the working tree sits on a non-main branch overnight.
+
+** DONE Roam-write lock scope: host-local only
+- Context: observed conflicts are same-host multi-agent; cross-host races go through roam-sync's rebase, which aborts loudly.
+- Decision: We will ship the host-local lock and file cross-host conflict-surfacing as a separate task.
+- Consequences: easier — small, testable v1. Harder — a true cross-host race still forks; we accept the rarity.
+
+** DONE Interval default: hourly, with a knob
+- Context: passes short-circuit in seconds when idle; the real cost is digest noise, not compute.
+- Decision: We will default to hourly and expose the interval as the /loop argument.
+- Consequences: easier — projects stay clean through the night. Harder — a capture-heavy evening produces several small commits; the knob is the escape hatch.
+
+** DONE Entry gates are interactive, not silent
+- Context: Craig types the sentry command in, so the first fire runs with him at the terminal.
+- Decision: We will stop at entry on a dirty tracked tree (describe what's dirty; offer finish-the-job / stash / rollback-named-changes) and on a red suite (describe failures; offer to investigate). The loop starts only after both are answered. Untracked files never block.
+- Consequences: easier — no guessing about his in-progress work. Harder — sentry can't start unattended from a dirty state; that's the point.
+
+** DONE No report-only mode; unsafe unattended states skip
+- Context: a degraded pass mode blurs whether a pass ran; Craig rejected it explicitly.
+- Decision: We will make every pass run fully or not at all. Unattended unsafe states (dirty tree, red suite, lost lock, unmerged prior branch) skip the poisoned scope with one digest line; the next fire retries. The morning-approval queue for destructive/judgment actions stays — it's a permanent contract, not degradation.
+- Consequences: easier — a pass line in the digest means it fully ran. Harder — a persistent unsafe state means zero hygiene until morning; accepted.
+
+** DONE Roam git discipline: roam-sync stays the only committer
+- Context: the proposal had sentry commit-and-push roam under the lock; the 06-24 fix made roam-sync the repo's single git owner because the tree is chronically dirty from live captures.
+- Decision: We will wrap only edit-plus-trigger in the roam-write lock; sentry never runs git against ~/org/roam (pass 1's ff-only pull is the sole, read-only exception).
+- Consequences: easier — one git owner, no mid-rebase states from agents. Harder — an edit lands remotely only when roam-sync fires; the manual trigger closes most of that gap.
+
+** DONE Lock mechanics: mkdir-atomic helper with staleness reclaim
+- Context: flock can't span tool calls (short-lived shells, no shared process across /loop turns).
+- Decision: We will build .ai/scripts/agent-lock — atomic mkdir acquire, PID/host/timestamp metadata, age-based reclaim, acquire/release/status subcommands, bats-tested — and use it for both locks.
+- Consequences: easier — locks survive between calls and self-clear after crashes. Harder — TTL tuning; a reclaim during a genuinely slow pass is possible, so the helper surfaces every reclaim rather than reclaiming silently.
+
+** DONE Autonomy gate: :COMMIT_AUTONOMY: yes is the entry ticket
+- Context: commits.md gates commits on approval; sentry commits unattended, so it needs standing, per-project authorization.
+- Decision: We will have sentry decline to start in any project whose notes.org lacks :COMMIT_AUTONOMY: yes, naming the marker.
+- Consequences: easier — running sentry somewhere is a deliberate grant; no half-running mode to reason about. Harder — read-only passes don't run in ungranted projects either; that's acceptable, since launch is one marker away.
+
+** DONE Pass portability: detection over configuration
+- Context: several proposed passes are work/home-specific; run verbatim elsewhere they're vacuous or error.
+- Decision: We will open every pass with a cheap existence probe and skip with one digest line when the target is absent.
+- Consequences: easier — zero config, passes self-activate when targets appear. Harder — an intentionally-unwanted pass needs a vNext exclusion marker if that ever becomes real.
+
+** DONE Suite policy: entry run plus conditional fire-end run, no per-pass runs
+- Context: the verification discipline requires a full suite run before every commit, but sentry commits per pass, hourly, mostly touching org files the suite doesn't exercise; a per-commit run would turn a seconds-long fire into minutes, all night.
+- Decision: We will run the suite once at entry (the green baseline the gates require) and again at fire-end only when a pass modified files outside the org/spine set. No per-pass runs. The deviation is justified by the unpushed branch and the morning review gating everything before push.
+- Consequences: easier — idle fires stay cheap and the rare code-touching pass is still caught before its commits age. Harder — a suite break introduced by an org-only edit (possible via fixtures) surfaces at morning review rather than at the offending commit.
+
+* Review findings [12/12]
+
+** DONE Overnight working-tree ownership is undefined
+Resolved 2026-07-14 (Craig): launch contract with in-place checkout. Launching sentry hands the repo to sentry until the morning merge; reclaiming it mid-night means stopping the loop first. The entry gate (clean tree, Craig present) fronts the handoff, the unattended dirty-skip backstops anything that slips, and the workflow documents the Emacs buffer-revert caveat at launch and after the morning merge. Worktree isolation was rejected because untracked inbox drops exist only in the main tree, which blinds the inbox pass; plumbing commits were rejected as fragile.
+
+** DONE Roam-write lock guards nothing unless every roam writer acquires it
+Resolved 2026-07-14 (Craig): Phase 3 strengthened from notes to mandatory write-path changes. inbox.org core §5 gains acquire/release around the Phase D edit; knowledge-base.md's write recipe gains the same around its write block. Both degrade gracefully when the helper isn't installed (proceed unlocked — today's behavior); an interactive caller finding the lock busy does a bounded wait (~30s) then surfaces to the user rather than proceeding unlocked.
+
+** DONE Roam lock location gets committed by roam-sync
+Resolved 2026-07-14 (Craig): both locks live under /run/user/<uid>/agent-locks/<name>/, with ~/.cache/agent-locks/ as the fallback where no runtime dir exists. The agent-lock helper owns the path scheme; callers pass names, never paths. tmpfs gives host-locality by construction and reboot self-clearing for free.
+
+** DONE Sentry's own spine writes trip its dirty-tree skip
+Resolved 2026-07-14 (Craig), all three parts: the unsafe-state dirty check excludes the spine set (session-context.org / session-context.d/), each fire ends with one digest commit that sweeps accumulated spine writes so read-only fires still leave a clean tree, and the spine path resolves through .ai/scripts/session-context-path so a concurrent agent's anchor is never clobbered.
+
+** DONE Pass 8 depends on a known data-corrupting bug
+Resolved 2026-07-14, mid-review: the lint-org fix landed in 951b6fc — block-type-aware scanning in both helpers, CLI report-only by default with writes behind --fix, and the true corruption path (wrap-org-table's load-time dispatch firing on lint-org's require) guarded to entry-script-only. Pass 8 runs against the fixed linter; the prerequisite is satisfied and no Implementation-phases dependency is needed.
+
+** DONE Unattended inbox-pass semantics are undefined
+Resolved 2026-07-14 (Craig): sentry runs inbox processing under inbox.org's no-approvals contract verbatim — quick+solo+agreed items execute, shared-asset and convention proposals park (prepared diff, VERIFY task, sender reply). Everything the pass did lands in the morning queue, and anything the engine would ask interactively defers to the queue instead of blocking the fire.
+
+** DONE Per-pass commits skip the pre-commit suite run — undecided convention conflict
+Resolved 2026-07-14 (Craig): recorded as a Decision (see Decisions — suite policy). Entry run establishes the green baseline; no per-pass runs; a fire-end suite run fires only when a pass modified files outside the org/spine set, so the rare code-touching pass is caught before its commits age overnight. Justification recorded with both halves of the consequences.
+
+** DONE KB promotion pass has no defined lesson source
+Resolved 2026-07-14 (Craig): pass 11 is cut from v1 and deferred to vNext. KB promotion stays a wrap-up concern — the editorial moment where Craig answers the promotion prompt. An unattended judgment pass writing to the shared KB waits until sentry has quiet weeks behind it AND a designed detection heuristic; the heuristic design is filed in todo.org ([#D] "KB lesson-detection heuristic", which blocks re-adding the pass).
+
+** DONE Lock contention and reclaim mechanics are half-specified
+Resolved 2026-07-14 (Craig): both mechanics adopted. Contention: bounded wait (~30s, capture-guard's --wait shape), then defer — the pass skips with a digest line and the next fire retries. Reclaim: heartbeat refresh — the runner re-touches the lock timestamp between passes, so a live run's lock is never older than one pass and the TTL sizes to the longest single pass (minutes). Every reclaim surfaces in the digest.
+
+** DONE Wrap-up while the loop is live is undefined
+Resolved 2026-07-14 (Craig): wrap-up refuses while sentry is active. It detects the live single-runner lock and stops with "sentry is active — say 'stop sentry' first." The stop-sentry operation is defined in sentry.org and owns the shutdown: cancel the loop, walk the branch disposition (squash-merge now or leave named), walk-or-carry the approval queue. Wrap-up itself gains only the one guard; the shutdown logic lives with sentry.
+
+** DONE Entry should reconcile the project branch before branching
+Resolved 2026-07-14 (Craig): adopted. After the entry gates pass, sentry runs the same fetch-and-ff-only reconcile startup uses, then creates the branch. A diverged branch joins the interactive entry gate (Craig is present at launch) rather than being auto-resolved.
+
+** DONE Multi-day unmerged branch stalls hygiene silently
+Resolved 2026-07-14 (Craig): adopted. After the second consecutive fire skipped for the unmerged-branch reason, sentry sends one persistent desktop notification naming the project and branch ("sentry stalled: <branch> unmerged — merge or delete to resume"), then repeats at most daily. Persistent notify matches the paging convention: it stays on screen until dismissed.
+
+* Implementation phases
+
+** Phase 1 — agent-lock helper
+.ai/scripts/agent-lock (canonical: claude-templates/.ai/scripts/) with bats tests: acquire/release/status, contention, staleness reclaim, metadata. Tree stays working; nothing calls it yet.
+
+** Phase 2 — sentry.org engine
+The workflow file: entry ticket, interactive gates, branch mechanics, pass runner with the probe→work→log→commit contract, digest + approval queue, skip semantics. INDEX.org entry. Mirror synced.
+
+** Phase 3 — companion reconciliations
+knowledge-base.md write recipe (acquire/release the roam-write lock around the write block; edit-plus-trigger replaces inline pull/commit/push); inbox.org core §5 (acquire/release around the Phase D edit, capture-guard staying as the human layer underneath; graceful degradation when the helper is absent); roam-sync.sh header comment; triage-intake.org note (runs as a sentry pass; own triggers kept); wrap-it-up.org active-sentry guard (refuse and point at "stop sentry").
+
+** Phase 4 — verification
+make test green; a live trial night on rulesets (ratio): entry gates exercised, one fire observed end to end, morning branch review performed; follow-up tasks filed from what the trial surfaces.
+
+* Acceptance criteria
+- [ ] agent-lock: two concurrent acquires produce exactly one winner; release frees; a stale lock (aged past TTL) is reclaimed with a surfaced note; a busy lock is a bounded wait then defer; heartbeat refresh keeps a live lock young; locks live under the runtime dir (cache fallback), never inside a repo; bats suite green.
+- [ ] Sentry declines to start without :COMMIT_AUTONOMY: yes, naming the marker.
+- [ ] Entry on a dirty tracked tree stops with the file-by-file description and the three options; untracked files don't trigger it.
+- [ ] A fire on a clean project produces one digest with every pass either run or skipped-with-reason; no other output shape exists.
+- [ ] Writing passes commit individually to sentry/<date>-<host>; main's ref is untouched; nothing is pushed.
+- [ ] A roam-writing pass acquires the roam-write lock, passes capture-guard, edits, triggers roam-sync, releases, and never runs git write commands against ~/org/roam.
+- [ ] An unmerged prior sentry branch at entry causes a skip-and-note, not a second branch.
+- [ ] Destructive/judgment actions appear only in the approval queue, never executed unattended.
+- [ ] On a project missing a pass target (no todo.org, no triage plugins), the pass skips with one line and the rest run.
+- [ ] Entry reconciles the project branch ff-only before creating the sentry branch; a diverged branch stops at the interactive gate.
+- [ ] Sentry's own spine writes never trip the dirty-tree skip (a full fire of read-only passes ends with a clean tree via the digest commit).
+- [ ] "Wrap it up" during an active loop refuses and names "stop sentry"; "stop sentry" cancels the loop and walks the branch and queue disposition.
+- [ ] The second consecutive unmerged-branch skip produces one persistent desktop notification; repeats are at most daily.
+- [ ] The inbox pass parks a shared-asset proposal (VERIFY + prepared diff + sender reply) rather than applying it, and the morning queue lists everything the pass did.
+
+* Readiness dimensions
+- Data model & ownership: locks own their directories under /run/user/<uid>/agent-locks/ (tmpfs, outside every repo); digest and queue live in session-context.org (session-owned, path via session-context-path); commits on the sentry branch are Craig-authored per commits.md.
+- Errors, empty states & failure: every skip names its reason in the digest; a failed pass banners and the rest continue; the digest surfaces lock-reclaim events. No silent outcomes.
+- Security & privacy: no credentials touched; KB promotion honors the knowledge-base.md work-project refusal contract; commit messages follow the no-tooling-enumeration rule where applicable.
+- Observability: the digest is the observability surface (per-fire, timestamped, failures on top); session-context.org persists it across crashes.
+- Performance & scale: idle fire is seconds (probes + short-circuits); heaviest pass is triage (network); hourly cadence assumed fine; the knob covers it.
+- Reuse & lost opportunities: reuses inbox.org, triage-intake.org, clean-todo, task-audit, lint-org.el, capture-guard, roam-sync wholesale; the only new code is agent-lock and the engine file.
+- Architecture fit & weak points: engine+passes mirrors the engine+plugin convention; weak points are lock TTL tuning (reclaim vs slow pass) and /loop session lifetime (a killed session ends sentry; the lock's TTL clears the residue).
+- Config surface: /loop interval; :COMMIT_AUTONOMY: marker; lock TTLs as script flags with defaults. Nothing else.
+- Documentation plan: the workflow file is the doc (When to Use + the morning-review section); INDEX.org entry; no README change needed.
+- Dev tooling: bats for agent-lock via the existing glob-discovered suites; make test covers it with no Makefile edit.
+- Rollout, compatibility & rollback: additive, new files plus comment-level reconciliations; no consuming project changes behavior until Craig launches sentry there. Rollback is deleting the branch and not launching.
+- External APIs & deps: none beyond tools already in use (git, systemctl, bats). N/A for schema verification.
+
+* Risks, Rabbit Holes, and Drawbacks
+- The heartbeat bounds the TTL question to a single pass, but a pass that legitimately outruns its TTL (a slow triage sweep on a bad network) can still be reclaimed under itself. Start generous, surface every reclaim, tune from the digest.
+- /loop lifetime: sentry dies with its session (harness restart, machine sleep). Acceptable for v1: the morning state is a readable branch plus session-context, and nothing corrupts.
+- Digest fatigue: hourly fires on an active evening could stack noise. The interval knob and the one-line-when-idle rule are the mitigations; revisit after a week of use.
+- The sentry branch holds todo.org/notes.org edits overnight; Craig editing those files on main before the morning merge makes the squash-merge conflict. That conflict is small, self-inflicted, and resolvable by hand; the morning-review section documents it.
+
+* Review and iteration history
+
+** 2026-07-14 Tue @ 02:03:28 -0500 — Claude Code (rulesets) — responder
+- What: all 12 findings walked with Craig one by one and dispositioned; each completed in place and folded into the body. Load-bearing calls: launch contract with in-place checkout (worktrees rejected — untracked inbox drops are invisible there); every roam writer adopts the lock; locks relocate to the XDG runtime dir; spine-set exclusion + fire-end digest commit; inbox pass runs the no-approvals contract; suite policy recorded as Decision 10; KB promotion pass cut to vNext (heuristic task filed); heartbeat + bounded-wait lock mechanics; wrap-up refuses during a live loop ("stop sentry" owns shutdown); entry ff-only reconcile; stall notify after two skips. Mid-walk, Craig redirected the pass-8 finding into fixing the lint-org bug immediately — landed as 951b6fc, which also found and closed the true 2026-07-09 corruption path (wrap-org-table's load-time dispatch firing on lint-org's require).
+- Why: Craig chose to resolve every open question together rather than batch them into a written response round.
+- Artifacts: findings section above (12/12 done); todo.org tasks (KB heuristic [#D], bug task closed); commit 951b6fc.
+
+** 2026-07-14 Tue @ 01:15:00 -0500 — Claude Code (rulesets) — reviewer
+- What: adversarial weakness review at Craig's direction — batches of candidate weaknesses checked against the spec, looped until a round produced fewer than five substantive new ones. Round 1: 20 candidates, 11 answered or mitigated by the spec, 9 became findings. Round 2: 7 candidates, 3 answered, 4 folded into findings. Round 3 produced fewer than five substantive candidates; loop terminated. Result: 10 blocking + 2 non-blocking findings recorded above. Rubric: Not ready until the blocking findings are dispositioned. Status stays DRAFT for Craig's deeper review.
+- Why: Craig asked for the weakness loop before his own deep review, in place of a standard first-pass spec-review.
+- Artifacts: findings grounded in code reads — roam-sync.sh:28 (git add -A commits anything inside the roam repo), .ai/scripts/session-context-path (AI_AGENT_ID scoping), todo.org [#B] "Org-table helpers corrupt example blocks" (lint-org mutate-on-lint), inbox.org core §2 no-approvals park path, verification.md pre-commit suite rule.
+
+** 2026-07-14 Tue @ 00:52:03 -0500 — Craig Jennings — author
+- What: initial draft.
+- Why: work project's sentry proposal accepted after skeptical review; nine design decisions resolved live in-session, recorded under Decisions.
+- Artifacts: [[file:../design/2026-07-14-sentry-workflow-proposal.org][origin proposal]]; todo.org sentry build task.
diff --git a/docs/specs/agent-knowledge-base-spec.org b/docs/specs/agent-knowledge-base-spec.org
index e36d897..d5c0ce7 100644
--- a/docs/specs/agent-knowledge-base-spec.org
+++ b/docs/specs/agent-knowledge-base-spec.org
@@ -1,5 +1,5 @@
#+TITLE: Agent Knowledge Base on Org-roam — Spec
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-10
#+TODO: TODO | DONE
#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED
diff --git a/docs/specs/inbox-workflow-consolidation-spec.org b/docs/specs/inbox-workflow-consolidation-spec.org
index 4543e77..24ce30a 100644
--- a/docs/specs/inbox-workflow-consolidation-spec.org
+++ b/docs/specs/inbox-workflow-consolidation-spec.org
@@ -1,5 +1,5 @@
#+TITLE: Inbox Workflow Consolidation — Spec
-#+AUTHOR: Craig Jennings & Claude
+#+AUTHOR: Craig Jennings
#+DATE: 2026-06-23
#+TODO: TODO | DONE
#+TODO: DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED