aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-08-05 17:49:17 -0500
committerCraig Jennings <c@cjennings.net>2026-08-05 17:49:17 -0500
commit91ac5a10d02c65c721c0bd83df60982d468b5df6 (patch)
tree2e08d5ce337d23cf1a42a5acb4c9c2d0c66f2f9b
parent771c669706a36908d41deb42b1a14b98ec6dd5d3 (diff)
downloadrulesets-91ac5a10d02c65c721c0bd83df60982d468b5df6.tar.gz
rulesets-91ac5a10d02c65c721c0bd83df60982d468b5df6.zip
chore: track the teardown-gate, anchor-default and bundle-ignore findings
Four findings arrived from other projects. Each is graded and filed. Teardown kills the project's tmux session without checking whether another agent is living in it (P2). Shutdown mode has had that gate all along and calls it load-bearing. The agent-scoped session anchor should probably be the default rather than the opt-in. TMUX_PANE is a fourth option the proposal didn't consider: it's stable across calls and keeps the resolver a pure function of its environment. Every language bundle appends the same unconditional tooling block, which asserts the opposite of the truth in a project that tracks those files (P3). The wrap hook's block message names a workflow step instead of the command that clears it, so a project running an older workflow gets no route to the fix (P3).
-rw-r--r--.ai/notes.org2
-rw-r--r--todo.org232
2 files changed, 233 insertions, 1 deletions
diff --git a/.ai/notes.org b/.ai/notes.org
index b6c85f8..be6708c 100644
--- a/.ai/notes.org
+++ b/.ai/notes.org
@@ -85,6 +85,6 @@ Format:
Markers maintained by workflows to record when they last ran. Read by other workflows that gate their behavior on freshness.
:LAST_AUDIT: 2026-07-20 (open set current — this session's shipped work (working/temp, triage-source-activation, silent-until-signal, suspend detach) closed as it went; sentry cluster consolidated (merged the /schedule tasks, added cross-host-coordination); nothing shipped-but-open per git reconcile. Live finding: the Polyglot + Subprojects scouting tasks are SCHEDULED 2026-07-20 and due.)
-:LAST_INBOX_PROCESS: 2026-07-31 (four work handoffs: standup-scripts proposal accepted with four changes and shipped as a212eeb; voice pattern #48 filed [#B], then its interaction.md mirror folded in on Craig's approval; a correction FYI acknowledged — my project-workflows fork claim was wrong, Phase 11 composes rather than shadows)
+:LAST_INBOX_PROCESS: 2026-08-04 (ten handoffs, batch-approved: four implemented — signature paths, lint-org anchor scope, gmail pagination floor, telega 404 callback; two folded — work's link sweep as independent confirmation, KB orphans 42 → 113; four filed — [#B] teardown live-sibling gate, [#B] agent-scoped anchor default, [#C] install-lang track-mode ignores, [#C] hook message names a step not a command; all four senders replied to)
Format: one =:MARKER: YYYY-MM-DD= line per workflow. Workflows overwrite their own marker on completion.
diff --git a/todo.org b/todo.org
index 6750eed..b288a4f 100644
--- a/todo.org
+++ b/todo.org
@@ -179,6 +179,201 @@ The gate already exists and is nearly right. =scripts/lint.sh='s =check_md_links
Found by the adversarial reviewer on the =references/= fix, 2026-07-28, as the sibling class the original report missed.
+*** 2026-08-04 Tue @ 09:51:13 -0500 Independently confirmed by work's link sweep
+Work swept all 533 tracked org files in their project (sentry cycle, 2026-08-03)
+and landed on the same defect from the opposite direction — a consuming project
+finding the dead targets rather than rulesets predicting them. Their table
+matches this task site for site: task-review ×3 plus one to =docs/design/=,
+task-audit ×1, open-tasks ×1, suspend ×1.
+
+Two things the confirmation adds. They checked =inbox.org=:97's
+=[[file:working/<slug>/proposed.diff]]= and correctly excluded it — that one is
+a template placeholder inside an example block, so the fix must not sweep it up.
+And they independently proposed the same fix direction I'd filed, plus an
+absolute =~/code/rulesets/...= target as an alternative to prose. I'd still
+prefer prose: an absolute path resolves in both trees but bakes a machine layout
+into a file that ships everywhere.
+
+Nothing to change in the plan; this raises confidence that the seven sites are
+the whole set.
+
+** TODO [#B] Teardown has no live-sibling gate :bug:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-08-04
+:END:
+Wrap-up's teardown mode kills the =aiv-<project>= tmux session without checking
+whether another agent is living in it. Archsetup hit this on 2026-08-04: two
+primaries in one project, two windows in one tmux session, and teardown would
+have taken both. They caught it by reading the pane layout before dropping the
+sentinel and aborted the wrap by hand.
+
+Shutdown mode already has exactly this guard — it gates on
+=cj/ai-term-live-count= and =wrap-it-up.org= calls that gate "the load-bearing
+safety of the whole feature." Teardown, twenty lines away, has nothing. Verified
+in the canonical: Step 6's teardown branch drops =/tmp/ai-wrap-teardown-<project>=
+unconditionally.
+
+Grading: Critical severity (kills another agent's live session and its
+in-progress work — the anchor survives, but anything unwritten does not) ×
+rare edge case (needs two primaries in one project) = P2 = =[#B]=.
+
+Fix direction, and archsetup's own suggestion is the better one: gate on the
+pane count of *this* project's session (=tmux list-panes -t aiv-<project>=)
+rather than reusing the machine-wide =cj/ai-term-live-count=. Live-count is
+global, so reusing it would abort a legitimate teardown whenever any unrelated
+project had a session up. The narrow check also keeps the fix inside rulesets —
+no =.emacs.d= handoff needed, unlike anything routed through =ai-term.el=.
+
+Add the check in both places, because the window between them is real: at
+sentinel-drop (where the decision is made and can be reported in the
+valediction) and again in the hook before it consumes the sentinel (a sibling
+can arrive in between). The hook already re-verifies the clean-tree certificate,
+so it has the shape for a second gate.
+
+Not =:solo:= — the verification that matters is a live two-session drill in
+Craig's terminal, and a fix that passes bats while still killing a real sibling
+is exactly the failure mode here.
+
+Source: inbox/2026-08-04-0023-from-archsetup-addendum-to-today-s-agent-anchor.org
+
+** TODO [#B] Agent-scoped session anchor as the default :spec:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-08-04
+:END:
+Archsetup proposes that =.ai/session-context.d/<id>.org= become the path
+always, rather than the =AI_AGENT_ID=-set special case. Their argument is that
+the unset case is also the common case: every agent wants an anchor that is
+its own, and the single-agent situation is just the one with no contention yet.
+Defaulting to the directory removes the race by construction.
+
+The collision that prompted it was real. Two archsetup sessions both resolved
+to =.ai/session-context.org= and interleaved their logs; nothing was lost, but
+only because the second one noticed and appended rather than writing. At wrap
+they had to hand-split the file to avoid archiving the sibling's in-progress
+record along with their own.
+
+The obstacle is protocols.org's invariant: =session-context-path= is called
+many times per session and must return the same path every time, so it must
+never mint an id itself. Archsetup lists three ways out — derive from something
+already stable, let the resolver mint once and persist, or make every launcher
+set =AI_AGENT_ID= — and leans on the third. I don't think the third can work:
+a bare =claude= is always available, protocols.org already concedes it runs
+full primary startup regardless, and their own collision was two bare
+invocations.
+
+There's a fourth option they didn't list, and I think it beats all three:
+=$TMUX_PANE= is exported into the agent's Bash environment and is stable across
+calls (measured =%19= on two separate invocations, 2026-08-04). A resolver that
+falls back to the pane id when =AI_AGENT_ID= is unset stays a *pure function of
+the environment* — no minting, no state file, no clearing rule — which is
+exactly the invariant the three listed options each have to bargain with.
+
+What to check before committing to it: pane ids are unique per tmux server but
+reset when the server restarts, so the collision moves rather than vanishing
+(narrower, since an old anchor is archived at wrap). And an agent outside tmux
+has no =$TMUX_PANE= at all, so the fallback needs its own fallback — possibly
+the controlling terminal, possibly the current unscoped path.
+
+Also decide in the same pass: the absence of =.ai/session-context.org= is
+currently the signal that the last session wrapped cleanly, and both startup and
+wrap-up key on it. Moving to a directory turns that into "is the directory
+empty," and both call sites have to change together or a crashed session stops
+being detectable.
+
+Their second proposal — a startup roster check so a bare invocation learns a
+sibling is live — is already scoped in the helper-agent task ("Startup
+detection-first: the roster check runs before Phase A.0's pulls"), so it belongs
+there rather than here. Worth sequencing: the roster check surfaces the
+collision, this removes it.
+
+Source: inbox/2026-08-03-1443-from-archsetup-proposal-from-a-concrete-collision-in.org
+
+** TODO [#C] install-lang ignores tooling a track-mode project tracks :bug:solo:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-08-04
+:END:
+Every language bundle's =gitignore-add.txt= opens with the same unconditional
+block — =.claude/=, =CLAUDE.md=, =githooks/= — which =install-lang.sh= appends
+to the target's =.gitignore=. In a track-mode project those three lines assert
+the opposite of the project's actual policy. Work hit it installing the python
+bundle into a project that tracks both =CLAUDE.md= and =githooks/pre-commit=.
+
+Verified across the whole set: bash, elisp, go, python and typescript all carry
+the identical header block, so this is not python-specific. The build-artifact
+entries below it (=*.pyc=, =htmlcov/=, =node_modules/=, =*.elc= …) are correct
+unconditionally — only the tooling header is mode-dependent.
+
+Nothing breaks today, which is why it went unnoticed: git keeps honoring a path
+that is already tracked. The failure is latent and quiet — a *new* file added
+under =githooks/= goes untracked with no warning, so a hook that should travel
+to the other daily driver silently doesn't. And the =.gitignore= now
+misrepresents the project's own policy to whoever reads it next.
+
+Grading: Major severity (a new hook or rules file silently untracked, and
+nothing signals it — the change just fails to reach the other machine) × some
+users sometimes (a track-mode project installing a language bundle) = P3 =
+=[#C]=.
+
+Fix, specified so it needs no design call: split =gitignore-add.txt= into the
+tooling header and the build-artifact body, and append the header only in
+gitignore mode. The mode detection already exists — =scripts/sweep-gitignore-tooling.sh=
+recognizes both the anchored (=/.ai/=) and unanchored (=.ai/=) ignore styles for
+exactly this purpose; reuse its predicate rather than writing a second one that
+can disagree with it. Track mode gets the build-artifact body alone, plus a
+comment saying why the tooling entries were omitted, so a later install isn't
+read as having forgotten them.
+
+Done when: all five bundles split, the predicate shared with the sweep script
+rather than duplicated, bats covering both modes against both ignore styles, and
+=make test= green.
+
+Smaller thing from the same report, worth doing in the same pass: =install-lang=
+seeds =coverage-makefile.txt= at the project root unconditionally, so a project
+with no suite for those targets gets a stray fragment. Either gate the seed or
+say in the file what it's for.
+
+Source: inbox/2026-08-02-2210-from-work-install-lang-mis-ignores-a-track-mode.org
+
+** TODO [#C] Stop hook blocks on a step its workflow may not carry :bug:quick:solo:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-08-04
+:END:
+The wrap teardown hook blocked a wrap in website because that project's synced
+=wrap-it-up.org= predated the =git-worktree-gate certify= step the hook enforces.
+The block message told them to rerun the final wrap verification, which doesn't
+point at the fix, so diagnosing it meant reading the hook's source.
+
+The underlying asymmetry is real: the hook reaches a project as a =~/.claude=
+symlink from =make install=, which startup runs unconditionally in Phase A.0,
+while the workflow that drives it arrives by the *guarded* rsync in Phase A. The
+two halves of one feature can move independently, and nothing signals the skew
+until the hook fires.
+
+Partly overtaken by events, and the report says so honestly without knowing it:
+the per-path sync narrowing (=f69dc22=) landed four minutes before that handoff
+was written. The specific cause named — one dirty =sentry.org= withholding all
+three rsyncs — can't happen now. What survives is the general case: a workflow
+withheld for its *own* dirt, or a project branch behind upstream (that guard is
+still all-or-nothing), still runs old against a new hook.
+
+Grading: Minor severity (a blocked wrap with a misdirecting message; the
+sentinel survives, running the gate by hand clears it, nothing is lost) × some
+users sometimes = P3 = =[#C]=.
+
+Fix, and take only the first half: make the hook's block message name the
+command (=git-worktree-gate certify=) rather than describing a workflow step.
+That is correct whatever version of the workflow the project holds, which is
+precisely the property the skew breaks — a message that names the *step* is
+another thing that can drift, while a message that names the *command* can't.
+
+The report's second suggestion — startup warns when a rulesets-owned hook is
+newer than the project's synced copy of the workflow that drives it — is a
+version-skew detector, and a real idea, but it needs a hook-to-workflow mapping
+that nothing maintains today. Leaving it here as a note rather than filing it:
+if the message fix doesn't stop the class, that's the escalation.
+
+Source: inbox/2026-07-31-2247-from-website-drift-report-from-website-s-wrap-up.org
+
** TODO [#C] start-work Phase 7 still summarizes the old publish flow :chore:solo:
:PROPERTIES:
:LAST_REVIEWED: 2026-07-28
@@ -484,6 +679,27 @@ Both found 2026-07-23 reading =claude-templates/bin/agent-text= and =scripts/sig
Grading: Minor severity for both (one is a latent hang in a tool with a documented fallback, the other a warning on an account nothing currently depends on) x most users, frequently (the stale warning is a standing condition on this host; the hang needs a stall) = P3 = [#C].
** TODO Manual testing and validation
+*** Telegram scan — no spurious 404 after the loadChats callback fix
+What we're verifying: passing =#'ignore= to =telega--loadChats= stops the two
+=Telega error 404: Not Found= lines each Telegram scan printed into the echo area
+and =*Messages*=, without changing what the scan reads. The fix is code-verified
+against telega 20260706.2147 but has never been run — it needs a live telega, so
+it can't be checked from an agent session.
+- Clear the decks so the count is unambiguous.
+#+begin_src emacs-lisp
+(with-current-buffer "*Messages*" (let ((inhibit-read-only t)) (erase-buffer)))
+#+end_src
+- Run a Telegram triage scan (the =triage-intake= telegram source).
+- Check what the chat table holds and whether the noise reappeared.
+#+begin_src emacs-lisp
+(list :chats (hash-table-count telega--chats)
+ :errors (with-current-buffer "*Messages*"
+ (how-many "Telega error 404" (point-min) (point-max))))
+#+end_src
+Expected: =:chats= is populated as before (non-zero, matching what a scan used to
+report) and =:errors= is 0. Before the fix this reliably printed two per scan, so
+a non-zero count means the callback isn't being consumed and the fix is wrong.
+
*** Sentry — entry gates fire with Craig present
What we're verifying: the interactive entry gates stop for the right states and start the loop only on a clean, green baseline.
- On rulesets (ratio), with a clean tree and green suite, say "start sentry hourly".
@@ -599,6 +815,22 @@ From the roam inbox (2026-07-11): work in progress in one project shouldn't stop
: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).
+*** 2026-08-04 Tue @ 09:51:13 -0500 Fresh hygiene report — orphans 42 → 113
+The 2026-08-01 report: 124 agent nodes, 625 total KB org files, 0 conflicts, no
+duplicate titles, and 113 orphans. So orphans grew 2.7× in a month while agent
+nodes roughly doubled — nearly every node written since 2026-07-01 landed
+unlinked.
+
+That reframes the task. At 42 this was a cleanup backlog; at 113, with the ratio
+holding at about nine in ten, the backlog is a symptom and the write path is the
+cause. Nodes are being written without =[[id:]]= links to their neighbours, so
+pruning the list would leave the same list regrowing. Worth folding into the
+pass: does =knowledge-base.md='s write block ask for the links firmly enough,
+and would a node-write prompt to name one related node close it at the source?
+
+Grading unchanged at =[#C]= — orphan-ness still isn't a defect on its own, and
+=rg= still finds these nodes. The number is a signal, not a failure.
+
** TODO [#B] Helper-agent instance support — concurrent same-project Claude :feature:spec:
:PROPERTIES:
:CREATED: [2026-06-11 Thu]