aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* feat: optionally show the card's outline path in the drill promptHEADmainCraig Jennings13 days2-1/+76
| | | | | | A new defcustom org-drill-show-outline-path-during-drill (default off) prepends the card's ancestor path to the mini-buffer prompt, for example [Spanish > Greetings], so during a drill you can see where the current card sits in the deck. With it off the prompt is byte-for-byte unchanged. I ported this from m.galimski's fork (commit c6d0c850) and gated it behind the defcustom rather than leaving it always on. The path comes from org-get-outline-path through a small org-drill--outline-path-string helper. Tests cover the helper (nested and top-level), the prompt with the switch on and off, and the default value.
* docs: fix README default-scheduler marker (simple8, not SM5)Craig Jennings13 days1-1/+1
|
* feat: add statistics dashboard CSV export and docsCraig Jennings14 days4-1/+419
| | | | | | | | org-drill-statistics-export-csv, bound to e in the dashboard and now implemented, writes three files into a chosen directory honoring the active scope and range: sessions.csv (one row per recorded session), cards.csv (one row per drill card in scope with its scheduling properties and computed status), and daily.csv (per-day reviews, passes, fails, pass percent, and duration). Fields are quoted per RFC 4180 by a small csv-quote helper, since csv-mode isn't a dependency. This is the dashboard's last piece, step 3 of the spec. The row builders for the three views are pure and unit-tested with deterministic fixtures, and csv-quote covers the comma, quote, and newline cases. I documented the dashboard and the export in org-drill.org (a new section with the keymap, the CSV columns, and the settings table) and added a feature bullet to the README. I also removed the now-redundant declare-function forward reference for export-csv. It named this file as the source, so once the real defun landed the byte-compiler counted the function twice and warned.
* feat: add the org-drill statistics dashboard rendererCraig Jennings14 days16-0/+3387
| | | | | | | | | | Step 1 shipped the session-log data layer. This is the renderer on top of it. org-drill-statistics opens a read-only dashboard with five sections: an overview (card counts plus a last-session recap), trends (reviews-per-day and pass-rate-per-day quadrant-block sparklines over the trend window, plus a 12-week table), a quality histogram, a needs-attention view (leech candidates, long-overdue, and forgotten-new cards), and a 7-day forecast counted from SCHEDULED dates. A buffer-wide filter (scope, range, algorithm) sits in the header and cycles with s/r/a. The other keys are q to bury, g to refresh, e for the CSV-export hook that lands next, and RET to follow the card link at point. The aggregation math lives in pure helpers (day-bucketing, sparkline scaling, weekly aggregates, the histogram, the attention selectors, forecast bucketing). The render helpers are thin string formatters over them, so the logic is unit-tested independently of the UI. New defcustoms tune the views: org-drill-statistics-trend-days, -forecast-days, -attention-row-limit, and -leech-quality-threshold. I added require 'calendar for the Monday week-start arithmetic in the weekly aggregates. CSV export and the manual and README entries are the step-3 follow-on.
* docs: fold Review 2 into the FSRS spec (Response 2)Craig Jennings14 days2-157/+623
| | | | | | | | | | Review 2 (Codex) flagged three blockers in my Response 1, and two were mine to own. I'd reversed DRILL_CARD_WEIGHT: I described SM as multiplying the interval when it actually divides the delta, so weight 2 means more frequent review, not less. And I'd locked a quality mapping that both claimed to honor org-drill-failure-quality and hard-coded a fixed table. Craig's calls: FSRS matches the existing SM/Simple8 delta interpolation for weight, and honors org-drill-failure-quality for the Again boundary with a fixed Hard/Good/Easy sub-mapping. I fixed both throughout the spec and grounded each claim in the cited org-drill.el line so the description can't drift again. I also reframed the status to "Needs research" with the three real prerequisites stated honestly (pin the py-fsrs source, cross-check the equations, generate the reference vectors), fixed DRILL_LAST_REVIEWED ownership back to the shared reschedule flow, and noted that fsrs has to join the algorithm defcustom and the safe-local whitelist. The review file is committed alongside the spec as the audit trail. The spec is still not implementation-ready, and now says so plainly.
* feat: add org-drill-treat-headline-as-card-p for empty-bodied cardsCraig Jennings14 days3-2/+110
| | | | | | | | A drill entry with an empty body is skipped unless its card type opts into empty bodies via the DRILL-EMPTY-P slot of org-drill-card-type-alist. That left no global way to drill headline-only items, or hierarchical-notes decks where the heading is the question and the answer lives in child entries (upstream #30, #41). I added org-drill-treat-headline-as-card-p, default nil so existing decks are unchanged. When it's on, the empty-skip gate in org-drill--entry-empty-and-not-empty-friendly-p short-circuits, so every empty-bodied entry is drilled with its heading as the question regardless of card type. I added the safe-local-variable booleanp declaration alongside the other booleans and documented the switch in org-drill.org. Tests pin the predicate and the classify-status outcome both on and off, and confirm the per-card-type DRILL-EMPTY-P path stays independent of the new switch.
* docs: archive 2026-05-30 follow-up to upstream maintainerCraig Jennings14 days1-0/+22
|
* fix: detect #+FILETAGS decks so org-drill-mode auto-enablesCraig Jennings2026-05-302-10/+79
| | | | | | | | org-drill-buffer-has-cards-p only scanned for a per-heading :drill:/:leitner: tag, so a deck tagged through #+FILETAGS: had no match and org-drill-mode never auto-enabled. Those files opened without cloze highlighting. I extended the predicate to also scan #+FILETAGS: lines, handling both the space-separated and colon-delimited syntaxes, with [: \t] boundaries so a value like drilldown can't false-match drill. The inherited-top-level-tag case already worked, since the ancestor heading line carries the literal tag and the per-heading scan catches it. Tests cover filetag-only decks (space, colon, leitner), the inherited-top-level lock, the substring boundary, and auto-enable on a filetag-only buffer.
* docs: archive 2026-04-29 outreach to upstream maintainerCraig Jennings2026-05-281-0/+49
| | | | | | I copied the outreach email I sent on 2026-04-29 into assets/ so the maintainership-adoption thread has a tracked record in the repo. The .eml file follows the YYYY-MM-DD-<topic>-<descriptor> naming so future correspondence lands alongside it. The email offered three options (push rights, formal handover, consolidated MR batch) and hasn't received a reply as of today, 29 days out.
* docs: relocate v0 design specs to docs/design/Craig Jennings2026-05-285-9/+7
| | | | | | | | | | | | | | | | I moved both v0 design specs out of working/ and into docs/design/. That's the conventional permanent home for project documentation, where engineers will look during implementation. working/ is meant for transient in-progress artifacts that file away once the work ships, and these specs are long-lived design docs that don't fit that contract. Files moved: - working/stats-dashboard/stats-dashboard.org → docs/design/stats-dashboard.org - working/fsrs-spec/fsrs-spec.org → docs/design/fsrs-spec.org The git rename detection picked both up, so file history follows the move. I also dropped the stale /docs entry from .gitignore. The Makefile doesn't write to docs/ and nothing else references it as a build output, so the ignore was inherited cruft that would have silently dropped any tracked file under docs/. I updated path references in seven spots: three docstring/comment refs in org-drill.el, one in tests/test-org-drill-session-record.el (the Commentary block), and three inside the specs themselves. Two refs in fsrs-spec.org now point at the correct location for its defcustom docstring and option description. One in stats-dashboard.org's References section points at the sister spec. Full make test-unit green. eask compile clean.
* feat: persist a session log for the stats dashboardCraig Jennings2026-05-283-8/+970
| | | | | | | | | | | | | | | | I added the persist + recording layer that the future stats dashboard reads, plus the v0 design spec at working/stats-dashboard/stats-dashboard.org that scoped the work. Every completed (non-suspended) drill session now appends one org-drill-session-record to a persisted org-drill-session-log via persist-defvar, mirroring the SM5-matrix pattern that's already in place. A new sibling defgroup org-drill-statistics groups the dashboard's customs. The record carries: start-time, end-time, scope, algorithm, qualities (as a vector), pass-percent, new-count, mature-count, failed-count, cram-mode. Scope and algorithm are captured at session-start time onto two new org-drill-session slots (scope-at-start, algorithm-at-start) so a mid-session defcustom flip doesn't misrepresent what was actually drilled. Both org-drill--prepare-fresh-session and the org-drill-again resume path populate the slots. I extracted pass-percent into org-drill--compute-pass-percent and call it from both org-drill-final-report and org-drill-session-record-from-session, so the user-visible report and the persisted record can't drift on the rounding or the failure-quality threshold. I wrapped recording in condition-case at the call site, not ignore-errors. Any recorder bug or persist-save IO failure gets messaged so silent data loss leaves a forensic trail. Corrupt-load recovery follows the SM5 path's condition-case fallback and adds an org-drill--session-log-quarantine helper that renames the bad file to a .corrupt-YYYY-MM-DDTHHMMSS sibling so the next save doesn't overwrite it. The seconds-granularity suffix prevents a same-day double corruption from clobbering the earlier quarantine. The helper depends on persist--file-location, an internal symbol guarded behind fboundp and documented inline. The spec at working/stats-dashboard/stats-dashboard.org is ratified, with all 10 originally-open decisions resolved in the Ratified Decisions table at the bottom: persist-defvar, warn-and-rename corrupt-load recovery, quadrant-block sparklines, a single buffer-wide filter, CSV with proper quoting, sync dashboard open, defer aborted-session recording, the q/g/e/s/r/a/RET keymap, leech-quality threshold default 2.5, and a sibling org-drill-statistics defcustom group. The remaining work (dashboard renderer, CSV export, docs) lands in follow-up commits. I followed TDD throughout. 24 tests in tests/test-org-drill-session-record.el cover struct construction, the shared pass-percent helper, the builder including scope/algorithm capture and mature-count summing, log appending with newest-first ordering and persist-save call-through, the symbol-bound smoke check, the quarantine rename plus its seconds-granularity contract, and the end-message hook (records on normal completion, skips on suspend, logs to message on recorder error). Full make test-unit green. eask compile clean.
* chore: gitignore the .notes session-memory fileCraig Jennings2026-05-281-0/+1
|
* feat: change default scheduling algorithm to simple8 with ADRCraig Jennings2026-05-272-2/+71
| | | | | | | | Closes upstream #46. I changed the default for org-drill-spaced-repetition-algorithm from sm5 to simple8. The defcustom now carries an ADR-style comment recording the reasoning: simple8 gives most of sm5's per-user adaptation value via its per-card difficulty learning, without sm5's dependency on a persisted optimal-factor matrix that can rot (the upstream #45 fragility), and it adjusts intervals for early or late reviews, a real-world concern sm2 and sm5 don't address. Existing users with the option set in their config see no change. Existing users on the previous default get simple8 on their next session, with no state migration. Simple8 reads what it can from the SM-style item-data and carries on. Sm5's persisted optimal-factor matrix stays on disk and is available if the user switches back. I added a test that pins the default value, so an accidental flip surfaces in CI.
* docs: add v0 design spec for FSRS schedulerCraig Jennings2026-05-271-0/+354
| | | | | | I drafted a design spec for adding FSRS (Free Spaced Repetition Scheduler) as a fourth choice in org-drill-spaced-repetition-algorithm. It pins FSRS-4.5 as the version target, documents the DSR state model, the rating mapping from org-drill's 0-5 quality to FSRS's 1-4 scale, the update equations, the new DRILL_FSRS_* properties, and the integration shape into the existing scheduler dispatch. The spec is v0 with six explicit DECIDE: markers carrying recommended defaults: version pin, parameter tuple, quality mapping, return shape, cold-start UI, and equation cross-check. The companion todo entry stays open at todo.org:125 until those land and implementation begins. The file moves to fsrs-spec.org at the project root once the DECIDEs resolve.
* refactor: take card-state in org-drill-determine-next-interval-simple8Craig Jennings2026-05-274-61/+64
| | | | | | | | Stage 5 of #147, closing the scheduler migration. simple8 now takes (state quality &optional delta-days) instead of seven positional args, binding the recall fields from the struct at the top so the algorithm body is unchanged. simple8 doesn't use ease, so the binding skips that slot. Both call-site branches collapse to (state quality [delta-days]), dropping the per-branch accessor unpacking. The testutil adapter test-scheduler--call-simple8 keeps the simple8 test calls a one-symbol rename per site. One direct simple8 call in tests/test-org-drill-prompt-and-format-helpers.el now uses the new struct API inline. With this stage landed, all three schedulers, the item-data round-trip, and every test caller go through the org-drill-card-state struct, finishing #147.
* refactor: take card-state in org-drill-determine-next-interval-sm5Craig Jennings2026-05-275-63/+75
| | | | | | | | Stage 4 of #147. sm5 now takes (state quality of-matrix &optional delta-days) instead of nine positional args, binding the recall fields from the struct at the top so the algorithm body is unchanged. Both call-site branches pass the state they already hold, dropping the per-branch accessor unpacking. The testutil adapter test-scheduler--call-sm5 keeps the sm5 test calls a one-symbol rename per site. I also kept the return as the existing list, matching the stage-3 refinement: the goal is reducing the input signature, and changing the return shape would force the shared return-extractors and every return-read to change for no real gain. Also folds in two stage-3 follow-ons I missed when sm2 landed: a direct sm5 call in tests/test-org-drill-small-branch-coverage.el now uses the new struct API inline, and five direct sm2 calls in the simple-workflow integration test now go through the testutil adapter (the integration file picks up the testutil-scheduler require). Caught by running make test-integration this stage, which I should have run on the sm2 stage.
* refactor: take card-state in org-drill-determine-next-interval-sm2Craig Jennings2026-05-273-68/+75
| | | | | | Stage 3 of #147. sm2 now takes (state quality) instead of seven positional args, binding the recall fields from the struct at the top so the algorithm body is unchanged. The smart-reschedule and hypothetical-next-review-date call sites pass the state they already hold, which drops the per-branch accessor unpacking. I kept the return as the existing positional list rather than restructuring it too. The goal is reducing the input signature, and changing the return shape would force the shared test extractors and every return-read to change for no real gain. A testutil adapter, test-scheduler--call-sm2, packs positional args into the struct, so the test call sites stay readable as the documented algorithm inputs and the migration is a one-symbol rename per call.
* test: drop translate_number from card-type integration expectationsCraig Jennings2026-05-271-1/+5
| | | | The card-type-system-complete integration test expected translate_number to be registered in org-drill-card-type-alist, but issue #43 removed that entry because its presenter never existed, and test-org-drill-translate-number-regression.el asserts it stays out. The integration test contradicted the shipped fix and had been failing on that assertion since the test suite landed. I dropped translate_number from its expected-types list and noted why inline.
* feat: add org-drill-on-timeout-action to drop unfinished cards at the time limitCraig Jennings2026-05-272-11/+74
| | | | | | Implements upstream #56. When org-drill-maximum-duration is reached, the session used to keep presenting the in-progress card and the again-queue until they drained, so the only way out was to finish them or interrupt. I added the defcustom org-drill-on-timeout-action with a discard-current value that ends the session as soon as the limit is hit, leaving the dropped cards untouched: they keep their existing scheduling and turn up again next session. The default, finish-current, preserves the old behavior. The gate lives in org-drill-entries-pending-p, the single predicate the drill loop checks between cards. Under discard-current past the limit, the in-progress and again items stop counting as pending, so the loop ends instead of draining them.
* refactor: thread card-state struct through the item-data round-tripCraig Jennings2026-05-278-139/+216
| | | | | | | | Second step of #147. get-item-data now returns an org-drill-card-state and store-item-data takes one, so the six recall fields move as named slots instead of a positional list. The three call sites (smart-reschedule, hypothetical-next-review-date, copy-scheduling-to-marker) read scheduler inputs through accessors and build a struct for the store, which removes the hand re-ordering between the get-shape and the store-shape. Behavior is unchanged. The legacy LEARN_DATA read path and the virgin-item sentinel are preserved field-for-field, and store takes just the struct because its last-interval slot already holds the interval to persist. The schedulers still take positional args; they adopt the struct in the following commits. I updated the round-trip, integration, and setup-helper tests to build and read the struct via a small list-view helper, so the existing expected-value assertions stay readable.
* refactor: add org-drill-card-state struct + ADRCraig Jennings2026-05-271-0/+22
| | | | | | First step of #147. I added a cl-defstruct bundling the recall fields the schedulers and the item-data round-trip shuttle around (last-interval, repetitions, ease, failures, meanq, total-repeats), with an ADR comment recording why: the same fields were passed as positional lists in three different orderings, and three call sites re-ordered between them by hand. The struct is inert here. The item-data round-trip and the schedulers adopt it in the following commits.
* refactor: sharpen opaque local variable names across org-drill.elCraig Jennings2026-05-271-39/+39
| | | | | | I renamed several cryptic locals to say what they hold: idx to index-var in the pop-random gensym, val to raw-value in the five DRILL_* property getters, q to quality in hypothetical-next-review-dates, m to marker in free-markers, the a/b sort and filter lambdas in order-overdue-entries to entry/other, and dat to card-def in the empty-card-type check. These are pure renames with no behavior change. Byte-compile and the full unit suite stay green.
* refactor: name the progress-meter chars in org-drill-progress-messageCraig Jennings2026-05-271-4/+4
| | | | sym1/sym2 gave no hint of their role. current-meter-char toggles per meter wrap and alternate-meter-char is its inverse, which is what the two make-string calls actually consume.
* ci: bump checkout to v5 and force node24 for setup-easkCraig Jennings2026-05-271-6/+13
| | | | actions/checkout@v4 and emacs-eask/setup-eask@master both run on the node20 runtime GitHub force-migrates to node24 on 2026-06-02. checkout@v5 ships the node24 runtime, so I bumped it across all three jobs. setup-eask has no node24 release yet, so I set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 at the workflow level to run it under node24 now, ahead of the forced switch rather than after.
* fix: guard sm2 cl-assert error tests behind Emacs-30 skipCraig Jennings2026-05-271-6/+23
| | | | | | The three error-case tests in test-org-drill-determine-next-interval-sm2.el asserted their cl-assert preconditions with a bare should-error :type 'cl-assertion-failed. On Emacs 29, ERT installs a signal-hook that keeps should-error from matching the condition, so the tests failed the 29.4 job outright. Under undercover's edebug instrumentation the same assert dropped into a blocking debugger in batch mode, which hung the coverage job until the 6-hour timeout on every push. The sm5 and simple8 files already wrap their equivalent error tests in a skip-unless guard for this quirk. I copied that helper into sm2 so all three scheduler files handle Emacs 29 the same way.
* chore: gitignore the inbox dir and editor backup filesCraig Jennings2026-05-271-0/+2
| | | | inbox/ holds local Claude handoff files and *~ are Emacs backups; neither belongs in the repo.
* feat: undo last rating, customizable keys, and configurable text limitCraig Jennings2026-05-274-49/+323
| | | | | | | | | | | | | | A batch of self-contained user-facing improvements, squashed from the feat/org-drill-solo-features branch. I added an undo for the last rating (issue #2 follow-up). The rating prompt now takes an undo key (org-drill--undo-key, default u): it restores the previous card's scheduling snapshot, drops the recorded quality, and re-queues that card, then returns to the current prompt. Each rating snapshots the scheduling properties and SCHEDULED line onto a per-session stack capped at org-drill-undo-limit (default 3). org-drill-reschedule loops on the rating read so undo doesn't rate the current card. I made the five session-control keys (quit, edit, help, skip, tags) defcustoms so they can be rebound from customize-group (issue #35), keeping their defaults. The 0-5 rating keys stay as-is, since they're tied to the quality scale rather than being variables. I lifted the hardcoded 100-line entry-text limit in org-drill-get-entry-text into the org-drill-entry-text-max-lines defcustom, defaulting to 100. I also deleted a commented-out old org-entry-empty-p that the real definition had already replaced. Existing tests stay green and each change added its own, including snapshot/restore and prompt-loop tests for undo.
* refactor: dedupe presenters, group defcustoms, and fill in docstringsCraig Jennings2026-05-265-232/+428
| | | | | | | | | | | | | | A cleanup pass over org-drill internals, squashed from the refactor/wave3-cleanup branch. No behavior change. Each step kept the existing tests green and added its own. I shared two duplicated helpers across the language card getters: org-drill--read-property-string and org-drill--face-from-alist. I factored the cloze body-scan out of the two multicloze presenters into org-drill--cloze-body-bounds, org-drill--count-cloze-matches, and org-drill--hide-cloze-by-index, so each presenter just picks which indices to hide. I pulled the presenter resolution and the four-way result classification out of org-drill-entry-f into org-drill--resolve-presenter and org-drill--classify-presentation-result, untangling the pivot of every drill iteration. I split the 37 defcustoms (and the three cloze faces) into four customize sub-groups (display, algorithm, session, leech) so customize-group org-drill is navigable. There's no leitner group because the Leitner settings are defvars. I documented the 22 defuns that had no docstring, rewrote the corrupted org-drill-presentation-prompt-in-mini-buffer docstring, and switched eleven docstrings to the imperative "Return" (issue #2).
* fix: scope cloze fontification to drill buffers via org-drill-modeCraig Jennings2026-05-264-30/+230
| | | | | | | | org-drill-add-cloze-fontification ran on org-font-lock-set-keywords-hook, which fires in every org buffer, and pushed the cloze rule into org's global org-font-lock-extra-keywords. The cloze regexp is built from the [ and ] delimiters, so an org priority cookie like [#A] matched the cloze pattern and got fontified as a cloze in every org buffer, colliding with org's headline fontification and stripping the heading's org-level-N face. I replaced the global install with org-drill-mode, a buffer-local minor mode that adds the cloze keywords only to its own buffer via font-lock-add-keywords. org-drill-auto-enable-mode (default on) turns the mode on from org-mode-hook in buffers that hold drill cards, so existing drill files keep their cloze highlighting while plain org buffers stay clean. Highlighting still respects org-drill-use-visible-cloze-face-p. The cloze regexp itself is unchanged, so the single-line cloze constraint from #38 is preserved.
* chore: coverage, autoload fix, and internal cleanup for org-drillCraig Jennings2026-05-2611-41/+345
| | | | | | | | | | | | | | | | A batch of test-coverage and hardening work, squashed from the test-work branch. Tests: deduplicated a colliding leitner-capture test name so make test-name loads again. Added SM2 assert-failure cases, the six basic multicloze variant delegations, the three English-side spanish-verb branches, and org-drill-current-scope branch coverage. Fix: the entry-point commands (org-drill itself, cram-tree, tree, directory, resume, relearn-item, strip-all-data, merge-buffers) carried no autoload cookies, so M-x failed from a fresh install until something pulled the file in. They're autoloaded now. Perf: org-drill-shuffle was quadratic because it indexed a list with elt on every swap. It runs a linear Fisher-Yates pass over a vector now, and it checks its argument is a list. Feat: added org-drill-version, a constant plus an interactive command, so a bug reporter doesn't have to open the file header. Refactor: extracted org-drill--format-tense-mood, shared by the two verb-conjugation presenters that each carried a copy. Docs: explained the SM8 magic numbers in the simple8 helpers as empirical fits rather than tunable knobs.
* test: fix date-sensitive days-since-creation assertionCraig Jennings2026-05-111-7/+13
| | | | | | test-org-drill-entry-days-since-creation-with-date-added pinned DATE_ADDED to a hardcoded 2026-04-01 and asserted 33-35 days, which only holds when the wall clock is near 2026-05-05. The with-fixed-now mock that was supposed to make it deterministic rebinds `current-time`. But the DATE_ADDED branch of org-drill-entry-days-since-creation goes through `org-time-stamp-to-now`, which reads the real clock. The rebind never reaches it. CI went red once the calendar moved past the +35-day window. I rewrote the test to derive DATE_ADDED 34 calendar days before today (via decode-time / encode-time, so month rollover and DST are handled) and assert the function returns exactly 34. No mock needed. Both sides read the same real clock.
* ci: install Eask instead of Cask in workflow setupCraig Jennings2026-05-101-6/+6
| | | | | | The Cask→Eask migration in d012894 switched the Makefile to drive Eask, but ci.yml still installed Cask via cask/setup-cask. All three jobs (test, lint, coverage) failed at `make setup` with "Eask not found". I swapped each job to use emacs-eask/setup-eask. No other CI logic changed.
* build: migrate from Cask to EaskCraig Jennings2026-05-106-37/+56
| | | | | | | | | | Cask's upstream has slowed. Eask is the actively maintained successor. Eask's `package-file` directive doesn't auto-install the deps from the .el header's Package-Requires, so the Eask file mirrors emacs/seq/org/persist explicitly. `eask install-deps` also doesn't pull transitive deps, so dash, m-buffer, and shut-up needed their own `depends-on` lines for undercover and elisp-lint to activate. The Makefile swaps are mechanical: $(CASK) → $(EASK), `cask install` → `eask install-deps --dev`, `cask build` → `eask compile`. The URL in org-drill.el's header pointed at the abandoned upstream's GitLab issues page. Eask cross-validates that against website-url, so I updated it to the GitHub mirror — where users file issues now.
* test: cover the active-input-method branch of response-get-buffer-createCraig Jennings2026-05-051-0/+12
| | | | | | | | I added a small case for `org-drill-response-get-buffer-create' that sets an input method then asserts the helper propagates it into the new response buffer. Coverage stayed near 94.7% (instrumentation noise can move it ±0.2%).
* test: cover present-simple-card-with-typed-answerCraig Jennings2026-05-051-0/+18
| | | | | | | | | I added a test for `org-drill-present-simple-card-with-typed-answer' that mocks `prompt-for-string' and the surrounding hide-* helpers, then asserts the presenter forwards the session and returns the prompt's result. Coverage moved from 94.4% to 94.8%.
* test: cover org-drill-presentation-prompt-in-bufferCraig Jennings2026-05-051-0/+61
| | | | | | | | | | I extended the presentation-prompt tests with cases for the in-buffer variant: default prompt assembled when none is supplied, explicit prompt fed straight through, drill-answer cleared on entry, and the session's exit-kind flowing back to the caller after recursive-edit returns. Recursive-edit, display-buffer, and the timer are all mocked. Coverage moved from 93.2% to 94.4%.
* test: cover org-drill-test-display dev helperCraig Jennings2026-05-051-0/+30
| | | | | | | | | | I added a test for the developer helper `org-drill-test-display' that mocks `org-drill-entry-f' to confirm the dispatcher fires and that the zysygy tag is toggled off again on exit. I also added a small case for `org-drill-test-display-rescheduler' that verifies it runs `org-drill-display-answer-hook' and waits on read-key-sequence. Coverage moved from 92.7% to 93.2%.
* test: cover all navigation arrow keys in --read-rating-keyCraig Jennings2026-05-051-0/+9
| | | | | | | | | I extended the existing rating-key test with a dolist that fires every remaining navigation key (up, left, right, prior, next) — previously only `down' was covered. Each key advances the loop without ending it, then a numeric input terminates. Coverage moved from 92.3% to 92.7%.
* test: cover org-drill-add-cloze-fontification flag branchCraig Jennings2026-05-051-0/+19
| | | | | | | | | I added a couple of cases for `org-drill-add-cloze-fontification': when `org-drill-use-visible-cloze-face-p' is t, a fontification spec gets pushed onto `org-font-lock-extra-keywords'; with the flag nil, the list is left alone. Coverage moved from 92.1% to 92.3%.
* test: cover SM5 random-noise, --read-key-sequence input-method dance, ↵Craig Jennings2026-05-051-0/+79
| | | | | | | | | | | | | goto-heading error I added small-branch tests for the SM5 dispersal-factor multiplier when `org-drill-add-random-noise-to-intervals-p' is t, `org-drill--read-key-sequence' deactivating/reactivating an active input method (and skipping the dance when none is active), and the error branch in `org-drill-goto-drill-entry-heading' when no parent heading carries the drill tag. Coverage moved from 92.1% to 92.1%.
* test: cover --pick-next-marker and resume happy-pathsCraig Jennings2026-05-052-0/+96
| | | | | | | | | | | | | I extended `tests/test-org-drill-route-rating-result.el' with four `org-drill--pick-next-marker' cases (no resume → pop, resume with live drill marker → keep current-item and clear resume-p, resume with nil or non-drill current-item → fall through to fresh pop). I also extended the resume regression file with the three happy-path branches of `org-drill-resume': pending entries → resume, finished with pending count → y-or-n-p offers a new session, finished with nothing → print 'finished'. Coverage moved from 91.7% to 92.1%.
* test: cover --route-rating-result quit/edit/skip/next branchesCraig Jennings2026-05-051-0/+71
| | | | | | | | | | | I added tests for `org-drill--route-rating-result' covering all four return values: nil → 'quit (end-pos becomes :quit), 'edit → 'edit (end-pos becomes a marker), 'skip → 'skip (current-item cleared), quality 0 → 'next (pushes to again-entries) and quality 5 → 'next (pushes to done-entries). Also a case where again-entries is non-empty so the shuffle branch runs. Coverage moved from 91.6% to 91.7%.
* test: cover the leitner main entry orchestratorCraig Jennings2026-05-051-0/+106
| | | | | | | | | | I added tests for `org-drill-leitner' with mocked `leitner-entry' returning t (full loop completes, summary printed), 'quit (pcase quit branch returns t), 'edit (pcase edit branch jumps to marker), and a case where the boxed queue is short of `org-drill-maximum-items-per-session' so `leitner-start-box' runs to top it up. Coverage moved from 89.8% to 91.6% — the suite is now over 90%.
* test: cover the org-drill main entry and hypothetical-next-review-date dispatchCraig Jennings2026-05-052-0/+129
| | | | | | | | | | | I added tests for the public `org-drill' command that mock `org-drill-entries' so the orchestrator runs in batch: empty buffer → 'no pending' message, populated buffer → entry loop runs, cram=t flag flips the session's cram-mode slot, resume-p skips entry collection. I also extended the cloze + scheduling helpers file with sm2 and simple8 coverage for `org-drill-hypothetical-next-review-date'. Coverage moved from 89.0% to 89.8%.
* test: cover replace-entry-text multi mode, multicloze weight errors, ↵Craig Jennings2026-05-051-0/+93
| | | | | | | | | | | | | copy-to-buffer fallback I added tests for `org-drill-replace-entry-text' with the multi-p flag (list of replacements creates multiple overlays) and the simple single-overlay case, the multicloze weight-validation error branches in `-firstmore' and `-firstless', and the `org-drill-copy-entry-to-other-buffer' recovery path that appends to the end of DEST when the source's outline path doesn't exist there. Coverage moved from 88.6% to 89.0%.
* test: cover leech-warning, minibuffer timer, cloze length flag, simple8 noiseCraig Jennings2026-05-051-0/+115
| | | | | | | | | | | I added tests for `org-drill--maybe-prepend-leech-warning' (three branches: not a leech, method not warn, leech with warn), the presentation minibuffer-timer function (emits MM:SS prompt, cancels after 10 calls, switches to '++:++' after an hour), the `org-drill-cloze-length-matches-hidden-text-p' branch (display becomes a dotted string), and the simple8 random-noise dispersal-factor branch. Coverage moved from 87.8% to 88.6%.
* test: cover marker end-pos, variable-pitch restore, young/overdue queue branchesCraig Jennings2026-05-053-0/+104
| | | | | | | | | | | I extended three existing test files with cases that hit branches the suite was missing: `org-drill--show-end-message' with a live-marker end-pos (jumps to the marker), `org-drill--restore-display' for the variable-pitch-on / variable-pitch-off / text-scale paths, and `org-drill-pop-next-pending-entry' for the young-mature and overdue branches in the queue priority cond. Coverage moved from 87.0% to 87.8%.
* test: cover leitner-start-box and --read-rating-key input shapesCraig Jennings2026-05-052-0/+198
| | | | | | | | | | I added tests for `org-drill-leitner-start-box' (move N entries from unboxed into box 1, respect the count arg, zero is a noop) and for `org-drill--read-rating-key' (string input, arrow vector, wheel-event vector, help key showing help block, tags key triggering set-tags, typed-answer rendering in the prompt). Coverage moved from 85.7% to 87.0%.
* test: cover merge-buffers, all-leitner-capture, leitner-vs-drill summaryCraig Jennings2026-05-051-0/+142
| | | | | | | | | I added tests for `org-drill-merge-buffers' (yes/no confirmation, defaulting dest to current buffer, full migrate pipeline), `org-drill-all-leitner-capture' (populate and reverse boxed/unboxed queues), and the `org-drill-leitner-vs-drill-entries' summary message. Coverage moved from 82.4% to 85.7%.
* test: cover --setup-display, --restore-display, --migrate-from-source, ↵Craig Jennings2026-05-052-0/+214
| | | | | | | | | | | scope=directory I added unit tests for the display-state helpers (capture text scale, variable-pitch, modeline; restore them on session exit), the directory branch of `org-drill-current-scope', and `--migrate-from-source''s three-branch cond (matching ID, no ID, ignore-new-items). Coverage moved from 81.8% to 82.4%.