Skip to content

Function Index

Ramiro Cantu edited this page May 30, 2026 · 1 revision

Function Index

The master cross-reference for every documented function, class, dataclass, and path-operation handler in gradient-server. Grouped by module (file path, alphabetical), every entry carries file:line, its layer tag, a one-line description, and a link to the Feature page where it is documented in context.

This page is the single point of truth for where a symbol lives; the Feature pages explain how it behaves and the SPEC (SPEC.md, §G/§C/§I/§V/§T/§B) is the forward authority on why. Where docs and code disagree, code wins.

See also: Home · Architecture · Data Models · API Reference · Glossary · Getting Started · design notes Intelligence Layer and Core Library.

Note: Line ranges track the code at the time of writing. The watcher-backed index lags edits by ~1s; treat file:line as a starting anchor, not a byte offset.

Legend — layer tags

Layer Meaning
handler FastAPI path-operation function bound to an HTTP route.
dependency Injectable (Depends(...)) — session, settings, auth, per-request client.
middleware App-level lifespan / exception handler wired into the ASGI app.
service Domain logic in app/services/**; pure-ish, transport-agnostic, the Core Library candidate surface.
job Scheduler entrypoint or its core (run_*), invoked by APScheduler in lifespan.
schema Dataclass / result type / Pydantic-ish shape (no behavior beyond construction + derived props).
util Pure helper, factory, or config/infra construct.

Feature pages linked below: Capture, Knowledge Base, Outline, Anki, Tutor / MCP, Platform / Admin — all sections of the consolidated Features page.


Most-central functions (full template)

A handful of load-bearing functions get the long form; everything else is a one-line index row in the by-module tables below.

ingest_capture

Location: app/services/ingest.py:17-19 · Layer: service · Feature(s): Capture Purpose: Thin dispatcher for POST /api/v1/captures — routes a capture to its registered source adapter by payload.source (the §A plugin-seam dispatch contract). Signature:

async def ingest_capture(payload: CapturePayload, session: AsyncSession) -> IngestResponse

Parameters:

Name Type Required Description
payload CapturePayload yes Normalized capture envelope; payload.source selects the adapter.
session AsyncSession yes Caller-owned DB transaction; the adapter writes inside it.

Returns: IngestResponse. Side effects: Delegates all DB/filesystem writes to the selected adapter. Errors: UnknownSourceError when source has no registered adapter. Called by / Calls: Called by post_capture. Calls get_adapter, adapter.ingestnormalize_capture.

normalize_capture

Location: app/services/adapters/extension_capture.py:294-368 · Layer: service · Feature(s): Capture Purpose: Shared source-agnostic normalizer that turns one capture into the {RawCapture, media, Passage, Question, Attempt} row set, stamping payload.source on each row. Every source adapter delegates here verbatim, proving the §A seam takes a new source without touching the entrypoint. Signature:

async def normalize_capture(payload: CapturePayload, session: AsyncSession) -> IngestResponse

Parameters:

Name Type Required Description
payload CapturePayload yes The capture to normalize; payload.source is stamped onto each persisted row.
session AsyncSession yes Caller-owned transaction; this writes + flushes but does not commit.

Returns: IngestResponse. Side effects: Writes RawCapture/Media/Passage/Question/Attempt; writes media bytes to disk; flushes (no commit). Errors: Propagates UnknownCourseError from _resolve_course_id. Called by / Calls: Called by ManualAdapter.ingest, UWorldAdapter.ingest, WebQbankAdapter.ingest. Calls _resolve_course_id, _persist_media, _upsert_passage, _resolve_choice, _upsert_question.

tag_pending

Location: app/services/kb/jobs.py:271-386 · Layer: service · Feature(s): Knowledge Base Purpose: THE grounded categorizer core (run_grounded_tag): tag untagged atomic_facts and needs_categorization questions via recall → grounded pick → calibrate → persist, course-scoped per V-CAP2. See Intelligence Layer for the chain. Signature:

async def tag_pending(session, *, tagging_client, calibrator_client=None, version=None) -> TagReport

Parameters:

Name Type Required Description
session AsyncSession yes Caller-owned transaction; this flushes per V41 isolation.
tagging_client AsyncOpenAI yes Client for the grounded-pick call.
calibrator_client AsyncOpenAI | None no V69 calibrator client; defaults to the tagging client wiring downstream.
version str | None no Embedding version; defaults to current_version().

Returns: TagReport (counts, token accounting, manual-review flags, partial_failure). Side effects: OpenAI tagging + calibrator calls; INSERT/DELETE question_tags & atomic_fact_tags; denormalizes atomic_facts.node_id; sets questions.needs_categorization=False; flush. V41 per-entity isolation. Errors: Per-entity failures isolated into report.failures (V41); no fatal raise on a single bad item. Called by / Calls: Called by run_grounded_tag_job_do_run_grounded_tag. Calls _tag_one, current_version. Governed by V-L3, V69, V-T2, V-T3, V-CAP2.

retrieve_candidates

Location: app/services/kb/recall.py:448-570 · Layer: service · Feature(s): Knowledge Base Purpose: Build the V-L3 constrained candidate set merging the three recall meta-paths — C2T (cosine to node embeddings), C2C2T (tags borrowed from similar facts), and T2T (similarity-edge expansion) — deduped by node, with optional prior-tag exemplars. The grounded prompt may pick only from this set. Signature:

async def retrieve_candidates(session, *, course_id, query_embedding, top_k=10,
    edge_expansion=True, content_expansion=True, exemplars_per_node=0,
    embedding_version=None, ...) -> RecallResult

Parameters: (selected — full keyword surface in source)

Name Type Required Description
session AsyncSession yes Read-only DB session.
course_id int yes Course scope; recall never crosses courses (V-CAP2).
query_embedding list[float] yes The entity's embedding vector.
top_k int no Cap for the C2T cosine path (default 10).
edge_expansion / content_expansion bool no Toggle the T2T / C2C2T paths.

Returns: RecallResult (ordered Candidate list + embedding_version). Side effects: None — read-only. Errors: None thrown for empty recall; returns an empty candidate list. Called by / Calls: Called by _tag_one. Calls _embedding_candidates, _content_candidates, _expand_via_edges, _load_course_node_map, _exemplars_for_node. Governed by V-L3.

sync_deck

Location: app/services/anki/sync.py:77-208 · Layer: service · Feature(s): Anki Purpose: §T3 core: pull a deck's cards/notes from AnkiConnect, upsert the anki_notes/anki_cards SRS mirror, replace schema_map tag sets, and append revlog rows — returning the {synced_cards, linked_qids, error, reviews_synced} envelope. Signature:

async def sync_deck(session, client, *, deck_name: str,
    outline_lookup: Optional[OutlineLookup] = None) -> SyncSummary

Parameters:

Name Type Required Description
session AsyncSession yes Transaction for the SRS-mirror writes.
client AnkiConnectClient yes Read-only AnkiConnect surface (V13).
deck_name str yes Deck to sync.
outline_lookup OutlineLookup | None no In-memory path index for AAMC CC tag resolution.

Returns: SyncSummary. Side effects: AnkiConnect reads (V13 read-only) + DB upserts (anki_notes, anki_cards, anki_note_tags, anki_card_reviews). Errors: Unreachable → error='anki_not_running' (V4, soft); empty deck → error='deck_empty_or_misspelled' (V20). No HTTPException. Called by / Calls: Called by sync_anki route + run_anki_sync_job. Calls client.find_cards, client.cards_info, client.notes_info, OutlineLookup.load, _replace_tags.

post_capture

Location: app/api/v1/captures.py:17 · Layer: handler · Feature(s): Capture Purpose: POST /api/v1/captures (200, IngestResponse) — ingest a UWorld (or other-source) question capture from the Chrome extension into the question/attempt/media tables. Signature:

async def post_capture(payload: CapturePayload, ...) -> IngestResponse

Parameters: body CapturePayload (extra='forbid'); deps get_session, verify_coach_token. Returns: IngestResponse. Side effects: Delegates to ingest_capture (all DB/FS writes downstream). Errors: 422 from UnknownSourceError/UnknownCourseError (re-raised with str(exc) detail); 422 RequestValidationError on malformed body (logged by the capture-path handler in main.py). Called by / Calls: ASGI route. Calls ingest_capture.


By-module index

app/api/deps.py — request dependencies

Function file:line Layer Description Feature
get_settings app/api/deps.py:16-17 dependency Return the singleton Settings for injection. Platform / Admin
get_session app/api/deps.py:20-28 dependency Yield an AsyncSession; commit on clean exit, rollback on exception. Platform / Admin
verify_coach_token app/api/deps.py:31-39 dependency Constant-time check of X-Coach-Token vs settings.COACH_TOKEN; 401 on miss. Platform / Admin

app/api/v1/admin.py — admin / status routes + helpers

Function file:line Layer Description Feature
_anki_status_client app/api/v1/admin.py:45-51 dependency Per-request AnkiConnect client for the status probe; closed on teardown (V16). Platform / Admin
_openai_status_client app/api/v1/admin.py:54-58 dependency OpenAI client for the status probe (max_retries=0, fails fast). Platform / Admin
_notion_status_client app/api/v1/admin.py:61-74 dependency Notion AsyncClient for the status probe; None when token unset. Platform / Admin
create_manual_tag (handler) app/api/v1/admin.py:119 handler POST /admin/questions/{question_id}/tags (201) — create a manual override tag. No token/localhost guard. Platform / Admin
list_jobs_payload app/api/v1/admin.py:159-169 util Snapshot scheduler jobs as {job_id, next_run_time}; shared by /admin/jobs + /admin/status. Platform / Admin
trigger_job_logic app/api/v1/admin.py:172-190 util Validate job_name vs _VALID_JOBS and nudge next_run_time=now; 404/409/503 guard. Platform / Admin
list_jobs (handler) app/api/v1/admin.py:194 handler GET /admin/jobs (200) — scheduler job snapshot. Platform / Admin
trigger_job (handler) app/api/v1/admin.py:203 handler POST /admin/jobs/{job_name}/trigger (202) — run a named job now. Platform / Admin
system_status (handler) app/api/v1/admin.py:217 handler GET /admin/status (200) — Anki/OpenAI/Notion reachability + per-job last-run (T39). Platform / Admin
delete_tag (handler) app/api/v1/admin.py:236 handler DELETE /admin/tags/{tag_id} (204) — manual hard-delete / llm soft-override / else 403. No token/localhost guard. Platform / Admin

app/api/v1/anki.py — Anki sync + read routes

Function file:line Layer Description Feature
_anki_client app/api/v1/anki.py:57-63 dependency Per-request AnkiConnectClient for POST /anki/sync (test-overridable). Anki
sync_anki (handler) app/api/v1/anki.py:76 handler POST /anki/sync (200) — one-off deck sync; soft V4 error envelope, never 500. Anki
get_review_queue (handler) app/api/v1/anki.py:110 handler GET /anki/review-queue (200) — due cards + retention + retrievability (T43). Anki
get_cards_for_qid (handler) app/api/v1/anki.py:132 handler GET /anki/cards/by-qid/{qid} (200) — cards carrying the uworld qid tag. Anki

app/api/v1/anki_assign.py — Anki assignment routes

Function file:line Layer Description Feature
create_assignment_route (handler) app/api/v1/anki_assign.py:48 handler POST /anki/assignments (201) — create + snapshot resolved card_ids (V52). Anki
list_assignments_route (handler) app/api/v1/anki_assign.py:71 handler GET /anki/assignments (200) — list by unlock time, optional status/window filter. Anki
patch_assignment_route (handler) app/api/v1/anki_assign.py:97 handler PATCH /anki/assignments/{assignment_id} (200) — skipped/completed transition (V51). Anki

app/api/v1/anki_load.py — Anki load-config routes

Function file:line Layer Description Feature
_get_or_seed app/api/v1/anki_load.py:38-48 util Read singleton AnkiLoadConfig (id=1); seed V59 default (200 cards / 60 min) on miss. Anki
get_load_config_route (handler) app/api/v1/anki_load.py:56 handler GET /anki/load-config (200) — read singleton config, auto-seed on miss. Anki
upsert_load_config_route (handler) app/api/v1/anki_load.py:68 handler POST /anki/load-config (200) — upsert id=1 in place (V59). Anki
get_load_adherence_route (handler) app/api/v1/anki_load.py:93 handler GET /anki/load-adherence (200) — V54 plan-adherence payload + reviewed series. Anki

app/api/v1/anki_review.py — Anki review routes

Function file:line Layer Description Feature
create_review_route (handler) app/api/v1/anki_review.py:30 handler POST /anki/reviews (201) — pending review for a set of card_ids on a date (V53). Anki
list_reviews_route (handler) app/api/v1/anki_review.py:47 handler GET /anki/reviews (200) — list by review_date, optional status/window filter. Anki

app/api/v1/attempts.py — attempt-note routes

Function file:line Layer Description Feature
get_notes (handler) app/api/v1/attempts.py:35 handler GET /attempts/{attempt_id}/notes (200) — list attempt notes. Capture
add_note (handler) app/api/v1/attempts.py:44 handler POST /attempts/{attempt_id}/notes (201) — create a note, optionally flagged. Capture
remove_note (handler) app/api/v1/attempts.py:63 handler DELETE /attempts/notes/{note_id} (204) — delete a note by id. Capture

app/api/v1/captures.py — capture ingress

Function file:line Layer Description Feature
post_capture (handler) app/api/v1/captures.py:17 handler POST /captures (200) — ingest a question capture. (full template above) Capture

app/api/v1/kb_reads.py — knowledge-base read feeds

Function file:line Layer Description Feature
list_concept_edges (handler) app/api/v1/kb_reads.py:54 handler GET /concept-edges (200) — similarity + manual edge feed (T45, V-E2). Knowledge Base
list_atomic_facts (handler) app/api/v1/kb_reads.py:92 handler GET /atomic-facts (200) — facts by node or PDF source (T46, V-KB1). Knowledge Base
list_notion_pages (handler) app/api/v1/kb_reads.py:131 handler GET /notion/pages (200) — Notion pointer index; never reads Notion back (T47, V-N1/V-N2). Knowledge Base
list_pdf_sources (handler) app/api/v1/kb_reads.py:162 handler GET /pdf-sources (200) — ingested-PDF inbox + per-source fact count (T48). Knowledge Base

app/api/v1/outline.py — course + outline routes

Function file:line Layer Description Feature
list_courses (handler) app/api/v1/outline.py:52 handler GET /courses (200) — list courses slug-ordered. Outline
create_course (handler) app/api/v1/outline.py:65 handler POST /courses (201) — create a course from slug/name/description. Outline
import_outline (handler) app/api/v1/outline.py:87 handler POST /courses/{course_id}/outline:import (200) — validate-then-materialize (V-O2/V-O3). Outline
read_outline (handler) app/api/v1/outline.py:164 handler GET /courses/{course_id}/outline (200) — materialized node tree. Outline
node_mastery (handler) app/api/v1/outline.py:188 handler GET /outline/nodes/{node_id}/mastery (200) — subtree set-rollup (T44, V-O5). Outline
course_mastery (handler) app/api/v1/outline.py:199 handler GET /outline/courses/{course_id}/mastery (200) — course rollup (T44). Outline

app/api/v1/pdf.py — PDF ingest route

Function file:line Layer Description Feature
post_pdf_ingest (handler) app/api/v1/pdf.py:33 handler POST /pdf/ingest (200) — upload + synchronously vision-ingest a lecture PDF. Knowledge Base

app/api/v1/pkm.py — discriminator write

Function file:line Layer Description Feature
post_discriminator (handler) app/api/v1/pkm.py:23 handler POST /pkm/discriminators (200) — persist-only discriminator write (V-M1, V-M3). Tutor / MCP

app/api/v1/tutor.py — MCP tutor read seam

Function file:line Layer Description Feature
get_question_by_qid (handler) app/api/v1/tutor.py:32 handler GET /tutor/questions/by-qid/{qid} (200) — full review payload by qid. Tutor / MCP
get_question_by_attempt_id (handler) app/api/v1/tutor.py:44 handler GET /tutor/questions/by-attempt-id/{attempt_id} (200) — review payload by attempt. Tutor / MCP
get_recent_captures (handler) app/api/v1/tutor.py:61 handler GET /tutor/captures/recent (200) — N most-recent captures. Tutor / MCP
get_latest_session_id (handler) app/api/v1/tutor.py:70 handler GET /tutor/sessions/latest (200) — {test_id} of the latest session. Tutor / MCP
get_recent_sessions (handler) app/api/v1/tutor.py:78 handler GET /tutor/sessions/recent (200) — N most-recent sessions. Tutor / MCP
get_session_summary (handler) app/api/v1/tutor.py:87 handler GET /tutor/sessions/{test_id}/summary (200) — per-topic session debrief. Tutor / MCP
get_flagged_attempts (handler) app/api/v1/tutor.py:99 handler GET /tutor/attempts/flagged (200) — flagged-attempt review queue. Tutor / MCP
search_outline_nodes (handler) app/api/v1/tutor.py:110 handler GET /tutor/outline/nodes/search (200) — node name search (T22). Tutor / MCP
get_outline_tree (handler) app/api/v1/tutor.py:128 handler GET /tutor/outline (200) — full outline tree by course slug. Tutor / MCP
get_node_subtree (handler) app/api/v1/tutor.py:142 handler GET /tutor/outline/nodes/{node_id}/subtree (200) — node + descendant subtree. Tutor / MCP
healthcheck (handler) app/api/v1/tutor.py:156 handler GET /tutor/healthz (200) — token-gated DB-touching tutor health check. Tutor / MCP

app/config.py — settings + media root

Function file:line Layer Description Feature
Settings app/config.py:16-118 util Pydantic-settings singleton; env + .env + defaults across db/auth/media/openai/anki/notion+pdf/scheduler groups (T40, V-TC1). Platform / Admin
ensure_media_root app/config.py:115-118 util Create settings.MEDIA_ROOT if missing and return its absolute path. Platform / Admin

app/database.py — async DB infra

Function file:line Layer Description Feature
engine / AsyncSessionLocal / Base app/database.py:1-11 dependency Module-level async engine, async_sessionmaker (expire_on_commit=False), and DeclarativeBase. Platform / Admin

app/kb_config.py — KB-substrate startup validation

Function file:line Layer Description Feature
validate_kb_config app/kb_config.py:28-68 util Startup validator for the opt-in KB substrate; WARN-logs missing optionals, never raises (T25, V-KB2). Platform / Admin

app/main.py — app factory, lifespan, handlers

Function file:line Layer Description Feature
lifespan app/main.py:29-37 middleware ASGI lifespan: validate KB config + start/stop the scheduler. Platform / Admin
health_check (handler) app/main.py:69 handler GET /healthz (200) — open root liveness probe, no DB, no auth. Platform / Admin
_capture_validation_handler app/main.py:82-98 middleware Global RequestValidationError handler; WARN-logs capture-path 422 detail, returns standard 422. Capture

app/scheduler.py — APScheduler jobs

Function file:line Layer Description Feature
run_anki_sync_job app/scheduler.py:97-107 job Interval (ANKI_SYNC_INTERVAL_MINUTES); sync_deck for the configured deck (§T4); V4 unreachable = success. Platform / Admin
run_anki_assignment_unlock_job app/scheduler.py:180-190 job Interval (ANKI_ASSIGNMENT_UNLOCK_INTERVAL_MINUTES); run_unlock_due (§T63, V51/V55). Platform / Admin
run_anki_assignment_complete_job app/scheduler.py:255-265 job Cron (ANKI_ASSIGNMENT_COMPLETE_CRON_*); run_complete_unlocked, DB-only, no Anki (§T64, V51). Platform / Admin
run_anki_review_job app/scheduler.py:334-344 job Interval (ANKI_REVIEW_PUSH_INTERVAL_MINUTES); run_review_due (§T76, V53/V55). Platform / Admin
run_pdf_ingest_job app/scheduler.py:433-443 job Interval (PDF_INGEST_INTERVAL_MINUTES); poll_inbox (T51, V-KB1, V41); no-op without OPENAI_API_KEY. Knowledge Base
run_notion_sync_job app/scheduler.py:537-547 job Interval (NOTION_SYNC_INTERVAL_MINUTES); sync_pending_nodes (T51, V-N1/V-N2, V41); no-op without token/db id. Knowledge Base
run_embed_job app/scheduler.py:627-637 job Interval (EMBED_INTERVAL_MINUTES); embed_pending (T50, V-E1, V41); gates tagging; no-op without key. Knowledge Base
run_grounded_tag_job app/scheduler.py:725-735 job Interval (GROUNDED_TAG_INTERVAL_MINUTES); tag_pending — THE categorizer (T50, V-L3, V69, V-T2/V-T3, V41). Knowledge Base
start_scheduler app/scheduler.py:743-803 util Lifespan boot: register all 8 jobs (7 interval + 1 cron) with replace_existing=True; no-op when disabled. Platform / Admin
stop_scheduler app/scheduler.py:806-809 util Lifespan shutdown: scheduler.shutdown(wait=False) if running. Platform / Admin

app/services/admin_tags.py — manual tag mutations

Function file:line Layer Description Feature
create_manual_tag app/services/admin_tags.py:45-81 service Insert a manual (question_id, node_id, source='manual') tag, NULL confidence (V-T3). Platform / Admin
delete_tag app/services/admin_tags.py:84-102 service V-T2 source policy: manual hard-delete, llm soft-override, schema_map forbidden. Platform / Admin
QuestionNotFoundError app/services/admin_tags.py:25-26 service Raised when create_manual_tag targets a missing question. Platform / Admin
ManualTagValidationError app/services/admin_tags.py:29-30 service Raised on an invalid manual-tag payload. Platform / Admin
ManualTagConflictError app/services/admin_tags.py:33-34 service Raised on uniqueness/check-constraint violation. Platform / Admin
TagNotFoundError app/services/admin_tags.py:37-38 service Raised when delete_tag targets a missing tag. Platform / Admin
TagDeleteForbiddenError app/services/admin_tags.py:41-42 service Raised when a tag's source (e.g. schema_map) is not deletable. Platform / Admin

app/services/adapters/ — capture source adapters

Function file:line Layer Description Feature
normalize_capture app/services/adapters/extension_capture.py:294-368 service Shared source-agnostic normalizer. (full template above) Capture
ManualAdapter.ingest app/services/adapters/manual.py:24-25 service source='manual' adapter; delegates to normalize_capture. Capture
UWorldAdapter.ingest app/services/adapters/uworld.py:24-25 service source='uworld' reference adapter; delegates to normalize_capture. Capture
WebQbankAdapter.ingest app/services/adapters/web_qbank.py:25-26 service source='web-qbank' adapter; delegates to normalize_capture. Capture

app/services/analytics.py — mastery rollups

Function file:line Layer Description Feature
wilson_lower app/services/analytics.py:38-46 service 95% Wilson score lower bound on success rate (pure; 0.0 with no attempts). Outline
compute_node_mastery app/services/analytics.py:116-161 service V-O1 subtree set-rollup + per-direct-child breakdown for a node. Outline
compute_course_mastery app/services/analytics.py:164-207 service Course-total V-O1 set-rollup + per-root-node breakdown. Outline
NodeNotFoundError app/services/analytics.py:30-31 service Raised when compute_node_mastery node_id is absent. Outline
CourseNotFoundError app/services/analytics.py:34-35 service Raised when compute_course_mastery course_id is absent. Outline

app/services/anki/assignment.py — assignment resolution + jobs

Function file:line Layer Description Feature
resolve_card_ids app/services/anki/assignment.py:272-297 service V52: ordered in-scope suspended card_ids for a cc/topic scope, sliced to max_cards. Anki
create_assignment app/services/anki/assignment.py:300-345 service Two-pass create of a pending assignment with a frozen card/note snapshot (V52). Anki
mark_skipped app/services/anki/assignment.py:365-372 service V51 pending/unlocked → skipped (accounting only, no Anki). Anki
mark_completed_manual app/services/anki/assignment.py:375-383 service V51 manual pending/unlocked → completed (human override of T64). Anki
run_unlock_due app/services/anki/assignment.py:421-574 job T63 core: unsuspend snapshot cards, flip to unlocked, chain audit addTags; V55 retry-cap 3. Anki
run_complete_unlocked app/services/anki/assignment.py:598-662 job T64 auto-completion: unlocked → completed once every card reviewed after unlock (V51, DB-only). Anki
AssignmentUnlockSummary app/services/anki/assignment.py:393-399 schema Counts returned by run_unlock_due. Anki
AssignmentCompleteSummary app/services/anki/assignment.py:580-584 schema Counts returned by run_complete_unlocked. Anki

app/services/anki/client.py — AnkiConnect JSON-RPC client

Function file:line Layer Description Feature
AnkiConnectClient.version app/services/anki/client.py:122-124 service Read-only version action (V13 allowlist). Anki
AnkiConnectClient.find_cards app/services/anki/client.py:126-130 service findCards query → native card ids. Anki
AnkiConnectClient.cards_info app/services/anki/client.py:132-138 service cardsInfo scheduler state; [] short-circuit. Anki
AnkiConnectClient.notes_info app/services/anki/client.py:140-150 service notesInfo per-note metadata incl. tags. Anki
AnkiConnectClient.card_reviews app/services/anki/client.py:152-164 service cardReviews revlog rows for incremental sync (V26). Anki
AnkiConnectClient.deck_names app/services/anki/client.py:166-173 service deckNames for the V20 loud-fail log. Anki
AnkiConnectClient.unsuspend_cards app/services/anki/client.py:210-224 service V50 write-allowlist: queue −1→0, no SRS mutation; dedup (V64). Anki
AnkiConnectClient.add_tags app/services/anki/client.py:226-250 service V50 write-allowlist: add audit-trail note tags (note-scoped, V75). Anki
AnkiConnectClient.create_filtered_deck app/services/anki/client.py:252-274 service V50 write-allowlist: cram deck in ::review::* namespace; dedup (V64). Anki
AnkiConnectClient.aclose app/services/anki/client.py:92-95 service Close the lazily-created httpx client. Anki

app/services/anki/load_adherence.py — load adherence

Function file:line Layer Description Feature
compute_load_adherence app/services/anki/load_adherence.py:194-227 service T66 entry (V54/V60): projected-load metric, headroom %, status label, dense reviewed series. Anki
AnkiLoadAdherence app/services/anki/load_adherence.py:65-83 schema V54 payload (no recommended_changes per V60); status_label feasible/tight/overload. Anki
ReviewedDay app/services/anki/load_adherence.py:57-61 schema One point of the dense reviewed-count series (T43). Anki

app/services/anki/queries.py — Anki read queries

Function file:line Layer Description Feature
list_review_queue app/services/anki/queries.py:39-53 service Scheduled cards soonest-first (limit clamped 1..200), tags eager-loaded. Anki
get_tag_parse_stats app/services/anki/queries.py:56-60 service {parsed_kind: row_count} over all note tags. Anki
get_tag_card_coverage app/services/anki/queries.py:63-71 service {parsed_kind: distinct_card_count} joined to cards. Anki
get_anki_card_total app/services/anki/queries.py:74-76 service Total anki_cards row count. Anki
list_cards_for_qid app/services/anki/queries.py:79-87 service Cards whose note carries a matching uworld qid tag. Anki

app/services/anki/retention.py — retention windows (FENCED)

Function file:line Layer Description Feature
retention_for_cc app/services/anki/retention.py:72-80 service FENCED (T18, V-RB2): empty windowed summary pending node_id subtree port. Anki
retention_for_topic app/services/anki/retention.py:83-91 service FENCED (T18, V-RB2): empty windowed summary pending port. Anki
RetentionSummary app/services/anki/retention.py:56-59 schema Windowed retention container (window_days → RetentionWindow). Anki
RetentionWindow app/services/anki/retention.py:41-53 schema Per-window pass/fail counts + derived total/retention. Anki

app/services/anki/review.py — review creation + push job

Function file:line Layer Description Feature
create_review app/services/anki/review.py:78-128 service Two-flush create of a pending review; deck name derived from own PK (V53/T76). Anki
run_review_due app/services/anki/review.py:140-285 job T76 core: create filtered deck, flip to pushed, chain audit addTags; V55 retry-cap 3. Anki
ReviewBatchSummary app/services/anki/review.py:131-137 schema Counts returned by run_review_due. Anki

app/services/anki/review_metrics.py — retention metrics

Function file:line Layer Description Feature
retention_by_card app/services/anki/review_metrics.py:41-60 service Lifetime true-retention per card in one grouped query (no N+1). Anki
retrievability app/services/anki/review_metrics.py:63-78 util Forgetting-curve R = 0.9 ** (elapsed/interval), clamped [0,1]; pure. Anki

app/services/anki/state.py — state counts (FENCED)

Function file:line Layer Description Feature
state_for_cc app/services/anki/state.py:66-69 service FENCED (T18, V-RB2): empty subtree StateCounts pending node_id port. Anki
state_for_topic app/services/anki/state.py:72-75 service FENCED (T18, V-RB2): empty subtree StateCounts pending port. Anki
StateCounts app/services/anki/state.py:37-50 schema Per-scope state-count container + derived unlock_pct. Anki

app/services/anki/sync.py — deck sync

Function file:line Layer Description Feature
sync_deck app/services/anki/sync.py:77-208 service §T3 deck sync core. (full template above) Anki
SyncSummary app/services/anki/sync.py:55-60 schema Result returned by sync_deck and exposed to the /anki/sync surface. Anki

app/services/anki/tag_parser.py — AnKing tag classification

Function file:line Layer Description Feature
parse_tag app/services/anki/tag_parser.py:64-118 util Classify one AnKing tag → uworld_qid/aamc_cc/aamc_skill/unparsed; resolves CC → node_id (V3/B3, V-T1). Anki
ParsedTag app/services/anki/tag_parser.py:52-57 schema Immutable classification result; node_id is the only persisted target (V-T1). Anki

app/services/attempt_notes.py — attempt notes

Function file:line Layer Description Feature
create_note app/services/attempt_notes.py:18-37 service Create an AttemptNote (stripped text); raise if attempt absent. Tutor / MCP
list_notes app/services/attempt_notes.py:40-52 service All notes for an attempt, newest-first. Tutor / MCP
delete_note app/services/attempt_notes.py:55-59 service Hard-delete a note by id; raise if absent. Tutor / MCP
AttemptNotFoundError app/services/attempt_notes.py:10-11 service Raised when create_note attempt_id is absent. Tutor / MCP
NoteNotFoundError app/services/attempt_notes.py:14-15 service Raised when delete_note note_id is absent. Tutor / MCP

app/services/ingest.py — capture dispatch

Function file:line Layer Description Feature
ingest_capture app/services/ingest.py:17-19 service §A plugin-seam dispatcher. (full template above) Capture

app/services/kb/embeddings.py — embedding persistence

Function file:line Layer Description Feature
expected_dim app/services/kb/embeddings.py:56-61 service Canonical embedding dimension for a model (or None). Knowledge Base
current_version app/services/kb/embeddings.py:64-77 service Build the V-E1 embedding version stamp (<model>-v2). Knowledge Base
embed_and_persist app/services/kb/embeddings.py:80-136 service Idempotent embed + persist content_embeddings; V-E1 dim guard. Knowledge Base
DimMismatchError app/services/kb/embeddings.py:42-46 service V-E1 error on embedding-length disagreement before persistence. Knowledge Base
EmbedResult app/services/kb/embeddings.py:49-53 schema Persisted/reused row + reuse flag + token usage. Knowledge Base

app/services/kb/inbox.py — PDF inbox poll

Function file:line Layer Description Feature
poll_inbox app/services/kb/inbox.py:54-119 service Walk inbox/<slug>/*.pdf, run each through ingest_pdf, isolate per-file failures (V41). Knowledge Base
InboxReport app/services/kb/inbox.py:34-45 schema Per-run tally with partial_failure for scheduler partial-success (V41). Knowledge Base

app/services/kb/jobs.py — embed + tag job cores

Function file:line Layer Description Feature
embed_pending app/services/kb/jobs.py:154-217 service run_embed core: embed un-embedded nodes/facts/questions for the version. Knowledge Base
tag_pending app/services/kb/jobs.py:271-386 service THE categorizer core. (full template above) Knowledge Base
_tag_one app/services/kb/jobs.py:225-268 service Inner chain step: load embedding → recall → grounded → persist for one entity. Knowledge Base
EmbedReport app/services/kb/jobs.py:49-58 schema Tally of embed_pending with partial_failure. Knowledge Base
TagReport app/services/kb/jobs.py:61-76 schema Tally of tag_pending incl. token accounting + manual-review flags. Knowledge Base

app/services/kb/notion.py — Notion one-way mirror

Function file:line Layer Description Feature
sync_node_to_notion app/services/kb/notion.py:144-200 service Upsert a one-way Notion page per node + append fact blocks (V-N1/V-N2/V-M3). Knowledge Base
mirror_discriminator_to_notion app/services/kb/notion.py:231-324 service Append a discriminator factor as one backlink block; idempotent (V-M3). Knowledge Base
sync_pending_nodes app/services/kb/notion.py:339-405 service run_notion_sync core: mirror every node owning tagged facts; V41 isolation. Knowledge Base
NotionMirrorError app/services/kb/notion.py:40-43 service Raised when a discriminator factor cannot be mirrored. Knowledge Base
SyncReport app/services/kb/notion.py:46-51 schema Result of sync_node_to_notion. Knowledge Base
DiscriminatorSyncReport app/services/kb/notion.py:54-60 schema Result of mirror_discriminator_to_notion (skipped=True on V-M3 no-op). Knowledge Base
PendingSyncReport app/services/kb/notion.py:327-336 schema Tally of sync_pending_nodes with partial_failure. Knowledge Base

app/services/kb/pdf_ingest.py — PDF ingest pipeline

Function file:line Layer Description Feature
render_pages app/services/kb/pdf_ingest.py:118-132 service Default injectable renderer; rasterize each PDF page to PNG via PyMuPDF. Knowledge Base
transcribe_page app/services/kb/pdf_ingest.py:168-210 service One OpenAI vision call → page text (V-KB3); optional Flex tier. Knowledge Base
extract_atomic_facts app/services/kb/pdf_ingest.py:263-306 service One strict json_schema call → atomic claims (V-KB4); blank input short-circuits. Knowledge Base
ingest_pdf app/services/kb/pdf_ingest.py:326-470 service Orchestrate render → vision → extract → persist; SHA-256 idempotent (V-KB1). Knowledge Base
RenderedPage app/services/kb/pdf_ingest.py:73-76 schema One rasterized page (page number + PNG bytes). Knowledge Base
PageTranscription app/services/kb/pdf_ingest.py:79-84 schema transcribe_page result + token accounting. Knowledge Base
FactExtraction app/services/kb/pdf_ingest.py:87-92 schema extract_atomic_facts result + token accounting. Knowledge Base
IngestReport app/services/kb/pdf_ingest.py:95-106 schema ingest_pdf summary (facts/pages/reuse + V-L1 tokens). Knowledge Base

app/services/kb/persist_tags.py — tag persistence

Function file:line Layer Description Feature
persist_grounded_tags app/services/kb/persist_tags.py:213-258 service Persist calibrated decisions to question/fact tags (V-T2 delete-then-insert, V-T3). Knowledge Base
_persist_question app/services/kb/persist_tags.py:107-151 service Replace source='llm' question tags with fresh quantized rows (V-T2/V-T3). Knowledge Base
_persist_atomic_fact app/services/kb/persist_tags.py:154-210 service Replace source='llm' fact tags + set denormalized atomic_facts.node_id. Knowledge Base
PersistResult app/services/kb/persist_tags.py:46-54 schema Rows inserted/replaced + manual-review flag + chosen primary node. Knowledge Base
EntityNotFoundError app/services/kb/persist_tags.py:42-43 service Raised when the target question/fact id is absent during persistence. Knowledge Base

app/services/kb/recall.py — recall meta-paths

Function file:line Layer Description Feature
load_embedding app/services/kb/recall.py:122-147 service Fetch the version-stamped embedding for an entity (None when absent). Knowledge Base
retrieve_candidates app/services/kb/recall.py:448-570 service V-L3 constrained candidate set. (full template above) Knowledge Base
format_candidates_for_prompt app/services/kb/recall.py:578-611 service Render a RecallResult as the byte-stable numbered candidate surface (V-L3). Knowledge Base
_embedding_candidates app/services/kb/recall.py:186-242 service C2T path: cosine-rank node embeddings, floored at min_score, capped top_k. Knowledge Base
_content_candidates app/services/kb/recall.py:336-445 service C2C2T path: borrow tags from similar tagged facts, gold/silver-weighted. Knowledge Base
_expand_via_edges app/services/kb/recall.py:245-287 service T2T path: follow concept_edges.kind='similarity' neighbors (manual excluded, V-E2). Knowledge Base
_exemplars_for_node app/services/kb/recall.py:290-333 service SRKI: prior calibrated question-tag exemplars for a node. Knowledge Base
Candidate app/services/kb/recall.py:87-108 schema One retrieved node candidate with recall provenance + exemplars. Knowledge Base
Exemplar app/services/kb/recall.py:64-85 schema One few-shot example attached to a candidate node. Knowledge Base
RecallResult app/services/kb/recall.py:111-114 schema Ordered candidate list + embedding version. Knowledge Base

app/services/kb/similarity.py — similarity edges

Function file:line Layer Description Feature
cosine app/services/kb/similarity.py:35-54 util Pure cosine similarity; 0.0 for zero-norm, raises on dim mismatch. Knowledge Base
derive_similarity_edges app/services/kb/similarity.py:57-125 service Pairwise-cosine → persist kind='similarity' edges above threshold; never touches manual (V-E2). Knowledge Base
DeriveReport app/services/kb/similarity.py:28-32 schema Tally of derive_similarity_edges. Knowledge Base

app/services/llm/cache.py — LLM result cache base

Function file:line Layer Description Feature
LlmCacheBase app/services/llm/cache.py:26-143 service Shared SQLite-cache base (lifecycle, schema, stats, context-manager). Knowledge Base
LlmCacheBase.lookup_cost app/services/llm/cache.py:95-101 service Stored original LLM cost for a key (0.0 if missing). Knowledge Base
LlmCacheBase.stats app/services/llm/cache.py:103-126 service Aggregate entry counts by version/model + total cost saved. Knowledge Base
LlmCacheBase.clear app/services/llm/cache.py:128-137 service Delete all rows, or only one extractor_version's; returns rows removed. Knowledge Base
LlmCacheBase.get app/services/llm/cache.py:139-140 service Abstract per-cache content-hash lookup (subclass implements). Knowledge Base
LlmCacheBase.put app/services/llm/cache.py:142-143 service Abstract per-cache content-hash write (subclass implements). Knowledge Base
LlmCacheBase.close app/services/llm/cache.py:83-87 service Close the SQLite connection (backs context-manager exit). Knowledge Base

app/services/llm/calibrator.py — V69 confidence calibration

Function file:line Layer Description Feature
grade_yes_no app/services/llm/calibrator.py:90-153 service V69 core: Yes/No grade with logprobs → [0,1] confidence (reasoning off). Knowledge Base
calibrate_tag app/services/llm/calibrator.py:171-197 service Calibrate one (question, candidate_tag) pair via the discriminator framing. Knowledge Base
CalibrationResult app/services/llm/calibrator.py:45-53 schema V69 grade result: confidence, manual_review (<0.5), Yes/No logprobs. Knowledge Base

app/services/llm/client.py — OpenAI client factory

Function file:line Layer Description Feature
build_openai_client app/services/llm/client.py:15-27 service Central AsyncOpenAI factory; V41 retry budget (≥5), optional base URL; the V16 patch seam. Knowledge Base

app/services/llm/grounded.py — grounded tagging

Function file:line Layer Description Feature
generate_grounded_tags app/services/llm/grounded.py:241-373 service Grounded-pick step: pick tags ONLY from recall candidates (V-L3/V44/V45), then V69 re-score. Knowledge Base
build_pick_schema app/services/llm/grounded.py:105-150 service V44/V45 strict json_schema; node_index enum [1..N] enforces V-L3 at the grammar level. Knowledge Base
build_system_prompt app/services/llm/grounded.py:153-166 service Compose preamble + numbered candidate list (positions = returned indices). Knowledge Base
GroundedTag app/services/llm/grounded.py:56-72 schema One calibrated tag decision; calibrated_confidence is the sole trusted confidence (V69). Knowledge Base
GroundedResult app/services/llm/grounded.py:75-84 schema Output of generate_grounded_tags: decisions + models + tokens + warnings. Knowledge Base

app/services/media_store.py — media blob storage

Function file:line Layer Description Feature
media_path app/services/media_store.py:28-31 service Canonical absolute path MEDIA_ROOT/{hash[:2]}/{hash}{ext}. Platform / Admin
relative_media_path app/services/media_store.py:34-37 service Relative path stored in media.local_path. Platform / Admin
write_media app/services/media_store.py:68-78 service Decode base64 + atomically write media bytes; idempotent. Platform / Admin

app/services/outline/importer.py — outline import

Function file:line Layer Description Feature
validate_outline_schema app/services/outline/importer.py:220-229 service Pure whole-upload-or-reject validator → ValidatedOutline or all-problems error (V-O2). Outline
materialize_outline app/services/outline/importer.py:232-295 service Upsert course, wipe nodes, insert validated tree depth-ordered (V-O3). Outline
OutlineImportError app/services/outline/importer.py:40-41 service Raised when materialization fails after validation. Outline
OutlineSchemaValidationError app/services/outline/importer.py:44-50 service Validation-failure with .errors carrying every problem for one 422. Outline

app/services/outline/lookup.py — in-memory path index

Function file:line Layer Description Feature
normalize_typographic_punctuation app/services/outline/lookup.py:33-41 service Map curly quotes/apostrophes → ASCII for exact-path lookups. Outline
OutlineLookup.load app/services/outline/lookup.py:75-105 service Build a path-indexed lookup for one course; raise if unseeded. Outline
OutlineLookup.__init__ app/services/outline/lookup.py:60-73 service Construct by-id / roots-by-name / children-by-name indices. Outline
OutlineLookup.course_id app/services/outline/lookup.py:107-109 service Property: the course_id this lookup was built for. Outline
OutlineLookup.node_id_by_path app/services/outline/lookup.py:111-152 service Resolve a >>-path to node_id; None on missing/ambiguous (V-O4). Outline
OutlineLookup.node app/services/outline/lookup.py:154-155 service In-memory _Node for a node_id, or None. Outline
OutlineLookup.path_of app/services/outline/lookup.py:157-166 service Inverse of node_id_by_path: render the >>-path for a node_id. Outline
OutlineNotSeededError app/services/outline/lookup.py:44-45 service Raised when load runs against a missing/unseeded course. Outline

app/services/outline_subtree.py — subtree set rollup

Function file:line Layer Description Feature
subtree_node_ids app/services/outline_subtree.py:31-34 service {root_id} + descendants via recursive CTE (V-O1 subtree set). Outline
subtree_node_ids_many app/services/outline_subtree.py:37-44 service Union of subtrees over many roots; empty list → empty set. Outline

app/services/system_status.py — health probes + status

Function file:line Layer Description Feature
probe_anki app/services/system_status.py:48-55 service Reachability probe via AnkiConnect version(); never raises. Platform / Admin
probe_openai app/services/system_status.py:58-67 service Reachability probe via models.retrieve; configured=False when key unset. Platform / Admin
probe_notion app/services/system_status.py:70-79 service Token-validity probe via users.me() (bot identity only, V-N1). Platform / Admin
job_last_runs app/services/system_status.py:82-96 service Latest TaskRun per job via Postgres DISTINCT ON. Platform / Admin
build_jobs_health app/services/system_status.py:111-126 service Merge scheduler snapshot with last-run rollup, snapshot order (pure). Platform / Admin
collect_system_status app/services/system_status.py:129-151 service Single testable entry assembling the full /admin/status payload (V16 injectable). Platform / Admin
ServiceHealth app/services/system_status.py:35-46 service Dataclass: one dependency's configured/reachable/detail. Platform / Admin

app/services/tutor/captures.py — recent captures

Function file:line Layer Description Feature
get_recent_captures app/services/tutor/captures.py:13-94 service N most-recent attempts + question + resolved node topics + notes (clamp 1..50). Tutor / MCP

app/services/tutor/discriminators.py — discriminator write

Function file:line Layer Description Feature
write_discriminator_factor app/services/tutor/discriminators.py:48-118 service Append-only, deduped on (question_id, factor_text) (V-M3); persist-only (V-M1). Tutor / MCP
QuestionNotFoundError app/services/tutor/discriminators.py:27-28 service Raised when the target question_id is absent. Tutor / MCP
NodeNotFoundError app/services/tutor/discriminators.py:31-32 service Raised when a provided node_id is absent. Tutor / MCP

app/services/tutor/flags.py — flagged attempts

Function file:line Layer Description Feature
get_flagged_attempts app/services/tutor/flags.py:20-65 service Recent flag-for-review attempts + qid/preview/note/dashboard URL (clamp 1..100). Tutor / MCP

app/services/tutor/health.py — tutor health

Function file:line Layer Description Feature
healthcheck app/services/tutor/health.py:14-58 service DB reachability + attempt counts + recommender_ready; never raises (degrades). Tutor / MCP

app/services/tutor/outline.py — node labels + search

Function file:line Layer Description Feature
resolve_node_labels app/services/tutor/outline.py:84-120 service node_ids → {name, path, kind} walking parents (V-O5, V-O3 cross-course). Tutor / MCP
search_nodes app/services/tutor/outline.py:123-197 service Domain-blind substring node search, optional course filter (V-M1). Tutor / MCP
get_outline_tree app/services/tutor/outline.py:200-239 service Course header + flat node list ordered (depth, position, id). Tutor / MCP
get_subtree app/services/tutor/outline.py:242-281 service Root node + descendant ids via recursive CTE (V-O1); no metric rollup (V-M1). Tutor / MCP
CourseNotFoundError app/services/tutor/outline.py:38-39 service Raised when a course_slug filter matches no course. Tutor / MCP
NodeNotFoundError app/services/tutor/outline.py:42-43 service Raised by get_subtree when node_id is absent. Tutor / MCP

app/services/tutor/questions.py — question read payload

Function file:line Layer Description Feature
get_question app/services/tutor/questions.py:92-96 service Full tutor review payload by external qid. Tutor / MCP
get_question_by_attempt_id app/services/tutor/questions.py:99-106 service Same payload resolved from an attempt_id. Tutor / MCP
_build_question_payload app/services/tutor/questions.py:20-89 service Private helper assembling the review shape (tags/features/history/distribution). Tutor / MCP
QuestionNotFoundError app/services/tutor/questions.py:12-13 service Raised when the question cannot be found. Tutor / MCP
AttemptNotFoundError app/services/tutor/questions.py:16-17 service Raised when the attempt_id does not exist. Tutor / MCP

app/services/tutor/sessions.py — session reads

Function file:line Layer Description Feature
get_latest_session_id app/services/tutor/sessions.py:17-26 service uworld_test_id of the most recently attempted session (or None). Tutor / MCP
get_recent_sessions app/services/tutor/sessions.py:29-65 service N most-recent sessions grouped by test id with accuracy + timestamps (clamp 1..50). Tutor / MCP
get_session_summary app/services/tutor/sessions.py:68-174 service Full debrief: totals, per-node by_topic, top topics, flags, notes (V-O1). Tutor / MCP
SessionNotFoundError app/services/tutor/sessions.py:13-14 service Raised when no attempt exists for the test_id. Tutor / MCP

app/web/media.py — media serving

Function file:line Layer Description Feature
serve (handler) app/web/media.py:12-27 handler GET /media/{file_path} — serve media from MEDIA_ROOT with a path-traversal guard. Knowledge Base

Coverage check

Every function in the catalog and every path-operation handler appears exactly once above. Handlers are listed under their owning module; their behavioral contracts (status codes, body models, error mapping) live in API Reference. Job cores (run_*) and their scheduler wrappers are both indexed — the wrapper in app/scheduler.py (layer job) and the service core under app/services/** (layer service/job).

Clone this wiki locally