main 与 spec-driven 无共同祖先(orphan)。本 merge commit 的 tree 取 spec-driven
的全部内容(完全以 spec-driven 为准),main 原有的 seed 文件不再保留。此后在 main
上准备发版本。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Acceptance fixture for ADR-0011's template build model. TH-141 gains
exports/student.typ + exports/teacher.typ (the framework default templates,
copied from render/templates/ — these should be auto-seeded at project creation,
out of scope this round). manifest.toml [targets.*] converted to v2: artifact =
{type="single-file", filepath} + a typst-compile step naming the template;
numbering removed (it now lives in the template).
Verified end-to-end through the new path (template compiled as main + augmented
manifest, no generated driver): `cph check` → 0 errors/0 warnings; `cph build`
student → 9pp PDF, teacher → 12pp PDF; numbering correct (一、 / 2.1 / 2.2, zero
"二、一"); real content rendered.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace generated-driver compilation with the template model.
cph-typst:
- driver.rs DELETED (generate_driver, static-include driver, numbering-threading).
- New manifest.rs: build_augmented_manifest(&Lesson) emits a TOML doc with [info]
+ ordered [[parts]] each carrying a `fields` array = the kind's content fields
whose <root>/<path>/<field>.typ exists on disk. Reuses cph-schema's
content_field_names (new dep; no cycle). This closes the OPEN point WU-C'
surfaced: typst has no file-exists primitive, so the engine (which has disk
access) tells the template which optional content (lemma proof) is present.
- World main = the real engineering-file template (<root>/<template>); the
augmented manifest is served as an in-memory virtual file at /.cph/manifest.toml
(never written to the tree), injected via sys.inputs.manifest. render_dir kept
only for @local/cph-render + vendored @preview/numbly resolution.
- Artifact handling: SingleFile+TypstCompile compiles; FileTree, shell-only, and
undeclared-target are blocking "deferred/not-declared" diagnostics (ADR-0011).
- Engine public signatures unchanged; LessonWorld::new takes template+manifest_src.
cph-check: no source change needed (only uses lesson.targets/target.name +
unchanged Engine methods); pipeline + Legal alignment intact.
Fixtures: mini gets exports/{student,teacher}.typ (from render/templates/) + v2
manifest + a second proof-less lemma to exercise optional content. render-stub
retired (tests use the real render/). Through-template PDFs offline: student 44KB,
teacher 56KB; proof-less lemma compiles clean (optional-content confirmed).
Workspace: fmt + clippy -D warnings + all tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the display(parts) entry (which a generated driver called) with the
template model: real editable templates (render/templates/{student,teacher}.typ,
the framework defaults copied into an engineering file's exports/) read the
manifest via toml(sys.inputs.manifest) and assemble parts themselves.
Key finding (drives the engine): a dynamic `include` inside a package resolves
against the PACKAGE root, never --root — so the include loop must live in the
TEMPLATE (which sits under --root): `include "/" + part.path + "/" + field +
".typ"` (root-relative absolute, runtime-computed — legal per ADR-0011's verified
fact). cph-render exposes render-lesson(info, target, parts, heading-numbering)
(content-in, never includes) + part-fields (kind→fields) + default-heading-numbering.
Numbering (numbly per-level) is set in the template, editable per engineering file.
Surfaced OPEN point closed by the engine (WU-D'): typst has no file-exists
primitive, so optional content (lemma proof) presence comes from a per-part
`fields` array the engine computes from disk. Manifest injected as root-relative
absolute (--input manifest=/...). numbly stays vendored/offline. display removed.
local-packages symlink (test-only @local resolution) gitignored — regenerable,
self-referential; the embedded engine mounts cph-render directly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[targets.*] reworked: artifact is now an inline table with a type discriminator
({type="single-file",filepath} / {type="file-tree",root,outputs}) and steps is an
ordered array-of-tables ({type="typst-compile",template} / {type="shell",run}).
numbering removed entirely (presentation lives in the template per ADR-0011).
TargetConfig { name, artifact: Artifact, steps: Vec<Step> }; Artifact and Step
mirror Spec.Courseware.Artifact / Spec.Courseware.Step (doc-cited). Sensible
defaults so a bare [targets.student] still works: single-file build/<name>.pdf +
one typst-compile exports/<name>.typ. Malformed artifact/step type → non-fatal
SchemaViolation with fallback. 8 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fix the abstraction level review flagged. lake build green (20 jobs), no sorry.
- Artifact.lean: bare enum → ADT with fields — singleFile(filepath) /
fileTree(root, outputs:glob). The product semantics (one file where, vs a tree
of what) are pinned in fields + doc, not erased behind empty constructors.
Paths/globs carried as String (semantics in doc; no filesystem algebra).
- Render.lean: Step inductive (typstCompile(template) | shell(run)) replaces the
loose BuildStepForm. TargetSpec = artifact + steps:List Step + covers:Kind→Prop
(the old renders:Kind→Option RenderRule is gone — coverage is now a plain
declaration; the render "how" lives in the typstCompile template). covers /
renderIgnored semantics continuous with ADR-0005.
- Primitives.lean: RenderRule field removed (the per-target payload is retired per
ADR-0011); a note records where the "how" now lives.
- Diagnostic.lean unchanged in meaning (covers signature stable; Legal still
quantifies over declared targets).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Refines ADR-0009 by fixing the shape of an artifact and a build; corrects a
false premise that propagated into the implementation.
ADR-0011:
- Artifact is an ADT WITH fields: SingleFile{filepath} / FileTree{root,
outputs:glob} — the product semantics (one file where, vs a tree of what) are
pinned, not erased behind a bare tag.
- A target = artifact + ordered typed steps: typstCompile{template} (typed
because the framework wires the manifest in — not a raw shell line) | shell{run}
(the hatch where ADR-0005 category-b medium-only builds land).
- Presentation (numbly numbering, styling) lives in the template file
(exports/<target>.typ), NOT the manifest; manifest [targets.*.numbering] removed.
- Correction (verified against typst source: typst-eval/src/import.rs:22,174;
tests/suite/scripting/include.typ): include/import paths MAY be runtime-computed
(only a bare `import expr` without `as` is restricted). So the framework does
NOT generate a static-include driver — a template toml()-reads the manifest and
includes each part via a computed path. Removes a whole generated layer.
- RenderRule-as-payload retired; coverage downgraded to a declaration (which
kinds a target renders) feeding the unchanged renderIgnored warning.
ADR-0008: corrected the two clauses (the "generated driver / static #import"
wording) that were residue of the false premise, pointing them at the
template-injection model and citing ADR-0011.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Integrate the WU-B (structured targets) and WU-C (numbly, config) changes.
cph-typst:
- World resolves any @preview/* package from the in-repo vendored dir
(<render_dir>/vendor/typst-packages/preview/<name>/<version>/), fully offline —
no typst-kit download. Proven: build_pdf_with_real_render compiles the real
render package (which imports @preview/numbly:0.1.0) through the embedded
engine, PDF out.
- Driver threads each target's presentation config: looks up the TargetConfig by
name, emits `config: (numbering: (heading: (...)))` (escaped) when an override
is present, else `config: (:)` (render default).
- target_precheck guards artifact/target: FileTree → blocking diagnostic (MVP is
single-file, deferred per ADR-0009); --target not declared → blocking
diagnostic; no-targets lesson still defaults through.
cph-check:
- Migrated to Lesson.targets: Vec<TargetConfig> (target_names() / target.name at
the 6 sites). Pipeline unchanged (already matches ADR-0010 phases). Documented
that !has_errors() implements Spec.Courseware.Legal (no error-level diagnostic).
Workspace: fmt + clippy -D warnings + all tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes the numbering bug (single pattern "一、" reused across levels → level-2
rendered "二、一、…"). display now takes a `config` dict; heading numbering uses
numbly per level — framework default ("{1:一}、","{1:1}.{2:1}","{1:1}.{2:1}.{3:1}")
→ 一、 / 1.1 / 1.1.1 — overridable via config.numbering.heading from the
engineering file (ADR-0009's file-resident override path, the layer whose absence
caused the bug). Verified: grep "二、一" = 0 across all smoke PDFs.
Adds @preview/numbly:0.1.0 (pure typst, zero transitive deps), VENDORED in-repo
at render/vendor/typst-packages/preview/numbly/0.1.0/ for network-free CI;
resolve via --package-cache-path render/vendor/typst-packages. parts contract +
student/teacher field visibility unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>