aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
...
* chore(ai): archive session record + lint follow-upsCraig Jennings2026-05-302-0/+52
|
* fix(drill-deck): cut leakage false positives and codify source/date conventionsCraig Jennings2026-05-3010-16/+274
| | | | | | | | Health ran the new leakage check on a 43-card deck and hit two false-positive classes. The check read the whole card body, so a =Source: <label> — <url>= citation line inflated the front/back overlap whenever the URL slug repeated the question's words. Range/category cards ("What are the HbA1c ranges across normal, prediabetes, and diabetes?") tripped it too, because the question's categories echo in the answer even though the recalled content is the numbers. drill-deck-stats.py now routes leakage through an is_leaky helper. It strips =Source:= and created-date lines before computing overlap, and exempts a card when the answer carries a numeric range or threshold the question lacks. leakage_ratio itself is unchanged, so the genuine-restatement case still flags. Two body conventions now hold: a =Source:= citation goes at the end of a card after two blank lines, and no created/added date goes on a card. drill-to-anki.py now strips =Created:= / =:CREATED:= lines from the back as a backstop, and the workflow's Phase C removes them from the source during the rewrite. I added tests for the source-strip, the numeric carve-out, and the created-line strip, and documented all of it in drill-deck-review.org.
* fix(startup): exclude Python cache from script sync and restore script exec bitsCraig Jennings2026-05-3010-2/+4
| | | | | | From health's handoff: the startup =.ai/scripts/= sync was dragging pytest build artifacts into every consuming project. =rsync -a= copies by disk presence, not git status, so the =__pycache__/= and =.pytest_cache/= that rulesets' own pytest leaves in =claude-templates/.ai/scripts/tests/= rode along to each project's tree even though the root =.gitignore= already keeps them out of rulesets' commits. Phase A's scripts rsync now excludes =__pycache__=, =.pytest_cache=, and =*.pyc=. A project that already received the cache has to remove it once by hand, since =--delete= leaves excluded paths in place. I noted that in the startup doc. Health also flagged that =inbox-send.py= kept needing a manual chmod. The cause wasn't rsync dropping the bit. Four shebang scripts (=inbox-send.py=, =cj-scan.py=, =cj-remove-block.py=, =eml-view-and-extract-attachments.py=) were committed mode 100644, so rsync faithfully copied the wrong mode. I set the exec bit on all four so the synced copies are runnable.
* chore(ai): update session logCraig Jennings2026-05-301-0/+12
|
* feat(drill-deck): add authoring-quality checks and a card-authoring sectionCraig Jennings2026-05-306-144/+826
| | | | | | | | I researched spaced-repetition best practices (Wozniak's twenty rules, Matuschak's prompt-writing guide, Nielsen, the Anki and FSRS docs) and folded the findings into the drill-deck pipeline. drill-deck-stats.py now checks authoring quality on top of structure. Two checks block: answer leakage (a question that echoes >= 80% of its own answer's content words tests recognition, not recall) and duplicate / near-duplicate fronts (confusable cards interfere). Three checks warn without blocking, surfacing rewrite candidates without failing the gate: overloaded backs, list-shaped backs, and binary yes/no prompts. The fuzzy thresholds live in constants at the top of the script, so a real deck that trips false positives can be tuned. I pulled the card-parsing into a parse_cards helper that captures each card's body, and added focused tests for every new helper plus CLI coverage of the leaky, duplicate, and notes-only cases. drill-deck-review.org gains a Card Authoring Principles section (the why behind the canonical shapes, with sources), a person-card splitting path bounded by the :ID:-preservation rule, a Phase B cost-benefit-removal and leech-reformulation disposition, and a scheduling-is-Anki-side note so a future editor doesn't try to encode FSRS retention in the org source. I left out cloze cards (would need a second note type), per-card tractability targeting and retention encoding (Anki-side telemetry that never reaches the source), and on-face source-stamping (the converter strips those drawers by design). Each is noted with its reason.
* chore(ai): refresh session log and workflow-state markerCraig Jennings2026-05-302-1/+70
|
* test(scripts): cover drill-to-anki internals, broadcast, and daily-prepCraig Jennings2026-05-3012-2/+822
| | | | | | | | I backfilled the gaps left after the flashcard work landed. drill-to-anki.py had tests only for its two default helpers. I added coverage for the core parser and its pieces: parse (section-to-tag mapping, drawer-only body, blank trimming, multiline join, no-card input), strip_org_metadata (drawer and planning-line stripping, unclosed drawer), section_to_tag, escape_html, and the deterministic stable_id. I also filled the remaining drill-deck-stats / drill-deck-diff-ids branches (missing-title and PROPERTIES-mismatch warnings, the appeared-IDs note path). I added test_cross_project_broadcast.py for the two scripts that had none here: is_broadcastable / discover (SEARCH_ROOTS pointed at a tmp tree) / sender_project / inbox_send_path, plus an ERT suite for daily-prep-agenda.el (dp-iso-date, dp-bucket with the clock pinned, dp-format-entry, and dp-collect end to end on a temp org file). daily-prep-agenda.el needed one change to be loadable under ERT: its batch entrypoint fired on any load. I gated it behind dp--cli-invocation-p, the same readable-files check lint-org.el already uses, so requiring the file for tests no longer runs the extractor. A real invocation with a file argument still fires. A no-argument run now no-ops instead of printing an empty header.
* chore(scripts): add drill-deck stats, diff-ids, and sync wrapperCraig Jennings2026-05-3014-48/+1340
| | | | | | I incorporated the flashcard-tooling bundle from the work project's deck-review workflow, validated there against a 93-card deck. Three scripts now live under .ai/scripts/: drill-deck-stats.py (pre-rewrite inventory plus a gate that warns on stray *** Answer headers, missing :ID:, non-prompt headings, and #+TITLE jargon like "org-drill"), drill-deck-diff-ids.py (SRS-state preservation check that flags any :ID: lost across a rewrite), and drill-deck-sync (bash wrapper chaining stats, optional diff-ids, then drill-to-anki, writing to ~/sync/phone/anki/ only when the gates pass). The drill-deck-review.org workflow gains a Helper Scripts section and references the scripts from its phases. I reconciled its output-path prose with the drill-to-anki default that just moved to ~/sync/phone/anki/, so it no longer claims the script still defaults to ~/sync/org/drill/. I added tests for both Python scripts (pure logic plus CLI gate behavior) and a bats suite for the wrapper's guard paths. The clean end-to-end sync path stays uncovered since it needs uv-resolved genanki.
* feat(drill-to-anki): default to phone sync dir and basename deck nameCraig Jennings2026-05-304-50/+114
| | | | | | Two default-behavior tweaks from real use. Output now defaults to ~/sync/phone/anki/ instead of ~/sync/org/drill/. The .apkg is a mobile-Anki artifact the phone picks up from its sync dir, while the org source stays in the project. Deck name now defaults to the raw input basename, case preserved. That drops the #+TITLE preference, which leaked tool-name jargon like "DeepSat org-drill flashcards" into the Anki deck list. The --deck and --output flags still override both. I dropped the now-unused title_from_org helper and added a test covering the two defaults. genanki is stubbed in the test since uv resolves it only at runtime.
* chore: sync .ai mirror with canonical (drill-deck-review)Craig Jennings2026-05-302-0/+212
|
* feat(workflows): add drill-deck-review + extend drill-to-anki scriptCraig Jennings2026-05-304-0/+266
| | | | | | | | I added a drill-deck-review workflow that walks an org-drill deck end-to-end: question-form audit on every heading (so the heading is the prompt, not the answer), content-accuracy audit via subagent against project source-of-truth, source rewrite preserving SRS state, regenerate to ~/sync/phone/anki/. The workflow covers three card families (acronym/concept, person, talking-point) and codifies the person-card pattern as "Who is X? Tell me about their Y." where X is a role descriptor that doesn't name the person and Y is the topical anchor from the answer body. The drill-to-anki.py script picks up a new strip_org_metadata helper that drops :PROPERTIES: drawers and SCHEDULED/DEADLINE/CLOSED planning lines from the rendered Anki back. Org-drill needs both in the source for SRS state and review scheduling; Anki cards shouldn't show them. INDEX entry under "On-demand utilities" wires the trigger phrases.
* chore(todo): fix lint findings on todo.orgCraig Jennings2026-05-302-16/+7
| | | | Relocate two LAST_REVIEWED property drawers to sit directly under their headings (and after the CLOSED line on the DONE task) so org reads them as real properties instead of inert body text. Convert the dangling #16 custom-id link to a fuzzy heading link, since the target heading carries no CUSTOM_ID. The two remaining misplaced-heading warnings are verbatim org markup inside prose, so they're false positives and stay as-is.
* docs(ai): populate Summary section in archived session recordCraig Jennings2026-05-291-1/+41
| | | | | | | | git mv in the prior wrap-up commit (0237465) renamed the index entry without re-staging the disk content, so the archive landed with an empty Active Goal and missing Decisions, Findings, Files Modified, and Next Steps sections. This follow-up commits the populated Summary that was already on disk at wrap-up time.
* chore(ai): archive session record + lint follow-upsCraig Jennings2026-05-292-0/+7
| | | | | | | Multi-arc session: no-approvals batch, codex backlog triage, page-signal + broadcast infra, voice profile Phase 1 + Phase 2 with SKILL.md split, process-inbox formalization, wrap-it-up inbox sanity check.
* fix(wrap-it-up): inbox sanity check exempts lint-followups.orgCraig Jennings2026-05-292-8/+22
| | | | | | | | | | | | | | | | The inbox sanity check I added in 8424e8f counted lint-followups.org as unprocessed, which surfaced as a false alarm during this session's wrap-up. lint-org writes its judgment items into inbox/lint-followups.org earlier in the same wrap-up workflow by design. The file is a pipeline artifact for the next morning's daily-prep, not a handoff that needs the value gate. The find filter now excludes .gitkeep, lint-followups.org, and PROCESSED-* prefixes. The Validation Checklist line names the same expected-artifacts list explicitly. Smoke test: post-fix count is 0 in the rulesets project (where lint-org just wrote 5 judgment items into inbox/lint-followups.org).
* docs(cross-project-broadcast): codify capability-and-rule cadence guidelineCraig Jennings2026-05-292-2/+28
| | | | | | | | | | | | | | | | | | | | | | | Adds one bullet to When NOT to Use and a new Cadence Guideline section that names the per-commit-broadcast anti-pattern explicitly. The new section lays out the five reasons broadcasts stay capability-and-rule-level rather than commit-level: cost per broadcast across the fleet, signal-to-noise on project-internal commits, the rsync-already-does-the-work observation, aggregation winning over per-commit pings, and the train-projects-to-ignore-inbox risk. The end-of-session bundling guidance lands too. If a session ships several broadcastable changes, bundle them into one broadcast at session end instead of firing one per commit. Source: session-end conversation 2026-05-29 surveying today's cross-project changes. Today's session shipped 13 cross-project items (4 new workflows, 6 workflow updates, 2 new scripts, 1 new bin tool). A per-commit broadcast cadence would have fired 13 inbox files across 23 targets, or 299 total inbox files. One consolidated broadcast (or no broadcast at all, since Craig already coordinated manually) covered the same ground.
* docs(wrap-it-up): add inbox sanity check + checklist lineCraig Jennings2026-05-292-0/+40
| | | | | | | | | | | | | | | | | | | | | | | | Wrap-up never knew about the inbox. After process-inbox.org landed today as the formal workflow for handoff intake, wrap-it-up.org needs to surface inbox/ when it's non-empty so the wrap doesn't silently defer handoffs to next session. This commit adds a new Inbox sanity check sub-step under Step 3 (project hygiene). The check runs find inbox -maxdepth 1 -type f filtered to skip .gitkeep and PROCESSED-* prefixes. A non-zero count surfaces with the file list and a recommendation to run process-inbox.org or explicitly defer each item. A zero count or no inbox/ directory makes the check a silent no-op. It also adds one line to the Validation Checklist: inbox is empty (excluding .gitkeep and PROCESSED-* prefixes), OR each remaining item has an explicit deferral logged in the valediction. This is the only integration gap surfaced today. The other gaps considered (LAST_INBOX_PROCESS marker stamp, sync-check, make status) didn't justify wrap-up changes. The marker is process-inbox's own responsibility. sync-check is project-specific to rulesets. make status doesn't generalize across projects.
* chore(session): pre-stage signal-mcp config + snapshot session-contextCraig Jennings2026-05-292-0/+136
| | | | | | | | | | | | | | | | | | | | | | | | | | This commit is end-of-day housekeeping for the rulesets session that ran 2026-05-28 through 2026-05-29. mcp/servers.json gains the signal-mcp stdio entry pointing at the locally-cloned rymurr/signal-mcp install at ~/.local/share/signal-mcp/. The entry uses Craig's primary +15103169357 as --user-id, which will need updating to the Google Voice number once that signal-cli registration lands (TODO scheduled today). The configuration is staged but not registered. The make install-mcp pipeline was interrupted earlier by the GPG pinentry (Craig on vacation, away from desk). Running make install-mcp at the desktop with the GPG passphrase will complete the registration. .ai/session-context.org captures the live session narrative covering the no-approvals batch (6 :quick:solo: TODOs), the codex backlog triage, the page-signal infra, the voice profile Phase 1 plus Phase 2, the SKILL.md / voice-profile.org structural split, and the inbox processing pass. No wrap-up performed. The session-context stays under that name pending the wrap-it-up workflow when Craig is back at the desk. The substantive work in this session shipped across many prior commits, each with its own focused message.
* chore(intake): file startup-rsync-dirty TODO, stamp :LAST_INBOX_PROCESS:, ↵Craig Jennings2026-05-292-0/+20
| | | | | | | | | | | | | | | | | | | | | | | | | | | | clear processed inbox The process-inbox workflow walked the four inbox arrivals (one from home, one from jr-estate, two from health): - 2026-05-28-0858 home spec-naming-convention: implemented in bb28cfa. Deleted. - 2026-05-29-0832 jr-estate startup-rsync-carried-dirty: filed as [#B] :feature: TODO under Rulesets Open Work. Three options captured (skip-when-dirty, clean-files-only, clean-ref-based) with my recommendation noted. Deleted. - 2026-05-29-1111 health org-drill-anki initial: superseded by 1114. Deleted without filing. - 2026-05-29-1114 health org-drill-anki updated: implemented in 506ab6e. Deleted. The startup-rsync-dirty TODO also captures an anomaly the jr-estate handoff flagged: even with --delete, two files that DO exist in rulesets canonical (cross-project-broadcast.org, page-signal.org) did NOT propagate. This is likely a timing issue (those files were added after the jr-estate Phase A rsync ran). The anomaly is worth confirming when the TODO is picked up. notes.org Workflow State stamped :LAST_INBOX_PROCESS: 2026-05-29. Inbox now empty.
* feat(scripts): add drill-to-anki.py template script (org-drill to Anki .apkg)Craig Jennings2026-05-292-0/+428
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Generalizes the health-drill-to-anki.py converter into a template script under claude-templates/.ai/scripts/. Every project's startup rsync now picks it up at .ai/scripts/drill-to-anki.py. The converter walks an org-drill file. Top-level * Section headings become Anki tags. Each ** Card name :drill: entry becomes a card with the heading as Front and the body as Back (newlines converted to <br>). Parameterized from the original health-specific version: - Input file is a positional argument (was hardcoded health-drill.org). - Deck name defaults to the org file's #+TITLE if present, else the input basename in Title Case (was hardcoded "Health Drill"). - Output path defaults to ~/sync/org/drill/<input-basename>.apkg (was hardcoded health-drill.apkg in the same dir). - Deck and model IDs are derived from the deck name via SHA-256, so re-running against the same source produces stable IDs. Anki imports update existing cards in place rather than duplicating. Dependencies via PEP 723 inline script metadata. The shebang is #!/usr/bin/env -S uv run --script. First run resolves and caches genanki>=0.13. Subsequent runs are instant. There is no venv to maintain and no PEP 668 friction. Smoke tested against ~/projects/health/health-drill.org. The script wrote 43 cards into the Health Drill deck, matching the original script's output. Inbox source: 2026-05-29-1114-from-health-todo-b-org-drill-anki-export-updated.org. Craig confirmed all projects will likely have org-drill files, justifying template-tier promotion.
* feat(workflows): add -spec.org precondition to spec-review and spec-responseCraig Jennings2026-05-294-0/+64
| | | | | | | | | | | | | | | | | | | | | | | | Both workflows now check that the file under review (or the spec being responded to) ends with -spec.org before proceeding. If it does not, the workflow stops and surfaces the mismatch with the rename suggestion. The suffix is the identifier per Craig's spec-naming convention: every design, decision, or planning document under a project's docs/ directory ends with -spec.org. The .org extension alone is not enough because docs/ holds non-spec org files too (tutorials, frozen inventories, reference material). spec-review.org gained a top-level Precondition section between When to Use and Approach. spec-response.org gained the same Precondition section in the parallel position, with a note that the review-file convention <spec-basename>-review.org means a misnamed spec produces a mis-pointed review file too. Inbox source: 2026-05-28-0858-from-home-spec-naming-convention-apply-to-spec.org. Home renamed two docs to the new convention and asked rulesets to update the template workflows so the next startup rsync in every project picks up the guard.
* docs(voice): Phase 2 corpus findings: email + PR registers added to ↵Craig Jennings2026-05-291-9/+45
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | voice-profile.org Phase 2 of the writing voice corpus adds four sub-corpora to the profile: personal email (1139 messages, 283k words), work email (22 messages, small sample), PR descriptions (9 PRs), and PR review comments (3 comments, very small sample). Mu queries on local maildirs and gh API calls on the cjennings github identity. Signatures, quoted replies, and forwarded blocks stripped before analysis. No corpus files written to disk. The headline finding is the register split. Phase 1's commit-prose signal does not generalize cleanly. Em-dashes and semicolons are concentrated in commit prose (3.49 and 3.16 per 1000). Conversational and PR prose run an order of magnitude lower (em-dash 0.28 in personal email, 0.00 in PR review comments). Semicolon shows the same shape (0.64 in personal email, 0.00 in PR comments). The personal-mode rules on those still earn their place, but the basis shifts. The rules mostly enforce what is already true for non-commit registers. Contractions invert the pattern: commits 3.57 per 1000, personal email 38.52, PR review comments 50.78. The Phase 1 curiosity (I'm and I'll surprisingly rare relative to standalone I in commits) is resolved as a register effect. Personal email shows I'm at 6.04 per 1000 vs standalone I at 36.91, near natural English. Craig's voice is heavily-contracted in conversational prose and uniquely suppresses contractions in commit prose. The contraction rule is strongly confirmed in the registers where contractions are most expected. Updates land in: - Top-level Corpus section: a new Phase 2 subsection with the four sub-corpora and a cross-register findings table. - Curiosities (resolved by Phase 2) section: I'm/I'll rarity puzzle answered. - §7 (AI vocabulary) Basis: cross-register watch-word measurements. Comprehensive is concentrated in commits. Leverage shows up modestly in personal email. - §13 (em-dash) Basis: register split documented. - §32 (first-person) Basis: standalone I rates across all five registers. - §33 (semicolon) Basis: register split parallels em-dash. - §34 (contractions) Basis: register inversion documented, Phase 1 curiosity resolved. - §38 (terse cut) Basis: single-sentence-paragraph rate across registers, highest in PR descriptions. AI tells stay near zero across all five corpora. Leverage 18 occurrences in personal email is the only non-zero hit on the watch-list outside commits.
* docs(voice): scrub prose em-dashes from voice-profile.orgCraig Jennings2026-05-291-20/+20
| | | | | | | | | | | | | | | | | | This hygiene sweep covers the profile's prose sections (Problem, Basis, History, Phase 1 findings, Phase 2 list) where em-dashes had carried over from the original SKILL.md text. 21 prose em-dashes were replaced with context-appropriate punctuation (periods, colons, parentheses, or rewords). Eight em-dashes are preserved as legitimate exceptions: the literal symbol reference in §13 Rule, §13 Before example (shows source text with em-dashes), §36 Before example (felt-experience tic), §38 heading "Terse Cut — Rhetorical Padding" (paired with SKILL.md heading verbatim), §39 Before example (WARN output format), §40 example dialogue (shows what a kind correction reads like). The profile now follows its own rule for prose voice. The known follow-up flagged in 10d0bc1's commit message is closed.
* docs(voice): complete the SKILL.md / voice-profile.org structural splitCraig Jennings2026-05-292-360/+1297
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Backfills the remaining 40 patterns into the paired source-of-truth shape Pattern 13 demonstrated in 296b2e6. Each pattern in SKILL.md now collapses to a mode-tagged header plus a one-line Rule plus a profile pointer. The full Problem, Basis, Before/After, Detection, and History land in the corresponding voice-profile.org §N section. Sizes: - voice/SKILL.md: 678 to 437 lines. - voice/references/voice-profile.org: 134 to 1311 lines. - Net: 1297 insertions, 360 deletions across both files. Mode-tag conventions: general-only patterns (#1-12, 14-16, 18-31) tagged [general]. Prose+personal patterns (#33-38, 41) tagged [prose · personal]. Personal-only patterns (#32, 39, 40) tagged [personal]. Pattern 13 keeps its dual-mode tag (general overuse-reduction vs prose/personal zero-tolerance). Corpus-confirmed Basis entries cite the Phase 1 measurements for patterns 7, 17, 22, 32, 33, 34, 38. All other patterns are tagged observation-derived with the appropriate source (Wikipedia Signs of AI Writing, Strunk and White, Orwell, Plain English, Garner, or Craig's commits.md / interaction.md rules). Seven em-dashes were caught in the new SKILL.md Rule lines during a post-migration spot-check and rewritten as periods or colons. The em-dash on Pattern 13's Rule line stays because the rule references the symbol literally. Known follow-up: the profile's prose sections (Problem, Basis, History) still contain em-dashes carried over from the original SKILL.md Problem paragraphs. They don't break the loading model but they do violate the rule the file documents. A separate hygiene pass can scrub them. The em-dash sweep was not bundled here so the migration shape stays the deliverable. Bulk migration was dispatched to a subagent with strict format requirements. Spot-checks of patterns 1, 13, 17, 32, and 41 confirmed the shape lands correctly across all four mode-tag conventions.
* docs(voice): split SKILL.md and voice-profile.org into paired ↵Craig Jennings2026-05-292-12/+60
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | source-of-truth, Pattern 13 worked example This is option C from the structural-split proposal. SKILL.md becomes the thin rule-set. voice-profile.org becomes the canonical home for rationale, basis, examples, and per-pattern history. Pattern 13 is migrated as the worked example to confirm the form. The remaining 40 patterns are scheduled for a backfill pass. SKILL.md gains a Source of Truth section near the top stating the pairing rule. Every change to a pattern must land in both files. A SKILL.md edit without a profile update is incomplete. A profile update without a SKILL.md edit is fine. Pattern 13 in SKILL.md collapses to a single Rule line plus mode tags plus a pointer to voice/references/voice-profile.org §13. The Problem paragraph, the Mode-dependent-strength paragraph, the Note-on-basis paragraph, and the Before/After examples all move to the profile. voice-profile.org gains a How this combines with SKILL.md preamble that names the pairing rule explicitly. Pattern §13 lands with all the migrated content plus a History section recording the prior commits (original SKILL.md entry, the 2026-05-26 prose-mode addition in 4fac2a0, the 2026-05-29 basis note in c3cf9a5, and this migration). If Craig confirms the shape, the next pass backfills patterns 1 through 12 and 14 through 41 in the same form.
* feat(signal): page-signal CLI wrapper + workflows + cross-project broadcast ↵Craig Jennings2026-05-2910-15/+889
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | helper Three coupled additions ship together. claude-templates/bin/page-signal is a bash wrapper around signal-cli send. It defaults to --note-to-self for safety. The wrapper supports --file for attachments, --to <+number> for outbound (explicit per call, no defaults, no batch), --quiet, and --json. Exit codes: 0 sent, 1 signal-cli failure, 2 usage error, 3 signal-cli not installed. claude-templates/.ai/workflows/page-signal.org carries the discrimination rules and safety rails. When desktop notify covers it, don't reach for Signal. Long-running task completion is the canonical case. Outbound to other contacts requires explicit Craig instruction per send. A known-limitation note covers the current notification gap. signal-cli registered on Craig's primary number means messages don't fire notifications until the pending Google Voice registration lands. claude-templates/.ai/workflows/cross-project-broadcast.org and its helper cross-project-broadcast.py fan out a single message file to every AI project's inbox in one operation. Discovery is fingerprint-based: any directory under ~/code, ~/projects, ~/.emacs.d with both .ai/protocols.org and a top-level inbox/ is broadcastable. Senders are auto-excluded. Verified discovery against 23 broadcastable targets. Makefile's install target gains a general bin/ loop. The previous version hardcoded bin/ai. The new version iterates over every executable under claude-templates/bin/ and symlinks each into ~/.local/bin/. install-hooks (existing Claude hook installer) is unchanged. install-githooks (sync-check pre-commit hook setup, added earlier today) is unchanged. The bin/ loop now picks up bin/page-signal automatically. INDEX entries for both new workflows landed under Tools and meta. No bats tests on the new scripts. page-signal was smoke-tested with a live send. The send succeeded. The notification gap is covered by the workflow's known-limitation note. cross-project-broadcast.py was smoke-tested via --list against the live project set. Tests can be added when the broadcast pattern proves out across multiple use cases.
* docs(voice): apply Phase 1 deltas to SKILL.md per Craig's confirmationCraig Jennings2026-05-291-3/+9
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Three rationale-honesty edits land. Craig confirmed (vacation chat 2026-05-29) that the em-dash and semicolon zero-tolerance rules are intentional self-discipline, not codified habit. He also confirmed he's trying to use "comprehensive" sparingly despite its high natural rate in his prose. Pattern 13 (em-dash) gained a Note-on-basis paragraph naming the 3.49-per-1000-words corpus rate. The note states the rule is self-discipline, not habit-reflection. The zero-tolerance directive itself is unchanged. Pattern 33 (semicolons) gained the same shape. The note names the 3.16-per-1000-words rate. The directive is unchanged. One em-dash in the existing Problem paragraph that contradicted the rule was replaced with a comma. Pattern 7 (AI vocabulary watch-list) gained "comprehensive" as an entry. A note explains the rationale. The corpus shows 42 uses vs zero-or-one for every other watch-word, but Craig is consciously reducing his use, so flag-and-suggest is the right action. Three of the six proposed deltas from voice-profile.org are now applied. The other three (new positive patterns: single-sentence paragraph cadence, parenthetical density, declarative-default register) await Craig's call.
* docs(voice): land Phase 1 voice profile derived from git-commit corpusCraig Jennings2026-05-291-0/+89
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Phase 1 of the writing voice profile TODO (filed 7a861ed). The work covers corpus assembly, statistics, and a cross-check against the 41 SKILL.md patterns. Email, PR, Slack, and long-form sources deferred to Phase 2. Corpus: 5355 commits, 1895 with non-trivial bodies, 128608 words across 33 repos. Strong findings: - Pattern 17 (no emojis), Pattern 7 (AI vocabulary), Pattern 22 (filler), Pattern 32 (first-person), Pattern 34 (contractions), and Pattern 38 (terse cut) are all confirmed by direct corpus measurement. - Pattern 13 (em-dash zero-tolerance) and Pattern 33 (semicolons to period) contradict the corpus. Craig USES em-dashes at 3.49 per 1000 words and semicolons at 3.16 per 1000 words, rates comparable to AI-generated prose. The rules are self-discipline, not habit-reflection. SKILL.md should say so honestly. - Pattern 7 watch-word "comprehensive" appears 42 times in the corpus while every other watch-word clocks zero or one. "comprehensive" is genuine Craig vocabulary. The rule should pull it from the watch-list or flag only when it co-occurs with other AI tells. New patterns the corpus suggests adding: single-sentence-paragraph cadence (41.1% of paragraphs are exactly one sentence), parenthetical density (23 opening parens per 1000), declarative-default register (0.33 question marks per 1000). Six concrete SKILL.md edits proposed in the doc, none applied. The deltas await Craig's call. Phase 2 sources are documented in the doc body.
* docs(todo): file [#C] :spec: TODO to build Craig's writing voice profile ↵Craig Jennings2026-05-291-0/+52
| | | | | | | | | | | | | | | | | | | | | | | | | | from real corpora Files a TODO under Rulesets Open Work to mine Craig's actual writing (sent email across all three accounts, commit messages, PR bodies, org files he authored, slack threads, long-form artifacts) into a grounded voice profile. The voice/SKILL.md patterns today are observation-derived. Some are spot-on. Others are intuition. A corpus pass would tell us which patterns are genuinely Craig's voice, which were guesses, and which Craig-specific positive traits the current ruleset misses entirely. Output: voice/references/voice-profile.org with findings cited to evidence samples, plus a reconciliation pass against voice/SKILL.md to confirm, strengthen, weaken, add, or remove patterns based on what the corpus shows. Approach phased into corpus assembly, analysis (subagent-friendly), draft profile, reconcile-with-user. The body includes a privacy note: raw corpus stays out of commits if the project's remote ever stops being private. There's no urgency. The work is useful but optional, hence [#C].
* docs(todo): schedule Signal MCP server install for 2026-05-29Craig Jennings2026-05-291-112/+132
| | | | | | | | | | | | | | | | | | | | | | | Files a [#B] :feature: TODO under Rulesets Open Work to install rymurr/signal-mcp as an MCP server. The MCP gives Claude native tool access to send_message_to_user, send_message_to_group, and receive_message instead of shelling out to the page-signal wrapper. The differentiator is receive_message. Claude can listen for replies and act on them, enabling page-as-confirm flows and structured Q&A across devices. Dependency: signal-cli has to be registered with the Google Voice number first. Sending from Craig's primary number to itself doesn't notify (Signal treats it as one account on linked devices). The MCP server takes --user-id at startup, one account per instance, so it has to point at the GV account. Implementation touches mcp/servers.json (stdio entry), mcp/README.org (dependency note), and possibly mcp/secrets.env.gpg if any auth material needs encrypting. Verification: make install-mcp followed by make check-mcp shows signal-mcp ok. Smoke-test via a tool call. The task is scheduled for 2026-05-29.
* feat(mcp): add uninstall + --check + README section for MCP pipelineCraig Jennings2026-05-284-7/+143
| | | | | | | | | | | | | | | | | | | | | | | | | | | Three coupled additions close the MCP pipeline thread. mcp/install.py grew --uninstall and --check modes via argparse. The default install behavior is unchanged. --uninstall iterates over servers.json and runs `claude mcp remove <name> -s user` for each, skipping anything not registered. Idempotent. --check is the dry-run drift report. For each server, classify as ok (in both servers.json and `claude mcp list`), MISSING (configured but not registered), or EXTRA (registered but not in servers.json). Exit non-zero only on MISSING since EXTRA entries are often deliberate (the claude.ai web servers register out-of-band). Smoke test against the live config: 9 ok, 0 missing, 3 EXTRA, exit 0. Two new Makefile targets: - make uninstall-mcp invokes the --uninstall mode. - make check-mcp invokes the --check mode. README.org gained an MCP section under Two install modes covering all three targets, the OAuth-token-on-disk story, and a pointer to mcp/README.org for the full pipeline. Closes TODO #7 (uninstall + --check) and TODO #8 (README MCP section).
* fix(sync-check): ignore generated python and elisp artifactsCraig Jennings2026-05-284-44/+56
| | | | | | | | | | | | | | Pre-commit caught a false-positive on its first real-world run. The .ai/scripts/__pycache__ directory created by make test (pytest writes .pyc and .pytest_cache, emacs writes .elc) was flagged as drift against the canonical's clean tree. Added matching --exclude patterns to the diff -rq check and the --fix rsync calls. Patterns: __pycache__, *.pyc, *.pyo, .pytest_cache, *.elc. The --fix excludes prevent rsync's --delete from wiping the mirror's __pycache__ when the canonical has none. Four new bats tests cover the exclusions. All 12 pass.
* feat(status): add `make status` for compact health summaryCraig Jennings2026-05-283-1/+82
| | | | | | | | | | | | | | | | | | | | | | | | | | scripts/status.sh prints a six-line summary composing existing checks: - audit + doctor (one call, since audit.sh runs doctor internally) - canonical/mirror sync state via sync-check.sh - open todo count under * <Project> Open Work - inbox count (excluding .gitkeep and PROCESSED- prefixes) - git working-tree state with ahead/behind upstream Sample output: rulesets status — 2026-05-28 09:13 CDT audit Summary: 41 ok, 0 warnings, 2 failures sync canonical = mirror todo 22 open inbox 1 unprocessed git main dirty — in sync with origin/main The script adds no new logic beyond formatting. `make status` is the entry point. The scope here is limited per the triage disposition for codex item #12. The rest of #12 was rejected. `make sync` duplicates the existing sync flow, `make health` wraps existing checks without adding signal, `make bootstrap-project` duplicates `install-ai` + `install-lang`.
* feat(sync-check): canonical/mirror drift detection + pre-commit hookCraig Jennings2026-05-285-1/+182
| | | | | | | | | | | | | | | | | | | | | | | | | | scripts/sync-check.sh diffs claude-templates/.ai/{protocols.org, workflows,scripts} against the .ai/ mirror. Exits 0 when clean, 1 with a diff report on drift, 2 outside a rulesets-shaped repo or git checkout. --fix mode rsyncs canonical -> mirror and re-checks, then prompts to re-stage. githooks/pre-commit wraps the script. Commits abort on drift so the issue surfaces at publish time, not at the next session's startup rsync. Two new Makefile targets: - make sync-check [FIX=1] runs the script (FIX=1 passes --fix through). - make install-githooks sets core.hooksPath=githooks (idempotent). scripts/tests/sync-check.bats holds 8 tests covering clean, drift-per-path, --fix, extra-file removal, missing canonical, and outside-git. All eight pass. This catches the exact drift I had to fix manually during this morning's audit pass. The mirror's open-tasks.org PROPERTIES drawer sat below a sub-heading because the mirror commit was older than canonical.
* feat(open-tasks): archive-done sweep at start of Phase ACraig Jennings2026-05-283-3/+34
| | | | | | | | | | | | | | | | | | open-tasks.org Phase A now runs `emacs --batch -q -l .ai/scripts/todo-cleanup.el --archive-done todo.org` as a pre-step before the parallel read batch. A level-2 task that completed during the session sits as ** DONE under Open Work until something archives it, which means Next Mode would surface it as a "what's next" candidate between cleanups. The pre-step ensures Phase A's read of todo.org reflects current state. The pre-step skips only in an explicit read-only or dry-run context. Default is always to sweep. Common Mistake #13 records "recommending a freshly-DONE task" with the pointer back to the pre-step. The proposal came from a pearl handoff on 2026-05-28.
* feat(workflows): add process-inbox.org with value-gate disciplineCraig Jennings2026-05-286-18/+350
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Generic process-inbox workflow at claude-templates/.ai/workflows/ and mirror. Owns inbox discipline across every project: items are ideas to evaluate, not orders to execute, and earn a place in todo.org or git history only when they pass a three-question value gate. The gate (Phase B): - Advances an existing TODO (look up by topic). - Improves how the project works (architecture, workflows, tooling, rule hygiene). - Serves the project's stated mission (read from notes.org Project-Specific Context). One yes accepts. Three nos reject. Per-source rejection flow (Phase D): - From Craig: state in chat, wait for override. - From another project: write a response via inbox-send naming which gate question failed plus any reconsideration condition. Silent rejection on a handoff is worse than no reply. - From a script or automated system: just delete. Phase B.1 gates filing on priority-scheme presence. If todo.org has a scheme at the top, file with cookie + mandatory type tag + optional effort/autonomy tags. If not, surface a one-sentence nudge to adopt one (or to skip grading and flag the gap in the commit). Phase D within accepts splits implement-now / fold-into-existing / file-as-TODO so the inbox doesn't default to inflating todo.org for every item that passes the gate. Phase E stamps :LAST_INBOX_PROCESS: in notes.org Workflow State if the section exists. startup.org Phase C step 2 now delegates here instead of inlining the inbox-processing language. INDEX entry under Tasks and planning lists the full set of trigger phrases.
* chore(intake): triage codex backlog into 5 actionable TODOs + dispositionsCraig Jennings2026-05-282-12/+109
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Walked the 14-item codex enhancement backlog with Craig and recorded dispositions in docs/design/2026-05-28-rulesets-enhancement-backlog.org under a new "Triage Dispositions" section. The triage TODO closed. Accept (filed below as TODOs): - Item #8 .aiignore for agent inventory exclusions ([#C] :chore:) - Item #10 Workflow test harness for drift and integrity tests ([#C] :feature:) Pilot or scope-limit (filed below): - Item #5 Token-tier pilot on startup.org and triage-intake.org ([#C] :feature:) - Item #7 Canonical/mirror drift detection via pre-commit hook or make sync-check ([#C] :feature:quick:solo:). Rejected the wholesale dedupe. The dual source is a feature. - Item #12 make status only ([#C] :feature:quick:solo:). Rejected the rest of #12. Convention, not tracked: - Item #11 script interface normalization (adopt as scripts are touched) - Item #13 ADR layer (watch-and-wait on growth signal) Reject with rationale (recorded in the backlog doc): - Item #1 broader runtime-neutral arc. Heavy infrastructure for marginal payoff at current use. - Item #3 INDEX.org compression. Already token-efficient. Parallel layer adds drift risk. - Item #4 universal catalog.json. Looks valuable on paper, rots fast in practice. - Item #6 install manifest JSON. Current Makefile pattern works. Data layer adds indirection. - Item #9 project-facts.org. Duplicates notes.org's role. - Item #14 local model profiles. Premature without a real use case. Item #2 (per-agent live session files) was already filed earlier today as the Codex Phase 1 [#B] TODO, slicing the broader item #1 arc.
* chore(intake): file codex enhancement backlog as docs/design + triage TODOCraig Jennings2026-05-283-80/+448
| | | | | | | | | | | | | | | | | | Codex left a 14-item enhancement backlog for rulesets at inbox/enhancements.org overnight. Moved it to docs/design/2026-05-28-rulesets-enhancement-backlog.org as the canonical reference and filed a single [#C] :spec: TODO to walk the items and decide accept-as-TODO, fold, defer, or reject for each. Two of the 14 already have homes: item #1 (runtime-neutral core) aligns with the existing Generic agent runtime support spec (#16), and item #2 (per-agent live session files) is exactly the Codex Phase 1 [#B] TODO filed earlier today. The triage focuses on the remaining 12. Also cleared the last-session PROCESSED-prefixed inbox file (open-tasks-hybrid-friction-cascade). The cascade+friction restructure it tracked landed in this morning's open-tasks.org edits, so the reference is no longer needed.
* feat(workflows): promote no-approvals.org to template, merge pearl framingCraig Jennings2026-05-286-176/+262
| | | | | | | | | | | | | | | | | | | | | | | Pearl independently built its own no-approvals workflow (handoff in this morning's inbox) and asked rulesets to take the best from both. That's cross-project signal that the workflow earns a place at the template tier. Promoted from =.ai/project-workflows/= to =claude-templates/.ai/workflows/= with a mirror under =.ai/workflows/=. INDEX entry added under "Tools and meta" listing the full set of trigger phrases. Merged from pearl's draft: the prominent "What's Suspended" / "What Stays On" split (same content as the prior "Contract" section, easier to scan), wider trigger phrases (the project-only version had fewer), the mode-resets-when-Craig-switches-topics guard, an explicit destructive-action carve-out as its own bullet rather than buried in the "real question" section, and a subagent-review-gate callout. Kept from the rulesets version: the Session Log update emphasis between items, the real-question include/exclude lists, and the don't-auto-wrap-up guard. A History section at the bottom records the merge for future archaeology.
* fix(todo): move task #15 PROPERTIES drawer above sub-headingsCraig Jennings2026-05-281-4/+4
| | | | | | | | The dated sub-entry added in the audit pass left the LAST_REVIEWED line below the first level-3 heading, where the staleness checker can't see it (it scans only between the level-2 heading and the next heading of any depth). The fix moves the PROPERTIES drawer to its standard org-mode placement, immediately after the level-2 heading.
* chore(audit): task-audit pass + pearl intake from 2026-05-28 morningCraig Jennings2026-05-288-198/+175
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | All 16 open tasks bucketed and updated. 14 received autonomous Phase C edits (13 type-tag additions per the new scheme plus #15's body refresh for the accumulating pearl signal). Phase D adjudicated two priority bumps and the morning's inbox spillover. Phase E stamped :LAST_AUDIT: in notes.org Workflow State. Autonomous Phase C: - Tagged tasks 1-11 and 13-14 with their type tag (:feature:, :chore:, :spec:), bumped LAST_REVIEWED to 2026-05-28. Tasks 12, 15, 16 already carried type tags. - Refreshed task #15 body to reference the four pearl pattern-catalog notes now in docs/design/ (six worked patterns total). Phase D adjudication: - #15 (cross-project pattern catalog) bumped [#C] to [#B]. Pearl shipped 6 worked examples plus a synthesizing principle and is asking for spec-review iterations. Design questions still open but evidence is past the tipping point. - Filed new [#B] :feature: TODO for the codex Phase 1 race-fix (AI_AGENT_ID + session-context.d/<id>.org), lifted from the broader runtime spec (#16). Phase 1 alone is low-risk and fixes a real correctness issue under simultaneous agents. Pearl intake (4 inbox files from this morning): - Moved 0155 (patterns 4-5) and 0303 (pattern 6) into docs/design/ alongside the prior two pattern-catalog notes. Referenced from #15. - Filed new [#C] :chore:quick:solo: TODO for pearl 0138's --archive-done sweep at the start of open-tasks.org Phase A. - Filed new [#C] :feature:solo: TODO for pearl 0226's spec-review.org Phase 6 implementation-task enumeration. - Deleted 0124. Already implemented this session as the audit-warranted pre-step plus the LAST_AUDIT stamp now live in the workflow files.
* feat(workflows): audit-warranted pre-step + priority and tag enforcementCraig Jennings2026-05-284-20/+158
| | | | | | | | | | | | | | | | | | | Two changes land together because each is broken without the other. open-tasks.org gains a new Phase A.1, evaluated only in Next Mode. The phase reads :LAST_AUDIT: from notes.org and walks five state signals (reminder/task mismatch, passed scheduled date, "waiting on X" matches a shipped X, dead file: link, sub-task >75% DONE coverage). If the temporal threshold of 14 days trips, or any signal fires, Next Mode offers a task-audit run before producing the recommendation. Item 1 in the offer is "run task-audit first" per the recommendation-at-item-1 convention. task-audit.org gains two pieces. Phase C now enforces priority and type-tag presence per the project's legend, applies the [#A] dating rule from that legend, and re-assesses :quick: and :solo: from reconciled facts. Unambiguous calls land autonomously. Ambiguous ones flag NEEDS-USER instead of being guessed. A new Phase E stamps :LAST_AUDIT: on completion.
* docs(todo): add priority and tag schemeCraig Jennings2026-05-282-0/+35
| | | | | | | | | | | | | | | | Adds an explicit [#A]-[#D] legend at the top of todo.org defining what each priority level means in practice. Type and effort tag conventions live alongside, since task-audit enforces both. [#A] now requires a SCHEDULED or DEADLINE date. A task that can't be dated isn't really [#A]. It belongs at [#B]. Tags split two ways. Type tags (:feature:, :chore:, :spec:, :bug:) are mandatory. Effort and autonomy tags (:quick:, :solo:) are optional and orthogonal. Both can apply to the same task. Seeds a Workflow State section in notes.org for the :LAST_AUDIT: marker that open-tasks.org Next Mode reads to decide whether to offer a task-audit run. The marker starts unset. The first task-audit run populates it.
* chore(ai): archive session recordCraig Jennings2026-05-281-0/+38
|
* chore(session): snapshot WIP, session-context, and inbox arrivalsCraig Jennings2026-05-288-16/+623
| | | | | | | | | | Bundles end-of-session state across three concerns. claude-templates/.ai/workflows/open-tasks.org: in-flight restructure of Next Mode into a two-question output (cascade + friction filter), +62 lines. Separate from the iteration-history work that landed in 55adf6e and 684b273. .ai/session-context.org: live session record covering startup through the iteration-history backfill. Captures the no-approvals mode and the full spec-review/spec-response cycle on the working draft. inbox/: five new arrivals from this session: four pearl notes (pattern catalog, prompts-ux, spec-review implementation, prompt collapse) plus one .emacs.d note (whats-next + task-audit). A sixth file, PROCESSED-prefixed, represents mid-handling state on the open-tasks friction-cascade suggestion from earlier in the session.
* feat(workflows): backfill iteration history in spec workflowsCraig Jennings2026-05-285-1/+90
| | | | | | Closes the org-drill backfill TODO. Both workflow files now carry a bottom "Review and iteration history" section with four entries: Draft 1 (linear-emacs origin, 2026-05-23, placeholder timestamp), Review-and-Fold (commit 7f2aea1, 2026-05-23), Requirement addition (commit 55adf6e, 2026-05-28), and this backfill commit. Canonical and mirror updated together. Working directory working/spec-workflows-iteration-history-backfill/ removed on this commit. The spec-response cycle on the working draft (two Codex reviews, three rulesets responses) validated the entry-shape and the content before splice. Codex caught file-history conflation that would have made per-file provenance inaccurate. Craig's direct rationale on the Iteration 1 and Iteration 3 Why lines replaced the original INFERRED markers.
* feat(workflows): add iteration-history requirement to spec workflowsCraig Jennings2026-05-284-16/+86
| | | | | | Specs reviewed under either workflow now carry a bottom =Review and iteration history= section. Each entry is an org subheading with a compound id (timestamp, contributor, role) plus What/Why/Artifacts body fields. The id is opaque. Timestamp, contributor, and role concatenate without implying any decision ordering. spec-review.org adds the gate item, the entry-shape spec, and a "Preserve iteration provenance" principle. spec-response.org adds the matching Phase 4 step and a "history explains provenance" principle. Canonical and mirror updated together.
* docs(todo): clarify what blocks the iteration-history backfill TODOCraig Jennings2026-05-281-1/+1
| | | | The earlier body listed two reasons to wait: a file-conflict block (correct, since spec-review.org and spec-response.org have uncommitted edits) and a guess that the entry-shape spec might still change (inference without evidence). The revised dependency drops the guess. The read-only research portion can run in parallel without touching the files.
* chore(intake): file org-drill iteration-history backfill as TODOCraig Jennings2026-05-281-0/+17
| | | | The org-drill inbox asks rulesets to backfill =Review and iteration history= sections on spec-review.org and spec-response.org. It lands as a follow-up TODO because the requirement itself is still being established in uncommitted WIP.
* chore(intake): file pearl pattern-catalog and codex runtime spec as TODOsCraig Jennings2026-05-284-0/+613
| | | | Moved three inbox notes into docs/design/ so the task body links survive: pearl's two pattern-catalog handoffs and codex's v0 generic-agent-runtime spec. Added two corresponding TODOs under Rulesets Open Work, both [#C].