Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ This pattern generalizes beyond ADRs. When any Loaf artifact is later judged to

## Change-First Execution Model

New bounded work uses a Change as its primary contract. The Change folder splits role-named narrative (settles at shaping) from task-file state (mutates during execution), so execution evidence is machine-derivable from git history alone (ADR-022, ADR-023; operating view in [knowledge/work-model.md](knowledge/work-model.md)). The project journal remains the execution trace and resumption protocol.
New bounded work uses a Change as its primary contract. The Change folder splits role-named narrative (settles at shaping) from task-file state (mutates during execution), so execution evidence is machine-derivable from committed content — checkbox-flip history where the merge strategy preserves it, receipt-vouched content where it does not (ADR-022, ADR-023, ADR-027; operating view in [knowledge/work-model.md](knowledge/work-model.md)). The project journal remains the execution trace and resumption protocol.

```
capture → /shape → Change → /implement (task commits) → review → /reflect → /ship
Expand All @@ -243,7 +243,7 @@ SQLite journal_entries # Project-scoped event record across conve

**Changes** define the problem, scope, decisions, verification contract, and definition of done. `loaf change check` validates both layouts and derives the display ladder (captured → shaped → executable → executing → complete, plus verified for cohort members) — no status fields exist anywhere; every state is computed.

**Releases read cohorts.** A change declaring `target_release` opts into the strong gate: cutting that version stable requires the whole cohort executed at flip grade (true `- [ ]`→`- [x]` transitions outside fences, same hunk and label) and receipt-verified, with all criteria passing. The gate is a pure reader of committed evidence — `loaf change verify` is the only surface that runs criteria; stale or failing receipts block with the mechanical remedy named. Prereleases always flow; retargets are reviewable diffs, surfaced and never blocked (ADR-023).
**Releases read cohorts.** A change declaring `target_release` opts into the strong gate: cutting that version stable requires the whole cohort executed and receipt-verified, with all criteria passing. Execution grades as a disjunct — a true `- [ ]`→`- [x]` flip transition in ancestry (outside fences, same hunk and label), **or** a fresh verify receipt vouching for a folder whose every committed box is checked — so the grade holds under every merge strategy, squash included; a receipt cannot exist without the implementation in the tree, which is what keeps the shaping-only attack blocked (ADR-023, ADR-027; PR #154). Release commits may be changelog-only when version files already carry the candidate, the self-carrying shape guardrail 4 proves before guardrail 5 reads the diff (PR #155). In a multi-Change cohort, later members' content stales earlier members' receipts: all cohort receipts re-verify at the final pre-merge tree, terminating because receipt commits are content-free and digest-excluded. The gate is a pure reader of committed evidence — `loaf change verify` is the only surface that runs criteria; stale or failing receipts block with the mechanical remedy named. Prereleases always flow; retargets are reviewable diffs, surfaced and never blocked (ADR-023).

**Releases gate on capability evidence.** `loaf release` validates the capability-evidence registry in-process after the artifact rebuild on every mutating path — a post-rebuild refusal in the shared apply executor and a ninth post-merge guardrail. Resume after a refusal is verify-then-restore with no persisted state; post-merge recovery is a single receipt-only repair commit classified against the parent commit's registry; every registry and candidate-artifact read is symlink-hostile through a shared component-wise regular-file walk (PR #147; change record `docs/changes/20260730-release-evidence-gate/`).

Expand Down Expand Up @@ -396,6 +396,8 @@ The native CLI version must report the package version consistently through the

Any value that must be identical across runtime modes should be injected at build time, not independently resolved by multiple runtime paths. Divergent version discovery creates false positives in every downstream comparison.

One deliberate exception: a dev build's timestamp identity (`<major>.<minor>.<unix timestamp>`, ADR-026) is *not* build-time-injected, because a baked timestamp would break the byte-for-byte reproducibility that `verify:go-artifacts` asserts. It derives at runtime from two facts — absent release build metadata and a source checkout beside the distribution — with the executable's own mtime as the clock. Injecting it via ldflags would be the natural "fix"; it is the one that breaks the build contract.

### Generated Runtime Plugin Artifacts Parsed From Emitted Output

Files the build emits for downstream runtimes to execute — OpenCode `hooks.ts`, Amp `loaf.js`, and any future per-target runtime plugin — must have tests against the **actual emitted file**, not just the generator's input string.
Expand Down
5 changes: 5 additions & 0 deletions docs/STRATEGY.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,15 @@ The implication for both personas: Loaf's value is the *framework* -- mechanical

**Release is separate from shipping.** A Change may land through one or more coherent pull requests; publishing a project version is a distinct operation over already-landed work. CI verifies reproducible outputs and must not silently repair the source branch.

**Evidence that lives in history shape dies in transit; evidence bound to content survives every merge strategy.** The 0.2.20 cut — the first stable candidate the cohort gate ever evaluated — refused a fully implemented, receipt-verified Change because the squash merge had rewritten the checkbox flips its execution grade scanned for, while the content-digest receipt sailed through the same squash untouched. The fix inverted the anchor (executed = flip in ancestry *or* fresh receipt with all boxes checked, ADR-027), and its sibling followed immediately: a self-carrying release has no version-file diff for the release-commit shape check, so that demand now relaxes only under the consistency proof an earlier guardrail already computed. The corollary is a testing pattern: the first stable release after any gate change is that gate's first real test — observed twice now (the alpha.16 evidence canary, then 0.2.20 twice in one day) — so expect the collision there and budget for it (PRs #154, #155).

**Version strings carry narrative weight, and the weight suppresses shipping.** Nineteen `2.0.0-alpha.N` releases in two months trained every cut to read as a statement about 2.0, so cuts stopped and field bugs waited on the daily-driver machine while new work stacked. Renumbering to plain major-zero (`0.2.20`, ADR-026) removed the narrative from the number, and the fix cadence resumed the same day the reset shipped. The scheme was refined mid-shape by the operator — renumbered continuity (history mapped 1:1 onto the new line) beat the pitched clean restart, because the changelog stayed the sole honest carrier of the past without pretending it didn't happen.

## Current Priorities

- **Journal reliability across installed targets.** Converge content-addressed installation, target adapter ownership, capability diagnosis, and isolated installed-runtime dogfood without mutating users' production state.
- **Loaf Flow completeness.** The entry stage shipped in v0.2.17: `/pitch` at both scales, captured-folder promotion in `loaf change init`, bootstrap series-prep under the landing matrix. Next is dogfood-driven fine-tuning (the deferred end-to-end pitch→shape proof, seam oiling), then the Intent-tracked follow-ons: the Arc decision-map, the review-convergence loop, and the skills audit. Existing spec and task records remain supported compatibility surfaces until deliberately converted.
- **The 0.2.x fix cadence.** The versioning reset (0.2.20, ADR-026) made releases boring on purpose; the queue that cadence exists to serve: hooks-entry reconciliation ships as 0.2.21 (dissolves the one remaining Codex upgrade refusal), the OpenCode session-start fix as 0.2.22, then the content-addressed skills store and the skills audit ride later 0.2.x. The capture-only spec-conversion sweep folder must be shaped or retargeted before any 0.3.0 candidate can derive.
- **Evidence-driven target support.** Keep capability classifications conservative, version-pinned, and reproducible. Promote native behavior only after the installed target proves model-visible delivery; otherwise retain narrower runtime gating or an explicit fallback. The release command now refuses stale receipts mechanically after the artifact rebuild (PR #147); decoupling receipts from binary rebuilds remains the Intent-tracked structural fix.
- **Durable knowledge with low ceremony.** Preserve decisions, discoveries, and operational lessons where later work can retrieve them, while removing lifecycle machinery and planning vocabulary that do not carry product meaning.

Expand Down
45 changes: 45 additions & 0 deletions docs/decisions/ADR-027-content-bound-release-evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
id: ADR-027
title: "Content-bound release evidence — receipts vouch where history shape cannot"
status: Accepted
date: 2026-08-07
supersedes: null
superseded_by: null
related:
- ADR-022
- ADR-023
- ADR-024
- ADR-026
---

# ADR-027: Content-bound release evidence

## Context

Cutting `0.2.20` — the first *stable* candidate ever evaluated by the release cohort gate — surfaced a latent contradiction between two evidence systems that had never been tested together. Verify receipts bind content: they digest the scope tree they verified (ADR-024), which is why they survived the squash merge that landed the versioning reset. Execution provenance bound history shape: `changeFolderExecuted` scanned `git log HEAD -- <folder>` for a commit whose diff flips `- [ ]` → `- [x]` while touching code outside `docs/changes/` (ADR-023). The squash merge preserved every byte of the tree and rewrote every commit of the history — the receipt sailed through, the flip evidence ceased to exist, and a fully implemented, receipt-verified Change was refused as `targets 0.2.20 but is not executed`.

The gap hid for two months because prerelease candidates only warn and the one prior stable-path arc fast-forwarded (28 linear commits, flips intact). It was going to bite every squash-merged stable-target Change from then on, and the operator's requirement was explicit: all merge strategies are first-class, including strategies not yet adopted — a squash-cleanup-then-merge-commit flow can manufacture the same broken shape before the merge commit ever happens.

A second instance of the same class followed immediately. Post-merge guardrail 5 demanded the release commit diff both `CHANGELOG.md` and a version file — the shape only ceremony-minted release commits have. A *self-carrying* release, where the version flip landed as the Change's own receipt-verified content (the reset itself), can never honestly produce that commit, while guardrail 4 had already proven every version file equals the candidate.

## Decision

**Evidence that gates a release binds content — trees and receipts — never history shape.** History-derived signals may enrich display and remain a valid fast path, but no release gate refuses on the shape of history alone when content-bound evidence vouches. Three concrete rules implement this:

1. **Execution grade is a disjunct.** A cohort member grades executed when a flip transition exists in ancestry (the ADR-023 rule, still first) **or** when its verify receipt is fresh against HEAD and every committed task checkbox is checked. A receipt is unfakeable without the implementation in the tree — `loaf change verify` only passes against actual content — so the shaping-only attack ADR-023 guards against stays blocked: checked boxes with nothing vouching for them refuse, and a stale or digest-mismatched receipt vouches for nothing. Refusals for the receipt-less squash shape name the cause and the one-command remedy (PR #154).

2. **Release commits may be changelog-only under proof.** Post-merge guardrail 5 accepts a release commit with no version-file diff exactly when guardrail 4's own comparison attests every version file already equals the candidate — the same predicate, handed down, never recomputed. The changelog demand never relaxes (PR #155).

3. **Cohort receipts re-verify at the final tree.** In a multi-Change cohort, a later Change's content commits stale every earlier member's receipt, so all members re-verify on the last branch before merge — receipt commits are content-free and digest-excluded, so the ordering terminates, and receipts re-minted pre-squash carry through the squash unchanged (proven: the recomputed post-squash digest was byte-identical to the pre-squash receipt's).

## Consequences

- Fast-forward, merge-commit, rebase, squash, and any cleanup-then-merge hybrid produce identical gate outcomes, by construction rather than by convention. The merge strategy is a style choice again.
- The flip discipline — commit packets unchecked, flip in delivering commits — remains best practice for in-branch auditability, but is no longer load-bearing at the release gate. One structural exception stands: a Change's final flip must precede `loaf change verify`, because the receipt digests the tree and `tasks/**` is not excluded.
- Guardrail 5's version-file branch is unreachable in the composed pipeline (guardrail 4 aborts first) and survives as the function's own contract, pinned by tests — recorded plainly rather than implied to guard production.
- The first stable candidate through any gate is that gate's first real test. This arc is the second instance of the pattern (the alpha.16 evidence-canary incident was the first); gates changed since the last stable cut should expect their collision at the next one.

## Evidence

- PR #153 refused post-squash with `targets 0.2.20 but is not executed`; PR #154 (receipt-vouched execution) and PR #155 (guardrail relaxation) each landed through adversarial review gates, including an independently re-run falsification of the guardrail fix; the `v0.2.20` ceremony then passed all nine post-merge guardrails on a changelog-only release commit — both rules proven live in one run.
- Journal: `finding(release-gate)` and `decision(release-gate)` entries of 2026-08-07; `finding(cohort)` recording the receipt-ordering rule; change records `docs/changes/20260806-receipt-vouched-execution/` and `docs/changes/20260807-self-carried-release-guardrail/`.
Loading