diff options
| author | Craig Jennings <c@cjennings.net> | 2026-08-20 09:45:49 -0700 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-08-20 09:45:49 -0700 |
| commit | 435f622da565ca8ccb34b44b8917186b30d03a7d (patch) | |
| tree | aeeacd25b0e67c07356b9515b329eb130ec77de2 | |
| parent | 23d1d998995a776b5c184c6719602b1aba2c1d41 (diff) | |
| download | rulesets-435f622da565ca8ccb34b44b8917186b30d03a7d.tar.gz rulesets-435f622da565ca8ccb34b44b8917186b30d03a7d.zip | |
docs: file the inbox-send and test-determinism findings
I found two bugs while fixing the cmail-action path, both unrelated to it.
inbox-send has the same defect cmail-action had: bare invocation is the documented form across the rule layer and five workflows, and it isn't on PATH. Graded [#B] rather than [#C] on frequency, since cross-project handoffs are routine where mail sends aren't.
The argument for grading it worse is that a lost handoff is silent where a failed send is loud. I checked rather than assuming. The router resolves the script by path and gates on the return code, and a bare invocation fails visibly, so both paths are loud and the band holds. That argument stays on file because it would be right the moment a caller wraps the send in a swallowed subshell.
The other is a bats case that reads the real PATH for a binary it asserts about, so it passes only on a machine that happens to have codex installed. It was failing before today's work. Until it lands, green here means "only that case fails".
| -rw-r--r-- | todo.org | 153 |
1 files changed, 152 insertions, 1 deletions
@@ -39,6 +39,157 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest * Rulesets Open Work +** TODO [#B] ai-launcher-runtime bats test depends on an unstubbed host binary :bug:quick:solo: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-20 +:END: +=scripts/tests/ai-launcher-runtime.bats= test 8 ("--print-runtimes lists +claude, codex, and one line per ollama model") fails on velox and passes on a +machine that happens to have =codex= installed. Found as a red green-baseline +on 2026-08-20; unrelated to the work that surfaced it. + +Root cause: the test stubs =ollama= into =$STUB_BIN= but not =codex=, while +=build_runtime_choices()= in =claude-templates/bin/ai= gates *both* the codex +line and the =local:= lines on =command -v codex=: + +#+begin_src bash +command -v codex >/dev/null 2>&1 && echo "codex — ChatGPT (Codex CLI)" +if command -v codex >/dev/null 2>&1 && command -v ollama >/dev/null 2>&1; then +#+end_src + +So on a host without codex all three of the test's remaining assertions fail +at the first one. Test 9 survives only because it asserts =*"claude"*=, which +is present either way. + +Fix: stub =codex= in that test's setup exactly as =ollama= is stubbed, so the +case tests the launcher's logic rather than the host's package list. A test +that reads the real =PATH= for a binary it is asserting about isn't +deterministic (=testing.md=). + +Grading: Major severity (a chronically red suite blocks the pre-commit gate +and trains readers to ignore red — =verification.md= treats a red baseline as +work-blocking) × most users, frequently (red on every run on velox, one of +two daily drivers) = P2 = =[#B]=. + +Until this lands, the green bar for other work in this repo is "only +ai-launcher-runtime.bats test 8 fails." + +** DONE [#C] cmail-action documented as a PATH command it wasn't :bug:quick: +CLOSED: [2026-08-20 Thu] +:PROPERTIES: +:LAST_REVIEWED: 2026-08-20 +:END: +Fixed by moving the script to =claude-templates/bin/cmail-action=, which +=make install= links into =~/.local/bin= — so the documented invocation is now +the true one. =command -v cmail-action= resolves and =cmail-action --help= +runs. The =inbox-send= half is split out as its own task below, per Craig's +call. + +The original report and analysis: + +=protocols.org= line 347 claims =cmail-action send= is "symlinked into +=~/.local/bin=, on PATH from any project." It isn't, on any machine. +=command -v cmail-action= returns nothing; =~/.local/bin= has no cmail entry. +The only copies on disk are the 17 per-project =.ai/scripts/cmail-action.py= +files the template sync delivers. Reported by home 2026-08-19 after it cost +them a real failed send; every claim verified here against the source. + +Grading: Major severity (the documented primary interface for a core +capability is nonfunctional, and the prose actively misdirects) × some users, +sometimes (fires on every documented mail send, but sends are occasional +across the fleet) = P3 = =[#C]=. + +Wider than reported, in two directions: + +- =send-email.org= uses the bare =cmail-action send= form in four places. It + is a synced workflow and the one protocols.org points at for the guided + flow, so fixing protocols.org alone leaves the failure reachable through + the very workflow the corrected prose recommends. +- The same defect class hits =inbox-send=: bare =inbox-send <target>= is the + documented invocation in =cross-project.md=, =todo-format.md=, and five + synced workflows, and =inbox-send= is not on PATH either. protocols.org's + own colloquialism section gives the correct =python3 .ai/scripts/inbox-send.py= + form, so the two shapes already contradict each other across the rule layer. + +Two facts decide the fix, and neither was available to home: + +- =cmail-action.py= has *zero* project coupling — every path in it is + =Path.home()=-based (=~/.config/.cmailpass=, =~/.config/protonbridge.pem=) + against a hardcoded =127.0.0.1:1143=. It is a machine-global personal tool + being distributed by a per-project mechanism, which is why 17 identical + copies exist. +- The Makefile's install loop already globs =claude-templates/bin/*=, so a + file moved there is linked with no new install logic. Home assumed the fix + needed an install step written; it needs a move. + +So the fix is a move to =claude-templates/bin/cmail-action=, not a Makefile +special case, and not path-prefixing the prose (which would cement the wrong +architecture into five more files). + +Companion edits the move requires: =send-email.org='s four bare invocations, +and =triage-intake.cmail.org='s =:ENABLED:= gate (=test -f .ai/scripts/cmail-action.py= +→ =command -v cmail-action=, which tests what it actually runs) plus its five +path invocations. Note the ordering constraint: =sync-templates= runs +=--delete= on =scripts/=, so removing the template copy propagates the +deletion to all 17 projects — the symlink must exist on a machine before the +deletion lands there. Startup runs =make install= in Phase A.0 before the +Phase A rsync, so a single session already orders this correctly. + +Also worth one sentence: protocols.org never names Proton Bridge as a +precondition for sending. (Home guessed a Bridge-down failure would look +opaque; it wouldn't — both the IMAP and SMTP paths already exit with "Is +protonmail-bridge running?" — but the precondition is undocumented.) + + +** TODO [#B] inbox-send is documented as a bare PATH command it isn't :bug:quick:solo: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-20 +:END: +Split from the cmail-action fix above (Craig's call, 2026-08-20) — same defect +class, second tool, wider blast radius. + +=command -v inbox-send= returns nothing, yet bare =inbox-send <target>= is the +documented invocation in =cross-project.md= (four places, including the +worked-example block agents copy from), =todo-format.md= (two), and five synced +workflows: =triage-intake.org=, =wrap-it-up.org=, =inbox.org=, =open-tasks.org=, +=process-meeting-transcript.org=. + +The rule layer already contradicts itself: protocols.org's colloquialism +section gives the correct =python3 .ai/scripts/inbox-send.py <project> --text= +form, so an agent's behavior depends on which file it happened to read. +Confirmed live this session — the reply to home had to use the path form. + +Graded higher than the cmail one because the frequency row differs, not the +severity: cross-project handoffs are routine and every workflow above reaches +for this. Major severity (documented primary interface nonfunctional) × most +users, frequently = P2 = =[#B]=. + +Fix by the same move, with one caveat cmail-action didn't have: +=inbox-send.py= is genuinely cwd-aware (it derives =from-<this-project>= +provenance from the working directory). Being cwd-aware doesn't require living +inside the project — it reads =$PWD= at run time either way — but verify that +before moving, and check =route-batch=, which shells out to it. + +=:solo:= now that the approach is settled: bounded file set, mechanical edits, +verifiable by =command -v= plus a real cross-project send. + +Home argued for a higher grade (2026-08-20): a failed mail send is loud, but a +failed handoff would be silent in the direction that matters — the sender +believes delivery happened and the receiver never learns it was owed a message. +A lost message rather than an error. + +Checked it rather than taking it, and it doesn't hold. =route-batch= resolves +the script by path (=Path(__file__).with_name("inbox-send.py")=), so the +automated router never touches PATH at all, and it gates on +=result.returncode == 0= while surfacing stderr. A bare hand invocation fails +with a visible command-not-found. Both paths are loud, so the severity band is +unchanged and this stays =[#B]=. + +Worth keeping the argument on file anyway: it describes a real failure shape +this repo keeps producing (a check that can't tell "nothing to do" from +"couldn't do it"), and it would be correct the moment any caller wraps +=inbox-send= in a swallowed subshell. + ** TODO [#A] github-prs plugin returns work PRs into personal triage :bug: SCHEDULED: <2026-08-19 Wed> :PROPERTIES: @@ -108,7 +259,7 @@ home worked around the *read* half by scanning the maildir through =mu=, which their notes already name as the reliable path when an MCP binding is in doubt — but the plugin does not sanction it, so that was their substitution rather than the workflow's. Fix: declare the mu/maildir fallback as a real scan tier and give -it action verbs. =cmail-action.py= is the working model (local script over the +it action verbs. =cmail-action= is the working model (local script over the maildir, with mark-read/star/trash); a =gmail-action.py= sibling closes it, and mbsync already syncs the maildir. |
