Files
suaveplan/openspec/archive/2026-04-19-enterprise-diff/specs/diff/spec.md
2026-05-27 23:29:53 +01:00

3.1 KiB

Capability Spec: Diff

Package: @suaveplan/diff Version: 0.0.0 (genesis) Status: ADDED


ADDED Requirements

DIFF-01 — RFC 6902 JSON Patch operations

The package MUST implement all six RFC 6902 operation types (add, remove, replace, move, copy, test) with typed factories createAdd/createRemove/createReplace/createMove/createCopy/createTest. Operations MUST be validated via validateOperation; invalid shapes MUST throw DiffError with code DIFF.INVALID_PATCH.

DIFF-02 — Structural diff round-trip

diff(a, b, options?) MUST produce a Patch such that applyPatch(a, diff(a, b)).doc is structurally equal (deepEqual) to b for any JSON-serializable pair. DiffOptions.arrayStrategy MUST accept 'index' (positional replacement) or 'lcs' (longest-common-subsequence minimal edits).

DIFF-03 — Apply / revert contract

applyPatch(doc, patch, options?) MUST return PatchResult { doc, applied, errors } where applied is the count of successfully applied operations. When a test operation fails, the patch MUST abort and throw DiffError with code DIFF.TEST_FAILED, leaving doc unchanged. revertPatch(doc, patch) MUST be implemented such that revertPatch(applyPatch(doc, p).doc, p) equals doc for any patch that does not contain copy or move operations affecting removed data.

DIFF-04 — Patch inversion

invertPatch(patch) MUST produce a patch that, when applied to the result of the original, yields the original document. invertOperation(op) MUST satisfy invertOperation(invertOperation(op)) deep-equals op for all operation types. Operations that cannot be losslessly inverted (remove without prior value capture) MUST throw DiffError with code DIFF.INVERT_FAILED.

DIFF-05 — Path utilities

parsePath and formatPath MUST implement RFC 6901 JSON Pointer grammar. parsePath(formatPath(p)) MUST deep-equal p for every valid Path. Escape/unescape MUST handle ~0 (~) and ~1 (/). isPrefix, pathEquals, parentPath, lastSegment MUST be pure functions with zero allocation on the hot path where possible.

DIFF-06 — History manager

History MUST implement bounded linear undo/redo with branch truncation: pushing a new entry after an undo MUST discard all redo entries. Size limits MUST be configurable via HistoryOptions.maxEntries. HistoryState MUST be serializable via JSON.stringify. Listeners MUST be notified on every state transition.

DIFF-07 — Sync manager

SyncManager MUST accept local deltas via push and remote deltas via pull, emitting SyncDelta objects to registered listeners. Conflicts MUST be detected via operationsConflict and resolved per the configured SyncOptions.conflictResolution strategy ('local-wins' | 'remote-wins' | 'reject').

DIFF-08 — Observability

Every applyPatch call MUST emit OTel counters diff.apply.total (labels: outcome: 'success' | 'failure') and histogram diff.apply.duration_ms. Every diff call MUST emit diff.diff.duration_ms. Meters MUST use @opentelemetry/api and be no-ops when no exporter is registered.