| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
| |
Renaming an .ai artifact by hand is the kind of mechanical job that gets done incompletely: the canonical copy moves but the mirror doesn't, a reference in the INDEX is missed, a trigger phrase points at the old name. I'd also assumed a rename was costly because references scatter, when the index update is trivial and the drift check already guards it. So I built the discipline into a script instead of re-deriving it each time.
scripts/rename-ai-artifact.sh takes old and new basenames, moves the file in both the canonical and mirror trees, and rewrites every reference repo-wide on a token boundary so renaming "foo" can't corrupt "foobar" or "foo-bar". It rewrites the underscore module-name variant too (a hyphenated script imported as foo_bar via importlib), leaves the archived session records under sessions/ alone because they're history, and runs workflow-integrity + sync-check at the end to prove no drift. rename-artifact.org documents it and indexes the triggers.
Then I used the tool to do the rename that prompted it: the org-drill deck workflow and its helpers are now flashcard-named, since "flashcard" is the word you'd actually search for. The renamed set is flashcard-review.org plus flashcard-stats.py, flashcard-sync, flashcard-to-anki.py, and flashcard-diff-ids.py, with their tests, every reference, and the INDEX entry updated. The deck is still an org-drill deck under the hood, so the ":drill:" tag handling and the "drill deck" trigger phrases stay. I added "review/update the flashcards" alongside them.
Tests: 9 bats for the rename tool (including the prefix-collision and history-preservation edges), and the renamed script suites all pass under make test.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
detection
A line-weighted coverage total has a blind spot: a module no test loads never shows up in the SimpleCov report, so it can't drag the number down. The suite looks healthier than it is. This adds a summary that counts every source file on disk against the report and treats an absent file as 0%, weighting the project number by file instead of by line so untested modules stay visible.
The script ships at languages/elisp/claude/scripts/coverage-summary.el, self-contained on stock Emacs (just the built-in json). It parses the undercover SimpleCov shape directly rather than depending on the editor's coverage engine, so it runs anywhere the bundle lands. I proved it against a real 103-file report: 93 tracked, 27 untested modules surfaced, project number 66.4%.
Delivery follows the bundle convention. The script lives under the gitignored .claude/ footprint and gets auto-fixed on drift by sync-language-bundle.sh, which I made generic for any claude/scripts/* rather than coverage-specific. The Makefile targets ship as a project-owned fragment (languages/elisp/coverage-makefile.txt) that install-lang.sh seeds at the project root and sync drops into .ai/inbox/ when that convention exists. The bundle never edits the project's own Makefile.
Tests: 12 ERT for the kernel (Normal/Boundary/Error per function), wired into make test via a new languages/*/tests/ discovery path, plus bats for the sync auto-fix and the inbox-drop guards.
This is the Elisp pilot. The pattern is proven, so fanning out to Python, Go, and TypeScript is now a follow-up. Each one needs only its own parser and fragment. The plumbing is already generic.
|
| |
|
|
|
|
|
|
|
|
| |
I split each into lanes so a reader can stop at the level that answers the question: Summary for "what does this do and what does it produce", Execution for the steps to follow, Reference for examples and edge cases, History for old decisions. Both files are large enough that an agent loading them at routing time pays for context it doesn't need yet.
startup.org keeps Summary, Execution, and Reference (workflow discovery and common mistakes moved under Reference). triage-intake.org gets all four, including a History lane for its design notes. Every instruction is preserved. The triage reorder ran through a content-preservation check that compared the multiset of content lines before and after, so only heading depth and lane grouping moved. Nothing was dropped or reworded.
workflow-integrity.py now counts "Summary" as a valid orientation heading, since that's the new top section both files lead with.
This is the pilot from the codex backlog, scoped to the two largest workflows. Whether the lanes actually cut session token use gets evaluated before any wider rollout.
|
| |
|
|
|
|
| |
daily-prep's Phase 3 re-implemented email/Slack/Linear/PR scanning inline (sub-steps 3b-3g, ~280 lines): the same fan-out, classify, and reactive-task work the triage-intake engine has owned since its 2026-05-26 plugin refactor. I collapsed those to four steps: 3b runs the engine, 3c surfaces today's reactive items as Day's Priorities thin links, 3d re-sorts by urgency, 3e writes the audit footer from the engine's per-source coverage.
Source coverage carries because the engine's Phase 0 globs both .ai/workflows/ and .ai/project-workflows/ plugins, so the work account's Gmail/Slack/Linear/GHE plugins are still scanned, and a source change now lives in one plugin instead of being duplicated here. I adapted the downstream references (the Prep-Doc-Structure rule, the Heads-up FYI source, the Recommended Approach Pattern reframed as engine-applied), dropped the orphaned Linear-digest note, and added a Living Document entry. The file goes 825 to 576 lines, and the prep-doc contract (Day's Priorities, Heads-up, Sources-checked footer) is unchanged.
|
| |
|
|
| |
Diffed the testing.md / verification.md copies in the deepsat coding-rulesets and orchestration_dashboard_mvp trees against canonical. Both are byte-identical to each other and stale (testing 221 lines behind, verification 40), with only 5 lines unique to the copies. Left untouched per the standing decision: they're team-owned, and canonicalizing would create a cross-repo dependency on the private rulesets, with the orchestration copy team-visible. Audit-only; no files modified.
|
| |
|
|
|
|
| |
Audited the python-testing / typescript-testing / elisp rule copies across the project mirrors. Four are in sync with canonical; gloss and chime are byte-identical to each other and purely stale (44 + 1 lines behind, with zero project-specific additions). No intentional divergence exists anywhere.
Disposition: leave them project-local. The language-rule copies in code projects are the bundle's deliberate copy-and-sync model, and sync-language-bundle.sh auto-fixes drifted bundle rules on each startup, so gloss and chime self-heal on their next boot. Symlinking would fight that model, and the work/deepsat copies stay untouched as team territory. Audit-only; findings recorded in the task.
|
| |
|
|
|
|
| |
Agents (and any future inventory tool) doing a naive recursive read of a project pick up node_modules, __pycache__, build output, and token artifacts even when those are gitignored, because a recursive read sees the disk, not git. I added a gitignore-syntax .aiignore at the repo root with the default skip list, and a protocols.org "Recursive Reads" subsection documenting the convention, the defaults to assume absent a file, and the lockfile policy (skip on agent reads, independent of git-tracking).
I did not wire the walking scripts (audit.sh, diff-lang.sh, sync-language-bundle.sh): they do targeted finds over .ai/.claude/bundle dirs, never whole-tree walks, so honoring .aiignore there would be dead code. That belongs in a future catalog tool.
|
| |
|
|
|
|
| |
Startup's drift check catches index-vs-directory mismatches. This goes deeper: scripts/workflow-integrity.py runs six checks over the canonical .ai/workflows/: each file is indexed-or-a-plugin-of-an-indexed-engine, each index entry resolves to a file, each .ai/scripts/ reference resolves, each plugin maps to an indexed parent, each non-plugin workflow has an orientation section, and no trigger phrase is claimed by two workflows. Exit 1 on any finding.
scripts/tests/workflow-integrity.bats covers the clean canonical state plus a fixture per breakage class. make test already globs scripts/tests/*.bats, so it's wired in. I calibrated against the 38 current workflows (clean). The orientation check accepts the real heading variety (Overview / Purpose / When to Use|Run / Status) and exempts plugins.
|
| |
|
|
| |
Reviewed the open-task list for solo-ness (whether I can complete a task end to end and verify it without input). Tagged seven :solo: — five that already qualified (workflow test harness, daily-prep delegation, rule-duplication audit, .aiignore, coverage-summary) and two unblocked by a decision today: the category-3 rule copies (leave team-tree copies alone, don't reach into team repos) and the token-tier pilot (approved the four-lane structure for both startup.org and triage-intake.org). The google-docs token-rotation helper stays unmarked, held until a real rotation lets me write and verify in one pass. Each decision is recorded in its task body.
|
| |
|
|
| |
From a pearl handoff: Phase 6 logged deferred and v1 work only in passing, so the implementer handoff was a re-read of the spec rather than a paste. I added a step that lifts the spec's Implementation phases section into a drop-in todo.org block: one [#B] TODO per phase plus a test-surface entry mirroring the Acceptance criteria. A spec with no phase decomposition fails the step, surfacing the shape problem as a finding before Ready rather than inventing phases. Added Exit Criterion 6 and a review-history entry.
|
| | |
|
| |
|
|
|
|
| |
Handoffs that arrive mid-session used to sit unseen until the next startup or a manual check. Today's burst of cross-project handoffs made that gap obvious. I added monitor-inbox.org, the cadence-and-decision layer over process-inbox: check the inbox at every task boundary, decide act-now (just do it) versus file (ask, with filing as option 1), and reply to the sender. An opt-in background-monitor /loop recipe covers unattended watching.
inbox-status (with bats tests) is the cheap check the cadence calls. It lists unprocessed handoffs and exits nonzero when any are pending, using the same artifact exclusions as the wrap-up sanity check. protocols.org gets a short cadence note so the habit fires every session, and INDEX.org lists the new workflow. The act-vs-file rule (act-now is silent, filing asks with file as option 1, ambiguity asks) is the decision protocol we settled today.
|
| |
|
|
| |
An org-drill session asked to send a follow-up email first claimed it couldn't, then hand-built MIME through msmtp, because nothing told it cmail send exists. I added a "Sending Email" subsection to protocols.org (read every session): cmail (c@cjennings.net) is the default for personal mail, dmail for work, and cmail-action send is the tool, with one-liner examples for body-file, attachments, Cc/Bcc, and threaded replies. I also rewrote send-email.org Step 4, replacing the inline-Python heredoc that taught the hard way with the cmail-action send call.
|
| |
|
|
| |
cmail-action send couldn't do a proper reply (no Cc/Bcc, no In-Reply-To/References), so an org-drill session that needed to reply to an upstream maintainer hand-rolled a raw MIME message through msmtp instead. I extended build_message (the pure function) with cc, bcc, in_reply_to, and references, wired the matching --cc/--bcc (repeatable), --in-reply-to, and --references flags through cmd_send, and wrote the tests first. send_message derives recipients from the To/Cc/Bcc headers and strips Bcc, so no manual recipient list is needed.
|
| |
|
|
|
|
| |
A single .ai/session-context.org races when two agents share a project: each agent's writes clobber the other's session log. I added .ai/scripts/session-context-path, which resolves the active path from AI_AGENT_ID: unset gives the legacy .ai/session-context.org singleton (so every existing one-agent session is unchanged), set gives .ai/session-context.d/<id>.org with the id sanitized to filename-safe characters. This is Codex's Phase 1 slice from the runtime-neutral spec: the race fix on its own, no broader refactor.
startup.org's existence check and wrap-it-up.org's rename now resolve through the helper, each with a singleton fallback so older checkouts that haven't synced the script still work. Wrap folds the agent id into the archive name so two agents wrapping in the same minute don't collide. protocols.org documents the rule. Verified with 5 bats cases and a two-agent simulation showing distinct paths per id.
|
| |
|
|
|
|
| |
From jr-estate's handoff: Phase A's =rsync -a --delete= copies the rulesets working tree by disk presence, so a downstream session that starts while rulesets has in-flight WIP pulls that WIP into its own =.ai/workflows/= and =.ai/scripts/=, where it reads as drift the user never authored. I guarded the three rsyncs behind a =git status --porcelain= check on the synced source paths (=claude-templates/.ai/{protocols.org,workflows/,scripts/}=). It syncs when those are clean and skips with a message when dirty, catching up on the next clean session. The check is scoped to those paths, so unrelated rulesets dirt (a stray session-context.org, scratch files) doesn't block the sync.
The handoff's secondary anomaly (two workflow files that didn't reach jr-estate) was a timeline artifact, not a Phase A bug. Both were added in 664bf01 on 2026-05-29, after jr-estate's rsync had already run, so they correctly didn't exist to copy yet.
|
| |
|
|
|
|
| |
org-lint reads an =** Foo= verbatim span in body prose as a possible misplaced heading, but verbatim markup is never a real heading. lint-org kept surfacing these as judgment items, so they recurred in lint-followups.org on every wrap and could never be acted on, since the todo.org content was already correct.
I added lo--verbatim-asterisk-at-line-p, which mirrors the markdown-bold detector: it checks the reported line and the one before it, since org-lint marks the blank line after the offender. A match is now suppressed silently, the same way the cj-comment false positives already are. I flipped the two tests that pinned the old judgment behavior, and confirmed todo.org lints clean (judgment=0). This resolves the checker-bug report I filed in the inbox earlier, which I removed.
|
| |
|
|
| |
The wrap-up lint pass kept re-flagging two verbatim-asterisk misplaced-heading items (=** DONE= and =** Startup Pull Ordering= shown as verbatim references in prose) that aren't real misplaced headings and can't be fixed in todo.org. Filed a checker-bug report in the inbox to suppress that class going forward, and dropped the two unactionable items from lint-followups.org.
|
| | |
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
| |
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.
|
| | |
|
| |
|
|
|
|
|
|
| |
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.
|
| | |
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
| |
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.
|
| |
|
|
|
|
| |
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.
|
| | |
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
| |
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.
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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].
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|