| title | Architecture |
|---|---|
| category | FS.GG |
| categoryindex | 6 |
| index | 0 |
| description | A newcomer's guide to the FS-GG architecture — the component split, the one-way dependency rule, the contract registry, and how the repositories compose into one runnable workspace. |
FsQuint is the independent public Quint–F# correspondence library. SDD delegates generic replay through a compatibility facade; Coordination owns domain adapters and consumes the same package. Package upgrades are qualified separately from tool pins and installed adoption (ADR-0085).
Audience. This document is for people who want to understand how FS-GG is built — its repositories, boundaries, contracts, and the decisions that shape them. If you only want to use FS-GG to build an app, start with the consumer guide instead.
FS-GG is F# tooling for building desktop UI apps on net10.0: a
SkiaSharp/OpenGL UI framework, a spec-driven development (SDD) lifecycle CLI, and
optional governance tooling, that compose into one runnable workspace. It began as the
split of the archived FS-Skia-UI
monolith into four focused, independently shippable components plus an
organization-level coordination repository. It has since grown three framework components: FS.GG.Game,
the render-independent simulation core extracted from Rendering under
ADR-0022; FS.GG.Audio,
onboarded as a standalone component under
ADR-0023; and FS.GG.Net, the
render-independent transport component (protobuf over WebSocket / gRPC) onboarded under
ADR-0052. All three are published
to the org feed and nuget.org — §5 carries
the versions, generated from the registry. A ninth organization repository,
FS.GG.Coordination, is the independently qualified new-only v2 product approved by
ADR-0078; it is rostered and active at its selected clean-path boundary, but is not a framework or
v1-fabric participant.
ADR-0091 enables the clean-start ordinary V2 settlement path
through checked native GitHub source delivery and a fresh shared authority epoch. Its selected C0–C3
functional profile is accepted for .github, Audio, Rendering, Net, Governance, Game, SDD, Templates and
Coordination: each selected receiver has checked source delivery, one real ordinary settlement, independent
Authority readback and an unchanged normal already-complete rerun. The
acceptance report keeps that bounded result separate from
historical economics, which remains insufficient with no efficiency benefit claimed. Historical V1 admission
and migration proofs do not gate this route; generated workspace defaults and other receivers or operation
classes retain their own adoption and authority.
ADR-0092 records the accepted descriptor-driven polyglot provider architecture. Its implementation design assigns catalog, naming, capability, skill and publication boundaries to SDD, Governance, Templates and the wizard. Generated product languages remain independent of F# orchestration and NuGet template transport; source design delivery does not establish the new providers' public installed support.
This page is a map. Authoritative detail lives in each component repository and in the decision records linked throughout.
Terms (ADR-0020). The platform is FS-GG as a whole — the current nine organization repositories below (seven framework components +
.github+ the selected v2 product; the sixth, FS.GG.Game, was extracted under ADR-0022 and published at P5; the seventh, FS.GG.Audio, was onboarded as a standalone component under ADR-0023; the eighth, FS.GG.Net, the transport component, under ADR-0052). ADR-0078 approved the early-provisioned FS.GG.Coordination component; ADR-0091 now governs its selected clean-path activation as the ninth repository and independently qualified v2 coordination runtime. Each repository is a component. What a consumer scaffolds with the platform is a workspace — the generated repo with a runnable app, the.fsgg/lifecycle, skills, and optional governance. This page uses those words precisely; see ADR-0020.
┌───────────────────────────────────────────────┐
│ FS-GG/.github (this repo — coordination) │
│ • registry/dependencies.yml (contract truth) │
│ • registry/repos.yml (repo roster; ADR-0019) │
│ • registry/repos.lock (kit digests; #527) │
│ • dist/dotnet/ (org-shared build config) │
│ • docs/ (decision records + consumer guide) │
│ • the org CLIs: new-sdd-workspace, and the │
│ coordination engine (fsgg-coord; ADR-0034) │
└───────────────────────────────────────────────┘
│ syncs build config to ▼ all repos
┌───────────────────────────────┼───────────────────────────────────────┐
│ │ │
┌──┴───────────────┐ ┌───────────┴──────────┐ ┌──────────────────┐ ┌──┴──────────────┐
│ FS.GG.Rendering │ │ FS.GG.SDD │ │ FS.GG.Governance │ │ FS.GG.Templates │
│ UI framework │ │ lifecycle CLI + │ │ optional rule / │ │ scaffold-time │
│ FS.GG.UI.* + │ │ FS.GG.Contracts │ │ evidence kernel │ │ composition │
│ fs-gg-ui template│ │ (fsgg-sdd) │ │ (fsgg-governance)│ │ (overlay+provider)│
└───┬─────────▲────┘ └──────────┬───────────┘ └────────┬─────────┘ └────────┬────────┘
│ │ │ │ │
│ │ Game.Render▲ └── scaffold-provider ────┴── governance-* ──────┤
│ └──────────┐ │ (Templates consumes fs-gg-ui-template + the two above)│
┌───┴──────────────────┐│ │ │
│ FS.GG.Game ││ └────── fs-gg-ui-template ───────────────────────────────┘
│ (extracted+published,││ (all three consumed by Templates at scaffold time; see §6)
│ ADR-0022) │└─ Game.Render ─▶ FS.GG.UI.Scene (adapter reaches UP — allowed)
│ Game.Core (BCL sim) │
│ + Game.Render │ Game.Core → nothing (BCL-only BOTTOM layer, sibling to Rendering).
└──────────────────────┘ FS.GG.Audio.* → nothing either (the second such bottom layer, ADR-0023).
FS.GG.Net.* → nothing either (a third bottom layer, ADR-0052; consumed by
apps — SC2/BAR clients — not by the template).
Rendering's template reaches UP to the game + audio bottom layers
(game-sim-core, fs-gg-audio) and to nothing else; none reaches back. One-way preserved.
Nine repositories under github.com/FS-GG (seven framework
components + .github + the inert FS.GG.Coordination v2 product; FS.GG.Game was extracted under ADR-0022 — its packages
FS.GG.Game.Core + .Render are published to the org feed + nuget.org;
game-extraction coherent: true since P5, 2026-07-06; FS.GG.Audio was onboarded
as a standalone render-independent component under ADR-0023, its FS.GG.Audio.* set
published — promoted off the -preview channel on 2026-07-09 (FS.GG.Audio#4), which made it
the last FS.GG.* producer to go stable; and FS.GG.Net was onboarded under ADR-0052, its
six-package FS.GG.Net.* set published at 0.1.0 (v0.1.0, 2026-07-19) on both feeds — every
producer in the org ships on a stable channel). §5 carries what each is
published at:
| Repository | Role | Ships |
|---|---|---|
| FS.GG.Rendering | The UI framework — Scene, layout, input, viewer/host, controls, themes; Elmish/MVU over SkiaSharp/OpenGL. | FS.GG.UI.* packages + the fs-gg-ui dotnet new template |
| FS.GG.SDD | The lifecycle CLI + the typed cross-repo contract backbone. | FS.GG.SDD.Cli (fsgg-sdd) + FS.GG.Contracts |
| FS.GG.Governance | Optional rule / evidence / gate tooling — a pure inference kernel, advisory by default. | FS.GG.Governance.Cli (fsgg-governance) + the reference gate set |
| FS.GG.Templates | The composition — wires SDD + framework producers into one workspace at scaffold time. ADR-0071/0072/0073 define the console, web, fable-game, and fable-bindings providers; FS.GG.Workspace.Template 0.18.2 is published and registry-active (see the versions table in §5 for the current pin), and new-sdd-workspace 0.12.0 selects each through --template, including the fable-game bundle matrix. |
the rendering scaffold provider + fs-gg-governance overlay; FS.GG.Workspace.Template package (fs-gg-console, fs-gg-web, fs-gg-governance, fs-gg-fable-game, fs-gg-fable-bindings, fs-gg-project-knowledge, fs-gg-python), on the org feed + nuget.org |
| FS.GG.Game (extracted, ADR-0022; published P5) | The render-independent simulation core + a thin Scene adapter — the new BCL-only bottom layer, extracted from Rendering. Developed with fsgg-sdd as its lifecycle. |
FS.GG.Game.Core (BCL-only sim) + FS.GG.Game.Render (Scene adapter), on the org feed + nuget.org |
| FS.GG.Audio (onboarded, ADR-0023) | The render-independent game-audio component — pure AudioEffect vocabulary, an IAudioBackend device seam, a mixing Engine (buses / fades / ducking / 3D), and an Elmish Cmd bridge. Depends on no FS-GG component — a BCL-only bottom layer, sibling to Rendering and FS.GG.Game.Core. First consumed cross-repo by Rendering's template game/sample-pack profiles (ADR-0024 step 3, .github#238), shipped in fs-gg-ui-template 0.3.1-preview.1. Developed with fsgg-sdd as its lifecycle. |
FS.GG.Audio.Core / .Host / .Engine / .Elmish, on the org feed + nuget.org |
| FS.GG.Net (onboarded, ADR-0052; published 0.1.0) | The render-independent, domain-neutral transport component — an ITransport / IMessageChannel seam with Sequential / Multiplexed client correlation and serve / ServerEcho on the server side, a client + Kestrel-server WebSocket transport, Google.Protobuf + protobuf-net codecs, a thin gRPC lifecycle bridge, and an Elmish Cmd / Sub bridge. Depends on no FS-GG component — a BCL-first bottom layer, sibling to FS.GG.Game.Core and FS.GG.Audio. Consumers are app repos (SC2 / BAR clients), not FS-GG components. Verified against a real SC2 server + an in-process gRPC service. |
FS.GG.Net.Core / .WebSocket / .WebSocket.Server / .Protobuf / .Grpc / .Elmish, on the org feed + nuget.org |
| FS.GG.Coordination (approved by ADR-0078; inert repository created early by explicit operator authorization; rostered by GS2-01.2) | The new-only GitHub Substrate v2 coordination component. It consumes the published FS.GG.SDD Quint-profile/compiled-contract producer, owns the canonical coordination model and GitHub adapters, and qualifies independently of v1. GS2-01 owns its active bootstrap and qualification; its roster row is a non-participant with zero legacy capabilities and no production authority before the protected OpenV2 transition. |
Planned v2 CLI/qualification packages; nothing published or production-authorized during GS2-01.2 |
| FS-GG/.github (this repo) | Cross-repo contract registry, the org repo roster + coordination-kit authority (ADR-0019), org-shared build config, consumer + decision docs. Also a producer — it owns the two org-level CLIs, and their release workflows live here because the tools do (ADR-0016, ADR-0034). | FS.GG.NewSddWorkspace (new-sdd-workspace) + FS.GG.Coord.Cli (the ADR-0034 engine) — both published, org feed + nuget.org |
The earlier proposal was a single repo-native "SpecFlow graph operating system"
with a ProjectGraph/ProductGraph/FeatureGraph, an evidence ledger,
generated projections, route planning, product contracts, and platform policy —
all in one place. It was internally consistent but monolithic: it asked
maintainers to develop a changing UI framework on top of a changing governance
framework, creating a recursive maintenance cost (product changes became
governance-schema changes; governance changes could block rendering work; every
contributor had to learn a custom operating model before touching runtime code).
The current direction — recorded in
docs/project-split-decision.md — is deliberately
simpler:
- keep the rendering framework buildable, testable, and releasable without an experimental governance platform;
- use standard Spec Kit for feature workflow in each repository;
- keep only narrow, deterministic, locally-worth-it checks in the rendering repo;
- make governance tooling earn adoption from the outside;
- keep SDD lifecycle tooling in its own repo so project workflow can evolve without becoming the governance rule engine.
The full index of split documents is
docs/index.md. Background research that motivated the split is
preserved in docs/research-notes.md, and the design-system
layering rules live in docs/design-and-controls.md.
Running the Governance CLI or enforcement remains optional. Its Config library can be a declared build dependency without requiring Governance enforcement.
FS.GG.Rendering depends on no other FS-GG component — never on Governance.
SDD Commands now references the published FS.GG.Governance.Config 0.3.0
library for configuration parsing and capability-binding resolution, adopted in
SDD #1104. The Governance CLI handoff
remains optional; SDD 2.3 publication and installed adoption remain pending.
The library dependency does not activate Governance enforcement in the development
loop. The selected dependency edges are recorded in §5 and the
registry.
Reading one repo teaches you all of them. The conventions are consistent:
- F# with
.fsisignature files as the sole public surface. Every library ships a curated.fsi; the matching.fscarries no access modifiers. The public API is what the signature exposes — and it is drift-guarded by committed surface baselines (surface/*.surface.txtin Governance,PublicSurface.baselineSurfaceBaselineTestsin SDD,readiness/surface-baselines/in Rendering).
- Pure cores, I/O at the edge. Domain logic is pure and total; file/process/
network effects are pushed to a thin interpreter at the boundary. Commands are
modeled as Elmish/MVU loops (pure
init/updateover a deferred-effect union, interpreted to a fixpoint by the host). net10.0, FSharp.Core10.1.301, central package management with transitive pinning, deterministic builds, warnings-as-errors.- Spec Kit feature history. Each repo keeps numbered
specs/NNN-*/folders (spec.md/plan.md/tasks.md/contracts/…) as a design-history index, and a.specify/engine with an F# constitution. - Output rule: "JSON is the contract; Plain and Rich are projections." CLIs
emit deterministic JSON by default;
--textand--rich(Spectre.Console) are projections of the same report, and Rich degrades to zero-ANSI when output is non-interactive, redirected, orNO_COLOR/TERM=dumbis set. - Org-shared build config.
Directory.Build.props/Directory.Packages.propsare byte-identical copies distributed from this repo'sdist/dotnet/byscripts/sync-build-config.sh; repo-specific settings live in*.local.props. A drift check fails the PR — but it measures against the commit the receiver pinned, not against.github@main, so being behind is green and only a hand-edit is red (ADR-0036; see §7).
A SkiaSharp/OpenGL + Elmish UI framework. 18 src/ projects, 17 test projects,
123 Spec Kit features, on the new .slnx solution format
(FS.GG.Rendering.slnx).
The Elmish-free core / optional adapter split is real in the project graph, not just an aspiration. The render core packages carry no Elmish dependency:
FS.GG.UI.Scene— the dependency-light scene vocabulary every other package builds on.FS.GG.UI.Layout— pure layout + graph scene builders (usesYoga.Net).FS.GG.UI.DesignSystem— token model,Themerecord,ResolvedStyle, pureStyle.resolve.FS.GG.UI.Controls— declarative Skia controls (Button, TextBox, DataGrid, charts…).FS.GG.UI.Canvas,FS.GG.UI.Symbology(+Symbology.Render),FS.GG.UI.KeyboardInput,FS.GG.UI.Diagnostics.FS.GG.UI.Themes.Default/FS.GG.UI.Themes.AntDesign— concrete themes over the sharedDesignSystem(no control fork; one semanticButton, many themes — seedesign-and-controls.md).FS.GG.UI.SkiaViewer— the only project carrying native dependencies (SkiaSharp, HarfBuzz, Silk.NET windowing/GL).
The optional Elmish adapter layer is two separate opt-in packages:
FS.GG.UI.Elmish and FS.GG.UI.Controls.Elmish (over Fable.Elmish).
Headless degrade-and-disclose. Live windowed GL rendering needs an X11/GL
session, but the deterministic tiers run headless. The reference path
(src/SkiaViewer/ReferenceRendering.fsi)
returns a three-way verdict — ReferencePassed | ReferenceFailed | ReferenceEnvironmentLimited — so a capability-absent CI runner is disclosed,
not failed. The tools/Rendering.Harness CLI declares per run what each tier
proves and what it does not.
The fs-gg-ui template (manifest
.template.config/template.json,
packaged by .template.package/ as FS.GG.UI.Template). Parameters: profile
(app/game/headless-scene/governed/sample-pack — game is a minimal
replaceable Pong-style starter and the intended game/rendering default, Feature
220), designSystem (wcag/ant),
lifecycle (spec-kit/sdd/typed-sdd/none — sdd remains the default,
typed-sdd is the distinct canonical-F# opt-in, and spec-kit is a frozen legacy lane since
ADR-0056,
amending ADR-0002), productName (the additive alias
of the canonical --name, ADR-0005), and initGit (the side-effect-free opt-in
from the Feature 205 behavior break — generation no longer auto-runs git/chmod).
The template generates a root-buildable workspace: Product.slnx + global.json
build.sh/build.cmdFAKE verb wrapper, sodotnet restore|build|test|runworks at the workspace root with zero FAKE knowledge.
An optional BOM metapackage FS.GG.UI (src/Meta/) pins all 16
FS.GG.UI.* members at one exact version so drift fails restore.
CI. gate.yml
is the single required pre-merge check; release.yml does the heavy packaging and
consumption tests; template-dispatch.yml fires the cross-repo
fs-gg-ui-template-released event to Templates on a release tag.
Two packages in one repo (11 projects: 5 src + 6 test).
FS.GG.Contracts — the typed cross-repo contract backbone. A
FSharp.Core-only BCL leaf (no project references, no I/O), namespace Fsgg,
five modules:
Fsgg.ContractVersion— a self-describing package SemVer (val value, carrying thefsgg-contractsversion §5 records) so a consumer knows which surface it compiled against.Fsgg.Schemas— one typed source of truth for every.fsggschema shape and its version constant (SDD- and Governance-owned).Fsgg.Provider— the extended scaffold-provider descriptor (canonicalNameParameter).Fsgg.Registry— the validator for this repo'sregistry/dependencies.yml. Its version grammar deliberately mirrorsscripts/validate-registry.pybyte-for-byte, including the 4-segmentmajor.minor.patch.revisionform (ADR-0007), so the typed validator and the (now retired) Python authority cannot disagree.Fsgg.SkillMirror— the onemirror/verifylibrary that owns all orchestrated skill fan-out (ADR-0014); §5's skill-union invariant below is stated against it.
The fsgg-sdd CLI drives the lifecycle
charter → specify → clarify → checklist → plan → tasks → analyze → evidence → verify → ship, modeled as an Elmish/MVU loop
(CommandWorkflow.fs)
where pure planning emits a deferred CommandEffect union performed only at the
edge. Each command projects a CommandReport as JSON/text/rich. Authored output
lands in work/<id>/*; structured output (including governance-handoff.json)
in readiness/<id>/*.
Coming from Spec Kit? There is no
implementcommand — by design. SDD brackets implementation rather than owning it: it tracks the artifacts and evidence around your work, it does not produce your application code. The Spec Kit/implementstep is the unmanaged gap betweenanalyzeandevidence— the quickstart lifecycle table names the action afteranalyzeliterally as "implement, thenevidence."analyzereportsanalysis.jsonas implementation-ready (the gate before you code); you implement; thenevidenceauthorswork/<id>/evidence.ymlrecording proof that it happened — the closest command equivalent. The migration guide mapsspec/clarify/plan/tasks/evidencebut has noimplementrow, and there is noImplementcase in theSddCommandunion.
Spec Kit SDD /specify/clarify/plan/tasks/analyzespecifyclarify(checklist)plantasksanalyze/implement(no command — the act between analyzeandevidence);evidencerecords it— verify→ship(SDD-specific readiness / merge-boundary stages)
The scaffold provider mechanism (fsgg-sdd scaffold --provider rendering)
reads .fsgg/providers.yml, validates the provider's contract major, then plans
dotnet new install <source> → SDD skeleton → dotnet new <templateId>. It
guards SDD-owned trees (.fsgg/, work/, readiness/, AGENTS.md,
CLAUDE.md): a provider that writes into them is reported ProviderFailed. SDD
embeds no rendering-specific identity — the runnable provider ships in Templates
(descriptor) and Rendering (template). The skeleton (fsgg-sdd init) writes the
.fsgg/ slots SDD owns (project.yml/sdd.yml/agents.yml), per ADR-0005.
A separate fsgg-sdd registry validate <path> composes the YAML load edge with
Fsgg.Registry.validateDocument — this is the typed validator the contract
coherence gate runs (§5).
The CLI is also the orchestrator (ADR-0008 / ADR-0009). A scaffolded workspace is
template@<pin> + fsgg-sdd@<installed>, and the CLI seeds artifacts that pin's
workspace is expected to contain (fs-gg-sdd-* process skills, .fsgg/early-stage-guidance.md),
so the CLI is a first-class member of the coherent set — the orchestrator axis of §5.
It is the single orchestration and enforcement surface but not the source of truth
(that stays declarative in the registry): on every command it detects drift
read-only — its own version vs. the pin's required minimum, and the seeded artifacts
present vs. those the pin expects — warning when interactive and failing closed in
CI. Remediation is never a side effect: a read-only fsgg-sdd doctor reports, and an
explicit fsgg-sdd upgrade (self-update + template re-pin + refresh-agents re-seed)
reconciles, each as a confirmable diff, touching only consumer-owned state. It
stamps the CLI version used + required minimum into scaffold-provenance.json. The one
bounded exception is creation (ADR-0030): new-sdd-workspace self-updates the CLI to
the newest coherent set before it scaffolds, by default (--pinned opts back into a
reproducible pin) — there is no existing consumer to clobber and no prior run to reproduce,
so the detect-and-warn policy above applies only to in-project invocations.
The largest repo by count (166 .fsproj: 80 src + 84 Expecto/FsCheck test
projects, ~70 packable). Running its CLI and enforcement remains optional;
SDD Commands now has the explicit Config 0.3.0 library dependency described above.
A pure, BCL-only kernel. A fixed-point reasoner with full provenance, a
three-valued Kleene Verdict (Pass/Fail/Uncertain), and a reified Check
algebra folded six ways from one source (eval/render/hash/explain/
reads/isReified) so projections cannot drift apart.
Two orthogonal axes (the core model):
CheckTier = Deterministic | AgentReviewed | HumanOnly— who is competent to decide.Severity = Advisory | Blocking— whether failure stops you.
These never collapse into each other; Deterministic is structurally refused for
opaque (non-reified) checks.
The file-driven pipeline — Config → Routing → Gates → Route → Enforcement →
Ship — is a real per-stage project chain. Enforcement runs on a six-value ordered
mode ladder Sandbox < Inner < Focused < Verify < Gate < Release, where only
Verify / Gate / Ship are blocking boundaries; Sandbox/Inner (the local
loop) are advisory-only. Profiles Light | Standard | Strict | Release tighten how
a maturity floor maps to a blocking boundary. The escape hatch is local-only and
loud — the merge boundary recomputes from scratch and ignores any local mode.
The governance-handoff consumer (Adapters.SddHandoff) reads SDD's optional
readiness/<id>/governance-handoff.json, importing no SDD code and mutating no
SDD-owned field. fsgg-governance route --mode gate returns exit 2
(GovernedBlocking) when a consumed handoff blocks, and is profile-aware
(ADR-0005): strict tightens, light relaxes.
The reference gate set — samples/sdd-reference-gate-set/.fsgg/
(governance.yml/policy.yml/capabilities.yml/tooling.yml) — is packed in
place into a content-only NuGet package FS.GG.Governance.ReferenceGateSet
whose version is a plain SemVer that bumps on any change to the packed set — content or schema —
rather than derived from the four contained schemaVersions (ADR-0055, superseding ADR-0007; the
four schema generations now ship as an in-package schema-manifest.json, and
§5 carries the current one). This is the
one versioned source of truth the Templates overlay diffs against.
Deliberately tiny: no src/, no compiled code — a dotnet new overlay, a
provider descriptor, helper scripts, and a shell composition harness.
Composition by scaffold, not vendoring (ADR-0002). dotnet new cannot depend
on another template, so rather than vendoring a rendering copy that goes stale,
the provider descriptor
providers/rendering.providers.yml
pins FS.GG.UI.Template::<version> and SDD installs the live, pinned upstream
package at scaffold time. The new-sdd-workspace
dotnet tool (F# / Spectre.Console) does the three steps — register the provider → fsgg-sdd scaffold
→ apply the governance overlay after (so it is not flagged writing into the SDD-owned .fsgg/ tree)
— with no FS.GG.Templates checkout, fetching the pinned descriptor over the network. By default it
first self-updates fsgg-sdd to the newest coherent set so the scaffold is built by current tooling
(ADR-0030; --pinned skips it for a reproducible pin). Its interactive wizard therefore asks only
for meaningful values: it follows default-on coordination without a second confirmation and does not
offer the normally redundant immediate fsgg-sdd upgrade. The explicit CLI escape hatches remain
--no-coordination and --upgrade.
The Creator's 0.15 source candidate reuses the accepted coordination 0.97 binaries for product-scoped board preparation. Its eleven-project graph remains intact; the dependency manifest pins the published archive and all 81 selected files, and candidate checks require exact source and packaged-file closure. This independently versioned Creator release does not republish the coordination coherent set. Creator 0.14 remains published; 0.15 publication and fresh/retained workspace adoption are pending in the board integration plan.
The fs-gg-governance overlay ships a populated gate set (real
build/test/evidence checks wired to tooling commands), authored to Governance's
schemas. The composition harness
tests/composition/run.sh
is effectively the live integration test for the registry's dependency edges: it
packs, installs, instantiates, asserts pin coherence (provider, tag comment, and
README must name the same version), and proves the governance matrix end-to-end —
strict + failing → exit 2, strict + satisfied → exit 0, light + failing → exit
0 — with independent SKIP probes so it never passes by omission.
The workspace-provider contract (ADR-0071,
ADR-0072,
ADR-0073)
adds a top-level distinction that the current rendering-only path does not need:
new-sdd-workspace --template selects a provider, while --profile is interpreted only by that
provider. Omission preserves the current rendering behaviour during the compatibility window.
FS.GG.Templates owns one FS.GG.Workspace.Template package (registry package-version 0.18.2 —
the feed's literal newest, per the versions table in §5; published to both feeds and registry-active
through SVG-COHERENCE-01.8) with eight configurations and seven selectors: the
current and legacy fable-game configurations share one selector. The existing
fs-gg-project-knowledge overlay and fs-gg-python workspace are included alongside
the following providers. fs-gg-console is a minimal
F# executable with tests and no npm lane. fs-gg-web is an F# ASP.NET Core plus plain
TypeScript/Vite baseline; fs-gg-fable-game is a bounded Fable/Elmish game workspace.
fs-gg-fable-bindings is a package-producing Fable interop workspace over an exactly pinned npm
package and declaration closure, with assisted generation, curated source, compile/runtime/drift
evidence, and browser/Node/universal targets. The Babylon bindings prototype
(EHotwagner/babylonjsBindings, itself
unpublished) is its first forcing reference, not a universal API choice. The game shape uses plain
HTTP endpoints with explicit versioned DTOs for typed request/response and SignalR for real-time
session traffic — ADR-0073 dropped Fable.Remoting from that role because the upstream
Fable.Remoting.MsgPack package does not compile under the pinned Fable compiler
(FS.GG.Templates#370) and the one upstream report is unfixed and unacknowledged; SignalR's role,
and the rest of ADR-0071's design, are unchanged — and consumes Game's producer-owned
fs-gg-game-core-fable-lockstep-v1 profile at FS.GG.Game.Core 0.16.0. Templates owns generic
Fable workspace skills; Game owns the lockstep skill and publishes it in the independently
versioned FS.GG.Game.Skills package (registry package-version 0.9.0 — the feed's literal
newest, registry contract game-skills), materialized from its declared owner under ADR-0063.
SDD PR #984 advances the receiver to SDD 1.8.0,
embedding Game Skills 0.9.0, Rendering Skills 0.2.0 and Audio Skills 0.1.0 from their closed owner
manifests, plus Drivers 0.89.0's routine policy, helper and native workflow. Public qualification
34845942599 verifies the retained
package payloads on both feeds and the installed Quint profile. Complete bundle composition remains
the Templates-owned SVG workspace milestone .3.
The earlier Game Skills 0.7.0 transport was established by SDD#817/PR#819. The registry rows for
fs-gg-workspace-template and game-skills were activated in .github#2070 (epic #2067 rollout
phases 1-5). The wizard selector was introduced in 0.10.1 (.github#2968); its current published
version is 0.12.0. Omitted fable-game creation now selects typed-sdd with the default
quint-specification-v1 backend; explicit lifecycle tokens retain their qualified behavior.
FS.GG.Workspace.Template 0.15.0 preserves the P4 additive Typed SDD adoption
across all five registered identities and makes SVG Player the omitted/default fable-game product,
with explicit Studio, Tactical, Arcade and Complete bundles plus the local Caddy/Compose deployment
boundary, after installed public qualification on both feeds. The
sibling EHotwagner/S.I.R. repository is the first forcing consumer: EHotwagner/S.I.R.#138 (merge
b17ac33b) incorporated the fs-gg-fable-game scaffold, pinning FS.GG.Workspace.Template 0.8.0
(its consumed version — distinct from the registry's newest-tracking 0.18.2 pin above; S.I.R. has
not re-pinned) and FS.GG.Game.Core 0.13.0 from the public feed with zero sibling-checkout edges;
S.I.R.-specific rules remain consumer-owned.
The platform's simulation core, extracted from Rendering and published under
ADR-0022. Game logic is
render-independent but has lived inside the render core — the FS.GG.UI.Canvas.* sim
primitives, the Scene.Geometry collision module, the game template profile, and four
game-gated skills. FS.GG.Game gives that subsystem its own home:
FS.GG.Game.Core— a packable, FSharp.Core-only simulation core (RNG, fixed-step, collision, pathfinding, grids/spatial partitioning, FOV/LOS, ECS/state model). It becomes the new bottom layer: it reaches up to nothing, sibling to Rendering. A headless game sim builds and tests with zero Skia.FS.GG.Game.Render— a thin adapter (depends onGame.Core+FS.GG.UI.Scene) that maps sim state ontoScene. It reaches up to Rendering — allowed under the one-way rule, which the split preserves. (Rendering itself reaches up only to the BCL-only bottom layersFS.GG.Game.Coreand, since ADR-0024 step 3,FS.GG.Audio.*— both of which reach up to nothing.)
The cut line was settled by a pre-ADR usage audit
(docs/reports/2026-07-06-p0-scene-geometry-cut-line-audit.md):
Rect/Point stay render-core in Scene, the game-only Geometry module moves to
Game.Core (Option D), and no FS.GG.Math leaf is born. The extraction forces a SemVer
major on both FS.GG.UI.Canvas (loses the four primitives) and FS.GG.UI.Scene (loses
the Geometry module) — a coordinated contract-change, not [<Obsolete>] aliases.
FS.GG.Game is the platform's SDD dogfood. It is developed with fsgg-sdd as its own dev
lifecycle (the .fsgg/ charter → ship process), coexisting with a standard Spec Kit
specs/NNN-* history rather than replacing it — because SDD brackets implementation (§4.2)
rather than authoring it. As the first repo init'd by hand (no fs-gg-ui-template pin), it
is the forcing workload that makes SDD define a provider-less "dev-repo" provenance shape.
The consumer game scaffold provider was deferred and dotnet new fs-gg-ui --profile game
frozen for the epic's duration. ADR-0063
then cancelled that deferred provider: the freeze is retired not by a second provider but by the
scaffold materializer sourcing FS.GG.Game's product-skills/ (and any owner repo's skills) directly
from the owner/source the registry row already names. Phased plan:
docs/reports/2026-07-06-extract-fs-gg-game-component-sdd-driven.md.
FS.GG.Game.Render projects Game.Core onto FS.GG.UI.Scene (consumed from nuget.org), and the
four game product skills migrated owner: fs-gg-rendering → fs-gg-game byte-identically (reconciled
from FS.GG.Game's own producer skill-manifest; registry = manifest = bytes). Rendering keeps
frozen byte-identical copies of the game starter + skills — an accepted two-copies cost tracked
as the game-starter-two-copies coherence row, retired by owner-repo materializer sourcing
(ADR-0063) rather than the
originally-planned provider epic. Live phase state is
the game-extraction coherence row's to report, not this page's.
Because the system is split, cross-repo coherence is explicit work. The
machine-readable source of truth is
registry/dependencies.yml in this repo (human
projection: docs/registry/compatibility.md). It declares:
- the component repos and their roles (FS.GG.Game's contract rows land as it
publishes —
coherent: falseuntil then, ADR-0022); - versioned
contracts:— each with an owner, a surface (the on-disk file or package that is the contract), and its consumers; - hard dependency
dependencies:edges (downstream → upstream); - a
coherence:list, wherecoherent: falseis a standing cross-repo request — a tracked promise not yet fully kept.
The protocol: a contract-change issue MUST update this file as part of its
resolution. The registry is validated in CI on three axes, and the third is the
one that keeps it honest:
- against its schema — the typed
Fsgg.Registryvalidator (fsgg-sdd registry validate), run by the reusablecontract-coherence.yml; - against its projection —
scripts/check-projection.py, socompatibility.mdcannot drift from the registry it projects; - against reality — two gates, one per subject, because
versionandpackage-versionname different facts:scripts/check-source-coherence.py(.github#741) assertsfsgg-contracts'versionequals theFS.GG.Contractssource SemVer onFS.GG.SDD@main, andscripts/check-feed-coherence.py(coherence idregistry-feed-coherence, .github#267) asserts everypackage-versionequals the newest version live on the org feed, in both directions. Both run on every registry PR and daily — because an SDD source bump, or a release that publishes without flipping the registry, touches no file here, so nothing else can see it. Until the feed half existed, publish-before-flip (FR-007) step 2 was gated by nobody noticing, and drifted three times.
The source half used to live under the schema axis, inside the reusable
contract-coherence.yml that all six repos call — which was wrong twice over. It is not a
schema fact, and asserting another repo's main from an org-wide required check meant a
Contracts bump wedged every repo at once with no safe landing order, since no PR spans both
repos (FS.GG.SDD#432). The reusable gate now
asserts only pure functions of committed files; registry-vs-reality is .github-local, so a
red stops the repo that owns the registry rather than the org.
A gate that passes when its subject is absent manufactures confidence, so each of these fails closed: "nothing to check" and "checked, and it's fine" must not share an exit code (.github#266).
A fourth axis, one layer up: the gates' own pins. The schema axis above runs a pinned
version of the typed validator, so that pin is a subject too — and a frozen one silently
degrades the typed gate toward a "does the YAML parse" check, while the registry row asserting
its coherence stays green. It froze twice (.github#127,
.github#263), each time found by eye during a
release. scripts/check-pin-coherence.py (coherence id
pin-feed-coherence) now compares every # renovate:-annotated literal in this repo against the
newest version live on the org feed, and asserts the hostRules feed token without which
Renovate cannot bump any of them — the mechanism the pin's coherence had always been assumed to
rest on, and which .github alone never configured. It takes its notion of "what is a pin" from
the org preset's own annotation regex, so the gate and the bot cannot disagree about the subject.
Sibling registries in this repo. Two more .github-owned registries sit alongside
dependencies.yml: registry/skills.yml (the skill catalog —
also a versioned contract, skill-registry, in the table below) and
registry/repos.yml (the org repo roster, ADR-0019 — the
single authoritative list of framework repos each org fabric iterates, gated per a
receives capability). The roster is not a versioned cross-repo contract — it is
validated self-contained by scripts/repos.sh, not the typed Fsgg.Registry — but it is
the source of truth for participation in each fabric (labels, the coordination-kit
distribution/audit, …), with .github as the kit authority.
The contracts that hold the system together:
| Contract | Owner | Surface | Consumed by |
|---|---|---|---|
scaffold-provider |
SDD | .fsgg/providers.yml + dotnet new wrapper protocol |
Templates, Rendering |
fsgg-contracts |
SDD | the FS.GG.Contracts package (typed schemas + registry validator) |
SDD, Governance, Templates |
scaffold-provenance |
SDD | .fsgg/scaffold-provenance.json |
SDD |
governance-handoff |
SDD | readiness/<id>/governance-handoff.json (optional) — @1.1.0: ADR-0035 stage 3 made the blocking diagnostic id ship.unobservedEvidence reachable, additively (no enum moved, schemaVersion still 1). SDD reports the unobserved fact; Governance owns what it costs at a merge boundary. contractVersion: "1.0.0"" — false since 2026-07-14 (FS.GG.SDD#427, commit f51680d3): the emitter reads the constant and the artifact self-declares 1.1.0, agreeing with this row. What remains is that nothing gates the two — they are kept in step by hand, and that gate is .github's: #1085. Coherence governance-handoff-emitted-version is coherent: true (#1082). |
Governance |
governance-policy / -capabilities / -tooling / -descriptor |
Governance | the four .fsgg/*.yml slots |
Templates |
governance-reference-gate-set |
Governance | the content-only FS.GG.Governance.ReferenceGateSet package |
Templates |
fs-gg-ui-template |
Rendering | dotnet new fs-gg-ui + FS.GG.UI.* packages |
Templates, SDD |
fs-gg-workspace-template |
Templates | the FS.GG.Workspace.Template package (see the versions table below for the current pin) with dotnet new fs-gg-console, fs-gg-web, fs-gg-governance, fs-gg-fable-game, and fs-gg-fable-bindings identities; fable-game exposes Player, Studio, Tactical, Arcade and Complete bundles |
.github wizard 0.12.0, scaffold-provider@SDD |
game-skills |
Game | the independently versioned FS.GG.Game.Skills owner package, carrying Fable, lockstep, replay and rules guidance with verified file digests and bundle predicates |
SDD scaffold materializer, generated fs-gg-fable-game workspaces |
rendering-skills |
Rendering | the independently versioned FS.GG.Rendering.Skills owner package, including SVG assets/performance guidance and verified sidecars |
SDD scaffold materializer |
audio-skills |
Audio | the independently versioned FS.GG.Audio.Skills owner package and its browser-audio guidance |
SDD scaffold materializer |
game-sim-core |
Game | the FS.GG.Game.Core package (BCL-only sim bottom layer, $(FsGgGameVersion) axis) |
Rendering (template game/sample-pack), Templates (fs-gg-fable-game lockstep profile) |
game-scene-adapter |
Game | the FS.GG.Game.Render package (projects sim state onto FS.GG.UI.Scene drawables — the one edge back down) |
Rendering |
fs-gg-audio |
Audio | the FS.GG.Audio.Core/.Host/.Engine/.Elmish packages (BCL-only audio bottom layer, $(FsGgAudioVersion) axis) |
Rendering (template game/sample-pack, gated) |
keyboard-input |
Rendering | the FS.GG.UI.KeyboardInput Keymap surface (value type + rebind + Keymap.resolve/conflict diagnostics; ships in the fs-gg-ui coherent set @ 0.5.0, ADR-0028) |
Game (FS.GG.Game.Render default command→key keymap) |
shared-build-config |
.github | dist/dotnet/* + sync-build-config.sh (+ the receiver's .config/fsgg-build-config.sha pin, ADR-0036) |
all component repos |
registry-schema |
SDD | the registry/dependencies.yml document schema (schemaVersion + field vocabulary), modeled by Fsgg.Registry |
.github (the contract-coherence gate) |
skill-registry |
.github | registry/skills.yml — the org's authoritative skill catalog (process + product + driver + operator; id, scope, owner, source, canonical-body sha256, and materializes-when), reconciled from the producer skill-manifests (ADR-0017). The former ADR-0022 §6 frozen-mirror classification was retired by .github#1862 after Rendering removed its second copies; the catalog now records one authoritative owner and source per body. |
.github (the union gate + registry validation) |
Generated from registry/dependencies.yml. version is the contract SURFACE's SemVer and
package-version is what is LIVE on the org feed: different facts, and publish-before-flip
(FR-007) means package-version never leads version — so read them together, because
version alone never tells you what a consumer can restore. One exception the schema itself
carves: for fs-gg-ui-template, version is the FRAMEWORK pin (FsGgUiVersion — the generated
product's FS.GG.UI.* pin), which is a different axis from the template package's
package-version, not a lagging copy of it.
| Contract | Owner | version |
package-version |
|---|---|---|---|
fsquint-replay |
FsQuint | 0.1.0 |
0.1.0 |
fsgg-contracts |
FS.GG.SDD | 7.6.0 |
7.6.0 |
governance-config |
FS.GG.Governance | 0.3.0 |
0.3.0 |
governance-reference-gate-set |
FS.GG.Governance | 1.7.0 |
1.7.0 |
fs-gg-ui-template |
FS.GG.Rendering | 0.32.1 |
0.32.1 |
game-sim-core |
FS.GG.Game | 0.17.0 |
0.17.0 |
wasm-shared-runtime |
FS.GG.Game | 0.2.0 |
0.3.0 |
game-physics-box2d |
FS.GG.Game | 0.17.0 |
0.17.0 |
game-scene-adapter |
FS.GG.Game | 0.17.0 |
0.17.0 |
fs-gg-audio |
FS.GG.Audio | 0.6.0 |
0.6.0 |
fs-gg-net |
FS.GG.Net | 0.6.0 |
0.6.0 |
coord-engine |
FS-GG/.github | 0.103.0 |
0.102.0 |
telemetry-host |
FS-GG/.github | 0.5.0 |
0.5.0 |
new-sdd-workspace |
FS-GG/.github | 0.16.0 |
0.16.0 |
fs-gg-workspace-template |
FS.GG.Templates | 0.18.2 |
0.18.2 |
game-skills |
FS.GG.Game | 0.9.0 |
0.9.0 |
rendering-skills |
FS.GG.Rendering | 0.2.0 |
0.2.0 |
audio-skills |
FS.GG.Audio | 0.1.0 |
0.1.0 |
The orchestrator axis. fs-gg-ui-template pins minimum-fsgg-sdd at 2.1.0 — the oldest published fsgg-sdd that seeds the artifacts a workspace on this pin is expected to contain (ADR-0008; see The coherent set has three axes below).
The orchestrator axis. fs-gg-workspace-template pins minimum-fsgg-sdd at 2.1.0 — the oldest published fsgg-sdd that seeds the artifacts a workspace on this pin is expected to contain (ADR-0008; see The coherent set has three axes below).
Dependency edges (downstream → upstream): Templates → Rendering (template),
Templates → SDD (scaffold-provider), Templates → Governance (policy/overlay),
SDD → Governance (optional CLI handoff; Config 0.3.0 Commands source dependency,
SDD 2.3 publication pending), .github → SDD (registry-schema —
the coherence gate validates this registry with SDD's typed Fsgg.Registry), and
.github → Rendering + SDD (skill-registry — skills.yml is reconciled from the two
producers' skill-manifests).
Rendering → Game (game-sim-core, ADR-0022 P5) and
Rendering → Audio (fs-gg-audio@0.1.0 — the template payload re-pinned onto the stable
channel in FS.GG.Rendering#238, alongside game-sim-core@0.2.0; ADR-0024 step 3,
.github#238): Rendering's template game/sample-pack
profiles reach up to two BCL-only bottom layers that are siblings of Rendering and themselves reach
up to nothing (FS.GG.Game.Core on the $(FsGgGameVersion) axis; FS.GG.Audio.* on
$(FsGgAudioVersion)). Game → Rendering now runs over two contracts back down: game-scene-adapter (FS.GG.Game.Render projects sim state onto FS.GG.UI.Scene drawables) and — since .github#365 / ADR-0028 — keyboard-input (FS.GG.Game.Render's default command→key keymap consumes FS.GG.UI.KeyboardInput's Keymap surface @ 0.5.0; FS.GG.Game#109). A
scaffolded product receives all of these through the template, so Templates consumes them
transitively — there is no templates → game or templates → audio edge.
The registry's own schema is a governed contract too (ADR-0015). It was the one
contract in the system that wasn't: the typed validator (the registry-validator-typed
coherence row) only pays off if the schema is versioned and the gate's FS.GG.SDD.Cli
pin advances with it — a review
found the pin frozen for three minors while the schema grew fields under additive
tolerance, degrading the gate toward a YAML-parses check. The registry-schema entry (owner SDD, consumer .github) versions the on-disk
schemaVersion, so schema growth is now a tracked contract-change (bump the version +
advance the pin, in lockstep) rather than silent drift.
The coherent set has three axes, not two. A fs-gg-ui-template@<V> pin snapshots
the template and the framework — but a scaffolded workspace also carries the
fsgg-sdd CLI that generated it, and the CLI seeds artifacts the pin's workspace is
expected to contain (the fs-gg-sdd-* process skills, .fsgg/early-stage-guidance.md).
An old CLI on the newest pin silently omits them. ADR-0008 closes that hole by
making the CLI a first-class member of the coherent set — the orchestrator axis
alongside template and framework — so the fs-gg-ui-template registry entry carries a
minimum-fsgg-sdd version (the oldest CLI that seeds those artifacts), validated by
fsgg-sdd registry validate and gated by contract-coherence. ADR-0009 fixes the
policy: the CLI is the single orchestration and enforcement surface but not the
source of truth — it detects drift read-only on every command (interactive warns,
CI fails closed) and remediates only through an explicit, diff-reviewed fsgg-sdd upgrade (self-update + re-pin + re-seed), never a silent auto-update — for in-project
invocations. ADR-0030 carves the one creation-time exception: new-sdd-workspace
self-updates the CLI to the newest coherent set before scaffolding by default
(--pinned restores a reproducible pin), because at creation there is no consumer artifact
to clobber. Truth stays declarative in the registry so it can be diffed, gated, and flipped
after publish (FR-007). The registry pins minimum-fsgg-sdd (the version is above, with the other
version leads) — the oldest published CLI
that seeds those artifacts, advanced 0.4.0→0.6.0 on 2026-07-04 because feature 073
(the transient-artifact taxonomy) made fsgg-sdd seed a .gitignore for regenerable output
into scaffolded products, growing the expected seeded surface (FR-011). Both halves are in
lockstep (Templates#99/PR#100, merged 2026-07-05): the provider descriptor mirrors
minimumFsggSdd 0.6.0, and the fsgg-sdd-orchestrator-axis coherence
row is coherent: true. A behind-CLI scaffold is verified to warn
(scaffold.cliBehindMinimum) and stamp used+minimum into scaffold-provenance
(original axis resolution closed epic #85).
The skill union is content-verified in every lane (ADR-0014/ADR-0065). Skills are
content-addressed directory data: each producer manifest retains the canonical
SKILL.md body digest for compatibility and additionally records every directory member's
relative path, digest, executable mode, and the whole-tree digest. ONE mirror/verify library
(Fsgg.SkillMirror, FS.GG.Contracts ≥ 1.4.0) owns all orchestrated fan-out, and the
standalone spec-kit lane runs a vendored byte-equivalent as its single materialize
step. The invariant — two byte-identical union roots, nothing dangling — is
asserted where skills are produced (doctor, per-skill sha256 in
scaffold-provenance) and where they are consumed: the Templates composition gate
enforces it hard in both lanes via the reusable skill-union-assert.sh
(skill-mirror-verified coherence row, coherent: true since 2026-07-02).
The root set was three until #1636 retired
.codex/skills (ADR-0065 as amended by ADR-0067 §5):
it was Codex's other native root, so it carried no runtime the remaining two do not.
A declared root now has three dispositions, and only two of them are copies
(#1696, FS.GG.Kit 0.15.0, ADR-0065
§A root's three dispositions). A root is materialized (the kit copies into it), a view
(still a runtime root, but generated locally from the live root by scripts/skill-view — which
FS.GG.Kit also delivers now), or retired (out of the contract, its copies swept by the
materializer that wrote them). The runtime root set is the union of the first two, so the
picture above is unchanged; what moves is which of them holds bytes of its own. Under a view root
the byte-identity invariant is not asserted but structurally impossible to violate — there is one
object — and what is asserted instead is visibility, because an absent root, a dangling link and a
core.symlinks=false checkout are all exit 0 and silent in both runtimes (ADR-0067 §8). The property
defaulted empty while turning it on was per-receiver work sequenced by
the retirement order; as of FS.GG.Kit 0.19.0 it
defaults to .agents/skills and FsggKitSkillRoots defaults to .claude/skills alone
(#1868, the order's §6 stage 2). The union — and so the
picture above — is unchanged by that flip; what changed is what a receiver that overrides neither
property gets.
EVERY rostered receiver holds a view root as of 2026-07-28, and the picture above has flipped from
rule to exception. Each generates .agents/skills from .claude/skills, and no receiver's runtime
root set changed — it is the union of FsggKitSkillRoots and FsggKitViewSkillRoots, and that union
is still ADR-0011's two.
This map deliberately carries NO retirement count, and this paragraph is where one used to be. It said "FIVE of the seven receivers" over a seven-row table of squashes and file counts, and it was stale within the hour — the third file to go stale the same way on the same day, while citing the issue filed about the first two. The count lives in exactly one place: the standing verdict in the retirement order §4, which carries the per-receiver mechanisms, squash shas, file counts and the command that produced them. Read it there. Do not restate it here — see #1750.
The receivers are deliberately unalike, and that is the useful fact. Templates is the thin receiver —
no gate.yml, no repo-owned skills in the audited roots. Audio has a gate.yml and 16 fs-gg-sdd-*
skills it owns inside both audited roots. Game is the large-harness receiver: 18 required contexts and
17 repo-owned skills. Governance is a build-config receiver with eight required contexts and a
substantial F# suite. All were cheap, and for one reason: diff -r between the two audited roots was
silent on each, so nothing lived only in the copy being retired. What predicts a receiver's cost is
that diff and whether it wires a union caller — not how large or framework-shaped the repo is.
diff -r is necessary but NOT sufficient, and this is the finding that outlives the sequence. It
compares two directories' contents; it can never see a third file that names one of them. On
FS.GG.Rendering both roots were byte-identical and .template.config/template.json still vendored the
retired root into the shipped dotnet new payload — dotnet new install failed loudly and dotnet pack did not fail at all, silently shipping 1843 entries instead of 1914. The companion check is a
grep for path references graded by kind: a literal file path fails closed; a directory or glob
reference fails open, because the tooling treats a missing directory as an empty set. It is stated
with both measured data points in the retirement order §6.
Three consequences the map has to carry, because they change what a green gate means:
-
coordination-coherencenarrows automatically on a repo that does this — it derives its roots from that receiver's ownFsggKitSkillRoots, so each adopter's verdict now covers 28 files where it covered 51, with no gate edit and no effect on any other receiver. A green there is a smaller claim than it was, and deliberately so: the bytes it stopped grading are the ones that stopped existing. -
A view root's MEMBERSHIP is watched by the kit as of FS.GG.Kit 0.17.0, and was not before.
FsggKitCheckSkillViewasserts a declared view root's content; if the root is later dropped fromFsggKitViewSkillRootsit reports "nothing to assert" andcoordination-syncstops looking at it — both green, root gone from the runtime contract (measured on five adopters independently). For one day each adopting repo owed its own membership assertion, and the result was seven hand-written implementations of one invariant with a 55% size spread, which is the cost #1710 named and has now collected.The collapse (
#1710piece 2).scripts/skill-view check— already a kit-delivered source, already run byFsggKitCheckSkillViewin every receiver — now owns all three lanes: the roots declaration (the union ofFsggKitSkillRootsandFsggKitViewSkillRootsmust be the runtime root set), the view resolution (absent / dangling / text-file / not-a-directory / partial), and a tracked view root. No new delivery mechanism was built; the seven receivers had bolted two lanes onto an existing seam becausecheckdid not cover them.It raises the bar rather than levelling it: partial views are graded per-skill by identity where all seven copies compared directory counts; the dangling/absent split is
classify_root's, fixing Rendering's and Governance's symlink-following[[ ! -e ]]by construction; and the tracked-view-root lane is adopted fromFS.GG.Net's copy, the only one of the seven that had it, and now runs for all of them.One parameter, and it costs a measurement.
--absent-ok "<why>": absence stays RED by default, and a receiver whose alarm runs on a bare checkout must excuse it or fire on every green build. The reason is required and printed, because what else catches an absent root differs per receiver — on Game the kit's own check reds a required job; on Audio it does not, becausekit / coordination-kitgradesFsggKitSkillRootsbytes only and the materialize job is not required. The required-context name the alarm rides stays per-receiver by construction — it is the workflow line that invokes it, not a parameter of the tool.Every lane needs a can-fire demonstration, and the demonstration must itself be mutation-tested. Audio's shipped copy guarded its view lane with
[[ ! -e ]], which follows symlinks, so a dangling view root — §8's headline class — reported green until FS.GG.Audio#212 fixed it. That rule is now structural:skill-view selftestships in the tool, so a receiver runs the same demonstration rather than a copy of it, and every negative case asserts the specific diagnostic class its branch emits. Eight mutations were run against it and each was caught by a named case; one initially survived and the demo grew the discriminating fixture that fails it.Adoption is per-receiver and sequenced (ADR-0067 §9): nothing is retired before its replacement is proven there.
FS.GG.TemplatesandFS.GG.Netwere the first adopters, because they were the two broken ones — FS.GG.Templates#324 (no view-resolution lane at all) and #1727 (an alarm on no required context) are fixed by construction rather than by repairing code about to be deleted.7 of 7 as of 2026-07-28, and every local copy is deleted (#1777). The fleet is on
FS.GG.Kit0.17.0, each pin read back frommain; 119,692 bytes across seven files are gone. The order was strict per receiver — bump → observe the kit's alarm reporting in that repo by run id and by job log → delete the copy → re-prove the mutation on that tree, at 0.17.0 rather than inheriting the measurement the deleted script's own header recorded at 0.15.0. On every one of the five: withFsggKitViewSkillRootsemptied and the view root deleted, the materialize is green, the requiredkit / coordination-kitis green, and the shared alarm reds — including where--absent-okis in force, because that excuse is scoped to view roots and the membership lane fires before the roots loop. The pin bump lands as its own pull request, becausematerialize / kit-bump-shaperefuses a bump carrying receiver-authored edits (#1726) — a refusal that happens to enforce §9's ordering.The
[[ ! -e ]]claim above, measured rather than repeated. Rendering's and Governance's copies red on a dangling view root and misattribute the class as "does not exist"; Audio's treated absence as expected, so its dangling root went green. Only the last was a silent pass. A misattributed class still sends the reader to the wrong repair, and it is not the same defect. -
A repo that wires the
skill-unionreceiver caller could not hold a view root at all — and that caller is now RETIRED. Measured 2026-07-28 onFS.GG.SDD@387adc6:skill-union-assert.ymlis a reusable workflow, so it checks the caller out and asserts over that checkout, and a root that only exists after a localgenerateis absent in its subject —configured root is absent, exit 2, on a context SDD makes required underenforce_admins. That is why the second receiver was refused rather than retired. #1715 then decided shape (b): the caller is gone in all three repos that wired one, replaced by each repo's own requiredskill-view-check. The decision turned on a second measurement — on the half-view layout the union gate's invariants are tautologies (union_ids()usesfindwithout-L, so a view root contributes zero ids, andbyte-identical=N/Ncompares a file with itself), so it would have kept reportingsuccesson a required context while asserting nothing.FS.GG.Governanceis the first retirement that clearance made possible, rather than merely failed to block. -
A reusable workflow the receiver cannot add a step to is a recurring hazard, not a one-off. The same shape hit a second gate on Governance:
kit-materialize.ymlis auses:of this repo's workflow and runs-t:FsggKitMaterializeon a bare checkout, where a view root does not exist. A receiver cannot put ageneratestep in a workflow it does not own — so the generate belongs in the receiver project (BeforeTargets="FsggKitCheckSkillView"), which is the only placement that reaches every tree the materialize runs in. The obligation is on every receiver that declares a view root — not on thebuild-configfour (.github#1759):FsggKitCheckSkillViewisAfterTargets="FsggKitMaterialize", so it runs on every materialize, and all seven receivers wirekit-materialize.yml, not the four its own header names (#1724). Measured on a bare clone of all seven on 2026-07-28: every one already carries the target and every one builds green, so the suspected fleet-wide red does not exist — it was reported from an intermediate tree, before Governance's target landed in the same pull request. Delete that one target from a copy of a retired tree and the command reds, which is what makes the green mean something. What holds the count at zero is seven independent hand-copies (#1710piece 1 owns removing them), sorepos-audit.shsweeps every receiver project daily and reds when a declared view root has nothing generating it.
ADR-0065 applies the same .claude/.agents default to framework coordination-kit receivers:
FS.GG.Kit and coordination-sync are separate delivery triggers over the same root contract.
That declaration is the transport/parity contract, not a command to expose duplicate catalog
entries in one runtime. Claude catalogs .claude/skills; Codex natively discovers .agents/skills
with no configuration. agents/openai.yaml carries host selection policy, including explicit-only
autonomous drivers, without changing which directory bytes are delivered.
The set was three until 2026-07-28 (ADR-0067 §5, .github#1636). The retired .codex/skills was
Codex's other native root rather than a third runtime's, so it delivered no reachability the
remaining two do not — it only made every skill appear twice in Codex's catalog, which each developer
machine then had to suppress by hand in ~/.codex/config.toml. Removing a root is a contract
migration, never a mirror deletion: see ADR-0065 §Retiring a root.
Skill absence is checkable too (ADR-0017). The manifest is a superset catalog —
a producer declares every skill it can emit, but emission is profile/lifecycle-gated,
so "declared ∧ absent" was blanket-tolerated and a genuinely-dropped skill was
indistinguishable from an off-profile one. ADR-0017 records the emission condition per
entry (materializes-when) and lifts it into a single authoritative catalog
registry/skills.yml (the skill-registry governed contract);
the union gate's --params mode then evaluates each condition against a scaffold's
provenance and adds [missing] (declared ∧ true ∧ absent) and [unexpected]
(present ∧ false) — closing the blind spot. Rollout is board-sequenced (skill-registry-published
coherence row): the catalog + contract landed, and both producer halves are now emitted — Rendering's
product manifest (Feature 238) and SDD's process manifest (.agents/skills/skill-manifest.json,
SDD#111 closing #109). Both producer-side conditions have now cleared (.github#290): Rendering's
predicate-grammar alignment landed in Rendering#77 (closed 2026-07-04), so producer and registry both
speak the ADR-0017 canonical grammar. The enforcing flip now waits on a .github-side step only —
publishing the typed Fsgg.Registry validator assertion over skills.yml — and must not land over a
red skill-registry-coherence (the fails-open class, epic #266).
The coherence: rows record verified, structurally-enforced invariants — for
example lockfile-restore-enforcement (a stale or silently-substituted dependency
fails restore in CI in every repo — but only where that restore is cold, per
ADR-0032 §5: validated against a warm package folder the row is fails-open — it compares
a record against a record and never contacts the feed, #429/epic #266; SDD and the
lockfile-sync generator are cold, Game/Rendering/Audio adoption is in flight. Note
FSharp.Core was never re-published (ADR-0032, #471): the SDK bundles a different
.nupkg than nuget.org serves at the same id+version, so a lock's contentHash depends
on which source resolved it. Cold was therefore not the same as hermetic — the SDK's
library-packs folder is injected by MSBuild (RestoreAdditionalProjectSources) and a
fresh NUGET_PACKAGES does not bypass it. That hole is now closed (#504): the org-shared
build config sets DisableImplicitLibraryPacksFolder, which removes the folder from the
source list, and all five F# repos (SDD, Rendering, Governance, Game, Audio) have synced it
and re-pinned to nuget.org's hash — so a lock now regenerates byte-identically on any
machine, whatever its SDK patch level or packageSourceMapping. lockfile-sync's
source report is fail-closed accordingly: a library-packs resolution is a regression,
not an un-adopted repo), apicompat-publicapi-gate (a public-API break
on a packable forces a SemVer major), fs-gg-ui-version/-bom (single-pin and
BOM coherence guarded on every Rendering PR), and
governance-cli-handoff-consumer-published (the full strict/light matrix proven
through the composed workspace). Cross-repo decisions are recorded as ADRs
(ADR-0002 composition-by-scaffold, ADR-0005 .fsgg slot ownership + canonical
name, ADR-0006 shared-build-config, ADR-0055 reference-gate-set version
(plain SemVer + in-package schema manifest, superseding ADR-0007), ADR-0008 the CLI orchestrator axis, ADR-0009 detect-and-remediate
orchestration policy).
Composition happens at scaffold time, not by vendoring:
you ─▶ fsgg-sdd scaffold --provider rendering --param productName=MyApp
│ (FS.GG.SDD)
│ reads .fsgg/providers.yml ───────────────── scaffold-provider@1
├─ dotnet new install FS.GG.UI.Template::<pin> ──── fs-gg-ui-template (FS.GG.Rendering)
├─ writes .fsgg/ + work/ + readiness/ skeleton
└─ (optional) fs-gg-governance overlay ─────────── reference-gate-set (FS.GG.Governance)
applied via (FS.GG.Templates)
FS.GG.Contracts ── validates ──▶ registry/dependencies.yml (FS-GG/.github)
dist/dotnet/* ── FS.GG.Kit package ──▶ Directory.Build.props + Directory.Packages.props
materialized in the 4 build-config receivers (ADR-0062;
sync-build-config.sh now only DERIVES the kit's FILES)
The result is a real, windowed F# UI app plus a .fsgg/ lifecycle skeleton you
can drive from charter to ship, with governance gates dropped in only if you
opt into them. There is no single all-in-one template, because that could only
exist by bundling a rendering copy that goes stale — the live, version-pinned
install is what keeps the composition honest. See the
consumer guide for the walkthrough.
-
One org-shared build config, now delivered as a package (ADR-0062, #1262).
dist/dotnet/holds the canonicalDirectory.Build.propsandDirectory.Packages.props. They no longer byte-copy: they ship inside the FS.GG.Kit package, and each of the 4 build-config receivers materializes them from a pinnedFS.GG.Kit(FsggKitMaterializeBuildConfig), refreshed bykit-materialize.ymlon a Renovate bump. Each repo imports a*.local.propsfor repo-specific settings. The drift gate is now each receiver'sbuild-config-driftjob asserting its committed.propsmatch the pinned package (adotnet build -t:FsggKitMaterialize+ git-clean check); the byte-copybuild-config-propagatepush and the reusablesync-build-config.sh --checkstep are retired.sync-build-config.shsurvives only as the kit'sFILES/marker derive source (ADR-0058). Seedocs/build/README.md..config/dotnet-tools.jsonis no longer distributed by any fabric (#1615, ADR-0068, 2026-07-28). It was acoordination-kitrow from #1077 until then, so that "receives the tool ⇒ can run the tool" held by construction — but the kit is content-addressed, so bumpingfs.gg.coord.cliby one integer edited kit content and obliged a republish plus a seven-receiver fan-out (four of the nine republishes measured on 2026-07-27/28, none of which carried a skill change). Each receiver now owns its own manifest;dist/dotnet/.config/dotnet-tools.jsonremains this repo's canonical copy andengine-pin-coherence's subject. Per-repo delivery is Renovate, and it is live (#1798, 2026-07-28). It was not, for the six hours between the two:default.json'spackageRules[5]disabled.config/dotnet-tools.jsonin all seven receivers — a rule written when the file was kit-materialized, correct for its whole life, and left standing when ADR-0068 removed its premise. That rule is now deleted, and the fact is asserted rather than recorded:tests/preset-repo-scope-coherence/drive-package-rules.mjsdrives the preset through Renovate's realapplyPackageRuleson every CI run and reds if the receiver copy ever goes disabled or thedist/dotnet/baseline ever goes unmanaged. A second leg ties the preset to the roster biconditionally — the manifest is disabled iff thekit:block delivers it — so re-adding either half without the other cannot pass quietly again. What is still missing is CONVERGENCE, not delivery: nothing yet reds when a receiver's engine version falls behind and no bump is ever offered (#1803) —repos-audit's bump-offer sweep gradesFS.GG.Kitonly. The declaration is asserted daily either way, so #1077's invariant holds throughout. #1077's invariant is preserved by assertion rather than arrangement:repos-audit's engine-manifest sweep reads everycoordination-kitreceiver's actual manifest daily —f(roster, receiver tree), strictly stronger than the roster rule it replaces, which never read a receiver at all. -
dist/dotnet/also holdsglobal.json, and it is deliberately NOT managed (ADR-0006's 2026-07-17 amendment, #903). It is a fourth canonical file, but it is not insync-build-config.sh'sFILES, so nothing distributes it and no drift gate judges it: per-repo SDK bands are legitimate. Receivers copy it by hand and Renovate bumps each copy independently, so divergence here is the steady state rather than a defect — enforcing byte-coherence would merge-freeze every receiver still holding the previous canonical bytes. This is a settled decision, not a pending rollout step;tests/sync-build-configholds the line. -
The build-config drift gate no longer compares against a
.shapin — RETIRED (ADR-0062, #1262; superseding ADR-0036, #592). ADR-0036 had each receiver commit.config/fsgg-build-config.sha— the.githubcommit its managed files came from — and--checkdiff against that commit'sdist/dotnet/, so a required gate's verdict depended only on the receiver's own tree rather than on.github@mainat CI time (which had twice red-lit every open PR in every adopter, once for an XML comment change). Under package delivery that model is gone: the pin is not managed by the package path, so it would freeze while the materialize moved the.propsforward. Each receiver's committed.propsare now checked against its pinned FS.GG.Kit (above), and the.config/fsgg-build-config.shapin is deleted from each. -
Distribution edges must not make the consumer's gate depend on the producer's
main. ADR-0036 generalises past build config: any org→repo distribution whose enforcement runs in the consumer's CI has to be judged against something in the consumer's own tree, or the producer's every commit becomes a merge freeze downstream. -
Locked restore everywhere, and it must be COLD. Every project commits
packages.lock.json; CI restores--locked-mode(gated onGITHUB_ACTIONS), andNU1603/NU1608are promoted to errors so a silent version substitution fails the build. A silent re-publication — same version, different bytes — is a different animal:NU1603/NU1608never fire, and the only thing that catches it is the lock file'scontentHash, which--locked-modevalidates against the package already in the package folder. So the restore that writes (--force-evaluate) or enforces (--locked-mode) a lock file must run against a freshNUGET_PACKAGESwith the HTTP cache cleared (ADR-0032 §5); a warm folder compares a record to a record and the gate becomes a lottery on runner cache state — green on an unrestorable lock file, red on a correct one (#429). -
Public-API breaking-change gate. For F# packables the operative detector is the SDK's Package Validation / ApiCompat (PublicApiAnalyzers is C#-only); a real break fails the gate against the published feed baseline, forcing a SemVer major that the registry ranges enforce.
-
Cross-repo automation. This repo hosts a reusable dispatch-sender workflow (App-token
repository_dispatch, sinceGITHUB_TOKENcannot dispatch cross-repo) and an org-shared Renovate preset (default.json, consumed viaextends: ["github>FS-GG/.github"]) with custom managers for the embedded pins the standard NuGet manager misses. Producers push to the org GitHub Packages feed on release; consumers auto-PR the bump. -
Coordination candidate qualification. This repository owns the governing ADR-0084; the FS.GG.Coordination producer owns semantic classification, execution selection, sharding, aggregate receipts, and recovery. Full coherent execution is the default. The scoped profile is limited to exact-head reused routine PRs with authentic complete prior full evidence and an audited nonauthority path set. It retains the base formal fragment and all nonformal partitions, records the omitted scenarios as outside scope, and emits a distinct receipt that cannot supply full reuse or recovery evidence. Main, nightly, merge-group, dispatch, release, and comprehensive acceptance boundaries retain full execution. The profile remains disabled until the producer's versioned selection and adversarial fixtures land together.
-
The coordination fabric is a typed, packaged component (ADR-0034, accepted 2026-07-12; cut over and finished under ADR-0040, 2026-07-15). The client every worker and CI job drives —
scripts/fsgg-coord— was ~7,000 lines of bash whose state model was jq regexes over prose: claims are HTML comments, touch-sets are aPaths:line in an issue body, dependency edges are free text (Projects v2 has no typed dependency field). That is a concurrent, transactional, budget-constrained domain — and it was modelled in a substrate with no types, noResult, and whose default failure mode is to fail open, which is why "is this item startable?" was computed in five places and agreed in none (#485; 34 issues), and why epic #266 has 51 children.ADR-0034 moved the domain into a pure typed F# core (
FS.GG.Coord.Core) with one schedulability function and a three-valuedGreen | Red | NoVerdicton every check, whereNoVerdictis non-zero. That engine is now the sole implementation. ADR-0040 Phase D.4 deleted the bash monolith and the shadow/differential gates that drove it;scripts/fsgg-coordis today an 83-line resolver shim that finds thefs.gg.coord.clitool via.config/dotnet-tools.jsonandexecs it, passing argv and exit code through unchanged. Every doc, workflow, and skill that references that PATH still works — which was the point of keeping it.What gated the flip was the defect corpus, not a clock (ADR-0038). ADR-0034 staged the cut-over behind a shadow-mode criterion — both engines run, bash stays authoritative, nothing flips until divergence is zero on live traffic. That clock could not tick, for a structural reason: workers run in per-item worktrees, a worktree worker resolved no engine (#728), and a worker who banks no evidence can never be one of the "≥2 distinct workers" the criterion required — while any engine republish reset the window, so the engine could not be improved while waiting for the clock that waited for the engine. ADR-0038 replaced it with a defect corpus: one case per historical defect, which needs only a checkout, survives a rebuild, and covers every path that has actually broken rather than whatever floated past a live fleet for three days. It lives in
tests/coord-engine-parity/(each defect a hermetic fixture server;shim.shre-runs the whole corpus through the shim, which is how the swap was proven transparent) andtests/coord-engine-e2e/, which drives the compiled engine over HTTP against a fixture GitHub — no token, no network. The shadow was demoted from gate to telemetry, then removed with the bash it compared against.Two consequences landed on this map.
.githubis a two-tool producer (above), the engine shipping as adotnet toolon the coherent set. And thekit:row is a digest-pinned shim — the publish cycle breaks by asymmetry:.githubbuilds the engine from source and never depends on the feed, so a broken feed cannot prevent the coordination tool from being fixed. That kit-row shape change is a contract-change under ADR-0015 and landed with the implementation, not with the decision.The larger prize was not the language.
fsgg-coordwas already the model — in every drift that can be dated, the tool was right and the prose was wrong — it simply was not the source. ADR-0034 §4.5 inverted that, and it has landed (#731): the rules live once inFS.GG.Coord.Core/Protocol.fs, and the prose that states them is a build artifact — a<!-- BEGIN GENERATED: fsgg-protocol -->region emitted byscripts/generate-projectionsintodocs/coordination/parallel-work.mdand the twointra-repo-parallel-work/SKILL.mdroots, guarded by theprojectionsgate exactly likeregistry/repos.lock. A protocol rule can no longer land in one tier and not the others, because there are no tiers — which is what 54 vendored copies of the protocol used to guarantee it would.The delivered boundary is the Typed SDD kernel; future authority is Quint-first (ADR-0076, delivered 2026-08-24; ADR-0077, accepted 2026-08-25). S.I.R. proved reusable identity, normalization, provenance, evidence, projection, and authoring contracts against its executable-rules corpus. The candidate future authoring unit is literate Markdown: ordered, named Quint blocks own behavioral meaning, surrounding prose supplies human review context, and a pinned extractor produces non-authoritative
.qntmodules. Q1 must qualify that split and amend ADR-0077 before implementation; until then the published F# backend remains authority. After acceptance, FS.GG.SDD publishes a small generated integration contract and F#/Fable bindings without recreating Quint expressions. Model-based testing follows the same boundary: FS.GG.SDD owns generic Quint/ITF mechanics, while each product owns its canonical model, real-operation adapter, observable-state projection, and replay tests. S.I.R. keeps gameplay interpreters, while coordination consumes process and mutation identities after the Quint-capable producer is published..githubowns routing and fleet evidence rather than centralized product replay. Publication still precedes adoption.The consumer lifecycle is Typed SDD (
typed-sdd). It will first join Standard SDD (sdd) and Freeform (none) as an explicit option across every supported workspace provider/profile. It is the intended successor workspace default, butsddremains the current default until a separate ADR amends ADR-0056 after published-kernel, migration, installed-artifact, all-profile, and non-S.I.R. soak evidence. A future flip moves every default-bearing surface together and does not retire explicitsddornone.There is one tier left, and it is the fleet's own engine (#1075).
projectionscompares files in this repo, so it makes the prose andProtocol.fsagree here and has nothing to say about the engine the fleet actually runs — which is a published package, restored from the feed. So a protocol rule lands inProtocol.fs, regenerates into everySKILL.md, and the fleet keepsexecing the release that predates it: main's own documents then describe a verb contract the fleet's engine does not implement. That is not hypothetical — it is #846 verbatim ("next/take/done/add are all unknown command"), and it has happened four times (#844, #846, #964, #1067), each found by whoever happened to notice.No version comparison can see it, which is why it needed a gate of its own rather than a row in §5. Every other producer's source version moves with the change, so
versiondrifting ahead ofpackage-versionis their legible "merged, not yet published" signal. This engine's<Version>moves only at release time —release-coord-engine.ymlevaluates the fsproj property and requires thecoord-engine/v<version>tag to match it, so the bump is the release act. While the source outruns the feed the two scalars are equal, and the registry row is green because the defect is happening.scripts/check-engine-freshness.pytherefore counts commits, not scalars: it resolves the tag that produced the feed's newest version (never the newest tag — a tag is not a publish) and reds when the wire surface,Protocol.fsitself, has drifted unreleased. Drift behind the wire is reported and not red — at this repo's velocity adrift > 0bar would be red by design between releases, and a gate that cries wolf on the happy path teaches that FAILED is noise. -
Public distribution (dual-publish, ADR-0012 + ADR-0013). On release each producer additionally pushes the byte-identical
.nupkgto public nuget.org (after the org-feed push), so public consumersdotnet tool install/dotnet add packagewith no--add-source. Auth is Trusted Publishing (OIDC) — a short-livedNuGet/loginkey per run, no stored secret. nuget.org is the read path; the org feed is the publish path (ADR-0039, accepted 2026-07-15, amending ADR-0012 §1): Renovate resolves everyFS.GG.*lookup fromapi.nuget.org(default.json), because the org feed requires a credential even to read and a 401 on a Renovate datasource is not an error — it is an empty version list, which is why the bot froze theFS.GG.SDD.Clipin four times and opened no PR (#576). The org feed remains the publish target and the-previewchannel, and thefeed-coherencegate still reads it — that gate assertspackage-versionagainst the feed a producer just pushed to, so the feed is the right subject for it. Package IDs on nuget.org are permanent, freezing theFS.GG.*identities (no rename — ADR-0003).
- Use FS-GG to build an app → the consumer guide (install, scaffold, run, drive the lifecycle, optionally govern).
- Develop FS-GG itself → start at
docs/index.md(the split decision record), then read the target component's repoREADME.md,CONTRIBUTING.md, and itsspecs/history. - Change a cross-repo contract → read this page's §5, follow the
contract-changeprotocol, and updateregistry/dependencies.ymlas part of the resolution.
This repository
registry/dependencies.yml— the contract & dependency registryprofile/README.md— the org landing pagedocs/index.md— the project-split decision record indexdocs/project-split-decision.md,docs/design-and-controls.md,docs/research-notes.mddocs/consumer/index.md— the consumer guidedist/dotnet/,scripts/sync-build-config.sh,scripts/apply-labels.sh
Component repositories
- FS.GG.Rendering — solution, template manifest, reference rendering verdict
- FS.GG.SDD —
Fsgg.Registry,Fsgg.Schemas,CommandWorkflow.fs - FS.GG.Governance —
README.md, reference gate set - FS.GG.Templates — provider descriptor, composition harness
Process status. This page is the project's one system-overview artifact — the synthesis the point artifacts (ADRs, the registry) don't individually produce. It is owned by
FS-GG/.githuband non-authoritative (detail stays in the registry, the ADRs, and each component repo). Its maintenance is a process obligation, mirroring the "acontract-changemust update the registry" rule: any ADR that changes the shape of the system, and anycontract-changethat alters the §5 picture, MUST reconcile this page as part of its resolution — updateregistry/dependencies.ymlfirst (the protocol), then this page. See the coordination protocol.
FS-GG/.github owns an active routine-development default and a human-selected heavyweight route. The
machine contract is .fsgg/routine-development.json: absence or
ambiguity selects routine delivery, and heavyweight process requires a recorded explicit human instruction
for named scope. Strict labels, GS2 registration, protected or sensitive paths, policy or modeled changes,
protected operations and inherited strict state do not select heavyweight ceremony.
The default-branch routine-eligibility workflow reads the
pull request base and head as Git objects and executes policy and validator bytes extracted from the exact
base SHA. It validates the routine branch and single exact-head marker, allowed source operation, changed
paths and required-check set without executing candidate bytes or reading an issue, claim, SDD family,
phase ledger, critique, feedback cycle, receipt cycle or metadata-Done state. A moved head refuses until the
marker is reviewed and rebound. Candidate-side claim-generation applies only to an explicitly selected
strict item route and is not routine authority.
Technical checks and operation authority remain independent and fail closed. Sensitive paths select their
applicable technical checks without changing the process route. Publication, deployment, credential,
destructive, migration/cutover and external-contract effects remain pending until their actual authority is
satisfied; naming a protected operation in a routine marker refuses that effect without selecting
heavyweight ceremony. This is deliberately a trusted-repository-writer reliability boundary: it prevents
accidental mistakes and drift, but does not claim adversarial protection from a repository writer able to
alter Actions workflows or spoof a name-based check context. The exact activation state, deferred stronger
alternatives, and observed platform limitations are recorded in
.fsgg/routine-eligibility-activation.json.
The former automatic legacy board writer is retired under ADR-0091. The tracked
coord-board-reconcile workflow is now a manual-only,
credential-free diagnostic: it has no repository permissions, checkout, App/token/secret access, REST client
or reconcile --apply path. It reports that board state was not evaluated and does not claim an empty or
clean board, project Ready state, successful legacy reconciliation, or activation of a V2 board writer. The
historical failed writer run remains failed. No reconcile context is added to native protection.
For the accepted selected profile, observer failure stays outside delivery authority. Missing, corrupt, unsupported, inconsistent or unreachable telemetry produces a typed unavailable/invalid result and cannot authorize source or a protected effect. An otherwise valid native exact-head delivery remains valid. Missing usage remains unknown and cannot establish collection coverage or an efficiency result; the current functional/economics boundary is recorded in the acceptance report.
The coherent FS.GG.Coord.Cli package also carries the host-neutral
FS.GG.Telemetry.Dashboard projection and embedded UI. Its foreground local
server uses framework-only HttpListener: it resolves one explicit
workspace/repository association, reads only workspace-scoped Store snapshots,
binds loopback, and exchanges a one-use URL capability for a bounded browser
session. It adds no dependency from the CLI to the optional telemetry Host,
Akka, ASP.NET, Python, or Node. The independently versioned Host consumes the
same projection and assets behind its separate access-key and workspace
authorization boundary.
Runtime usage, lifecycle chains, critique/feedback structure, and roadmap acceptance projection are one
compiled FS.GG.Coord.Cli authority. Provider adapters immediately reduce JSON/CSV into closed F# records;
canonical output exposes content digests and aggregate counters, never source paths or conversation content.
Lifecycle terminal events join immutable runtime/history receipts before sealing, and GitHub comment ids
elect concurrent children. Roadmap closure derives heads and outcomes from content-addressed receipt bytes,
then limits rendering and verification to one marker-bounded unit. Semantic critique, finding disposition,
exceptions, and merge authorization remain independent judgment boundaries.
Durable private telemetry keeps its SQLite implementation in FS.GG.Telemetry.Store; the coordination CLI
references that assembly and retains its existing command facade. FS.GG.Telemetry.Contracts and
FS.GG.Telemetry.Client add the dependency-light HTTPS envelope, scoped receipt and bounded retry edge.
The optional FS.GG.Telemetry.Host executable alone carries ASP.NET Core and the pinned Akka actor runtime.
Workers atomically publish bounded, immutable typed batches into private inboxes, while one host drain
serializes SQLite writes.
Readers use read-only WAL snapshots; a stable host writer lock plus native-identity deduplication protects
recovery and old/new process overlap. The remote receiver uses authenticated, explicitly enrolled workspace
stores, a fixed actor set, host-wide admission bounds, and an isolated blocking-I/O dispatcher; it never accepts
producer-selected paths or treats an HTTP status without a verified receipt as acknowledgement. No actor
remoting, network-shared database, collection hook, or publication path is introduced.
The host ships as its own Linux x64 .NET tool and release line, outside the coordination CLI's three-package
coherent set. Its private browser surface authenticates separately provisioned access keys, binds bounded
sessions to explicit workspace allowlists, and projects only host-selected, provenance-verified snapshots.
Offline host commands own initialization, producer enrollment, readiness checks, and atomic data-only
backup/restore; configuration, TLS material, and credential recovery remain an operator-owned companion
boundary. The host release contract keeps source acceptance,
publication, and deployment qualification as separate states.
Future explicit codex exec --json --ephemeral launches can pass through the source-only runtime adapter. Its
closed private assignment and content-discarding JSONL projector publish admission/start/turn-usage/terminal
facts to that same inbox; current collaboration launches and generated workspaces remain unwired. The maintained
schema and operator contract is the local telemetry store reference.
Adoption is publish-before-use: release the compiled command family, verify a receiver against those exact
package bytes, flip both item-process skills together, regenerate Claude projections from .agents, and
only then delete the Python compatibility implementations and references.