F5 — Cross-rung git-native persistence format + round-trip to the Gitea substrate rung #14

Closed
opened 2026-06-11 05:25:14 +00:00 by benstull · 0 comments
Owner

Summary

Cross-rung git-native persistence format + Gitea round-trip: graduate the
inner-loop .threads/ sidecar (threads · attributions · proposals on shared
anchors/Provenance) from "the editor's private shape" into the cross-rung
contract
— the one shared git-native model the three-rung ladder (editor →
Gitea substrate → rfc-app) reads and writes — and prove it with a real
round-trip: the coauthoring record travels with ordinary git push/pull
and survives being read, displayed, and written-back from outside the editor
rung without re-homing.

Problem / pain

The epic's promise is that the coauthoring record is "persisted git-natively so
it is serverless, durable, and portable up to the Gitea rung later without
re-homing
" — but portability has never been exercised. Every graduated spec
deliberately deferred it: the F2 design called its format "the inner-loop
shape, not yet the cross-rung contract" (coauthoring-inner-loop.md §9
OPEN→F5), and F3/F4 each routed "may re-home attributions[] /
proposals[]" here. Today nothing outside the extension can consume the
sidecar with any guarantee: there is no published schema contract, no
compatibility/versioning rules for readers that aren't this codebase, no
cross-rung identity story (Provenance.id is a local $USER, not a forge
identity), and no answer for what happens when a second writer — a teammate's
editor via git pull, or a future Gitea-rung surface — touches the same
record. Until F5, the ladder's "git is truth; the surfaces are clients"
architecture is an assertion, not a property.

Who feels it

  • The coauthoring pair of Epic #1 the moment a second machine or second
    human enters: clone the repo elsewhere and the record must arrive intact and
    merge sanely.
  • The future Gitea-substrate rung (rfc-app#46) and rfc-app above it:
    both are specified as clients of this same model ("continuity of substrate —
    no migration cliff"); they can only be built against a contract that exists.
  • The Wiggleverse doc-in-git estate rfc-app#46 names (specs, handbook, BDD
    corpora): each becomes coauthorable only if the record format is consumable
    beyond one editor.

Desired outcome / value

The sidecar format is a published, versioned, validated contract — the
cross-rung schema, not an implementation detail: documented field-by-field,
with explicit schema-evolution rules (what a schemaVersion bump means, what
unknown-field/unknown-version readers must do), a cross-rung identity
story for Provenance, and a defined merge/conflict story for concurrent
writers. A demonstrated round-trip proves it: the record a coauthoring
session produces is pushed, consumed and modified by a non-extension client
(the Gitea-rung posture), pulled back, and the editor renders the result
correctly — re-anchored or honestly orphaned, never corrupted, never
silently re-homed. This converts the epic's portability promise into a tested
property and unblocks the rfc-app#46 brainstorming on the substrate side.

Acceptance — "done right"

  • A contract document exists in the content repo specifying the cross-rung
    format: every section (anchors, threads, attributions[],
    proposals[], Provenance, schemaVersion), its invariants (stable key
    order, pending-only proposals, fingerprint semantics), and the
    compatibility rules — how readers treat unknown fields and newer/older
    schemaVersions, and what is allowed to change without a major break.
  • Identity crosses rungs: Provenance can carry a forge-resolvable
    identity (not just a local username) for both human and agent authors,
    without breaking existing sidecars.
  • Round-trip proven, no re-homing: a record written by the extension is
    (a) pushed and pulled through ordinary git with full fidelity (byte-stable
    serialization holds), (b) modified by a non-extension writer (the
    Gitea-rung posture — e.g. a thread reply appended out-of-editor per the
    contract), and (c) on pull, the editor renders the out-of-editor change
    correctly: new messages appear, anchors re-resolve or orphan per INV-1,
    nothing is silently dropped or rewritten.
  • Concurrent-writer story defined and exercised: two writers touching the
    same sidecar produce a merge that is human-resolvable per the contract
    (stable ordering limits the blast radius), and the documented resolution
    rules are validated by a test, not just asserted.
  • Validation is runnable outside the extension: a deterministic check (a
    schema and/or a small validator) that any rung can run against a sidecar
    file; the extension's own unit suite uses it.
  • Unit tests cover format round-trip fidelity, version/unknown-field
    handling, and the merge cases; host E2E covers the out-of-editor-change
    → pull → render path (the F2/F3/F4 external-change pattern; no LLM in CI).

Scope / non-goals

In scope: the cross-rung contract document + versioning/compatibility
rules; any format changes the contract forces (re-homing or extending sidecar
sections, a schemaVersion bump if break-class changes are needed); the
Provenance cross-rung identity extension; the out-of-editor write +
round-trip proof and its tests; a rung-neutral validation artifact.

Out of scope (deferred, not forgotten): the Gitea-side substrate
primitives themselves
(file-level discussions UI, LLM participants,
native proposal layers — rfc-app#46's build, on its own tracker); any rfc-app
work; a sync daemon or server (git push/pull IS the transport); file
rename/move tracking (inner-loop §9 OPEN→later, unchanged); multi-file
orchestration; rejected-proposal history trails (propose-accept §9 OPEN→later
— unless the contract work resolves it for free).

Assumptions · constraints · dependencies

  • Parent: #1 (the coauthoring Epic — F5 is its final decomposition item;
    the three acceptance pillars shipped in F2 #4, F3 #6, F4 #12). Blocked
    by:
    #12 (F4 — shipped; PR #13). Feeds: the rfc-app#46 substrate
    brainstorming (the contract is its input) and the rfc-app rung above.
  • The ladder is the architecture (Epic #1, rfc-app#46): three rungs, one
    shared git-native model — git is truth; the surfaces are clients. F5 is
    the editor rung's half of that bargain: make the model consumable beyond
    the editor. "No migration cliff" is the strategic outcome F5 must not
    foreclose.
  • Established format facts (the contract starts from what shipped, not a
    blank page): per-document .threads/<path>.json sidecar; shared anchors
    map + Fingerprint (text/before/after/lineHint); Provenance
    (kind: human|agent, agent carries sdk/model/sessionId); threads[] /
    attributions[] (state, not history) / pending-only proposals[]
    (INV-13); SCHEMA_VERSION = 1; stable key order + trailing newline
    (INV-2) precisely so diffs/merges stay sane; extension is currently the
    sole writer — F5 deliberately ends that assumption.
  • Open design questions (the forks) — routed to the follow-on
    brainstorming session; this Feature consumes its reviewed Solution Design:
    (a) substrate: does the sidecar stay the canonical cross-rung carrier
    (the F2 decision rejected git notes/trailers for the inner loop — does
    that hold cross-rung, per the epic's ".threads/ sidecar vs. git notes
    vs. trailers" fork)? (b) contract form: prose spec vs. JSON Schema vs.
    both, and where it lives (content repo vs. engineering schemas/); (c)
    identity: how Provenance.id maps to forge identities (email? forge
    username? signed?) for humans and agents; (d) merge semantics: per-
    section merge rules vs. whole-file last-writer + repair, and whether the
    contract mandates a merge driver; (e) round-trip proof shape: what
    plays the Gitea-rung writer in CI (a script appending a thread reply per
    the contract is the cheapest honest stand-in); (f) versioning: does
    the identity extension fit additively in schemaVersion 1 or force 2,
    and the deprecation rules either way.
  • No new auth/credential surface; no server (INV-8 spirit unchanged).

Source / signal

Epic #1 (coauthoring-plugin-on-cline) decomposition item F5 — Git-native
persistence format (.threads/ sidecar vs. git notes vs. trailers) +
round-trip to the Gitea rung (#46)
; the graduated Solution Designs'
deferrals — coauthoring-inner-loop.md §9 OPEN→F5 ("F2's format is the
inner-loop shape, not yet the cross-rung contract"),
coauthoring-attribution.md §9 OPEN→F5, coauthoring-propose-accept.md §9
OPEN→F5 ("the proposed→ratified→attributed story is what crosses rungs");
architecture lineage ben.stull/rfc-app#48 (the ladder) and
ben.stull/rfc-app#46 (the Gitea-substrate rung: "continuity of substrate —
no migration cliff"; "git is truth"). Builds on F4 shipped in #12 (PR #13,
session vscode-cowriting-plugin-0012). Captured in session
vscode-cowriting-plugin-0013.

Priority (WSJF)

Provisional: Value medium-high (converts the epic's portability promise
into a tested property; second-writer safety matters the moment a teammate
clones) · Time-criticality low-medium (no Gitea-rung deployment exists
yet; rises to P1 when rfc-app#46 work is scheduled) ·
Opportunity-enablement high (the contract is the prerequisite input for
the rfc-app#46 substrate brainstorming and the whole ladder) ÷ Size
medium-large → P2.

Decomposition (Feature → slice checklist)

  • Resolve the design forks (substrate carrier; contract form/home;
    cross-rung identity; merge semantics; round-trip proof shape; versioning)
    — consume the follow-on Solution Design.
  • Author the cross-rung contract document (+ JSON Schema / validator if
    the design picks one), including compatibility + merge rules.
  • Format changes the contract forces (identity extension on
    Provenance; any re-homing/section changes; schemaVersion handling),
    with migration/compat tests for existing sidecars.
  • Rung-neutral validation artifact wired into the unit suite.
  • Out-of-editor writer (the Gitea-rung stand-in) + round-trip host E2E:
    write → push/pull → out-of-editor modify → pull → render
    (re-anchor/orphan honest), plus concurrent-writer merge cases.
  • Docs: README cross-rung section; contract pointer from the plugin repo.

Solution notes (optional — NOT a design)

The shipped format already has cross-rung virtues the contract can simply
ratify: plain pretty JSON, stable key order, trailing newline, shared
anchors, state-not-history sections — all chosen in F2 for mergeability.
The F2 decision table already weighed git notes (bind to commits, weak
tooling, no default push) and commit trailers (only fit diffs) against the
sidecar — the cross-rung question is whether those trade-offs change when the
reader is a forge, not whether to relitigate the inner loop. Provenance has
room for an additive identity field (serializer omits optionals — F4
precedent). The engineering repo's central-schema discipline
(schemas/app.schema.json, versioned centrally, apps conform) is the obvious
template for where/how the contract is published — and
wiggleverse/engineering#19 (validated collection metadata) is the adjacent
conversation rfc-app#46 names. All non-binding — the forks above are the
design session's to resolve.

<!-- Set the type/* label: type/feature --> ## Summary **Cross-rung git-native persistence format + Gitea round-trip**: graduate the inner-loop `.threads/` sidecar (threads · attributions · proposals on shared `anchors`/`Provenance`) from "the editor's private shape" into the **cross-rung contract** — the one shared git-native model the three-rung ladder (editor → Gitea substrate → rfc-app) reads and writes — and prove it with a real **round-trip**: the coauthoring record travels with ordinary `git push`/`pull` and survives being read, displayed, and written-back from outside the editor rung without re-homing. ## Problem / pain The epic's promise is that the coauthoring record is "persisted git-natively so it is serverless, durable, and **portable up to the Gitea rung later without re-homing**" — but portability has never been exercised. Every graduated spec deliberately deferred it: the F2 design called its format "the inner-loop shape, **not yet the cross-rung contract**" (coauthoring-inner-loop.md §9 OPEN→F5), and F3/F4 each routed "may re-home `attributions[]` / `proposals[]`" here. Today nothing outside the extension can consume the sidecar with any guarantee: there is no published schema contract, no compatibility/versioning rules for readers that aren't this codebase, no cross-rung identity story (`Provenance.id` is a local `$USER`, not a forge identity), and no answer for what happens when a *second* writer — a teammate's editor via `git pull`, or a future Gitea-rung surface — touches the same record. Until F5, the ladder's "git is truth; the surfaces are clients" architecture is an assertion, not a property. ## Who feels it - The **coauthoring pair** of Epic #1 the moment a second machine or second human enters: clone the repo elsewhere and the record must arrive intact and merge sanely. - The **future Gitea-substrate rung** (rfc-app#46) and **rfc-app** above it: both are specified as clients of this same model ("continuity of substrate — no migration cliff"); they can only be built against a contract that exists. - The **Wiggleverse doc-in-git estate** rfc-app#46 names (specs, handbook, BDD corpora): each becomes coauthorable only if the record format is consumable beyond one editor. ## Desired outcome / value The sidecar format is a **published, versioned, validated contract** — the cross-rung schema, not an implementation detail: documented field-by-field, with explicit schema-evolution rules (what a `schemaVersion` bump means, what unknown-field/unknown-version readers must do), a cross-rung **identity** story for `Provenance`, and a defined **merge/conflict** story for concurrent writers. A demonstrated **round-trip** proves it: the record a coauthoring session produces is pushed, consumed and modified by a non-extension client (the Gitea-rung posture), pulled back, and the editor renders the result correctly — re-anchored or honestly orphaned, never corrupted, never silently re-homed. This converts the epic's portability promise into a tested property and unblocks the rfc-app#46 brainstorming on the substrate side. ## Acceptance — "done right" - A **contract document** exists in the content repo specifying the cross-rung format: every section (`anchors`, `threads`, `attributions[]`, `proposals[]`, `Provenance`, `schemaVersion`), its invariants (stable key order, pending-only proposals, fingerprint semantics), and the **compatibility rules** — how readers treat unknown fields and newer/older `schemaVersion`s, and what is allowed to change without a major break. - **Identity crosses rungs:** `Provenance` can carry a forge-resolvable identity (not just a local username) for both human and agent authors, without breaking existing sidecars. - **Round-trip proven, no re-homing:** a record written by the extension is (a) pushed and pulled through ordinary git with full fidelity (byte-stable serialization holds), (b) **modified by a non-extension writer** (the Gitea-rung posture — e.g. a thread reply appended out-of-editor per the contract), and (c) on pull, the editor renders the out-of-editor change correctly: new messages appear, anchors re-resolve or orphan per INV-1, nothing is silently dropped or rewritten. - **Concurrent-writer story defined and exercised:** two writers touching the same sidecar produce a merge that is human-resolvable per the contract (stable ordering limits the blast radius), and the documented resolution rules are validated by a test, not just asserted. - **Validation is runnable outside the extension:** a deterministic check (a schema and/or a small validator) that any rung can run against a sidecar file; the extension's own unit suite uses it. - **Unit tests** cover format round-trip fidelity, version/unknown-field handling, and the merge cases; **host E2E** covers the out-of-editor-change → pull → render path (the F2/F3/F4 external-change pattern; no LLM in CI). ## Scope / non-goals **In scope:** the cross-rung contract document + versioning/compatibility rules; any format changes the contract forces (re-homing or extending sidecar sections, a `schemaVersion` bump if break-class changes are needed); the `Provenance` cross-rung identity extension; the out-of-editor write + round-trip proof and its tests; a rung-neutral validation artifact. **Out of scope (deferred, not forgotten):** the **Gitea-side substrate primitives themselves** (file-level discussions UI, LLM participants, native proposal layers — rfc-app#46's build, on its own tracker); any rfc-app work; a sync daemon or server (git push/pull IS the transport); file rename/move tracking (inner-loop §9 OPEN→later, unchanged); multi-file orchestration; rejected-proposal history trails (propose-accept §9 OPEN→later — unless the contract work resolves it for free). ## Assumptions · constraints · dependencies - **Parent:** #1 (the coauthoring Epic — F5 is its final decomposition item; the three acceptance pillars shipped in F2 #4, F3 #6, F4 #12). **Blocked by:** #12 (F4 — shipped; PR #13). **Feeds:** the rfc-app#46 substrate brainstorming (the contract is its input) and the rfc-app rung above. - **The ladder is the architecture** (Epic #1, rfc-app#46): three rungs, one shared git-native model — *git is truth; the surfaces are clients.* F5 is the editor rung's half of that bargain: make the model consumable beyond the editor. "No migration cliff" is the strategic outcome F5 must not foreclose. - **Established format facts** (the contract starts from what shipped, not a blank page): per-document `.threads/<path>.json` sidecar; shared `anchors` map + `Fingerprint` (text/before/after/lineHint); `Provenance` (`kind: human|agent`, agent carries sdk/model/sessionId); `threads[]` / `attributions[]` (state, not history) / pending-only `proposals[]` (INV-13); `SCHEMA_VERSION = 1`; stable key order + trailing newline (INV-2) precisely so diffs/merges stay sane; extension is currently the sole writer — F5 deliberately ends that assumption. - **Open design questions (the forks)** — routed to the follow-on brainstorming session; this Feature consumes its reviewed Solution Design: (a) **substrate**: does the sidecar stay the canonical cross-rung carrier (the F2 decision rejected git notes/trailers for the inner loop — does that hold cross-rung, per the epic's "`.threads/` sidecar vs. git notes vs. trailers" fork)? (b) **contract form**: prose spec vs. JSON Schema vs. both, and where it lives (content repo vs. engineering schemas/); (c) **identity**: how `Provenance.id` maps to forge identities (email? forge username? signed?) for humans and agents; (d) **merge semantics**: per- section merge rules vs. whole-file last-writer + repair, and whether the contract mandates a merge driver; (e) **round-trip proof shape**: what plays the Gitea-rung writer in CI (a script appending a thread reply per the contract is the cheapest honest stand-in); (f) **versioning**: does the identity extension fit additively in `schemaVersion` 1 or force 2, and the deprecation rules either way. - No new auth/credential surface; no server (INV-8 spirit unchanged). ## Source / signal Epic #1 (`coauthoring-plugin-on-cline`) decomposition item **F5 — Git-native persistence format (`.threads/` sidecar vs. git notes vs. trailers) + round-trip to the Gitea rung (#46)**; the graduated Solution Designs' deferrals — `coauthoring-inner-loop.md` §9 OPEN→F5 ("F2's format is the inner-loop shape, not yet the cross-rung contract"), `coauthoring-attribution.md` §9 OPEN→F5, `coauthoring-propose-accept.md` §9 OPEN→F5 ("the proposed→ratified→attributed story is what crosses rungs"); architecture lineage `ben.stull/rfc-app#48` (the ladder) and `ben.stull/rfc-app#46` (the Gitea-substrate rung: "continuity of substrate — no migration cliff"; "git is truth"). Builds on F4 shipped in #12 (PR #13, session `vscode-cowriting-plugin-0012`). Captured in session `vscode-cowriting-plugin-0013`. ## Priority (WSJF) Provisional: **Value** medium-high (converts the epic's portability promise into a tested property; second-writer safety matters the moment a teammate clones) · **Time-criticality** low-medium (no Gitea-rung deployment exists yet; rises to P1 when rfc-app#46 work is scheduled) · **Opportunity-enablement** high (the contract is the prerequisite input for the rfc-app#46 substrate brainstorming and the whole ladder) ÷ **Size** medium-large → **P2**. ## Decomposition (Feature → slice checklist) - [ ] Resolve the design forks (substrate carrier; contract form/home; cross-rung identity; merge semantics; round-trip proof shape; versioning) — consume the follow-on Solution Design. - [ ] Author the cross-rung contract document (+ JSON Schema / validator if the design picks one), including compatibility + merge rules. - [ ] Format changes the contract forces (identity extension on `Provenance`; any re-homing/section changes; `schemaVersion` handling), with migration/compat tests for existing sidecars. - [ ] Rung-neutral validation artifact wired into the unit suite. - [ ] Out-of-editor writer (the Gitea-rung stand-in) + round-trip host E2E: write → push/pull → out-of-editor modify → pull → render (re-anchor/orphan honest), plus concurrent-writer merge cases. - [ ] Docs: README cross-rung section; contract pointer from the plugin repo. ## Solution notes (optional — NOT a design) The shipped format already has cross-rung virtues the contract can simply ratify: plain pretty JSON, stable key order, trailing newline, shared `anchors`, state-not-history sections — all chosen in F2 *for* mergeability. The F2 decision table already weighed git notes (bind to commits, weak tooling, no default push) and commit trailers (only fit diffs) against the sidecar — the cross-rung question is whether those trade-offs change when the reader is a forge, not whether to relitigate the inner loop. `Provenance` has room for an additive identity field (serializer omits optionals — F4 precedent). The engineering repo's central-schema discipline (`schemas/app.schema.json`, versioned centrally, apps conform) is the obvious template for where/how the contract is published — and `wiggleverse/engineering#19` (validated collection metadata) is the adjacent conversation rfc-app#46 names. All non-binding — the forks above are the design session's to resolve.
benstull added the type/featurepriority/P2 labels 2026-06-11 05:25:14 +00:00
Sign in to join this conversation.