Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ be able to decide from the body alone whether the work is sound, without
reconstructing a finding from the diff.
-->

Completes: none

<!--
`Completes:` names every issue this pull request completes, as `#N, #M`, or
`none`. It is not a closing keyword (#108): each issue it names needs a
`Closes #N` trailer line on a commit in the pull request, and
`eng/land.cs` refuses the landing when one is missing, or when this line is
(#209).
-->

## What this changes, and why

## What was measured
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,33 @@ jobs:
--out "$RUNNER_TEMP/sbom-$version" --syft "${{ steps.syft.outputs.cmd }}" --descriptor "$descriptor"
done

- name: A descriptor's digests rewritten on a later day
# #248, docs/releases.md rule 3 (amended 2026-11-10): eng/release.cs
# --sbom writes the SBOMs and sets date: to the day it ran, in one
# edit, and eng/sbom.cs --descriptor, created at that date, matches
# every line. The descriptor is the newest with an sbom: block, renamed
# and backdated, in a scratch releases/ directory, so nothing in the
# repository changes.
if: runner.os == 'Linux'
shell: bash
run: |
set -euo pipefail
version=v0.0.0-sbom-rewrite
scratch="$RUNNER_TEMP/sbom-rewrite"
mkdir -p "$scratch/releases"
newest=$(grep -l '^sbom:' releases/v*.yaml | sort -V | tail -1)
sed -e "s/^version: .*/version: $version/" -e "s/^date: .*/date: 2026-01-01/" \
"$newest" > "$scratch/releases/$version.yaml"
dotnet run eng/release.cs -- --sbom "$version" --releases "$scratch/releases" --out "$scratch/written"
today=$(date -u +%F)
grep -qx "date: $today" "$scratch/releases/$version.yaml" \
|| { echo "::error::release.cs --sbom did not set date: to $today"; exit 1; }
if grep -rhoE '"created": *"[^"]*"' "$scratch/written" | grep -v "\"$today"'T00:00:00Z"' ; then
echo "::error::an SBOM was not created at ${today}T00:00:00Z"; exit 1
fi
dotnet run eng/sbom.cs -- --version "${version#v}" --created "$today" \
--out "$scratch/again" --descriptor "$scratch/releases/$version.yaml"

- name: Upload the test results
# #126: every run keeps its TRX, pass or fail, so a slow or red run
# can be explained test by test from its own artifacts.
Expand Down
59 changes: 51 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ severity on every project in `src/`. Every ADR is also a decision set in
**Every milestone ends in a release, and a milestone PR carries its
descriptor** (ADR 0102, `docs/releases.md`). The close-out:
- adds `releases/<version>.yaml` (`dotnet run eng/release.cs -- --draft
<version>` starts it), with a commit whose trailer line says `Closes
#<milestone issue>`;
<version>` starts it, and `-- --sbom <version>` writes its digests and sets
its `date:` to that day, whenever they change), with a commit whose
trailer line says `Closes #<milestone issue>`; `eng/land.cs` refuses one
dated more than three days before the landing (#248);
- runs `dotnet run eng/changelog.cs -- --release <version>`, because
`CHANGELOG.md` is the projection of `releases/` and never edited by hand;
- brings `README.md`'s **Status** section current: its opening line names the
Expand Down Expand Up @@ -159,10 +161,51 @@ Conventional commits, one logical change each, and three things in each:
exactly**, and the report. The model is the identifier the session's metadata
reports. A tool that will not write it says so in the record, and the maintainer
fills it in: a blank model is an incomplete record. **A commit message never
carries a model's identifier** (the string an API takes to select a model); a
tool's co-author trailer naming the product is not one (ADR 0033, amended
2026-10-02). Prose says "developed with AI assistance under human review" and
**names no product**.
carries a model's name** (a family word, alone or after the product, followed
by a version); a model's identifier (the string an API takes to select a
model) is allowed, and so is a co-author trailer naming only the product
(ADR 0241, superseding ADR 0033's amendment of 2026-10-02). Prose says
"developed with AI assistance under human review" and **names no product**.

## Issues are the work log

The issue, not the session's transcript or close-out, is where anyone looks to
see what is being worked on, what was decided and what is left.

- **Starting.** Work starts from an issue. The session comments on it when it
starts (branch, and the prompt step it is working from) and posts its plan
there once the plan is agreed. Before opening an issue, search for an open
one that covers the work; a duplicate is closed pointing at the original.
- **While working.** Decisions taken, questions for the maintainer and blockers
go on the issue when they arise, not only in the close-out. The pull request
names its issues as bare #N in prose.
- **Found work is filed when it is found.** A defect, a follow-up or a deferred
part becomes its own issue at once, linked from the issue it came from. A
close-out, a TODO or a commit message is not a place to keep work.
- **Landing.** Before asking for approval, the session lists in the pull
request every issue the change touches, and its `Completes:` line names
those it resolves fully, or says `none`:
- each one it resolves fully gets its own `Closes #N` trailer, and
`eng/land.cs -- N` lists what the push will close and refuses a landing
whose `Completes:` line is missing or names an issue no trailer closes
(#209);
- each one it resolves in part gets a comment saying what landed and what is
left, and tracking issues (#15 and the milestone issues) get their boxes
ticked.

"The session" here and below is whichever session lands. The maintainer's
delegate lands and checks the closings (ADR 0239); a cloud session posts its
head, lists the issues its change touches, and asks.
- **After landing.** The session checks that every issue it meant to close is
closed: `eng/land.cs -- N --main`, once `main` has moved, prints every issue
a pushed trailer names that is still open, with its commit, and exits
non-zero, as it does when GitHub cannot be read. It closes nothing itself.
If a trailer did not take, the session closes the issue by hand with a
comment naming the commit on `main`.
- **Stopping.** Work abandoned or superseded is closed as not planned, with a
comment saying why and what replaces it.
- The traceability record and the close-out link the issues; they do not
replace them.

## `Varve.Store` behaviour

Expand Down Expand Up @@ -567,8 +610,8 @@ exists.
- A native dependency, or any package that ships a native asset.
- `System.Uri` where an IRI is meant. Use `Varve.Iri` (ADR 0004).
- A commit with no issue reference or no DCO sign-off, or one whose message
carries a model's identifier; a long-lived branch; a trunk left
un-releasable.
carries a model's name (its identifier is allowed, ADR 0241); a long-lived
branch; a trunk left un-releasable.
- Reflection, `Reflection.Emit` or `dynamic` in shipped code.
- A `Common` / `Core` / `Utils` / `Abstractions` package, or an upward or
same-layer reference. A `.cs` file without the MPL-2.0 notice (ADR 0031).
Expand Down
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,9 @@ and tied to issues ([ADR 0033](docs/adr/0033-commit-traceability.md)).
and the model, named exactly**, and the report it returned. The model is the
identifier the session's metadata reports; where the tool will not write it,
the record says so and the maintainer fills it in. A commit message never
carries a model's identifier (ADR 0033, amended 2026-10-02).
carries a model's name; a model's identifier is allowed, and so is a co-author
trailer naming only the product (ADR 0241, superseding ADR 0033's amendment of
2026-10-02).

Prose in `README.md`, `CONTRIBUTING.md`, `GOVERNANCE.md` and the documentation
describes the project as developed with AI assistance under human review and
Expand Down
5 changes: 5 additions & 0 deletions docs/adr/0033-commit-traceability.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ the records.
under *The wording rule splits*: what "named exactly" means for the model, and
that a commit message never carries a model's identifier.

**Superseded in part by [0241](0241-model-names-refused-identifiers-allowed.md)**
(filed 2026-11-10): the amendment's ruling on the commit message is reversed.
A commit message never names a model, and a model's identifier is allowed. The
record still names the model exactly.

## Context

The Mind Over Machine stewardship standard asks for one thing under this
Expand Down
43 changes: 43 additions & 0 deletions docs/adr/0102-a-release-is-a-descriptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ revisited.
[0224](0224-supply-chain-transparency.md): descriptor format 2, with an `sbom:`
list naming every artefact and its SBOM's digest; see the end.

**Amended 2026-11-10** (ADR 0068), by #248: a format-2 descriptor's `date`
is the day its `sbom:` digests were last written, and `eng/land.cs` refuses
one more than three days old; see the end.

## Context

A release is a decision, and this repository files decisions: an ADR, a
Expand Down Expand Up @@ -413,3 +417,42 @@ sbom:
**The ledger.** The rulings of this ADR stand; the ruling of this block is
`TheDescriptorNamesEveryArtefactsSbom`, unaccepted until the maintainer
accepts it.

## Amendment, 2026-11-10 — `date` follows the digests

Filed by the session for #248 (step 3 of the "issues are the work log"
session), on the maintainer's ruling on #248 of 2026-11-10, unaccepted until
he accepts it (ADR 0066). It adds detail to what `date` means for a format-2
descriptor, and leaves ADR 0224 as it is.

**The problem.** `date` was the day the descriptor was written, set once by
`--draft`. A milestone pull request takes days to land, and its digests are
rewritten on the way, while the date stays. Under ADR 0224 point 2 (D3) every
SBOM is created at `date`, so a correction by hand changes every digest:
preview.18's descriptor says 2026-10-27, and its digests were last written on
2026-11-06, the day it landed.

**The detail.** The preparation date is **the day the digests were last
written**, which is the preparation date of the artefacts being released:
- `eng/release.cs -- --sbom <version>` runs `eng/sbom.cs` created at the
current UTC day, replaces the `sbom:` block with its `sbom.txt` and sets
`date:` to that day in one edit, then regenerates `CHANGELOG.md`;
- `eng/land.cs -- N` refuses a descriptor the head adds or changes whose
`date:` is more than three days before the landing day (UTC), and prints
that command. Three days is the release gate's freshness window;
- a descriptor with no `sbom:` block keeps its date, and a cut descriptor is
never edited (rule 9), so preview.18 keeps 2026-10-27.

`CHANGELOG.md` is still a projection that does not depend on when the cut
ran. `docs/releases.md` rule 3 and rule 11 say the same.

**Not chosen:**
- a second field, `sbom-created:`, beside a free `date:`: a format bump to
fix a date, for two fields that move together anyway;
- the landing day exactly: a landing across midnight, or one waiting a day,
would need a new head and a new run for nothing;
- the tag's date: `CHANGELOG.md` is generated inside the pull request,
before any tag exists.

**The ledger.** No ruling changes; the block adds detail to the descriptor's
`date` (ADR 0068 point 2).
92 changes: 92 additions & 0 deletions docs/adr/0241-model-names-refused-identifiers-allowed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# 0241 — A commit message never names a model; a model's identifier is allowed

## Status

**Accepted — filed unaccepted by the session for #257, 2026-11-10** (ADR 0066),
its ruling accepted on #259 on 2026-11-11, at the maintainer's word, by his
delegate (ADR 0239). Decided
by the maintainer on 2026-10-27, in the scope of
[#169](https://github.com/Hafeok/Varve/issues/169). Acceptance is the
maintainer's act, or his delegate's for a decision he has read (ADR 0239).

**Supersedes one ruling of [0033](0033-commit-traceability.md)**,
`ModelNamedInTheRecordNotTheCommit`, as its amendment of 2026-10-02 stated it.
The half about the record is restated here unchanged; the half about the
commit message is reversed. Under ADR 0068 point 2, that reversal is a change of
meaning, so it is a supersession, not an amendment. The ruling moves to this
ADR's set under the same key (ADR 0068 point 4).

## Context

ADR 0033's amendment of 2026-10-02 said a commit message never carries a
model's *identifier*, the string a provider's API takes to select a model,
and allowed a co-author trailer that names the product. Its reason was that a
commit message cannot be corrected without rewriting the trunk (ADR 0032),
and the record is where the fact can be corrected.

In practice the trailer did the opposite. The tool these sessions use writes a
co-author trailer that names the model by its marketing name, family and
version (`Claude Opus 5.5`). `main` carried 241 such commits by 2026-10-27,
all signed and merged, so they stay. The name was in the commit, and the
identifier was barred. The identifier is what a reader can resolve exactly
and what the record holds; a name is the form that drifts and gets
advertised.

On 2026-10-27 the maintainer ruled the other way round, for #169's gate:
**names refused, identifiers allowed.** `AGENTS.md` and `CONTRIBUTING.md`
still said the reverse, and a gate built to the ruling would contradict an
accepted decision. This ADR records the ruling so that the gate, the rule
files and the ledger agree.

## Decision

1. **A commit message never carries a model's name.** A name is a model
family word (`Opus`, `Sonnet`, `Haiku`, `Fable`, `Mythos`, and any family
the gate's configuration adds), alone or after `Claude`, followed by a
version: `Opus 5.5`, `Claude Sonnet 5.5`. The rule covers the subject, the
body and every trailer.
2. **A model's identifier is allowed**, in the API form a provider takes
(`claude-<family>-<major>-<minor>`), as is the bare product name in a co-author trailer:
`Co-Authored-By: Claude <noreply@anthropic.com>`.
3. **The traceability record still names the tool and the model exactly**,
unchanged from 0033's amendment: the model is the identifier the session's
metadata reports. Where the tool will not write it into a pushed file, the
record says so where the model belongs and the maintainer fills it in. A
blank model is an incomplete record.
4. **History is left as it is.** #169's gate checks the commits a pull
request or a `land/` branch adds over `main`, never `main`'s history.

## Alternatives considered

- **Amend 0033 instead.** Rejected: ADR 0068 point 2 makes a change of meaning
a supersession, and this reverses which form is allowed.
- **Refuse both names and identifiers.** Rejected by the maintainer's ruling.
An identifier in a commit is harmless and exact. Refusing it would also
refuse the one form a tool can write mechanically from its own metadata.
- **Keep 0033's rule and fix the trailer.** Not chosen. It leaves the rule
barring the precise form and allowing nothing that says which model wrote
the commit, while the gate that would enforce it does not exist.

## Consequences

- **`AGENTS.md` and `CONTRIBUTING.md` change with this ADR.** **Every
commit** and **Never** state the ruling, and so does `CONTRIBUTING.md`'s
traceability paragraph.
- **#169's gate implements this ADR**, not 0033's amendment. Its rule page
cites both: 0033 for the record, 0241 for the commit message.
- **The 241 commits on `main` that name a model stay.** Nothing rewrites the
trunk (ADR 0032).
- **Until this ADR is accepted**, `ModelNamedInTheRecordNotTheCommit` is filed
unaccepted in this ADR's set, and a citation of it is `CS0618` (ADR 0066).

## Checks

- **Checked against the accepted ADRs.** Touches:
- **0033:** one ruling superseded, as above. Every other ruling stands,
`TraceabilityRecordPerSession` and `ProseNamesNoProduct` included.
- **0032:** the trunk is not rewritten.
- **0066, 0068:** filed unaccepted; the ruling moves under its key.
- **Layer ownership.** None.
- **Analyzer rule.** None here. The gate is #169's, in the commit-message stage
beside `eng/issue-refs.cs` and `eng/dco.cs`.
- **Open questions owned.** None.
8 changes: 8 additions & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,14 @@ Filed by [#101](https://github.com/Hafeok/Varve/issues/101), unaccepted until th
|---:|---|---|
| [0223](0223-provider-legs-by-federated-credentials.md) | The provider legs by federated credentials: GitHub's OIDC token for Entra ID and Google | Filed unaccepted (#101) |

## Model names in commit messages (#257)

Filed by the session for [#257](https://github.com/Hafeok/Varve/issues/257), recording the maintainer's ruling of 2026-10-27 for [#169](https://github.com/Hafeok/Varve/issues/169), unaccepted until he accepts it (ADR 0066). Numbered 0241: 0240 is taken on another branch. It supersedes one ruling of 0033.

| # | Title | Status |
|---:|---|---|
| [0241](0241-model-names-refused-identifiers-allowed.md) | A commit message never names a model; a model's identifier is allowed; supersedes one ruling of 0033 | Filed unaccepted (#257) |

## Conformance — differential testing against Oxigraph

| # | Title | Status |
Expand Down
9 changes: 2 additions & 7 deletions docs/decisions/commit-traceability.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ decisions:
statement: "Prose describes the project as developed with AI assistance under human review and names no product, while records and issues name the tool and model"
accepted-by: mailto:emil@okkels-klein.dk
accepted-at: 2026-09-22T00:00:00Z
- key: ModelNamedInTheRecordNotTheCommit
statement: "The traceability record names the model by the identifier the session's metadata reports, filled in by the maintainer where the tool will not write it, and a commit message never carries a model's API identifier"
accepted-by: mailto:emil@okkels-klein.dk
accepted-at: 2026-10-02T00:00:00Z
- key: AgentsMdHoldsTheRules
statement: "The agent rules live in the vendor-neutral AGENTS.md, and CLAUDE.md is one line pointing at it"
accepted-by: mailto:emil@okkels-klein.dk
Expand All @@ -48,6 +44,5 @@ decisions:
The rulings of [ADR 0033](../adr/0033-commit-traceability.md) still in force, one line each. The ADR is the
narrative; this is what code cites. Acceptance is transcribed from the ADR's Status (ADR 0062).

`ModelNamedInTheRecordNotTheCommit` is the ruling of the amendment of 2026-10-02. It is filed unaccepted,
and its `accepted-at` will be the date the maintainer accepts it, which can be no earlier than the
amendment (ADR 0068).
Moved to a later set: `ModelNamedInTheRecordNotTheCommit`, the ruling of the amendment of 2026-10-02, is
superseded by ADR 0241, which refuses a model's name in a commit message and allows its identifier, in its set.
16 changes: 16 additions & 0 deletions docs/decisions/model-names-refused-identifiers-allowed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
set: model-names-refused-identifiers-allowed
namespace: varve
adr: 0241
decisions:
- key: ModelNamedInTheRecordNotTheCommit
statement: "The traceability record names the model by the identifier the session's metadata reports, filled in by the maintainer where the tool will not write it, and a commit message never carries a model's name, while a model's API identifier and the bare product name are allowed"
accepted-by: mailto:emil@okkels-klein.dk
accepted-at: 2026-11-11T00:00:00Z
---

The ruling of [ADR 0241](../adr/0241-model-names-refused-identifiers-allowed.md),
filed unaccepted by the session for #257 (ADR 0066) and accepted on #259 on
2026-11-11, at the maintainer's word, by his delegate (ADR 0239). It supersedes the ruling of
ADR 0033's amendment of 2026-10-02 under the same key, which moved here from
`commit-traceability` (ADR 0068 point 4).
Loading
Loading