From ddf48dc7ac780da1aacdff4e03f1d7da255b8f39 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 31 May 2026 12:19:34 -0500 Subject: feat: add rename-ai-artifact tool and rename the drill-deck family to flashcard 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. --- .ai/workflows/INDEX.org | 6 +- .ai/workflows/drill-deck-review.org | 327 ------------------------------------ .ai/workflows/flashcard-review.org | 327 ++++++++++++++++++++++++++++++++++++ .ai/workflows/rename-artifact.org | 44 +++++ 4 files changed, 375 insertions(+), 329 deletions(-) delete mode 100644 .ai/workflows/drill-deck-review.org create mode 100644 .ai/workflows/flashcard-review.org create mode 100644 .ai/workflows/rename-artifact.org (limited to '.ai/workflows') diff --git a/.ai/workflows/INDEX.org b/.ai/workflows/INDEX.org index c8554d4..157a4e7 100644 --- a/.ai/workflows/INDEX.org +++ b/.ai/workflows/INDEX.org @@ -86,14 +86,16 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e - Triggers: "page me on signal", "signal me when X is done", "send a signal note about X" - =cross-project-broadcast.org= — fan out a single message to every AI project's inbox via the discovery helper =cross-project-broadcast.py= + the existing =inbox-send.py=. Use sparingly for capability announcements and shared rule changes; not for project-specific handoffs. - Triggers: "broadcast this to every project", "notify every project about X", "fan out this announcement", "let every project know X is available" -- =drill-deck-review.org= — review an org-drill flashcard file, restructure cards to question-form headings (no answer hints), audit content accuracy against project source-of-truth via subagent, rewrite source preserving SRS state, regenerate the Anki =.apkg= to =~/sync/phone/anki/=. Person cards use "Who is X? Tell me about their Y."; talking-points cards stay as-is. Script behavior: =drill-to-anki.py= strips =:PROPERTIES:= drawers + =SCHEDULED:= / =DEADLINE:= planning lines from Anki output. - - Triggers: "review the drill deck", "update the drill deck", "refresh the Anki cards", "let's run the drill-deck-review workflow" +- =flashcard-review.org= — review an org-drill flashcard file, restructure cards to question-form headings (no answer hints), audit content accuracy against project source-of-truth via subagent, rewrite source preserving SRS state, regenerate the Anki =.apkg= to =~/sync/phone/anki/=. Person cards use "Who is X? Tell me about their Y."; talking-points cards stay as-is. Script behavior: =flashcard-to-anki.py= strips =:PROPERTIES:= drawers + =SCHEDULED:= / =DEADLINE:= planning lines from Anki output. + - Triggers: "review the flashcards", "update the flashcards", "review the drill deck", "update the drill deck", "refresh the Anki cards", "let's run the flashcard-review workflow" - =page-me.org= — set a timed notification. - Triggers: anything containing the word "page" used as a verb ("page me", "page me in 10 minutes", "page me at 3pm") - =status-check.org= — proactive long-running-job updates. - Triggers: "keep me posted on this", "provide status checks on this job", "let me know when it's done", "monitor this for me". Auto: any job estimated 10+ min. - =create-workflow.org= — define a new workflow. - Triggers: "let's create/define/design a workflow for [activity]", or unmatched workflow request after this index returns no hit. +- =rename-artifact.org= — rename an =.ai/= workflow or script across the canonical + mirror trees, rewriting every reference on a token boundary and leaving =sessions/= history alone. Backed by =scripts/rename-ai-artifact.sh=, which runs =workflow-integrity= + =sync-check= after the move. + - Triggers: "rename this workflow", "rename the [X] workflow/script", "let's run the rename-artifact workflow". - =no-approvals.org= — drop the interaction-level approval gates for a pre-agreed batch while keeping engineering-discipline gates (=/review-code=, =/voice personal=, tests, session-log updates, subagent reviews, destructive-action consent). Mode stays on until Craig turns it off, a real question arises, the queue empties, or the conversation switches topics. - Triggers: "no-approvals mode", "no approvals", "no-approval", "no need for approval gates", "stop asking, just keep going", "I'll check back in when you're done or stuck", "do all == with no-approval" - =cross-agent-comms.org= — protocol for cross-project agent coordination via =inbox/from-agents/= (file-based IPC, GPG-signed, supports cross-machine over Tailscale). Auto: when =cross-agent-watch= detects a new inbound message, or when an agent decides to initiate a cross-project conversation. Operational scripts (=cross-agent-send=, =-recv=, =-watch=, =-status=, =-discover=, =-halt=, =-resume=) and their READMEs live at =.ai/scripts/cross-agent-comms/=. diff --git a/.ai/workflows/drill-deck-review.org b/.ai/workflows/drill-deck-review.org deleted file mode 100644 index 390f296..0000000 --- a/.ai/workflows/drill-deck-review.org +++ /dev/null @@ -1,327 +0,0 @@ -#+TITLE: Drill Deck Review Workflow -#+AUTHOR: Craig Jennings & Claude -#+DATE: 2026-05-30 - -* Overview - -Take an org-drill flashcard file and bring it into the canonical shape — every card a question that doesn't give the answer away, every fact current — then regenerate the Anki =.apkg= and drop it where the phone can sync it. - -The workflow has three substantive passes (question-form audit, content-accuracy audit, source rewrite) followed by a mechanical regenerate-and-place step. Content review is dispatched to a subagent because it's bounded research across project source-of-truth files; the structural rewrite stays in the main thread because it touches the SRS state we don't want to lose. Three helper scripts (=drill-deck-stats.py=, =drill-deck-diff-ids.py=, =drill-deck-sync=) automate the inventory, the safety check, and the regenerate-and-place. - -*Scheduling lives on the Anki side.* Desired retention and the FSRS scheduling model are per-deck Anki options set on the phone, never controlled by the org source or =drill-to-anki.py=. The pipeline's only scheduling job is keeping each card's identity (the =:ID:=-derived GUID) stable so Anki's review history survives a rewrite. Don't try to encode retention, intervals, or org-drill's SM-2 state into the Anki output — the two schedulers are separate, and the import carries only card content plus identity. (Anki's desired-retention default is 90%; see [[https://docs.ankiweb.net/deck-options.html][the deck-options manual]].) - -* When to Use This Workflow - -Trigger phrases: - -- "Review the drill deck" -- "Update the drill deck" -- "Refresh the Anki cards" -- "Let's run the drill-deck-review workflow" - -Typical timing: - -- After a wave of personnel changes (titles, roles, employment status) -- After a major milestone (a demo ships, a contract closes, a submission goes in) -- When org-drill review surfaces a card with stale or wrong content -- When the Anki deck on the phone hasn't been regenerated in weeks - -* Inputs - -- *Source file*: the org-drill file. Common locations: - - =deepsat.org= at the work project root (symlinked from =~/sync/org/drill/=) - - =health-drill.org= in the health project - - Any =:drill:= deck under =~/sync/org/drill/= -- *Source-of-truth docs for content accuracy*: project-specific. Typical set: - - Project-root =knowledge.org=, =status.org=, =notes.org= - - =todo.org= for the freshest signal on people / partnerships / projects - - =deepsat/assets/= (or equivalent) for meeting transcripts when a specific fact needs confirmation -- *Output location*: =~/sync/phone/anki/.apkg= (the phone-sync target). Both =drill-to-anki.py= and the =drill-deck-sync= wrapper default there. - -* Canonical Card Shape - -** Deck title (=#+TITLE:= line) - -The =#+TITLE:= line at the top of the source file drives two surfaces: the org-drill display in Emacs and the Anki deck name on the phone. Pick a title that reads well in Anki — drop tool-name jargon like "Org-Drill" / "Drill" that's meaningful in Emacs but noise on the consumption side. - -Good: =DeepSat Flashcards=, =Health Flashcards=, =Philosophy Flashcards=. -Bad: =DeepSat Org-Drill Flashcards=, =DeepSat Drill Deck=. - -=drill-deck-stats.py= flags any title containing =org-drill= (case-insensitive, hyphenated or spaced) as a workflow violation. - -*Stable-ID caveat.* =drill-to-anki.py= derives the Anki deck ID from the deck name. Changing =#+TITLE:= changes the deck ID, so the next import lands as a new deck rather than updating the existing one. Two consequences worth flagging: - -- Any review history accumulated in Anki under the old deck name stays attached to the old deck — it doesn't migrate. -- On rename, delete the old deck from Anki to avoid having two decks with similar content. - -For most decks (especially on first deployment), this is a one-time event. The rename is cheap to do early. - -** Heading (the question) - -Every card heading is a question that doesn't reveal the answer. Not the topic name, not the acronym, not the person's name — a question that tests recall. - -Three card families have different question shapes: - -*** Acronym / concept cards -"What does X stand for and what is it?" or "What is X and why does it matter?" Promote the question that was already in the body up to the heading. - - Before: - : ** AFRL :drill: - : What does AFRL stand for and what is it? - : *** Answer - : Air Force Research Laboratory. ... - - After: - : ** What does AFRL stand for and what is it? :drill: - : Air Force Research Laboratory. ... - -*** Person cards -Format: "Who is X? Tell me about their Y." where X is a role descriptor that doesn't name the person, and Y is whatever the answer body covers (background, role, limitations, scope). The answer body opens by naming the person, then continues. - - Before: - : ** Vrezh Mikayelyan :drill: - : Who is Vrezh? What are his key limitations? - : *** Answer - : Developer (also called "Reg"). Armenia-based ... - - After: - : ** Who is DeepSat's Armenia-based developer? Tell me about his background and limitations. :drill: - : Vrezh Mikayelyan. Armenia-based, full-time as of April 2026. Worked with Hayk at Bazoomq on Armenia's first satellite ... - - Note: pick a role descriptor that genuinely identifies one person. If multiple people share the role description, add a single distinguishing detail (e.g., "the one who works evenings", "the Vineti alum"). Don't pile on parentheticals. - - Splitting: the person card deliberately trades atomicity for narrative recall — one card carries identity plus several attributes. When a body bundles genuinely unrelated attributes (role, employment history, limitations, scope) rather than one coherent topic, split it into multiple cards. One inherits the existing =:ID:= (and its SRS history); each new sibling starts fresh and will correctly show in =drill-deck-diff-ids.py= as an appeared ID. The criterion: split when the body reads as a list of separate facts, keep it whole when it reads as one story. (Minimum-information principle — Wozniak rule 4, Matuschak "Focused".) - -*** Talking-points and directive cards -Already in prompt form ("Introduce Yourself", "Spell out these orbital regime acronyms", "What is DeepSat?"). Leave the heading alone. Still strip the =*** Answer= sub-header and audit the body content for staleness. - -The =drill-deck-stats.py= helper recognizes both =?=-form and imperative-verb form as valid prompts (verbs like Spell, Describe, Explain, Name, List, Give, Show, Tell, Define, Compare, Identify, Outline, Introduce, Walk, State, Recite, Recall, Summarize). - -** Body (the answer) - -- *No =*** Answer= sub-header.* The body /is/ the answer; the heading /is/ the question. The sub-header was a workaround for topic-as-heading cards. -- *Body opens by naming the topic.* "Air Force Research Laboratory. Air Force's R&D arm." or "Vrezh Mikayelyan. Armenia-based, full-time as of ..." The Anki back shows this directly under the front question; restating the topic makes the back read as a complete answer. -- *PROPERTIES drawer stays.* Org-drill needs the =:ID:=, =:DRILL_LAST_INTERVAL:=, =:DRILL_EASE:= etc. for SRS state. The Anki output strips it (see the script change). -- *=SCHEDULED:= / =DEADLINE:= planning lines stay.* Same reason. The Anki output strips them. -- *Source citation goes at the very end, after two blank lines.* When a card cites a source, put a =Source: