diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-20 23:39:55 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-20 23:39:55 -0500 |
| commit | 50fc7ca0d15455f508a50e7d227047a8b3664b9d (patch) | |
| tree | ecfc1c57efb2d4d1286918513dbb2e0312dcfbd9 | |
| parent | e27aea2e473433d782da518bf03739b40f5d34f7 (diff) | |
| download | rulesets-50fc7ca0d15455f508a50e7d227047a8b3664b9d.tar.gz rulesets-50fc7ca0d15455f508a50e7d227047a8b3664b9d.zip | |
Moved 5 completed level-2 tasks into Resolved. Logged one review-health followup: a top-level task has gone unreviewed past 30 days.
| -rw-r--r-- | .ai/sessions/2026-07-20-23-36-signal-pager-and-triage-phone-push.org | 185 | ||||
| -rw-r--r-- | inbox/lint-followups.org | 2 | ||||
| -rw-r--r-- | todo.org | 303 |
3 files changed, 334 insertions, 156 deletions
diff --git a/.ai/sessions/2026-07-20-23-36-signal-pager-and-triage-phone-push.org b/.ai/sessions/2026-07-20-23-36-signal-pager-and-triage-phone-push.org new file mode 100644 index 0000000..fe43f61 --- /dev/null +++ b/.ai/sessions/2026-07-20-23-36-signal-pager-and-triage-phone-push.org @@ -0,0 +1,185 @@ +#+TITLE: Session Context — Sentry live trial (ratio, 2026-07-19) +#+AUTHOR: Craig Jennings + +* Summary + +** Active Goal + +Post-flush session. Two items shipped: (1) the archsetup voice-#46 (comma-budget) handoff, reviewed and committed rulesets-side (2ea5d9a); (2) the triage-intake auto-mode phone-push, built onto agent-text, signal-only per Craig's ruling (e27aea2). Items 2 (reply-correlation spec) and 3 (token-rotation discussion) from the prior next-steps list remain untouched, carried forward. + +** Decisions (this session, all shipped) + +- Voice pattern #46 (comma budget, max two per sentence): personal mode only, in the attestation high-recurrence set. Craig's 2026-07-20 direction via archsetup; committed rulesets-side 2ea5d9a. +- Triage auto-mode phone-push is signal-only (Craig's option 1): a quiet sweep's "nothing" heartbeat never pushes to the phone — silent-until-signal governs the phone channel too. Send half shipped (e27aea2); reply-polling deferred to the reply-correlation spec. +- Sentry live trial: ran on ratio, 8 hourly fires, stopped on "sentry off", branch fast-forwarded to main and deleted. +- working/ is tracked-from-creation + gitignored temp/ in both modes (Shape A). Committed. +- Triage source activation: general (synced) plugins gate on a per-project =:TRIAGE_SOURCES:= declaration; project-specific plugins active by presence; gate in triage-intake Phase 0 (interactive + unattended). Spec IMPLEMENTED. Migrations done (home + work declared their sources). +- Silent-until-signal: a POLICY not a mechanism — an in-session monitor fire heartbeats =<workflow> at HH:MM: nothing= on an empty check; detection stays in-session (MCP-safe). Applied to sentry, auto triage-intake, auto inbox-zero. Spec IMPLEMENTED. +- Suspend detach change applied to canonical. Sentry cluster consolidated (merged /schedule tasks, added cross-host-coordination task). Task audit stamped 2026-07-20. +- Polyglot: case-by-case, no option-2 machinery (bundles already compose; only coverage-makefile.txt collides, and it's a manual paste). Subprojects: don't promote (N=1). Both closed. + +** Data Collected / Findings — Signal pager (what the resume needs) + +RECONCILED (2026-07-13, in the task's dated log): there is ONE pager identity, =+15045173983=, registered in *velox's* signal-cli (account file 465310). =signal-mcp= is a velox-local MCP server (invisible from ratio). ratio's signal-cli holds only Craig's personal number (note-to-self, no push). =agent-page= already shipped (=claude-templates/bin/agent-page=): runs signal-cli directly on velox, ssh-relays from anywhere else, desktop-fallback on failure, 4 bats, live-verified. protocols.org "Paging Craig" was already rewritten around the two channels (notify desktop + agent-page phone). Craig's Signal UUID: =b1b5601e-6126-47f8-afaa-0a59f5188fde=. Reliability finding: both signal-cli accounts throw receive-staleness warnings (velox ~40 days, ratio ~26) — the Signal protocol wants regular receives, and a systemd receive timer on velox is the roam-sync-shaped fix. + +REMAINING deliverables (this is the work to do): (1) the runbook proper — send + read-replies + receive-timer + signal-cli account/setup notes, the Signal equivalent of the retired ntfy runbook, canonical home in rulesets docs/; (2) a systemd receive timer on velox (roam-sync-shaped) so receives don't go stale; (3) the ssh-over-tailnet-only vs register-ratio-as-a-linked-device decision (a design call for Craig); (4) confirm protocols.org "Paging Craig" is accurate (agent-page did most of it — verify, don't redo). Task body has full history. Source: home handoff 2026-07-04. + +** Files Modified (this session — all committed + pushed to main) + +Post-flush commits: 2ea5d9a (voice #46 comma budget — SKILL.md + voice-profile.org), 9721a49 (chore: mark archsetup handoff PROCESSED), e27aea2 (feat: triage phone-push via agent-text — canonical + mirror triage-intake.org, todo.org task closed). Left unstaged: .claude/settings.json model change (opus→fable, not this session's work — deferred). + +Earlier same-session (pre-flush): f625cf5 (working/temp feat), b02eade + 4d87f35 (triage source activation spec + build), 986d6ca + 0767af8 + 0e9958a (silent-until-signal spec + Phases), af565ba (suspend detach), 70fbe01 (link fixes), 93a2e6d (working-dir filing), c82b625 (sentry cluster), up to fecdf8c, plus the pager work (302b062, 6145489) and overnight sentry commits. + +** Next Steps + +Item 1 (triage phone-push) shipped this session. Two remain from the prior list: + +1. *Spec the reply-correlation follow-up.* The two-way gap: with the account linked on velox + ratio, a Signal reply reaches BOTH devices (one account, Signal fans out per-device, independent queues) and neither knows which page/session it answers. Only bites when two sessions page-and-wait at once (fire-and-forget is fine). Options laid out to Craig: (1) correlation tag stamped on the page, echoed in reply; (2) quote-reply matching (needs verifying signal-cli surfaces quotes); (3) single reply-owner (velox-only). Overlaps the helper-instance work (same shared-channel problem). This spec also owns the deferred triage-intake reply-polling (phone-recv) half. Also fix the runbook "Reading replies" section, which oversells it. Write as a spec in docs/specs/ (spec-create spine). + +2. *Discuss the token-rotation helper* (todo.org =[#C] Token-rotation helper for @a-bonus/google-docs-mcp OAuth refresh=, :feature:quick:) — a discussion first, not a build. Read the task body. + +Publish flow (review + voice + suite) for any commits. + +KB: promoted 0 / consulted no + +* Session Log + +** 2026-07-19 23:52 CDT — Sentry armed (entry) + +Startup ran clean: rulesets pull no-op, make install nothing new, project repo up to date on main (0/0). Session-context was absent (prior session 2026-07-19-21-15 wrapped cleanly). + +Sentry entry gates, all passed with Craig present: +- Autonomy ticket: =:COMMIT_AUTONOMY: yes= in notes.org Workflow State. +- Host: ratio (intended live-trial machine). +- Dirty-tree gate: tracked tree clean; only two untracked inbox files (=2026-07-19-2141-from-.emacs.d-version-working-directories.org=, =2026-07-19-2350-from-home-craig-approved-the-colloquialisms-the.org=) — do not block; sentry's inbox-zero pass (pass 2) handles them. +- Green-suite gate: =make test= exit 0, 377 bats ok, all pytest + ERT green. +- Prior sentry branch: none. +- Reconcile: main 0 behind / 0 ahead of upstream. +- =agent-lock= helper present. + +Created daily branch =sentry/2026-07-19-ratio= from HEAD (main @ f76bf40). Working tree now sits on this branch overnight. Arming the hourly loop next. + +Two inbox items pending at entry, both to be handled by pass 2 (inbox zero): +- .emacs.d working/ version-control ruling — a shared-asset change (working-files convention + install .gitignore behavior across projects). Parks for morning approval; does not fire unattended. +- home colloquialisms wiring approval — already shipped on the rulesets side in the prior session (protocols.org Colloquialisms section + wrap-it-up Before-Close Queue step, commits 8fd9e39/f6a2701). Inbox zero replies-and-files, confirming it's already wired. + +** 2026-07-20 00:06 CDT — Fire 1 digest (manual first fire, Craig present) + +Ran end to end on branch =sentry/2026-07-19-ratio=. Single-runner lock =sentry-rulesets= acquired at fire start, on the sentry branch, tree clean. Pass-by-pass: + +- P1 roam pull — SKIPPED: =~/org/roam= working tree dirty; roam-sync owns that case (pass is read-only ff-only otherwise). No write. +- P2 inbox zero — RAN. Two project handoffs processed. (a) home's colloquialisms + "the list" before-close-queue: already wired canonically before it arrived — replied to home confirming, marked PROCESSED. (b) .emacs.d's working/ tracked-from-creation ruling: shared-asset + convention change with a temp/-placement design decision → PARKED (see approval queue). Staged proposal at =working/sentry-2026-07-19-working-files-ruling/proposed.org=, filed a [#B] VERIFY, replied to .emacs.d with two findings. Committed 727a900. +- P3 triage intake — SKIPPED (probe-too-loose; queued as a finding). The template-synced general plugins (personal Gmail/calendar/cmail/Telegram/GitHub-PRs) are present in *every* project, so the "plugins present" probe self-activates triage everywhere — including rulesets, which is not a triage target. Running it would file Craig's personal action items into rulesets' todo.org and run trash/mark-read/star hygiene on his real accounts unattended. Skipped for scope + safety. No write. +- P4 todo cleanup — RAN: hygiene 0 fixes, --convert-subtasks 0, --archive-done 0. todo.org already clean. No write. +- P5 task audit — RAN (mechanical subset). No unambiguous autonomous staleness fixes: all open tasks carry a priority cookie + type tag except the intentional "Manual testing and validation" container; spec-lifecycle clean (P7); no dead file: links in todo.org. The full reconciliation (per-task fact-check, consolidation, parent-retirement, judgment flags) is interactive → queued. Did NOT bump :LAST_AUDIT: (only the mechanical subset ran). No write. +- P6 working-files hygiene — RAN: flagged =working/inbox-zero-phase-e/= (tracked, backs the now-IMPLEMENTED autonomous-batch-execution spec) as a filing candidate. Filing is a judgment move (3 inbound spec links need updating) → queued. No write. +- P7 spec status board — RAN: clean. All specs READY or IMPLEMENTED; the sentry spec is IMPLEMENTED; no DOING spec with a closed/missing bound parent. No write. +- P8 link integrity — RAN (report-only): ~4 candidate broken file: links in live docs — the autonomous-batch spec's =../../.ai/workflows/inbox-zero.org= (renamed to inbox.org), two folded-in spec-review siblings (wrapup-routing, pattern-catalog), and =subprojects-log.org=. Some may be intentional (review docs folded in and deleted). No unattended rewrites. No write. +- P9 git health — RAN: clean. main = origin/main (0/0), no unpushed commits on other branches, no stale merged branches, sentry branch is the only local extra. No write. +- P10 prep + symlink freshness — SKIPPED: no prep dir (work/home only). No write. + +Fire-end: only org/spine files touched this fire (todo.org, inbox renames, working/ proposal — all org) → conditional suite skipped per contract. Session-context spine is untracked in rulesets, so no digest commit needed; tracked tree is clean. Single-runner lock released. + +Branch state after fire: one commit ahead of main (727a900). Next scheduled fire at :17. + +** 2026-07-20 00:21 CDT — Fire 2 digest (scheduled, :17 cron) + +Lock =sentry-rulesets= acquired, on the sentry branch, tree clean. Little changed in the 14 min since fire 1 — one new inbox handoff, everything else steady. + +- P1 roam pull — RAN: =~/org/roam= clean this fire (was dirty in fire 1), ff-only pull → already up to date. No write. +- P2 inbox zero — RAN: one new handoff. .emacs.d routed three sentry-workflow design considerations from its own hand-run trial (codebase-gated bug/enhancement logging pass; system-health pass; sibling-machine freshness pass). Filed as =** TODO [#C] Sentry vNext passes — from live-trial design input= rather than applied (design input for the Living Document; two hinge on an unresolved cross-driver coordination question). Replied to .emacs.d, marked PROCESSED. Committed 067ed55. +- P3 triage intake — SKIPPED: same probe-too-loose reason as fire 1; the finding is already in the approval queue, not re-queued. +- P4 todo cleanup — RAN: 0 fixes. No write. +- P5 task audit — no change since fire 1; the full-audit-due item stays in the queue, not re-added. +- P6 working-files hygiene — RAN: =working/inbox-zero-phase-e/= still a filing candidate (already queued fire 1, not re-queued). =working/sentry-2026-07-19-working-files-ruling/= is an *active* working dir backing the open VERIFY task — correctly not a candidate. No new queue item. +- P7 spec status board — RAN: clean, no DOING specs. No write. +- P8 link integrity — not re-scanned: no doc changes since fire 1's scan except the new backlog task, whose one internal =file:todo.org::*...= link resolves (KB lesson-detection heading exists). Fire 1's report stands. No write. +- P9 git health — RAN: clean, main = origin/main (0/0), branch now 2 ahead. No write. +- P10 prep freshness — SKIPPED: no prep dir. No write. + +Fire-end: only org files touched → conditional suite skipped. Spine untracked → no digest commit; tracked tree clean. Lock released. Branch 2 commits ahead of main (727a900, 067ed55). No new approval-queue items this fire. + +** 2026-07-20 01:20 CDT — Fire 3 digest (scheduled, :17 cron) + +Quiet read-only fire — nothing changed in the hour since fire 2. All passes no-op or skip; no writes, no commit, no new approval-queue items. + +- P1 roam pull — RAN: clean, ff-only → already up to date. +- P2 inbox zero — RAN: 0 new items. +- P3 triage intake — SKIPPED: same probe-too-loose reason (finding already queued fire 1). +- P4 todo cleanup — RAN: 0 fixes. +- P5 task audit — no change; full-audit-due item stays queued. +- P6 working-files hygiene — RAN: same two dirs (inbox-zero-phase-e already queued; sentry-ruling active for the open VERIFY). No new item. +- P7 spec status board — RAN: clean, no DOING specs. +- P8 link integrity — not re-scanned: no doc changes since fire 1. +- P9 git health — RAN: clean, main = origin/main, branch 2 ahead. +- P10 prep freshness — SKIPPED: no prep dir. + +Branch unchanged at 2 commits ahead. Lock released. + +** 2026-07-20 02:20 CDT — Fire 4 digest (scheduled, :17 cron) + +Quiet read-only fire, same as fire 3 — nothing changed in the hour. All passes no-op or skip; no writes, no commit, no new queue items. P1 roam up to date; P2 inbox 0 new; P3 triage skipped (finding already queued); P4 0 fixes; P5 no change (full audit still queued); P6 same two working dirs (inbox-zero-phase-e queued, sentry-ruling active); P7 clean, no DOING specs; P8 not re-scanned (no doc changes); P9 clean, branch 2 ahead; P10 skipped (no prep dir). Lock released. + +** 2026-07-20 03:20 CDT — Fire 5 digest (scheduled, :17 cron) + +Quiet read-only fire, same as fires 3-4. No changes in the hour; all passes no-op or skip; no writes, no commit, no new queue items. P1 roam up to date; P2 inbox 0 new; P3 triage skipped (queued); P4 0 fixes; P5 no change; P6 same two working dirs; P7 clean; P8 not re-scanned; P9 clean, branch 2 ahead; P10 skipped. Lock released. + +** 2026-07-20 04:20 CDT — Fire 6 digest (scheduled, :17 cron) + +Quiet read-only fire, same as fires 3-5. No changes in the hour; all passes no-op or skip; no writes, no commit, no new queue items. P1 roam up to date; P2 inbox 0 new; P3 triage skipped (queued); P4 0 fixes; P5 no change; P6 same two working dirs; P7 clean; P8 not re-scanned; P9 clean, branch 2 ahead; P10 skipped. Lock released. + +** 2026-07-20 05:20 CDT — Fire 7 digest (scheduled, :17 cron) + +Quiet read-only fire, same as fires 3-6. No changes in the hour; all passes no-op or skip; no writes, no commit, no new queue items. P1 roam up to date; P2 inbox 0 new; P3 triage skipped (queued); P4 0 fixes; P5 no change; P6 same two working dirs; P7 clean; P8 not re-scanned; P9 clean, branch 2 ahead; P10 skipped. Lock released. + +** 2026-07-20 06:20 CDT — Fire 8 digest (scheduled, :17 cron) + +Quiet read-only fire, same as fires 3-7. No changes in the hour; all passes no-op or skip; no writes, no commit, no new queue items. P1 roam up to date; P2 inbox 0 new; P3 triage skipped (queued); P4 0 fixes; P5 no change; P6 same two working dirs; P7 clean; P8 not re-scanned; P9 clean, branch 2 ahead; P10 skipped. Lock released. + +* Sentry approval queue (2026-07-19) + +Judgment/parked items from the overnight fires. Review top to bottom; run or discard each. + +** [Fire 1 · P2] Apply the .emacs.d working/ tracked-from-creation ruling +- *What:* update the canonical working-files convention + add a gitignored temp/ pattern across projects. +- *Why:* Craig's ruling relayed from .emacs.d (2026-07-19). Parked because it's a shared-asset + convention change and carries a design decision (temp/ must be ignored in both track and gitignore modes — it's orthogonal to the personal-tooling set). +- *Prepared:* =working/sentry-2026-07-19-working-files-ruling/proposed.org= (exact edits + the two findings). Filed as the =** VERIFY [#B] working/ tracked-from-creation + gitignored temp/= task in todo.org. +- *Fires on approval:* edit =claude-rules/working-files.md= (tracked-from-creation + temp/ subsections), =scripts/install-ai.sh= + =scripts/sweep-gitignore-tooling.sh= (emit temp/ ignore in both modes — decide shape (a) vs (b) in the proposal), =.ai/protocols.org= Working-Files Convention (one-line mirror); then =scripts/sync-check.sh --fix= for the .ai mirror, run =make test=, commit. + +** [Fire 1 · P3] Tighten the sentry triage-intake pass probe — SPECCED during morning review +- *What:* the narrow "fix the probe" framing grew, on Craig's flexibility question, into a per-project source-activation model for triage-intake. +- *Resolution:* written up as a spec for review — =docs/specs/2026-07-20-triage-source-activation-spec.org= (DRAFT), linked from the =** TODO [#B] Triage source activation= task. General plugins gate on a per-project =:TRIAGE_SOURCES:= declaration; project-specific plugins stay active by presence; the activation layer lives in triage-intake Phase 0 so it fixes the interactive over-pull too; sentry's pass-3 probe reads the same signal. +- *Next:* Craig's deep read → DRAFT → READY → spec-response decomposes the 5 phases. Two decisions still open (declaration format, whether interactive adopts the same gate). No code landed — this item is now tracked by the spec + task, not the queue. + +** [Fire 1 · P5] A full interactive task-audit is due +- *What:* run task-audit.org interactively (its consolidation / parent-retirement / judgment-flag phases need Craig). +- *Why:* :LAST_AUDIT: is 2026-07-04 (~16 days); task-review-staleness flags 2 top-level tasks unreviewed >7 days. Fire 1's mechanical subset found nothing unambiguous to auto-fix, so the marker was deliberately not bumped. +- *Fires on approval:* "let's do a task audit" (or task-review for the lighter pass). + +** [Fire 1 · P6] File working/inbox-zero-phase-e/ to a permanent home +- *What:* file the three artifacts under =working/inbox-zero-phase-e/= per working-files.md and update inbound links. +- *Why:* the backing work (autonomous-batch-execution spec) is IMPLEMENTED, so the working dir is a filing candidate. Filing is a judgment move — 3 inbound =file:= links in =docs/specs/2026-06-16-autonomous-batch-execution-spec.org= point at it and need updating in the same move. +- *Fires on approval:* rename+move the 3 files flat into their permanent home, update the spec's 3 links, delete the empty working subdir. + +** [Fire 1 · P8] Resolve ~4 candidate broken file: links in live docs (report-only) +- *What:* fix or confirm-intentional the broken links P8 flagged. +- *Why:* report-only pass; no unattended rewrites. The clearest real one: =docs/specs/2026-06-16-autonomous-batch-execution-spec.org= links =../../.ai/workflows/inbox-zero.org= (renamed to inbox.org). Others (=wrapup-routing-spec-review.org=, a pattern-catalog inbox source, =subprojects-log.org=) may be folded-in/deleted review docs — Craig's call. +- *Fires on approval:* update the inbox-zero.org→inbox.org link; decide the rest. + +** 2026-07-20 Mon @ 15:28:41 -0500 — flushed (auto) +Auto-flush checkpoint mid-session. Session's shipped work is all committed + pushed (main == origin == velox). In flight: resuming into finishing the Signal pager task ([#B] in todo.org) — the runbook, velox receive timer, and the ssh-vs-linked-device decision. See Summary → Next Steps and Data Collected for the reconciled facts needed to resume blind. + +** 2026-07-20 Mon @ 16:48 CDT — Notification vocabulary split (page/text) + agent-page → agent-text rename +Craig's follow-on from the reply-ambiguity discussion: reserve trigger words by channel. "page me" = desktop notify, "text me" = Signal, "text and page me" = both. Renamed the tool agent-page → agent-text to match (deprecated agent-page shim delegates to it, removable later). Rewrote protocols.org "Paging Craig" → "Reaching Craig", page-me.org, work-the-backlog's away-run logic, INDEX, and the runbook; updated install-ai doc comments; bats renamed agent-page.bats → agent-text.bats (5 pass incl. shim delegation). /review-code + /voice ran; make test exit 0, shellcheck clean, no new lint. Commit 6145489, pushed; make install re-run on both ratio + velox, agent-text + shim resolve on both. Vocabulary decision arc (this session): first "page=Signal, notify=desktop" (rejected, inverted current default), then "page=desktop, message=Signal" (Craig's), then settled on "text me" for Signal (agent-text). NOTE excluded from staging: .claude/settings.json shows model opus→fable (not mine — left unstaged). + +** 2026-07-20 Mon @ 23:36 CDT — Post-flush resume: voice #46 handoff + triage phone-push (item 1) +Resumed from the auto-flush anchor. Two things landed. + +Inbox handoff (archsetup, voice pattern #46 comma budget): archsetup edited voice/SKILL.md + voice-profile.org through the shared ~/.claude/skills/voice symlink (points into rulesets) at Craig's 2026-07-20 direction — "no more than two commas per sentence" in the /voice personal pass. Reviewed both files (count 45→46 everywhere, personal-only set four→five, #46 in the attestation high-recurrence set, before/after from the Hyprland draft). /review-code approved, /voice personal on the message, make test green. Committed 2ea5d9a; replied to archsetup; marked PROCESSED (chore 9721a49). The .claude/settings.json model change (opus→fable) was left unstaged — not this session's work. + +Item 1 — triage-intake auto-mode phone-push, built. Folded a "Phone delivery" subsection into canonical triage-intake.org: a full-three-section sweep pushes to Craig's phone over Signal via agent-text, with a pointer from End-of-sweep output and a Living Document note. Craig's design ruling (option 1, signal-only): a quiet sweep's "nothing" heartbeat never reaches the phone — silent-until-signal governs the phone channel too — while the in-session heartbeat stays as proof-of-life. Send half only; reply-polling (old phone-recv) deferred to the reply-correlation follow-up (item 2), which owns the recv wiring. sync-check --fix synced the mirror; make test green; /review-code approved; /voice personal. Committed e27aea2. todo.org task closed DONE (send half shipped, recv deferred). + +Next: items 2 (reply-correlation spec) and 3 (token-rotation discussion) remain untouched — carried to the next session. + +** 2026-07-20 Mon @ 16:14 CDT — Signal pager DONE (all four deliverables + topology decision) +Finished the task end to end. (1) Runbook: docs/design/2026-07-20-signal-pager-runbook.org — send, read-replies, receive timer, signal-cli account/setup notes, the resolved decision. (2) Receive timer: scripts/signal-receive.sh + scripts/systemd/signal-receive.{service,timer} (roam-sync-shaped, 15-min, no-ops where the account isn't local, 4 bats); stowed via dotfiles common; enabled + verified on both ratio and velox; a manual drain cleared the 47-day staleness live. (3) Topology decision (Craig, option 2 — linked device over ssh-relay-only): ratio linked as Device 2 "ratio-pager", direct send verified; agent-page generalized from a velox-only check to "any machine holding the account sends directly, else relay" (bats updated). (4) protocols.org "Paging Craig": verified accurate, no edit. Publish flow: /review-code caught a runbook/script contradiction (runbook claimed no-op-clean; script errored) → added the account-presence guard + a bats case. make test green, shellcheck clean. Commits: rulesets 302b062 (feat(pager)), dotfiles d8b0462 (feat(systemd)); both pushed; velox synced. todo.org task closed DONE. Craig's follow-up worry addressed: the pager only fires on an explicit page-me or an away-run's end-of-set page; the receive timer is silent (receive-only, no push). diff --git a/inbox/lint-followups.org b/inbox/lint-followups.org new file mode 100644 index 0000000..52905dd --- /dev/null +++ b/inbox/lint-followups.org @@ -0,0 +1,2 @@ + +* 2026-07-20 Mon — Task-review health: 1 top-level [#A]/[#B]/[#C] tasks unreviewed for >30 days (daily review may have slipped) @@ -82,103 +82,6 @@ What we're verifying: triage-intake pulls only the sources a project declares (s - Under sentry, confirm pass 3 (triage) probe-skips in a project with no active source, and runs where one is declared. Expected: undeclared general plugins never scan; declared ones and project-specific ones do; sentry's triage pass activates only where a source is active. -** DONE [#B] working/ tracked-from-creation + gitignored temp/ :feature: -CLOSED: [2026-07-20 Mon] -Craig's ruling relayed from .emacs.d (2026-07-19): working/ is version-controlled from creation (not excluded until graduation); ephemeral artifacts go in a gitignored temp/ or /tmp; graduation reorganizes durable artifacts into permanent homes rather than marking when they become durable. - -Implemented (Shape A) during the morning sentry review, 2026-07-20: -- =claude-rules/working-files.md=: added "working/ Is Version-Controlled From Creation" section (tracked-from-creation, graduation-is-a-move, temp/ for ephemeral). -- =.ai/protocols.org= (canonical + mirror): one-paragraph mirror in the Working-Files Convention section. -- =scripts/install-ai.sh=: emits a =temp/= ignore block in BOTH track and gitignore modes; working/ never ignored. Idempotent. -- =scripts/sweep-gitignore-tooling.sh=: separate mode-independent temp/ backfill pass (a distinct loop, not an IGNORE_SET member — that would skip track-mode projects, the set that most needs it). -- Tests: 3 new install-ai bats + 4 new sweep bats (temp/ in both modes, never working/, idempotent). Suite green, 384 bats ok. - -Finding confirmed at implementation: the canonical machinery already never ignored working/ (tooling set is only =.ai/ .claude/ CLAUDE.md AGENTS.md=), so .emacs.d's =/working/= ignore was a purely local deviation. The staging proposal dir was removed after shipping; its content lives in the feat commit and this body. - -** DONE [#C] Polyglot projects — supported, or refused? :spec: -CLOSED: [2026-07-20 Mon] -DECISION (2026-07-20, scouting with Craig): *case-by-case, and it already composes — no option-2 machinery.* The evidence: bundle contents split into namespaced/additive files (rules =<lang>.md=, =validate-<lang>.sh= hooks, =coverage-summary.<ext>= scripts, appended =gitignore-add.txt=) that compose cleanly, and exactly three colliding files — =claude/settings.json= and =githooks/pre-commit= (full bundles only: bash/elisp/go) and =coverage-makefile.txt= (elisp/go/python/typescript). Of the three, only =coverage-makefile.txt= occurs in the fleet, and clock-panel (the one real polyglot, python+typescript) proves it's benign: both bundles installed, no breakage, because that fragment is a hand-pasted Makefile block nobody pasted twice. So: keep the install-lang collision guard (it blocks the destructive full+full settings/githooks clobber, which no project actually hits), and document the =coverage-<lang>:= + =coverage:= aggregate namespacing as the one manual step when going polyglot (filed below). No two-full-co-equal-bundle project exists, so the settings.json/githooks merge is unwarranted. Follow-up doc: [[file:todo.org::*Document polyglot coverage-makefile namespacing][Document polyglot coverage-makefile namespacing]]. - -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. - -** DONE [#C] Subproject pattern — promote to claude-rules? :spec: -CLOSED: [2026-07-20 Mon] -DECISION (2026-07-20, scouting with Craig): *don't promote — keep it local at home.* The scouting confirmed N=1: across all 27 =.ai= scopes, home (9 subprojects, all from the single 2026-06-11 fold) is the only real user. The nearest neighbors aren't the pattern — rulesets folds claude-templates in as a git *subtree* (different mechanism, not shared-=.ai/=-scope), archsetup's dotfiles/ likewise; every other project is a focused single package with no subproject structure or need. Promoting a 282-line convention into the always-on claude-rules layer for one project fails the thin-always-on principle (the precedent is patterns.md at 29 lines + docs-lifecycle.md's depth-in-a-spec). Keep the convention as home's local instance. Revisit only if a genuine second case appears, and then as a thin pointer + a spec, never 282 lines always-on. - -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] Document polyglot coverage-makefile namespacing :chore: The one manual step the polyglot decision (2026-07-20) left: when a project installs two bundles that both ship =coverage-makefile.txt= (elisp/go/python/typescript), their =coverage:= / =coverage-summary:= Makefile targets duplicate. Document the fix — rename to per-language =coverage-<lang>:= targets and add a =coverage:= aggregate that depends on them — where a polyglot user would meet it (install-lang docs, or a short note in the bundle README / languages/ overview). Low urgency: clock-panel is polyglot today and unaffected because it has no assembled Makefile. Do it when the coverage-makefile fragments are next actually pasted, or opportunistically. @@ -209,18 +112,6 @@ Two consumers, one contract: Design the one contract; both features consume it. Merged 2026-07-20 from the separate "Sentry unattended /schedule variant" and "Fully-unattended scheduled inbox check" tasks — same no-session design problem. -** DONE [#B] Triage source activation — per-project source declaration :feature:spec: -CLOSED: [2026-07-20 Mon] -Spec: [[file:docs/specs/2026-07-20-triage-source-activation-spec.org][docs/specs/2026-07-20-triage-source-activation-spec.org]] (IMPLEMENTED, ID af73ef0b-cd1d-46f1-9e1d-62695733a4de). Craig approved after his read, both open decisions resolved via cj comments; built same session. - -From the sentry live trial (2026-07-20): triage-intake self-activated in every project because the general (personal-account) plugins are template-synced everywhere, so sentry's pass-3 probe misfired and would pull Craig's personal inboxes into whatever project the fire ran in. Fixed with an activation gate in triage-intake Phase 0 — general plugins gate on a per-project =:TRIAGE_SOURCES:= declaration; project-specific plugins stay active by presence. Applies to interactive and unattended alike. sentry pass-3 probe reads the same signal. Migration handoffs sent to home + work. Supersedes Fire 1's narrower probe-only approval-queue item. - -** DONE [#B] Silent-until-signal for in-session monitor loops :feature:spec: -CLOSED: [2026-07-20 Mon] -Spec: [[file:docs/specs/2026-07-20-silent-until-signal-monitors-spec.org][docs/specs/2026-07-20-silent-until-signal-monitors-spec.org]] (IMPLEMENTED, ID af592bd6-d3e6-47e2-8804-2a287b4d9303). Craig approved without changes 2026-07-20; all 5 phases shipped that day. Sentry, auto triage-intake, and auto inbox-zero all collapse an empty fire to =<workflow> at HH:MM: nothing=; a manual-testing entry covers the live-loop verification. - -Craig-approved proposal from .emacs.d (2026-07-20), demonstrated by the sentry live trial (fires 3-8 were walls of no-op lines). Reframed live from a watcher *mechanism* to a *policy*: an in-session monitor fire detects first, and on an empty check collapses to one labelled heartbeat line (=<workflow> at HH:MM: nothing=) instead of a full turn; only a real item earns the full surface-and-judge turn. Keeping detection in-session dissolves the MCP-auth split (triage's Gmail/Slack/Linear need session auth), so it applies uniformly to sentry, auto triage-intake, and auto inbox-zero. No external watcher, no new seen-list. Five build phases in the spec. Phase 1 (sentry quiet-fire heartbeat) shipped 2026-07-20 ahead of the full READY gate at Craig's direction — a quiet fire now collapses to =sentry at HH:MM: nothing=. Phases 2-4 (auto triage-intake, auto inbox-zero, shared-policy home) + Phase 5 verification pending Craig's spec read. Overlaps the sentry cluster ([[file:todo.org::*Sentry vNext passes][Sentry vNext passes]], [[file:todo.org::*Triage source activation][Triage source activation]]). - ** TODO [#D] Research: MCP for device locations shared with you :feature: From .emacs.d (2026-07-20, rulesets-owned research). Is there a Google Maps MCP (or similar) that reports the locations of devices sharing their location with you? If none exists, research how hard it would be to build one. (Google's location-sharing has no official public API; likely needs investigation of unofficial routes or a different provider.) @@ -233,12 +124,6 @@ Three considerations captured by .emacs.d's own hand-run sentry trial (routed vi Take up with the sentry Living Document refinements once the trial has quiet nights behind it. Passes #2 and #3 are blocked on [[file:todo.org::*Cross-host agent coordination][Cross-host agent coordination]] (the missing ratio↔velox lock). Related: [[file:todo.org::*Unattended /schedule cron contract][Unattended /schedule cron contract]] (the no-session variant). -** DONE [#C] Apply suspend.org detach-on-suspend change to canonical :feature: -CLOSED: [2026-07-20 Mon] -Craig's change relayed from archsetup (2026-07-20): add Step 6 to suspend.org — detach the tmux client (=tmux detach-client -s "$sess"=) as the final action of every suspend, so a suspended session parks in the re-attachable set instead of cluttering the alt-space rotation. Also reworded the neighbors bullet and the "does NOT do" teardown bullet to draw the detach-vs-teardown line. - -Applied to canonical 2026-07-20 (verified the diff was exactly the intended change, canonical + mirror synced, lint clean bar one pre-existing flush/SKILL.md link). Replied to archsetup that its local stopgap is now canonical. - ** TODO [#B] Extend ui-prototyping rule with build-to-prototype :feature: :PROPERTIES: :CREATED: [2026-07-11 Sat] @@ -260,31 +145,6 @@ Open question from the roam inbox (2026-07-11): could the startup sequence for . :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. -** DONE [#B] Document (and own) the Signal pager :feature:spec: -CLOSED: [2026-07-20 Mon] -: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. - -*** 2026-07-20 Mon @ 15:56:56 -0500 Runbook shipped; ratio linked as a device; receive timer on both machines -All four remaining deliverables landed. (1) Runbook: [[file:docs/design/2026-07-20-signal-pager-runbook.org]] — send, read-replies, receive timer, signal-cli account/setup notes, the resolved topology decision. (2) Receive timer: =scripts/signal-receive.sh= + =scripts/systemd/signal-receive.{service,timer}= (roam-sync-shaped, 15-min cadence, 3 bats), stowed via dotfiles =common=, enabled + verified on ratio; a manual drain also cleared the 47-day staleness live. (3) Topology decision (Craig, option 2): register daily drivers as linked devices rather than ssh-relay-only — ratio linked as Device 2 "ratio-pager", direct send verified, =agent-page= generalized from a velox-only check to "any machine holding the account sends directly, else relay" (bats updated). (4) protocols.org "Paging Craig": verified accurate, no edit — already describes both channels and the caveats, and its generic runbook pointer is correct to leave un-pathed since it syncs into every project. Remaining one-time step: enable the timer on velox after it pulls dotfiles. - ** TODO [#C] KB orphan-node review pass :chore: :PROPERTIES: :CREATED: [2026-07-01 Wed] @@ -515,22 +375,6 @@ Once specs carry lifecycle TODO keywords under =docs/specs/=, add a custom org-a :END: 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. -** DONE [#C] triage-intake auto mode — push signal sweeps to phone via agent-text :feature:solo: -CLOSED: [2026-07-20 Mon] -:PROPERTIES: -:CREATED: [2026-06-20 Sat] -:LAST_REVIEWED: 2026-07-13 -:END: -Send half shipped 2026-07-20. Folded a "Phone delivery" subsection into canonical =triage-intake.org= auto mode: a full-three-section sweep pushes to Craig's phone over Signal via =agent-text=, with a pointer from "End-of-sweep output" and a Living Document note. Signal-only by Craig's 2026-07-20 ruling — a quiet sweep's =nothing= heartbeat never reaches the phone, so silent-until-signal governs the phone channel too and the in-session heartbeat stays as proof-of-life. Falls back to inline when =agent-text= is absent. - -Reply-polling half (the old =phone-recv=) deferred to the reply-correlation follow-up: with the Signal account linked on more than one device a reply fans out to every device and neither knows which page it answers, so auto mode pushes but does not poll until that's resolved. The recv wiring is owned by that spec, not this task. - -Origin: the work project's 2026-06-18 fold-in request; 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]]. Transport re-pointed off retired ntfy → agent-text (renamed from agent-page 2026-07-20). - -** CANCELLED [#D] Fully-unattended scheduled inbox check (/schedule cron pass) :feature: -CLOSED: [2026-07-20 Mon] -Merged 2026-07-20 into [[file:todo.org::*Unattended /schedule cron contract][Unattended /schedule cron contract — no-session variant]]. Same no-session design problem as the sentry /schedule variant (mutation rights, async surfacing, cross-run dedup, cron auth context); its inbox-specific context was absorbed there. - ** TODO [#D] Warn-only pre-commit hook for tooling-path enumeration :feature: :PROPERTIES: :CREATED: [2026-06-22 Mon] @@ -1619,3 +1463,150 @@ section vs a new shipped reference file); where the queue itself lives (home use insertion point (before the teardown/valediction, alongside the roam-inbox sub-step). Then it's a synced-file change (canonical-then-mirror) + a test that the wrap step drains the queue. +** DONE [#B] working/ tracked-from-creation + gitignored temp/ :feature: +CLOSED: [2026-07-20 Mon] +Craig's ruling relayed from .emacs.d (2026-07-19): working/ is version-controlled from creation (not excluded until graduation); ephemeral artifacts go in a gitignored temp/ or /tmp; graduation reorganizes durable artifacts into permanent homes rather than marking when they become durable. + +Implemented (Shape A) during the morning sentry review, 2026-07-20: +- =claude-rules/working-files.md=: added "working/ Is Version-Controlled From Creation" section (tracked-from-creation, graduation-is-a-move, temp/ for ephemeral). +- =.ai/protocols.org= (canonical + mirror): one-paragraph mirror in the Working-Files Convention section. +- =scripts/install-ai.sh=: emits a =temp/= ignore block in BOTH track and gitignore modes; working/ never ignored. Idempotent. +- =scripts/sweep-gitignore-tooling.sh=: separate mode-independent temp/ backfill pass (a distinct loop, not an IGNORE_SET member — that would skip track-mode projects, the set that most needs it). +- Tests: 3 new install-ai bats + 4 new sweep bats (temp/ in both modes, never working/, idempotent). Suite green, 384 bats ok. + +Finding confirmed at implementation: the canonical machinery already never ignored working/ (tooling set is only =.ai/ .claude/ CLAUDE.md AGENTS.md=), so .emacs.d's =/working/= ignore was a purely local deviation. The staging proposal dir was removed after shipping; its content lives in the feat commit and this body. +** DONE [#C] Polyglot projects — supported, or refused? :spec: +CLOSED: [2026-07-20 Mon] +DECISION (2026-07-20, scouting with Craig): *case-by-case, and it already composes — no option-2 machinery.* The evidence: bundle contents split into namespaced/additive files (rules =<lang>.md=, =validate-<lang>.sh= hooks, =coverage-summary.<ext>= scripts, appended =gitignore-add.txt=) that compose cleanly, and exactly three colliding files — =claude/settings.json= and =githooks/pre-commit= (full bundles only: bash/elisp/go) and =coverage-makefile.txt= (elisp/go/python/typescript). Of the three, only =coverage-makefile.txt= occurs in the fleet, and clock-panel (the one real polyglot, python+typescript) proves it's benign: both bundles installed, no breakage, because that fragment is a hand-pasted Makefile block nobody pasted twice. So: keep the install-lang collision guard (it blocks the destructive full+full settings/githooks clobber, which no project actually hits), and document the =coverage-<lang>:= + =coverage:= aggregate namespacing as the one manual step when going polyglot (filed below). No two-full-co-equal-bundle project exists, so the settings.json/githooks merge is unwarranted. Follow-up doc: [[file:todo.org::*Document polyglot coverage-makefile namespacing][Document polyglot coverage-makefile namespacing]]. + +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. +** DONE [#C] Subproject pattern — promote to claude-rules? :spec: +CLOSED: [2026-07-20 Mon] +DECISION (2026-07-20, scouting with Craig): *don't promote — keep it local at home.* The scouting confirmed N=1: across all 27 =.ai= scopes, home (9 subprojects, all from the single 2026-06-11 fold) is the only real user. The nearest neighbors aren't the pattern — rulesets folds claude-templates in as a git *subtree* (different mechanism, not shared-=.ai/=-scope), archsetup's dotfiles/ likewise; every other project is a focused single package with no subproject structure or need. Promoting a 282-line convention into the always-on claude-rules layer for one project fails the thin-always-on principle (the precedent is patterns.md at 29 lines + docs-lifecycle.md's depth-in-a-spec). Keep the convention as home's local instance. Revisit only if a genuine second case appears, and then as a thin pointer + a spec, never 282 lines always-on. + +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. +** DONE [#B] Triage source activation — per-project source declaration :feature:spec: +CLOSED: [2026-07-20 Mon] +Spec: [[file:docs/specs/2026-07-20-triage-source-activation-spec.org][docs/specs/2026-07-20-triage-source-activation-spec.org]] (IMPLEMENTED, ID af73ef0b-cd1d-46f1-9e1d-62695733a4de). Craig approved after his read, both open decisions resolved via cj comments; built same session. + +From the sentry live trial (2026-07-20): triage-intake self-activated in every project because the general (personal-account) plugins are template-synced everywhere, so sentry's pass-3 probe misfired and would pull Craig's personal inboxes into whatever project the fire ran in. Fixed with an activation gate in triage-intake Phase 0 — general plugins gate on a per-project =:TRIAGE_SOURCES:= declaration; project-specific plugins stay active by presence. Applies to interactive and unattended alike. sentry pass-3 probe reads the same signal. Migration handoffs sent to home + work. Supersedes Fire 1's narrower probe-only approval-queue item. +** DONE [#B] Silent-until-signal for in-session monitor loops :feature:spec: +CLOSED: [2026-07-20 Mon] +Spec: [[file:docs/specs/2026-07-20-silent-until-signal-monitors-spec.org][docs/specs/2026-07-20-silent-until-signal-monitors-spec.org]] (IMPLEMENTED, ID af592bd6-d3e6-47e2-8804-2a287b4d9303). Craig approved without changes 2026-07-20; all 5 phases shipped that day. Sentry, auto triage-intake, and auto inbox-zero all collapse an empty fire to =<workflow> at HH:MM: nothing=; a manual-testing entry covers the live-loop verification. + +Craig-approved proposal from .emacs.d (2026-07-20), demonstrated by the sentry live trial (fires 3-8 were walls of no-op lines). Reframed live from a watcher *mechanism* to a *policy*: an in-session monitor fire detects first, and on an empty check collapses to one labelled heartbeat line (=<workflow> at HH:MM: nothing=) instead of a full turn; only a real item earns the full surface-and-judge turn. Keeping detection in-session dissolves the MCP-auth split (triage's Gmail/Slack/Linear need session auth), so it applies uniformly to sentry, auto triage-intake, and auto inbox-zero. No external watcher, no new seen-list. Five build phases in the spec. Phase 1 (sentry quiet-fire heartbeat) shipped 2026-07-20 ahead of the full READY gate at Craig's direction — a quiet fire now collapses to =sentry at HH:MM: nothing=. Phases 2-4 (auto triage-intake, auto inbox-zero, shared-policy home) + Phase 5 verification pending Craig's spec read. Overlaps the sentry cluster ([[file:todo.org::*Sentry vNext passes][Sentry vNext passes]], [[file:todo.org::*Triage source activation][Triage source activation]]). +** DONE [#C] Apply suspend.org detach-on-suspend change to canonical :feature: +CLOSED: [2026-07-20 Mon] +Craig's change relayed from archsetup (2026-07-20): add Step 6 to suspend.org — detach the tmux client (=tmux detach-client -s "$sess"=) as the final action of every suspend, so a suspended session parks in the re-attachable set instead of cluttering the alt-space rotation. Also reworded the neighbors bullet and the "does NOT do" teardown bullet to draw the detach-vs-teardown line. + +Applied to canonical 2026-07-20 (verified the diff was exactly the intended change, canonical + mirror synced, lint clean bar one pre-existing flush/SKILL.md link). Replied to archsetup that its local stopgap is now canonical. +** DONE [#B] Document (and own) the Signal pager :feature:spec: +CLOSED: [2026-07-20 Mon] +: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. + +*** 2026-07-20 Mon @ 15:56:56 -0500 Runbook shipped; ratio linked as a device; receive timer on both machines +All four remaining deliverables landed. (1) Runbook: [[file:docs/design/2026-07-20-signal-pager-runbook.org]] — send, read-replies, receive timer, signal-cli account/setup notes, the resolved topology decision. (2) Receive timer: =scripts/signal-receive.sh= + =scripts/systemd/signal-receive.{service,timer}= (roam-sync-shaped, 15-min cadence, 3 bats), stowed via dotfiles =common=, enabled + verified on ratio; a manual drain also cleared the 47-day staleness live. (3) Topology decision (Craig, option 2): register daily drivers as linked devices rather than ssh-relay-only — ratio linked as Device 2 "ratio-pager", direct send verified, =agent-page= generalized from a velox-only check to "any machine holding the account sends directly, else relay" (bats updated). (4) protocols.org "Paging Craig": verified accurate, no edit — already describes both channels and the caveats, and its generic runbook pointer is correct to leave un-pathed since it syncs into every project. Remaining one-time step: enable the timer on velox after it pulls dotfiles. +** DONE [#C] triage-intake auto mode — push signal sweeps to phone via agent-text :feature:solo: +CLOSED: [2026-07-20 Mon] +:PROPERTIES: +:CREATED: [2026-06-20 Sat] +:LAST_REVIEWED: 2026-07-13 +:END: +Send half shipped 2026-07-20. Folded a "Phone delivery" subsection into canonical =triage-intake.org= auto mode: a full-three-section sweep pushes to Craig's phone over Signal via =agent-text=, with a pointer from "End-of-sweep output" and a Living Document note. Signal-only by Craig's 2026-07-20 ruling — a quiet sweep's =nothing= heartbeat never reaches the phone, so silent-until-signal governs the phone channel too and the in-session heartbeat stays as proof-of-life. Falls back to inline when =agent-text= is absent. + +Reply-polling half (the old =phone-recv=) deferred to the reply-correlation follow-up: with the Signal account linked on more than one device a reply fans out to every device and neither knows which page it answers, so auto mode pushes but does not poll until that's resolved. The recv wiring is owned by that spec, not this task. + +Origin: the work project's 2026-06-18 fold-in request; 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]]. Transport re-pointed off retired ntfy → agent-text (renamed from agent-page 2026-07-20). +** CANCELLED [#D] Fully-unattended scheduled inbox check (/schedule cron pass) :feature: +CLOSED: [2026-07-20 Mon] +Merged 2026-07-20 into [[file:todo.org::*Unattended /schedule cron contract][Unattended /schedule cron contract — no-session variant]]. Same no-session design problem as the sentry /schedule variant (mutation rights, async surfacing, cross-run dedup, cron auth context); its inbox-specific context was absorbed there. |
