Logo FS-Skia-UI

Single-Source Generation

Several files in this repository look like policy but are not: they are generated views of a single canonical source. validation.contract.yml is rendered from the compiled routing rules; the .claude/skills/** tree is rendered from the canonical .agents/skills/** tree; docs/evidence-formats.md, docs/skillist-reference.md, the typed controls catalog, the design-token module, and the generated API-surface docs are all derived from upstream sources. None of them is hand-synced. A single FAKE target — RefreshSurfaceBaselines — regenerates every one of them, and a set of currency gates fail the build if a committed view drifts from what its source would regenerate. This page explains the pattern, the concrete generated artifacts, the regeneration entry point, and why nothing is kept in sync by hand. For the routing rules that drive the contract view, see routing and gates; for the evidence model, see evidence and audit.

The pattern: one source, a rendered view, a currency check

Every generated artifact follows the same three-part shape:

canonical source  ──render──▶  generated view  ──currency check──▶  PASS / FAIL

This is stronger than two-way drift comparison between peer files. There is only one source of truth; the view is a function of it. You edit the source and regenerate — you never edit the view.

validation.contract.yml is generated from Routing.fs

The authoritative routing policy — tiers, path globs, required gates, expected artifacts, failure owners — lives in the compiled F# module build/Governance/Routing.fs. validation.contract.yml at the repository root is a YAML rendering of those rules, produced by ContractView.render so that consumers which scan a contract file (and the compatibility AgentValidation selector) see the same rules the typed selector enforces.

The generated file says so in its own header:

# GENERATED from build/Governance/Routing.fs (feature 042). Do not hand-edit;
# regenerate via ./fake.sh build -t RefreshSurfaceBaselines. The compiled
# Routing module is the single source of truth for tiers and routing rules.

ContractView.render walks the typed Routing.rules and emits each rule's id, tier, paths, required_gates, expected_artifacts, timeout_class, and failure_owner — and because it renders the same Paths list that the typed Matches predicate is derived from, the rendered paths: view and the compiled matcher cannot diverge.

Currency is enforced by TargetMetadataDrift. That gate reads the committed validation.contract.yml, calls ContractView.currencyDrift against the live Routing.rules, and fails when the on-disk file does not match a fresh render — with the diagnostic "validation.contract.yml is stale — regenerate from Routing.fs via ./fake.sh build -t RefreshSurfaceBaselines". A missing file is also a failure. The comparison normalises line endings and trailing whitespace, so the only way to satisfy it is to regenerate. The gate's pure currency computation lives in ContractView; only the file read happens at the interpreter edge.

The .claude skill tree is generated from .agents

Skills exist in two trees. The canonical, FS-authored source is .agents/skills/**; the .claude/skills/** tree is its generated mirror for the Claude Code surface. You author and edit a skill under .agents; you never hand-edit its .claude peer.

Currency is enforced by SkillSyncCheck, which asserts that .claude/skills/** is a current regeneration of .agents/skills/**. The two trees are treated as synchronized peers, but only one of them is editable — the other is output. Routing also reflects this: an edit under .agents/skills/** routes SkillSyncCheck (alongside the skill-quality rubric) so the generated mirror cannot silently fall behind a source edit. The vendored speckit-* skills are excluded inside the gate, not by path.

The other generated artifacts

RefreshSurfaceBaselines regenerates a family of single-source artifacts in one operation, each with its own currency gate (the doc-reference and metadata currency checks all fold into TargetMetadataDrift):

Generated view

Canonical source

Currency gate

validation.contract.yml

Routing.rules (Routing.fs)

TargetMetadataDrift

.claude/skills/**

.agents/skills/**

SkillSyncCheck

docs/evidence-formats.md (under template/base/docs/)

Evidence.EvidenceFormatSchema

TargetMetadataDrift

docs/skillist-reference.md

live SkillRegistry + the closed owns vocabulary

TargetMetadataDrift

src/Controls/catalog.yml + Catalog.fs typed rows

CatalogGen.catalogFacts

ControlsCatalogGenerationCheck

src/Controls/DesignTokens.fs

the DTCG design-tokens.tokens.json

DesignTokenDrift

template/base/docs/api-surface/**

template/capabilities.yml contracts:

TargetMetadataDrift (via ApiSurfaceGen)

constitution principle fragments + governed prose blocks

.specify/memory/constitution.md / GovernedBlocks.governedBlocks

TargetMetadataDrift

The pattern is the point, not the exact list: a fact that must appear in more than one place is authored once and rendered into the others, and a gate re-renders and compares so a hand-edit to a generated copy is caught.

RefreshSurfaceBaselines: the single regeneration entry point

There is exactly one command to regenerate every generated artifact:

./fake.sh build -t RefreshSurfaceBaselines

A single target body emits the full set of regeneration effects. In source order, RefreshSurfaceBaselines:

Because all generation flows through this one target, the regeneration order is deterministic and the dependency between regen steps (for example, governed-block splicing before skill-tree mirroring) is encoded once, in the target body — not left to whoever happens to be editing.

Why nothing is hand-synced

Hand-syncing two files that must agree is a standing invitation to drift: a change lands in one and not the other, and the disagreement is silent until something downstream breaks. Single-source generation removes the failure mode by construction:

The practical loop is therefore: edit the canonical source (the routing rules, the .agents skill, the DTCG tokens, the catalog facts), run RefreshSurfaceBaselines, and commit the source together with the regenerated views. If you skip the regeneration, TargetMetadataDrift, SkillSyncCheck, ControlsCatalogGenerationCheck, or DesignTokenDrift will stop you — by design.


See also: governance index · routing and gates · evidence and audit · speckit placement · API reference.

Type something to start searching.