Files
curriculum-project-hub/docs/adr/0008-engineering-file-layout-conventions.md
T
sjfhsjfh 9b37914bfb docs(adr): add ADR-0011 (export builds = typed steps over templates), correct ADR-0008
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>
2026-06-22 09:44:32 +08:00

134 lines
6.8 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 at build time by the export template, not hand-authored. (Originally
this said "the generated driver"; ADR-0011 supersedes that — there is no generated
driver, an export template reads the manifest and includes each part's content.)
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 per-target **export template** (a real, editable `.typ` file, e.g.
`exports/student.typ`) reads the manifest and renders the lesson; it is the
build entrypoint, not the canonical lesson. (This clause originally described a
*generated* driver with static `#import`s, on a since-corrected belief that
typst forbids runtime-computed include paths — see ADR-0011's *Correction*.
Include/import paths may be computed, so no driver is generated.)
- 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.