aboutsummaryrefslogtreecommitdiff
path: root/claude-templates
Commit message (Collapse)AuthorAgeFilesLines
* feat(tags): hard :solo:/:quick: definitions + mandatory review/audit assessmentCraig Jennings10 days2-3/+5
| | | | | | Phase 0 of the autonomous-batch (speedrun) build. todo-format.md now carries fixed cross-project definitions: :solo: is the autonomy/eligibility tag (buildable, agent-verifiable, no design deliberation — at most one or two quick upfront-answerable decisions, which the speedrun pre-flight Q&A batches), and :quick: is a ≤30-minute effort hint that never gates eligibility. task-review and task-audit now treat the tag assessment as mandatory — a pass that skips it is incomplete. task-review's :solo: gate 3 also moves from "no upfront decision" to the no-deliberation form: the stricter wording predated the pre-flight Q&A decision and would have wrongly excluded tasks with a quick answerable question.
* feat(spec-review): add operational-panel UI-traps dimensionCraig Jennings10 days1-0/+1
| | | | | | Six checks for specs covering a user-facing panel or control surface: name each list item's source, don't gate immediately-showable data behind a scan, don't ask users to retype discovered values, tense-correct destructive confirmations, review diagnostics/repair affordances as one flow, and make popups visually belong to their launcher. Conditional — non-UI specs skip it. Promoted from archsetup's Waybar network-panel review (2026-06-30 handoff).
* fix(sweep): recognize anchored /.ai/ style; warn on publicly reachable toolingCraig Jennings10 days1-0/+2
| | | | | | | | sweep-gitignore-tooling.sh decided gitignore-mode with an exact unanchored match on `.ai/`, so a project using the anchored `/.ai/` form was misclassified as track-mode and silently skipped — which left .emacs.d's tracked tooling on a public GitHub mirror until its 2026-06-30 scrub. Both forms now count for mode detection and per-pattern presence, and appended lines follow whichever style the file already uses. Track-mode projects also get a new check: tracked tooling paths combined with a non-cjennings.net remote draw a loud WARN, since a track-mode repo on a public host is the exposure the convention exists to prevent. The convention itself is now written down in protocols.org: a non-cjennings.net remote means the tooling set is gitignored, a deliberate team-shared config being the only explicit exception, and a private remote is not proof of privacy because a server-side mirror hook republishes invisibly. From the .emacs.d handoff (2026-06-30 tooling-exposure broadcast).
* feat(task-audit): add Phase C.6 — retire completed parents, promote stragglersCraig Jennings10 days1-0/+19
| | | | | | A parent whose child tasks are all resolved closes (DONE/CANCELLED + CLOSED) and archives on the next --archive-done; a parent with one or two open children gets those promoted to standalone level-2 tasks first. Carries the leaf-with-notes carve-out (a task whose only descendant is a dated design note is unstarted work, not a finished container — flag NEEDS-USER, never close) and a warning to verify open-child counts against a real subtree scan, not a fragile regex. Depends on --convert-subtasks running first so the counts are accurate. From the .emacs.d handoff (2026-07-01 task-audit note).
* feat(todo-cleanup): add --convert-subtasks dated-rewrite modeCraig Jennings10 days8-11/+457
| | | | | | | | Rewrites every level-3+ DONE/CANCELLED/FAILED heading into a dated event-log entry from its CLOSED cookie, enforcing the todo-format depth rule that interactive closes and --archive-done (level-2 only) leave unapplied. A new lint-org checker (subtask-done-not-dated) flags stragglers, and the clean-todo, wrap-up, open-tasks, and task-review workflows now run the converter before archiving. Removing the CLOSED cookie keeps a DEADLINE or SCHEDULED cookie that shares its planning line, rather than dropping the whole line. From the .emacs.d handoff (2026-07-01 convert-subtasks bundle).
* feat(lint-org): add four structural heading checkers org-lint missesCraig Jennings12 days2-0/+191
| | | | | | org-lint validates links, drawers, blocks, and babel, but not heading well-formedness. These four catch hand-edit defects it stays silent on: an indented heading demoted to body text (the task vanishes from the agenda and never archives), bare stars with no title, a malformed priority cookie org rejected, and a level-2 DONE/CANCELLED with no CLOSED line. All judgment-only and regex-based, wired in after the existing dated-header check. The last one pairs with the new aging step, which archives an undated completed task immediately. I tightened the indented-heading check to two-or-more stars. The proposed one-or-more-stars regex flagged indented single-star lines, but an indented single * is a valid plain-list bullet, not a lost heading, so it false-positived on legitimate lists (confirmed: three valid bullets flagged). A ** is never a bullet, so an indented one is unambiguously a demoted heading. Added a test that a single-star list stays silent.
* feat(todo-cleanup): age the Resolved section out to a tracked archiveCraig Jennings12 days2-14/+429
| | | | | | A project's in-file Resolved section grew without bound: --archive-done only moved closures from Open Work into Resolved, same file. .emacs.d's todo.org reached 768KB that way. So --archive-done now has a second step: a Resolved subtree closed longer ago than tc-archive-retain-days (default 7), or with no parseable CLOSED date, moves out to archive/task-archive.org beside the todo file. Only the last week of closures stays in the file. The step honors --check, and tc-archive-retain-days nil disables it. I added a guard the proposal lacked: the archive inherits the todo file's gitignore status. gitignore-mode projects gitignore todo.org, so without this the aging step would shed previously-private task history into a tracked archive, a leak on any public repo. When the todo file is gitignored and the archive isn't, the script adds the archive path to .gitignore before the first write. Track-mode projects leave both tracked. Covered by two tests in a temp git repo, one per branch.
* chore: prune dangling bin symlinks, point paging at signal-mcpCraig Jennings12 days1-0/+9
| | | | | | The install bin loop linked every script under claude-templates/bin but never pruned orphans, so a deleted script left a dangling ~/.local/bin symlink. page-signal hit exactly this: the wrapper was removed 2026-06-12 but its symlink stayed, resolving to a dead target on the daily drivers. I added a prune step that drops symlinks pointing into claude-templates/bin whose target is gone, so any removed script self-cleans on the next install. I also added a protocols section naming the two paging channels: notify --persist at the machine, and the signal-mcp send_message_to_user tool when away from it. The pager account was never deregistered (only the page-signal script went away), so the live path is the MCP tool, and the section retires the script for good.
* feat(scripts): add wrap-up routing recommendation engineCraig Jennings14 days2-0/+260
| | | | I added route_recommend.py, a pure recommend(item, projects) → (destination, confidence). It has strong, weak, and none tiers, word-boundary literal matching that also handles dot-stripped name aliases, and a deterministic tie-break that downgrades an ambiguous top-tier tie to weak. An empty candidate list yields none. The CLI reuses inbox-send's discover_projects, so the candidate set is the same project universe inbox-send already knows. This covers Phases 1 and 3 of the wrap-up routing spec. The marker and router sub-tasks call it next.
* feat(workflows): add code-quality sweep workflowCraig Jennings14 days2-0/+85
| | | | A thin orchestrator that runs the behavior-preserving quality passes over a scope of existing code in order: /refactor, then readability-audit, then it surfaces the :refactor: tasks readability filed and any deferred /refactor findings. It leaves /simplify out, since that works the current diff rather than existing code.
* feat(inbox-send): resolve dot-stripped project namesCraig Jennings14 days2-3/+66
| | | | .emacs.d resolves as emacsd and .dotfiles as dotfiles, in both inbox-send and the launch trigger. An exact basename match still wins, and --list shows the stripped name. triggers.md documents the same resolution so the spoken name is consistent across both.
* feat(workflows): add suspend and readability-audit workflowsCraig Jennings14 days4-0/+365
| | | | | | suspend is a capture-only mid-session pause for an abrupt departure: it writes a resume-weighted entry and leaves the session anchor in place, so the next startup resumes from it. It's the capture half. Startup is the resume half. I registered it with its trigger phrases. readability-audit is a language-agnostic pass over comments, file headers, names, and organization. The cheap comment and name fixes land inline. Structural findings get filed as :refactor: tasks. It feeds /refactor rather than duplicating it.
* refactor(tasks): use a :blocker: tag, not a :BLOCKS: propertyCraig Jennings2026-06-242-9/+9
| | | | :BLOCKS: rulesets: was a malformed org tag, and the property form (:BLOCKED_BY: / :BLOCKS: carrying <project>: <what>) was more structure than the dependency needs. The blocking side now carries a plain :blocker: tag, mirroring :blocked: on the waiting side, with the which-project detail in the task body rather than a property. open-tasks.org reads the body for the blocking/requesting project; the scheme, the todo-format convention, and the inbox blocking-dependency handoff all move to the two-tag form. No property anywhere.
* feat(tasks): make cross-project dependencies bidirectionalCraig Jennings2026-06-242-0/+11
| | | | | | | | The :blocked: tag only marked the waiting side, so a blocker could stay unaware it was holding up another project: the dependency was visible to the one project that couldn't act on it and invisible to the one that could. This closes that gap. Setting :blocked: now requires a reciprocal inbox-send to the blocker, which files the work with a :BLOCKS: <project>: <what> property on its side. open-tasks.org surfaces :BLOCKS: tasks first, since clearing one unblocks another project (the highest-leverage pick), the mirror of pulling :blocked: tasks out of the cascade. Inbox process mode recognizes the blocking-dependency handoff shape, and the convention documents the resolution flow (drop :BLOCKS:, notify the waiter, who lifts :blocked:). This works for any project pair, since the convention (todo-format.md) and the surfacing (open-tasks.org) live in the shared rule and workflow layer, not in one project. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* fix(flashcard): name the Anki deck from #+TITLE, not the filenameCraig Jennings2026-06-242-12/+45
| | | | | | | | | | flashcard-to-anki.py's default_deck_name returned the input basename, so a deck built through flashcard-sync (which passes no --deck) was named after the file slug (refutation-drill) instead of the curated #+TITLE the phone deck should read (Refutations). flashcard-review.org already documented the #+TITLE behavior, and the script never matched it. default_deck_name now scans for a #+TITLE line (case-insensitive, trimmed) and falls back to the basename when there's none. Five new tests cover title-drives-name, trimming, case-insensitivity, and the two basename fallbacks. The two old tests that asserted basename-always are replaced. The pre-staged script and test (validated 2026-06-21) applied cleanly red-to-green; their redundant copies are removed and the rationale doc kept. Migration: deck IDs derive from the name, so decks previously built without --deck land as new decks on the next import. Old basename-named decks keep their history and can be deleted by hand. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(task-audit): consolidate related tasks into a merge or parentCraig Jennings2026-06-241-0/+15
| | | | | | Reconciliation keeps each task honest on its own, but a single effort fragmented across several tasks (the agent-agnostic work, say) is hard to see and finish as a whole. Adds Phase C.5: read the open-task set, spot semantic clusters by judgment rather than a brittle keyword match, and propose per cluster either a merge (fold same-work members into one) or a parent-with-children grouping (related-but-distinct members). Proposes, never applies, until Craig picks; broader than Phase C's exact-duplicate fold. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(tasks): surface cross-project dependencies in what's-nextCraig Jennings2026-06-241-1/+15
| | | | | | A task can be blocked by work owed by another project, and today it keeps getting recommended as "next" even though it can't move, while the blocker sits at low priority over there and the dependency stalls silently. Adds a :blocked: tag plus a :BLOCKED_BY: <project>: <what> property (todo-format.md) to mark the dependency, and teaches open-tasks.org Next Mode to pull :blocked: tasks out of the cascade pick and surface them in a dedicated "Blocked on other projects" section that names each blocker and offers an inbox-send nudge. Distinct from VERIFY, which waits on Craig rather than another project. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(inbox): poll-and-retry the capture-guard instead of bouncingCraig Jennings2026-06-243-24/+96
| | | | | | | | | | When a roam edit hits a live org-capture, the guard used to bounce the caller right away (surface to the user, or skip the cycle) even though the capture is usually a few seconds of mid-finalize that clears on its own. capture-guard gets a --wait poll mode: it re-checks every ~10s up to a budget (default 30s, each sleep capped so a short --wait never overshoots), returns the instant the capture clears, and reports blocked only at the deadline. The no-capture common case still returns instantly without sleeping. Roam mode now uses --wait on every write, and the per-caller fallback fires only after the wait: an interactive run surfaces, the auto /loop defers to the next cycle (the loop cadence is the retry), wrap-up skips and self-heals. Surfaced live this session: a transient capture blocked a roam reconcile and had cleared a minute later. Covered by three new bats cases (instant-when-safe, timeout-when-blocked, target-after-flag). Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(wrap): end the valediction with a "session wrapped." signoffCraig Jennings2026-06-241-1/+5
| | | | | | The wrap-up valediction now always ends with "session wrapped." on its own line — a consistent, unmistakable end-of-session marker, since it's the last user-facing output before Step 6's silent teardown. Craig's request via the roam inbox. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* fix(inbox): stop pulling the roam repo during triageCraig Jennings2026-06-242-11/+18
| | | | | | | | | | The roam repo's working tree is dirty most of the time (Craig captures into it constantly, and roam-sync only commits every 15 minutes), so roam mode's pull --ff-only failed on nearly every run and blocked triage. The auto inbox zero loop hit it every cycle. Roam mode now never pulls. The scan reads the working-tree file directly, since that's already the latest local state, and the rare write removes the claimed items in place and then triggers roam-sync to commit and push. roam-sync already commits-first-then-rebases, so it handles the dirty tree, and the ownership partition (only this project touches its own prefixed lines) means its rebase can't conflict on the edit. Trade-off: the roam-repo commit carries roam-sync's generic auto-sync message instead of a descriptive one. The provenance for routed tasks lives in the project's todo.org and session log anyway. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(wrap): add session teardown and shutdown to wrap-it-upCraig Jennings2026-06-232-2/+56
| | | | | | | | | | | | A bare "wrap it up" now tears the session down after the valediction: it kills the ai-term buffer and the aiv-<project> tmux session (which takes claude with it) and restores geometry. "wrap it up with summary" or "and summarize" keeps the buffer. "wrap it up and shutdown" gates on this being the only live ai-term session, then powers the machine off through an abort-able Emacs countdown. Teardown can't run inline because it kills the session claude runs in, so the valediction would never flush. Step 6 instead drops a basename-keyed sentinel after commit+push is verified, and a new Stop hook (ai-wrap-teardown.sh) does the teardown when the response ends, by which point the valediction has rendered. The hook is a no-op on every normal stop because the sentinel only exists after a teardown wrap. The runtime lives in .emacs.d/modules/ai-term.el (cj/ai-term-quit, cj/ai-term-live-count, cj/ai-term-shutdown-countdown), and the rulesets side calls it via emacsclient. I routed that companion to .emacs.d, so the feature is end-to-end once it lands. The hook has 8 bats tests. The live teardown and shutdown paths are a manual checklist in todo.org. Built from the proposal. I went with both summary qualifiers, the Emacs-timer countdown, and the live-count gate. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(inbox): consolidate three inbox workflows into one engineCraig Jennings2026-06-2313-486/+507
| | | | | | | | | | | | I merged process-inbox, monitor-inbox, and inbox-zero into one inbox.org engine. A shared core (value gate, skeptical review, disposition ladder, reply discipline, capture-guard, priority-scheme check) holds the logic that used to be duplicated and cross-referenced across the three files. Each mode (process, monitor, roam) references the core by name instead of restating it. Every trigger phrase still works, now routing to a mode, so there's nothing to relearn. I added the interactive auto inbox zero mode: ask for an interval, run roam mode on /loop, acknowledge-only on an empty cycle, surface a find to a queue gated on a yes. The fully-unattended /schedule pass stays vNext, tracked separately. I repointed every live caller (INDEX, protocols, startup Phase C, wrap-up Step 3, triage-intake, broadcast) at inbox.org and its modes, then deleted the three old files. triage-intake and no-approvals stay separate by design. The value gate, dispositions, capture-guard, and reply discipline all behave as before. Built from the Ready spec. Workflow-integrity and sync-check pass on both the canonical and mirror trees, the stale-reference grep is clean, and the full suite is green. Claude-Session: https://claude.ai/code/session_017PtX1nt1rtYVATuzmzBS4f
* feat(inbox-zero): sweep empty roam entries on triageCraig Jennings2026-06-231-7/+8
| | | | | | An aborted org-capture can leave a heading with no title and no body (just stars and maybe a TODO keyword). Those belong to nobody and were piling up in the shared roam inbox. inbox-zero now buckets them as empty in Phase B and removes them in Phase D in the same edit as the claimed items. An empties-only run still enters Phase D and commits, so the sweep runs on every triage, not only when this project owns something. A heading with any title or body is never touched.
* feat(inbox-zero): guard roam-inbox writes against live org-captureCraig Jennings2026-06-233-3/+166
| | | | | | | | | | Editing the roam inbox on disk while Emacs has an indirect org-capture buffer cloned from it reverts the base buffer under the capture: the capture can't finalize with C-c C-c, and a freshly-typed item can be lost. inbox-zero Phase D edits that file, which Craig captures into constantly, so the collision recurs every session. I added a capture-guard helper that asks the running daemon whether any CAPTURE buffer's base buffer visits a given file (file-equal-p, so symlinks and path spelling don't matter), exiting non-zero with the names when so. No reachable Emacs or no capture means exit 0, so it never blocks a write that was safe. Phase D calls it before the pull, not only before the remove, because the ff-only pull also rewrites the file on disk and would wedge a capture the same way. On a collision an on-demand run stops and asks Craig to finalize or abort. The wrap-up sub-step skips the roam reconcile without blocking the wrap, since the items are already filed and the next run reclaims them. emacs.md gains the inverse of the reload rule: don't yank a file out from under the daemon's live buffers.
* docs: extend commit rules to cover tooling-path enumerationCraig Jennings2026-06-221-1/+1
| | | | The no-attribution rules covered AI credit but not incidental mentions of tooling filenames in commit prose. The case that bit: a .gitignore commit naming .claude, CLAUDE.md, and .ai in its message leaks the tooling layer into a public log. Adds a tooling-path-enumeration ban with that gitignore case named, extends the Before-Committing scan, and adds the missing paths to the protocols keep-out list. Both carry the file-is-the-change and private-single-user-repo exemptions, so a rule edit or a rulesets commit can still name what it touched.
* refactor: fold spec review findings into the spec itselfCraig Jennings2026-06-214-100/+73
| | | | | | | | The spec-review/spec-response pair wrote findings to a sibling <spec>-review.org file that spec-response deleted once processed. The deletion left the iteration-history Artifacts line dangling and dropped the verbatim review. Keeping the file instead collided with spec-response's file discovery and its "no review files remain" done-condition. Findings now live in the spec under a * Review findings section as TODO tasks with a [/] cookie, the same shape * Decisions already uses. The reviewer records findings there. The responder completes each in place (accept and modify finish DONE, reject finishes CANCELLED with the reason), and the readiness rubric gates on the cookie. A scope-expanding response re-runs the rubric and files any new obligation as a finding or decision before claiming Ready, because resolving every finding can still introduce unreviewed assumptions. Also folds in two reviewer-practice principles: keep review and response roles explicit, and cite the source for external-dependency facts in a finding. Updates spec-create.org and the workflow INDEX so the trio describes one convention.
* docs: have inbox-zero check project handoffsCraig Jennings2026-06-211-10/+22
|
* feat(lint-org): flag level-2 dated headers as a completion defectCraig Jennings2026-06-202-0/+53
| | | | A `** <YYYY-MM-DD> …` heading carries no keyword, so todo-cleanup's --archive-done can never archive it and task-review drops it from selection. The new level-2-dated-header check (custom, like org-table-standard) emits a judgment item per offending heading so the wrap-up sweep routes it to the next morning's review. Judgment-only, never auto-fixed: the repair needs a DONE-vs-CANCELLED call and the original heading text. Three ERT cases cover it (flagged at level 2, clean for DONE+CLOSED, clean for a level-3 dated entry).
* feat(kb): wire consult + contribute KB prompts into the workflowsCraig Jennings2026-06-204-0/+26
| | | | Recent session receipts read "promoted 0 / consulted no" across the board: the wrap-up KB-promotion check existed but fired too late, and nothing surfaced existing lessons to read. This adds the spec's four light prompts plus the read-side step it was missing. Startup gets two Phase C nudges (gated on the roam clone): a consult line listing project-relevant node titles, and a contribute line pointing at the best-practices node. Triage-intake and inbox-zero get a conditional end-of-flow capture reminder that fires only on real signal. Wrap-up gets an early reflection prompt at the top of Step 1 that feeds the existing receipt, so learnings are captured while fresh instead of reconstructed after the Summary. Ratifies the spec's five decisions and adds D6 (the read-side surfacing).
* docs: level-2 VERIFY completes task-shaped, not as a dated headerCraig Jennings2026-06-201-1/+1
| | | | The old rule dated a resolved VERIFY at every depth, including the top level. A level-2 dated header carries no keyword, so todo-cleanup's --archive-done can never archive it and task-review drops it from selection. Now a top-level VERIFY closes like any other top-level task (DONE/CANCELLED + CLOSED:), and dated rewrites are reserved for level 3 and deeper. Updated the rule and the three places that encoded the old behavior: todo-format.md, respond-to-cj-comments.md, and process-inbox.org. Also repaired two pre-existing level-2 dated headers.
* feat(ai): discover ~/.dotfiles in the launcher pickerCraig Jennings2026-06-201-0/+1
| | | | build_candidates() only scanned ~/.emacs.d, ~/code/*, and ~/projects/*, so ~/.dotfiles (a bootstrapped AI project living directly in $HOME) never showed in the launcher picker. I added it as an explicit candidate next to ~/.emacs.d. The maybe_add_candidate guard keys off .ai/protocols.org, so the line stays inert where ~/.dotfiles isn't bootstrapped.
* refactor: remove unused cross-agent-comms subsystemCraig Jennings2026-06-1624-3958/+8
| | | | Nothing used the cross-agent message system (send/recv/watch/status/discover/halt/resume over the inbox/from-agents/ file-IPC protocol). Every cross-project handoff goes through inbox-send instead. I removed the scripts, READMEs, workflow, tests, INDEX entry, the three startup.org wirings, and the legacy bin symlinks, then repointed helper-mode's escalation to inbox-send and noted the removal in the generic-agent-runtime spec.
* feat(triage): add a per-sweep timestamp to auto mode outputCraig Jennings2026-06-151-2/+7
| | | | | | Auto-mode sweeps now end with the date, time, and timezone on their own final line, so an away reader gauges each summary's freshness at a glance without computing it. The stamp prints on every sweep, including a quiet "no changes" one — there the stamp is the proof the loop ran. Same-day addendum to the work-project auto-mode proposal.
* feat(triage): add auto mode for unattended monitoringCraig Jennings2026-06-151-0/+78
| | | | | | | | | | Add an auto mode to the triage-intake engine: a self-running variant for when Craig is away but wants tight awareness. It runs the standard sweep on a short interval (default 20 min) as an in-session loop, accumulates findings instead of mutating state, and gates the mutations behind "close the triage" (flush the batch, keep looping) and "stop the triage" (flush, then stop). A sweep advances nothing — no sentinel write, no todos, no mail actions, no commit. The scan window grows from the last close to the next, so nothing between sweeps is dropped, and the sentinel still means "everything before this timestamp has been scanned" — it just advances once per close. Each sweep reports deltas plus a running "responses awaiting your acknowledgment" list, the primitive an away user needs that a delta-only sweep loses. The unacked list is durable in .ai/triage-intake-unacked.org so it survives a crash, a clear, or a restart — the away-from-desk case the mode exists for. Delivery is an in-session loop so MCP auth is inherited; a detached cron schedule stays out of scope and belongs to the morning-ops orchestrator, which can reuse this accumulate behavior as its triage limb. Source proposal from the work project, design decisions ratified 2026-06-15.
* feat(inbox): define monitor-inbox as a 15-min loop with clean-tree gatesCraig Jennings2026-06-153-25/+58
| | | | | | | | | | Redefine "monitor the inbox" as the explicit behavior Craig wants: run one process-inbox pass now, then loop process-inbox every 15 minutes. The 15-minute loop was previously an opt-in background recipe; it's now the defined meaning of the phrase. Gate the workflow at both ends on a clean worktree and a green full-suite run. Starting on a dirty tree lets the per-item auto-commit sweep up unrelated changes; starting on a red suite hides whether the monitor broke anything. On a dirty tree, offer to commit in discrete batches; on a red suite, offer to investigate — never start until both are satisfied, and leave the tree clean and green when the loop stops. Add the no-approvals execute criteria: an accepted item self-applies only when agreed (passed the value gate and Skeptical Review), quick (under ~15 min including verification), and solo (no decision needed from Craig). All three commit and push at the end of the item; miss any and it files or, for shared-asset and convention changes, parks. Broaden the Skeptical Review to run on every arriving task and file, not only shared-asset proposals — a core right/complete/simpler pass on everything, with the cross-project battery added for changes that sync to consuming projects.
* feat(ai): add helper-mode workflow contractCraig Jennings2026-06-153-0/+104
| | | | | | | | helper-mode.org is the canonical home of the helper-instance rules: a second Claude alongside a live primary in the same project. It defines the four read/write tiers (always-safe reads and own-context writes, safe-by-discipline scoped single-heading edits, primary-only file-wide passes and all git mutation, escalation), the four data-integrity windows, the light startup, and the helper wrap-up (archive own context file, skip commit, with the git ban lifting only for an orphaned helper that ends up alone). protocols.org gets a one-paragraph pointer, and INDEX.org gets a triggerless catalog entry like startup.org, so the no-trigger workflow clears the integrity check without a special case. The contract is the canonical home. The routing that sends a session here (ai --helper, startup's roster branch, the wrap-up helper branch) ships behind the feature's drill gate and isn't live yet. Until then a session adopts it by an explicit "you are a helper" instruction.
* feat(ai): add agent-roster detection script with testsCraig Jennings2026-06-152-0/+225
| | | | | | | | agent-roster is the single detection primitive for concurrent same-project Claude sessions: pgrep -x claude, resolve each pid's cwd from /proc, keep those at or inside the project root, and drop the scanner's own ancestry. It exits 0 when alone, 1 when other agents are present (printed pid + cwd), and 2 when the roster can't run. Both the helper launcher and the in-session startup check will call this rather than re-scanning. pgrep and /proc are the system boundary, so I made them injectable (ROSTER_PGREP, ROSTER_PROC, ROSTER_SELF_PID) and the bats exercise the real include/exclude logic against fixtures, no agents spawned. The unavailable paths (no /proc, or pgrep absent) report on stderr and exit 2 rather than a false "alone". This is the first slice of the helper-instance task. Startup and ai --helper wiring follow.
* docs(ai): require an epoch on the tail of helper-agent idsCraig Jennings2026-06-143-1/+13
| | | | | | A helper agent's session-context file is .ai/session-context.d/<id>.org. A bare, reused id like "codex" makes the next run resolve to the previous run's leftover anchor, which it then mistakes for a crash to recover or clobbers. That bit on 2026-06-13: a codex run left codex.org for the next session to clean up. The fix is a convention, not a resolver change. The spawner appends an epoch on the tail (host.project.runtime.<epoch>) so each run gets a fresh anchor. The epoch can't be minted inside session-context-path, since that resolver runs many times per session and must return the same path each call. I documented it in protocols.org, the wrap-up recommended-shape note, and the resolver header.
* feat(ai): disable C-z suspend in launched Claude panesCraig Jennings2026-06-141-3/+10
| | | | An accidental C-z suspended Claude to the shell mid-session. C-z isn't a Claude Code keybinding. It's the tty's SIGTSTP char, delivered below the app, so the only place to clear it is the tty. The launcher now runs stty susp undef in each pane right before claude starts, so it's scoped to ai-launched panes. C-z keeps working as job control in every other terminal, shell, and program.
* feat(workflows): add inbox-zero for routing the roam inbox by projectCraig Jennings2026-06-134-0/+111
| | | | | | | | The global roam inbox (~/org/roam/inbox.org) is one shared capture file every project can see, and nothing routed its items to the project that owns them. inbox-zero claims the items prefixed for the current project, files them into that project's todo.org per the process-inbox discipline, and removes them from the shared inbox. Foreign-prefixed and unowned items stay. Every scan reports the total item count plus how many appear related to this project. This v1 is single-destination: it routes by explicit <project>: prefix only. The domain-aware mode that would guess every item's owner and empty the whole inbox in one run is deferred until the multi-project need is concrete. Wired into both session ends so each project touches the inbox twice a session: startup surfaces a read-only count and offer, wrap-up Step 3 sweeps the claimed set before the cleanup scripts so imported tasks ride the wrap commit. INDEX carries the trigger phrases.
* feat(workflows): skeptical review gate for inbox change proposalsCraig Jennings2026-06-123-22/+81
| | | | | | | | The value gate asks whether to take an inbox item, never whether the proposed change is right. process-inbox gains a Skeptical Review for proposals that change shared assets: a written question battery (fit for all consumers, conflicts elsewhere, effect on common activities, enhancement, simplification, plus at least three change-specific questions), ending in a summary and recommendation Craig approves before the change lands. In a no-approvals session, behavior-changing proposals park instead of self-applying: prepared diff in working/, a [#B] VERIFY carrying the decision package, a reply to the sender. Wording-only fixes proceed, logged. monitor-inbox's act-vs-file rule and protocols.org's act-now line gain the matching exception so all three statements of the rule agree. protocols.org's tables picked up the org-table-standard reflow in the same pass. The motivating case is today's spec-decisions handoff. I applied it as-is, and the after-the-fact review surfaced a lost state and a vacuous gate pass the battery would have caught up front.
* feat(workflows): SUPERSEDED/CANCELLED decision states + old-model gateCraig Jennings2026-06-122-4/+10
| | | | | | A superseded decision now flips to SUPERSEDED (linking its replacement) and a moot one to CANCELLED. Both are done-class via a #+TODO: header the spec template auto-adds, so the [/] cookie counts them resolved and neither blocks implementation-ready. The TODO/DONE pair alone had lost the old State: field's superseded value. spec-review's gate and Ready rubric now read "no decision is still TODO", and a spec still on the retired State: field model fails the gate item until converted. The gate as first written would have vacuously passed old specs, which have no decision tasks at all.
* feat(workflows): spec decisions become org TODO/DONE tasksCraig Jennings2026-06-123-15/+31
| | | | | | Each spec decision is now an org TODO task that flips to DONE when the decision-maker agrees, with a [/] cookie on the Decisions heading and a Discussion child for disputes. This replaces the inline State: proposed | accepted | superseded field. spec-response folds settled decisions by flipping them to DONE. spec-review's readiness gate and Ready rubric require the cookie to read complete. A spec can't move past draft to implementation-ready while any decision is still TODO. From the .emacs.d handoff 2026-06-12.
* chore: drop the Signal triage-intake pluginCraig Jennings2026-06-122-118/+2
| | | | Remove the triage-intake Signal source plugin and de-list Signal from the engine's plugin enumeration. I'm rebuilding the Signal client (signel + signal-cli) from scratch, so the plugin would scan against an unstable client. The signal MCP server and its README setup stay. Re-add the plugin when the client is stable.
* chore: delete the page-signal pager wrapperCraig Jennings2026-06-124-220/+1
| | | | Remove the page-signal CLI wrapper, its workflow, and the references in INDEX.org, broadcast.org, and mcp/README.org. The signal MCP server stays. It's the two-way path and a separate capability. The pager number had deregistered and the send-only wrapper isn't worth re-registering.
* fix(todo-cleanup): keep --archive-done silent on a real-mode no-opCraig Jennings2026-06-122-14/+80
| | | | The wrap runs --archive-done twice (wrap-it-up, then open-tasks.org Phase A). The first pass archives and reports. The second finds nothing and used to print "0 subtree(s) moved", which reads as alarming next to the first pass's diff. Now a real-mode run that moves nothing and skips nothing says nothing. Check mode still previews "0 would move", and a missing-section skip still reports, since those are conditions the caller needs.
* docs(task-audit): add tag-vocabulary enforcement and verify-then-closeCraig Jennings2026-06-121-0/+14
| | | | Two Phase C behaviors, both surfaced auditing an Emacs-config todo.org. Enforce a project's declared closed tag set (strip tags outside it) where the legend marks the set exhaustive, leaving open-vocabulary projects untouched. For a task whose code shipped but awaits a manual or visual check, file that check under the project's manual-testing parent (dedup first) and close the implementation task, rather than letting "done but unverified" linger half-open.
* fix(scripts): lint-org pre-registers runtime org link typesCraig Jennings2026-06-122-0/+37
| | | | mu4e registers its link type in a live Emacs, so batch org-lint parsed [[mu4e:msgid:...]] links as fuzzy heading refs and flagged "Unknown fuzzy location" on links that work interactively. lint-org now registers each type in lo-runtime-link-types as a no-op before linting. org-link-set-parameters merges rather than replaces, so a genuinely loaded mu4e keeps its real parameters.
* feat(triage): deltas-only sweep summaries and silent telegram dev groupsCraig Jennings2026-06-112-11/+37
| | | | | | A sweep now reports only what changed: a new invite, a moved or cancelled event, a message needing attention. Unchanged sources get no block. An all-quiet sweep renders as one line. Scan failures keep their loud banner and the suggested-actions line stays when actions are queued. Telegram dev-community group traffic (zed, GNU Emacs, Kitty) is dropped from sweep reports entirely unless Craig asks. Real DMs from known contacts still surface as Action.
* feat(workflows): session-harvest monthly promotion-mining passCraig Jennings2026-06-112-0/+106
| | | | | | session-harvest runs a monthly pass over recent session summaries across every AI project and proposes promotion candidates in four lanes: patterns catalog, KB facts, rule refinements, workflow learnings. Capture already happens continuously. This adds the batched review cadence that turns it into curated promotion. The window filter reads each session filename's date prefix instead of mtime. Clones and syncs reset mtime, which let 2025 sessions pass a recency filter. The run also aggregates the KB receipt lines from session summaries, so it doubles as the 30-day KB metrics readout.