Files
curriculum-project-hub/docs/adr/0007-engineering-file-as-directory-tree.md
sjfhsjfh f1dce07789 feat(spec): build out System layer and element schema model
Picture the whole framework, then pin the element-kind language layer.

System platform layer — rebuild the semantics likec4 can't draw (ADR-0001..0004):
- Prelude: opaque Identifiers carrier (ProjectId/RunId/SessionId/Principal)
- System/Run: RunState + Terminal subset (set completeness left OPEN, no invented pending)
- System/Lock: lock owner=run (typed), LockTable exclusivity, WellFormed invariant
  (a lock holder must be a non-terminal run — couples Lock and Run)
- System/Permission: read<edit<manage role lattice, capability derivation,
  can_mono monotonicity theorem; force-release sits outside the lattice (admin-only)
- System/Audit: intentionally thin (mostly plumbing, OPEN)

Courseware product layer — split the single Lesson file into focused modules and
fix doc tautology: Primitives / Element / Lesson / Render / Diagnostic, plus
QuestionBank and Course skeletons (core relations OPEN, not invented).
Diagnostic upgrades WarnsIgnored into a severity-tagged checker rule.

Element schema + rich-content model (ADR-0006, ADR-0007):
- docs/adr/0006: kind schema is declarative JSON Schema; field types are built-in
  scalars plus a `content` extension; a `content` value is typst source taken as
  module body; rich content must carry a VirtualPath (else click-to-jump and
  relative import break — verified against typst source); import boundary =
  within the engineering file + @package
- docs/adr/0007: on-disk form is a real directory tree (agent/grep friendly);
  VirtualPath = real relative path; layout conventions deferred
- spec/Courseware/RichContent: prose anchor for rich content (opaque VPath,
  RichContentRef); ElementData kept abstract — JSON/typst internals are
  implementation detail, not contract

lake build green (18 jobs), no sorry, toolchain v4.31.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:31:14 +08:00

3.7 KiB

ADR 0007: The Engineering File Is A Directory Tree On Disk

Status

Accepted — for the on-disk decision below. The concrete layout conventions are deliberately left open; see Open Questions / Deferred.

Grounds the abstract premise ADR-0006 relied on. ADR-0006 established that rich content needs a virtual path and that the engineering file therefore constitutes a path-structured virtual file system — but it deliberately did not say what that path structure is physically. This ADR fixes that: it is a real directory tree on a real file system.

Context

ADR-0006 showed that a rich-content (content) value cannot be free-floating Typst source: it must be a first-class file with a virtual path so that click-to-jump (spans) and relative imports work. That leaves a choice for the physical form of the engineering file: a packed/opaque archive, a database blob, or a plain directory tree.

The decisive constraint is who edits the engineering file: a coding agent (Claude) collaboratively with teachers (ADR-0005's premise), and ordinary tools. An agent works best when it can read and modify files directly and use existing tooling (grep, find, diff, version control) over plain files in plain directories. A packed or database form would force a custom access layer for every such operation.

The developer's earlier prototype already had this shape implicitly — segments/, examples/, lemmas/ directories with per-item files — which is the directory tree in embryo, not a crude stand-in.

Decision

The on-disk form of an engineering file is a directory tree on a real file system.

  • A content field's virtual path (ADR-0006) is a real relative path within that directory tree. The compiler's World maps virtual paths to the actual files in the tree; no synthetic/in-memory file system is needed.
  • Relative imports inside rich content resolve relative to the importing file's real position in the tree — which is exactly the import boundary ADR-0006 pinned ("within the engineering file, plus @package"). This ADR gives that boundary its physical basis: "within the engineering file" = "within this directory tree".

This keeps the engineering file legible to agents and to off-the-shelf tooling, and makes the relationship between content fields and their sources directly inspectable on disk.

Consequences

  • The engineering file is a directory of plain text/Typst files: directly greppable, diffable, version-controllable, and editable by an agent without a bespoke access layer.
  • World is a thin mapping from the directory tree; virtual path = relative path removes any need to invent or maintain a separate virtual file system.
  • The engineering file remains self-contained on disk: its import closure is the tree itself plus published packages (ADR-0006), so it can be moved, copied, or archived as a directory without dangling references to sibling lessons.
  • This is the physical representation of ADR-0005's abstract Lesson (an ordered sequence of typed elements); how that ordered structure is encoded into the directory is a separate question (below), not implied by "it's a tree".

Open Questions / Deferred

  • Layout conventions — the concrete directory layout is not fixed here. Specifically deferred: the manifest file (name, location, what it holds); how element instances are arranged in the tree and how their order (ADR-0005's Lesson is an ordered sequence) is encoded; naming/organization of rich-content .typ files. This is the on-disk encoding of ADR-0005's Lesson and warrants its own ADR — kept separate so this one fixes only "it is a directory tree", not "how the tree is arranged".