diff options
Diffstat (limited to '.ai')
| -rw-r--r-- | .ai/metrics/work-the-backlog.jsonl | 2 | ||||
| -rw-r--r-- | .ai/notes.org | 1 | ||||
| -rwxr-xr-x | .ai/scripts/inbox-send.py | 21 | ||||
| -rw-r--r-- | .ai/scripts/tests/test_inbox_send.py | 75 | ||||
| -rw-r--r-- | .ai/sessions/2026-07-02-09-29-docs-lifecycle-speedrun-autonomous-loop.org | 110 | ||||
| -rw-r--r-- | .ai/workflows/page-me.org | 26 | ||||
| -rw-r--r-- | .ai/workflows/work-the-backlog.org | 4 |
7 files changed, 222 insertions, 17 deletions
diff --git a/.ai/metrics/work-the-backlog.jsonl b/.ai/metrics/work-the-backlog.jsonl index be0af37..1067b3a 100644 --- a/.ai/metrics/work-the-backlog.jsonl +++ b/.ai/metrics/work-the-backlog.jsonl @@ -1,3 +1,5 @@ {"ts":"2026-07-02T05:14:42-04:00","run_id":"c726f526-2e35-4513-b25c-18ef61061333","project":"rulesets","caller":"speedrun","task":"id-link-conversion-pass","outcome":"implemented-committed","defer_reason":"","upfront_decision":false,"wall_clock_s":284,"commit_sha":"78bbaae","review_findings":0} {"ts":"2026-07-02T05:19:03-04:00","run_id":"c726f526-2e35-4513-b25c-18ef61061333","project":"rulesets","caller":"speedrun","task":"host-identity-guard-rule-plus-startup-lint","outcome":"implemented-committed","defer_reason":"","upfront_decision":true,"wall_clock_s":261,"commit_sha":"b6a977c","review_findings":0} {"ts":"2026-07-02T05:22:11-04:00","run_id":"c726f526-2e35-4513-b25c-18ef61061333","project":"rulesets","caller":"speedrun","task":"template-sync-gitignored-only-changes","outcome":"implemented-committed","defer_reason":"","upfront_decision":true,"wall_clock_s":188,"commit_sha":"ed75d3c","review_findings":0} +{"ts":"2026-07-02T05:58:16-04:00","run_id":"a48f2977-4493-48a3-9238-9b2f5ff5383b","project":"rulesets","caller":"loop","task":"inbox-send-filename-collision-fix","outcome":"implemented-committed","defer_reason":"","upfront_decision":false,"wall_clock_s":300,"commit_sha":"8099377","review_findings":0} +{"ts":"2026-07-02T05:58:16-04:00","run_id":"a48f2977-4493-48a3-9238-9b2f5ff5383b","project":"rulesets","caller":"loop","task":"page-me-notify-info-level","outcome":"implemented-committed","defer_reason":"","upfront_decision":false,"wall_clock_s":120,"commit_sha":"a6b534f","review_findings":0} diff --git a/.ai/notes.org b/.ai/notes.org index 698cd4b..e570597 100644 --- a/.ai/notes.org +++ b/.ai/notes.org @@ -77,6 +77,7 @@ Format: * Workflow State :COMMIT_AUTONOMY: yes +:LOOP_MAY_COMMIT: yes :LAST_SPEC_SORT: 2026-07-02 Markers maintained by workflows to record when they last ran. Read by other workflows that gate their behavior on freshness. diff --git a/.ai/scripts/inbox-send.py b/.ai/scripts/inbox-send.py index 1362a1f..1ebb636 100755 --- a/.ai/scripts/inbox-send.py +++ b/.ai/scripts/inbox-send.py @@ -177,6 +177,23 @@ def build_text_org(message: str, source_name: str, timestamp: str) -> str: ) +def uniquify(dest: Path) -> Path: + """Return dest, or dest with a -2/-3/... stem suffix when it already exists. + + Two sends in the same minute whose text starts with the same phrase + derive identical filenames, and the second silently overwrote the + first (a message was lost this way, 2026-07-02). Never overwrite. + """ + if not dest.exists(): + return dest + n = 2 + while True: + candidate = dest.with_name(f"{dest.stem}-{n}{dest.suffix}") + if not candidate.exists(): + return candidate + n += 1 + + def send_text( target_inbox: Path, message: str, @@ -191,7 +208,7 @@ def send_text( if not slug: raise ValueError(f"could not derive a slug from text: {message!r}") filename = f"{now.strftime(TS_FILENAME_FMT)}-from-{source_name}-{slug}.org" - dest = target_inbox / filename + dest = uniquify(target_inbox / filename) dest.write_text(build_text_org(message, source_name, now.strftime(TS_DOC_FMT))) return dest @@ -211,7 +228,7 @@ def send_file( raise ValueError(f"could not derive a slug from file: {src_path}") ext = src_path.suffix filename = f"{now.strftime(TS_FILENAME_FMT)}-from-{source_name}-{slug}{ext}" - dest = target_inbox / filename + dest = uniquify(target_inbox / filename) shutil.copy2(src_path, dest) return dest diff --git a/.ai/scripts/tests/test_inbox_send.py b/.ai/scripts/tests/test_inbox_send.py index cb60e63..f75d7a1 100644 --- a/.ai/scripts/tests/test_inbox_send.py +++ b/.ai/scripts/tests/test_inbox_send.py @@ -401,3 +401,78 @@ class TestInboxSendErrors: assert result.returncode != 0 files = list((tmp_path / "projects" / "target" / "inbox").iterdir()) assert files == [] + + +# ---------------------------------------------------------------------- +# Filename collisions (two sends deriving the same name must not overwrite) +# ---------------------------------------------------------------------- + +def _load_module(): + import importlib.util + spec = importlib.util.spec_from_file_location("inbox_send", SCRIPT) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) + return mod + + +class TestFilenameCollisions: + """Two sends in the same minute with the same leading phrase derived + identical filenames and the second silently overwrote the first + (a message was lost this way, 2026-07-02).""" + + def test_send_text_same_minute_same_phrase_keeps_both(self, tmp_path): + from datetime import datetime + mod = _load_module() + inbox = tmp_path / "inbox" + inbox.mkdir() + now = datetime(2026, 7, 2, 5, 42, 0) + prefix = "identical leading phrase long enough to fill the whole slug budget entirely" + first = mod.send_text(inbox, prefix + " tail one", "archsetup", None, now) + second = mod.send_text(inbox, prefix + " tail two", "archsetup", None, now) + assert first != second + assert first.exists() and second.exists() + assert first.name != second.name + assert "tail one" in first.read_text() + assert "tail two" in second.read_text() + + def test_send_text_collision_suffix_increments(self, tmp_path): + from datetime import datetime + mod = _load_module() + inbox = tmp_path / "inbox" + inbox.mkdir() + now = datetime(2026, 7, 2, 5, 42, 0) + paths = [mod.send_text(inbox, "same lead phrase differs later A", "src", "fixed-slug", now) + for _ in range(3)] + names = [p.name for p in paths] + assert names[0].endswith("fixed-slug.org") + assert names[1].endswith("fixed-slug-2.org") + assert names[2].endswith("fixed-slug-3.org") + + def test_send_file_collision_preserves_extension(self, tmp_path): + from datetime import datetime + mod = _load_module() + inbox = tmp_path / "inbox" + inbox.mkdir() + src = tmp_path / "note.org" + src.write_text("body one") + now = datetime(2026, 7, 2, 5, 42, 0) + first = mod.send_file(inbox, src, "src", None, now) + src.write_text("body two") + second = mod.send_file(inbox, src, "src", None, now) + assert second.name.endswith("note-2.org") + assert first.read_text() == "body one" + assert second.read_text() == "body two" + + def test_cli_two_rapid_sends_lose_nothing(self, project_root, run_script, tmp_path): + project_root("sender") + target = project_root("receiver") + roots = [tmp_path / "projects"] + prefix = "identical leading phrase long enough to fill the whole slug budget entirely" + run_script(["receiver", "--text", prefix + " message one"], + cwd=tmp_path / "projects" / "sender", roots=roots) + run_script(["receiver", "--text", prefix + " message two"], + cwd=tmp_path / "projects" / "sender", roots=roots) + files = list((target / "inbox").iterdir()) + assert len(files) == 2 + bodies = "".join(f.read_text() for f in files) + assert "message one" in bodies and "message two" in bodies diff --git a/.ai/sessions/2026-07-02-09-29-docs-lifecycle-speedrun-autonomous-loop.org b/.ai/sessions/2026-07-02-09-29-docs-lifecycle-speedrun-autonomous-loop.org new file mode 100644 index 0000000..8fc23e9 --- /dev/null +++ b/.ai/sessions/2026-07-02-09-29-docs-lifecycle-speedrun-autonomous-loop.org @@ -0,0 +1,110 @@ +#+TITLE: Session Context — Docs-lifecycle build, wrap-up router, speedrun build +#+DATE: 2026-07-02 + +* Summary + +** Active Goal +(Session closed 2026-07-02 ~09:30 — the standing loop directive below ran through the night and morning and ended with this wrap; cron job 752624d1 deleted at wrap.) +Standing directive (Craig, 2026-07-02 ~01:30): run auto-inbox-zero every 30 minutes with a STANDING YES — execute on all items found each cycle. Per-item disposition: feature-level task → write a spec (spec-create); decisions I can't confidently guess → file a VERIFY; well-defined → implement with the full quality bar. Autonomous commit + push under rulesets' :COMMIT_AUTONOMY: waiver. Auto-flush (/flush auto, self-inject) at clean boundaries when context grows heavy. Earlier directive items all DONE tonight: wrap-up routing, speedrun Phases 1-6 (work-the-backlog.org), roam inbox zero, auto-flush implementation + speedrun incorporation + disposition rule. + +** Decisions +- Inbox: Craig approved all five dispositions (convert-subtasks bundle + planning-line fix, task-audit C.6, sweep security fix + public-reachability convention + broadcast, spec-review UI-traps promotion, KB orphan report → [#C] task). +- Wrap-teardown: Craig ran all five manual tests, all passed — feature closed DONE, armed as the default (a bare "wrap it up" tears the session down; "with summary" keeps the buffer). +- Speedrun Phase 0: hard :solo:/:quick: definitions are fixed cross-project in todo-format.md; review/audit tag assessment is mandatory; task-review gate 3 realigned to no-deliberation (1-2 upfront-answerable quick decisions allowed). +- Docs-lifecycle spec: ratified through two independent review rounds (Codex + fresh-context Claude agent; 14 findings, all fixed, verify passes held); Codex flipped READY; spec-response decomposition flipped DOING. Key forks: two-sequence keyword header; :SPEC_ID: parent-keyword binding; fail-safe --apply; file: links through the pilot with id: conversion gated on the .emacs.d id-index mechanism; evidence-based status confirmation. +- Lesson: never flip a lifecycle state in the same pass that authored the fixes — the reviewer owns the flip. + +** Data Collected / Findings +- Spec: docs/specs/2026-07-01-docs-lifecycle-spec.org, status DOING, :ID: 80b0787b-4a60-4c82-8a16-b383d3e3c8f2; build parent in todo.org carries :SPEC_ID: (task "Spec storage location + lifecycle-status convention", ~line 361). +- Pilot surface: docs/design has 41 files (3 spec-spine candidates: Decisions AND Implementation phases); 2 stray root specs (agent-knowledge-base-spec.org, inbox-workflow-consolidation-spec.org); docs/design/task-review.org is the note counter-case (Metadata only). +- Validations: KB check 1 verified (55 rg = 55 org-roam DB); check 4 velox half verified (probe node agents/20260701214910-kb-sync-validation-probe.org pushed f0252bb, 0 conflicts) — ratio half blocked (ssh times out; probe left in place; confirm command logged in todo.org). KB checks 2+3 need work/unknown-project sessions. Roam inbox holds 2 rulesets items (ai-term colors → .emacs.d territory; "wrap it up closes window" → already delivered by wrap-teardown). +- Bare [N/N] tokens in org prose get mangled by cookie updates — spell counts in words. + +** Files Modified +All committed and pushed through 9ad415d. Tonight: 80ca5d0 spec-sort helper + 33-test bats; f4b64d6 pilot (5 specs sorted to docs/specs/, board live, :LAST_SPEC_SORT: stamped); 21639cb startup nudge (find-based probe, compgen was bash-only) + .emacs.d convention-live/id-index note; 7c12007 wrap-up router (route-batch + 13-test bats, inbox.org :ROUTE_CANDIDATE: stamp, wrap-it-up router step, cross-project.md); 9ad415d speedrun decomposition + spec DOING flip. Earlier (2026-07-01): d0c92d0 docs-lifecycle Phase 1 and everything before it. + +** Next Steps +SESSION CLOSED — the auto-inbox-zero loop ended with the wrap (job deleted; a future session re-arms it only on a fresh directive from Craig). What carries forward: Craig's parked [#C] wrap-summary keep-or-cut think-through; ratio probe confirm (ssh was timing out — verify agents/20260701214910-kb-sync-validation-probe.org landed on ratio); KB refusal checks 2+3 (need work/unknown-project sessions); docs-lifecycle leftovers (Craig's manual tests: nudge visibility + Emacs id-link click-through; 4 anomaly renames; id-conversion gated on .emacs.d id-index). All build work from this session is DONE and pushed. + +Original loop contract (historical): continue the hourly auto-inbox-zero loop (cron job 752624d1, fires at :37, session-only). Each cycle: inbox-status + roam scan (capture-guard before roam writes); quiet → one acknowledgement line; finds → file, then execute ALL under Craig's standing yes with autonomous-commit + push (:COMMIT_AUTONOMY: + :LOOP_MAY_COMMIT: both stamped in notes.org Workflow State), disposition feature→spec / unguessable→VERIFY / well-defined→implement, full quality bar, metrics JSONL per task, session-log update per state-mutating cycle, /flush auto at heavy-context clean boundaries. Parked for Craig at his choosing: wrap-summary keep-or-cut think-through ([#C] in todo.org). Carryovers: ratio probe confirm (ssh), KB refusal checks (work/unknown sessions), docs-lifecycle leftovers (Craig's manual tests: nudge visibility + Emacs id-link click-through; 4 anomaly renames). Everything else from tonight is DONE and pushed (speedrun spec IMPLEMENTED after live trial; auto-flush + self-inject shipped; inbox-send collision fix; page info-styling; host-identity rule; template-sync policy; id-link conversion). + +KB: promoted 1 / consulted no +(Node: agents/20260702093025-reviewer-owns-the-lifecycle-flip.org — the don't-flip-your-own-fixes lesson from the docs-lifecycle spec review.) + +OLD (completed): build the speedrun / autonomous-batch phases per the spec docs/specs/2026-06-16-autonomous-batch-execution-spec.org (DOING, :ID: 90f623cd-fdbe-4f5c-b63d-b2f84d9151cf; build parent "No-approvals speedrun" in todo.org carries :SPEC_ID:, children Phases 1-6 + live-trial + flip). Read the spec's Design section (lines ~66-177: loop at both altitudes, eligibility gate, defer checklist, pre-flight Q&A, session modes/preset, run cap + kill switch, paging) before writing. Phase 1: write claude-templates/.ai/workflows/work-the-backlog.org (eligibility gate, defer checklist, per-task quality bar, run-cap; inputs task set + session mode + cap) AND revert inbox.org's "auto inbox zero" per-cycle item 3 yes-path to routing-only in the same commit (one home for execution). Phase 2: wire both callers (auto-inbox-zero yes-path → work-the-backlog tag-query/file-only/cap-1; speedrun preset → explicit list/autonomous-commit/always-push/paging after pre-flight Q&A). Phases 3-6 per the child task bodies. Canonical-side edits, sync-check --fix, make test, commit per phase. THEN: roam inbox zero (inbox.org roam mode; 2 known rulesets-related items: ai-term colors → .emacs.d territory, wrap-it-up-closes-window → already delivered) and react. Docs-lifecycle leftovers for Craig: flip decision pending his manual tests (nudge visibility + Emacs link click-through), 4 anomaly renames, id-conversion gated on .emacs.d. Carryovers: ratio probe confirm (ssh still timing out), KB refusal checks. + +* Session Log + +** 2026-07-02 Thu @ 09:30 -0400 — Session wrapped (teardown mode) +Morning loop cycles after the 07:51 auto-flush were all quiet (0 pending handoffs, roam inbox empty; last manual cycle ~09:15). Craig called the wrap. Cron job 752624d1 deleted; one KB node promoted (reviewer-owns-the-lifecycle-flip); todo cleanup + lint ran; wrap commit pushed. Teardown sentinel dropped per the validated default. + +** 2026-07-02 Thu @ 07:51:13 -0400 — flushed (auto-flush, self-injected) +Clean boundary: 07:50 loop cycle came back empty, tree clean, everything pushed through the metrics commit after a6b534f, suites green. Nothing in flight. First live use of the auto-flush mechanism shipped tonight (self-inject via tmux run-shell -b). Post-clear resume: read this Summary and continue the hourly loop per Next Steps — the cron job survives the clear (session-only, not conversation-only). Craig's standing yes remains in force (Active Goal). + +** 2026-07-02 Thu @ 06:00 -0400 — Loop cycle executed 3 finds (commits through a6b534f + metrics, pushed) +First executing loop cycle under the marker-granted autonomy. Found 3 archsetup handoffs + 2 rulesets roam entries (duplicates of the routed ones). Shipped: inbox-send collision fix (uniquify -2/-3 suffix, 4 red-first deterministic tests, 30/30 — a wild data-loss find, graded [#B] P2), page styling alarm → info --persist (page-me.org + work-the-backlog page; status-check untouched), dupre-blue ai-term refinement forwarded to .emacs.d (#67809c). Roam inbox: swept the 2 rulesets entries (archsetup's 3 left, roam-sync owns the git). Local inbox 0 pending; archsetup replied. Suites green, sync clean (one drift caught by the pre-commit check and re-synced). Loop continues hourly at :37 (job 752624d1). + +** 2026-07-02 Thu @ 05:30 -0400 — Live trial validated, spec IMPLEMENTED, loop now hourly (5eae9e0, pushed) +Craig's "1" granted :LOOP_MAY_COMMIT: (stamped in notes.org Workflow State) and validated the run. Closed the live-trial + flip children dated, parent "No-approvals speedrun" DONE + CLOSED, spec flipped DOING → IMPLEMENTED (board: 2 IMPLEMENTED / 2 READY / 2 DOING). Wrap-summary keep-or-cut think-through PARKED (stays filed [#C] in todo.org). Auto-inbox-zero rescheduled: old 30-min job deleted, new hourly job 752624d1 (at :37, off-minute per fleet guidance), same standing-yes contract, now with loop commits authorized by the marker rather than only the directive. + +** 2026-07-02 Thu @ 05:25 -0400 — First no-approvals speedrun complete: 3/3 (78bbaae, b6a977c, ed75d3c + metrics commit, all pushed) +Live-trial run c726f526 over Craig's ordered set. Pre-flight Q&A fired once (2 questions, Craig took both recommendations, answers stamped into task bodies as dated lines). Task 1 id-link conversion: 13 links → id: form, Review-findings heading got its own :ID: for the 2 search-target links, residue zero, all ids verified. Task 2 host-identity: claude-rules/host-identity.md (linked machine-wide, verified) + startup probe 13 (fixture-verified bash+zsh) + Phase C flag line. Task 3 template-sync: freshness policy in startup Phase A.0 (dirty = tracked-only; WIP-guard named as deliberate exception), monitor-inbox precondition fixed to --untracked-files=no + close-out symmetrized. Every task: /review-code, /voice, make test green, sync clean, one JSONL record (3 records in .ai/metrics/work-the-backlog.jsonl). End-of-set page fired via notify --persist. AWAITING: Craig's read on the run → then close the live-trial child dated + flip the autonomous-batch spec DOING → IMPLEMENTED. Then his two parked decisions: :LOOP_MAY_COMMIT: grant, wrap-summary keep-or-cut think-through. + +** 2026-07-02 Thu @ 01:40 -0400 — Inbox pass done; auto-flush shipped (d4f132b, 794b248, pushed); 30-min executing loop armed +Local inbox zero + roam inbox zero (roam was already empty — archsetup routed it). Processed: .emacs.d org-id delivery → id-conversion task ungated (:solo: now); archsetup's three roam items → template-sync-gitignored filed [#C], ai-term colors forwarded to .emacs.d, wrap-summary keep-or-cut filed [#C]; auto-flush bundle → self-inject.sh canonicalized + 6-test bats, flush skill auto mode (gate order preserved: verify anchor write BEFORE arming), work-the-backlog auto-flush section + preset step 6 + per-item disposition rule (feature→spec, unguessable→VERIFY, well-defined→implement). Design note preserved at docs/design/2026-07-02-auto-flush-mechanism-note.org. Replies sent to .emacs.d (x2) + archsetup (x2). All suites green, sync clean. NOW: /loop 30m auto-inbox-zero with Craig's standing yes (see Active Goal). + +** 2026-07-02 Thu @ 01:30 -0400 — Speedrun Phases 2-6 shipped (263138a, 8d790c0, 04561b2, eea93f1, 44c8cc2 — all pushed) +Phase 2: both callers wired (auto-mode chain ask scoped to the queued batch — a deliberate judgment, logged in the commit; speedrun preset section + trigger routing, "speedrun" always beats "no approvals" with disambiguation in no-approvals.org + INDEX). Phase 3: waiver pinned as :COMMIT_AUTONOMY:/:LOOP_MAY_COMMIT: markers in notes.org Workflow State; rulesets stamped :COMMIT_AUTONOMY: yes, :LOOP_MAY_COMMIT: deliberately left for Craig; .emacs.d told to stamp its own (inbox-send 0118). Phase 4: VERIFY-filing dedup (existing-sibling check), quick-question discriminator, batch-ask contract, page finalized. Phase 5: JSONL field table (+failed outcome, +manual caller, +comma-separated commit_sha — three traceable spec gaps closed). Phase 6: synthesis section + "synthesize backlog metrics" trigger. Every phase: /review-code, /voice, make test green, sync clean, todo.org child flipped dated. Parent stays DOING pending Craig's live trial + the flip task. NEXT: local inbox (2 handoffs: .emacs.d org-id delivery unblocks the docs-lifecycle id-conversion task; archsetup roam-routed item unread), then roam inbox zero + react. + +** 2026-07-02 Thu @ 01:15 -0400 — Speedrun Phase 1 shipped (d379a23, pushed) +work-the-backlog.org created (canonical + mirror + INDEX entry): caller contract, five-outcome vocabulary, mechanical eligibility gate (TODO + :solo:, no-scheme-header → don't run), four-item defer checklist, quality bar, cap semantics, Phase 3-5 stubs. inbox.org auto-mode item 3 reverted to routing-only. Review fixed a cap-default contradiction (explicit set defaults to list length, not 1) pre-commit. make test green twice, sync clean, todo.org Phase 1 child rewritten dated. NOTE: new inbox handoff from .emacs.d (2026-07-02-0056) — org-id resolution delivered, the gated id-conversion task is unblocked; process during the inbox pass after the speedrun build. Next: Phase 2 (wire the two callers). + +** 2026-07-02 Thu @ 00:47:00 -0400 — flushed +Clean boundary: wrap-up router shipped (7c12007 — route-batch helper + 13-test bats, inbox.org marker stamp, wrap-it-up router step, cross-project.md note; review fixed a reproduced nested-candidate data-loss bug pre-commit) and the speedrun spec decomposed + flipped DOING (9ad415d). Nothing half-edited; tree clean, pushed through 9ad415d, suite green. Post-clear resume goes straight to speedrun Phase 1 (contract pointers in Next Steps). Remaining under wrapup-routing: manual e2e (Craig's) + vNext transcript task. + +** 2026-07-02 Thu @ 00:25 -0400 — Phases 3 + 4 shipped: pilot ran, nudge live, .emacs.d notified +Craig confirmed all five pilot keywords as-is (option 1) plus the IMPLEMENTED reason for agent-knowledge-base-spec. Applied with --allow-dirty (only the untracked session anchor was dirty): 5 specs moved to docs/specs/, 12 todo.org links + the moved specs' outbound links rewritten, :LAST_SPEC_SORT: 2026-07-02 stamped, residue zero, board live (6 specs: 1 IMPLEMENTED, 3 READY, 2 DOING). Phase 4: spec-sort probe added to startup.org Phase A + Phase C nudge line; replaced the spec's compgen sketch with a find-based check (compgen is bash-only, zsh false-negatived on stray root specs) — fixture-verified both shells, four project shapes; fixed startup.org's stale path to the moved encourage-kb spec; sent .emacs.d the convention-live note + id-index ask (2026-07-02-0022 handoff). f4b64d6 + 21639cb pushed, make test green, sync clean. + +** 2026-07-02 Wed @ 00:15 -0400 — Phase 2 shipped: spec-sort + 33-test bats suite (80ca5d0, pushed) +TDD'd the retrofit helper (bats red-first, then the Python implementation). A fresh-context review agent found 4 real issues, all fixed pre-commit: acknowledged bare mentions weren't mapped through moves (a self-mention turned a successful apply into a false FAILURE with a destructive recovery recipe — regression-tested), real OSError mid-apply lost the applied-ops list (both failure paths now share ApplyFailure), "incomplete" status proposed terminal IMPLEMENTED (word-boundary matching now), and file-relative vs root-anchored link ambiguity now blocks validation as AMBIGUOUS. Real-data dry run matches predictions (5 candidates / 4 anomalies / 30 notes / 1 self bare mention / 10 report-only incl. the Codex-flagged startup.org case). make test green, sync clean. Next: Phase 3 pilot — candidate keywords + anomaly dispositions need Craig (see Next Steps). + +** 2026-07-01 Wed @ 23:41:36 -0400 — flushed +Clean boundary after docs-lifecycle Phase 1 (d0c92d0, pushed, tree clean, suite green). In flight: nothing half-edited. Post-clear resume goes straight to Phase 2 — the spec-sort build (contract pointers in Next Steps above). + +** 2026-07-01 21:05 EDT — Session resumed after interruption; inbox first, then validations/specs +The 2026-06-30 session died right after the validation pre-flight (nothing lost beyond the plan itself — no edits had landed). New session startup found this anchor plus 15 pending inbox handoffs. Craig's call: process the inbox first, then return to the validations + spec writing goal above. Also committed the one-line .claude/settings.json change from his /model command (c976f5b, "chore: set fable as project default model"). Green baseline confirmed before the commit: make test exit 0 — pytest 370+67+12 passed, all ERT suites 0 unexpected, 0 bats failures. + +Inbox inventory (15): the .emacs.d convert-subtasks bundle (10 files — todo-cleanup/lint-org + tests + 5 workflow/rule wirings), the .emacs.d task-audit Phase C.6 follow-on (2 files), the .emacs.d sweep-gitignore anchored-pattern security fix ask, the archsetup spec-review UI-traps promotion proposal, and a KB hygiene report (42 orphan agent nodes). Plan: skeptical review via diff-against-canonical for the bundles, then surface dispositions for approval. + +** 2026-07-01 ~21:45 EDT — Inbox items A, B, C shipped +Craig approved all five recommendations (A1 B1 C1 D1 E1). Shipped so far: +- A (19ba7cb): convert-subtasks bundle applied to canonicals + mirror — todo-cleanup --convert-subtasks, lint-org subtask-done-not-dated checker, wiring in wrap-it-up/clean-todo/open-tasks/task-review/todo-format.md. TDD'd the planning-line edge on top (CLOSED removal now preserves a DEADLINE/SCHEDULED sharing the line; red then green). Suites 45/45, 49/49, full make test green. +- B (356b905): task-audit Phase C.6 (retire completed parents / promote stragglers) applied as sent. +- C (909b21b + bac3fe4): sweep-gitignore-tooling.sh now recognizes anchored /.ai/ (mode detection + per-pattern presence + style-matched append) and WARNs on tracked tooling reachable via a non-cjennings.net remote; bare cjennings ssh-alias counts as private (false positive on rulesets itself caught by the real-data dry run, fixed with a 13th bats test). protocols.org gained the public-reachability convention. Real sweep run: 6 projects backfilled (archsetup, chime, emacs-wttrin in anchored style), archsetup's tracked CLAUDE.md flagged. Broadcast sent to 14 projects via inbox-send.py (the inbox-send wrapper isn't on PATH here — used the .py directly); archsetup got an extra tailored note about its tracked CLAUDE.md. +Still open in this pass: D (spec-review UI-traps), E (KB orphan task), replies to .emacs.d + archsetup, inbox file cleanup, LAST_INBOX_PROCESS stamp, push. + +** 2026-07-01 ~21:55 EDT — Inbox pass complete: D, E, replies, cleanup +D (9814b94): archsetup's six UI-traps checks promoted into spec-review.org Phase 4 as the conditional "Operational-panel UI traps" dimension; accept reply sent to archsetup. E: KB orphan review filed as [#C] :chore: in todo.org (orphan-ness isn't a defect; periodic prune/merge/link pass, regenerate the list before running); report deleted, no reply (script source). Full reply to .emacs.d sent covering all three of its handoffs, including the planning-line divergence from what it sent. All 15 inbox files deleted; inbox-status 0 pending; :LAST_INBOX_PROCESS: stamped 2026-07-01. Next: commit todo/notes, push all five commits, then return to the interrupted goal — manual validations (wrap-teardown task 42, Agent-KB refusal checks task 309) and the queued specs. + +** 2026-07-01 ~22:00 EDT — Validations: agent-runnable half done +Inbox pass pushed (7 commits, e36e932..6ec05bb). Resumed the validations goal. Done tonight: wrap-teardown plumbing re-verified fresh (Stop hook + symlink + no stale sentinel + companion fns (t t t); 3 live aiv-* sessions available for the gate test); Agent-KB check 1 verified (55 :agent: nodes in rg inventory AND 55 in the live org-roam DB — match); check 4 velox half verified (probe node agents/20260701214910-kb-sync-validation-probe.org committed + pushed by roam-sync in seconds, f0252bb, 0 conflicts). BLOCKED: ratio ssh times out (tailscale ping pongs via DERP, TCP:22 unreachable — likely suspended); probe left in place for later confirmation. Still needing Craig: wrap-teardown 5-test checklist (scratch session), KB checks 2+3 (work/unknown-project refusal sessions), work-machine-no-clone check. Evidence logged in todo.org under both tasks. Next: surface status + spec-writing choice to Craig. + +** 2026-07-01 ~22:00 EDT — Wrap-teardown feature validated and closed +Craig ran all five manual tests live — teardown-after-valediction, both summary qualifiers, the multi-session shutdown refusal, the cancellable countdown + stubbed shutdown, and the push-failure guard. All passed ("works great"). Rewrote the checklist sub-task to a dated entry and closed the parent DONE + CLOSED [2026-07-01 Wed] (archives on the next --archive-done). Sent .emacs.d a one-line FYI since its companion functions are half the feature. NOTE for this session's own wrap: teardown is now the validated default — a bare "wrap it up" here will tear this session down; use "with summary" to keep the buffer. Remaining validation carryover: KB refusal checks (work/unknown project sessions), ratio probe confirmation, work-machine-no-clone check. + +** 2026-07-01 ~22:15 EDT — Speedrun Phase 0 + docs-lifecycle spec drafted +Craig picked "2 then 1". Phase 0 shipped (2a45f07): hard :solo:/:quick: definitions in todo-format.md (fixed cross-project; :solo: = buildable + agent-verifiable + no deliberation with 1-2 upfront-answerable quick decisions allowed; :quick: = ≤30-min effort hint, never a gate), mandatory-assessment language in task-review + task-audit, and task-review gate 3 realigned from "no upfront decision" to the ratified no-deliberation form. Then the docs-lifecycle spec drafted at docs/specs/2026-07-01-docs-lifecycle-spec.org from the five 2026-06-28 decisions, dogfooding itself (first resident of docs/specs/, status heading DRAFT, :ID: link). Design call made in the draft: the authoritative keyword lives on a prepended top-level status heading (vocabulary DRAFT READY DOING | IMPLEMENTED SUPERSEDED CANCELLED) — additive, retrofittable, grep- and agenda-scannable. lint-org --check on the spec: 0 mechanical, 6 judgment items that are todo-shape checkers misreading spec conventions (spec DONE decisions carry no CLOSED; review-history dated headers are template shape) — noted, no action. Task flipped DOING with a dated entry. Awaiting Craig's spec review to flip DRAFT → READY. + +** 2026-07-01 ~22:30 EDT — Dual review of the docs-lifecycle spec; all nine findings fixed +Craig had two independent agents review the spec: Codex (4 blocking findings, recorded in the spec's Review findings section) and my dispatched fresh-context reviewer (9 findings: same top blocker as Codex plus 5 unique, incl. the unowned DOING→IMPLEMENTED flip). Both rated Not ready; both independently caught that my #+TODO replacement destroyed the decision-task keyword machinery — the spec's own cookie was hand-faked. Craig approved fixing all nine. Responder pass done: merged ledger [9/9] with per-finding responses, two-sequence keyword header (verified: org computes [5/5] and [9/9]), transition-ownership table + spec-response flip task + task-audit safety net, single classification predicate, -spec.org rename, full relink contract, marker/nudge contract, compatibility rule, org-id prerequisite, three-line transition. Lint judgments on the spec are known todo-shape false positives (spec DONE entries and dated history headers). Status stays DRAFT; Craig decides the READY flip (option: send the fixed spec back to the reviewer agent, a082bea09c72a4e15, for a verify pass). + +** 2026-07-01 ~22:55 EDT — Second review round: five more findings, fixed and verified +After the first nine fixes, my premature READY flip raced Codex's re-review — no data lost (commit 642be35 carries both my flip and Codex's demotion + five new blocking implementation-readiness findings; twelve seconds of READY). Craig approved a second responder pass, including the fork on the org-id finding (keep file: links through the pilot; id: conversion gated on a concrete .emacs.d id-index mechanism). Fixed all five (b163637): canonical-placement contract, :SPEC_ID: parent-keyword binding for task-audit (dissolves the flip-task chicken-and-egg, survives --convert-subtasks), fail-safe --apply (preflight/plan/recovery), staged id conversion, evidence-based status confirmation. Also de-cookified bracket [N/N] prose tokens org's cookie updater would mangle. My reviewer's second verify pass: ready, all held, nothing regressed, three minor nits — folded in (43cecd4): scoped id-link criterion, untracked-copy cleanup in recovery, two stale prose spots. Spec parked at DRAFT [14/14]; the authoritative READY flip is left to Codex's rerun or Craig. Lesson recorded: don't flip a lifecycle state the same pass that authored the fixes. + +** 2026-07-01 ~23:40 EDT — Spec READY (Codex flip), decomposed, Phase 1 built +Codex's rerun flipped the spec READY at 23:22 (all fourteen findings closed). Craig: run with it, then flush and do Phase 2. Committed the reviewer flip, then ran spec-response Phase 6 as the first live exercise of the convention: :SPEC_ID: stamped on the build parent in todo.org, six child tasks (Phases 1-4, the gated id-conversion pass, the flip-to-IMPLEMENTED task) plus a manual-testing child (nudge visibility, link click-through), spec flipped READY→DOING (328ca18). Phase 1 then built and committed: claude-rules/docs-lifecycle.md (new rule, linked machine-wide), spec-create location + template updates (two-sequence header, DRAFT status heading with :ID:), spec-review location expectation + compatibility rule + READY flip ownership (incl. the demote path), spec-response DOING flip + :SPEC_ID: + mandatory flip task, task-audit :SPEC_ID: reconcile query. Mirror synced, make test green. NEXT AFTER FLUSH: Phase 2 — build claude-templates/.ai/scripts/spec-sort + bats per the spec's retrofit contract (classify predicate, evidence panel, plan/validate/apply, preflight, recovery, relink, marker stamp); the spec section "The retrofit" and the Phase 2 task body carry the full contract. + +** 2026-06-30 ~14:25 EDT — Startup + validation plan (interrupted session's log) +Fresh session, clean startup (no crash anchor, clean tree, repos current). Craig: "do all the validations now, then write all the specs." Two validation items pending: wrap-teardown (task 42, 5-test checklist) and Agent-KB refusal checks (task 309, work/unknown project refusal — the cross-machine half is already confirmed on velox+ratio). + +Pre-flight on wrap-teardown plumbing: Stop hook wired in settings.json, hook script present+executable, all three companion functions (cj/ai-term-quit, -live-count, -shutdown-countdown) live in the daemon (t t t). Four live aiv-* sessions right now (aiv-_emacs_d, aiv-archsetup, aiv-rulesets [this], aiv-work). Read the hook + wrap-it-up Teardown mode + the three functions: cj/ai-term-quit is a safe idempotent no-op on a nonexistent project; shutdown-countdown aborts when >1 session live. So the gate + hook-wiring pieces are safely verifiable from this session without endangering it; only the buffer-teardown/geometry-restore and the countdown render/C-g need Craig's scratch session + eyes. diff --git a/.ai/workflows/page-me.org b/.ai/workflows/page-me.org index 607ed51..8069830 100644 --- a/.ai/workflows/page-me.org +++ b/.ai/workflows/page-me.org @@ -5,9 +5,9 @@ * Overview -This workflow enables Claude to set timers and alarms that reliably notify Craig, even if the terminal session ends or is accidentally closed. Notifications are distinctive (audible + visual with alarm icon) and persist until manually dismissed. +This workflow enables Claude to set timers and alarms that reliably notify Craig, even if the terminal session ends or is accidentally closed. Notifications are distinctive (audible + visual with the blue info icon) and persist until manually dismissed. -Uses the =notify= command (alarm type) for consistent notifications across all AI workflows. +Uses the =notify= command (info type) for consistent notifications across all AI workflows. Info-level on purpose: the earlier alarm styling read as all-red urgency, and Craig's verdict was that a page "should be a persistent info notification" — noticeable, never crash-scary (2026-07-02). * Trigger Phrase @@ -63,8 +63,8 @@ Craig tells Claude when and why: Claude schedules the alarm using the =at= daemon with =notify=: #+begin_src bash -echo "notify alarm 'Page' 'Time to call the dentist' --persist" | at 3:30pm -echo "notify alarm 'Page' 'Meeting starts' --persist" | at now + 45 minutes +echo "notify info 'Page' 'Time to call the dentist' --persist" | at 3:30pm +echo "notify info 'Page' 'Meeting starts' --persist" | at now + 45 minutes #+end_src The =at= daemon: @@ -89,26 +89,26 @@ Craig dismisses the notification and acts on it. ** Setting Alarms -Use the =at= daemon to schedule a =notify alarm= command: +Use the =at= daemon to schedule a =notify info= command: #+begin_src bash # Schedule for specific time -echo "notify alarm 'Page' 'Meeting starts' --persist" | at 3:30pm +echo "notify info 'Page' 'Meeting starts' --persist" | at 3:30pm # Schedule for relative time -echo "notify alarm 'Page' 'Check the build' --persist" | at now + 30 minutes +echo "notify info 'Page' 'Check the build' --persist" | at now + 30 minutes # Schedule for tomorrow -echo "notify alarm 'Page' 'Call the dentist' --persist" | at 3:30pm tomorrow +echo "notify info 'Page' 'Call the dentist' --persist" | at 3:30pm tomorrow #+end_src ** Notification System -Uses the =notify= command with the =alarm= type. The =notify= command provides 8 notification types with matching icons and sounds. +Uses the =notify= command with the =info= type. The =notify= command provides 8 notification types with matching icons and sounds. #+begin_src bash -# Immediate alarm notification (for testing) -notify alarm "Page" "Your message here" --persist +# Immediate page notification (for testing) +notify info "Page" "Your message here" --persist #+end_src The =--persist= flag keeps the notification on screen until manually dismissed. All page-me notifications should use =--persist= by default. @@ -139,10 +139,10 @@ The alarm must fire. Use the =at= daemon which is designed for exactly this purp Simple invocation - Claude runs one command. No complex setup required per alarm. ** Fail Audibly -If the alarm fails to schedule, report the error clearly. Don't fail silently. +If the page fails to schedule, report the error clearly. Don't fail silently. ** Testable -The =notify alarm= command can be called directly to verify notifications work without waiting for a timer. +The =notify info= command can be called directly to verify notifications work without waiting for a timer. ** Non-Alarming Use normal urgency, not critical. The notification should be noticeable but not imply something has gone horribly wrong. diff --git a/.ai/workflows/work-the-backlog.org b/.ai/workflows/work-the-backlog.org index 642162d..b0666e7 100644 --- a/.ai/workflows/work-the-backlog.org +++ b/.ai/workflows/work-the-backlog.org @@ -149,10 +149,10 @@ Task boundaries are clean boundaries by construction: the previous task is close With paging on, fire one page when the set is done or the cap is hit — end-of-set only, never per-task: #+begin_src sh -notify alarm "Page" "<project>: <N> done, <M> remaining — <one-line summary>" --persist +notify info "Page" "<project>: <N> done, <M> remaining — <one-line summary>" --persist #+end_src -=--persist= keeps it on screen until dismissed (the page-me convention). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. There is no separate page-signal call — =notify= is the paging surface. +=--persist= keeps it on screen until dismissed, and =info= is the page-me urgency convention (persistent but never crash-scary). The page fires when the set completes *or* the cap stops the run — either way exactly once. The message carries the project name, the completed count, and the remaining count (with skipped tasks noted in the run summary) so Craig can confirm ready and name the next project in one reply. There is no separate page-signal call — =notify= is the paging surface. * Metrics |
