generated from coulomb/repo-seed
FolderAdapter(writable=True) declares WRITE+PER_PAGE, implements write() and current_rev() (mtime token for drift detection). Conformance gains a content-preserving positive write probe for WRITE-claiming adapters. 5 tests green, full suite green, pyflakes clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
143 lines
5.3 KiB
Markdown
143 lines
5.3 KiB
Markdown
---
|
|
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
|
|
state_hub_workstream_id: "12bed418-39d6-47fa-a359-ff04bae6ec99"
|
|
---
|
|
|
|
# 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: done
|
|
priority: high
|
|
state_hub_task_id: "80492f8e-125c-4015-b3c0-821fbec038e0"
|
|
```
|
|
|
|
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
|
|
state_hub_task_id: "cc6bf9a3-667d-468d-972d-dae51931a657"
|
|
```
|
|
|
|
`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
|
|
state_hub_task_id: "90d98c16-ed3b-414f-802c-b0400eca6ede"
|
|
```
|
|
|
|
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
|
|
state_hub_task_id: "2a0179b1-802e-44e6-883d-9f1babefee80"
|
|
```
|
|
|
|
`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
|
|
state_hub_task_id: "4536d74f-3860-4b4c-82d2-e8d20e6e2125"
|
|
```
|
|
|
|
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
|
|
state_hub_task_id: "ab01fffb-61ad-416c-9f13-fdfbfd503153"
|
|
```
|
|
|
|
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.
|