forked from EduCraft/curriculum-project-hub
docs(adr): add ADR-0008 — declarative manifest + on-disk layout conventions
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>
This commit is contained in:
@@ -0,0 +1,128 @@
|
|||||||
|
# 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 `#import`s 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:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[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):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
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` + each
|
||||||
|
`element.toml` without evaluating any typst — order and membership are plain
|
||||||
|
data, diffable and greppable.
|
||||||
|
- A typst driver entrypoint (static `#import`s + 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).
|
||||||
|
- `path` being a real relative folder makes "the lesson references element X"
|
||||||
|
inspectable on disk; a dangling `path` is 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 in
|
||||||
|
`manifest.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 outside `parts`. 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.
|
||||||
Reference in New Issue
Block a user