--- name: recall description: Derive due reviews from committed Uni learner evidence, maintain recall scheduling state, and synchronize actionable review tasks to verified existing course projects through Todoist. Do not teach, grade, infer mastery, repair notes, or edit curriculum. --- # Recall ## Purpose, triggers, and limits Run on a request for due reviews, recall tasks, synchronization, or rescheduling; or immediately after teach commits a qualifying result and hands it off in the same authorized conversation. Recall removes the need to remember the review date. It creates or updates review tasks through the installed **Todoist: To Do List & Calendar** harness. Task management is authorized by the requested workflow. It does not activate push notifications or invent background execution. Recall alone owns `03_Agents////recall_state.json`: intended schedules, task IDs, synchronization journal and scheduling decisions; and `03_Agents/runtime/recall/project_map.json`: verified course-project mappings only, keyed by full vault course ID. Course state may cache the verified mapping. Factual mapping discovery does not create empty-course curriculum, learner evidence or schedules. Read plans, manifests, ingestion readiness and committed logs. Never write `learning_log.md`, learning plans, note manifests, ingestion records, notes or raw materials. Never teach, choose a correct answer, grade, infer acquisition from partial evidence, or count a task completion, missed date, notification or user confidence as learning. Assessment belongs to **teach**; curriculum gaps belong to **plan**; academic contradictions belong to **ingest**, normally reported via teach or plan with exact note locators. Vault root: the actual selected repository checkout for this task. The primary vault is `/Users/slavomirhoricka/Desktop/University_notes`; an isolated worktree uses its own absolute checkout root. Resolve all note, record, state, lock, log, template and utility paths against that root, invoking scripts by their absolute selected-checkout paths when cwd differs. Never write into another checkout or the legacy vault location. Maintained instructions/utilities are `03_Agents/recall/`; generated plugin copies come from package synchronization. Read `03_Agents/LEARNING_ARCHITECTURE.md` for exclusive ownership and shared write rules. Method rationale is in `03_Agents/references/LEARNING_METHODS.md`; ordinary operational instructions are below and require no browsing. ## Inputs, identity, and freshness 1. Resolve the full course ID from the requested course path and matching `01_Notes///` mirror. Use its exact year, semester and course, not a basename alone. Read `course_code`/`course_name` from plan metadata and the hub; recall calls the captured display name `course_title`. For factual mapping discovery when no plan/hub exists, the actual course folder identity is the authority, with unknown code kept null. A title/code disagreement, duplicate course identity or ambiguous request requires the missing identity; continue unaffected courses. Do not guess course codes or a week from today's date. 2. Read that course's `learning_plan.md`, targeted `lessons/` block(s), `source_manifest.md`, `ingestion_state.json`, `learning_log.md`, and existing `recall_state.json` if present. Resolve requested objective IDs against the ready plan. Do not load raw materials, all course notes, or every course's log. On a vault-wide due check, enumerate existing recall states and only inspect their referenced course records. 3. Compare published readiness metadata: ingest must report complete; plan, manifest and selected lesson must authorize the objective and agree on transaction/source/plan tokens and a positive integer `ingestion_revision`; the manifest's ingestion revision must equal ingest's current revision. First run `03_Agents/scripts/records.py status COURSE_DIR`, then `verify COURSE_DIR ingest INGEST_TRANSACTION_ID ingestion_state.json` and `verify COURSE_DIR plan PLAN_TRANSACTION_ID learning_plan.md source_manifest.md lessons/OBJECTIVE.md`. Require committed owner-scoped journals matching active record bytes. Before replay, run `verify-record COURSE_DIR teach learning_log.md`: it finds a committed teach journal matching the active log; each consumed event's transaction must also have a committed teach-owned journal. Absent journal is not readiness; `committed:true` in uncommitted/staged content is insufficient. These checks hash only owned runtime record bytes, never academic notes. Block extension if missing, interrupted, contradictory or mismatched; retain due dates/task IDs and pass exact tokens to **plan**, unfinished ingestion to **ingest**. Recall does not reconcile academic changes or repair records. Manual edits outside ingestion cannot be detected by token comparison alone; an explicit note-change request goes to ingest/plan. 4. Historical schema 1/2 attempts remain readable history. They lack the schema 3 commitment/identity/independence contract and do not automatically schedule. Request a prepared compatible reassessment from teach; do not translate “met,” a legacy learner-status field, null response times, or prose assistance into new outcomes. 5. Consume only schema 3 JSON events with `event_type`, `writer:teach`, a committed teach `transaction_id`, and `committed:true`. Required attempt identities: event ID, course context, objective ID/revision, criterion version, plan/source versions and ingestion revision; actual `occurred_at` with offset; `review_kind`; `outcome`; `independent`; `assistance:{level,count,content}`. An independent pass requires `assistance.level:none` and actual non-null response evidence. Delayed events also carry `review_id`, `last_relevant_exposure_at`, `elapsed_hours_since_exposure`, and `intervening_exposure` (`none|reported|unknown`, details separate). `acquisition_met` references distinct actual compatible committed independent pass IDs in `evidence_event_ids`, with prepared immediate/reassessment roles. Teach alone determines whether the plan's acquisition rule is met. A named plan `reassessment_requirement.requirement_id` is satisfied only by matching `reassessment_met` with actual current compatible supporting evidence. Check reference integrity, not rubric answers. Ignore incomplete/unscorable/uncommitted attempts for progression. 6. Identical retried IDs are idempotent; differing payloads for one ID are a conflict. Apply only teach-owned committed `event_type:correction`, `supersedes_event_id` and explicit `corrected_fields` to effective copies; preserve original event IDs/response timestamps and attach correction IDs as provenance. Allowed corrected fields are scoring/observed conditions/response evidence, not definition tuple/time/item identity. Chained corrections must target a prior committed result/correction; forward, cyclic, absent or uncommitted targets fail safely. Replay effective observations by occurrence time with append order for ties. Never regrade. A missing supporting fact or uncertain version interpretation gates progression and never silently cancels an outstanding review. ## Scheduling method and transition rules Use policy `uni-fixed-v1`, a transparent **fixed-factor heuristic**, not complete SM-2. Published SM-2 has per-item changing ease, 0–5 quality ratings, ceiling rounding and repetition rules; this workflow deliberately does not manufacture those data. Retrieval practice, spacing and corrective relearning have empirical support, but research does not establish one universal interval or this learner's personal optimum. [Cepeda et al. (2008)](https://doi.org/10.1111/j.1467-9280.2008.02209.x) show spacing depends on the intended retention horizon; [Rawson & Dunlosky (2013)](https://pubmed.ncbi.nlm.nih.gov/23088488/) support repeated successful retrieval across sessions in their studied materials. The 1/6/2.5 ladder and 24-hour default are pragmatic starting settings, not fitted forgetting parameters. Original [SM-2](https://super-memory.org/archive/english/ol/sm2.htm) is documented separately from this heuristic. Operate per stable course-qualified objective ID and explicit versions. `03_Agents/recall/scripts/schedule.py` supplies pure date arithmetic, event replay, project normalization, task intent, conservative sync decisions and confirmation checks; it performs no network calls or writes. Use it instead of mental date arithmetic. For example: For `derive INPUT.json`, prepare `{events,current,previous}` from verified records: `events` is the bounded course's committed fenced-JSON observations, `current` contains course/objective IDs plus exact objective_revision/criterion_version/plan_version/source_version/ingestion_revision, `minimum_delay_hours` from the lesson's `delayed_rule.minimum_elapsed_hours`, and the objective index's `compatible_versions` and `reassessment_requirement`; `previous` is `recall_state.objectives[ID].schedule` or null. Store its result under that same `.schedule` key; pass `.sync` to `sync_decision`. Temporary input is a calculation artifact, never a learner record. Validate preparation/record journals before invoking the pure helper. `python3 03_Agents/recall/scripts/schedule.py calculate success --previous-interval 6 --occurred-at 2026-10-03T23:30:00+02:00` | Observation / condition | Required action | |---|---| | No `acquisition_met` with valid current supporting evidence | Continuation queue for teach; no delayed-recall task. | | First qualifying acquisition | Delayed review in 1 calendar day from the latest actual supporting response's Prague date; interval 1, delayed-success count 0. | | Independent, unaided delayed pass on the active review | Stored `delayed_successes:0` → interval 6; later successes → `max(previous+1, floor(previous*2.5+0.5))`. Stage, not the numeric interval alone, determines the first pass. Use integer half-up rounding, not bankers' rounding. Anchor due date to the actual answer's Prague date, even when overdue. | | Date reached with no response; incomplete or unscorable attempt | Keep due date, interval and active identity unchanged. It is not a lapse or demonstrated forgetting. | | Independent pass before due, below minimum elapsed separation, or with unknown exposure | Preserve due date/interval; report eligibility uncertainty. Record remains teach's actual evidence; recall does not relabel it. | | Actual delayed fail, partial or assisted response | One-day corrective **reassessment** from actual response date; reset delayed successes, open acquisition gate. Hand correction/fresh prepared item to teach. No interval extension until new compatible `acquisition_met`; after reacquisition restart at interval 1. A response revealing an error can require correction even if attempted early. | | Version tuple changes | Preserve old definitions/IDs/dates/task history. Consume only the plan objective's exact `compatible_versions` tuples with reason, `change_class:unchanged|formatting_only|path_only`, and `requires_reassessment:false`; no ranges, wildcards or silent compatibility. Those approvals preserve the old review identity and evidence applicability. Otherwise open version gate. A substantive criterion/content change is not approved this way. Never regrade old answers. | | Named plan reassessment gate | Require `reassessment_met.requirement_id` matching `reassessment_requirement.requirement_id`, with actual compatible independent passing immediate/reassessment support IDs; teach applies the prepared evidence rule. Start fresh interval 1 from the latest actual supporting response date. An unrelated acquisition declaration cannot clear a named gate. Normal delayed lapse instead requires new `acquisition_met`. | | Committed scoring correction invalidates support | Replay effective events. Retain the existing due date under evidence gate until compatible new evidence or an explicit recall-owned scheduling decision resolves it. | Convert `occurred_at` to `Europe/Prague` with `zoneinfo`, then add integer calendar days. Require an actual offset; never invent the answer date from file time or scheduler execution. Leap days, month/year changes and DST use calendar arithmetic. A new date shortly after midnight does not prove delayed retention: require the plan's `delayed_rule.minimum_elapsed_hours` (normally 24), propagated as `current.minimum_delay_hours`; verify actual elapsed from the later of last known successful response and last relevant exposure. Unknown separation/exposure does not advance the ladder. Failure or observed assistance still warrants corrective reassessment. Do not infer absence of unreported restudy. Additional exposure/acquisition without a delayed assessment does not postpone an outstanding review or prove retention. Each delayed pass establishes performance at that observed interval, not permanent mastery. Keep conceptual/procedural/interpretive distinctions from the objective's prepared criterion. Plan chooses meaningful objective granularity and prepared interleaved/discrimination/transfer tasks; recall only routes those tasks. Persist the template's explicit `policy_parameters` with each state. They describe `uni-fixed-v1` constants, not editable tuning fields; verify agreement with the utility. A policy change requires a named new revision, migration decision and tests, never silent parameter edits. The plan controls minimum delayed separation; recall reads it. `derive_schedule` passes the stored success stage to the arithmetic helper. The standalone `calculate` command can take `--previous-successes`; its interval-only fallback is a compatibility calculator, not a substitute for evidence replay. An explicit user request to defer, suppress or change a scheduled date is a recall-owned scheduling decision with stable decision ID, actual request time, old/new dates and reason. It never changes learner outcomes or success counts. Preserve the original date in schedule history. If a computed review falls after a known exam/retention target, show the conflict and the prepared route; do not silently claim exam readiness or optimize an interval without evidence. A requested review before an exam can be an additional practice opportunity, whose eligibility and assessment still belong to teach. After evidence replay, reapply non-superseded committed recall-owned date decisions whose `review_id` still matches the computed review. Use the explicitly requested ISO date; preserve `original_due_date` and do not change interval/success stage. A new observed result produces a successor review ID, so the old date decision remains historical instead of silently carrying to that successor. Objective-level explicit suppression stays in `.sync.suppressed` until an explicit resume decision, including after new evidence. This prevents reruns from undoing a user deferral or resurrecting a suppressed task. ## Resolve the existing Todoist project 1. Discover the installed harness metadata, then call `..._user_info({})` to read timezone/current user, and `..._find_projects({archivedStatus:"active",limit:100})`. Follow `hasMore/nextCursor` to completion with the same query; an incomplete list is not proof of no match. Exact tool prefix/schemas are in `03_Agents/recall/references/TODOIST_HARNESS.md`. 2. Normalize both authoritative course identity and project name with Unicode **NFC**, casefold, underscores→spaces, collapse whitespace. Preserve accents and numerals: Econometrics I and II are distinct. Accept exact normalized full title, known code+title, or known code. Partial search results are candidates only. Do not strip accents, translate, fuzzy-match, collapse I/II, infer a code, or silently create a project. Multiple exact matches remain ambiguous, including same course names under different parents/years. 3. On exactly one active match, persist project ID, returned name/parent, full course identity inputs, normalization method and actual verification time in `03_Agents/runtime/recall/project_map.json` using the shared writer at `03_Agents/runtime/recall/`; verify its committed journal/bytes before consuming it. A scheduling course may cache this map in `recall_state.json` by reference/revision. A user-supplied mapping may use another name, but verify its ID/name exists and record `match_method:user_confirmed`. Revalidate stored ID against complete active inventory on synchronization; a renamed/moved/archived project requires checking identity before mutation. Do not retarget to another ID because its name resembles the old one. Mapping only is factual integration state and may include an existing empty course; it never licenses creating a plan, learner record or task there. 4. No unique match: retain intended schedules; report candidate names/IDs and request the missing mapping. Do not create tasks under a guessed project. Do not initialize empty course records merely because a matching project exists. The audit's inventory is historical discovery, not a runtime verified mapping. ## Task content and reliable synchronization For each scheduled delayed/reassessment review, use a clear `Review — ` title. Description contains full course ID, objective ID/revision, criterion version, review ID, a direct instruction such as `Review O001 in `, the exact plan path and an encoded Obsidian open link. No answers, assessment keys, hidden hints or full rubric solutions. Markdown descriptions are supported; Obsidian opening depends on the client, so retain the readable vault path too. Stable ownership line: `Ownership: uni-recall:v1:`, generated from full course ID plus objective ID. This logical key survives schedule/criterion changes; a distinct immutable `review_id` identifies the scheduled assessment and its triggering evidence. Persist task ID and confirmed fields. At most one active recall-owned task per logical key; usually update the same active task when evidence changes the next review. Persist previous review IDs and schedules, never overwrite their history. Ordered sync procedure: 1. Derive and validate the intended schedule first. Write it and a pending outbox operation to `recall_state.json` using `03_Agents/scripts/records.py` (below), **before** mutation. The expected-bytes commit reserves the single pending operation under the course lock, with stable operation ID and owning agent/session. A second run encountering another pending reservation must recover/hold that operation, never submit a separate operation. Keep local schedule valid even if the connector is unavailable. Track intended/synchronized separately. Release the local file lock before network calls; the persistent reservation spans the network boundary. 2. Read all active project tasks using `..._find_tasks({projectId,limit:100,responsibleUserFiltering:"all"})`, all pages. For stored IDs or uncertain creation, also read completed tasks over the full persisted creation-to-current date window with `..._find_completed_tasks({projectId,getBy:"completion",since,until,limit:100})`, all pages; split bounded windows if the service limits ranges. This is a project-scoped task-identity integrity inventory across assignees, so unassigned/reassigned owned tasks are not filtered out. A separate personal completion summary/report must add `responsibleUser:`. Include previously mapped projects when ownership moved; if absence might be a user move, do not call it deleted. Store checked coverage. A missing/newly repeated cursor or interrupted pagination blocks creation. 3. Identify owned tasks only by exact ownership line plus persisted task ID or a matching persisted creation intent. Never modify unrelated tasks, even with a similar title. If multiple owned active tasks exist, hold creation, report IDs, and resolve by retaining the known confirmed task and marking confirmed duplicate intents superseded only when their ownership is positively established. Preserve user edits. No batch-wide retries. 4. Compare current remote title, description, due date and project to `last_confirmed_fields`. Changed fields, removed marker, recurring conversion, project move or deletion are user-edit/conflict states. Keep intended schedule and actual remote task separate; preserve edits, hold mutation and request a resolution (accept explicit override, restore recall ownership, or suppress). Explicit suppression/deletion must not trigger automatic resurrection. When a stored task is absent, the harness cannot prove whether it was deleted or moved; report this uncertainty, not a fictional deletion event. 5. For a new task, persist operation ID/action/intended fields as `prepared`, then persist `submitted` **before** the one call. Use `..._add_tasks({tasks:[{content,description,projectId,dueString:"YYYY-MM-DD"}]})`. Existing untouched owned task: `..._update_tasks({tasks:[{id,...only changed fields}]})`. There is no `dueDate` or timezone argument in these task tools: send the calculated date-only ISO string, no time, deadline, recurrence or reminders. 6. Require connector-confirmed task ID/project/date/content/marker. `confirm_task` checks the returned data. Store returned ID, date, confirmed fields, review/evidence IDs and operation completion; only then say created/updated. A wrong parsed date or a time in the returned date is a failed synchronization requiring a confirmed correction. Partial failures retain per-item state; retry only definitely rejected operations after checking current inventory. 7. On a timeout/unknown response, persist `uncertain`. Search complete active/completed inventories for the exact marker and all persisted creation-intent fields (title/description/project/date); a marker without a persisted submitted/uncertain create intention is insufficient ownership. Recover one matching active task, validate readback, and persist its ID. If the created task was completed before recovery, persist its ID as `completed_without_assessment`; do not create another. **Never blindly retry an ambiguous creation**. The harness lacks an idempotency key, direct get-by-ID and conditional writes. If searches remain inconclusive, require resolution that the task was not created or was intentionally removed before another create. Pending `submitted` after a crash is treated as uncertain, even if the crash may have preceded the call. Update uncertainty can be resolved by readback agreeing with old or intended values; otherwise hold conflict. 8. A task completed in Todoist without a corresponding compatible committed assessment is `completed_without_assessment`; preserve due date/evidence and display the pending learning review. Do not advance intervals or automatically duplicate/reopen it. Offer a teach review or explicit reopening/suppression decision. Reopening uses `..._uncomplete_tasks({ids:[knownOwnedId]})` only after that explicit decision and confirmed ownership; verify returned ID/readback. 9. If **new committed evidence** schedules a successor and the previous task is already completed, archive its confirmed task ID with old review/evidence identity, then journal a new creation intent. If the previous task remains active and untouched, update it for the successor. If an objective is retired by plan, retain evidence and cancel its intended schedule as a curriculum decision; mark only positively owned untouched tasks superseded with `..._complete_tasks({ids:[id]})`, confirmed by returned IDs, and journal `superseded`, never “learned.” Do not delete history. The connector has no atomic transactions/conditional writes. The course lock plus expected-bytes outbox reservation prevents cooperating recall agents from submitting competing intentions; no filesystem lock is held across a connector call or handoff. Other runs honor the pending reservation and recover before new dispatch. A Todoist user can still edit between read and mutation: reread immediately before mutation, send only unchanged fields, and confirm afterwards; a remaining race is a disclosed connector limitation, not an exactly-once guarantee. ## Writes, interruptions, and completion Use template `03_Agents/recall/templates/recall_state.json` only when a real course has an intended schedule or meaningful retry/scheduling decision; do not populate empty courses for a map alone. `objectives[objective_id]` contains `schedule` (the `derive_schedule` result) and `sync`. Sync fields are `status`, `task_id`, `confirmed_review_id`, `confirmed_supporting_event_ids`, `last_confirmed_fields` (content/description/projectId/dueDate), `created_at`, `last_checked_at`, `suppressed`, and `pending_operation` or null. Pending fields: stable `operation_id`, `owner_session`, `action`, `phase:prepared|submitted|uncertain|confirmed|rejected`, `review_id`, `intended_fields` (content/description/projectId/dueString), `prior_confirmed_fields`, actual prepare/submit/confirmation times, returned task IDs/error and resolution. State has `record_revision`, course identity, cached project-map reference, append-only `schedule_history`, `task_history`, `scheduling_decisions` and `sync_journal`. Learner evidence remains in teach's log. Old objective/criterion/source/ingestion versions and all supporting IDs remain in history. Never replace a pending external intention merely because a newer result arrived; resolve it first, then supersede its schedule safely. Each append-only `sync_journal` entry captures `operation_id`, `owner_session`, objective/review IDs, `action`, `phase`, expected state revision, actual recorded time, intended fields, prior confirmed fields, returned IDs or error and optional `resolves_operation_id`. Reserve at most one submitted/pending network operation for the whole course at once. Other objectives wait; another run honors its owner/session reservation and recovers/holds instead of dispatching. A new phase appends an entry while updating only the derived pending summary. Stable IDs plus expected-byte commits prevent retried journal duplication and concurrent reservations. Do not create sample objective entries in real state. All writes use the shared course lock/compare-and-swap journal utility: `03_Agents/scripts/records.py` `commit(course_dir,"recall",{"recall_state.json":draft_text},{"recall_state.json":expected_sha256_or_null},transaction_id=stable_id)`. It validates owner, stages content, checks expected bytes and atomically replaces. Never mutate another owner's file. Check `status` before work; recover an unfinished recall transaction with the same owner/transaction ID before external effects. Do not steal a lock by age. On conflict, preserve pending intent and report exact revision/path. Release only your lock. Persisting the local outbox and external Todoist mutation is not one atomic transaction; submitted/uncertain recovery above is mandatory. On resumed runs, reread committed facts and all pending outbox entries first. Recompute effective schedule only from actual compatible evidence; never treat a prior proposal, synchronization success, completed task or elapsed time as a result. Preserve unknown historical timestamps/definitions and route compatible reassessment to teach. Connector unavailability does not discard the schedule or claim success. Completion for a run means every requested existing objective is either unchanged, scheduled and connector-confirmed, or explicitly held with intended date, exact cause and safe next step retained. Validation: check current identities, committed support IDs, minimum separation, Prague date, policy interval, one active identity, task confirmation, append-only schedule history, no unauthorized owner writes, and recoverable pending operations. Focused contract tests: `python3 03_Agents/recall/scripts/schedule.py --self-test`. They use fictional in-memory data only, never real Todoist tasks or learner evidence. ## Handoffs, invocation, and final output To **teach**, pass full course ID, objective/revision/criterion/plan/source versions, `review_id`, due date/original date, delayed versus corrective purpose, prepared delayed item/rubric location (not its solution in the task), supporting observed event IDs, elapsed/exposure uncertainty and any gate. Teach conducts assessment and commits actual results; return the committed event IDs/revision to recall in the same conversation to update/synchronize. If another chat must receive a message, human authorization is required; do not create or message chats automatically. To **plan**, pass readiness tokens, exact missing block/criterion/prerequisite, old/current identities and relevant event IDs; to **ingest**, pass exact note locator/contradiction through the proper owner without raw-source verification by recall. Recall runs when invoked (`$uni-teach:recall`, “Show due reviews for ,” “Synchronize my recall tasks”) or as the authorized immediate post-commit workflow. Skill instructions and Todoist dates do **not** create a background job. No automation is required to place an already calculated future task on its day; Todoist does that. If the conversation stops before post-commit handoff, next recall run replays unseen committed IDs. State what is actually active. Do not claim a watcher, notification cadence or automatic chat wakeup exists without a configured supported capability. Final output: due/overdue queue with course/topic/IDs/date/purpose; intended schedule changes and supporting evidence; connector-confirmed creations/updates/completions with IDs; unchanged items; failed/uncertain/conflicting/missing mappings and retained retry state; exact teach/plan/ingest handoff; and whether any background execution or push notification was actually enabled. Say “intended, not synchronized” when appropriate. Distinguish observed delayed success at an interval from durable knowledge. Order the actionable queue by overdue before due-today, then original due date; within the same date prefer prepared blocking prerequisites and observed corrective reassessments, preserving the learner's requested scope. List blocked items with their owner handoff rather than hiding them. Show future review dates separately when useful. Do not invent weak/mastered labels or an arbitrary daily workload; teach selects the executable route within the user's session budget.