diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-30 12:40:02 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-30 12:40:02 -0500 |
| commit | 80e76cb91e645758d53755d2b7fbe94426d45a06 (patch) | |
| tree | b55945297dcd8fb9782d39683f03fdc8ec782a57 /.ai/scripts | |
| parent | e3059241c61bd97e837f218f29eb657f02c2cddb (diff) | |
| download | rulesets-80e76cb91e645758d53755d2b7fbe94426d45a06.tar.gz rulesets-80e76cb91e645758d53755d2b7fbe94426d45a06.zip | |
feat(workflows): add drill-deck-review + extend drill-to-anki script
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.
Diffstat (limited to '.ai/scripts')
| -rwxr-xr-x | .ai/scripts/drill-to-anki.py | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/.ai/scripts/drill-to-anki.py b/.ai/scripts/drill-to-anki.py index 50e1afd..543ccd8 100755 --- a/.ai/scripts/drill-to-anki.py +++ b/.ai/scripts/drill-to-anki.py @@ -100,6 +100,32 @@ def title_from_org(org_text: str) -> str | None: return None +def strip_org_metadata(body_lines: list[str]) -> list[str]: + """Drop :PROPERTIES: drawers and SCHEDULED/DEADLINE/CLOSED planning lines. + + Org-drill needs these in the source file (SRS state lives in the + PROPERTIES drawer; SCHEDULED carries the next-review date), but they + are noise on the back of an Anki card. + """ + cleaned: list[str] = [] + in_drawer = False + planning_re = re.compile(r"^\s*(SCHEDULED|DEADLINE|CLOSED):\s") + drawer_start_re = re.compile(r"^\s*:PROPERTIES:\s*$") + drawer_end_re = re.compile(r"^\s*:END:\s*$") + for line in body_lines: + if in_drawer: + if drawer_end_re.match(line): + in_drawer = False + continue + if drawer_start_re.match(line): + in_drawer = True + continue + if planning_re.match(line): + continue + cleaned.append(line) + return cleaned + + def parse(org_text: str) -> list[tuple[str, str, str]]: """Return [(front, back_html, tag), ...] for every :drill: card.""" cards: list[tuple[str, str, str]] = [] @@ -130,6 +156,7 @@ def parse(org_text: str) -> list[tuple[str, str, str]]: break body_lines.append(nxt) i += 1 + body_lines = strip_org_metadata(body_lines) while body_lines and not body_lines[0].strip(): body_lines.pop(0) while body_lines and not body_lines[-1].strip(): |
