Convert a confirmed materials folder into a validated cram workspace. Build and repair the knowledge base only; do not teach or grade. The normal path produces structured ingestion facts under .ingest/, compiled chapter wiki and bank files, progress state, visual evidence, and an explicit readiness verdict before handing control back to exam-cram.
Activate when the confirmed workspace lacks its wiki, bank, or progress state; when the student supplies new/changed course materials; or when validate_workspace.py reports ingestion readiness blocked. Do not treat the mere existence of generated files as proof that the workspace is ready.
-
Pass the executable start gate, then use the official ingestion entry. The exact materials/workspace pair and all three choices must already have been persisted with exam_start.py confirm as specified by exam-cram; a bare registry row or update_progress.py set is insufficient. Verify read-only with exam_start.py status --materials <dir> --workspace <ws> --json, then run from the package root:
python scripts/ingest_course.py --materials <dir> --workspace <ws> --json [--course-name <name>] [--lang zh|en] [--artifact-mode chat|visual]
The default core orchestrator performs dependency preflight, deterministic extraction, provenance-preserving structured compilation, state initialization, visual indexing/repair, and canonical workspace validation. It never installs a dependency. Pass --artifact-mode only for an explicit standing student choice; omit it to retain the existing/default chat preference.
-
Interpret process and readiness separately. Exit 0 means the engineering process completed and the JSON readiness is ready or usable_with_gaps; preserve and report any warnings in the latter. Exit 10 means process_success=true but readiness=blocked: do not teach, quiz, or claim completion. Any other nonzero is a dependency, input, or operation failure. For a missing required capability, ask once with the active language pack's consent line, install only on yes, then rerun the same command. A business/data failure is never evidence that Python is absent.
-
Require ingestion-v2 parser receipts. The regular path writes .ingest/parser_receipts.json with one receipt for every discovered source. Each row binds canonical source path, exact source SHA-256/media type, adapter/module/distribution/version, requested and produced location anchors, config SHA-256, result status, and the exact policy {network:false, upload:false, install:false}. Missing/duplicate rows, source or page drift, a policy mismatch, or a receipt referring to an unknown source blocks validation. A legacy ingestion-v1 payload remains readable only as legacy and must not be described as having v2 receipts. Unit language comes only from its payload: zxx is formula/symbol-only, never inherited, and never zh/en Guide support; otherwise review.
The normal orchestrator publishes .ingest/material_build_pending.json before any successful candidate asset/raw/report generation becomes visible. A nonzero builder result publishes none of those candidates, preserves the canonical raw input and parse report, and returns diagnostics only in the command result; if publication itself cannot roll back cleanly, the blocker is retained. Pending binds the prior build manifest, new raw/report, complete candidate asset policy, and exact migration receipt ledger. While it exists, ordinary ingestion publication/mutation—including review, claim, and Guide writers—and validation fail closed; only the explicit generation-aware builder/compiler path may proceed. Only a receipt-bijective answer_context -> student_attempt correction is migratable; standalone builder migration, stale bytes, missing/extra receipts, and every other role change fail closed.
A pending generation plus a missing or drifted exam_runtime_receipt.json is recovered only through python scripts/exam_start.py recover-material-build --workspace <ws> --materials <dir> --action resume|supersede --json; ordinary confirm intentionally refuses. resume may compile only the exact pending generation: it skips parsing when both bound source documents are exact, permits blocker-first reconstruction when they are incomplete, and publishes nothing if reconstruction produces a different generation. Only an explicit supersede may publish that different candidate; its schema-2 pending marker binds the immediate predecessor. Audit records are generation-addressed under .ingest/material_build_recovery/, bounded to 64 authorization events and 64 direct predecessor edges. Every abandoned edge names its direct child. A final receipt binds at most those 64 rows plus one current completed resume row, and the build manifest hash-binds exactly that declared recovery-log set. Never remove or edit pending/recovery facts by hand.
For that generation, the compiler places structured facts, the build manifest, wiki/bank/teaching layers, retrieval index, reports/plans, and the pending-to-receipt transition in one bounded ingestion transaction. It writes .ingest/pending_ingest.json and backups before the first registered target changes; validation blocks on a crash residue, and the next locked mutation restores all registered targets before continuing. Candidate assets/raw/report remain the builder generation outside this rollback set, so material pending stays available for an exact retry. Successful finalization writes .ingest/material_build_receipt.json, emits build-manifest schema 2 with an exact material_build contract and raw/report/receipt artifact hashes, re-verifies live bindings, and removes material pending last. Current-protocol output must not be refreshed or re-emitted as schema 1; legacy schema 1 remains readable but does not claim this gate. ingest_course.py performs later study_state.json initialization and optional artifact-preference writes only after compiler success; those learner-state operations are outside the compiler transaction. This protocol is lock-coordinated and crash-recoverable for process interruption, not a claim of power-loss durability or a filesystem-atomic snapshot for arbitrary unlocked readers.
-
Use the dedicated XLSX/raster routes and honest anchors. XLSX is parsed locally with the standard library: each worksheet is one page-equivalent and preserves workbook order, sparse cell coordinates/values, formulas plus stored cached values, defined-table metadata, and supported embedded raster assets without requiring Excel. The parser does not calculate formulas; missing cached values, external/network-looking formulas, hidden sheets, and unsupported relationships become typed review signals. A standalone raster is one page-equivalent with signature-checked dimensions/hash and a local source_page asset. Safe UTF-8 sidecars may supply text; otherwise emit standalone_raster_needs_ocr and route to an installed local OCR/vision capability or typed review—never fake empty-text success. PDF page values are page ordinals, PPTX values are slide ordinals, and DOCX values are logical segments split only at explicit page breaks; never call a DOCX anchor a physical rendered page.
-
Keep optional high-fidelity parsing host-controlled. --ingest-adapter docling|mineru is an explicit host integration point, not an automatic fallback. Package discovery is only a probe; extraction requires a host-supplied callable runner for ingest_course.run(..., adapter_runner=...) (or the lower-level API), and a CLI flag alone does not configure it. The bundled adapter neither eagerly imports a vendor nor itself installs, accesses the network, or uploads. Its validated network/upload/install=false values are configuration declarations, not a sandbox or attestation of runner internals; the host must constrain and audit its runner. A missing or invalid selected adapter/runner fails explicitly and returns to core/typed review with no success claim.
-
Check derived duplicate/conflict facts. In ingestion-v2, .ingest/duplicate_candidates.jsonl, canonical_groups.jsonl, source_conflicts.jsonl, and source_priorities.jsonl are deterministic derived facts, not mutable source truth. They bind exact content-unit/source revisions. Exact groups may choose a deterministic display occurrence while preserving every source occurrence and its location-derived unit_id; near matches are not folded automatically. Priority is evidence metadata, never an implicit winner. Any unresolved conflict fails closed and must be surfaced/resolved through evidence-backed review before teaching, quizzes, guide material claims, or completion.
-
Take over typed issues one by one. Treat .ingest/review_queue.jsonl as the canonical lifecycle, not .ingest/ai_review_manifest.json (legacy view only). A new type_defaulted issue is scoped to exactly one question/external ID; never close a source-wide legacy issue after checking only one chapter. For a gradable subjective question with an official paired answer but no grading points, subjective_keywords_missing targets the answer unit and binds the official answer source revision/pages. Add narrow source-backed metadata.keywords there; the compiler uses question-side keywords first and otherwise inherits the paired answer's reviewed keywords. No official answer means no inferred keywords. Start with:
python scripts/ingest_review.py --workspace <ws> --json list
python scripts/ingest_review.py --workspace <ws> --json show <issue_id>
python scripts/ingest_review.py --workspace <ws> --json claim <issue_id>
Read each issue's source hash, page/evidence references, reason codes, description, and suggested action. Recover scans/images through the host's available OCR/vision path; inspect ambiguous chapter or problem/solution assignments against the original pages; never infer an official answer from filename alone.
After an AI/human reviewer has finished a page-by-page visual audit of a batch of formula_hint issues, scripts/import_formula_audit.py --workspace <ws> --audit <audit.json> [--audit <more.json>] --output-dir <draft-dir> --reviewer <name> --json may convert that audit into deterministic evidence-bound patch drafts. It only drafts: it does not claim issues, apply patches, rebuild derivatives, or treat an audit-supplied render path as ledger evidence. Continue through ingest_review.py validate-patch and apply-batch --patch-list <draft-dir>/patch-list.json; the importer never replaces those gates.
-
Apply only evidence-bound patches. Build one strict ReviewPatch per issue from show and run validate-patch on every file. Use apply for one patch. For many inspected independent issues, apply-batch --patch-list <json> keeps separate context validation, transactions, and ledger identities while compiling derivatives once; partial progress remains replay-safe. Never combine issue identities. Allowed operations add/replace a unit, assign chapter/phase, pair Q&A, classify an asset, or mark unrecoverable. A cross-source pair_qa operation must include a sorted source_revisions binding for both the question and answer source revisions; drift on either side reopens review instead of replaying the old decision. Use mark-unrecoverable --reason ... only after recovery is impossible. Never hand-edit the append-only ledger, queue, compiled units, facts, wiki, or bank.
-
Rebuild and validate after review. Run ingest_review.py --workspace <ws> rebuild, then validate_workspace.py <ws> --json. Source drift, stale parser/fact hashes, unresolved conflicts or blocking issues, missing location anchors, or unbound blocking review entries keep readiness blocked. unrecoverable issues remain visible warnings rather than disappearing.
-
Account for every alert. Read the stable .ingest/parse_report.json, .ingest/unbound_review.json, typed queue, parser/fact warnings and conflicts, and ingest_report.json.missing_answer_ids in full. Recover each supported gap or tell the student exactly which material remains incomplete and why. Never silently skip an alert.
-
Advanced lower-level diagnostic path only. To isolate a compiler/parser defect, maintainers may run scripts/build_raw_input_from_workspace.py and then scripts/ingest.py directly. This is not the normal student workflow and does not replace final validation. scripts/ingest.py compiles a prepared payload; it does not independently prove readiness.
-
Three-sided visual cross-check AFTER ingest has created the workspace. The normal orchestrator already runs build_visual_index.py --apply --apply-wiki and recompiles. In lower-level diagnostics, inspect wiki visual coverage, prompt suspects, answer suspects, deferred answer pages, and shared prompt/answer blockers separately. A zero count on one side proves nothing about the others; answer-only pages never enter prompt/wiki context early.
-
True no-Python fallback only. Manual writing is allowed only after a direct interpreter probe proves Python truly cannot start. A nonzero command is a fail-loud operation error, not permission to degrade silently. In the confirmed fallback, disclose that structured validation, typed review, source-version/parser-receipt/conflict checks, and visual cross-checks are unavailable, then create only the minimum workspace from the selected locale templates. Missing package files are not evidence that Python is unavailable.
-
Label compiled provenance honestly: 🟢 来自资料 for material-derived content, 🟡 AI补充,可能与你老师讲的不完全一致 for an explicit supplement, and ⚠️ AI生成答案,非老师/教材提供 for a generated answer when no official answer exists.