From 45a85d46190be7fda73dfb8bdca5564608b2b588 Mon Sep 17 00:00:00 2001 From: lr00rl Date: Sun, 26 Jul 2026 05:19:25 -0700 Subject: [PATCH 1/6] Document the release pin graph as it stands TASK-0010 slice 1 (operator ruling 2026-07-26 $1b): every cross-repo pin with its source of truth, current version state, and the six gaps (G1-G6) that block a coordinated train. Documentation only; no behavior change. --- docs/contracts/release-pin-graph.md | 64 +++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 docs/contracts/release-pin-graph.md diff --git a/docs/contracts/release-pin-graph.md b/docs/contracts/release-pin-graph.md new file mode 100644 index 0000000..2ad9ff7 --- /dev/null +++ b/docs/contracts/release-pin-graph.md @@ -0,0 +1,64 @@ +# Release pin graph — current state (2026-07-26) + +> TASK-0010 slice 1 (operator ruling 2026-07-26 §1b). Every cross-repo pin as it exists today, +> each claim with its source of truth (repo + file). Gaps are named explicitly — they are the +> requirements list for the release-manifest format (slice 2) and the `rules/01 §8.5` +> promotion amendment (slice 3). Nothing in this document changes behavior. + +## 1. The graph — who pins whom, and where + +| Consumer → dependency | Pin mechanism | Value today | Source of truth | +|---|---|---|---| +| lattice-server → lattice-sdk | `sdk.ref` SHA file + go.mod pseudo-version | `4a318f24…` / `v0.2.18-0.20260722123932-4a318f246d23` | `lattice-server:sdk.ref`, `lattice-server:go.mod` (integration `86422a1`) | +| lattice-server → lattice-dashboard | `dashboard.ref` SHA file (image embeds the built dashboard) | `a927c6c…` | `lattice-server:dashboard.ref` (integration `86422a1`) | +| lattice-node-agent → lattice-sdk | go.mod pseudo-version | `v0.2.18-…-4a318f246d23` (same commit as server's) | `lattice-node-agent:go.mod` (integration `03f730a`) | +| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | latest observed: `alpha-0.2.2a2` | `lattice-server:.github/workflows/container.yml`, git tags | +| plugin-index → each plugin | `plugins.json`: per-plugin `channels` (stable/alpha) + `releases[]` carrying `manifest_url`, `artifact_url`, `artifact_sha256`, `signature_ed25519`; publisher ed25519 key pinned at index level | schema `lattice.plugin.index.v1`, `status: "draft"`, generated 2026-07-22 | `lattice-plugin-index:plugins.json` | +| each plugin (internal) | manifest `version` = `ui/package.json` = Go const, moved together; artifact digest ↔ manifest signature | e.g. sub-store `0.3.2-alpha.4` | each plugin repo: `manifest.json`, `ui/package.json`, `system-go` const; `tools/bump.sh` | +| dashboard → server | none at build time; runtime API compatibility only. Post-deploy check: About page must show matching versions | — | workspace release law (root AGENTS.md) | +| plugins → server | **NOTHING** — see gap G1 | — | — | +| Astra → sing-box fork / server | not machine-resolvable from `Package.swift` — see gap G4 | — | — | +| fleet nodes → sing-box fork binary | deployed binary; version recorded in no repo file — see gap G4 | — | — | + +## 2. Version state today (observed 2026-07-26, sources: git tags + manifests) + +| Repo | Stable | Prerelease / dev line | +|---|---|---| +| lattice-server | `v0.2.1` | image train `alpha-0.2.2a2`; `integration` @ `86422a1`; binary version injected at build (source default `"dev"`) | +| lattice-sdk | `v0.2.9` (stable-tags-only lane) | consumers pin pseudo-version @ `4a318f24` | +| lattice-dashboard | — (no stable tag observed) | `v0.2.2-alpha.6` (= `package.json`) | +| lattice-node-agent | — | `v0.3.3-alpha.2` | +| plugin vpn-core | index `stable: 0.7.2` | manifest `0.8.0-alpha.5` | +| plugin netguard / wireguard | — | `0.1.0-alpha.7` each | +| plugin sub-store | — | `0.3.2-alpha.4` | +| plugin template | — | `0.2.1-alpha.3` on `main`; `0.2.1-alpha.4` signed on the open `feat/execute-reference` PR | + +## 3. Gaps blocking a coordinated train (requirements for slices 2–3) + +- **G1 — no plugin→server minimum-version declaration.** Compatibility is implicit: a manifest + loads or is rejected by the running server's validator. The ruling requires this resolvable + from the release. Candidate homes: a signed manifest field (schema change, re-sign wave) or + a plugin-index per-release field (no re-sign; index is regenerated anyway). Decide in slice 2. +- **G2 — `dashboard.ref` currently points at a line missing a security fix.** `a927c6c` + (dashboard `main`) lacks dashboard#9 (frame-reload boundary), which lives on `integration` + (`a40af9a`). The pending integration↔main reconciliation produces the union; the ref must + move to the merged tip before the next image tag is cut. +- **G3 — no single artifact resolves the whole train.** Today the answer to "what versions make + up a deployment" is assembled from six places in this table. The release manifest (slice 2) + is that artifact. +- **G4 — Astra and the sing-box fork are unpinned in writing.** The iOS client's fork pin and + the fleet's deployed sing-box version are tribal knowledge. Record both; the train cannot + promote what it cannot name. +- **G5 — plugin-index is hand-generated.** `status: "draft"`, `generated_at` hand-run. Train + discipline needs index regeneration + validation as a release step (ties into TASK-0006's + gate). +- **G6 — server version constant.** The binary reports an injected build version (`"dev"` + default in source); slice 2 must verify the injection path so the About-page check is + load-bearing rather than cosmetic. + +## 4. Standing law this document leans on (unchanged here) + +Three tag lanes per `rules/01 §8.5` (image train `alpha-X.Y.ZaN` · prerelease semver +`vX.Y.Z-alpha.N`, never GitHub Latest · stable semver `vX.Y.Z` on explicit operator decision); +release order `sdk → server / dashboard / node-agent → docs site → plugins → plugin-index`; +tag pushes are operator-only. The promotion protocol amendment is slice 3. From 0d863797c48d53f779bf68c4d0ac8ccbd5b15ece Mon Sep 17 00:00:00 2001 From: lr00rl Date: Mon, 27 Jul 2026 07:47:51 -0700 Subject: [PATCH 2/6] Refresh the pin graph after the first signing wave G2 closed (dashboard.ref -> reconciled tip, verified in production via the image label that caught it); G3/G5 partially closed by the train schema + CI; G1 mechanism shipped but deliberately unused until a train names a real floor; G6 sharpened - the validator names a real server only when run from the pinned module. Version table now carries the signed digests, each CI-confirmed. --- docs/contracts/release-pin-graph.md | 75 ++++++++++++++++------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/docs/contracts/release-pin-graph.md b/docs/contracts/release-pin-graph.md index 2ad9ff7..9c0a138 100644 --- a/docs/contracts/release-pin-graph.md +++ b/docs/contracts/release-pin-graph.md @@ -1,4 +1,4 @@ -# Release pin graph — current state (2026-07-26) +# Release pin graph — current state (refreshed 2026-07-27) > TASK-0010 slice 1 (operator ruling 2026-07-26 §1b). Every cross-repo pin as it exists today, > each claim with its source of truth (repo + file). Gaps are named explicitly — they are the @@ -20,41 +20,50 @@ | Astra → sing-box fork / server | not machine-resolvable from `Package.swift` — see gap G4 | — | — | | fleet nodes → sing-box fork binary | deployed binary; version recorded in no repo file — see gap G4 | — | — | -## 2. Version state today (observed 2026-07-26, sources: git tags + manifests) +## 2. Version state (refreshed 2026-07-27 after the first signing wave + two image trains) -| Repo | Stable | Prerelease / dev line | -|---|---|---| -| lattice-server | `v0.2.1` | image train `alpha-0.2.2a2`; `integration` @ `86422a1`; binary version injected at build (source default `"dev"`) | -| lattice-sdk | `v0.2.9` (stable-tags-only lane) | consumers pin pseudo-version @ `4a318f24` | -| lattice-dashboard | — (no stable tag observed) | `v0.2.2-alpha.6` (= `package.json`) | -| lattice-node-agent | — | `v0.3.3-alpha.2` | -| plugin vpn-core | index `stable: 0.7.2` | manifest `0.8.0-alpha.5` | -| plugin netguard / wireguard | — | `0.1.0-alpha.7` each | -| plugin sub-store | — | `0.3.2-alpha.4` | -| plugin template | — | `0.2.1-alpha.3` on `main`; `0.2.1-alpha.4` signed on the open `feat/execute-reference` PR | +| Repo | Stable | Current line | Signed bundle digest | +|---|---|---|---| +| lattice-server | `v0.2.1` | image train **`alpha-0.2.2a4`** (deployed); integration `c9c6710` | — | +| lattice-sdk | `v0.2.9` | integration `c3f2973`; carries the plugin SDK module (`00943f6e`) | — | +| lattice-dashboard | — | `v0.2.2-alpha.6`; integration `8e6c206` (reconciled union) | — | +| lattice-node-agent | — | `v0.3.3-alpha.2` | — | +| plugin vpn-core | index `stable: 0.7.2` | **0.8.0-alpha.6** | `d2e681a6…` | +| plugin sub-store | — | **0.4.0-alpha.1** (embedded-engine line) | `e0524e35…` | +| plugin wireguard | — | **0.1.0-alpha.8** | `34eb6c07…` | +| plugin netguard | — | **0.1.0-alpha.8** | `c00334a8…` | +| plugin template | — | **0.2.1-alpha.5** | `c4bfe8be…` | + +Every plugin digest above is the SIGNED value at its integration tip, independently confirmed +by that repo's CI (the double-pack byte-compare gate) after the wave. + +## 3. Gaps — status after 2026-07-27 -## 3. Gaps blocking a coordinated train (requirements for slices 2–3) +- **G1 — plugin→server minimum version: MECHANISM SHIPPED, NOT YET USED.** `min_server` is a + signed, additive manifest field (server#22, merged; index mirrors, never owns — + rules/01 §8.5). All five manifests currently declare it ABSENT, deliberately: the honest + floor is "a server containing the budget/backing work", which no released version string + named at signing time. First train assembly is when it gets real values. +- **G2 — CLOSED.** `dashboard.ref` now points at the reconciled tip `8e6c206`; verified in + production via the image label `dashboard-revision` on `alpha-0.2.2a4`. The gap was caught + BY that label after `alpha-0.2.2a3` shipped the pre-reconciliation ref — the label is + therefore load-bearing evidence, not decoration. +- **G3 — FORMAT SHIPPED.** `lattice.release.train.v1` (schema + zero-dependency validator + + CI) lives in `lattice-plugin-index`; a real `train.json` gets written at the first + coordinated cut. +- **G4 — OPEN.** Astra's fork pin and the fleet's deployed sing-box version are still + unrecorded. Unchanged today. +- **G5 — PARTIALLY CLOSED.** The index is still hand-generated, but train manifests are now + CI-validated on every push/PR; index regeneration itself remains a manual release step. +- **G6 — SHARPENED.** The validator reports `server_version` from module metadata when run as + `go run …@` (e.g. `v0.2.2-0.20260727095611-c9c671079ab6`) but reports `dev` when + run from a plain local build — so the plugin CI gate, which pins a released module version, + DOES name a real server, while a hand-built binary does not. Train assembly must take the + version from the pinned module reference, not from a local build. -- **G1 — no plugin→server minimum-version declaration.** Compatibility is implicit: a manifest - loads or is rejected by the running server's validator. The ruling requires this resolvable - from the release. Candidate homes: a signed manifest field (schema change, re-sign wave) or - a plugin-index per-release field (no re-sign; index is regenerated anyway). Decide in slice 2. -- **G2 — `dashboard.ref` currently points at a line missing a security fix.** `a927c6c` - (dashboard `main`) lacks dashboard#9 (frame-reload boundary), which lives on `integration` - (`a40af9a`). The pending integration↔main reconciliation produces the union; the ref must - move to the merged tip before the next image tag is cut. -- **G3 — no single artifact resolves the whole train.** Today the answer to "what versions make - up a deployment" is assembled from six places in this table. The release manifest (slice 2) - is that artifact. -- **G4 — Astra and the sing-box fork are unpinned in writing.** The iOS client's fork pin and - the fleet's deployed sing-box version are tribal knowledge. Record both; the train cannot - promote what it cannot name. -- **G5 — plugin-index is hand-generated.** `status: "draft"`, `generated_at` hand-run. Train - discipline needs index regeneration + validation as a release step (ties into TASK-0006's - gate). -- **G6 — server version constant.** The binary reports an injected build version (`"dev"` - default in source); slice 2 must verify the injection path so the About-page check is - load-bearing rather than cosmetic. +**Also now true (not a gap, a property to preserve)**: every plugin repo's CI runs the +released-server manifest validator on `main`, `integration`, and PRs, so a manifest that the +released server would reject cannot reach a merge unnoticed. ## 4. Standing law this document leans on (unchanged here) From dc0f5c1c530872eaefed22fb2b514c1d8ab36fbc Mon Sep 17 00:00:00 2001 From: lr00rl Date: Mon, 27 Jul 2026 07:48:17 -0700 Subject: [PATCH 3/6] Refresh the graph table and the standing-law note Section 1's Value column and the closing note still described 26 July: stale server/dashboard refs, the a2 image, the pre-wave sub-store version, and a promotion amendment listed as pending after it shipped. A half-refreshed doc is worse than an unrefreshed one. --- docs/contracts/release-pin-graph.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/contracts/release-pin-graph.md b/docs/contracts/release-pin-graph.md index 9c0a138..be95042 100644 --- a/docs/contracts/release-pin-graph.md +++ b/docs/contracts/release-pin-graph.md @@ -2,19 +2,20 @@ > TASK-0010 slice 1 (operator ruling 2026-07-26 §1b). Every cross-repo pin as it exists today, > each claim with its source of truth (repo + file). Gaps are named explicitly — they are the -> requirements list for the release-manifest format (slice 2) and the `rules/01 §8.5` -> promotion amendment (slice 3). Nothing in this document changes behavior. +> requirements list that slices 2 and 3 then closed (schema+CI in `lattice-plugin-index`; the +> `rules/01 §8.5` amendment). Refreshed after the 2026-07-27 signing wave so it describes +> reality, not the morning of the 26th. Nothing in this document changes behavior. ## 1. The graph — who pins whom, and where | Consumer → dependency | Pin mechanism | Value today | Source of truth | |---|---|---|---| -| lattice-server → lattice-sdk | `sdk.ref` SHA file + go.mod pseudo-version | `4a318f24…` / `v0.2.18-0.20260722123932-4a318f246d23` | `lattice-server:sdk.ref`, `lattice-server:go.mod` (integration `86422a1`) | -| lattice-server → lattice-dashboard | `dashboard.ref` SHA file (image embeds the built dashboard) | `a927c6c…` | `lattice-server:dashboard.ref` (integration `86422a1`) | +| lattice-server → lattice-sdk | `sdk.ref` SHA file + go.mod pseudo-version | `4a318f24…` / `v0.2.18-0.20260722123932-4a318f246d23` | `lattice-server:sdk.ref`, `lattice-server:go.mod` (integration `c9c6710`) | +| lattice-server → lattice-dashboard | `dashboard.ref` SHA file (image embeds the built dashboard) | `8e6c206…` (reconciled tip — was `a927c6c`, see G2) | `lattice-server:dashboard.ref` (integration `c9c6710`) | | lattice-node-agent → lattice-sdk | go.mod pseudo-version | `v0.2.18-…-4a318f246d23` (same commit as server's) | `lattice-node-agent:go.mod` (integration `03f730a`) | -| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | latest observed: `alpha-0.2.2a2` | `lattice-server:.github/workflows/container.yml`, git tags | +| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | latest: **`alpha-0.2.2a4`**, deployed and verified | `lattice-server:.github/workflows/container.yml`, git tags | | plugin-index → each plugin | `plugins.json`: per-plugin `channels` (stable/alpha) + `releases[]` carrying `manifest_url`, `artifact_url`, `artifact_sha256`, `signature_ed25519`; publisher ed25519 key pinned at index level | schema `lattice.plugin.index.v1`, `status: "draft"`, generated 2026-07-22 | `lattice-plugin-index:plugins.json` | -| each plugin (internal) | manifest `version` = `ui/package.json` = Go const, moved together; artifact digest ↔ manifest signature | e.g. sub-store `0.3.2-alpha.4` | each plugin repo: `manifest.json`, `ui/package.json`, `system-go` const; `tools/bump.sh` | +| each plugin (internal) | manifest `version` = `ui/package.json` = Go const, moved together; artifact digest ↔ manifest signature | e.g. sub-store `0.4.0-alpha.1` | each plugin repo: `manifest.json`, `ui/package.json`, `system-go` const; `tools/bump.sh` | | dashboard → server | none at build time; runtime API compatibility only. Post-deploy check: About page must show matching versions | — | workspace release law (root AGENTS.md) | | plugins → server | **NOTHING** — see gap G1 | — | — | | Astra → sing-box fork / server | not machine-resolvable from `Package.swift` — see gap G4 | — | — | @@ -70,4 +71,7 @@ released server would reject cannot reach a merge unnoticed. Three tag lanes per `rules/01 §8.5` (image train `alpha-X.Y.ZaN` · prerelease semver `vX.Y.Z-alpha.N`, never GitHub Latest · stable semver `vX.Y.Z` on explicit operator decision); release order `sdk → server / dashboard / node-agent → docs site → plugins → plugin-index`; -tag pushes are operator-only. The promotion protocol amendment is slice 3. +tag pushes are operator-only. **The promotion protocol is no longer pending**: `rules/01 §8.5` +carries the coordinated-train amendment (changelog row #3, co-signed 2026-07-27) — a train is a +standalone `vX.Y.Z` defined by its CI-validated `train.json`, promoted in one operator act, and +a plain train may contain no prerelease component. From 2c99204ccc7a496ac18b5651019cf0793fa5eed2 Mon Sep 17 00:00:00 2001 From: lr00rl Date: Mon, 27 Jul 2026 07:55:28 -0700 Subject: [PATCH 4/6] Make the version table a dated snapshot and correct three stale values Athena's review (lattice#3): dashboard was alpha.6 not alpha.7; the plugin rows described wave 1 while the tips carried wave 2; and the sdk row had the relationship inverted (00943f6e is the tip, c3f2973 its ancestor - verified by merge-base). Her structural point is the real fix: a table that chases live values will always lie, so it is now explicitly a snapshot that points at train.json as the living form. --- docs/contracts/release-pin-graph.md | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/contracts/release-pin-graph.md b/docs/contracts/release-pin-graph.md index be95042..697e156 100644 --- a/docs/contracts/release-pin-graph.md +++ b/docs/contracts/release-pin-graph.md @@ -21,22 +21,29 @@ | Astra → sing-box fork / server | not machine-resolvable from `Package.swift` — see gap G4 | — | — | | fleet nodes → sing-box fork binary | deployed binary; version recorded in no repo file — see gap G4 | — | — | -## 2. Version state (refreshed 2026-07-27 after the first signing wave + two image trains) +## 2. Version state — SNAPSHOT at 2026-07-27T15:48Z (second signing wave) -| Repo | Stable | Current line | Signed bundle digest | +> This table is a **dated snapshot, not a live view**, and it will be wrong the next time +> anything is signed — it went stale twice while being written. The living form of exactly +> this data is `train.json` (`lattice.release.train.v1`, `lattice-plugin-index`), which is +> generated per train and CI-validated. Read this section as history; read a train file for +> truth. + +| Repo | Stable | Line at snapshot | Signed bundle digest | |---|---|---|---| -| lattice-server | `v0.2.1` | image train **`alpha-0.2.2a4`** (deployed); integration `c9c6710` | — | -| lattice-sdk | `v0.2.9` | integration `c3f2973`; carries the plugin SDK module (`00943f6e`) | — | -| lattice-dashboard | — | `v0.2.2-alpha.6`; integration `8e6c206` (reconciled union) | — | +| lattice-server | `v0.2.1` | image train `alpha-0.2.2a4` (deployed); integration `c9c6710` | — | +| lattice-sdk | `v0.2.9` | integration tip `00943f6e` (the plugin SDK module merge); `c3f2973` — the reconciliation merge — is its ancestor | — | +| lattice-dashboard | — | `v0.2.2-alpha.7`; integration `8e6c206`+ (reconciled union) | — | | lattice-node-agent | — | `v0.3.3-alpha.2` | — | -| plugin vpn-core | index `stable: 0.7.2` | **0.8.0-alpha.6** | `d2e681a6…` | -| plugin sub-store | — | **0.4.0-alpha.1** (embedded-engine line) | `e0524e35…` | -| plugin wireguard | — | **0.1.0-alpha.8** | `34eb6c07…` | -| plugin netguard | — | **0.1.0-alpha.8** | `c00334a8…` | -| plugin template | — | **0.2.1-alpha.5** | `c4bfe8be…` | +| plugin vpn-core | index `stable: 0.7.2` | `0.8.0-alpha.7` | `89e4d484…` | +| plugin sub-store | — | `0.4.0-alpha.1` (embedded-engine line) | `e0524e35…` | +| plugin wireguard | — | `0.1.0-alpha.9` | `decba2ac…` | +| plugin netguard | — | `0.1.0-alpha.9` | `ac7e1d66…` | +| plugin template | — | `0.2.1-alpha.6` | `0a806be8…` | Every plugin digest above is the SIGNED value at its integration tip, independently confirmed -by that repo's CI (the double-pack byte-compare gate) after the wave. +by that repo's CI double-pack byte-compare gate. Two signing waves reached this state on +2026-07-27: the embedded-engine/SDK wave, then the bridge-migration wave. ## 3. Gaps — status after 2026-07-27 From dad071d038dee5bfcefd076a4fe4c2a881da1e93 Mon Sep 17 00:00:00 2001 From: lr00rl Date: Fri, 31 Jul 2026 03:51:35 -0700 Subject: [PATCH 5/6] Preserve the release pin graph as a dated evidence snapshot The pin graph mixed snapshot-era facts with later train implementation details, which made historical claims read like live guarantees. Separate those periods, correct the recorded versions, and narrow validator claims to the invariants it actually checks. Constraint: The snapshot must precede the document commit and retain the train.json wording that governed at that time. Rejected: Present the versioned train candidate as live truth | it represents one selected train and does not verify remote tags, pins, digests, latest status, or deployment. Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep snapshot facts distinct from later implementation and current operational state. Tested: git diff --check; five-component candidate inspected with zero min_server fields; independent r3 review [ack]. Not-tested: Browser rendering; cross-repository tag, pin, digest, latest, and deployed-state truth. --- docs/contracts/release-pin-graph.md | 88 ++++++++++++++++------------- 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/docs/contracts/release-pin-graph.md b/docs/contracts/release-pin-graph.md index 697e156..bcd34c7 100644 --- a/docs/contracts/release-pin-graph.md +++ b/docs/contracts/release-pin-graph.md @@ -1,19 +1,19 @@ -# Release pin graph — current state (refreshed 2026-07-27) +# Release pin graph — mechanism and gap snapshot at 2026-07-27T14:53Z -> TASK-0010 slice 1 (operator ruling 2026-07-26 §1b). Every cross-repo pin as it exists today, -> each claim with its source of truth (repo + file). Gaps are named explicitly — they are the -> requirements list that slices 2 and 3 then closed (schema+CI in `lattice-plugin-index`; the -> `rules/01 §8.5` amendment). Refreshed after the 2026-07-27 signing wave so it describes -> reality, not the morning of the 26th. Nothing in this document changes behavior. +> TASK-0010 slice 1 (operator ruling 2026-07-26 §1b). This page records the pin mechanisms, +> observed values, and open gaps at the timestamp above; it is not a live/latest view. Each +> observation names its repo source. Later train artifacts are versioned records for one selected +> train, not a replacement for live fleet or latest-release discovery. Nothing here changes +> behavior. ## 1. The graph — who pins whom, and where -| Consumer → dependency | Pin mechanism | Value today | Source of truth | +| Consumer → dependency | Pin mechanism | Value at snapshot | Source of truth | |---|---|---|---| | lattice-server → lattice-sdk | `sdk.ref` SHA file + go.mod pseudo-version | `4a318f24…` / `v0.2.18-0.20260722123932-4a318f246d23` | `lattice-server:sdk.ref`, `lattice-server:go.mod` (integration `c9c6710`) | | lattice-server → lattice-dashboard | `dashboard.ref` SHA file (image embeds the built dashboard) | `8e6c206…` (reconciled tip — was `a927c6c`, see G2) | `lattice-server:dashboard.ref` (integration `c9c6710`) | | lattice-node-agent → lattice-sdk | go.mod pseudo-version | `v0.2.18-…-4a318f246d23` (same commit as server's) | `lattice-node-agent:go.mod` (integration `03f730a`) | -| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | latest: **`alpha-0.2.2a4`**, deployed and verified | `lattice-server:.github/workflows/container.yml`, git tags | +| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | **`alpha-0.2.2a4`**, operator-recorded as deployed and verified by the snapshot | `lattice-server:.github/workflows/container.yml`, git tags | | plugin-index → each plugin | `plugins.json`: per-plugin `channels` (stable/alpha) + `releases[]` carrying `manifest_url`, `artifact_url`, `artifact_sha256`, `signature_ed25519`; publisher ed25519 key pinned at index level | schema `lattice.plugin.index.v1`, `status: "draft"`, generated 2026-07-22 | `lattice-plugin-index:plugins.json` | | each plugin (internal) | manifest `version` = `ui/package.json` = Go const, moved together; artifact digest ↔ manifest signature | e.g. sub-store `0.4.0-alpha.1` | each plugin repo: `manifest.json`, `ui/package.json`, `system-go` const; `tools/bump.sh` | | dashboard → server | none at build time; runtime API compatibility only. Post-deploy check: About page must show matching versions | — | workspace release law (root AGENTS.md) | @@ -21,19 +21,20 @@ | Astra → sing-box fork / server | not machine-resolvable from `Package.swift` — see gap G4 | — | — | | fleet nodes → sing-box fork binary | deployed binary; version recorded in no repo file — see gap G4 | — | — | -## 2. Version state — SNAPSHOT at 2026-07-27T15:48Z (second signing wave) +## 2. Version state at the snapshot (second signing wave) -> This table is a **dated snapshot, not a live view**, and it will be wrong the next time -> anything is signed — it went stale twice while being written. The living form of exactly -> this data is `train.json` (`lattice.release.train.v1`, `lattice-plugin-index`), which is -> generated per train and CI-validated. Read this section as history; read a train file for -> truth. +> This table will be wrong after the next signing or tag. A versioned path such as +> `train/v0.3.0-alpha.1.json` (added after this snapshot) records one explicitly selected train; +> the associated validator proves document structure, tag lanes, and local cross-field +> invariants. It does **not** prove +> that remote tags resolve to the listed commits, repo pin files match, signed digests are true, +> or that the train is latest/deployed. Those cross-repo checks remain work. | Repo | Stable | Line at snapshot | Signed bundle digest | |---|---|---|---| | lattice-server | `v0.2.1` | image train `alpha-0.2.2a4` (deployed); integration `c9c6710` | — | -| lattice-sdk | `v0.2.9` | integration tip `00943f6e` (the plugin SDK module merge); `c3f2973` — the reconciliation merge — is its ancestor | — | -| lattice-dashboard | — | `v0.2.2-alpha.7`; integration `8e6c206`+ (reconciled union) | — | +| lattice-sdk | `v0.2.17` | integration tip `00943f6e` (the plugin SDK module merge); `c3f2973` — the reconciliation merge — is its ancestor | — | +| lattice-dashboard | — | `v0.2.2-alpha.7`; integration `8e6c206` (reconciled union) | — | | lattice-node-agent | — | `v0.3.3-alpha.2` | — | | plugin vpn-core | index `stable: 0.7.2` | `0.8.0-alpha.7` | `89e4d484…` | | plugin sub-store | — | `0.4.0-alpha.1` (embedded-engine line) | `e0524e35…` | @@ -45,40 +46,47 @@ Every plugin digest above is the SIGNED value at its integration tip, independen by that repo's CI double-pack byte-compare gate. Two signing waves reached this state on 2026-07-27: the embedded-engine/SDK wave, then the bridge-migration wave. -## 3. Gaps — status after 2026-07-27 +## 3. Gaps at the snapshot -- **G1 — plugin→server minimum version: MECHANISM SHIPPED, NOT YET USED.** `min_server` is a - signed, additive manifest field (server#22, merged; index mirrors, never owns — - rules/01 §8.5). All five manifests currently declare it ABSENT, deliberately: the honest - floor is "a server containing the budget/backing work", which no released version string - named at signing time. First train assembly is when it gets real values. -- **G2 — CLOSED.** `dashboard.ref` now points at the reconciled tip `8e6c206`; verified in - production via the image label `dashboard-revision` on `alpha-0.2.2a4`. The gap was caught - BY that label after `alpha-0.2.2a3` shipped the pre-reconciliation ref — the label is +- **G1 — plugin→server minimum version: MECHANISM PRESENT, VALUES ABSENT.** `min_server` is a + signed, additive manifest field (server#22, merged; index mirrors, never owns — rules/01 + §8.5). All five signed manifests omitted it at the snapshot. The later first train also mirrors + no `min_server` values because none exist in those manifests. Closing G1 requires assigning and + signing real compatibility floors first; train assembly cannot invent them. +- **G2 — CLOSED.** At the snapshot, `dashboard.ref` pointed at the reconciled tip `8e6c206`, + verified in production via the image label `dashboard-revision` on `alpha-0.2.2a4`. The gap + was caught by that label after `alpha-0.2.2a3` shipped the pre-reconciliation ref — the label is therefore load-bearing evidence, not decoration. -- **G3 — FORMAT SHIPPED.** `lattice.release.train.v1` (schema + zero-dependency validator + - CI) lives in `lattice-plugin-index`; a real `train.json` gets written at the first - coordinated cut. -- **G4 — OPEN.** Astra's fork pin and the fleet's deployed sing-box version are still - unrecorded. Unchanged today. -- **G5 — PARTIALLY CLOSED.** The index is still hand-generated, but train manifests are now - CI-validated on every push/PR; index regeneration itself remains a manual release step. +- **G3 — FORMAT IN REVIEW, NOT SHIPPED.** At the snapshot, `lattice.release.train.v1` (schema, + zero-dependency validator, CI wiring) existed only on open `lattice-plugin-index#3`. The later + first candidate is a versioned `train/v0.3.0-alpha.1.json`, not a live `train.json` alias. +- **G4 — OPEN.** Astra's fork pin and the fleet's deployed sing-box version were unrecorded. +- **G5 — OPEN, WITH STRUCTURAL VALIDATION IN REVIEW.** The index was hand-generated. PR #3's + train validator checked JSON structure, tag lanes, and local cross-field rules; it did not + generate the index or cross-check remote tag→commit, pin-file, or signed-digest truth. - **G6 — SHARPENED.** The validator reports `server_version` from module metadata when run as `go run …@` (e.g. `v0.2.2-0.20260727095611-c9c671079ab6`) but reports `dev` when run from a plain local build — so the plugin CI gate, which pins a released module version, DOES name a real server, while a hand-built binary does not. Train assembly must take the version from the pinned module reference, not from a local build. -**Also now true (not a gap, a property to preserve)**: every plugin repo's CI runs the -released-server manifest validator on `main`, `integration`, and PRs, so a manifest that the -released server would reject cannot reach a merge unnoticed. +**A property present at the snapshot**: every plugin repo's CI configuration runs the +released-server manifest validator on `main`, `integration`, and PRs. A rejection therefore +produces a failing check on those paths; branch policy and human review still decide whether a +failure can be merged. -## 4. Standing law this document leans on (unchanged here) +## 4. Standing law at the snapshot, and the later implementation Three tag lanes per `rules/01 §8.5` (image train `alpha-X.Y.ZaN` · prerelease semver `vX.Y.Z-alpha.N`, never GitHub Latest · stable semver `vX.Y.Z` on explicit operator decision); release order `sdk → server / dashboard / node-agent → docs site → plugins → plugin-index`; -tag pushes are operator-only. **The promotion protocol is no longer pending**: `rules/01 §8.5` -carries the coordinated-train amendment (changelog row #3, co-signed 2026-07-27) — a train is a -standalone `vX.Y.Z` defined by its CI-validated `train.json`, promoted in one operator act, and -a plain train may contain no prerelease component. +tag pushes are operator-only. The coordinated-train amendment had landed in `rules/01 §8.5` +(changelog row #3, co-signed 2026-07-27). At the snapshot, that law specified a standalone +`vX.Y.Z` train defined by a CI-validated `train.json`, promoted in one operator act, with no +prerelease component inside a plain train. + +The later PR implements candidates as versioned files such as +`train/v0.3.0-alpha.1.json`. Its present validator proves only structure, tag lanes, and local +cross-field invariants; cross-repo tag, pin, and digest truth remains unverified as recorded in +G5. The later implementation detail does not retroactively rewrite what the snapshot-era law +said. From 680be3a6aa0caccf25cbef47615b9f810015b94e Mon Sep 17 00:00:00 2001 From: lr00rl Date: Fri, 31 Jul 2026 04:08:45 -0700 Subject: [PATCH 6/6] Make every release snapshot claim traceable to its real source The pin graph still omitted the node-agent stable line, collapsed compatibility metadata and a selected minimum-server floor into one empty edge, and cited build configuration for a deployment observation. Record the distinct states and point the deployment claim at the persisted field evidence. Constraint: Preserve the dated snapshot boundary and avoid turning signed metadata into a runtime-enforcement claim. Rejected: Remove the deployment observation | a persisted operator field record exists and is the correct source to cite. Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep mechanism, selected value, structural validation, and runtime enforcement as separate claims in pin graphs. Tested: node-agent tag ancestry/date; five signed manifests inspected; server manifest semantics inspected; deployment record verified; semantic assertions; git diff --check; independent r4 review [ack]. Not-tested: Live deployment state after the snapshot; browser rendering; cross-repository train-truth enforcement. --- docs/contracts/release-pin-graph.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/contracts/release-pin-graph.md b/docs/contracts/release-pin-graph.md index bcd34c7..ff319ca 100644 --- a/docs/contracts/release-pin-graph.md +++ b/docs/contracts/release-pin-graph.md @@ -13,11 +13,11 @@ | lattice-server → lattice-sdk | `sdk.ref` SHA file + go.mod pseudo-version | `4a318f24…` / `v0.2.18-0.20260722123932-4a318f246d23` | `lattice-server:sdk.ref`, `lattice-server:go.mod` (integration `c9c6710`) | | lattice-server → lattice-dashboard | `dashboard.ref` SHA file (image embeds the built dashboard) | `8e6c206…` (reconciled tip — was `a927c6c`, see G2) | `lattice-server:dashboard.ref` (integration `c9c6710`) | | lattice-node-agent → lattice-sdk | go.mod pseudo-version | `v0.2.18-…-4a318f246d23` (same commit as server's) | `lattice-node-agent:go.mod` (integration `03f730a`) | -| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | **`alpha-0.2.2a4`**, operator-recorded as deployed and verified by the snapshot | `lattice-server:.github/workflows/container.yml`, git tags | +| ghcr image ← lattice-server | tag-triggered build: pushing `alpha-X.Y.ZaN` IS the release trigger; the image embeds both refs above | **`alpha-0.2.2a4`**, operator-recorded as deployed and verified by the snapshot | build trigger: `lattice-server:.github/workflows/container.yml` + git tags; deployment observation: `lattice-olympus:messages/archive/20260727-1012Z-zeus-deploy-verified.md` | | plugin-index → each plugin | `plugins.json`: per-plugin `channels` (stable/alpha) + `releases[]` carrying `manifest_url`, `artifact_url`, `artifact_sha256`, `signature_ed25519`; publisher ed25519 key pinned at index level | schema `lattice.plugin.index.v1`, `status: "draft"`, generated 2026-07-22 | `lattice-plugin-index:plugins.json` | | each plugin (internal) | manifest `version` = `ui/package.json` = Go const, moved together; artifact digest ↔ manifest signature | e.g. sub-store `0.4.0-alpha.1` | each plugin repo: `manifest.json`, `ui/package.json`, `system-go` const; `tools/bump.sh` | | dashboard → server | none at build time; runtime API compatibility only. Post-deploy check: About page must show matching versions | — | workspace release law (root AGENTS.md) | -| plugins → server | **NOTHING** — see gap G1 | — | — | +| plugins → server | required signed `compatibility.server` metadata + optional signed top-level `min_server` train floor | all five declare `compatibility.server`; all five omit `min_server`, so no train floor is selected/signed — see G1 | each plugin's `manifest.json`; `lattice-server:internal/plugin/plugin.go`, `internal/plugin/manifest_v2.go` | | Astra → sing-box fork / server | not machine-resolvable from `Package.swift` — see gap G4 | — | — | | fleet nodes → sing-box fork binary | deployed binary; version recorded in no repo file — see gap G4 | — | — | @@ -35,7 +35,7 @@ | lattice-server | `v0.2.1` | image train `alpha-0.2.2a4` (deployed); integration `c9c6710` | — | | lattice-sdk | `v0.2.17` | integration tip `00943f6e` (the plugin SDK module merge); `c3f2973` — the reconciliation merge — is its ancestor | — | | lattice-dashboard | — | `v0.2.2-alpha.7`; integration `8e6c206` (reconciled union) | — | -| lattice-node-agent | — | `v0.3.3-alpha.2` | — | +| lattice-node-agent | `v0.2.9` | `v0.3.3-alpha.2` | — | | plugin vpn-core | index `stable: 0.7.2` | `0.8.0-alpha.7` | `89e4d484…` | | plugin sub-store | — | `0.4.0-alpha.1` (embedded-engine line) | `e0524e35…` | | plugin wireguard | — | `0.1.0-alpha.9` | `decba2ac…` | @@ -48,11 +48,13 @@ by that repo's CI double-pack byte-compare gate. Two signing waves reached this ## 3. Gaps at the snapshot -- **G1 — plugin→server minimum version: MECHANISM PRESENT, VALUES ABSENT.** `min_server` is a - signed, additive manifest field (server#22, merged; index mirrors, never owns — rules/01 - §8.5). All five signed manifests omitted it at the snapshot. The later first train also mirrors - no `min_server` values because none exist in those manifests. Closing G1 requires assigning and - signing real compatibility floors first; train assembly cannot invent them. +- **G1 — plugin→server minimum version: METADATA AND MECHANISM PRESENT, FLOOR ABSENT.** Every + signed v2 manifest carries required `compatibility.server` metadata. Separately, `min_server` + is the optional signed train-floor field (server#22, merged; index mirrors, never owns — + rules/01 §8.5). The server validator requires the compatibility metadata to be bounded and + non-empty; that is not evidence of a selected `min_server` floor. All five signed manifests + omitted `min_server` at the snapshot, and the later first train therefore mirrors none. Closing + G1 requires assigning and signing real floors first; train assembly cannot invent them. - **G2 — CLOSED.** At the snapshot, `dashboard.ref` pointed at the reconciled tip `8e6c206`, verified in production via the image label `dashboard-revision` on `alpha-0.2.2a4`. The gap was caught by that label after `alpha-0.2.2a3` shipped the pre-reconciliation ref — the label is