Skip to content

Authoring · 11 of 17

Migrating from YAML

In this chapter: the bridge is crossed, the Primmel packages are the single source of truth and the YAML trees are generated artifacts. What that means for an author day to day, the regeneration discipline that keeps every derived tree honest, the gates that prove it, and what remains of the post-release program. Read this before touching data/<rec>/ by hand, for domain content, you almost never should.

Status markers: ● exists in the running system · ◐ partial · ○ planned. Paths are relative to the oimlsmart/smart repository.


11.1 Two trees, one direction

Every Recommendation in this volume exists twice in the repository:

  • primmel-packages/oiml-<rec>/, the PRL package. ● This is the source of truth. All domain content, subjects, requirements, tests, forms, processes, roles, state machines, quantity registers, edition manifests, is authored here in the Primmel language (Volume I), with clause-URN provenance on every element, exactly as chapters 2–7 describe (the provenance blocks are PRL source { doc clause } constructs).
  • data/<rec>/, the YAML tree. ● Fully generated for its package-covered files, cd browser && npm run gen:data regenerates it from the package (browser/scripts/regenerate-data.ts, wired into npm run build / validate / dev). Never edit a generated file by hand: each generated tree carries a machine-written GENERATED.md listing the exact generated set, and the drift guard (npm run test:ssot) fails on any hand-edit, the next regeneration silently wins anyway.

The hand-authored remainder of a data tree is app configuration, not Recommendation content: the standard.yaml / layer.yaml manifests, workflow / gateway / approval / certificate-template config, evaluation dimensions and profiles, navigation, obligation, value-types, parts, redirects, sample data, notes, the sources-prd/ extracts and coverage declarations, and the ANR / documents / examples payloads. Those stay YAML-authored by design (§11.6, the app-config question).

Alongside the four rec packages (oiml-r60, oiml-r91, oiml-r129, oiml-r144) the release ships the shared packages the recs compose: oiml-smart-core (the Tier-1 content every rec shares), the seven oiml-smart-module-* packages (chapter 7, §7.5), the oiml-cs scheme reference package (Volume IV), and the four ISO/IEC CASCO foundation packages, seventeen packages in all. Every layer package regenerates its own YAML tree (data/core/, data/oiml-cs/, data/iso-iec-*/, data/modules/*) by the same discipline; a rec package never duplicates content it consumes from a layer, its layers.prl include record pulls the layer’s .prl files in verbatim.

The package manifest is the package’s identity card (primmel-packages/<pkg>/package.primmel): id, kind core|module|rec, title, version, editions, baseUrn, the uses composition list, provides/requires capability tokens, and the edition-lifecycle facets (supersedes, validity, status) that Volume I, chapter 13 specifies. primmel check C77–C80 validates them. The manifest and its sibling projection files (.primmel-allowlist.prl, sources-prd/*.json, value-types.prl, layers.prl) are the residual YAML→PRL projections of the hand-authored app config, generated by the authoring aid, kept byte-honest by the drift guard (§11.3).

11.2 What the v3 model adds, the author’s view

The v3 kernel is described in Volume I; this is the checklist of what changes for a Recommendation author moving from the YAML era:

  • The subject is first-class (Volume I, ch. 2). The instrument model is a subject with an IS/HAS/DOES anatomy: identity/design aspects (is), exhibited aspects (has), and processes (does). Classification dimensions, attribute definitions (origin/scope/ category), capability mixins, behaviors, and condition sets hang off the same construct the round-trip maps to model/*.yaml.
  • Facets, not conventions. Three classification facets authored in the YAML era as plain keys are machine-checked facets in the package: activity_kind (the ISO/IEC 17000 activity-archetype register), segregation: (machine-checkable non-involvement, 17065), and scheme_type: (the oiml-cs layer’s type_1a self-classification, 17067), Volume IV, chapters 1–2.
  • Quantities are typed (Volume I, ch. 6). value_type and units resolve against the package’s quantity register; primmel check C32–C34 enforces INV-1 (no bare numbers) and quantity-kind coherence.
  • Promises and artifacts (Volume I, ch. 2; tasks 08/09). Manufacturer claims on characteristics and artifact definitions have kernel constructs (is.promises, artifact_definition).
  • Operational state (task 07). The instrument’s off/warming/ready/measuring/fault machine is a state_machine of kind operational, family-separated from the lifecycle machines (check C38); tests gate on states via preconditions.
  • Set-valued dimensions (task 11). R 144’s multi-component gas mixtures classify with set values, not single tokens.
  • Packages compose with uses (Volume I, ch. 8; task 05). A rec consumes core + modules + the CASCO/CS reference packages topologically, with no-redefine semantics: reference upstream ids, never restate them (check C27–C31).
  • Editions are package metadata (Volume I, ch. 13; task 28). supersedes/validity/status live on the manifest, never in subject models, and primmel diff gives the structural model diff that powers the release changelog (§11.5).

11.3 The regeneration discipline

One content, one source, several derived trees, and machine-enforced proof that none of them drifts. The pipeline and its gates:

 primmel-packages/oiml-<rec>/ (PRL — the source of truth, edited by you)
      │  scripts/regenerate-data.ts  (PRL → YAML, the primary generator)
      ▼
 data/<rec>/ (YAML — GENERATED, committed; never hand-edited)
      │  the standards registry composes layers + rec
      ▼
 the app builds and runs from the YAML trees
      │
      │  SMART_STANDARDS_SOURCE=primmel (the direct path):
      │  scripts/build-primmel-cache.ts <rec>
      ▼
 browser/.cache/primmel/oiml-<rec>/ (the build-consumable tree)

Three disciplines make this safe:

  1. The codecs are construct-symmetric. browser/build/yaml-to-prl.ts and browser/build/prl-to-yaml.ts map every shared construct both ways; prl-round-trip.test.ts proves the YAML → PRL → YAML′ fixed point over the whole tree, and prl-fidelity.test.ts proves a hermetic 3-package build. Both regeneration paths share the .prl file-layout doctrine (browser/build/prl-layout.ts): one YAML twin per declaring .prl file, so the file slugs the linker resolves against survive the round trip.

  2. Equivalence and idempotence are gates. npm run test:round-trip (the TODO.refactor/16 gate) builds the app from the YAML tree and from the packages and asserts the generated data is identical, then mutates one requirement inside the package and asserts the mutation propagates: the PRL tree is a real source, not a shadow. npm run test:ssot (the drift guard) proves the regeneration itself is clean in BOTH directions: every generated YAML file byte-identical to a fresh regeneration from the committed packages, and every package byte-identical to a rebuild from the hand-authored app config (the authoring aid). A hand-edit to a generated file, or an app-config edit never migrated into the packages, fails loudly with the file named.

  3. The exclusions ship in the package, documented. A small, named set of content has no PRL construct yet. It is not silently dropped, it ships inside the package as YAML payloads under payload/, extracted by the authoring aid and composed back into the generated trees by the generator and the cache build (drift fails loudly):

    • payload/attributes-pair-list.yaml, the pair_list blocks (R 144’s interfering_components);
    • payload/symbols-formula-notes.yaml, R 60-2’s annex formula notes;
    • payload/formulas-variants.yaml, engine-facing formula variants that share a calculations.yaml id (the calculation construct holds one expression per id);
    • payload/conditions-common-test.yaml, the common_test_conditions register.

    Separate from the payloads, three more surfaces stay outside the PRL content by design: the app config listed in §11.1 (hand-authored YAML); the certificate print projections (model/promises.yaml’s certificate: block) and the typed slot registries (model/identity.yaml, model/aspects.yaml), app-facing surfaces with no construct yet; and the YAML-only layer content (the OIML-CS framework model, abstract-process registers, activity archetypes, functional approach, scheme types, the UNSHIPPED_PROVIDES doctrine of AGENTS.d/10: a provides token is advertised only when its content actually ships as PRL).

    Each exclusion is a codec or app-config gap with an address, not a semantic difference: the round-trip gate proves the app’s generated data identical from either source.

A note on rec overlays: a rec file that overlays a layer file (entities/workflow.yaml, the skeleton-materialized forms of R 129, R 144’s cgm-point subform) regenerates as the minimal merge-delta over the layers-only composition (browser/build/overlay-delta.ts), with the fixpoint merge(layers, delta) ≡ package content asserted per file. The hand-authored delta discipline of the YAML era (“never complete an overlay by hand”) is now mechanical.

11.4 The gates that hold the line

Run these constantly; the release runbook runs them all from the packages.

Gate Command What it proves
Package lint primmel check --strict --audit <pkg> 17 packages, 0 errors; the KNOWN/STALE allowlist discipline (a stale allowlist entry is an error, the list only shrinks); coverage budgets enforced (C55/C72)
Check-all browser/src/__tests__/prl-check-all.test.ts the 6 composition-relevant packages at all four lint levels, all 17 at default and --strict --audit, built hermetically
SSOT drift guard cd browser && npm run test:ssot data trees ≡ regeneration from the packages; packages ≡ rebuild from the app config, byte-clean both directions
Round-trip cd browser && npm run test:round-trip YAML-source ≡ primmel-source build output + PRL-mutation detectability
App gates npx vue-tsc --noEmit, npx astro check, npx vitest run, npm run build, npm run validate, npm run test:e2e the platform itself (AGENTS.md command gates)
From-packages proof cd browser && npm run test:from-packages the release gate: every gate above run from the committed packages only, lint, the drift guard, cache regeneration, round-trip, build, type checks, tests, validation, coverage reports, the headless e2e suite, and a file-drop mutation proof

The last row is the acceptance contract made executable: a clean checkout builds the entire system from the packages, every gate green, in about four minutes.

11.5 What changes in your daily workflow

Primmel-native authoring (the default):

  1. Author in primmel-packages/oiml-<rec>/ per chapters 2–7, with source { doc clause } provenance on new content.
  2. Regenerate the YAML trees: cd browser && npm run gen:data (wired into npm run build / validate / dev, so you usually build). Commit the package change AND the regenerated data trees together , the two trees move in one commit.
  3. Run the gates: npm run test:ssot, bin/check (vue-tsc + astro check + round-trip), then npx vitest run, npm run validate.
  4. Before a release: npm run test:from-packages, the full proof from the packages.

The YAML-draft aid (for bulk drafting or the YAML-fluent):

  1. Draft the content in data/<rec>/ YAML, the schemas and your editor tooling still work there.
  2. Migrate the draft into the package: cd browser && npx tsx scripts/build-prl-package.ts <rec>, the authoring aid emits the .prl constructs and extracts any payload-class exclusions (pair_list, formula notes, formula variants, the common-test register) into payload/*.yaml.
  3. Review the .prl diff, regenerate (npm run gen:data), gates. From here the package is the only place the content is edited.

Reading a diff for review. The changelog of any change is machine-computable: primmel diff <old-package-dir> <new-package-dir> (a git worktree or git archive gives you the old tree). The v3 release’s own changelog (docs/releases/v3-changelog.md, regenerated by scripts/v3-changelog.ts) is built exactly this way from the pinned YAML-era baseline.

What you never do: edit a GENERATED.md-listed file by hand; hand-tune browser/.cache/** (100 % derived); add content to a generated file “for the app alone” (author it in the package, or as a package payload when no construct exists, the payload compose fails loudly on drift, that is the point).

11.6 What remains of the post-release program

The flip landed in three of the v2 plan’s phases:

  • Phase A, packages able to be authoritative (●, the v3 release): the from-packages proof stands on the committed packages alone.
  • Phase B, YAML becomes generated output (●, this flip): the data trees regenerate from the packages byte-clean; the drift guard holds both directions; authoring is Primmel-native with the YAML-draft aid for migration. What remains inside phase B:
    • codec homes for the §11.3 payload classes, pair_list, formula_notes, the formula engine variants, common_test_conditions, plus the process/gateway/approval inverse mappings, the promises certificate projection, and the identity/aspect slot registries, each is a grammar/codec task; when one lands its payload file retires;
    • the textual include transport retired in favor of uses composition in the load path (AGENTS.d/10: the two are never mixed in one load today, the rec packages include layer files verbatim);
    • the app-config question answered in full, how much of workflow/certificate/navigation YAML is model content (gains a construct) vs platform configuration (stays YAML by design).
  • Phase C, new content authored Primmel-native only (◐, de facto current for the four recs): new Recommendations author in .prl from the start; the YAML tree is what the generated TypeScript is: derived, committed, never edited.

The rule of this chapter: edit Primmel, regenerate, let the gates prove every derived tree honest.