docs: additive-v2 implementation plan + spec §3 fix (no marker auto-stamp)

This commit is contained in:
Brian Majewski 2026-06-08 09:58:23 -07:00
parent 54575af61c
commit ee4ea413ef
2 changed files with 609 additions and 9 deletions

View file

@ -65,12 +65,24 @@ No stored state is required: the presence/absence of the v1 twin **is** the
signal. Mental model: *"keep alive what old clients already knew; new things are
v2-only."*
### 3. The marker — auto-stamped, informational only
### 3. The marker — unchanged meaning, NOT auto-stamped
The first push by an upgraded client stamps `.secrets-format = 2`. Because writes
are now driven by the twin rule (not the marker), the marker no longer decides
blob location — it becomes purely "a v2-aware client has touched this store."
`secrets which` continues to report `format: vN`.
Additive-v2 read/write behavior does **not** depend on the `.secrets-format`
marker at all (reads try both suffixes; writes follow the twin rule). So the
marker is left exactly as it is today: set only by `init` (born-v2, a fresh pure-v2
store) or `migrate --finalize` (a store GC'd to pure v2). A transitional store
that upgraded clients are dual-writing stays **markerless (v1)** — which is
*accurate*: it has not been finalized to pure v2, and v1 twins still exist.
Push deliberately does **not** auto-stamp the marker. (An earlier draft proposed
auto-stamping on first push; that was dropped during planning because it would
make a v1 store read as v2 the moment anyone pushed — turning every `migrate`
into a no-op and contradicting the "v1 until finalized" model the whole
migration relies on. The marker's only purposes — `secrets which` display and
gating `migrate`/`finalize` — are better served by it continuing to mean
"finalized/pure v2.")
`secrets which` continues to report `format: vN` from the marker.
### 4. `finalize` → optional GC, never required
@ -145,8 +157,8 @@ blobs — YAGNI for v1).
path(s) to write: for `properties`, both suffixes when a v1 twin already
exists, else v2-only; single path for `file`.
- **`push_external_files`** — write to every path from
`_external_blob_write_targets` (was a single `age -o`); auto-stamp the marker
on first push by an upgraded client.
`_external_blob_write_targets` (was a single `age -o`). Push does **not** stamp
the marker (see §3).
- **`pull_external_files` / `cmd_verify`** — resolve blobs via
`_resolve_external_blob_read` (was the single-suffix lookup).
- **`_migrate_finalize`** — unchanged logic; doc/help reframed as optional GC.
@ -174,8 +186,9 @@ blobs — YAGNI for v1).
3. Twin rule — new external → v2-only: push a brand-new `properties` external;
assert **only** `.properties.age` is written (no v1 twin created) — the
forcing function.
4. Marker auto-stamp: first push by an upgraded client on a markerless store
stamps `.secrets-format = 2`.
4. Marker NOT stamped on push: a `make_v1_store` + push leaves `.secrets-format`
absent (store stays v1/transitional); the existing `make_v1_store; push;
migrate` flow is unaffected.
5. dotenv/`file` unaffected: a dotenv and a `file` external round-trip identically
regardless of store marker.
6. `migrate --status` coverage: reports which externals are dual-written vs