diff options
Diffstat (limited to 'todo.org')
| -rw-r--r-- | todo.org | 337 |
1 files changed, 337 insertions, 0 deletions
@@ -39,6 +39,343 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest * Rulesets Open Work +** TODO [#A] github-prs plugin returns work PRs into personal triage :bug: +SCHEDULED: <2026-08-19 Wed> +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +=triage-intake.github-prs.org:15= gates on =command -v gh && gh auth status=. +=gh auth status= exits 0 when *any* configured host authenticates, including a +GitHub Enterprise host, so the precondition can pass on the strength of the work +credential alone. The scan then runs =gh search prs --author=@me=, which resolves +against the enterprise host and returns work PRs into a personal project's +triage. home hit this today and discarded a =deepsat/sbir-pleo-d2p2-submission= +PR by hand. + +Reproduced on velox: + +: gh auth status rc 0 +: gh auth status --hostname github.com rc 1 +: gh auth status --hostname deepsat.ghe.com rc 0 + +So the precondition passes with no *usable* github.com credential, and every +result would be work rather than merely some. + +home corrected one detail I had wrong. I first read this as github.com not being +configured at all; the entry does exist, its token is simply invalid +(=GH_HOST=github.com gh auth status= prints "The token in default is invalid"). +Same functional state and the same fix, but the entry's existence is what makes +the failure quiet — gh knows the host, so nothing in the config looks absent. + +Fix needs both halves. Pin the precondition +(=gh auth status --hostname github.com=) and pin the scan, because =gh search +prs= has no =--hostname= flag — =GH_HOST=github.com= is the lever. The +precondition alone only caught today's case because github.com's token happens +to be broken; with two valid hosts it would pass and the scan would still be +ambiguous. + +The plugin's own header says a work project on GHE declares its own plugin and +does not edit this one, so github.com-only is the stated design. The defect is +that nothing enforced it. + +*Generalize before closing:* an =:ENABLED:= test proving *a* credential exists is +not the same as proving the *right* one does. Every plugin whose tool can address +more than one account has this shape — audit the others rather than fixing this +one in isolation. + +Grading: severity-alone carve-out. This is a confidentiality boundary violation — +work material surfacing in a personal project, in a system whose boundary +discipline exists to keep them apart — and it presents identically to a healthy +scan. One occurrence with the wrong PR in it is a showstopper regardless of +frequency, so Critical = P1 = =[#A]=. Source: home handoff 2026-08-19. + +** TODO [#B] triage-intake reports a clean close over an unactionable backlog :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +Two levels, and the engine half is the one that matters. + +*Plugin:* =triage-intake.personal-gmail.org:11= gates on the +=google-docs-personal= MCP, and all three hygiene verbs (lines 73-75: trash, +mark-read, star+read) are MCP-only. When that MCP is absent the source goes +inactive and the sweep announces a skip — going blind on the highest-volume +personal channel, which held 873 unread when home looked. The other Gmail MCP on +offer is work-bound (there is a recorded 2026-07-23 incident where a pass used it +expecting personal and got 201 unread work messages), so it is not a substitute. +Confirmed absent from this velox session too. + +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 +maildir, with mark-read/star/trash); a =gmail-action.py= sibling closes it, and +mbsync already syncs the maildir. + +*Engine, and this is the durable half:* =triage-intake.org= Phase D says hygiene +runs on every scanned account. It has no concept of a source that was *scanned* +but whose action verbs are unavailable, so the sweep classifies everything and +acts on nothing while reporting a clean close. Today that gap surfaced only +because home wrote a prose line about it. It should be a first-class close +outcome alongside =SCAN FAILED= — e.g. =ACTIONS UNAVAILABLE: <source> — scanned +via <fallback>, hygiene not run, N items left unprocessed=. + +*This is the third instance today of one failure shape:* a mechanism that cannot +distinguish "nothing to do" from "could not do it" reports success either way. +The others are the pager receive timer (=[#A]= above) and the reviewer's own +bats variant that went green at exit 127. Worth fixing as a class, not three +times. + +Grading: Major severity (the sweep reads but cannot act, and says nothing, so the +backlog only grows and every later sweep re-reads it) x most sweeps, frequently = +P2 = =[#B]=. Source: home handoff 2026-08-19. + +** DONE [#A] Pager receive timer was dead on ratio and reported success :bug: +CLOSED: [2026-08-19 Wed] +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +Fixed and verified. =signal-receive.service= on ratio had been exiting +=status=0/SUCCESS= every 15 minutes while receiving nothing. + +Root cause was a PATH version skew. ratio carried two signal-cli installs: +=~/.local/bin/signal-cli= symlinked to a manual 0.14.5 tree (2026-06-12) and +=/usr/bin/signal-cli= at 0.14.7 from pacman. The systemd user manager puts +=~/.local/bin= first, so the timer ran 0.14.5 against a database 0.14.7 had +already upgraded and signal-cli refused with "Database has been updated by a +newer signal-cli version". An interactive shell resolved =/usr/bin=, which is +why it worked by hand and failed under the unit. + +Fix: removed the stale =~/.local/bin/signal-cli= symlink, so the pacman-managed +0.14.7 wins for every caller rather than just the timer. Nothing else referenced +the 0.14.5 tree. Left the 107M install directory in place — reclaiming it is a +separate call. + +Verified after: the service drained real queued envelopes, including a receipt +from Craig timestamped 2026-08-16 that had been sitting undelivered, and exited +=Result=success=. Timer still armed on its 15-minute cadence. + +*Correction to this task's original claim.* I first wrote that the pager account +had not received in 17 days. That number was wrong — the 17-day staleness +warning belonged to the *personal* account =+15103169357=, proven by draining it +and watching the warning clear. The pager's failure was real and separately +established (the database-version error under the unit, plus the 2026-08-16 +envelope still queued), but the 17-day figure was never its. I had attributed +one account's number to the other because =listAccounts= emits the warning once, +above the account list, without naming which account it is about. + +*Still open, and pre-existing:* nothing keeps the personal account warm. +=signal-receive.sh= hardcodes the pager account and takes no others, so +=+15103169357= goes stale until drained by hand — already filed as part of the +2026-07-23 signal findings, and unchanged by today's fix. + +*The durable half is not done and is filed separately below:* the guard in +=signal-receive.sh= still cannot tell "the account is not here" from +"listAccounts failed", which is the only reason this ran undetected. Fixing the +skew without fixing the guard leaves the next failure just as silent. + +** TODO [#B] signal-receive guard cannot tell absence from failure :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +=scripts/signal-receive.sh= guards with: + +: if ! signal-cli listAccounts 2>/dev/null | grep -q "$account"; then + +The redirect discards any error and =grep= finds nothing either way, so a hard +failure is indistinguishable from a genuine absence. Both produce the benign +"not registered on this machine — nothing to do" and a clean exit 0. + +That is what let the ratio PATH skew (=[#A]= above) run 17 days undetected while +reporting success every 15 minutes. The skew is fixed; this is not, so the next +failure of any kind on that path is equally silent. + +Fix: capture =listAccounts= output and its exit code separately. A non-zero exit +is a failure and should exit non-zero with the error surfaced, never the +nothing-to-do path. Only a clean run that genuinely lacks the account takes the +no-op branch. + +*Same shape as two other things found today*, which is the argument for treating +it as a class rather than one script's bug: =triage-intake= reports a clean close +over a backlog it could not act on (=[#B]= above), and an isolated reviewer's own +test variant went green because the path under test did not exist and exit 127 +satisfies a =-ne 0= check. A check that cannot distinguish "nothing to do" from +"could not do it" reports success either way. Worth a sweep for the pattern +across =scripts/= rather than three separate fixes. + +Grading: Major severity (it masks arbitrary failures on a channel whose job is +reaching Craig when he is away, and masking is worse than failing) x every +occurrence on that path = P2 = =[#B]=. Not =[#A]= because the live outage it hid +is now closed. + +** TODO [#C] ai-launcher runtime test depends on codex being installed :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +=scripts/tests/ai-launcher-runtime.bats= test 8 ("--print-runtimes lists claude, +codex, and one line per ollama model") fails on velox, so =make test= exits 1 +here. Found as the green-baseline run before the agent-text work, and triaged +rather than fixed so that work could proceed against a known bar. + +The test stubs =ollama= but not =codex=, so it asserts on whatever the host +really has. codex is absent on velox after the 2026-08-13 reinstall, and the +assertion =[[ "$output" == *"codex"* ]]= fails. The launcher is behaving +correctly — it lists the runtimes that exist. + +This is a test-isolation defect, the same shape as the three ratio-local +launcher failures filed alongside it: a characterization test that reads the +host instead of a fixture will pass or fail on where it runs rather than on +what the code does. Fix by stubbing =codex= on PATH the way =ollama= already +is, so the test asserts on the launcher's logic. + +Two things it is worth not confusing. codex being missing on velox is a real +reinstall casualty and may be worth reinstalling on its own merits, but that +would only paper over the test. And the ratio failures are a separate task — +different tests, different machine, likely different cause. + +Grading: Major severity (a red suite makes the green baseline unreadable on the +affected machine, so every later regression check there is guesswork) x some +machines sometimes = P3 = =[#C]=. + +** TODO [#C] inbox-send cannot reach ~/.emacs.d or ~/.dotfiles :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +=inbox-send= discovers projects under =~/projects= and =~/code= only +(=DEFAULT_ROOTS=), so a project sitting directly in =$HOME= is invisible to it. +=.emacs.d= and =.dotfiles= are both such projects and both carry =.ai/= and +=inbox/=. So =inbox-send emacsd= fails with "unknown target" even though the +dot-stripped name resolution it would need is already implemented and working +(=find_target=, lines 154-175). + +Hit for real today: =.emacs.d= sent two handoffs, and I could not reply through +the normal channel. Worked around with +=INBOX_SEND_ROOTS="$HOME/projects:$HOME/code:$HOME/.emacs.d"=. + +The asymmetry is the defect. =.emacs.d= can send to any project, but no project +can answer it, which breaks the reply-to-sender discipline in the inbox +engine's core §4 — and that section's own rationale is that silent +non-response trains a sender to escalate around the channel. The failure is at +least loud rather than silent, and an agent that does not know the override +just drops the reply. + +Fix direction: add =$HOME= itself as a scanned root for dotted project +directories, or add =~/.emacs.d= and =~/.dotfiles= to =DEFAULT_ROOTS= +explicitly. The docstring already claims "if the root itself is a project, it's +included", so the intent exists; the roots list is what is short. Note +=cross-project.md= and =triggers.md= both document =emacsd= as an addressable +name, so the docs already promise this works. + +Grading: Major severity (a protocol obligation is dropped, with a workaround +only if you know it) x some projects sometimes = P3 = =[#C]=. + +** DONE [#B] agent-text relay fallback :bug: +CLOSED: [2026-08-19 Wed] +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +Approved and applied. =agent-text= hardcoded =VELOX_HOST= as its only ssh relay +target, and velox's 2026-08-13 reinstall wiped that machine's signal-cli +registration — so the relay branch pointed at the one machine guaranteed to +fail, and every machine without a local account relayed into the same dead end. + +Took archsetup's proposal over .emacs.d's competing one for the same bug. Both +replaced the constant with an ordered list; only archsetup's skips a candidate +whose short name matches =uname -n=, which is the actual defect. .emacs.d's +works on velox by accident of listing ratio first, so it repairs the instance +and leaves the defect reachable. + +Answered the open question neither sender had tested — a relay host reachable +but unregistered — by running it on velox, which is that case: =signal-cli +send= exits 1 with "User +15045173983 is not registered", so the loop advances +instead of reporting a delivery that never happened. Recorded in protocols.org. + +Also corrected =protocols.org= "Reaching Craig", which asserted the same dead +fact (account "registered on velox (primary) and ratio (linked device)"). Now +states how to derive it rather than a snapshot, per the host-identity rule — +the snapshot is what rotted, and a fresh one would rot at the next rebuild. Its +=signal-mcp= claim was false too: velox's =~/.claude.json= holds only =linear= +and =slack-deepsat= after the reinstall. + +Tests 5 → 10, and the suite now stubs =uname= so the self-skip is +deterministic rather than dependent on the machine running it. Mutation-proven: +removing the guard reds 3 tests, an unconditional =break= reds 1, seeding +=rc=0= reds 1. Decision record: +[[file:docs/design/2026-08-19-agent-text-relay-fallback-decision.org]]. + +** TODO [#B] /model writes into the tracked settings.json and blocks the pull :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +=~/.claude/settings.json= is a symlink into this checkout, and Claude Code +saves a =/model= or effort change by writing user settings. So every runtime +model change writes straight into a tracked file, dirties the tree, and blocks +=git pull --ff-only= at every machine's session start. It blocked this +session's rulesets pull. + +Not a one-off: =git log= on =.claude/settings.json= shows ten prior commits +flipping the =model= line (=c6bd31f=, =5feaa94=, =e3d3c1c=, =c976f5b=, +=73835a2=, =d5bc9b3=, =e91073d=, =bd76d98=, =3a5292c=, =ada0e6d=), and +=effortLevel= has begun doing the same. Treat it as a property of the file, not +of one key. + +Two options, from .emacs.d's handoff (2026-08-14), neither yet chosen: +1. Stop installing the tracked file as user settings — rulesets holds a + canonical template, install copies or merges it, runtime writes land on the + machine's own file. Cost: canonical updates stop propagating automatically. +2. =git update-index --assume-unchanged= per machine. Cheap, but it hides + genuine canonical edits too and must be reapplied on every fresh clone, + which is exactly the manual step that silently drifts. + +Grading: Major severity (recurring startup blocker, no clean workaround) x most +machines frequently = P2 = =[#B]=. + +** TODO [#C] kb-hygiene ships the same report on every run :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +Ten =kb-hygiene-report.org= files arrived in =inbox/= between 09:15 and 12:56 +on 2026-08-19, all byte-identical (one md5 across all ten). So whatever drives +=scripts/kb-hygiene.sh= re-ran it ten times in four hours and delivered the +same output each time. =kb-hygiene.sh= names its report by timestamp +(=$(date +%Y-%m-%d-%H%M)-kb-hygiene-report.org=), so every run is a new file +and nothing dedups. + +The content was also stale news — 142 agent nodes and ~114 orphans, which the +=[#C]= KB orphan-node review task already records from 2026-08-04. Deleted all +ten as script-sourced. + +Fix direction: dedup on content before delivering (skip when the new report +matches the last one), or have the caller deliver on change only. Find the +caller first — the repeat cadence is the actual bug and it is not in +=kb-hygiene.sh=. + +Grading: Minor severity (inbox noise, wasted processing) x most runs frequently += P3 = =[#C]=. + +** TODO [#C] Three launcher characterization tests fail on ratio only :bug: +:PROPERTIES: +:LAST_REVIEWED: 2026-08-19 +:END: +.emacs.d reported (2026-08-14) that =scripts/tests/ai-launcher-characterization.bats= +fails three functional tests on ratio, so =make test= exits 1 there: +=find_window_id=, =sort_windows=, =attach_mode=. They confirmed the failures +are pre-existing rather than caused by their two commits, by stashing and +re-running against a pristine tree. + +I re-ran the same suite on velox today: 36 tests, all green, including exactly +those three. So this is environment-dependent and ratio-local, not a code +defect that reproduces fleet-wide. All three are the tmux-driven functional +tests, which points at ratio's tmux state or version rather than the launcher. + +Diagnose on ratio (reachable over tailscale). It matters more than the [#C] +suggests in one respect: a red suite on ratio poisons the green-baseline +discipline, so every later "did I break this?" check there is unreadable until +it is fixed. + +Grading: Major severity (unreadable baseline on the affected machine) x rare +edge case (one machine, and velox is clean) = P3 = =[#C]=. + ** TODO [#B] Voice pattern #48 — corrective antithesis :feature: :PROPERTIES: :LAST_REVIEWED: 2026-07-31 |
