#+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 (=flashcard-stats.py=, =flashcard-diff-ids.py=, =flashcard-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 =flashcard-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 flashcard-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 =flashcard-to-anki.py= and the =flashcard-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=. =flashcard-stats.py= flags any title containing =org-drill= (case-insensitive, hyphenated or spaced) as a workflow violation. *Stable-ID caveat.* =flashcard-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 =flashcard-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 =flashcard-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: