| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
The skill's chat echo (report, criterion table, verdict, draft summaries) was rendering bold and backtick spans as reverse video, which is hard to read in the terminal. Phase 5 now requires plain text for everything echoed to chat, while the artifact posted to GitHub keeps normal markdown. It's the same constraint as interaction.md's no-reverse-video rule, repeated at the print step where the violation actually happens.
|
| |
|
|
| |
A freshly-added authinfo.gpg entry still reads as missing in the running daemon until auth-source's result cache expires. It caches both positive and negative lookups for auth-source-cache-expiry (default 7200s), so a key you just synced in keeps erroring as "not set" with no sign the file is fine. Clear it with (auth-source-forget-all-cached) and re-trigger the lookup. I hit this diagnosing a Linear/pearl API-key error after the key landed via a dotfiles sync.
|
| | |
|
| |
|
|
|
|
| |
install-ai now creates a top-level inbox/ with a .gitkeep in every project it bootstraps. inbox-send treats a project as a messaging target only when it has both a .ai/ marker and a top-level inbox/, so before this a freshly bootstrapped project couldn't receive cross-project handoffs until the inbox was made by hand.
The directory is created in both track and gitignore modes, since inbox/ is a project-root convention independent of whether .ai/ is tracked. The step is idempotent, so a project that already has an inbox keeps its contents.
|
| |
|
|
|
|
| |
Prep docs are now born in daily-prep/YYYY-MM-DD-daily-prep.org and never move. A stable symlink at the project root, daily-prep.org, points at the current day's file and is the only thing that changes day to day. I replaced the old model where the doc was born in inbox/, yesterday's lingered there, and older docs were moved into a daily-prep/ archive.
Consumers resolve the root symlink instead of reconstructing a dated filename or scanning inbox/: the standup lookback, next-day Phase 2, and the Emacs opener. Phase 8 becomes a symlink repoint rather than an archive move, and triage-intake's prep-doc anchor fallback now points at daily-prep/ only.
|
| | |
|
| |
|
|
|
|
|
|
| |
Phase A's startup rsync copies template updates from rulesets into each project's .ai/, but nothing committed that churn, so it accumulated across sessions and eventually blocked Phase A.0's auto-fast-forward (git won't ff a dirty tree). Two projects hit it the same day.
I added a Step 4.0 to wrap-it-up.org that commits the churn as its own chore commit before the session-work commit, guarded so it only auto-commits synced .ai paths matching rulesets canonical byte-for-byte and surfaces anything that doesn't. startup.org Phase C now surfaces leftover churn at session start as the crashed-session safety net. Both skip the rulesets repo, where .ai/ is a committed mirror.
I also moved four misplaced PROPERTIES drawers in todo.org (DONE tasks) from after the resolution prose to immediately under the CLOSED line, so org parses them as real drawers.
|
| |
|
|
| |
Move the completed solo-batch, rename-tool, and coverage-fan-out tasks into Resolved, and file the lint-org judgment items for the next daily-prep.
|
| |
|
|
|
|
|
|
|
|
| |
Last language in the coverage-summary fan-out, after Elisp, Python, and Go. Same kernel: count every source file on disk that's absent from the coverage report as 0% and weight the project number by file, so an untested file stays visible instead of being averaged away.
The script at languages/typescript/claude/scripts/coverage-summary.js parses an Istanbul json-summary report (the coverage-summary.json that c8, Vitest, and Jest all emit), takes per-file statements covered over total, and reports a file-weighted number plus the missing files. It walks the source dir for .ts/.js, skipping test files, declarations, and node_modules. Node built-ins only, so it runs via node with no install, and it doesn't reimplement the per-file table nyc already prints.
Tests are black-box, run with node's own test runner: a temp tree plus a json-summary report, the script invoked via node, output asserted. They cover missing-file detection, all-tracked, test-file and node_modules exclusion, and the missing-report error. make test gained a node --test discovery path for languages/*/tests, guarded so environments without Node skip it cleanly. As with Python, the TypeScript bundle had no gitignore-add.txt, which would have left the script un-gitignored on install, so I added one.
This finishes the fan-out: coverage-summary now ships in all four bundles, each parsing its own tool's report behind the same file-weighted, missing-as-0% kernel. I proved the Go and TypeScript scripts by running them (Go against a live profile, TS against a synthetic report and the CLI). Python and TypeScript weren't run against a live coverage tool, since neither coverage.py nor nyc is installed here, so the first adopter of each should check against a real report.
|
| |
|
|
|
|
|
|
|
|
| |
Third language in the coverage-summary fan-out, after Elisp and Python. Same kernel: count every source file on disk that's absent from the coverage profile as 0% and weight the project number by file, so an untested file stays visible instead of being averaged away.
The script at languages/go/claude/scripts/coverage-summary.go parses a cover.out profile, maps each import-path-qualified entry back to an on-disk relative path using the module path from go.mod, and reports a file-weighted number plus the missing files. It's standard library only, so it runs anywhere via go run, and it doesn't reimplement the per-function table that go tool cover -func already prints. I proved it against a real go test -coverprofile run, not just a synthetic fixture, since the Go toolchain is installed here.
Two findings to flag. Modern go test ./... already lists every module package in the profile at 0% even when untested, so for in-module code the missing-file list is usually empty. The detection earns its keep on build-tagged files and dirs outside ./.... And this is a coverage-only slice of a Go bundle that doesn't otherwise exist yet: there's no go.md rule file, so sync-language-bundle.sh can't fingerprint it (detection keys on a bundle's own .claude/rules). The script installs via make install-lang LANG=go but won't be sync-maintained until the Go bundle gets real rules and a CLAUDE.md. Building that out is the natural companion task.
Tests are black-box: a Go test in its own throwaway module runs the script via go run against temp fixtures and checks output, so the shipped script dir stays test-free. They cover missing-file detection, all-tracked, _test.go exclusion, and the missing-report error. make test gained a go test discovery path for languages/*/tests, guarded so environments without Go skip it cleanly.
|
| |
|
|
|
|
|
|
|
|
| |
Second language in the coverage-summary fan-out, after the Elisp pilot. Same kernel: a module no test imports never appears in coverage.py's report, so a line-weighted total skips it silently and the suite looks healthier than it is. This counts every source file on disk that's absent from the report as 0% and weights the project number by file, so untested modules stay visible.
The script at languages/python/claude/scripts/coverage-summary.py parses coverage.py's JSON (files[path].summary.covered_lines / num_statements), resolves report paths against the report's directory since coverage records them relative to where it ran, and recurses the source dir for *.py. Unlike the Elisp version it doesn't print a per-file table, because coverage.py's own coverage report already does. The script adds the missing-file accounting that report lacks. It uses only the standard library, parsing the report rather than importing coverage.
The Python run confirmed the plumbing from the pilot is genuinely generic. install-lang and sync deliver the script and the project-owned coverage-makefile.txt with no Python-specific code. The one gap I had to close: the Python bundle shipped without a gitignore-add.txt, so the .claude/ footprint wasn't ignored and the script would have been committable. Added one mirroring the Elisp footprint plus Python artifacts (__pycache__, .coverage, coverage.json). make test gained a languages/*/tests/test_*.py discovery path alongside the existing Elisp ERT one.
Tests: 12 pytest covering the parser, the file-weighted number, and the missing-file detection including subpackage recursion, plus an install-lang check that the script lands in the gitignored footprint. I proved it against a report matching coverage.py's documented schema and the CLI end to end, but not against a live coverage json run, because coverage.py isn't installed in this repo's env. The first project to adopt it should sanity-check against a real report.
|
| |
|
|
|
|
|
|
|
|
| |
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.
|