From b405fff94d6ddcdd5b2278f20c327c583aad487d Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 11 Jun 2026 19:53:38 -0500 Subject: docs(spec): data-integrity rules for helper instances MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four loss windows the scoped-edit discipline doesn't cover: a primary file-wide hygiene pass silently clobbering a helper's concurrent edit (gate on live session-context.d/ files before any such pass), a new primary misreading helper dirt as leftover mess (surface live helper files at startup), crash recovery for shared-file edits (helpers journal each edit before applying it), and MEMORY.md's anchor-less read-modify-write index (memory writes stay primary-only). Backstop: every file-wide pass snapshots to /tmp before modifying. lint-org and wrap-org-table already conform; todo-cleanup — the pass that moves whole subtrees — does not, and Phase 1.5 brings it up to the invariant. --- .../2026-05-28-generic-agent-runtime-spec.org | 60 +++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) (limited to 'docs') diff --git a/docs/design/2026-05-28-generic-agent-runtime-spec.org b/docs/design/2026-05-28-generic-agent-runtime-spec.org index 5e547e0..bd9d60b 100644 --- a/docs/design/2026-05-28-generic-agent-runtime-spec.org +++ b/docs/design/2026-05-28-generic-agent-runtime-spec.org @@ -422,6 +422,56 @@ silently (both read, both write, last write wins). The contract, by tier: cross-agent form already specced above (=machine.project.agent-id=), or just gets reported to Craig. +*** Data-integrity rules — 2026-06-11 second pass + +The scoped-edit discipline covers helper-vs-primary edits on /different/ +headings. Four loss/corruption windows remain, each with a rule. They matter +doubly in the consolidated home project, where a single =todo.org= carries +every personal task and corruption has maximal blast radius. + +1. /Primary file-wide passes vs a live helper./ =todo-cleanup.el=, + =lint-org.el=, and =wrap-org-table.el= rewrite whole files; run while a + helper is mid-edit, they clobber the helper's just-written change (last + write wins, silently). Rule: before any file-wide pass, the primary checks + =session-context.d/= for live helper files. If any exist, it pauses the + pass and asks Craig (or skips hygiene for that wrap). A crashed helper's + stale file would block hygiene forever, so staleness is surfaced as a + judgment call — the file's own content and timestamps show whether the + helper is really gone — never silently skipped past and never silently + honored indefinitely. +2. /A new primary starting while a helper runs./ The previous primary may + wrap and exit while a helper keeps working; the next =ai= launch becomes + primary and runs full startup. The existing guards already do the right + thing — the helper's uncommitted edits make the tree dirty, and Phase A.0 + skips pulls on a dirty tree. Rule: startup additionally surfaces live + =session-context.d/= files as active agents (already in the + session-context contract above), so the new primary knows /why/ the tree + is dirty instead of treating it as leftover mess to resolve. +3. /Write-ahead edit journal./ The helper logs each intended shared-file + edit — file, heading, one-line intent — to its own session-context file + /before/ applying it. The Session Log discipline already requires logging + state-mutating turns; this tightens it to log-before-write for shared + files specifically. After a crash, the journal shows which edits landed, + and nothing about the shared file has to be reconstructed from memory. +4. /The memory dir./ =MEMORY.md= is a shared read-modify-write index with no + heading anchors — the same lost-update shape as =todo.org= with none of + the scoped-edit protection. Rule: helpers don't write memory at all. + Candidate memories go into the helper's session log; the primary (or its + wrap-up promotion check) writes them. + +Backstop, independent of all four: every file-wide pass snapshots its target +to =/tmp= before modifying. =lint-org.el= and =wrap-org-table.el= already do; +=todo-cleanup.el= — the pass that moves whole subtrees — does not, and gets +brought up to the invariant in Phase 1.5. Beyond that, the primary's normal +commit cadence keeps git the recovery layer: a corrupted =todo.org= is one +checkout away from its last committed state, which is exactly why all git +mutation stays primary-only and frequent. + +One small collision nit: =inbox-send= filenames carry minute-resolution +timestamps (=YYYY-MM-DD-HHMM-from--=). A helper and primary +sending to the same target in the same minute with the same slug would +collide; helper-originated sends include the agent id in the slug. + *** Helper startup and wrap-up Helper startup is deliberately light: resolve the context path, read @@ -504,9 +554,17 @@ Independent of the phases 2-6 go/no-go; same-runtime only. - Bats tests: id assignment and sanitization through the launcher, helper vs primary path resolution, two simultaneous context files (extends the existing =session-context-path= suite). +- Data-integrity items (the second-pass rules): the live-helper gate before + any file-wide hygiene pass (with stale-file surfacing); =todo-cleanup.el= + brought up to the backup-to-=/tmp= invariant the other two passes already + meet; log-before-write journaling for helper shared-file edits in + protocols.org; memory writes primary-only (helpers log candidates); agent + id in helper-originated =inbox-send= slugs. - Manual validation: Craig runs a real helper session against a live primary — a task lookup, one scoped =todo.org= edit, wrap-up — and the primary's - next commit picks up the helper's edit cleanly. + next commit picks up the helper's edit cleanly. Then the corruption drill: + with the helper mid-task, the primary attempts a wrap-up and the hygiene + gate visibly pauses on the live helper. ** Phase 2: Introduce runtime manifests and generic install commands -- cgit v1.2.3