aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-15 12:58:24 -0500
committerCraig Jennings <c@cjennings.net>2026-05-15 12:58:24 -0500
commit236dc42fdd8ab471ca7b8df70e6b88ad46acfc8b (patch)
tree93a720afa8a57f3f36aa5454d7818242198f6772
parent77eaaf814ace3fdb456d9683898d5693d402b3df (diff)
downloadrulesets-236dc42fdd8ab471ca7b8df70e6b88ad46acfc8b.tar.gz
rulesets-236dc42fdd8ab471ca7b8df70e6b88ad46acfc8b.zip
docs: simplify cj-comments skill + add VERIFY placement/completion rules
Rounds of simplification on the cj-comments skill: converged on source-block-only in org files, dropped the non-org parsing entirely, factored out the mechanical parts into helper scripts (added separately in another repo), and codified the VERIFY placement + completion rules in a canonical rule file so the conventions stop being implicit. Skill (.claude/commands/respond-to-cj-comments.md): - Drop the file-type comment-marker table and the non-org multi-line continuation rule. The skill now recognizes only the org source-block form (#+begin_src cj: ... #+end_src). Craig's yasnippet keeps typing cost flat. - Defensive parsing for legacy inline cj annotations in older org files (back-compat). - Prefer the new helper scripts: step 1 calls cj-scan for structured detection; step 4 sub-step 6 calls cj-remove-block for validated removal. Both have explicit fallback paths to grep+Read / Edit. - Completion rules now split by keyword: TODO/DOING at *,** stay task-shaped (DONE + CLOSED); deeper TODO/DOING flip to dated headings; VERIFY at any depth flips to a dated heading with body replacement. - New VERIFY-answer pattern: when a cj's parent_heading_chain ends with VERIFY, the cj is the answer to that VERIFY. Lift its body into the dated rewrite (or execute the instruction first if indirect), then delete the cj. - New placement rule for new VERIFYs: sibling of the trigger heading, not a child. Climb to *** when the trigger is buried at ****+. This is the active force that keeps todo.org flat. - Rename "cj: comment" to "cj comment" in prose; preserve the literal cj: token in code examples and grep targets. Canonical rule (claude-rules/todo-format.md): - New "VERIFY tasks" section with four subsections: Placement (** or *** only), Creating a new VERIFY (sibling of trigger), Completion (dated rewrite + body replacement at any depth), Don't leave stale placeholders. - Worked before/after examples for both top-level and first-level VERIFYs.
-rw-r--r--.claude/commands/respond-to-cj-comments.md167
-rw-r--r--claude-rules/todo-format.md118
2 files changed, 218 insertions, 67 deletions
diff --git a/.claude/commands/respond-to-cj-comments.md b/.claude/commands/respond-to-cj-comments.md
index 3700cf8..6d5caa1 100644
--- a/.claude/commands/respond-to-cj-comments.md
+++ b/.claude/commands/respond-to-cj-comments.md
@@ -1,11 +1,11 @@
---
-description: Scan a target file for inline `cj:` annotations (Craig's in-line questions and instructions; multi-line continuations recognized across most comment markers, plus org source-block form `#+begin_src cj: ... #+end_src`) and process each via subagent-delegated accuracy. Each item is classified instruction / question / both, then dispatched to an instruction subagent (proposes a file:line patch) or a question subagent (researches with explicit scope, reports answer + evidence + confidence). Main thread reviews proposals before editing — subagents don't write to the source file. Org-mode TODO parents flip to DOING; new content lands under timestamped subheadings one level deeper; on completion, top and second-level tasks advance to `DONE` while third-level-and-deeper tasks get their heading rewritten to a dated action description (no DONE keyword) so they become an in-place event log. Public-facing writing (commits, PRs, Slack, email, public docs) gets `/voice personal`; private writing skips the voice pass. Summary lists handled instructions, answered questions with evidence + confidence, follow-ups, unresolved items, and an explicit clean / N-remain verdict. Anything needing Craig's input becomes a `VERIFY` task in `todo.org` under the relevant parent (he answers inline with `cj:`) rather than a separate summary file. File/URL references render as clickable org-mode links. Use when a file accumulates inline `cj:` comments. Do NOT use for general code review (`/review-code`), new work without inline comments, or trivial items.
+description: Scan an org file for cj comments — Craig's annotations wrapped in `#+begin_src cj: ... #+end_src` source blocks — and process each via subagent-delegated accuracy. Each item is classified instruction / question / both, then dispatched to an instruction subagent (proposes a file:line patch) or a question subagent (researches with explicit scope, reports answer + evidence + confidence). Main thread reviews proposals before editing — subagents don't write to the source file. Org-mode TODO parents flip to DOING; new content lands under timestamped subheadings one level deeper; on completion, top and second-level tasks advance to `DONE` while third-level-and-deeper tasks get their heading rewritten to a dated action description (no DONE keyword) so they become an in-place event log. VERIFY tasks at any depth flip to dated log entries on completion with body replaced by the answer or action taken. Public-facing writing (commits, PRs, Slack, email, public docs) gets `/voice personal`; private writing skips the voice pass. Summary lists handled instructions, answered questions with evidence + confidence, follow-ups, unresolved items, and an explicit clean / N-remain verdict. Anything needing Craig's input becomes a `VERIFY` task in `todo.org` (top-level or first-level child of a parent task — never deeper) rather than a separate summary file. File/URL references render as clickable org-mode links. Use when an org file accumulates cj comments. Do NOT use for general code review (`/review-code`), new work without cj comments, or trivial items.
disable-model-invocation: true
---
-# /respond-to-cj-comments — Process `cj:` Annotations in a File
+# /respond-to-cj-comments — Process cj Comments in an Org File
-Scan a file for `cj:` comments (Craig's in-line instructions and questions) and handle each one with subagent-delegated accuracy. Used to batch-process notes Craig leaves in documents, code, or org files for later action.
+Scan an org file for cj comments (Craig's instructions and questions wrapped in `#+begin_src cj: ... #+end_src` source blocks) and handle each one with subagent-delegated accuracy. Used to batch-process notes Craig leaves in his org files for later action.
## Usage
@@ -15,32 +15,9 @@ Scan a file for `cj:` comments (Craig's in-line instructions and questions) and
If no file is given, ask the user for the target file. If the user references "this file" or similar with a visible editor buffer, confirm the path before starting.
-## What counts as a `cj:` comment
+## What counts as a cj comment
-A `cj:` comment is any line where the text `cj:` (case-insensitive) starts the comment content. The comment marker varies by file type:
-
-| File type | Example |
-|------------------------------------------------|--------------------------------------------------------------|
-| Org / plain text | `cj: what does this section actually deliver?` |
-| Markdown | `<!-- cj: split this paragraph before the Scope section -->` |
-| Python / shell / YAML | `# cj: check whether this still matches reality` |
-| JavaScript / C / Java / TypeScript / Rust / Go | `// cj: verify the error wrapping logic here` |
-| Lisp / Elisp / Scheme | `;; cj: is this hook still wired?` |
-| LaTeX | `% cj: rewrite, this sounds too corporate` |
-| HTML / XML | `<!-- cj: move this above the fold -->` |
-
-Multi-line comments are supported when continuation lines keep the same comment prefix:
-
-```
-# cj: please check whether the feature flag is still wired.
-# also confirm the fallback path matches the diagram in docs/arch.org.
-```
-
-Treat the whole contiguous block as one `cj:` item.
-
-### Org source-block form (`#+begin_src cj: ...`)
-
-In org files, Craig sometimes uses org's source-block syntax to wrap a longer or paragraph-shaped cj: instruction. The `cj:` tag goes where a source-block language identifier normally goes (org doesn't execute it — it's a structural marker that lets the whole block fold and render as a unit):
+A cj comment is Craig's instruction or question wrapped in an org source-block:
```
#+begin_src cj: comment
@@ -50,16 +27,18 @@ ones about both documentation and execution.
#+end_src
```
-Recognize this pattern as one cj: item. The instruction body is everything between the `#+begin_src cj:` opening line and the matching `#+end_src` line. The body does *not* need `cj:` prefixes on continuation lines — the fences carry the scope.
+That's the only form the skill recognizes. Craig keeps all cj annotations in his org files (`todo.org`, daily-prep docs, scratch org notes) — never in source code, configs, markdown, or anything else that gets checked into a non-org artifact. If a cj annotation is about a code change, it lives in the relevant org task body referencing the source path, not in the code file itself. One rule, one place to look.
+
+The `cj:` tag goes where a source-block language identifier normally goes (org doesn't execute it — it's a structural marker that lets the whole block fold and render as a unit). The instruction body is everything between the `#+begin_src cj:` opening line and the matching `#+end_src` line; the body doesn't need any `cj:` prefix on continuation lines because the fences carry the scope.
Common variants:
-- `#+begin_src cj: comment` (the "comment" label is the most common form Craig uses)
-- `#+begin_src cj:` (no trailing label — still valid)
-- `#+begin_src cj: <any label>` (any short label after the colon is fine; it doesn't affect processing)
+- `#+begin_src cj: comment` — what Craig's yasnippet emits (`cj` + Tab expands the full fence).
+- `#+begin_src cj:` — no trailing label, still valid.
+- `#+begin_src cj: <any short label>` — any label after the colon is fine; the parser ignores it.
-Removal at cleanup time means deleting all three parts: the `#+begin_src cj:` opening line, the body lines, and the closing `#+end_src` line. Don't leave one fence behind.
+Removal at cleanup time deletes all three parts: the `#+begin_src cj:` opening line, the body lines, and the closing `#+end_src` line. Don't leave one fence behind.
-When both inline `cj:` lines and src-block `cj:` blocks appear in the same file, treat them as independent items; each one gets its own classification + subagent + cleanup.
+**Legacy inline `cj:` lines (pre-2026-05-15).** Some older org files may still contain inline `cj: ...` annotations from before the source-block-only convention. Parse them defensively when encountered — treat each as a one-line item and clean up by deleting the line on resolution. Don't author new inline `cj:` lines anywhere going forward.
## Instructions
@@ -92,19 +71,29 @@ Canonical rule: `~/code/rulesets/claude-rules/cross-project.md`.
### 1. Scan the file
-Read the target file and collect every `cj:` comment. For each one, record:
+**Prefer the script.** If `.ai/scripts/cj-scan.py` exists in the project (it ships with claude-templates and rsync'd into every project on startup), invoke it instead of stitching the picture together with grep + Read:
-- File path and line number (or range for multi-line items)
-- Enclosing context:
- - *Org file:* the parent `* TODO` / heading path (e.g. `Parent ▸ Subheading ▸ TODO Foo`)
- - *Code:* the surrounding function or class
- - *Other:* a few lines of surrounding prose
-- The full comment text, including continuation lines
-- The raw comment-marker style so the line can be located and removed later
+```bash
+python3 .ai/scripts/cj-scan.py <target-file.org>
+```
+
+The script outputs JSON with three top-level keys:
+- `cj_blocks` — every cj annotation with `file`, `form` (source-block or legacy-inline), `start_line`, `end_line`, `body`, `label`, `parent_heading_chain`, `parent_depth`.
+- `verify_tasks` — every VERIFY heading with `line`, `depth`, `heading`, `valid_depth`, `promotion_target`. Use this to flag buried VERIFYs (`valid_depth=false`) and propose promotion before cleanup ends.
+- `unclosed_blocks` — any source-block fence that opened but never closed (handle as Unresolved in the summary).
+
+**Fall back to grep + Read** when the script isn't installed: scan for `#+begin_src cj:` and (defensively) `^cj:` lines, then read the surrounding context to reconstruct each block's parent heading chain. This is the slow path; prefer the script.
+
+For each cj item collected, record:
+
+- File path and line range (open fence → close fence for source-block; the single line for legacy inline)
+- Enclosing context: the parent heading chain (e.g. `Parent ▸ Subheading ▸ TODO Foo`)
+- The full comment body
+- Whether the item is a source-block (`#+begin_src cj: ... #+end_src`) or legacy inline (`cj: ...` line) so cleanup deletes the right shape
### 2. Classify each item
-For each `cj:` comment, decide whether it's:
+For each cj comment, decide whether it's:
- **Instruction** — a request for action. Signals: imperative verbs (check, rewrite, fix, remove, add, draft), "please do X".
- **Question** — a request for information. Signals: ends with `?`, starts with who/what/when/where/why/how, "is this", "does this", "should we".
@@ -127,7 +116,7 @@ Prompt every subagent with four required fields (see `subagents.md` for the full
1. **Scope** — one bounded comment, named file, specific action or question
2. **Context** — paste the comment verbatim plus the surrounding context from step 1
-3. **Constraints** — do not touch other `cj:` comments, do not refactor unrelated code, preserve file formatting
+3. **Constraints** — do not touch other cj comments, do not refactor unrelated code, preserve file formatting
4. **Output format** — for instructions: a specific change proposal (before/after or file:line patch), plus URLs or file paths for every external source consulted. For questions: answer + evidence (including URLs or file:line refs for every claim) + confidence level + under 300 words. Subagents must cite sources. A claim without a URL or file reference doesn't belong in the report.
The main thread applies edits. Subagents report; they do not write to the source file. This keeps formatting consistent and makes review easier.
@@ -149,21 +138,44 @@ For **instructions**:
Use one more `*` than the parent task's heading level. If the parent is `** TODO`, the subheader is `***`. If the parent is `**** TODO`, the subheader is `*****`. Generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"` so it's accurate, not estimated.
-4. **Surface URLs.** If the subagent's output includes URLs, file paths, or external references, list them under the updated task content. After applying the edit, offer to convert them into explicit org-mode links (`[[url][label]]` or `[[file:path][label]]`) at the location where the `cj:` comment originated, or elsewhere in the task if that fits better.
+4. **Surface URLs.** If the subagent's output includes URLs, file paths, or external references, list them under the updated task content. After applying the edit, offer to convert them into explicit org-mode links (`[[url][label]]` or `[[file:path][label]]`) at the location where the cj comment originated, or elsewhere in the task if that fits better.
+
+5. If the comment is inside an org-mode `TODO` heading, mark that `TODO` as `DOING` when work begins. **When the work is complete, the form depends on heading depth *and* keyword:**
+
+ - **`TODO` / `DOING` at top-level (`*`) and second-level (`**`)** advance to `DONE` with a `CLOSED: [YYYY-MM-DD Day HH:MM]` line under the heading. The keyword and original task name stay visible so the task remains in the agenda.
+ - **`TODO` / `DOING` at third-level (`***`) and deeper** get the dated-heading rewrite: replace the heading line with `*** YYYY-MM-DD Day @ HH:MM:SS -ZZZZ <what was decided / done>` (generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`); drop the keyword and CLOSED line. The body/sub-headers stay as the record of what was done. These deeper tasks become an in-place event log under their parent.
+ - **`VERIFY` at any depth** gets the dated-heading rewrite *and* a body replacement: rewrite the heading to `<depth> YYYY-MM-DD Day @ HH:MM:SS -ZZZZ <what was answered or done>` (same timestamp generator); replace the body with either the information Craig provided (when the VERIFY was a question) or a description of the action taken (when it was an instruction / pending-decision marker). VERIFYs at `**` follow this dated-rewrite rule even though regular `**` DONE tasks stay task-shaped — a resolved VERIFY is an answered question, not a finished task. Canonical rule: [todo-format.md § VERIFY tasks → Completion](../../claude-rules/todo-format.md).
+
+ **VERIFY-answer pattern.** When a cj annotation's `parent_heading_chain` ends with a `VERIFY ...` heading (i.e., the cj sits directly inside a VERIFY task), the cj is Craig's answer to the question that VERIFY held open. The cj content is the source for the dated-rewrite body. Two shapes:
-5. If the comment is inside an org-mode `TODO` heading, mark that `TODO` as `DOING` when work begins. **When the work is complete, the form depends on heading depth:**
+ - *Direct answer.* The cj body IS the answer (a value, decision, link, paste from elsewhere). Lift the cj body verbatim into the new dated body; trim filler ("okay," "approved," "yes,") that isn't load-bearing.
+ - *Indirect answer.* The cj points at where the answer lives ("Kostya gave this in Slack — pull it from DM channel X," "see the attached doc"). Execute the instruction first (per step 3 — subagent if research is needed), then the resolved info becomes the dated body.
- - **Top-level (`*`) and second-level (`**`)** tasks advance to `DONE` with a `CLOSED: [YYYY-MM-DD Day HH:MM]` line under the heading. The keyword and original task name stay visible so the task remains in the agenda.
- - **Third-level (`***`) and deeper** tasks get the dated-heading rewrite: replace the heading line with `*** YYYY-MM-DD Day @ HH:MM:SS -ZZZZ <what was decided / done>` (generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`); drop the keyword and CLOSED line. The body/sub-headers stay as the record of what was done. These deeper tasks become an in-place event log under their parent.
+ Both shapes land at the same end state:
- Craig's convention for `todo.org` and prep docs: top and second-level remain task-shaped so they stay visible in the agenda; only sub-tasks under a parent flip into a dated log. (Feedback memory: `feedback_done_tasks_become_dated_log_headings`.) Leave a still-in-progress task as `DOING` for the user; only the *completed* ones get the `DONE`-or-dated-rewrite.
+ 1. Generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`.
+ 2. Rewrite the VERIFY heading to its dated form (depth-preserving) with a short summary of what got answered.
+ 3. Replace the body with the resolved info (the cj body for direct, the executed result for indirect).
+ 4. Delete the cj annotation — it's now folded into the body. Don't keep both.
-6. Remove the `cj:` comment from the file (the entire contiguous block, including continuation lines).
+ Detection signal from `cj-scan`: a cj block whose `parent_heading_chain[-1].heading` starts with `VERIFY`.
+
+ Craig's convention for `todo.org` and prep docs: regular tasks at top and second-level remain task-shaped on completion so they stay visible in the agenda; deeper sub-tasks flip into a dated log; VERIFYs always flip to a dated log regardless of depth. (Feedback memory: `feedback_done_tasks_become_dated_log_headings`.) Leave a still-in-progress task as `DOING` for the user; only the *completed* ones get the `DONE`-or-dated-rewrite.
+
+6. Remove the cj comment from the file. **Prefer the script:**
+
+ ```bash
+ python3 .ai/scripts/cj-remove-block.py --file <target> --start <N> --end <M>
+ ```
+
+ `cj-remove-block` validates that lines `N..M` actually look like a cj annotation before deleting (a `#+begin_src cj:` / `#+end_src` fence pair, or a single `cj:` line) and refuses if they don't. Use the `start_line`/`end_line` values from `cj-scan`'s output. This protects against accidentally trimming the wrong block when working with long bodies where exact-match Edit is fragile.
+
+ Fall back to the Edit tool when the script isn't installed: for a source-block item delete all three parts (`#+begin_src cj:` opening line, body, `#+end_src` closing line); for a legacy inline item delete the single line.
For **questions**:
1. Capture the answer in the summary (step 5).
-2. Remove the `cj:` comment. The user can re-open the thread conversationally if they have follow-ups.
+2. Remove the cj comment. The user can re-open the thread conversationally if they have follow-ups.
For **writing destined for public channels** (commit messages, PR descriptions, PR comments, Slack or email messages, public docs):
@@ -209,26 +221,43 @@ Produce one summary at the end, structured:
### Unresolved
-- <any `cj:` comments that couldn't be handled — say why, leave them in place>
+- <any cj comments that couldn't be handled — say why, leave them in place>
### File state
State explicitly one of:
-- "File is now clean. All `cj:` comments resolved and removed."
-- "File still contains N unresolved `cj:` comment(s), listed under Unresolved above."
+- "File is now clean. All cj comments resolved and removed."
+- "File still contains N unresolved cj comment(s), listed under Unresolved above."
Never leave the reader guessing about whether the file is ready for follow-up work.
```
Keep answers direct. If a question has a simple answer, one sentence. If it needs nuance, two or three. Do not pad. The user reads the summary, not the intermediate work.
-**Items needing Craig's input go to `todo.org`, not a summary file.** When the run leaves something that needs Craig to decide, answer, or approve — a blocker, an open question, a draft awaiting sign-off, anything in the Follow-ups list above that requires his action — add a `VERIFY` task in `todo.org` under the parent task it belongs to (the one whose subject it concerns). Name the heading so the question or ask is self-evident on scan:
+**Items needing Craig's input go to `todo.org`, not a summary file.** When the run leaves something that needs Craig to decide, answer, or approve — a blocker, an open question, a draft awaiting sign-off, anything in the Follow-ups list above that requires his action — add a `VERIFY` task in `todo.org` under the parent task it belongs to (the one whose subject it concerns). Name the heading so the question or ask is self-evident on scan.
+
+**Placement rule — sibling of the trigger.** Place a new VERIFY as a *sibling* of the heading that triggered its creation:
+
+- Trigger at `**` → VERIFY at `**` (sibling of the trigger, under the same `*` section).
+- Trigger at `***` → VERIFY at `***` (sibling under the same `**` parent).
+- Trigger at `****` or deeper → VERIFY climbs to `***` (the deepest valid depth); the trigger's parent tree gets flagged for flattening.
+- Trigger at `*` (top-level section) → VERIFY at `**`.
+
+Valid VERIFY depths are `**` and `***` only — never deeper. File placement: insert the new VERIFY after the trigger's entire sub-tree ends, alongside any other sub-tasks and dated logs already under the same `**` parent. Without the sibling rule, VERIFYs accumulate one level deeper than the trigger every time and the file goes vertical fast. Canonical rule: [todo-format.md § VERIFY tasks → Creating a new VERIFY](../../claude-rules/todo-format.md).
+
+The placement constraint applies to *new* VERIFYs this skill creates and to *existing* VERIFYs the skill encounters at `****+` — flatten the buried ones up to one of the two allowed depths as part of the cleanup pass.
+
+Form:
```
-*** VERIFY <self-evident question or ask — phrased so the heading alone tells Craig what's needed>
+** VERIFY <self-evident question or ask> (sibling of a ** trigger)
+
+** TODO [#A] Parent task
+*** Some sub-task or dated log under Parent task
+*** VERIFY <self-evident question or ask> (sibling of the *** trigger)
```
-Do **not** append a `cj: <placeholder>` line beneath the heading. A well-named VERIFY heading carries the question on its own, scans cleanly in the agenda, and Craig adds his own `cj:` annotation (inline `cj: ...` or `#+begin_src cj: ... #+end_src`) when he's ready to answer — that's the signal to come back and process the response. Leaving an empty `cj: <fill in>` placeholder is noise.
+Do **not** append a `cj: <placeholder>` line beneath the heading. A well-named VERIFY heading carries the question on its own, scans cleanly in the agenda, and Craig adds his own `#+begin_src cj: ... #+end_src` annotation when he's ready to answer — that's the signal to come back and process the response. Leaving an empty `cj: <fill in>` placeholder is noise.
Pair the VERIFY task with a dated child header for the work-log entry where one fits (`*** YYYY-MM-DD Day @ HH:MM:SS -ZZZZ <desc>` — generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`). Do **not** default to writing a summary file in `/tmp/` and opening it in `emacsclient` — Craig prefers the in-place `VERIFY` task: it lives next to the work, surfaces in his agenda, and he resolves it inline rather than having to go find a separate doc. The chat summary above still gets written (it's the FYI recap); the `VERIFY` task is the durable home for anything he must act on. (Craig's standing instruction, 2026-05-12; the no-placeholder rule added 2026-05-14.)
@@ -236,7 +265,7 @@ Pair the VERIFY task with a dated child header for the work-log entry where one
### 6. Cleanup pass
-Remove every `cj:` comment that was handled in step 4 (instructions done) or step 5 (questions answered). The file is clean after the skill runs. Any comment left unresolved stays in place and is listed under `### Unresolved` in the summary with the reason.
+Remove every cj comment that was handled in step 4 (instructions done) or step 5 (questions answered). The file is clean after the skill runs. Any comment left unresolved stays in place and is listed under `### Unresolved` in the summary with the reason.
Confirm the cleanup by re-scanning the file after removals. If any `cj:` line survives that shouldn't, remove it and note the correction.
@@ -251,7 +280,7 @@ If step 0 detected a cross-project invocation and Craig picked option 1, write t
Contents:
- Top-of-file note: this work happened cross-project. The acting Claude was running in `<this-project>` cwd; the file lives in `<other-project>`. The target project's next Claude reads this during inbox processing and folds the work-log into its own `session-context.org` before deleting the handoff.
-- Scope of changes: what `cj:` items were processed, what was changed, what's still open.
+- Scope of changes: what cj items were processed, what was changed, what's still open.
- Files touched: full paths, line-level granularity where it helps.
- Pending Craig-asks: every `VERIFY` entry the run created or left in place.
- Subagent traceability: which subagents ran, what they cited, what changes they proposed.
@@ -263,7 +292,7 @@ See `~/code/rulesets/claude-rules/cross-project.md` for the canonical rule.
## Principles
-- **Accuracy > speed.** Subagent generously. A wrong answer to a `cj:` comment is worse than a slow one.
+- **Accuracy > speed.** Subagent generously. A wrong answer to a cj comment is worse than a slow one.
- **Don't guess.** If a question needs verification and verification isn't possible, say so. Surface unknowns rather than fabricate.
- **Preserve the file.** Don't reformat surrounding lines. Don't reorder tasks. Touch only what the comment asks for, plus the comment's own removal.
- **The user reads the summary, not the process log.** Write summaries that are directly useful.
@@ -272,9 +301,9 @@ See `~/code/rulesets/claude-rules/cross-project.md` for the canonical rule.
## Anti-patterns
-- Handling complex `cj:` items inline in the main thread instead of subagenting.
-- Batching unrelated `cj:` comments into one giant subagent prompt.
-- Removing a `cj:` comment before the user has seen the answer in the summary.
+- Handling complex cj items inline in the main thread instead of subagenting.
+- Batching unrelated cj comments into one giant subagent prompt.
+- Removing a cj comment before the user has seen the answer in the summary.
- Skipping the `/voice personal` pass on public-facing writing because it "looked fine already."
- Guessing on a question instead of spawning a research subagent.
- Letting a subagent edit the source file directly — review surface loss.
@@ -285,18 +314,22 @@ Input file: `todo.org` containing:
```
** TODO Draft the D2P2 Pillar 1 writeup
-cj: what do we actually know about Orion's Belt's partner API contract?
-cj: check whether Redwire's AdTech feed is still in scope. Eric mentioned deprecating it in sprint planning.
+#+begin_src cj: comment
+what do we actually know about Orion's Belt's partner API contract?
+#+end_src
+#+begin_src cj: comment
+check whether Redwire's AdTech feed is still in scope. Eric mentioned deprecating it in sprint planning.
+#+end_src
```
Skill run:
-1. Scans, finds two `cj:` items (one question, one instruction-with-verification).
+1. Scans, finds two cj items (one question, one instruction-with-verification).
2. Marks the parent `** TODO` as `DOING`.
3. Spawns two subagents in parallel:
- **Q subagent:** reads `deepsat/knowledge.org`, greps ticket history for "Orion's Belt API", checks recent meeting transcripts. Reports findings with evidence.
- **Instruction-with-verification subagent:** reads sprint-planning transcripts, searches for "Redwire AdTech deprecation", checks Linear for related tickets. Reports whether the feed is still in scope.
-4. Main thread applies the `DOING` status change, writes the summary with both answers, removes both `cj:` comments.
+4. Main thread applies the `DOING` status change, writes the summary with both answers, removes both cj blocks (fences and all).
5. Reports:
- Q1: What we know about Orion's Belt's partner API contract → answer + file:line refs + confidence.
- Q2/Instruction: Redwire AdTech scope → answer + evidence + recommendation for whether to cite in Pillar 1.
diff --git a/claude-rules/todo-format.md b/claude-rules/todo-format.md
index f8bc88f..24b5ca2 100644
--- a/claude-rules/todo-format.md
+++ b/claude-rules/todo-format.md
@@ -56,3 +56,121 @@ When adding a new task:
When restructuring an existing entry that's already sentence-shaped, split
it: keep the topic as the heading, move the rest to the body.
+
+## VERIFY tasks
+
+`VERIFY` is the keyword for "waiting on Craig's input" — open questions,
+pending decisions, drafts awaiting approval. The placement and completion
+rules below are stricter than normal TODO tasks because VERIFYs are
+open-question placeholders, not regular tasks.
+
+### Placement — top-level or first-level child only
+
+A VERIFY task lives at exactly one of two depths:
+
+1. **Top-level** under the relevant section (e.g., `** VERIFY ...` directly
+ under `* Work Open Work`).
+2. **First-level child** of a parent task (e.g., `*** VERIFY ...` under a
+ `** TODO` / `** DOING` parent).
+
+Never deeper. A VERIFY at `****` or below is buried — the agenda view
+collapses it under its grandparent and Craig stops seeing it. When you
+catch a VERIFY at `****+`, flatten it up to one of the two allowed depths.
+
+The goal is a flat, scannable task list: the parent task is the topic; its
+VERIFYs are the open threads under that topic; the dated history sits as
+log entries (which *can* be deeper).
+
+### Creating a new VERIFY — sibling of its trigger
+
+When you add a new VERIFY task, place it as a **sibling of the heading
+that triggered its creation** — not as a child of that heading.
+
+The trigger is whatever surfaced the open question:
+
+- A cj annotation that asks something or marks a pending decision →
+ trigger is the heading the annotation sits under.
+- A task body or sub-task that uncovered an unanswered question while you
+ were working it → trigger is that task.
+- A draft awaiting Craig's sign-off → trigger is the draft's parent task.
+
+Depth-wise, this means:
+
+- Trigger at `**` → new VERIFY at `**` (top-level sibling under the
+ section).
+- Trigger at `***` → new VERIFY at `***` (first-level-child sibling under
+ the same `**` parent).
+- Trigger at `****` or deeper → VERIFY can't follow it there (Placement
+ rule above). Climb the VERIFY up to `***` and surface the buried
+ trigger as a candidate to flatten.
+- Trigger at `*` (a top-level section) → VERIFY goes at `**` (top-level
+ task under the section).
+
+File placement: insert the new VERIFY *after* the trigger's entire
+sub-tree ends — i.e., after any sub-tasks, dated log headers, or draft
+sub-headings the trigger already contains. This keeps everything under a
+`**` parent flat: sub-tasks, dated logs, and VERIFYs all live as
+siblings at `***`, never burrowing deeper.
+
+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
+
+When a VERIFY resolves, **rewrite the heading and body together** at the
+same depth — regardless of whether the VERIFY is at `**` or `***`:
+
+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>
+
+ 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 ...`).
+
+2. **Replace the body.** Drop the original question/instruction prose and
+ replace with either:
+ - **The information Craig provided** (when the VERIFY was a question
+ — paste the answer, a quote, a link, whatever resolved it), or
+ - **A description of the action taken** (when the VERIFY was an
+ 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.
+
+**Note on the top-level case.** Regular `**` DONE tasks normally stay
+task-shaped with a `DONE` keyword + `CLOSED:` line (so they remain visible
+in the agenda). 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.
+
+### Don't leave stale placeholders
+
+A well-named VERIFY heading carries the question on its own. Don't append
+`cj: <fill in>` placeholder lines under the heading — Craig adds his own
+cj comment (a `#+begin_src cj: ... #+end_src` block) when he's ready to
+answer, and that's the trigger to process the response. Empty placeholders
+are noise that pollute his `cj:` greps.
+
+### Examples
+
+**Top-level VERIFY, before / after:**
+
+ ** VERIFY What's our position on the BBN NDA reciprocal-term length?
+
+ ** 2026-05-15 Fri @ 14:00:00 -0500 BBN NDA — standard 2-year reciprocal terms confirmed
+ Per Nerses 2026-05-15 (Slack DM D0AAAEW3BS4): DeepSat's standard NDA template uses 2-year reciprocal. Sent to BBN legal for sign-off.
+
+**First-level child VERIFY, before / after:**
+
+ ** DOING [#A] Kostya's contract :admin:kostya:
+ *** VERIFY Confirm Kostya's basis — part-time hr/week or full-time?
+
+ ** 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.