<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/claude-templates/.ai/workflows/wrap-it-up.org, branch main</title>
<subtitle>Claude Code skills, rules, and language bundles
</subtitle>
<id>https://git.cjennings.net/rulesets/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/rulesets/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/'/>
<updated>2026-07-30T12:42:47+00:00</updated>
<entry>
<title>feat(ai): add --helper for a second session in a live project</title>
<updated>2026-07-30T12:42:47+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-30T12:42:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=84bd121683add4440a3652b12b70e6e2d45f5384'/>
<id>urn:sha1:84bd121683add4440a3652b12b70e6e2d45f5384</id>
<content type='text'>
A helper is a second agent session in a project that already has a live one. It reads freely, makes only scoped single-heading edits, and never touches git. Until now the only way to get one was to tell a session by hand it was a helper, and nothing checked whether that was true.

--helper runs agent-roster first, so the claim gets verified rather than asserted. It opens its own tmux window and hands the session helper-mode.org instead of the normal startup workflow. Git prep is skipped because pulls belong to the primary. With no other agent live it warns and falls back to a primary launch.

When the roster can't answer, it opens a helper anyway. I picked that direction because a helper that turns out to be alone merely does less, while a primary that turns out not to be alone runs pulls and rsync underneath a live session.

Wrap-up gains a Step 0 branch. A helper archives its own context file and stops, and the primary's next commit carries its edits. An orphaned helper whose primary already exited runs the full wrap, because otherwise its edits strand as a dirty tree nobody owns.

That branch resolves the project root before consulting the roster. agent-roster defaults to $PWD and only matches agents at or inside it, so a call from a subdirectory would miss a primary at the root and report the helper orphaned. That is the one path that commits and pushes.

The agent id is sanitized to the charset session-context-path keeps, because it's interpolated into the command typed into the pane. A helper launching a helper gets a fresh id rather than inheriting its parent's. Two agents resolving to one context file is the collision the contract exists to prevent.

Startup still has no roster check, so a bare claude launched into a busy project runs full primary startup regardless. helper-mode.org and protocols.org now say so instead of listing the routing as though it works.
</content>
</entry>
<entry>
<title>refactor(sentry): call one loop cycle a cycle, not a fire</title>
<updated>2026-07-29T00:53:45+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-29T00:53:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=f3f5bfd651962934b230785d5a3449b738ce44ed'/>
<id>urn:sha1:f3f5bfd651962934b230785d5a3449b738ce44ed</id>
<content type='text'>
I read "nine fires" as nine emergencies and went looking for what was burning. The word reaches me directly, since digest headings render as Fire 11 in the anchor I read every morning.

72 noun-sense instances in sentry.org become "cycle". The four verb-sense uses stay, because firing is the right word for a trigger.

home proposed "pass" and I turned it down. sentry.org already uses that as a numbered noun for the eleven hygiene passes, so "Pass 11" would have collided with a referent in the same file. home confirmed it by walking into exactly that: its anchor ended up carrying "Pass 11" three lines from "pass 12". "Cycle" appears nowhere in the file, and it is the word I reached for myself when I flagged the problem.

Three more instances lived outside sentry.org: wrap-it-up.org, todo-cleanup.el, and its test. All three named a sentry cycle, so they move too and the vocabulary stays consistent across files.
</content>
</entry>
<entry>
<title>fix(hooks): don't let a wrap sentinel outlive its session</title>
<updated>2026-07-27T18:32:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-27T18:32:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=2c664cb5651fbf03bc52d7848add0c571708adcd'/>
<id>urn:sha1:2c664cb5651fbf03bc52d7848add0c571708adcd</id>
<content type='text'>
wrap-it-up drops /tmp/ai-wrap-teardown-&lt;project&gt; to ask the Stop hook to kill the tmux session once the wrap certifies clean. When certification fails the Stop hook blocks and leaves the sentinel armed, which I did on purpose so a wrap blocked by a dirty tree can retry on a later stop without re-running the workflow.

I never bounded that retry to the session. An uncertified sentinel sat armed indefinitely and fired in whatever session next reached a clean tree. work's 11:37 wrap today left one armed. The 13:20 session ran startup, committed the task filing and the template sync, went clean, and the next stop consumed the two-hour-old sentinel and killed the terminal mid-work. Every stop before those commits had been blocked by the same sentinel failing certification, so the session spent its whole life either blocked or dead. archsetup's had been armed since Saturday on a live attached terminal, and home's was armed and waiting.

session-start-disarm.sh clears the project's sentinels at SessionStart. A new session means the wrap that armed one is gone, so its pending teardown is meaningless. Within-session retry is untouched, since the hook only runs at session start, and a test pins that. If teardown is still wanted, wrap-it-up re-arms it.

I disarmed the three live ones by hand before writing this, backed up under /tmp/disarmed-sentinels.

Four tests cover the disarm, one pins the retry behavior I did not want to lose. The scoping test matters most: a concurrent session in another project keeps its own sentinel.
</content>
</entry>
<entry>
<title>feat: enforce clean wraps and inbox-safe sync</title>
<updated>2026-07-25T20:35:14+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-25T20:35:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=f2609d9f9ad33486bef43211d753ba53e1e24181'/>
<id>urn:sha1:f2609d9f9ad33486bef43211d753ba53e1e24181</id>
<content type='text'>
Centralize repository-state checks, bind teardown to a certified clean HEAD, and allow inbox-only refreshes. Guard installed symlinks from cross-project writes and add regression coverage.
</content>
</entry>
<entry>
<title>feat(workflows): clear temp/ at wrap, plus three rule and template fixes</title>
<updated>2026-07-23T13:41:44+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-23T13:41:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=10ea44b6de3be1872f7f0bd4501ccf3878105bc4'/>
<id>urn:sha1:10ea44b6de3be1872f7f0bd4501ccf3878105bc4</id>
<content type='text'>
Four proposals from other projects, reviewed and applied together.

wrap-it-up gains a Clear temp/ step in Step 3. temp/ is gitignored everywhere, so nothing there survives deletion. The step confirms before removing anything that reads as in-progress rather than throwaway, and skips where temp/ isn't gitignored. This closes the last open clause of the working/temp work from 2026-07-20.

interaction.md drops the fenced-code-block carve-out. Chat output carries no markup at all now, fences included. Fences don't invert the way inline spans do, but the carve-out kept pulling them back in.

sentry.org folds in four notes from its first two live runs elsewhere. Two are traps for anyone reviewing a night by git log: archive-done touches .gitignore on its first run, and a mirror-only project's quiet fires leave no commits at all. The others make property sweeps sanctioned quiet-fire work and split the task audit into a mechanical hourly subset plus a nightly judgment half.

The notes.org template now lints clean, down from four flags in every project on every sweep. Two of those were mechanical, so lint-org --fix anywhere would have rewritten the template and drifted it from canonical.
</content>
</entry>
<entry>
<title>feat(protocols): add the "the list" before-close queue and colloquialisms convention</title>
<updated>2026-07-20T02:10:34+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-20T02:10:34+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=8fd9e39a1ebf6c6513c77dbc6e70eeee85f9ccb8'/>
<id>urn:sha1:8fd9e39a1ebf6c6513c77dbc6e70eeee85f9ccb8</id>
<content type='text'>
Home proposed two linked cross-project norms and Craig approved rolling them out. Both now ship in the template so every project inherits them.

A new "Colloquialisms and Expansions" section in protocols.org maps Craig's shorthand to a defined action the agent applies without asking. Two seed entries: "put X on the list" appends to a session-scoped Before-Close Queue, and "tell &lt;project&gt; &lt;message&gt;" drops the message in that project's inbox via inbox-send. The set is extensible.

The queue lives under a "* Before-Close Queue" heading in the session anchor, so it resets when the anchor is archived at wrap. Anything that must outlive the session stays a todo.org task. wrap-it-up Step 1 gets a sub-step that works the queue oldest-first before the Summary, so any resulting edits ride the wrap commit, with unfinished items surfaced in the valediction rather than dropped.

I settled three design calls. The reference lives in protocols.org rather than a per-project notes.org (home's local reference), since a synced section is what makes it a shared norm. The queue lives in the session anchor, as home did. The wrap step sits at the front of Step 1 rather than a new half-step, which keeps the "Steps 1 through 5" framing intact. A documentation-integrity bats guards the wiring and the before-the-Summary ordering against a future edit or sync dropping it.
</content>
</entry>
<entry>
<title>feat(sentry): wire the roam writers and wrap-up guard for sentry</title>
<updated>2026-07-19T10:01:51+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-19T10:01:51+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=c6383e97f7dc1113959a000f6273d30c2f20415e'/>
<id>urn:sha1:c6383e97f7dc1113959a000f6273d30c2f20415e</id>
<content type='text'>
Phase 3 of the sentry supervisor: reconcile the existing roam writers and wrap-up so sentry's locks actually guard something, and its shutdown has one enforced entry point.

The roam-write lock only helps if every roam writer takes it, so both writers now do. knowledge-base.md's write recipe and inbox.org core §5 acquire the roam-write lock around their edit and trigger roam-sync instead of committing themselves. That closes a gap the one-git-owner rule already implied but the KB recipe still violated: the recipe told agents to run git add -A &amp;&amp; commit &amp;&amp; push against a tree that's chronically dirty from live captures, which could sweep an in-flight capture into a stray commit. roam-sync stays the roam repo's only committer. Agents edit-plus-trigger under the lock, and roam-sync.sh's header now states that contract instead of the old "agents commit inline" note.

Both writers degrade as the spec settled: an absent agent-lock proceeds unlocked (today's behavior), and only a present helper reporting the lock busy after its bounded wait defers or surfaces.

wrap-it-up.org gains a Step 0 that refuses while sentry is live. It checks the single-runner lock and points at "stop sentry" rather than archiving the anchor and tearing down the buffer under a still-firing loop. Both files derive the lock name the same way (sentry-&lt;repo-basename&gt;), so the guard and the engine agree. triage-intake.org notes that it also runs as sentry's triage pass under the no-approvals contract, with its trigger phrases unchanged.
</content>
</entry>
<entry>
<title>fix(scripts): stop org table helpers rewriting block content</title>
<updated>2026-07-14T06:27:17+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-14T06:27:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=951b6fc63129577a80e1d46f46eeafff005fe636'/>
<id>urn:sha1:951b6fc63129577a80e1d46f46eeafff005fe636</id>
<content type='text'>
Both helpers treated any pipe-led line as a table row, so ASCII art in an example block got mangled into a bordered table (a work file took a 1949-line reformat on 2026-07-09). The scanners now track the open block's type and skip its content. Only the matching end marker closes a block, so a literal end_src quoted inside an example block can't re-expose it.

The deeper cause was wrap-org-table's load-time dispatch: it fired when lint-org merely required the library, running the reformatter over files lint-org was only asked to report on. It now dispatches only when named as the entry script. lint-org's CLI is also report-only by default now. Writes require --fix, and the wrap-up workflow and lint command pass it.
</content>
</entry>
<entry>
<title>chore: drop AI co-author from generated-document headers</title>
<updated>2026-07-09T19:02:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-09T19:02:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=6def7c4d63f499aba10fe93c7bb2c7e206a7d7f5'/>
<id>urn:sha1:6def7c4d63f499aba10fe93c7bb2c7e206a7d7f5</id>
<content type='text'>
Every org document an agent writes carried `#+AUTHOR: Craig Jennings &amp; Claude`. No template stamps that line. Agents copy it from a neighboring file, so one stray header propagates through everything generated afterward.

My own repos tolerate the co-author line. Employers whose policy is that work product carries employee names alone do not. An `#+AUTHOR:` line survives conversion into docx, a wiki page, or a PDF that reaches a customer.

I rewrote the header to `Craig Jennings` across the workflows, templates, specs, and design docs. The rule now lives in commits.md, so the next generated document starts correct rather than inheriting the mistake.

Archived session logs keep their original headers as a record of what happened. The two Codex-authored design docs keep their byline, because Codex wrote them and relabeling would be a false attribution rather than the removal of one.
</content>
</entry>
<entry>
<title>feat(routing): wire the wrap-up cross-project router end to end</title>
<updated>2026-07-02T04:43:15+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-02T04:43:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=7c120073a7de96e67a4f51e539c45d2d22d74f81'/>
<id>urn:sha1:7c120073a7de96e67a4f51e539c45d2d22d74f81</id>
<content type='text'>
This closes the build half of the wrap-up routing spec: Phases 2 and 4 here, with the engine and discovery already shipped.

inbox.org's "File as TODO" disposition now runs route_recommend on each keeper and stamps :ROUTE_CANDIDATE: &lt;destination&gt; on strong and weak matches, so the wrap-up router has a candidate set without ever scanning the standing backlog. wrap-it-up.org Step 3 gains the optional router after the inbox sanity check, with the gate-vs-optional split named in the prose: surface the batch with destinations and confidence labels, then go or skip. An empty set stays silent.

The go path is mechanical rather than prose-driven: the new route-batch helper lists candidates read-only, and on go extracts each subtree (children ride along, markers stripped, headings promoted), delivers it via inbox-send for provenance, and removes the local copy only after a successful send, rewriting todo.org per send so a crash never strands an already-sent task locally. Overlapping candidate spans (a tagged child inside a tagged parent) are a loud conflict, left in place with a non-zero exit, because routing either span would silently take the other along.

A 13-test bats suite covers list/backlog exclusion, empty-set silence, delivery with provenance and children, promotion, drawer pruning, the no-todo.org destination, failed-send recovery with the marker intact, the nested-candidate conflict, and duplicate-marker dedupe. cross-project.md notes the router as a sanctioned cross-project write path.
</content>
</entry>
</feed>
