Skip to content

Slice 0: the repository skeleton (D1.25) - #168

Merged
Hafeok merged 14 commits into
mainfrom
claude/affectionate-darwin-89uq7g
Nov 6, 2026
Merged

Hafeok merged 14 commits into
mainfrom
claude/affectionate-darwin-89uq7g

Conversation

@Hafeok

@Hafeok Hafeok commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Refs #167

Step 0 of the order of work, D1.25`` (decision-driven-design 799cbe84f2a010fb5b65a687eb253666948c73c8). It carries out the approved plan, with the answers given on approval.

Head c81271474341dbf0aa1a66933d7968f3d2715b4e: all eleven gates.yml jobs green in run 41638721267, the AOT matrix included. main is still 53bd547, so there was nothing to reconcile.

Left for you

  • ADR-0013 is Proposed. Your signed commit changes it to Accepted (date) and adds accepted-by to releases/v0.8.0-preview.2.yaml, as you said.
  • Required checks. Add the new jobs to main's: build-test, aot (linux-x64), aot (linux-arm64), aot (osx-arm64) and aot (win-x64).
  • The merge. Fast-forward main after an Approved at <sha> comment.

What it adds

  • ADR-0013. It names the test stack, xunit v3 on Microsoft.Testing.Platform and CsCheck, and admits the three analyzers. It states that no analyzer assembly ships; the DD generator adds internal attribute types and one [assembly: ArchLayer] to each of our assemblies. It amends ADR-0004 D1's wording: Adr= is on the PackageVersion. Its consequence: every DD rule is on as an error, and the first public interface needs a decision set in the package's format.
  • The build policy:
    • global.json: SDK 10.0.401, latestFeature, Microsoft.Testing.Platform;
    • Directory.Build.props, Directory.Build.targets and Directory.Packages.props;
    • .editorconfig and .globalconfig (dd_require_layer);
    • nuget.config, .gitattributes;
    • eng/BannedSymbols.txt.
  • The build's own errors:
    • PCLI0001: a PackageVersion with no Adr=;
    • PCLI0002: an Adr= naming no ADR;
    • PCLI0003: a reference from outside the entry's Scope=;
    • PCLI0004: a library with no API baseline;
    • PCLI0005: a host built without a commit.
  • The eight projects of ADR-0002 D1, each referencing only the layer below. Each library is AOT-compatible and trimmable, with a baseline that holds only #nullable enable.
  • product, one Native AOT binary. product --version prints product 0.8.0-preview.2 (commit <sha>), and every other argument exits 2.
  • CI: build-test, and aot (<rid>) for linux-x64, linux-arm64, osx-arm64 and win-x64, each on its own OS's runner, with eng/gates/smoke.sh.
  • no-toolchain: the only toolchain is .NET, pinned in global.json. A Rust, Node or Python file anywhere in the tree fails it.
  • The Varve admission test passes: Varve.Turtle and Varve.Rdf 0.1.0-preview.15 reproduce sha256:10cd90893f7fadb1bdf80064a43795be95e2175684c6d3a93a7736abc7c2156b for acme-shop.ttl.

Packages, resolved from nuget.org on 2026-11-03

Package Version Adr= Scope=
Varve.Rdf 0.1.0-preview.15 0004
Varve.Iri 0.1.0-preview.15 0004
Varve.Turtle 0.1.0-preview.15 0004 tests/
xunit.v3 4.0.2 0004 tests/
CsCheck 5.0.1 0004 tests/
DecisionDriven.Analyzers 0.1.0-preview.10 0013
Microsoft.CodeAnalysis.PublicApiAnalyzers 5.6.0 0013
Microsoft.CodeAnalysis.BannedApiAnalyzers 5.6.0 0013

The AOT binaries

Runtime Runner Binary Size Smoke at c812714
linux-x64 ubuntu-24.04 product 1,829,032 bytes passed
linux-arm64 ubuntu-24.04-arm product 1,914,896 bytes passed
osx-arm64 macos-15 product 1,766,640 bytes passed
win-x64 windows-2025 product.exe 1,692,672 bytes passed

The sizes are from run 41638171084 at 0bbb9ff, which carries the same code.

The planted failures: permanent tests

As approved, there are no plant commits. BuildPolicyTests builds each plant on every run. Here are the plants built directly:

ProbePlant=no-adr: exit 1
  error PCLI0001: PackageVersion 'ProductCli.PlantedPackage' carries no Adr= naming the ADR that admits it (ADR-0004 D1, as amended by ADR-0013 D4).
ProbePlant=dangling-adr: exit 1
  error PCLI0002: PackageVersion 'ProductCli.PlantedPackage' cites Adr="9999", and docs/decisions/ holds no ADR-9999-*.md (ADR-0013 D4).
ProbePlant=out-of-scope: exit 1
  error PCLI0003: ProductCli.PolicyProbe (src/ProductCli.PolicyProbe/) references 'CsCheck', whose register entry is scoped to tests/ (ADR-0013 D4).
ProbePlant=reflection-json: exit 1
  error IL2026: Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresUnreferencedCodeAttribute' …
  error IL3050: Using member 'System.Text.Json.JsonSerializer.Serialize<TValue>(TValue, JsonSerializerOptions)' which has 'RequiresDynamicCodeAttribute' …

A test run of ProductCli.Host.Tests (--output Detailed, Release):

passed ProductCli.Host.Tests.BuildPolicyTests.TheProbeBuildsWithNoPlant (2s 023ms)
passed ProductCli.Host.Tests.BuildPolicyTests.AnEntryWithoutAdrFails (1s 698ms)
passed ProductCli.Host.Tests.BuildPolicyTests.AnEntryCitingNoAdrFileFails (2s 278ms)
passed ProductCli.Host.Tests.BuildPolicyTests.ATestOnlyPackageInProductCodeFails (2s 954ms)
passed ProductCli.Host.Tests.BuildPolicyTests.ReflectionBasedJsonInALibraryFails (1s 915ms)
passed ProductCli.Host.Tests.LayeringTests.AReferenceFromLayer7WithinItsLayerIsRefused (2s 952ms)
passed ProductCli.Host.Tests.LayeringTests.AnUnplacedProjectIsRefused (2s 609ms)
passed ProductCli.Host.Tests.LayerTableTests.TheAssemblyDeclaresItsLayer × 8 (layers 0 to 7)
passed ProductCli.Host.Tests.VersionTests.VersionWritesTheVersionAndTheSourceCommitAsOneLine (901ms)
passed ProductCli.Host.Tests.VerbTests.AVerbNamesTheStepItArrivesIn × 13
passed ProductCli.Host.Tests.VerbTests.AnArgumentThatIsNoVerbIsNamed (940ms)
passed ProductCli.Host.Tests.VerbTests.NoArgumentIsRefused (52ms)
passed ProductCli.Host.Tests.VerbTests.VersionWithAnArgumentIsRefused (54ms)
passed ProductCli.Host.Tests.ArgumentPropertyTests.EveryArgumentVectorButVersionExitsTwoWithOneLine (4s 045ms)
  total: 33, failed: 0, succeeded: 33

The whole suite, in build-test: 43 tests, 0 failed. The other 10 tests:

  • each layer's upward probe, layers 0 to 6;
  • the downward probe;
  • the Varve admission test and the check of the vendored files' hashes.

Every commit builds green

Before the push, each commit was checked out into a clean worktree and run through commits, adr-shape, adr-text, no-toolchain and selftest. From 748673f on, each also ran dotnet build -c Release, and from fb989e3 on, dotnet test -c Release. All passed. The table is in the traceability record.

Commit verification, read through the REST API

SHA verified reason subject
3587d29197e194e8ca3279ded1b0c5ba408dc017 true valid docs(decisions): ADR-0013, build-time analyzers and the test stack
303bc8c8caa48d39b30f0371ae296f5bd38870fd true valid build: the build policy and the package register
83827833e12c372800e2b9c187dab42277c1ee5e true valid ci(gates): no-toolchain allows exactly one toolchain, .NET
748673f20b7ec2350aa78371d6f769a5613f2336 true valid feat: the seven layer libraries, with their API baselines
191b7904d01f0ad679ee709b9284a06abe7e47e7 true valid feat(host): product --version, and exit 2 for every verb not yet built
fb989e3af9c999ace2e470d0764cd553627d6002 true valid test: the layering probes, one per layer
af27ad42f0ef624fca60867b324bdf3eeef45e24 true valid test(canonical): the Varve admission test, with the vendored example
b0048205d0d88bd1a520aea66521ba7ecf059e71 true valid test(host): the version bytes, the verb messages, the argument property
ceb7226625dba590fa143e7793e967382b290906 true valid test(build): the build policy's planted failures, built on every run
0d2836d83a11c329cda896b6dbbc9bddac6dc854 true valid ci(gates): build-test and the AOT publish and smoke matrix
0bbb9ff7a880d3daa1c8d967b1aabec0039b3baf true valid docs: AGENTS.md and README for the build
4a6303f068bc05fb65cbab3109d0c6f8f05a6c81 true valid releases: v0.8.0-preview.2, the repository skeleton
c81271474341dbf0aa1a66933d7968f3d2715b4e true valid docs(traceability): the session record for #167

Deviations from the plan

The traceability record gives the reason for each.

  • Plants: no plant commits; they are permanent tests instead, as approved.
  • Probes and Scope=: a probe under tests/fixtures/ is checked as if it were at src/<name>/, so it can stand in for a library.
  • Analyzer configuration: .editorconfig and .globalconfig are named for every project. The SDK's discovery started from source files, so it passed neither to a library with no source. That meant no dd_require_layer and no IL severities in the seven libraries. A probe caught it.
  • Files beyond the plan's list:
    • .gitattributes (LF on the Windows runner);
    • tests/Harness/;
    • one source file per probe.
  • The layer-6 probe references the host, which is layer 7.
  • The publish folder holds only product and its symbol file.

Nothing was written to Varve, Writ or decision-driven-design, and no gap there was found.

Developed with AI assistance under human review.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg

Hafeok and others added 14 commits November 3, 2026 12:39
Proposed. It names the test stack ADR-0004 D2 admits once slice 0's plan
names it: xunit v3 on Microsoft.Testing.Platform, and CsCheck, chosen over
FsCheck for having no dependencies (FsCheck brings FSharp.Core). It admits
the analyzers ADR-0002 D3 requires: DecisionDriven.Analyzers for layering
(DD0001), and PublicApiAnalyzers and BannedApiAnalyzers. It states that no
analyzer assembly ships, and the one thing that does reach our assemblies:
the DD generator's internal attribute types and one [assembly: ArchLayer].

D4 amends ADR-0004 D1's wording, as approved on #167: the register entry is
the PackageVersion, which carries Adr= (and Scope= when test-only). ADR-0004
is not edited (ADR-0001 D3).

The consequence is stated: every DD rule is on as an error, and the first
public interface needs a decision set in the package's format.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
The .NET build policy of ADR-0002, ADR-0003 and ADR-0004 as amended by
ADR-0013, after Varve's pattern, with nothing copied that names Varve's
packages or ADRs.

- global.json: SDK 10.0.401, rollForward latestFeature, and
  Microsoft.Testing.Platform as the test runner.
- Directory.Build.props: net10.0, nullable, warnings as errors,
  latest-recommended analysis, code style in the build, deterministic
  builds, ArchFamily=ProductCli, the product version 0.8.0-preview.2, and
  the supported runtime identifiers (ADR-0003 D5). The root's .editorconfig
  and .globalconfig are named for every project: the SDK's discovery
  starts from a project's source files, and a library with none would build
  without them.
- Directory.Build.targets: every src/ library IsAotCompatible and
  IsTrimmable with the AOT, trim and single-file analyzers, a public API
  baseline required (PCLI0004), the banned-symbol list on all product code,
  the host's source commit stamped (PCLI0005), and the register checks:
  PCLI0001 (no Adr=), PCLI0002 (Adr= names no ADR), PCLI0003 (a reference
  from outside Scope=).
- Directory.Packages.props, the register: Varve.Rdf and Varve.Iri
  0.1.0-preview.15 (Adr 0004); Varve.Turtle 0.1.0-preview.15, xunit.v3
  4.0.2 and CsCheck 5.0.1, test-only (Adr 0004, Scope tests/);
  DecisionDriven.Analyzers 0.1.0-preview.10, and PublicApiAnalyzers and
  BannedApiAnalyzers 5.6.0 (Adr 0013). Resolved from nuget.org on
  2026-11-03.
- .globalconfig: dd_require_layer, so no ProductCli project stays unplaced.
- .editorconfig: the IL trim and AOT diagnostics at error; nothing lowered.
- .gitattributes: LF everywhere, so the gate scripts run under bash on the
  Windows runner; the vendored fixtures are never converted.
- nuget.config: nuget.org only, with source mapping.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
The rule that replaces "no Rust, Node or Python toolchain file at the
root", as the plan on #167 states it. The check fails when:

1. a Rust, Node or Python toolchain file (the same list), or a
   requirements*.txt, is anywhere in the tree outside .git/, bin/, obj/
   and artifacts/, not only at the root;
2. the root has no global.json, or its sdk.version is not 10.0.401, its
   sdk.rollForward not latestFeature, or its test.runner not
   Microsoft.Testing.Platform;
3. a global.json exists below the root.

The job keeps its name, so a required-check setting still matches; its
step is retitled. selftest's clean case now carries the pin, and it gains
plants for a package.json under src/, a missing pin, another SDK version,
another rollForward, no test runner and a second global.json, and a clean
case showing bin/, obj/ and .git/ are not the tree.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
Layers 0 to 6 of ADR-0002 D1: ProductCli.Vocabulary, Canonical, Shapes,
Draft, Simulation, Runner and Session. Each declares its ArchLayer and
references only the project directly below it, so the chain is ADR-0002
D1's diagram. ProductCli.Canonical references Varve.Rdf, the one runtime
package of slice 0.

Each is empty apart from its public API baseline (ADR-0002, Consequences):
PublicAPI.Shipped.txt holds "#nullable enable" and PublicAPI.Unshipped.txt
nothing. ProductCli.slnx lists them.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
ProductCli.Host, layer 7 and the composition root, publishes one Native AOT
executable, product (ADR-0003 D1). Its assembly keeps the family name, which
DD0001 reads; the published binary is renamed after ILC writes it.

- product --version writes "product <version> (commit <sha>)" and a line
  feed to stdout as ASCII bytes, the same on every platform, and exits 0.
  Both values are read from the assembly's metadata: the informational
  version, and the SourceCommit the build stamps from SourceRevisionId.
- Every other argument vector exits 2 with one line on stderr. A verb of
  chapter 3 names the step of the order of work it arrives in (the table
  approved on #167); anything else is named as not a verb, escaped to
  printable ASCII so the line stays one line. The line is not a format.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
ADR-0002 D3 and its consequence for slice 0: one test per layer proving the
analyzer refuses an upward reference. The probe, under tests/fixtures/
layering/, is built with dotnet build under the repository's real build
files (approved on #167): Directory.Build.*, .globalconfig and the
analyzer package's build props, so the tests prove the wiring as well as
the rule. The tests read DD0001 in the compiler's output (ADR-0006).

- Layers 0 to 6: a probe at layer L referencing the layer L+1 project fails
  with "DD0001: 'ProductCli.Probe' is at layer L and references
  'ProductCli.<L+1>', which is at layer L+1".
- Layer 7: nothing is above it, so its refusal is the same-layer case, a
  probe at 7 referencing ProductCli.Host.
- An unplaced probe fails with DD0001 (dd_require_layer).
- The positive probe: layer 1 referencing ProductCli.Vocabulary builds,
  with no DD0001.

A probe builds into its own temporary directory, against the solution's
outputs (--no-dependencies, RestoreRecursive=false), so the eight test
projects run their probes in parallel and write nothing under src/. The
probe has one source file beside its project, which places the compilation
in the repository for the compiler's configuration files. Test projects
are unlayered: DD0001 does not check a *.Tests assembly.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
ADR-0004 D2's evidence for the Varve preview this repository references,
0.1.0-preview.15 (tag 6cdb402). The earlier canonicaliser checks ran on
Varve main at c0a776b, 120 commits later; this is the first check of the
bytes that ship.

decision-driven-design's applications/sdlc/what/examples/acme-shop.ttl and
applications/sdlc/what/vectors.json are vendored under tests/fixtures/ddd/
at 799cbe84f2a010fb5b65a687eb253666948c73c8, with SOURCE.md giving the
commit, the paths and each file's sha256:
- acme-shop.ttl 4018cb9ad45c13fceafd7e585649777e7c8b9e7231fab46a42eb3286df91b777
- vectors.json  953666f42051e50632290f73200298c9b5d518d2a153c20a31f9589f94374a1d

The test checks both hashes against SOURCE.md, parses the Turtle with
Varve.Turtle, canonicalises it with Varve.Rdf's RdfCanonicaliser, and
asserts that the sha256 of the canonical N-Quads bytes is vectors.json's
graphHash, sha256:10cd90893f7fadb1bdf80064a43795be95e2175684c6d3a93a7736abc7c2156b.
It passes. Varve.Turtle is test-only (Scope tests/).

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
Each test runs the built host as a process and reads its stdout and stderr
bytes (ADR-0006).

- --version: stdout is exactly "product <version> (commit <sha>)\n", where
  the version is the build's and the commit is git rev-parse HEAD; stderr
  is empty; the exit code is 0.
- Each chapter-3 verb: exit 2, nothing on stdout, and exactly its line on
  stderr. No argument, an argument that is not a verb, and --version with
  an argument each have theirs.
- The property (CsCheck, 100 cases): any argument vector but exactly
  ["--version"], the empty one included, exits 2 with nothing on stdout and
  exactly one line on stderr beginning "product: ".
- The layer table: each built assembly's [assembly: ArchLayer(n)], read
  from its metadata where DD0001 reads it, is its row of ADR-0002 D1.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
The plants of #167 are permanent tests rather than commits shown red and
removed, as approved there: every commit that reaches main builds green,
and these are the evidence, on every run.

tests/fixtures/build-policy/ holds a library probe, checked as product code
at src/ProductCli.PolicyProbe/. BuildPolicyTests builds it clean, then once
per plant, and reads the error in the build's output:
1. a PackageVersion with no Adr=: PCLI0001;
2. a PackageVersion citing Adr="9999": PCLI0002;
3. CsCheck, scoped to tests/, referenced from the library: PCLI0003;
4. reflection-based JsonSerializer.Serialize in the library: IL2026 and
   IL3050, as errors (ADR-0003 D2).

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
ADR-0003 D3 and D5 in gates.yml, on every pull request.

- build-test (ubuntu-24.04): dotnet build -c Release, warnings as errors,
  then dotnet test -c Release, the probes included.
- aot (<rid>), one job per supported runtime identifier, each on its own
  operating system's runner, since Native AOT does not cross between them:
  linux-x64 on ubuntu-24.04, linux-arm64 on ubuntu-24.04-arm, osx-arm64 on
  macos-15, win-x64 on windows-2025. Each checks the RID is in
  ProductCliRuntimeIdentifiers, runs dotnet publish -c Release -r <rid>,
  runs the smoke test on the binary, and reports its size.
- eng/gates/smoke.sh <binary> <version> <commit>: --version must write
  exactly the expected line (compared with cmp), nothing to stderr, and
  exit 0; "domain" must exit 2 with nothing on stdout. selftest gains its
  plants: another version, another commit, a CRLF, exit 1, output on
  stderr, a verb exiting 0, and a verb writing to stdout.

Both jobs check out the pull request's head, so the stamped commit is the
head under review. actions/setup-dotnet is pinned to v6.1.0,
0f4a354dd5547a28c70b7853dbd95d5b67886e73, and reads global.json.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
AGENTS.md:
- the build, test, publish and smoke commands;
- the layer table of ADR-0002 D1, and the projects that do not exist yet;
- the package rule: a new package needs an ADR and a PackageVersion with
  Adr=, and Scope= when test-only (ADR-0004 as amended by ADR-0013), with
  the PCLI errors that enforce it;
- the gates table, with build-test, aot (<rid>) and the new no-toolchain
  rule;
- that the host's exit-2 line is not a format and nothing parses it.

README.md: how to build, test and publish, and the supported runtime
identifiers.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
The descriptor of slice 0 (ADR-0007 D3), on D1.25 at decision-driven-design
799cbe8, ADR-0002, 0003, 0004 and 0013, and Varve.Rdf 0.1.0-preview.15. No
sdlc: edition: slice 0 needs none.

The Native AOT binary sizes, from gates.yml run 41638171084 at 0bbb9ff:
linux-x64 1,829,032 bytes; linux-arm64 1,914,896; osx-arm64 1,766,640;
win-x64 1,692,672.

accepted-by is left for the maintainer, who adds it with ADR-0013's
acceptance in his own commit.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
The prompt and the approval, verbatim; what was read; what the session
found, including the analyzer configuration the SDK did not pass to a
library with no source; the commits with GitHub's verification; every
commit checked on its own; gates.yml run 41638171084 on every job, with
the AOT binary sizes; the planted failures' errors; and the deviations
from the plan, with their reasons.

Refs #167

Signed-off-by: Emil Okkels Klein <emil@okkels-klein.dk>
Claude-Session: https://claude.ai/code/session_01NbWSYTf9GvhrtXDW5cHcYg
Refs #167

Signed-off-by: Emil Klein <emil@okkels-klein.dk>
@Hafeok

Hafeok commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator Author

Approved at 4ce90e9c2a28c3a010c95419a8dbb88dbcfba595.

@Hafeok
Hafeok merged commit 4ce90e9 into main Nov 6, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant