From 99dfe6873196641b08f738c08ba2bf7d07caae64 Mon Sep 17 00:00:00 2001 From: Ayla Croft Date: Sun, 20 Sep 2026 07:25:35 -0400 Subject: [PATCH] plan: four slices added: 002 supply chain early, 003 FIPS build leg, 025 encryption at rest, 026 store-and-forward receipts Changes 7, 8 and 9 of the 2026-09-20 plan, landed as one branch because all three move the same change-log count. 002 (phase 0, S, depends on 000, counted in M9): a CycloneDX SBOM from the gate and on every release, build provenance attested on release artifacts, one README line for the TLS floor. 003 (phase 0, M, depends on 000, counted in M2): a second gate matrix entry on a UBI9 container with OTP 28.5.0.5 built from source with --enable-fips, FIPS mode entered before crypto loads, a smoke that proves it, and the crypto:supports/1 diff committed; it is where 024's FIPS properties run. 025 (phase 2, M, depends on 010 and 024): the Trinity.Keys custody seam with a local adapter first, envelope encryption for blobs nothing indexes, and the measured dm-crypt baseline for everything SQLite indexes. 026 (phase 2, L, depends on 024): receipt/2 gains a queued-then-acknowledged mode with hybrid logical clocks and Merkle merge; blocked on the external plane's answer, routed through the owner. ROADMAP: rows, the M2 and M9 conditions, the graph, a note that 025 and 026 carry no milestone yet, and the change-log entry deriving 40. One correction to the previous change-log entry: it escaped the pipe inside the deriving command, which plan_check rule 5's pattern does not match, so the rule read the older entry's 36 and passed by coincidence. Both entries now use the bare pipe the rule expects and the rule reads 40. plan_check: PASS (191 tracked files). trinity.names: OK. trinity.reuse: OK. Signed-off-by: Ayla Croft --- ROADMAP.md | 20 ++++- slices/002-supply-chain-early/SLICE.md | 72 +++++++++++++++++ slices/003-fips-build-leg/SLICE.md | 79 ++++++++++++++++++ slices/025-encryption-at-rest/SLICE.md | 81 +++++++++++++++++++ .../026-store-and-forward-receipts/SLICE.md | 72 +++++++++++++++++ 5 files changed, 320 insertions(+), 4 deletions(-) create mode 100644 slices/002-supply-chain-early/SLICE.md create mode 100644 slices/003-fips-build-leg/SLICE.md create mode 100644 slices/025-encryption-at-rest/SLICE.md create mode 100644 slices/026-store-and-forward-receipts/SLICE.md diff --git a/ROADMAP.md b/ROADMAP.md index 934b0c6..6b691dc 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -13,14 +13,17 @@ A size given as `M or L` is conditional on a decision named in that slice's file |---|---|---| | **M0 Stands** | Repo, gate, packaging path proven | 000, 001 approved | | **M1 Talks** | Streaming chat with any provider, persisted, crash-safe | 010–013 approved | -| **M2 Acts** | Tools with permission gate, one side-effect membrane, local receipts, context compaction | 020–024 approved | +| **M2 Acts** | Tools with permission gate, one side-effect membrane, local receipts, context compaction; the FIPS build leg that proves 024's properties | 003, 020–024 approved | | **M3 Remembers** | Persona, always-on memory, FTS + semantic recall, project context, and data you can take with you | 030–034 approved | | **M4 Learns** | Skills system with agent self-management + approval | 040–041 approved | | **M5a Automates** | Cron tasks and MCP, client and server, with authorization | 050, 059–062 approved | | **M5b Reaches** | Gateways over PubSub, subagents | 070–072, 080 approved (081 optional, outside the milestone) | | **M6 Ships** | Observability, native desktop shell, signed releases | 090–101 approved | | **M7 Sandboxed** | Executable skills in an in-VM sandbox | 110 approved | -| **M9 Donatable** | OSS hygiene audited, supply chain signed, AAIF Sandbox package complete, shared libraries extracted | 120–123 approved (122 filing is an owner action) | +| **M9 Donatable** | OSS hygiene audited, supply chain signed, AAIF Sandbox package complete, shared libraries extracted | 002, 120–123 approved (122 filing is an owner action) | + +Slices 025 and 026 carry no milestone: they serve regulated deployments after M2 and are assigned when the +standards register names the rows that ask for them. ## Slices @@ -28,6 +31,8 @@ A size given as `M or L` is conditional on a decision named in that slice's file |---|---|---|---|---|---| | 000 | Toolchain, repo bootstrap, quality gate | 0 Foundation | L | none | approved | | 001 | Packaging spike: Burrito + ex_tauri smoke build | 0 Foundation | M | 000 | approved | +| 002 | Supply chain, early: SBOM, build provenance, the TLS floor | 0 Foundation | S | 000 | planned | +| 003 | FIPS build leg in CI, from source | 0 Foundation | M | 000 | planned | | 010 | Core domain + persistence (Ecto/SQLite, schemas, Repo owner) | 1 Core loop | M | 000 | planned | | 011 | LLM provider layer (req_llm behind `Trinity.LLM` behaviour) | 1 Core loop | M | 010 | planned | | 012 | Session process + agent loop (gen_statem, DynamicSupervisor, rehydration) | 1 Core loop | L | 010, 011 | planned | @@ -37,6 +42,8 @@ A size given as `M or L` is conditional on a decision named in that slice's file | 022 | Core tools: filesystem, web fetch/search, shell (MuonTrap) | 2 Tools | L | 021 | planned | | 023 | Context compaction + session lineage | 2 Tools | M | 012 | planned | | 024 | Effect catalog, authority selection (`TRINITY_AUTHORITY`), local receipts | 2 Tools | L | 021, 022 | planned | +| 025 | Encryption at rest, and the key-custody seam | 2 Tools | M | 010, 024 | planned | +| 026 | Store-and-forward receipts for disconnected operation | 2 Tools | L | 024 | planned | | 030 | Persona (SOUL) + always-on memory tier | 3 Memory | M | 012 | planned | | 031 | Session search (SQLite FTS5) | 3 Memory | S | 010 | planned | | 032 | Embeddings + semantic memory + hybrid retrieval | 3 Memory | L | 031 | planned | @@ -81,8 +88,12 @@ for planning, not deadlines; the point here is the cut line, not a duration. ``` 000 ─┬─ 001 ─────────────────────────────────────────── 100 ── 101 + ├─ 002 (supply chain, early) + ├─ 003 (FIPS build leg; runs 024's FIPS tests) └─ 010 ─┬─ 011 ─┬─ 090 - │ └─ 012 ─┬─ 013 ── 021 ── 022 ── 024 ── 061 ── 062 + │ └─ 012 ─┬─ 013 ── 021 ── 022 ── 024 ─┬─ 061 ── 062 + │ │ ├─ 025 (also needs 010) + │ │ └─ 026 │ ├─ 020 ─┬─ 040 ── 041 ── 110 │ │ ├─ 059 ── 060 ── 061 │ │ └─ 080 ── (081 optional, also needs 061) @@ -110,4 +121,5 @@ After 012, multiple branches are independent. If running more than one coding ag | 2026-09-05 | Authority made an adapter behind a behaviour rather than a mode (ADR-0008, ADR-0010). ADR-0009 opened for the Jido question, decided at the 012 checkpoint. Slice 024 added: effect catalog, `TRINITY_AUTHORITY`, local receipts. Alignment appendices on 012, 020, 021, 022, 023, 030, 032, 040, 041. | | 2026-09-05 | Review pass before commit 1. Counts in the three entries above were typed, not derived, and none matches the tree; a decreasing count is impossible under insert-never-renumber. Derived this date: `find slices -name SLICE.md | wc -l` → **38**. Milestones are derived from the Milestones table, not from memory. From here, any count in this log names the command that produced it. Entries above are not rewritten. | | 2026-09-06 | Plan corrections, round 2. **Supersedes the slice count in the entry above:** that entry derived **38** on 2026-09-05, before slices 082, 083 and 084 were withdrawn and slice 034 was added. Re-derived this date, not adjusted by arithmetic: `find slices -name SLICE.md | wc -l` → **36**. The entry above is not rewritten. `scripts/plan_check.sh` now enforces this count, the acceptance-criteria numbering, the Definition-of-Done ranges, ROADMAP/SLICE.md agreement, and the absence of references to paths not in `git ls-files`. | -| 2026-09-20 | The MCP phase replanned under owner decisions of 2026-09-08, recorded in ADR-0007 decisions 5 to 8: beam_mcp 0.8.0 is the server core; 059 measures the capability gap and probes the `:server` seam (S/M); 060 is Trinity's thin driver (L, the M/L condition decided); the OAuth client role moves from 060 to 062; 061 carries a named blocker on the MRTR wrapper; the four `M5 Always-on` headers on 059 to 062 read M5a Automates, as this file has since 2026-09-08. R14 re-scoped, R15 closed. Re-derived this date: `find slices -name SLICE.md \| wc -l` → **36**. | +| 2026-09-20 | The MCP phase replanned under owner decisions of 2026-09-08, recorded in ADR-0007 decisions 5 to 8: beam_mcp 0.8.0 is the server core; 059 measures the capability gap and probes the `:server` seam (S/M); 060 is Trinity's thin driver (L, the M/L condition decided); the OAuth client role moves from 060 to 062; 061 carries a named blocker on the MRTR wrapper; the four `M5 Always-on` headers on 059 to 062 read M5a Automates, as this file has since 2026-09-08. R14 re-scoped, R15 closed. Re-derived this date: `find slices -name SLICE.md | wc -l` → **36**. | +| 2026-09-20 | Four slices added under the accepted 2026-09-20 plan: 002 (supply chain, early: SBOM, provenance, TLS floor; counted in M9), 003 (FIPS build leg from source; counted in M2 because it runs 024's FIPS properties), 025 (encryption at rest and the key-custody seam) and 026 (store-and-forward receipts; blocked on the external plane's answer). Re-derived this date: `find slices -name SLICE.md | wc -l` → **40**. | diff --git a/slices/002-supply-chain-early/SLICE.md b/slices/002-supply-chain-early/SLICE.md new file mode 100644 index 0000000..fd18765 --- /dev/null +++ b/slices/002-supply-chain-early/SLICE.md @@ -0,0 +1,72 @@ +# Slice 002: Supply chain, early: SBOM, build provenance, the TLS floor + +| Field | Value | +|---|---| +| Phase | 0 Foundation | +| Milestone | M9 Donatable | +| Size | S | +| Depends on | 000 | +| Status | see ROADMAP.md | + +Added 2026-09-20. Slice 121 keeps Scorecard, Sigstore signing of release artifacts, SLSA and the registry entry, +and depends on the release pipeline; the three items here need none of that and are cheap enough to carry from +now on. + +## Goal +A CycloneDX software bill of materials generated by the gate and attached to every release; build provenance +attested on release artifacts through GitHub's attestation action; one README line stating the TLS floor and +that any MAC in an envelope Trinity mints is Trinity's and an approved one in FIPS mode. + +## Why +Vision goal 10. A reviewer of a public tree expects to find what is in the binary and where the binary came +from without asking; a regulated deployment requires both on request. Deferring them to slice 121 means every +release before it ships without them. + +## Scope +**In:** +- `sbom` (hex.pm, `mix sbom.cyclonedx`, CycloneDX) as a dev and test dependency; a gate step that generates + `sbom.cdx.json` and fails on a generation error; the file attached to every release by the package workflow. +- `actions/attest-build-provenance`, pinned to a commit SHA with its release in a comment like the other actions, + attesting each artifact the package workflow uploads. The attestation is verified in the same workflow with + `gh attestation verify` and the output kept in the run summary. +- One README line under Running from source: the TLS floor is 1.2, and any MAC over an envelope Trinity mints is + Trinity's own and is an approved algorithm in FIPS mode. +- `VERSIONS.md` row for `sbom`. +**Out:** +- Signing the SBOM or the artifacts with a Trinity-held key; Scorecard; SLSA levels; the registry entry. All + slice 121. +- A cryptography bill of materials. It follows once the minimum elements for one are published. + +## Design notes +The SBOM is generated from `mix.lock`, so it is exact for Hex dependencies and blind to the Rust crates of the +desktop shell; the file says so in a top-level property and slice 121 adds the crate half. Provenance is +GitHub's, over the artifact's digest, and proves where the bytes were built, not what they do. + +## Deliverables +- `mix.exs` dependency and gate step; `.github/workflows/package.yml` upload and attest steps; `README.md` line; + `lib/trinity/versions.ex` row and regenerated `VERSIONS.md`. + +## Acceptance criteria +1. [auto] `mix gate` generates `sbom.cdx.json` and the file validates as CycloneDX (the validator's command and + exit code pasted). +2. [auto] A package run attaches the SBOM to its artifacts and attests each artifact; `gh attestation verify` + exits 0 for each, output in the run summary (run id cited). +3. [auto] The README carries the TLS floor line, and a test pins it so the line cannot vanish silently. +4. [auto] Gate green; coverage line reported. + +## Proof required +- For each criterion: the command and its output, or a test name and its result. A sentence is not proof. + +## Manual verification queue +None. Every acceptance criterion in this slice is `[auto]` and is proven by a command or a test. +If that changes during the slice, the criterion is retagged and this section is filled at G1. + +## Definition of Done +- [ ] `mix gate` green · [ ] AC1–4 proven · [ ] docs/ADR/VERSIONS updated if affected · [ ] ROADMAP status → done · [ ] final commit + tag + +## Commit & tag +`feat(s002): complete slice 002 (supply chain, early)` · tag `slice/002` + +## Risks / open questions +- The SBOM's blindness to the desktop shell's crates is stated in the file itself; a reader who misses the + property misreads the bill. Slice 121 closes it. diff --git a/slices/003-fips-build-leg/SLICE.md b/slices/003-fips-build-leg/SLICE.md new file mode 100644 index 0000000..eef6649 --- /dev/null +++ b/slices/003-fips-build-leg/SLICE.md @@ -0,0 +1,79 @@ +# Slice 003: A FIPS build leg in CI, from source, so FIPS claims are proven rather than asserted + +| Field | Value | +|---|---| +| Phase | 0 Foundation | +| Milestone | M2 Acts | +| Size | M | +| Depends on | 000 | +| Status | see ROADMAP.md | + +Added 2026-09-20. Counted toward M2 Acts because its reason to exist is slice 024's FIPS property tests, which +cannot run anywhere else: the developer machine's OTP reports `crypto:info_fips()` as `not_supported`, and the +hosted BEAM images the gate uses are not FIPS builds. + +## Goal +A second entry in the gate workflow's matrix that runs the whole suite on an OTP 28.5.0.5 built from source with +`--enable-fips` against the validated OpenSSL of a UBI9 container, with FIPS mode entered before the crypto +application loads, and a smoke that proves the mode is on. The default leg stays exactly as it is. + +## Why +Slice 024's amendment says FIPS mode selects P-384 and never denies for want of a signer. Until a build runs +in FIPS mode, that is a sentence. This leg is the measurement. It also answers, once, what else in the tree +reaches an algorithm FIPS mode removes. + +## Scope +**In:** +- A container image built by its own workflow, rebuilt when `.tool-versions` changes: UBI9, the distribution's + OpenSSL and its FIPS provider, OTP 28.5.0.5 from source with `--enable-fips`, Elixir 1.20.4. Published to the + repository's container registry so the gate leg pulls rather than builds. +- The gate leg: `fips_mode: true` for `:crypto` in the release's `sys.config` equivalent for tests, set before + crypto loads; the smoke `crypto:info_fips() =:= enabled` and the `fips_provider_buildinfo` key present in + `crypto:info/0`; then `mix test` and the rest of the gate as the default leg runs it. +- A committed record of `crypto:supports/1` on the FIPS leg diffed against the default leg, so the removed + algorithms are a list in the tree and not a memory. +- Runner minutes measured over the first ten runs and written into NOTES.md. +**Out:** +- Making FIPS mode the default anywhere; changing the OTP pin; ML-DSA (the UBI9 OpenSSL is a 3.0 line and does + not carry it; that measurement waits for a build against OpenSSL 3.5 or later and is `:unknown` until then). +- A FIPS-mode desktop bundle. Whether Burrito can wrap a `--enable-fips` ERTS is slice 100's question. + +## Design notes +Entering or leaving FIPS mode on a running node is unsupported by OTP, so the leg sets the mode in configuration +and never toggles it in a test. A test that needs a removed algorithm on this leg is a finding, not a skip: it is +listed in the committed diff with the slice that owns it. + +## Deliverables +- `.github/workflows/fips-image.yml` and the Containerfile; the second matrix entry in `.github/workflows/gate.yml`; + `docs/fips-leg.md` with the `crypto:supports/1` diff and how to reproduce the image; NOTES.md with the minutes. + +## Acceptance criteria +1. [auto] On the FIPS leg, `crypto:info_fips()` returns `enabled` and `crypto:info/0` carries + `fips_provider_buildinfo` (log excerpt with the run id). +2. [auto] On the FIPS leg, `crypto:sign/4` with `eddsa` returns `notsup` and with `ecdsa` on `secp384r1` and + `sha384` returns a signature that verifies (test). +3. [auto] The committed `crypto:supports/1` diff between the two legs matches what the leg prints (test). +4. [auto] The whole gate runs on the leg; every red is either fixed or listed by test name with its owning slice + in `docs/fips-leg.md`; there is no skip tag. +5. [auto] Gate green on both legs; coverage line reported. + +## Proof required +- For each criterion: the command and its output, or a test name and its result, with the run id. A sentence is + not proof. + +## Manual verification queue +None. Every acceptance criterion in this slice is `[auto]` and is proven by a command or a test. +If that changes during the slice, the criterion is retagged and this section is filled at G1. + +## Definition of Done +- [ ] `mix gate` green · [ ] AC1–5 proven · [ ] docs/ADR/VERSIONS updated if affected · [ ] ROADMAP status → done · [ ] final commit + tag + +## Commit & tag +`feat(s003): complete slice 003 (FIPS build leg)` · tag `slice/003` + +## Risks / open questions +- Building OTP from source is slow; the image workflow exists so the gate leg does not pay for it on every push. + If the image cannot be cached in the registry for a reason found at G1, the leg runs on a schedule and on + demand rather than on every push, and the slice says so. +- The validated provider a container ships is the distribution's claim; the slice cites the certificate number + the distribution publishes and does not restate it as Trinity's. diff --git a/slices/025-encryption-at-rest/SLICE.md b/slices/025-encryption-at-rest/SLICE.md new file mode 100644 index 0000000..79ee7f2 --- /dev/null +++ b/slices/025-encryption-at-rest/SLICE.md @@ -0,0 +1,81 @@ +# Slice 025: Encryption at rest, and the key-custody seam + +| Field | Value | +|---|---| +| Phase | 2 Tools | +| Milestone | none (regulated deployment, after M2) | +| Size | M | +| Depends on | 010, 024 | +| Status | see ROADMAP.md | + +Added 2026-09-20 under two accepted recommendations: keys live where the deployment controls them, with a local +adapter first; and encryption at rest splits by whether SQLite indexes the data. + +## Goal +A key-custody behaviour (`Trinity.Keys`) with a local adapter (passphrase-derived, TPM-sealed or +systemd-credential-supplied, whichever the machine offers, chosen at boot and named in the boot receipt) through +which the 024 signer and this slice's envelope encryption obtain keys; envelope encryption in code, AES-256-GCM +through `:crypto`, for the blobs nothing indexes: skill files, staged skill changes, exported artifacts; and the +documented, measured baseline for everything SQLite indexes, which is volume or page level encryption below the +database file and zero code in Trinity. + +## Why +Field-level ciphertext in the memory store would break slice 032's full-text and vector search, which index +plaintext tokens and raw vectors. The split keeps the indexes working and still leaves no plaintext blob on disk +that Trinity could have encrypted. The custody seam is what lets a later deployment put keys in a KMS or an HSM +without touching either consumer. + +## Scope +**In:** +- `Trinity.Keys` behaviour: `fetch/2`, `wrap/2`, `unwrap/2`, `rotate/1`; the `Local` adapter with its three + sources; selection at boot; the 024 signer and the 100 keychain path retrofitted to read through it. +- Envelope encryption for the blob classes above: a data key per blob, wrapped by the custody adapter, the + wrapped key stored beside the ciphertext; decryption on read; a receipt on every key wrap and unwrap. +- `docs/encryption-at-rest.md`: the split, the reasons, the dm-crypt baseline with the commands to set it up + and the measured overhead on this machine from the 010 insert bench run on an encrypted and a plain volume. +**Out:** +- Encrypting the SQLite files in code (page-level libraries): only as a later option for a deployment that + already licenses a validated one, and not in this tree. +- KMS and PKCS#11 adapters: their own slices when a deployment asks; the behaviour is shaped so they fit. +- Key escrow, recovery and multi-party custody. + +## Design notes +The behaviour is shaped around a local round trip, on purpose; a network adapter later has to fit a callback +that was proven without one, rather than the other way round. Keys never appear in receipts, logs or the +database; the receipt names the key id and the operation. + +## Deliverables +- `lib/trinity/keys/` with the behaviour and the `Local` adapter; envelope encryption in the skill and export + paths; migrations for wrapped keys beside blobs; `docs/encryption-at-rest.md`; the bench results in PROOF.md. + +## Acceptance criteria +1. [auto] The boot receipt names the custody adapter and source; each of the three local sources works when + present and is refused with a named reason when absent (tests, one per source, with the absent case). +2. [auto] A skill file, a staged change and an export are ciphertext on disk, readable through the seam, and + unreadable with the wrapped key removed (tests). +3. [auto] The 024 signer obtains its key through the seam and no other path; a census finds no key read outside + `Trinity.Keys` (test with a planted read). +4. [auto] Rotation: after `rotate/1` new blobs use the new key, old blobs still open, and the registry records + both (test). +5. [auto] The insert bench from slice 010 run on a dm-crypt volume and on a plain volume, same machine, same + day, the two numbers in PROOF.md with the commands. +6. [auto] Gate green; coverage line reported. + +## Proof required +- For each criterion: the command and its output, or a test name and its result. A sentence is not proof. + +## Manual verification queue +None. Every acceptance criterion in this slice is `[auto]` and is proven by a command or a test. +If that changes during the slice, the criterion is retagged and this section is filled at G1. + +## Definition of Done +- [ ] `mix gate` green · [ ] AC1–6 proven · [ ] docs/ADR/VERSIONS updated if affected · [ ] ROADMAP status → done · [ ] final commit + tag + +## Commit & tag +`feat(s025): complete slice 025 (encryption at rest and the key-custody seam)` · tag `slice/025` + +## Risks / open questions +- A TPM is absent on many developer machines; the passphrase source is the one every test can exercise, and the + TPM and systemd-credential sources are proven where present and recorded as not measured where not. +- Retrofitting 024's signer to the seam is a change to an approved slice's code if 024 lands first; it is a + fix commit referencing 024, never a rewrite of its record. diff --git a/slices/026-store-and-forward-receipts/SLICE.md b/slices/026-store-and-forward-receipts/SLICE.md new file mode 100644 index 0000000..c631fd2 --- /dev/null +++ b/slices/026-store-and-forward-receipts/SLICE.md @@ -0,0 +1,72 @@ +# Slice 026: Store-and-forward receipts for disconnected operation + +| Field | Value | +|---|---| +| Phase | 2 Tools | +| Milestone | none (regulated deployment, after M2) | +| Size | L | +| Depends on | 024 | +| Status | see ROADMAP.md | + +Added 2026-09-20. Blocked until the external authority plane's maintainers answer whether a queued-then-acknowledged +`receipt/2` fits their adapter; that question is routed through the owner and this slice does not open before the +answer is recorded in NOTES.md. Opening it changes ADR-0008's contract and needs an appended decision there. + +## Goal +`receipt/2` gains a queued-then-acknowledged mode so that the local authority and an external adapter behave +identically when the machine is offline: receipts are appended locally with a hybrid logical clock on every row, +queued for the adapter, acknowledged when it confirms, and merged on reconnect by Merkle comparison of the two +chains rather than by replay. + +## Why +A field or clinical site loses its link and keeps working. Today `receipt/2` is synchronous: offline, the local +chain continues and the adapter's view stops, and nothing reconciles them. A chain that cannot survive a +partition is evidence only while the network is up. + +## Scope +**In:** +- Hybrid logical clock on every receipt row; the clock's rules and its trust limits written into the security + model (a signed receipt with an untrusted clock is weaker evidence, and the model says so). +- A durable outbound queue per chain scope; acknowledgement tracking; back-pressure when the queue is bounded. +- Merkle merge on reconnect: per-device chains, a comparison of tree heads, and a merge that never rewrites + either side; conflicts are recorded as receipts of their own. +- The `Local` authority exercising the same path, so standalone Trinity proves the mode without an adapter. +**Out:** +- Multi-master conflict resolution beyond recording; a witness or gossip network; the external plane's side of the + merge, which is theirs. + +## Design notes +Nothing here changes what a receipt says, only when it is acknowledged and how two chains are compared. The +signer seam and chain scopes from 024 are used unchanged. + +## Deliverables +- Queue and clock in `Trinity.Receipts`; the merge; the ADR-0008 appended decision; docs/07 clock section; tests + that partition and reconnect. + +## Acceptance criteria +1. [auto] With the adapter unreachable, effects proceed under the local chain, every receipt carries a clock, and + the queue grows; with the adapter back, every queued receipt is acknowledged in order (test). +2. [auto] Two chains diverged during a partition merge on reconnect with neither rewritten and a conflict receipt + for each divergence (test). +3. [auto] A receipt whose clock is behind the previous row's is refused and the refusal receipted (test). +4. [auto] Bounded queue: past the bound, effects are denied, not written unacknowledged (test). +5. [auto] Gate green; coverage line reported. + +## Proof required +- For each criterion: the command and its output, or a test name and its result. A sentence is not proof. + +## Manual verification queue +None. Every acceptance criterion in this slice is `[auto]` and is proven by a command or a test. +If that changes during the slice, the criterion is retagged and this section is filled at G1. + +## Definition of Done +- [ ] `mix gate` green · [ ] AC1–5 proven · [ ] docs/ADR/VERSIONS updated if affected · [ ] ROADMAP status → done · [ ] final commit + tag + +## Commit & tag +`feat(s026): complete slice 026 (store-and-forward receipts)` · tag `slice/026` + +## Risks / open questions +- The external plane may answer that its adapter cannot take a queued mode; then this slice narrows to the local + authority only and says so. +- Clock trust under disconnection has no software fix; a trusted time source is a deployment matter and is + named in the register, not solved here.