diff --git a/workplans/SHARD-WP-0008-write-path.md b/workplans/SHARD-WP-0008-write-path.md new file mode 100644 index 0000000..19d876d --- /dev/null +++ b/workplans/SHARD-WP-0008-write-path.md @@ -0,0 +1,135 @@ +--- +id: SHARD-WP-0008 +type: workplan +title: "write path — overlay engine, writable adapter, apply-under-drift" +domain: whynot +repo: shard-wiki +status: active +owner: tegwick +topic_slug: whynot +created: "2026-06-15" +updated: "2026-06-15" +depends_on: + - SHARD-WP-0007 +--- + +# SHARD-WP-0008 — Write path + +## Goal + +Implement the **write path** on top of the foundation slice (SHARD-WP-0007): the +**overlay-before-mutation** lifecycle (draft → patch → apply-under-drift) plus write-through +for capable shards, faithful to `FederationRequirements.md` ADR-05, `CoreArchitectureBlueprint` +§8.2 (overlay engine) / §8.6 (apply-under-drift) / §8.1 (overlays are coordination-canonical +events). Target capability: **edit a page → draft overlay recorded in the decision log → +apply (fast-forward) to a writable shard, or refuse on drift, or keep as local truth on a +read-only shard**, with `overlay_state` surfaced in provenance (union without erasure). + +**Non-goal (this slice):** three-way/auto merge (refuse-on-conflict is enough now), federation +propagation, network API, lossy native-syntax overlays. Those are later. + +## Guiding rules + +- Overlay-before-mutation (I-5); no silent remote mutation. Detection is core, resolution is + policy (I-7). Overlays/decisions are coordination-canonical (the decision log, §8.1). +- Honour the §11 dependency rule and capability-as-data: write only where the verified profile + supports `WRITE`; everything below write-through degrades to overlay (I-8). + +--- + +## Writable file-store adapter + positive write conformance + +```task +id: SHARD-WP-0008-T1 +status: todo +priority: high +``` + +Make `FolderAdapter` optionally **writable** (`writable=True`): declare `WRITE` + +`write_granularity=PER_PAGE` in the profile, implement `write(key, body)` (write-through to +disk, return the updated `Page` with a new rev), and `current_rev(key)` for drift detection. +Extend the conformance suite with a **positive, non-destructive write probe** for adapters that +claim `WRITE` (write a probe key, read back, clean up). Tests: writable round-trip; read-only +folder still rejects write; conformance passes for both. + +## Overlay model + OverlayEngine.draft() + +```task +id: SHARD-WP-0008-T2 +status: todo +priority: high +``` + +`coordination/overlay.py`: an `Overlay` value type (id, target identity, base_rev, body, state) +and `OverlayEngine.draft(identity, body, base_rev)` that records an `OVERLAY_CREATED` event in +the decision log (coordination-canonical) and returns the draft. The log fold exposes open +overlays. Tests: draft recorded + retrievable via fold; overlay id stable. + +## Patch rendering + +```task +id: SHARD-WP-0008-T3 +status: todo +priority: medium +``` + +Render an overlay as a reviewable **patch** (a `Patch` with a unified diff of base→overlay body, +Markdown/native). Pure function over (base body, overlay body). Tests: patch shows the change; +empty patch when unchanged. + +## apply-under-drift + +```task +id: SHARD-WP-0008-T4 +status: todo +priority: high +``` + +`OverlayEngine.apply(overlay_id)` with §8.6 semantics: compare overlay `base_rev` to the +shard's `current_rev`; **unchanged → fast-forward** (write-through via the adapter, record an +applied/`MERGE_DECIDED` event, overlay state → APPLIED); **changed → refuse + re-present** as a +conflict (no silent clobber); **read-only target → stays DRAFT** (local truth, fully attributed). +Tests: ff apply mutates the shard; drift refuses; read-only keeps draft. + +## Overlay-aware union read + +```task +id: SHARD-WP-0008-T5 +status: todo +priority: medium +``` + +When resolving a page that has an **open overlay**, surface it: the read reflects +`overlay_state=DRAFT` in the provenance envelope and (where policy shows drafts) the overlaid +body as a local projection over the canonical page — never hiding that it is an unapplied +overlay. Tests: page with a draft reads with overlay_state DRAFT; applied/none reads clean. + +## Wiring into InformationSpace + integration + +```task +id: SHARD-WP-0008-T6 +status: todo +priority: medium +``` + +Add `InformationSpace.edit(name, body)` (write-through if the resolved shard supports WRITE, +else create an overlay), `overlay(name, body)`, and `apply_overlay(id)`. Integration test for +the full path (write-through on a writable shard; overlay→apply fast-forward; drift refusal; +read-only shard keeps a draft). Update SCOPE; `pytest` + pyflakes green. + +--- + +## Acceptance criteria + +- `pytest` green, pyflakes clean, no new runtime dependencies. +- Overlay lifecycle works: draft (logged) → patch → apply (fast-forward) / refuse-on-drift / + stay-draft-on-read-only; write-through works for capable shards. +- No silent remote mutation: applying against a drifted/read-only target never clobbers. +- Overlays are coordination-canonical (in the decision log); `overlay_state` is surfaced in + provenance (union without erasure). +- Each task committed; state-hub synced. + +## Suggested task order + +T1 writable adapter → T2 overlay/draft → T3 patch → T4 apply-under-drift → T5 overlay-aware +read → T6 wiring + integration.