<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/.ai/protocols.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-06-15T13:25:37+00:00</updated>
<entry>
<title>feat(ai): add helper-mode workflow contract</title>
<updated>2026-06-15T13:25:37+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-15T13:25:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=0b681dca75299a81549c78681850f353e6a64a87'/>
<id>urn:sha1:0b681dca75299a81549c78681850f353e6a64a87</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>docs(ai): require an epoch on the tail of helper-agent ids</title>
<updated>2026-06-15T03:07:00+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-15T03:07:00+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=e0f914d510c081db45cafaf4fe5c8f7b65e46fec'/>
<id>urn:sha1:e0f914d510c081db45cafaf4fe5c8f7b65e46fec</id>
<content type='text'>
A helper agent's session-context file is .ai/session-context.d/&lt;id&gt;.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.&lt;epoch&gt;) 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.
</content>
</entry>
<entry>
<title>feat(workflows): skeptical review gate for inbox change proposals</title>
<updated>2026-06-13T01:04:05+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-13T01:04:05+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=19829305721a327d07b6fe5b6dfba5bf34c8ed37'/>
<id>urn:sha1:19829305721a327d07b6fe5b6dfba5bf34c8ed37</id>
<content type='text'>
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.
</content>
</entry>
<entry>
<title>feat(kb): roam-sync script + timer units, old roam path repointed</title>
<updated>2026-06-10T23:13:03+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-10T23:13:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=fcf554a6be8b02aeb9c521ea5d7b7d86465aea0f'/>
<id>urn:sha1:fcf554a6be8b02aeb9c521ea5d7b7d86465aea0f</id>
<content type='text'>
Phase 0 of the agent KB spec: the org-roam KB now lives at ~/org/roam as a git repo on cjennings.net. roam-sync.sh (bats-tested: commit, rebase, push, conflict-abort) runs from a 15-minute systemd user timer; canonical unit files live in scripts/systemd/. Live references to the old ~/sync/org/roam path (the task-list pointer, the journal workflow, the notes template) repoint to ~/org/roam, and a transition symlink at the old location covers stragglers.
</content>
</entry>
<entry>
<title>feat(install-ai): gitignore the full personal-tooling set, add backfill sweep</title>
<updated>2026-06-10T06:14:46+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-10T06:14:46+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=cc72aa635f733da36010567c8718b1ede7622c52'/>
<id>urn:sha1:cc72aa635f733da36010567c8718b1ede7622c52</id>
<content type='text'>
A gitignore-mode project only ignored .ai/. CLAUDE.md was left untracked but not ignored, so an accidental git add or a codify run could still commit a personal CLAUDE.md, the private rule copies under .claude/, or an AGENTS.md. install-ai now ignores the whole set (.ai/, .claude/, CLAUDE.md, AGENTS.md) at bootstrap, line-idempotent so an existing .gitignore isn't duplicated.

.claude/ goes in the set because it's rulesets-owned (copies of claude-rules/*.md plus the language bundle's rules, hooks, and settings), re-synced from rulesets every startup, so git isn't how it travels. Ignoring it also keeps those private rule copies out of the repo, which ignoring CLAUDE.md alone would miss. The gate is unchanged: track-mode projects (personal/doc repos, team repos sharing config) keep tracking the set.

sweep-gitignore-tooling.sh backfills the set across existing gitignore-mode projects, idempotent and skipping track-mode by design. It warns when a now-ignored path is already tracked, since the ignore won't untrack it. protocols.org states the policy once.
</content>
</entry>
<entry>
<title>feat(aiignore): add .aiignore and the recursive-read convention</title>
<updated>2026-05-31T16:11:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-31T16:11:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=0f0905fe6635751edd9ad7e9e02b36e8b8e4dbc4'/>
<id>urn:sha1:0f0905fe6635751edd9ad7e9e02b36e8b8e4dbc4</id>
<content type='text'>
Agents (and any future inventory tool) doing a naive recursive read of a project pick up node_modules, __pycache__, build output, and token artifacts even when those are gitignored, because a recursive read sees the disk, not git. I added a gitignore-syntax .aiignore at the repo root with the default skip list, and a protocols.org "Recursive Reads" subsection documenting the convention, the defaults to assume absent a file, and the lockfile policy (skip on agent reads, independent of git-tracking).

I did not wire the walking scripts (audit.sh, diff-lang.sh, sync-language-bundle.sh): they do targeted finds over .ai/.claude/bundle dirs, never whole-tree walks, so honoring .aiignore there would be dead code. That belongs in a future catalog tool.
</content>
</entry>
<entry>
<title>feat(workflows): add monitor-inbox workflow + inbox-status script</title>
<updated>2026-05-31T05:07:03+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-31T05:07:03+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=8c0eca8375db2c2d346f5fd08ac752209349f94e'/>
<id>urn:sha1:8c0eca8375db2c2d346f5fd08ac752209349f94e</id>
<content type='text'>
Handoffs that arrive mid-session used to sit unseen until the next startup or a manual check. Today's burst of cross-project handoffs made that gap obvious. I added monitor-inbox.org, the cadence-and-decision layer over process-inbox: check the inbox at every task boundary, decide act-now (just do it) versus file (ask, with filing as option 1), and reply to the sender. An opt-in background-monitor /loop recipe covers unattended watching.

inbox-status (with bats tests) is the cheap check the cadence calls. It lists unprocessed handoffs and exits nonzero when any are pending, using the same artifact exclusions as the wrap-up sanity check. protocols.org gets a short cadence note so the habit fires every session, and INDEX.org lists the new workflow. The act-vs-file rule (act-now is silent, filing asks with file as option 1, ambiguity asks) is the decision protocol we settled today.
</content>
</entry>
<entry>
<title>docs(protocols): surface cmail-action send as the default email path</title>
<updated>2026-05-31T03:13:37+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-31T03:13:37+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=ddcde66a768758844a5be705de6a89e68697fa1a'/>
<id>urn:sha1:ddcde66a768758844a5be705de6a89e68697fa1a</id>
<content type='text'>
An org-drill session asked to send a follow-up email first claimed it couldn't, then hand-built MIME through msmtp, because nothing told it cmail send exists. I added a "Sending Email" subsection to protocols.org (read every session): cmail (c@cjennings.net) is the default for personal mail, dmail for work, and cmail-action send is the tool, with one-liner examples for body-file, attachments, Cc/Bcc, and threaded replies. I also rewrote send-email.org Step 4, replacing the inline-Python heredoc that taught the hard way with the cmail-action send call.
</content>
</entry>
<entry>
<title>feat(session-context): resolve the active path per AI_AGENT_ID</title>
<updated>2026-05-31T02:48:13+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-31T02:48:13+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=143feda0644d2289954b694f3ce4cee2fc74b808'/>
<id>urn:sha1:143feda0644d2289954b694f3ce4cee2fc74b808</id>
<content type='text'>
A single .ai/session-context.org races when two agents share a project: each agent's writes clobber the other's session log. I added .ai/scripts/session-context-path, which resolves the active path from AI_AGENT_ID: unset gives the legacy .ai/session-context.org singleton (so every existing one-agent session is unchanged), set gives .ai/session-context.d/&lt;id&gt;.org with the id sanitized to filename-safe characters. This is Codex's Phase 1 slice from the runtime-neutral spec: the race fix on its own, no broader refactor.

startup.org's existence check and wrap-it-up.org's rename now resolve through the helper, each with a singleton fallback so older checkouts that haven't synced the script still work. Wrap folds the agent id into the archive name so two agents wrapping in the same minute don't collide. protocols.org documents the rule. Verified with 5 bats cases and a two-agent simulation showing distinct paths per id.
</content>
</entry>
<entry>
<title>docs(protocols): gate credential-leak warnings on project type, not the credential</title>
<updated>2026-05-26T17:22:08+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-26T17:22:08+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=7beb8d10451bd4c425bf71d22734a9cb1272f83c'/>
<id>urn:sha1:7beb8d10451bd4c425bf71d22734a9cb1272f83c</id>
<content type='text'>
A session false-alarmed on a leak risk when restoring a credentials doc into a tracked .ai/ file. The reasoning was wrong: a tracked secret is only a public-leak risk where the repo can reach a public remote, which means code projects on public GitHub, the ones that already gitignore .ai/. Personal and documentation projects push to a private single-user repo on cjennings.net, so tracked credentials in their .ai/ files are fine and expected.

I added the rule next to the existing "should .ai/ be committed?" decision in protocols.org, since it's a direct corollary of the same code-vs-personal split. The "is this a leak?" question now resolves on which kind of project and remote it is, not on the mere presence of a credential in a tracked file.

Origin: an elibrary session raised the false alarm and Craig corrected it.
</content>
</entry>
</feed>
