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>
6.4 KiB
ADR 0008: Engineering-File Layout — Declarative Manifest + Element Descriptors
Status
Accepted — for the layout conventions below. Some sub-decisions are deferred; see Open Questions / Deferred.
Discharges the layout question ADR-0007 left open. ADR-0007 fixed that the
engineering file is a real directory tree but explicitly deferred the concrete
layout: the manifest (name, location, contents), how element instances are
arranged and how their order (ADR-0005's Lesson is an ordered sequence) is
encoded, and how rich-content .typ files are named. This ADR fixes those.
Context
Two real sample engineering files exist (TH-141, TH-144). They encode the lesson
as a typst file main.typ containing #imports of each element's main.typ
plus a #let parts = (("segment", 模块), ("lemma", 模块), …) ordered tuple list.
Each element is a folder under a per-kind directory (segments/, examples/,
lemmas/, sop/); its fields are typst source files (textbook.typ,
problem.typ/solution.typ, stmt.typ/proof.typ, sop.typ) wired up by the
element's own main.typ via #let f = include "f.typ".
That embryo has one property in tension with the contract: the ordering
manifest is itself a typst script. ADR-0005 rejected typst as the
definition/scripting language (its typing is too weak), and ADR-0006 moved the
kind schema off typst onto declarative JSON Schema. A checker that had to parse
typst to recover the lesson's element order would re-introduce exactly the
dependency those ADRs removed: the order — a first-class part of the Lesson
contract — would be locked inside an evaluated typst program.
Decision
The lesson manifest is declarative, not a typst script
The lesson's order and membership live in a declarative manifest.toml at
the engineering-file root, read directly by the checker. TOML is chosen for
consistency with the samples' existing project.toml / info.toml /
meta.toml. It subsumes project.toml and info.toml. Shape:
[project]
id = "…" # stable project id
name = "…" # folder/display name
[info] # passed through to render targets verbatim
title = "…"
author = "…"
[[parts]] # ORDER OF THIS ARRAY IS THE LESSON ORDER (ADR-0005)
kind = "segment" # one of the known kinds (ADR-0006 / stdlib set)
path = "segments/开场对照导言" # element folder, relative to root
# … one [[parts]] block per element, in order …
[targets.student] # which export targets exist for this lesson
[targets.teacher]
The checker reads parts for order and membership. It must not parse typst
to recover ordering — the declarative array is the single source of truth. The
sample's main.typ #let parts is replaced by this; any typst entrypoint
that imports elements in order is a generated build artifact derived from the
manifest (see ADR-0007's note that the on-disk encoding of Lesson is separate
from "it's a tree"), never the canonical order.
An element folder is self-describing via element.toml
Each element folder carries an element.toml declaring its kind and its
scalar fields (per the kind's JSON Schema, ADR-0006):
kind = "example"
source = "41 届物理竞赛复赛第三大题(1)" # a scalar field of the example kind
kind is explicit in the descriptor (not inferred from the parent directory
name) so a folder is self-describing and the segments/-vs-lemmas/ directory
grouping is a human convenience, not load-bearing for the checker.
content fields are convention-named .typ siblings
A kind's schema (ADR-0006) marks some fields as the content extension type.
For each such field F, its value is the typst source file F.typ in the
element folder. The schema — not element.toml, not a per-element main.typ —
is the source of truth for which .typ files must exist. Concretely for the
MVP kind set: segment ⇒ textbook.typ; example ⇒ problem.typ,
solution.typ; lemma ⇒ stmt.typ, proof.typ; sop ⇒ sop.typ. This
drops the samples' per-element main.typ (#let textbook = include …):
that field-to-file wiring is now implied by the schema + naming convention and
materialized by the generated driver, not hand-authored.
A content field's denotation remains ADR-0006's: the module body content
of F.typ, not its #let exports. The file sits at a real relative path
(ADR-0007), so its spans resolve and its relative imports anchor within the tree
(ADR-0006 import boundary).
Consequences
- The checker recovers the full ordered lesson from
manifest.toml+ eachelement.tomlwithout evaluating any typst — order and membership are plain data, diffable and greppable. - A typst driver entrypoint (static
#imports + the render call) becomes a generated artifact the renderer produces from the manifest per target; it is never the canonical lesson and is not hand-edited. - Element folders lose their per-element
main.typ;element.toml+ the schema's content-file convention replace it. Migrating the samples is a mechanical rewrite (the migration is part of the MVP). pathbeing a real relative folder makes "the lesson references element X" inspectable on disk; a danglingpathis a structural error the checker can point at.
Open Questions / Deferred
- Per-file render override. ADR-0005 says the (kind × target) render matrix
lives in the engineering file with framework defaults the file may
override. This ADR's
[targets.*]only declares which targets exist; the MVP keeps the render rules entirely in the framework render layer with no per-file override. Whether/how a lesson overrides a render rule (a block inmanifest.toml? a per-target file?) is deferred until a real override need appears. This is a known, surfaced gap against ADR-0005, not an oversight. - Question bank (题库) on disk. TH-144 has a
题目/tree of problem/answer pairs outsideparts. Its layout and its element relationship (ADR-0005 Deferred: reference vs inline) stay open; out of MVP scope. - Manifest richness. Per-part metadata, grouping/sectioning (TH-144's A/B/C structure is only in folder names today), and target-specific options on a part are not modeled here; add when needed rather than guessing now.
- stdlib kind set & schema dialect remain as ADR-0006 left them; this ADR only fixes the on-disk arrangement, not the schemas themselves.