aboutsummaryrefslogtreecommitdiff
path: root/todo.org
diff options
context:
space:
mode:
Diffstat (limited to 'todo.org')
-rw-r--r--todo.org507
1 files changed, 225 insertions, 282 deletions
diff --git a/todo.org b/todo.org
index 577679d..cdf2397 100644
--- a/todo.org
+++ b/todo.org
@@ -39,16 +39,179 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest
* Rulesets Open Work
+** TODO [#B] Sentry workflow — build from spec :feature:
+Build the sentry supervisor workflow from the READY-gated spec:
+[[file:docs/specs/2026-07-14-sentry-workflow-spec.org][sentry workflow spec]] (DRAFT as of 2026-07-14; needs spec-review first).
+Four phases: agent-lock helper + bats, sentry.org engine, companion
+reconciliations (knowledge-base.md, roam-sync.sh header, inbox.org,
+triage-intake.org), live trial night on rulesets. Origin: work project's
+proposal, [[file:docs/design/2026-07-14-sentry-workflow-proposal.org][docs/design/2026-07-14-sentry-workflow-proposal.org]]. All nine
+design decisions resolved with Craig 2026-07-14 (recorded in the spec).
+
+** TODO [#C] Polyglot projects — supported, or refused? :spec:
+SCHEDULED: <2026-07-20 Mon>
+Do we support more than one language bundle per project? The honest answer today
+is "partly, by accident." The collision guard added 2026-07-16 refuses a
+*colliding* second bundle rather than silently replacing the first's config, but
+a non-overlapping pair still installs fine: bash ships =settings.json= +
+githooks and no coverage fragment, python ships only a coverage fragment, so
+=bash= + =python= composes cleanly today and yields a real polyglot project with
+both rule sets. So the line isn't polyglot-vs-not, it's overlap-vs-not — and
+nobody chose that line, it fell out of which bundle happens to ship what. Origin:
+home's report after scaffolding clock-panel with python + typescript,
+[[file:docs/design/2026-07-16-polyglot-bundle-collision.txt][docs/design/2026-07-16-polyglot-bundle-collision.txt]].
+
+Pair this with the subproject scouting below — it's the same question in a
+different costume ("which projects would actually be polyglot, and why"), so
+they should be one conversation.
+
+The three options, in the order they'd be weighed:
+
+1. *Unsupported, explicitly.* Keep the guard as the answer. Cheapest, and
+ matches how little polyglot exists (one project, clock-panel).
+2. *Supported.* Needs per-bundle filenames, a merged =settings.json= (the hooks
+ arrays compose rather than clobber), composed githooks, and namespaced
+ Makefile targets with a =coverage= aggregate. This is the real work.
+3. *Case-by-case.* Support the pairs that come up, refuse the rest.
+
+What the decision needs to know:
+
+- *The target-name collision is the deeper half* (home's point, and it's right).
+ Every bundle's fragment defines =coverage:= and =coverage-summary:=, so even
+ with both files present a polyglot project can't paste both into one Makefile.
+ Renaming files doesn't fix it.
+- *Only three of five shared filenames actually collide.* =gitignore-add.txt=
+ (5 bundles) appends deduped and composes. =CLAUDE.md= (3) is seed-only, and
+ its fallback comment shows multi-bundle was already considered there.
+ =claude/settings.json= (3), =githooks/*= (3), and =coverage-makefile.txt= (4)
+ are the real ones.
+- *=FORCE=1= is a poor escape hatch* (home's catch): it also re-seeds
+ =CLAUDE.md=, which is destructive on a customized project. If polyglot
+ becomes supported, the override wants to be its own flag.
+
+** TODO [#C] Subproject pattern — promote to claude-rules? :spec:
+SCHEDULED: <2026-07-20 Mon>
+home proposes promoting its subproject pattern (a former standalone project
+folded into a parent, living as a self-contained subdir sharing the parent's
+=.ai/= scope) into the rules layer: vocabulary, the read-first
+=<subproject>/<subproject>-brief.org= convention, the parent-vs-subproject
+content criterion ("one fact, one home"), and create/archive criteria.
+Proposal + home's full instance:
+[[file:docs/design/2026-07-15-subproject-pattern-proposal.org][proposal]],
+[[file:docs/design/2026-07-15-subprojects-convention-home-instance.org][home's convention doc]].
+
+Deferred 2026-07-16 rather than promoted. *Craig's framing:* he wants to scout
+which projects would actually get subprojects, and why, before we shape a rule.
+If he hasn't done that scouting by the time this comes up, offer to do it
+together — brainstorm the candidates, then explore the reasons behind each. That
+evidence decides it: either we drop the pattern, or we know enough to adjust it
+so it's effective. Don't shape the rule before the scouting.
+
+*Review findings from the 2026-07-16 pass* (the inputs the decision needs):
+
+- *N=1.* home is the only project with subprojects, across all 27 =.ai= scopes;
+ its nine all came from the single 2026-06-11 fold. This is the fact the
+ scouting tests.
+- *Placement contradicts the proposal's own principle.* =claude-rules/*.md=
+ loads into every session of every project. home's doc argues the always-on
+ layer is "a tax paid whether or not it's relevant today" and depth belongs
+ "one open away". At 282 lines the doc would be the third-largest rule and add
+ ~11% to the always-on layer, so every .emacs.d / takuzu / chime session would
+ carry a one-project convention.
+- *Precedent for the shape:* =patterns.md= (29 lines, explicit "don't carry the
+ catalog in context") and =docs-lifecycle.md= (75 lines, depth in a spec).
+ Thin rule + on-demand depth is the established answer.
+- *Dangling reference:* the doc cites =claude-rules/git-hosting-privacy-model=
+ as authority for its shared-scope-safety criterion. No such file exists — the
+ real content is the gitignore-vs-track and public-reachability decision in
+ =protocols.org=. Fix before any promotion.
+- *Instance vs rule:* the metrics, self-improvement log, kill criteria, rollout
+ dates, and adoption table are home's instance, not rule content.
+
+** TODO [#D] Cross-host roam conflict surfacing :feature:
+Deferred from the sentry spec (Decision: host-local lock only). The
+roam-write lock serializes same-host agents; a true cross-host race still
+forks a sync-conflict file, surfaced only by roam-sync's rebase abort.
+Investigate making the conflict loud (roam-sync retry-once, or a
+cross-host lock node in the repo) when it proves frequent in practice.
+
+** TODO [#D] KB lesson-detection heuristic :spec:
+Deferred from the sentry spec review (2026-07-14): sentry's KB promotion pass was cut from v1 because "promote recent durable lessons" had no defined lesson source, and an unattended judgment pass writing to the shared KB needs one that doesn't spam or go silent. Design the detection heuristic — candidate source: Session Log entries and memory-dir changes since the last fire, judged against knowledge-base.md's inclusion bar — and define how precision gets verified before the pass ships in a sentry vNext. Blocks re-adding the pass.
+
+** TODO [#D] Sentry unattended /schedule variant :feature:
+Deferred from the sentry spec (Non-Goal). The v1 contract is interactive:
+a live session Craig starts, /loop-driven. A cron variant firing with no
+session needs its own contract (mutation rights, async surfacing, dedup
+state across runs) — same open questions inbox.org logs for unattended
+auto inbox zero. Take up only if the interactive shape proves too narrow.
+
+** TODO [#C] ai launcher hardening — bug hunt + refactor pass :refactor:solo:
+:PROPERTIES:
+:CREATED: [2026-07-13 Mon]
+:LAST_REVIEWED: 2026-07-13
+:END:
+From the roam inbox (2026-07-13): find bugs in =bin/ai= until no more are visible, then refactor until no worthwhile opportunities remain. The launcher gained =--runtime= + =--print-launch= + 6 bats tests today (04c3b29); this pass extends coverage over the legacy paths (multi_mode, sort_windows, git prep, window sorting) and cleans as it goes. The runtime-selection asks from the same capture are tracked on the generic-agent-runtime parent (claude/codex shipped; ollama/qwen pending the model-floor eval); an interactive runtime picker is fair game here if it stays lightweight.
+
+** TODO [#B] Extend ui-prototyping rule with build-to-prototype :feature:
+:PROPERTIES:
+:CREATED: [2026-07-11 Sat]
+:LAST_REVIEWED: 2026-07-13
+:END:
+.emacs.d proposal (2026-07-11, Craig-approved promotion), extending the ui-prototyping rule shipped tonight (=claude-rules/ui-prototyping.md=, 53f6ce6). The base rule covers research → prototypes → iterate → decisions-backed-by-a-prototype, but not the "build to the prototype" half. Add: after prototyping, fold what settled back into the spec and make the *build target* the prototype rather than the original spec text — the built feature should match the prototype, and any deviation is documented in a "Prototype & deviations" addendum section the build keeps current. Wire into four touch points: =brainstorm= Phase 3 (a UI design isn't "accepted" until it's been through the prototype loop; Next Steps say "build to the prototype"), =spec-create= (emit the deviations-addendum section for UI specs), =spec-response= (a UI spec decomposes into a prototype loop first, then build-to-prototype tasks), =start-work= (its verify phase drives the UI end-to-end; the bar becomes "matches the prototype," deviations logged). Worked example: the takuzu Emacs game — colored tiles read as all-black in the real terminal frame (dark faces + GUI-only box cursor), caught by a prototype loop on the first screenshot where build-to-spec would have shipped an unusable board. Multi-asset synced-rule change, so review-gated and needs a focused session; decide brainstorm-vs-lifecycle placement with Craig. Source: =inbox/PROCESSED-2026-07-11-0222-from-.emacs.d-ui-prototype-rule-proposal.org=.
+
+** TODO [#C] Roam-only startup for .ai projects — investigate :spec:
+:PROPERTIES:
+:CREATED: [2026-07-11 Sat]
+:LAST_REVIEWED: 2026-07-13
+:END:
+Open question from the roam inbox (2026-07-11): could the startup sequence for .ai projects use org-roam as the single store instead of local files? Potential gain is near-guaranteed shared information across projects — lessons and proven techniques on a common thread, scannable across similar projects. It would need a way to isolate a project from the rest. Weigh the pros/cons, the risk, and whether it's worth it before any build. Exploratory, no commitment yet.
+
+** TODO [#C] Keep WIP from blocking the template sync gate :feature:
+:PROPERTIES:
+:CREATED: [2026-07-11 Sat]
+:LAST_REVIEWED: 2026-07-13
+:END:
+From the roam inbox (2026-07-11): work in progress in one project shouldn't stop the sync gate. Idea: keep all diffs/changes in a =working/= directory and exclude it (and its subdirectories) from the sync gate. Many projects run at once, so their WIP files need to be grouped. Also add a per-project count of when the gate tripped, tracked as a metric to investigate. Distinct from the 2026-07-02 policy (untracked/gitignored changes already pass — this is about *tracked* WIP under =working/=). Verify how the gate detects dirtiness today before designing.
+
+** TODO [#C] Put install-ai on PATH, launchable as =install-ai= :chore:quick:solo:
+:PROPERTIES:
+:CREATED: [2026-07-11 Sat]
+:LAST_REVIEWED: 2026-07-13
+:END:
+From the roam inbox (2026-07-11): make install-ai launchable as =install-ai= (no =.sh=) from PATH. dotfiles needs a copy that stays in sync with the rulesets canonical — decide whether the startup script-sync already covers it or a dedicated mechanism is needed.
+
+** TODO [#B] Document (and own) the Signal pager :feature:spec:
+:PROPERTIES:
+:CREATED: [2026-07-11 Sat]
+:LAST_REVIEWED: 2026-07-13
+:END:
+home retired the ntfy phone-notification channel (phone-notify/phone-recv, self-hosted ntfy on ratio) on 2026-07-04 in favor of paging over Signal, and tore ntfy down. The Signal pager it replaced ntfy with is undocumented: no pager script in =~/.local/bin=, and =notify= doesn't reference Signal. What exists on ratio: signal-cli 0.14.5, account 404211. Deliverable: a documented Signal pager (send + read-replies), the signal-cli setup/account notes, and the sync path — the Signal equivalent of the retired ntfy runbook. Cross-machine tooling, so canonical home + docs belong in rulesets.
+
+RECONCILE FIRST: =protocols.org= "Paging Craig" already documents an agent-paging path via the *signal-mcp* tool (=send_message_to_user=, pager account +15045173983, Craig's UUID =b1b5601e-…=, verified 2026-06-30). home's handoff is about a *different* mechanism (signal-cli / account 404211, home's own paging). Decide whether these are one channel or two, and whether the signal-cli side needs its own runbook or should route through signal-mcp. Source: home handoff 2026-07-04 (=inbox/2026-07-04-1302-from-home-task-for-rulesets-document-and-decide.org=). Successor to the 2026-06-17 two-way-comms proposal.
+
+*** 2026-07-13 Mon @ 05:16:37 -0500 Folded home's ownership ack + current-state report into the reconcile scope
+home confirmed (2026-07-11 reply) rulesets owns this task and the two-paths reconciliation is the right first step. New facts for the reconcile: from a home session on 2026-07-09, signal-mcp was NOT connected, and the local signal-cli is registered as Craig's own number — =send --note-to-self= returns a message id but produces no phone push. So home currently has no working ad-hoc page channel at all; whatever the runbook lands on must give home a live path.
+
+*** 2026-07-13 Mon @ 14:40:00 -0500 Added runtime-portability as a second motivation (Craig approved)
+The MCP portability inventory ([[file:docs/design/2026-07-13-runtime-portability-inventories.org]]) found signal-mcp exists only claude.ai-side — no local config anywhere — so a non-Claude agent (Codex-style or local LLM) has no paging path at all. The signal-cli runbook this task produces is therefore also the runtime-neutral page channel, not just home's replacement for ntfy.
+
+*** 2026-07-13 Mon @ 18:30:19 -0500 agent-page shipped — every project now knows both channels
+Craig's call: make paging universal and rename it the agent pager. NEW =claude-templates/bin/agent-page= (runs signal-cli directly on velox, ssh-relays from anywhere else, desktop-fallback hint on failure; 4 bats tests; live-verified from ratio through the real script). protocols.org "Paging Craig" rewritten around the two channels (notify desktop + agent-page phone; signal-mcp demoted to a velox-local nicety); page-me.org gained the phone section + fire-both guidance; work-the-backlog's end-of-set page names both surfaces; INDEX updated. Every project inherits via the startup sync + make install. Remaining here: the runbook proper, the receive timer, ssh-only vs linked-device.
+
+*** 2026-07-13 Mon @ 18:13:07 -0500 RECONCILED — one channel, on velox; live page verified end to end
+The two-paths question is answered: there is ONE pager identity, +15045173983, registered in velox's signal-cli (account file 465310) — and signal-mcp is a locally-configured MCP server in velox's global ~/.claude.json (not claude.ai-side as the 14:40 entry inferred; it's just invisible from ratio, which is why home and this session couldn't find it). ratio's signal-cli holds only Craig's personal number (note-to-self, no push). Verified live today: =ssh velox 'signal-cli -a +15045173983 send -m … b1b5601e-6126-47f8-afaa-0a59f5188fde'= buzzed Craig's phone — his remembered CLI page was this same account on velox. Reliability findings for the runbook: both accounts throw receive-staleness warnings (velox 40 days, ratio 26; the signal protocol wants regular receives — a systemd receive timer on velox is the roam-sync-shaped fix), and the channel requires velox to be up. Remaining deliverables sharpened: the runbook (send + read-replies + receive timer + account notes); decide ssh-over-tailnet-only vs registering ratio as a linked device of the pager account; update protocols.org "Paging Craig" (it names signal-mcp as the only supported path — true only on velox; the ssh recipe is the cross-machine path) — shared-asset edit, own review pass. Interim recipe sent to home so it's unblocked today.
+
** TODO [#C] KB orphan-node review pass :chore:
:PROPERTIES:
:CREATED: [2026-07-01 Wed]
+:LAST_REVIEWED: 2026-07-13
:END:
The 2026-07-01 kb-hygiene report listed 42 agent KB nodes with no inbound id: links (of 53 agent nodes; 0 conflicts, no duplicate titles). Orphan-ness alone isn't a defect — agent nodes are found by rg, not only by links — but a periodic pass is worth doing: prune nodes that aged out, merge near-duplicates, add id: links where clusters exist. Regenerate the list with the kb-hygiene script rather than trusting the snapshot. Propose deletions/merges to Craig before applying (auto-cleanup allowed only for :agent:-tagged nodes after approval, per knowledge-base.md).
** TODO [#B] Helper-agent instance support — concurrent same-project Claude :feature:spec:
:PROPERTIES:
:CREATED: [2026-06-11 Thu]
-:LAST_REVIEWED: 2026-06-24
+:LAST_REVIEWED: 2026-07-13
:END:
SPEC REVIEWED 2026-06-12: [[file:docs/design/2026-05-28-generic-agent-runtime-spec-review.org][Codex review]] now rates Phase 1.5 =Ready with caveats=. Before any build, keep the Emacs integration as a cross-project handoff to =~/.emacs.d=, preserve the three-ring gate (bats → sandbox drills → pilot project), and do not let startup/helper changes reach synced template paths until the live drills pass.
@@ -89,30 +252,10 @@ DEPENDENCY QUESTION (Craig, 2026-06-15, resolved 2026-06-24 — see below): does
*** 2026-06-24 Wed @ 00:30:32 -0400 RESOLVED — independent, unblocked (keyword VERIFY → TODO)
Craig's call (2026-06-24): helper-instance is independent of the generic-runtime refactor and builds on its own. The shipped pieces and the remaining wiring are all shared-file concurrency-safety (two Edit writers, one file), orthogonal to which LLM runtime launches — none of it assumes the runtime-manifest / multi-runtime machinery of phases 2-6. One caveat: =ai --helper= overlaps the launcher refactor the generic-runtime arc plans, so keep that launcher change small and contained so the later refactor doesn't fight it. Now a buildable [#B] task behind its own three-ring gate (bats → sandbox drills → live pilot).
-** DONE [#B] Wrap-up inbox/transcript routing to destination projects :feature:spec:
-CLOSED: [2026-07-04 Sat]
+** TODO [#B] Wrap-up routing — manual end-to-end validation :test:
:PROPERTIES:
-:CREATED: [2026-06-13 Sat]
-:LAST_REVIEWED: 2026-06-24
+:LAST_REVIEWED: 2026-07-13
:END:
-Optional wrap-up step that surfaces filed keepers belonging to another project, recommends a destination, and routes each to that project's =inbox/= via =inbox-send= (the destination's own =process-inbox= files it; transcript filing deferred to vNext). Spec: [[id:00b47414-2213-4a99-be35-48ceb266fc08][wrapup-routing-spec.org]] — Ready, [9/9] decisions. Source proposal: [[file:docs/design/2026-06-13-wrapup-inbox-transcript-routing-proposal.org]].
-
-*** 2026-06-21 Sun @ 02:06:37 -0400 Spec-review + spec-response complete — Ready
-Craig's review challenge reshaped the design from a direct cross-repo =todo.org= move to =inbox-send= delivery into the destination's inbox (safer: reuses the sanctioned cross-project path, gets provenance + per-project filing for free, degrades gracefully where a destination has an =inbox/= but no =todo.org=). D2/D3 superseded; D7 (inbox-send delivery), D8 (=:ROUTE_CANDIDATE:= marker at file time), D9 (local source removal + reject-flow recovery) added. Spec-review file consumed and deleted. Implementation-task breakdown filed below (spec-response Phase 6).
-
-*** 2026-06-24 Wed @ 00:21:20 -0400 Reconcile — marker sub-task repointed at inbox.org
-The 2026-06-23 inbox consolidation (24ca58d) merged =process-inbox= + =monitor-inbox= + =inbox-zero= into one =inbox.org= engine (process/monitor/roam modes) and deleted the three old files. The =:ROUTE_CANDIDATE:= marker sub-task targeted =process-inbox.org='s Phase D — repointed it to =inbox.org= process mode (core §3 "File as TODO"). No build has started, so this is a target-rename only; the spec design is unaffected.
-
-*** 2026-06-28 Sun @ 13:02:42 -0400 Built the recommendation engine + destination discovery
-Added =.ai/scripts/route_recommend.py= (canonical + mirror): pure =recommend(item, projects) → (destination, confidence)= with strong (name/path literal, word-boundary matched, dot-stripped alias aware), weak (distinctive name-token overlap), and none tiers; a multi-way top-tier tie downgrades to weak with a deterministic pick (most overlap, then alphabetical); empty list → none. The CLI (=--item=, =--exclude=) reuses =inbox-send.py='s =discover_projects= via importlib so the candidate set matches inbox-send's project universe. 13 tests (the five spec'd cases + boundary/path/strong-beats-weak + 3 sandboxed CLI integration tests), full =make test= green. Covers spec Phases 1 + 3. Next sub-tasks (=:ROUTE_CANDIDATE:= marker, wrap-up router) call this engine.
-
-*** 2026-07-02 Thu @ 00:36:12 -0400 Phases 2 + 4 + test surface landed — marker, router, route-batch helper
-inbox.org's "File as TODO" disposition now runs route_recommend on each keeper and stamps =:ROUTE_CANDIDATE: <destination>= on strong/weak matches (none stamps nothing; local keepers stay unstamped) — spec Phase 2 / D8. wrap-it-up.org Step 3 gained the optional router directly after the inbox sanity check, with the gate-vs-optional split named in the prose: surface the batch (task / destination / delivery mode / confidence, weak visibly labeled), go/skip, empty set = zero interaction — spec Phase 4 / D7 / D9. The go path is mechanical: new =.ai/scripts/route-batch= (--list read-only, --go extracts the subtree minus the marker with children riding along and headings promoted, delivers via inbox-send for provenance, removes the local subtree only after a successful send; a failed send leaves the task in place and exits non-zero). Test surface: engine unit tests existed (13); route-batch adds a 9-test bats suite (list/backlog-exclusion, empty-set silence, list-modifies-nothing = skip semantics, delivery + provenance + children, local-task survival, drawer-minus-marker, inbox-without-todo.org delivery, empty go, failed-send recovery). cross-project.md notes the router as a sanctioned cross-project write path. make test green, sync clean.
-
-*** 2026-07-04 Sat @ 11:49:59 -0500 Flipped the spec to IMPLEMENTED; parent closed
-The routing build shipped green: route_recommend.py (destination discovery + recommendation, 13 unit tests), the =:ROUTE_CANDIDATE:= marker in inbox process mode, the wrap-it-up router sub-step, and route-batch (9 bats tests). Spec [[id:00b47414-2213-4a99-be35-48ceb266fc08][wrapup-routing]] flipped DOING → IMPLEMENTED. Manual end-to-end validation and the transcript vNext promoted to their own tasks.
-
-** TODO [#B] Wrap-up routing — manual end-to-end validation :test:
What we're verifying: a real keeper routes through a live wrap and the destination actually files it. The task-routing build shipped IMPLEMENTED 2026-07-04 (spec [[id:00b47414-2213-4a99-be35-48ceb266fc08][wrapup-routing]]); this confirms it works end to end across a real cross-project wrap. A failed check promotes to a bug.
- In a project session, let process-inbox file a handoff whose home is a different project; confirm the local task carries =:ROUTE_CANDIDATE: <dest>=.
- Run wrap-it-up; at the router sub-step, confirm the candidate is surfaced with the right destination + confidence, then choose "go".
@@ -126,17 +269,20 @@ File a meeting recording into the destination =assets/= per =working-files.md=,
** TODO [#C] Multiple agent-source improvements :spec:
:PROPERTIES:
:CREATED: [2026-06-23 Tue]
-:LAST_REVIEWED: 2026-06-24
+:LAST_REVIEWED: 2026-07-13
:END:
Make the tooling agent-agnostic instead of Claude-specific. Three threads from Craig (roam 2026-06-23): (1) give the agent a name so workflows don't say "Claude" everywhere — a non-Claude agent (Codex) reading "you are Claude" gets confused; evaluate whether naming resolves the confusion or whether other spots also leak Claude-specificity. (2) Pull agent-neutral content out of Anthropic-specific files (=CLAUDE.md=) into a shared source that each agent's own entry file points to, so Codex (which runs more literal) reads the same rules; or link =CLAUDE.md= and the Codex equivalent to one source. Have Codex review the workflows for literal-reading wording gaps. (3) Send =.emacs.d= a note (inbox-send) to let =ai-term= launch Claude / Codex / a local ollama LLM, switchable seamlessly at session start. Spec-shaped — needs design before build. From the roam inbox 2026-06-23 (deferred from the 2026-06-21 session).
*** 2026-06-24 Wed @ 00:21:20 -0400 Partial — agent-neutral wording sweep + thread-3 note landed
Thread 2's wording half shipped in 6ad0442 (=refactor(rules): use agent-neutral language in shared rules=): agent-as-actor phrasing replaced with "the agent" across interaction.md, cross-project.md, triggers.md, working-files.md. Thread 3's note reached =.emacs.d=, whose 2026-06-23 inbox FYI confirms it received and filed the "multi-LLM support" ai-term handoff. Remaining and still TODO: thread 1 (give the agent a name), and thread 2's structural half (extract agent-neutral content into a shared source with a Codex entry-file pointer, then have Codex review the workflows for literal-reading gaps).
+*** 2026-07-13 Mon @ 16:04:04 -0500 Thread 2's entry-file half landed via the runtime-portability build
+The Codex entry-file pointer now exists: =claude-templates/AGENTS.md= (thin pointer at protocols.org + rules + /name resolution), linked to =~/.codex/AGENTS.md= by =make install= and seeded per-project by =install-ai.sh= (see the generic-agent-runtime parent's 2026-07-13 children). Remaining here: thread 1 (agent naming — the entry file's "you are this project's agent" phrasing is a start, not the whole answer) and the Codex literal-reading review of workflows.
+
** TODO [#C] Flashcard tooling improvements :feature:
:PROPERTIES:
:CREATED: [2026-06-28 Sun]
-:LAST_REVIEWED: 2026-06-28
+:LAST_REVIEWED: 2026-07-13
:END:
Three flashcard-tooling tasks that all edit =flashcard-to-anki.py= and/or =flashcard-stats.py=, grouped so they get built together instead of colliding on the same files (prior sessions flagged the conflict risk). The Anki =#+TITLE= deck-name fix already landed (commit 060a938), so any preserved pre-fix script copy gets re-derived against the current canonical, never copied wholesale. The three children each ship independently.
@@ -165,111 +311,17 @@ The work project edited two synced scripts locally as a stopgap (2026-06-17) and
Note (2026-06-24): the Anki =#+TITLE= deck-name fix landed (commit 060a938) — =default_deck_name= is now =default_deck_name(input_path, org_text)= with a new docstring. The preserved 2026-06-17 =to-anki.py= predates that, so *don't* copy it wholesale (it would revert the title-fix). Re-derive the multi-tag changes against the current canonical =flashcard-to-anki.py= and keep the =#+TITLE= behavior.
-** TODO [#C] coverage-summary.el documented as a local-only helper :chore:
+** TODO [#C] coverage-summary.el documented as a local-only helper :chore:quick:solo:
:PROPERTIES:
:CREATED: [2026-06-22 Mon]
-:LAST_REVIEWED: 2026-06-28
+:LAST_REVIEWED: 2026-07-13
:END:
The elisp bundle installs =coverage-summary.el= into =.claude/scripts/=, gitignored in code projects, so CI can't run =make coverage-summary= against it. Decision (Craig, 2026-06-28): keep it in =.claude/scripts/= and document it as a local-only helper — don't ship it to a tracked =scripts/= dir, don't expect CI to run it. Remaining work (docs only, no move): state the local-only status in the script's header comment and wherever =make coverage-summary= is described, so the gitignored install reads as intentional rather than a gap. Note: emacs-wttrin rewrote its copy's header to claim a tracked =scripts/= home, which now contradicts this decision and should be reverted on their side. Surfaced 2026-06-21 during the coverage-summary autoloads bugfix (commit fb86736).
-** DONE [#C] Check that memories are sync'd across machines via git :spec:
-CLOSED: [2026-07-04 Sat]
+** TODO [#C] Agent-KB / memory-sync — work + unknown-project write refusal :test:
:PROPERTIES:
-:LAST_REVIEWED: 2026-06-28
+:LAST_REVIEWED: 2026-07-13
:END:
-v1 implemented end-to-end 2026-06-10 (Phases 0-4, all shipped + pushed) and the agent-KB spec is IMPLEMENTED. Both daily drivers (velox + ratio) carry the =roam.git= clone + sync-timer. Cross-machine sync verified as a full round-trip 2026-07-04: velox created a probe node 2026-07-01 and ratio held it; ratio then deleted it and velox pulled the deletion. Both clones sit at the same HEAD with zero sync-conflict files and both roam-sync timers active. The work/unknown-project write-refusal checks need live sessions in those projects and are tracked as a standalone manual-validation task below.
-*** 2026-05-14 Thu @ 19:14:11 -0500 Investigate current memory storage
-
-Memory files live at
-[[file:/home/cjennings/.claude/projects/-home-cjennings-code-rulesets/memory/][~/.claude/projects/-home-cjennings-code-rulesets/memory/]]
-— four files including =MEMORY.md= and three individual entries
-(=feedback_never_guess.md=, =project_ai_scripts_canonical_source.md=,
-=reference_pdftools_venv.md=). The directory is a plain unmanaged dir
-(no symlink, no enclosing git checkout). Neither
-[[file:/home/cjennings/.claude/][~/.claude/]] itself nor any subtree
-containing the project-memory dirs is tracked in
-[[file:/home/cjennings/code/archsetup/][archsetup]] or
-[[file:/home/cjennings/code/rulesets/][rulesets]]. Without a symlink
-into a stowed or tracked location, memory files don't survive a new
-machine setup or a dotfiles restore.
-
-Proposed setup: stow =~/.claude/projects= →
-=archsetup/dotfiles/common/.claude/projects/= (path doesn't exist yet
-— it's the target location pending VERIFY).
-Create the destination in archsetup, move existing per-project
-=projects/<encoded-cwd>/memory/= dirs there, run =stow= to link, then
-commit + push archsetup. After that, every machine running =stow=
-picks up the same memory tree.
-
-*** 2026-05-23 Sat @ 16:12:48 -0500 Decided: dedicated private repo, not stow
-Worked through dotfiles → rulesets → dedicated repo. Dropped stow/dotfiles (machine config, wrong cadence) and rulesets (it's pulled first in every session, so memory edits would dirty its tree and skip the startup =git pull --ff-only=). Chose a dedicated private repo on cjennings.net: storage is unified there while recall stays per-project (the encoded-cwd subdirs), since pooling recall would hurt relevance and risk work-private facts surfacing in personal-project artifacts.
-
-*** 2026-05-23 Sat @ 16:12:48 -0500 Shipped: claude-memory.git + folded symlinks
-Created bare =git@cjennings.net:claude-memory.git=, cloned to =~/.claude-memory= (later deleted in the reversal below), moved all 7 per-project =memory/= dirs in (54 files; work has 40) and replaced each live =~/.claude/projects/<enc>/memory= with a folded dir-symlink so new memory lands in the clone and a push syncs it. Added =link-claude-memory.sh= (idempotent — recreates the symlinks on a new machine after clone) + README. Private repo, never GitHub (carries work/DeepSat memory). Initial import pushed (=f496370=).
-
-*** 2026-05-24 Sun @ 01:53:35 -0500 Reversed the migration — back to unmanaged per-project memory
-Cancelled the follow-up brainstorm and undid the dedicated-repo migration at Craig's call. Moved all 7 memory dirs back to =~/.claude/projects/<enc>/memory/= (content preserved), deleted the =~/.claude-memory= clone, and deleted the bare =claude-memory.git= on the server. Memory is back to its original at-risk state, so the task reopens at [#C] pending a direction. The brainstorm landed on a two-tier idea for whenever this resumes: promote general lessons into a rulesets-tracked file symlinked into =~/.claude/rules/= (loaded into every project natively, one repo), and keep project-specific memory under each project's own =.ai/memory/= (committed where =.ai/= is tracked, at-risk where it's gitignored). Not implemented.
-
-*** 2026-06-05 Fri @ 05:57:35 -0500 Pivot: adopt the existing org-roam KB as the shared agent substrate
-Pressure-tested the two-tier idea, then Craig redirected: a shared org-roam knowledge base any project can read and write makes this simpler. Ground truth verified: =~/sync/org/roam/= already exists (484 org files, curated since 2023, Syncthing-synced, not git). So cross-machine sync is already solved, and the task stops being "build a memory-sync system" and becomes "point agents at the KB that already syncs." The dedicated-repo and two-tier approaches are both superseded for the storage+sync half.
-
-Wrote a one-page spec: [[id:08a5ec99-9e1e-40e4-8241-e8a41e9de49f][agent-knowledge-base-spec.org]] (originally docs/design/2026-06-05-org-roam-knowledge-base-spec.org; superseded by the 2026-06-10 spec-create rewrite at the new path). Five decisions, mechanics recommended: (1) KB is a queried substrate accessed as files (ripgrep + follow =[[id:]]= by grep), not via the org-roam package; (2) capture in harness memory, promote durable facts into the KB (same cadence as the pattern catalog) — resolves the at-risk problem since the valuable knowledge moves to the synced KB; (3) a =claude-rules/knowledge-base.md= pointer rule carries path/query/write-schema/boundary; (4) write schema = roam-valid node + =:agent:= filetag so agent notes stay distinguishable and index on the next =org-roam-db-sync=. The rules layer (=claude-rules/=, =CLAUDE.md=) is untouched — the KB replaces the memory tier, not the rules tier.
-
-*** 2026-06-10 Wed @ 14:29:20 -0500 Spec ratified — write boundary is option C; rewritten to spec-create format
-Craig answered via cj annotations in the spec (2026-06-10): DECISION 5 is option C (read-shared, write-scoped — work agents never write the KB). Syncthing does replicate ~/sync/ to a work machine and Craig is fine with how C handles it. Node granularity: per-fact nodes. Write review: agent writes land freely in the KB only — explicitly not permission to post to email, Linear, or any public channel without review and consent. The spec was rewritten into the spec-create format at [[id:08a5ec99-9e1e-40e4-8241-e8a41e9de49f][agent-knowledge-base-spec.org]] (old draft removed). Implementation explicitly held pending Craig's go-ahead; one decision still open (D7, next VERIFY).
-
-*** 2026-06-10 Wed @ 14:35:40 -0500 Spec review — not ready
-Review written at docs/agent-knowledge-base-spec-review.org (deleted on disposition completion; content summarized in the spec's Review dispositions). Rubric: =Not ready=. Blockers: resolve D7 (keep vs retire harness memory) and define the executable personal/work/unknown write-boundary classifier plus work-side write/refusal destination. Medium notes: use concrete ripgrep commands that exclude =*.sync-conflict-*= files, and define seed-node approval/rollback.
-
-*** 2026-06-10 Wed @ 14:44:00 -0500 D7 resolved — keep harness memory as the capture layer
-Craig ratified "keep" in chat (2026-06-10). Harness memory stays the ephemeral, auto-recalled capture layer; the KB holds promoted durable facts; Phase 3's wrap-up promotion cadence is mandatory. Spec D7 flipped to accepted; D2 stands as written.
-
-*** 2026-06-10 Wed @ 14:44:00 -0500 Project classification defined — work-root denylist, unknown refuses
-Resolved in the spec-response pass: =knowledge-base.md= carries an explicit work-root denylist (initially =~/projects/work=) as the source of truth. Personal = under a known project parent (=~/code/=, =~/projects/=, =~/.emacs.d=) and not denylisted → KB writes allowed. Work or unknown → no KB write; the agent reports the refusal with a one-line redacted summary of the fact. v1 adds no new work-side store — work projects keep their existing project-tree conventions. See the "Project classification and write routing" section of [[id:08a5ec99-9e1e-40e4-8241-e8a41e9de49f][the spec]]. Denylist completeness is the one open caveat (next VERIFY).
-
-*** 2026-06-10 Wed @ 14:44:00 -0500 Codex review incorporated — spec ready with caveats
-Spec-response pass processed the 2026-06-10 Codex review with D7 = keep as a pre-agreed input. Both blockers cleared (D7 accepted; classification/write-routing section added). Mediums accepted: canonical rg commands with conflict-file exclusion, Phase 2 seed-node approval/rollback mechanics, Makefile no-change note, Testing/Verification section. Three recommendations modified, none rejected — see the spec's Review dispositions. Review file deleted per the workflow. Rubric: ready with caveats (denylist confirmation). Implementation tasks broken out below; implementation itself awaits Craig's go.
-
-*** 2026-06-10 Wed @ 17:29:37 -0500 Work-root denylist confirmed — ~/projects/work only
-Craig confirmed (2026-06-10, in chat): the denylist is just =~/projects/work=. Archangel is not work-scoped. The spec's one caveat clears — status now ready. Phase 1 is unblocked, but implementation still awaits Craig's explicit go.
-
-*** 2026-06-10 Wed @ 17:57:08 -0500 Spec amended — D8 git transport + migration/metrics/docs/maintenance folds
-Craig's five design questions answered and folded into the spec, and D8 ratified (Shape A): the KB moves out of the =~/sync/org= Syncthing share into its own git repo on cjennings.net, with an =agents/= subdirectory for agent writes, a systemd auto-sync timer for Craig's edits, opt-in-by-clone replication (work machine doesn't clone), and the phone staying on the on-demand =~/sync/phone= pattern. Folded in: inclusion criteria + a Phase 1.5 guided memory sweep, a Success metrics section with a 30-day checkpoint, the seed node redefined as the KB's own documentation, and Phase 4 maintenance automation. Phases renumbered 0-4; tasks below updated. Implementation still held.
-
-*** 2026-06-10 Wed @ 18:21:33 -0500 Phase 0 done — roam migrated to git
-Backed up (~/roam-backup-2026-06-10.tar.gz), copied to =~/org/roam=, 63 conflict files deleted (424 org files), git repo with origin =git@cjennings.net:roam.git= (initial commit 515693d), old location replaced with a transition symlink. Emacs =roam-dir= updated in user-constants.el + live-reloaded (db rebuilt, 416 nodes); handoff to .emacs.d for the commit. =roam-sync.sh= (6 bats green) on a 15-min systemd user timer, installed + enabled + round-trip verified. Old-path references repointed (protocols task-list pointer, journal workflow, notes template). archsetup handoff covers dotfiles adoption + other-machine clones. rulesets commit fcf554a.
-
-*** 2026-06-10 Wed @ 18:21:33 -0500 Phase 1 done — knowledge-base.md rule live
-=claude-rules/knowledge-base.md= written (path, git discipline, query commands, agents/ write schema, denylist + refusal contract, inclusion criteria, capture-then-promote). =make install= linked it machine-wide; verified the link, a known-note query, and conflict-glob exclusion with a planted file. Commit d071f1f.
-
-*** 2026-06-10 Wed @ 18:21:33 -0500 Phase 1.5 done — rulesets swept, 10 projects broadcast
-Rulesets' 6 memories classified: 3 promoted as =agents/= nodes (notify-attention pattern, pdftools venv, gpg-agent SSH TTL trap), 2 kept local (rule-encoded in verification.md / interaction.md), 1 kept + de-staled (ai-scripts-canonical updated for the claude-templates subtree fold). Sweep handoff broadcast to the 10 other memory-bearing projects (archsetup, org-drill, pearl, .emacs.d, elibrary, finances, health, home, jr-estate, kit); work skipped by the boundary; the orphaned =linear-emacs= memory dir (project retired, likely pearl's predecessor) noted for Craig.
-
-*** 2026-06-10 Wed @ 18:21:33 -0500 Phase 2 done — seed/doc node written and indexed
-=agents/20260610181640-how-the-agent-knowledge-base-works.org= written: the KB's user-facing guide (what agents do, how it syncs, finding/pruning agent content, the rule pointer). Index verified programmatically: =org-roam-node-from-title-or-alias= resolves it with tags (agent reference); node count 416 → 420. Craig's visual check remains in the manual-testing child.
-
-*** 2026-06-10 Wed @ 18:21:33 -0500 Phase 3 done — wrap-up promotes + records the KB receipt
-wrap-it-up.org Step 1 gains the promotion check (inclusion-criteria bar) and the mandatory "KB: promoted N / consulted yes-no" Summary line; validation checklist enforces it. Mirror synced, integrity OK (44), parse OK. Commit 242b95e.
-
-*** 2026-06-11 Thu @ 19:26:26 -0500 .emacs.d memory sweep complete (first broadcast response)
-First of the 10 broadcast projects to report Phase 1.5 done (handoff 18:23). Inventory 7: promoted 3 to KB (no-make-frame-in-live-daemon, proton-bridge-headless-cert-mismatch, open-images-with-imv — roam commit a915760), kept 3 local at Craig's call (commit-flow-no-approval-gate per-project-scoped; two theme-scoped ones possibly superseded by the palette-columns spec), deleted 1 (superseded by canonical interaction.md rule). 9 projects' sweeps outstanding.
-
-*** 2026-06-12 Fri @ 02:25:12 -0500 Five more sweeps complete via the home folds
-Overnight handoffs from home closed five more broadcast targets, each swept at fold-time triage with Craig's approval: jr-estate 2 promoted (forms name-with-number, PDF-editing tooling split; roam 45d8e6c) / 3 kept with area attribution / 2 deleted as rule-encoded or duplicate; finances 0/1/0 (rosalea-daly contact fact kept local); elibrary 0/0/2, health 0/0/1, kit 1/0/2 (hand-prep-items-to-work-inbox promoted into home's memory; the rest duplicated rules or home memories). Nothing from these five met the KB bar that wasn't already encoded. All folded projects' session archives merged area-prefixed into home's .ai/sessions/, so session-harvest's first run sees them. Home covers its own and remaining areas' sweeps through ongoing discipline; still pending from the broadcast: archsetup and work.
-
-*** 2026-07-04 Sat @ 11:52:00 -0500 Manual validation — checks 1 + 4 verified; refusal checks split out
-Check 1 (seed node in org-roam + rg inventory) verified on velox 2026-07-01 (55 =:agent:= nodes matched the live org-roam DB). Check 4 (cross-machine sync) verified as a full round-trip 2026-07-04: velox pushed a probe 2026-07-01, ratio held it, ratio deleted it, velox pulled the deletion; both clones at the same HEAD, zero sync-conflict files, both timers active. Checks 2 + 3 (work / unknown-project write refusal) need live sessions in those projects — promoted to a standalone manual-validation task.
-
-*** 2026-06-10 Wed @ 18:21:33 -0500 Phase 4 done — monthly hygiene automation live
-=scripts/kb-hygiene.sh= (6 bats green, shellcheck clean, read-only by design) inventories =:agent:= nodes, flags orphans / duplicate titles / conflict files, and writes an org report into the rulesets inbox; =roam-hygiene.timer= (monthly, Persistent) installed + enabled. Live run against the real KB verified (4 agent nodes, 428 files, 0 conflicts). Conditional vNext stays in the spec's scope tiers: a =/promote= command if the wrap-up prompt proves insufficient, an =:agent:inbox:= staging tag if free writes prove too noisy. Commit b014095.
-
-*** 2026-06-30 Tue @ 13:53:34 -0400 ratio roam clone + sync-timer confirmed (cross-machine half done)
-Verified ratio over tailscale ssh: =~/org/roam= is a clone of =git@cjennings.net:roam.git= (HEAD auto-synced 13:11 today), and =roam-sync.timer= is enabled and actively firing (last run 5 min prior, next in 10). Both unit files present. velox was already confirmed, so the one-time clone+timer setup is now done on both daily drivers — the (b) half of this VERIFY's remaining work. Only the manual-validation child (work/unknown-project refusal checks needing Craig's eyes) is left before DONE. Cleared the matching "Current open instance" line in =daily-drivers.md=.
-
-*** 2026-07-01 Wed @ 21:52:15 -0400 Manual-validation checks 1 + 4 (velox half) verified; ratio unreachable
-Check 1 (seed node in org-roam + rg inventory): 55 =:agent:= nodes found by the rg inventory AND 55 nodes under =agents/= indexed in the live org-roam DB (emacsclient query) — match. Check 4 (cross-machine edit): created a temporary probe node =agents/20260701214910-kb-sync-validation-probe.org=, triggered =roam-sync= — committed and pushed to origin within seconds (f0252bb), zero =sync-conflict= files. The ratio half could NOT be verified tonight: =tailscale ping ratio= pongs via DERP, but ssh to 100.71.182.1:22 times out (machine likely suspended). Probe left in place; when ratio is back, confirm with: ssh cjennings@100.71.182.1 'ls ~/org/roam/agents/ | grep kb-sync-validation-probe' — then delete the probe node. Checks 2 + 3 (work/unknown-project refusal) still need live sessions in those projects, and "work machine has no KB clone" needs the work machine named + checked.
-
-** TODO [#C] Agent-KB / memory-sync — work + unknown-project write refusal :test:
Residual manual validation from the memory-sync task (closed 2026-07-04, implementation IMPLEMENTED). Two checks need live sessions in other project contexts:
- In the work project, a durable-storage request produces no KB write and the refusal report names the fact.
- In an unknown project (outside =~/code/=, =~/projects/=, =~/.emacs.d=), the agent refuses or asks rather than guessing.
@@ -277,7 +329,7 @@ Expected: both refusal checks behave per the spec; any miss promotes to a bug. N
** TODO [#C] Token-rotation helper for =@a-bonus/google-docs-mcp= OAuth refresh :feature:quick:
:PROPERTIES:
-:LAST_REVIEWED: 2026-06-28
+:LAST_REVIEWED: 2026-07-13
:END:
When a Google refresh token gets revoked (re-grant scopes, removed Connected App, account password reset), recovery is currently manual: run =npx -y @a-bonus/google-docs-mcp= with the right env, follow the URL in a browser, kill the process, base64-encode the new =token.json=, decrypt =secrets.env.gpg=, replace the var, re-encrypt. A small =mcp/refresh-google-docs-token.sh <profile>= would chain that into one command.
@@ -328,70 +380,53 @@ Codex ran the spec-review workflow. Outcome: the combined spec is =Not ready= be
*** 2026-06-12 Fri @ 02:39:38 -0500 Second review after response pass
Codex re-ran spec-review after the dispositions were folded in. Outcome by arc: Phase 1.5 helper instances =Ready with caveats=; phases 2-5 remain =Not ready= behind the explicit decisions/reverification gate. No new blocking findings for the helper slice. Review file updated in place: [[file:docs/design/2026-05-28-generic-agent-runtime-spec-review.org]].
-** DONE [#C] Spec storage location + lifecycle-status convention :spec:
-CLOSED: [2026-07-04 Sat]
-:PROPERTIES:
-:CREATED: [2026-06-15 Mon]
-:LAST_REVIEWED: 2026-06-24
-:SPEC_ID: 80b0787b-4a60-4c82-8a16-b383d3e3c8f2
-:END:
-Two coupled documentation conventions for rulesets to adopt, surfaced by .emacs.d while triaging ~28 design docs. Both land in =spec-create= ([[file:.ai/workflows/spec-create.org]]) and likely a new =docs-lifecycle= rule under =claude-rules/=. Source proposal: [[file:docs/design/2026-06-15-spec-storage-lifecycle-proposal.org]] (.emacs.d handoff 2026-06-15).
-
-The two conventions:
-- *Location split* — formal specs live in =docs/specs/=; =docs/design/= keeps working notes, brainstorms, inventories, reviews. A spec is a doc proposing a buildable change with a Decisions section and phases; everything else is a note.
-- *Glanceable lifecycle status* — a spec's state (draft / doing / implemented / superseded / cancelled) is visible without opening the file, plus an authoritative in-file record.
-
-We handle the task in priority order. Mechanism decided 2026-06-28; migrates into the spec when built.
-
-*** Decisions (settled 2026-06-28 — migrate into the spec when built)
-1. *Location split — adopt.* =docs/specs/= for formal specs, =docs/design/= for notes (brainstorms, inventories, reviews). A spec is a doc proposing a buildable change with a Decisions section and phases; everything else is a note. Document in spec-create and the docs-lifecycle rule.
-2. *Status mechanism — org-keyword authoritative.* The spec's =#+TODO:= state on its top heading is authoritative (specs already carry =#+TODO: TODO | DONE SUPERSEDED CANCELLED=), mirrored in a =Status= field in the Metadata table. Drop the filename suffix entirely — it's redundant with the Status field and adds rename churn across a cross-linked, template-synced doc set. (Craig 2026-06-28, choosing org-keyword over his earlier filename-suffix lean.)
-3. *Link safety — adopt =org-id= ([[id:...]]) for cross-doc spec links.* Decouples link stability from the status mechanism; good hygiene regardless.
-4. *Generalize.* Capture the shape (lifecycle state authoritative-in-artifact, formal-vs-notes split, rename-safe links) as a general =docs-lifecycle= convention in =claude-rules/=, with spec-create as the first instance.
-5. *Retrofit existing files across ALL projects* (Craig 2026-06-28). The convention is worthless if legacy docs stay misfiled — every project's existing =docs/design/= pile (the ~28 in .emacs.d that surfaced this) must be sorted: formal specs move to =docs/specs/=, notes stay in =docs/design/=, inbound =file:= links updated. This is a one-time per-project migration that template sync can't perform, so the spec must design the reach mechanism. Proposed shape: a synced classify-and-move helper under =.ai/scripts/= (heuristic: a doc with a Decisions section + phases/Metadata is a spec) that proposes moves for confirmation and relinks, plus a startup nudge gated on a per-project =:LAST_SPEC_SORT:= marker so each project runs it once. Classification is a judgment call — the helper proposes, a human confirms.
+*** 2026-07-13 Mon @ 13:26:57 -0500 Gap assessment decomposed into child tasks
+Craig asked what's left to run ChatGPT or a local LLM as the agent. Assessment: the =.ai/= layer (protocols, workflows, scripts, inbox, todo, session anchors) is already runtime-neutral — plain org + bash, and a Codex session has run in it (2026-06-13). The Claude-bound remainder decomposed into the child tasks below; each overlapping spec-blocker decision is named in its body. The phases 2-5 go/no-go above still gates any big build, but several children are useful standalone.
-Follow-up once built: update spec-create to emit into =docs/specs/= with the org-keyword status; write the =docs-lifecycle= rule; ship the retrofit helper + startup nudge; retrofit rulesets' own =docs/design/= first as the pilot; send a note if .emacs.d should pilot before generalizing.
+*** 2026-07-13 Mon @ 16:04:04 -0500 Instruction bootstrap built — thin-pointer AGENTS.md, both install paths
+Craig picked the thin-pointer shape (Decisions: option 1, definitively). Shipped TDD (bats red → green): canonical =claude-templates/AGENTS.md= (you-are-this-project's-agent + read protocols.org + rules locations + the /name resolution rule from the skill-parity finding + degrade-per-fallback, never skip gates); =make install= links it to =~/.codex/AGENTS.md= (new CODEX_DIR stanza, house skip/WARN/link idiom, covered by NEW =scripts/tests/install-agents-entry.bats=, 3 tests); =install-ai.sh= seeds a project-owned copy at bootstrap, never overwriting (+2 tests in install-ai.bats); rulesets root gets a tracked symlink as dogfood. Resolves the spec's "generic instruction-file strategy" blocker. velox picks up the global link automatically — startup Phase A.0 runs =make install= every session. Existing projects get seeded on demand (no auto-sweep in v1).
-*** 2026-07-01 Wed @ 22:13:00 -0400 Spec drafted — first resident of docs/specs/, awaiting review
-Wrote [[id:80b0787b-4a60-4c82-8a16-b383d3e3c8f2][the spec]] from the five settled decisions, dogfooding its own conventions: it lives in the new =docs/specs/=, opens with the =* DRAFT Docs lifecycle= status heading (org keyword authoritative, =:ID:= for id-links, dated history in the body), and drops the status filename suffix. It pins the one mechanism the decisions left open — where the keyword lives: a prepended top-level status heading with vocabulary =DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED=, additive and retrofittable, giving both the one-line =rg= board and free org-agenda scanning. Four build phases: rule + template updates → =spec-sort= helper (classify/confirm/move/relink, bats) → rulesets pilot (41 design files, 3 spec-spine candidates, 2 stray root specs) → startup nudge gated on =:LAST_SPEC_SORT:= + .emacs.d note. Status DRAFT until Craig's review flips it READY.
+*** 2026-07-13 Mon @ 14:52:38 -0500 Skill parity resolved — one resolution rule, no per-skill matrix
+Analysis in [[file:docs/design/2026-07-13-runtime-portability-inventories.org]] (Skill and command parity section). All 29 artifacts (11 skills + 18 commands) are markdown bodies; a single resolution sentence in the bootstrap entry file ("a /name reference resolves to the skill/command file — read and follow it") makes the library portable to any file-reading harness. Auto-invocation degrades to by-name invocation (the publish flow already invokes by name), native slash registration is an optional install nicety, flush is explicitly excluded (session-plumbing child owns it). Folds into the instruction-bootstrap child's build.
-*** 2026-07-01 Wed @ 22:22:34 -0400 Codex spec-review complete — Not ready
-Review findings live in [[id:cc77a7f6-e4c3-488a-ac3b-e739420a5c2b][the spec]]. Four blockers before implementation: the proposed lifecycle =#+TODO:= line drops the =TODO=/=DONE= states needed by Decisions and Review findings cookies; =spec-sort= needs an exact relink/unsupported-residue contract; the sort marker/startup nudge must name the actual =.ai/notes.org= state surface and detection flow; and the stricter =docs/specs/= review precondition must not strand legacy specs before retrofit.
+*** 2026-07-13 Mon @ 13:34:17 -0500 Hook parity inventoried — only two hooks carry real porting work
+Full mapping in [[file:docs/design/2026-07-13-runtime-portability-inventories.org]]. AskUserQuestion deny is moot off Claude (no popup tool to deny); PostToolUse validators survive via the bundles' git pre-commit hooks; clear-resume folds into the session-plumbing child; session-title is cosmetic. Real gaps: PreCompact priority-save (prose downgrade) and Stop wrap-teardown (Codex notify / manual elsewhere) — decisions in the VERIFY below.
-*** 2026-07-01 Wed @ 22:30:06 -0400 Second review merged + responder pass — all nine findings fixed
-A fresh-context Claude reviewer independently rated the draft Not ready, converging on Codex's keyword-vocabulary blocker (adding the cross-sequence uniqueness wrinkle) and contributing five unique findings — the biggest: nobody owned the DOING→IMPLEMENTED flip, the exact mechanism whose failure produced this spec. Craig approved fixing all nine. Fixed in place: two-sequence collision-free keyword header (dogfooded in the spec's own header; org now computes the [5/5] and [9/9] cookies — verified in batch), transition-ownership table incl. spec-response's mandatory flip-to-IMPLEMENTED task + task-audit safety net, single classification predicate (Decisions AND Implementation phases), the -spec.org rename step, the full relink data-safety contract (rewritten roots / report-only surfaces / dry-run default / residue-grep gate), the =.ai/notes.org= marker + Phase A probe + Phase C nudge contract, the legacy-location compatibility rule, the org-id Emacs-resolution prerequisite for .emacs.d, and the three-line transition definition. Ledger + per-finding responses in the spec's Review findings section. Status stays DRAFT pending Craig's READY flip.
+*** 2026-07-13 Mon @ 23:21:08 -0500 Interactive agent picker — bare =ai= asks which brain first
+Craig's ask: the fzf flow should offer the LLMs before the projects. Bare =ai= now runs a runtime picker (claude first so Enter-Enter keeps the old muscle memory; codex labeled ChatGPT; one =local:<model>= line per ollama model, live-queried with a 3s timeout — a dead server just drops the local lines), then the familiar annotated project multi-select. =--runtime= / =AI_RUNTIME= skip the pick; single-dir mode unchanged. New =--print-runtimes= test seam; 9/9 launcher bats, suite 441/0. This substantially delivers the "easy lightweight way to change agents / agentically democratic" roam ask — the launcher-hardening task keeps the deeper refactor.
-*** 2026-07-01 Wed @ 22:41:33 -0400 Codex spec-review rerun — Not ready
-Fresh review after the response/READY flip added five new blocking findings in [[id:cc77a7f6-e4c3-488a-ac3b-e739420a5c2b][the spec]] and demoted the spec back to DRAFT. Remaining blockers: shared helper/workflow edits must name the canonical =claude-templates/.ai/= + mirror sync contract; task-audit needs an explicit spec-to-task binding before it can police =DOING= specs; =spec-sort --apply= needs a failure-safe/rollback contract; the org-id Emacs prerequisite must be executable before link conversion; and lifecycle status confirmation must be evidence-based so the retrofit does not encode stale reality.
+*** 2026-07-13 Mon @ 23:04:21 -0500 Local runtime wired — ai --runtime local runs codex --oss over ollama
+The reserved lane went live the same day: =local= maps to =codex --oss --local-provider=ollama -m $AI_LOCAL_MODEL= (default gpt-oss:120b; qwen3-coder:30b via the env var). Verified end to end: =codex exec --oss --local-provider=ollama -m gpt-oss:120b= returned a correct completion through the local model (~7K tokens). The explicit provider flag avoids config.toml dependence (a root-level =oss_provider= key would also work; appending to the file lands in the last TOML table and silently does nothing — learned live). Dependency check keys on AGENT_BIN (codex) now that AGENT_CMD carries flags. This partially answers the spec's "first supported local editing CLI" blocker: codex-over-ollama is the de-facto v1. The model-floor child's live trial (a takuzu session under =ai --runtime local=) is the next step.
-*** 2026-07-01 Wed @ 22:46:52 -0400 Second responder pass — all fourteen findings closed
-Fixed Codex's five re-review findings: the canonical-placement contract now opens the retrofit section (helper + tests + workflow edits land in claude-templates first, sync-check --fix propagates, sync-check-clean is an acceptance criterion); spec-response stamps a =:SPEC_ID:= property on the build parent, and task-audit's query checks that parent's keyword — which dissolves the flip-task chicken-and-egg; =--apply= got the fail-safe contract (clean-tree preflight, validate-then-write from a recorded plan, named recovery recipe); id-link conversion is staged (pilot rewrites =file:= links only; =id:= conversion is a follow-up gated on the concrete .emacs.d id-index mechanism — Craig picked this fork); and status confirmation is evidence-based (evidence panel, conservative non-terminal default, terminal states need a stated reason). Also de-cookified bracket tokens in prose that org's cookie updater would mangle. Status stays DRAFT; the READY flip belongs to the reviewers this round — verify pass dispatched.
+*** 2026-07-13 Mon @ 16:39:41 -0500 Launcher runtime flag built — ai --runtime claude|codex
+Shipped TDD (new =scripts/tests/ai-launcher-runtime.bats=, 6 tests red→green): =--runtime= flag + =AI_RUNTIME= env on =bin/ai=, runtime→CLI mapping (claude default, codex 1:1 — both take the opening line as a positional prompt), runtime-aware dependency check, =local= reserved with a clear not-wired-yet error (pending the model-floor evaluation), and a =--print-launch= mode as the test seam (prints the pane launch command, no tmux/fzf). Kept small per the 2026-06-24 helper-task caveat — dispatch pre-parse only, no launcher restructure. The Emacs-side equivalent (ai-term multi-LLM) remains .emacs.d's June handoff. Live smoke: correct commands for both runtimes against real projects.
-*** 2026-07-01 Wed @ 23:22:50 -0400 Codex spec-review rerun — Ready
-Codex re-read the revised [[id:80b0787b-4a60-4c82-8a16-b383d3e3c8f2][docs lifecycle spec]] after the second responder pass. All fourteen findings are closed, decisions remain [5/5], and the remaining implementation contracts are concrete enough to build and test. Status flipped to READY in the spec; implementation can proceed.
+*** 2026-07-13 Mon @ 17:05:00 -0500 Roam capture folded — the agent-switching ask
+Two roam items (2026-07-13) asked for lightweight agent selection (claude, qwen, chatgpt, ollama — "agentically democratic"). Partly shipped the same day: =--runtime claude|codex= (04c3b29; codex is the ChatGPT-side CLI). The ollama/qwen choice is the reserved =local= runtime, landing with the model-floor eval below. An interactive runtime picker rides the new launcher-hardening task ([#C] :refactor:solo:, filed from the same capture).
-*** 2026-07-01 Wed @ 23:34:15 -0400 Decomposed into build tasks; spec flipped READY → DOING
-spec-response Phase 6 run: this parent now carries the =:SPEC_ID:= binding (the spec's status-heading UUID), the phase tasks below track the build, and the spec's status heading is DOING. Completeness pass done: all ten acceptance criteria have homes across the phase tasks; vNext (org-agenda view) was already filed as the [#D] task below.
+*** 2026-07-13 Mon @ 16:41:14 -0500 Session plumbing assessed — no build needed
+Analysis in [[file:docs/design/2026-07-13-runtime-portability-inventories.org]] (Session plumbing section). session-context-path is runtime-aware by design; the anchor cycle is plain files any agent drives identically; self-inject.sh is harness-agnostic (only its PAYLOAD is Claude-shaped — a codex auto-flush is a payload variant whose second injected line carries the resume instruction itself, no hook needed); session-clear-resume.sh stays Claude-only and the payload variant obsoletes it off Claude. Wire the codex variant the day a codex session wants it.
-*** 2026-07-01 Wed @ 23:39:10 -0400 Phase 1 landed — docs-lifecycle rule + four spec-workflow updates
-claude-rules/docs-lifecycle.md written and linked machine-wide (make install). Canonical-side updates: spec-create Phase 5 + template (docs/specs/ location, two-sequence keyword header, DRAFT status heading with :ID:, transition mechanics), spec-review (location expectation with the legacy compatibility rule keyed on :LAST_SPEC_SORT:, plus the DRAFT→READY flip — and the demote-back-to-DRAFT path a failed re-review takes), spec-response Phase 6 (owns READY→DOING, stamps :SPEC_ID: on the build parent, always emits the flip-to-IMPLEMENTED task), task-audit Phase B (the :SPEC_ID: reconcile query, checking the parent's keyword rather than counting children). Mirror synced; make test green end to end.
+*** 2026-07-13 Mon @ 13:34:17 -0500 Memory story confirmed — KB is already the cross-agent store
+Details in [[file:docs/design/2026-07-13-runtime-portability-inventories.org]]. Auto-memory stays Claude-owned; non-Claude agents use the org-roam KB + file artifacts (todo/notes/session anchors), all runtime-neutral already. One wording gap: knowledge-base.md's capture-then-promote names harness memory as the capture layer — a one-sentence addition (session log as the capture layer for agents without harness memory) closes it, pending approval in the VERIFY below.
-*** 2026-07-01 Wed @ 23:57:44 -0400 Phase 2 landed — spec-sort helper + 30-test bats suite
-Built claude-templates/.ai/scripts/spec-sort (Python, TDD — the 30-test bats suite written red-first in claude-templates/.ai/scripts/tests/spec-sort.bats) covering the full retrofit contract: spine classification with the -spec.org-name-without-spine anomaly case, evidence panel (Status field, cookies, linking todo.org task, dated history, artifact existence) with conservative non-terminal proposals, per-candidate --confirm/--skip gate with --reason required on terminal keywords, clean-worktree preflight (--allow-dirty prints what recovery loses), validate-then-write from a recorded plan file, relink across the rewritten roots (inbound AND the moved doc's own outbound relative links) with report-only for sessions + synced templates (naming the canonical claude-templates file), bare-path mentions blocking until --acknowledge-bare, named recovery on injected mid-apply failure, post-apply residue gate, idempotent :LAST_SPEC_SORT: stamp. Real-data dry run against rulesets' pile matched predictions: 5 candidates, 4 anomalies, 30 notes, 0 bare, 10 report-only (incl. the startup.org synced-template case Codex flagged). make test green; sync-check clean.
+*** 2026-07-13 Mon @ 13:34:17 -0500 MCP portability checked — portable except paging
+Details in [[file:docs/design/2026-07-13-runtime-portability-inventories.org]]. Nine locally-configured servers (linear, notion, figma, slack-deepsat, google-calendar, google-docs x2, drawio, google-keep) port mechanically to any MCP-speaking harness. claude.ai-managed connectors (Gmail + claude.ai Calendar/Drive) don't travel but are redundant with local servers / cmail-action. The real finding: signal-mcp is NOT locally configured anywhere — the protocols.org paging path is claude.ai-side only, so off Claude there is no page channel. The Signal-pager [#C] task's signal-cli runbook is the fix; add runtime-portability as motivation there (VERIFY below).
-*** 2026-07-02 Thu @ 00:18:28 -0400 Phase 3 pilot ran — rulesets' pile sorted, board live
-Craig confirmed all five proposed keywords as-is plus the IMPLEMENTED reason; spec-sort --apply moved the five specs to docs/specs/ (agent-knowledge-base IMPLEMENTED, inbox-workflow-consolidation READY, autonomous-batch-execution READY, encourage-kb-contribution READY, wrapup-routing DOING — joining the docs-lifecycle spec's DOING on the board), rewrote 12 todo.org links plus the moved specs' own outbound links, and stamped :LAST_SPEC_SORT: 2026-07-02. Acceptance verified: status board matches reality, all re-homed specs carry -spec.org, residue zero in the rewritten roots (one acknowledged self bare mention rode along inside inbox-workflow-consolidation-spec), no id: links emitted, make test green. Surfaced and left in place: the four -spec.org-named files in docs/design without a spec spine (generic-agent-runtime, pattern-catalog, daily-prep-template, auto-triage-intake) — notes by predicate, misleading names; rename or leave is a Craig call. Report-only references: 9 frozen session archives + the synced startup.org (canonical edit lands with Phase 4's nudge work).
+*** 2026-07-13 Mon @ 14:40:00 -0500 Four inventory decisions — Craig approved all four
+Recorded in [[file:docs/design/2026-07-13-runtime-portability-inventories.org][the inventories doc]]: prose-only PreCompact downgrade off Claude; Stop-teardown via Codex notify where available, manual elsewhere; runtime-portability note added to the Signal-pager task; capture-layer sentence added to knowledge-base.md (non-Claude runtimes capture into the session log and promote from there).
-*** 2026-07-02 Thu @ 00:23:32 -0400 Phase 4 landed — startup nudge live, .emacs.d notified
-Added the spec-sort probe to startup.org Phase A (item 12) and the one-line nudge to Phase C's findings list, canonical-side, mirror synced. One refinement over the spec's sketch: the stray-root check uses find instead of compgen, because compgen is bash-only and zsh aborts on an unmatched glob — the original snippet false-negatived on stray root specs under zsh (spec snippet updated with a note). Fixture-verified in both shells: fires on an unsorted docs/design and on a stray docs/*-spec.org, silent with the marker stamped, silent with no docs at all. Also fixed startup.org's own stale reference to the moved encourage-kb-contribution spec (the pilot's report-only finding). Sent .emacs.d the convention-live note with its ~28-doc pile nudge and the id-index ask (org-id-extra-files enumeration or periodic org-id-update-id-locations, verify by clicking the docs-lifecycle spec's :ID:), asking it to tag the owning task :blocker: since rulesets' id-conversion task waits on it.
+*** TODO Local model floor evaluation
+The workflows assume long-context instruction-following (startup's multi-file read; the commits.md publish chain). Establish the minimum viable local tier (likely strong-70B+/MoE, 100k+ context), and what compensations a weaker model needs: shortened protocols, more checklist gates, more hook-level enforcement. Feeds the spec's "default local runtime/server" and "first supported local editing CLI" blocker decisions.
-*** 2026-07-02 Thu @ 05:12:33 -0400 Converted spec-target file: links to id: form (rulesets)
-All 13 file:docs/specs/ links lived in todo.org (zero in .ai/ or docs/ outside specs). 11 converted straight to [[id:UUID][label]] (bare links labeled with the spec filename); the 2 links carrying a ::*Review findings search target got full fidelity by minting an :ID: on that heading in the docs-lifecycle spec (cc77a7f6-e4c3-488a-ac3b-e739420a5c2b) — the id index scans whole files, so heading-level ids resolve. Residue grep zero; every id verified against its target's :ID:. Gate had cleared earlier tonight via .emacs.d's org-spec-links.el delivery (verified org-id-find on their side); M-x cj/org-id-refresh-spec-locations is the fix if a fresh id doesn't resolve on click.
+Environment inventory done 2026-07-13 (KB node "Local LLM inference inventory — daily drivers"): ratio is the inference box — Strix Halo iGPU + 125 GiB unified RAM. velox is out of scope (Iris Xe, 60 GiB, no ollama).
-*** 2026-07-04 Sat @ 11:46:31 -0500 Flipped the spec to IMPLEMENTED
-All four build phases had shipped (docs-lifecycle rule + spec-workflow updates, spec-sort helper + 30-test bats suite, rulesets pilot + status board, startup nudge) plus the file:→id: link conversion, so the spec's status heading went DOING → IMPLEMENTED with a dated history line and the Metadata mirror, per the transition-ownership table. Parent closed; manual validation promoted to its own task.
+Environment BUILT and verified, evening of 2026-07-13: ollama 0.31.2 as a systemd service, Vulkan/RADV backend forced via systemd override (ROCm loaded a 61 GB model at <25 MB/s — 40+ min, unusable; Vulkan loads it in 44 s), models swapped to the MoE shape this platform wants: gpt-oss:120b (44 s load, 37.9 tok/s, 100% GPU) and qwen3-coder:30b (5 s load, 76 tok/s). All four legacy dense models dropped; the orphaned ~/.ollama user store purged. Gotcha for the eval harness: pass num_ctx 8-32K — gpt-oss's native 128K default balloons the load. Remaining work is now purely the evaluation: drive a scripted startup read + a publish-flow transcript through gpt-oss:120b (and qwen3-coder as the fast comparator), grade instruction-following against the protocol stack, and answer the spec's default-local-runtime + first-supported-CLI blockers.
** TODO [#C] Docs-lifecycle convention — manual validation :test:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-07-13
+:END:
The human-eyes half of the docs-lifecycle acceptance surface. The convention shipped IMPLEMENTED 2026-07-04 (spec [[id:80b0787b-4a60-4c82-8a16-b383d3e3c8f2][docs-lifecycle]]); these checks confirm the human-facing behavior. A failed check promotes to a bug.
*** Startup nudge appears and clears
@@ -408,20 +443,17 @@ Expected: each opens the spec at its new docs/specs/ path.
Once specs carry lifecycle TODO keywords under =docs/specs/=, add a custom org-agenda view that lists =DRAFT= / =READY= / =DOING= / terminal specs by status. Deferred from [[id:80b0787b-4a60-4c82-8a16-b383d3e3c8f2][the docs-lifecycle spec]]; not part of v1 because the grep board is sufficient until the status headings exist.
** TODO [#C] Wrap-it-up summary mode — keep or cut :feature:
-From Craig via the roam inbox (2026-07-02, routed by archsetup). Teardown-by-default already shipped (bare "wrap it up" closes the window; "with summary" keeps it). Craig's follow-on: "maybe we cut the summary altogether. help me think through when I'd want a summary and how I would recognize it before confirming and then having it close." Run that think-through with him (brainstorm-shaped, not solo), then adjust wrap-it-up.org's Step 6 + trigger phrases to the outcome.
-
-** TODO [#C] ntfy phone channel as general two-way agent-comms :feature:spec:
:PROPERTIES:
-:CREATED: [2026-06-20 Sat]
-:LAST_REVIEWED: 2026-06-24
+:LAST_REVIEWED: 2026-07-13
:END:
-Proposal from the home project (2026-06-17): promote the self-hosted ntfy-over-Tailscale phone channel it built and verified on ratio into a general two-way agent-comms tool rulesets owns. Full proposal: [[file:docs/design/2026-06-17-ntfy-agent-comms-proposal.org]] (as-built runbook stays in the home project at =working/phone-notifications/spec.org=). What rulesets would decide: canonicalize =phone-notify= (send) plus a new =phone-recv= (check-since) as synced bin scripts; the per-machine config/secret convention (token in =~/.config/phone-notify/config= chmod 600 today, vs GPG-encrypted in dotfiles); a reference =ntfy-inbound-handler= plus systemd user-unit for event-driven delivery (Tier A subscriber routes inbound to inbox/notify, Tier B inbound spawns an agent session, Tier C notify a live session — harness research); approval-button workflows for the commits.md gates when Craig is away from the desk (tap-to-approve, the high-value concrete use); and the relationship to the retired cross-agent-comms scripts (ntfy may be the transport they lacked). Worked via =spec-create=. Blocks the triage-intake phone-push task below.
+From Craig via the roam inbox (2026-07-02, routed by archsetup). Teardown-by-default already shipped (bare "wrap it up" closes the window; "with summary" keeps it). Craig's follow-on: "maybe we cut the summary altogether. help me think through when I'd want a summary and how I would recognize it before confirming and then having it close." Run that think-through with him (brainstorm-shaped, not solo), then adjust wrap-it-up.org's Step 6 + trigger phrases to the outcome.
+
** TODO [#C] triage-intake.org auto mode — push each sweep to phone (ntfy) :feature:solo:
:PROPERTIES:
:CREATED: [2026-06-20 Sat]
-:LAST_REVIEWED: 2026-06-24
+:LAST_REVIEWED: 2026-07-13
:END:
-The work project (2026-06-18) added a "Push each sweep to Craig's phone (ntfy) — the primary delivery" subsection under "Trigger and delivery" in triage-intake.org auto mode, and asks to fold it into the canonical engine plus re-sync. Preserved bundle: [[file:docs/design/2026-06-18-triage-intake-phone-push-note.org][note]] + [[file:docs/design/2026-06-18-triage-intake-phone-push-workflow.org][edited workflow]]. Auto mode is the away-from-desk / vacation mode, so phone-notify becomes the primary delivery each sweep (fuller end-of-sweep output: per-source deltas, open-PR/Linear state, awaiting-ack list, one-line verdict, timestamp; SCAN FAILED banner on any source failure), plus phone-recv polling each sweep for Craig's replies. Falls back to inline when phone-notify is absent. Depends on the ntfy agent-comms task above (phone-notify/phone-recv must be canonicalized first). Shared template-workflow change, so review-gated.
+The work project (2026-06-18) added a "Push each sweep to Craig's phone (ntfy) — the primary delivery" subsection under "Trigger and delivery" in triage-intake.org auto mode, and asks to fold it into the canonical engine plus re-sync. Preserved bundle: [[file:docs/design/2026-06-18-triage-intake-phone-push-note.org][note]] + [[file:docs/design/2026-06-18-triage-intake-phone-push-workflow.org][edited workflow]]. Auto mode is the away-from-desk / vacation mode, so phone-notify becomes the primary delivery each sweep (fuller end-of-sweep output: per-source deltas, open-PR/Linear state, awaiting-ack list, one-line verdict, timestamp; SCAN FAILED banner on any source failure), plus phone-recv polling each sweep for Craig's replies. Falls back to inline when phone-notify is absent. Transport re-pointed 2026-07-13: ntfy is retired; agent-page (shipped today) is the send channel, so the push half is buildable now — substitute agent-page for phone-notify throughout. The reply-polling half (phone-recv) waits on the Signal-pager runbook's read-replies deliverable. Shared template-workflow change, so review-gated.
** TODO [#D] Fully-unattended scheduled inbox check (/schedule cron pass) :feature:
:PROPERTIES:
@@ -1279,112 +1311,23 @@ speculatively — defense-specific notations are narrow enough that each
skill should be driven by a concrete contract need, not aspiration.
* Rulesets Resolved
-** DONE [#B] wrap-it-up teardown + "wrap it up and shutdown" :feature:
-CLOSED: [2026-07-01 Wed]
-:PROPERTIES:
-:CREATED: [2026-06-23 Tue]
-:LAST_REVIEWED: 2026-06-24
-:END:
-Two additions to =wrap-it-up.org=, designed by Craig (home, 2026-06-23). Item 1: bare "wrap it up" also tears down the session — kill the =aiv-<proj>= tmux session (takes =claude= with it), kill the vterm buffer, restore geometry. Teardown is the default; "wrap it up with summary" wraps without teardown (keeps the buffer readable). Must fire from a Stop/SessionEnd hook via a sentinel file, decoupled and last, so the valediction flushes before the session dies, and strictly after commit+push is verified. Item 2: "wrap it up and shutdown" → wrap, then a hard blocking gate (abort unless this is the only live =aiv-*= session), then an abort-able 10→1 countdown, then =sudo shutdown now=. Countdown can't run through the Bash tool (stdout buffers — prints all ten at once); needs a detached tty writer or an Emacs =run-at-time= timer. Companion: =cj/ai-term-quit= (and optional =cj/ai-term-live-count=) must live in =.emacs.d/modules/ai-term.el= — route there via inbox-send when building so both sides land together. Open decisions for Craig first: qualifier wording ("with summary" vs "and summarize"), countdown home (tty script vs Emacs timer), session-count mechanism (=tmux ls= / =pgrep claude= / helper). Shared-asset, review-gated. Proposal: [[file:docs/design/2026-06-23-wrap-teardown-shutdown-proposal.org][proposal]]. From home 2026-06-23.
-
-*** 2026-06-23 Tue @ 23:31:59 -0400 Built the rulesets side; companion routed to .emacs.d
-Craig's three decisions (2026-06-23): non-destructive qualifier is *both* "with summary" and "and summarize"; countdown is an Emacs =run-at-time= timer; the gate uses =cj/ai-term-live-count=. Built and pushed: =hooks/ai-wrap-teardown.sh= (Stop hook, sentinel-gated, 8 bats tests green), =hooks/settings-snippet.json= Stop wiring, =wrap-it-up.org= Teardown-mode section + Step 6 + checklist, INDEX trigger update. Architecture: both teardown and shutdown fire from the Stop hook via a basename-keyed sentinel (=/tmp/ai-wrap-teardown-<proj>=, =/tmp/ai-wrap-shutdown-<proj>=) dropped only at the end of Step 6 after commit+push, so the valediction flushes first. No bin script — the gate/countdown/teardown are all =emacsclient= calls into the companion. Companion spec (=cj/ai-term-quit=, =cj/ai-term-live-count=, =cj/ai-term-shutdown-countdown=) routed to .emacs.d via inbox-send. Remaining: .emacs.d lands the three functions, then the manual end-to-end validation below. Task stays DOING until both sides verify.
-
-*** 2026-06-23 Tue @ 23:40 .emacs.d received + filed the companion spec
-Per a roam-inbox FYI from .emacs.d (2026-06-23 23:38): both ai-term handoffs (multi-LLM support + this wrap-teardown companion spec) landed and are filed as .emacs.d tasks. The teardown one is flagged for its own focused session to land alongside the rulesets half. Part (c) is now in progress on the .emacs.d side.
-
-*** 2026-06-24 Wed @ 06:51:13 -0400 Unblocked — .emacs.d companion landed; feature now live
-The three companion functions are in =.emacs.d/modules/ai-term.el= (=cj/ai-term-quit= 1068, =cj/ai-term-live-count= 1087, =cj/ai-term-shutdown-countdown= 1109), matching the contract — double-checked the bodies: quit kills session+buffer+restores layout idempotently, live-count returns the gate integer, shutdown-countdown re-checks the gate (TOCTOU guard), runs an abort-able =run-at-time= countdown (C-g cancels), then a configurable =cj/ai-term-shutdown-command=. 13 ERT tests, headless-verified live (.emacs.d FYI 2026-06-24 06:44). Dropped =:blocked:= / =:BLOCKED_BY:= — the build dependency is resolved; only the manual end-to-end validation below remains. NOTE: with the Stop hook wired and the companion present, the feature is now functional — the next bare "wrap it up" will actually tear the session down. Run the validation below before relying on it.
-
-*** 2026-07-01 Wed @ 21:52:15 -0400 Plumbing pre-flight re-verified; only the eyes-on tests remain
-Fresh pre-flight, all green: Stop hook block in =~/.claude/settings.json= points at =~/.claude/hooks/ai-wrap-teardown.sh=, the symlink resolves to the rulesets canonical, no stale =/tmp/ai-wrap-teardown-*= sentinel, and all three companion functions are live in the daemon (=(t t t)=). Three =aiv-*= sessions live right now (=_emacs_d=, =archsetup=, =rulesets=), so the shutdown-gate refusal test has its multi-session condition available. Everything left in the checklist below needs Craig's eyes on a scratch session: buffer teardown + geometry restore, the qualifier opt-outs, the countdown render + C-g, and the push-failure guard.
-
-*** 2026-07-01 Wed @ 21:59:43 -0400 Manual end-to-end validation passed — all five tests, Craig's live run
-Craig ran the full checklist in a live Emacs/tmux ai-term setup: (1) bare "wrap it up" tore down after the valediction rendered, geometry restored, no lingering sentinel; (2) "with summary" / "and summarize" both wrapped without teardown, buffer stayed readable; (3) "wrap it up and shutdown" with another aiv-* session live refused the shutdown, named the other session, fell back to a normal wrap; (4) as the sole session, the 10→1 echo-area countdown rendered one-per-second, C-g cancelled cleanly, and a full run fired the (stubbed) shutdown command; (5) with the push made to fail, the wrap stopped at the failure and no sentinel was dropped. Works great — feature validated and live. Both sides complete: rulesets Stop hook + wrap-it-up Teardown mode, .emacs.d companion functions.
-** DONE [#C] Guard against hardcoded host identity in synced files :feature:solo:
-CLOSED: [2026-07-02 Thu]
+** CANCELLED [#C] ntfy phone channel as general two-way agent-comms :feature:spec:
+CLOSED: [2026-07-13 Mon]
:PROPERTIES:
-:CREATED: [2026-06-22 Mon]
-:LAST_REVIEWED: 2026-06-24
-:END:
-A =CLAUDE.md= / notes file that asserts mutable environment identity as a fixed fact ("This machine is ratio", a current OS, an IP, "the laptop") is false on every machine the synced/tracked file lands on but one. It bit a real archsetup session: a stale "this machine is ratio" line made the agent reason backwards all session while on velox. Proposal: a claude-rule — don't assert mutable host/env identity as a fixed fact in a tracked/synced project file; derive it at runtime and name the command (=uname -n= for host; the =hostname= binary is often absent). Optionally a codify- or startup-time lint flagging "this machine is <name>" / "the current host is" style claims. Proposal: [[file:docs/design/2026-06-21-host-identity-guard-proposal.org][proposal]]. From archsetup 2026-06-21.
-
-2026-07-02 Thu @ 05:09:58 -0400 — Craig (speedrun pre-flight): rule + startup lint. A new claude-rules file plus a cheap grep probe in startup flagging host-identity claims in CLAUDE.md / notes.org fleet-wide.
-
-Resolution 2026-07-02: claude-rules/host-identity.md written (fixed-identity claims banned in tracked/synced docs, runtime derivation via uname -n, fleet-description carve-out, the archsetup worked failure) and linked machine-wide by make install. startup.org gained Phase A probe 13 (grep for "this machine/host/box is" claims in CLAUDE.md + notes.org, fixture-verified bash+zsh) and the Phase C host-identity flag line. Flags for judgment, never blocks.
-** DONE [#C] No-approvals speedrun — cross-project autonomous-batch mode :feature:spec:
-CLOSED: [2026-07-02 Thu]
-:PROPERTIES:
-:CREATED: [2026-06-15 Mon]
+:CREATED: [2026-06-20 Sat]
:LAST_REVIEWED: 2026-06-24
-:SPEC_ID: 90f623cd-fdbe-4f5c-b63d-b2f84d9151cf
:END:
-A named mode for coding projects: Craig names an ordered task set and says "speedrun" / "no approvals speedrun"; the set is worked autonomously, each task held to the full quality bar (TDD red→green, =/review-code=, =/voice= on the commit) and committed + pushed as its own logical commit, with all needed quick decisions gathered in one pre-flight Q&A (answer or "skip this") and a VERIFY filed for anything underspecified or needing deliberation, plus an end-of-set page listing completed + remaining + skipped tasks. Task size is not a gate — large tasks decompose into per-commit chunks. Surfaced by .emacs.d from a 2026-06-15 theme-studio session where the shape worked. Source proposal: [[file:docs/design/2026-06-15-fix-speedrun-workflow-proposal.org]] (.emacs.d handoff 2026-06-15). Build via =spec-create= when worked; we handle the task in priority order.
-
-Skeptical-review read (open design questions to resolve in the spec, not settled here):
-- *Is it a new workflow or a documented preset?* The proposal frames it as no-approvals + always-push session modes plus an end page. Decide whether it needs its own workflow file or is mostly documentation of a preset over the two existing modes.
-- *Where/how the page fires* — every task vs end-of-set, and via what. The paging surface is in flux (=page-signal= removed 2026-06-12), so reconcile against =notify --persist= or whatever paging stands now.
-- *Auto-pull vs explicit list* — whether the set comes from an explicit ordered list or a tag/priority query.
-- *Guardrails* — must refuse to speedrun tasks needing design decisions or carrying data-loss risk without a checkpoint (the sender's biased-safe unused-tile flag is the worked example).
-
-*** 2026-07-01 Wed @ 22:10:35 -0400 Phase 0 landed — hard tag definitions + review/audit enforcement
-todo-format.md gained the "Hard definitions: :solo: and :quick:" subsection under the scheme header (fixed across projects: :solo: = buildable + agent-verifiable + no deliberation, with one-or-two upfront-answerable quick decisions allowed per the ratified spec; :quick: = ≤30-min effort hint, never a gate). task-review.org: the two tagging sections are now explicitly mandatory ("a review that skips them is incomplete") and gate 3 was realigned from "no upfront decision" to the spec's no-deliberation form — the stricter old wording predated the pre-flight-Q&A decision and would have wrongly excluded quick-question tasks. task-audit.org: the re-assess bullet is marked mandatory and points at the todo-format hard definitions as canonical. Phases 1-6 (work-the-backlog extraction, callers, commit gate, checklist/Q&A/page, metrics, synthesis) remain.
-
-*** 2026-06-16 Tue @ 00:53:36 -0500 Spec written; design questions answered
-Craig's "your call" (2026-06-16) answered in [[id:90f623cd-fdbe-4f5c-b63d-b2f84d9151cf][the autonomous-batch execution spec]], which reconciles this with Phase E into one feature:
-- *Most effective / workflow-vs-preset:* one dedicated =work-the-backlog.org= workflow holds the execution loop; "fix speedrun" is a thin named preset (no-approvals + always-push + end page) feeding it an explicit list, and the inbox-zero loop feeds it a tag query. Pros of the shared workflow: one execution loop to audit, inbox-zero's three callers stay clean, both input shapes reuse one guardrail set. Cons: one more workflow file and a caller-to-workflow indirection. The con list is shorter and lighter than the duplication cost of two separate features, which is why the shared workflow wins. The pros carry the more important entries (single audit surface, clean seam).
-- *Paging:* end-of-set only, via =notify ... --persist= (reconciled past the removed page-signal wrapper).
-- *Auto-pull vs explicit list:* both — explicit list for the preset, tag/priority query for the loop.
-- *Effectiveness measurement (the trial Craig asked for):* the spec designs a per-task JSONL metrics log (=.ai/metrics/work-the-backlog.jsonl=), a corrections-in-next-session signal, and a periodic synthesis step that writes =:agent:metrics:= org-roam articles for later review — the "gather data + create org-roam articles" loop.
-*** 2026-06-29 Mon @ 03:48:09 -0400 Ratified the autonomous-batch execution spec
-Craig ratified all eight decisions in [[id:90f623cd-fdbe-4f5c-b63d-b2f84d9151cf][2026-06-16-autonomous-batch-execution-spec.org]] (revised this session — size gate removed, crisp four-item defer checklist, =:solo:= / =:quick:= definitions + task-review/audit enforcement, speedrun pre-flight Q&A). Spec Status → ready; implementation-ready across Phase 0–6. Decisions grew from six to eight during the revision.
-
-*** 2026-07-02 Thu @ 00:44:59 -0400 spec-response decomposition — :SPEC_ID: bound, spec DOING
-Stamped the spec's UUID on this parent, broke Phases 1-6 into the build tasks below (plus the flip task and a live-trial validation child), and flipped the spec's status heading READY → DOING per the transition-ownership table.
-
-*** 2026-07-02 Thu @ 01:07:29 -0400 Phase 1 landed — execution loop extracted into work-the-backlog.org
-work-the-backlog.org written (canonical + mirror): caller contract (task set + session mode + cap), five-outcome vocabulary, the loop, mechanical eligibility gate (TODO + :solo: per scheme header, safe-by-omission, no-scheme-header → don't run), four-item defer checklist, per-task quality bar, cap/kill-switch semantics, page + metrics stubs pointing at Phases 4-5. inbox.org's auto-mode per-cycle item 3 reverted to routing-only (yes-path execution removed; mode intro + closing line updated to match). INDEX.org entry added. make test green, sync clean; nothing invokes the new workflow yet.
-
-*** 2026-07-02 Thu @ 01:13:33 -0400 Phase 2 landed — both callers wired
-inbox.org auto-mode item 3 regained its "run this batch next?" ask, now chaining into work-the-backlog as an explicit second step after routing (eligibility query + file-only + paging off + cap 1). work-the-backlog.org gained the two caller sections: the auto-loop contract and the no-approvals speedrun preset (seven-step pre-flight → autonomous-commit + always-push + paging-on over an explicit list; finer Q&A mechanics deferred to Phase 4). Speedrun trigger phrases live in the workflow + INDEX; "speedrun" always routes to the preset, with a disambiguation note in no-approvals.org and its INDEX entry. Each caller independently exercisable.
-
-*** 2026-07-02 Thu @ 01:18:07 -0400 Phase 3 landed — waiver-gated commit autonomy
-Pinned the waiver format per D5: two marker lines in .ai/notes.org Workflow State — :COMMIT_AUTONOMY: yes (has the waiver) and :LOOP_MAY_COMMIT: yes (the unattended loop may also commit; requires the first). Absent or non-yes reads as no; the read is a fresh grep each run, never memory. Degrade contract written into work-the-backlog.org (surface in run intro + summary, never honor without the marker, never degrade silently); caller sections + Common Mistakes updated. Stamped rulesets' own :COMMIT_AUTONOMY: yes; :LOOP_MAY_COMMIT: deliberately not granted — Craig's call. .emacs.d holds the waiver too but its notes.org is its own scope; told via inbox-send to stamp its marker.
-
-*** 2026-07-02 Thu @ 01:21:47 -0400 Phase 4 landed — checklist mechanics, pre-flight Q&A contract, page
-The four-item checklist (in since Phase 1) gained its mechanics: a VERIFY-filing subsection (dedup against an existing sibling first — the deferred task stays TODO, so without the check every run re-files; placement/heading/body per todo-format.md) and a quick-question routing subsection (discriminator: one-line factual/preference pick vs tradeoff-weighing; three-plus questions = underspecified = file; item 2 data-loss never routes to Q&A). Preset section gained the batch-ask contract (one message, recommendation-first numbered options per interaction.md, answers recorded as dated lines in the task bodies before the run). Page section finalized (fires once on set-done or cap-hit; notify --persist is the paging surface). Common Mistakes 12-13 added. Checklist only ever reduces what runs; pre-flight fires only under the preset.
-
-*** 2026-07-02 Thu @ 01:24:50 -0400 Phase 5 landed — per-task JSONL metrics log
-Metrics section written into work-the-backlog.org: one record per task at outcome time, appended to the project's .ai/metrics/work-the-backlog.jsonl (git-tracked, append-only, dir+file created on first append). Full field table per the spec (ts, run_id, project, caller, task, outcome, defer_reason, upfront_decision, wall_clock_s, commit_sha, review_findings), outcome slugs mapped to the prose vocabulary, commit_sha flagged as the corrections-signal key (comma-separated when a task decomposed into several commits). Added the sixth outcome the spec's readiness section demanded but the enum missed: failed (tree left working, surfaced, run continues) — wired into the Outcomes vocabulary and loop step 4. A failed append warns in the run summary but never blocks, reorders, or aborts execution.
-
-*** 2026-07-02 Thu @ 01:27:43 -0400 Phase 6 landed — synthesis step to org-roam
-Synthesis section written into work-the-backlog.org (trigger "synthesize backlog metrics", INDEX row added): discover the JSONL union across project roots, classify each project per knowledge-base.md's denylist before reading, exclude work/unknown projects with the refusal contract, compute per-run rollups + trends, compute the corrections signal (later revert/fix commit touching the same files within ~14 days — a flag for human review, not a conviction), write one :agent:metrics: KB node under ~/org/roam/agents/ with [[id:...]] links to prior synthesis nodes, pull-before/commit-push-after. Read-only over the logs plus the single KB write; never mutates JSONL, todo.org, or any tree.
-
-*** 2026-07-02 Thu @ 05:26:07 -0400 Live trial passed — first speedrun ran 3/3, every loop part exercised
-Craig named the ordered set (id-link conversion, host-identity guard, template-sync policy) and said it was the validation run. Pre-flight Q&A fired once (two questions, both answered, answers stamped as dated lines before the run); each task landed as its own reviewed commit under the waiver (78bbaae, b6a977c, ed75d3c); metrics JSONL carries one record per task (run c726f526); the end-of-set page arrived via notify --persist. Nothing needed a VERIFY this run (all three cleared the checklist). Craig's read: granted :LOOP_MAY_COMMIT: on the strength of the run.
-
-*** 2026-07-02 Thu @ 05:26:07 -0400 Flipped the spec DOING → IMPLEMENTED
-All six phases built and the live trial validated. Keyword, dated history line, and Metadata mirror all flipped per the transition-ownership table.
-** DONE [#B] inbox-send filename collision silently overwrote a message :bug:solo:
-CLOSED: [2026-07-02 Thu]
-:PROPERTIES:
-:CREATED: [2026-07-02 Thu]
-:END:
-From archsetup (2026-07-02 0543, found in the wild): two --text sends in the same minute whose text starts with the same phrase derive identical filenames, and the second silently overwrites the first — archsetup lost a message at 05:42 and had to resend. Severity data-loss x rare-edge = P2 = [#B].
-
-Resolution 2026-07-02 (auto-inbox-zero loop, standing yes): uniquify() guard in inbox-send.py — an existing target gets a -2/-3/... stem suffix, both send_text and send_file paths, extension preserved. Four red-first tests reproduce the loss (module-level with a fixed timestamp so the same-minute collision is deterministic, plus a CLI loss-proof check); 30/30 green.
-** DONE [#C] page-me notify styling — all-red too alarming :bug:solo:
-CLOSED: [2026-07-02 Thu]
+Killed at the 2026-07-13 task review: home retired and tore down the ntfy channel on 2026-07-04, so this proposal's transport no longer exists. Its living successors are the Signal pager ([#B] task above — one identity on velox, runbook pending) and agent-page (shipped 2026-07-13), which cover the send half; two-way (read-replies) rides the Signal runbook.
+Proposal from the home project (2026-06-17): promote the self-hosted ntfy-over-Tailscale phone channel it built and verified on ratio into a general two-way agent-comms tool rulesets owns. Full proposal: [[file:docs/design/2026-06-17-ntfy-agent-comms-proposal.org]] (as-built runbook stays in the home project at =working/phone-notifications/spec.org=). What rulesets would decide: canonicalize =phone-notify= (send) plus a new =phone-recv= (check-since) as synced bin scripts; the per-machine config/secret convention (token in =~/.config/phone-notify/config= chmod 600 today, vs GPG-encrypted in dotfiles); a reference =ntfy-inbound-handler= plus systemd user-unit for event-driven delivery (Tier A subscriber routes inbound to inbox/notify, Tier B inbound spawns an agent session, Tier C notify a live session — harness research); approval-button workflows for the commits.md gates when Craig is away from the desk (tap-to-approve, the high-value concrete use); and the relationship to the retired cross-agent-comms scripts (ntfy may be the transport they lacked). Worked via =spec-create=. Blocks the triage-intake phone-push task below.
+** DONE [#B] Org-table helpers corrupt example blocks :bug:solo:
+CLOSED: [2026-07-14 Tue]
:PROPERTIES:
-:CREATED: [2026-07-02 Thu]
+:CREATED: [2026-07-11 Sat]
+:LAST_REVIEWED: 2026-07-13
:END:
-From Craig via the roam inbox (2026-07-02, routed by archsetup): the page notify's all-red styling "makes me feel like somehow the system is about to crash" — should be a persistent info-level notification.
-
-Resolution 2026-07-02 (auto-inbox-zero loop, standing yes): pages now use notify info --persist instead of notify alarm — page-me.org (all examples + prose, with Craig's verdict recorded) and work-the-backlog.org's end-of-set page. status-check's success/fail types untouched (job outcomes, not pages).
-** DONE [#C] Template sync with gitignored-only local changes :feature:
-CLOSED: [2026-07-02 Thu]
-From Craig via the roam inbox (2026-07-02, routed by archsetup): downstream projects should still pull template updates when their local changes sit entirely in gitignored files or directories — an inbox drop or a file left to read doesn't affect the templates, yet it currently holds the sync back and projects fall behind. When worked: verify how the sync gate actually detects dirtiness today, then let gitignored-only changes pass it.
+Fixed in 951b6fc, test-first. Both defects landed as filed (block-type-aware scanning in both helpers; lint-org CLI report-only by default, writes behind --fix), plus the true corruption path found during the work: wrap-org-table's load-time CLI dispatch fired on lint-org's require and reformatted lint-org's file arguments. Entry-script guard added. The named regression test (example block byte-identical) is in the suite.
+=wrap-org-table.el= and =lint-org.el= both scan for =/^\s*|/= lines and rewrite them as org tables without skipping =#+begin_example=/=src=/=quote= regions, so ASCII art using pipe characters gets mangled into bordered tables. Reproduced 2026-07-09 in the work project against an architecture doc with a pipe/=v= flow diagram; a plain indented block became a table with =|---|= rules between every line. Two separable defects: (1) table detection is line-based — both helpers should use =org-element-at-point= / =org-in-block-p= to skip example/src/quote/verse blocks; (2) =lint-org.el= mutates its input on disk with no confirmation — passing five files reformatted all five (one by 1949 lines). A linter must report, not write; put the reformat behind an explicit =--fix= flag.
-2026-07-02 Thu @ 05:09:58 -0400 — Craig (speedrun pre-flight): policy + audit. Scope read found startup's git gates already ignore untracked/ignored files; state the policy in startup.org and audit every dirty-check in the synced workflows to match (monitor-inbox's bare porcelain check is the known offender; tracked-modification blocking stays).
+Grading (severity × frequency, per todo-format.md): Critical severity (silent org data loss; recoverable here only because the content was git-staged) × rare-edge-case frequency (fires only when a mixed table+example file is passed to the helper) = P2 = [#B].
-Resolution 2026-07-02: template-freshness policy stated in startup.org Phase A.0 (dirty = tracked modifications only; untracked/gitignored never block pulls, ffs, or monitoring gates; the rsync WIP-guard named as the one deliberate exception — it holds back rulesets' own outbound WIP). Full audit of dirty-checks across synced workflows: startup's two git gates already complied; inbox.org monitor mode was the one offender — its precondition now uses --untracked-files=no with the explicit-staging rationale, and its close-out sweeps tracked changes only. triage-intake auto mode borrows monitor's gates, so it inherits the fix by reference.
+Regression test: run =wrap-org-table.el= against a file containing a =#+begin_example= block whose lines start with =|= and assert the block is byte-identical afterward. Source: work handoff 2026-07-09 (=inbox/2026-07-09-1341-from-work-bug-data-loss-wrap-org-table-el-and.org=).