diff --git a/README.md b/README.md index 1c1fea8..001d5bd 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ cph outline <工程目录> --format pdf --force # 明确允许覆盖已有 o hub 语义):`init` 产出一个 `cph check` 可过的工程根;`add` 按 kind 建 `<子目录>/<名称>/` + `element.toml` + 必填内容字段(`segment→textbook.typ`、 `example→problem/solution.typ`、`lemma→stmt.typ`、`sop→sop.typ`),并把配套 -`[[children]]` 追加进根 `manifest.toml`(保持数组连续,不破坏注释;ADR-0029)。缺省 +`[[children]]` 追加进根 `manifest.toml`(保持数组连续,不破坏注释;ADR-0036)。缺省 `--root` 为当前目录。 **版本契约(ADR-0016):** 教研工程文件根放一个 `.cph-version` 文件,内容为它面向的 cph 版本(如 `0.0.2`)。`cph` 加载时比对自身版本,不相容则报 `E-CPH-VERSION` error 并拒绝(当前判定为版本完全相等;后续可放宽为 semver 区间,只改一处谓词)。`examples/` 与本仓 fixture 已带该文件作为迁移起点;缺文件的工程暂时跳过此检查(OPEN)。 diff --git a/crates/README.md b/crates/README.md index e47838a..e471cdf 100644 --- a/crates/README.md +++ b/crates/README.md @@ -3,11 +3,11 @@ These crates implement the rule-based lesson checker whose semantics are pinned by the ADRs in `docs/adr/`: it reads an engineering-file (one lesson, ADR-0005) -laid out per ADR-0029 (a nested outline manifest — every container folder +laid out per ADR-0036 (a nested outline manifest — every container folder carries `manifest.toml`, every leaf carries `element.toml`; supersedes ADR-0008's flat `[[parts]]`), validates structure and content, and emits diagnostics. `cph-diag` (the shared diagnostic vocabulary), `cph-model` (the -ADR-0029 loader, also loading `bundle.toml` arrangements per ADR-0030), and +ADR-0036 loader, also loading `bundle.toml` arrangements per ADR-0037), and `cph-typst` (the typst `World` / compile / span-mapping layer) are deliberately reusable by future components such as an `exporter`, which is why they live in this repo-wide `crates/` directory rather than under any single @@ -20,7 +20,7 @@ entrypoint) are the checker proper. | crate | owner | role | |---------------|-------|------| | `cph-diag` | WU-1 | shared diagnostic vocabulary (`Severity`, `DiagCode`, `Diagnostic`, `SourceSpan`) — reusable | -| `cph-model` | WU-1 | parses the ADR-0029 nested outline layout (+ ADR-0030 bundles) into an in-memory ordered `Lesson`/`Bundle` — reusable | +| `cph-model` | WU-1 | parses the ADR-0036 nested outline layout (+ ADR-0037 bundles) into an in-memory ordered `Lesson`/`Bundle` — reusable | | `cph-schema` | WU-3 | the 4 stdlib kind JSON Schemas + structural validation | | `cph-typst` | WU-4 | typst `World`, driver generation, compile, PDF, span mapping — reusable | | `cph-check` | WU-5 | orchestration: render-coverage and the full check pipeline | diff --git a/crates/cph-check/src/lib.rs b/crates/cph-check/src/lib.rs index e9163ce..34c1b61 100644 --- a/crates/cph-check/src/lib.rs +++ b/crates/cph-check/src/lib.rs @@ -229,9 +229,9 @@ pub fn build(root: &Path, engine: &Engine, target: &str) -> (Option>, Ch } } -/// Build a PDF for a **bundle** target (ADR-0030): the multi-lesson combined +/// Build a PDF for a **bundle** target (ADR-0037): the multi-lesson combined /// artifact. Mirrors [`build`]'s contract and gating, but runs phases (a)–(c) -/// over **every member lesson independently** (ADR-0030's invariant: each +/// over **every member lesson independently** (ADR-0037's invariant: each /// lesson stays independently checkable; the bundle only reads them for /// assembly). Any member's structural/schema error refuses the whole bundle /// build — a broken member lesson makes the combined artifact invalid too. @@ -288,7 +288,7 @@ pub fn build_bundle(root: &Path, engine: &Engine, target: &str) -> (Option Vec { let (bundle, _) = cph_model::load_bundle(root); @@ -301,7 +301,7 @@ pub fn declared_bundle_target_names(root: &Path) -> Vec { } /// The lesson's declared export-target names, in declared order — or -/// `[DEFAULT_TARGET]` if it declares none (ADR-0030 batch default: `cph build` +/// `[DEFAULT_TARGET]` if it declares none (ADR-0037 batch default: `cph build` /// with no `--target` builds every declared target). /// /// Loads the lesson read-only, ignoring diagnostics: an unloadable lesson (or @@ -564,7 +564,7 @@ pub struct MarkdownAssembleReport { } /// Execute the `AssembleMarkdown` steps of a target (ADR-0015): concatenate each -/// element's `.md` markdown content file in `parts` order (ADR-0029's +/// element's `.md` markdown content file in `parts` order (ADR-0036's /// depth-first element sequence) into the target's single-file artifact. /// This is the **third typed step**: unlike /// [`build`] (typst template → PDF) the framework owns the read/concatenate/write diff --git a/crates/cph-cli/src/main.rs b/crates/cph-cli/src/main.rs index 8133962..8943df4 100644 --- a/crates/cph-cli/src/main.rs +++ b/crates/cph-cli/src/main.rs @@ -41,7 +41,7 @@ enum Command { /// Build one or more render targets. Exits 1 if any target fails. /// /// With no `--target`, batches every target the lesson declares - /// (ADR-0030): each target builds independently — one failing does not + /// (ADR-0037): each target builds independently — one failing does not /// stop the rest — and the exit code is non-zero if any target failed. /// Repeat `--target` to build an explicit ordered subset instead. Build { @@ -57,7 +57,7 @@ enum Command { #[arg(short = 'o', long, value_name = "OUT")] out: Option, }, - /// Build one or more bundle targets (ADR-0030): combine an ordered + /// Build one or more bundle targets (ADR-0037): combine an ordered /// arrangement of self-contained lessons (`bundle.toml`) into one /// artifact. Same batching/exit-code contract as `build`. Bundle { @@ -209,12 +209,12 @@ fn run_completions(shell: CompletionTarget) -> ExitCode { // authoritative: whatever these write, `cph check` must accept. /// The stock `exports/student.typ` written by `init` — the framework's default -/// render template (ADR-0011, outline-shape ADR-0029), the same file the +/// render template (ADR-0011, outline-shape ADR-0036), the same file the /// examples ship. Kept verbatim so a freshly scaffolded engineering file /// renders out of the box; it imports `@local/cph-render:0.1.0`, which the /// engine resolves from the embedded package. Presentation (heading numbering, /// styling) is editable here per engineering file, not in the manifest. -const DEFAULT_STUDENT_TEMPLATE: &str = r##"// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +const DEFAULT_STUDENT_TEMPLATE: &str = r##"// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // This is a *real, editable* file that lives in an engineering file at // `exports/student.typ`. The framework compiles it AS THE MAIN FILE with the @@ -232,7 +232,7 @@ const DEFAULT_STUDENT_TEMPLATE: &str = r##"// DEFAULT STUDENT TEMPLATE (framewor // PACKAGE, not the engineering root. A `//.typ` written HERE // (this template lives under `--root`) resolves against `--root`. So the // template loads content and hands cph-render an already-assembled `outline` -// array (elements interleaved with section headings, ADR-0029). +// array (elements interleaved with section headings, ADR-0036). // // OPEN CONTRACT POINT — optional-content presence. typst has no "does this file // exist" primitive (a missing `include` is a hard compile error). So the @@ -259,7 +259,7 @@ const DEFAULT_STUDENT_TEMPLATE: &str = r##"// DEFAULT STUDENT TEMPLATE (framewor // resolved against --root) and read scalar fields from /element.toml. // `part-fields` (from cph-render) is the single source of truth for // kind->fields. -// - a "section" entry (ADR-0029): pass its title/depth straight through — no +// - a "section" entry (ADR-0036): pass its title/depth straight through — no // content to load, it is a heading. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { @@ -432,7 +432,7 @@ template = "exports/student.typ" /// Add a new part to the engineering file at `root`: create its folder with /// `element.toml` + blank required content files, then append its `[[children]]` -/// entry to the root `manifest.toml` (ADR-0029 root children). Rejects unknown +/// entry to the root `manifest.toml` (ADR-0036 root children). Rejects unknown /// kinds, unsafe names, and anything that would double-register an existing part. fn run_add(root: &std::path::Path, kind: &str, name: &str) -> ExitCode { let dir = match kind_dir(kind) { @@ -524,7 +524,7 @@ fn manifest_has_child(manifest: &str, rel: &str) -> bool { /// adjacent). The block goes immediately before the first section header that is /// neither `[project]`/`[info]` nor an existing `[[children]]` entry (i.e. before /// `[targets.*]`), or at end-of-file if none — either way it lands at the tail -/// of the root-children run, after `[info]` and any existing children (ADR-0029). +/// of the root-children run, after `[info]` and any existing children (ADR-0036). /// Comment blocks are preserved. fn insert_child(manifest: &str, kind: &str, rel: &str) -> String { let block = format!("[[children]]\nkind = \"{kind}\"\npath = \"{rel}\"\n"); @@ -717,13 +717,13 @@ fn write_outline_output(path: &std::path::Path, bytes: &[u8], force: bool) -> Re } } -/// Dispatch `cph build` (ADR-0030): with an explicit `--target` (repeatable), +/// Dispatch `cph build` (ADR-0037): with an explicit `--target` (repeatable), /// build exactly that ordered set; with none, batch every target the lesson /// declares. Each target builds **independently** — one failing does not stop /// the rest — and prints a per-target ledger when building more than one. /// Exits non-zero iff **any** target failed to produce its artifact (a build /// failure is a real defect, distinct from the non-blocking `renderIgnored` -/// warning class — ADR-0030). +/// warning class — ADR-0037). fn run_build_command( path: &std::path::Path, engine: &Engine, @@ -819,10 +819,10 @@ fn run_build_one( } } -/// Dispatch `cph bundle` (ADR-0030) — same batching/exit-code contract as +/// Dispatch `cph bundle` (ADR-0037) — same batching/exit-code contract as /// [`run_build_command`], over a bundle's own declared targets. MVP bundle /// targets are `typst-compile` only (no shell/markdown-assemble routing — -/// ADR-0030 did not extend those step kinds to bundles). +/// ADR-0037 did not extend those step kinds to bundles). fn run_bundle_command( path: &std::path::Path, engine: &Engine, diff --git a/crates/cph-diag/src/lib.rs b/crates/cph-diag/src/lib.rs index d92f825..2efb177 100644 --- a/crates/cph-diag/src/lib.rs +++ b/crates/cph-diag/src/lib.rs @@ -90,8 +90,8 @@ pub enum DiagCode { /// A `manifest.toml`/`bundle.toml` is structurally broken: invalid TOML, a /// required table missing (root `[project]`/`[info]`), a folder that is /// neither a container (`manifest.toml`) nor a leaf (`element.toml`) — or - /// is ambiguously both (ADR-0029) — or a bundle `lessons` entry malformed - /// (ADR-0030). Distinct from `SchemaViolation` (instance data vs. its + /// is ambiguously both (ADR-0036) — or a bundle `lessons` entry malformed + /// (ADR-0037). Distinct from `SchemaViolation` (instance data vs. its /// kind's schema): this code is for the *carrier document's own* /// structure. Added to discharge the manifest-level errors that used to /// overload `SchemaViolation` before this code existed. diff --git a/crates/cph-model/src/lib.rs b/crates/cph-model/src/lib.rs index 4d08155..74a3074 100644 --- a/crates/cph-model/src/lib.rs +++ b/crates/cph-model/src/lib.rs @@ -1,4 +1,4 @@ -//! `cph-model` — load the ADR-0029 nested outline manifest into an in-memory +//! `cph-model` — load the ADR-0036 nested outline manifest into an in-memory //! lesson. //! //! This crate is the **loader**, not the full checker. A lesson's structure is @@ -12,7 +12,7 @@ //! pipeline validate and include content from — unaffected by nesting. //! - [`Lesson::outline`] — the **full rendering-order sequence**: elements //! interleaved with section headings, at the depth-first traversal position -//! they open at (ADR-0029). This is what the augmented manifest (built by +//! they open at (ADR-0036). This is what the augmented manifest (built by //! `cph-typst`) walks to hand the template a rendering order that includes //! headings. //! @@ -34,7 +34,7 @@ use std::path::{Component, Path, PathBuf}; use cph_diag::{DiagCode, Diagnostic, Severity}; use serde::{Deserialize, Serialize}; -/// An in-memory lesson loaded from an engineering file (ADR-0029). +/// An in-memory lesson loaded from an engineering file (ADR-0036). #[derive(Debug, Clone, PartialEq, Serialize)] pub struct Lesson { /// `[project]` from the root manifest (id, name). @@ -43,12 +43,12 @@ pub struct Lesson { pub info: Info, /// The ordered **element** sequence — the lesson's element order (ADR-0005). /// Contains only leaves; containers never appear here (a container - /// "contributes no element of its own", ADR-0029). Index-stable: an + /// "contributes no element of its own", ADR-0036). Index-stable: an /// [`OutlineEntry::Element`] names a position in this `Vec` by index. pub parts: Vec, /// The full depth-first rendering order: elements (by index into `parts`) /// interleaved with section headings, at the position they open in the - /// tree (ADR-0029). Consumed by the augmented-manifest builder so a + /// tree (ADR-0036). Consumed by the augmented-manifest builder so a /// template can render headings in their real position; `cph-check`'s /// structural/schema/coverage phases do not need it (they use `parts`). pub outline: Vec, @@ -77,7 +77,7 @@ impl Lesson { } } -/// One entry in the lesson's full rendering-order sequence (ADR-0029). +/// One entry in the lesson's full rendering-order sequence (ADR-0036). #[derive(Debug, Clone, PartialEq, Eq, Serialize)] pub enum OutlineEntry { /// An element at this position: `part_index` into [`Lesson::parts`]. @@ -88,7 +88,7 @@ pub enum OutlineEntry { depth: u32, }, /// A section container opens here. Contributes no element; it is a - /// heading, not a part (ADR-0029). `depth` is the section's nesting depth + /// heading, not a part (ADR-0036). `depth` is the section's nesting depth /// (1 = a section directly under the engineering-file root). Section { /// The container's declared kind (MVP: always `"section"`; validated @@ -378,17 +378,17 @@ pub struct Info { /// One element leaf, plus its loaded `element.toml` descriptor. /// /// `path` is the element folder's path relative to the engineering-file root, -/// regardless of how deeply nested it is in the outline tree (ADR-0029) — the +/// regardless of how deeply nested it is in the outline tree (ADR-0036) — the /// loader accumulates full root-relative paths during the depth-first walk, so /// every downstream consumer (schema validation, augmented-manifest include -/// paths) keeps working against a root-relative path exactly as before ADR-0029. +/// paths) keeps working against a root-relative path exactly as before ADR-0036. #[derive(Debug, Clone, PartialEq, Serialize)] pub struct Part { /// Declared kind from the containing outline entry (ADR-0006). This is the /// **declared** kind, which may disagree with the `element.toml` kind (a /// disagreement is reported as [`DiagCode::UnknownKind`], but the declared /// kind — not the descriptor's — is what `cph-check`'s known-kind check - /// tests, matching pre-ADR-0029 behavior). + /// tests, matching pre-ADR-0036 behavior). pub kind: String, /// Element folder path, root-relative, kept verbatim (forward/back slashes /// as the OS provides) for diagnostics/display. @@ -418,7 +418,7 @@ pub struct ElementDescriptor { // --- raw deserialization shapes (mirror the on-disk TOML) --------------------- -/// One `manifest.toml`, root or container (ADR-0029). Root-only tables +/// One `manifest.toml`, root or container (ADR-0036). Root-only tables /// (`project`/`info`/`targets`) and the container-only `group` table coexist in /// one shape; [`load`]/[`load_children`] enforce which is expected where and /// flag misplacement rather than rejecting parse outright (non-fatal, like @@ -471,14 +471,14 @@ impl RawAuthor { } } -/// A container's optional `[group]` table (ADR-0029): presentation metadata for +/// A container's optional `[group]` table (ADR-0036): presentation metadata for /// a section, kept minimal per the ADR's recommended default. #[derive(Debug, Deserialize)] struct RawGroup { title: Option, } -/// One `children` entry (ADR-0029): a `kind` + a **parent-relative** `path`. +/// One `children` entry (ADR-0036): a `kind` + a **parent-relative** `path`. /// The loader resolves on disk whether the named folder is a leaf /// (`element.toml`) or a container (`manifest.toml`) — `kind` is the declared /// label, cross-checked against the leaf's `element.toml` kind (unchanged from @@ -600,7 +600,7 @@ pub fn load(root: &Path) -> (Option, Vec) { } }; - // The root is the implicit top container (ADR-0029): it must not declare + // The root is the implicit top container (ADR-0036): it must not declare // `[group]` (that is container-only presentation metadata). if raw.group.is_some() { diags.push( @@ -625,7 +625,7 @@ pub fn load(root: &Path) -> (Option, Vec) { .map(|(name, value)| parse_target(name, value, &mut diags)) .collect(); - // Walk the outline tree depth-first (ADR-0029), starting at the root's own + // Walk the outline tree depth-first (ADR-0036), starting at the root's own // children. A direct child section of the root opens at depth 1. let mut parts = Vec::new(); let mut outline = Vec::new(); @@ -652,7 +652,7 @@ pub fn load(root: &Path) -> (Option, Vec) { } /// Recursively load one container's ordered `children` into `parts`/`outline` -/// (ADR-0029). `container_dir` is the container's own absolute directory; +/// (ADR-0036). `container_dir` is the container's own absolute directory; /// `rel_prefix` is that container's own root-relative path (empty for the /// engineering-file root). `next_section_depth` is the depth a **direct** /// section child of this container would open at (1 for the root's children). @@ -884,7 +884,7 @@ fn read_container_manifest(dir: &Path, diags: &mut Vec) -> Option, @@ -1498,9 +1498,9 @@ fn has_parent_traversal(path: &Path) -> bool { path.components().any(|c| matches!(c, Component::ParentDir)) } -// --- bundle: an ordered arrangement of lessons (ADR-0030) -------------------- +// --- bundle: an ordered arrangement of lessons (ADR-0037) -------------------- -/// A **bundle** (ADR-0030): a directory carrying `bundle.toml`, which arranges +/// A **bundle** (ADR-0037): a directory carrying `bundle.toml`, which arranges /// an ordered list of already-authored, self-contained lessons into one export /// unit. Discharges ADR-0005's deferred "course = arrangement of lessons" for /// the export purpose, without inventing a full course-authoring model. @@ -1540,7 +1540,7 @@ pub struct BundleLesson { /// `bundle.toml` (kept verbatim for diagnostics/display). pub path: PathBuf, /// Which of that lesson's own declared targets to render into the bundle - /// (ADR-0030: the bundle assembles a lesson's already-authored content, it + /// (ADR-0037: the bundle assembles a lesson's already-authored content, it /// does not re-target it). Defaults to the lesson's first declared target, /// or `"student"` if the lesson declares none. pub target: String, diff --git a/crates/cph-model/tests/bundle.rs b/crates/cph-model/tests/bundle.rs index 6074b35..8c49743 100644 --- a/crates/cph-model/tests/bundle.rs +++ b/crates/cph-model/tests/bundle.rs @@ -1,4 +1,4 @@ -//! Integration tests for `cph_model::load_bundle` (ADR-0030): an ordered +//! Integration tests for `cph_model::load_bundle` (ADR-0037): an ordered //! arrangement of self-contained lessons, loaded from `bundle.toml`. use std::path::PathBuf; diff --git a/crates/cph-model/tests/load.rs b/crates/cph-model/tests/load.rs index b635d57..e7c6363 100644 --- a/crates/cph-model/tests/load.rs +++ b/crates/cph-model/tests/load.rs @@ -1,5 +1,5 @@ //! Integration tests for `cph_model::load`, driven by static fixtures under -//! `tests/fixtures/`. The fixtures double as documentation of the ADR-0029 +//! `tests/fixtures/`. The fixtures double as documentation of the ADR-0036 //! on-disk format (a nested outline manifest; supersedes ADR-0008's flat //! `[[parts]]`). @@ -111,7 +111,7 @@ fn nested_sections_flatten_depth_first_with_correct_depths() { "nested fixture must have no diagnostics, got: {diags:?}" ); - // DFS pre-order element sequence (ADR-0029): containers contribute no + // DFS pre-order element sequence (ADR-0036): containers contribute no // element of their own. let paths: Vec<_> = lesson.parts.iter().map(|p| p.path.clone()).collect(); assert_eq!( diff --git a/crates/cph-typst/src/lib.rs b/crates/cph-typst/src/lib.rs index b0b418f..dff7741 100644 --- a/crates/cph-typst/src/lib.rs +++ b/crates/cph-typst/src/lib.rs @@ -181,7 +181,7 @@ impl Engine { )) } - /// Compile-check `bundle` for `target` (ADR-0030) — same contract as + /// Compile-check `bundle` for `target` (ADR-0037) — same contract as /// [`Engine::compile_check`], but over a [`Bundle`]'s own declared targets /// and the augmented **bundle** manifest (each member lesson's outline, /// path-prefixed to resolve against the bundle root). @@ -200,7 +200,7 @@ impl Engine { out } - /// Build a PDF for `bundle` / `target` (ADR-0030) — same contract as + /// Build a PDF for `bundle` / `target` (ADR-0037) — same contract as /// [`Engine::build_pdf`], over a [`Bundle`]'s own declared targets. pub fn build_bundle_pdf( &self, @@ -244,7 +244,7 @@ impl Default for Engine { /// Validate a `(targets, target)` request and resolve the template path to /// compile. Shared by [`Engine::world_for`] (a lesson's `targets`) and -/// [`Engine::world_for_bundle`] (a bundle's own `targets` — ADR-0030 gives a +/// [`Engine::world_for_bundle`] (a bundle's own `targets` — ADR-0037 gives a /// bundle target the exact same build/artifact/step shape). Returns /// `Ok(template_path)` (relative to the lesson/bundle root) when the request is /// buildable, or `Err(blocking_diagnostics)` when it is not: diff --git a/crates/cph-typst/src/manifest.rs b/crates/cph-typst/src/manifest.rs index db2f185..3eabf11 100644 --- a/crates/cph-typst/src/manifest.rs +++ b/crates/cph-typst/src/manifest.rs @@ -1,4 +1,4 @@ -//! Augmented-manifest construction (ADR-0011, outline shape per ADR-0029). +//! Augmented-manifest construction (ADR-0011, outline shape per ADR-0036). //! //! The template (`exports/.typ`) reads the manifest via //! `toml(sys.inputs.manifest)`, then for each **element** outline entry @@ -10,13 +10,13 @@ //! //! The ENGINE has filesystem access, so it closes that gap: it builds an //! **augmented manifest** = the lesson's `[info]` + the ordered `[[outline]]` -//! (ADR-0029's depth-first rendering order — elements interleaved with section +//! (ADR-0036's depth-first rendering order — elements interleaved with section //! headings at their DFS-open position). Each `[[outline]]` entry carries a //! `type` discriminator (`"element"` | `"section"`): //! //! - `type = "element"`: `kind`, `path`, and a per-part **`fields` array** //! listing the content fields whose `.typ` actually exists under the -//! lesson root (same contract as before ADR-0029). +//! lesson root (same contract as before ADR-0036). //! - `type = "section"`: `kind`, `title`, `depth`, `path` — a section heading; //! the template renders it without touching any content file. //! @@ -41,7 +41,7 @@ use cph_model::{Bundle, BundleLesson, Lesson, OutlineEntry}; /// /// The result is a self-contained TOML document the template's /// `toml(sys.inputs.manifest)` reads. It carries `[info]` (title + optional -/// author) and the ordered `[[outline]]` (ADR-0029's depth-first rendering +/// author) and the ordered `[[outline]]` (ADR-0036's depth-first rendering /// order), each entry typed `"element"` or `"section"` per the module docs. It /// does **not** reproduce `[project]` or `[targets.*]` — the template only /// consumes `info` and `outline`. @@ -50,7 +50,7 @@ pub fn build_augmented_manifest(lesson: &Lesson) -> String { doc.insert("info".to_string(), toml::Value::Table(info_table(lesson))); - // [[outline]] — ADR-0029's depth-first rendering order: elements + // [[outline]] — ADR-0036's depth-first rendering order: elements // interleaved with section headings at their DFS-open position. let outline: Vec = lesson .outline @@ -62,7 +62,7 @@ pub fn build_augmented_manifest(lesson: &Lesson) -> String { toml::to_string(&doc).expect("augmented manifest serializes") } -/// Build the augmented **bundle** manifest TOML source for `bundle` (ADR-0030). +/// Build the augmented **bundle** manifest TOML source for `bundle` (ADR-0037). /// /// The bundle template (`exports/.typ` under the `bundle.toml` root) /// reads it via `toml(sys.inputs.manifest)`. It carries `[info]` (the bundle's @@ -71,7 +71,7 @@ pub fn build_augmented_manifest(lesson: &Lesson) -> String { /// would assemble, except every outline entry's `path` is **prefixed with that /// lesson's own bundle-root-relative directory** (`BundleLesson::path`), since /// the bundle template's computed include paths resolve against the *bundle* -/// root, not each lesson's own root (ADR-0030: combination reads +/// root, not each lesson's own root (ADR-0037: combination reads /// already-authored lessons at export time; each lesson's `path` bookkeeping /// stays correct because the prefix is applied only here, in the manifest the /// template consumes — never inside a lesson's own authored content). @@ -147,7 +147,7 @@ fn info_table(lesson: &Lesson) -> toml::Table { } /// Build one `[[outline]]` entry's table for either variant of -/// [`OutlineEntry`]. `bundle_prefix`, when set (ADR-0030's bundle case), is +/// [`OutlineEntry`]. `bundle_prefix`, when set (ADR-0037's bundle case), is /// joined onto the emitted `path` so the bundle template's computed include /// resolves against the bundle root rather than the lesson's own root. fn outline_entry_table( diff --git a/crates/cph-typst/tests/bundle.rs b/crates/cph-typst/tests/bundle.rs index d0fd920..8dced65 100644 --- a/crates/cph-typst/tests/bundle.rs +++ b/crates/cph-typst/tests/bundle.rs @@ -1,4 +1,4 @@ -//! Integration tests for the bundle build path (ADR-0030): compiling a bundle +//! Integration tests for the bundle build path (ADR-0037): compiling a bundle //! target's template (`exports/.typ` under a `bundle.toml` root) as //! main, injecting the augmented **bundle** manifest, against the real //! `render/` package. @@ -32,7 +32,7 @@ fn load_bundle() -> cph_model::Bundle { /// PURE UNIT TEST (no fonts, no render package): the augmented bundle manifest /// carries the bundle's own `[info]` and an ordered `[[lessons]]`, each with -/// that member's own `info`/`target` and a `path`-prefixed outline (ADR-0030). +/// that member's own `info`/`target` and a `path`-prefixed outline (ADR-0037). #[test] fn augmented_bundle_manifest_prefixes_member_paths() { let bundle = load_bundle(); diff --git a/crates/cph-typst/tests/compile.rs b/crates/cph-typst/tests/compile.rs index bd5314d..739ad52 100644 --- a/crates/cph-typst/tests/compile.rs +++ b/crates/cph-typst/tests/compile.rs @@ -39,7 +39,7 @@ fn load_mini() -> cph_model::Lesson { } /// PURE UNIT TEST (no fonts, no render package): the augmented manifest carries -/// `[info]` and the ordered `[[outline]]` (ADR-0029) — elements (with a +/// `[info]` and the ordered `[[outline]]` (ADR-0036) — elements (with a /// per-element `fields` array of the content fields present on disk) /// interleaved with the section heading the mini fixture nests its two lemmas /// under. @@ -58,7 +58,7 @@ fn augmented_manifest_has_outline_with_section_and_fields() { .get("outline") .and_then(|p| p.as_array()) .expect("outline array present"); - // segment, section, lemma, lemma, example — 5 entries (ADR-0029: the + // segment, section, lemma, lemma, example — 5 entries (ADR-0036: the // section contributes a heading entry, not an element). assert_eq!(outline.len(), 5, "five outline entries:\n{src}"); diff --git a/crates/cph-typst/tests/fixtures/bundle/exports/merged.typ b/crates/cph-typst/tests/fixtures/bundle/exports/merged.typ index 5e6c00a..7e70557 100644 --- a/crates/cph-typst/tests/fixtures/bundle/exports/merged.typ +++ b/crates/cph-typst/tests/fixtures/bundle/exports/merged.typ @@ -1,4 +1,4 @@ -// DEFAULT BUNDLE TEMPLATE (ADR-0030, outline shape ADR-0029). +// DEFAULT BUNDLE TEMPLATE (ADR-0037, outline shape ADR-0036). // // Lives in a bundle at `/exports/.typ`, e.g. // `exports/merged.typ`. Compiled AS MAIN with the augmented BUNDLE manifest @@ -66,7 +66,7 @@ )) // Presentation: shared per-level heading numbering across the whole bundle, -// and the ADR-0030 recommended default of resetting auto-counters at each +// and the ADR-0037 recommended default of resetting auto-counters at each // lesson boundary (override `reset-counters: false` for continuous numbering). #render-bundle( info: info, diff --git a/crates/cph-typst/tests/fixtures/mini/exports/student.typ b/crates/cph-typst/tests/fixtures/mini/exports/student.typ index 14d091d..e0d5909 100644 --- a/crates/cph-typst/tests/fixtures/mini/exports/student.typ +++ b/crates/cph-typst/tests/fixtures/mini/exports/student.typ @@ -1,4 +1,4 @@ -// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // This is a *real, editable* file that lives in an engineering file at // `exports/student.typ`. The framework compiles it AS THE MAIN FILE with the @@ -16,7 +16,7 @@ // PACKAGE, not the engineering root. A `//.typ` written HERE // (this template lives under `--root`) resolves against `--root`. So the // template loads content and hands cph-render an already-assembled `outline` -// array (elements interleaved with section headings, ADR-0029). +// array (elements interleaved with section headings, ADR-0036). // // OPEN CONTRACT POINT — optional-content presence. typst has no "does this file // exist" primitive (a missing `include` is a hard compile error). So the @@ -43,7 +43,7 @@ // resolved against --root) and read scalar fields from /element.toml. // `part-fields` (from cph-render) is the single source of truth for // kind->fields. -// - a "section" entry (ADR-0029): pass its title/depth straight through — no +// - a "section" entry (ADR-0036): pass its title/depth straight through — no // content to load, it is a heading. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/crates/cph-typst/tests/fixtures/mini/exports/teacher.typ b/crates/cph-typst/tests/fixtures/mini/exports/teacher.typ index abc5b8a..4b64ced 100644 --- a/crates/cph-typst/tests/fixtures/mini/exports/teacher.typ +++ b/crates/cph-typst/tests/fixtures/mini/exports/teacher.typ @@ -1,4 +1,4 @@ -// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // Lives in an engineering file at `exports/teacher.typ`. Compiled AS MAIN with // the manifest injected: @@ -21,7 +21,7 @@ #let raw-outline = manifest.at("outline", default: ()) // Assemble each outline entry: an "element" entry includes its content fields -// and reads scalars from element.toml; a "section" entry (ADR-0029) passes +// and reads scalars from element.toml; a "section" entry (ADR-0036) passes // title/depth straight through as a heading, no content to load. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/docs/adr/0029-engineering-file-nested-manifest.md b/docs/adr/0036-engineering-file-nested-manifest.md similarity index 99% rename from docs/adr/0029-engineering-file-nested-manifest.md rename to docs/adr/0036-engineering-file-nested-manifest.md index 64c25ae..924166e 100644 --- a/docs/adr/0029-engineering-file-nested-manifest.md +++ b/docs/adr/0036-engineering-file-nested-manifest.md @@ -1,4 +1,4 @@ -# ADR 0029: Engineering-File Structure Is A Nested Outline Manifest +# ADR 0036: Engineering-File Structure Is A Nested Outline Manifest ## Status diff --git a/docs/adr/0030-batch-and-combined-export.md b/docs/adr/0037-batch-and-combined-export.md similarity index 99% rename from docs/adr/0030-batch-and-combined-export.md rename to docs/adr/0037-batch-and-combined-export.md index 1a7c8da..059d61c 100644 --- a/docs/adr/0030-batch-and-combined-export.md +++ b/docs/adr/0037-batch-and-combined-export.md @@ -1,4 +1,4 @@ -# ADR 0030: Batch & Combined Export +# ADR 0037: Batch & Combined Export ## Status diff --git a/examples/TH-141/exports/student.typ b/examples/TH-141/exports/student.typ index 14d091d..e0d5909 100644 --- a/examples/TH-141/exports/student.typ +++ b/examples/TH-141/exports/student.typ @@ -1,4 +1,4 @@ -// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // This is a *real, editable* file that lives in an engineering file at // `exports/student.typ`. The framework compiles it AS THE MAIN FILE with the @@ -16,7 +16,7 @@ // PACKAGE, not the engineering root. A `//.typ` written HERE // (this template lives under `--root`) resolves against `--root`. So the // template loads content and hands cph-render an already-assembled `outline` -// array (elements interleaved with section headings, ADR-0029). +// array (elements interleaved with section headings, ADR-0036). // // OPEN CONTRACT POINT — optional-content presence. typst has no "does this file // exist" primitive (a missing `include` is a hard compile error). So the @@ -43,7 +43,7 @@ // resolved against --root) and read scalar fields from /element.toml. // `part-fields` (from cph-render) is the single source of truth for // kind->fields. -// - a "section" entry (ADR-0029): pass its title/depth straight through — no +// - a "section" entry (ADR-0036): pass its title/depth straight through — no // content to load, it is a heading. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/examples/TH-141/exports/teacher.typ b/examples/TH-141/exports/teacher.typ index abc5b8a..4b64ced 100644 --- a/examples/TH-141/exports/teacher.typ +++ b/examples/TH-141/exports/teacher.typ @@ -1,4 +1,4 @@ -// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // Lives in an engineering file at `exports/teacher.typ`. Compiled AS MAIN with // the manifest injected: @@ -21,7 +21,7 @@ #let raw-outline = manifest.at("outline", default: ()) // Assemble each outline entry: an "element" entry includes its content fields -// and reads scalars from element.toml; a "section" entry (ADR-0029) passes +// and reads scalars from element.toml; a "section" entry (ADR-0036) passes // title/depth straight through as a heading, no content to load. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/render/examples/smoke-eng/exports/student.typ b/render/examples/smoke-eng/exports/student.typ index 14d091d..e0d5909 100644 --- a/render/examples/smoke-eng/exports/student.typ +++ b/render/examples/smoke-eng/exports/student.typ @@ -1,4 +1,4 @@ -// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // This is a *real, editable* file that lives in an engineering file at // `exports/student.typ`. The framework compiles it AS THE MAIN FILE with the @@ -16,7 +16,7 @@ // PACKAGE, not the engineering root. A `//.typ` written HERE // (this template lives under `--root`) resolves against `--root`. So the // template loads content and hands cph-render an already-assembled `outline` -// array (elements interleaved with section headings, ADR-0029). +// array (elements interleaved with section headings, ADR-0036). // // OPEN CONTRACT POINT — optional-content presence. typst has no "does this file // exist" primitive (a missing `include` is a hard compile error). So the @@ -43,7 +43,7 @@ // resolved against --root) and read scalar fields from /element.toml. // `part-fields` (from cph-render) is the single source of truth for // kind->fields. -// - a "section" entry (ADR-0029): pass its title/depth straight through — no +// - a "section" entry (ADR-0036): pass its title/depth straight through — no // content to load, it is a heading. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/render/examples/smoke-eng/exports/teacher.typ b/render/examples/smoke-eng/exports/teacher.typ index abc5b8a..4b64ced 100644 --- a/render/examples/smoke-eng/exports/teacher.typ +++ b/render/examples/smoke-eng/exports/teacher.typ @@ -1,4 +1,4 @@ -// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // Lives in an engineering file at `exports/teacher.typ`. Compiled AS MAIN with // the manifest injected: @@ -21,7 +21,7 @@ #let raw-outline = manifest.at("outline", default: ()) // Assemble each outline entry: an "element" entry includes its content fields -// and reads scalars from element.toml; a "section" entry (ADR-0029) passes +// and reads scalars from element.toml; a "section" entry (ADR-0036) passes // title/depth straight through as a heading, no content to load. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/render/examples/smoke-eng/manifest.toml b/render/examples/smoke-eng/manifest.toml index 876632f..d4c29d0 100644 --- a/render/examples/smoke-eng/manifest.toml +++ b/render/examples/smoke-eng/manifest.toml @@ -1,5 +1,5 @@ # Throwaway smoke engineering file for the cph-render template round -# (ADR-0011, outline shape ADR-0029). Exercises all 4 kinds + a section heading +# (ADR-0011, outline shape ADR-0036). Exercises all 4 kinds + a section heading # + nested headings (per-level numbering) + an example with a `source` scalar # and one without + a lemma with proof and one without. # @@ -27,7 +27,7 @@ kind = "segment" path = "segments/向量数量积" fields = ["textbook"] -# A section heading (ADR-0029): opens depth-1, groups the two examples that +# A section heading (ADR-0036): opens depth-1, groups the two examples that # follow. Contributes no element to the sequence. [[outline]] type = "section" diff --git a/render/lib.typ b/render/lib.typ index 80b4ab4..4e05e31 100644 --- a/render/lib.typ +++ b/render/lib.typ @@ -2,7 +2,7 @@ // // PUBLIC ENTRY: `render-lesson(info, target, outline, heading-numbering)`. // -// MODEL (ADR-0011, outline shape per ADR-0029). A build compiles a *template +// MODEL (ADR-0011, outline shape per ADR-0036). A build compiles a *template // file* (e.g. `exports/student.typ`) as the typst main, with the manifest // injected via `--input manifest=`. The template reads the manifest, // loads each **element** entry's content, and calls `render-lesson` here. @@ -25,7 +25,7 @@ // - an ELEMENT: `entry-type: "element"`, `kind` plus that kind's // content/scalar fields. Content field VALUES are already-evaluated typst // content (the template produced them via `include`). -// - a SECTION (ADR-0029): `entry-type: "section"`, `title` (heading text), +// - a SECTION (ADR-0036): `entry-type: "section"`, `title` (heading text), // `depth` (1-based heading level). Contributes no element; it is a // heading at its depth-first-open position in the outline. // @@ -109,14 +109,14 @@ } } -/// Render one section-heading outline entry (ADR-0029): `entry.title` at +/// Render one section-heading outline entry (ADR-0036): `entry.title` at /// `entry.depth` (1-based heading level). #let _render-section(entry) = { heading(level: entry.at("depth", default: 1))[#entry.at("title", default: "")] } /// Render one outline entry: an element dispatches on `kind` via -/// [`_render-part`]; a section (ADR-0029) renders its heading and contributes +/// [`_render-part`]; a section (ADR-0036) renders its heading and contributes /// no element. #let _render-entry(entry, flags) = { if entry.at("entry-type", default: "element") == "section" { @@ -132,7 +132,7 @@ /// Templates typically pass `manifest.at("info", default: (:))`. /// - `target`: string. MVP: "student" | "teacher". Unknown => conservative. /// Each template hardcodes its own target (student.typ => "student"). -/// - `outline`: ordered array of outline-entry dicts (ADR-0029), ALREADY +/// - `outline`: ordered array of outline-entry dicts (ADR-0036), ALREADY /// ASSEMBLED by the template (element content fields included, /// scalars read; section entries carry `title`/`depth`). See file /// header for the entry shapes. @@ -183,7 +183,7 @@ entry } -/// THE BUNDLE ENTRY POINT (ADR-0030) — called by a bundle target's template +/// THE BUNDLE ENTRY POINT (ADR-0037) — called by a bundle target's template /// (`exports/.typ` under a `bundle.toml` root) to assemble several /// already-loaded lessons into one combined document. /// @@ -195,14 +195,14 @@ /// bundle (ADR-0011 presentation-in-template stance; same knob as /// `render-lesson`). /// - `reset-counters`: whether `example`/`lemma` auto-counters reset at each -/// lesson boundary. **Default `true`** (ADR-0030's recommended default: a +/// lesson boundary. **Default `true`** (ADR-0037's recommended default: a /// lesson's internal "例题3" means that lesson's 例题3, so cross-lesson /// continuation would silently break author references). Pass `false` for /// a genuine "全书连续编号" 合集. /// /// Each lesson's own outline is rendered under a depth-1 heading naming that /// lesson (`lesson.info.title`); the lesson's own section headings shift one -/// level deeper (ADR-0029's per-lesson depths are relative to that lesson, so +/// level deeper (ADR-0036's per-lesson depths are relative to that lesson, so /// nesting under the lesson-title heading keeps the outline↔structure /// correspondence meaningful in the combined document). `@label`/`@ref` /// cross-references stay global across the whole compiled document (typst's diff --git a/render/templates/README.md b/render/templates/README.md index 510c524..79e7f57 100644 --- a/render/templates/README.md +++ b/render/templates/README.md @@ -1,4 +1,4 @@ -# Default export templates (ADR-0011, outline shape ADR-0029) +# Default export templates (ADR-0011, outline shape ADR-0036) `student.typ` / `teacher.typ` are the **framework default templates**. In a real engineering file they live at `exports/student.typ` / `exports/teacher.typ`; the @@ -9,7 +9,7 @@ offline smoke test below. Each template: 1. reads the injected manifest: `toml(sys.inputs.manifest)`; -2. loops `manifest.outline` (ADR-0029's depth-first rendering order — elements +2. loops `manifest.outline` (ADR-0036's depth-first rendering order — elements interleaved with section headings at their DFS-open position). For an `type = "element"` entry: `include`-ing each content field via a computed **root-relative absolute** path `//.typ`, and reading @@ -46,7 +46,7 @@ content and hands `render-lesson` an already-assembled `outline` array; disk. *(OPEN: the exact manifest shape for this is for the Rust/manifest contract to pin.)* - **Sections carry no content fields.** A `type = "section"` outline entry - (ADR-0029) has only `kind`/`title`/`depth`/`path`; the template passes it + (ADR-0036) has only `kind`/`title`/`depth`/`path`; the template passes it through untouched — no `include`, no `element.toml` read. ## Offline smoke test diff --git a/render/templates/merged.typ b/render/templates/merged.typ index 5e6c00a..7e70557 100644 --- a/render/templates/merged.typ +++ b/render/templates/merged.typ @@ -1,4 +1,4 @@ -// DEFAULT BUNDLE TEMPLATE (ADR-0030, outline shape ADR-0029). +// DEFAULT BUNDLE TEMPLATE (ADR-0037, outline shape ADR-0036). // // Lives in a bundle at `/exports/.typ`, e.g. // `exports/merged.typ`. Compiled AS MAIN with the augmented BUNDLE manifest @@ -66,7 +66,7 @@ )) // Presentation: shared per-level heading numbering across the whole bundle, -// and the ADR-0030 recommended default of resetting auto-counters at each +// and the ADR-0037 recommended default of resetting auto-counters at each // lesson boundary (override `reset-counters: false` for continuous numbering). #render-bundle( info: info, diff --git a/render/templates/student.typ b/render/templates/student.typ index 14d091d..e0d5909 100644 --- a/render/templates/student.typ +++ b/render/templates/student.typ @@ -1,4 +1,4 @@ -// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT STUDENT TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // This is a *real, editable* file that lives in an engineering file at // `exports/student.typ`. The framework compiles it AS THE MAIN FILE with the @@ -16,7 +16,7 @@ // PACKAGE, not the engineering root. A `//.typ` written HERE // (this template lives under `--root`) resolves against `--root`. So the // template loads content and hands cph-render an already-assembled `outline` -// array (elements interleaved with section headings, ADR-0029). +// array (elements interleaved with section headings, ADR-0036). // // OPEN CONTRACT POINT — optional-content presence. typst has no "does this file // exist" primitive (a missing `include` is a hard compile error). So the @@ -43,7 +43,7 @@ // resolved against --root) and read scalar fields from /element.toml. // `part-fields` (from cph-render) is the single source of truth for // kind->fields. -// - a "section" entry (ADR-0029): pass its title/depth straight through — no +// - a "section" entry (ADR-0036): pass its title/depth straight through — no // content to load, it is a heading. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" { diff --git a/render/templates/teacher.typ b/render/templates/teacher.typ index abc5b8a..4b64ced 100644 --- a/render/templates/teacher.typ +++ b/render/templates/teacher.typ @@ -1,4 +1,4 @@ -// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0029). +// DEFAULT TEACHER TEMPLATE (framework default; ADR-0011, outline shape ADR-0036). // // Lives in an engineering file at `exports/teacher.typ`. Compiled AS MAIN with // the manifest injected: @@ -21,7 +21,7 @@ #let raw-outline = manifest.at("outline", default: ()) // Assemble each outline entry: an "element" entry includes its content fields -// and reads scalars from element.toml; a "section" entry (ADR-0029) passes +// and reads scalars from element.toml; a "section" entry (ADR-0036) passes // title/depth straight through as a heading, no content to load. #let outline = raw-outline.map(raw => { if raw.at("type", default: "element") == "section" {