Files
curriculum-project-hub/docs/adr/0008-engineering-file-layout-conventions.md
T
sjfhsjfh 8599f472c0 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>
2026-06-22 01:18:48 +08:00

129 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.