diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-27 20:52:02 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-27 20:52:02 -0500 |
| commit | 16756135ff2b70a47aded26e8ccf33dc249a9f17 (patch) | |
| tree | e3e6b8e2c8a30daadb38d15190eff8226add3cc9 | |
| parent | 2f45b6e0010ce375e2c52c91a6537c6d6e8bc0a5 (diff) | |
| download | rulesets-16756135ff2b70a47aded26e8ccf33dc249a9f17.tar.gz rulesets-16756135ff2b70a47aded26e8ccf33dc249a9f17.zip | |
I filed the remaining rightsizing work as a DOING task, because what's left is decisions I haven't made rather than work to execute: whether verification.md's honesty core survives the over-verification warning, what to do with interaction.md now that it's the largest rule, whether to cut the TDD rationalization table, and which of my approval gates are preference versus guardrail. The three working docs are a commit behind the two splits, so they get reconciled first.
I also fixed the first-person rule I added earlier today. It said to write in the first person without saying whose voice that is, so it read as an agent narrating work on my behalf. The "I" is mine. I'm the author of record on every commit and review in these repos and they go out under my name, so the voice is me writing about my own work, and an agent never appears in the prose as a separate party. That lives in commits.md, which is symlinked machine-wide, so it covers every project from now on. Pattern #32 in the voice skill carried the same ambiguity and now matches.
I filed work's recurring-loop proposal with three findings, and work's reply corrected the weakest of them. I'd offered either a rare heartbeat or failure-breaks-silence, and both are needed: a failed subagent covers a scan-level failure, but only a heartbeat catches the scheduler dying, since then nothing spawns and there's no failure to report. CronCreate expires recurring jobs after seven days, so a fully silent loop is guaranteed to die quietly.
The session archive covers the day. Always-loaded rules went from about 57,800 tokens to 28,949, path-scoping and the de-duplication were both confirmed against a live session, and two bugs in my own tooling turned up, one of which had killed a work session mid-flight.
| -rw-r--r-- | .ai/notes.org | 2 | ||||
| -rw-r--r-- | .ai/sessions/2026-07-27-17-02-context-engineering-rightsizing.org | 176 | ||||
| -rw-r--r-- | claude-rules/commits.md | 25 | ||||
| -rw-r--r-- | todo.org | 43 | ||||
| -rw-r--r-- | voice/SKILL.md | 2 |
5 files changed, 240 insertions, 8 deletions
diff --git a/.ai/notes.org b/.ai/notes.org index 447c33c..828fde3 100644 --- a/.ai/notes.org +++ b/.ai/notes.org @@ -61,6 +61,8 @@ This section tracks decisions that need Craig's input before work can proceed. ** Current Reminders +- =[2026-07-27]= Finish the context-engineering rightsizing — Craig's explicit ask at wrap. Surface is 57,800 → 28,949 tokens; the remaining work needs *his decisions*, not execution: =verification.md= (C1 — its honesty core vs the Opus 5 over-verification warning), =interaction.md= (3,828 tok, largest remaining), the TDD rationalization table (cut or keep), and D3 the gate separation (which approval gates are preference vs guardrail). Task: "Finish context-engineering rightsizing" in todo.org. Docs in =working/context-engineering-rightsizing/= are one commit behind — reconcile them first. + - =[2026-07-14]= Review the sentry spec (docs/specs/2026-07-14-sentry-workflow-spec.org) — Craig's explicit ask at wrap: strongly suggest he reviews it before ending the next session. All 12 review findings and 10 decisions are resolved and folded in; the spec is open in his Emacs; the READY flip and the [#B] build task both wait on his deep read. ** Instructions for This Section diff --git a/.ai/sessions/2026-07-27-17-02-context-engineering-rightsizing.org b/.ai/sessions/2026-07-27-17-02-context-engineering-rightsizing.org new file mode 100644 index 0000000..54020e4 --- /dev/null +++ b/.ai/sessions/2026-07-27-17-02-context-engineering-rightsizing.org @@ -0,0 +1,176 @@ +* Summary + +** Active Goal + +Review three Anthropic posts on context engineering against what this repo ships downstream, then act on the findings. Ended with the always-loaded rules surface cut from ~57,800 tokens to ~28,949 (plus 13,461 path-scoped), two mechanisms proven in a live session, and two of my own bugs found and fixed — one of which had killed Craig's work session. + +** Decisions + +- *Split rules by blast radius, not by size.* What must hold whether or not you're publishing, and where a violation is permanent and reaches other people, stays always-loaded. Everything recoverable can ride a trigger. That's what let =commits.md= and =testing.md= ship without waiting on any pilot. +- *Goal is output quality first, tokens second* (Craig's correction). Anthropic's 80% was a finding, not a target. P4/Phase 8 (effort reduction) dropped outright for trading quality for cost; P5 (positive framing over prohibition) promoted as the lever that actually targets guardrails working against output. +- *Don't apply the posts additively.* The harness system prompt already carries most of what the Opus 5 guide recommends adding, near-verbatim. Adding it to =claude-rules/= would worsen the duplicate-and-conflict problem the first post opens with. The posts' value here is subtractive. +- *Everything authored in or about the repo is first person* (Craig's instruction), with one carve-out: a comment describing what the code does stays third person, since there the code is the actor. +- *No unmerging home.* Its domains are already separated by tag (24 finances, 18 kit, 17 jrestate). The real problem is a tag namespace flattening four orthogonal axes, and cross-domain priority is a judgment no scheme can make — splitting projects hides the question rather than answering it. + +** Data Collected / Findings + +- *Path-scoping works at user level*, confirmed by =/context= in a live work session: 17 generic rules listed, the three path-scoped ones absent. Deterministic glob match, so no trial needed for that tier. +- *User-level and project-level rules both load, project wins.* work and =.emacs.d= carried 19 byte-identical duplicates, and a stale project copy silently overrode the fresh global rule. +- *My token estimates were 45% low.* Real ratio 2.28 tok/word. =commits.md= was 12,800 tokens, not the ~7,000 I claimed. =/context= reported the true per-file numbers the whole time and I used a word-count estimate because it was easier to compute from inside the repo. +- *Two loading paths, not one.* Memory files arrive via the harness; =protocols.org= and the workflows are read by startup and land in Messages. They shrink by editing the workflow, not by scoping a rule. +- *41 execution/hygiene workflows against 6 discovery/design.* The system is heavily built on the half of the problem that got easier. +- *The instructions don't practice what they demand:* =commits.md= argued terseness at 5,561 words, =interaction.md= bans bold while the rules carry 591 bold markers, =testing.md= argues TDD across eight more rows of rationalizations. +- *Two of my own mechanical guards failed the same day, both certifying success while doing damage.* =wrap-org-table.el= reflowed a table into a worse shape and =lint-org= then passed it; the wrap-teardown hook consumed a two-hour-old sentinel and killed Craig's live work session. + +** Files Modified + +Seven commits, all pushed, velox synced throughout. =6c1ea8b= peer-reasoning rule + Chrome convention + KB probe fix. =0adcb1a= =paths:= frontmatter on the three file-type rules + the lint checker that catches prose/frontmatter mismatch. =7ea1d7b= generic rules no longer ship per project, sweep + gitignored session anchor. =79ed3b0= the three rightsizing docs. =2c664cb= =hooks/session-start-disarm.sh= for the sentinel bug. =d74d98d= docs corrected against live measurements. =931f364= =commits.md= → invariant core + =publish= skill. =2f45b6e= =testing.md= → directive core + =testing-standards= skill, approval-gate signal fixed, first-person directive. + +** Next Steps + +Everything remaining needs Craig's decisions rather than execution — see the =[#B] Finish context-engineering rightsizing= task and the =[2026-07-27]= reminder. In order: reconcile the three working docs (one commit behind), then C1 (=verification.md='s honesty core vs the over-verification warning), =interaction.md=, the TDD rationalization table, and D3 (which approval gates are preference vs guardrail). + +Also open: the work sentry triage split and the recurring-loop proposal, both filed =[#B]= with their reviews. The sentry spec review is still waiting, now two weeks old. + +KB: promoted 2 / consulted no + +* Session Log + +** Startup — 2026-07-27 10:25 CDT + +Ran startup. Rulesets already current; =make install= had nothing new to link; project repo clean at f2609d9 with no upstream drift. =.ai/= synced from templates (no churn — the sync is a no-op mirror refresh in this repo). Previous session wrapped cleanly (no session-context anchor present). + +Startup signals: 6 top-level tasks unreviewed for >7 days; roam inbox empty; KB at 106 =:agent:= nodes but the best-practices node path resolved empty (=rg -l 'agent-kb-best-practices'= found nothing — worth checking whether that node exists); no spec-sort or host-identity flags; language-bundle sync silent. + +Five new inbox handoffs arrived since the last wrap. Read all five and ran the skeptical review on each before surfacing dispositions. + +Disposed of one without asking: home's 07-26 10:21 file was a pure FYI acknowledging that the parked MCP-registry spec decision and the separate memory-auditor track matched its handoff. It asked for nothing, so it needed no reply and no approval — deleted it. Four remain, all shared-asset or convention changes, all waiting on Craig's approval per the inbox engine's core §2. + +Skeptical-review conclusions on the four pending: + +- *archsetup publish-lock* — design sound, acceptance checks testable. Three gaps: TTL sizing has to survive an indefinite human approval wait (refresh-on-re-entry covers the agent, not a Craig who steps away); the blocked second session's behavior (wait / defer / stop) is unspecified; and it's a real build, not a quick edit. Recommend filing =[#B] :feature:=. +- *home Chrome convention* — protocols.org already prefers google-chrome-stable over xdg-open, so the new parts are =--new-tab=, multi-URL, and the confirmation line. The confirmation half contradicts the existing =&>/dev/null &= form, which discards exactly the message to be verified. Recommend applying with a foreground-when-running / background-on-cold-start reconciliation. +- *work sentry triage correction* — Craig's 07-27 correction supersedes his 07-21 ruling; today's work fire missed a Hayk DM and a Kostya PR-review request. The gap is that the current rule excludes by category (mail / messenger) and the new split is work-vs-personal, which category can't express. Shipping plugins: cmail, personal-gmail, personal-calendar, telegram, github-prs — no general work-mail plugin, so work's source is project-specific. A denylist of personal plugin names fails open on the next personal source added; a per-plugin eligibility declaration is the durable shape, and that's a design call. Recommend filing =[#B] :bug:= (Major × most-users-frequently = P2). +- *work peer-reasoning rule* — approved exact text, well-formed. Two notes: it's a reasoning contract in a file scoped to communication style (the framing line should widen), and "process serves the outcome" sits one reading away from licensing deviation from the mandatory gates. Its own wording says surface-before-proceeding, so no edit needed, but that's the line to watch. Recommend installing as written. + +** Inbox pass applied — commit 6c1ea8b + +Craig approved all four dispositions plus the probe fix. Two corrections from him along the way: I had inverted the render-merge guard (numerals belong to the options list, dashes to every other enumeration in the same message — I did the reverse), and processed items shouldn't be left sitting in =inbox/=. + +Shipped: the peer-reasoning section at the top of =claude-rules/interaction.md= with the file's framing line widened; the Chrome convention rewritten in canonical =protocols.org=; the KB best-practices probe switched from a content grep to a filename =find=. Filed two =[#B]= tasks (sentry triage split, repository publish-lock), both stamped =:LAST_REVIEWED: 2026-07-27=. Swept the 40-file =PROCESSED-*= backlog out of =inbox/= along with the four handoffs; =inbox-status= now reports 0. Replies sent to archsetup, home, and work. + +*The review caught my own error.* I had written that Chrome's confirmation line prints to stderr and told every project to capture it with =2>&1=. It prints to *stdout*; stderr is empty. Verified both directions on ratio before correcting. An agent following the original text would have captured stderr, seen nothing, and concluded the tab failed to open — the exact silent-failure shape as the KB probe it shipped alongside. I asserted a stream rather than checking it, inside the same change that told others to verify. Side effect: four =about:blank= tabs opened in Craig's live browser during the check. + +Deliberate departure recorded: =route_recommend= returned =work strong= for the sentry task, but the work happens in rulesets' canonical =sentry.org=, so it's a local keeper and I left it unstamped rather than letting the wrap router offer to ship it away. + +Possible KB promotion at wrap: an identifier that lives only in a filename is invisible to a content search, and the lookup fails *silently* rather than erroring. Adjacent to the existing enumerate-vs-discover node but a distinct failure surface. Two instances in one commit (the KB probe, the Chrome stream) argue it generalizes. + +Startup-workflow bug found while checking the KB nudge: Phase A resolves the best-practices node with =rg -l 'agent-kb-best-practices' "$ra"=, which greps file *content*. The node's slug lives in its filename, so the probe returns empty and the contribute nudge points at nothing — in every project, every session. The node exists at =~/org/roam/agents/20260620232112-agent-kb-best-practices.org=. Synced-workflow change, so it waits on approval too. + +** Pushed and synced velox + +Pushed 6c1ea8b to origin/main (ahead-only, reconciled immediately before). On ratio, so velox needed the pull: it fast-forwarded and =make install= linked three things it had been missing since 2026-07-25 — the Codex =hooks.json=, =rulesets-write-boundary.py=, and =git-worktree-gate=. That drift is exactly the one-time-setup case =daily-drivers.md= names: the files traveled with the pull, but nothing re-runs the installer, so the symlinks only land where someone runs it. A new Codex session on velox will now hit the hook review/trust prompt, which was already on the 2026-07-25 next-steps list. + +** Context-engineering rightsizing — analysis and rollout plan + +Craig supplied three Anthropic posts (the 2026-07-24 Claude 5 context-engineering post, the Opus 5 prompting guide, the 2026-07-06 Fable field guide) and asked for a review, proposals, and a consistency audit of what this repo ships downstream. Then he reframed twice, and both reframes were better than the question I'd been answering. + +*First reframe:* consider the files as *his prompts*, not my context. That changed the finding. My first pass measured the always-loaded surface (32,123 words — =claude-rules/= 25,386 + =protocols.org= 6,620 + CLAUDE.md 117, roughly 40k tokens before the user's first word) and proposed shrinking it. Read as a map he hands every project, the finding is different: 41 execution/hygiene workflows against 6 discovery/design, seven to one. That ratio was right when the risk was the model doing things wrong. The field guide's claim is the bottleneck moved to the human's ability to clarify unknowns, so the system is heavily built on the half that got easier. + +*Second reframe:* metrics per claim, not one go/no-go. Turns the rollout into a set of separable testable claims rather than one bet. + +Three checkable "doesn't practice what it demands" findings: =commits.md= argues terseness at 5,561 words (longest file in the set); =interaction.md= bans bold in chat while the rules carry 591 bold markers; =testing.md= mandates TDD then argues eight more rows against rationalizations. + +*The finding that changed the plan:* the harness system prompt already carries most of what the Opus 5 guide recommends adding — its task-scope block, correction-narration block, and subagent cap are present nearly verbatim, and post 1's replacement comment guidance is present as the post's own new wording. So applying the posts additively would make the duplicate-and-conflict problem worse. The posts' value here is subtractive. It also exposes a third dedup axis nobody has audited: =claude-rules/= against the harness prompt, invisible from inside the repo. + +*Pilot selection rule* (the part that matters more than the list): the six pilot files were chosen because a silent miss is *detectable*, not because they're small. Four have a mechanical checker (=lint-org= =org-table-standard=, spec-board grep, =spec-review=), two produce an error Craig sees in seconds. =daily-drivers.md= and =emacs.md= were considered and held back — low risk, but a miss surfaces too slowly to learn from inside the trial window. + +Artifacts in =working/context-engineering-rightsizing/=: =proposals.org= (P1-P6, conflicts C1-C2, the from-your-side-of-the-desk section), =rollout.org= (Phases 0-8, decisions D1-D7, target trajectory), =metrics.org= (claim-by-claim testability, pilot go/no-go with the denominator rule, turn-back vs abandon triggers). + +Two honesty notes carried into the docs: I have a stake in arguing my own instructions should be shorter, so the plan weights mechanical detectors over my self-report; and about half the posts' claims aren't testable here without an eval harness, so those are labelled judgment rather than measurement so a future session doesn't mistake an adopted opinion for a tested result. + +Not started. Awaiting D1 (confirm pilot set) and D2 (skill index in the core). + +** Path-scoping shipped (0adcb1a) and work pre-synced + +The session's biggest finding: Claude Code scopes a rule by a =paths:= field in YAML frontmatter, and none of the 20 rules had one — even though three already declared a file-type scope in their =Applies to:= prose line. So =todo-format.md= (4,494), =org-tables.md= (464), and =emacs.md= (923) loaded into every session in every project, contradicting their own first line. 5,896 words. Fixed by adding the frontmatter, plus a =lint.sh= checker that warns when prose names a concrete extension without matching frontmatter (flags exactly those three, nothing else), plus teaching the heading check to skip a frontmatter block. Always-loaded rules surface: 25,386 → 19,505. + +Also confirmed from the docs: user-level and project-level rules *both* load, and project rules take priority. So work and =.emacs.d= carry 19 byte-identical duplicate copies, and a stale project copy overrides a fresh global one — which is exactly what was happening to work's =interaction.md= between this morning's commit and its next startup. + +Pre-synced work via =scripts/sync-language-bundle.sh ~/projects/work= (rulesets' own installer, run early rather than waiting for work's startup) so Craig's next work session is a valid test rather than one running the set it loaded before the sync. Verified: all four files now match canonical, frontmatter present, and work's =.claude/= is gitignored there so nothing was dirtied. + +Open question the next session answers: does =paths:= frontmatter apply to *user-level* rules or project-level only? The docs don't draw the distinction. =/context= in a fresh session settles it — if =todo-format.md= is absent from Memory files until an org file is opened, it works. If it's listed, the frontmatter is inert (no harm) and semantic skills are the only route. + +Not done: the double-load fix. Removing the 19 duplicates means changing what =install-lang= pushes into projects, and there may be a teammate-facing reason for them. Surfaced as Craig's call, not urgent — wasteful, not harmful. + +** De-duplicated the rules layer, unblocked sync (7ea1d7b, 79ed3b0) + +Craig confirmed no teammates depend on the per-project rule copies, so I removed them. =install-lang.sh= no longer copies the generic rules; =sync-language-bundle.sh= sweeps the ones earlier installs left, guarded on the global rule existing so a machine mid-bootstrap isn't stranded with none. Swept 20 files each from work and =.emacs.d=, leaving only their language rules plus work's =publishing.md= overlay. Three existing tests encoded the old contract and were rewritten; the generic-drift test now asserts sweep-not-repair, which is the stronger fix since the drifted copy outranked the global rule while it existed. Four new tests cover the sweep, the two keep-cases, and the no-global-rule guard. + +Also gitignored =.ai/session-context.org= and =.ai/session-context.d/=. This repo tracks =.ai/=, so the live anchor read as untracked all session and =git-worktree-gate= reported rulesets sync-blocked — meaning every other project skipped its rulesets pull until wrap, every session. Craig spotted the blocked state and inferred it was why I pre-synced work; it wasn't (rules load at launch, before the startup sync runs, which was the real reason), but chasing his inference found the anchor problem, which was the better bug. + +Corrections from Craig this stretch: the goal is output quality first, token reduction second — my docs led with the wrong number and P4 (effort reduction) should be demoted or dropped since it trades quality for cost. And all authored prose goes first person; I amended the first commit rather than leaving it. Code comments stay third-person by agreement, since they describe what the code does for the next reader. + +Docs not yet updated for either the goal reordering or the last two hours of findings (path-scoping, the double-load, the harness overlap). That's the next task. + +** Inbox: archsetup ack + +archsetup acknowledged the publish-lock acceptance and the three implementation gaps, confirming the decision stays closed on its side. Pure FYI, nothing asked, no reply owed. Deleted it. Inbox back to zero. + +** Killed Craig's work session with my own hook, then fixed it (2c664cb) + +Craig's 13:20 work session was blocked repeatedly and then had its terminal closed under it. The cause was mine, from Saturday's clean-wrap work. + +=wrap-it-up= drops =/tmp/ai-wrap-teardown-<project>= so the =Stop= hook tears down once the wrap certifies clean. I deliberately made a failed certification *preserve* the sentinel, so a wrap blocked by a dirty tree could retry on a later stop. I never bounded that retry to the session. work's 11:37 wrap left an uncertified sentinel armed; the 13:20 session's stops were all blocked by it failing certification; then startup's two commits (task filing, template sync) made the tree clean, the next stop certified, and =cj/ai-term-quit= killed the tmux session mid-work. + +Two others were armed and dangerous at the same moment: archsetup's since Saturday 15:02 on a live attached terminal, and home's from 13:21 on a live session. Disarmed all three by hand (backed up to =/tmp/disarmed-sentinels=) before writing any fix, since both were minutes from the same fate. + +Fix: =hooks/session-start-disarm.sh= clears the project's sentinels at =SessionStart= — a new session means the wrap that armed one is gone. Within-session retry is untouched (the hook only runs at session start) and a test pins that so the deliberate behavior isn't lost to the fix. Four tests on the disarm including project-scoping, one on the retry. Wired into =.claude/settings.json=, installed on both machines, =wrap-it-up.org= documents the session-scoping with the worked failure. + +Diagnostic note worth keeping: I found it by reading work's own crashed session anchor, which showed startup completing normally and then stopping dead, plus its git log showing two commits at 13:21 — the exact moment the tree went clean. The anchor being left behind by the interrupted session is what made the timeline reconstructable. That's the crash-recovery purpose earning itself. + +** /context settled both open questions; docs corrected (d74d98d) + +Craig ran =/context= in work. Memory files lists 17 generic rules; =todo-format.md=, =org-tables.md=, and =emacs.md= are absent, and only =python-testing.md= and =publishing.md= come from the project's own rules dir. So *path-scoping works at user level* and *the de-duplication holds*. Both were open. + +Three corrections the live numbers forced: + +1. *My token figures were low by ~45%.* Real ratio is 2.28 tok/word, not the ~1.3 I assumed. =commits.md= is 12,800 tokens (I said ~7,000); =claude-rules/= was ~57,800/session before today, now 44,410, with 13,390 path-scoped out. Worth naming the actual error: =/context= reports per-file token counts and I used a word-count estimate instead because it was easier to compute from inside the repo. The instrument existed the whole time. +2. *Two loading paths, not one.* Memory files arrive via the harness at session start. =protocols.org= and the workflows are *read by startup*, so they land in Messages and never appear under Memory files. They shrink by editing the workflow, not by scoping a rule. My "always-loaded surface" number conflated them. +3. *The harness's own suggestion* names =commits.md=, =testing.md=, =MEMORY.md= as the top three to prune — independently the same Phase 4 list I'd proposed. + +Because a glob match is deterministic, the remaining work splits: path-scopable rules ship with no trial (=docs-lifecycle.md= on =docs/**= is next), and only semantic-condition rules need the skills route and the stop conditions. =commits.md= is the real test there — largest single item, and almost all publish machinery that only applies when a commit is in play. + +Recorded a caution the confirmation doesn't cover: path-scoping fires on a *read* of a matching file, so creating a new org file from scratch never triggers =todo-format.md=. Edits are safe (Edit requires a prior read). + +Also folded in Craig's goal correction (quality first, tokens second): P4/Phase 8 dropped outright since lowering effort trades quality for cost, P5 promoted since positive-framing-over-prohibition is what targets guardrails working against output. + +** Split commits.md: 12,800 tokens → 2,342 always-loaded (931f364) + +Craig picked the commits.md split over docs-lifecycle after I checked the latter and found I'd overstated it — =docs-lifecycle.md= scopes to "any project carrying a docs/ tree," a *project-level* condition a glob can't express, and 2 of its 6 trigger points are creation cases a read-triggered path rule misses. Only three rules ever named a concrete extension and all three are already converted, so there is no other clean path-scope candidate. + +The split line is *blast radius*, not size. Stayed always-loaded (1,027 words / ~2,342 tokens): author identity, the no-AI-attribution ban, the generated-document byline rule, the public-artifact content-scope rules, and "If You Catch Yourself." Moved to =publish/SKILL.md= (4,871 words): message format, Voice and Focus, PR description structure, Review and Publish Steps 0-2, the three review shapes, hook authorization, merge strategy, the pre-commit checklist. + +Why that line: if the skill fails to trigger I don't know the flow and have to be told — visible and recoverable. I don't silently commit with AI attribution, because that guard never moved. Only the recoverable half rides the skill-triggering bet, which is what let this ship without waiting on the pilot. + +*Verified by using it.* The skill registered mid-session and I invoked =/publish= to publish its own commit; it loaded with the full flow present. Content conserved and checked rather than assumed: 5,561 words in, 5,898 across both files (delta = frontmatter + the pointer added to the core). Repointed five cross-references in =voice=, =review-code=, =inbox.org=, and =no-approvals.org= that named moved sections. + +Always-loaded rules surface: 44,410 → ~33,950 tokens. Started the day at ~57,800. + +Noted and deliberately not done: =publish/SKILL.md= is a single 4,871-word blob, and both posts argue a long skill should use progressive disclosure internally. It loads on demand now, which is the win worth taking; splitting it further is its own change. + +Also surfaced: the Step 2 =.ai=-tracking heuristic misfires here. It reads tracked =.ai/= as "shared team repo → skip the approval gate," but rulesets tracks =.ai/= as a committed mirror while being a private single-user repo. I kept asking rather than skipping, and flagged it to Craig. + +** testing.md split, gate fixed, first-person directive added (2f45b6e) + +Three changes. *testing.md split* the same way as commits.md — by what has to be resident, not by size. Core keeps TDD-is-default and the three-category requirement (347 words), because those fire *before* any code is written, which is exactly when no skill has been summoned. Everything else → =testing-standards= skill (2,903 words): characterization recipes, per-category detail, property/mutation testing, pyramid, integration rules, naming, test-quality and mocking rules, coverage targets, spike exception, anti-patterns. + +*Approval-gate fix.* The publish flow decided whether to ask by checking whether =.ai/= is tracked, as a proxy for "team repo." Wrong in the direction that matters: rulesets, home, and work all track =.ai/= and all three are private single-user repos, so the rule skipped the gate on Craig's three most-used projects. Now checks whether any remote is on a host other than cjennings.net. Verified both directions including a synthetic GitHub remote. Every current project → gate applies, which matches how the flow has actually been run all session. + +*First-person directive* added to the always-loaded core, at Craig's instruction. One existed for commit bodies/PR prose but it moved into the publish skill, and it never covered code comments at all. Now: everything authored in or about the repo is first person, with one carve-out — a comment describing what the code *does* stays third person, since there the code is the actor. + +Also split =publish/SKILL.md= internally: PR descriptions + the three review shapes → =references/pull-requests.md=, since a plain commit never needs them. SKILL.md 5,012 → 3,888 words. + +*Surface: ~57,800 tokens this morning → ~28,949 always-loaded now* (plus 13,461 path-scoped). Largest remaining: =interaction.md= 3,828, =verification.md= 3,388, =commits.md= core 2,804, =subagents.md= 2,373, =cross-project.md= 2,305. + +Risk recorded rather than buried: testing.md's margin is thinner than commits.md's. If =testing-standards= fails to trigger mid-test-writing I lose the mocking-boundary rules — a quality regression, visible in review, but a real bet where commits.md's moved half was purely procedural. Also moved the TDD rationalization table rather than cutting it; the posts say that kind of over-argument is counterproductive now, but deleting Craig's defense against me skipping TDD is his call. diff --git a/claude-rules/commits.md b/claude-rules/commits.md index 51c452a..3283b0a 100644 --- a/claude-rules/commits.md +++ b/claude-rules/commits.md @@ -91,13 +91,24 @@ Edge case: when one of these files *is* the change (a commit in the rulesets rep **Tooling-path enumeration is the same leak.** Citing a rule as authority isn't the only way the tooling layer leaks into history. A commit whose *content* must name these paths — a `.gitignore` adding `.claude/`, `CLAUDE.md`, `.ai/` — has unavoidable, correct file content, but its *message prose* must not enumerate them ("chore: ignore .claude tooling, CLAUDE.md, and session files"). On a public or shared-remote repo that enumeration exposes the tooling layer's structure in the log just as a citation would. Name the category instead: "chore: extend gitignore for local tooling and build artifacts". The same holds for any incidental mention, not only `.gitignore` commits. Two exemptions: a commit whose change *is* one of these files (the edge case above), and private single-user repos with no shared remote, where the history is the project and there's no third party to leak to. -## Write in the first person - -Everything I author in or about this repo is first person: code comments, -commit messages, PR descriptions, PR review comments, and any note that lands -in the repo or its history. When I made a choice, say so as a choice — "I swept -the copies rather than repairing them, because a drifted copy outranked the -global rule" beats "the copies are swept rather than repaired." +## Write in the first person, as Craig + +Everything authored in or about this repo is first person: code comments, commit +messages, PR descriptions, PR review comments, and any note that lands in the +repo or its history. State a choice as a choice — "I swept the copies rather +than repairing them, because a drifted copy outranked the global rule" beats +"the copies are swept rather than repaired." + +**The "I" is Craig.** He is the author of record on every commit, comment, and +review in his repos, and these artifacts go out under his name. So the voice is +his, writing about his own work — not an agent narrating what it did on his +behalf. Never write the agent into the prose as a separate party: no "Craig +asked me to", no "I filed this for Craig", no "needs Craig's decision". Where a +decision is still open, it is *his* open decision, written as "I haven't decided +whether…" or "this needs a call I haven't made yet." + +The same holds for anyone else's work. Name them ("Kostya's PR #116 did X"), +because they are a third party. Craig is not. Third-person constructions like "This change introduces X" or "This PR restores Y" read as press-release self-narration. The commit is the change, so it does @@ -39,6 +39,49 @@ Tags are assigned and refreshed by =task-audit=; =task-review= keeps them honest * Rulesets Open Work +** DOING [#B] Finish context-engineering rightsizing :refactor: +:PROPERTIES: +:LAST_REVIEWED: 2026-07-27 +:END: +Started 2026-07-27 from three Anthropic posts Craig supplied. Always-loaded rules went from ~57,800 tokens to ~28,949, plus 13,461 path-scoped. Shipped: =paths:= frontmatter on the three file-type rules, the per-project generic-rule de-duplication, =commits.md= split into a 2,804-token invariant core plus the =publish= skill, =testing.md= split into a 347-word directive plus the =testing-standards= skill, the approval-gate signal fixed from =.ai/=-tracking to remote host, and the first-person directive. + +*What remains is Craig's decisions, not execution.* Each of these needs him: + +1. *C1 — =verification.md= (3,388 tok).* The Opus 5 guide says explicit verification instructions cause over-verification and should be removed. His standing direction is never guess, always check. My read: the honesty core (don't claim a green suite you didn't run) stays and shortens, the process injection (green baseline, suite-as-its-own-step) moves to the publish skill. His call — and it's the rule closest to a preference he's stated outright. +2. *=interaction.md= (3,828 tok, now the largest).* Carries genuinely universal output constraints (no popup menus, no reverse-video markup) plus the new peer-reasoning contract. Splitting it means deciding which parts must fire on every turn. +3. *The TDD rationalization table.* Moved to =testing-standards= rather than cut. The posts argue that kind of over-argument is counterproductive on current models. Deleting his defense against me skipping TDD is his call. +4. *D3 — the gate separation.* Which approval gates are preference (he wants to see what goes out under his name) versus guardrail (written when the worst case was worse). They read identically in the files; only he can tell them apart. Highest-leverage input remaining. + +*Do first:* the three docs in =working/context-engineering-rightsizing/= are one commit behind — they were corrected at d74d98d, before the two splits and the gate fix. Reconcile before deciding anything from them. + +Risk on the record: =testing.md='s margin is thinner than =commits.md='s. If =testing-standards= fails to trigger mid-test-writing the mocking-boundary rules are lost — a quality regression, visible in review, but a real bet where =commits.md='s moved half was purely procedural. + +** TODO [#B] Recurring-loop mechanics as a shared rule :feature: +:PROPERTIES: +:LAST_REVIEWED: 2026-07-27 +:END: +Work proposes promoting the pattern behind its 2026-07-27 auto-mode triage-intake into a standing rule covering all recurring agent tasks: fixed interval via =CronCreate= rather than dynamic self-pacing, a subagent per firing so raw scan output never reaches the orchestrator, silence with no heartbeat when subagent-backed, and accumulate-don't-mutate between closes with explicit "close the X" / "stop the X" verbs. Likely touches =triage-intake.org= auto mode, =inbox.org= monitor mode, and a new short rule in =claude-rules/= so individual workflows point at one definition of cadence, isolation, and silence. + +Points 1, 2, and 4 mostly promote existing practice. Point 3 changes documented behavior and needs a real decision. Three findings from the skeptical review, all to resolve before this ships: + +1. *Point 1 is too absolute.* Fixed interval is the right default, but not the right universal. A loop waiting on unpredictable external state (a CI run, a deploy queue) should pace to how fast that state actually changes, which is what dynamic scheduling exists for. Write it as "default to a fixed interval; use dynamic pacing only when polling external state whose timing you can't predict." +2. *Point 2 collides with a standing instruction.* Craig's harness prompt says not to spawn subagents unless he requested it. Making subagent-per-firing the standing pattern needs that reconciled explicitly — the honest reading is that asking for the loop *is* the request, and the rule should say so rather than leaving two instructions to fight. The isolation argument itself is sound and matches the Opus 5 guidance (delegate for genuinely independent, sizeable work). +3. *Point 3 has a silent-failure hole.* Removing the heartbeat means a loop that died looks exactly like a loop quietly finding nothing. "The subagent completing is proof it ran" only holds if a *failed* subagent still surfaces. Either keep a rare heartbeat (hourly, not per-fire) or specify that failure always breaks silence. Suppressing success is fine; suppressing failure is not. + +Also underspecified: what counts as "signal" for a subagent-backed loop. And the silent-until-signal spec (=docs/specs/2026-07-20-silent-until-signal-monitors-spec.org=, IMPLEMENTED) documents the per-fire heartbeat, so it needs a superseding history line rather than a silent contradiction. + +*** 2026-07-27 Mon @ 16:55 Work accepted all three findings and sharpened point 3 + +Work agreed without pushback and corrected my either/or on point 3, which was too weak. Both mechanisms are needed, not one: a failed or hung subagent breaking silence covers a *scan-level* failure, but only a periodic heartbeat catches the *scheduler* dying — because in that case nothing spawns at all and there is no failure to report. Heartbeat rare (hourly), not per-fire. + +The live consequence makes it urgent rather than theoretical: =CronCreate= auto-expires recurring jobs after seven days, so any fully-silent loop is *guaranteed* to die quietly. Work's auto-triage loop is running under exactly that contract right now. + +Work also added the reason that makes point 2's carve-out principled and should go in the rule text: the subagent exists to keep N sources' worth of raw scan output out of the orchestrator across a multi-hour loop, not to parallelize. + +Craig's call on point 3 is pending; work is surfacing it and will send the answer. + +Source: work handoff 2026-07-27 14:51, reply 16:55. + ** TODO [#B] Sentry triage split — work mail and messengers in, personal mail out :bug: :PROPERTIES: :LAST_REVIEWED: 2026-07-27 diff --git a/voice/SKILL.md b/voice/SKILL.md index cb378a2..cdf7874 100644 --- a/voice/SKILL.md +++ b/voice/SKILL.md @@ -300,7 +300,7 @@ These patterns carry Craig's writing voice. Most apply in **both** prose mode (e ### 32. First-Person Voice Rewrite [personal] -**Rule.** Rewrite impersonal third-person publish-artifact bodies into first person ("I added X", "I kept Y because..."). The commit subject line stays imperative per Conventional Commits. Skip for mechanical changes where the subject alone carries the message. +**Rule.** Rewrite impersonal third-person publish-artifact bodies into first person ("I added X", "I kept Y because..."). **The "I" is Craig**, who is the author of record and whose name the artifact goes out under — not an agent narrating work done on his behalf. Cut any construction that writes an agent in as a separate party ("Craig asked me to", "I filed this for Craig", "needs Craig's decision"); an open decision is his own, written as "I haven't decided whether…". The commit subject line stays imperative per Conventional Commits. Skip for mechanical changes where the subject alone carries the message. See `voice/references/voice-profile.org` §32 for problem, basis, examples, and history. |
