diff options
Diffstat (limited to '.claude/rules')
| -rw-r--r-- | .claude/rules/cross-project.md | 37 | ||||
| -rw-r--r-- | .claude/rules/daily-drivers.md | 49 | ||||
| -rw-r--r-- | .claude/rules/emacs.md | 6 | ||||
| -rw-r--r-- | .claude/rules/interaction.md | 6 | ||||
| -rw-r--r-- | .claude/rules/knowledge-base.md | 60 | ||||
| -rw-r--r-- | .claude/rules/org-tables.md | 60 | ||||
| -rw-r--r-- | .claude/rules/patterns.md | 29 | ||||
| -rw-r--r-- | .claude/rules/todo-format.md | 131 | ||||
| -rw-r--r-- | .claude/rules/triggers.md | 6 | ||||
| -rw-r--r-- | .claude/rules/working-files.md | 2 |
10 files changed, 360 insertions, 26 deletions
diff --git a/.claude/rules/cross-project.md b/.claude/rules/cross-project.md index 50bc34e26..caceec9bd 100644 --- a/.claude/rules/cross-project.md +++ b/.claude/rules/cross-project.md @@ -35,10 +35,45 @@ Two acceptable outcomes: ``` Output filenames follow `YYYY-MM-DD-HHMM-from-<this-project>-<slug>.<ext>` automatically, so the target's next session sees the source + timestamp at a glance without you having to construct the name. Fall back to `Write`/`Edit` only when the script isn't available (e.g. a freshly-cloned project before the first startup-rsync). -2. **"Switch projects"** — stop. Let the user reopen Claude in the right cwd. +2. **"Switch projects"** — stop. Let the user reopen the agent session in the right cwd. Don't assume which one was meant. Either guess is wrong half the time and the cost of asking once is one short turn. +## Changing a Rulesets-Owned Synced File from a Downstream Project + +Some files in every project are owned by rulesets and overwritten by the +template sync at each session start: workflows under `.ai/workflows/`, scripts +under `.ai/scripts/`, rules under `.claude/rules/`, `protocols.org` — anything +whose canonical home is `~/code/rulesets/`. When work in a downstream project +needs one of these files to change, a local edit alone is a stopgap that the +next sync reverts. The durable change happens only in the rulesets canonical. + +The process, every time: + +1. **Make the change locally** in the downstream project so it's usable + immediately. +2. **Send rulesets a copy** of the edited file: + `inbox-send rulesets --file <edited-file>`. +3. **Include an intro note** (a second `inbox-send rulesets --text` or + `--file`) covering what changed, why, and any companion files that need + reconciling, so the rulesets session can update the canonical and re-sync + without re-deriving the intent. + +Don't wait for the user to spell these steps out — recognizing that an edit +targets a synced file and propagating it is the agent's job. The rulesets +session applies its own value gate on arrival, so sending is a proposal, not +a bypass. + +This doesn't conflict with the stop-and-ask rule at the top of this file: +ask-first governs doing work inside another project's scope. Dropping a +proposal in its inbox is the sanctioned alternative to that, so a proactive +inbox-send needs no confirmation. + +Worked example: the 2026-06-12 `spec-create.org` decisions-as-TODO change — +`.emacs.d` edited its local copy as a stopgap, sent the edited file plus an +intro note naming the two companion workflows to reconcile, and rulesets +updated the canonical the same evening. + ## Recovery When It Goes Wrong If you do the work first and the boundary issue surfaces afterwards: diff --git a/.claude/rules/daily-drivers.md b/.claude/rules/daily-drivers.md new file mode 100644 index 000000000..eeda33fd5 --- /dev/null +++ b/.claude/rules/daily-drivers.md @@ -0,0 +1,49 @@ +# Daily-Driver Machines + +Applies to: `**/*` + +Craig runs exactly two daily-driver machines: **ratio** and **velox**. They are +kept in sync, and an important change made on one usually needs to reach the +other. + +## The Rule + +When you make or notice a change that is **machine-level and important** — +dotfiles, installed tooling, a synced repo's clone or timer setup, a global +config, a systemd unit, a credential, a one-time bootstrap step — consider +whether the *other* daily driver needs the same change, and flag it. Don't +assume a change made on the current machine is live everywhere. + +This is a prompt to think, not a script to run. The agent can't reach the other +machine; the point is to surface "the other daily driver may need this too" at +the moment the change lands, so it doesn't silently drift to one box. + +## How the sync actually happens + +The mechanism depends on what changed: + +- **A tracked repo** (rulesets, dotfiles, a project) — the other machine just + needs a `git pull` (and, for rulesets, a `make install` to relink anything + new). Most changes are this. +- **Dotfiles** — ride the dotfiles repo; the other machine picks them up on its + next stow/pull. +- **A one-time setup** — a new repo clone, a new systemd timer, a freshly + installed tool, a credential — has to be done by hand on each machine. These + are the ones that silently drift, because nothing carries them automatically. + +When the change is the one-time kind, say so explicitly: name the manual step +the other machine still needs. + +## Knowing which machine you're on + +`uname -n` returns the hostname (`ratio` or `velox`). Use it when a reminder is +machine-specific ("on ratio, you still need to …") so the note is actionable +rather than abstract. + +## Current open instance + +The org-roam knowledge-base clone — `git@cjennings.net:roam.git` — plus its +`roam-sync` systemd timer is confirmed set up on **velox**. It still needs +verifying (clone + timer) on **ratio**. This is the last piece before the +"memory sync across machines" work closes (tracked in the rulesets `todo.org`). +Clear this line once ratio is confirmed. diff --git a/.claude/rules/emacs.md b/.claude/rules/emacs.md index 702b40e7b..ae4f7cb2b 100644 --- a/.claude/rules/emacs.md +++ b/.claude/rules/emacs.md @@ -27,3 +27,9 @@ This re-evaluates the file and redefines its `defun`s live. For straight functio 3. Verify: for visual changes, screenshot and read it (the `screenshot.py` tool under `.ai/scripts/` can capture an app off-screen on a headless output); for behavior, eval or exercise it. This replaces the quit → relaunch → re-find-and-load-files cycle for most edits. A real restart stays the gold standard for a guaranteed-clean state — anything touching `:config`, load order, or when in doubt. + +## Don't edit on disk a file the daemon is capturing into + +The reload caveats above are about pushing changes *into* the daemon. The inverse hazard: a tool that edits a file *on disk* while the daemon has an indirect buffer cloned from it. org-capture works through such a buffer, and a disk write (a hand edit, a `git pull` that fast-forwards the file, a `sed`/Write) reverts the base buffer underneath the capture. The capture is left on stale state, can no longer finalize with `C-c C-c`, and a freshly-typed item can be lost or written back against post-edit content. Orphaned `CAPTURE-*` buffers piling up as Craig retries is the visible symptom. + +The roam inbox (`~/org/roam/inbox.org`) is the live case — Craig captures into it constantly, and the inbox workflow's roam mode (Phase D) edits it. Before a disk write to a file the daemon may be capturing into, check first: `.ai/scripts/capture-guard <file>` exits non-zero (and names the buffer) when a live capture is cloned from `<file>`, and exits 0 — safe — when there's no capture or no reachable Emacs. Same principle as the reload rule, one layer out: leave the daemon's live buffers authoritative rather than yanking the file from under them. diff --git a/.claude/rules/interaction.md b/.claude/rules/interaction.md index fa09d6d0f..9148b4ffd 100644 --- a/.claude/rules/interaction.md +++ b/.claude/rules/interaction.md @@ -2,11 +2,11 @@ Applies to: `**/*` -How Claude communicates with the user during a session — choice prompts, status updates, decision points. +How the agent communicates with the user during a session — choice prompts, status updates, decision points. ## No Popup Menus for Choices -When Claude needs the user to pick between options, **do not** use the AskUserQuestion popup. Present the options inline in chat as a numbered list and ask the user to reply with a number. +When the agent needs the user to pick between options, **do not** use the AskUserQuestion popup. Present the options inline in chat as a numbered list and ask the user to reply with a number. **Why:** The popup menu UI sits at the bottom of the chat window and obscures the chat content directly above it — exactly the area the user needs to read to make the choice. Inline numbered options keep the question, the surrounding context, and the proposed text all visible in the same scrollback. @@ -26,6 +26,8 @@ For pick-one decisions, same shape: numbered list, one-line prompt at the end. For multi-select decisions, say so explicitly: "Pick any combination — reply with the numbers." +**Render-merge guard.** GFM renderers merge adjacent ordered lists and renumber them — a message with a 1-3 content list followed by a 1-3 options list rendered as options 4-6 on the user's screen, and his pick ("4") didn't exist in the author's numbering (work session, 2026-06-10). Numerals belong to the options list only: any other enumeration in the same message uses dashes or prose, and a short prose lead-in line sits directly above the options so no renderer can merge them. When the user picks a number outside the offered range, suspect the render-merge and ask which item text they meant rather than treating it as an error. + Reserve `AskUserQuestion` only when the user explicitly asks for the popup form ("use the popup for this one") or for genuinely free-form input where numbered options don't fit. This rule applies to all three approval gates in the `commits.md` publish flow (commit message, PR description, PR review reply): print the draft inline, then offer numbered approve / changes / edit options inline. Do not switch to the popup form for the gate even though the prior protocol referenced it. diff --git a/.claude/rules/knowledge-base.md b/.claude/rules/knowledge-base.md new file mode 100644 index 000000000..56584987b --- /dev/null +++ b/.claude/rules/knowledge-base.md @@ -0,0 +1,60 @@ +# Agent Knowledge Base (org-roam) + +Applies to: `**/*` + +Craig's org-roam knowledge base is the shared, cross-project store for durable agent knowledge. It lives at `~/org/roam/` — a git repo (origin `git@cjennings.net:roam.git`), auto-synced on Craig's machines by the `roam-sync` systemd timer. Per-project harness memory stays the fast capture layer; durable facts get promoted here. + +## Reading (any project) + +Before relying on a remembered project fact, a prior decision, or reference material, search the KB first. The interface is plain files — never the org-roam SQLite cache: + +```sh +# content/tag search +rg --glob '*.org' --glob '!*sync-conflict*' '<query>' ~/org/roam/ +# follow an [[id:UUID]] link to its node +rg --glob '*.org' --glob '!*sync-conflict*' ':ID:[[:space:]]+<uuid>' ~/org/roam/ +``` + +Pull before querying (`git -C ~/org/roam pull --ff-only`); skip silently if offline. If `~/org/roam/` doesn't exist on this machine, proceed without the KB and say so — never fabricate recall. + +## Writing (personal projects only) + +Classify the project before any write. The source of truth is the work-root denylist below — never inference from remotes, names, or task content: + +- **Work** — project root is, or sits under, a denylisted root. No KB write, ever. Record durable facts per that project's own conventions. +- **Personal** — project root sits under `~/code/`, `~/projects/`, or `~/.emacs.d` and is not denylisted. KB writes allowed. +- **Unknown** — anything else. No KB write. + +Work-root denylist (confirmed by Craig, 2026-06-10): `~/projects/work` + +**Refusal contract** (work and unknown alike): state the classification, name the durable fact in a one-line redacted summary, and say where it was or wasn't written — so Craig can re-route it deliberately instead of losing it silently. + +A write is one node per fact, under `agents/`, roam-valid so Craig's org-roam indexes it: + +``` +~/org/roam/agents/YYYYMMDDHHMMSS-<slug>.org +--- +:PROPERTIES: +:ID: <uuid — generate with uuidgen> +:END: +#+title: <concise title> +#+filetags: :agent:<scope>: + +<the fact, with [[id:...]] links to related nodes> +``` + +Pull before writing, commit and push after (`git -C ~/org/roam add -A && git commit && git push`) — same session discipline as any repo. Never edit Craig's hand-authored nodes; link to them. This write autonomy is scoped to the KB alone — it is not permission to send email, comment on tickets, or post to any public or external channel. + +## What goes in, what stays out + +**In:** durable facts with cross-project or cross-machine value — decisions and their why, environment and tooling gotchas, reference pointers (URLs, dashboards, key paths), lessons that transfer beyond the project that learned them. + +**Out:** anything the repo already records (code structure, git history, CLAUDE.md content), session state, task state (todo.org owns that), high-churn facts that will be stale in a month, secrets and credentials, anything work-confidential. + +## Capture, then promote + +Harness memory (`~/.claude/projects/<enc>/memory/`) remains the per-project capture layer: fast, automatic, allowed to be at-risk. At wrap-up (or a task audit, or an explicit prompt), promote facts that meet the inclusion bar into the KB as nodes. The wrap-up workflow asks; answer it honestly — promotion discipline is what keeps the capture layer from silting up. + +## Inventory + +`rg '#\+filetags:.*:agent:' ~/org/roam/` lists everything agents ever wrote; the git log is the per-write audit trail. Craig prunes at will — deletion or revert of an `:agent:` node is never something to argue with. diff --git a/.claude/rules/org-tables.md b/.claude/rules/org-tables.md new file mode 100644 index 000000000..1b7008590 --- /dev/null +++ b/.claude/rules/org-tables.md @@ -0,0 +1,60 @@ +# Org Table Standard + +Applies to: `**/*.org` + +Every org table in project docs follows one shape. Wide tables overflow the +page in exported PDF/docx and run off the edge of the org buffer; this is the +standing fix. Promoted from the work project's local convention 2026-06-11. + +## Three requirements + +1. **Max width 120 columns — measured at render width.** The whole table + line, leading/trailing pipes included, is ≤120 characters as the table + *renders* (exported output, or the org buffer). An org link counts as its + visible label, not its full `[[target][label]]` source, because export and + the live buffer show only the label. This is the one place source width + and render width diverge; **never split a link** to chase a source-width + number — the render is what overflows the page. Non-link cells have no + source/render gap. +2. **Multi-line cells.** When a cell's text would push the row past 120, wrap + it onto continuation rows: repeat the row with the overflow column's text + continued and the other columns left blank, as many continuation rows as + the content needs. Never truncate content to hit the width; wrap it. +3. **A rule under the header and under every logical row.** Put a horizontal + rule (`|---+---|`) after the header and after every data row, closing rule + included. Each logical row then reads as a bordered block, and the rules + are what mark where a logical row (with its continuation lines) ends. + +Example — the logical row "arch-00" wrapped across two physical rows, rules +between every row: + + | Document | Doc Status | Notes | + |----------+------------+----------------------------------| + | arch-00 | Current | Source-of-truth spec; references | + | | | arch-NN as authority | + |----------+------------+----------------------------------| + | arch-01 | Current | Linear introduction for | + | | | first-time readers | + |----------+------------+----------------------------------| + +## How to apply + +When authoring or editing any table, produce this shape from the start. When +a table already violates it, reformat in place — preserve every cell's +content and any links verbatim, only change the layout. + +Tooling (in every project's `.ai/scripts/` via the template sync): + +- `wrap-org-table.el` reflows tables to the standard mechanically: + `emacs --batch -q -l .ai/scripts/wrap-org-table.el [--width=120] FILE.org`. + It wraps over-budget cells onto continuation rows, adds the rules, measures + links at label width, and never splits a token or a link. Re-running on a + conformant table is a no-op. +- `lint-org.el` flags violating tables as judgment items (checker + `org-table-standard`) during its sweep — width overruns, missing rules, or + both — and names the helper in the message. + +The helper can't fix a table whose single narrowest-possible columns still +exceed the budget (some token or link label is just too wide). That table +needs restructuring — merge or drop columns, shorten labels — which is a +judgment call: the lint item stays until a human reshapes it. diff --git a/.claude/rules/patterns.md b/.claude/rules/patterns.md new file mode 100644 index 000000000..e079a2881 --- /dev/null +++ b/.claude/rules/patterns.md @@ -0,0 +1,29 @@ +# Pattern Catalog Pointer + +Applies to: `**/*` + +A curated catalog of reusable interaction-design patterns lives at `~/code/rulesets/patterns/`. Each pattern is one file capturing a small principle with wide surface area — discovered in one project, written down so the next project doesn't re-derive it. + +## When to consult it + +Before designing any user-facing interaction choice, read the relevant pattern instead of reinventing the shape: + +- A prompt or a chain of prompts (a completing-read, a wizard, a multi-step form) +- A picker over N candidates where the kind of candidate matters, not just the name +- A default for a yes/no or multiple-choice prompt +- A confirmation whose friction should match its consequence +- Any "should this be one prompt or several?" decision + +These patterns came out of Emacs/Elisp prompt design, but the principles are interface-general — they apply to a CLI flow, a web form, or a TUI just as well. + +## How to load it + +Don't carry the whole catalog in context. Read `~/code/rulesets/patterns/README.org` for the index and the one-line principle of each pattern, then read the single pattern file you need (the patterns are `.org` files). Humans grep `patterns/` directly. + +## The root principle + +The patterns converge on one idea: the choices the user has should all be on screen, accurately labeled, ordered by what they'll most often want, with friction sized to the cost of being wrong. Each pattern file is one worked shape of that principle. + +## Adding to it + +A new pattern is captured as a raw note in a project's `docs/design/` when it lands, then promoted into `patterns/` in a batched review (see `patterns/README.md` for the cadence and the frontmatter contract). Don't formalize every rough note inline — capture on landing, promote on review. diff --git a/.claude/rules/todo-format.md b/.claude/rules/todo-format.md index a8df76ad6..5c3496690 100644 --- a/.claude/rules/todo-format.md +++ b/.claude/rules/todo-format.md @@ -5,6 +5,34 @@ Applies to: `**/*.org` (org-mode todo and inbox files) How task entries are structured in org-mode todo files (`todo.org`, `inbox.org`, any GTD-style org file). Same shape across every project. +## Priority and Tag Scheme Header + +Every project's `todo.org` opens with a top-level section named +`[Projectname] Priority Scheme` (e.g. `* Rulesets Priority Scheme`, +`* Work Priority Scheme`), placed above the first `* <Project> Open Work` +section. It declares two things so the rest of the file is legible without +guessing: + +1. **Priorities** — what `[#A]` through `[#D]` mean for this project. At + minimum, what makes something `[#A]` (urgent / blocking) versus `[#D]` + (someday / watchlist). +2. **Tags** — the tag vocabulary in use. Name each tag and what it marks. + For code projects the typical set is `:feature:`, `:bug:`, `:test:`, + `:refactor:`, plus the effort/autonomy tags `:quick:` and `:solo:`. A + project may add, drop, or rename tags to fit its work — the requirement + is that the set is declared, not that it matches a fixed list. + +The section is mandatory. A `todo.org` without it leaves `[#A]` and the tags +undefined, so task-audit can't enforce a vocabulary, task-review can't grade +against agreed semantics, and the inbox workflow can't file new tasks correctly +(its priority-scheme check already gates on this scheme). Each project defines the +scheme its own way; the floor is that priorities and tags are both spelled +out under the header. + +When a project's `todo.org` lacks the section, add it before filing or +grading further tasks — propose the priority semantics and tag set from the +project's existing usage, and confirm with Craig. + ## The Rule A todo entry has two parts: @@ -102,7 +130,7 @@ becomes The agenda view (`org-agenda`) shows entries at the section + top-task level. Letting `**` tasks stay task-shaped preserves their visibility as "things that recently shipped." Letting `***+` sub-tasks flip to dated entries keeps the agenda from being clogged with a long list of completed sub-tasks at every depth — those become history within their parent instead. -`VERIFY` is the documented exception: it follows the dated-rewrite rule at **all** depths (including `**`), because a resolved VERIFY is an answered question rather than a finished task. See the VERIFY section below. +`VERIFY` follows the dated-rewrite rule at `***` and deeper, the same as any sub-task. At `**` it does *not*: a top-level VERIFY completes task-shaped — a `DONE`/`CANCELLED` keyword plus a `CLOSED:` line, exactly like a top-level `TODO`. Dated headers never appear at `**`. Level 2 always carries a terminal keyword; dated headers are a `***`-and-deeper shape only. See the VERIFY section below. ## VERIFY tasks @@ -163,19 +191,31 @@ The sibling rule is the active force that keeps `todo.org` flat. Without it, VERIFYs accumulate one level deeper than their trigger every time — turning a clean parent tree into a long pole of nested sub-headings. -### Completion — dated rewrite + content replacement +### Completion — depth decides the heading shape + +When a VERIFY resolves, **rewrite the heading and body together**. The body +replacement is the same at every depth (step 2 below); the heading shape +depends on the VERIFY's level, mirroring the depth-based rule for ordinary +tasks — dated entries at `***` and deeper, terminal keyword at `**`. + +1. **Replace the heading — by depth.** -When a VERIFY resolves, **rewrite the heading and body together** at the -same depth — regardless of whether the VERIFY is at `**` or `***`: + - **At `***` and deeper — dated event-log entry.** Drop the `VERIFY` + keyword (and any priority cookie / tags) and replace with a timestamp + + short description: -1. **Replace the heading.** Drop the `VERIFY` keyword (and any priority - cookie / tags) and replace with a timestamp + short description: + *** 2026-05-15 Fri @ 14:00:00 -0500 <what was answered or done> - *** 2026-05-15 Fri @ 14:00:00 -0500 <what was answered or done> + Generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`. - Generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`. - Match the original depth (a `**` VERIFY becomes `** YYYY-MM-DD ...`; - a `***` VERIFY becomes `*** YYYY-MM-DD ...`). + - **At `**` — terminal keyword, like any top-level task.** Change + `VERIFY` to `DONE` (answered / check passed) or `CANCELLED` (abandoned), + keep the heading text, priority cookie, and tags, and add a + `CLOSED: [YYYY-MM-DD Day]` line. Never a dated heading — a `**` dated + header is a defect; repair it to `DONE`/`CANCELLED` + `CLOSED:`. + + ** DONE [#B] <original VERIFY topic> :tags: + CLOSED: [2026-05-15 Fri] 2. **Replace the body.** Drop the original question/instruction prose and replace with either: @@ -185,16 +225,18 @@ same depth — regardless of whether the VERIFY is at `**` or `***`: instruction or pending-decision marker — what was done, when, where the artifact lives). -The completed VERIFY becomes an in-place event log entry. The original -question is preserved by the dated heading + body shape; anyone scanning -the agenda or `git log` can see what was asked and what landed. +Either way the completed VERIFY records what was asked and what landed: at +`***` and deeper as a dated event-log entry, at `**` as a `DONE`/`CANCELLED` +task whose body holds the answer. Anyone scanning the agenda or `git log` +can see both. -**Note on the top-level case.** Regular `**` DONE tasks stay task-shaped -with a `DONE` keyword + `CLOSED:` line per *Completion — depth-based* -above. VERIFYs at `**` are the exception — they convert to dated log -entries on completion because a resolved VERIFY isn't a "done task," it's -an answered question. The dated-rewrite rule wins for VERIFYs at all -depths. +**Note on the top-level case.** A `**` VERIFY completes exactly like a `**` +`TODO`: a `DONE`/`CANCELLED` keyword + `CLOSED:` line, with the answer or +action in the body. The earlier habit of dating a resolved top-level VERIFY +— treating "answered question, not a finished task" as license for a `**` +dated header — is retired. It put dated headers at level 2, where the agenda +truncates them out of a clean keyword scan. Dated rewrite is for `***` and +deeper only; `**` always carries a terminal keyword. ### Don't leave stale placeholders @@ -221,3 +263,54 @@ are noise that pollute his `cj:` greps. ** DOING [#A] Kostya's contract :admin:kostya: *** 2026-05-15 Fri @ 14:00:00 -0500 Kostya basis — part-time, 20 hr/week Nerses confirmed 5/15 13:30 CDT: Kostya runs at 20 hr/week part-time, mirroring Vrezh's structure. Plugged into Exhibit A § 2 of the contract draft. + +## Cross-Project Dependency Tags + +A task can be blocked by work that has to happen in a *different project* — a rulesets task that can't finish until `.emacs.d` ships a companion function, say. Left unmarked, two things go wrong: the what's-next workflow keeps recommending the blocked task even though it can't move, and the blocker sits at low priority in the other project, so the dependency stalls silently. + +Two plain org tags track it, one on each side, so neither the waiter nor the blocker loses sight of the dependency: `:blocked:` on the task that's waiting, `:blocker:` on the task that owes the work. The cross-project detail — which project, what work — goes in the task *body*, not a property. This applies to *any* project pair; the convention here and the surfacing in `open-tasks.org` live in the shared rule + workflow layer, not in one project. + +### `:blocked:` — the waiting side + +The task that can't proceed carries `:blocked:`. Its body names the project it's waiting on and what that project owes: + +``` +** DOING [#B] Wrap-teardown feature :feature:blocked: +Blocked on emacsd: needs the ai-term companion functions +(cj/ai-term-quit, -live-count) before the manual validation can run. +``` + +`open-tasks.org` reads the `:blocked:` tag to pull the task out of the "do this next" cascade (it can't be worked) and surface it in a dedicated "Blocked on other projects" section, reading the body for which project to name and nudge. + +### Registering with the blocker — the reciprocal handoff (required) + +Setting `:blocked:` is not complete until the blocking project knows it's blocking. The moment you mark a task `:blocked:` on another project's work, send that project a dependency handoff: + +``` +inbox-send <project> --text "Blocking dependency: <this-project>'s task \"<task>\" is blocked on you — it needs <what>. It stays blocked until this lands. Tag the owning task :blocker: on your side so it surfaces as priority work." +``` + +This is what closes the gap: without it, the blocker only learns it's blocking by accident. The handoff lands in `<project>`'s `inbox/` and its normal inbox processing tags the work (below). A `:blocked:` task with no matching reciprocal handoff is half-done — the dependency is invisible to the one project that can clear it. Skip the send only when the blocker demonstrably already tracks the work (e.g. it's the same handoff that spawned the dependency); it dedups against an existing task either way. + +### `:blocker:` — the blocking side + +When a project processes a blocking-dependency handoff (inbox process mode), it tags the owning task `:blocker:` and names the requesting project in the body: + +``` +** TODO [#B] ai-term wrap-teardown companion :feature:blocker: +Rulesets' wrap-teardown feature is blocked on this — it needs the three +ai-term functions. Surface first so rulesets unblocks. +``` + +The blocking task does *not* carry `:blocked:` — it isn't blocked, it's the blocker. `:blocker:` is a priority signal: `open-tasks.org` surfaces a `:blocker:` task *first*, since clearing it unblocks work in another project, so a dependency that would otherwise stall at low priority gets pulled forward. This is the "surface dependencies first" half of the design. + +### Resolving the dependency + +When the blocker delivers: + +1. The blocking project completes its `:blocker:` task, drops the `:blocker:` tag, and notifies the waiter (`inbox-send <waiter> --text "Delivered: <what> — you're unblocked."`). +2. The waiting project drops the `:blocked:` tag; the task is workable again. Either side noticing the delivery can lift its own tag — the notification just makes it prompt. + +### Not the same as VERIFY + +`:blocked:` marks "waiting on another *project's* work"; `VERIFY` marks "waiting on Craig's input." If Craig's input is what's needed, it's a VERIFY, not `:blocked:`. And `:blocker:` only ever sits on the project that *owes* the work, never the one waiting. diff --git a/.claude/rules/triggers.md b/.claude/rules/triggers.md index e45e660a2..a8d5e772c 100644 --- a/.claude/rules/triggers.md +++ b/.claude/rules/triggers.md @@ -8,13 +8,13 @@ Trigger phrases the user can say from any session to invoke a cross-project acti Synonyms: "Launch X", "Open project X", "Switch to project X". -**Action:** run the `ai` script (the Claude Code session launcher, installed at `~/.local/bin/ai`) in single-project mode targeting the named project. +**Action:** run the `ai` script (the agent session launcher, installed at `~/.local/bin/ai`) in single-project mode targeting the named project. ``` ai <project-path> ``` -The `ai` script handles tmux session creation, window placement, and the per-project Claude opening line — see `~/code/rulesets/claude-templates/bin/ai` for the canonical source. +The `ai` script handles tmux session creation, window placement, and the per-project agent opening line — see `~/code/rulesets/claude-templates/bin/ai` for the canonical source. **Resolving X.** Match against project basenames discoverable by `ai` — directories under `~/code/`, `~/projects/`, and `~/.emacs.d` that contain `.ai/protocols.org`. @@ -22,7 +22,7 @@ The `ai` script handles tmux session creation, window placement, and the per-pro - No match → list all available basenames, ask which to launch. - Multiple partial matches (X is a substring of two or more candidates) → list the matching basenames, ask which. -Do not guess. The cost of asking once is one short turn; launching the wrong project is a wrong-context Claude session that has to be killed and restarted. +Do not guess. The cost of asking once is one short turn; launching the wrong project is a wrong-context agent session that has to be killed and restarted. ## Why a separate file diff --git a/.claude/rules/working-files.md b/.claude/rules/working-files.md index 9a7270271..243226866 100644 --- a/.claude/rules/working-files.md +++ b/.claude/rules/working-files.md @@ -120,7 +120,7 @@ When the task is marked done: - *Inbox content* — `inbox/` and `daily-prep/` follow their own conventions (dated filenames, processed and moved on cadence). -## Implementation Note for Claude Sessions +## Implementation Note for Agent Sessions When the user starts a new task that's going to produce file artifacts: |
