manifest.toml [targets.*] upgraded from bare names to structured build configs.
Lesson.targets: Vec<String> → Vec<TargetConfig> { name, artifact, numbering }.
ArtifactKind { SingleFile, FileTree } mirrors Spec.Courseware.Artifact.
NumberingConfig.heading: Option<Vec<String>> — None means "use framework
default" (render package owns it), so absence is distinguishable from a value.
Empty [targets.x] body = all defaults, no diagnostics. Malformed config (bad
artifact value, non-array numbering.heading) → non-fatal SchemaViolation, target
kept with defaults. Enabled toml `preserve_order` so target declaration order is
real (the old Vec<String> never actually preserved order). target_names() helper
for call sites that only want names. 8 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lift the business decisions the MVP made into the Lean master, per constitution
rule 5. lake build green (20 jobs), no sorry.
- Artifact.lean: `inductive Artifact | singleFile | fileTree` — product shape is
part of a target's value (ADR-0009); it determines the assemble/reduce step.
- Render.lean reworked target-centric (ADR-0009, amends ADR-0005's matrix):
`TargetSpec` carries `artifact` + a per-kind `renders` map (field visibility
lives in this map); `RenderConfig.spec : TargetId → Option TargetSpec`; `covers`
rewritten (target must exist AND its map covers the kind) — semantics continuous
with ADR-0005 so renderIgnored still typechecks. `BuildStepForm` (declarative |
shell) is a structural anchor for the build form (shell hatch = ADR-0005's
medium-only category b) without replicating the schema.
- Diagnostic.lean extended: `DiagKind` (7 classes) + `DiagKind.severity` (six
error, renderIgnored warning); `Oracle` makes external-facility verdicts
(compiles/refsResolve/dataConforms/contentFilesPresent) an explicit
implementation boundary; `Legal` = no error-level diagnostic (backfills
ADR-0005's deferred full-legality), with renderIgnored deliberately excluded
(it's a warning).
- Pipeline.lean: the 5 phases (load→structural→schema→compile→coverage) as
structure — order + the compile gate (compile runs only on zero prior errors) +
load halts the pipeline. Algorithms stay out of Lean (depth ceiling).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lift business decisions made during the MVP build into the contract, per
constitution rule 5 (divergence points belong in the spec).
ADR-0009 — amends ADR-0005's render-matrix framing: an export target is a *build*
producing a typed Artifact (SingleFile | FileTree). build = map (per-kind, the
field-visibility lives here) + reduce (assemble, determined by artifact type:
SingleFile concatenates in lesson order then compiles — which is why @ref and
counters work). Build form = declarative schema + a run-shell hatch (where
ADR-0005's medium-only category (b) and npm/HTML builds live), not a Makefile.
typst compile is hidden backend mechanism, not part of a target. Defaults seeded
at project creation, overridable in the file — the heading-numbering bug
(一、 reused → 二、一、) is the worked example of that missing override layer.
ADR-0010 — backfills ADR-0005's deferred "legal lesson": the 7-class diagnostic
taxonomy with per-class meaning + severity; legal lesson = no error-level
diagnostic; the 5-phase pipeline (load→structural→schema→compile→coverage) with
compile gated on zero prior errors. External-facility diagnostics (TypstCompile,
DanglingReference, schema conformance) modeled as abstract predicates whose
verdict comes from an implementation oracle, not from Lean — keeps the contract
honest about what it does and doesn't decide.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The repo-layout block said implementation parts were "尚未创建"; they now exist.
Document the repo-root cargo workspace (so future parts reuse base crates), the
crates/ map (cph-diag/model/schema/typst reusable; cph-check/cli the checker),
the render/ typst package, and examples/.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The heart of the pipeline. Embeds the typst 0.15 compiler as a library to
compile-check a lesson and export a PDF, with diagnostics mapped back to source
spans (the product's value).
- LessonWorld: a typst World over the engineering-file directory tree. vpath =
real relative path (ADR-0007); the per-target driver is mounted in-memory as
`main` (.cph/driver-<target>.typ, never written to disk); @local/cph-render is
resolved from render_dir (CPH_RENDER_DIR / with_render_dir), no package install
or network. Fonts via typst-kit FontStore (system CJK).
- Driver generation: from the manifest, per target — static root-relative
`include` of each content file (⇒ module body content, ADR-0006), assembled
into the frozen `display(info, target, parts)` call; optional lemma proof
emitted only if proof.typ exists; example `source` scalar threaded through.
- Diagnostics: typst SourceDiagnostic (errors + warnings) → cph_diag::Diagnostic.
0.15 DiagSpan handled via span.id() + WorldExt::range → file-relative
SourceSpan with 1-based line/col; driver-internal spans re-pointed with a
"generated driver" hint, never dropped. typst hints/trace carried through.
- Engine API: new / with_render_dir / compile_check / build_pdf.
- 6 tests incl. build_pdf_with_real_render — full World→driver→cph-render→PDF
producing real PDF-1.7 output (student 42KB / teacher 54KB). clippy/fmt clean.
Also fixes render/typst.toml `compiler = ">=0.15.0"` → `"0.15.0"`: typst 0.15's
manifest parser rejects a `>=` comparator on the compiler field (the 0.14 CLU
WU-2 tested against was lenient). This unblocks loading the real render package.
Pinned: typst/typst-pdf/typst-syntax/typst-library/typst-layout/typst-kit =0.15.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Declarative JSON Schema per kind (segment/example/lemma/sop), embedded as real
.json artifacts (ADR-0006) and validated by a small hand-rolled interpreter
(properties/required/type/enum/additionalProperties + the `x-cph-content`
marker) — no jsonschema-crate churn for a one-optional-string scalar surface.
`x-cph-content: true` marks content fields (not in element.toml; require sibling
<field>.typ); everything else is an element.toml scalar. `validate(desc)` is
self-contained: UnknownKind for unknown kinds, MissingContentFile (path named in
message) for absent required .typ, SchemaViolation for bad scalars; lemma proof
optional ⇒ no diagnostic when absent. 8 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Single entry `display(info, target, parts)` over an ordered parts array, the
frozen contract the generated typst driver calls. Per-kind dispatch for
segment/example/lemma/sop; student/teacher matrix derived internally from
`target` (student hides example solution + lemma proof; teacher shows all).
Content-field values are consumed as already-evaluated content (driver
`include`s them ⇒ module body, ADR-0006), never imported/stringified. Optional
fields (lemma proof, example source) handled gracefully; unknown kind/target
degrade without crashing. Zero @preview deps (CI-robust); CJK + math styled.
Smoke compiles to non-empty student/teacher PDFs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mirrors spec-check's framing: an internal health gate on the implementation
(build/test/lint), not a spec→impl conformance gate (alignment is by review).
Installs fonts-noto-cjk ahead of the typst-compiling crates (WU-4+), since the
engineering files are CJK-heavy.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stand up the Rust implementation of the rule-based checker (the thing that
"stands in Lean's position" at product runtime). Single repo-wide cargo
workspace at the repo root so future components (e.g. an exporter) can reuse
shared crates.
- workspace: 6 member crates under crates/; cph-schema/cph-typst/cph-check/
cph-cli are stubs for later WUs that compile clean today.
- cph-diag: shared diagnostic vocabulary. Severity { Warning, Error } mirrors
Spec.Courseware.Diagnostic.Severity exactly (two-valued, no info/note — a
contract decision documented in-code since there's no CI gate for alignment).
Closed DiagCode set with stable string forms; Diagnostic with optional span +
fix hint; Display renders `error[CODE]: msg / --> file:line:col / hint: …`.
- cph-model: the ADR-0008 loader. Reads manifest.toml ([project]/[info]/ordered
[[parts]]/[targets.*]) + each part's element.toml into an ordered Lesson
(mirrors Lean `Lesson = List (Element P)`). Structure-only: cross-checks
part.kind == element.toml kind, rejects `..` traversal; does NOT do schema
validation (WU-3), content-file existence (WU-3), or typst (WU-4).
`load(root) -> (Option<Lesson>, Vec<Diagnostic>)`: Some even on collectible
part errors, None only on hard manifest failure.
- 13 tests (7 cph-diag, 6 cph-model incl. static fixtures doubling as format
docs). build + test + clippy -D warnings + fmt --check all green.
Judgment call flagged for review: cph-diag has no ManifestMalformed code, so
manifest-structure errors map to SchemaViolation (TODO noted in-code).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Discharge the layout question ADR-0007 deferred. Fix the engineering-file
on-disk arrangement so the checker recovers lesson order/membership without
evaluating typst:
- manifest.toml at root: [project] + [info] + ordered [[parts]] (kind+path) +
[targets.*]. Array order IS lesson order (ADR-0005). Subsumes the samples'
project.toml/info.toml and replaces main.typ's `#let parts`.
- element.toml per element folder: explicit kind + scalar fields (self-describing,
parent-dir grouping not load-bearing).
- content fields are convention-named <field>.typ siblings; the kind's JSON Schema
(ADR-0006) is the source of truth for which .typ files must exist. Drops the
samples' per-element main.typ wiring (now generated, not hand-authored).
Surfaced gaps (deferred): per-file render override (ADR-0005 says the matrix is
overridable in-file; MVP keeps rules in the render layer, no override), 题库
on-disk layout, manifest richness (per-part metadata, sectioning).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Picture the whole framework, then pin the element-kind language layer.
System platform layer — rebuild the semantics likec4 can't draw (ADR-0001..0004):
- Prelude: opaque Identifiers carrier (ProjectId/RunId/SessionId/Principal)
- System/Run: RunState + Terminal subset (set completeness left OPEN, no invented pending)
- System/Lock: lock owner=run (typed), LockTable exclusivity, WellFormed invariant
(a lock holder must be a non-terminal run — couples Lock and Run)
- System/Permission: read<edit<manage role lattice, capability derivation,
can_mono monotonicity theorem; force-release sits outside the lattice (admin-only)
- System/Audit: intentionally thin (mostly plumbing, OPEN)
Courseware product layer — split the single Lesson file into focused modules and
fix doc tautology: Primitives / Element / Lesson / Render / Diagnostic, plus
QuestionBank and Course skeletons (core relations OPEN, not invented).
Diagnostic upgrades WarnsIgnored into a severity-tagged checker rule.
Element schema + rich-content model (ADR-0006, ADR-0007):
- docs/adr/0006: kind schema is declarative JSON Schema; field types are built-in
scalars plus a `content` extension; a `content` value is typst source taken as
module body; rich content must carry a VirtualPath (else click-to-jump and
relative import break — verified against typst source); import boundary =
within the engineering file + @package
- docs/adr/0007: on-disk form is a real directory tree (agent/grep friendly);
VirtualPath = real relative path; layout conventions deferred
- spec/Courseware/RichContent: prose anchor for rich content (opaque VPath,
RichContentRef); ElementData kept abstract — JSON/typst internals are
implementation detail, not contract
lake build green (18 jobs), no sorry, toolchain v4.31.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
First product-core concept in the semantic master spec: a curriculum
engineering file is one lesson, modeled as an ordered sequence of typed
element instances over an open kind universe.
- docs/adr/0005: lesson model decision narrative + deferred OPEN items
- spec/Spec/Courseware/Lesson.lean: Primitives carrier (KindId/ElementData/
TargetId/RenderRule kept abstract), Element, Lesson, RenderConfig, and the
first checker seed (WarnsIgnored: used-kind-with-no-render-rule => warning)
- wire Spec.lean -> Spec.Courseware; drop placeholder Basic.lean
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>