Files
lda-wf/docs/superpowers/specs/2026-07-29-safe-compatibility-merges-and-draft-transfer-design.md
T

259 lines
9.6 KiB
Markdown

# Safe Compatibility Merges And Draft Transfer Design
## Status
Approved for implementation planning on 2026-07-29.
## Problem
Draft step inputs and outputs are stored as ordered canonical binding lists.
Those lists support repeated sources, literal values, and ordering that the
older compatibility maps cannot represent. A later compatibility-map merge can
therefore read valid canonical bindings into dictionaries and silently rewrite
them into a different list.
Draft workspaces also lack a direct CLI workflow for moving a complete draft
document between tools or restoring an edited snapshot. Operators can extract a
draft from `inspect`, but there is no revision-checked import operation, file
overwrite policy, or local/remote parity.
## Goals
- Prevent compatibility-map merges from silently changing canonical bindings
they cannot represent.
- Reject unsafe merges before mutation and direct callers to canonical binding
replacement.
- Preserve compatibility merges for drafts that are exactly representable.
- Export the exact stored draft document to a JSON file.
- Import a complete draft into an existing workspace under revision control.
- Recompute semantic validation for imported documents.
- Give local and remote CLI targets identical behavior.
## Non-Goals
- Removing the compatibility map operations.
- Inventing syntax for selecting one binding from an ambiguous fan-out group.
- Making maps capable of representing canonical binding lists.
- Creating, renaming, or cloning workspaces during import.
- Exporting workspace revision, timestamps, status, or diagnostics.
- Adding TypeScript JSON-RPC coverage or generating transport contracts.
- Adding a generic backup archive containing artifacts, deployments, or runs.
## Compatibility Merge Policy
Compatibility merges remain available only when the operation can preserve the
existing canonical list exactly, except for the entries explicitly updated or
appended by the requested map.
An unsafe merge returns an input error before any patch or revision increment.
The error identifies the selected step or workflow output and tells the caller
to inspect the canonical list, edit it explicitly, and replace it through the
corresponding canonical binding operation.
### Step Inputs
Step-input compatibility maps use graph source paths as dictionary keys and
keep literal values in a separate target-keyed dictionary. The API checks that
the existing ordered input list survives conversion to those maps and back
without changing:
- binding count;
- binding order;
- source and target paths;
- literal values;
- path/literal interleaving.
This rejects source fan-out, duplicate literal targets, and any ordering that
the map serializer would rearrange. A simple list with an exactly reproducible
literal prefix followed by unique path sources remains mergeable.
### Step Outputs
Step-output compatibility maps use local source paths as dictionary keys. The
API checks that the existing output list survives map conversion and
serialization exactly. Repeated local sources therefore reject compatibility
merge because the request cannot identify which fan-out binding should change.
### Workflow Outputs
The current workflow-output merge implementation already preserves unrelated
ordered bindings and literals without rebuilding the complete list through a
dictionary. It remains safe when each requested source identifies at most one
existing path binding.
If a requested source occurs more than once, the merge is ambiguous and is
rejected rather than updating every fan-out target. Duplicate sources that are
not mentioned by the requested map remain unchanged.
### Error Guidance
Representative messages are:
```text
step 'render' inputs cannot be safely merged through a compatibility map;
replace the complete canonical binding list instead
```
```text
workflow output source 'state.title' has multiple bindings and cannot be
updated through a compatibility map; replace the complete canonical binding
list instead
```
CLI help continues to label `--merge` as compatibility-only, but no longer
describes it as silently or acceptably lossy.
## Draft Export
Add:
```text
wf draft export WORKSPACE --output PATH [--force]
```
The command requests the workspace with `include_draft=True` and writes only
the `draft` object as formatted UTF-8 JSON with a final newline.
- `--output` is required.
- An existing path is rejected unless `--force` is supplied.
- Parent directories must already exist.
- File errors are reported as CLI input errors.
- The command does not print a second JSON representation to stdout.
- Export does not mutate the workspace.
The exported document intentionally excludes workspace metadata. Revision is a
property of the destination workspace and must be supplied explicitly during
import.
## Draft Import
Add:
```text
wf draft import WORKSPACE --revision N --file PATH
```
Import replaces the draft document in an existing workspace. It does not create
a workspace when the ID is missing and does not take a workspace ID from the
file.
The operation performs these steps:
1. Parse the file as a JSON object.
2. Validate the request envelope.
3. Check the expected workspace revision.
4. Structurally validate the complete document as `WorkflowDraft`.
5. Resolve current capability definitions and semantically validate the whole
draft.
6. Persist the document and its fresh status and diagnostics in one
revision-checked store replacement.
A structurally malformed document, stale revision, missing workspace, or store
conflict leaves the workspace unchanged. A structurally valid but semantically
invalid draft is persisted with fresh diagnostics so it can be inspected and
repaired through the normal draft-authoring workflow.
Importing a document identical to the stored draft is an exact no-op and does
not increment the revision. Revalidation without replacement remains the
responsibility of `wf draft validate`.
## Full-Document Replacement Boundary
The existing `replace_validated_draft_document` helper must not power import.
That helper is intentionally limited to focused edits whose fields have already
been validated and cannot change capability contracts; it preserves the
workspace's current semantic status and diagnostics.
Add a distinct full-document replacement operation in the draft workspace/API
layer. Its name and docstring must make semantic revalidation explicit. The
operation reuses the existing `WorkflowDraft` model, capability-definition
resolution, draft validation, summary, conflict, and store replacement helpers.
It must not add another workflow validator.
## Transport Design
Export needs no new server operation because the existing inspect operation can
return the full draft.
Import adds one Python JSON-RPC operation and matching remote client handler for
full-document replacement. The CLI calls the same handler method for local and
remote targets. The request carries:
```json
{
"workspace_id": "report",
"revision": 4,
"draft": {}
}
```
The draft field uses the existing raw draft/document model boundary. Transport
validation must reject non-object values before semantic authoring logic runs.
This slice does not add the operation to the TypeScript RPC package.
## Testing
### Compatibility Merges
Add focused API tests for:
- representable step-input merge;
- step-input source fan-out rejection;
- step-input literal/interleaving rejection;
- representable step-output merge;
- step-output source fan-out rejection;
- workflow-output merge preserving unrelated fan-out and literals;
- workflow-output rejection when the requested source is ambiguous;
- stale revision precedence where required by the existing API contract;
- unchanged draft and revision after every rejected merge.
Add CLI tests proving `--merge` still selects the compatibility handlers and
that remote errors present the canonical-replacement guidance.
### Draft Transfer
Add tests for:
- exact export document shape and formatted JSON;
- overwrite refusal and `--force`;
- missing parent and file-write failures;
- local export and remote export through full inspection;
- successful import with a revision increment;
- identical import as a no-op;
- stale revision conflict;
- malformed JSON and non-object input;
- structurally invalid draft without mutation;
- semantically invalid draft persisted with fresh diagnostics;
- current capability definitions used during import validation;
- local and remote CLI dispatch parity;
- JSON-RPC request and response behavior.
Round-trip one exported document into a different existing workspace and assert
that its stored draft equals the exported object. Workspace IDs and revisions
remain destination metadata and are not copied.
## Documentation And Issue State
After verification:
- mark the compatibility-map fan-out issue complete in `ISSUES.md`;
- document that compatibility merge now rejects unrepresentable canonical
lists;
- add export/import examples to CLI-facing documentation and `skills/wf-cli`;
- add a completed roadmap entry linked to the archived implementation plan;
- archive the implementation plan under
`docs/historical/superpowers/plans/`.
## Success Criteria
- No compatibility-map merge can silently collapse or ambiguously rewrite
canonical fan-out.
- Simple representable map merges remain operational.
- Rejected merges are atomic and explain how to use canonical replacement.
- A draft document can be exported and imported through local or remote CLI.
- Import is revision checked and recomputes semantic status and diagnostics.
- Export/import do not leak or overwrite workspace metadata.
- No second binding model, workflow validator, or TypeScript transport contract
is introduced.
- Focused tests, Ruff, formatting, and basedpyright pass.