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

6.4 KiB
Raw Blame History

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: segmenttextbook.typ; exampleproblem.typ, solution.typ; lemmastmt.typ, proof.typ; sopsop.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 #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).
  • 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.