From a4e8fafcb75f1c9a75bb8dc52744e4dcc6e91ede Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 15:50:15 -0700 Subject: [PATCH 01/63] docs: freeze ReplaceRange fact-law cycle --- .../0158-replace-range-canonical-fact-law.md | 315 ++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 docs/design/0158-replace-range-canonical-fact-law.md diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md new file mode 100644 index 00000000..d8ba4c71 --- /dev/null +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -0,0 +1,315 @@ +--- +title: "DL-0158 - ReplaceRange Canonical Fact Law And Oracle" +legend: "DL" +lane: "design" +issue: "https://github.com/flyingrobots/jedit/issues/292" +status: "active" +owners: + - "@flyingrobots" +created: "2026-07-19" +updated: "2026-07-19" +--- + +# DL-0158 - ReplaceRange Canonical Fact Law And Oracle + +## Linked Issue + +- [#292 Freeze the ReplaceRange text-fact law and differential oracle](https://github.com/flyingrobots/jedit/issues/292) + +## Decision Summary + +Jedit will publish one machine-readable `jedit.text.schema@1` declaration for +the native text facts used by production `ReplaceRange`, together with a +committed differential corpus regenerated from the current handwritten rope +planner. Version 1 preserves the existing compact, field-ordered UTF-8 JSON +fact bytes, node-alpha atom storage, domain-separated BLAKE3 identities, and +edge-free native graph representation. The richer TypeScript graph-rope model +is not silently merged into this surface: propositions absent from the native +runtime are explicitly excluded, and any future structural-edge or codec +migration must be a separately admitted operation. + +## Sponsored Human + +A Jim maintainer wants the first Edict-authored `ReplaceRange` operation to +preserve existing production text and history exactly, without discovering +during cutover that Edict and Echo compiled against a different rope model. + +## Sponsored Agent + +An Echo or Edict agent needs exact, digest-locked fact, codec, identity, and +finite-corpus resources so it can implement and verify the target operation +without importing Jedit's handwritten planner or inferring semantics from +TypeScript types. + +## Hill + +By the end of this cycle, a clean Jedit checkout can regenerate the canonical +text-schema declaration and every `ReplaceRange` oracle case from the current +planner, compare exact bytes and identities with committed resources, and fail +when the production fact law or finite semantic corpus drifts. + +## Current Truth + +The merge target is +[`c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/commit/c70e12d73b4b00bc92412bab67e1761f7dd22f82). + +- The production host defines eight native text fact types. `ReplaceRange` + reads or writes seven of them: Buffer, Blob, Leaf, Branch, Head, Rewrite, + and Diff. + [`native/jedit-echo-host/src/records.rs#L7:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/records.rs#L7) +- Native fact bytes are produced by `serde_json::to_vec`; content-addressed + fact identifiers hash an exact per-fact domain followed by those bytes. + [`native/jedit-echo-host/src/records.rs#L123:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/records.rs#L123) +- The planner stores each fact as one typed node plus one node-alpha atom + attachment. It emits no Echo edges and deletes no retained nodes. + [`native/jedit-echo-host/src/rope.rs#L217:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope.rs#L217) +- `plan_replace` enforces the canonical-head basis, half-open UTF-8 byte + range, exact no-op refusal, checked sequence/version increments, persistent + split/build/join, immutable Head/Rewrite/Diff creation, and Buffer advance. + [`native/jedit-echo-host/src/rope.rs#L385:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope.rs#L385) +- Rope construction caps leaves at 4,096 UTF-8 bytes, path-copies slices, + and deterministically height-balances joins. + [`native/jedit-echo-host/src/rope/tree.rs#L9:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope/tree.rs#L9) +- The TypeScript graph-rope model contains propositions and fields that are + not equivalent to the native retained representation. It is therefore not + an alternate serialization of the same version-1 schema. + [`src/domain/graph-rope-types.ts#L85:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/src/domain/graph-rope-types.ts#L85) + +Echo ADR 0023 assigns the fact schemas, codec/identity law, operation +semantics, and differential corpus to Jedit. This cycle earns those Jedit-owned +inputs; it does not claim that Echo can execute them yet. + +## Problem + +The production behavior is deterministic, but its cross-repository contract +is implicit in Rust struct order, serde behavior, private planner state, and +hash-domain constants. Edict cannot bind an exact Jedit lawpack and Echo cannot +validate or interpret one without choosing a schema and codec. Choosing the +richer TypeScript model would silently migrate production history; choosing +the native model without a published declaration would leave package closure +dependent on source-code interpretation. + +The current tests cover useful behavior, but they do not publish the exact +initial fact graph, invocation bytes, ordered writes, actual read/write +support, resulting facts, and obstruction posture needed for a separately +implemented Echo evaluator comparison. + +## Scope + +This cycle includes: + +- one versioned, machine-readable canonical text-schema declaration; +- the seven native fact shapes used by `ReplaceRange`; +- exact compact JSON codec, attachment, type-label, and identity laws; +- a type-level split between ordinary typed facts and content-addressed facts, + so the mutable keyed Buffer fact cannot accidentally receive a second + content-derived identity; +- explicit fixed-width ranges and UTF-8 requirements; +- an explicit no-edge posture for version 1; +- explicit exclusion and migration posture for TypeScript-only propositions; +- deterministic success cases for insertion, deletion, Unicode replacement, + cross-leaf replacement, and replacement larger than one leaf; +- deterministic obstruction cases for stale basis, malformed/out-of-bounds + range, invalid UTF-8 boundary, no-op, and arithmetic overflow; +- exact committed initial graph, invocation, read/write support, ordered patch, + result, and obstruction evidence for each case; +- byte-for-byte regeneration tests and checked SHA-256 resource digests. + +## Non-Goals + +This cycle does not include: + +- an Echo operation-program ABI or evaluator; +- an Edict target lowerer, package, or generated client; +- production invocation, routing, fact-byte, or identity changes; +- an application callback, native rope intrinsic, or caller-supplied patch; +- unifying the TypeScript and native graph-rope models; +- migrating existing JSON facts to CBOR or structural Echo edges; +- checkpoint, `TextWindow`, save/export, causal diff, or authorization work; +- Cyber Kitten runtime machinery, child lanes, wormholes, or Continuum. + +## User Experience / Product Shape + +Not applicable. This cycle freezes a machine contract and conformance corpus; +it changes no editor command, rendering, input behavior, or user-visible text. + +### Accessibility Considerations + +Not applicable. The resources are structured UTF-8 JSON consumable without a +visual interface. Hex-encoded bytes always have adjacent semantic field names. + +## Runtime / API Contract + +The declaration coordinate is `jedit.text.schema@1`. Its exact bytes receive a +SHA-256 digest for Edict resource binding. That digest is substitution +evidence only; it does not confer an operation coordinate, invocability, or +Echo authority. + +Version 1 declares: + +| Fact | Type label | Identity posture | +| --- | --- | --- | +| Buffer | `jedit.text.BufferWorldline.v1` | Stable buffer-key node identifier only; canonical bytes may change at that node. | +| Blob | `jedit.text.TextBlob.v1` | Content-addressed from canonical fact bytes. | +| Leaf | `jedit.text.RopeLeaf.v1` | Content-addressed from canonical fact bytes. | +| Branch | `jedit.text.RopeBranch.v1` | Content-addressed from canonical fact bytes. | +| Head | `jedit.text.RopeHead.v1` | Content-addressed from canonical fact bytes. | +| Rewrite | `jedit.text.RopeRewrite.v1` | Content-addressed from canonical fact bytes. | +| Diff | `jedit.text.RopeDiff.v1` | Content-addressed from canonical fact bytes. | + +Each graph fact is represented by one typed Echo node and one node-alpha atom +whose atom type equals the node type and whose payload is the canonical fact +bytes. Version 1 has no structural edge propositions. Relationships such as +Head-to-root and Branch-to-child are exact 32-byte identifiers in canonical +record fields. + +Canonical fact JSON is: + +- UTF-8 with no byte-order mark or insignificant whitespace; +- one object whose fields occur in declaration order; +- decimal unsigned integers with no leading zeroes; +- exact `null` for absent optional identifiers; +- 32-byte identifiers and digests encoded as arrays of 32 decimal octets; +- byte strings encoded as arrays of decimal octets; +- JSON strings encoded with the compact serde-json version-1 escaping profile; +- no maps, floats, signed integers, duplicate members, or unknown members. + +Content-addressed identifiers are BLAKE3-256 over the exact declared domain +bytes followed by canonical fact bytes. Buffer identifiers, Blob content +hashes, and empty-root digests use their separately declared BLAKE3 domains. +The generic content-fact constructor does not accept Buffer facts. This removes +the currently representable but unused `BufferFact` content identity while +preserving every production byte and authoritative keyed Buffer identifier. + +The oracle resource is finite conformance evidence. It does not prove semantic +equivalence for every possible `ReplaceRange` input. Its legacy planner path +must not invoke, link, or share an evaluator implementation with the future +Echo operation program. Sharing the published fact schema and comparison codec +is disclosed and expected. + +## Lower Modes + +The contract is entirely headless. `cargo test` regenerates and compares the +resources. A deliberately named fixture-update mode may rewrite generated +corpus bytes during development, but CI only checks and never updates them. +Malformed resources fail with ordinary test errors; there is no fallback to +source-code inference. + +## Data / State Model + +| Category | Description | +| --- | --- | +| Source of truth | Existing native production fact types and `plan_replace` semantics at the merge target. | +| Derived state | Machine-readable schema and finite golden corpus. | +| Invalid states | Noncanonical fact bytes, wrong domains or identities, schema drift, corpus drift, or ambiguous native/TypeScript ownership. | +| Reset behavior | Fixtures regenerate from a clean synthetic graph; no production WAL or user data is read. | +| Serialization | Digest-locked JSON resources containing exact fact bytes as lowercase hexadecimal. | +| Deterministic assumptions | Ordered Rust structs/maps, exact domain bytes, explicit UTF-8 inputs, no clock/randomness/host paths. | + +## Accessibility Posture + +| Concern | Posture | +| --- | --- | +| Semantic labels or facts | Every identifier and byte vector is labeled by role and fact type. | +| Focus order or ownership | Not applicable. | +| Hidden or visual-only information | None. | +| Keyboard behavior | Not applicable. | +| Secret or redaction behavior | Fixtures contain only synthetic text. | + +## Localization / Directionality Posture + +| Concern | Posture | +| --- | --- | +| User-visible strings | None. | +| Catalog keys | None. | +| Supported locales updated | No. | +| Directionality assumptions | JSON member order is semantic to the codec; human language direction is irrelevant. | +| Validation command | Native Rust tests plus `npm run check`. | + +## Agent Inspectability / Explainability Posture + +Agents can inspect one declaration and one corpus manifest without executing +the product or scraping Rust debug output. Every case names its semantic +purpose, exact basis, input, terminal posture, support, facts, and result. The +manifest states the source commit and evidence grade so a consumer cannot +mistake finite-corpus agreement for formal equivalence. + +## Linked Invariants + +- Jedit owns product semantics and canonical application schemas. +- Echo owns runtime admission, execution, commitment, receipts, and recovery. +- Edict artifacts do not prove that Echo executed them. +- A resource digest supplies substitution evidence, not authority. +- Fixed-width byte coordinates remain `u64`; GraphQL `Int` is not inherited. +- A failed plan produces no parent-visible patch. +- Existing native fact bytes remain stable in version 1. +- Tests are executable specification. + +## Design Alternatives Considered + +### Adopt the richer TypeScript graph-rope model now + +Rejected. It has different propositions and identity material from the native +production graph. Selecting it would make this prerequisite a data migration +and would invalidate the very oracle intended to protect the cutover. + +### Migrate version 1 to canonical CBOR or structural edges + +Rejected for this cycle. Either change may be desirable, but both alter +retained fact bytes and content identities. They require an explicit migration +operation and compatibility proof rather than being hidden in package work. + +### Publish prose plus example snapshots + +Rejected. Prose does not constrain field order, exact bytes, hash domains, or +corpus drift. The declaration and corpus must be regenerated and checked by +the implementation that currently owns the law. + +### Chosen: freeze native JSON version 1 and a finite differential corpus + +This preserves current production history and gives the next Echo slice exact +inputs and outputs. It is intentionally conservative: structural graph schema +evolution can proceed later under a new coordinate and explicit migration. + +## Implementation Slices + +- [x] Slice 1: land this decision record and freeze the executable witnesses. +- [ ] Slice 2: publish and byte-lock `jedit.text.schema@1`. +- [ ] Slice 3: publish and regenerate the `ReplaceRange` differential corpus. + +## Tests To Write First + +- [ ] Schema resource test: regenerate exact bytes, verify the checked SHA-256 + digest, and reject drift from native fact labels, field order, domains, + attachment posture, and excluded propositions. +- [ ] Type-law test: prove Buffer has no generic content-addressed constructor + while every immutable ReplaceRange fact retains its existing identifier. +- [ ] Success corpus test: regenerate each initial graph and replacement plan, + then compare exact reads, writes, patch order, fact bytes, identities, + result metrics, and preserved facts. +- [ ] Obstruction corpus test: regenerate each failure, compare its Jedit-owned + semantic category and legacy evidence, and prove no write plan exists. +- [ ] Determinism test: regenerate the complete resources twice from fresh + synthetic graphs and compare exact bytes. +- [ ] Fixed-width test: retain `u64` values above GraphQL/i32 range in the + declaration and invocation vectors without narrowing. + +## Acceptance Checklist + +- [ ] Issue #292 remains the live cycle ledger. +- [ ] The declaration selects the native production model unambiguously. +- [ ] All seven ReplaceRange fact shapes and identity domains are explicit. +- [ ] TypeScript-only propositions and migration posture are explicit. +- [ ] Schema and corpus resource digests are checked from exact bytes. +- [ ] The corpus covers representative persistent-rope successes and typed + obstructions. +- [ ] No production behavior, fact bytes, authoritative identities, Echo + dependency, or user-visible behavior changes. +- [ ] Focused Rust tests and `npm run check` pass. +- [ ] The retrospective records evidence limits and the exact next Echo RED. + +## Retrospective + +To be completed before review. This section will identify the landed resource +digests, executable witnesses, any deviation from the planned corpus, and the +remaining gap before Echo can admit and execute the Jedit-owned program. From ce7bbc5c591bd468cbc91bba2c7f529216b1964a Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 16:08:21 -0700 Subject: [PATCH 02/63] feat: freeze ReplaceRange fact schema --- .../replace-range-v1/codec-vectors-v1.json | 637 ++++++++++++++++++ .../replace-range-v1/codec-vectors-v1.sha256 | 1 + .../replace-range-v1/text-schema-v1.json | 324 +++++++++ .../replace-range-v1/text-schema-v1.sha256 | 1 + .../0158-replace-range-canonical-fact-law.md | 9 +- native/jedit-echo-host/Cargo.lock | 81 ++- native/jedit-echo-host/Cargo.toml | 2 + native/jedit-echo-host/src/records.rs | 43 +- native/jedit-echo-host/src/rope.rs | 12 +- native/jedit-echo-host/src/rope/tree.rs | 12 +- .../tests/replace_range_schema.rs | 487 +++++++++++++ 11 files changed, 1583 insertions(+), 26 deletions(-) create mode 100644 contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json create mode 100644 contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 create mode 100644 contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json create mode 100644 contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 create mode 100644 native/jedit-echo-host/tests/replace_range_schema.rs diff --git a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json new file mode 100644 index 00000000..17473c2e --- /dev/null +++ b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json @@ -0,0 +1,637 @@ +{ + "formatVersion": 1, + "schemaCoordinate": "jedit.text.schema@1", + "vectors": [ + { + "fact": "Buffer", + "typeLabel": "jedit.text.BufferWorldline.v1", + "typeIdHex": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "identity": { + "kind": "keyed", + "bufferKey": "schema\n\"猫\\path.txt", + "nodeIdHex": "1d9b601e15c0e27ba93de3943427732d91ef5fba75e138a88b799a15694b144b" + }, + "value": { + "buffer_key": "schema\n\"猫\\path.txt", + "canonical_head_id": [ + 76, + 178, + 60, + 219, + 191, + 119, + 177, + 237, + 26, + 9, + 114, + 148, + 120, + 237, + 246, + 209, + 215, + 241, + 23, + 15, + 54, + 128, + 175, + 203, + 44, + 181, + 251, + 183, + 107, + 149, + 41, + 200 + ], + "projection_path": null, + "version": 2147483648 + }, + "canonicalBytesHex": "7b226275666665725f6b6579223a22736368656d615c6e5c22e78cab5c5c706174682e747874222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b37362c3137382c36302c3231392c3139312c3131392c3137372c3233372c32362c392c3131342c3134382c3132302c3233372c3234362c3230392c3231352c3234312c32332c31352c35342c3132382c3137352c3230332c34342c3138312c3235312c3138332c3130372c3134392c34312c3230305d2c2276657273696f6e223a323134373438333634387d" + }, + { + "fact": "Blob", + "typeLabel": "jedit.text.TextBlob.v1", + "typeIdHex": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "identity": { + "kind": "content-addressed", + "domainHex": "6a656469742e746578742e626c6f622e763100", + "nodeIdHex": "501b5f1bd32d468c5d9221f80693f447e5a7e3146714eda0b80fe611add60e21" + }, + "value": { + "bytes": [ + 65, + 240, + 159, + 152, + 128, + 10 + ], + "content_hash": [ + 160, + 88, + 200, + 187, + 228, + 195, + 174, + 104, + 85, + 103, + 22, + 50, + 129, + 147, + 199, + 190, + 206, + 58, + 140, + 54, + 245, + 219, + 241, + 63, + 195, + 0, + 31, + 4, + 154, + 208, + 125, + 185 + ] + }, + "canonicalBytesHex": "7b22636f6e74656e745f68617368223a5b3136302c38382c3230302c3138372c3232382c3139352c3137342c3130342c38352c3130332c32322c35302c3132392c3134372c3139392c3139302c3230362c35382c3134302c35342c3234352c3231392c3234312c36332c3139352c302c33312c342c3135342c3230382c3132352c3138355d2c226279746573223a5b36352c3234302c3135392c3135322c3132382c31305d7d" + }, + { + "fact": "Leaf", + "typeLabel": "jedit.text.RopeLeaf.v1", + "typeIdHex": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "identity": { + "kind": "content-addressed", + "domainHex": "6a656469742e746578742e6c6561662e763100", + "nodeIdHex": "543c5617ada7ac68cfeed18a6d504fbb31c4693358bf5d4b6e41cb1b30b9c8dd" + }, + "value": { + "blob_id": [ + 80, + 27, + 95, + 27, + 211, + 45, + 70, + 140, + 93, + 146, + 33, + 248, + 6, + 147, + 244, + 71, + 229, + 167, + 227, + 20, + 103, + 20, + 237, + 160, + 184, + 15, + 230, + 17, + 173, + 214, + 14, + 33 + ], + "byte_length": 6, + "byte_start": 0, + "line_breaks": 1, + "utf16_length": 4 + }, + "canonicalBytesHex": "7b22626c6f625f6964223a5b38302c32372c39352c32372c3231312c34352c37302c3134302c39332c3134362c33332c3234382c362c3134372c3234342c37312c3232392c3136372c3232372c32302c3130332c32302c3233372c3136302c3138342c31352c3233302c31372c3137332c3231342c31342c33335d2c22627974655f7374617274223a302c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f627265616b73223a317d" + }, + { + "fact": "Branch", + "typeLabel": "jedit.text.RopeBranch.v1", + "typeIdHex": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "identity": { + "kind": "content-addressed", + "domainHex": "6a656469742e746578742e6272616e63682e763100", + "nodeIdHex": "8a3ed35007c5621ff1dba65ef526532d479c650cac73bc8caf1ab38165d793a5" + }, + "value": { + "byte_length": 12, + "height": 2, + "left": [ + 84, + 60, + 86, + 23, + 173, + 167, + 172, + 104, + 207, + 238, + 209, + 138, + 109, + 80, + 79, + 187, + 49, + 196, + 105, + 51, + 88, + 191, + 93, + 75, + 110, + 65, + 203, + 27, + 48, + 185, + 200, + 221 + ], + "line_breaks": 2, + "right": [ + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34, + 34 + ], + "utf16_length": 8 + }, + "canonicalBytesHex": "7b226c656674223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c227269676874223a5b33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33342c33345d2c22627974655f6c656e677468223a31322c2275746631365f6c656e677468223a382c226c696e655f627265616b73223a322c22686569676874223a327d" + }, + { + "fact": "Head", + "typeLabel": "jedit.text.RopeHead.v1", + "typeIdHex": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "identity": { + "kind": "content-addressed", + "domainHex": "6a656469742e746578742e686561642e763100", + "nodeIdHex": "4cb23cdbbf77b1ed1a09729478edf6d1d7f1170f3680afcb2cb5fbb76b9529c8" + }, + "value": { + "basis_head_id": [ + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51 + ], + "buffer_id": [ + 29, + 155, + 96, + 30, + 21, + 192, + 226, + 123, + 169, + 61, + 227, + 148, + 52, + 39, + 115, + 45, + 145, + 239, + 95, + 186, + 117, + 225, + 56, + 168, + 139, + 121, + 154, + 21, + 105, + 75, + 20, + 75 + ], + "byte_length": 6, + "line_count": 2, + "root_digest": [ + 84, + 60, + 86, + 23, + 173, + 167, + 172, + 104, + 207, + 238, + 209, + 138, + 109, + 80, + 79, + 187, + 49, + 196, + 105, + 51, + 88, + 191, + 93, + 75, + 110, + 65, + 203, + 27, + 48, + 185, + 200, + 221 + ], + "root_node_id": [ + 84, + 60, + 86, + 23, + 173, + 167, + 172, + 104, + 207, + 238, + 209, + 138, + 109, + 80, + 79, + 187, + 49, + 196, + 105, + 51, + 88, + 191, + 93, + 75, + 110, + 65, + 203, + 27, + 48, + 185, + 200, + 221 + ], + "sequence": 2147483648, + "utf16_length": 4 + }, + "canonicalBytesHex": "7b226275666665725f6964223a5b32392c3135352c39362c33302c32312c3139322c3232362c3132332c3136392c36312c3232372c3134382c35322c33392c3131352c34352c3134352c3233392c39352c3138362c3131372c3232352c35362c3136382c3133392c3132312c3135342c32312c3130352c37352c32302c37355d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c22726f6f745f6e6f64655f6964223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c2273657175656e6365223a323134373438333634387d" + }, + { + "fact": "Rewrite", + "typeLabel": "jedit.text.RopeRewrite.v1", + "typeIdHex": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "identity": { + "kind": "content-addressed", + "domainHex": "6a656469742e746578742e726577726974652e763100", + "nodeIdHex": "0a36f333e6a19081f1373716d32287ce528e7e75fa4c7dd3b56ff44d9f0f5a44" + }, + "value": { + "basis_head_id": [ + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51 + ], + "buffer_id": [ + 29, + 155, + 96, + 30, + 21, + 192, + 226, + 123, + 169, + 61, + 227, + 148, + 52, + 39, + 115, + 45, + 145, + 239, + 95, + 186, + 117, + 225, + 56, + 168, + 139, + 121, + 154, + 21, + 105, + 75, + 20, + 75 + ], + "end_byte": 2147483652, + "inserted_byte_length": 6, + "next_head_id": [ + 76, + 178, + 60, + 219, + 191, + 119, + 177, + 237, + 26, + 9, + 114, + 148, + 120, + 237, + 246, + 209, + 215, + 241, + 23, + 15, + 54, + 128, + 175, + 203, + 44, + 181, + 251, + 183, + 107, + 149, + 41, + 200 + ], + "start_byte": 2147483648 + }, + "canonicalBytesHex": "7b226275666665725f6964223a5b32392c3135352c39362c33302c32312c3139322c3232362c3132332c3136392c36312c3232372c3134382c35322c33392c3131352c34352c3134352c3233392c39352c3138362c3131372c3232352c35362c3136382c3133392c3132312c3135342c32312c3130352c37352c32302c37355d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b37362c3137382c36302c3231392c3139312c3131392c3137372c3233372c32362c392c3131342c3134382c3132302c3233372c3234362c3230392c3231352c3234312c32332c31352c35342c3132382c3137352c3230332c34342c3138312c3235312c3138332c3130372c3134392c34312c3230305d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a367d" + }, + { + "fact": "Diff", + "typeLabel": "jedit.text.RopeDiff.v1", + "typeIdHex": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "identity": { + "kind": "content-addressed", + "domainHex": "6a656469742e746578742e646966662e763100", + "nodeIdHex": "80a0911f47cf558ce57904f412824537d4d53f294116849e62473f06279c2782" + }, + "value": { + "basis_head_id": [ + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51, + 51 + ], + "deleted_byte_length": 18446744073709551615, + "end_byte": 2147483652, + "inserted_byte_length": 6, + "next_head_id": [ + 76, + 178, + 60, + 219, + 191, + 119, + 177, + 237, + 26, + 9, + 114, + 148, + 120, + 237, + 246, + 209, + 215, + 241, + 23, + 15, + 54, + 128, + 175, + 203, + 44, + 181, + 251, + 183, + 107, + 149, + 41, + 200 + ], + "rewrite_id": [ + 10, + 54, + 243, + 51, + 230, + 161, + 144, + 129, + 241, + 55, + 55, + 22, + 211, + 34, + 135, + 206, + 82, + 142, + 126, + 117, + 250, + 76, + 125, + 211, + 181, + 111, + 244, + 77, + 159, + 15, + 90, + 68 + ], + "start_byte": 2147483648 + }, + "canonicalBytesHex": "7b22726577726974655f6964223a5b31302c35342c3234332c35312c3233302c3136312c3134342c3132392c3234312c35352c35352c32322c3231312c33342c3133352c3230362c38322c3134322c3132362c3131372c3235302c37362c3132352c3231312c3138312c3131312c3234342c37372c3135392c31352c39302c36385d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b37362c3137382c36302c3231392c3139312c3131392c3137372c3233372c32362c392c3131342c3134382c3132302c3233372c3234362c3230392c3231352c3234312c32332c31352c35342c3132382c3137352c3230332c34342c3138312c3235312c3138332c3130372c3134392c34312c3230305d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a362c2264656c657465645f627974655f6c656e677468223a31383434363734343037333730393535313631357d" + } + ] +} diff --git a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 new file mode 100644 index 00000000..b7195c18 --- /dev/null +++ b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 @@ -0,0 +1 @@ +643ceb95bcb4d480a750f27df96c00bff7cb2861dd01744eaafbec228af2f141 diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json new file mode 100644 index 00000000..b4b73663 --- /dev/null +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json @@ -0,0 +1,324 @@ +{ + "coordinate": "jedit.text.schema@1", + "excludedPropositions": [ + "jedit.text.RopeStructuralMaintenance", + "jedit.text.RopeCheckpoint", + "jedit.text.RopeCheckpointAnchored", + "receipt-attribution-fields", + "structural-echo-edges", + "typed-diff-spans" + ], + "factCodec": { + "byteOrderMark": "forbidden", + "byteString": "array-u8", + "encoding": "utf-8", + "forbiddenValues": [ + "map-valued-fields", + "floats", + "signed-integers", + "duplicate-members", + "unknown-members" + ], + "identifier": "array-32-u8", + "insignificantWhitespace": "forbidden", + "objectMemberOrder": "declared-field-order", + "optionalIdentifier": "null-or-array-32-u8", + "profile": "jedit.compact-serde-json.v1", + "stringEscaping": "serde-json-v1", + "unsignedInteger": "decimal-no-leading-zeroes" + }, + "facts": [ + { + "fields": [ + { + "name": "buffer_key", + "type": "utf8-string" + }, + { + "name": "projection_path", + "type": "optional-utf8-string" + }, + { + "name": "canonical_head_id", + "type": "node-id-32" + }, + { + "name": "version", + "type": "u64" + } + ], + "identity": { + "domainHex": "6a656469742e746578742e6275666665722d6b65792e763100", + "kind": "keyed", + "material": "buffer_key utf8" + }, + "name": "Buffer", + "typeLabel": "jedit.text.BufferWorldline.v1" + }, + { + "fields": [ + { + "name": "content_hash", + "type": "digest-32" + }, + { + "name": "bytes", + "type": "bytes" + } + ], + "identity": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e626c6f622e763100", + "kind": "content-addressed", + "material": "domain || canonical-fact-bytes" + }, + "name": "Blob", + "typeLabel": "jedit.text.TextBlob.v1" + }, + { + "fields": [ + { + "name": "blob_id", + "type": "node-id-32" + }, + { + "name": "byte_start", + "type": "u64" + }, + { + "name": "byte_length", + "type": "u64" + }, + { + "name": "utf16_length", + "type": "u64" + }, + { + "name": "line_breaks", + "type": "u64" + } + ], + "identity": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e6c6561662e763100", + "kind": "content-addressed", + "material": "domain || canonical-fact-bytes" + }, + "name": "Leaf", + "typeLabel": "jedit.text.RopeLeaf.v1" + }, + { + "fields": [ + { + "name": "left", + "type": "node-id-32" + }, + { + "name": "right", + "type": "node-id-32" + }, + { + "name": "byte_length", + "type": "u64" + }, + { + "name": "utf16_length", + "type": "u64" + }, + { + "name": "line_breaks", + "type": "u64" + }, + { + "name": "height", + "type": "u32" + } + ], + "identity": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e6272616e63682e763100", + "kind": "content-addressed", + "material": "domain || canonical-fact-bytes" + }, + "name": "Branch", + "typeLabel": "jedit.text.RopeBranch.v1" + }, + { + "fields": [ + { + "name": "buffer_id", + "type": "node-id-32" + }, + { + "name": "basis_head_id", + "type": "optional-node-id-32" + }, + { + "name": "root_node_id", + "type": "optional-node-id-32" + }, + { + "name": "byte_length", + "type": "u64" + }, + { + "name": "utf16_length", + "type": "u64" + }, + { + "name": "line_count", + "type": "u64" + }, + { + "name": "root_digest", + "type": "digest-32" + }, + { + "name": "sequence", + "type": "u64" + } + ], + "identity": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e686561642e763100", + "kind": "content-addressed", + "material": "domain || canonical-fact-bytes" + }, + "name": "Head", + "typeLabel": "jedit.text.RopeHead.v1" + }, + { + "fields": [ + { + "name": "buffer_id", + "type": "node-id-32" + }, + { + "name": "basis_head_id", + "type": "node-id-32" + }, + { + "name": "next_head_id", + "type": "node-id-32" + }, + { + "name": "start_byte", + "type": "u64" + }, + { + "name": "end_byte", + "type": "u64" + }, + { + "name": "inserted_byte_length", + "type": "u64" + } + ], + "identity": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e726577726974652e763100", + "kind": "content-addressed", + "material": "domain || canonical-fact-bytes" + }, + "name": "Rewrite", + "typeLabel": "jedit.text.RopeRewrite.v1" + }, + { + "fields": [ + { + "name": "rewrite_id", + "type": "node-id-32" + }, + { + "name": "basis_head_id", + "type": "node-id-32" + }, + { + "name": "next_head_id", + "type": "node-id-32" + }, + { + "name": "start_byte", + "type": "u64" + }, + { + "name": "end_byte", + "type": "u64" + }, + { + "name": "inserted_byte_length", + "type": "u64" + }, + { + "name": "deleted_byte_length", + "type": "u64" + } + ], + "identity": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e646966662e763100", + "kind": "content-addressed", + "material": "domain || canonical-fact-bytes" + }, + "name": "Diff", + "typeLabel": "jedit.text.RopeDiff.v1" + } + ], + "graphEncoding": { + "attachment": { + "atomType": "node-type", + "key": "node-alpha", + "payload": "canonical-fact-bytes", + "value": "atom" + }, + "edges": "none", + "node": "typed-node" + }, + "identityLaws": { + "blobContent": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e626c6f622d636f6e74656e742e763100", + "material": "blob-bytes" + }, + "bufferNode": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e6275666665722d6b65792e763100", + "material": "buffer-key-utf8" + }, + "contentFact": { + "algorithm": "blake3-256", + "material": "fact-domain || canonical-fact-bytes" + }, + "emptyRootDigest": { + "algorithm": "blake3-256", + "domainHex": "6a656469742e746578742e656d7074792d726f6f742e763100", + "material": "empty-bytes" + }, + "typeId": { + "algorithm": "blake3-256", + "domainHex": "747970653a", + "material": "type-label-utf8" + } + }, + "migrationPosture": { + "codecOrStructuralChange": "new-schema-coordinate-and-admitted-migration", + "nativeJsonV1": "authoritative", + "typescriptGraphRope": "not-an-alternate-encoding" + }, + "ropeLaw": { + "coordinateType": "u64", + "emittedOperations": [ + "upsert-node", + "set-node-alpha" + ], + "leafChunkBoundary": "largest-utf8-boundary-at-or-before-limit", + "leafSlices": "may-share-blob-with-nonzero-byte-start", + "lineCount": "line-breaks-plus-one-including-empty", + "maxLeafBytes": 4096, + "nonemptyRootDigest": "root-node-id", + "persistence": "path-copy-no-delete", + "range": "half-open-utf8-byte", + "replacement": "utf8-bytes" + }, + "schemaVersion": 1 +} diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 new file mode 100644 index 00000000..4aefa6a4 --- /dev/null +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 @@ -0,0 +1 @@ +e6ced0c88935337460d5685418cb387789a1d4cd174452499f3a474ad51eef70 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index d8ba4c71..2f7a4d83 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -174,6 +174,13 @@ Canonical fact JSON is: - JSON strings encoded with the compact serde-json version-1 escaping profile; - no maps, floats, signed integers, duplicate members, or unknown members. +This is a writer and identity law. The legacy `serde_json` reader accepts some +noncanonical spellings and does not currently recompute every content node +identifier from attachment bytes. That permissiveness is not promoted into +the version-1 contract. Future package validation and Echo evaluation must +reject noncanonical or misaddressed facts; this cycle does not falsely claim +the compatibility reader already performs those checks. + Content-addressed identifiers are BLAKE3-256 over the exact declared domain bytes followed by canonical fact bytes. Buffer identifiers, Blob content hashes, and empty-root digests use their separately declared BLAKE3 domains. @@ -274,7 +281,7 @@ evolution can proceed later under a new coordinate and explicit migration. ## Implementation Slices - [x] Slice 1: land this decision record and freeze the executable witnesses. -- [ ] Slice 2: publish and byte-lock `jedit.text.schema@1`. +- [x] Slice 2: publish and byte-lock `jedit.text.schema@1`. - [ ] Slice 3: publish and regenerate the `ReplaceRange` differential corpus. ## Tests To Write First diff --git a/native/jedit-echo-host/Cargo.lock b/native/jedit-echo-host/Cargo.lock index 004fae27..cc731a24 100644 --- a/native/jedit-echo-host/Cargo.lock +++ b/native/jedit-echo-host/Cargo.lock @@ -43,7 +43,16 @@ dependencies = [ "cc", "cfg-if", "constant_time_eq", - "cpufeatures", + "cpufeatures 0.3.0", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", ] [[package]] @@ -121,6 +130,15 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + [[package]] name = "cpufeatures" version = "0.3.0" @@ -136,6 +154,26 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "echo-registry-api" version = "0.1.0" @@ -182,6 +220,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.4.3" @@ -228,6 +276,8 @@ dependencies = [ "hex", "serde", "serde_json", + "sha2", + "static_assertions", "tempfile", "thiserror 2.0.18", "warp-core", @@ -377,12 +427,29 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + [[package]] name = "shlex" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "2.0.119" @@ -447,12 +514,24 @@ dependencies = [ "syn", ] +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "warp-core" version = "0.1.1" diff --git a/native/jedit-echo-host/Cargo.toml b/native/jedit-echo-host/Cargo.toml index ec617e4a..cff590b7 100644 --- a/native/jedit-echo-host/Cargo.toml +++ b/native/jedit-echo-host/Cargo.toml @@ -19,4 +19,6 @@ thiserror = "2" warp-core = { git = "https://github.com/flyingrobots/echo", rev = "90f63ae27c673fb7d398aba12c98cfbcf1ef24c3", features = ["native_rule_bootstrap", "trusted_runtime"] } [dev-dependencies] +sha2 = "0.10" +static_assertions = "1" tempfile = "3" diff --git a/native/jedit-echo-host/src/records.rs b/native/jedit-echo-host/src/records.rs index dab30b38..63c00722 100644 --- a/native/jedit-echo-host/src/records.rs +++ b/native/jedit-echo-host/src/records.rs @@ -13,7 +13,6 @@ pub const REWRITE_FACT_TYPE: &str = "jedit.text.RopeRewrite.v1"; pub const DIFF_FACT_TYPE: &str = "jedit.text.RopeDiff.v1"; pub const CHECKPOINT_FACT_TYPE: &str = "jedit.text.RopeCheckpoint.v1"; -const BUFFER_ID_DOMAIN: &[u8] = b"jedit.text.buffer-worldline.v1\0"; const BLOB_ID_DOMAIN: &[u8] = b"jedit.text.blob.v1\0"; const LEAF_ID_DOMAIN: &[u8] = b"jedit.text.leaf.v1\0"; const BRANCH_ID_DOMAIN: &[u8] = b"jedit.text.branch.v1\0"; @@ -22,6 +21,10 @@ const REWRITE_ID_DOMAIN: &[u8] = b"jedit.text.rewrite.v1\0"; const DIFF_ID_DOMAIN: &[u8] = b"jedit.text.diff.v1\0"; const CHECKPOINT_ID_DOMAIN: &[u8] = b"jedit.text.checkpoint.v1\0"; +pub const BUFFER_NODE_ID_DOMAIN: &[u8] = b"jedit.text.buffer-key.v1\0"; +pub const BLOB_CONTENT_HASH_DOMAIN: &[u8] = b"jedit.text.blob-content.v1\0"; +pub const EMPTY_ROOT_DIGEST_DOMAIN: &[u8] = b"jedit.text.empty-root.v1\0"; + #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)] pub struct BufferFact { pub buffer_key: String, @@ -122,26 +125,44 @@ impl From for NodeId { pub trait TypedFact: DeserializeOwned + Serialize { const TYPE_LABEL: &'static str; +} + +pub trait ContentAddressedFact: TypedFact { const ID_DOMAIN: &'static [u8]; } macro_rules! typed_fact { - ($fact:ty, $label:expr, $domain:expr) => { + ($fact:ty, $label:expr) => { impl TypedFact for $fact { const TYPE_LABEL: &'static str = $label; + } + }; +} + +macro_rules! content_addressed_fact { + ($fact:ty, $domain:expr) => { + impl ContentAddressedFact for $fact { const ID_DOMAIN: &'static [u8] = $domain; } }; } -typed_fact!(BufferFact, BUFFER_FACT_TYPE, BUFFER_ID_DOMAIN); -typed_fact!(BlobFact, BLOB_FACT_TYPE, BLOB_ID_DOMAIN); -typed_fact!(LeafFact, LEAF_FACT_TYPE, LEAF_ID_DOMAIN); -typed_fact!(BranchFact, BRANCH_FACT_TYPE, BRANCH_ID_DOMAIN); -typed_fact!(HeadFact, HEAD_FACT_TYPE, HEAD_ID_DOMAIN); -typed_fact!(RewriteFact, REWRITE_FACT_TYPE, REWRITE_ID_DOMAIN); -typed_fact!(DiffFact, DIFF_FACT_TYPE, DIFF_ID_DOMAIN); -typed_fact!(CheckpointFact, CHECKPOINT_FACT_TYPE, CHECKPOINT_ID_DOMAIN); +typed_fact!(BufferFact, BUFFER_FACT_TYPE); +typed_fact!(BlobFact, BLOB_FACT_TYPE); +typed_fact!(LeafFact, LEAF_FACT_TYPE); +typed_fact!(BranchFact, BRANCH_FACT_TYPE); +typed_fact!(HeadFact, HEAD_FACT_TYPE); +typed_fact!(RewriteFact, REWRITE_FACT_TYPE); +typed_fact!(DiffFact, DIFF_FACT_TYPE); +typed_fact!(CheckpointFact, CHECKPOINT_FACT_TYPE); + +content_addressed_fact!(BlobFact, BLOB_ID_DOMAIN); +content_addressed_fact!(LeafFact, LEAF_ID_DOMAIN); +content_addressed_fact!(BranchFact, BRANCH_ID_DOMAIN); +content_addressed_fact!(HeadFact, HEAD_ID_DOMAIN); +content_addressed_fact!(RewriteFact, REWRITE_ID_DOMAIN); +content_addressed_fact!(DiffFact, DIFF_ID_DOMAIN); +content_addressed_fact!(CheckpointFact, CHECKPOINT_ID_DOMAIN); pub fn fact_type_id() -> TypeId { make_type_id(T::TYPE_LABEL) @@ -152,7 +173,7 @@ pub fn fact_bytes(fact: &T) -> HostResult> { .map_err(|error| HostError::MalformedFact(format!("encode {}: {error}", T::TYPE_LABEL))) } -pub fn fact_id(fact: &T) -> HostResult { +pub fn fact_id(fact: &T) -> HostResult { Ok(content_node_id(T::ID_DOMAIN, &fact_bytes(fact)?)) } diff --git a/native/jedit-echo-host/src/rope.rs b/native/jedit-echo-host/src/rope.rs index 968cd499..e09f809e 100644 --- a/native/jedit-echo-host/src/rope.rs +++ b/native/jedit-echo-host/src/rope.rs @@ -13,14 +13,14 @@ use crate::error::{HostError, HostResult}; use crate::identity::{hash_bytes, node_id_hex}; use crate::records::{ decode_fact, fact_bytes, fact_id, fact_type_id, BlobFact, BranchFact, BufferFact, - CheckpointFact, CheckpointReason, DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, - TypedFact, + CheckpointFact, CheckpointReason, ContentAddressedFact, DiffFact, HeadFact, LeafFact, + NodeIdBytes, RewriteFact, TypedFact, BLOB_CONTENT_HASH_DOMAIN, BUFFER_NODE_ID_DOMAIN, }; use tree::{build_text, join, root_digest, root_metrics, split}; use window::read_range_bytes; pub use window::{read_window, WindowLine, WindowProjection, WindowSupport}; -const BUFFER_NODE_DOMAIN: &[u8] = b"jedit.text.buffer-key.v1\0"; +pub const MAX_LEAF_BYTES: usize = 4096; pub trait GraphFacts { fn warp_id(&self) -> WarpId; @@ -121,7 +121,7 @@ impl<'a, T: GraphFacts> PlanContext<'a, T> { Ok(()) } - fn write_content_fact(&mut self, fact: &F) -> HostResult { + fn write_content_fact(&mut self, fact: &F) -> HostResult { let id = fact_id(fact)?; self.write_fact_at(id, fact)?; Ok(id) @@ -165,7 +165,7 @@ impl<'a, T: GraphFacts> PlanContext<'a, T> { fn verified_blob(&mut self, id: NodeId) -> HostResult { let blob: BlobFact = self.read_fact(id)?; - if hash_bytes(b"jedit.text.blob-content.v1\0", &blob.bytes) != blob.content_hash { + if hash_bytes(BLOB_CONTENT_HASH_DOMAIN, &blob.bytes) != blob.content_hash { return Err(HostError::MalformedFact(format!( "blob {} content hash does not match", node_id_hex(id) @@ -256,7 +256,7 @@ impl CheckpointPlan { } pub fn buffer_node_id(buffer_key: &str) -> NodeId { - crate::identity::content_node_id(BUFFER_NODE_DOMAIN, buffer_key.as_bytes()) + crate::identity::content_node_id(BUFFER_NODE_ID_DOMAIN, buffer_key.as_bytes()) } pub fn existing_buffer( diff --git a/native/jedit-echo-host/src/rope/tree.rs b/native/jedit-echo-host/src/rope/tree.rs index 103a261f..6ab8c454 100644 --- a/native/jedit-echo-host/src/rope/tree.rs +++ b/native/jedit-echo-host/src/rope/tree.rs @@ -2,13 +2,11 @@ use warp_core::NodeId; use crate::error::{HostError, HostResult}; use crate::identity::{hash_bytes, node_id_hex}; -use crate::records::{BlobFact, BranchFact, LeafFact}; - -use super::{GraphFacts, NodeMetrics, PlanContext, RopeNode}; - -const MAX_LEAF_BYTES: usize = 4096; -const EMPTY_ROOT_DIGEST_DOMAIN: &[u8] = b"jedit.text.empty-root.v1\0"; +use crate::records::{ + BlobFact, BranchFact, LeafFact, BLOB_CONTENT_HASH_DOMAIN, EMPTY_ROOT_DIGEST_DOMAIN, +}; +use super::{GraphFacts, NodeMetrics, PlanContext, RopeNode, MAX_LEAF_BYTES}; pub(super) fn build_text( context: &mut PlanContext<'_, T>, bytes: &[u8], @@ -52,7 +50,7 @@ fn make_blob_leaf( let utf16_length = text.encode_utf16().count() as u64; let line_breaks = bytes.iter().filter(|byte| **byte == b'\n').count() as u64; let blob = BlobFact { - content_hash: hash_bytes(b"jedit.text.blob-content.v1\0", &bytes), + content_hash: hash_bytes(BLOB_CONTENT_HASH_DOMAIN, &bytes), bytes, }; let byte_length = blob.bytes.len() as u64; diff --git a/native/jedit-echo-host/tests/replace_range_schema.rs b/native/jedit-echo-host/tests/replace_range_schema.rs new file mode 100644 index 00000000..86add135 --- /dev/null +++ b/native/jedit-echo-host/tests/replace_range_schema.rs @@ -0,0 +1,487 @@ +use std::fmt::Debug; + +use jedit_echo_host::identity::hash_bytes; +use jedit_echo_host::records::{ + fact_bytes, fact_id, fact_type_id, BlobFact, BranchFact, BufferFact, ContentAddressedFact, + DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, TypedFact, BLOB_CONTENT_HASH_DOMAIN, + BUFFER_NODE_ID_DOMAIN, EMPTY_ROOT_DIGEST_DOMAIN, +}; +use jedit_echo_host::rope::{buffer_node_id, MAX_LEAF_BYTES}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::{json, Value}; +use sha2::{Digest, Sha256}; +use static_assertions::{assert_impl_all, assert_not_impl_any}; + +const SCHEMA_BYTES: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json" +)); +const SCHEMA_SHA256: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256" +)); +const CODEC_VECTOR_BYTES: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json" +)); +const CODEC_VECTOR_SHA256: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256" +)); + +const SCHEMA_COORDINATE: &str = "jedit.text.schema@1"; + +assert_not_impl_any!(BufferFact: ContentAddressedFact); +assert_impl_all!(BlobFact: ContentAddressedFact); +assert_impl_all!(LeafFact: ContentAddressedFact); +assert_impl_all!(BranchFact: ContentAddressedFact); +assert_impl_all!(HeadFact: ContentAddressedFact); +assert_impl_all!(RewriteFact: ContentAddressedFact); +assert_impl_all!(DiffFact: ContentAddressedFact); + +#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct CodecResource { + format_version: u32, + schema_coordinate: String, + vectors: Vec, +} + +#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct CodecVector { + fact: String, + type_label: String, + type_id_hex: String, + identity: IdentityVector, + value: Value, + canonical_bytes_hex: String, +} + +#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde( + rename_all = "camelCase", + rename_all_fields = "camelCase", + tag = "kind", + deny_unknown_fields +)] +enum IdentityVector { + #[serde(rename = "keyed")] + Keyed { + buffer_key: String, + node_id_hex: String, + }, + #[serde(rename = "content-addressed")] + ContentAddressed { + domain_hex: String, + node_id_hex: String, + }, +} + +#[test] +fn published_schema_and_codec_vectors_regenerate_byte_for_byte() { + assert_eq!(SCHEMA_BYTES, pretty_json_bytes(&expected_schema())); + assert_eq!( + CODEC_VECTOR_BYTES, + pretty_json_bytes(&expected_codec_resource()) + ); + assert_eq!(sha256_hex(SCHEMA_BYTES), checked_digest(SCHEMA_SHA256)); + assert_eq!( + sha256_hex(CODEC_VECTOR_BYTES), + checked_digest(CODEC_VECTOR_SHA256) + ); +} + +#[test] +fn schema_declares_the_native_fact_and_attachment_law() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema JSON should decode"); + assert_eq!(schema, expected_schema()); +} + +#[test] +fn codec_vectors_match_native_bytes_types_and_identity_domains() { + let resource: CodecResource = + serde_json::from_slice(CODEC_VECTOR_BYTES).expect("codec vectors should decode"); + assert_eq!(resource, expected_codec_resource()); + assert_eq!(resource.schema_coordinate, SCHEMA_COORDINATE); + let [buffer, blob, leaf, branch, head, rewrite, diff] = resource.vectors.as_slice() else { + panic!("codec resource should contain exactly seven ordered fact vectors"); + }; + + check_keyed::(buffer, "Buffer"); + check_content::(blob, "Blob"); + check_content::(leaf, "Leaf"); + check_content::(branch, "Branch"); + check_content::(head, "Head"); + check_content::(rewrite, "Rewrite"); + check_content::(diff, "Diff"); +} + +fn expected_schema() -> Value { + json!({ + "schemaVersion": 1, + "coordinate": SCHEMA_COORDINATE, + "factCodec": { + "profile": "jedit.compact-serde-json.v1", + "encoding": "utf-8", + "byteOrderMark": "forbidden", + "insignificantWhitespace": "forbidden", + "objectMemberOrder": "declared-field-order", + "unsignedInteger": "decimal-no-leading-zeroes", + "optionalIdentifier": "null-or-array-32-u8", + "identifier": "array-32-u8", + "byteString": "array-u8", + "stringEscaping": "serde-json-v1", + "forbiddenValues": [ + "map-valued-fields", + "floats", + "signed-integers", + "duplicate-members", + "unknown-members" + ] + }, + "graphEncoding": { + "node": "typed-node", + "attachment": { + "key": "node-alpha", + "value": "atom", + "atomType": "node-type", + "payload": "canonical-fact-bytes" + }, + "edges": "none" + }, + "identityLaws": { + "typeId": { + "algorithm": "blake3-256", + "domainHex": hex::encode(b"type:"), + "material": "type-label-utf8" + }, + "bufferNode": { + "algorithm": "blake3-256", + "domainHex": hex::encode(BUFFER_NODE_ID_DOMAIN), + "material": "buffer-key-utf8" + }, + "contentFact": { + "algorithm": "blake3-256", + "material": "fact-domain || canonical-fact-bytes" + }, + "blobContent": { + "algorithm": "blake3-256", + "domainHex": hex::encode(BLOB_CONTENT_HASH_DOMAIN), + "material": "blob-bytes" + }, + "emptyRootDigest": { + "algorithm": "blake3-256", + "domainHex": hex::encode(EMPTY_ROOT_DIGEST_DOMAIN), + "material": "empty-bytes" + } + }, + "ropeLaw": { + "coordinateType": "u64", + "range": "half-open-utf8-byte", + "replacement": "utf8-bytes", + "maxLeafBytes": MAX_LEAF_BYTES, + "leafChunkBoundary": "largest-utf8-boundary-at-or-before-limit", + "leafSlices": "may-share-blob-with-nonzero-byte-start", + "lineCount": "line-breaks-plus-one-including-empty", + "nonemptyRootDigest": "root-node-id", + "persistence": "path-copy-no-delete", + "emittedOperations": ["upsert-node", "set-node-alpha"] + }, + "facts": [ + fact_declaration::( + "Buffer", + json!({ + "kind": "keyed", + "domainHex": hex::encode(BUFFER_NODE_ID_DOMAIN), + "material": "buffer_key utf8" + }), + &[ + ("buffer_key", "utf8-string"), + ("projection_path", "optional-utf8-string"), + ("canonical_head_id", "node-id-32"), + ("version", "u64") + ] + ), + content_fact_declaration::( + "Blob", + &[("content_hash", "digest-32"), ("bytes", "bytes")] + ), + content_fact_declaration::( + "Leaf", + &[ + ("blob_id", "node-id-32"), + ("byte_start", "u64"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_breaks", "u64") + ] + ), + content_fact_declaration::( + "Branch", + &[ + ("left", "node-id-32"), + ("right", "node-id-32"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_breaks", "u64"), + ("height", "u32") + ] + ), + content_fact_declaration::( + "Head", + &[ + ("buffer_id", "node-id-32"), + ("basis_head_id", "optional-node-id-32"), + ("root_node_id", "optional-node-id-32"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_count", "u64"), + ("root_digest", "digest-32"), + ("sequence", "u64") + ] + ), + content_fact_declaration::( + "Rewrite", + &[ + ("buffer_id", "node-id-32"), + ("basis_head_id", "node-id-32"), + ("next_head_id", "node-id-32"), + ("start_byte", "u64"), + ("end_byte", "u64"), + ("inserted_byte_length", "u64") + ] + ), + content_fact_declaration::( + "Diff", + &[ + ("rewrite_id", "node-id-32"), + ("basis_head_id", "node-id-32"), + ("next_head_id", "node-id-32"), + ("start_byte", "u64"), + ("end_byte", "u64"), + ("inserted_byte_length", "u64"), + ("deleted_byte_length", "u64") + ] + ) + ], + "excludedPropositions": [ + "jedit.text.RopeStructuralMaintenance", + "jedit.text.RopeCheckpoint", + "jedit.text.RopeCheckpointAnchored", + "receipt-attribution-fields", + "structural-echo-edges", + "typed-diff-spans" + ], + "migrationPosture": { + "nativeJsonV1": "authoritative", + "typescriptGraphRope": "not-an-alternate-encoding", + "codecOrStructuralChange": "new-schema-coordinate-and-admitted-migration" + } + }) +} + +fn fact_declaration(name: &str, identity: Value, fields: &[(&str, &str)]) -> Value { + json!({ + "name": name, + "typeLabel": T::TYPE_LABEL, + "identity": identity, + "fields": fields + .iter() + .map(|(field, kind)| json!({"name": field, "type": kind})) + .collect::>() + }) +} + +fn content_fact_declaration(name: &str, fields: &[(&str, &str)]) -> Value { + fact_declaration::( + name, + json!({ + "kind": "content-addressed", + "algorithm": "blake3-256", + "domainHex": hex::encode(T::ID_DOMAIN), + "material": "domain || canonical-fact-bytes" + }), + fields, + ) +} + +fn expected_codec_resource() -> CodecResource { + let buffer_key = "schema\n\"猫\\path.txt"; + let buffer_id = buffer_node_id(buffer_key); + let blob_bytes = "A😀\n".as_bytes().to_vec(); + let blob = BlobFact { + content_hash: hash_bytes(BLOB_CONTENT_HASH_DOMAIN, &blob_bytes), + bytes: blob_bytes, + }; + let blob_id = fact_id(&blob).unwrap(); + let leaf = LeafFact { + blob_id: blob_id.into(), + byte_start: 0, + byte_length: 6, + utf16_length: 4, + line_breaks: 1, + }; + let leaf_id = fact_id(&leaf).unwrap(); + let branch = BranchFact { + left: leaf_id.into(), + right: NodeIdBytes([0x22; 32]), + byte_length: 12, + utf16_length: 8, + line_breaks: 2, + height: 2, + }; + let head = HeadFact { + buffer_id: buffer_id.into(), + basis_head_id: Some(NodeIdBytes([0x33; 32])), + root_node_id: Some(leaf_id.into()), + byte_length: 6, + utf16_length: 4, + line_count: 2, + root_digest: *leaf_id.as_bytes(), + sequence: i32::MAX as u64 + 1, + }; + let head_id = fact_id(&head).unwrap(); + let buffer = BufferFact { + buffer_key: buffer_key.to_owned(), + projection_path: None, + canonical_head_id: head_id.into(), + version: i32::MAX as u64 + 1, + }; + let rewrite = RewriteFact { + buffer_id: buffer_id.into(), + basis_head_id: NodeIdBytes([0x33; 32]), + next_head_id: head_id.into(), + start_byte: i32::MAX as u64 + 1, + end_byte: i32::MAX as u64 + 5, + inserted_byte_length: 6, + }; + let rewrite_id = fact_id(&rewrite).unwrap(); + let diff = DiffFact { + rewrite_id: rewrite_id.into(), + basis_head_id: NodeIdBytes([0x33; 32]), + next_head_id: head_id.into(), + start_byte: i32::MAX as u64 + 1, + end_byte: i32::MAX as u64 + 5, + inserted_byte_length: 6, + deleted_byte_length: u64::MAX, + }; + + CodecResource { + format_version: 1, + schema_coordinate: SCHEMA_COORDINATE.to_owned(), + vectors: vec![ + keyed_vector("Buffer", buffer_key, &buffer), + content_vector("Blob", &blob), + content_vector("Leaf", &leaf), + content_vector("Branch", &branch), + content_vector("Head", &head), + content_vector("Rewrite", &rewrite), + content_vector("Diff", &diff), + ], + } +} + +fn keyed_vector(fact_name: &str, buffer_key: &str, fact: &T) -> CodecVector { + CodecVector { + fact: fact_name.to_owned(), + type_label: T::TYPE_LABEL.to_owned(), + type_id_hex: hex::encode(fact_type_id::().as_bytes()), + identity: IdentityVector::Keyed { + buffer_key: buffer_key.to_owned(), + node_id_hex: hex::encode(buffer_node_id(buffer_key).as_bytes()), + }, + value: serde_json::to_value(fact).unwrap(), + canonical_bytes_hex: hex::encode(fact_bytes(fact).unwrap()), + } +} + +fn content_vector(fact_name: &str, fact: &T) -> CodecVector { + CodecVector { + fact: fact_name.to_owned(), + type_label: T::TYPE_LABEL.to_owned(), + type_id_hex: hex::encode(fact_type_id::().as_bytes()), + identity: IdentityVector::ContentAddressed { + domain_hex: hex::encode(T::ID_DOMAIN), + node_id_hex: hex::encode(fact_id(fact).unwrap().as_bytes()), + }, + value: serde_json::to_value(fact).unwrap(), + canonical_bytes_hex: hex::encode(fact_bytes(fact).unwrap()), + } +} + +fn check_keyed(vector: &CodecVector, expected_name: &str) +where + T: TypedFact + DeserializeOwned + Debug, +{ + check_codec::(vector, expected_name); + let IdentityVector::Keyed { + buffer_key, + node_id_hex, + } = &vector.identity + else { + panic!("{expected_name} should have keyed identity"); + }; + assert_eq!( + hex::encode(buffer_node_id(buffer_key).as_bytes()), + *node_id_hex + ); +} + +fn check_content(vector: &CodecVector, expected_name: &str) +where + T: ContentAddressedFact + DeserializeOwned + Debug, +{ + let fact = check_codec::(vector, expected_name); + let IdentityVector::ContentAddressed { + domain_hex, + node_id_hex, + } = &vector.identity + else { + panic!("{expected_name} should have content-addressed identity"); + }; + assert_eq!(hex::encode(T::ID_DOMAIN), *domain_hex); + assert_eq!( + hex::encode(fact_id(&fact).unwrap().as_bytes()), + *node_id_hex + ); +} + +fn check_codec(vector: &CodecVector, expected_name: &str) -> T +where + T: TypedFact + DeserializeOwned + Debug, +{ + assert_eq!(vector.fact, expected_name); + assert_eq!(vector.type_label, T::TYPE_LABEL); + assert_eq!( + vector.type_id_hex, + hex::encode(fact_type_id::().as_bytes()) + ); + let fact: T = serde_json::from_value(vector.value.clone()).unwrap(); + assert_eq!( + hex::encode(fact_bytes(&fact).unwrap()), + vector.canonical_bytes_hex + ); + fact +} + +fn pretty_json_bytes(value: &T) -> Vec { + let mut bytes = serde_json::to_vec_pretty(value).unwrap(); + bytes.push(b'\n'); + bytes +} + +fn sha256_hex(bytes: &[u8]) -> String { + hex::encode(Sha256::digest(bytes)) +} + +fn checked_digest(value: &str) -> &str { + let digest = value + .strip_suffix('\n') + .expect("digest resource should end with one newline"); + assert_eq!(digest.len(), 64); + assert!(digest + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))); + digest +} From 3f712cd274dc92688fe73ece0f1dbafba5c0ba01 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 16:09:18 -0700 Subject: [PATCH 03/63] test: publish ReplaceRange oracle corpus --- .../replace-range-v1.oracle.json | 1825 +++++++++++++++++ .../replace-range-v1.oracle.sha256 | 1 + .../0158-replace-range-canonical-fact-law.md | 2 +- .../tests/replace_range_oracle.rs | 276 +++ .../tests/support/replace_range_basis.rs | 173 ++ .../tests/support/replace_range_oracle.rs | 420 ++++ 6 files changed, 2696 insertions(+), 1 deletion(-) create mode 100644 contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json create mode 100644 contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 create mode 100644 native/jedit-echo-host/tests/replace_range_oracle.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_basis.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_oracle.rs diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json new file mode 100644 index 00000000..75d9192f --- /dev/null +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -0,0 +1,1825 @@ +{ + "schemaVersion": 1, + "coordinate": "jedit.text.ReplaceRange.oracle@1", + "applicationSchemaCoordinate": "jedit.text.schema@1", + "sourceCommit": "c70e12d73b4b00bc92412bab67e1761f7dd22f82", + "evidenceGrade": "deterministic-self-validation", + "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", + "warpId": "50ac1cc17b2ccc05195d403cba33921618a03b9143e8e6e169bd57712fae0d2a", + "cases": [ + { + "id": "empty-unicode-insert", + "purpose": "finite persistent-rope ReplaceRange conformance witness", + "basisFacts": [ + { + "nodeId": "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22656d7074792d756e69636f64652d696e73657274222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3231342c38382c3133362c3138342c3137372c3134322c31382c35322c3234392c3137302c3131312c36382c3133372c39362c3134302c3131332c3130352c33372c3134392c3130322c34382c39392c35322c3233352c35362c33352c3131382c3137392c3233312c38312c38312c3234375d2c2276657273696f6e223a307d" + }, + { + "nodeId": "d65888b8b18e1234f9aa6f4489608c7169259566306334eb382376b3e75151f7", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b38382c31392c3132342c39382c3230302c3230382c3231312c38382c3130352c32302c3137332c3133372c3235322c36342c3133312c3135352c31382c36352c36302c372c3132302c3139392c3134322c3231332c3138312c3137372c392c34352c3234392c3135392c3136382c38385d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a6e756c6c2c22627974655f6c656e677468223a302c2275746631365f6c656e677468223a302c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3138332c3135362c38392c3139352c36382c34332c34352c39302c34302c3136302c3235312c3139392c3235352c31382c3131372c3231362c3130382c36362c34332c3232382c3134372c3131322c3131342c3231372c3139312c302c38372c34332c34392c31332c3233322c38385d2c2273657175656e6365223a307d" + } + ], + "invocation": { + "bufferId": "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "basisHeadId": "d65888b8b18e1234f9aa6f4489608c7169259566306334eb382376b3e75151f7", + "startByte": 0, + "endByte": 0, + "replacementUtf8Hex": "e78cab0a" + }, + "terminal": { + "posture": "committable", + "footprint": { + "nodeReads": [ + "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "d65888b8b18e1234f9aa6f4489608c7169259566306334eb382376b3e75151f7" + ], + "nodeWrites": [ + "1f4ce47e0ea86a8ed65fd1bf82834b32b0187d20023e361eb431cc3ffafabcee", + "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "7312130c9693715646ad46b641e768a0fbb6805b7fec173a33e955dee095fa17", + "7b5a5e4291fc41641320e437bfad5a56c49330c30b13a45c96c441a53ac63a13", + "9adc06013f853037d340431a5db6caec255bf04342657f7905994f6fcc100591", + "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b" + ], + "attachmentReads": [ + "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "d65888b8b18e1234f9aa6f4489608c7169259566306334eb382376b3e75151f7" + ], + "attachmentWrites": [ + "1f4ce47e0ea86a8ed65fd1bf82834b32b0187d20023e361eb431cc3ffafabcee", + "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "7312130c9693715646ad46b641e768a0fbb6805b7fec173a33e955dee095fa17", + "7b5a5e4291fc41641320e437bfad5a56c49330c30b13a45c96c441a53ac63a13", + "9adc06013f853037d340431a5db6caec255bf04342657f7905994f6fcc100591", + "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b" + ], + "edgeReads": [], + "edgeWrites": [] + }, + "patch": [ + { + "kind": "upsert-node", + "nodeId": "1f4ce47e0ea86a8ed65fd1bf82834b32b0187d20023e361eb431cc3ffafabcee", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9" + }, + { + "kind": "upsert-node", + "nodeId": "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e" + }, + { + "kind": "upsert-node", + "nodeId": "7312130c9693715646ad46b641e768a0fbb6805b7fec173a33e955dee095fa17", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a" + }, + { + "kind": "upsert-node", + "nodeId": "7b5a5e4291fc41641320e437bfad5a56c49330c30b13a45c96c441a53ac63a13", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd" + }, + { + "kind": "upsert-node", + "nodeId": "9adc06013f853037d340431a5db6caec255bf04342657f7905994f6fcc100591", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e" + }, + { + "kind": "upsert-node", + "nodeId": "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "set-node-alpha", + "nodeId": "1f4ce47e0ea86a8ed65fd1bf82834b32b0187d20023e361eb431cc3ffafabcee", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3134362c38352c31392c37392c38362c35382c37352c3132392c3138352c3134332c33382c3133342c38352c37312c3233362c3231372c35322c3136342c3233362c38382c3133352c37362c36312c372c3234382c36392c3138302c36332c3232372c31312c31352c32305d2c226279746573223a5b3233312c3134302c3137312c31305d7d" + }, + { + "kind": "set-node-alpha", + "nodeId": "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22656d7074792d756e69636f64652d696e73657274222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3135342c3232302c362c312c36332c3133332c34382c35352c3231312c36342c36372c32362c39332c3138322c3230322c3233362c33372c39312c3234302c36372c36362c3130312c3132372c3132312c352c3135332c37392c3131312c3230342c31362c352c3134355d2c2276657273696f6e223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "7312130c9693715646ad46b641e768a0fbb6805b7fec173a33e955dee095fa17", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b38382c31392c3132342c39382c3230302c3230382c3231312c38382c3130352c32302c3137332c3133372c3235322c36342c3133312c3135352c31382c36352c36302c372c3132302c3139392c3134322c3231332c3138312c3137372c392c34352c3234392c3135392c3136382c38385d2c2262617369735f686561645f6964223a5b3231342c38382c3133362c3138342c3137372c3134322c31382c35322c3234392c3137302c3131312c36382c3133372c39362c3134302c3131332c3130352c33372c3134392c3130322c34382c39392c35322c3233352c35362c33352c3131382c3137392c3233312c38312c38312c3234375d2c226e6578745f686561645f6964223a5b3135342c3232302c362c312c36332c3133332c34382c35352c3231312c36342c36372c32362c39332c3138322c3230322c3233362c33372c39312c3234302c36372c36362c3130312c3132372c3132312c352c3135332c37392c3131312c3230342c31362c352c3134355d2c2273746172745f62797465223a302c22656e645f62797465223a302c22696e7365727465645f627974655f6c656e677468223a347d" + }, + { + "kind": "set-node-alpha", + "nodeId": "7b5a5e4291fc41641320e437bfad5a56c49330c30b13a45c96c441a53ac63a13", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b3131352c31382c31392c31322c3135302c3134372c3131332c38362c37302c3137332c37302c3138322c36352c3233312c3130342c3136302c3235312c3138322c3132382c39312c3132372c3233362c32332c35382c35312c3233332c38352c3232322c3232342c3134392c3235302c32335d2c2262617369735f686561645f6964223a5b3231342c38382c3133362c3138342c3137372c3134322c31382c35322c3234392c3137302c3131312c36382c3133372c39362c3134302c3131332c3130352c33372c3134392c3130322c34382c39392c35322c3233352c35362c33352c3131382c3137392c3233312c38312c38312c3234375d2c226e6578745f686561645f6964223a5b3135342c3232302c362c312c36332c3133332c34382c35352c3231312c36342c36372c32362c39332c3138322c3230322c3233362c33372c39312c3234302c36372c36362c3130312c3132372c3132312c352c3135332c37392c3131312c3230342c31362c352c3134355d2c2273746172745f62797465223a302c22656e645f62797465223a302c22696e7365727465645f627974655f6c656e677468223a342c2264656c657465645f627974655f6c656e677468223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "9adc06013f853037d340431a5db6caec255bf04342657f7905994f6fcc100591", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b38382c31392c3132342c39382c3230302c3230382c3231312c38382c3130352c32302c3137332c3133372c3235322c36342c3133312c3135352c31382c36352c36302c372c3132302c3139392c3134322c3231332c3138312c3137372c392c34352c3234392c3135392c3136382c38385d2c2262617369735f686561645f6964223a5b3231342c38382c3133362c3138342c3137372c3134322c31382c35322c3234392c3137302c3131312c36382c3133372c39362c3134302c3131332c3130352c33372c3134392c3130322c34382c39392c35322c3233352c35362c33352c3131382c3137392c3233312c38312c38312c3234375d2c22726f6f745f6e6f64655f6964223a5b3233322c3232382c3131332c3137382c3136392c3133302c3134322c36332c32322c3231392c3132322c3130352c3235302c3235342c3230342c33322c3234322c37302c3233352c3235342c362c3234362c3135352c3232342c32372c3137312c3232382c3131302c3132332c3232352c3234342c37355d2c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a322c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b3233322c3232382c3131332c3137382c3136392c3133302c3134322c36332c32322c3231392c3132322c3130352c3235302c3235342c3230342c33322c3234322c37302c3233352c3235342c362c3234362c3135352c3232342c32372c3137312c3232382c3131302c3132332c3232352c3234342c37355d2c2273657175656e6365223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b33312c37362c3232382c3132362c31342c3136382c3130362c3134322c3231342c39352c3230392c3139312c3133302c3133312c37352c35302c3137362c32342c3132352c33322c322c36322c35342c33302c3138302c34392c3230342c36332c3235302c3235302c3138382c3233385d2c22627974655f7374617274223a302c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a317d" + } + ], + "createdNodeIds": [ + "1f4ce47e0ea86a8ed65fd1bf82834b32b0187d20023e361eb431cc3ffafabcee", + "7312130c9693715646ad46b641e768a0fbb6805b7fec173a33e955dee095fa17", + "7b5a5e4291fc41641320e437bfad5a56c49330c30b13a45c96c441a53ac63a13", + "9adc06013f853037d340431a5db6caec255bf04342657f7905994f6fcc100591", + "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b" + ], + "updatedNodeIds": [ + "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858" + ], + "untouchedBasisNodeIds": [ + "d65888b8b18e1234f9aa6f4489608c7169259566306334eb382376b3e75151f7" + ], + "result": { + "bufferId": "58137c62c8d0d3586914ad89fc40839b12413c0778c78ed5b5b1092df99fa858", + "headId": "9adc06013f853037d340431a5db6caec255bf04342657f7905994f6fcc100591", + "rootNodeId": "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b", + "rootDigest": "e8e471b2a9828e3f16db7a69fafecc20f246ebfe06f69be01babe46e7be1f44b", + "rewriteId": "7312130c9693715646ad46b641e768a0fbb6805b7fec173a33e955dee095fa17", + "diffId": "7b5a5e4291fc41641320e437bfad5a56c49330c30b13a45c96c441a53ac63a13", + "byteLength": 4, + "utf16Length": 2, + "lineCount": 2, + "sequence": 1, + "version": 1, + "materializedTextUtf8Hex": "e78cab0a" + } + } + }, + { + "id": "middle-ascii-insert", + "purpose": "finite persistent-rope ReplaceRange conformance witness", + "basisFacts": [ + { + "nodeId": "03f150de0814d97ad25a26ff414ef48d2ec0871ac939acef1cdad52df6380e7c", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b35342c3231322c3137392c37372c33382c3139392c3137382c322c3234342c3231372c38342c37382c39322c3130332c3137332c37392c3130392c3136382c3136352c35342c312c3130342c36382c3137392c31352c37372c3132392c3230372c35322c34312c35332c3234385d2c22627974655f7374617274223a302c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a342c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "36d4b34d26c7b202f4d9544e5c67ad4f6da8a536016844b30f4d81cf342935f8", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3138352c3134342c3233382c3134322c3135302c33342c3134302c3135312c37312c3233352c37312c3135352c37372c3234372c3131352c3136302c3230352c3133322c3136302c36362c38312c322c3231332c3136372c3133312c3139322c3130312c3132342c3136332c32352c3130332c35315d2c226279746573223a5b39372c39382c39392c3130305d7d" + }, + { + "nodeId": "431817db784a33420d1859566b85b23f484ded55725a38d532790ce79382f452", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3139392c38322c33362c3138332c3235322c3231362c37342c35352c3138382c3231352c3135332c36302c36312c34302c3139392c3137372c3231332c3134372c3132322c3230372c3235352c3131372c3234302c31332c3137302c32342c32392c3139372c3232352c32392c3234332c3233325d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b332c3234312c38302c3232322c382c32302c3231372c3132322c3231302c39302c33382c3235352c36352c37382c3234342c3134312c34362c3139322c3133352c32362c3230312c35372c3137322c3233392c32382c3231382c3231332c34352c3234362c35362c31342c3132345d2c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b332c3234312c38302c3232322c382c32302c3231372c3132322c3231302c39302c33382c3235352c36352c37382c3234342c3134312c34362c3139322c3133352c32362c3230312c35372c3137322c3233392c32382c3231382c3231332c34352c3234362c35362c31342c3132345d2c2273657175656e6365223a307d" + }, + { + "nodeId": "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226d6964646c652d61736369692d696e73657274222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b36372c32342c32332c3231392c3132302c37342c35312c36362c31332c32342c38392c38362c3130372c3133332c3137382c36332c37322c37372c3233372c38352c3131342c39302c35362c3231332c35302c3132312c31322c3233312c3134372c3133302c3234342c38325d2c2276657273696f6e223a307d" + } + ], + "invocation": { + "bufferId": "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "basisHeadId": "431817db784a33420d1859566b85b23f484ded55725a38d532790ce79382f452", + "startByte": 2, + "endByte": 2, + "replacementUtf8Hex": "5859" + }, + "terminal": { + "posture": "committable", + "footprint": { + "nodeReads": [ + "03f150de0814d97ad25a26ff414ef48d2ec0871ac939acef1cdad52df6380e7c", + "36d4b34d26c7b202f4d9544e5c67ad4f6da8a536016844b30f4d81cf342935f8", + "431817db784a33420d1859566b85b23f484ded55725a38d532790ce79382f452", + "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8" + ], + "nodeWrites": [ + "2b36e79a3d5b16a823e530c2428acf9f69a0aa06786ec6f7217533ae10cec21a", + "3bd4626bc71bff2931b94911f3987ef9ec5cbd2157a691b5cc77edf269c785ac", + "590054fca5e0ec5f79d5a474f3204579a77d4fbe0c64a00903cc372491e91978", + "6c269527a00ea013bc3cc84302f088612cb9b1c975ab7fa4b6e73811390780da", + "6f35b4d75864d0515e7de5397ee204ca3c5978943c80ce8facd7915eaddb08bf", + "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "ad1100adc61c927ca63848eb1764879cdd02abc1e192efe68ecc6dcf68703668", + "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "d6a24d09cebb1d5914a6d2275e23a7255f60d41a009a50ea7e79635a25dc8ab1", + "d903058e13d6f5af17984f4a8c206741c95e3c7514f0d78555ab604961cfd125" + ], + "attachmentReads": [ + "03f150de0814d97ad25a26ff414ef48d2ec0871ac939acef1cdad52df6380e7c", + "36d4b34d26c7b202f4d9544e5c67ad4f6da8a536016844b30f4d81cf342935f8", + "431817db784a33420d1859566b85b23f484ded55725a38d532790ce79382f452", + "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8" + ], + "attachmentWrites": [ + "2b36e79a3d5b16a823e530c2428acf9f69a0aa06786ec6f7217533ae10cec21a", + "3bd4626bc71bff2931b94911f3987ef9ec5cbd2157a691b5cc77edf269c785ac", + "590054fca5e0ec5f79d5a474f3204579a77d4fbe0c64a00903cc372491e91978", + "6c269527a00ea013bc3cc84302f088612cb9b1c975ab7fa4b6e73811390780da", + "6f35b4d75864d0515e7de5397ee204ca3c5978943c80ce8facd7915eaddb08bf", + "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "ad1100adc61c927ca63848eb1764879cdd02abc1e192efe68ecc6dcf68703668", + "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "d6a24d09cebb1d5914a6d2275e23a7255f60d41a009a50ea7e79635a25dc8ab1", + "d903058e13d6f5af17984f4a8c206741c95e3c7514f0d78555ab604961cfd125" + ], + "edgeReads": [], + "edgeWrites": [] + }, + "patch": [ + { + "kind": "upsert-node", + "nodeId": "2b36e79a3d5b16a823e530c2428acf9f69a0aa06786ec6f7217533ae10cec21a", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd" + }, + { + "kind": "upsert-node", + "nodeId": "3bd4626bc71bff2931b94911f3987ef9ec5cbd2157a691b5cc77edf269c785ac", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a" + }, + { + "kind": "upsert-node", + "nodeId": "590054fca5e0ec5f79d5a474f3204579a77d4fbe0c64a00903cc372491e91978", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "6c269527a00ea013bc3cc84302f088612cb9b1c975ab7fa4b6e73811390780da", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "6f35b4d75864d0515e7de5397ee204ca3c5978943c80ce8facd7915eaddb08bf", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e" + }, + { + "kind": "upsert-node", + "nodeId": "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "ad1100adc61c927ca63848eb1764879cdd02abc1e192efe68ecc6dcf68703668", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e" + }, + { + "kind": "upsert-node", + "nodeId": "d6a24d09cebb1d5914a6d2275e23a7255f60d41a009a50ea7e79635a25dc8ab1", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "d903058e13d6f5af17984f4a8c206741c95e3c7514f0d78555ab604961cfd125", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9" + }, + { + "kind": "set-node-alpha", + "nodeId": "2b36e79a3d5b16a823e530c2428acf9f69a0aa06786ec6f7217533ae10cec21a", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b35392c3231322c39382c3130372c3139392c32372c3235352c34312c34392c3138352c37332c31372c3234332c3135322c3132362c3234392c3233362c39322c3138392c33332c38372c3136362c3134352c3138312c3230342c3131392c3233372c3234322c3130352c3139392c3133332c3137325d2c2262617369735f686561645f6964223a5b36372c32342c32332c3231392c3132302c37342c35312c36362c31332c32342c38392c38362c3130372c3133332c3137382c36332c37322c37372c3233372c38352c3131342c39302c35362c3231332c35302c3132312c31322c3233312c3134372c3133302c3234342c38325d2c226e6578745f686561645f6964223a5b3131312c35332c3138302c3231352c38382c3130302c3230382c38312c39342c3132352c3232392c35372c3132362c3232362c342c3230322c36302c38392c3132302c3134382c36302c3132382c3230362c3134332c3137322c3231352c3134352c39342c3137332c3231392c382c3139315d2c2273746172745f62797465223a322c22656e645f62797465223a322c22696e7365727465645f627974655f6c656e677468223a322c2264656c657465645f627974655f6c656e677468223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "3bd4626bc71bff2931b94911f3987ef9ec5cbd2157a691b5cc77edf269c785ac", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b3139392c38322c33362c3138332c3235322c3231362c37342c35352c3138382c3231352c3135332c36302c36312c34302c3139392c3137372c3231332c3134372c3132322c3230372c3235352c3131372c3234302c31332c3137302c32342c32392c3139372c3232352c32392c3234332c3233325d2c2262617369735f686561645f6964223a5b36372c32342c32332c3231392c3132302c37342c35312c36362c31332c32342c38392c38362c3130372c3133332c3137382c36332c37322c37372c3233372c38352c3131342c39302c35362c3231332c35302c3132312c31322c3233312c3134372c3133302c3234342c38325d2c226e6578745f686561645f6964223a5b3131312c35332c3138302c3231352c38382c3130302c3230382c38312c39342c3132352c3232392c35372c3132362c3232362c342c3230322c36302c38392c3132302c3134382c36302c3132382c3230362c3134332c3137322c3231352c3134352c39342c3137332c3231392c382c3139315d2c2273746172745f62797465223a322c22656e645f62797465223a322c22696e7365727465645f627974655f6c656e677468223a327d" + }, + { + "kind": "set-node-alpha", + "nodeId": "590054fca5e0ec5f79d5a474f3204579a77d4fbe0c64a00903cc372491e91978", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3137332c31372c302c3137332c3139382c32382c3134362c3132342c3136362c35362c37322c3233352c32332c3130302c3133352c3135362c3232312c322c3137312c3139332c3232352c3134362c3233392c3233302c3134322c3230342c3130392c3230372c3130342c3131322c35342c3130345d2c227269676874223a5b3130382c33382c3134392c33392c3136302c31342c3136302c31392c3138382c36302c3230302c36372c322c3234302c3133362c39372c34342c3138352c3137372c3230312c3131372c3137312c3132372c3136342c3138322c3233312c35362c31372c35372c372c3132382c3231385d2c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a342c226c696e655f627265616b73223a302c22686569676874223a327d" + }, + { + "kind": "set-node-alpha", + "nodeId": "6c269527a00ea013bc3cc84302f088612cb9b1c975ab7fa4b6e73811390780da", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3231372c332c352c3134322c31392c3231342c3234352c3137352c32332c3135322c37392c37342c3134302c33322c3130332c36352c3230312c39342c36302c3131372c32302c3234302c3231352c3133332c38352c3137312c39362c37332c39372c3230372c3230392c33375d2c22627974655f7374617274223a302c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "6f35b4d75864d0515e7de5397ee204ca3c5978943c80ce8facd7915eaddb08bf", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3139392c38322c33362c3138332c3235322c3231362c37342c35352c3138382c3231352c3135332c36302c36312c34302c3139392c3137372c3231332c3134372c3132322c3230372c3235352c3131372c3234302c31332c3137302c32342c32392c3139372c3232352c32392c3234332c3233325d2c2262617369735f686561645f6964223a5b36372c32342c32332c3231392c3132302c37342c35312c36362c31332c32342c38392c38362c3130372c3133332c3137382c36332c37322c37372c3233372c38352c3131342c39302c35362c3231332c35302c3132312c31322c3233312c3134372c3133302c3234342c38325d2c22726f6f745f6e6f64655f6964223a5b3131312c3130322c3130352c3133312c3135302c3131332c3231362c35372c32302c33382c3137342c36372c37372c32352c3137342c3135322c32362c3232392c3137352c3133312c3139352c34302c3133382c38332c3135352c3131322c31322c3234382c3134322c31372c39332c33345d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a362c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3131312c3130322c3130352c3133312c3135302c3131332c3231362c35372c32302c33382c3137342c36372c37372c32352c3137342c3135322c32362c3232392c3137352c3133312c3139352c34302c3133382c38332c3135352c3131322c31322c3234382c3134322c31372c39332c33345d2c2273657175656e6365223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b38392c302c38342c3235322c3136352c3232342c3233362c39352c3132312c3231332c3136342c3131362c3234332c33322c36392c3132312c3136372c3132352c37392c3139302c31322c3130302c3136302c392c332c3230342c35352c33362c3134352c3233332c32352c3132305d2c227269676874223a5b3231342c3136322c37372c392c3230362c3138372c32392c38392c32302c3136362c3231302c33392c39342c33352c3136372c33372c39352c39362c3231322c32362c302c3135342c38302c3233342c3132362c3132312c39392c39302c33372c3232302c3133382c3137375d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a362c226c696e655f627265616b73223a302c22686569676874223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "ad1100adc61c927ca63848eb1764879cdd02abc1e192efe68ecc6dcf68703668", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b35342c3231322c3137392c37372c33382c3139392c3137382c322c3234342c3231372c38342c37382c39322c3130332c3137332c37392c3130392c3136382c3136352c35342c312c3130342c36382c3137392c31352c37372c3132392c3230372c35322c34312c35332c3234385d2c22627974655f7374617274223a302c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226d6964646c652d61736369692d696e73657274222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3131312c35332c3138302c3231352c38382c3130302c3230382c38312c39342c3132352c3232392c35372c3132362c3232362c342c3230322c36302c38392c3132302c3134382c36302c3132382c3230362c3134332c3137322c3231352c3134352c39342c3137332c3231392c382c3139315d2c2276657273696f6e223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "d6a24d09cebb1d5914a6d2275e23a7255f60d41a009a50ea7e79635a25dc8ab1", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b35342c3231322c3137392c37372c33382c3139392c3137382c322c3234342c3231372c38342c37382c39322c3130332c3137332c37392c3130392c3136382c3136352c35342c312c3130342c36382c3137392c31352c37372c3132392c3230372c35322c34312c35332c3234385d2c22627974655f7374617274223a322c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "d903058e13d6f5af17984f4a8c206741c95e3c7514f0d78555ab604961cfd125", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b35322c39372c38302c3134332c38372c33342c3230322c3234332c3233352c3137312c3134362c3138332c37332c3138372c39342c3133312c38312c3136302c3234362c32382c3139322c3131302c36312c38362c3234322c36322c32382c3233322c3130382c3233302c36382c3232395d2c226279746573223a5b38382c38395d7d" + } + ], + "createdNodeIds": [ + "2b36e79a3d5b16a823e530c2428acf9f69a0aa06786ec6f7217533ae10cec21a", + "3bd4626bc71bff2931b94911f3987ef9ec5cbd2157a691b5cc77edf269c785ac", + "590054fca5e0ec5f79d5a474f3204579a77d4fbe0c64a00903cc372491e91978", + "6c269527a00ea013bc3cc84302f088612cb9b1c975ab7fa4b6e73811390780da", + "6f35b4d75864d0515e7de5397ee204ca3c5978943c80ce8facd7915eaddb08bf", + "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "ad1100adc61c927ca63848eb1764879cdd02abc1e192efe68ecc6dcf68703668", + "d6a24d09cebb1d5914a6d2275e23a7255f60d41a009a50ea7e79635a25dc8ab1", + "d903058e13d6f5af17984f4a8c206741c95e3c7514f0d78555ab604961cfd125" + ], + "updatedNodeIds": [ + "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8" + ], + "untouchedBasisNodeIds": [ + "03f150de0814d97ad25a26ff414ef48d2ec0871ac939acef1cdad52df6380e7c", + "36d4b34d26c7b202f4d9544e5c67ad4f6da8a536016844b30f4d81cf342935f8", + "431817db784a33420d1859566b85b23f484ded55725a38d532790ce79382f452" + ], + "result": { + "bufferId": "c75224b7fcd84a37bcd7993c3d28c7b1d5937acfff75f00daa181dc5e11df3e8", + "headId": "6f35b4d75864d0515e7de5397ee204ca3c5978943c80ce8facd7915eaddb08bf", + "rootNodeId": "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "rootDigest": "6f6669839671d8391426ae434d19ae981ae5af83c3288a539b700cf88e115d22", + "rewriteId": "3bd4626bc71bff2931b94911f3987ef9ec5cbd2157a691b5cc77edf269c785ac", + "diffId": "2b36e79a3d5b16a823e530c2428acf9f69a0aa06786ec6f7217533ae10cec21a", + "byteLength": 6, + "utf16Length": 6, + "lineCount": 1, + "sequence": 1, + "version": 1, + "materializedTextUtf8Hex": "616258596364" + } + } + }, + { + "id": "whole-buffer-delete", + "purpose": "finite persistent-rope ReplaceRange conformance witness", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a2277686f6c652d6275666665722d64656c657465222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3134362c3132372c3135392c34332c3233362c33322c39332c342c32302c3136342c3230382c31362c3131382c38322c3234372c352c3232392c34322c3135332c3233362c3131382c3232342c38322c3132392c3233372c34332c3230392c34342c36342c33342c3136342c3132365d2c2276657273696f6e223a307d" + }, + { + "nodeId": "927f9f2bec205d0414a4d0107652f705e52a99ec76e05281ed2bd12c4022a47e", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3134302c332c3136322c3132312c3234302c3133322c3231312c31392c3234362c3233302c3134302c3137342c3231302c392c382c3230302c3233322c3132392c352c31342c3233332c32392c33312c3136342c3232352c33392c37312c33332c3234302c3138302c37342c3136365d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "basisHeadId": "927f9f2bec205d0414a4d0107652f705e52a99ec76e05281ed2bd12c4022a47e", + "startByte": 0, + "endByte": 3, + "replacementUtf8Hex": "" + }, + "terminal": { + "posture": "committable", + "footprint": { + "nodeReads": [ + "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "927f9f2bec205d0414a4d0107652f705e52a99ec76e05281ed2bd12c4022a47e", + "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc" + ], + "nodeWrites": [ + "1a6d115b1c8352ae4d46fcff7f40fac0f5c7a64471225a8b4cc60242bbcff533", + "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "a78efae08f57282b98760c965c4df321fd9daeafc1840e4529252aec85fcc884", + "c9c27a15901bff12f089a124d55adf246897ef1e6023f3c5a1895edac848d77b" + ], + "attachmentReads": [ + "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "927f9f2bec205d0414a4d0107652f705e52a99ec76e05281ed2bd12c4022a47e", + "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc" + ], + "attachmentWrites": [ + "1a6d115b1c8352ae4d46fcff7f40fac0f5c7a64471225a8b4cc60242bbcff533", + "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "a78efae08f57282b98760c965c4df321fd9daeafc1840e4529252aec85fcc884", + "c9c27a15901bff12f089a124d55adf246897ef1e6023f3c5a1895edac848d77b" + ], + "edgeReads": [], + "edgeWrites": [] + }, + "patch": [ + { + "kind": "upsert-node", + "nodeId": "1a6d115b1c8352ae4d46fcff7f40fac0f5c7a64471225a8b4cc60242bbcff533", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a" + }, + { + "kind": "upsert-node", + "nodeId": "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e" + }, + { + "kind": "upsert-node", + "nodeId": "a78efae08f57282b98760c965c4df321fd9daeafc1840e4529252aec85fcc884", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd" + }, + { + "kind": "upsert-node", + "nodeId": "c9c27a15901bff12f089a124d55adf246897ef1e6023f3c5a1895edac848d77b", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e" + }, + { + "kind": "set-node-alpha", + "nodeId": "1a6d115b1c8352ae4d46fcff7f40fac0f5c7a64471225a8b4cc60242bbcff533", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b3134302c332c3136322c3132312c3234302c3133322c3231312c31392c3234362c3233302c3134302c3137342c3231302c392c382c3230302c3233322c3132392c352c31342c3233332c32392c33312c3136342c3232352c33392c37312c33332c3234302c3138302c37342c3136365d2c2262617369735f686561645f6964223a5b3134362c3132372c3135392c34332c3233362c33322c39332c342c32302c3136342c3230382c31362c3131382c38322c3234372c352c3232392c34322c3135332c3233362c3131382c3232342c38322c3132392c3233372c34332c3230392c34342c36342c33342c3136342c3132365d2c226e6578745f686561645f6964223a5b3230312c3139342c3132322c32312c3134342c32372c3235352c31382c3234302c3133372c3136312c33362c3231332c39302c3232332c33362c3130342c3135312c3233392c33302c39362c33352c3234332c3139372c3136312c3133372c39342c3231382c3230302c37322c3231352c3132335d2c2273746172745f62797465223a302c22656e645f62797465223a332c22696e7365727465645f627974655f6c656e677468223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a2277686f6c652d6275666665722d64656c657465222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3230312c3139342c3132322c32312c3134342c32372c3235352c31382c3234302c3133372c3136312c33362c3231332c39302c3232332c33362c3130342c3135312c3233392c33302c39362c33352c3234332c3139372c3136312c3133372c39342c3231382c3230302c37322c3231352c3132335d2c2276657273696f6e223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "a78efae08f57282b98760c965c4df321fd9daeafc1840e4529252aec85fcc884", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b32362c3130392c31372c39312c32382c3133312c38322c3137342c37372c37302c3235322c3235352c3132372c36342c3235302c3139322c3234352c3139392c3136362c36382c3131332c33342c39302c3133392c37362c3139382c322c36362c3138372c3230372c3234352c35315d2c2262617369735f686561645f6964223a5b3134362c3132372c3135392c34332c3233362c33322c39332c342c32302c3136342c3230382c31362c3131382c38322c3234372c352c3232392c34322c3135332c3233362c3131382c3232342c38322c3132392c3233372c34332c3230392c34342c36342c33342c3136342c3132365d2c226e6578745f686561645f6964223a5b3230312c3139342c3132322c32312c3134342c32372c3235352c31382c3234302c3133372c3136312c33362c3231332c39302c3232332c33362c3130342c3135312c3233392c33302c39362c33352c3234332c3139372c3136312c3133372c39342c3231382c3230302c37322c3231352c3132335d2c2273746172745f62797465223a302c22656e645f62797465223a332c22696e7365727465645f627974655f6c656e677468223a302c2264656c657465645f627974655f6c656e677468223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "c9c27a15901bff12f089a124d55adf246897ef1e6023f3c5a1895edac848d77b", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3134302c332c3136322c3132312c3234302c3133322c3231312c31392c3234362c3233302c3134302c3137342c3231302c392c382c3230302c3233322c3132392c352c31342c3233332c32392c33312c3136342c3232352c33392c37312c33332c3234302c3138302c37342c3136365d2c2262617369735f686561645f6964223a5b3134362c3132372c3135392c34332c3233362c33322c39332c342c32302c3136342c3230382c31362c3131382c38322c3234372c352c3232392c34322c3135332c3233362c3131382c3232342c38322c3132392c3233372c34332c3230392c34342c36342c33342c3136342c3132365d2c22726f6f745f6e6f64655f6964223a6e756c6c2c22627974655f6c656e677468223a302c2275746631365f6c656e677468223a302c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3138332c3135362c38392c3139352c36382c34332c34352c39302c34302c3136302c3235312c3139392c3235352c31382c3131372c3231362c3130382c36362c34332c3232382c3134372c3131322c3131342c3231372c3139312c302c38372c34332c34392c31332c3233322c38385d2c2273657175656e6365223a317d" + } + ], + "createdNodeIds": [ + "1a6d115b1c8352ae4d46fcff7f40fac0f5c7a64471225a8b4cc60242bbcff533", + "a78efae08f57282b98760c965c4df321fd9daeafc1840e4529252aec85fcc884", + "c9c27a15901bff12f089a124d55adf246897ef1e6023f3c5a1895edac848d77b" + ], + "updatedNodeIds": [ + "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6" + ], + "untouchedBasisNodeIds": [ + "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "927f9f2bec205d0414a4d0107652f705e52a99ec76e05281ed2bd12c4022a47e", + "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc" + ], + "result": { + "bufferId": "8c03a279f084d313f6e68caed20908c8e881050ee91d1fa4e1274721f0b44aa6", + "headId": "c9c27a15901bff12f089a124d55adf246897ef1e6023f3c5a1895edac848d77b", + "rootNodeId": null, + "rootDigest": "b79c59c3442b2d5a28a0fbc7ff1275d86c422be4937072d9bf00572b310de858", + "rewriteId": "1a6d115b1c8352ae4d46fcff7f40fac0f5c7a64471225a8b4cc60242bbcff533", + "diffId": "a78efae08f57282b98760c965c4df321fd9daeafc1840e4529252aec85fcc884", + "byteLength": 0, + "utf16Length": 0, + "lineCount": 1, + "sequence": 1, + "version": 1, + "materializedTextUtf8Hex": "" + } + } + }, + { + "id": "unicode-replacement", + "purpose": "finite persistent-rope ReplaceRange conformance witness", + "basisFacts": [ + { + "nodeId": "4ca1d0af6f518e869ba7ef0f98524ab013ae63d3b4ace8ba7b396bb3d2cc75e7", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132382c34302c3138352c3232342c3133352c332c37342c3235332c39322c3134372c3134382c3138372c3231322c3130302c36382c3233372c3131302c3137312c38342c3233342c33352c31342c33362c3139332c3134312c3235342c38362c3233382c3130342c3232302c34352c315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3134302c33322c3130392c3139332c3232302c3138302c34322c3133322c3230362c3132312c39362c3133352c32392c3231312c38322c3130312c3139352c3135312c38322c35352c3231322c3235332c32382c38382c32372c32322c3231362c3139322c34362c3131392c39302c395d2c22627974655f6c656e677468223a392c2275746631365f6c656e677468223a362c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b3134302c33322c3130392c3139332c3232302c3138302c34322c3133322c3230362c3132312c39362c3133352c32392c3231312c38322c3130312c3139352c3135312c38322c35352c3231322c3235332c32382c38382c32372c32322c3231362c3139322c34362c3131392c39302c395d2c2273657175656e6365223a307d" + }, + { + "nodeId": "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22756e69636f64652d7265706c6163656d656e74222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b37362c3136312c3230382c3137352c3131312c38312c3134322c3133342c3135352c3136372c3233392c31352c3135322c38322c37342c3137362c31392c3137342c39392c3231312c3138302c3137322c3233322c3138362c3132332c35372c3130372c3137392c3231302c3230342c3131372c3233315d2c2276657273696f6e223a307d" + }, + { + "nodeId": "8c206dc1dcb42a84ce7960871dd35265c3975237d4fd1c581b16d8c02e775a09", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3139302c3133372c37332c32312c38312c3132302c3232352c39302c3232322c38392c3233322c3137302c36352c3230302c33382c3231352c39332c3138382c38312c36322c3138362c3233332c34312c3234382c3233312c3130312c3134312c34302c3235312c3138372c3131322c37325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a392c2275746631365f6c656e677468223a362c226c696e655f627265616b73223a317d" + }, + { + "nodeId": "be8949155178e15ade59e8aa41c826d75dbc513ebae929f8e7658d28fbbb7048", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3131312c3234382c39352c31332c3135312c3233332c35372c33372c3137302c32342c3230302c3133352c36372c36372c302c3138302c3234352c32342c35312c39302c3131322c3232332c3235332c33362c34322c3232392c37312c3232392c392c3133332c3136332c3139355d2c226279746573223a5b36352c3234302c3135392c3135322c3132382c3139352c3136392c31302c36365d7d" + } + ], + "invocation": { + "bufferId": "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "basisHeadId": "4ca1d0af6f518e869ba7ef0f98524ab013ae63d3b4ace8ba7b396bb3d2cc75e7", + "startByte": 1, + "endByte": 5, + "replacementUtf8Hex": "cebb0a" + }, + "terminal": { + "posture": "committable", + "footprint": { + "nodeReads": [ + "4ca1d0af6f518e869ba7ef0f98524ab013ae63d3b4ace8ba7b396bb3d2cc75e7", + "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "8c206dc1dcb42a84ce7960871dd35265c3975237d4fd1c581b16d8c02e775a09", + "be8949155178e15ade59e8aa41c826d75dbc513ebae929f8e7658d28fbbb7048" + ], + "nodeWrites": [ + "0953b40b6434bfdef2fd10afb2318626b74cf712e4dc253a53b1b066a7c92f0c", + "531f189bca556328a4141c2a56688bcebef4f7326add66515c0b1e64a6118599", + "624dfeaf0d86691cf4af9bfadcbe23b239c973bde92a2d3be07d9e9e8bc560a6", + "7e0ea8e49c450d74a6aee640493b5edb7da971548c51eafc34f081705b6519a7", + "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "88b5ce6420bf26f155f4c5870c73c86e510c011bbbdd0d7f7c4a9c5816e4e0e2", + "892de50db20728e8f8c2c75f55c995e9efecde6bec8f6edc24cdfcd559aa5a78", + "9bb29f54373162317da113a54fbc152ad498642c91f7b59b81b5130aef2b2637", + "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "b591b47c47bef32126cab7dc07f494e9cbcdee9e01addb444aec1ce800729958", + "baeb0fd1dbbbb5c9ef6142ba9586fbf99e77ad412f27cc0d39cdb15229c8dbbe", + "e7473ebef62c3d8a699077741bc73204fb3c8bb3cb9789fbb20ec29c145d4086" + ], + "attachmentReads": [ + "4ca1d0af6f518e869ba7ef0f98524ab013ae63d3b4ace8ba7b396bb3d2cc75e7", + "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "8c206dc1dcb42a84ce7960871dd35265c3975237d4fd1c581b16d8c02e775a09", + "be8949155178e15ade59e8aa41c826d75dbc513ebae929f8e7658d28fbbb7048" + ], + "attachmentWrites": [ + "0953b40b6434bfdef2fd10afb2318626b74cf712e4dc253a53b1b066a7c92f0c", + "531f189bca556328a4141c2a56688bcebef4f7326add66515c0b1e64a6118599", + "624dfeaf0d86691cf4af9bfadcbe23b239c973bde92a2d3be07d9e9e8bc560a6", + "7e0ea8e49c450d74a6aee640493b5edb7da971548c51eafc34f081705b6519a7", + "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "88b5ce6420bf26f155f4c5870c73c86e510c011bbbdd0d7f7c4a9c5816e4e0e2", + "892de50db20728e8f8c2c75f55c995e9efecde6bec8f6edc24cdfcd559aa5a78", + "9bb29f54373162317da113a54fbc152ad498642c91f7b59b81b5130aef2b2637", + "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "b591b47c47bef32126cab7dc07f494e9cbcdee9e01addb444aec1ce800729958", + "baeb0fd1dbbbb5c9ef6142ba9586fbf99e77ad412f27cc0d39cdb15229c8dbbe", + "e7473ebef62c3d8a699077741bc73204fb3c8bb3cb9789fbb20ec29c145d4086" + ], + "edgeReads": [], + "edgeWrites": [] + }, + "patch": [ + { + "kind": "upsert-node", + "nodeId": "0953b40b6434bfdef2fd10afb2318626b74cf712e4dc253a53b1b066a7c92f0c", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "531f189bca556328a4141c2a56688bcebef4f7326add66515c0b1e64a6118599", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "624dfeaf0d86691cf4af9bfadcbe23b239c973bde92a2d3be07d9e9e8bc560a6", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "7e0ea8e49c450d74a6aee640493b5edb7da971548c51eafc34f081705b6519a7", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e" + }, + { + "kind": "upsert-node", + "nodeId": "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e" + }, + { + "kind": "upsert-node", + "nodeId": "88b5ce6420bf26f155f4c5870c73c86e510c011bbbdd0d7f7c4a9c5816e4e0e2", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "892de50db20728e8f8c2c75f55c995e9efecde6bec8f6edc24cdfcd559aa5a78", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "9bb29f54373162317da113a54fbc152ad498642c91f7b59b81b5130aef2b2637", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9" + }, + { + "kind": "upsert-node", + "nodeId": "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "b591b47c47bef32126cab7dc07f494e9cbcdee9e01addb444aec1ce800729958", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "baeb0fd1dbbbb5c9ef6142ba9586fbf99e77ad412f27cc0d39cdb15229c8dbbe", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a" + }, + { + "kind": "upsert-node", + "nodeId": "e7473ebef62c3d8a699077741bc73204fb3c8bb3cb9789fbb20ec29c145d4086", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd" + }, + { + "kind": "set-node-alpha", + "nodeId": "0953b40b6434bfdef2fd10afb2318626b74cf712e4dc253a53b1b066a7c92f0c", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b38332c33312c32342c3135352c3230322c38352c39392c34302c3136342c32302c32382c34322c38362c3130342c3133392c3230362c3139302c3234342c3234372c35302c3130362c3232312c3130322c38312c39322c31312c33302c3130302c3136362c31372c3133332c3135335d2c227269676874223a5b3138312c3134352c3138302c3132342c37312c3139302c3234332c33332c33382c3230322c3138332c3232302c372c3234342c3134382c3233332c3230332c3230352c3233382c3135382c312c3137332c3231392c36382c37342c3233362c32382c3233322c302c3131342c3135332c38385d2c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a312c22686569676874223a327d" + }, + { + "kind": "set-node-alpha", + "nodeId": "531f189bca556328a4141c2a56688bcebef4f7326add66515c0b1e64a6118599", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3139302c3133372c37332c32312c38312c3132302c3232352c39302c3232322c38392c3233322c3137302c36352c3230302c33382c3231352c39332c3138382c38312c36322c3138362c3233332c34312c3234382c3233312c3130312c3134312c34302c3235312c3138372c3131322c37325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "624dfeaf0d86691cf4af9bfadcbe23b239c973bde92a2d3be07d9e9e8bc560a6", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3139302c3133372c37332c32312c38312c3132302c3232352c39302c3232322c38392c3233322c3137302c36352c3230302c33382c3231352c39332c3138382c38312c36322c3138362c3233332c34312c3234382c3233312c3130312c3134312c34302c3235312c3138372c3131322c37325d2c22627974655f7374617274223a312c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "7e0ea8e49c450d74a6aee640493b5edb7da971548c51eafc34f081705b6519a7", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132382c34302c3138352c3232342c3133352c332c37342c3235332c39322c3134372c3134382c3138372c3231322c3130302c36382c3233372c3131302c3137312c38342c3233342c33352c31342c33362c3139332c3134312c3235342c38362c3233382c3130342c3232302c34352c315d2c2262617369735f686561645f6964223a5b37362c3136312c3230382c3137352c3131312c38312c3134322c3133342c3135352c3136372c3233392c31352c3135322c38322c37342c3137362c31392c3137342c39392c3231312c3138302c3137322c3233322c3138362c3132332c35372c3130372c3137392c3231302c3230342c3131372c3233315d2c22726f6f745f6e6f64655f6964223a5b3137382c3134332c3130382c3233332c3130362c3131312c34342c39322c3235312c3137302c3139372c3133342c3133342c3130342c39332c3230302c38362c38312c362c3233352c38362c3134332c3136352c3135302c3130322c3234392c34322c302c3230302c3235332c31392c31355d2c22627974655f6c656e677468223a382c2275746631365f6c656e677468223a362c226c696e655f636f756e74223a332c22726f6f745f646967657374223a5b3137382c3134332c3130382c3233332c3130362c3131312c34342c39322c3235312c3137302c3139372c3133342c3133342c3130342c39332c3230302c38362c38312c362c3233352c38362c3134332c3136352c3135302c3130322c3234392c34322c302c3230302c3235332c31392c31355d2c2273657175656e6365223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22756e69636f64652d7265706c6163656d656e74222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3132362c31342c3136382c3232382c3135362c36392c31332c3131362c3136362c3137342c3233302c36342c37332c35392c39342c3231392c3132352c3136392c3131332c38342c3134302c38312c3233342c3235322c35322c3234302c3132392c3131322c39312c3130312c32352c3136375d2c2276657273696f6e223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "88b5ce6420bf26f155f4c5870c73c86e510c011bbbdd0d7f7c4a9c5816e4e0e2", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3139302c3133372c37332c32312c38312c3132302c3232352c39302c3232322c38392c3233322c3137302c36352c3230302c33382c3231352c39332c3138382c38312c36322c3138362c3233332c34312c3234382c3233312c3130312c3134312c34302c3235312c3138372c3131322c37325d2c22627974655f7374617274223a312c22627974655f6c656e677468223a382c2275746631365f6c656e677468223a352c226c696e655f627265616b73223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "892de50db20728e8f8c2c75f55c995e9efecde6bec8f6edc24cdfcd559aa5a78", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3139302c3133372c37332c32312c38312c3132302c3232352c39302c3232322c38392c3233322c3137302c36352c3230302c33382c3231352c39332c3138382c38312c36322c3138362c3233332c34312c3234382c3233312c3130312c3134312c34302c3235312c3138372c3131322c37325d2c22627974655f7374617274223a352c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "9bb29f54373162317da113a54fbc152ad498642c91f7b59b81b5130aef2b2637", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37302c3131312c38352c3132322c3134382c3136312c3235302c3132392c34362c3134372c33382c3134362c3139332c3230322c3233382c3131382c3231382c3136352c34332c3233382c3134302c32302c38342c3137372c3137362c3137362c3132342c39372c3232332c31372c3139362c33315d2c226279746573223a5b3230362c3138372c31305d7d" + }, + { + "kind": "set-node-alpha", + "nodeId": "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b392c38332c3138302c31312c3130302c35322c3139312c3232322c3234322c3235332c31362c3137352c3137382c34392c3133342c33382c3138332c37362c3234372c31382c3232382c3232302c33372c35382c38332c3137372c3137362c3130322c3136372c3230312c34372c31325d2c227269676874223a5b3133372c34352c3232392c31332c3137382c372c34302c3233322c3234382c3139342c3139392c39352c38352c3230312c3134392c3233332c3233392c3233362c3232322c3130372c3233362c3134332c3131302c3232302c33362c3230352c3235322c3231332c38392c3137302c39302c3132305d2c22627974655f6c656e677468223a382c2275746631365f6c656e677468223a362c226c696e655f627265616b73223a322c22686569676874223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "b591b47c47bef32126cab7dc07f494e9cbcdee9e01addb444aec1ce800729958", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3135352c3137382c3135392c38342c35352c34392c39382c34392c3132352c3136312c31392c3136352c37392c3138382c32312c34322c3231322c3135322c3130302c34342c3134352c3234372c3138312c3135352c3132392c3138312c31392c31302c3233392c34332c33382c35355d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "baeb0fd1dbbbb5c9ef6142ba9586fbf99e77ad412f27cc0d39cdb15229c8dbbe", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132382c34302c3138352c3232342c3133352c332c37342c3235332c39322c3134372c3134382c3138372c3231322c3130302c36382c3233372c3131302c3137312c38342c3233342c33352c31342c33362c3139332c3134312c3235342c38362c3233382c3130342c3232302c34352c315d2c2262617369735f686561645f6964223a5b37362c3136312c3230382c3137352c3131312c38312c3134322c3133342c3135352c3136372c3233392c31352c3135322c38322c37342c3137362c31392c3137342c39392c3231312c3138302c3137322c3233322c3138362c3132332c35372c3130372c3137392c3231302c3230342c3131372c3233315d2c226e6578745f686561645f6964223a5b3132362c31342c3136382c3232382c3135362c36392c31332c3131362c3136362c3137342c3233302c36342c37332c35392c39342c3231392c3132352c3136392c3131332c38342c3134302c38312c3233342c3235322c35322c3234302c3132392c3131322c39312c3130312c32352c3136375d2c2273746172745f62797465223a312c22656e645f62797465223a352c22696e7365727465645f627974655f6c656e677468223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "e7473ebef62c3d8a699077741bc73204fb3c8bb3cb9789fbb20ec29c145d4086", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b3138362c3233352c31352c3230392c3231392c3138372c3138312c3230312c3233392c39372c36362c3138362c3134392c3133342c3235312c3234392c3135382c3131392c3137332c36352c34372c33392c3230342c31332c35372c3230352c3137372c38322c34312c3230302c3231392c3139305d2c2262617369735f686561645f6964223a5b37362c3136312c3230382c3137352c3131312c38312c3134322c3133342c3135352c3136372c3233392c31352c3135322c38322c37342c3137362c31392c3137342c39392c3231312c3138302c3137322c3233322c3138362c3132332c35372c3130372c3137392c3231302c3230342c3131372c3233315d2c226e6578745f686561645f6964223a5b3132362c31342c3136382c3232382c3135362c36392c31332c3131362c3136362c3137342c3233302c36342c37332c35392c39342c3231392c3132352c3136392c3131332c38342c3134302c38312c3233342c3235322c35322c3234302c3132392c3131322c39312c3130312c32352c3136375d2c2273746172745f62797465223a312c22656e645f62797465223a352c22696e7365727465645f627974655f6c656e677468223a332c2264656c657465645f627974655f6c656e677468223a347d" + } + ], + "createdNodeIds": [ + "0953b40b6434bfdef2fd10afb2318626b74cf712e4dc253a53b1b066a7c92f0c", + "531f189bca556328a4141c2a56688bcebef4f7326add66515c0b1e64a6118599", + "624dfeaf0d86691cf4af9bfadcbe23b239c973bde92a2d3be07d9e9e8bc560a6", + "7e0ea8e49c450d74a6aee640493b5edb7da971548c51eafc34f081705b6519a7", + "88b5ce6420bf26f155f4c5870c73c86e510c011bbbdd0d7f7c4a9c5816e4e0e2", + "892de50db20728e8f8c2c75f55c995e9efecde6bec8f6edc24cdfcd559aa5a78", + "9bb29f54373162317da113a54fbc152ad498642c91f7b59b81b5130aef2b2637", + "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "b591b47c47bef32126cab7dc07f494e9cbcdee9e01addb444aec1ce800729958", + "baeb0fd1dbbbb5c9ef6142ba9586fbf99e77ad412f27cc0d39cdb15229c8dbbe", + "e7473ebef62c3d8a699077741bc73204fb3c8bb3cb9789fbb20ec29c145d4086" + ], + "updatedNodeIds": [ + "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01" + ], + "untouchedBasisNodeIds": [ + "4ca1d0af6f518e869ba7ef0f98524ab013ae63d3b4ace8ba7b396bb3d2cc75e7", + "8c206dc1dcb42a84ce7960871dd35265c3975237d4fd1c581b16d8c02e775a09", + "be8949155178e15ade59e8aa41c826d75dbc513ebae929f8e7658d28fbbb7048" + ], + "result": { + "bufferId": "8028b9e087034afd5c9394bbd46444ed6eab54ea230e24c18dfe56ee68dc2d01", + "headId": "7e0ea8e49c450d74a6aee640493b5edb7da971548c51eafc34f081705b6519a7", + "rootNodeId": "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "rootDigest": "b28f6ce96a6f2c5cfbaac58686685dc8565106eb568fa59666f92a00c8fd130f", + "rewriteId": "baeb0fd1dbbbb5c9ef6142ba9586fbf99e77ad412f27cc0d39cdb15229c8dbbe", + "diffId": "e7473ebef62c3d8a699077741bc73204fb3c8bb3cb9789fbb20ec29c145d4086", + "byteLength": 8, + "utf16Length": 6, + "lineCount": 3, + "sequence": 1, + "version": 1, + "materializedTextUtf8Hex": "41cebb0ac3a90a42" + } + } + }, + { + "id": "cross-leaf-rebalance", + "purpose": "finite persistent-rope ReplaceRange conformance witness", + "basisFacts": [ + { + "nodeId": "01562d87f0f9dacf7851d40539542ee2ef6754a77e871facccf9d00fdd9af0c5", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3134302c3135302c36342c36332c34382c3133342c3230312c34362c3136372c35382c35352c39392c31362c32312c3230332c3131352c36302c3135382c39362c3234302c36352c3133302c3136302c3130302c3133332c3132312c35372c3231322c31342c36332c33322c3139325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039362c2275746631365f6c656e677468223a343039362c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "02aedab2e321cd11b064b9b560e7a1b2b58b119ccefb64fcf01679a8ad14253c", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b31392c33382c3135392c36382c3137322c3139372c33322c3137362c3230342c3135352c3232352c3137352c3130362c3139362c3134322c3231322c3231312c3137302c37372c32332c3235352c3132362c3131302c3230392c3232342c32382c39312c38312c3133362c3139312c39382c3136385d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3233302c3130312c3135322c3133392c31322c39382c3138362c3235302c3133342c3134352c3232392c3132322c35372c3234392c3135352c3130302c3231372c3132392c34322c33362c39322c39312c3138362c3130302c3130302c3133362c34332c3132312c32352c3233362c34332c3138325d2c22627974655f6c656e677468223a31323238382c2275746631365f6c656e677468223a31323238382c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3233302c3130312c3135322c3133392c31322c39382c3138362c3235302c3133342c3134352c3232392c3132322c35372c3234392c3135352c3130302c3231372c3132392c34322c33362c39322c39312c3138362c3130302c3130302c3133362c34332c3132312c32352c3233362c34332c3138325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a2263726f73732d6c6561662d726562616c616e6365222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b322c3137342c3231382c3137382c3232372c33332c3230352c31372c3137362c3130302c3138352c3138312c39362c3233312c3136312c3137382c3138312c3133392c31372c3135362c3230362c3235312c3130302c3235322c3234302c32322c3132312c3136382c3137332c32302c33372c36305d2c2276657273696f6e223a307d" + }, + { + "nodeId": "4d197711c188115df4eb1268a70574a93f16a2127a178a4e3cd1c99a76c5f043", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37302c3131302c39362c3234382c39382c3135302c35362c37372c31302c34392c3233342c3232342c33372c3139322c34322c34362c36362c3130342c33332c3231362c32342c3135352c34362c302c3130322c3137382c3231392c3230392c3234362c31332c38372c34315d2c226279746573223a5b39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39375d7d" + }, + { + "nodeId": "4e1b4ee17eb1129bdcc8081c5b04c63702038252e8298234208854017fc68d54", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136302c3135382c31312c32312c38332c3135332c3230392c3231332c3234302c3130362c3134372c39312c3138312c3134362c3131332c3137312c3130352c3133312c3231372c3233382c33312c3234392c3133372c35382c3135362c3230392c35382c3134332c3139332c3130312c3230382c3232365d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039362c2275746631365f6c656e677468223a343039362c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "89d138e87e58cce6c0c8362e4924c259f45dd8811bc03fda8625e96244cf6e83", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b37372c32352c3131392c31372c3139332c3133362c31372c39332c3234342c3233352c31382c3130342c3136372c352c3131362c3136392c36332c32322c3136322c31382c3132322c32332c3133382c37382c36302c3230392c3230312c3135342c3131382c3139372c3234302c36375d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039362c2275746631365f6c656e677468223a343039362c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "8c96403f3086c92ea73a37631015cb733c9e60f04182a064857939d40e3f20c0", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37352c3132352c3133302c3133352c3130392c3135312c3133312c36312c36392c3131392c3134382c3134392c362c38332c3135382c37312c3233382c3136362c3137382c3231372c33322c352c34342c39352c382c3132392c3138312c33392c32352c3233372c32332c3230355d2c226279746573223a5b39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39382c39385d7d" + }, + { + "nodeId": "a09e0b155399d1d5f06a935bb59271ab6983d9ee1ff9893a9cd13a8fc165d0e2", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37312c3135332c3233352c3232352c32352c38302c36332c3134362c3136302c3230322c36382c38372c3134392c322c36322c3135342c31302c3135332c3130342c3138322c3137342c38332c3132312c32392c3130322c3230372c3131352c3135342c3134322c3230352c3230392c3137315d2c226279746573223a5b39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39392c39395d7d" + }, + { + "nodeId": "c0ac798dea10d63d1c706ddc6acb666a6a55fb22564bcc573b73aecbb085097e", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3133372c3230392c35362c3233322c3132362c38382c3230342c3233302c3139322c3230302c35342c34362c37332c33362c3139342c38392c3234342c39332c3231362c3132392c32372c3139322c36332c3231382c3133342c33372c3233332c39382c36382c3230372c3131302c3133315d2c227269676874223a5b312c38362c34352c3133352c3234302c3234392c3231382c3230372c3132302c38312c3231322c352c35372c38342c34362c3232362c3233392c3130332c38342c3136372c3132362c3133352c33312c3137322c3230342c3234392c3230382c31352c3232312c3135342c3234302c3139375d2c22627974655f6c656e677468223a383139322c2275746631365f6c656e677468223a383139322c226c696e655f627265616b73223a302c22686569676874223a327d" + }, + { + "nodeId": "e665988b0c62bafa8691e57a39f99b64d9812a245c5bba6464882b7919ec2bb6", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3139322c3137322c3132312c3134312c3233342c31362c3231342c36312c32382c3131322c3130392c3232302c3130362c3230332c3130322c3130362c3130362c38352c3235312c33342c38362c37352c3230342c38372c35392c3131352c3137342c3230332c3137362c3133332c392c3132365d2c227269676874223a5b37382c32372c37382c3232352c3132362c3137372c31382c3135352c3232302c3230302c382c32382c39312c342c3139382c35352c322c332c3133302c38322c3233322c34312c3133302c35322c33322c3133362c38342c312c3132372c3139382c3134312c38345d2c22627974655f6c656e677468223a31323238382c2275746631365f6c656e677468223a31323238382c226c696e655f627265616b73223a302c22686569676874223a337d" + } + ], + "invocation": { + "bufferId": "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "basisHeadId": "02aedab2e321cd11b064b9b560e7a1b2b58b119ccefb64fcf01679a8ad14253c", + "startByte": 4094, + "endByte": 8194, + "replacementUtf8Hex": "ceb20a5a" + }, + "terminal": { + "posture": "committable", + "footprint": { + "nodeReads": [ + "01562d87f0f9dacf7851d40539542ee2ef6754a77e871facccf9d00fdd9af0c5", + "02aedab2e321cd11b064b9b560e7a1b2b58b119ccefb64fcf01679a8ad14253c", + "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "4d197711c188115df4eb1268a70574a93f16a2127a178a4e3cd1c99a76c5f043", + "4e1b4ee17eb1129bdcc8081c5b04c63702038252e8298234208854017fc68d54", + "89d138e87e58cce6c0c8362e4924c259f45dd8811bc03fda8625e96244cf6e83", + "8c96403f3086c92ea73a37631015cb733c9e60f04182a064857939d40e3f20c0", + "a09e0b155399d1d5f06a935bb59271ab6983d9ee1ff9893a9cd13a8fc165d0e2", + "c0ac798dea10d63d1c706ddc6acb666a6a55fb22564bcc573b73aecbb085097e", + "e665988b0c62bafa8691e57a39f99b64d9812a245c5bba6464882b7919ec2bb6" + ], + "nodeWrites": [ + "0e1886c0fb0eb9b0ce9ae0d72ca5870163ebcdb5ad40d1230cf4d71e753b077f", + "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "1be4400b3d12df9c6d9217c7161a62a9ec9b88adb69e329980d0c8fd6d480b0d", + "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "4d7e1cbb323c7bf2e6226887f35e42132fc0ae89b80ad73118ad769964194f5f", + "53944ec802f7ac24209dcbd261da9da9af4b1bc308e192d15ccd98c8c4ee2b6f", + "59645ec30419509f3f7a4ca80bcffd85203ba7a2792b07f3a73fa86cc278f56e", + "5984dc86ddd2631f51ec589f23bf0975a53f7422ff47b8028297ea356e64f5f3", + "9153d0ad9b6d249f9d9d48785699a64ebdc378271711496bfc3de768526849f2", + "aab4280bf8d823a0692cbdc998c99d143362f857cc943768a1e123bc5369ebaf", + "bb7602b30a93833e7afc1abb4a9c8a4fdbb3aeb982e0e404bebcf91403e51a5a", + "d8b4236fef26140fbae630016a83e62c78d7111485c631402e7d10c01a0cc8ee", + "e7369137c62d0c6ce1b64065c0d3311325918075238e8952dba105b945e2093d", + "f5e7e901d1017721cfd70bb9868b9453134e81d5ccab38f853e0d80048e61fed", + "ffcddc1cb682508a527fe6c94216d9a8cce40b362446df7f66a6c53804153a46" + ], + "attachmentReads": [ + "01562d87f0f9dacf7851d40539542ee2ef6754a77e871facccf9d00fdd9af0c5", + "02aedab2e321cd11b064b9b560e7a1b2b58b119ccefb64fcf01679a8ad14253c", + "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "4d197711c188115df4eb1268a70574a93f16a2127a178a4e3cd1c99a76c5f043", + "4e1b4ee17eb1129bdcc8081c5b04c63702038252e8298234208854017fc68d54", + "89d138e87e58cce6c0c8362e4924c259f45dd8811bc03fda8625e96244cf6e83", + "8c96403f3086c92ea73a37631015cb733c9e60f04182a064857939d40e3f20c0", + "a09e0b155399d1d5f06a935bb59271ab6983d9ee1ff9893a9cd13a8fc165d0e2", + "c0ac798dea10d63d1c706ddc6acb666a6a55fb22564bcc573b73aecbb085097e", + "e665988b0c62bafa8691e57a39f99b64d9812a245c5bba6464882b7919ec2bb6" + ], + "attachmentWrites": [ + "0e1886c0fb0eb9b0ce9ae0d72ca5870163ebcdb5ad40d1230cf4d71e753b077f", + "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "1be4400b3d12df9c6d9217c7161a62a9ec9b88adb69e329980d0c8fd6d480b0d", + "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "4d7e1cbb323c7bf2e6226887f35e42132fc0ae89b80ad73118ad769964194f5f", + "53944ec802f7ac24209dcbd261da9da9af4b1bc308e192d15ccd98c8c4ee2b6f", + "59645ec30419509f3f7a4ca80bcffd85203ba7a2792b07f3a73fa86cc278f56e", + "5984dc86ddd2631f51ec589f23bf0975a53f7422ff47b8028297ea356e64f5f3", + "9153d0ad9b6d249f9d9d48785699a64ebdc378271711496bfc3de768526849f2", + "aab4280bf8d823a0692cbdc998c99d143362f857cc943768a1e123bc5369ebaf", + "bb7602b30a93833e7afc1abb4a9c8a4fdbb3aeb982e0e404bebcf91403e51a5a", + "d8b4236fef26140fbae630016a83e62c78d7111485c631402e7d10c01a0cc8ee", + "e7369137c62d0c6ce1b64065c0d3311325918075238e8952dba105b945e2093d", + "f5e7e901d1017721cfd70bb9868b9453134e81d5ccab38f853e0d80048e61fed", + "ffcddc1cb682508a527fe6c94216d9a8cce40b362446df7f66a6c53804153a46" + ], + "edgeReads": [], + "edgeWrites": [] + }, + "patch": [ + { + "kind": "upsert-node", + "nodeId": "0e1886c0fb0eb9b0ce9ae0d72ca5870163ebcdb5ad40d1230cf4d71e753b077f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e" + }, + { + "kind": "upsert-node", + "nodeId": "1be4400b3d12df9c6d9217c7161a62a9ec9b88adb69e329980d0c8fd6d480b0d", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "4d7e1cbb323c7bf2e6226887f35e42132fc0ae89b80ad73118ad769964194f5f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "53944ec802f7ac24209dcbd261da9da9af4b1bc308e192d15ccd98c8c4ee2b6f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "59645ec30419509f3f7a4ca80bcffd85203ba7a2792b07f3a73fa86cc278f56e", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "5984dc86ddd2631f51ec589f23bf0975a53f7422ff47b8028297ea356e64f5f3", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "9153d0ad9b6d249f9d9d48785699a64ebdc378271711496bfc3de768526849f2", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a" + }, + { + "kind": "upsert-node", + "nodeId": "aab4280bf8d823a0692cbdc998c99d143362f857cc943768a1e123bc5369ebaf", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "bb7602b30a93833e7afc1abb4a9c8a4fdbb3aeb982e0e404bebcf91403e51a5a", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "d8b4236fef26140fbae630016a83e62c78d7111485c631402e7d10c01a0cc8ee", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9" + }, + { + "kind": "upsert-node", + "nodeId": "e7369137c62d0c6ce1b64065c0d3311325918075238e8952dba105b945e2093d", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e" + }, + { + "kind": "upsert-node", + "nodeId": "f5e7e901d1017721cfd70bb9868b9453134e81d5ccab38f853e0d80048e61fed", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "ffcddc1cb682508a527fe6c94216d9a8cce40b362446df7f66a6c53804153a46", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd" + }, + { + "kind": "set-node-alpha", + "nodeId": "0e1886c0fb0eb9b0ce9ae0d72ca5870163ebcdb5ad40d1230cf4d71e753b077f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b37372c32352c3131392c31372c3139332c3133362c31372c39332c3234342c3233352c31382c3130342c3136372c352c3131362c3136392c36332c32322c3136322c31382c3132322c32332c3133382c37382c36302c3230392c3230312c3135342c3131382c3139372c3234302c36375d2c22627974655f7374617274223a343039342c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a2263726f73732d6c6561662d726562616c616e6365222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3233312c35342c3134352c35352c3139382c34352c31322c3130382c3232352c3138322c36342c3130312c3139322c3231312c34392c31392c33372c3134352c3132382c3131372c33352c3134322c3133372c38322c3231392c3136312c352c3138352c36392c3232362c392c36315d2c2276657273696f6e223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "1be4400b3d12df9c6d9217c7161a62a9ec9b88adb69e329980d0c8fd6d480b0d", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136302c3135382c31312c32312c38332c3135332c3230392c3231332c3234302c3130362c3134372c39312c3138312c3134362c3131332c3137312c3130352c3133312c3231372c3233382c33312c3234392c3133372c35382c3135362c3230392c35382c3134332c3139332c3130312c3230382c3232365d2c22627974655f7374617274223a322c22627974655f6c656e677468223a343039342c2275746631365f6c656e677468223a343039342c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3137302c3138302c34302c31312c3234382c3231362c33352c3136302c3130352c34342c3138392c3230312c3135322c3230312c3135372c32302c35312c39382c3234382c38372c3230342c3134382c35352c3130342c3136312c3232352c33352c3138382c38332c3130352c3233352c3137355d2c227269676874223a5b32372c3232382c36342c31312c36312c31382c3232332c3135362c3130392c3134362c32332c3139392c32322c32362c39382c3136392c3233362c3135352c3133362c3137332c3138322c3135382c35302c3135332c3132382c3230382c3230302c3235332c3130392c37322c31312c31335d2c22627974655f6c656e677468223a383139322c2275746631365f6c656e677468223a383139312c226c696e655f627265616b73223a312c22686569676874223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "4d7e1cbb323c7bf2e6226887f35e42132fc0ae89b80ad73118ad769964194f5f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136302c3135382c31312c32312c38332c3135332c3230392c3231332c3234302c3130362c3134372c39312c3138312c3134362c3131332c3137312c3130352c3133312c3231372c3233382c33312c3234392c3133372c35382c3135362c3230392c35382c3134332c3139332c3130312c3230382c3232365d2c22627974655f7374617274223a302c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "53944ec802f7ac24209dcbd261da9da9af4b1bc308e192d15ccd98c8c4ee2b6f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b37372c32352c3131392c31372c3139332c3133362c31372c39332c3234342c3233352c31382c3130342c3136372c352c3131362c3136392c36332c32322c3136322c31382c3132322c32332c3133382c37382c36302c3230392c3230312c3135342c3131382c3139372c3234302c36375d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039342c2275746631365f6c656e677468223a343039342c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "59645ec30419509f3f7a4ca80bcffd85203ba7a2792b07f3a73fa86cc278f56e", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b31342c32342c3133342c3139322c3235312c31342c3138352c3137362c3230362c3135342c3232342c3231352c34342c3136352c3133352c312c39392c3233352c3230352c3138312c3137332c36342c3230392c33352c31322c3234342c3231352c33302c3131372c35392c372c3132375d2c227269676874223a5b312c38362c34352c3133352c3234302c3234392c3231382c3230372c3132302c38312c3231322c352c35372c38342c34362c3232362c3233392c3130332c38342c3136372c3132362c3133352c33312c3137322c3230342c3234392c3230382c31352c3232312c3135342c3234302c3139375d2c22627974655f6c656e677468223a343039382c2275746631365f6c656e677468223a343039382c226c696e655f627265616b73223a302c22686569676874223a327d" + }, + { + "kind": "set-node-alpha", + "nodeId": "5984dc86ddd2631f51ec589f23bf0975a53f7422ff47b8028297ea356e64f5f3", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b38392c3130302c39342c3139352c342c32352c38302c3135392c36332c3132322c37362c3136382c31312c3230372c3235332c3133332c33322c35392c3136372c3136322c3132312c34332c372c3234332c3136372c36332c3136382c3130382c3139342c3132302c3234352c3131305d2c227269676874223a5b37382c32372c37382c3232352c3132362c3137372c31382c3135352c3232302c3230302c382c32382c39312c342c3139382c35352c322c332c3133302c38322c3233322c34312c3133302c35322c33322c3133362c38342c312c3132372c3139382c3134312c38345d2c22627974655f6c656e677468223a383139342c2275746631365f6c656e677468223a383139342c226c696e655f627265616b73223a302c22686569676874223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "9153d0ad9b6d249f9d9d48785699a64ebdc378271711496bfc3de768526849f2", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b31392c33382c3135392c36382c3137322c3139372c33322c3137362c3230342c3135352c3232352c3137352c3130362c3139362c3134322c3231322c3231312c3137302c37372c32332c3235352c3132362c3131302c3230392c3232342c32382c39312c38312c3133362c3139312c39382c3136385d2c2262617369735f686561645f6964223a5b322c3137342c3231382c3137382c3232372c33332c3230352c31372c3137362c3130302c3138352c3138312c39362c3233312c3136312c3137382c3138312c3133392c31372c3135362c3230362c3235312c3130302c3235322c3234302c32322c3132312c3136382c3137332c32302c33372c36305d2c226e6578745f686561645f6964223a5b3233312c35342c3134352c35352c3139382c34352c31322c3130382c3232352c3138322c36342c3130312c3139322c3231312c34392c31392c33372c3134352c3132382c3131372c33352c3134322c3133372c38322c3231392c3136312c352c3138352c36392c3232362c392c36315d2c2273746172745f62797465223a343039342c22656e645f62797465223a383139342c22696e7365727465645f627974655f6c656e677468223a347d" + }, + { + "kind": "set-node-alpha", + "nodeId": "aab4280bf8d823a0692cbdc998c99d143362f857cc943768a1e123bc5369ebaf", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b38332c3134382c37382c3230302c322c3234372c3137322c33362c33322c3135372c3230332c3231302c39372c3231382c3135372c3136392c3137352c37352c32372c3139352c382c3232352c3134362c3230392c39322c3230352c3135322c3230302c3139362c3233382c34332c3131315d2c227269676874223a5b3234352c3233312c3233332c312c3230392c312c3131392c33332c3230372c3231352c31312c3138352c3133342c3133392c3134382c38332c31392c37382c3132392c3231332c3230342c3137312c35362c3234382c38332c3232342c3231362c302c37322c3233302c33312c3233375d2c22627974655f6c656e677468223a343039382c2275746631365f6c656e677468223a343039372c226c696e655f627265616b73223a312c22686569676874223a327d" + }, + { + "kind": "set-node-alpha", + "nodeId": "bb7602b30a93833e7afc1abb4a9c8a4fdbb3aeb982e0e404bebcf91403e51a5a", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b38392c3130302c39342c3139352c342c32352c38302c3135392c36332c3132322c37362c3136382c31312c3230372c3235332c3133332c33322c35392c3136372c3136322c3132312c34332c372c3234332c3136372c36332c3136382c3130382c3139342c3132302c3234352c3131305d2c227269676874223a5b37372c3132362c32382c3138372c35302c36302c3132332c3234322c3233302c33342c3130342c3133352c3234332c39342c36362c31392c34372c3139322c3137342c3133372c3138342c31302c3231352c34392c32342c3137332c3131382c3135332c3130302c32352c37392c39355d2c22627974655f6c656e677468223a343130302c2275746631365f6c656e677468223a343130302c226c696e655f627265616b73223a302c22686569676874223a337d" + }, + { + "kind": "set-node-alpha", + "nodeId": "d8b4236fef26140fbae630016a83e62c78d7111485c631402e7d10c01a0cc8ee", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3231392c3136312c3133382c3134312c3133342c3232322c35342c32372c37342c3139362c3133332c3134392c33332c3132312c3139352c32362c3138392c3130392c3132302c3133392c3231352c32312c3234302c3130322c3138332c31362c3134342c312c3233362c33302c3230352c3230365d2c226279746573223a5b3230362c3137382c31302c39305d7d" + }, + { + "kind": "set-node-alpha", + "nodeId": "e7369137c62d0c6ce1b64065c0d3311325918075238e8952dba105b945e2093d", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b31392c33382c3135392c36382c3137322c3139372c33322c3137362c3230342c3135352c3232352c3137352c3130362c3139362c3134322c3231322c3231312c3137302c37372c32332c3235352c3132362c3131302c3230392c3232342c32382c39312c38312c3133362c3139312c39382c3136385d2c2262617369735f686561645f6964223a5b322c3137342c3231382c3137382c3232372c33332c3230352c31372c3137362c3130302c3138352c3138312c39362c3233312c3136312c3137382c3138312c3133392c31372c3135362c3230362c3235312c3130302c3235322c3234302c32322c3132312c3136382c3137332c32302c33372c36305d2c22726f6f745f6e6f64655f6964223a5b35312c3139352c31362c3231342c3130342c32342c36372c372c3131382c3136382c3134302c3138392c3138332c38372c3139352c3134382c332c3137372c3132352c34312c3132392c382c38322c3234312c3130382c3132312c3132342c3230332c3139372c34392c31392c3131375d2c22627974655f6c656e677468223a383139322c2275746631365f6c656e677468223a383139312c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b35312c3139352c31362c3231342c3130342c32342c36372c372c3131382c3136382c3134302c3138392c3138332c38372c3139352c3134382c332c3137372c3132352c34312c3132392c382c38322c3234312c3130382c3132312c3132342c3230332c3139372c34392c31392c3131375d2c2273657175656e6365223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "f5e7e901d1017721cfd70bb9868b9453134e81d5ccab38f853e0d80048e61fed", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3231362c3138302c33352c3131312c3233392c33382c32302c31352c3138362c3233302c34382c312c3130362c3133312c3233302c34342c3132302c3231352c31372c32302c3133332c3139382c34392c36342c34362c3132352c31362c3139322c32362c31322c3230302c3233385d2c22627974655f7374617274223a302c22627974655f6c656e677468223a342c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "ffcddc1cb682508a527fe6c94216d9a8cce40b362446df7f66a6c53804153a46", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b3134352c38332c3230382c3137332c3135352c3130392c33362c3135392c3135372c3135372c37322c3132302c38362c3135332c3136362c37382c3138392c3139352c3132302c33392c32332c31372c37332c3130372c3235322c36312c3233312c3130342c38322c3130342c37332c3234325d2c2262617369735f686561645f6964223a5b322c3137342c3231382c3137382c3232372c33332c3230352c31372c3137362c3130302c3138352c3138312c39362c3233312c3136312c3137382c3138312c3133392c31372c3135362c3230362c3235312c3130302c3235322c3234302c32322c3132312c3136382c3137332c32302c33372c36305d2c226e6578745f686561645f6964223a5b3233312c35342c3134352c35352c3139382c34352c31322c3130382c3232352c3138322c36342c3130312c3139322c3231312c34392c31392c33372c3134352c3132382c3131372c33352c3134322c3133372c38322c3231392c3136312c352c3138352c36392c3232362c392c36315d2c2273746172745f62797465223a343039342c22656e645f62797465223a383139342c22696e7365727465645f627974655f6c656e677468223a342c2264656c657465645f627974655f6c656e677468223a343130307d" + } + ], + "createdNodeIds": [ + "0e1886c0fb0eb9b0ce9ae0d72ca5870163ebcdb5ad40d1230cf4d71e753b077f", + "1be4400b3d12df9c6d9217c7161a62a9ec9b88adb69e329980d0c8fd6d480b0d", + "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "4d7e1cbb323c7bf2e6226887f35e42132fc0ae89b80ad73118ad769964194f5f", + "53944ec802f7ac24209dcbd261da9da9af4b1bc308e192d15ccd98c8c4ee2b6f", + "59645ec30419509f3f7a4ca80bcffd85203ba7a2792b07f3a73fa86cc278f56e", + "5984dc86ddd2631f51ec589f23bf0975a53f7422ff47b8028297ea356e64f5f3", + "9153d0ad9b6d249f9d9d48785699a64ebdc378271711496bfc3de768526849f2", + "aab4280bf8d823a0692cbdc998c99d143362f857cc943768a1e123bc5369ebaf", + "bb7602b30a93833e7afc1abb4a9c8a4fdbb3aeb982e0e404bebcf91403e51a5a", + "d8b4236fef26140fbae630016a83e62c78d7111485c631402e7d10c01a0cc8ee", + "e7369137c62d0c6ce1b64065c0d3311325918075238e8952dba105b945e2093d", + "f5e7e901d1017721cfd70bb9868b9453134e81d5ccab38f853e0d80048e61fed", + "ffcddc1cb682508a527fe6c94216d9a8cce40b362446df7f66a6c53804153a46" + ], + "updatedNodeIds": [ + "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8" + ], + "untouchedBasisNodeIds": [ + "01562d87f0f9dacf7851d40539542ee2ef6754a77e871facccf9d00fdd9af0c5", + "02aedab2e321cd11b064b9b560e7a1b2b58b119ccefb64fcf01679a8ad14253c", + "4d197711c188115df4eb1268a70574a93f16a2127a178a4e3cd1c99a76c5f043", + "4e1b4ee17eb1129bdcc8081c5b04c63702038252e8298234208854017fc68d54", + "89d138e87e58cce6c0c8362e4924c259f45dd8811bc03fda8625e96244cf6e83", + "8c96403f3086c92ea73a37631015cb733c9e60f04182a064857939d40e3f20c0", + "a09e0b155399d1d5f06a935bb59271ab6983d9ee1ff9893a9cd13a8fc165d0e2", + "c0ac798dea10d63d1c706ddc6acb666a6a55fb22564bcc573b73aecbb085097e", + "e665988b0c62bafa8691e57a39f99b64d9812a245c5bba6464882b7919ec2bb6" + ], + "result": { + "bufferId": "13269f44acc520b0cc9be1af6ac48ed4d3aa4d17ff7e6ed1e01c5b5188bf62a8", + "headId": "e7369137c62d0c6ce1b64065c0d3311325918075238e8952dba105b945e2093d", + "rootNodeId": "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "rootDigest": "33c310d66818430776a88cbdb757c39403b17d29810852f16c797ccbc5311375", + "rewriteId": "9153d0ad9b6d249f9d9d48785699a64ebdc378271711496bfc3de768526849f2", + "diffId": "ffcddc1cb682508a527fe6c94216d9a8cce40b362446df7f66a6c53804153a46", + "byteLength": 8192, + "utf16Length": 8191, + "lineCount": 2, + "sequence": 1, + "version": 1, + "materializedTextUtf8Hex": "6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161ceb20a5a6363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363" + } + } + }, + { + "id": "replacement-larger-than-leaf", + "purpose": "finite persistent-rope ReplaceRange conformance witness", + "basisFacts": [ + { + "nodeId": "18560c12d14a67205c3bb96056986ed76fe7751ddf6150f1042818fb7a55e47a", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3233352c3132372c3138352c3130322c3133312c3139382c302c31322c3133392c31322c3133352c3133352c3138302c392c34342c32322c3138332c3130352c3132302c3133302c35322c332c34332c37302c3231302c32352c3136332c3138382c3232302c32382c3135312c31325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a352c2275746631365f6c656e677468223a352c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a227265706c6163656d656e742d6c61726765722d7468616e2d6c656166222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3232332c39352c3232322c38312c33352c3134362c34382c36372c3137342c35392c3138322c39342c3134372c3133322c3132342c3230392c3131342c3136312c3133392c3130382c32372c33302c38332c34362c35342c3138382c38352c34342c39392c37362c302c345d2c2276657273696f6e223a307d" + }, + { + "nodeId": "df5fde5123923043ae3bb65e93847cd172a18b6c1b1e532e36bc552c634c0004", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3130312c31332c3234322c3136322c3135392c3139312c38312c34372c3231332c3136322c3131312c35352c35312c3138392c37312c38352c36342c3138382c3134372c3233372c38352c36362c38302c38322c3130352c3136392c36342c3133342c362c36362c3232332c3231325d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b32342c38362c31322c31382c3230392c37342c3130332c33322c39322c35392c3138352c39362c38362c3135322c3131302c3231352c3131312c3233312c3131372c32392c3232332c39372c38302c3234312c342c34302c32342c3235312c3132322c38352c3232382c3132325d2c22627974655f6c656e677468223a352c2275746631365f6c656e677468223a352c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b32342c38362c31322c31382c3230392c37342c3130332c33322c39322c35392c3138352c39362c38362c3135322c3131302c3231352c3131312c3233312c3131372c32392c3232332c39372c38302c3234312c342c34302c32342c3235312c3132322c38352c3232382c3132325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "eb7fb96683c6000c8b0c8787b4092c16b769788234032b46d219a3bcdc1c970c", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3130302c3133332c39372c34352c34342c3133372c38332c3233302c3230322c39382c3139382c3132322c372c3135312c3139372c3139322c35332c3134352c3231372c3138332c3231372c3136372c3231322c3230372c3232352c37362c36312c3136352c35352c3133302c39372c3232335d2c226279746573223a5b3130312c3130302c3130332c3130312c3131355d7d" + } + ], + "invocation": { + "bufferId": "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "basisHeadId": "df5fde5123923043ae3bb65e93847cd172a18b6c1b1e532e36bc552c634c0004", + "startByte": 0, + "endByte": 5, + "replacementUtf8Hex": "e78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cab0a" + }, + "terminal": { + "posture": "committable", + "footprint": { + "nodeReads": [ + "18560c12d14a67205c3bb96056986ed76fe7751ddf6150f1042818fb7a55e47a", + "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "df5fde5123923043ae3bb65e93847cd172a18b6c1b1e532e36bc552c634c0004", + "eb7fb96683c6000c8b0c8787b4092c16b769788234032b46d219a3bcdc1c970c" + ], + "nodeWrites": [ + "055bace365d4bc0f864c35716307ac08870ac97f83af88f6f2e7adbd05c3f3ae", + "07a9352f16e5e0864b30cba05c2399a172d7afdd298ad7bdb86c9cf269068112", + "100f589f0d9182b0c91c89f08864b8faf3f2a63f49a2bde4c447bd5548c946bc", + "5f01ecc74054ec3a4f446f8d29b1f4d1dd738780f9872c2b02f559269238624a", + "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "b535d8a661f942f2c1e34ac0f1f072f0a21ebe8a0b515f2ae71ff41a3da466b8", + "bc2dd50c36efe3ffa2c51672853051d48774c5fc8ec548ed42fd5091cd5280c5", + "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "f9954850fe25bb9fd0f23388728805a505b0d2edb24d64bec65a2d18a32b5818" + ], + "attachmentReads": [ + "18560c12d14a67205c3bb96056986ed76fe7751ddf6150f1042818fb7a55e47a", + "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "df5fde5123923043ae3bb65e93847cd172a18b6c1b1e532e36bc552c634c0004", + "eb7fb96683c6000c8b0c8787b4092c16b769788234032b46d219a3bcdc1c970c" + ], + "attachmentWrites": [ + "055bace365d4bc0f864c35716307ac08870ac97f83af88f6f2e7adbd05c3f3ae", + "07a9352f16e5e0864b30cba05c2399a172d7afdd298ad7bdb86c9cf269068112", + "100f589f0d9182b0c91c89f08864b8faf3f2a63f49a2bde4c447bd5548c946bc", + "5f01ecc74054ec3a4f446f8d29b1f4d1dd738780f9872c2b02f559269238624a", + "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "b535d8a661f942f2c1e34ac0f1f072f0a21ebe8a0b515f2ae71ff41a3da466b8", + "bc2dd50c36efe3ffa2c51672853051d48774c5fc8ec548ed42fd5091cd5280c5", + "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "f9954850fe25bb9fd0f23388728805a505b0d2edb24d64bec65a2d18a32b5818" + ], + "edgeReads": [], + "edgeWrites": [] + }, + "patch": [ + { + "kind": "upsert-node", + "nodeId": "055bace365d4bc0f864c35716307ac08870ac97f83af88f6f2e7adbd05c3f3ae", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e" + }, + { + "kind": "upsert-node", + "nodeId": "07a9352f16e5e0864b30cba05c2399a172d7afdd298ad7bdb86c9cf269068112", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9" + }, + { + "kind": "upsert-node", + "nodeId": "100f589f0d9182b0c91c89f08864b8faf3f2a63f49a2bde4c447bd5548c946bc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "upsert-node", + "nodeId": "5f01ecc74054ec3a4f446f8d29b1f4d1dd738780f9872c2b02f559269238624a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9" + }, + { + "kind": "upsert-node", + "nodeId": "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e" + }, + { + "kind": "upsert-node", + "nodeId": "b535d8a661f942f2c1e34ac0f1f072f0a21ebe8a0b515f2ae71ff41a3da466b8", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a" + }, + { + "kind": "upsert-node", + "nodeId": "bc2dd50c36efe3ffa2c51672853051d48774c5fc8ec548ed42fd5091cd5280c5", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd" + }, + { + "kind": "upsert-node", + "nodeId": "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1" + }, + { + "kind": "upsert-node", + "nodeId": "f9954850fe25bb9fd0f23388728805a505b0d2edb24d64bec65a2d18a32b5818", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4" + }, + { + "kind": "set-node-alpha", + "nodeId": "055bace365d4bc0f864c35716307ac08870ac97f83af88f6f2e7adbd05c3f3ae", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3130312c31332c3234322c3136322c3135392c3139312c38312c34372c3231332c3136322c3131312c35352c35312c3138392c37312c38352c36342c3138382c3134372c3233372c38352c36362c38302c38322c3130352c3136392c36342c3133342c362c36362c3232332c3231325d2c2262617369735f686561645f6964223a5b3232332c39352c3232322c38312c33352c3134362c34382c36372c3137342c35392c3138322c39342c3134372c3133322c3132342c3230392c3131342c3136312c3133392c3130382c32372c33302c38332c34362c35342c3138382c38352c34342c39392c37362c302c345d2c22726f6f745f6e6f64655f6964223a5b3139332c35312c3233302c3132362c3139322c33332c31312c3131382c3232382c34362c3132342c39392c3235302c33342c3137362c3233352c3235352c3231382c3232342c3135352c3139332c33392c35322c38342c3139322c35372c3230362c37322c3232382c33382c3134342c3134355d2c22627974655f6c656e677468223a343230312c2275746631365f6c656e677468223a313430312c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b3139332c35312c3233302c3132362c3139322c33332c31312c3131382c3232382c34362c3132342c39392c3235302c33342c3137362c3233352c3235352c3231382c3232342c3135352c3139332c33392c35322c38342c3139322c35372c3230362c37322c3232382c33382c3134342c3134355d2c2273657175656e6365223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "07a9352f16e5e0864b30cba05c2399a172d7afdd298ad7bdb86c9cf269068112", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37382c34332c38352c3132312c31332c392c34302c3234322c32352c37382c36322c3135372c34382c3230332c31352c37392c3230312c3137382c3232312c3234342c3133372c3138372c3130312c3234312c3232372c3134392c3135392c3136342c3130302c3134322c3234352c3232385d2c226279746573223a5b3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137315d7d" + }, + { + "kind": "set-node-alpha", + "nodeId": "100f589f0d9182b0c91c89f08864b8faf3f2a63f49a2bde4c447bd5548c946bc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b372c3136392c35332c34372c32322c3232392c3232342c3133342c37352c34382c3230332c3136302c39322c33352c3135332c3136312c3131342c3231352c3137352c3232312c34312c3133382c3231352c3138392c3138342c3130382c3135362c3234322c3130352c362c3132392c31385d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039352c2275746631365f6c656e677468223a313336352c226c696e655f627265616b73223a307d" + }, + { + "kind": "set-node-alpha", + "nodeId": "5f01ecc74054ec3a4f446f8d29b1f4d1dd738780f9872c2b02f559269238624a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3139352c3135362c3232342c3232352c3133322c3139332c3231362c3138392c3136332c37362c31312c39342c34392c3234372c3139392c3131362c3138362c38312c3232352c35352c37392c3130342c3133362c3231372c35352c38342c3230332c3139302c3232312c39392c3135342c32375d2c226279746573223a5b3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c3233312c3134302c3137312c31305d7d" + }, + { + "kind": "set-node-alpha", + "nodeId": "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a227265706c6163656d656e742d6c61726765722d7468616e2d6c656166222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b352c39312c3137322c3232372c3130312c3231322c3138382c31352c3133342c37362c35332c3131332c39392c372c3137322c382c3133352c31302c3230312c3132372c3133312c3137352c3133362c3234362c3234322c3233312c3137332c3138392c352c3139352c3234332c3137345d2c2276657273696f6e223a317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "b535d8a661f942f2c1e34ac0f1f072f0a21ebe8a0b515f2ae71ff41a3da466b8", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b3130312c31332c3234322c3136322c3135392c3139312c38312c34372c3231332c3136322c3131312c35352c35312c3138392c37312c38352c36342c3138382c3134372c3233372c38352c36362c38302c38322c3130352c3136392c36342c3133342c362c36362c3232332c3231325d2c2262617369735f686561645f6964223a5b3232332c39352c3232322c38312c33352c3134362c34382c36372c3137342c35392c3138322c39342c3134372c3133322c3132342c3230392c3131342c3136312c3133392c3130382c32372c33302c38332c34362c35342c3138382c38352c34342c39392c37362c302c345d2c226e6578745f686561645f6964223a5b352c39312c3137322c3232372c3130312c3231322c3138382c31352c3133342c37362c35332c3131332c39392c372c3137322c382c3133352c31302c3230312c3132372c3133312c3137352c3133362c3234362c3234322c3233312c3137332c3138392c352c3139352c3234332c3137345d2c2273746172745f62797465223a302c22656e645f62797465223a352c22696e7365727465645f627974655f6c656e677468223a343230317d" + }, + { + "kind": "set-node-alpha", + "nodeId": "bc2dd50c36efe3ffa2c51672853051d48774c5fc8ec548ed42fd5091cd5280c5", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b3138312c35332c3231362c3136362c39372c3234392c36362c3234322c3139332c3232372c37342c3139322c3234312c3234302c3131342c3234302c3136322c33302c3139302c3133382c31312c38312c39352c34322c3233312c33312c3234342c32362c36312c3136342c3130322c3138345d2c2262617369735f686561645f6964223a5b3232332c39352c3232322c38312c33352c3134362c34382c36372c3137342c35392c3138322c39342c3134372c3133322c3132342c3230392c3131342c3136312c3133392c3130382c32372c33302c38332c34362c35342c3138382c38352c34342c39392c37362c302c345d2c226e6578745f686561645f6964223a5b352c39312c3137322c3232372c3130312c3231322c3138382c31352c3133342c37362c35332c3131332c39392c372c3137322c382c3133352c31302c3230312c3132372c3133312c3137352c3133362c3234362c3234322c3233312c3137332c3138392c352c3139352c3234332c3137345d2c2273746172745f62797465223a302c22656e645f62797465223a352c22696e7365727465645f627974655f6c656e677468223a343230312c2264656c657465645f627974655f6c656e677468223a357d" + }, + { + "kind": "set-node-alpha", + "nodeId": "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b31362c31352c38382c3135392c31332c3134352c3133302c3137362c3230312c32382c3133372c3234302c3133362c3130302c3138342c3235302c3234332c3234322c3136362c36332c37332c3136322c3138392c3232382c3139362c37312c3138392c38352c37322c3230312c37302c3138385d2c227269676874223a5b3234392c3134392c37322c38302c3235342c33372c3138372c3135392c3230382c3234322c35312c3133362c3131342c3133362c352c3136352c352c3137362c3231302c3233372c3137382c37372c3130302c3139302c3139382c39302c34352c32342c3136332c34332c38382c32345d2c22627974655f6c656e677468223a343230312c2275746631365f6c656e677468223a313430312c226c696e655f627265616b73223a312c22686569676874223a327d" + }, + { + "kind": "set-node-alpha", + "nodeId": "f9954850fe25bb9fd0f23388728805a505b0d2edb24d64bec65a2d18a32b5818", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b39352c312c3233362c3139392c36342c38342c3233362c35382c37392c36382c3131312c3134312c34312c3137372c3234342c3230392c3232312c3131352c3133352c3132382c3234392c3133352c34342c34332c322c3234352c38392c33382c3134362c35362c39382c37345d2c22627974655f7374617274223a302c22627974655f6c656e677468223a3130362c2275746631365f6c656e677468223a33362c226c696e655f627265616b73223a317d" + } + ], + "createdNodeIds": [ + "055bace365d4bc0f864c35716307ac08870ac97f83af88f6f2e7adbd05c3f3ae", + "07a9352f16e5e0864b30cba05c2399a172d7afdd298ad7bdb86c9cf269068112", + "100f589f0d9182b0c91c89f08864b8faf3f2a63f49a2bde4c447bd5548c946bc", + "5f01ecc74054ec3a4f446f8d29b1f4d1dd738780f9872c2b02f559269238624a", + "b535d8a661f942f2c1e34ac0f1f072f0a21ebe8a0b515f2ae71ff41a3da466b8", + "bc2dd50c36efe3ffa2c51672853051d48774c5fc8ec548ed42fd5091cd5280c5", + "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "f9954850fe25bb9fd0f23388728805a505b0d2edb24d64bec65a2d18a32b5818" + ], + "updatedNodeIds": [ + "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4" + ], + "untouchedBasisNodeIds": [ + "18560c12d14a67205c3bb96056986ed76fe7751ddf6150f1042818fb7a55e47a", + "df5fde5123923043ae3bb65e93847cd172a18b6c1b1e532e36bc552c634c0004", + "eb7fb96683c6000c8b0c8787b4092c16b769788234032b46d219a3bcdc1c970c" + ], + "result": { + "bufferId": "650df2a29fbf512fd5a26f3733bd475540bc93ed5542505269a940860642dfd4", + "headId": "055bace365d4bc0f864c35716307ac08870ac97f83af88f6f2e7adbd05c3f3ae", + "rootNodeId": "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "rootDigest": "c133e67ec0210b76e42e7c63fa22b0ebffdae09bc1273454c039ce48e4269091", + "rewriteId": "b535d8a661f942f2c1e34ac0f1f072f0a21ebe8a0b515f2ae71ff41a3da466b8", + "diffId": "bc2dd50c36efe3ffa2c51672853051d48774c5fc8ec548ed42fd5091cd5280c5", + "byteLength": 4201, + "utf16Length": 1401, + "lineCount": 2, + "sequence": 1, + "version": 1, + "materializedTextUtf8Hex": "e78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cab0a" + } + } + }, + { + "id": "reversed-range", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "5c9778f04fffc5a5d49549952e2f9c4893a1785e39101f7d6431ef13679d305b", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3135362c3234372c3132302c3130332c3231362c3235302c36332c3139312c3135332c3232362c3135332c34302c37372c3130372c3230392c33312c38322c3134392c3130392c3230322c34392c3138382c39372c38332c3139392c3139312c3234372c37392c35362c38382c39392c39395d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "9cf77867d8fa3fbf99e299284d6bd11f52956dca31bc6153c7bff74f38586363", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a2272657665727365642d72616e6765222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b39322c3135312c3132302c3234302c37392c3235352c3139372c3136352c3231322c3134392c37332c3134392c34362c34372c3135362c37322c3134372c3136312c3132302c39342c35372c31362c33312c3132352c3130302c34392c3233392c31392c3130332c3135372c34382c39315d2c2276657273696f6e223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "9cf77867d8fa3fbf99e299284d6bd11f52956dca31bc6153c7bff74f38586363", + "basisHeadId": "5c9778f04fffc5a5d49549952e2f9c4893a1785e39101f7d6431ef13679d305b", + "startByte": 2, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "range-order-invalid", + "legacyErrorClass": "invalid-request", + "legacyMessage": "replace range 2..1 exceeds 3 bytes", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "out-of-bounds-range", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "1b0a708a2fe3cbf3f7032588504563c8ce1d21ca66a3ae2ee67e447a6938700a", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226f75742d6f662d626f756e64732d72616e6765222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b38302c32392c34392c3232322c3232392c35312c32382c39392c33362c3130342c3136312c3138312c35322c3135312c35372c3137392c31322c3235332c3135392c39362c36352c35352c38382c3136302c3139312c31372c3232312c3233322c32322c3133372c3233302c3234355d2c2276657273696f6e223a307d" + }, + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "501d31dee5331c632468a1b5349739b30cfd9f60413758a0bf11dde81689e6f5", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b32372c31302c3131322c3133382c34372c3232372c3230332c3234332c3234372c332c33372c3133362c38302c36392c39392c3230302c3230362c32392c33332c3230322c3130322c3136332c3137342c34362c3233302c3132362c36382c3132322c3130352c35362c3131322c31305d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "1b0a708a2fe3cbf3f7032588504563c8ce1d21ca66a3ae2ee67e447a6938700a", + "basisHeadId": "501d31dee5331c632468a1b5349739b30cfd9f60413758a0bf11dde81689e6f5", + "startByte": 0, + "endByte": 4, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "range-out-of-bounds", + "legacyErrorClass": "invalid-request", + "legacyMessage": "replace range 0..4 exceeds 3 bytes", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "start-inside-codepoint", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "4a19e71439b9a987507f00733b25f63abe5bc609eb5390a6908ea68b281298d4", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3133362c3130332c3231312c38362c3234362c34312c3135362c3130382c35302c3133332c3131382c3134362c39342c3136392c3137312c3138372c34362c3232342c36342c35362c3132362c3131382c3232392c3137312c302c3131332c3132302c3135382c3234392c3139372c3230362c3135375d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3235332c3233322c3232312c39382c34342c3231322c3133362c38372c3137302c38362c3137322c39372c3135342c31382c38322c3234362c38302c3134362c3231302c3137312c3232372c3136342c3131342c3131312c3130362c31352c3133302c3234372c332c3131382c3133382c3233365d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3235332c3233322c3232312c39382c34342c3231322c3133362c38372c3137302c38362c3137322c39372c3135342c31382c38322c3234362c38302c3134362c3231302c3137312c3232372c3136342c3131342c3131312c3130362c31352c3133302c3234372c332c3131382c3133382c3233365d2c2273657175656e6365223a307d" + }, + { + "nodeId": "8867d356f6299c6c328576925ea9abbb2ee040387e76e5ab0071789ef9c5ce9d", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a2273746172742d696e736964652d636f6465706f696e74222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b37342c32352c3233312c32302c35372c3138352c3136392c3133352c38302c3132372c302c3131352c35392c33372c3234362c35382c3139302c39312c3139382c392c3233352c38332c3134342c3136362c3134342c3134322c3136362c3133392c34302c31382c3135322c3231325d2c2276657273696f6e223a307d" + }, + { + "nodeId": "ae2fcaae9f471f0bfcc5e1206f1340911c8290e9da837677c42fef4e0c2ef816", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3131372c3235302c3135392c3136352c3136372c3130342c38392c38332c3139382c302c3135312c3137302c3137362c332c3235302c38322c3132362c33392c342c3131312c3138362c3137382c372c3235322c3139362c39362c37332c31302c3131322c3139362c3130372c3230355d2c226279746573223a5b36352c3234302c3135392c3135322c3132382c36365d7d" + }, + { + "nodeId": "fde8dd622cd48857aa56ac619a1252f65092d2abe3a4726f6a0f82f703768aec", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3137342c34372c3230322c3137342c3135392c37312c33312c31312c3235322c3139372c3232352c33322c3131312c31392c36342c3134352c32382c3133302c3134342c3233332c3231382c3133312c3131382c3131392c3139362c34372c3233392c37382c31322c34362c3234382c32325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "8867d356f6299c6c328576925ea9abbb2ee040387e76e5ab0071789ef9c5ce9d", + "basisHeadId": "4a19e71439b9a987507f00733b25f63abe5bc609eb5390a6908ea68b281298d4", + "startByte": 2, + "endByte": 5, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "utf8-boundary-invalid", + "legacyErrorClass": "invalid-request", + "legacyMessage": "replace offset splits a UTF-8 code point", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "end-inside-codepoint", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "0ae3aa9121f7ab1edaf9518539b19ab7012b14532c4632166eef4e1e8396f378", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22656e642d696e736964652d636f6465706f696e74222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3232392c3233382c3130312c34342c36342c34352c38332c3234372c3139392c3232332c3233382c3235352c31362c372c35312c3136392c3131302c36392c38362c3135392c3233362c372c3136342c3132302c32332c34382c3135312c35362c3131302c31382c3233312c33395d2c2276657273696f6e223a307d" + }, + { + "nodeId": "ae2fcaae9f471f0bfcc5e1206f1340911c8290e9da837677c42fef4e0c2ef816", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3131372c3235302c3135392c3136352c3136372c3130342c38392c38332c3139382c302c3135312c3137302c3137362c332c3235302c38322c3132362c33392c342c3131312c3138362c3137382c372c3235322c3139362c39362c37332c31302c3131322c3139362c3130372c3230355d2c226279746573223a5b36352c3234302c3135392c3135322c3132382c36365d7d" + }, + { + "nodeId": "e5ee652c402d53f7c7dfeeff100733a96e45569fec07a478173097386e12e727", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b31302c3232372c3137302c3134352c33332c3234372c3137312c33302c3231382c3234392c38312c3133332c35372c3137372c3135342c3138332c312c34332c32302c38332c34342c37302c35302c32322c3131302c3233392c37382c33302c3133312c3135302c3234332c3132305d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3235332c3233322c3232312c39382c34342c3231322c3133362c38372c3137302c38362c3137322c39372c3135342c31382c38322c3234362c38302c3134362c3231302c3137312c3232372c3136342c3131342c3131312c3130362c31352c3133302c3234372c332c3131382c3133382c3233365d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3235332c3233322c3232312c39382c34342c3231322c3133362c38372c3137302c38362c3137322c39372c3135342c31382c38322c3234362c38302c3134362c3231302c3137312c3232372c3136342c3131342c3131312c3130362c31352c3133302c3234372c332c3131382c3133382c3233365d2c2273657175656e6365223a307d" + }, + { + "nodeId": "fde8dd622cd48857aa56ac619a1252f65092d2abe3a4726f6a0f82f703768aec", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3137342c34372c3230322c3137342c3135392c37312c33312c31312c3235322c3139372c3232352c33322c3131312c31392c36342c3134352c32382c3133302c3134342c3233332c3231382c3133312c3131382c3131392c3139362c34372c3233392c37382c31322c34362c3234382c32325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "0ae3aa9121f7ab1edaf9518539b19ab7012b14532c4632166eef4e1e8396f378", + "basisHeadId": "e5ee652c402d53f7c7dfeeff100733a96e45569fec07a478173097386e12e727", + "startByte": 1, + "endByte": 4, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "utf8-boundary-invalid", + "legacyErrorClass": "invalid-request", + "legacyMessage": "replace offset splits a UTF-8 code point", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "empty-no-op", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "58f691d61290cc43def016c286a77a43cbdff4910e58516c14ebf4876c61026e", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3231332c33362c37382c3135342c3233372c3139332c3131302c3230322c38332c38322c3133352c3231342c33302c38322c33382c3233382c38352c3139332c3139372c3233342c39362c3230342c3139352c3231302c302c3130372c35342c32322c3131302c3138352c39392c34345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "d5244e9aedc16eca535287d61e5226ee55c1c5ea60ccc3d2006b36166eb9632c", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22656d7074792d6e6f2d6f70222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b38382c3234362c3134352c3231342c31382c3134342c3230342c36372c3232322c3234302c32322c3139342c3133342c3136372c3132322c36372c3230332c3232332c3234342c3134352c31342c38382c38312c3130382c32302c3233352c3234342c3133352c3130382c39372c322c3131305d2c2276657273696f6e223a307d" + } + ], + "invocation": { + "bufferId": "d5244e9aedc16eca535287d61e5226ee55c1c5ea60ccc3d2006b36166eb9632c", + "basisHeadId": "58f691d61290cc43def016c286a77a43cbdff4910e58516c14ebf4876c61026e", + "startByte": 1, + "endByte": 1, + "replacementUtf8Hex": "" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "no-op", + "legacyErrorClass": "invalid-request", + "legacyMessage": "replace range is a no-op", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "identical-no-op", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "4bca4c37d1c349e181939b1e3b9dac5d8077106806d1371ffb418e9ee07abe20", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132392c33332c36352c32392c3234382c3134332c392c32382c3233322c3134332c3130372c3137352c3132332c3130362c3235352c35392c39362c37372c372c39342c37392c3132312c3134392c3136332c37362c3136372c392c382c38342c34372c342c3132335d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "8121411df88f091ce88f6baf7b6aff3b604d075e4f7995a34ca70908542f047b", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226964656e746963616c2d6e6f2d6f70222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b37352c3230322c37362c35352c3230392c3139352c37332c3232352c3132392c3134372c3135352c33302c35392c3135372c3137322c39332c3132382c3131392c31362c3130342c362c3230392c35352c33312c3235312c36352c3134322c3135382c3232342c3132322c3139302c33325d2c2276657273696f6e223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "8121411df88f091ce88f6baf7b6aff3b604d075e4f7995a34ca70908542f047b", + "basisHeadId": "4bca4c37d1c349e181939b1e3b9dac5d8077106806d1371ffb418e9ee07abe20", + "startByte": 0, + "endByte": 3, + "replacementUtf8Hex": "616263" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "no-op", + "legacyErrorClass": "invalid-request", + "legacyMessage": "replace range is a no-op", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "noncanonical-basis", + "purpose": "exact canonical-head equality covers stale and foreign basis posture", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "5ca183a06f61a66776f028640546bad66376510b858f8d8935a876c3dec58301", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226e6f6e63616e6f6e6963616c2d6261736973222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3234342c3137352c34342c3232302c3138322c3231302c34322c3139322c35312c36322c3137312c3131362c38302c33372c37312c34382c32392c35352c35372c3235302c3234392c3138362c31352c3234362c3139392c35372c3132312c3231312c39332c3137382c3235312c3138325d2c2276657273696f6e223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "f4af2cdcb6d22ac0333eab74502547301d3739faf9ba0ff6c73979d35db2fbb6", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b39322c3136312c3133312c3136302c3131312c39372c3136362c3130332c3131382c3234302c34302c3130302c352c37302c3138362c3231342c39392c3131382c38312c31312c3133332c3134332c3134312c3133372c35332c3136382c3131382c3139352c3232322c3139372c3133312c315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + } + ], + "invocation": { + "bufferId": "5ca183a06f61a66776f028640546bad66376510b858f8d8935a876c3dec58301", + "basisHeadId": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "basis-not-canonical", + "legacyErrorClass": "invalid-request", + "legacyMessage": "stale replace basis a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5; canonical head is f4af2cdcb6d22ac0333eab74502547301d3739faf9ba0ff6c73979d35db2fbb6", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "head-sequence-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "28cf20c879207dc9af92107a1391fcf23f1aaeb3904f14464a7f197d81f80c5e", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3131342c36312c3231332c3233392c3139342c3231332c31392c3233382c38382c3136352c3136322c37352c37342c3231322c3233382c3135322c3130362c3230342c3132342c3134312c36352c3135332c3135362c33322c31362c3133362c3230372c37382c33312c38322c3132392c3131345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "612841a7b66c7c7f3e954bffe99bc096b6ea554d0fc5ca42ed2a85c36415fa36", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3131342c36312c3231332c3233392c3139342c3231332c31392c3233382c38382c3136352c3136322c37352c37342c3231322c3233382c3135322c3130362c3230342c3132342c3134312c36352c3135332c3135362c33322c31362c3133362c3230372c37382c33312c38322c3132392c3131345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a31383434363734343037333730393535313631357d" + }, + { + "nodeId": "723dd5efc2d513ee58a5a24b4ad4ee986acc7c8d41999c201088cf4e1f528172", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22686561642d73657175656e63652d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b39372c34302c36352c3136372c3138322c3130382c3132342c3132372c36322c3134392c37352c3235352c3233332c3135352c3139322c3135302c3138322c3233342c38352c37372c31352c3139372c3230322c36362c3233372c34322c3133332c3139352c3130302c32312c3235302c35345d2c2276657273696f6e223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "723dd5efc2d513ee58a5a24b4ad4ee986acc7c8d41999c201088cf4e1f528172", + "basisHeadId": "612841a7b66c7c7f3e954bffe99bc096b6ea554d0fc5ca42ed2a85c36415fa36", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "head sequence overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "buffer-version-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "033a23c64d304b2f3891322139a0e85b380db4614aaf14218e4343f2b7d8c74b", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132312c382c3235332c3230382c32342c3130312c3130382c39332c372c3132312c3130382c37312c3131392c342c3139312c34382c3232322c3233302c38342c32372c3234342c3230392c37312c3130362c3139332c3130312c3132372c3138392c3139382c34382c35372c35365d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "7908fdd018656c5d07796c477704bf30dee6541bf4d1476ac1657fbdc6303938", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226275666665722d76657273696f6e2d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b332c35382c33352c3139382c37372c34382c37352c34372c35362c3134352c35302c33332c35372c3136302c3233322c39312c35362c31332c3138302c39372c37342c3137352c32302c33332c3134322c36372c36372c3234322c3138332c3231362c3139392c37355d2c2276657273696f6e223a31383434363734343037333730393535313631357d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "7908fdd018656c5d07796c477704bf30dee6541bf4d1476ac1657fbdc6303938", + "basisHeadId": "033a23c64d304b2f3891322139a0e85b380db4614aaf14218e4343f2b7d8c74b", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "buffer version overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "missing-buffer", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [], + "invocation": { + "bufferId": "1111111111111111111111111111111111111111111111111111111111111111", + "basisHeadId": "2222222222222222222222222222222222222222222222222222222222222222", + "startByte": 0, + "endByte": 0, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "fact-missing", + "legacyErrorClass": "missing-fact", + "legacyMessage": "jedit.text.BufferWorldline.v1 at 1111111111111111111111111111111111111111111111111111111111111111", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "malformed-buffer", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "0f59b7794cb8d181317b2ac66564c34c7b246590a3082b3dd8b03869618b4260", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b34302c3230302c3230382c3234312c3136382c3137312c3234342c3130372c39312c38382c33332c3232342c3134372c3231352c38362c36332c3233332c31322c3232342c36362c3133322c3134332c34322c3231312c32312c33352c3234322c3230312c36352c31302c3232302c3130315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "28c8d0f1a8abf46b5b5821e093d7563fe90ce042848f2ad31523f2c9410adc65", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b" + }, + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "28c8d0f1a8abf46b5b5821e093d7563fe90ce042848f2ad31523f2c9410adc65", + "basisHeadId": "0f59b7794cb8d181317b2ac66564c34c7b246590a3082b3dd8b03869618b4260", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "fact-malformed", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "decode jedit.text.BufferWorldline.v1: EOF while parsing an object at line 1 column 1", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "bad-blob-content-hash", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c302c305d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "555827b7604632ae760278902bac577e66c4175e252c75b62b2978bbc8a3897d", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226261642d626c6f622d636f6e74656e742d68617368222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3231362c3135322c3133302c35342c3132362c3231372c3133312c3132362c3137382c3131322c372c3133322c3134382c38392c32352c31322c3138362c3230352c342c362c3133342c31342c3139372c3131392c32342c3230372c3234352c3135332c34302c3137302c3137362c34315d2c2276657273696f6e223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "d89882367ed9837eb27007849459190cbacd0406860ec57718cff59928aab029", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b38352c38382c33392c3138332c39362c37302c35302c3137342c3131382c322c3132302c3134342c34332c3137322c38372c3132362c3130322c3139362c32332c39342c33372c34342c3131372c3138322c34332c34312c3132302c3138372c3230302c3136332c3133372c3132355d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + } + ], + "invocation": { + "bufferId": "555827b7604632ae760278902bac577e66c4175e252c75b62b2978bbc8a3897d", + "basisHeadId": "d89882367ed9837eb27007849459190cbacd0406860ec57718cff59928aab029", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "content-identity-mismatch", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "blob 2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a content hash does not match", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "coordinate-above-graphql-int", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "67d0e0573b4bd007ce1033f2117d47183544cef7a538fbdfad84861ea05894ff", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3133342c3138312c3134392c34342c312c33302c38392c37312c3135372c3133352c33322c3230302c392c3138322c38352c3134382c3132372c3231312c3139352c32302c38322c38342c3230392c3230322c33362c33322c38382c35362c3133372c3136352c3132332c3234355d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a6e756c6c2c22627974655f6c656e677468223a302c2275746631365f6c656e677468223a302c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3138332c3135362c38392c3139352c36382c34332c34352c39302c34302c3136302c3235312c3139392c3235352c31382c3131372c3231362c3130382c36362c34332c3232382c3134372c3131322c3131342c3231372c3139312c302c38372c34332c34392c31332c3233322c38385d2c2273657175656e6365223a307d" + }, + { + "nodeId": "7acee21e29fab35cc0e92c06da65b2d27a5de5ce14f65cf577b5e631c7527a3d", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3133342c3138312c3134392c34342c312c33302c38392c37312c3135372c3133352c33322c3230302c392c3138322c38352c3134382c3132372c3231312c3139352c32302c38322c38342c3230392c3230322c33362c33322c38382c35362c3133372c3136352c3132332c3234355d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a6e756c6c2c22627974655f6c656e677468223a323134373438333634382c2275746631365f6c656e677468223a302c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3138332c3135362c38392c3139352c36382c34332c34352c39302c34302c3136302c3235312c3139392c3235352c31382c3131372c3231362c3130382c36362c34332c3232382c3134372c3131322c3131342c3231372c3139312c302c38372c34332c34392c31332c3233322c38385d2c2273657175656e6365223a307d" + }, + { + "nodeId": "86b5952c011e59479d8720c809b655947fd3c3145254d1ca2420583889a57bf5", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22636f6f7264696e6174652d61626f76652d6772617068716c2d696e74222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3132322c3230362c3232362c33302c34312c3235302c3137392c39322c3139322c3233332c34342c362c3231382c3130312c3137382c3231302c3132322c39332c3232392c3230362c32302c3234362c39322c3234352c3131392c3138312c3233302c34392c3139392c38322c3132322c36315d2c2276657273696f6e223a307d" + } + ], + "invocation": { + "bufferId": "86b5952c011e59479d8720c809b655947fd3c3145254d1ca2420583889a57bf5", + "basisHeadId": "7acee21e29fab35cc0e92c06da65b2d27a5de5ce14f65cf577b5e631c7527a3d", + "startByte": 2147483648, + "endByte": 2147483648, + "replacementUtf8Hex": "78" + }, + "terminal": { + "posture": "obstructed", + "semanticCode": "malformed-rope", + "legacyErrorClass": "invalid-request", + "legacyMessage": "split exceeds empty rope", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + } + ] +} diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 new file mode 100644 index 00000000..5ed7576c --- /dev/null +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -0,0 +1 @@ +ad04b5256ddb28b2e336376e43343da27b8c6ea3cb9649edbbf9807c39199ad7 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 2f7a4d83..d46d89be 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -282,7 +282,7 @@ evolution can proceed later under a new coordinate and explicit migration. - [x] Slice 1: land this decision record and freeze the executable witnesses. - [x] Slice 2: publish and byte-lock `jedit.text.schema@1`. -- [ ] Slice 3: publish and regenerate the `ReplaceRange` differential corpus. +- [x] Slice 3: publish and regenerate the `ReplaceRange` differential corpus. ## Tests To Write First diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs new file mode 100644 index 00000000..5cb29627 --- /dev/null +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -0,0 +1,276 @@ +#[path = "support/replace_range_oracle.rs"] +mod oracle_support; + +use std::fs; +use std::path::PathBuf; + +use oracle_support::{ + canonical_corpus_bytes, generate_corpus, BasisSetup, CaseSpec, ExpectedPosture, +}; +use sha2::{Digest, Sha256}; + +const UPDATE_ENV: &str = "JEDIT_UPDATE_REPLACE_RANGE_ORACLE"; +const EXPECTED_CORPUS_SHA256: &str = include_str!( + "../../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256" +); + +#[test] +fn replace_range_oracle_matches_the_committed_corpus() { + let first = canonical_corpus_bytes(&generate_corpus(cases())); + let second = canonical_corpus_bytes(&generate_corpus(cases())); + assert_eq!(first, second, "fresh oracle generation must be byte-stable"); + + let path = corpus_path(); + if std::env::var_os(UPDATE_ENV).is_some() { + fs::write(&path, &first).expect("oracle corpus should update"); + } + let committed = fs::read(&path).expect("committed oracle corpus should exist"); + assert_eq!( + first, committed, + "run with {UPDATE_ENV}=1 to update intentionally" + ); + assert_eq!( + hex::encode(Sha256::digest(&committed)), + EXPECTED_CORPUS_SHA256.trim_end(), + "oracle resource digest must be updated deliberately" + ); +} + +fn corpus_path() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json") +} + +fn cases() -> Vec { + vec![ + success("empty-unicode-insert", "", BasisSetup::Empty, 0, 0, "猫\n"), + success("middle-ascii-insert", "abcd", BasisSetup::Plain, 2, 2, "XY"), + success("whole-buffer-delete", "abc", BasisSetup::Plain, 0, 3, ""), + success( + "unicode-replacement", + "A😀é\nB", + BasisSetup::Plain, + 1, + 5, + "λ\n", + ), + success( + "cross-leaf-rebalance", + &format!( + "{}{}{}", + "a".repeat(4096), + "b".repeat(4096), + "c".repeat(4096) + ), + BasisSetup::Plain, + 4094, + 8194, + "β\nZ", + ), + success( + "replacement-larger-than-leaf", + "edges", + BasisSetup::Plain, + 0, + 5, + &format!("{}\n", "猫".repeat(1400)), + ), + obstruction( + "reversed-range", + "abc", + BasisSetup::Plain, + 2, + 1, + "x", + "range-order-invalid", + "invalid-request", + "exceeds", + ), + obstruction( + "out-of-bounds-range", + "abc", + BasisSetup::Plain, + 0, + 4, + "x", + "range-out-of-bounds", + "invalid-request", + "exceeds", + ), + obstruction( + "start-inside-codepoint", + "A😀B", + BasisSetup::Plain, + 2, + 5, + "x", + "utf8-boundary-invalid", + "invalid-request", + "splits a UTF-8 code point", + ), + obstruction( + "end-inside-codepoint", + "A😀B", + BasisSetup::Plain, + 1, + 4, + "x", + "utf8-boundary-invalid", + "invalid-request", + "splits a UTF-8 code point", + ), + obstruction( + "empty-no-op", + "abc", + BasisSetup::Plain, + 1, + 1, + "", + "no-op", + "invalid-request", + "no-op", + ), + obstruction( + "identical-no-op", + "abc", + BasisSetup::Plain, + 0, + 3, + "abc", + "no-op", + "invalid-request", + "no-op", + ), + CaseSpec { + id: "noncanonical-basis", + purpose: "exact canonical-head equality covers stale and foreign basis posture", + initial_text: "abc".to_owned(), + setup: BasisSetup::Plain, + basis_override: Some([0xA5; 32]), + start_byte: 0, + end_byte: 1, + replacement: "x".to_owned(), + expected: ExpectedPosture::Obstruction { + semantic_code: "basis-not-canonical", + error_class: "invalid-request", + message_fragment: "stale replace basis", + }, + }, + obstruction( + "head-sequence-overflow", + "abc", + BasisSetup::SequenceOverflow, + 0, + 1, + "x", + "arithmetic-overflow", + "malformed-fact", + "head sequence overflow", + ), + obstruction( + "buffer-version-overflow", + "abc", + BasisSetup::VersionOverflow, + 0, + 1, + "x", + "arithmetic-overflow", + "malformed-fact", + "buffer version overflow", + ), + obstruction( + "missing-buffer", + "", + BasisSetup::MissingBuffer, + 0, + 0, + "x", + "fact-missing", + "missing-fact", + "BufferWorldline", + ), + obstruction( + "malformed-buffer", + "abc", + BasisSetup::MalformedBuffer, + 0, + 1, + "x", + "fact-malformed", + "malformed-fact", + "decode", + ), + obstruction( + "bad-blob-content-hash", + "abc", + BasisSetup::BadBlobContentHash, + 0, + 1, + "x", + "content-identity-mismatch", + "malformed-fact", + "content hash does not match", + ), + obstruction( + "coordinate-above-graphql-int", + "", + BasisSetup::AboveGraphqlIntRange, + i32::MAX as u64 + 1, + i32::MAX as u64 + 1, + "x", + "malformed-rope", + "invalid-request", + "split exceeds empty rope", + ), + ] +} + +fn success( + id: &'static str, + initial_text: &str, + setup: BasisSetup, + start_byte: u64, + end_byte: u64, + replacement: &str, +) -> CaseSpec { + CaseSpec { + id, + purpose: "finite persistent-rope ReplaceRange conformance witness", + initial_text: initial_text.to_owned(), + setup, + basis_override: None, + start_byte, + end_byte, + replacement: replacement.to_owned(), + expected: ExpectedPosture::Success, + } +} + +#[allow(clippy::too_many_arguments)] +fn obstruction( + id: &'static str, + initial_text: &str, + setup: BasisSetup, + start_byte: u64, + end_byte: u64, + replacement: &str, + semantic_code: &'static str, + error_class: &'static str, + message_fragment: &'static str, +) -> CaseSpec { + CaseSpec { + id, + purpose: "typed no-plan ReplaceRange obstruction witness", + initial_text: initial_text.to_owned(), + setup, + basis_override: None, + start_byte, + end_byte, + replacement: replacement.to_owned(), + expected: ExpectedPosture::Obstruction { + semantic_code, + error_class, + message_fragment, + }, + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_basis.rs b/native/jedit-echo-host/tests/support/replace_range_basis.rs new file mode 100644 index 00000000..a6127570 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_basis.rs @@ -0,0 +1,173 @@ +use jedit_echo_host::records::{ + decode_fact, fact_bytes, fact_id, fact_type_id, BlobFact, BufferFact, HeadFact, LeafFact, + NodeIdBytes, +}; +use jedit_echo_host::rope::plan_create; +use warp_core::{AttachmentValue, GraphStore, NodeId, NodeRecord, TickDelta, WarpOp}; + +#[derive(Clone, Copy)] +pub enum BasisSetup { + Plain, + Empty, + SequenceOverflow, + VersionOverflow, + MissingBuffer, + MalformedBuffer, + BadBlobContentHash, + AboveGraphqlIntRange, +} + +pub fn make_basis( + warp_id: warp_core::WarpId, + id: &str, + initial_text: &str, + setup: BasisSetup, +) -> (GraphStore, NodeId, NodeId) { + if matches!(setup, BasisSetup::MissingBuffer) { + return ( + GraphStore::new(warp_id), + NodeId([0x11; 32]), + NodeId([0x22; 32]), + ); + } + let basis_text = if matches!(setup, BasisSetup::Empty) { + "" + } else { + initial_text + }; + let mut store = GraphStore::new(warp_id); + let create = plan_create(&store, id, basis_text, None).expect("oracle basis should be created"); + let mut delta = TickDelta::new(); + create.emit(&mut delta); + apply_ops(&mut store, &delta.finalize()); + let mut head_id = create.head_id; + match setup { + BasisSetup::Plain | BasisSetup::Empty => {} + BasisSetup::SequenceOverflow => { + head_id = replace_head(&mut store, create.buffer_id, head_id, |head| { + head.sequence = u64::MAX; + }); + } + BasisSetup::VersionOverflow => { + replace_buffer(&mut store, create.buffer_id, |buffer| { + buffer.version = u64::MAX; + }); + } + BasisSetup::MalformedBuffer => { + store.set_node_attachment( + create.buffer_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + bytes::Bytes::from_static(b"{"), + ))), + ); + } + BasisSetup::BadBlobContentHash => corrupt_blob_hash(&mut store, head_id), + BasisSetup::AboveGraphqlIntRange => { + head_id = replace_head(&mut store, create.buffer_id, head_id, |head| { + head.byte_length = i32::MAX as u64 + 1; + }); + } + BasisSetup::MissingBuffer => unreachable!(), + } + (store, create.buffer_id, head_id) +} + +pub fn apply_ops(store: &mut GraphStore, ops: &[WarpOp]) { + for op in ops { + match op { + WarpOp::UpsertNode { node, record } => { + assert_eq!(node.warp_id, store.warp_id()); + store.insert_node(node.local_id, record.clone()); + } + WarpOp::SetAttachment { key, value } => { + let warp_core::AttachmentOwner::Node(node) = key.owner else { + panic!("oracle patch must not contain edge attachments") + }; + store.set_node_attachment(node.local_id, value.clone()); + } + _ => panic!("oracle patch contains an unsupported graph operation"), + } + } +} + +fn replace_head( + store: &mut GraphStore, + buffer_id: NodeId, + head_id: NodeId, + mutate: impl FnOnce(&mut HeadFact), +) -> NodeId { + let mut head: HeadFact = decode_fact( + store + .node_attachment(&head_id) + .expect("head attachment should exist"), + ) + .expect("head should decode"); + mutate(&mut head); + let next_head_id = fact_id(&head).expect("mutated head should identify"); + store.insert_node( + next_head_id, + NodeRecord { + ty: fact_type_id::(), + }, + ); + store.set_node_attachment( + next_head_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&head).expect("head should encode").into(), + ))), + ); + replace_buffer(store, buffer_id, |buffer| { + buffer.canonical_head_id = NodeIdBytes::from(next_head_id); + }); + next_head_id +} + +fn replace_buffer(store: &mut GraphStore, buffer_id: NodeId, mutate: impl FnOnce(&mut BufferFact)) { + let mut buffer: BufferFact = decode_fact( + store + .node_attachment(&buffer_id) + .expect("buffer attachment should exist"), + ) + .expect("buffer should decode"); + mutate(&mut buffer); + store.set_node_attachment( + buffer_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&buffer).expect("buffer should encode").into(), + ))), + ); +} + +fn corrupt_blob_hash(store: &mut GraphStore, head_id: NodeId) { + let head: HeadFact = decode_fact( + store + .node_attachment(&head_id) + .expect("head attachment should exist"), + ) + .expect("head should decode"); + let root_id = NodeId::from(head.root_node_id.expect("fixture should have a leaf root")); + let leaf: LeafFact = decode_fact( + store + .node_attachment(&root_id) + .expect("leaf attachment should exist"), + ) + .expect("leaf should decode"); + let blob_id = NodeId::from(leaf.blob_id); + let mut blob: BlobFact = decode_fact( + store + .node_attachment(&blob_id) + .expect("blob attachment should exist"), + ) + .expect("blob should decode"); + blob.content_hash = [0; 32]; + store.set_node_attachment( + blob_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&blob).expect("blob should encode").into(), + ))), + ); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs new file mode 100644 index 00000000..c285caeb --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -0,0 +1,420 @@ +use std::collections::BTreeSet; + +use jedit_echo_host::error::HostError; +use jedit_echo_host::records::{ + decode_fact, fact_type_id, BufferFact, DiffFact, HeadFact, RewriteFact, +}; +use jedit_echo_host::rope::{plan_replace, read_window, MutationPlan}; +use serde::Serialize; +use warp_core::{ + make_warp_id, AttachmentOwner, AttachmentPlane, AttachmentValue, Footprint, GraphStore, NodeId, + TickDelta, TypeId, WarpOp, +}; + +#[path = "replace_range_basis.rs"] +mod basis; + +pub use basis::BasisSetup; +use basis::{apply_ops, make_basis}; + +const ORACLE_WARP_LABEL: &str = "jedit.replace-range.oracle.v1"; +const SOURCE_COMMIT: &str = "c70e12d73b4b00bc92412bab67e1761f7dd22f82"; + +#[derive(Clone, Copy)] +pub enum ExpectedPosture { + Success, + Obstruction { + semantic_code: &'static str, + error_class: &'static str, + message_fragment: &'static str, + }, +} + +pub struct CaseSpec { + pub id: &'static str, + pub purpose: &'static str, + pub initial_text: String, + pub setup: BasisSetup, + pub basis_override: Option<[u8; 32]>, + pub start_byte: u64, + pub end_byte: u64, + pub replacement: String, + pub expected: ExpectedPosture, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct OracleCorpus { + schema_version: u32, + coordinate: &'static str, + application_schema_coordinate: &'static str, + source_commit: &'static str, + evidence_grade: &'static str, + independence_limit: &'static str, + warp_id: String, + cases: Vec, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct OracleCase { + id: &'static str, + purpose: &'static str, + basis_facts: Vec, + invocation: InvocationProjection, + terminal: TerminalProjection, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +struct FactProjection { + node_id: String, + type_id: String, + attachment_bytes_hex: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct InvocationProjection { + buffer_id: String, + basis_head_id: String, + start_byte: u64, + end_byte: u64, + replacement_utf8_hex: String, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde( + tag = "posture", + rename_all = "kebab-case", + rename_all_fields = "camelCase" +)] +enum TerminalProjection { + Committable { + footprint: FootprintProjection, + patch: Vec, + created_node_ids: Vec, + updated_node_ids: Vec, + untouched_basis_node_ids: Vec, + result: Box, + }, + Obstructed { + semantic_code: &'static str, + legacy_error_class: &'static str, + legacy_message: String, + parent_graph_unchanged: bool, + patch_posture: &'static str, + }, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +struct FootprintProjection { + node_reads: Vec, + node_writes: Vec, + attachment_reads: Vec, + attachment_writes: Vec, + edge_reads: Vec, + edge_writes: Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde( + tag = "kind", + rename_all = "kebab-case", + rename_all_fields = "camelCase" +)] +enum PatchOpProjection { + UpsertNode { + node_id: String, + type_id: String, + }, + SetNodeAlpha { + node_id: String, + type_id: String, + attachment_bytes_hex: String, + }, +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +struct ResultProjection { + buffer_id: String, + head_id: String, + root_node_id: Option, + root_digest: String, + rewrite_id: String, + diff_id: String, + byte_length: u64, + utf16_length: u64, + line_count: u64, + sequence: u64, + version: u64, + materialized_text_utf8_hex: String, +} + +pub fn generate_corpus(specs: Vec) -> OracleCorpus { + let warp_id = make_warp_id(ORACLE_WARP_LABEL); + let cases = specs + .into_iter() + .map(|spec| evaluate_case(warp_id, spec)) + .collect(); + OracleCorpus { + schema_version: 1, + coordinate: "jedit.text.ReplaceRange.oracle@1", + application_schema_coordinate: "jedit.text.schema@1", + source_commit: SOURCE_COMMIT, + evidence_grade: "deterministic-self-validation", + independence_limit: "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", + warp_id: hex::encode(warp_id.as_bytes()), + cases, + } +} + +pub fn canonical_corpus_bytes(corpus: &OracleCorpus) -> Vec { + let mut bytes = serde_json::to_vec_pretty(corpus).expect("oracle corpus should encode"); + bytes.push(b'\n'); + bytes +} + +fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { + let (store, buffer_id, canonical_head_id) = + make_basis(warp_id, spec.id, &spec.initial_text, spec.setup); + let basis_facts = project_store(&store); + let basis_head_id = spec.basis_override.map_or(canonical_head_id, NodeId); + let invocation = InvocationProjection { + buffer_id: hex::encode(buffer_id.as_bytes()), + basis_head_id: hex::encode(basis_head_id.as_bytes()), + start_byte: spec.start_byte, + end_byte: spec.end_byte, + replacement_utf8_hex: hex::encode(spec.replacement.as_bytes()), + }; + let before = project_store(&store); + let first = plan_replace( + &store, + buffer_id, + basis_head_id, + spec.start_byte, + spec.end_byte, + &spec.replacement, + ); + let after_evaluation = project_store(&store); + assert_eq!(before, after_evaluation, "{} mutated its basis", spec.id); + let terminal = match (first, spec.expected) { + (Ok(plan), ExpectedPosture::Success) => success_projection(&store, &plan, &spec), + ( + Err(error), + ExpectedPosture::Obstruction { + semantic_code, + error_class, + message_fragment, + }, + ) => { + let (actual_class, actual_message) = error_projection(&error); + assert_eq!(actual_class, error_class, "{} error class", spec.id); + assert!( + actual_message.contains(message_fragment), + "{} error message {actual_message:?} did not contain {message_fragment:?}", + spec.id + ); + TerminalProjection::Obstructed { + semantic_code, + legacy_error_class: error_class, + legacy_message: actual_message, + parent_graph_unchanged: before == project_store(&store), + patch_posture: "no-mutation-plan", + } + } + (Ok(_), ExpectedPosture::Obstruction { .. }) => { + panic!("{} unexpectedly produced a mutation plan", spec.id) + } + (Err(error), ExpectedPosture::Success) => { + panic!("{} unexpectedly obstructed: {error}", spec.id) + } + }; + OracleCase { + id: spec.id, + purpose: spec.purpose, + basis_facts, + invocation, + terminal, + } +} + +fn success_projection( + store: &GraphStore, + plan: &MutationPlan, + spec: &CaseSpec, +) -> TerminalProjection { + let buffer: BufferFact = decode_fact( + store + .node_attachment(&plan.buffer_id) + .expect("buffer attachment should exist"), + ) + .expect("buffer should decode"); + let second = plan_replace( + store, + plan.buffer_id, + NodeId::from(buffer.canonical_head_id), + spec.start_byte, + spec.end_byte, + &spec.replacement, + ) + .expect("deterministic rerun should plan"); + let first_plan = project_plan(plan); + let second_plan = project_plan(&second); + assert_eq!(first_plan, second_plan, "{} rerun drifted", spec.id); + + let basis_ids: BTreeSet<_> = store + .iter_nodes() + .map(|(id, _)| hex::encode(id.as_bytes())) + .collect(); + let mut next = store.clone(); + apply_ops(&mut next, &first_plan.1); + let result_head: HeadFact = decode_fact( + next.node_attachment(&plan.head_id) + .expect("result head attachment should exist"), + ) + .expect("result head should decode"); + let window = read_window( + &next, + plan.buffer_id, + plan.head_id, + 0, + plan.byte_length, + plan.byte_length, + ) + .expect("result should materialize"); + let write_ids: BTreeSet<_> = first_plan.0.node_writes.iter().cloned().collect(); + let created_node_ids = write_ids.difference(&basis_ids).cloned().collect(); + let updated_node_ids = write_ids.intersection(&basis_ids).cloned().collect(); + let untouched_basis_node_ids = basis_ids.difference(&write_ids).cloned().collect(); + TerminalProjection::Committable { + footprint: first_plan.0, + patch: first_plan.1.iter().map(project_op).collect::>(), + created_node_ids, + updated_node_ids, + untouched_basis_node_ids, + result: Box::new(ResultProjection { + buffer_id: hex::encode(plan.buffer_id.as_bytes()), + head_id: hex::encode(plan.head_id.as_bytes()), + root_node_id: result_head.root_node_id.map(|id| hex::encode(id.0)), + root_digest: hex::encode(result_head.root_digest), + rewrite_id: node_with_type(&next, fact_type_id::()), + diff_id: node_with_type(&next, fact_type_id::()), + byte_length: plan.byte_length, + utf16_length: result_head.utf16_length, + line_count: plan.line_count, + sequence: result_head.sequence, + version: plan.version, + materialized_text_utf8_hex: hex::encode(window.text.as_bytes()), + }), + } +} + +fn node_with_type(store: &GraphStore, type_id: TypeId) -> String { + let matches: Vec<_> = store + .iter_nodes() + .filter_map(|(node_id, record)| (record.ty == type_id).then_some(node_id)) + .collect(); + let [node_id] = matches.as_slice() else { + panic!( + "expected exactly one fact of type {}", + hex::encode(type_id.as_bytes()) + ) + }; + hex::encode(node_id.as_bytes()) +} + +fn project_plan(plan: &MutationPlan) -> (FootprintProjection, Vec) { + let mut footprint = Footprint::default(); + plan.extend_footprint(&mut footprint); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + (project_footprint(&footprint), delta.finalize()) +} + +fn project_footprint(footprint: &Footprint) -> FootprintProjection { + FootprintProjection { + node_reads: footprint + .n_read + .iter() + .map(|key| hex::encode(key.local_id.as_bytes())) + .collect(), + node_writes: footprint + .n_write + .iter() + .map(|key| hex::encode(key.local_id.as_bytes())) + .collect(), + attachment_reads: footprint.a_read.iter().map(attachment_node_hex).collect(), + attachment_writes: footprint.a_write.iter().map(attachment_node_hex).collect(), + edge_reads: footprint + .e_read + .iter() + .map(|key| hex::encode(key.local_id.as_bytes())) + .collect(), + edge_writes: footprint + .e_write + .iter() + .map(|key| hex::encode(key.local_id.as_bytes())) + .collect(), + } +} + +fn attachment_node_hex(key: &warp_core::AttachmentKey) -> String { + assert_eq!(key.plane, AttachmentPlane::Alpha); + let AttachmentOwner::Node(node) = key.owner else { + panic!("ReplaceRange footprint must not contain edge attachments") + }; + hex::encode(node.local_id.as_bytes()) +} + +fn project_store(store: &GraphStore) -> Vec { + store + .iter_nodes() + .map(|(node_id, record)| { + let AttachmentValue::Atom(atom) = store + .node_attachment(node_id) + .expect("every oracle fact should have an atom attachment") + else { + panic!("oracle facts must not use descended attachments") + }; + assert_eq!(record.ty, atom.type_id); + FactProjection { + node_id: hex::encode(node_id.as_bytes()), + type_id: hex::encode(record.ty.as_bytes()), + attachment_bytes_hex: hex::encode(&atom.bytes), + } + }) + .collect() +} + +fn project_op(op: &WarpOp) -> PatchOpProjection { + match op { + WarpOp::UpsertNode { node, record } => PatchOpProjection::UpsertNode { + node_id: hex::encode(node.local_id.as_bytes()), + type_id: hex::encode(record.ty.as_bytes()), + }, + WarpOp::SetAttachment { key, value } => { + let node_id = attachment_node_hex(key); + let Some(AttachmentValue::Atom(atom)) = value else { + panic!("ReplaceRange must set atom attachments") + }; + PatchOpProjection::SetNodeAlpha { + node_id, + type_id: hex::encode(atom.type_id.as_bytes()), + attachment_bytes_hex: hex::encode(&atom.bytes), + } + } + _ => panic!("ReplaceRange emitted an unsupported graph operation"), + } +} + +fn error_projection(error: &HostError) -> (&'static str, String) { + match error { + HostError::MissingFact(message) => ("missing-fact", message.clone()), + HostError::MalformedFact(message) => ("malformed-fact", message.clone()), + HostError::InvalidRequest(message) => ("invalid-request", message.clone()), + other => panic!("oracle received unexpected host error: {other}"), + } +} From 0d73aeda3cab16d5d932fdb85cc5d842753377d8 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 16:38:58 -0700 Subject: [PATCH 04/63] fix: close ReplaceRange law review gaps --- .../replace-range-v1/codec-vectors-v1.json | 378 +++++++++--------- .../replace-range-v1/codec-vectors-v1.sha256 | 2 +- .../replace-range-v1.oracle.json | 40 +- .../replace-range-v1.oracle.sha256 | 2 +- .../replace-range-v1/text-schema-v1.json | 55 ++- .../replace-range-v1/text-schema-v1.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 237 +++++++++-- .../tests/replace_range_oracle.rs | 20 +- .../tests/replace_range_schema.rs | 252 ++---------- .../tests/replace_range_schema_conformance.rs | 328 +++++++++++++++ .../tests/support/replace_range_oracle.rs | 18 +- .../tests/support/replace_range_schema.rs | 252 ++++++++++++ .../tests/support/replace_range_source_set.rs | 74 ++++ .../tests/support/resource_fixture.rs | 53 +++ package.json | 1 + 15 files changed, 1261 insertions(+), 453 deletions(-) create mode 100644 native/jedit-echo-host/tests/replace_range_schema_conformance.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_schema.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_source_set.rs create mode 100644 native/jedit-echo-host/tests/support/resource_fixture.rs diff --git a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json index 17473c2e..da73e4fc 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json +++ b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json @@ -8,49 +8,49 @@ "typeIdHex": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", "identity": { "kind": "keyed", - "bufferKey": "schema\n\"猫\\path.txt", - "nodeIdHex": "1d9b601e15c0e27ba93de3943427732d91ef5fba75e138a88b799a15694b144b" + "bufferKey": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\"\\/猫

", + "nodeIdHex": "61bafe2a221bcba3367f69a64063e00d0cd7293aae29d7384dec1190f65f2dc7" }, "value": { - "buffer_key": "schema\n\"猫\\path.txt", + "buffer_key": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\"\\/猫

", "canonical_head_id": [ - 76, - 178, - 60, - 219, - 191, - 119, - 177, - 237, - 26, - 9, - 114, + 153, + 40, + 12, + 140, + 6, + 137, + 1, + 46, + 230, + 232, + 107, + 235, + 142, + 50, 148, - 120, - 237, - 246, - 209, - 215, - 241, - 23, - 15, + 116, + 211, + 59, + 118, + 249, + 32, + 80, + 27, + 121, + 12, + 190, + 199, 54, - 128, - 175, - 203, - 44, - 181, - 251, - 183, - 107, - 149, - 41, - 200 + 122, + 80, + 247, + 132 ], "projection_path": null, "version": 2147483648 }, - "canonicalBytesHex": "7b226275666665725f6b6579223a22736368656d615c6e5c22e78cab5c5c706174682e747874222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b37362c3137382c36302c3231392c3139312c3131392c3137372c3233372c32362c392c3131342c3134382c3132302c3233372c3234362c3230392c3231352c3234312c32332c31352c35342c3132382c3137352c3230332c34342c3138312c3235312c3138332c3130372c3134392c34312c3230305d2c2276657273696f6e223a323134373438333634387d" + "canonicalBytesHex": "7b226275666665725f6b6579223a225c75303030305c75303030315c75303030325c75303030335c75303030345c75303030355c75303030365c75303030375c625c745c6e5c75303030625c665c725c75303030655c75303030665c75303031305c75303031315c75303031325c75303031335c75303031345c75303031355c75303031365c75303031375c75303031385c75303031395c75303031615c75303031625c75303031635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e280a9222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3135332c34302c31322c3134302c362c3133372c312c34362c3233302c3233322c3130372c3233352c3134322c35302c3134382c3131362c3231312c35392c3131382c3234392c33322c38302c32372c3132312c31322c3139302c3139392c35342c3132322c38302c3234372c3133325d2c2276657273696f6e223a323134373438333634387d" }, { "fact": "Blob", @@ -250,7 +250,7 @@ "identity": { "kind": "content-addressed", "domainHex": "6a656469742e746578742e686561642e763100", - "nodeIdHex": "4cb23cdbbf77b1ed1a09729478edf6d1d7f1170f3680afcb2cb5fbb76b9529c8" + "nodeIdHex": "99280c8c0689012ee6e86beb8e329474d33b76f920501b790cbec7367a50f784" }, "value": { "basis_head_id": [ @@ -288,38 +288,38 @@ 51 ], "buffer_id": [ - 29, - 155, - 96, - 30, - 21, - 192, - 226, - 123, - 169, - 61, - 227, - 148, - 52, - 39, - 115, - 45, - 145, - 239, - 95, + 97, 186, - 117, - 225, - 56, - 168, - 139, - 121, - 154, - 21, + 254, + 42, + 34, + 27, + 203, + 163, + 54, + 127, 105, - 75, - 20, - 75 + 166, + 64, + 99, + 224, + 13, + 12, + 215, + 41, + 58, + 174, + 41, + 215, + 56, + 77, + 236, + 17, + 144, + 246, + 95, + 45, + 199 ], "byte_length": 6, "line_count": 2, @@ -394,7 +394,7 @@ "sequence": 2147483648, "utf16_length": 4 }, - "canonicalBytesHex": "7b226275666665725f6964223a5b32392c3135352c39362c33302c32312c3139322c3232362c3132332c3136392c36312c3232372c3134382c35322c33392c3131352c34352c3134352c3233392c39352c3138362c3131372c3232352c35362c3136382c3133392c3132312c3135342c32312c3130352c37352c32302c37355d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c22726f6f745f6e6f64655f6964223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c2273657175656e6365223a323134373438333634387d" + "canonicalBytesHex": "7b226275666665725f6964223a5b39372c3138362c3235342c34322c33342c32372c3230332c3136332c35342c3132372c3130352c3136362c36342c39392c3232342c31332c31322c3231352c34312c35382c3137342c34312c3231352c35362c37372c3233362c31372c3134342c3234362c39352c34352c3139395d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c22726f6f745f6e6f64655f6964223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c2273657175656e6365223a323134373438333634387d" }, { "fact": "Rewrite", @@ -403,7 +403,7 @@ "identity": { "kind": "content-addressed", "domainHex": "6a656469742e746578742e726577726974652e763100", - "nodeIdHex": "0a36f333e6a19081f1373716d32287ce528e7e75fa4c7dd3b56ff44d9f0f5a44" + "nodeIdHex": "3722e6da72e3a3bed6d89963ad65194d3f6bf6e491c670d9ab474b7a9b5c2a31" }, "value": { "basis_head_id": [ @@ -441,78 +441,78 @@ 51 ], "buffer_id": [ - 29, - 155, - 96, - 30, - 21, - 192, - 226, - 123, - 169, - 61, - 227, - 148, - 52, - 39, - 115, - 45, - 145, - 239, - 95, + 97, 186, - 117, - 225, - 56, - 168, - 139, - 121, - 154, - 21, + 254, + 42, + 34, + 27, + 203, + 163, + 54, + 127, 105, - 75, - 20, - 75 + 166, + 64, + 99, + 224, + 13, + 12, + 215, + 41, + 58, + 174, + 41, + 215, + 56, + 77, + 236, + 17, + 144, + 246, + 95, + 45, + 199 ], "end_byte": 2147483652, "inserted_byte_length": 6, "next_head_id": [ - 76, - 178, - 60, - 219, - 191, - 119, - 177, - 237, - 26, - 9, - 114, + 153, + 40, + 12, + 140, + 6, + 137, + 1, + 46, + 230, + 232, + 107, + 235, + 142, + 50, 148, - 120, - 237, - 246, - 209, - 215, - 241, - 23, - 15, + 116, + 211, + 59, + 118, + 249, + 32, + 80, + 27, + 121, + 12, + 190, + 199, 54, - 128, - 175, - 203, - 44, - 181, - 251, - 183, - 107, - 149, - 41, - 200 + 122, + 80, + 247, + 132 ], "start_byte": 2147483648 }, - "canonicalBytesHex": "7b226275666665725f6964223a5b32392c3135352c39362c33302c32312c3139322c3232362c3132332c3136392c36312c3232372c3134382c35322c33392c3131352c34352c3134352c3233392c39352c3138362c3131372c3232352c35362c3136382c3133392c3132312c3135342c32312c3130352c37352c32302c37355d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b37362c3137382c36302c3231392c3139312c3131392c3137372c3233372c32362c392c3131342c3134382c3132302c3233372c3234362c3230392c3231352c3234312c32332c31352c35342c3132382c3137352c3230332c34342c3138312c3235312c3138332c3130372c3134392c34312c3230305d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a367d" + "canonicalBytesHex": "7b226275666665725f6964223a5b39372c3138362c3235342c34322c33342c32372c3230332c3136332c35342c3132372c3130352c3136362c36342c39392c3232342c31332c31322c3231352c34312c35382c3137342c34312c3231352c35362c37372c3233362c31372c3134342c3234362c39352c34352c3139395d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b3135332c34302c31322c3134302c362c3133372c312c34362c3233302c3233322c3130372c3233352c3134322c35302c3134382c3131362c3231312c35392c3131382c3234392c33322c38302c32372c3132312c31322c3139302c3139392c35342c3132322c38302c3234372c3133325d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a367d" }, { "fact": "Diff", @@ -521,7 +521,7 @@ "identity": { "kind": "content-addressed", "domainHex": "6a656469742e746578742e646966662e763100", - "nodeIdHex": "80a0911f47cf558ce57904f412824537d4d53f294116849e62473f06279c2782" + "nodeIdHex": "2eeda507e54880b0ce929563be07c4fd74a543e453ee90c7120847def2912018" }, "value": { "basis_head_id": [ @@ -562,76 +562,76 @@ "end_byte": 2147483652, "inserted_byte_length": 6, "next_head_id": [ - 76, - 178, - 60, - 219, - 191, - 119, - 177, - 237, - 26, - 9, - 114, + 153, + 40, + 12, + 140, + 6, + 137, + 1, + 46, + 230, + 232, + 107, + 235, + 142, + 50, 148, - 120, - 237, - 246, - 209, - 215, - 241, - 23, - 15, + 116, + 211, + 59, + 118, + 249, + 32, + 80, + 27, + 121, + 12, + 190, + 199, 54, - 128, - 175, - 203, - 44, - 181, - 251, - 183, - 107, - 149, - 41, - 200 + 122, + 80, + 247, + 132 ], "rewrite_id": [ - 10, - 54, - 243, - 51, - 230, - 161, - 144, - 129, - 241, - 55, 55, - 22, - 211, 34, - 135, - 206, - 82, - 142, - 126, - 117, - 250, - 76, - 125, - 211, - 181, - 111, - 244, + 230, + 218, + 114, + 227, + 163, + 190, + 214, + 216, + 153, + 99, + 173, + 101, + 25, 77, - 159, - 15, - 90, - 68 + 63, + 107, + 246, + 228, + 145, + 198, + 112, + 217, + 171, + 71, + 75, + 122, + 155, + 92, + 42, + 49 ], "start_byte": 2147483648 }, - "canonicalBytesHex": "7b22726577726974655f6964223a5b31302c35342c3234332c35312c3233302c3136312c3134342c3132392c3234312c35352c35352c32322c3231312c33342c3133352c3230362c38322c3134322c3132362c3131372c3235302c37362c3132352c3231312c3138312c3131312c3234342c37372c3135392c31352c39302c36385d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b37362c3137382c36302c3231392c3139312c3131392c3137372c3233372c32362c392c3131342c3134382c3132302c3233372c3234362c3230392c3231352c3234312c32332c31352c35342c3132382c3137352c3230332c34342c3138312c3235312c3138332c3130372c3134392c34312c3230305d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a362c2264656c657465645f627974655f6c656e677468223a31383434363734343037333730393535313631357d" + "canonicalBytesHex": "7b22726577726974655f6964223a5b35352c33342c3233302c3231382c3131342c3232372c3136332c3139302c3231342c3231362c3135332c39392c3137332c3130312c32352c37372c36332c3130372c3234362c3232382c3134352c3139382c3131322c3231372c3137312c37312c37352c3132322c3135352c39322c34322c34395d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b3135332c34302c31322c3134302c362c3133372c312c34362c3233302c3233322c3130372c3233352c3134322c35302c3134382c3131362c3231312c35392c3131382c3234392c33322c38302c32372c3132312c31322c3139302c3139392c35342c3132322c38302c3234372c3133325d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a362c2264656c657465645f627974655f6c656e677468223a31383434363734343037333730393535313631357d" } ] } diff --git a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 index b7195c18..7d76cab4 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 @@ -1 +1 @@ -643ceb95bcb4d480a750f27df96c00bff7cb2861dd01744eaafbec228af2f141 +903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 75d9192f..fbdd0366 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -2,7 +2,26 @@ "schemaVersion": 1, "coordinate": "jedit.text.ReplaceRange.oracle@1", "applicationSchemaCoordinate": "jedit.text.schema@1", - "sourceCommit": "c70e12d73b4b00bc92412bab67e1761f7dd22f82", + "invocationSchemaCoordinate": "jedit.text.ReplaceRange.oracle-invocation@1", + "semanticBaselineCommit": "c70e12d73b4b00bc92412bab67e1761f7dd22f82", + "sourceSet": { + "algorithm": "sha256", + "domainHex": "6a656469742e7265706c6163652d72616e67652e6f7261636c652d736f757263652e763100", + "framing": "domain || repeated(u32be-path-length || path-utf8 || u64be-byte-length || bytes)", + "paths": [ + "native/jedit-echo-host/src/error.rs", + "native/jedit-echo-host/src/identity.rs", + "native/jedit-echo-host/src/records.rs", + "native/jedit-echo-host/src/rope.rs", + "native/jedit-echo-host/src/rope/tree.rs", + "native/jedit-echo-host/src/rope/window.rs", + "native/jedit-echo-host/tests/replace_range_oracle.rs", + "native/jedit-echo-host/tests/support/replace_range_basis.rs", + "native/jedit-echo-host/tests/support/replace_range_oracle.rs", + "native/jedit-echo-host/tests/support/replace_range_source_set.rs" + ], + "digestHex": "d68c0e8f9b243f15608ac6ddc06e3cdec90ca29b644d22615b784d58f05b101c" + }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", "warpId": "50ac1cc17b2ccc05195d403cba33921618a03b9143e8e6e169bd57712fae0d2a", @@ -29,6 +48,7 @@ "endByte": 0, "replacementUtf8Hex": "e78cab0a" }, + "invocationBytesHex": "7b226275666665724964223a2235383133376336326338643064333538363931346164383966633430383339623132343133633037373863373865643562356231303932646639396661383538222c226261736973486561644964223a2264363538383862386231386531323334663961613666343438393630386337313639323539353636333036333334656233383233373662336537353135316637222c22737461727442797465223a302c22656e6442797465223a302c227265706c6163656d656e7455746638486578223a226537386361623061227d", "terminal": { "posture": "committable", "footprint": { @@ -188,6 +208,7 @@ "endByte": 2, "replacementUtf8Hex": "5859" }, + "invocationBytesHex": "7b226275666665724964223a2263373532323462376663643834613337626364373939336333643238633762316435393337616366666637356630306461613138316463356531316466336538222c226261736973486561644964223a2234333138313764623738346133333432306431383539353636623835623233663438346465643535373235613338643533323739306365373933383266343532222c22737461727442797465223a322c22656e6442797465223a322c227265706c6163656d656e7455746638486578223a2235383539227d", "terminal": { "posture": "committable", "footprint": { @@ -409,6 +430,7 @@ "endByte": 3, "replacementUtf8Hex": "" }, + "invocationBytesHex": "7b226275666665724964223a2238633033613237396630383464333133663665363863616564323039303863386538383130353065653931643166613465313237343732316630623434616136222c226261736973486561644964223a2239323766396632626563323035643034313461346430313037363532663730356535326139396563373665303532383165643262643132633430323261343765222c22737461727442797465223a302c22656e6442797465223a332c227265706c6163656d656e7455746638486578223a22227d", "terminal": { "posture": "committable", "footprint": { @@ -546,6 +568,7 @@ "endByte": 5, "replacementUtf8Hex": "cebb0a" }, + "invocationBytesHex": "7b226275666665724964223a2238303238623965303837303334616664356339333934626264343634343465643665616235346561323330653234633138646665353665653638646332643031222c226261736973486561644964223a2234636131643061663666353138653836396261376566306639383532346162303133616536336433623461636538626137623339366262336432636337356537222c22737461727442797465223a312c22656e6442797465223a352c227265706c6163656d656e7455746638486578223a22636562623061227d", "terminal": { "posture": "committable", "footprint": { @@ -825,6 +848,7 @@ "endByte": 8194, "replacementUtf8Hex": "ceb20a5a" }, + "invocationBytesHex": "7b226275666665724964223a2231333236396634346163633532306230636339626531616636616334386564346433616134643137666637653665643165303163356235313838626636326138222c226261736973486561644964223a2230326165646162326533323163643131623036346239623536306537613162326235386231313963636566623634666366303136373961386164313432353363222c22737461727442797465223a343039342c22656e6442797465223a383139342c227265706c6163656d656e7455746638486578223a226365623230613561227d", "terminal": { "posture": "committable", "footprint": { @@ -1134,6 +1158,7 @@ "endByte": 5, "replacementUtf8Hex": "e78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cabe78cab0a" }, + "invocationBytesHex": "7b226275666665724964223a2236353064663261323966626635313266643561323666333733336264343735353430626339336564353534323530353236396139343038363036343264666434222c226261736973486561644964223a2264663566646535313233393233303433616533626236356539333834376364313732613138623663316231653533326533366263353532633633346330303034222c22737461727442797465223a302c22656e6442797465223a352c227265706c6163656d656e7455746638486578223a226537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361626537386361623061227d", "terminal": { "posture": "committable", "footprint": { @@ -1341,6 +1366,7 @@ "endByte": 1, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2239636637373836376438666133666266393965323939323834643662643131663532393536646361333162633631353363376266663734663338353836333633222c226261736973486561644964223a2235633937373866303466666663356135643439353439393532653266396334383933613137383565333931303166376436343331656631333637396433303562222c22737461727442797465223a322c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "range-order-invalid", @@ -1382,6 +1408,7 @@ "endByte": 4, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2231623061373038613266653363626633663730333235383835303435363363386365316432316361363661336165326565363765343437613639333837303061222c226261736973486561644964223a2235303164333164656535333331633633323436386131623533343937333962333063666439663630343133373538613062663131646465383136383965366635222c22737461727442797465223a302c22656e6442797465223a342c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "range-out-of-bounds", @@ -1423,6 +1450,7 @@ "endByte": 5, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2238383637643335366636323939633663333238353736393235656139616262623265653034303338376537366535616230303731373839656639633563653964222c226261736973486561644964223a2234613139653731343339623961393837353037663030373333623235663633616265356263363039656235333930613639303865613638623238313239386434222c22737461727442797465223a322c22656e6442797465223a352c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "utf8-boundary-invalid", @@ -1464,6 +1492,7 @@ "endByte": 4, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2230616533616139313231663761623165646166393531383533396231396162373031326231343533326334363332313636656566346531653833393666333738222c226261736973486561644964223a2265356565363532633430326435336637633764666565666631303037333361393665343535363966656330376134373831373330393733383665313265373237222c22737461727442797465223a312c22656e6442797465223a342c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "utf8-boundary-invalid", @@ -1505,6 +1534,7 @@ "endByte": 1, "replacementUtf8Hex": "" }, + "invocationBytesHex": "7b226275666665724964223a2264353234346539616564633136656361353335323837643631653532323665653535633163356561363063636333643230303662333631363665623936333263222c226261736973486561644964223a2235386636393164363132393063633433646566303136633238366137376134336362646666343931306535383531366331346562663438373663363130323665222c22737461727442797465223a312c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a22227d", "terminal": { "posture": "obstructed", "semanticCode": "no-op", @@ -1546,6 +1576,7 @@ "endByte": 3, "replacementUtf8Hex": "616263" }, + "invocationBytesHex": "7b226275666665724964223a2238313231343131646638386630393163653838663662616637623661666633623630346430373565346637393935613334636137303930383534326630343762222c226261736973486561644964223a2234626361346333376431633334396531383139333962316533623964616335643830373731303638303664313337316666623431386539656530376162653230222c22737461727442797465223a302c22656e6442797465223a332c227265706c6163656d656e7455746638486578223a22363136323633227d", "terminal": { "posture": "obstructed", "semanticCode": "no-op", @@ -1587,6 +1618,7 @@ "endByte": 1, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2235636131383361303666363161363637373666303238363430353436626164363633373635313062383538663864383933356138373663336465633538333031222c226261736973486561644964223a2261356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "basis-not-canonical", @@ -1633,6 +1665,7 @@ "endByte": 1, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2237323364643565666332643531336565353861356132346234616434656539383661636337633864343139393963323031303838636634653166353238313732222c226261736973486561644964223a2236313238343161376236366337633766336539353462666665393962633039366236656135353464306663356361343265643261383563333634313566613336222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "arithmetic-overflow", @@ -1674,6 +1707,7 @@ "endByte": 1, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2237393038666464303138363536633564303737393663343737373034626633306465653635343162663464313437366163313635376662646336333033393338222c226261736973486561644964223a2230333361323363363464333034623266333839313332323133396130653835623338306462343631346161663134323138653433343366326237643863373462222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "arithmetic-overflow", @@ -1694,6 +1728,7 @@ "endByte": 0, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2231313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131222c226261736973486561644964223a2232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222c22737461727442797465223a302c22656e6442797465223a302c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "fact-missing", @@ -1735,6 +1770,7 @@ "endByte": 1, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2232386338643066316138616266343662356235383231653039336437353633666539306365303432383438663261643331353233663263393431306164633635222c226261736973486561644964223a2230663539623737393463623864313831333137623261633636353634633334633762323436353930613330383262336464386230333836393631386234323630222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "fact-malformed", @@ -1776,6 +1812,7 @@ "endByte": 1, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2235353538323762373630343633326165373630323738393032626163353737653636633431373565323532633735623632623239373862626338613338393764222c226261736973486561644964223a2264383938383233363765643938333765623237303037383439343539313930636261636430343036383630656335373731386366663539393238616162303239222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "content-identity-mismatch", @@ -1812,6 +1849,7 @@ "endByte": 2147483648, "replacementUtf8Hex": "78" }, + "invocationBytesHex": "7b226275666665724964223a2238366235393532633031316535393437396438373230633830396236353539343766643363333134353235346431636132343230353833383839613537626635222c226261736973486561644964223a2237616365653231653239666162333563633065393263303664613635623264323761356465356365313466363563663537376235653633316337353237613364222c22737461727442797465223a323134373438333634382c22656e6442797465223a323134373438333634382c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "malformed-rope", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 5ed7576c..9a665da8 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -ad04b5256ddb28b2e336376e43343da27b8c6ea3cb9649edbbf9807c39199ad7 +313afabca41f36f6d567d11533d65fbf5ed2e33546705d20885c48f60bc69971 diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json index b4b73663..4a44927f 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json @@ -24,7 +24,25 @@ "objectMemberOrder": "declared-field-order", "optionalIdentifier": "null-or-array-32-u8", "profile": "jedit.compact-serde-json.v1", - "stringEscaping": "serde-json-v1", + "stringEscaping": { + "goldenVector": { + "jsonStringUtf8Hex": "225c75303030305c75303030315c75303030325c75303030335c75303030345c75303030355c75303030365c75303030375c625c745c6e5c75303030625c665c725c75303030655c75303030665c75303031305c75303031315c75303031325c75303031335c75303031345c75303031355c75303031365c75303031375c75303031385c75303031395c75303031615c75303031625c75303031635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e280a922", + "sourceUtf8Hex": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f225c2fe78cabe280a8e280a9" + }, + "otherU0000ThroughU001f": "backslash-u00xx-lowercase-hex", + "otherU0020ThroughU10ffff": "literal-utf8-unicode-scalars", + "quotationMark": "backslash-quotation-mark", + "reverseSolidus": "backslash-reverse-solidus", + "shortEscapes": { + "u0008": "backslash-b", + "u0009": "backslash-t", + "u000a": "backslash-n", + "u000c": "backslash-f", + "u000d": "backslash-r" + }, + "solidus": "literal", + "surrogateEscapes": "forbidden" + }, "unsignedInteger": "decimal-no-leading-zeroes" }, "facts": [ @@ -305,6 +323,41 @@ "nativeJsonV1": "authoritative", "typescriptGraphRope": "not-an-alternate-encoding" }, + "oracleInvocation": { + "canonicalBytesField": "invocationBytesHex", + "coordinate": "jedit.text.ReplaceRange.oracle-invocation@1", + "encoding": "compact-utf8-json", + "fields": [ + { + "name": "bufferId", + "type": "lowercase-hex-node-id-32" + }, + { + "name": "basisHeadId", + "type": "lowercase-hex-node-id-32" + }, + { + "name": "startByte", + "type": "u64-json-decimal" + }, + { + "name": "endByte", + "type": "u64-json-decimal" + }, + { + "name": "replacementUtf8Hex", + "type": "lowercase-hex-utf8-bytes" + } + ], + "objectMemberOrder": [ + "bufferId", + "basisHeadId", + "startByte", + "endByte", + "replacementUtf8Hex" + ], + "posture": "finite-corpus-input-not-runtime-abi" + }, "ropeLaw": { "coordinateType": "u64", "emittedOperations": [ diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 index 4aefa6a4..2319acfe 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 @@ -1 +1 @@ -e6ced0c88935337460d5685418cb387789a1d4cd174452499f3a474ad51eef70 +d605042ba33da11f595c4d65bd4cb125c50555c0156d4a4dfbe12ba0c6bda688 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index d46d89be..94000081 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -105,6 +105,7 @@ This cycle includes: so the mutable keyed Buffer fact cannot accidentally receive a second content-derived identity; - explicit fixed-width ranges and UTF-8 requirements; +- a corpus-only `u64` invocation schema plus exact compact invocation bytes; - an explicit no-edge posture for version 1; - explicit exclusion and migration posture for TypeScript-only propositions; - deterministic success cases for insertion, deletion, Unicode replacement, @@ -119,7 +120,7 @@ This cycle includes: This cycle does not include: -- an Echo operation-program ABI or evaluator; +- an Echo operation-program or production invocation ABI or evaluator; - an Edict target lowerer, package, or generated client; - production invocation, routing, fact-byte, or identity changes; - an application callback, native rope intrinsic, or caller-supplied patch; @@ -171,9 +172,20 @@ Canonical fact JSON is: - exact `null` for absent optional identifiers; - 32-byte identifiers and digests encoded as arrays of 32 decimal octets; - byte strings encoded as arrays of decimal octets; -- JSON strings encoded with the compact serde-json version-1 escaping profile; +- JSON strings encoded by the exact `jedit.compact-serde-json.v1` law below; - no maps, floats, signed integers, duplicate members, or unknown members. +The string law emits quotation mark and reverse solidus as `\"` and `\\`. +U+0008, U+0009, U+000A, U+000C, and U+000D use `\b`, `\t`, `\n`, `\f`, and +`\r`. Every other scalar from U+0000 through U+001F uses six bytes +`\u00xx`, with lowercase hexadecimal. Solidus is never escaped. Every other +Unicode scalar from U+0020 through U+10FFFF is emitted as its literal UTF-8 +bytes; surrogate escapes are forbidden. These rules apply to member names and +string values. Maps are forbidden as canonical fact fields at every nesting +depth, even if an implementation uses a map internally. The schema's golden +vector covers every control scalar plus quotation mark, reverse solidus, +solidus, non-ASCII text, U+2028, and U+2029. + This is a writer and identity law. The legacy `serde_json` reader accepts some noncanonical spellings and does not currently recompute every content node identifier from attachment bytes. That permissiveness is not promoted into @@ -188,6 +200,42 @@ The generic content-fact constructor does not accept Buffer facts. This removes the currently representable but unused `BufferFact` content identity while preserving every production byte and authoritative keyed Buffer identifier. +The normative identity domains are: + +| Identity | Exact UTF-8 domain | Lowercase hexadecimal domain | Material | +| --- | --- | --- | --- | +| Type ID | `type:` | `747970653a` | type-label UTF-8 | +| Buffer node | `jedit.text.buffer-key.v1\0` | `6a656469742e746578742e6275666665722d6b65792e763100` | buffer-key UTF-8 | +| Blob fact | `jedit.text.blob.v1\0` | `6a656469742e746578742e626c6f622e763100` | canonical Blob bytes | +| Leaf fact | `jedit.text.leaf.v1\0` | `6a656469742e746578742e6c6561662e763100` | canonical Leaf bytes | +| Branch fact | `jedit.text.branch.v1\0` | `6a656469742e746578742e6272616e63682e763100` | canonical Branch bytes | +| Head fact | `jedit.text.head.v1\0` | `6a656469742e746578742e686561642e763100` | canonical Head bytes | +| Rewrite fact | `jedit.text.rewrite.v1\0` | `6a656469742e746578742e726577726974652e763100` | canonical Rewrite bytes | +| Diff fact | `jedit.text.diff.v1\0` | `6a656469742e746578742e646966662e763100` | canonical Diff bytes | +| Blob content | `jedit.text.blob-content.v1\0` | `6a656469742e746578742e626c6f622d636f6e74656e742e763100` | blob bytes | +| Empty-root digest | `jedit.text.empty-root.v1\0` | `6a656469742e746578742e656d7074792d726f6f742e763100` | empty bytes | + +Every row hashes raw `domain || material`, with no inserted delimiter or length +prefix. The terminal NUL shown in most domains is itself the final domain byte. + +The finite corpus uses +`jedit.text.ReplaceRange.oracle-invocation@1`, a compact UTF-8 JSON object with +ordered members `bufferId`, `basisHeadId`, `startByte`, `endByte`, and +`replacementUtf8Hex`. Identifiers are 64 lowercase hexadecimal characters; +coordinates are JSON decimal `u64`; replacement bytes are lowercase +hexadecimal and must decode as UTF-8. Every case carries the exact compact +object bytes in `invocationBytesHex`. This schema is a conformance input, not a +runtime authority or a prematurely frozen Echo ABI. + +The native planner and retained fact coordinates are `u64`, and the future +Edict-authored operation must preserve that domain. Current production still +routes `ReplaceRangeAsTickInput` through the Wesley/GraphQL compatibility ABI, +where `startByte` and `endByte` are `i32` and `bounded_i32` rejects values above +`i32::MAX`. The above-GraphQL-range oracle intentionally bypasses that legacy +ABI and proves only the planner and future-operation coordinate law. This cycle +therefore preserves current production behavior while leaving #285 open until +the Edict cutover removes the narrowing boundary. + The oracle resource is finite conformance evidence. It does not prove semantic equivalence for every possible `ReplaceRange` input. Its legacy planner path must not invoke, link, or share an evaluator implementation with the future @@ -196,11 +244,13 @@ is disclosed and expected. ## Lower Modes -The contract is entirely headless. `cargo test` regenerates and compares the -resources. A deliberately named fixture-update mode may rewrite generated -corpus bytes during development, but CI only checks and never updates them. -Malformed resources fail with ordinary test errors; there is no fallback to -source-code inference. +The contract is entirely headless. `cargo test` recomputes and compares the +resources. `npm run lawpack:replace-range:update` deliberately replaces all +three resources and their SHA-256 sidecars, then reruns check mode. Each +resource and sidecar replacement is an atomic same-directory rename; CI never +sets either update variable. Any present update variable other than exact `1` +fails. Malformed resources fail with ordinary test errors; there is no fallback +to source-code inference. ## Data / State Model @@ -211,7 +261,7 @@ source-code inference. | Invalid states | Noncanonical fact bytes, wrong domains or identities, schema drift, corpus drift, or ambiguous native/TypeScript ownership. | | Reset behavior | Fixtures regenerate from a clean synthetic graph; no production WAL or user data is read. | | Serialization | Digest-locked JSON resources containing exact fact bytes as lowercase hexadecimal. | -| Deterministic assumptions | Ordered Rust structs/maps, exact domain bytes, explicit UTF-8 inputs, no clock/randomness/host paths. | +| Deterministic assumptions | Ordered Rust structs, no map-valued fact fields, exact domain bytes, explicit UTF-8 inputs, no clock/randomness/host paths. | ## Accessibility Posture @@ -238,8 +288,10 @@ source-code inference. Agents can inspect one declaration and one corpus manifest without executing the product or scraping Rust debug output. Every case names its semantic purpose, exact basis, input, terminal posture, support, facts, and result. The -manifest states the source commit and evidence grade so a consumer cannot -mistake finite-corpus agreement for formal equivalence. +`semanticBaselineCommit` names the historical checkpoint whose behavior this +cycle freezes; the `sourceSet` digest binds the exact declared generating +source bytes. The evidence grade prevents a consumer from mistaking +finite-corpus agreement for formal equivalence. ## Linked Invariants @@ -247,7 +299,8 @@ mistake finite-corpus agreement for formal equivalence. - Echo owns runtime admission, execution, commitment, receipts, and recovery. - Edict artifacts do not prove that Echo executed them. - A resource digest supplies substitution evidence, not authority. -- Fixed-width byte coordinates remain `u64`; GraphQL `Int` is not inherited. +- Native facts and the future operation use `u64`; the current compatibility + ABI remains an explicitly documented `i32` limitation. - A failed plan produces no parent-visible patch. - Existing native fact bytes remain stable in version 1. - Tests are executable specification. @@ -272,51 +325,165 @@ Rejected. Prose does not constrain field order, exact bytes, hash domains, or corpus drift. The declaration and corpus must be regenerated and checked by the implementation that currently owns the law. -### Chosen: freeze native JSON version 1 and a finite differential corpus +## Decision -This preserves current production history and gives the next Echo slice exact -inputs and outputs. It is intentionally conservative: structural graph schema -evolution can proceed later under a new coordinate and explicit migration. +Freeze native JSON version 1 and a finite differential corpus. This preserves +current production history and gives the next Echo slice exact inputs and +outputs. It is intentionally conservative: structural graph schema evolution +can proceed later under a new coordinate and explicit migration. ## Implementation Slices - [x] Slice 1: land this decision record and freeze the executable witnesses. - [x] Slice 2: publish and byte-lock `jedit.text.schema@1`. - [x] Slice 3: publish and regenerate the `ReplaceRange` differential corpus. +- [x] Slice 4: close schema/corpus review gaps and complete cycle evidence. ## Tests To Write First -- [ ] Schema resource test: regenerate exact bytes, verify the checked SHA-256 +- [x] Schema resource test: regenerate exact bytes, verify the checked SHA-256 digest, and reject drift from native fact labels, field order, domains, attachment posture, and excluded propositions. -- [ ] Type-law test: prove Buffer has no generic content-addressed constructor +- [x] Type-law test: prove Buffer has no generic content-addressed constructor while every immutable ReplaceRange fact retains its existing identifier. -- [ ] Success corpus test: regenerate each initial graph and replacement plan, +- [x] Schema/writer conformance test: compare every declared field sequence with + the order observed directly from native canonical fact bytes. +- [x] Success corpus test: regenerate each initial graph and replacement plan, then compare exact reads, writes, patch order, fact bytes, identities, result metrics, and preserved facts. -- [ ] Obstruction corpus test: regenerate each failure, compare its Jedit-owned +- [x] Obstruction corpus test: regenerate each failure, compare its Jedit-owned semantic category and legacy evidence, and prove no write plan exists. -- [ ] Determinism test: regenerate the complete resources twice from fresh +- [x] Determinism test: regenerate the complete resources twice from fresh synthetic graphs and compare exact bytes. -- [ ] Fixed-width test: retain `u64` values above GraphQL/i32 range in the - declaration and invocation vectors without narrowing. +- [x] Fixed-width test: retain `u64` values above GraphQL/i32 range in the + corpus invocation and its exact compact bytes without narrowing. + +## Acceptance Criteria -## Acceptance Checklist +The work is done when: -- [ ] Issue #292 remains the live cycle ledger. -- [ ] The declaration selects the native production model unambiguously. -- [ ] All seven ReplaceRange fact shapes and identity domains are explicit. -- [ ] TypeScript-only propositions and migration posture are explicit. -- [ ] Schema and corpus resource digests are checked from exact bytes. -- [ ] The corpus covers representative persistent-rope successes and typed +- [x] Issue #292 remains the live cycle ledger. +- [x] The declaration selects the native production model unambiguously. +- [x] All seven ReplaceRange fact shapes and identity domains are explicit. +- [x] TypeScript-only propositions and migration posture are explicit. +- [x] Schema and corpus resource digests are checked from exact bytes. +- [x] The corpus covers representative persistent-rope successes and typed obstructions. -- [ ] No production behavior, fact bytes, authoritative identities, Echo +- [x] No production behavior, fact bytes, authoritative identities, Echo dependency, or user-visible behavior changes. -- [ ] Focused Rust tests and `npm run check` pass. -- [ ] The retrospective records evidence limits and the exact next Echo RED. +- [x] Focused Rust tests and `npm run check` pass. +- [x] The retrospective records evidence limits and the exact next Echo RED. + +## Validation Plan + +```bash +npm ci +npm run lawpack:replace-range:update +cargo test --manifest-path native/jedit-echo-host/Cargo.toml \ + --test replace_range_schema +cargo test --manifest-path native/jedit-echo-host/Cargo.toml \ + --test replace_range_schema_conformance +cargo test --manifest-path native/jedit-echo-host/Cargo.toml \ + --test replace_range_oracle +cargo clippy --manifest-path native/jedit-echo-host/Cargo.toml \ + --all-targets -- -D warnings +npm run check +node scripts/jedit-production-cutover-guard.mjs +npm run witness:echo +``` + +`cargo fmt --check` currently reports the same two pre-existing formatting +differences on `origin/main`, in unchanged `src/contract.rs` and +`src/rope/window.rs`. The directly changed Rust files are formatted, and this +cycle does not mix that unrelated cleanup into its semantic diff. + +## Playback / Witness + +Run `npm run lawpack:replace-range:update` from a clean checkout. It rebuilds +the declaration, codec vectors, corpus, and all sidecars, then proves check mode +is clean. Inspect +`contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json` for the exact +fact, string, identity, rope, and corpus-invocation laws. Inspect +`replace-range-v1.oracle.json` for 19 self-contained cases with basis facts, +exact invocation bytes, support, ordered patches, results, or typed no-patch +obstructions. No editor, terminal rendering, external service, or sibling +checkout is required. + +## Risks + +Known risks: + +- The corpus is finite and does not prove all-input semantic equivalence. +- The legacy reader accepts noncanonical JSON that the writer never emits. +- The current Wesley/GraphQL invocation remains limited to `i32` coordinates. +- A source change outside the declared oracle source set could still affect + behavior through a transitive dependency. + +Mitigations: + +- Evidence is graded deterministic self-validation until Echo #684 supplies a + separately implemented evaluator and finite-corpus comparison. +- The declaration makes canonical writer bytes normative and requires the new + evaluator to reject noncanonical or misaddressed facts. +- Jedit #285 remains open for the production cutover; this PR does not claim the + compatibility ABI has changed. +- The corpus binds the exact Jedit planner, record, identity, error, window, + case generator, basis builder, and source-set framing bytes, while the + repository lockfile and CI preserve transitive dependency selection. + +## Follow-On Debt + +- [Echo #684](https://github.com/flyingrobots/echo/issues/684) must add the + callback-free bounded declarative graph-operation evaluator and consume these + exact resources in its first RED. +- [Jedit #285](https://github.com/flyingrobots/jedit/issues/285) remains open + until production `ReplaceRange` leaves the GraphQL `Int` compatibility ABI. ## Retrospective -To be completed before review. This section will identify the landed resource -digests, executable witnesses, any deviation from the planned corpus, and the -remaining gap before Echo can admit and execute the Jedit-owned program. +What changed from the design: + +- The planned native-schema and 19-case corpus was implemented without + changing the production planner, fact bytes, identities, or routing. +- Review tightened the declaration with a fully specified string codec, a + corpus-only invocation schema and exact bytes, mechanical schema-to-writer + field-order comparison, strict/atomic fixture updates, and a digest over the + exact declared Jedit source set. These are proof-strengthening additions, + not a widening into the deferred Echo ABI. +- The published SHA-256 digests are + `d605042ba33da11f595c4d65bd4cb125c50555c0156d4a4dfbe12ba0c6bda688` + for the schema, + `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` + for codec vectors, and + `313afabca41f36f6d567d11533d65fbf5ed2e33546705d20885c48f60bc69971` + for the oracle. The oracle source-set digest is + `d68c0e8f9b243f15608ac6ddc06e3cdec90ca29b644d22615b784d58f05b101c`. + +What the tests proved: + +- Native Rust serialization regenerates all exact resources and identities; + every declared fact-member order equals the order observed from writer bytes; + the full string escape vector matches the native writer; and corpus invocation + bytes preserve `u64` above `i32::MAX`. +- Six success and thirteen obstruction cases regenerate deterministically. A + success yields the exact support, ordered patch, retained facts, metrics, and + materialized consequence; an obstruction yields no `MutationPlan` and leaves + the parent graph unchanged. +- This is deterministic self-validation. It is not an independent verifier, + all-input equivalence proof, Echo admission witness, runtime receipt, WAL, or + recovery witness. + +What remains open: + +- The exact next RED is Echo #684: load these pinned bytes as an external + consumer and fail because the current operation interpreter supports only its + tiny compare-and-set program. GREEN must come from a generic data-only bounded + evaluator, never a Jedit planner callback, native rope intrinsic, or + caller-supplied patch. +- Edict binding, Echo execution/receipt/recovery, Jim invocation, legacy + `ReplaceRange` cutover, and full `u64` production support remain later + campaigns. + +PR: + +- https://github.com/flyingrobots/jedit/pull/293 diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs index 5cb29627..dd547b0b 100644 --- a/native/jedit-echo-host/tests/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -1,13 +1,14 @@ #[path = "support/replace_range_oracle.rs"] mod oracle_support; +#[path = "support/resource_fixture.rs"] +mod resource_fixture; -use std::fs; use std::path::PathBuf; use oracle_support::{ canonical_corpus_bytes, generate_corpus, BasisSetup, CaseSpec, ExpectedPosture, }; -use sha2::{Digest, Sha256}; +use resource_fixture::{checked_sha256, sha256_hex, update_resource_pair}; const UPDATE_ENV: &str = "JEDIT_UPDATE_REPLACE_RANGE_ORACLE"; const EXPECTED_CORPUS_SHA256: &str = include_str!( @@ -21,17 +22,17 @@ fn replace_range_oracle_matches_the_committed_corpus() { assert_eq!(first, second, "fresh oracle generation must be byte-stable"); let path = corpus_path(); - if std::env::var_os(UPDATE_ENV).is_some() { - fs::write(&path, &first).expect("oracle corpus should update"); + if update_resource_pair(UPDATE_ENV, &path, &corpus_digest_path(), &first) { + return; } - let committed = fs::read(&path).expect("committed oracle corpus should exist"); + let committed = std::fs::read(&path).expect("committed oracle corpus should exist"); assert_eq!( first, committed, "run with {UPDATE_ENV}=1 to update intentionally" ); assert_eq!( - hex::encode(Sha256::digest(&committed)), - EXPECTED_CORPUS_SHA256.trim_end(), + sha256_hex(&committed), + checked_sha256(EXPECTED_CORPUS_SHA256), "oracle resource digest must be updated deliberately" ); } @@ -41,6 +42,11 @@ fn corpus_path() -> PathBuf { .join("../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json") } +fn corpus_digest_path() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256") +} + fn cases() -> Vec { vec![ success("empty-unicode-insert", "", BasisSetup::Empty, 0, 0, "猫\n"), diff --git a/native/jedit-echo-host/tests/replace_range_schema.rs b/native/jedit-echo-host/tests/replace_range_schema.rs index 86add135..9b0569a3 100644 --- a/native/jedit-echo-host/tests/replace_range_schema.rs +++ b/native/jedit-echo-host/tests/replace_range_schema.rs @@ -1,17 +1,24 @@ use std::fmt::Debug; +use std::path::PathBuf; + +#[path = "support/resource_fixture.rs"] +mod resource_fixture; +#[path = "support/replace_range_schema.rs"] +mod schema_support; use jedit_echo_host::identity::hash_bytes; use jedit_echo_host::records::{ fact_bytes, fact_id, fact_type_id, BlobFact, BranchFact, BufferFact, ContentAddressedFact, DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, TypedFact, BLOB_CONTENT_HASH_DOMAIN, - BUFFER_NODE_ID_DOMAIN, EMPTY_ROOT_DIGEST_DOMAIN, }; -use jedit_echo_host::rope::{buffer_node_id, MAX_LEAF_BYTES}; +use jedit_echo_host::rope::buffer_node_id; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::{json, Value}; -use sha2::{Digest, Sha256}; +use serde_json::Value; use static_assertions::{assert_impl_all, assert_not_impl_any}; +use resource_fixture::{checked_sha256, sha256_hex, update_resource_pair}; +use schema_support::{expected_schema, SCHEMA_COORDINATE, STRING_ESCAPE_VECTOR}; + const SCHEMA_BYTES: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/../../contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json" @@ -29,7 +36,7 @@ const CODEC_VECTOR_SHA256: &str = include_str!(concat!( "/../../contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256" )); -const SCHEMA_COORDINATE: &str = "jedit.text.schema@1"; +const UPDATE_ENV: &str = "JEDIT_UPDATE_REPLACE_RANGE_SCHEMA"; assert_not_impl_any!(BufferFact: ContentAddressedFact); assert_impl_all!(BlobFact: ContentAddressedFact); @@ -80,15 +87,30 @@ enum IdentityVector { #[test] fn published_schema_and_codec_vectors_regenerate_byte_for_byte() { - assert_eq!(SCHEMA_BYTES, pretty_json_bytes(&expected_schema())); - assert_eq!( - CODEC_VECTOR_BYTES, - pretty_json_bytes(&expected_codec_resource()) + let schema_bytes = pretty_json_bytes(&expected_schema()); + let codec_bytes = pretty_json_bytes(&expected_codec_resource()); + let schema_updated = update_resource_pair( + UPDATE_ENV, + &lawpack_path("text-schema-v1.json"), + &lawpack_path("text-schema-v1.sha256"), + &schema_bytes, ); - assert_eq!(sha256_hex(SCHEMA_BYTES), checked_digest(SCHEMA_SHA256)); + let codec_updated = update_resource_pair( + UPDATE_ENV, + &lawpack_path("codec-vectors-v1.json"), + &lawpack_path("codec-vectors-v1.sha256"), + &codec_bytes, + ); + assert_eq!(schema_updated, codec_updated); + if schema_updated { + return; + } + assert_eq!(SCHEMA_BYTES, schema_bytes); + assert_eq!(CODEC_VECTOR_BYTES, codec_bytes); + assert_eq!(sha256_hex(SCHEMA_BYTES), checked_sha256(SCHEMA_SHA256)); assert_eq!( sha256_hex(CODEC_VECTOR_BYTES), - checked_digest(CODEC_VECTOR_SHA256) + checked_sha256(CODEC_VECTOR_SHA256) ); } @@ -117,197 +139,8 @@ fn codec_vectors_match_native_bytes_types_and_identity_domains() { check_content::(diff, "Diff"); } -fn expected_schema() -> Value { - json!({ - "schemaVersion": 1, - "coordinate": SCHEMA_COORDINATE, - "factCodec": { - "profile": "jedit.compact-serde-json.v1", - "encoding": "utf-8", - "byteOrderMark": "forbidden", - "insignificantWhitespace": "forbidden", - "objectMemberOrder": "declared-field-order", - "unsignedInteger": "decimal-no-leading-zeroes", - "optionalIdentifier": "null-or-array-32-u8", - "identifier": "array-32-u8", - "byteString": "array-u8", - "stringEscaping": "serde-json-v1", - "forbiddenValues": [ - "map-valued-fields", - "floats", - "signed-integers", - "duplicate-members", - "unknown-members" - ] - }, - "graphEncoding": { - "node": "typed-node", - "attachment": { - "key": "node-alpha", - "value": "atom", - "atomType": "node-type", - "payload": "canonical-fact-bytes" - }, - "edges": "none" - }, - "identityLaws": { - "typeId": { - "algorithm": "blake3-256", - "domainHex": hex::encode(b"type:"), - "material": "type-label-utf8" - }, - "bufferNode": { - "algorithm": "blake3-256", - "domainHex": hex::encode(BUFFER_NODE_ID_DOMAIN), - "material": "buffer-key-utf8" - }, - "contentFact": { - "algorithm": "blake3-256", - "material": "fact-domain || canonical-fact-bytes" - }, - "blobContent": { - "algorithm": "blake3-256", - "domainHex": hex::encode(BLOB_CONTENT_HASH_DOMAIN), - "material": "blob-bytes" - }, - "emptyRootDigest": { - "algorithm": "blake3-256", - "domainHex": hex::encode(EMPTY_ROOT_DIGEST_DOMAIN), - "material": "empty-bytes" - } - }, - "ropeLaw": { - "coordinateType": "u64", - "range": "half-open-utf8-byte", - "replacement": "utf8-bytes", - "maxLeafBytes": MAX_LEAF_BYTES, - "leafChunkBoundary": "largest-utf8-boundary-at-or-before-limit", - "leafSlices": "may-share-blob-with-nonzero-byte-start", - "lineCount": "line-breaks-plus-one-including-empty", - "nonemptyRootDigest": "root-node-id", - "persistence": "path-copy-no-delete", - "emittedOperations": ["upsert-node", "set-node-alpha"] - }, - "facts": [ - fact_declaration::( - "Buffer", - json!({ - "kind": "keyed", - "domainHex": hex::encode(BUFFER_NODE_ID_DOMAIN), - "material": "buffer_key utf8" - }), - &[ - ("buffer_key", "utf8-string"), - ("projection_path", "optional-utf8-string"), - ("canonical_head_id", "node-id-32"), - ("version", "u64") - ] - ), - content_fact_declaration::( - "Blob", - &[("content_hash", "digest-32"), ("bytes", "bytes")] - ), - content_fact_declaration::( - "Leaf", - &[ - ("blob_id", "node-id-32"), - ("byte_start", "u64"), - ("byte_length", "u64"), - ("utf16_length", "u64"), - ("line_breaks", "u64") - ] - ), - content_fact_declaration::( - "Branch", - &[ - ("left", "node-id-32"), - ("right", "node-id-32"), - ("byte_length", "u64"), - ("utf16_length", "u64"), - ("line_breaks", "u64"), - ("height", "u32") - ] - ), - content_fact_declaration::( - "Head", - &[ - ("buffer_id", "node-id-32"), - ("basis_head_id", "optional-node-id-32"), - ("root_node_id", "optional-node-id-32"), - ("byte_length", "u64"), - ("utf16_length", "u64"), - ("line_count", "u64"), - ("root_digest", "digest-32"), - ("sequence", "u64") - ] - ), - content_fact_declaration::( - "Rewrite", - &[ - ("buffer_id", "node-id-32"), - ("basis_head_id", "node-id-32"), - ("next_head_id", "node-id-32"), - ("start_byte", "u64"), - ("end_byte", "u64"), - ("inserted_byte_length", "u64") - ] - ), - content_fact_declaration::( - "Diff", - &[ - ("rewrite_id", "node-id-32"), - ("basis_head_id", "node-id-32"), - ("next_head_id", "node-id-32"), - ("start_byte", "u64"), - ("end_byte", "u64"), - ("inserted_byte_length", "u64"), - ("deleted_byte_length", "u64") - ] - ) - ], - "excludedPropositions": [ - "jedit.text.RopeStructuralMaintenance", - "jedit.text.RopeCheckpoint", - "jedit.text.RopeCheckpointAnchored", - "receipt-attribution-fields", - "structural-echo-edges", - "typed-diff-spans" - ], - "migrationPosture": { - "nativeJsonV1": "authoritative", - "typescriptGraphRope": "not-an-alternate-encoding", - "codecOrStructuralChange": "new-schema-coordinate-and-admitted-migration" - } - }) -} - -fn fact_declaration(name: &str, identity: Value, fields: &[(&str, &str)]) -> Value { - json!({ - "name": name, - "typeLabel": T::TYPE_LABEL, - "identity": identity, - "fields": fields - .iter() - .map(|(field, kind)| json!({"name": field, "type": kind})) - .collect::>() - }) -} - -fn content_fact_declaration(name: &str, fields: &[(&str, &str)]) -> Value { - fact_declaration::( - name, - json!({ - "kind": "content-addressed", - "algorithm": "blake3-256", - "domainHex": hex::encode(T::ID_DOMAIN), - "material": "domain || canonical-fact-bytes" - }), - fields, - ) -} - fn expected_codec_resource() -> CodecResource { - let buffer_key = "schema\n\"猫\\path.txt"; + let buffer_key = STRING_ESCAPE_VECTOR; let buffer_id = buffer_node_id(buffer_key); let blob_bytes = "A😀\n".as_bytes().to_vec(); let blob = BlobFact { @@ -471,17 +304,8 @@ fn pretty_json_bytes(value: &T) -> Vec { bytes } -fn sha256_hex(bytes: &[u8]) -> String { - hex::encode(Sha256::digest(bytes)) -} - -fn checked_digest(value: &str) -> &str { - let digest = value - .strip_suffix('\n') - .expect("digest resource should end with one newline"); - assert_eq!(digest.len(), 64); - assert!(digest - .bytes() - .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))); - digest +fn lawpack_path(file_name: &str) -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../contracts/jedit/lawpacks/replace-range-v1") + .join(file_name) } diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs new file mode 100644 index 00000000..0f5479be --- /dev/null +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -0,0 +1,328 @@ +use std::fmt; + +use jedit_echo_host::records::{ + BlobFact, BranchFact, BufferFact, DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, +}; +use serde::de::{IgnoredAny, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; + +const SCHEMA_BYTES: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json" +)); +const CODEC_VECTOR_BYTES: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json" +)); +const ORACLE_BYTES: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json" +)); + +#[derive(Deserialize)] +struct CorpusProjection { + cases: Vec, +} + +#[derive(Deserialize)] +struct CaseProjection { + id: String, + invocation: InvocationProjection, + #[serde(rename = "invocationBytesHex")] + invocation_bytes_hex: String, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct InvocationProjection { + buffer_id: String, + basis_head_id: String, + start_byte: u64, + end_byte: u64, + replacement_utf8_hex: String, +} + +#[derive(Debug, Eq, PartialEq)] +struct OrderedMemberNames(Vec); + +impl<'de> Deserialize<'de> for OrderedMemberNames { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(MemberNameVisitor) + } +} + +struct MemberNameVisitor; + +impl<'de> Visitor<'de> for MemberNameVisitor { + type Value = OrderedMemberNames; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a canonical fact object") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut names = Vec::new(); + while let Some(name) = map.next_key::()? { + names.push(name); + map.next_value::()?; + } + Ok(OrderedMemberNames(names)) + } +} + +#[test] +fn schema_field_order_matches_the_native_writer_for_every_fact() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + let vectors: Value = + serde_json::from_slice(CODEC_VECTOR_BYTES).expect("codec vectors should decode"); + let declarations = schema["facts"] + .as_array() + .expect("facts should be an array"); + let vectors = vectors["vectors"] + .as_array() + .expect("vectors should be an array"); + + for declaration in declarations { + let fact_name = declaration["name"] + .as_str() + .expect("fact should have a name"); + let declared_names = declaration["fields"] + .as_array() + .expect("fields should be an array") + .iter() + .map(|field| field["name"].as_str().expect("field should have a name")) + .collect::>(); + let vector = vectors + .iter() + .find(|vector| vector["fact"] == fact_name) + .expect("every declared fact should have a codec vector"); + let bytes = hex::decode( + vector["canonicalBytesHex"] + .as_str() + .expect("vector should contain canonical bytes"), + ) + .expect("canonical bytes should be hexadecimal"); + let actual = serde_json::from_slice::(&bytes) + .expect("canonical fact should decode as an object"); + assert_eq!(actual.0, declared_names, "{fact_name} member-order drift"); + } +} + +#[test] +fn schema_field_types_match_the_native_fact_types() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + + let _: for<'a> fn(&'a BufferFact) -> &'a String = |fact| &fact.buffer_key; + let _: for<'a> fn(&'a BufferFact) -> &'a Option = |fact| &fact.projection_path; + let _: fn(&BufferFact) -> NodeIdBytes = |fact| fact.canonical_head_id; + let _: fn(&BufferFact) -> u64 = |fact| fact.version; + assert_declared_types( + &schema, + "Buffer", + &[ + ("buffer_key", "utf8-string"), + ("projection_path", "optional-utf8-string"), + ("canonical_head_id", "node-id-32"), + ("version", "u64"), + ], + ); + + let _: fn(&BlobFact) -> [u8; 32] = |fact| fact.content_hash; + let _: for<'a> fn(&'a BlobFact) -> &'a Vec = |fact| &fact.bytes; + assert_declared_types( + &schema, + "Blob", + &[("content_hash", "digest-32"), ("bytes", "bytes")], + ); + + let _: fn(&LeafFact) -> NodeIdBytes = |fact| fact.blob_id; + let _: fn(&LeafFact) -> u64 = |fact| fact.byte_start; + let _: fn(&LeafFact) -> u64 = |fact| fact.byte_length; + let _: fn(&LeafFact) -> u64 = |fact| fact.utf16_length; + let _: fn(&LeafFact) -> u64 = |fact| fact.line_breaks; + assert_declared_types( + &schema, + "Leaf", + &[ + ("blob_id", "node-id-32"), + ("byte_start", "u64"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_breaks", "u64"), + ], + ); + + let _: fn(&BranchFact) -> NodeIdBytes = |fact| fact.left; + let _: fn(&BranchFact) -> NodeIdBytes = |fact| fact.right; + let _: fn(&BranchFact) -> u64 = |fact| fact.byte_length; + let _: fn(&BranchFact) -> u64 = |fact| fact.utf16_length; + let _: fn(&BranchFact) -> u64 = |fact| fact.line_breaks; + let _: fn(&BranchFact) -> u32 = |fact| fact.height; + assert_declared_types( + &schema, + "Branch", + &[ + ("left", "node-id-32"), + ("right", "node-id-32"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_breaks", "u64"), + ("height", "u32"), + ], + ); + + let _: fn(&HeadFact) -> NodeIdBytes = |fact| fact.buffer_id; + let _: fn(&HeadFact) -> Option = |fact| fact.basis_head_id; + let _: fn(&HeadFact) -> Option = |fact| fact.root_node_id; + let _: fn(&HeadFact) -> u64 = |fact| fact.byte_length; + let _: fn(&HeadFact) -> u64 = |fact| fact.utf16_length; + let _: fn(&HeadFact) -> u64 = |fact| fact.line_count; + let _: fn(&HeadFact) -> [u8; 32] = |fact| fact.root_digest; + let _: fn(&HeadFact) -> u64 = |fact| fact.sequence; + assert_declared_types( + &schema, + "Head", + &[ + ("buffer_id", "node-id-32"), + ("basis_head_id", "optional-node-id-32"), + ("root_node_id", "optional-node-id-32"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_count", "u64"), + ("root_digest", "digest-32"), + ("sequence", "u64"), + ], + ); + + let _: fn(&RewriteFact) -> NodeIdBytes = |fact| fact.buffer_id; + let _: fn(&RewriteFact) -> NodeIdBytes = |fact| fact.basis_head_id; + let _: fn(&RewriteFact) -> NodeIdBytes = |fact| fact.next_head_id; + let _: fn(&RewriteFact) -> u64 = |fact| fact.start_byte; + let _: fn(&RewriteFact) -> u64 = |fact| fact.end_byte; + let _: fn(&RewriteFact) -> u64 = |fact| fact.inserted_byte_length; + assert_declared_types( + &schema, + "Rewrite", + &[ + ("buffer_id", "node-id-32"), + ("basis_head_id", "node-id-32"), + ("next_head_id", "node-id-32"), + ("start_byte", "u64"), + ("end_byte", "u64"), + ("inserted_byte_length", "u64"), + ], + ); + + let _: fn(&DiffFact) -> NodeIdBytes = |fact| fact.rewrite_id; + let _: fn(&DiffFact) -> NodeIdBytes = |fact| fact.basis_head_id; + let _: fn(&DiffFact) -> NodeIdBytes = |fact| fact.next_head_id; + let _: fn(&DiffFact) -> u64 = |fact| fact.start_byte; + let _: fn(&DiffFact) -> u64 = |fact| fact.end_byte; + let _: fn(&DiffFact) -> u64 = |fact| fact.inserted_byte_length; + let _: fn(&DiffFact) -> u64 = |fact| fact.deleted_byte_length; + assert_declared_types( + &schema, + "Diff", + &[ + ("rewrite_id", "node-id-32"), + ("basis_head_id", "node-id-32"), + ("next_head_id", "node-id-32"), + ("start_byte", "u64"), + ("end_byte", "u64"), + ("inserted_byte_length", "u64"), + ("deleted_byte_length", "u64"), + ], + ); +} + +fn assert_declared_types(schema: &Value, fact_name: &str, expected: &[(&str, &str)]) { + let declaration = schema["facts"] + .as_array() + .expect("facts should be an array") + .iter() + .find(|declaration| declaration["name"] == fact_name) + .expect("declared fact should exist"); + let actual = declaration["fields"] + .as_array() + .expect("fields should be an array") + .iter() + .map(|field| { + ( + field["name"].as_str().expect("field should have a name"), + field["type"].as_str().expect("field should have a type"), + ) + }) + .collect::>(); + assert_eq!(actual, expected, "{fact_name} field-type drift"); +} + +#[test] +fn string_escape_golden_vector_matches_the_native_writer() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + let vectors: Value = + serde_json::from_slice(CODEC_VECTOR_BYTES).expect("codec vectors should decode"); + let golden = &schema["factCodec"]["stringEscaping"]["goldenVector"]; + let source_bytes = hex::decode( + golden["sourceUtf8Hex"] + .as_str() + .expect("golden source should be hexadecimal"), + ) + .expect("golden source should decode"); + let source = std::str::from_utf8(&source_bytes).expect("golden source should be UTF-8"); + let expected_json = hex::decode( + golden["jsonStringUtf8Hex"] + .as_str() + .expect("golden JSON should be hexadecimal"), + ) + .expect("golden JSON should decode"); + assert_eq!(serde_json::to_vec(source).unwrap(), expected_json); + assert_eq!(vectors["vectors"][0]["value"]["buffer_key"], source); +} + +#[test] +fn oracle_invocation_bytes_follow_the_published_u64_codec() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + let schema_order = schema["oracleInvocation"]["objectMemberOrder"] + .as_array() + .expect("invocation member order should be an array") + .iter() + .map(|name| { + name.as_str() + .expect("member name should be a string") + .to_owned() + }) + .collect::>(); + let corpus: CorpusProjection = + serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); + for case in &corpus.cases { + let bytes = hex::decode(&case.invocation_bytes_hex) + .expect("invocation bytes should be hexadecimal"); + let actual_order = serde_json::from_slice::(&bytes) + .expect("invocation bytes should decode as an object"); + assert_eq!( + actual_order.0, schema_order, + "{} member-order drift", + case.id + ); + assert_eq!( + hex::encode(serde_json::to_vec(&case.invocation).unwrap()), + case.invocation_bytes_hex, + "{} invocation bytes drifted", + case.id + ); + } + let above_i32 = corpus + .cases + .iter() + .find(|case| case.id == "coordinate-above-graphql-int") + .expect("fixed-width witness should exist"); + assert_eq!(above_i32.invocation.start_byte, i32::MAX as u64 + 1); + assert_eq!(above_i32.invocation.end_byte, i32::MAX as u64 + 1); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index c285caeb..904217fe 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -13,12 +13,15 @@ use warp_core::{ #[path = "replace_range_basis.rs"] mod basis; +#[path = "replace_range_source_set.rs"] +mod source_set; pub use basis::BasisSetup; use basis::{apply_ops, make_basis}; +use source_set::{source_set, SourceSet}; const ORACLE_WARP_LABEL: &str = "jedit.replace-range.oracle.v1"; -const SOURCE_COMMIT: &str = "c70e12d73b4b00bc92412bab67e1761f7dd22f82"; +const SEMANTIC_BASELINE_COMMIT: &str = "c70e12d73b4b00bc92412bab67e1761f7dd22f82"; #[derive(Clone, Copy)] pub enum ExpectedPosture { @@ -48,7 +51,9 @@ pub struct OracleCorpus { schema_version: u32, coordinate: &'static str, application_schema_coordinate: &'static str, - source_commit: &'static str, + invocation_schema_coordinate: &'static str, + semantic_baseline_commit: &'static str, + source_set: SourceSet, evidence_grade: &'static str, independence_limit: &'static str, warp_id: String, @@ -62,6 +67,7 @@ struct OracleCase { purpose: &'static str, basis_facts: Vec, invocation: InvocationProjection, + invocation_bytes_hex: String, terminal: TerminalProjection, } @@ -163,7 +169,9 @@ pub fn generate_corpus(specs: Vec) -> OracleCorpus { schema_version: 1, coordinate: "jedit.text.ReplaceRange.oracle@1", application_schema_coordinate: "jedit.text.schema@1", - source_commit: SOURCE_COMMIT, + invocation_schema_coordinate: "jedit.text.ReplaceRange.oracle-invocation@1", + semantic_baseline_commit: SEMANTIC_BASELINE_COMMIT, + source_set: source_set(), evidence_grade: "deterministic-self-validation", independence_limit: "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", warp_id: hex::encode(warp_id.as_bytes()), @@ -189,6 +197,9 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { end_byte: spec.end_byte, replacement_utf8_hex: hex::encode(spec.replacement.as_bytes()), }; + let invocation_bytes_hex = hex::encode( + serde_json::to_vec(&invocation).expect("oracle invocation should encode canonically"), + ); let before = project_store(&store); let first = plan_replace( &store, @@ -237,6 +248,7 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { purpose: spec.purpose, basis_facts, invocation, + invocation_bytes_hex, terminal, } } diff --git a/native/jedit-echo-host/tests/support/replace_range_schema.rs b/native/jedit-echo-host/tests/support/replace_range_schema.rs new file mode 100644 index 00000000..e572a64e --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_schema.rs @@ -0,0 +1,252 @@ +use jedit_echo_host::records::{ + BlobFact, BranchFact, BufferFact, ContentAddressedFact, DiffFact, HeadFact, LeafFact, + RewriteFact, TypedFact, BLOB_CONTENT_HASH_DOMAIN, BUFFER_NODE_ID_DOMAIN, + EMPTY_ROOT_DIGEST_DOMAIN, +}; +use jedit_echo_host::rope::MAX_LEAF_BYTES; +use serde_json::{json, Value}; + +pub const SCHEMA_COORDINATE: &str = "jedit.text.schema@1"; +pub const STRING_ESCAPE_VECTOR: &str = concat!( + "\u{0000}\u{0001}\u{0002}\u{0003}\u{0004}\u{0005}\u{0006}\u{0007}", + "\u{0008}\u{0009}\u{000a}\u{000b}\u{000c}\u{000d}\u{000e}\u{000f}", + "\u{0010}\u{0011}\u{0012}\u{0013}\u{0014}\u{0015}\u{0016}\u{0017}", + "\u{0018}\u{0019}\u{001a}\u{001b}\u{001c}\u{001d}\u{001e}\u{001f}", + "\"\\/猫\u{2028}\u{2029}" +); +const STRING_ESCAPE_VECTOR_JSON_HEX: &str = concat!( + "225c75303030305c75303030315c75303030325c75303030335c7530303034", + "5c75303030355c75303030365c75303030375c625c745c6e5c7530303062", + "5c665c725c75303030655c75303030665c75303031305c75303031315c7530", + "3031325c75303031335c75303031345c75303031355c75303031365c753030", + "31375c75303031385c75303031395c75303031615c75303031625c75303031", + "635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e2", + "80a922" +); + +pub fn expected_schema() -> Value { + json!({ + "schemaVersion": 1, + "coordinate": SCHEMA_COORDINATE, + "factCodec": { + "profile": "jedit.compact-serde-json.v1", + "encoding": "utf-8", + "byteOrderMark": "forbidden", + "insignificantWhitespace": "forbidden", + "objectMemberOrder": "declared-field-order", + "unsignedInteger": "decimal-no-leading-zeroes", + "optionalIdentifier": "null-or-array-32-u8", + "identifier": "array-32-u8", + "byteString": "array-u8", + "stringEscaping": { + "quotationMark": "backslash-quotation-mark", + "reverseSolidus": "backslash-reverse-solidus", + "shortEscapes": { + "u0008": "backslash-b", + "u0009": "backslash-t", + "u000a": "backslash-n", + "u000c": "backslash-f", + "u000d": "backslash-r" + }, + "otherU0000ThroughU001f": "backslash-u00xx-lowercase-hex", + "solidus": "literal", + "otherU0020ThroughU10ffff": "literal-utf8-unicode-scalars", + "surrogateEscapes": "forbidden", + "goldenVector": { + "sourceUtf8Hex": hex::encode(STRING_ESCAPE_VECTOR.as_bytes()), + "jsonStringUtf8Hex": STRING_ESCAPE_VECTOR_JSON_HEX + } + }, + "forbiddenValues": [ + "map-valued-fields", + "floats", + "signed-integers", + "duplicate-members", + "unknown-members" + ] + }, + "graphEncoding": { + "node": "typed-node", + "attachment": { + "key": "node-alpha", + "value": "atom", + "atomType": "node-type", + "payload": "canonical-fact-bytes" + }, + "edges": "none" + }, + "identityLaws": { + "typeId": { + "algorithm": "blake3-256", + "domainHex": hex::encode(b"type:"), + "material": "type-label-utf8" + }, + "bufferNode": { + "algorithm": "blake3-256", + "domainHex": hex::encode(BUFFER_NODE_ID_DOMAIN), + "material": "buffer-key-utf8" + }, + "contentFact": { + "algorithm": "blake3-256", + "material": "fact-domain || canonical-fact-bytes" + }, + "blobContent": { + "algorithm": "blake3-256", + "domainHex": hex::encode(BLOB_CONTENT_HASH_DOMAIN), + "material": "blob-bytes" + }, + "emptyRootDigest": { + "algorithm": "blake3-256", + "domainHex": hex::encode(EMPTY_ROOT_DIGEST_DOMAIN), + "material": "empty-bytes" + } + }, + "ropeLaw": { + "coordinateType": "u64", + "range": "half-open-utf8-byte", + "replacement": "utf8-bytes", + "maxLeafBytes": MAX_LEAF_BYTES, + "leafChunkBoundary": "largest-utf8-boundary-at-or-before-limit", + "leafSlices": "may-share-blob-with-nonzero-byte-start", + "lineCount": "line-breaks-plus-one-including-empty", + "nonemptyRootDigest": "root-node-id", + "persistence": "path-copy-no-delete", + "emittedOperations": ["upsert-node", "set-node-alpha"] + }, + "oracleInvocation": { + "coordinate": "jedit.text.ReplaceRange.oracle-invocation@1", + "posture": "finite-corpus-input-not-runtime-abi", + "encoding": "compact-utf8-json", + "objectMemberOrder": [ + "bufferId", + "basisHeadId", + "startByte", + "endByte", + "replacementUtf8Hex" + ], + "fields": [ + {"name": "bufferId", "type": "lowercase-hex-node-id-32"}, + {"name": "basisHeadId", "type": "lowercase-hex-node-id-32"}, + {"name": "startByte", "type": "u64-json-decimal"}, + {"name": "endByte", "type": "u64-json-decimal"}, + {"name": "replacementUtf8Hex", "type": "lowercase-hex-utf8-bytes"} + ], + "canonicalBytesField": "invocationBytesHex" + }, + "facts": [ + fact_declaration::( + "Buffer", + json!({ + "kind": "keyed", + "domainHex": hex::encode(BUFFER_NODE_ID_DOMAIN), + "material": "buffer_key utf8" + }), + &[ + ("buffer_key", "utf8-string"), + ("projection_path", "optional-utf8-string"), + ("canonical_head_id", "node-id-32"), + ("version", "u64") + ] + ), + content_fact_declaration::( + "Blob", + &[("content_hash", "digest-32"), ("bytes", "bytes")] + ), + content_fact_declaration::( + "Leaf", + &[ + ("blob_id", "node-id-32"), + ("byte_start", "u64"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_breaks", "u64") + ] + ), + content_fact_declaration::( + "Branch", + &[ + ("left", "node-id-32"), + ("right", "node-id-32"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_breaks", "u64"), + ("height", "u32") + ] + ), + content_fact_declaration::( + "Head", + &[ + ("buffer_id", "node-id-32"), + ("basis_head_id", "optional-node-id-32"), + ("root_node_id", "optional-node-id-32"), + ("byte_length", "u64"), + ("utf16_length", "u64"), + ("line_count", "u64"), + ("root_digest", "digest-32"), + ("sequence", "u64") + ] + ), + content_fact_declaration::( + "Rewrite", + &[ + ("buffer_id", "node-id-32"), + ("basis_head_id", "node-id-32"), + ("next_head_id", "node-id-32"), + ("start_byte", "u64"), + ("end_byte", "u64"), + ("inserted_byte_length", "u64") + ] + ), + content_fact_declaration::( + "Diff", + &[ + ("rewrite_id", "node-id-32"), + ("basis_head_id", "node-id-32"), + ("next_head_id", "node-id-32"), + ("start_byte", "u64"), + ("end_byte", "u64"), + ("inserted_byte_length", "u64"), + ("deleted_byte_length", "u64") + ] + ) + ], + "excludedPropositions": [ + "jedit.text.RopeStructuralMaintenance", + "jedit.text.RopeCheckpoint", + "jedit.text.RopeCheckpointAnchored", + "receipt-attribution-fields", + "structural-echo-edges", + "typed-diff-spans" + ], + "migrationPosture": { + "nativeJsonV1": "authoritative", + "typescriptGraphRope": "not-an-alternate-encoding", + "codecOrStructuralChange": "new-schema-coordinate-and-admitted-migration" + } + }) +} + +fn fact_declaration(name: &str, identity: Value, fields: &[(&str, &str)]) -> Value { + json!({ + "name": name, + "typeLabel": T::TYPE_LABEL, + "identity": identity, + "fields": fields + .iter() + .map(|(field, kind)| json!({"name": field, "type": kind})) + .collect::>() + }) +} + +fn content_fact_declaration(name: &str, fields: &[(&str, &str)]) -> Value { + fact_declaration::( + name, + json!({ + "kind": "content-addressed", + "algorithm": "blake3-256", + "domainHex": hex::encode(T::ID_DOMAIN), + "material": "domain || canonical-fact-bytes" + }), + fields, + ) +} diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs new file mode 100644 index 00000000..ba9729a2 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -0,0 +1,74 @@ +use serde::Serialize; +use sha2::{Digest, Sha256}; + +const SOURCE_SET_DOMAIN: &[u8] = b"jedit.replace-range.oracle-source.v1\0"; +const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ + ( + "native/jedit-echo-host/src/error.rs", + include_bytes!("../../src/error.rs"), + ), + ( + "native/jedit-echo-host/src/identity.rs", + include_bytes!("../../src/identity.rs"), + ), + ( + "native/jedit-echo-host/src/records.rs", + include_bytes!("../../src/records.rs"), + ), + ( + "native/jedit-echo-host/src/rope.rs", + include_bytes!("../../src/rope.rs"), + ), + ( + "native/jedit-echo-host/src/rope/tree.rs", + include_bytes!("../../src/rope/tree.rs"), + ), + ( + "native/jedit-echo-host/src/rope/window.rs", + include_bytes!("../../src/rope/window.rs"), + ), + ( + "native/jedit-echo-host/tests/replace_range_oracle.rs", + include_bytes!("../replace_range_oracle.rs"), + ), + ( + "native/jedit-echo-host/tests/support/replace_range_basis.rs", + include_bytes!("replace_range_basis.rs"), + ), + ( + "native/jedit-echo-host/tests/support/replace_range_oracle.rs", + include_bytes!("replace_range_oracle.rs"), + ), + ( + "native/jedit-echo-host/tests/support/replace_range_source_set.rs", + include_bytes!("replace_range_source_set.rs"), + ), +]; + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SourceSet { + algorithm: &'static str, + domain_hex: String, + framing: &'static str, + paths: Vec<&'static str>, + digest_hex: String, +} + +pub fn source_set() -> SourceSet { + let mut digest = Sha256::new(); + digest.update(SOURCE_SET_DOMAIN); + for (path, bytes) in SOURCE_SET_FILES { + digest.update((path.len() as u32).to_be_bytes()); + digest.update(path.as_bytes()); + digest.update((bytes.len() as u64).to_be_bytes()); + digest.update(bytes); + } + SourceSet { + algorithm: "sha256", + domain_hex: hex::encode(SOURCE_SET_DOMAIN), + framing: "domain || repeated(u32be-path-length || path-utf8 || u64be-byte-length || bytes)", + paths: SOURCE_SET_FILES.iter().map(|(path, _)| *path).collect(), + digest_hex: hex::encode(digest.finalize()), + } +} diff --git a/native/jedit-echo-host/tests/support/resource_fixture.rs b/native/jedit-echo-host/tests/support/resource_fixture.rs new file mode 100644 index 00000000..2b7cb7fb --- /dev/null +++ b/native/jedit-echo-host/tests/support/resource_fixture.rs @@ -0,0 +1,53 @@ +use std::ffi::OsStr; +use std::fs; +use std::path::Path; + +use sha2::{Digest, Sha256}; + +pub fn checked_sha256(value: &str) -> &str { + let digest = value + .strip_suffix('\n') + .expect("digest resource should end with exactly one newline"); + assert_eq!(digest.len(), 64, "digest should contain 64 characters"); + assert!( + digest + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)), + "digest should contain lowercase hexadecimal only" + ); + digest +} + +pub fn sha256_hex(bytes: &[u8]) -> String { + hex::encode(Sha256::digest(bytes)) +} + +pub fn update_resource_pair( + env_name: &str, + resource_path: &Path, + digest_path: &Path, + bytes: &[u8], +) -> bool { + match std::env::var_os(env_name) { + None => false, + Some(value) if value == OsStr::new("1") => { + write_atomically(resource_path, bytes); + let digest = format!("{}\n", sha256_hex(bytes)); + write_atomically(digest_path, digest.as_bytes()); + true + } + Some(value) => { + panic!("{env_name} must be exactly 1 to update fixtures, received {value:?}") + } + } +} + +fn write_atomically(path: &Path, bytes: &[u8]) { + let file_name = path + .file_name() + .and_then(OsStr::to_str) + .expect("resource path should have a UTF-8 file name"); + let temporary = path.with_file_name(format!(".{file_name}.{}.tmp", std::process::id())); + fs::write(&temporary, bytes).expect("temporary resource should write"); + fs::rename(&temporary, path).expect("temporary resource should replace destination"); +} diff --git a/package.json b/package.json index ab7efe28..93ed490c 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "echo:build": "cargo build --manifest-path native/jedit-echo-host/Cargo.toml", "echo:test": "cargo test --manifest-path native/jedit-echo-host/Cargo.toml", + "lawpack:replace-range:update": "JEDIT_UPDATE_REPLACE_RANGE_SCHEMA=1 cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema published_schema_and_codec_vectors_regenerate_byte_for_byte && cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema && JEDIT_UPDATE_REPLACE_RANGE_ORACLE=1 cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_oracle && cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_oracle && cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema_conformance", "dev": "npm run echo:build && tsx src/main.ts", "clean": "node scripts/clean-dist.mjs", "build": "npm run echo:build && npm run clean && tsc -p tsconfig.json && node scripts/copy-assets.mjs", From 5f0ae9a4cb51c1004ed1f4e2a85a5928d59e1bac Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 16:40:27 -0700 Subject: [PATCH 05/63] docs: tighten ReplaceRange source evidence --- docs/design/0158-replace-range-canonical-fact-law.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 94000081..5bfaeb49 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -59,7 +59,8 @@ The merge target is [`native/jedit-echo-host/src/records.rs#L7:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/records.rs#L7) - Native fact bytes are produced by `serde_json::to_vec`; content-addressed fact identifiers hash an exact per-fact domain followed by those bytes. - [`native/jedit-echo-host/src/records.rs#L123:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/records.rs#L123) + [`native/jedit-echo-host/src/records.rs#L150:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/records.rs#L150) + [`native/jedit-echo-host/src/identity.rs#L6:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/identity.rs#L6) - The planner stores each fact as one typed node plus one node-alpha atom attachment. It emits no Echo edges and deletes no retained nodes. [`native/jedit-echo-host/src/rope.rs#L217:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope.rs#L217) @@ -70,6 +71,8 @@ The merge target is - Rope construction caps leaves at 4,096 UTF-8 bytes, path-copies slices, and deterministically height-balances joins. [`native/jedit-echo-host/src/rope/tree.rs#L9:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope/tree.rs#L9) + [`native/jedit-echo-host/src/rope/tree.rs#L69:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope/tree.rs#L69) + [`native/jedit-echo-host/src/rope/tree.rs#L148:c70e12d73b4b00bc92412bab67e1761f7dd22f82`](https://github.com/flyingrobots/jedit/blob/c70e12d73b4b00bc92412bab67e1761f7dd22f82/native/jedit-echo-host/src/rope/tree.rs#L148) - The TypeScript graph-rope model contains propositions and fields that are not equivalent to the native retained representation. It is therefore not an alternate serialization of the same version-1 schema. From 8766cd8c9e9207bf34f896732d9c48e34b7be15e Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 18:40:57 -0700 Subject: [PATCH 06/63] Fix: prove real stale and foreign bases --- CHANGELOG.md | 4 + .../replace-range-v1.oracle.json | 114 ++++++++++++++++-- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 11 +- .../tests/replace_range_oracle.rs | 110 ++++++++++++++--- .../tests/support/replace_range_basis.rs | 27 ++++- .../tests/support/replace_range_oracle.rs | 5 +- 7 files changed, 233 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a7d998d..84091ac6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Replaced the fabricated `ReplaceRange` noncanonical-basis fixture with + distinct retained stale-head and real foreign-head obstruction witnesses. + Both causal postures now prove that planning yields no patch and leaves the + parent graph unchanged. - Hard-cut production text authority to the real Echo WASM boundary. Jim now requires and initializes a configured Echo kernel before constructing the workspace; when the generated Jim Edict package is unavailable, every text diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index fbdd0366..0858eeae 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -20,7 +20,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "d68c0e8f9b243f15608ac6ddc06e3cdec90ca29b644d22615b784d58f05b101c" + "digestHex": "66991acc3aad646ae9a89279beacaf8e5bbf5526d31ed457fa3755ce2c0c06cc" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", @@ -1587,18 +1587,48 @@ } }, { - "id": "noncanonical-basis", - "purpose": "exact canonical-head equality covers stale and foreign basis posture", + "id": "stale-basis", + "purpose": "typed no-plan ReplaceRange obstruction witness", "basisFacts": [ + { + "nodeId": "1520e6699245a445b3aa9d319e9da8dcb7f5be63289bf249972e84dfc2ae462d", + "typeId": "c30835603fa3940cf7845d961d0e41d338c40fea99698639dfc155117465943a", + "attachmentBytesHex": "7b226275666665725f6964223a5b3136382c3230322c3132332c3133312c39392c3133372c3133322c39342c37362c3231322c3139312c33342c3232372c37372c33352c3133302c33362c302c3139392c3233332c34312c3233392c3138362c36342c36372c35352c3133342c37302c35342c302c39302c33385d2c2262617369735f686561645f6964223a5b3134392c3230372c3132352c32342c3234372c3131312c38342c3134362c38342c33342c3135382c33382c32372c3135382c3135342c3130372c3233342c37392c3235352c3134342c3130332c3132312c34322c39332c3230342c3235312c3135322c32322c3139312c3235332c3137302c3133305d2c226e6578745f686561645f6964223a5b3131352c3134342c3133372c39372c3134342c3135302c3132302c3139352c39342c35392c3137372c35302c3233352c3137352c3137392c3233322c3134312c3136322c34382c382c39332c37332c3134322c3233352c3132392c36302c36332c38372c3139342c3131322c3132312c3135305d2c2273746172745f62797465223a302c22656e645f62797465223a302c22696e7365727465645f627974655f6c656e677468223a367d" + }, + { + "nodeId": "27f8f8c5f95e4a7c2e778b2a00ad90659685d759ec471b3e03084274a3e695ae", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233302c3130392c3131392c36312c3138352c32302c3233322c37362c3137332c36372c34342c3130382c3139392c37342c3233352c3139392c3231342c3139322c3133302c3133382c3134322c3235302c36352c35362c3233362c3233392c35362c3134392c34372c3139332c33342c3234315d2c226279746573223a5b3130322c3131342c3130312c3131352c3130342c34355d7d" + }, { "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" }, { - "nodeId": "5ca183a06f61a66776f028640546bad66376510b858f8d8935a876c3dec58301", + "nodeId": "5c3a4a12e2d1fc419246771bd6535b786ceb137953c2feab9c0016fac5b6ed2e", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3234302c38362c3139342c3139372c33352c3139372c31352c3136372c3133312c37322c3233332c36362c3139332c3234382c3131382c3136382c33342c3135362c33392c3130332c3134342c35302c3136362c36372c3233332c37312c38382c3135302c39332c39342c39382c365d2c227269676874223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a392c2275746631365f6c656e677468223a392c226c696e655f627265616b73223a302c22686569676874223a327d" + }, + { + "nodeId": "73908961909678c35e3bb132ebafb3e88da230085d498eeb813c3f57c2707996", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3136382c3230322c3132332c3133312c39392c3133372c3133322c39342c37362c3231322c3139312c33342c3232372c37372c33352c3133302c33362c302c3139392c3233332c34312c3233392c3138362c36342c36372c35352c3133342c37302c35342c302c39302c33385d2c2262617369735f686561645f6964223a5b3134392c3230372c3132352c32342c3234372c3131312c38342c3134362c38342c33342c3135382c33382c32372c3135382c3135342c3130372c3233342c37392c3235352c3134342c3130332c3132312c34322c39332c3230342c3235312c3135322c32322c3139312c3235332c3137302c3133305d2c22726f6f745f6e6f64655f6964223a5b39322c35382c37342c31382c3232362c3230392c3235322c36352c3134362c37302c3131392c32372c3231342c38332c39312c3132302c3130382c3233352c31392c3132312c38332c3139342c3235342c3137312c3135362c302c32322c3235302c3139372c3138322c3233372c34365d2c22627974655f6c656e677468223a392c2275746631365f6c656e677468223a392c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b39322c35382c37342c31382c3232362c3230392c3235322c36352c3134362c37302c3131392c32372c3231342c38332c39312c3132302c3130382c3233352c31392c3132312c38332c3139342c3235342c3137312c3135362c302c32322c3235302c3139372c3138322c3233372c34365d2c2273657175656e6365223a317d" + }, + { + "nodeId": "95cf7d18f76f549254229e261b9e9a6bea4fff9067792a5dccfb9816bffdaa82", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3136382c3230322c3132332c3133312c39392c3133372c3133322c39342c37362c3231322c3139312c33342c3232372c37372c33352c3133302c33362c302c3139392c3233332c34312c3233392c3138362c36342c36372c35352c3133342c37302c35342c302c39302c33385d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "a015c99476489617aa0cd77dec3f9186d3b32024de982cf0e219728c10fc0233", + "typeId": "d5ed88f6007b97cda5106fc3548d6252f164bd86b18f6b41d22e7c6a72f21ccd", + "attachmentBytesHex": "7b22726577726974655f6964223a5b32312c33322c3233302c3130352c3134362c36392c3136342c36392c3137392c3137302c3135372c34392c3135382c3135372c3136382c3232302c3138332c3234352c3139302c39392c34302c3135352c3234322c37332c3135312c34362c3133322c3232332c3139342c3137342c37302c34355d2c2262617369735f686561645f6964223a5b3134392c3230372c3132352c32342c3234372c3131312c38342c3134362c38342c33342c3135382c33382c32372c3135382c3135342c3130372c3233342c37392c3235352c3134342c3130332c3132312c34322c39332c3230342c3235312c3135322c32322c3139312c3235332c3137302c3133305d2c226e6578745f686561645f6964223a5b3131352c3134342c3133372c39372c3134342c3135302c3132302c3139352c39342c35392c3137372c35302c3233352c3137352c3137392c3233322c3134312c3136322c34382c382c39332c37332c3134322c3233352c3132392c36302c36332c38372c3139342c3131322c3132312c3135305d2c2273746172745f62797465223a302c22656e645f62797465223a302c22696e7365727465645f627974655f6c656e677468223a362c2264656c657465645f627974655f6c656e677468223a307d" + }, + { + "nodeId": "a8ca7b836389845e4cd4bf22e34d23822400c7e929efba404337864636005a26", "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", - "attachmentBytesHex": "7b226275666665725f6b6579223a226e6f6e63616e6f6e6963616c2d6261736973222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3234342c3137352c34342c3232302c3138322c3231302c34322c3139322c35312c36322c3137312c3131362c38302c33372c37312c34382c32392c35352c35372c3235302c3234392c3138362c31352c3234362c3139392c35372c3132312c3231312c39332c3137382c3235312c3138325d2c2276657273696f6e223a307d" + "attachmentBytesHex": "7b226275666665725f6b6579223a227374616c652d6261736973222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3131352c3134342c3133372c39372c3134342c3135302c3132302c3139352c39342c35392c3137372c35302c3233352c3137352c3137392c3233322c3134312c3136322c34382c382c39332c37332c3134322c3233352c3132392c36302c36332c38372c3139342c3131322c3132312c3135305d2c2276657273696f6e223a317d" }, { "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", @@ -1606,24 +1636,86 @@ "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" }, { - "nodeId": "f4af2cdcb6d22ac0333eab74502547301d3739faf9ba0ff6c73979d35db2fbb6", + "nodeId": "f056c2c523c50fa78348e942c1f876a8229c27679032a643e94758965d5e6206", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b33392c3234382c3234382c3139372c3234392c39342c37342c3132342c34362c3131392c3133392c34322c302c3137332c3134342c3130312c3135302c3133332c3231352c38392c3233362c37312c32372c36322c332c382c36362c3131362c3136332c3233302c3134392c3137345d2c22627974655f7374617274223a302c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a362c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "a8ca7b836389845e4cd4bf22e34d23822400c7e929efba404337864636005a26", + "basisHeadId": "95cf7d18f76f549254229e261b9e9a6bea4fff9067792a5dccfb9816bffdaa82", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2261386361376238333633383938343565346364346266323265333464323338323234303063376539323965666261343034333337383634363336303035613236222c226261736973486561644964223a2239356366376431386637366635343932353432323965323631623965396136626561346666663930363737393261356463636662393831366266666461613832222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "basis-not-canonical", + "legacyErrorClass": "invalid-request", + "legacyMessage": "stale replace basis 95cf7d18f76f549254229e261b9e9a6bea4fff9067792a5dccfb9816bffdaa82; canonical head is 73908961909678c35e3bb132ebafb3e88da230085d498eeb813c3f57c2707996", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "foreign-basis", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "2b9d085b46f74a38983b02e94a21cfc68c63a99d2dbff2587ff423e9c254df2a", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233372c3139322c34362c3139322c3231332c3232312c36302c37302c3130372c3130332c3137342c3136322c3234312c32312c3131322c32362c332c3231332c3131312c39362c3134302c32392c3231302c33382c35342c3235352c3132382c3135362c3230352c32312c3131362c3135345d2c226279746573223a5b39372c39382c39395d7d" + }, + { + "nodeId": "3a95caa4f8246e57b24eb9e483fdf7e2d856325e70872d4b340cae2d8b0487f2", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3233382c33312c32392c35362c3139382c36372c3233332c3134382c3135332c3230352c3139312c3234392c37342c3139312c39302c3132342c3130352c37392c3234312c3130362c3231322c3139392c35322c3134382c3138392c3131302c3138352c35392c3130302c3133362c3137362c365d2c226279746573223a5b3130322c3131312c3131342c3130312c3130352c3130332c3131305d7d" + }, + { + "nodeId": "69a53646567e20cd6b98932a656fe26bacf1173b75dd149ed51bc289536e5ec0", "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", - "attachmentBytesHex": "7b226275666665725f6964223a5b39322c3136312c3133312c3136302c3131312c39372c3136362c3130332c3131382c3234302c34302c3130302c352c37302c3138362c3231342c39392c3131382c38312c31312c3133332c3134332c3134312c3133372c35332c3136382c3131382c3139352c3232322c3139372c3133312c315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + "attachmentBytesHex": "7b226275666665725f6964223a5b3138352c3233382c3137332c37342c3233312c3130302c3135302c3234312c3135302c3138312c3134332c3136382c3136302c3234392c34312c3130362c3139382c3131342c33312c3135342c3137372c3139362c35382c33332c3133372c38392c3232362c3131302c3132362c3131312c39372c37395d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137372c3134332c3139372c37322c3139382c3132302c3131362c3231372c3235352c3136382c38322c3233382c37332c38352c3234332c33352c39372c32312c3132312c3133392c3132312c3139362c3234352c39372c3139362c3135372c3235332c3232352c3130342c3233322c3231352c3232305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "a4dd16ed3056cdd04cf10f275a5e51a848a20a332b3e4a85e9e97ac21c306f47", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22666f726569676e2d62617369732d666f726569676e222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3137332c36322c3131342c3133392c36352c33382c31392c31302c3234312c3231382c3234352c3232362c3135382c31382c3130392c3133362c3232342c3137302c3135372c3230302c3230392c3235302c3138312c3131382c3137372c3130372c3234382c3132362c3137342c3130392c3136322c3135325d2c2276657273696f6e223a307d" + }, + { + "nodeId": "ad3e728b4126130af1daf5e29e126d88e0aa9dc8d1fab576b16bf87eae6da298", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3136342c3232312c32322c3233372c34382c38362c3230352c3230382c37362c3234312c31352c33392c39302c39342c38312c3136382c37322c3136322c31302c35312c34332c36322c37342c3133332c3233332c3233332c3132322c3139342c32382c34382c3131312c37315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3233332c37342c3136332c3132372c3136342c3132322c37352c35362c3136312c36352c3133312c3132322c37322c3136382c36392c37322c3133392c322c3139302c3135322c3138352c3232362c39332c3230342c39362c39372c3138332c3139352c3134332c3130392c3132322c3231385d2c22627974655f6c656e677468223a372c2275746631365f6c656e677468223a372c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3233332c37342c3136332c3132372c3136342c3132322c37352c35362c3136312c36352c3133312c3132322c37322c3136382c36392c37322c3133392c322c3139302c3135322c3138352c3232362c39332c3230342c39362c39372c3138332c3139352c3134332c3130392c3132322c3231385d2c2273657175656e6365223a307d" + }, + { + "nodeId": "b18fc548c67874d9ffa852ee4955f3236115798b79c4f561c49dfde168e8d7dc", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b34332c3135372c382c39312c37302c3234372c37342c35362c3135322c35392c322c3233332c37342c33332c3230372c3139382c3134302c39392c3136392c3135372c34352c3139312c3234322c38382c3132372c3234342c33352c3233332c3139342c38342c3232332c34325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a332c2275746631365f6c656e677468223a332c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "b9eead4ae76496f196b58fa8a0f9296ac6721f9ab1c43a218959e26e7e6f614f", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22666f726569676e2d6261736973222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3130352c3136352c35342c37302c38362c3132362c33322c3230352c3130372c3135322c3134372c34322c3130312c3131312c3232362c3130372c3137322c3234312c32332c35392c3131372c3232312c32302c3135382c3231332c32372c3139342c3133372c38332c3131302c39342c3139325d2c2276657273696f6e223a307d" + }, + { + "nodeId": "e94aa37fa47a4b38a141837a48a845488b02be98b9e25dcc6061b7c38f6d7ada", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b35382c3134392c3230322c3136342c3234382c33362c3131302c38372c3137382c37382c3138352c3232382c3133312c3235332c3234372c3232362c3231362c38362c35302c39342c3131322c3133352c34352c37352c35322c31322c3137342c34352c3133392c342c3133352c3234325d2c22627974655f7374617274223a302c22627974655f6c656e677468223a372c2275746631365f6c656e677468223a372c226c696e655f627265616b73223a307d" } ], "invocation": { - "bufferId": "5ca183a06f61a66776f028640546bad66376510b858f8d8935a876c3dec58301", - "basisHeadId": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5", + "bufferId": "b9eead4ae76496f196b58fa8a0f9296ac6721f9ab1c43a218959e26e7e6f614f", + "basisHeadId": "ad3e728b4126130af1daf5e29e126d88e0aa9dc8d1fab576b16bf87eae6da298", "startByte": 0, "endByte": 1, "replacementUtf8Hex": "78" }, - "invocationBytesHex": "7b226275666665724964223a2235636131383361303666363161363637373666303238363430353436626164363633373635313062383538663864383933356138373663336465633538333031222c226261736973486561644964223a2261356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135613561356135222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", + "invocationBytesHex": "7b226275666665724964223a2262396565616434616537363439366631393662353866613861306639323936616336373231663961623163343361323138393539653236653765366636313466222c226261736973486561644964223a2261643365373238623431323631333061663164616635653239653132366438386530616139646338643166616235373662313662663837656165366461323938222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", "terminal": { "posture": "obstructed", "semanticCode": "basis-not-canonical", "legacyErrorClass": "invalid-request", - "legacyMessage": "stale replace basis a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5; canonical head is f4af2cdcb6d22ac0333eab74502547301d3739faf9ba0ff6c73979d35db2fbb6", + "legacyMessage": "stale replace basis ad3e728b4126130af1daf5e29e126d88e0aa9dc8d1fab576b16bf87eae6da298; canonical head is 69a53646567e20cd6b98932a656fe26bacf1173b75dd149ed51bc289536e5ec0", "parentGraphUnchanged": true, "patchPosture": "no-mutation-plan" } diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 9a665da8..13b16b04 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -313afabca41f36f6d567d11533d65fbf5ed2e33546705d20885c48f60bc69971 +08edc645a83f5a65754bb43e898f8b3f2be614c8b6e8f089dc5d9d48232254ef diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 5bfaeb49..4bad2762 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -407,7 +407,7 @@ the declaration, codec vectors, corpus, and all sidecars, then proves check mode is clean. Inspect `contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json` for the exact fact, string, identity, rope, and corpus-invocation laws. Inspect -`replace-range-v1.oracle.json` for 19 self-contained cases with basis facts, +`replace-range-v1.oracle.json` for 20 self-contained cases with basis facts, exact invocation bytes, support, ordered patches, results, or typed no-patch obstructions. No editor, terminal rendering, external service, or sibling checkout is required. @@ -446,7 +446,8 @@ Mitigations: What changed from the design: -- The planned native-schema and 19-case corpus was implemented without +- The planned native-schema corpus was implemented with 20 cases, separating + retained stale-head and real foreign-head basis witnesses, without changing the production planner, fact bytes, identities, or routing. - Review tightened the declaration with a fully specified string codec, a corpus-only invocation schema and exact bytes, mechanical schema-to-writer @@ -458,9 +459,9 @@ What changed from the design: for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `313afabca41f36f6d567d11533d65fbf5ed2e33546705d20885c48f60bc69971` + `08edc645a83f5a65754bb43e898f8b3f2be614c8b6e8f089dc5d9d48232254ef` for the oracle. The oracle source-set digest is - `d68c0e8f9b243f15608ac6ddc06e3cdec90ca29b644d22615b784d58f05b101c`. + `66991acc3aad646ae9a89279beacaf8e5bbf5526d31ed457fa3755ce2c0c06cc`. What the tests proved: @@ -468,7 +469,7 @@ What the tests proved: every declared fact-member order equals the order observed from writer bytes; the full string escape vector matches the native writer; and corpus invocation bytes preserve `u64` above `i32::MAX`. -- Six success and thirteen obstruction cases regenerate deterministically. A +- Six success and fourteen obstruction cases regenerate deterministically. A success yields the exact support, ordered patch, retained facts, metrics, and materialized consequence; an obstruction yields no `MutationPlan` and leaves the parent graph unchanged. diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs index dd547b0b..7b241c12 100644 --- a/native/jedit-echo-host/tests/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -5,6 +5,7 @@ mod resource_fixture; use std::path::PathBuf; +use jedit_echo_host::records::{fact_type_id, BufferFact, HeadFact}; use oracle_support::{ canonical_corpus_bytes, generate_corpus, BasisSetup, CaseSpec, ExpectedPosture, }; @@ -37,6 +38,76 @@ fn replace_range_oracle_matches_the_committed_corpus() { ); } +#[test] +fn basis_obstructions_name_real_retained_heads() { + let corpus: serde_json::Value = serde_json::from_slice( + &std::fs::read(corpus_path()).expect("committed oracle corpus should exist"), + ) + .expect("committed oracle corpus should decode"); + let cases = corpus["cases"] + .as_array() + .expect("oracle cases should be an array"); + + assert_retained_basis(cases, "stale-basis", true); + assert_retained_basis(cases, "foreign-basis", false); +} + +fn assert_retained_basis(cases: &[serde_json::Value], case_id: &str, same_buffer: bool) { + let case = cases + .iter() + .find(|case| case["id"] == case_id) + .unwrap_or_else(|| panic!("{case_id} should exist")); + let invocation_buffer_id = case["invocation"]["bufferId"] + .as_str() + .expect("invocation buffer should be a string"); + let invocation_basis_id = case["invocation"]["basisHeadId"] + .as_str() + .expect("invocation basis should be a string"); + let facts = case["basisFacts"] + .as_array() + .expect("basis facts should be an array"); + let head_type_id = hex::encode(fact_type_id::().as_bytes()); + let retained_head = facts + .iter() + .find(|fact| fact["nodeId"] == invocation_basis_id && fact["typeId"] == head_type_id) + .unwrap_or_else(|| panic!("{case_id} basis should be a retained Head fact")); + let head: HeadFact = serde_json::from_slice( + &hex::decode( + retained_head["attachmentBytesHex"] + .as_str() + .expect("head bytes should be hexadecimal"), + ) + .expect("head bytes should decode"), + ) + .expect("retained head should decode"); + let head_buffer_id = hex::encode(head.buffer_id.0); + assert_eq!( + head_buffer_id == invocation_buffer_id, + same_buffer, + "{case_id} buffer relationship drifted" + ); + + let buffer_type_id = hex::encode(fact_type_id::().as_bytes()); + let retained_buffer = facts + .iter() + .find(|fact| fact["nodeId"] == invocation_buffer_id && fact["typeId"] == buffer_type_id) + .unwrap_or_else(|| panic!("{case_id} target Buffer fact should be retained")); + let buffer: BufferFact = serde_json::from_slice( + &hex::decode( + retained_buffer["attachmentBytesHex"] + .as_str() + .expect("buffer bytes should be hexadecimal"), + ) + .expect("buffer bytes should decode"), + ) + .expect("retained buffer should decode"); + assert_ne!( + invocation_basis_id, + hex::encode(buffer.canonical_head_id.0), + "{case_id} must not use the current canonical head" + ); +} + fn corpus_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json") @@ -147,21 +218,28 @@ fn cases() -> Vec { "invalid-request", "no-op", ), - CaseSpec { - id: "noncanonical-basis", - purpose: "exact canonical-head equality covers stale and foreign basis posture", - initial_text: "abc".to_owned(), - setup: BasisSetup::Plain, - basis_override: Some([0xA5; 32]), - start_byte: 0, - end_byte: 1, - replacement: "x".to_owned(), - expected: ExpectedPosture::Obstruction { - semantic_code: "basis-not-canonical", - error_class: "invalid-request", - message_fragment: "stale replace basis", - }, - }, + obstruction( + "stale-basis", + "abc", + BasisSetup::StaleHead, + 0, + 1, + "x", + "basis-not-canonical", + "invalid-request", + "stale replace basis", + ), + obstruction( + "foreign-basis", + "abc", + BasisSetup::ForeignHead, + 0, + 1, + "x", + "basis-not-canonical", + "invalid-request", + "stale replace basis", + ), obstruction( "head-sequence-overflow", "abc", @@ -244,7 +322,6 @@ fn success( purpose: "finite persistent-rope ReplaceRange conformance witness", initial_text: initial_text.to_owned(), setup, - basis_override: None, start_byte, end_byte, replacement: replacement.to_owned(), @@ -269,7 +346,6 @@ fn obstruction( purpose: "typed no-plan ReplaceRange obstruction witness", initial_text: initial_text.to_owned(), setup, - basis_override: None, start_byte, end_byte, replacement: replacement.to_owned(), diff --git a/native/jedit-echo-host/tests/support/replace_range_basis.rs b/native/jedit-echo-host/tests/support/replace_range_basis.rs index a6127570..6f31cc9c 100644 --- a/native/jedit-echo-host/tests/support/replace_range_basis.rs +++ b/native/jedit-echo-host/tests/support/replace_range_basis.rs @@ -2,7 +2,7 @@ use jedit_echo_host::records::{ decode_fact, fact_bytes, fact_id, fact_type_id, BlobFact, BufferFact, HeadFact, LeafFact, NodeIdBytes, }; -use jedit_echo_host::rope::plan_create; +use jedit_echo_host::rope::{plan_create, plan_replace}; use warp_core::{AttachmentValue, GraphStore, NodeId, NodeRecord, TickDelta, WarpOp}; #[derive(Clone, Copy)] @@ -15,6 +15,8 @@ pub enum BasisSetup { MalformedBuffer, BadBlobContentHash, AboveGraphqlIntRange, + StaleHead, + ForeignHead, } pub fn make_basis( @@ -22,12 +24,13 @@ pub fn make_basis( id: &str, initial_text: &str, setup: BasisSetup, -) -> (GraphStore, NodeId, NodeId) { +) -> (GraphStore, NodeId, NodeId, Option) { if matches!(setup, BasisSetup::MissingBuffer) { return ( GraphStore::new(warp_id), NodeId([0x11; 32]), NodeId([0x22; 32]), + None, ); } let basis_text = if matches!(setup, BasisSetup::Empty) { @@ -41,6 +44,7 @@ pub fn make_basis( create.emit(&mut delta); apply_ops(&mut store, &delta.finalize()); let mut head_id = create.head_id; + let mut invocation_basis_id = None; match setup { BasisSetup::Plain | BasisSetup::Empty => {} BasisSetup::SequenceOverflow => { @@ -68,9 +72,26 @@ pub fn make_basis( head.byte_length = i32::MAX as u64 + 1; }); } + BasisSetup::StaleHead => { + invocation_basis_id = Some(head_id); + let replacement = plan_replace(&store, create.buffer_id, head_id, 0, 0, "fresh-") + .expect("stale-basis setup should advance the canonical head"); + let mut delta = TickDelta::new(); + replacement.emit(&mut delta); + apply_ops(&mut store, &delta.finalize()); + head_id = replacement.head_id; + } + BasisSetup::ForeignHead => { + let foreign = plan_create(&store, &format!("{id}-foreign"), "foreign", None) + .expect("foreign-basis setup should create another buffer"); + let mut delta = TickDelta::new(); + foreign.emit(&mut delta); + apply_ops(&mut store, &delta.finalize()); + invocation_basis_id = Some(foreign.head_id); + } BasisSetup::MissingBuffer => unreachable!(), } - (store, create.buffer_id, head_id) + (store, create.buffer_id, head_id, invocation_basis_id) } pub fn apply_ops(store: &mut GraphStore, ops: &[WarpOp]) { diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index 904217fe..4d26ce5e 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -38,7 +38,6 @@ pub struct CaseSpec { pub purpose: &'static str, pub initial_text: String, pub setup: BasisSetup, - pub basis_override: Option<[u8; 32]>, pub start_byte: u64, pub end_byte: u64, pub replacement: String, @@ -186,10 +185,10 @@ pub fn canonical_corpus_bytes(corpus: &OracleCorpus) -> Vec { } fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { - let (store, buffer_id, canonical_head_id) = + let (store, buffer_id, canonical_head_id, invocation_basis_id) = make_basis(warp_id, spec.id, &spec.initial_text, spec.setup); let basis_facts = project_store(&store); - let basis_head_id = spec.basis_override.map_or(canonical_head_id, NodeId); + let basis_head_id = invocation_basis_id.unwrap_or(canonical_head_id); let invocation = InvocationProjection { buffer_id: hex::encode(buffer_id.as_bytes()), basis_head_id: hex::encode(basis_head_id.as_bytes()), From bddf1ad80907893de081856f29b2317b5d672850 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 18:42:47 -0700 Subject: [PATCH 07/63] Fix: bind oracle dependency selection --- CHANGELOG.md | 3 +++ .../replace-range-v1.oracle.json | 4 +++- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 15 ++++++++------ .../tests/replace_range_oracle.rs | 20 +++++++++++++++++++ .../tests/support/replace_range_source_set.rs | 8 ++++++++ 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84091ac6..44d4414f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Bound the native Cargo manifest and lockfile into the published + `ReplaceRange` oracle source-set digest, so generator provenance now includes + exact feature and dependency selection. - Replaced the fabricated `ReplaceRange` noncanonical-basis fixture with distinct retained stale-head and real foreign-head obstruction witnesses. Both causal postures now prove that planning yields no patch and leaves the diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 0858eeae..ddc6f0f3 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -9,6 +9,8 @@ "domainHex": "6a656469742e7265706c6163652d72616e67652e6f7261636c652d736f757263652e763100", "framing": "domain || repeated(u32be-path-length || path-utf8 || u64be-byte-length || bytes)", "paths": [ + "native/jedit-echo-host/Cargo.toml", + "native/jedit-echo-host/Cargo.lock", "native/jedit-echo-host/src/error.rs", "native/jedit-echo-host/src/identity.rs", "native/jedit-echo-host/src/records.rs", @@ -20,7 +22,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "66991acc3aad646ae9a89279beacaf8e5bbf5526d31ed457fa3755ce2c0c06cc" + "digestHex": "5db26270fe5d141d2c7dce323a7f28ab84dc6c6eadb4572a483561035f31379a" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 13b16b04..978ad607 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -08edc645a83f5a65754bb43e898f8b3f2be614c8b6e8f089dc5d9d48232254ef +7ce3331780ae68b8fea2d0232f3607c7b79b24ef187090cb126072b133f2d3b3 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 4bad2762..ab5d3cf6 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -420,7 +420,8 @@ Known risks: - The legacy reader accepts noncanonical JSON that the writer never emits. - The current Wesley/GraphQL invocation remains limited to `i32` coordinates. - A source change outside the declared oracle source set could still affect - behavior through a transitive dependency. + behavior through a dependency selected by the bound Cargo manifest and + lockfile. Mitigations: @@ -430,9 +431,11 @@ Mitigations: evaluator to reject noncanonical or misaddressed facts. - Jedit #285 remains open for the production cutover; this PR does not claim the compatibility ABI has changed. -- The corpus binds the exact Jedit planner, record, identity, error, window, - case generator, basis builder, and source-set framing bytes, while the - repository lockfile and CI preserve transitive dependency selection. +- The corpus binds the exact native Cargo manifest and lockfile together with + the Jedit planner, record, identity, error, window, case generator, basis + builder, and source-set framing bytes. CI preserves the bound dependency + selection while separately compiled Echo evidence remains the independence + boundary. ## Follow-On Debt @@ -459,9 +462,9 @@ What changed from the design: for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `08edc645a83f5a65754bb43e898f8b3f2be614c8b6e8f089dc5d9d48232254ef` + `7ce3331780ae68b8fea2d0232f3607c7b79b24ef187090cb126072b133f2d3b3` for the oracle. The oracle source-set digest is - `66991acc3aad646ae9a89279beacaf8e5bbf5526d31ed457fa3755ce2c0c06cc`. + `5db26270fe5d141d2c7dce323a7f28ab84dc6c6eadb4572a483561035f31379a`. What the tests proved: diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs index 7b241c12..e4885fb9 100644 --- a/native/jedit-echo-host/tests/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -52,6 +52,26 @@ fn basis_obstructions_name_real_retained_heads() { assert_retained_basis(cases, "foreign-basis", false); } +#[test] +fn source_set_binds_native_dependency_selection() { + let corpus: serde_json::Value = serde_json::from_slice( + &std::fs::read(corpus_path()).expect("committed oracle corpus should exist"), + ) + .expect("committed oracle corpus should decode"); + let paths = corpus["sourceSet"]["paths"] + .as_array() + .expect("source-set paths should be an array"); + for required in [ + "native/jedit-echo-host/Cargo.toml", + "native/jedit-echo-host/Cargo.lock", + ] { + assert!( + paths.iter().any(|path| path == required), + "source set must bind {required}" + ); + } +} + fn assert_retained_basis(cases: &[serde_json::Value], case_id: &str, same_buffer: bool) { let case = cases .iter() diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index ba9729a2..9bab345e 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -3,6 +3,14 @@ use sha2::{Digest, Sha256}; const SOURCE_SET_DOMAIN: &[u8] = b"jedit.replace-range.oracle-source.v1\0"; const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ + ( + "native/jedit-echo-host/Cargo.toml", + include_bytes!("../../Cargo.toml"), + ), + ( + "native/jedit-echo-host/Cargo.lock", + include_bytes!("../../Cargo.lock"), + ), ( "native/jedit-echo-host/src/error.rs", include_bytes!("../../src/error.rs"), From 9ffc0e4aa9313eba3774be970adbc1b976e5888a Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 18:49:21 -0700 Subject: [PATCH 08/63] Fix: type oracle obstruction contract --- CHANGELOG.md | 4 + .../replace-range-v1.oracle.json | 3 +- .../replace-range-v1.oracle.sha256 | 2 +- .../replace-range-v1/text-schema-v1.json | 111 ++++++++++ .../replace-range-v1/text-schema-v1.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 24 ++- .../tests/replace_range_oracle.rs | 31 +-- .../tests/replace_range_schema_conformance.rs | 56 +++++ .../tests/support/replace_range_contract.rs | 47 +++++ .../support/replace_range_corpus_contract.rs | 193 ++++++++++++++++++ .../tests/support/replace_range_oracle.rs | 7 +- .../tests/support/replace_range_schema.rs | 104 ++++++++++ .../tests/support/replace_range_source_set.rs | 4 + 13 files changed, 563 insertions(+), 25 deletions(-) create mode 100644 native/jedit-echo-host/tests/support/replace_range_contract.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 44d4414f..c4078539 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Replaced free-form `ReplaceRange` oracle obstruction strings with an + exhaustive semantic enum and published strict corpus, terminal, footprint, + patch, and result shapes. Conformance now rejects unknown fields and semantic + obstruction codes. - Bound the native Cargo manifest and lockfile into the published `ReplaceRange` oracle source-set digest, so generator provenance now includes exact feature and dependency selection. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index ddc6f0f3..533ef03d 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -19,10 +19,11 @@ "native/jedit-echo-host/src/rope/window.rs", "native/jedit-echo-host/tests/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_basis.rs", + "native/jedit-echo-host/tests/support/replace_range_contract.rs", "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "5db26270fe5d141d2c7dce323a7f28ab84dc6c6eadb4572a483561035f31379a" + "digestHex": "4571309c0b4df86aa98f727a62540d51a6fc115d11494cdb6b8e2dd86c0a7f49" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 978ad607..a546c130 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -7ce3331780ae68b8fea2d0232f3607c7b79b24ef187090cb126072b133f2d3b3 +bccde04b7cae5ad8c6044974edf2e116e8cb182328e6622b52c7a021ed7b7d78 diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json index 4a44927f..9af10e84 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json @@ -323,6 +323,117 @@ "nativeJsonV1": "authoritative", "typescriptGraphRope": "not-an-alternate-encoding" }, + "oracleCorpus": { + "basisFactMemberOrder": [ + "nodeId", + "typeId", + "attachmentBytesHex" + ], + "caseMemberOrder": [ + "id", + "purpose", + "basisFacts", + "invocation", + "invocationBytesHex", + "terminal" + ], + "coordinate": "jedit.text.ReplaceRange.oracle@1", + "encoding": "pretty-utf8-json-final-newline", + "footprintMemberOrder": [ + "nodeReads", + "nodeWrites", + "attachmentReads", + "attachmentWrites", + "edgeReads", + "edgeWrites" + ], + "objectMemberOrder": [ + "schemaVersion", + "coordinate", + "applicationSchemaCoordinate", + "invocationSchemaCoordinate", + "semanticBaselineCommit", + "sourceSet", + "evidenceGrade", + "independenceLimit", + "warpId", + "cases" + ], + "patchVariants": [ + { + "kind": "upsert-node", + "memberOrder": [ + "kind", + "nodeId", + "typeId" + ] + }, + { + "kind": "set-node-alpha", + "memberOrder": [ + "kind", + "nodeId", + "typeId", + "attachmentBytesHex" + ] + } + ], + "resultMemberOrder": [ + "bufferId", + "headId", + "rootNodeId", + "rootDigest", + "rewriteId", + "diffId", + "byteLength", + "utf16Length", + "lineCount", + "sequence", + "version", + "materializedTextUtf8Hex" + ], + "sourceSetMemberOrder": [ + "algorithm", + "domainHex", + "framing", + "paths", + "digestHex" + ], + "terminal": { + "committableMemberOrder": [ + "posture", + "footprint", + "patch", + "createdNodeIds", + "updatedNodeIds", + "untouchedBasisNodeIds", + "result" + ], + "discriminant": "posture", + "obstructedMemberOrder": [ + "posture", + "semanticCode", + "legacyErrorClass", + "legacyMessage", + "parentGraphUnchanged", + "patchPosture" + ], + "obstructedPatchPosture": "no-mutation-plan", + "semanticCodes": [ + "range-order-invalid", + "range-out-of-bounds", + "utf8-boundary-invalid", + "no-op", + "basis-not-canonical", + "arithmetic-overflow", + "fact-missing", + "fact-malformed", + "content-identity-mismatch", + "malformed-rope" + ] + }, + "unknownMembers": "forbidden" + }, "oracleInvocation": { "canonicalBytesField": "invocationBytesHex", "coordinate": "jedit.text.ReplaceRange.oracle-invocation@1", diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 index 2319acfe..6893f131 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 @@ -1 +1 @@ -d605042ba33da11f595c4d65bd4cb125c50555c0156d4a4dfbe12ba0c6bda688 +c7bf126245e9b2f8e7ff080983c16ad625f6b341d886281b79317be9a0b3b76d diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index ab5d3cf6..212658da 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -115,6 +115,8 @@ This cycle includes: cross-leaf replacement, and replacement larger than one leaf; - deterministic obstruction cases for stale basis, malformed/out-of-bounds range, invalid UTF-8 boundary, no-op, and arithmetic overflow; +- an exhaustive semantic-obstruction enum plus strict machine-readable corpus, + terminal, footprint, patch, and result shapes; - exact committed initial graph, invocation, read/write support, ordered patch, result, and obstruction evidence for each case; - byte-for-byte regeneration tests and checked SHA-256 resource digests. @@ -245,6 +247,13 @@ must not invoke, link, or share an evaluator implementation with the future Echo operation program. Sharing the published fact schema and comparison codec is disclosed and expected. +The declaration's `oracleCorpus` section freezes the exact envelope, case, +basis-fact, terminal, footprint, patch, and result member sets. Its obstruction +domain is an exhaustive generated Rust enum whose ten kebab-case values are +also enumerated in the machine declaration. A structurally separate +conformance validator rejects unknown members, terminal and patch variants, +and semantic obstruction codes before an external consumer compares evidence. + ## Lower Modes The contract is entirely headless. `cargo test` recomputes and compares the @@ -427,8 +436,8 @@ Mitigations: - Evidence is graded deterministic self-validation until Echo #684 supplies a separately implemented evaluator and finite-corpus comparison. -- The declaration makes canonical writer bytes normative and requires the new - evaluator to reject noncanonical or misaddressed facts. +- The declaration makes canonical writer bytes and corpus shapes normative and + requires the new evaluator to reject noncanonical or misaddressed facts. - Jedit #285 remains open for the production cutover; this PR does not claim the compatibility ABI has changed. - The corpus binds the exact native Cargo manifest and lockfile together with @@ -457,14 +466,17 @@ What changed from the design: field-order comparison, strict/atomic fixture updates, and a digest over the exact declared Jedit source set. These are proof-strengthening additions, not a widening into the deferred Echo ABI. +- Review replaced free-form obstruction labels with an exhaustive generated + enum and added a strict machine-readable corpus shape whose conformance path + rejects unknown members, variants, and semantic codes. - The published SHA-256 digests are - `d605042ba33da11f595c4d65bd4cb125c50555c0156d4a4dfbe12ba0c6bda688` + `c7bf126245e9b2f8e7ff080983c16ad625f6b341d886281b79317be9a0b3b76d` for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `7ce3331780ae68b8fea2d0232f3607c7b79b24ef187090cb126072b133f2d3b3` + `bccde04b7cae5ad8c6044974edf2e116e8cb182328e6622b52c7a021ed7b7d78` for the oracle. The oracle source-set digest is - `5db26270fe5d141d2c7dce323a7f28ab84dc6c6eadb4572a483561035f31379a`. + `4571309c0b4df86aa98f727a62540d51a6fc115d11494cdb6b8e2dd86c0a7f49`. What the tests proved: @@ -472,6 +484,8 @@ What the tests proved: every declared fact-member order equals the order observed from writer bytes; the full string escape vector matches the native writer; and corpus invocation bytes preserve `u64` above `i32::MAX`. +- Strict corpus conformance rejects unknown object members, terminal or patch + variants, and obstruction codes outside the ten-value semantic enum. - Six success and fourteen obstruction cases regenerate deterministically. A success yields the exact support, ordered patch, retained facts, metrics, and materialized consequence; an obstruction yields no `MutationPlan` and leaves diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs index e4885fb9..24f9b138 100644 --- a/native/jedit-echo-host/tests/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -8,6 +8,7 @@ use std::path::PathBuf; use jedit_echo_host::records::{fact_type_id, BufferFact, HeadFact}; use oracle_support::{ canonical_corpus_bytes, generate_corpus, BasisSetup, CaseSpec, ExpectedPosture, + SemanticObstructionCode, }; use resource_fixture::{checked_sha256, sha256_hex, update_resource_pair}; @@ -179,7 +180,7 @@ fn cases() -> Vec { 2, 1, "x", - "range-order-invalid", + SemanticObstructionCode::RangeOrderInvalid, "invalid-request", "exceeds", ), @@ -190,7 +191,7 @@ fn cases() -> Vec { 0, 4, "x", - "range-out-of-bounds", + SemanticObstructionCode::RangeOutOfBounds, "invalid-request", "exceeds", ), @@ -201,7 +202,7 @@ fn cases() -> Vec { 2, 5, "x", - "utf8-boundary-invalid", + SemanticObstructionCode::Utf8BoundaryInvalid, "invalid-request", "splits a UTF-8 code point", ), @@ -212,7 +213,7 @@ fn cases() -> Vec { 1, 4, "x", - "utf8-boundary-invalid", + SemanticObstructionCode::Utf8BoundaryInvalid, "invalid-request", "splits a UTF-8 code point", ), @@ -223,7 +224,7 @@ fn cases() -> Vec { 1, 1, "", - "no-op", + SemanticObstructionCode::NoOp, "invalid-request", "no-op", ), @@ -234,7 +235,7 @@ fn cases() -> Vec { 0, 3, "abc", - "no-op", + SemanticObstructionCode::NoOp, "invalid-request", "no-op", ), @@ -245,7 +246,7 @@ fn cases() -> Vec { 0, 1, "x", - "basis-not-canonical", + SemanticObstructionCode::BasisNotCanonical, "invalid-request", "stale replace basis", ), @@ -256,7 +257,7 @@ fn cases() -> Vec { 0, 1, "x", - "basis-not-canonical", + SemanticObstructionCode::BasisNotCanonical, "invalid-request", "stale replace basis", ), @@ -267,7 +268,7 @@ fn cases() -> Vec { 0, 1, "x", - "arithmetic-overflow", + SemanticObstructionCode::ArithmeticOverflow, "malformed-fact", "head sequence overflow", ), @@ -278,7 +279,7 @@ fn cases() -> Vec { 0, 1, "x", - "arithmetic-overflow", + SemanticObstructionCode::ArithmeticOverflow, "malformed-fact", "buffer version overflow", ), @@ -289,7 +290,7 @@ fn cases() -> Vec { 0, 0, "x", - "fact-missing", + SemanticObstructionCode::FactMissing, "missing-fact", "BufferWorldline", ), @@ -300,7 +301,7 @@ fn cases() -> Vec { 0, 1, "x", - "fact-malformed", + SemanticObstructionCode::FactMalformed, "malformed-fact", "decode", ), @@ -311,7 +312,7 @@ fn cases() -> Vec { 0, 1, "x", - "content-identity-mismatch", + SemanticObstructionCode::ContentIdentityMismatch, "malformed-fact", "content hash does not match", ), @@ -322,7 +323,7 @@ fn cases() -> Vec { i32::MAX as u64 + 1, i32::MAX as u64 + 1, "x", - "malformed-rope", + SemanticObstructionCode::MalformedRope, "invalid-request", "split exceeds empty rope", ), @@ -357,7 +358,7 @@ fn obstruction( start_byte: u64, end_byte: u64, replacement: &str, - semantic_code: &'static str, + semantic_code: SemanticObstructionCode, error_class: &'static str, message_fragment: &'static str, ) -> CaseSpec { diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index 0f5479be..7d2f0314 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -1,5 +1,8 @@ use std::fmt; +#[path = "support/replace_range_corpus_contract.rs"] +mod corpus_contract; + use jedit_echo_host::records::{ BlobFact, BranchFact, BufferFact, DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, }; @@ -7,6 +10,8 @@ use serde::de::{IgnoredAny, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; +use corpus_contract::validate_oracle_contract; + const SCHEMA_BYTES: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/../../contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json" @@ -326,3 +331,54 @@ fn oracle_invocation_bytes_follow_the_published_u64_codec() { assert_eq!(above_i32.invocation.start_byte, i32::MAX as u64 + 1); assert_eq!(above_i32.invocation.end_byte, i32::MAX as u64 + 1); } + +#[test] +fn schema_declares_the_exhaustive_oracle_obstruction_domain() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + assert_eq!( + schema["oracleCorpus"]["terminal"]["semanticCodes"], + serde_json::json!([ + "range-order-invalid", + "range-out-of-bounds", + "utf8-boundary-invalid", + "no-op", + "basis-not-canonical", + "arithmetic-overflow", + "fact-missing", + "fact-malformed", + "content-identity-mismatch", + "malformed-rope" + ]) + ); +} + +#[test] +fn committed_oracle_satisfies_the_strict_corpus_shape() { + let corpus: Value = serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); + validate_oracle_contract(&corpus).expect("oracle corpus should satisfy its strict contract"); +} + +#[test] +fn strict_corpus_shape_rejects_unknown_members_and_codes() { + let corpus: Value = serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); + + let mut unknown_member = corpus.clone(); + unknown_member + .as_object_mut() + .expect("corpus should be an object") + .insert("ambientAuthority".to_owned(), Value::Bool(true)); + assert!(validate_oracle_contract(&unknown_member).is_err()); + + let mut unknown_code = corpus; + let terminal = unknown_code["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find_map(|case| { + let terminal = case.get_mut("terminal")?; + (terminal["posture"] == "obstructed").then_some(terminal) + }) + .expect("an obstruction should exist"); + terminal["semanticCode"] = Value::String("invented-obstruction".to_owned()); + assert!(validate_oracle_contract(&unknown_code).is_err()); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_contract.rs b/native/jedit-echo-host/tests/support/replace_range_contract.rs new file mode 100644 index 00000000..2c7d83b4 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_contract.rs @@ -0,0 +1,47 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum SemanticObstructionCode { + RangeOrderInvalid, + RangeOutOfBounds, + Utf8BoundaryInvalid, + NoOp, + BasisNotCanonical, + ArithmeticOverflow, + FactMissing, + FactMalformed, + ContentIdentityMismatch, + MalformedRope, +} + +#[allow(dead_code)] +impl SemanticObstructionCode { + pub const ALL: [Self; 10] = [ + Self::RangeOrderInvalid, + Self::RangeOutOfBounds, + Self::Utf8BoundaryInvalid, + Self::NoOp, + Self::BasisNotCanonical, + Self::ArithmeticOverflow, + Self::FactMissing, + Self::FactMalformed, + Self::ContentIdentityMismatch, + Self::MalformedRope, + ]; + + pub const fn as_str(self) -> &'static str { + match self { + Self::RangeOrderInvalid => "range-order-invalid", + Self::RangeOutOfBounds => "range-out-of-bounds", + Self::Utf8BoundaryInvalid => "utf8-boundary-invalid", + Self::NoOp => "no-op", + Self::BasisNotCanonical => "basis-not-canonical", + Self::ArithmeticOverflow => "arithmetic-overflow", + Self::FactMissing => "fact-missing", + Self::FactMalformed => "fact-malformed", + Self::ContentIdentityMismatch => "content-identity-mismatch", + Self::MalformedRope => "malformed-rope", + } + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs new file mode 100644 index 00000000..4c015853 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs @@ -0,0 +1,193 @@ +use serde_json::{Map, Value}; + +pub fn validate_oracle_contract(corpus: &Value) -> Result<(), String> { + let root = exact_fields( + corpus, + &[ + "schemaVersion", + "coordinate", + "applicationSchemaCoordinate", + "invocationSchemaCoordinate", + "semanticBaselineCommit", + "sourceSet", + "evidenceGrade", + "independenceLimit", + "warpId", + "cases", + ], + )?; + exact_fields( + field(root, "sourceSet")?, + &["algorithm", "domainHex", "framing", "paths", "digestHex"], + )?; + let cases = field(root, "cases")? + .as_array() + .ok_or_else(|| "cases must be an array".to_owned())?; + for case in cases { + validate_oracle_case(case)?; + } + Ok(()) +} + +fn validate_oracle_case(case: &Value) -> Result<(), String> { + let case = exact_fields( + case, + &[ + "id", + "purpose", + "basisFacts", + "invocation", + "invocationBytesHex", + "terminal", + ], + )?; + for fact in field(case, "basisFacts")? + .as_array() + .ok_or_else(|| "basisFacts must be an array".to_owned())? + { + exact_fields(fact, &["nodeId", "typeId", "attachmentBytesHex"])?; + } + exact_fields( + field(case, "invocation")?, + &[ + "bufferId", + "basisHeadId", + "startByte", + "endByte", + "replacementUtf8Hex", + ], + )?; + validate_terminal(field(case, "terminal")?) +} + +fn validate_terminal(terminal: &Value) -> Result<(), String> { + match terminal["posture"].as_str() { + Some("committable") => validate_committable(terminal), + Some("obstructed") => validate_obstructed(terminal), + Some(posture) => Err(format!("unknown terminal posture {posture}")), + None => Err("terminal posture must be a string".to_owned()), + } +} + +fn validate_committable(terminal: &Value) -> Result<(), String> { + let terminal = exact_fields( + terminal, + &[ + "posture", + "footprint", + "patch", + "createdNodeIds", + "updatedNodeIds", + "untouchedBasisNodeIds", + "result", + ], + )?; + exact_fields( + field(terminal, "footprint")?, + &[ + "nodeReads", + "nodeWrites", + "attachmentReads", + "attachmentWrites", + "edgeReads", + "edgeWrites", + ], + )?; + for operation in field(terminal, "patch")? + .as_array() + .ok_or_else(|| "patch must be an array".to_owned())? + { + validate_patch_operation(operation)?; + } + exact_fields( + field(terminal, "result")?, + &[ + "bufferId", + "headId", + "rootNodeId", + "rootDigest", + "rewriteId", + "diffId", + "byteLength", + "utf16Length", + "lineCount", + "sequence", + "version", + "materializedTextUtf8Hex", + ], + )?; + Ok(()) +} + +fn validate_obstructed(terminal: &Value) -> Result<(), String> { + let terminal = exact_fields( + terminal, + &[ + "posture", + "semanticCode", + "legacyErrorClass", + "legacyMessage", + "parentGraphUnchanged", + "patchPosture", + ], + )?; + const CODES: &[&str] = &[ + "range-order-invalid", + "range-out-of-bounds", + "utf8-boundary-invalid", + "no-op", + "basis-not-canonical", + "arithmetic-overflow", + "fact-missing", + "fact-malformed", + "content-identity-mismatch", + "malformed-rope", + ]; + let code = field(terminal, "semanticCode")? + .as_str() + .ok_or_else(|| "semanticCode must be a string".to_owned())?; + if !CODES.contains(&code) { + return Err(format!("unknown semanticCode {code}")); + } + Ok(()) +} + +fn validate_patch_operation(operation: &Value) -> Result<(), String> { + match operation["kind"].as_str() { + Some("upsert-node") => { + exact_fields(operation, &["kind", "nodeId", "typeId"])?; + Ok(()) + } + Some("set-node-alpha") => { + exact_fields( + operation, + &["kind", "nodeId", "typeId", "attachmentBytesHex"], + )?; + Ok(()) + } + Some(kind) => Err(format!("unknown patch kind {kind}")), + None => Err("patch kind must be a string".to_owned()), + } +} + +fn exact_fields<'a>(value: &'a Value, expected: &[&str]) -> Result<&'a Map, String> { + let object = value + .as_object() + .ok_or_else(|| "contract value must be an object".to_owned())?; + let mut actual = object.keys().map(String::as_str).collect::>(); + actual.sort_unstable(); + let mut expected = expected.to_vec(); + expected.sort_unstable(); + if actual != expected { + return Err(format!( + "object fields differ: expected {expected:?}, received {actual:?}" + )); + } + Ok(object) +} + +fn field<'a>(object: &'a Map, name: &str) -> Result<&'a Value, String> { + object + .get(name) + .ok_or_else(|| format!("missing required field {name}")) +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index 4d26ce5e..fc05e618 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -13,11 +13,14 @@ use warp_core::{ #[path = "replace_range_basis.rs"] mod basis; +#[path = "replace_range_contract.rs"] +mod contract; #[path = "replace_range_source_set.rs"] mod source_set; pub use basis::BasisSetup; use basis::{apply_ops, make_basis}; +pub use contract::SemanticObstructionCode; use source_set::{source_set, SourceSet}; const ORACLE_WARP_LABEL: &str = "jedit.replace-range.oracle.v1"; @@ -27,7 +30,7 @@ const SEMANTIC_BASELINE_COMMIT: &str = "c70e12d73b4b00bc92412bab67e1761f7dd22f82 pub enum ExpectedPosture { Success, Obstruction { - semantic_code: &'static str, + semantic_code: SemanticObstructionCode, error_class: &'static str, message_fragment: &'static str, }, @@ -104,7 +107,7 @@ enum TerminalProjection { result: Box, }, Obstructed { - semantic_code: &'static str, + semantic_code: SemanticObstructionCode, legacy_error_class: &'static str, legacy_message: String, parent_graph_unchanged: bool, diff --git a/native/jedit-echo-host/tests/support/replace_range_schema.rs b/native/jedit-echo-host/tests/support/replace_range_schema.rs index e572a64e..aa5d9798 100644 --- a/native/jedit-echo-host/tests/support/replace_range_schema.rs +++ b/native/jedit-echo-host/tests/support/replace_range_schema.rs @@ -6,6 +6,11 @@ use jedit_echo_host::records::{ use jedit_echo_host::rope::MAX_LEAF_BYTES; use serde_json::{json, Value}; +#[path = "replace_range_contract.rs"] +mod contract; + +use contract::SemanticObstructionCode; + pub const SCHEMA_COORDINATE: &str = "jedit.text.schema@1"; pub const STRING_ESCAPE_VECTOR: &str = concat!( "\u{0000}\u{0001}\u{0002}\u{0003}\u{0004}\u{0005}\u{0006}\u{0007}", @@ -133,6 +138,105 @@ pub fn expected_schema() -> Value { ], "canonicalBytesField": "invocationBytesHex" }, + "oracleCorpus": { + "coordinate": "jedit.text.ReplaceRange.oracle@1", + "encoding": "pretty-utf8-json-final-newline", + "unknownMembers": "forbidden", + "objectMemberOrder": [ + "schemaVersion", + "coordinate", + "applicationSchemaCoordinate", + "invocationSchemaCoordinate", + "semanticBaselineCommit", + "sourceSet", + "evidenceGrade", + "independenceLimit", + "warpId", + "cases" + ], + "sourceSetMemberOrder": [ + "algorithm", + "domainHex", + "framing", + "paths", + "digestHex" + ], + "caseMemberOrder": [ + "id", + "purpose", + "basisFacts", + "invocation", + "invocationBytesHex", + "terminal" + ], + "basisFactMemberOrder": [ + "nodeId", + "typeId", + "attachmentBytesHex" + ], + "terminal": { + "discriminant": "posture", + "committableMemberOrder": [ + "posture", + "footprint", + "patch", + "createdNodeIds", + "updatedNodeIds", + "untouchedBasisNodeIds", + "result" + ], + "obstructedMemberOrder": [ + "posture", + "semanticCode", + "legacyErrorClass", + "legacyMessage", + "parentGraphUnchanged", + "patchPosture" + ], + "semanticCodes": SemanticObstructionCode::ALL + .iter() + .map(|code| code.as_str()) + .collect::>(), + "obstructedPatchPosture": "no-mutation-plan" + }, + "footprintMemberOrder": [ + "nodeReads", + "nodeWrites", + "attachmentReads", + "attachmentWrites", + "edgeReads", + "edgeWrites" + ], + "patchVariants": [ + { + "kind": "upsert-node", + "memberOrder": ["kind", "nodeId", "typeId"] + }, + { + "kind": "set-node-alpha", + "memberOrder": [ + "kind", + "nodeId", + "typeId", + "attachmentBytesHex" + ] + } + ], + "resultMemberOrder": [ + "bufferId", + "headId", + "rootNodeId", + "rootDigest", + "rewriteId", + "diffId", + "byteLength", + "utf16Length", + "lineCount", + "sequence", + "version", + "materializedTextUtf8Hex" + ] + }, "facts": [ fact_declaration::( "Buffer", diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index 9bab345e..66bc1307 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -43,6 +43,10 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ "native/jedit-echo-host/tests/support/replace_range_basis.rs", include_bytes!("replace_range_basis.rs"), ), + ( + "native/jedit-echo-host/tests/support/replace_range_contract.rs", + include_bytes!("replace_range_contract.rs"), + ), ( "native/jedit-echo-host/tests/support/replace_range_oracle.rs", include_bytes!("replace_range_oracle.rs"), From d87e7a1891cbefb6cf478bbc9b6a9ed87121b242 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 18:51:07 -0700 Subject: [PATCH 09/63] Fix: distinguish oracle evidence postures --- .../0158-replace-range-canonical-fact-law.md | 6 +++-- tests/replace-range-law-cycle.spec.mjs | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 tests/replace-range-law-cycle.spec.mjs diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 212658da..90f192f1 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -298,8 +298,10 @@ to source-code inference. ## Agent Inspectability / Explainability Posture Agents can inspect one declaration and one corpus manifest without executing -the product or scraping Rust debug output. Every case names its semantic -purpose, exact basis, input, terminal posture, support, facts, and result. The +the product or scraping Rust debug output. Successful cases name exact basis, +input, support, patch, retained facts, and result. Obstructed cases name exact +basis, input, typed obstruction, no-plan posture, and unchanged-parent evidence. +Every case also names its semantic purpose and terminal posture. The `semanticBaselineCommit` names the historical checkpoint whose behavior this cycle freezes; the `sourceSet` digest binds the exact declared generating source bytes. The evidence grade prevents a consumer from mistaking diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs new file mode 100644 index 00000000..bd39165e --- /dev/null +++ b/tests/replace-range-law-cycle.spec.mjs @@ -0,0 +1,23 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; +import test from 'node:test'; + +const DESIGN_PATH = path.join( + process.cwd(), + 'docs', + 'design', + '0158-replace-range-canonical-fact-law.md', +); + +function readDesign() { + return fs.readFileSync(DESIGN_PATH, 'utf8'); +} + +test('DL-0158 distinguishes committable evidence from obstruction evidence', () => { + const normalized = readDesign().replaceAll(/\s+/g, ' '); + assert.match( + normalized, + /Successful cases name exact basis, input, support, patch, retained facts, and result\. Obstructed cases name exact basis, input, typed obstruction, no-plan posture, and unchanged-parent evidence\./, + ); +}); From 94e02a425cff66debc09ed98181bae198314e6c1 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 18:52:29 -0700 Subject: [PATCH 10/63] Fix: pin retrospective evidence --- docs/design/0158-replace-range-canonical-fact-law.md | 9 +++++++++ tests/replace-range-law-cycle.spec.mjs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 90f192f1..1860ca16 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -463,14 +463,18 @@ What changed from the design: - The planned native-schema corpus was implemented with 20 cases, separating retained stale-head and real foreign-head basis witnesses, without changing the production planner, fact bytes, identities, or routing. + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L43:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L43) - Review tightened the declaration with a fully specified string codec, a corpus-only invocation schema and exact bytes, mechanical schema-to-writer field-order comparison, strict/atomic fixture updates, and a digest over the exact declared Jedit source set. These are proof-strengthening additions, not a widening into the deferred Echo ABI. + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86) - Review replaced free-form obstruction labels with an exhaustive generated enum and added a strict machine-readable corpus shape whose conformance path rejects unknown members, variants, and semantic codes. + [`native/jedit-echo-host/tests/support/replace_range_contract.rs#L3:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_contract.rs#L3) + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336) - The published SHA-256 digests are `c7bf126245e9b2f8e7ff080983c16ad625f6b341d886281b79317be9a0b3b76d` for the schema, @@ -486,12 +490,17 @@ What the tests proved: every declared fact-member order equals the order observed from writer bytes; the full string escape vector matches the native writer; and corpus invocation bytes preserve `u64` above `i32::MAX`. + [`native/jedit-echo-host/tests/replace_range_schema.rs#L89:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema.rs#L89) + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L295:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L295) - Strict corpus conformance rejects unknown object members, terminal or patch variants, and obstruction codes outside the ten-value semantic enum. + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L356:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L356) - Six success and fourteen obstruction cases regenerate deterministically. A success yields the exact support, ordered patch, retained facts, metrics, and materialized consequence; an obstruction yields no `MutationPlan` and leaves the parent graph unchanged. + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190) + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258) - This is deterministic self-validation. It is not an independent verifier, all-input equivalence proof, Echo admission witness, runtime receipt, WAL, or recovery witness. diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index bd39165e..4fb57164 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -21,3 +21,12 @@ test('DL-0158 distinguishes committable evidence from obstruction evidence', () /Successful cases name exact basis, input, support, patch, retained facts, and result\. Obstructed cases name exact basis, input, typed obstruction, no-plan posture, and unchanged-parent evidence\./, ); }); + +test('DL-0158 retrospective pins implemented truth to a full commit SHA', () => { + const retrospective = readDesign().split('## Retrospective')[1]; + + assert.match( + retrospective, + /https:\/\/github\.com\/flyingrobots\/jedit\/blob\/[0-9a-f]{40}\//, + ); +}); From edd41a35058fea354d46a38421641b0c173e3faa Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 18:57:32 -0700 Subject: [PATCH 11/63] Fix: preserve oracle WARP qualification --- CHANGELOG.md | 3 + .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../replace-range-v1/text-schema-v1.json | 1 + .../replace-range-v1/text-schema-v1.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 9 +- .../tests/replace_range_schema_conformance.rs | 9 ++ .../tests/support/replace_range_basis.rs | 25 ++++ .../tests/support/replace_range_oracle.rs | 114 +++++++++++++++--- .../tests/support/replace_range_schema.rs | 1 + 10 files changed, 145 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4078539..d6cebcd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Qualified every flattened `ReplaceRange` oracle footprint and patch + identifier by the corpus WARP. Generation and local fixture application now + reject foreign-WARP node, edge, and attachment keys before projection. - Replaced free-form `ReplaceRange` oracle obstruction strings with an exhaustive semantic enum and published strict corpus, terminal, footprint, patch, and result shapes. Conformance now rejects unknown fields and semantic diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 533ef03d..c59136b2 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -23,7 +23,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "4571309c0b4df86aa98f727a62540d51a6fc115d11494cdb6b8e2dd86c0a7f49" + "digestHex": "2144730f3182683641b9168e7269ee4f5d5507f78cb418d5b98866201fc6fe2a" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index a546c130..88746468 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -bccde04b7cae5ad8c6044974edf2e116e8cb182328e6622b52c7a021ed7b7d78 +9eaa16f104d21922bc6d6cde210b053da9a0673310be5b30bb49d18906aa0f44 diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json index 9af10e84..4f0ad14e 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json @@ -347,6 +347,7 @@ "edgeReads", "edgeWrites" ], + "localIdentifierQualification": "every footprint and patch identifier is qualified by top-level warpId", "objectMemberOrder": [ "schemaVersion", "coordinate", diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 index 6893f131..09946182 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 @@ -1 +1 @@ -c7bf126245e9b2f8e7ff080983c16ad625f6b341d886281b79317be9a0b3b76d +cb03407ff401033fd81312c31a1497519a2d1ae918ea4c19afd551bea1316a72 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 1860ca16..507b5b37 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -253,6 +253,9 @@ domain is an exhaustive generated Rust enum whose ten kebab-case values are also enumerated in the machine declaration. A structurally separate conformance validator rejects unknown members, terminal and patch variants, and semantic obstruction codes before an external consumer compares evidence. +Every flattened local identifier in footprint or patch evidence is qualified by +the corpus's top-level `warpId`; generation and local application reject a key +whose WARP differs before discarding that redundant qualifier. ## Lower Modes @@ -476,13 +479,13 @@ What changed from the design: [`native/jedit-echo-host/tests/support/replace_range_contract.rs#L3:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_contract.rs#L3) [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336) - The published SHA-256 digests are - `c7bf126245e9b2f8e7ff080983c16ad625f6b341d886281b79317be9a0b3b76d` + `cb03407ff401033fd81312c31a1497519a2d1ae918ea4c19afd551bea1316a72` for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `bccde04b7cae5ad8c6044974edf2e116e8cb182328e6622b52c7a021ed7b7d78` + `9eaa16f104d21922bc6d6cde210b053da9a0673310be5b30bb49d18906aa0f44` for the oracle. The oracle source-set digest is - `4571309c0b4df86aa98f727a62540d51a6fc115d11494cdb6b8e2dd86c0a7f49`. + `2144730f3182683641b9168e7269ee4f5d5507f78cb418d5b98866201fc6fe2a`. What the tests proved: diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index 7d2f0314..dcfb96fa 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -352,6 +352,15 @@ fn schema_declares_the_exhaustive_oracle_obstruction_domain() { ); } +#[test] +fn schema_qualifies_local_oracle_identifiers_by_the_top_level_warp() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + assert_eq!( + schema["oracleCorpus"]["localIdentifierQualification"], + "every footprint and patch identifier is qualified by top-level warpId" + ); +} + #[test] fn committed_oracle_satisfies_the_strict_corpus_shape() { let corpus: Value = serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); diff --git a/native/jedit-echo-host/tests/support/replace_range_basis.rs b/native/jedit-echo-host/tests/support/replace_range_basis.rs index 6f31cc9c..40238ab9 100644 --- a/native/jedit-echo-host/tests/support/replace_range_basis.rs +++ b/native/jedit-echo-host/tests/support/replace_range_basis.rs @@ -105,6 +105,11 @@ pub fn apply_ops(store: &mut GraphStore, ops: &[WarpOp]) { let warp_core::AttachmentOwner::Node(node) = key.owner else { panic!("oracle patch must not contain edge attachments") }; + assert_eq!( + node.warp_id, + store.warp_id(), + "oracle patch WARP must match its local store" + ); store.set_node_attachment(node.local_id, value.clone()); } _ => panic!("oracle patch contains an unsupported graph operation"), @@ -192,3 +197,23 @@ fn corrupt_blob_hash(store: &mut GraphStore, head_id: NodeId) { ))), ); } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic(expected = "oracle patch WARP must match its local store")] + fn local_patch_applier_rejects_a_foreign_warp_attachment() { + let mut store = GraphStore::new(warp_core::make_warp_id("oracle-local")); + let operation = WarpOp::SetAttachment { + key: warp_core::AttachmentKey::node_alpha(warp_core::NodeKey { + warp_id: warp_core::make_warp_id("oracle-foreign"), + local_id: NodeId([0xA5; 32]), + }), + value: None, + }; + + apply_ops(&mut store, &[operation]); + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index fc05e618..51b592ad 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -275,8 +275,8 @@ fn success_projection( &spec.replacement, ) .expect("deterministic rerun should plan"); - let first_plan = project_plan(plan); - let second_plan = project_plan(&second); + let first_plan = project_plan(plan, store.warp_id()); + let second_plan = project_plan(&second, store.warp_id()); assert_eq!(first_plan, second_plan, "{} rerun drifted", spec.id); let basis_ids: BTreeSet<_> = store @@ -305,7 +305,11 @@ fn success_projection( let untouched_basis_node_ids = basis_ids.difference(&write_ids).cloned().collect(); TerminalProjection::Committable { footprint: first_plan.0, - patch: first_plan.1.iter().map(project_op).collect::>(), + patch: first_plan + .1 + .iter() + .map(|operation| project_op(operation, store.warp_id())) + .collect::>(), created_node_ids, updated_node_ids, untouched_basis_node_ids, @@ -340,47 +344,74 @@ fn node_with_type(store: &GraphStore, type_id: TypeId) -> String { hex::encode(node_id.as_bytes()) } -fn project_plan(plan: &MutationPlan) -> (FootprintProjection, Vec) { +fn project_plan( + plan: &MutationPlan, + warp_id: warp_core::WarpId, +) -> (FootprintProjection, Vec) { let mut footprint = Footprint::default(); plan.extend_footprint(&mut footprint); let mut delta = TickDelta::new(); plan.emit(&mut delta); - (project_footprint(&footprint), delta.finalize()) + (project_footprint(&footprint, warp_id), delta.finalize()) } -fn project_footprint(footprint: &Footprint) -> FootprintProjection { +fn project_footprint(footprint: &Footprint, warp_id: warp_core::WarpId) -> FootprintProjection { FootprintProjection { node_reads: footprint .n_read .iter() - .map(|key| hex::encode(key.local_id.as_bytes())) + .map(|key| node_hex(key, warp_id)) .collect(), node_writes: footprint .n_write .iter() - .map(|key| hex::encode(key.local_id.as_bytes())) + .map(|key| node_hex(key, warp_id)) + .collect(), + attachment_reads: footprint + .a_read + .iter() + .map(|key| attachment_node_hex(key, warp_id)) + .collect(), + attachment_writes: footprint + .a_write + .iter() + .map(|key| attachment_node_hex(key, warp_id)) .collect(), - attachment_reads: footprint.a_read.iter().map(attachment_node_hex).collect(), - attachment_writes: footprint.a_write.iter().map(attachment_node_hex).collect(), edge_reads: footprint .e_read .iter() - .map(|key| hex::encode(key.local_id.as_bytes())) + .map(|key| edge_hex(key, warp_id)) .collect(), edge_writes: footprint .e_write .iter() - .map(|key| hex::encode(key.local_id.as_bytes())) + .map(|key| edge_hex(key, warp_id)) .collect(), } } -fn attachment_node_hex(key: &warp_core::AttachmentKey) -> String { +fn node_hex(key: &warp_core::NodeKey, warp_id: warp_core::WarpId) -> String { + assert_eq!( + key.warp_id, warp_id, + "oracle footprint WARP must match the corpus WARP" + ); + hex::encode(key.local_id.as_bytes()) +} + +fn edge_hex(key: &warp_core::EdgeKey, warp_id: warp_core::WarpId) -> String { + assert_eq!( + key.warp_id, warp_id, + "oracle footprint WARP must match the corpus WARP" + ); + hex::encode(key.local_id.as_bytes()) +} + +fn attachment_node_hex(key: &warp_core::AttachmentKey, warp_id: warp_core::WarpId) -> String { assert_eq!(key.plane, AttachmentPlane::Alpha); let AttachmentOwner::Node(node) = key.owner else { panic!("ReplaceRange footprint must not contain edge attachments") }; - hex::encode(node.local_id.as_bytes()) + node_hex(&node, warp_id) } fn project_store(store: &GraphStore) -> Vec { @@ -403,14 +434,14 @@ fn project_store(store: &GraphStore) -> Vec { .collect() } -fn project_op(op: &WarpOp) -> PatchOpProjection { +fn project_op(op: &WarpOp, warp_id: warp_core::WarpId) -> PatchOpProjection { match op { WarpOp::UpsertNode { node, record } => PatchOpProjection::UpsertNode { - node_id: hex::encode(node.local_id.as_bytes()), + node_id: node_hex(node, warp_id), type_id: hex::encode(record.ty.as_bytes()), }, WarpOp::SetAttachment { key, value } => { - let node_id = attachment_node_hex(key); + let node_id = attachment_node_hex(key, warp_id); let Some(AttachmentValue::Atom(atom)) = value else { panic!("ReplaceRange must set atom attachments") }; @@ -432,3 +463,52 @@ fn error_projection(error: &HostError) -> (&'static str, String) { other => panic!("oracle received unexpected host error: {other}"), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] + fn footprint_projection_rejects_a_foreign_node_read() { + let expected = make_warp_id("oracle-local"); + let mut footprint = Footprint::default(); + footprint + .n_read + .insert_with_warp(make_warp_id("oracle-foreign"), NodeId([0xA5; 32])); + + project_footprint(&footprint, expected); + } + + #[test] + #[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] + fn footprint_projection_rejects_a_foreign_attachment_write() { + let expected = make_warp_id("oracle-local"); + let mut footprint = Footprint::default(); + footprint + .a_write + .insert(warp_core::AttachmentKey::node_alpha(warp_core::NodeKey { + warp_id: make_warp_id("oracle-foreign"), + local_id: NodeId([0xA5; 32]), + })); + + project_footprint(&footprint, expected); + } + + #[test] + #[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] + fn patch_projection_rejects_a_foreign_node_write() { + let expected = make_warp_id("oracle-local"); + let operation = WarpOp::UpsertNode { + node: warp_core::NodeKey { + warp_id: make_warp_id("oracle-foreign"), + local_id: NodeId([0xA5; 32]), + }, + record: warp_core::NodeRecord { + ty: warp_core::make_type_id("oracle.foreign"), + }, + }; + + project_op(&operation, expected); + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_schema.rs b/native/jedit-echo-host/tests/support/replace_range_schema.rs index aa5d9798..41f7ddda 100644 --- a/native/jedit-echo-host/tests/support/replace_range_schema.rs +++ b/native/jedit-echo-host/tests/support/replace_range_schema.rs @@ -142,6 +142,7 @@ pub fn expected_schema() -> Value { "coordinate": "jedit.text.ReplaceRange.oracle@1", "encoding": "pretty-utf8-json-final-newline", "unknownMembers": "forbidden", + "localIdentifierQualification": "every footprint and patch identifier is qualified by top-level warpId", "objectMemberOrder": [ "schemaVersion", "coordinate", From 6a43bfd6a9064f7be91355527696de2400836395 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 19:01:36 -0700 Subject: [PATCH 12/63] Fix: validate retained oracle consequence --- CHANGELOG.md | 3 + .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 13 +- .../tests/support/replace_range_oracle.rs | 310 ++++++++++++++++-- 5 files changed, 297 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6cebcd3..1158a392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Added a retained-consequence consistency gate to the `ReplaceRange` oracle. + Buffer, Head, Rewrite, and Diff identities, cross-links, ranges, metrics, and + planner summaries must agree before result evidence can be published. - Qualified every flattened `ReplaceRange` oracle footprint and patch identifier by the corpus WARP. Generation and local fixture application now reject foreign-WARP node, edge, and attachment keys before projection. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index c59136b2..9ca5112a 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -23,7 +23,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "2144730f3182683641b9168e7269ee4f5d5507f78cb418d5b98866201fc6fe2a" + "digestHex": "96125197ece197fb6fde1f48ace8fc62c58cfca8b0fac60e4e145a9ffcf6ea28" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 88746468..7287b48d 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -9eaa16f104d21922bc6d6cde210b053da9a0673310be5b30bb49d18906aa0f44 +1c03efba1ba38ea17729385d49626795de74e7636444bef0b780f0ffb33fa129 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 507b5b37..26c91326 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -257,6 +257,13 @@ Every flattened local identifier in footprint or patch evidence is qualified by the corpus's top-level `warpId`; generation and local application reject a key whose WARP differs before discarding that redundant qualifier. +Before a committable case is serialized, the oracle independently decodes the +post-patch Buffer, Head, Rewrite, and Diff facts and checks their identities, +basis, range, next head, version, sequence, lengths, root digest, and cross-links +against one another and the planner summary. Result evidence is derived from +those validated retained facts and materialized text, not from an unchecked mix +of planner summaries and discovered type labels. + ## Lower Modes The contract is entirely headless. `cargo test` recomputes and compares the @@ -483,9 +490,9 @@ What changed from the design: for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `9eaa16f104d21922bc6d6cde210b053da9a0673310be5b30bb49d18906aa0f44` + `1c03efba1ba38ea17729385d49626795de74e7636444bef0b780f0ffb33fa12` for the oracle. The oracle source-set digest is - `2144730f3182683641b9168e7269ee4f5d5507f78cb418d5b98866201fc6fe2a`. + `996125197ece197fb6fde1f48ace8fc62c58cfca8b0fac60e4e145a9ffcf6ea28`. What the tests proved: @@ -504,6 +511,8 @@ What the tests proved: the parent graph unchanged. [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190) [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258) +- A retained-consequence consistency gate refuses mismatched Buffer, Head, + Rewrite, or Diff evidence before a committable result enters the corpus. - This is deterministic self-validation. It is not an independent verifier, all-input equivalence proof, Echo admission witness, runtime receipt, WAL, or recovery witness. diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index 51b592ad..dd9145b6 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -1,8 +1,10 @@ use std::collections::BTreeSet; use jedit_echo_host::error::HostError; +use jedit_echo_host::identity::hash_bytes; use jedit_echo_host::records::{ - decode_fact, fact_type_id, BufferFact, DiffFact, HeadFact, RewriteFact, + decode_fact, fact_bytes, fact_id, fact_type_id, BufferFact, DiffFact, HeadFact, RewriteFact, + EMPTY_ROOT_DIGEST_DOMAIN, }; use jedit_echo_host::rope::{plan_replace, read_window, MutationPlan}; use serde::Serialize; @@ -161,6 +163,15 @@ struct ResultProjection { materialized_text_utf8_hex: String, } +#[derive(Debug)] +struct ValidatedConsequence { + buffer: BufferFact, + head: HeadFact, + rewrite_id: NodeId, + diff_id: NodeId, + materialized_text: String, +} + pub fn generate_corpus(specs: Vec) -> OracleCorpus { let warp_id = make_warp_id(ORACLE_WARP_LABEL); let cases = specs @@ -285,20 +296,16 @@ fn success_projection( .collect(); let mut next = store.clone(); apply_ops(&mut next, &first_plan.1); - let result_head: HeadFact = decode_fact( - next.node_attachment(&plan.head_id) - .expect("result head attachment should exist"), - ) - .expect("result head should decode"); - let window = read_window( + let retained = validate_consequence( + store, &next, - plan.buffer_id, - plan.head_id, - 0, - plan.byte_length, - plan.byte_length, + plan, + NodeId::from(buffer.canonical_head_id), + spec.start_byte, + spec.end_byte, + &spec.replacement, ) - .expect("result should materialize"); + .unwrap_or_else(|error| panic!("{} retained consequence: {error}", spec.id)); let write_ids: BTreeSet<_> = first_plan.0.node_writes.iter().cloned().collect(); let created_node_ids = write_ids.difference(&basis_ids).cloned().collect(); let updated_node_ids = write_ids.intersection(&basis_ids).cloned().collect(); @@ -316,32 +323,235 @@ fn success_projection( result: Box::new(ResultProjection { buffer_id: hex::encode(plan.buffer_id.as_bytes()), head_id: hex::encode(plan.head_id.as_bytes()), - root_node_id: result_head.root_node_id.map(|id| hex::encode(id.0)), - root_digest: hex::encode(result_head.root_digest), - rewrite_id: node_with_type(&next, fact_type_id::()), - diff_id: node_with_type(&next, fact_type_id::()), - byte_length: plan.byte_length, - utf16_length: result_head.utf16_length, - line_count: plan.line_count, - sequence: result_head.sequence, - version: plan.version, - materialized_text_utf8_hex: hex::encode(window.text.as_bytes()), + root_node_id: retained.head.root_node_id.map(|id| hex::encode(id.0)), + root_digest: hex::encode(retained.head.root_digest), + rewrite_id: hex::encode(retained.rewrite_id.as_bytes()), + diff_id: hex::encode(retained.diff_id.as_bytes()), + byte_length: retained.head.byte_length, + utf16_length: retained.head.utf16_length, + line_count: retained.head.line_count, + sequence: retained.head.sequence, + version: retained.buffer.version, + materialized_text_utf8_hex: hex::encode(retained.materialized_text.as_bytes()), }), } } -fn node_with_type(store: &GraphStore, type_id: TypeId) -> String { +fn node_with_type(store: &GraphStore, type_id: TypeId) -> Result { let matches: Vec<_> = store .iter_nodes() .filter_map(|(node_id, record)| (record.ty == type_id).then_some(node_id)) .collect(); let [node_id] = matches.as_slice() else { - panic!( - "expected exactly one fact of type {}", - hex::encode(type_id.as_bytes()) - ) + return Err(format!( + "expected exactly one fact of type {}, received {}", + hex::encode(type_id.as_bytes()), + matches.len() + )); }; - hex::encode(node_id.as_bytes()) + Ok(**node_id) +} + +fn validate_consequence( + basis: &GraphStore, + next: &GraphStore, + plan: &MutationPlan, + basis_head_id: NodeId, + start_byte: u64, + end_byte: u64, + replacement: &str, +) -> Result { + let basis_buffer: BufferFact = read_fact(basis, plan.buffer_id)?; + require_equal( + "basis Buffer canonical head", + NodeId::from(basis_buffer.canonical_head_id), + basis_head_id, + )?; + let basis_head: HeadFact = read_fact(basis, basis_head_id)?; + let buffer: BufferFact = read_fact(next, plan.buffer_id)?; + require_equal( + "result Buffer canonical head", + NodeId::from(buffer.canonical_head_id), + plan.head_id, + )?; + require_equal( + "result Buffer key", + &buffer.buffer_key, + &basis_buffer.buffer_key, + )?; + require_equal( + "result Buffer projection path", + &buffer.projection_path, + &basis_buffer.projection_path, + )?; + let expected_version = basis_buffer + .version + .checked_add(1) + .ok_or_else(|| "basis Buffer version overflowed during validation".to_owned())?; + require_equal("result Buffer version", buffer.version, expected_version)?; + require_equal("plan Buffer version", plan.version, buffer.version)?; + + let head: HeadFact = read_fact(next, plan.head_id)?; + require_equal( + "result Head identity", + fact_id(&head).map_err(host_error)?, + plan.head_id, + )?; + require_equal( + "result Head buffer", + NodeId::from(head.buffer_id), + plan.buffer_id, + )?; + require_equal( + "result Head basis", + head.basis_head_id.map(NodeId::from), + Some(basis_head_id), + )?; + require_equal( + "result Head byte length", + head.byte_length, + plan.byte_length, + )?; + require_equal("result Head line count", head.line_count, plan.line_count)?; + let expected_sequence = basis_head + .sequence + .checked_add(1) + .ok_or_else(|| "basis Head sequence overflowed during validation".to_owned())?; + require_equal("result Head sequence", head.sequence, expected_sequence)?; + let expected_root_digest = head + .root_node_id + .map_or_else(|| hash_bytes(EMPTY_ROOT_DIGEST_DOMAIN, &[]), |id| id.0); + require_equal( + "result Head root digest", + head.root_digest, + expected_root_digest, + )?; + + let inserted_byte_length = u64::try_from(replacement.len()) + .map_err(|_| "replacement length exceeds u64".to_owned())?; + let deleted_byte_length = end_byte + .checked_sub(start_byte) + .ok_or_else(|| "validated range is reversed".to_owned())?; + let rewrite_id = node_with_type(next, fact_type_id::())?; + let rewrite: RewriteFact = read_fact(next, rewrite_id)?; + require_equal( + "Rewrite identity", + fact_id(&rewrite).map_err(host_error)?, + rewrite_id, + )?; + require_equal( + "Rewrite buffer", + NodeId::from(rewrite.buffer_id), + plan.buffer_id, + )?; + require_equal( + "Rewrite basis", + NodeId::from(rewrite.basis_head_id), + basis_head_id, + )?; + require_equal( + "Rewrite next head", + NodeId::from(rewrite.next_head_id), + plan.head_id, + )?; + require_equal("Rewrite start", rewrite.start_byte, start_byte)?; + require_equal("Rewrite end", rewrite.end_byte, end_byte)?; + require_equal( + "Rewrite inserted length", + rewrite.inserted_byte_length, + inserted_byte_length, + )?; + + let diff_id = node_with_type(next, fact_type_id::())?; + let diff: DiffFact = read_fact(next, diff_id)?; + require_equal( + "Diff identity", + fact_id(&diff).map_err(host_error)?, + diff_id, + )?; + require_equal("Diff rewrite", NodeId::from(diff.rewrite_id), rewrite_id)?; + require_equal( + "Diff basis", + NodeId::from(diff.basis_head_id), + basis_head_id, + )?; + require_equal( + "Diff next head", + NodeId::from(diff.next_head_id), + plan.head_id, + )?; + require_equal("Diff start", diff.start_byte, start_byte)?; + require_equal("Diff end", diff.end_byte, end_byte)?; + require_equal( + "Diff inserted length", + diff.inserted_byte_length, + inserted_byte_length, + )?; + require_equal( + "Diff deleted length", + diff.deleted_byte_length, + deleted_byte_length, + )?; + + let window = read_window( + next, + plan.buffer_id, + plan.head_id, + 0, + head.byte_length, + head.byte_length, + ) + .map_err(host_error)?; + let materialized_text = window.text; + require_equal( + "result Head UTF-16 length", + head.utf16_length, + materialized_text.encode_utf16().count() as u64, + )?; + require_equal( + "result Head materialized line count", + head.line_count, + materialized_text + .bytes() + .filter(|byte| *byte == b'\n') + .count() as u64 + + 1, + )?; + + Ok(ValidatedConsequence { + buffer, + head, + rewrite_id, + diff_id, + materialized_text, + }) +} + +fn read_fact( + store: &GraphStore, + node_id: NodeId, +) -> Result { + let attachment = store + .node_attachment(&node_id) + .ok_or_else(|| format!("missing retained fact {}", hex::encode(node_id.as_bytes())))?; + decode_fact(attachment).map_err(host_error) +} + +fn require_equal( + label: &str, + actual: T, + expected: T, +) -> Result<(), String> { + if actual != expected { + return Err(format!( + "{label} differs: expected {expected:?}, received {actual:?}" + )); + } + Ok(()) +} + +fn host_error(error: HostError) -> String { + error.to_string() } fn project_plan( @@ -511,4 +721,46 @@ mod tests { project_op(&operation, expected); } + + #[test] + fn retained_consequence_is_internally_consistent() { + let warp_id = make_warp_id("oracle-consistency"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "consistent", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + apply_ops(&mut next, &delta.finalize()); + + validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") + .expect("retained consequence should be internally consistent"); + } + + #[test] + fn retained_consequence_rejects_a_buffer_head_mismatch() { + let warp_id = make_warp_id("oracle-inconsistent"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "inconsistent", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + apply_ops(&mut next, &delta.finalize()); + let mut buffer: BufferFact = read_fact(&next, buffer_id).expect("Buffer should decode"); + buffer.canonical_head_id = basis_head_id.into(); + next.set_node_attachment( + buffer_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&buffer).expect("Buffer should encode").into(), + ))), + ); + + let error = validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") + .expect_err("inconsistent retained consequence should fail"); + assert!(error.contains("result Buffer canonical head")); + } } From c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 19:04:06 -0700 Subject: [PATCH 13/63] Fix: enforce oracle invocation lexemes --- CHANGELOG.md | 3 ++ .../0158-replace-range-canonical-fact-law.md | 7 +++- .../tests/replace_range_schema_conformance.rs | 20 ++++++++++ .../support/replace_range_corpus_contract.rs | 40 ++++++++++++++++++- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1158a392..51d26206 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Enforced the published `ReplaceRange` oracle-invocation lexical law. Strict + conformance now rejects uppercase, malformed, or wrong-width identifiers and + odd, malformed, or non-UTF-8 replacement-byte encodings. - Added a retained-consequence consistency gate to the `ReplaceRange` oracle. Buffer, Head, Rewrite, and Diff identities, cross-links, ranges, metrics, and planner summaries must agree before result evidence can be published. diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 26c91326..96759e07 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -230,7 +230,10 @@ ordered members `bufferId`, `basisHeadId`, `startByte`, `endByte`, and coordinates are JSON decimal `u64`; replacement bytes are lowercase hexadecimal and must decode as UTF-8. Every case carries the exact compact object bytes in `invocationBytesHex`. This schema is a conformance input, not a -runtime authority or a prematurely frozen Echo ABI. +runtime authority or a prematurely frozen Echo ABI. Strict corpus conformance +rejects uppercase, nonhexadecimal, short, or long identifiers; odd-length or +nonhexadecimal replacement bytes; replacement bytes that are not UTF-8; and +coordinates that are not JSON `u64` values. The native planner and retained fact coordinates are `u64`, and the future Edict-authored operation must preserve that domain. Current production still @@ -505,6 +508,8 @@ What the tests proved: - Strict corpus conformance rejects unknown object members, terminal or patch variants, and obstruction codes outside the ten-value semantic enum. [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L356:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L356) +- Corpus invocation conformance rejects identifiers and replacement bytes that + violate their exact lowercase hexadecimal, width, or UTF-8 lexical laws. - Six success and fourteen obstruction cases regenerate deterministically. A success yields the exact support, ordered patch, retained facts, metrics, and materialized consequence; an obstruction yields no `MutationPlan` and leaves diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index dcfb96fa..c16897fd 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -391,3 +391,23 @@ fn strict_corpus_shape_rejects_unknown_members_and_codes() { terminal["semanticCode"] = Value::String("invented-obstruction".to_owned()); assert!(validate_oracle_contract(&unknown_code).is_err()); } + +#[test] +fn strict_corpus_shape_rejects_noncanonical_invocation_lexemes() { + for (field, invalid) in [ + ("bufferId", "AA".repeat(32)), + ("bufferId", "aa".repeat(31)), + ("basisHeadId", "gg".repeat(32)), + ("replacementUtf8Hex", "0".to_owned()), + ("replacementUtf8Hex", "zz".to_owned()), + ("replacementUtf8Hex", "ff".to_owned()), + ] { + let mut corpus: Value = + serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); + corpus["cases"][0]["invocation"][field] = Value::String(invalid); + assert!( + validate_oracle_contract(&corpus).is_err(), + "{field} accepted a noncanonical lexeme" + ); + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs index 4c015853..74057c45 100644 --- a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs @@ -47,7 +47,7 @@ fn validate_oracle_case(case: &Value) -> Result<(), String> { { exact_fields(fact, &["nodeId", "typeId", "attachmentBytesHex"])?; } - exact_fields( + let invocation = exact_fields( field(case, "invocation")?, &[ "bufferId", @@ -57,9 +57,47 @@ fn validate_oracle_case(case: &Value) -> Result<(), String> { "replacementUtf8Hex", ], )?; + validate_node_id(field(invocation, "bufferId")?, "bufferId")?; + validate_node_id(field(invocation, "basisHeadId")?, "basisHeadId")?; + field(invocation, "startByte")? + .as_u64() + .ok_or_else(|| "startByte must be a u64 JSON decimal".to_owned())?; + field(invocation, "endByte")? + .as_u64() + .ok_or_else(|| "endByte must be a u64 JSON decimal".to_owned())?; + let replacement = decode_lowercase_hex( + field(invocation, "replacementUtf8Hex")?, + "replacementUtf8Hex", + )?; + std::str::from_utf8(&replacement) + .map_err(|_| "replacementUtf8Hex must encode UTF-8".to_owned())?; validate_terminal(field(case, "terminal")?) } +fn validate_node_id(value: &Value, label: &str) -> Result<(), String> { + let bytes = decode_lowercase_hex(value, label)?; + if bytes.len() != 32 { + return Err(format!("{label} must encode exactly 32 bytes")); + } + Ok(()) +} + +fn decode_lowercase_hex(value: &Value, label: &str) -> Result, String> { + let value = value + .as_str() + .ok_or_else(|| format!("{label} must be a string"))?; + if value.len() % 2 != 0 { + return Err(format!("{label} must contain an even number of digits")); + } + if !value + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) + { + return Err(format!("{label} must contain lowercase hexadecimal only")); + } + hex::decode(value).map_err(|error| format!("{label} must decode: {error}")) +} + fn validate_terminal(terminal: &Value) -> Result<(), String> { match terminal["posture"].as_str() { Some("committable") => validate_committable(terminal), From f3bafc8db633a53002c4bbcf39b256fd078ac4ba Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 19:52:53 -0700 Subject: [PATCH 14/63] Fix: split oracle support modules --- .../replace-range-v1.oracle.json | 4 +- .../replace-range-v1.oracle.sha256 | 2 +- .../support/replace_range_consequence.rs | 285 +++++++++++++++ .../tests/support/replace_range_oracle.rs | 333 +----------------- .../support/replace_range_oracle_tests.rs | 45 +++ .../tests/support/replace_range_source_set.rs | 8 + tests/replace-range-law-cycle.spec.mjs | 20 ++ 7 files changed, 371 insertions(+), 326 deletions(-) create mode 100644 native/jedit-echo-host/tests/support/replace_range_consequence.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 9ca5112a..466d9014 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -20,10 +20,12 @@ "native/jedit-echo-host/tests/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_basis.rs", "native/jedit-echo-host/tests/support/replace_range_contract.rs", + "native/jedit-echo-host/tests/support/replace_range_consequence.rs", "native/jedit-echo-host/tests/support/replace_range_oracle.rs", + "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "96125197ece197fb6fde1f48ace8fc62c58cfca8b0fac60e4e145a9ffcf6ea28" + "digestHex": "57d81b9824ec6c930579ba72b1384ca151acbde58996de8c6c37ada605db51de" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 7287b48d..d97b74ca 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -1c03efba1ba38ea17729385d49626795de74e7636444bef0b780f0ffb33fa129 +88bd14e17a3a79c5836d5902c3a0a7f1aeeac3b6728f8764ebee27e5e25dc3df diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence.rs b/native/jedit-echo-host/tests/support/replace_range_consequence.rs new file mode 100644 index 00000000..8fc91cac --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_consequence.rs @@ -0,0 +1,285 @@ +use jedit_echo_host::error::HostError; +use jedit_echo_host::identity::hash_bytes; +use jedit_echo_host::records::{ + decode_fact, fact_id, fact_type_id, BufferFact, DiffFact, HeadFact, RewriteFact, + EMPTY_ROOT_DIGEST_DOMAIN, +}; +use jedit_echo_host::rope::{read_window, MutationPlan}; +use warp_core::{GraphStore, NodeId, TypeId}; + +#[derive(Debug)] +pub(super) struct ValidatedConsequence { + pub(super) buffer: BufferFact, + pub(super) head: HeadFact, + pub(super) rewrite_id: NodeId, + pub(super) diff_id: NodeId, + pub(super) materialized_text: String, +} + +pub(super) fn validate_consequence( + basis: &GraphStore, + next: &GraphStore, + plan: &MutationPlan, + basis_head_id: NodeId, + start_byte: u64, + end_byte: u64, + replacement: &str, +) -> Result { + let basis_buffer: BufferFact = read_fact(basis, plan.buffer_id)?; + require_equal( + "basis Buffer canonical head", + NodeId::from(basis_buffer.canonical_head_id), + basis_head_id, + )?; + let basis_head: HeadFact = read_fact(basis, basis_head_id)?; + let buffer: BufferFact = read_fact(next, plan.buffer_id)?; + require_equal( + "result Buffer canonical head", + NodeId::from(buffer.canonical_head_id), + plan.head_id, + )?; + require_equal( + "result Buffer key", + &buffer.buffer_key, + &basis_buffer.buffer_key, + )?; + require_equal( + "result Buffer projection path", + &buffer.projection_path, + &basis_buffer.projection_path, + )?; + let expected_version = basis_buffer + .version + .checked_add(1) + .ok_or_else(|| "basis Buffer version overflowed during validation".to_owned())?; + require_equal("result Buffer version", buffer.version, expected_version)?; + require_equal("plan Buffer version", plan.version, buffer.version)?; + + let head: HeadFact = read_fact(next, plan.head_id)?; + require_equal( + "result Head identity", + fact_id(&head).map_err(host_error)?, + plan.head_id, + )?; + require_equal( + "result Head buffer", + NodeId::from(head.buffer_id), + plan.buffer_id, + )?; + require_equal( + "result Head basis", + head.basis_head_id.map(NodeId::from), + Some(basis_head_id), + )?; + require_equal( + "result Head byte length", + head.byte_length, + plan.byte_length, + )?; + require_equal("result Head line count", head.line_count, plan.line_count)?; + let expected_sequence = basis_head + .sequence + .checked_add(1) + .ok_or_else(|| "basis Head sequence overflowed during validation".to_owned())?; + require_equal("result Head sequence", head.sequence, expected_sequence)?; + let expected_root_digest = head + .root_node_id + .map_or_else(|| hash_bytes(EMPTY_ROOT_DIGEST_DOMAIN, &[]), |id| id.0); + require_equal( + "result Head root digest", + head.root_digest, + expected_root_digest, + )?; + + let inserted_byte_length = u64::try_from(replacement.len()) + .map_err(|_| "replacement length exceeds u64".to_owned())?; + let deleted_byte_length = end_byte + .checked_sub(start_byte) + .ok_or_else(|| "validated range is reversed".to_owned())?; + let rewrite_id = node_with_type(next, fact_type_id::())?; + let rewrite: RewriteFact = read_fact(next, rewrite_id)?; + require_equal( + "Rewrite identity", + fact_id(&rewrite).map_err(host_error)?, + rewrite_id, + )?; + require_equal( + "Rewrite buffer", + NodeId::from(rewrite.buffer_id), + plan.buffer_id, + )?; + require_equal( + "Rewrite basis", + NodeId::from(rewrite.basis_head_id), + basis_head_id, + )?; + require_equal( + "Rewrite next head", + NodeId::from(rewrite.next_head_id), + plan.head_id, + )?; + require_equal("Rewrite start", rewrite.start_byte, start_byte)?; + require_equal("Rewrite end", rewrite.end_byte, end_byte)?; + require_equal( + "Rewrite inserted length", + rewrite.inserted_byte_length, + inserted_byte_length, + )?; + + let diff_id = node_with_type(next, fact_type_id::())?; + let diff: DiffFact = read_fact(next, diff_id)?; + require_equal( + "Diff identity", + fact_id(&diff).map_err(host_error)?, + diff_id, + )?; + require_equal("Diff rewrite", NodeId::from(diff.rewrite_id), rewrite_id)?; + require_equal( + "Diff basis", + NodeId::from(diff.basis_head_id), + basis_head_id, + )?; + require_equal( + "Diff next head", + NodeId::from(diff.next_head_id), + plan.head_id, + )?; + require_equal("Diff start", diff.start_byte, start_byte)?; + require_equal("Diff end", diff.end_byte, end_byte)?; + require_equal( + "Diff inserted length", + diff.inserted_byte_length, + inserted_byte_length, + )?; + require_equal( + "Diff deleted length", + diff.deleted_byte_length, + deleted_byte_length, + )?; + + let window = read_window( + next, + plan.buffer_id, + plan.head_id, + 0, + head.byte_length, + head.byte_length, + ) + .map_err(host_error)?; + let materialized_text = window.text; + require_equal( + "result Head UTF-16 length", + head.utf16_length, + materialized_text.encode_utf16().count() as u64, + )?; + require_equal( + "result Head materialized line count", + head.line_count, + materialized_text + .bytes() + .filter(|byte| *byte == b'\n') + .count() as u64 + + 1, + )?; + + Ok(ValidatedConsequence { + buffer, + head, + rewrite_id, + diff_id, + materialized_text, + }) +} + +fn node_with_type(store: &GraphStore, type_id: TypeId) -> Result { + let matches: Vec<_> = store + .iter_nodes() + .filter_map(|(node_id, record)| (record.ty == type_id).then_some(node_id)) + .collect(); + let [node_id] = matches.as_slice() else { + return Err(format!( + "expected exactly one fact of type {}, received {}", + hex::encode(type_id.as_bytes()), + matches.len() + )); + }; + Ok(**node_id) +} + +fn read_fact( + store: &GraphStore, + node_id: NodeId, +) -> Result { + let attachment = store + .node_attachment(&node_id) + .ok_or_else(|| format!("missing retained fact {}", hex::encode(node_id.as_bytes())))?; + decode_fact(attachment).map_err(host_error) +} + +fn require_equal( + label: &str, + actual: T, + expected: T, +) -> Result<(), String> { + if actual != expected { + return Err(format!( + "{label} differs: expected {expected:?}, received {actual:?}" + )); + } + Ok(()) +} + +fn host_error(error: HostError) -> String { + error.to_string() +} + +#[cfg(test)] +mod tests { + use super::super::{apply_ops, make_basis, BasisSetup}; + use super::*; + use jedit_echo_host::records::{fact_bytes, BufferFact}; + use jedit_echo_host::rope::plan_replace; + use warp_core::{AttachmentValue, TickDelta}; + + #[test] + fn retained_consequence_is_internally_consistent() { + let warp_id = warp_core::make_warp_id("oracle-consistency"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "consistent", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + apply_ops(&mut next, &delta.finalize()); + + validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") + .expect("retained consequence should be internally consistent"); + } + + #[test] + fn retained_consequence_rejects_a_buffer_head_mismatch() { + let warp_id = warp_core::make_warp_id("oracle-inconsistent"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "inconsistent", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + apply_ops(&mut next, &delta.finalize()); + let mut buffer: BufferFact = read_fact(&next, buffer_id).expect("Buffer should decode"); + buffer.canonical_head_id = basis_head_id.into(); + next.set_node_attachment( + buffer_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&buffer).expect("Buffer should encode").into(), + ))), + ); + + let error = validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") + .expect_err("inconsistent retained consequence should fail"); + assert!(error.contains("result Buffer canonical head")); + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index dd9145b6..7b554195 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -1,27 +1,29 @@ use std::collections::BTreeSet; use jedit_echo_host::error::HostError; -use jedit_echo_host::identity::hash_bytes; -use jedit_echo_host::records::{ - decode_fact, fact_bytes, fact_id, fact_type_id, BufferFact, DiffFact, HeadFact, RewriteFact, - EMPTY_ROOT_DIGEST_DOMAIN, -}; -use jedit_echo_host::rope::{plan_replace, read_window, MutationPlan}; +use jedit_echo_host::records::{decode_fact, BufferFact}; +use jedit_echo_host::rope::{plan_replace, MutationPlan}; use serde::Serialize; use warp_core::{ make_warp_id, AttachmentOwner, AttachmentPlane, AttachmentValue, Footprint, GraphStore, NodeId, - TickDelta, TypeId, WarpOp, + TickDelta, WarpOp, }; #[path = "replace_range_basis.rs"] mod basis; +#[path = "replace_range_consequence.rs"] +mod consequence; #[path = "replace_range_contract.rs"] mod contract; #[path = "replace_range_source_set.rs"] mod source_set; +#[cfg(test)] +#[path = "replace_range_oracle_tests.rs"] +mod tests; pub use basis::BasisSetup; use basis::{apply_ops, make_basis}; +use consequence::validate_consequence; pub use contract::SemanticObstructionCode; use source_set::{source_set, SourceSet}; @@ -163,15 +165,6 @@ struct ResultProjection { materialized_text_utf8_hex: String, } -#[derive(Debug)] -struct ValidatedConsequence { - buffer: BufferFact, - head: HeadFact, - rewrite_id: NodeId, - diff_id: NodeId, - materialized_text: String, -} - pub fn generate_corpus(specs: Vec) -> OracleCorpus { let warp_id = make_warp_id(ORACLE_WARP_LABEL); let cases = specs @@ -337,223 +330,6 @@ fn success_projection( } } -fn node_with_type(store: &GraphStore, type_id: TypeId) -> Result { - let matches: Vec<_> = store - .iter_nodes() - .filter_map(|(node_id, record)| (record.ty == type_id).then_some(node_id)) - .collect(); - let [node_id] = matches.as_slice() else { - return Err(format!( - "expected exactly one fact of type {}, received {}", - hex::encode(type_id.as_bytes()), - matches.len() - )); - }; - Ok(**node_id) -} - -fn validate_consequence( - basis: &GraphStore, - next: &GraphStore, - plan: &MutationPlan, - basis_head_id: NodeId, - start_byte: u64, - end_byte: u64, - replacement: &str, -) -> Result { - let basis_buffer: BufferFact = read_fact(basis, plan.buffer_id)?; - require_equal( - "basis Buffer canonical head", - NodeId::from(basis_buffer.canonical_head_id), - basis_head_id, - )?; - let basis_head: HeadFact = read_fact(basis, basis_head_id)?; - let buffer: BufferFact = read_fact(next, plan.buffer_id)?; - require_equal( - "result Buffer canonical head", - NodeId::from(buffer.canonical_head_id), - plan.head_id, - )?; - require_equal( - "result Buffer key", - &buffer.buffer_key, - &basis_buffer.buffer_key, - )?; - require_equal( - "result Buffer projection path", - &buffer.projection_path, - &basis_buffer.projection_path, - )?; - let expected_version = basis_buffer - .version - .checked_add(1) - .ok_or_else(|| "basis Buffer version overflowed during validation".to_owned())?; - require_equal("result Buffer version", buffer.version, expected_version)?; - require_equal("plan Buffer version", plan.version, buffer.version)?; - - let head: HeadFact = read_fact(next, plan.head_id)?; - require_equal( - "result Head identity", - fact_id(&head).map_err(host_error)?, - plan.head_id, - )?; - require_equal( - "result Head buffer", - NodeId::from(head.buffer_id), - plan.buffer_id, - )?; - require_equal( - "result Head basis", - head.basis_head_id.map(NodeId::from), - Some(basis_head_id), - )?; - require_equal( - "result Head byte length", - head.byte_length, - plan.byte_length, - )?; - require_equal("result Head line count", head.line_count, plan.line_count)?; - let expected_sequence = basis_head - .sequence - .checked_add(1) - .ok_or_else(|| "basis Head sequence overflowed during validation".to_owned())?; - require_equal("result Head sequence", head.sequence, expected_sequence)?; - let expected_root_digest = head - .root_node_id - .map_or_else(|| hash_bytes(EMPTY_ROOT_DIGEST_DOMAIN, &[]), |id| id.0); - require_equal( - "result Head root digest", - head.root_digest, - expected_root_digest, - )?; - - let inserted_byte_length = u64::try_from(replacement.len()) - .map_err(|_| "replacement length exceeds u64".to_owned())?; - let deleted_byte_length = end_byte - .checked_sub(start_byte) - .ok_or_else(|| "validated range is reversed".to_owned())?; - let rewrite_id = node_with_type(next, fact_type_id::())?; - let rewrite: RewriteFact = read_fact(next, rewrite_id)?; - require_equal( - "Rewrite identity", - fact_id(&rewrite).map_err(host_error)?, - rewrite_id, - )?; - require_equal( - "Rewrite buffer", - NodeId::from(rewrite.buffer_id), - plan.buffer_id, - )?; - require_equal( - "Rewrite basis", - NodeId::from(rewrite.basis_head_id), - basis_head_id, - )?; - require_equal( - "Rewrite next head", - NodeId::from(rewrite.next_head_id), - plan.head_id, - )?; - require_equal("Rewrite start", rewrite.start_byte, start_byte)?; - require_equal("Rewrite end", rewrite.end_byte, end_byte)?; - require_equal( - "Rewrite inserted length", - rewrite.inserted_byte_length, - inserted_byte_length, - )?; - - let diff_id = node_with_type(next, fact_type_id::())?; - let diff: DiffFact = read_fact(next, diff_id)?; - require_equal( - "Diff identity", - fact_id(&diff).map_err(host_error)?, - diff_id, - )?; - require_equal("Diff rewrite", NodeId::from(diff.rewrite_id), rewrite_id)?; - require_equal( - "Diff basis", - NodeId::from(diff.basis_head_id), - basis_head_id, - )?; - require_equal( - "Diff next head", - NodeId::from(diff.next_head_id), - plan.head_id, - )?; - require_equal("Diff start", diff.start_byte, start_byte)?; - require_equal("Diff end", diff.end_byte, end_byte)?; - require_equal( - "Diff inserted length", - diff.inserted_byte_length, - inserted_byte_length, - )?; - require_equal( - "Diff deleted length", - diff.deleted_byte_length, - deleted_byte_length, - )?; - - let window = read_window( - next, - plan.buffer_id, - plan.head_id, - 0, - head.byte_length, - head.byte_length, - ) - .map_err(host_error)?; - let materialized_text = window.text; - require_equal( - "result Head UTF-16 length", - head.utf16_length, - materialized_text.encode_utf16().count() as u64, - )?; - require_equal( - "result Head materialized line count", - head.line_count, - materialized_text - .bytes() - .filter(|byte| *byte == b'\n') - .count() as u64 - + 1, - )?; - - Ok(ValidatedConsequence { - buffer, - head, - rewrite_id, - diff_id, - materialized_text, - }) -} - -fn read_fact( - store: &GraphStore, - node_id: NodeId, -) -> Result { - let attachment = store - .node_attachment(&node_id) - .ok_or_else(|| format!("missing retained fact {}", hex::encode(node_id.as_bytes())))?; - decode_fact(attachment).map_err(host_error) -} - -fn require_equal( - label: &str, - actual: T, - expected: T, -) -> Result<(), String> { - if actual != expected { - return Err(format!( - "{label} differs: expected {expected:?}, received {actual:?}" - )); - } - Ok(()) -} - -fn host_error(error: HostError) -> String { - error.to_string() -} - fn project_plan( plan: &MutationPlan, warp_id: warp_core::WarpId, @@ -673,94 +449,3 @@ fn error_projection(error: &HostError) -> (&'static str, String) { other => panic!("oracle received unexpected host error: {other}"), } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - #[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] - fn footprint_projection_rejects_a_foreign_node_read() { - let expected = make_warp_id("oracle-local"); - let mut footprint = Footprint::default(); - footprint - .n_read - .insert_with_warp(make_warp_id("oracle-foreign"), NodeId([0xA5; 32])); - - project_footprint(&footprint, expected); - } - - #[test] - #[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] - fn footprint_projection_rejects_a_foreign_attachment_write() { - let expected = make_warp_id("oracle-local"); - let mut footprint = Footprint::default(); - footprint - .a_write - .insert(warp_core::AttachmentKey::node_alpha(warp_core::NodeKey { - warp_id: make_warp_id("oracle-foreign"), - local_id: NodeId([0xA5; 32]), - })); - - project_footprint(&footprint, expected); - } - - #[test] - #[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] - fn patch_projection_rejects_a_foreign_node_write() { - let expected = make_warp_id("oracle-local"); - let operation = WarpOp::UpsertNode { - node: warp_core::NodeKey { - warp_id: make_warp_id("oracle-foreign"), - local_id: NodeId([0xA5; 32]), - }, - record: warp_core::NodeRecord { - ty: warp_core::make_type_id("oracle.foreign"), - }, - }; - - project_op(&operation, expected); - } - - #[test] - fn retained_consequence_is_internally_consistent() { - let warp_id = make_warp_id("oracle-consistency"); - let (basis, buffer_id, basis_head_id, _) = - make_basis(warp_id, "consistent", "abc", BasisSetup::Plain); - let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") - .expect("replacement should plan"); - let mut delta = TickDelta::new(); - plan.emit(&mut delta); - let mut next = basis.clone(); - apply_ops(&mut next, &delta.finalize()); - - validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") - .expect("retained consequence should be internally consistent"); - } - - #[test] - fn retained_consequence_rejects_a_buffer_head_mismatch() { - let warp_id = make_warp_id("oracle-inconsistent"); - let (basis, buffer_id, basis_head_id, _) = - make_basis(warp_id, "inconsistent", "abc", BasisSetup::Plain); - let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") - .expect("replacement should plan"); - let mut delta = TickDelta::new(); - plan.emit(&mut delta); - let mut next = basis.clone(); - apply_ops(&mut next, &delta.finalize()); - let mut buffer: BufferFact = read_fact(&next, buffer_id).expect("Buffer should decode"); - buffer.canonical_head_id = basis_head_id.into(); - next.set_node_attachment( - buffer_id, - Some(AttachmentValue::Atom(warp_core::AtomPayload::new( - fact_type_id::(), - fact_bytes(&buffer).expect("Buffer should encode").into(), - ))), - ); - - let error = validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") - .expect_err("inconsistent retained consequence should fail"); - assert!(error.contains("result Buffer canonical head")); - } -} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs new file mode 100644 index 00000000..10d11a67 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs @@ -0,0 +1,45 @@ +use super::*; + +#[test] +#[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] +fn footprint_projection_rejects_a_foreign_node_read() { + let expected = make_warp_id("oracle-local"); + let mut footprint = Footprint::default(); + footprint + .n_read + .insert_with_warp(make_warp_id("oracle-foreign"), NodeId([0xA5; 32])); + + project_footprint(&footprint, expected); +} + +#[test] +#[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] +fn footprint_projection_rejects_a_foreign_attachment_write() { + let expected = make_warp_id("oracle-local"); + let mut footprint = Footprint::default(); + footprint + .a_write + .insert(warp_core::AttachmentKey::node_alpha(warp_core::NodeKey { + warp_id: make_warp_id("oracle-foreign"), + local_id: NodeId([0xA5; 32]), + })); + + project_footprint(&footprint, expected); +} + +#[test] +#[should_panic(expected = "oracle footprint WARP must match the corpus WARP")] +fn patch_projection_rejects_a_foreign_node_write() { + let expected = make_warp_id("oracle-local"); + let operation = WarpOp::UpsertNode { + node: warp_core::NodeKey { + warp_id: make_warp_id("oracle-foreign"), + local_id: NodeId([0xA5; 32]), + }, + record: warp_core::NodeRecord { + ty: warp_core::make_type_id("oracle.foreign"), + }, + }; + + project_op(&operation, expected); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index 66bc1307..4373dead 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -47,10 +47,18 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ "native/jedit-echo-host/tests/support/replace_range_contract.rs", include_bytes!("replace_range_contract.rs"), ), + ( + "native/jedit-echo-host/tests/support/replace_range_consequence.rs", + include_bytes!("replace_range_consequence.rs"), + ), ( "native/jedit-echo-host/tests/support/replace_range_oracle.rs", include_bytes!("replace_range_oracle.rs"), ), + ( + "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", + include_bytes!("replace_range_oracle_tests.rs"), + ), ( "native/jedit-echo-host/tests/support/replace_range_source_set.rs", include_bytes!("replace_range_source_set.rs"), diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index 4fb57164..2e7656eb 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -9,6 +9,13 @@ const DESIGN_PATH = path.join( 'design', '0158-replace-range-canonical-fact-law.md', ); +const ORACLE_SUPPORT_PATH = path.join( + process.cwd(), + 'native', + 'jedit-echo-host', + 'tests', + 'support', +); function readDesign() { return fs.readFileSync(DESIGN_PATH, 'utf8'); @@ -30,3 +37,16 @@ test('DL-0158 retrospective pins implemented truth to a full commit SHA', () => /https:\/\/github\.com\/flyingrobots\/jedit\/blob\/[0-9a-f]{40}\//, ); }); + +test('ReplaceRange oracle support modules stay within the Rust file budget', () => { + const oversized = fs + .readdirSync(ORACLE_SUPPORT_PATH) + .filter((name) => /^replace_range_.*\.rs$/.test(name)) + .map((name) => { + const contents = fs.readFileSync(path.join(ORACLE_SUPPORT_PATH, name), 'utf8'); + return [name, contents.trimEnd().split(/\r?\n/).length]; + }) + .filter(([, lines]) => lines > 500); + + assert.deepEqual(oversized, []); +}); From 475b6dcc0eae9edbf228221069ab3aab975be868 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 19:59:41 -0700 Subject: [PATCH 15/63] Fix: bind consequences to the current patch --- .../replace-range-v1.oracle.json | 3 +- .../replace-range-v1.oracle.sha256 | 2 +- .../support/replace_range_consequence.rs | 116 +++++++------- .../replace_range_consequence_tests.rs | 144 ++++++++++++++++++ .../tests/support/replace_range_oracle.rs | 13 +- .../tests/support/replace_range_source_set.rs | 4 + 6 files changed, 210 insertions(+), 72 deletions(-) create mode 100644 native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 466d9014..37dd8e06 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -21,11 +21,12 @@ "native/jedit-echo-host/tests/support/replace_range_basis.rs", "native/jedit-echo-host/tests/support/replace_range_contract.rs", "native/jedit-echo-host/tests/support/replace_range_consequence.rs", + "native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs", "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "57d81b9824ec6c930579ba72b1384ca151acbde58996de8c6c37ada605db51de" + "digestHex": "d16fa576c0218778dc72a9c74d648371b8b141dfa9fe7b3f949d9c67d76111a8" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index d97b74ca..ac3f3f9b 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -88bd14e17a3a79c5836d5902c3a0a7f1aeeac3b6728f8764ebee27e5e25dc3df +5d781af5b05a7763ce08d9cd3fbdbe504dc0005d6ccd8b5cec91fc354ca23ea2 diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence.rs b/native/jedit-echo-host/tests/support/replace_range_consequence.rs index 8fc91cac..7dafa106 100644 --- a/native/jedit-echo-host/tests/support/replace_range_consequence.rs +++ b/native/jedit-echo-host/tests/support/replace_range_consequence.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use jedit_echo_host::error::HostError; use jedit_echo_host::identity::hash_bytes; use jedit_echo_host::records::{ @@ -5,7 +7,7 @@ use jedit_echo_host::records::{ EMPTY_ROOT_DIGEST_DOMAIN, }; use jedit_echo_host::rope::{read_window, MutationPlan}; -use warp_core::{GraphStore, NodeId, TypeId}; +use warp_core::{GraphStore, NodeId, TypeId, WarpId, WarpOp}; #[derive(Debug)] pub(super) struct ValidatedConsequence { @@ -16,15 +18,27 @@ pub(super) struct ValidatedConsequence { pub(super) materialized_text: String, } +#[derive(Clone, Copy)] +pub(super) struct ReplaceExpectation<'a> { + pub(super) basis_head_id: NodeId, + pub(super) start_byte: u64, + pub(super) end_byte: u64, + pub(super) replacement: &'a str, +} + pub(super) fn validate_consequence( basis: &GraphStore, next: &GraphStore, plan: &MutationPlan, - basis_head_id: NodeId, - start_byte: u64, - end_byte: u64, - replacement: &str, + patch: &[WarpOp], + expectation: ReplaceExpectation<'_>, ) -> Result { + let ReplaceExpectation { + basis_head_id, + start_byte, + end_byte, + replacement, + } = expectation; let basis_buffer: BufferFact = read_fact(basis, plan.buffer_id)?; require_equal( "basis Buffer canonical head", @@ -96,7 +110,7 @@ pub(super) fn validate_consequence( let deleted_byte_length = end_byte .checked_sub(start_byte) .ok_or_else(|| "validated range is reversed".to_owned())?; - let rewrite_id = node_with_type(next, fact_type_id::())?; + let rewrite_id = patch_node_with_type(patch, basis.warp_id(), fact_type_id::())?; let rewrite: RewriteFact = read_fact(next, rewrite_id)?; require_equal( "Rewrite identity", @@ -126,7 +140,7 @@ pub(super) fn validate_consequence( inserted_byte_length, )?; - let diff_id = node_with_type(next, fact_type_id::())?; + let diff_id = patch_node_with_type(patch, basis.warp_id(), fact_type_id::())?; let diff: DiffFact = read_fact(next, diff_id)?; require_equal( "Diff identity", @@ -191,19 +205,38 @@ pub(super) fn validate_consequence( }) } -fn node_with_type(store: &GraphStore, type_id: TypeId) -> Result { - let matches: Vec<_> = store - .iter_nodes() - .filter_map(|(node_id, record)| (record.ty == type_id).then_some(node_id)) - .collect(); - let [node_id] = matches.as_slice() else { +fn patch_node_with_type( + patch: &[WarpOp], + warp_id: WarpId, + type_id: TypeId, +) -> Result { + let mut matches = BTreeSet::new(); + for operation in patch { + let WarpOp::UpsertNode { node, record } = operation else { + continue; + }; + if record.ty != type_id { + continue; + } + if node.warp_id != warp_id { + return Err(format!( + "patch fact type {} belongs to foreign WARP {}", + hex::encode(type_id.as_bytes()), + hex::encode(node.warp_id.as_bytes()) + )); + } + matches.insert(node.local_id); + } + if matches.len() != 1 { return Err(format!( - "expected exactly one fact of type {}, received {}", + "expected exactly one patch fact of type {}, received {}", hex::encode(type_id.as_bytes()), matches.len() )); - }; - Ok(**node_id) + } + Ok(*matches + .first() + .expect("one patch fact must exist after the length check")) } fn read_fact( @@ -234,52 +267,5 @@ fn host_error(error: HostError) -> String { } #[cfg(test)] -mod tests { - use super::super::{apply_ops, make_basis, BasisSetup}; - use super::*; - use jedit_echo_host::records::{fact_bytes, BufferFact}; - use jedit_echo_host::rope::plan_replace; - use warp_core::{AttachmentValue, TickDelta}; - - #[test] - fn retained_consequence_is_internally_consistent() { - let warp_id = warp_core::make_warp_id("oracle-consistency"); - let (basis, buffer_id, basis_head_id, _) = - make_basis(warp_id, "consistent", "abc", BasisSetup::Plain); - let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") - .expect("replacement should plan"); - let mut delta = TickDelta::new(); - plan.emit(&mut delta); - let mut next = basis.clone(); - apply_ops(&mut next, &delta.finalize()); - - validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") - .expect("retained consequence should be internally consistent"); - } - - #[test] - fn retained_consequence_rejects_a_buffer_head_mismatch() { - let warp_id = warp_core::make_warp_id("oracle-inconsistent"); - let (basis, buffer_id, basis_head_id, _) = - make_basis(warp_id, "inconsistent", "abc", BasisSetup::Plain); - let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") - .expect("replacement should plan"); - let mut delta = TickDelta::new(); - plan.emit(&mut delta); - let mut next = basis.clone(); - apply_ops(&mut next, &delta.finalize()); - let mut buffer: BufferFact = read_fact(&next, buffer_id).expect("Buffer should decode"); - buffer.canonical_head_id = basis_head_id.into(); - next.set_node_attachment( - buffer_id, - Some(AttachmentValue::Atom(warp_core::AtomPayload::new( - fact_type_id::(), - fact_bytes(&buffer).expect("Buffer should encode").into(), - ))), - ); - - let error = validate_consequence(&basis, &next, &plan, basis_head_id, 1, 2, "XY") - .expect_err("inconsistent retained consequence should fail"); - assert!(error.contains("result Buffer canonical head")); - } -} +#[path = "replace_range_consequence_tests.rs"] +mod tests; diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs b/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs new file mode 100644 index 00000000..4086feaa --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs @@ -0,0 +1,144 @@ +use super::super::{apply_ops, make_basis, BasisSetup}; +use super::*; +use jedit_echo_host::records::{fact_bytes, fact_type_id, BufferFact, RewriteFact}; +use jedit_echo_host::rope::plan_replace; +use warp_core::{AttachmentValue, TickDelta, WarpOp}; + +#[test] +fn retained_consequence_is_internally_consistent() { + let warp_id = warp_core::make_warp_id("oracle-consistency"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "consistent", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + let patch = delta.finalize(); + apply_ops(&mut next, &patch); + + validate_consequence( + &basis, + &next, + &plan, + &patch, + ReplaceExpectation { + basis_head_id, + start_byte: 1, + end_byte: 2, + replacement: "XY", + }, + ) + .expect("retained consequence should be internally consistent"); +} + +#[test] +fn retained_consequence_selects_facts_from_the_current_edit() { + let warp_id = warp_core::make_warp_id("oracle-retained-history"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "retained-history", "abc", BasisSetup::Plain); + let first_plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("first replacement should plan"); + let mut first_delta = TickDelta::new(); + first_plan.emit(&mut first_delta); + let mut after_first = basis.clone(); + apply_ops(&mut after_first, &first_delta.finalize()); + + let second_plan = plan_replace(&after_first, buffer_id, first_plan.head_id, 0, 1, "Z") + .expect("second replacement should plan"); + let mut second_delta = TickDelta::new(); + second_plan.emit(&mut second_delta); + let mut after_second = after_first.clone(); + let second_patch = second_delta.finalize(); + apply_ops(&mut after_second, &second_patch); + + validate_consequence( + &after_first, + &after_second, + &second_plan, + &second_patch, + ReplaceExpectation { + basis_head_id: first_plan.head_id, + start_byte: 0, + end_byte: 1, + replacement: "Z", + }, + ) + .expect("current consequence should tolerate prior retained history"); +} + +#[test] +fn retained_consequence_requires_one_current_rewrite_candidate() { + let warp_id = warp_core::make_warp_id("oracle-current-candidate"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "current-candidate", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + let patch = delta.finalize(); + apply_ops(&mut next, &patch); + let expectation = ReplaceExpectation { + basis_head_id, + start_byte: 1, + end_byte: 2, + replacement: "XY", + }; + + let missing = validate_consequence(&basis, &next, &plan, &[], expectation) + .expect_err("a patch without a Rewrite candidate must fail"); + assert!(missing.contains("received 0")); + + let mut ambiguous = patch.clone(); + ambiguous.push(WarpOp::UpsertNode { + node: warp_core::NodeKey { + warp_id, + local_id: warp_core::NodeId([0xA5; 32]), + }, + record: warp_core::NodeRecord { + ty: fact_type_id::(), + }, + }); + let ambiguity = validate_consequence(&basis, &next, &plan, &ambiguous, expectation) + .expect_err("multiple Rewrite candidates must fail"); + assert!(ambiguity.contains("received 2")); +} + +#[test] +fn retained_consequence_rejects_a_buffer_head_mismatch() { + let warp_id = warp_core::make_warp_id("oracle-inconsistent"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "inconsistent", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let mut next = basis.clone(); + let patch = delta.finalize(); + apply_ops(&mut next, &patch); + let mut buffer: BufferFact = read_fact(&next, buffer_id).expect("Buffer should decode"); + buffer.canonical_head_id = basis_head_id.into(); + next.set_node_attachment( + buffer_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&buffer).expect("Buffer should encode").into(), + ))), + ); + + let error = validate_consequence( + &basis, + &next, + &plan, + &patch, + ReplaceExpectation { + basis_head_id, + start_byte: 1, + end_byte: 2, + replacement: "XY", + }, + ) + .expect_err("inconsistent retained consequence should fail"); + assert!(error.contains("result Buffer canonical head")); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index 7b554195..b7244e7b 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -23,7 +23,7 @@ mod tests; pub use basis::BasisSetup; use basis::{apply_ops, make_basis}; -use consequence::validate_consequence; +use consequence::{validate_consequence, ReplaceExpectation}; pub use contract::SemanticObstructionCode; use source_set::{source_set, SourceSet}; @@ -293,10 +293,13 @@ fn success_projection( store, &next, plan, - NodeId::from(buffer.canonical_head_id), - spec.start_byte, - spec.end_byte, - &spec.replacement, + &first_plan.1, + ReplaceExpectation { + basis_head_id: NodeId::from(buffer.canonical_head_id), + start_byte: spec.start_byte, + end_byte: spec.end_byte, + replacement: &spec.replacement, + }, ) .unwrap_or_else(|error| panic!("{} retained consequence: {error}", spec.id)); let write_ids: BTreeSet<_> = first_plan.0.node_writes.iter().cloned().collect(); diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index 4373dead..352a39c3 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -51,6 +51,10 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ "native/jedit-echo-host/tests/support/replace_range_consequence.rs", include_bytes!("replace_range_consequence.rs"), ), + ( + "native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs", + include_bytes!("replace_range_consequence_tests.rs"), + ), ( "native/jedit-echo-host/tests/support/replace_range_oracle.rs", include_bytes!("replace_range_oracle.rs"), From 6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:01:41 -0700 Subject: [PATCH 16/63] Fix: validate retained graph node types --- .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../support/replace_range_consequence.rs | 13 +++++++ .../replace_range_consequence_tests.rs | 36 ++++++++++++++++++- 4 files changed, 50 insertions(+), 3 deletions(-) diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 37dd8e06..4e5a9e87 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -26,7 +26,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "d16fa576c0218778dc72a9c74d648371b8b141dfa9fe7b3f949d9c67d76111a8" + "digestHex": "9361cfbd83cb44a22240effacb36bd60b08fcb83b2e4cfd6dad28ff87b41862d" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index ac3f3f9b..d773458b 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -5d781af5b05a7763ce08d9cd3fbdbe504dc0005d6ccd8b5cec91fc354ca23ea2 +16a8782b57ef7020d8a6db1341e5dc2caae5cc0fafa248db4882e8ecdf1fffb1 diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence.rs b/native/jedit-echo-host/tests/support/replace_range_consequence.rs index 7dafa106..2ff2036d 100644 --- a/native/jedit-echo-host/tests/support/replace_range_consequence.rs +++ b/native/jedit-echo-host/tests/support/replace_range_consequence.rs @@ -243,6 +243,19 @@ fn read_fact( store: &GraphStore, node_id: NodeId, ) -> Result { + let record = store + .node(&node_id) + .ok_or_else(|| format!("missing retained node {}", hex::encode(node_id.as_bytes())))?; + let expected_type = fact_type_id::(); + if record.ty != expected_type { + return Err(format!( + "{} node {} declares type {}, expected {}", + F::TYPE_LABEL, + hex::encode(node_id.as_bytes()), + hex::encode(record.ty.as_bytes()), + hex::encode(expected_type.as_bytes()) + )); + } let attachment = store .node_attachment(&node_id) .ok_or_else(|| format!("missing retained fact {}", hex::encode(node_id.as_bytes())))?; diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs b/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs index 4086feaa..60b62830 100644 --- a/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs +++ b/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs @@ -2,7 +2,7 @@ use super::super::{apply_ops, make_basis, BasisSetup}; use super::*; use jedit_echo_host::records::{fact_bytes, fact_type_id, BufferFact, RewriteFact}; use jedit_echo_host::rope::plan_replace; -use warp_core::{AttachmentValue, TickDelta, WarpOp}; +use warp_core::{make_type_id, AttachmentValue, NodeRecord, TickDelta, WarpOp}; #[test] fn retained_consequence_is_internally_consistent() { @@ -32,6 +32,40 @@ fn retained_consequence_is_internally_consistent() { .expect("retained consequence should be internally consistent"); } +#[test] +fn retained_consequence_rejects_node_and_atom_type_disagreement() { + let warp_id = warp_core::make_warp_id("oracle-node-type"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "node-type", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let patch = delta.finalize(); + let expectation = ReplaceExpectation { + basis_head_id, + start_byte: 1, + end_byte: 2, + replacement: "XY", + }; + let mut next = basis.clone(); + apply_ops(&mut next, &patch); + + for (node_id, label) in [(buffer_id, "Buffer"), (plan.head_id, "Head")] { + let mut corrupted = next.clone(); + corrupted.insert_node( + node_id, + NodeRecord { + ty: make_type_id("jedit.text.DeliberatelyWrong.v1"), + }, + ); + + let error = validate_consequence(&basis, &corrupted, &plan, &patch, expectation) + .expect_err("node and atom type disagreement must fail"); + assert!(error.contains(label), "{label} error was {error:?}"); + } +} + #[test] fn retained_consequence_selects_facts_from_the_current_edit() { let warp_id = warp_core::make_warp_id("oracle-retained-history"); From 65c9482dfe23973436b7f9a3125baae68eaad7f8 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:05:56 -0700 Subject: [PATCH 17/63] Fix: unify oracle obstruction codes --- .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../tests/replace_range_schema_conformance.rs | 23 ++++++++--------- .../tests/support/replace_range_contract.rs | 25 ++++++++++++++++++- .../support/replace_range_corpus_contract.rs | 22 +++------------- 5 files changed, 41 insertions(+), 33 deletions(-) diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 4e5a9e87..6d8b2e66 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -26,7 +26,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "9361cfbd83cb44a22240effacb36bd60b08fcb83b2e4cfd6dad28ff87b41862d" + "digestHex": "2ce99b5765c22af878660e012056d4c55779b0e1cb60eb08aea3c6381751410a" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index d773458b..422e49c5 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -16a8782b57ef7020d8a6db1341e5dc2caae5cc0fafa248db4882e8ecdf1fffb1 +924f107e2c620e1d39bc5a80799a61989005a629fb6e03f866ed51a2268c3ed7 diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index c16897fd..6a4c4399 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -1,5 +1,7 @@ use std::fmt; +#[path = "support/replace_range_contract.rs"] +mod contract; #[path = "support/replace_range_corpus_contract.rs"] mod corpus_contract; @@ -10,6 +12,7 @@ use serde::de::{IgnoredAny, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; +use contract::SemanticObstructionCode; use corpus_contract::validate_oracle_contract; const SCHEMA_BYTES: &[u8] = include_bytes!(concat!( @@ -24,6 +27,7 @@ const ORACLE_BYTES: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json" )); +const CORPUS_CONTRACT_SOURCE: &str = include_str!("support/replace_range_corpus_contract.rs"); #[derive(Deserialize)] struct CorpusProjection { @@ -335,23 +339,18 @@ fn oracle_invocation_bytes_follow_the_published_u64_codec() { #[test] fn schema_declares_the_exhaustive_oracle_obstruction_domain() { let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + let expected = SemanticObstructionCode::ALL.map(SemanticObstructionCode::as_str); assert_eq!( schema["oracleCorpus"]["terminal"]["semanticCodes"], - serde_json::json!([ - "range-order-invalid", - "range-out-of-bounds", - "utf8-boundary-invalid", - "no-op", - "basis-not-canonical", - "arithmetic-overflow", - "fact-missing", - "fact-malformed", - "content-identity-mismatch", - "malformed-rope" - ]) + serde_json::json!(expected) ); } +#[test] +fn corpus_validator_does_not_duplicate_the_obstruction_domain() { + assert!(!CORPUS_CONTRACT_SOURCE.contains("const CODES")); +} + #[test] fn schema_qualifies_local_oracle_identifiers_by_the_top_level_warp() { let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); diff --git a/native/jedit-echo-host/tests/support/replace_range_contract.rs b/native/jedit-echo-host/tests/support/replace_range_contract.rs index 2c7d83b4..03b7fb96 100644 --- a/native/jedit-echo-host/tests/support/replace_range_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_contract.rs @@ -15,7 +15,6 @@ pub enum SemanticObstructionCode { MalformedRope, } -#[allow(dead_code)] impl SemanticObstructionCode { pub const ALL: [Self; 10] = [ Self::RangeOrderInvalid, @@ -45,3 +44,27 @@ impl SemanticObstructionCode { } } } + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + + use super::*; + + #[test] + fn semantic_obstruction_wire_values_are_bijective() { + let mut wire_values = BTreeSet::new(); + for code in SemanticObstructionCode::ALL { + let encoded = serde_json::to_string(&code).expect("obstruction code should encode"); + assert_eq!(encoded, format!("\"{}\"", code.as_str())); + assert_eq!( + serde_json::from_str::(&encoded) + .expect("obstruction code should decode"), + code + ); + assert!(wire_values.insert(code.as_str())); + } + assert_eq!(wire_values.len(), SemanticObstructionCode::ALL.len()); + assert!(serde_json::from_str::("\"unknown\"").is_err()); + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs index 74057c45..ad9382e6 100644 --- a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs @@ -1,5 +1,7 @@ use serde_json::{Map, Value}; +use super::contract::SemanticObstructionCode; + pub fn validate_oracle_contract(corpus: &Value) -> Result<(), String> { let root = exact_fields( corpus, @@ -169,24 +171,8 @@ fn validate_obstructed(terminal: &Value) -> Result<(), String> { "patchPosture", ], )?; - const CODES: &[&str] = &[ - "range-order-invalid", - "range-out-of-bounds", - "utf8-boundary-invalid", - "no-op", - "basis-not-canonical", - "arithmetic-overflow", - "fact-missing", - "fact-malformed", - "content-identity-mismatch", - "malformed-rope", - ]; - let code = field(terminal, "semanticCode")? - .as_str() - .ok_or_else(|| "semanticCode must be a string".to_owned())?; - if !CODES.contains(&code) { - return Err(format!("unknown semanticCode {code}")); - } + serde_json::from_value::(field(terminal, "semanticCode")?.clone()) + .map_err(|error| format!("invalid semanticCode: {error}"))?; Ok(()) } From 9a309641edd4b809c37f5d055790de311e5d61f9 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:39:05 -0700 Subject: [PATCH 18/63] Fix: enforce the oracle corpus contract --- CHANGELOG.md | 4 + .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../tests/replace_range_corpus_conformance.rs | 324 +++++++++ .../tests/replace_range_schema_conformance.rs | 60 -- .../tests/support/replace_range_contract.rs | 26 + .../support/replace_range_corpus_contract.rs | 630 +++++++++++++----- .../support/replace_range_corpus_lexemes.rs | 115 ++++ .../tests/support/replace_range_oracle.rs | 22 +- .../tests/support/replace_range_schema.rs | 13 +- .../tests/support/replace_range_source_set.rs | 37 +- 11 files changed, 961 insertions(+), 274 deletions(-) create mode 100644 native/jedit-echo-host/tests/replace_range_corpus_conformance.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_corpus_lexemes.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 51d26206..2f2ecce9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Replaced the member-name-only `ReplaceRange` oracle check with a typed + raw-byte contract. Envelope identities, source provenance, canonical + identifier and UTF-8 lexemes, invocation bytes, footprints, patches, result + metrics, and no-partial-mutation obstruction evidence now fail closed. - Enforced the published `ReplaceRange` oracle-invocation lexical law. Strict conformance now rejects uppercase, malformed, or wrong-width identifiers and odd, malformed, or non-UTF-8 replacement-byte encodings. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 6d8b2e66..d29de28c 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -26,7 +26,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "2ce99b5765c22af878660e012056d4c55779b0e1cb60eb08aea3c6381751410a" + "digestHex": "2c18f9fc895fcd070134eb59592e704940c6816a4dbe628ff71f3183442a7815" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 422e49c5..e6ea2184 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -924f107e2c620e1d39bc5a80799a61989005a629fb6e03f866ed51a2268c3ed7 +dbcc3767d71bbeb1de44c6599fd840385b368eb9116230dea408e9b4511a3ee2 diff --git a/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs b/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs new file mode 100644 index 00000000..b302e3d8 --- /dev/null +++ b/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs @@ -0,0 +1,324 @@ +#[path = "support/replace_range_contract.rs"] +mod contract; +#[path = "support/replace_range_corpus_contract.rs"] +mod corpus_contract; +#[path = "support/replace_range_corpus_lexemes.rs"] +mod lexemes; +#[path = "support/replace_range_source_set.rs"] +mod source_set; + +use serde_json::Value; + +use corpus_contract::validate_oracle_contract; + +const ORACLE_BYTES: &[u8] = include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json" +)); +const CORPUS_CONTRACT_SOURCE: &str = include_str!("support/replace_range_corpus_contract.rs"); + +fn corpus() -> Value { + serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode") +} + +fn bytes(value: &Value) -> Vec { + let mut bytes = serde_json::to_vec_pretty(value).expect("mutated corpus should encode"); + bytes.push(b'\n'); + bytes +} + +fn assert_invalid(label: &str, mutate: impl FnOnce(&mut Value)) { + let mut corpus = corpus(); + mutate(&mut corpus); + let error = validate_oracle_contract(&bytes(&corpus)) + .expect_err("mutated corpus should violate its contract"); + assert!(!error.is_empty(), "{label} returned an empty error"); +} + +fn assert_pointer_invalid(pointer: &str, value: Value) { + assert_invalid(pointer, |corpus| { + *corpus + .pointer_mut(pointer) + .unwrap_or_else(|| panic!("{pointer} should exist")) = value; + }); +} + +fn committable(corpus: &mut Value) -> &mut Value { + corpus["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find(|case| case["terminal"]["posture"] == "committable") + .expect("a committable case should exist") +} + +fn obstruction(corpus: &mut Value) -> &mut Value { + corpus["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find(|case| case["terminal"]["posture"] == "obstructed") + .expect("an obstruction should exist") +} + +#[test] +fn committed_oracle_satisfies_the_strict_corpus_contract() { + validate_oracle_contract(ORACLE_BYTES).expect("oracle corpus should satisfy its contract"); +} + +#[test] +fn corpus_validator_does_not_duplicate_the_obstruction_domain() { + assert!(!CORPUS_CONTRACT_SOURCE.contains("const CODES")); +} + +#[test] +fn strict_corpus_rejects_invalid_typed_and_invariant_values() { + let mut wrong_version = corpus(); + wrong_version["schemaVersion"] = Value::from(2); + assert!(validate_oracle_contract(&bytes(&wrong_version)).is_err()); + + let mut wrong_algorithm = corpus(); + wrong_algorithm["sourceSet"]["algorithm"] = Value::String("blake3".to_owned()); + assert!(validate_oracle_contract(&bytes(&wrong_algorithm)).is_err()); + + let mut unsafe_obstruction = corpus(); + let obstruction = unsafe_obstruction["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find(|case| case["terminal"]["posture"] == "obstructed") + .expect("an obstruction should exist"); + obstruction["terminal"]["parentGraphUnchanged"] = Value::Bool(false); + assert!(validate_oracle_contract(&bytes(&unsafe_obstruction)).is_err()); + + let mut malformed_result = corpus(); + let committable = malformed_result["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find(|case| case["terminal"]["posture"] == "committable") + .expect("a committable case should exist"); + committable["terminal"]["result"]["headId"] = Value::String("aa".to_owned()); + assert!(validate_oracle_contract(&bytes(&malformed_result)).is_err()); + + let mut malformed_footprint = corpus(); + let committable = malformed_footprint["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find(|case| case["terminal"]["posture"] == "committable") + .expect("a committable case should exist"); + committable["terminal"]["footprint"]["nodeReads"] = Value::String("not-an-array".to_owned()); + assert!(validate_oracle_contract(&bytes(&malformed_footprint)).is_err()); + + let mut unbound_invocation = corpus(); + unbound_invocation["cases"][0]["invocationBytesHex"] = Value::String("7b7d".to_owned()); + assert!(validate_oracle_contract(&bytes(&unbound_invocation)).is_err()); +} + +#[test] +fn strict_corpus_binds_envelope_and_source_identity() { + for (pointer, value) in [ + ("/schemaVersion", Value::from(2)), + ("/coordinate", Value::String("other.operation@1".to_owned())), + ( + "/applicationSchemaCoordinate", + Value::String("other.schema@1".to_owned()), + ), + ( + "/invocationSchemaCoordinate", + Value::String("other.invocation@1".to_owned()), + ), + ("/semanticBaselineCommit", Value::String("00".repeat(20))), + ( + "/evidenceGrade", + Value::String("independent-verification".to_owned()), + ), + ("/independenceLimit", Value::String("none".to_owned())), + ("/warpId", Value::String("AA".repeat(32))), + ("/sourceSet/algorithm", Value::String("blake3".to_owned())), + ("/sourceSet/domainHex", Value::String("00".to_owned())), + ( + "/sourceSet/framing", + Value::String("unframed-concatenation".to_owned()), + ), + ("/sourceSet/digestHex", Value::String("00".repeat(32))), + ] { + assert_pointer_invalid(pointer, value); + } + + assert_invalid("foreign source-set path", |corpus| { + corpus["sourceSet"]["paths"][0] = Value::String("foreign/source.rs".to_owned()); + }); + assert_invalid("reordered source-set paths", |corpus| { + corpus["sourceSet"]["paths"] + .as_array_mut() + .expect("source-set paths should be an array") + .swap(0, 1); + }); +} + +#[test] +fn strict_corpus_validates_leaf_lexemes_and_numeric_domains() { + for (pointer, value) in [ + ( + "/cases/0/basisFacts/0/nodeId", + Value::String("AA".repeat(32)), + ), + ( + "/cases/0/basisFacts/0/typeId", + Value::String("aa".repeat(31)), + ), + ( + "/cases/0/basisFacts/0/attachmentBytesHex", + Value::String("0".to_owned()), + ), + ( + "/cases/0/terminal/footprint/nodeReads/0", + Value::String("gg".repeat(32)), + ), + ( + "/cases/0/terminal/patch/0/nodeId", + Value::String("aa".repeat(31)), + ), + ( + "/cases/0/terminal/patch/0/typeId", + Value::String("AA".repeat(32)), + ), + ( + "/cases/0/terminal/createdNodeIds/0", + Value::String("0".to_owned()), + ), + ( + "/cases/0/terminal/result/headId", + Value::String("aa".repeat(31)), + ), + ( + "/cases/0/terminal/result/rootDigest", + Value::String("zz".repeat(32)), + ), + ( + "/cases/0/terminal/result/byteLength", + Value::String("4".to_owned()), + ), + ( + "/cases/0/terminal/result/materializedTextUtf8Hex", + Value::String("ff".to_owned()), + ), + ] { + assert_pointer_invalid(pointer, value); + } + + assert_invalid("set-node-alpha attachment", |corpus| { + let operation = committable(corpus)["terminal"]["patch"] + .as_array_mut() + .expect("patch should be an array") + .iter_mut() + .find(|operation| operation["kind"] == "set-node-alpha") + .expect("set-node-alpha should exist"); + operation["attachmentBytesHex"] = Value::String("xyz".to_owned()); + }); +} + +#[test] +fn strict_corpus_enforces_terminal_and_partition_invariants() { + assert_invalid("false unchanged-parent evidence", |corpus| { + obstruction(corpus)["terminal"]["parentGraphUnchanged"] = Value::Bool(false); + }); + assert_invalid("wrong obstruction patch posture", |corpus| { + obstruction(corpus)["terminal"]["patchPosture"] = Value::String("mutation-plan".to_owned()); + }); + assert_invalid("duplicate case identity", |corpus| { + let cases = corpus["cases"] + .as_array_mut() + .expect("cases should be an array"); + cases[1]["id"] = cases[0]["id"].clone(); + }); + assert_invalid("empty case purpose", |corpus| { + corpus["cases"][0]["purpose"] = Value::String(String::new()); + }); + assert_invalid("duplicate created node", |corpus| { + let created = committable(corpus)["terminal"]["createdNodeIds"] + .as_array_mut() + .expect("created nodes should be an array"); + created.push(created[0].clone()); + }); + assert_invalid("patch node and atom type disagreement", |corpus| { + let operation = committable(corpus)["terminal"]["patch"] + .as_array_mut() + .expect("patch should be an array") + .iter_mut() + .find(|operation| operation["kind"] == "set-node-alpha") + .expect("set-node-alpha should exist"); + operation["typeId"] = Value::String("00".repeat(32)); + }); + assert_invalid("result metric mismatch", |corpus| { + committable(corpus)["terminal"]["result"]["byteLength"] = Value::from(999); + }); +} + +#[test] +fn strict_corpus_preserves_fixed_width_and_empty_root_witnesses() { + validate_oracle_contract(ORACLE_BYTES).expect("committed edge cases should remain lawful"); + let corpus = corpus(); + let above_i32 = corpus["cases"] + .as_array() + .expect("cases should be an array") + .iter() + .find(|case| case["id"] == "coordinate-above-graphql-int") + .expect("fixed-width case should exist"); + assert_eq!( + above_i32["invocation"]["startByte"], + i64::from(i32::MAX) + 1 + ); + let deletion = corpus["cases"] + .as_array() + .expect("cases should be an array") + .iter() + .find(|case| case["id"] == "whole-buffer-delete") + .expect("whole-buffer deletion should exist"); + assert!(deletion["terminal"]["result"]["rootNodeId"].is_null()); + assert_eq!( + deletion["terminal"]["result"]["materializedTextUtf8Hex"], + "" + ); +} + +#[test] +fn strict_corpus_rejects_unknown_members_codes_and_invocation_lexemes() { + let mut unknown_member = corpus(); + unknown_member + .as_object_mut() + .expect("corpus should be an object") + .insert("ambientAuthority".to_owned(), Value::Bool(true)); + assert!(validate_oracle_contract(&bytes(&unknown_member)).is_err()); + + let mut unknown_code = corpus(); + let terminal = unknown_code["cases"] + .as_array_mut() + .expect("cases should be an array") + .iter_mut() + .find_map(|case| { + let terminal = case.get_mut("terminal")?; + (terminal["posture"] == "obstructed").then_some(terminal) + }) + .expect("an obstruction should exist"); + terminal["semanticCode"] = Value::String("invented-obstruction".to_owned()); + assert!(validate_oracle_contract(&bytes(&unknown_code)).is_err()); + + for (field, invalid) in [ + ("bufferId", "AA".repeat(32)), + ("bufferId", "aa".repeat(31)), + ("basisHeadId", "gg".repeat(32)), + ("replacementUtf8Hex", "0".to_owned()), + ("replacementUtf8Hex", "zz".to_owned()), + ("replacementUtf8Hex", "ff".to_owned()), + ] { + let mut corpus = corpus(); + corpus["cases"][0]["invocation"][field] = Value::String(invalid); + assert!( + validate_oracle_contract(&bytes(&corpus)).is_err(), + "{field} accepted a noncanonical lexeme" + ); + } +} diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index 6a4c4399..4ad2079a 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -2,8 +2,6 @@ use std::fmt; #[path = "support/replace_range_contract.rs"] mod contract; -#[path = "support/replace_range_corpus_contract.rs"] -mod corpus_contract; use jedit_echo_host::records::{ BlobFact, BranchFact, BufferFact, DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, @@ -13,7 +11,6 @@ use serde::{Deserialize, Deserializer, Serialize}; use serde_json::Value; use contract::SemanticObstructionCode; -use corpus_contract::validate_oracle_contract; const SCHEMA_BYTES: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), @@ -27,7 +24,6 @@ const ORACLE_BYTES: &[u8] = include_bytes!(concat!( env!("CARGO_MANIFEST_DIR"), "/../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json" )); -const CORPUS_CONTRACT_SOURCE: &str = include_str!("support/replace_range_corpus_contract.rs"); #[derive(Deserialize)] struct CorpusProjection { @@ -346,11 +342,6 @@ fn schema_declares_the_exhaustive_oracle_obstruction_domain() { ); } -#[test] -fn corpus_validator_does_not_duplicate_the_obstruction_domain() { - assert!(!CORPUS_CONTRACT_SOURCE.contains("const CODES")); -} - #[test] fn schema_qualifies_local_oracle_identifiers_by_the_top_level_warp() { let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); @@ -359,54 +350,3 @@ fn schema_qualifies_local_oracle_identifiers_by_the_top_level_warp() { "every footprint and patch identifier is qualified by top-level warpId" ); } - -#[test] -fn committed_oracle_satisfies_the_strict_corpus_shape() { - let corpus: Value = serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); - validate_oracle_contract(&corpus).expect("oracle corpus should satisfy its strict contract"); -} - -#[test] -fn strict_corpus_shape_rejects_unknown_members_and_codes() { - let corpus: Value = serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); - - let mut unknown_member = corpus.clone(); - unknown_member - .as_object_mut() - .expect("corpus should be an object") - .insert("ambientAuthority".to_owned(), Value::Bool(true)); - assert!(validate_oracle_contract(&unknown_member).is_err()); - - let mut unknown_code = corpus; - let terminal = unknown_code["cases"] - .as_array_mut() - .expect("cases should be an array") - .iter_mut() - .find_map(|case| { - let terminal = case.get_mut("terminal")?; - (terminal["posture"] == "obstructed").then_some(terminal) - }) - .expect("an obstruction should exist"); - terminal["semanticCode"] = Value::String("invented-obstruction".to_owned()); - assert!(validate_oracle_contract(&unknown_code).is_err()); -} - -#[test] -fn strict_corpus_shape_rejects_noncanonical_invocation_lexemes() { - for (field, invalid) in [ - ("bufferId", "AA".repeat(32)), - ("bufferId", "aa".repeat(31)), - ("basisHeadId", "gg".repeat(32)), - ("replacementUtf8Hex", "0".to_owned()), - ("replacementUtf8Hex", "zz".to_owned()), - ("replacementUtf8Hex", "ff".to_owned()), - ] { - let mut corpus: Value = - serde_json::from_slice(ORACLE_BYTES).expect("oracle corpus should decode"); - corpus["cases"][0]["invocation"][field] = Value::String(invalid); - assert!( - validate_oracle_contract(&corpus).is_err(), - "{field} accepted a noncanonical lexeme" - ); - } -} diff --git a/native/jedit-echo-host/tests/support/replace_range_contract.rs b/native/jedit-echo-host/tests/support/replace_range_contract.rs index 03b7fb96..0da95848 100644 --- a/native/jedit-echo-host/tests/support/replace_range_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_contract.rs @@ -1,5 +1,15 @@ use serde::{Deserialize, Serialize}; +pub const ORACLE_SCHEMA_VERSION: u32 = 1; +pub const ORACLE_COORDINATE: &str = "jedit.text.ReplaceRange.oracle@1"; +pub const APPLICATION_SCHEMA_COORDINATE: &str = "jedit.text.schema@1"; +pub const INVOCATION_SCHEMA_COORDINATE: &str = "jedit.text.ReplaceRange.oracle-invocation@1"; +pub const SEMANTIC_BASELINE_COMMIT: &str = "c70e12d73b4b00bc92412bab67e1761f7dd22f82"; +pub const EVIDENCE_GRADE: &str = "deterministic-self-validation"; +pub const INDEPENDENCE_LIMIT: &str = "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees"; +pub const ORACLE_WARP_LABEL: &str = "jedit.replace-range.oracle.v1"; +pub const OBSTRUCTED_PATCH_POSTURE: &str = "no-mutation-plan"; + #[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "kebab-case")] pub enum SemanticObstructionCode { @@ -51,6 +61,22 @@ mod tests { use super::*; + #[test] + fn operation_contract_constants_are_frozen() { + assert_eq!(ORACLE_SCHEMA_VERSION, 1); + assert_eq!(ORACLE_COORDINATE, "jedit.text.ReplaceRange.oracle@1"); + assert_eq!(APPLICATION_SCHEMA_COORDINATE, "jedit.text.schema@1"); + assert_eq!( + INVOCATION_SCHEMA_COORDINATE, + "jedit.text.ReplaceRange.oracle-invocation@1" + ); + assert_eq!(SEMANTIC_BASELINE_COMMIT.len(), 40); + assert_eq!(EVIDENCE_GRADE, "deterministic-self-validation"); + assert!(INDEPENDENCE_LIMIT.contains("separately implemented Echo evaluator")); + assert_eq!(ORACLE_WARP_LABEL, "jedit.replace-range.oracle.v1"); + assert_eq!(OBSTRUCTED_PATCH_POSTURE, "no-mutation-plan"); + } + #[test] fn semantic_obstruction_wire_values_are_bijective() { let mut wire_values = BTreeSet::new(); diff --git a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs index ad9382e6..574fbea8 100644 --- a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs @@ -1,217 +1,479 @@ -use serde_json::{Map, Value}; - -use super::contract::SemanticObstructionCode; - -pub fn validate_oracle_contract(corpus: &Value) -> Result<(), String> { - let root = exact_fields( - corpus, - &[ - "schemaVersion", - "coordinate", - "applicationSchemaCoordinate", - "invocationSchemaCoordinate", - "semanticBaselineCommit", - "sourceSet", - "evidenceGrade", - "independenceLimit", - "warpId", - "cases", - ], - )?; - exact_fields( - field(root, "sourceSet")?, - &["algorithm", "domainHex", "framing", "paths", "digestHex"], - )?; - let cases = field(root, "cases")? - .as_array() - .ok_or_else(|| "cases must be an array".to_owned())?; - for case in cases { - validate_oracle_case(case)?; +use std::collections::{BTreeMap, BTreeSet}; + +use jedit_echo_host::identity::hash_bytes; +use jedit_echo_host::records::EMPTY_ROOT_DIGEST_DOMAIN; +use serde::{Deserialize, Serialize}; + +use super::contract::{ + SemanticObstructionCode, APPLICATION_SCHEMA_COORDINATE, EVIDENCE_GRADE, INDEPENDENCE_LIMIT, + INVOCATION_SCHEMA_COORDINATE, OBSTRUCTED_PATCH_POSTURE, ORACLE_COORDINATE, + ORACLE_SCHEMA_VERSION, ORACLE_WARP_LABEL, SEMANTIC_BASELINE_COMMIT, +}; +use super::lexemes::{CommitSha, Hex32, HexBytes, Utf8Hex}; +use super::source_set::source_set; + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct CorpusEnvelope { + schema_version: u32, + coordinate: String, + application_schema_coordinate: String, + invocation_schema_coordinate: String, + semantic_baseline_commit: CommitSha, + source_set: SourceSetEnvelope, + evidence_grade: String, + independence_limit: String, + warp_id: Hex32, + cases: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct SourceSetEnvelope { + algorithm: String, + domain_hex: HexBytes, + framing: String, + paths: Vec, + digest_hex: Hex32, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct CorpusCase { + id: String, + purpose: String, + basis_facts: Vec, + invocation: Invocation, + invocation_bytes_hex: HexBytes, + terminal: Terminal, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct BasisFact { + node_id: Hex32, + type_id: Hex32, + attachment_bytes_hex: HexBytes, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Invocation { + buffer_id: Hex32, + basis_head_id: Hex32, + start_byte: u64, + end_byte: u64, + replacement_utf8_hex: Utf8Hex, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde( + tag = "posture", + rename_all = "kebab-case", + rename_all_fields = "camelCase", + deny_unknown_fields +)] +enum Terminal { + Committable { + footprint: Footprint, + patch: Vec, + created_node_ids: Vec, + updated_node_ids: Vec, + untouched_basis_node_ids: Vec, + result: Box, + }, + Obstructed { + semantic_code: SemanticObstructionCode, + legacy_error_class: String, + legacy_message: String, + parent_graph_unchanged: bool, + patch_posture: String, + }, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct Footprint { + node_reads: Vec, + node_writes: Vec, + attachment_reads: Vec, + attachment_writes: Vec, + edge_reads: Vec, + edge_writes: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde( + tag = "kind", + rename_all = "kebab-case", + rename_all_fields = "camelCase", + deny_unknown_fields +)] +enum PatchOperation { + UpsertNode { + node_id: Hex32, + type_id: Hex32, + }, + SetNodeAlpha { + node_id: Hex32, + type_id: Hex32, + attachment_bytes_hex: HexBytes, + }, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +struct ResultEvidence { + buffer_id: Hex32, + head_id: Hex32, + root_node_id: Option, + root_digest: Hex32, + rewrite_id: Hex32, + diff_id: Hex32, + byte_length: u64, + utf16_length: u64, + line_count: u64, + sequence: u64, + version: u64, + materialized_text_utf8_hex: Utf8Hex, +} + +struct CommittableEvidence<'a> { + footprint: &'a Footprint, + patch: &'a [PatchOperation], + created: &'a [Hex32], + updated: &'a [Hex32], + untouched: &'a [Hex32], + result: &'a ResultEvidence, +} + +pub fn validate_oracle_contract(bytes: &[u8]) -> Result<(), String> { + let corpus: CorpusEnvelope = serde_json::from_slice(bytes) + .map_err(|error| format!("oracle corpus transport is invalid: {error}"))?; + validate_envelope(&corpus) +} + +fn validate_envelope(corpus: &CorpusEnvelope) -> Result<(), String> { + require_equal( + "schemaVersion", + corpus.schema_version, + ORACLE_SCHEMA_VERSION, + )?; + require_equal("coordinate", corpus.coordinate.as_str(), ORACLE_COORDINATE)?; + require_equal( + "applicationSchemaCoordinate", + corpus.application_schema_coordinate.as_str(), + APPLICATION_SCHEMA_COORDINATE, + )?; + require_equal( + "invocationSchemaCoordinate", + corpus.invocation_schema_coordinate.as_str(), + INVOCATION_SCHEMA_COORDINATE, + )?; + require_equal( + "semanticBaselineCommit", + corpus.semantic_baseline_commit.as_str(), + SEMANTIC_BASELINE_COMMIT, + )?; + require_equal( + "evidenceGrade", + corpus.evidence_grade.as_str(), + EVIDENCE_GRADE, + )?; + require_equal( + "independenceLimit", + corpus.independence_limit.as_str(), + INDEPENDENCE_LIMIT, + )?; + let expected_warp_id = hex::encode(warp_core::make_warp_id(ORACLE_WARP_LABEL).as_bytes()); + require_equal("warpId", corpus.warp_id.as_str(), expected_warp_id.as_str())?; + validate_source_set(&corpus.source_set)?; + if corpus.cases.is_empty() { + return Err("cases must not be empty".to_owned()); + } + let mut case_ids = BTreeSet::new(); + for case in &corpus.cases { + if case.id.is_empty() || case.purpose.is_empty() { + return Err("case id and purpose must not be empty".to_owned()); + } + if !case_ids.insert(case.id.as_str()) { + return Err(format!("duplicate case id {}", case.id)); + } + validate_case(case)?; } Ok(()) } -fn validate_oracle_case(case: &Value) -> Result<(), String> { - let case = exact_fields( - case, - &[ - "id", - "purpose", - "basisFacts", - "invocation", - "invocationBytesHex", - "terminal", - ], - )?; - for fact in field(case, "basisFacts")? - .as_array() - .ok_or_else(|| "basisFacts must be an array".to_owned())? +fn validate_source_set(claimed: &SourceSetEnvelope) -> Result<(), String> { + let expected = source_set(); + require_equal( + "sourceSet.algorithm", + claimed.algorithm.as_str(), + expected.algorithm, + )?; + require_equal( + "sourceSet.domainHex", + claimed.domain_hex.as_str(), + expected.domain_hex.as_str(), + )?; + require_equal( + "sourceSet.framing", + claimed.framing.as_str(), + expected.framing, + )?; + if claimed + .paths + .iter() + .map(String::as_str) + .ne(expected.paths.iter().copied()) { - exact_fields(fact, &["nodeId", "typeId", "attachmentBytesHex"])?; + return Err("sourceSet.paths differ from the generator source set".to_owned()); } - let invocation = exact_fields( - field(case, "invocation")?, - &[ - "bufferId", - "basisHeadId", - "startByte", - "endByte", - "replacementUtf8Hex", - ], - )?; - validate_node_id(field(invocation, "bufferId")?, "bufferId")?; - validate_node_id(field(invocation, "basisHeadId")?, "basisHeadId")?; - field(invocation, "startByte")? - .as_u64() - .ok_or_else(|| "startByte must be a u64 JSON decimal".to_owned())?; - field(invocation, "endByte")? - .as_u64() - .ok_or_else(|| "endByte must be a u64 JSON decimal".to_owned())?; - let replacement = decode_lowercase_hex( - field(invocation, "replacementUtf8Hex")?, - "replacementUtf8Hex", - )?; - std::str::from_utf8(&replacement) - .map_err(|_| "replacementUtf8Hex must encode UTF-8".to_owned())?; - validate_terminal(field(case, "terminal")?) -} - -fn validate_node_id(value: &Value, label: &str) -> Result<(), String> { - let bytes = decode_lowercase_hex(value, label)?; - if bytes.len() != 32 { - return Err(format!("{label} must encode exactly 32 bytes")); + require_equal( + "sourceSet.digestHex", + claimed.digest_hex.as_str(), + expected.digest_hex.as_str(), + ) +} + +fn validate_case(case: &CorpusCase) -> Result<(), String> { + unique_ids( + "basisFacts.nodeId", + case.basis_facts.iter().map(|fact| fact.node_id.as_str()), + )?; + let expected_invocation = serde_json::to_vec(&case.invocation) + .map_err(|error| format!("invocation encoding failed: {error}"))?; + require_equal( + "invocationBytesHex", + case.invocation_bytes_hex.bytes(), + expected_invocation, + )?; + match &case.terminal { + Terminal::Committable { + footprint, + patch, + created_node_ids, + updated_node_ids, + untouched_basis_node_ids, + result, + } => validate_committable( + case, + CommittableEvidence { + footprint, + patch, + created: created_node_ids, + updated: updated_node_ids, + untouched: untouched_basis_node_ids, + result, + }, + ), + Terminal::Obstructed { + semantic_code: _, + legacy_error_class, + legacy_message, + parent_graph_unchanged, + patch_posture, + } => { + if legacy_error_class.is_empty() || legacy_message.is_empty() { + return Err("obstruction legacy evidence must not be empty".to_owned()); + } + require_equal("parentGraphUnchanged", *parent_graph_unchanged, true)?; + require_equal( + "patchPosture", + patch_posture.as_str(), + OBSTRUCTED_PATCH_POSTURE, + ) + } } - Ok(()) } -fn decode_lowercase_hex(value: &Value, label: &str) -> Result, String> { - let value = value - .as_str() - .ok_or_else(|| format!("{label} must be a string"))?; - if value.len() % 2 != 0 { - return Err(format!("{label} must contain an even number of digits")); +fn validate_committable( + case: &CorpusCase, + evidence: CommittableEvidence<'_>, +) -> Result<(), String> { + let CommittableEvidence { + footprint, + patch, + created, + updated, + untouched, + result, + } = evidence; + let basis = unique_ids( + "basisFacts.nodeId", + case.basis_facts.iter().map(|fact| fact.node_id.as_str()), + )?; + let reads = unique_ids("footprint.nodeReads", ids(&footprint.node_reads))?; + let writes = unique_ids("footprint.nodeWrites", ids(&footprint.node_writes))?; + require_equal( + "attachmentReads", + unique_ids( + "footprint.attachmentReads", + ids(&footprint.attachment_reads), + )?, + reads, + )?; + require_equal( + "attachmentWrites", + unique_ids( + "footprint.attachmentWrites", + ids(&footprint.attachment_writes), + )?, + writes.clone(), + )?; + if !footprint.edge_reads.is_empty() || !footprint.edge_writes.is_empty() { + return Err("ReplaceRange footprint must not contain edges".to_owned()); } - if !value - .bytes() - .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) + let created = unique_ids("createdNodeIds", ids(created))?; + let updated = unique_ids("updatedNodeIds", ids(updated))?; + let untouched = unique_ids("untouchedBasisNodeIds", ids(untouched))?; + if !created.is_disjoint(&updated) + || !created.is_disjoint(&untouched) + || !updated.is_disjoint(&untouched) { - return Err(format!("{label} must contain lowercase hexadecimal only")); + return Err("created, updated, and untouched node sets must be disjoint".to_owned()); } - hex::decode(value).map_err(|error| format!("{label} must decode: {error}")) + require_equal( + "nodeWrites partition", + created.union(&updated).copied().collect::>(), + writes.clone(), + )?; + require_equal( + "basis partition", + updated.union(&untouched).copied().collect::>(), + basis, + )?; + validate_patch(patch, &writes)?; + validate_result(case, result, &writes, &updated) } -fn validate_terminal(terminal: &Value) -> Result<(), String> { - match terminal["posture"].as_str() { - Some("committable") => validate_committable(terminal), - Some("obstructed") => validate_obstructed(terminal), - Some(posture) => Err(format!("unknown terminal posture {posture}")), - None => Err("terminal posture must be a string".to_owned()), +fn validate_patch(patch: &[PatchOperation], writes: &BTreeSet<&str>) -> Result<(), String> { + let mut upserts = BTreeMap::new(); + let mut attachments = BTreeMap::new(); + for operation in patch { + let (target, type_id, destination) = match operation { + PatchOperation::UpsertNode { node_id, type_id } => { + (node_id.as_str(), type_id.as_str(), &mut upserts) + } + PatchOperation::SetNodeAlpha { + node_id, + type_id, + attachment_bytes_hex: _, + } => (node_id.as_str(), type_id.as_str(), &mut attachments), + }; + if destination.insert(target, type_id).is_some() { + return Err(format!("duplicate patch operation for {target}")); + } } + require_equal( + "patch upsert targets", + upserts.keys().copied().collect::>(), + writes.clone(), + )?; + require_equal( + "patch attachment targets", + attachments.keys().copied().collect::>(), + writes.clone(), + )?; + for (node_id, upsert_type) in upserts { + require_equal( + "patch node and atom type", + upsert_type, + *attachments + .get(node_id) + .expect("attachment target equality was already checked"), + )?; + } + Ok(()) } -fn validate_committable(terminal: &Value) -> Result<(), String> { - let terminal = exact_fields( - terminal, - &[ - "posture", - "footprint", - "patch", - "createdNodeIds", - "updatedNodeIds", - "untouchedBasisNodeIds", - "result", - ], - )?; - exact_fields( - field(terminal, "footprint")?, - &[ - "nodeReads", - "nodeWrites", - "attachmentReads", - "attachmentWrites", - "edgeReads", - "edgeWrites", - ], - )?; - for operation in field(terminal, "patch")? - .as_array() - .ok_or_else(|| "patch must be an array".to_owned())? - { - validate_patch_operation(operation)?; +fn validate_result( + case: &CorpusCase, + result: &ResultEvidence, + writes: &BTreeSet<&str>, + updated: &BTreeSet<&str>, +) -> Result<(), String> { + require_equal( + "result.bufferId", + result.buffer_id.as_str(), + case.invocation.buffer_id.as_str(), + )?; + if !updated.contains(result.buffer_id.as_str()) { + return Err("result Buffer must be an updated basis node".to_owned()); + } + for (label, id) in [ + ("headId", &result.head_id), + ("rewriteId", &result.rewrite_id), + ("diffId", &result.diff_id), + ] { + if !writes.contains(id.as_str()) { + return Err(format!("result {label} must be in the declared writes")); + } + } + match &result.root_node_id { + Some(root) => require_equal( + "nonempty root digest", + result.root_digest.as_str(), + root.as_str(), + )?, + None => require_equal( + "empty root digest", + result.root_digest.as_str(), + hex::encode(hash_bytes(EMPTY_ROOT_DIGEST_DOMAIN, &[])).as_str(), + )?, } - exact_fields( - field(terminal, "result")?, - &[ - "bufferId", - "headId", - "rootNodeId", - "rootDigest", - "rewriteId", - "diffId", - "byteLength", - "utf16Length", - "lineCount", - "sequence", - "version", - "materializedTextUtf8Hex", - ], + let text_bytes = result.materialized_text_utf8_hex.bytes(); + let text = + std::str::from_utf8(&text_bytes).expect("Utf8Hex guarantees valid materialized UTF-8"); + require_equal( + "result.byteLength", + result.byte_length, + u64::try_from(text_bytes.len()).map_err(|_| "materialized text exceeds u64")?, )?; + require_equal( + "result.utf16Length", + result.utf16_length, + u64::try_from(text.encode_utf16().count()).map_err(|_| "UTF-16 length exceeds u64")?, + )?; + require_equal( + "result.lineCount", + result.line_count, + u64::try_from(text.bytes().filter(|byte| *byte == b'\n').count()) + .map_err(|_| "line count exceeds u64")? + + 1, + )?; + if result.sequence == 0 || result.version == 0 { + return Err("committable result sequence and version must advance".to_owned()); + } Ok(()) } -fn validate_obstructed(terminal: &Value) -> Result<(), String> { - let terminal = exact_fields( - terminal, - &[ - "posture", - "semanticCode", - "legacyErrorClass", - "legacyMessage", - "parentGraphUnchanged", - "patchPosture", - ], - )?; - serde_json::from_value::(field(terminal, "semanticCode")?.clone()) - .map_err(|error| format!("invalid semanticCode: {error}"))?; - Ok(()) +fn ids(values: &[Hex32]) -> impl Iterator { + values.iter().map(Hex32::as_str) } -fn validate_patch_operation(operation: &Value) -> Result<(), String> { - match operation["kind"].as_str() { - Some("upsert-node") => { - exact_fields(operation, &["kind", "nodeId", "typeId"])?; - Ok(()) +fn unique_ids<'a>( + label: &str, + values: impl IntoIterator, +) -> Result, String> { + let mut unique = BTreeSet::new(); + for value in values { + if !unique.insert(value) { + return Err(format!("{label} contains duplicate {value}")); } - Some("set-node-alpha") => { - exact_fields( - operation, - &["kind", "nodeId", "typeId", "attachmentBytesHex"], - )?; - Ok(()) - } - Some(kind) => Err(format!("unknown patch kind {kind}")), - None => Err("patch kind must be a string".to_owned()), } + Ok(unique) } -fn exact_fields<'a>(value: &'a Value, expected: &[&str]) -> Result<&'a Map, String> { - let object = value - .as_object() - .ok_or_else(|| "contract value must be an object".to_owned())?; - let mut actual = object.keys().map(String::as_str).collect::>(); - actual.sort_unstable(); - let mut expected = expected.to_vec(); - expected.sort_unstable(); +fn require_equal( + label: &str, + actual: T, + expected: T, +) -> Result<(), String> { if actual != expected { return Err(format!( - "object fields differ: expected {expected:?}, received {actual:?}" + "{label} differs: expected {expected:?}, received {actual:?}" )); } - Ok(object) -} - -fn field<'a>(object: &'a Map, name: &str) -> Result<&'a Value, String> { - object - .get(name) - .ok_or_else(|| format!("missing required field {name}")) + Ok(()) } diff --git a/native/jedit-echo-host/tests/support/replace_range_corpus_lexemes.rs b/native/jedit-echo-host/tests/support/replace_range_corpus_lexemes.rs new file mode 100644 index 00000000..8c30fc57 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_corpus_lexemes.rs @@ -0,0 +1,115 @@ +use serde::de::Error as _; +use serde::{Deserialize, Deserializer, Serialize}; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(transparent)] +pub(super) struct Hex32(String); + +impl Hex32 { + pub(super) fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'de> Deserialize<'de> for Hex32 { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + let bytes = + decode_lowercase_hex(&value, "32-byte hexadecimal value").map_err(D::Error::custom)?; + if bytes.len() != 32 { + return Err(D::Error::custom( + "32-byte hexadecimal value must encode exactly 32 bytes", + )); + } + Ok(Self(value)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(transparent)] +pub(super) struct CommitSha(String); + +impl CommitSha { + pub(super) fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'de> Deserialize<'de> for CommitSha { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + let bytes = decode_lowercase_hex(&value, "commit SHA").map_err(D::Error::custom)?; + if bytes.len() != 20 { + return Err(D::Error::custom("commit SHA must encode exactly 20 bytes")); + } + Ok(Self(value)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(transparent)] +pub(super) struct HexBytes(String); + +impl HexBytes { + pub(super) fn as_str(&self) -> &str { + &self.0 + } + + pub(super) fn bytes(&self) -> Vec { + hex::decode(&self.0).expect("validated hexadecimal bytes must decode") + } +} + +impl<'de> Deserialize<'de> for HexBytes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + decode_lowercase_hex(&value, "hexadecimal bytes").map_err(D::Error::custom)?; + Ok(Self(value)) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(transparent)] +pub(super) struct Utf8Hex(String); + +impl Utf8Hex { + pub(super) fn bytes(&self) -> Vec { + hex::decode(&self.0).expect("validated UTF-8 hexadecimal bytes must decode") + } +} + +impl<'de> Deserialize<'de> for Utf8Hex { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + let bytes = + decode_lowercase_hex(&value, "UTF-8 hexadecimal bytes").map_err(D::Error::custom)?; + std::str::from_utf8(&bytes) + .map_err(|_| D::Error::custom("UTF-8 hexadecimal bytes must encode valid UTF-8"))?; + Ok(Self(value)) + } +} + +fn decode_lowercase_hex(value: &str, label: &str) -> Result, String> { + if !value.len().is_multiple_of(2) { + return Err(format!("{label} must contain an even number of digits")); + } + if !value + .bytes() + .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte)) + { + return Err(format!("{label} must contain lowercase hexadecimal only")); + } + hex::decode(value).map_err(|error| format!("{label} must decode: {error}")) +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index b7244e7b..545e53c2 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -25,11 +25,13 @@ pub use basis::BasisSetup; use basis::{apply_ops, make_basis}; use consequence::{validate_consequence, ReplaceExpectation}; pub use contract::SemanticObstructionCode; +use contract::{ + APPLICATION_SCHEMA_COORDINATE, EVIDENCE_GRADE, INDEPENDENCE_LIMIT, + INVOCATION_SCHEMA_COORDINATE, OBSTRUCTED_PATCH_POSTURE, ORACLE_COORDINATE, + ORACLE_SCHEMA_VERSION, ORACLE_WARP_LABEL, SEMANTIC_BASELINE_COMMIT, +}; use source_set::{source_set, SourceSet}; -const ORACLE_WARP_LABEL: &str = "jedit.replace-range.oracle.v1"; -const SEMANTIC_BASELINE_COMMIT: &str = "c70e12d73b4b00bc92412bab67e1761f7dd22f82"; - #[derive(Clone, Copy)] pub enum ExpectedPosture { Success, @@ -172,14 +174,14 @@ pub fn generate_corpus(specs: Vec) -> OracleCorpus { .map(|spec| evaluate_case(warp_id, spec)) .collect(); OracleCorpus { - schema_version: 1, - coordinate: "jedit.text.ReplaceRange.oracle@1", - application_schema_coordinate: "jedit.text.schema@1", - invocation_schema_coordinate: "jedit.text.ReplaceRange.oracle-invocation@1", + schema_version: ORACLE_SCHEMA_VERSION, + coordinate: ORACLE_COORDINATE, + application_schema_coordinate: APPLICATION_SCHEMA_COORDINATE, + invocation_schema_coordinate: INVOCATION_SCHEMA_COORDINATE, semantic_baseline_commit: SEMANTIC_BASELINE_COMMIT, source_set: source_set(), - evidence_grade: "deterministic-self-validation", - independence_limit: "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", + evidence_grade: EVIDENCE_GRADE, + independence_limit: INDEPENDENCE_LIMIT, warp_id: hex::encode(warp_id.as_bytes()), cases, } @@ -239,7 +241,7 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { legacy_error_class: error_class, legacy_message: actual_message, parent_graph_unchanged: before == project_store(&store), - patch_posture: "no-mutation-plan", + patch_posture: OBSTRUCTED_PATCH_POSTURE, } } (Ok(_), ExpectedPosture::Obstruction { .. }) => { diff --git a/native/jedit-echo-host/tests/support/replace_range_schema.rs b/native/jedit-echo-host/tests/support/replace_range_schema.rs index 41f7ddda..e5ee141b 100644 --- a/native/jedit-echo-host/tests/support/replace_range_schema.rs +++ b/native/jedit-echo-host/tests/support/replace_range_schema.rs @@ -9,9 +9,12 @@ use serde_json::{json, Value}; #[path = "replace_range_contract.rs"] mod contract; -use contract::SemanticObstructionCode; +use contract::{ + SemanticObstructionCode, APPLICATION_SCHEMA_COORDINATE, INVOCATION_SCHEMA_COORDINATE, + OBSTRUCTED_PATCH_POSTURE, ORACLE_COORDINATE, +}; -pub const SCHEMA_COORDINATE: &str = "jedit.text.schema@1"; +pub const SCHEMA_COORDINATE: &str = APPLICATION_SCHEMA_COORDINATE; pub const STRING_ESCAPE_VECTOR: &str = concat!( "\u{0000}\u{0001}\u{0002}\u{0003}\u{0004}\u{0005}\u{0006}\u{0007}", "\u{0008}\u{0009}\u{000a}\u{000b}\u{000c}\u{000d}\u{000e}\u{000f}", @@ -119,7 +122,7 @@ pub fn expected_schema() -> Value { "emittedOperations": ["upsert-node", "set-node-alpha"] }, "oracleInvocation": { - "coordinate": "jedit.text.ReplaceRange.oracle-invocation@1", + "coordinate": INVOCATION_SCHEMA_COORDINATE, "posture": "finite-corpus-input-not-runtime-abi", "encoding": "compact-utf8-json", "objectMemberOrder": [ @@ -139,7 +142,7 @@ pub fn expected_schema() -> Value { "canonicalBytesField": "invocationBytesHex" }, "oracleCorpus": { - "coordinate": "jedit.text.ReplaceRange.oracle@1", + "coordinate": ORACLE_COORDINATE, "encoding": "pretty-utf8-json-final-newline", "unknownMembers": "forbidden", "localIdentifierQualification": "every footprint and patch identifier is qualified by top-level warpId", @@ -198,7 +201,7 @@ pub fn expected_schema() -> Value { .iter() .map(|code| code.as_str()) .collect::>(), - "obstructedPatchPosture": "no-mutation-plan" + "obstructedPatchPosture": OBSTRUCTED_PATCH_POSTURE }, "footprintMemberOrder": [ "nodeReads", diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index 352a39c3..d3939f8e 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -1,7 +1,10 @@ use serde::Serialize; use sha2::{Digest, Sha256}; -const SOURCE_SET_DOMAIN: &[u8] = b"jedit.replace-range.oracle-source.v1\0"; +pub const SOURCE_SET_ALGORITHM: &str = "sha256"; +pub const SOURCE_SET_DOMAIN: &[u8] = b"jedit.replace-range.oracle-source.v1\0"; +pub const SOURCE_SET_FRAMING: &str = + "domain || repeated(u32be-path-length || path-utf8 || u64be-byte-length || bytes)"; const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ ( "native/jedit-echo-host/Cargo.toml", @@ -72,14 +75,28 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct SourceSet { - algorithm: &'static str, - domain_hex: String, - framing: &'static str, - paths: Vec<&'static str>, - digest_hex: String, + pub algorithm: &'static str, + pub domain_hex: String, + pub framing: &'static str, + pub paths: Vec<&'static str>, + pub digest_hex: String, } pub fn source_set() -> SourceSet { + SourceSet { + algorithm: SOURCE_SET_ALGORITHM, + domain_hex: hex::encode(SOURCE_SET_DOMAIN), + framing: SOURCE_SET_FRAMING, + paths: expected_source_paths(), + digest_hex: expected_source_digest_hex(), + } +} + +pub fn expected_source_paths() -> Vec<&'static str> { + SOURCE_SET_FILES.iter().map(|(path, _)| *path).collect() +} + +pub fn expected_source_digest_hex() -> String { let mut digest = Sha256::new(); digest.update(SOURCE_SET_DOMAIN); for (path, bytes) in SOURCE_SET_FILES { @@ -88,11 +105,5 @@ pub fn source_set() -> SourceSet { digest.update((bytes.len() as u64).to_be_bytes()); digest.update(bytes); } - SourceSet { - algorithm: "sha256", - domain_hex: hex::encode(SOURCE_SET_DOMAIN), - framing: "domain || repeated(u32be-path-length || path-utf8 || u64be-byte-length || bytes)", - paths: SOURCE_SET_FILES.iter().map(|(path, _)| *path).collect(), - digest_hex: hex::encode(digest.finalize()), - } + hex::encode(digest.finalize()) } From 8112752b85862d065811fbe133fdfe8f9e8021c1 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:41:29 -0700 Subject: [PATCH 19/63] Fix: enforce canonical oracle bytes --- CHANGELOG.md | 3 ++- .../tests/replace_range_corpus_conformance.rs | 22 +++++++++++++++++++ .../support/replace_range_corpus_contract.rs | 9 +++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f2ecce9..0a1d9b49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,8 @@ - Replaced the member-name-only `ReplaceRange` oracle check with a typed raw-byte contract. Envelope identities, source provenance, canonical identifier and UTF-8 lexemes, invocation bytes, footprints, patches, result - metrics, and no-partial-mutation obstruction evidence now fail closed. + metrics, no-partial-mutation obstruction evidence, member order, whitespace, + and final-newline framing now fail closed. - Enforced the published `ReplaceRange` oracle-invocation lexical law. Strict conformance now rejects uppercase, malformed, or wrong-width identifiers and odd, malformed, or non-UTF-8 replacement-byte encodings. diff --git a/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs b/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs index b302e3d8..98ef43f1 100644 --- a/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs @@ -66,6 +66,28 @@ fn committed_oracle_satisfies_the_strict_corpus_contract() { validate_oracle_contract(ORACLE_BYTES).expect("oracle corpus should satisfy its contract"); } +#[test] +fn strict_corpus_rejects_noncanonical_member_order_and_framing() { + let canonical = std::str::from_utf8(ORACLE_BYTES).expect("oracle should be UTF-8"); + let ordered = concat!( + " \"schemaVersion\": 1,\n", + " \"coordinate\": \"jedit.text.ReplaceRange.oracle@1\"," + ); + let reordered = concat!( + " \"coordinate\": \"jedit.text.ReplaceRange.oracle@1\",\n", + " \"schemaVersion\": 1," + ); + assert_eq!(canonical.matches(ordered).count(), 1); + let reordered = canonical.replacen(ordered, reordered, 1); + serde_json::from_str::(&reordered).expect("reordered corpus remains valid JSON"); + assert!(validate_oracle_contract(reordered.as_bytes()).is_err()); + + let without_final_newline = ORACLE_BYTES + .strip_suffix(b"\n") + .expect("canonical corpus should end in one newline"); + assert!(validate_oracle_contract(without_final_newline).is_err()); +} + #[test] fn corpus_validator_does_not_duplicate_the_obstruction_domain() { assert!(!CORPUS_CONTRACT_SOURCE.contains("const CODES")); diff --git a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs index 574fbea8..c1673261 100644 --- a/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_corpus_contract.rs @@ -150,7 +150,14 @@ struct CommittableEvidence<'a> { pub fn validate_oracle_contract(bytes: &[u8]) -> Result<(), String> { let corpus: CorpusEnvelope = serde_json::from_slice(bytes) .map_err(|error| format!("oracle corpus transport is invalid: {error}"))?; - validate_envelope(&corpus) + validate_envelope(&corpus)?; + let mut canonical = serde_json::to_vec_pretty(&corpus) + .map_err(|error| format!("oracle corpus canonicalization failed: {error}"))?; + canonical.push(b'\n'); + if canonical != bytes { + return Err("oracle corpus bytes are not canonical".to_owned()); + } + Ok(()) } fn validate_envelope(corpus: &CorpusEnvelope) -> Result<(), String> { From 13b16f2e7195458138865460c5948744ea8615ed Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:44:03 -0700 Subject: [PATCH 20/63] Fix: serialize lawpack resource updates --- CHANGELOG.md | 3 +++ package.json | 3 ++- tests/replace-range-law-cycle.spec.mjs | 26 ++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a1d9b49..a8d959e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Made the `ReplaceRange` lawpack updater run only its exact serial writer tests + before the complete locked reader/conformance pass, eliminating an + update-time race against stale committed resources. - Replaced the member-name-only `ReplaceRange` oracle check with a typed raw-byte contract. Envelope identities, source provenance, canonical identifier and UTF-8 lexemes, invocation bytes, footprints, patches, result diff --git a/package.json b/package.json index 93ed490c..d0490ecb 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "scripts": { "echo:build": "cargo build --manifest-path native/jedit-echo-host/Cargo.toml", "echo:test": "cargo test --manifest-path native/jedit-echo-host/Cargo.toml", - "lawpack:replace-range:update": "JEDIT_UPDATE_REPLACE_RANGE_SCHEMA=1 cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema published_schema_and_codec_vectors_regenerate_byte_for_byte && cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema && JEDIT_UPDATE_REPLACE_RANGE_ORACLE=1 cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_oracle && cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_oracle && cargo test --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema_conformance", + "lawpack:replace-range:check": "cargo test --locked --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema && cargo test --locked --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_oracle && cargo test --locked --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema_conformance && cargo test --locked --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_corpus_conformance", + "lawpack:replace-range:update": "JEDIT_UPDATE_REPLACE_RANGE_SCHEMA=1 cargo test --locked --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_schema published_schema_and_codec_vectors_regenerate_byte_for_byte -- --exact --test-threads=1 && JEDIT_UPDATE_REPLACE_RANGE_ORACLE=1 cargo test --locked --manifest-path native/jedit-echo-host/Cargo.toml --test replace_range_oracle replace_range_oracle_matches_the_committed_corpus -- --exact --test-threads=1 && npm run lawpack:replace-range:check", "dev": "npm run echo:build && tsx src/main.ts", "clean": "node scripts/clean-dist.mjs", "build": "npm run echo:build && npm run clean && tsc -p tsconfig.json && node scripts/copy-assets.mjs", diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index 2e7656eb..02daec72 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -50,3 +50,29 @@ test('ReplaceRange oracle support modules stay within the Rust file budget', () assert.deepEqual(oversized, []); }); + +test('ReplaceRange updater isolates writers before committed-resource readers', () => { + const packageJson = JSON.parse( + fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'), + ); + const update = packageJson.scripts['lawpack:replace-range:update']; + const check = packageJson.scripts['lawpack:replace-range:check']; + + assert.match( + update, + /JEDIT_UPDATE_REPLACE_RANGE_SCHEMA=1 cargo test --locked .* published_schema_and_codec_vectors_regenerate_byte_for_byte -- --exact --test-threads=1/, + ); + assert.match( + update, + /JEDIT_UPDATE_REPLACE_RANGE_ORACLE=1 cargo test --locked .* replace_range_oracle_matches_the_committed_corpus -- --exact --test-threads=1/, + ); + assert.match(update, /&& npm run lawpack:replace-range:check$/); + for (const target of [ + 'replace_range_schema', + 'replace_range_oracle', + 'replace_range_schema_conformance', + 'replace_range_corpus_conformance', + ]) { + assert.match(check, new RegExp(`cargo test --locked .* --test ${target}`)); + } +}); From d57e0d239c005898839a15041807580ed2dfe595 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:50:33 -0700 Subject: [PATCH 21/63] Fix: prove retained basis relationships --- CHANGELOG.md | 4 + .../replace-range-v1.oracle.json | 3 +- .../replace-range-v1.oracle.sha256 | 2 +- .../tests/replace_range_oracle.rs | 94 +++++------ .../support/replace_range_basis_witness.rs | 156 ++++++++++++++++++ .../tests/support/replace_range_source_set.rs | 4 + 6 files changed, 204 insertions(+), 59 deletions(-) create mode 100644 native/jedit-echo-host/tests/support/replace_range_basis_witness.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a8d959e7..4d9725b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Strengthened the `ReplaceRange` stale- and foreign-basis witnesses to prove + the retained causal relationships they claim. A stale basis must be the + direct ancestor of the target Buffer's canonical Head, while a foreign basis + must be the canonical Head of its own retained Buffer. - Made the `ReplaceRange` lawpack updater run only its exact serial writer tests before the complete locked reader/conformance pass, eliminating an update-time race against stale committed resources. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index d29de28c..0698262a 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -19,6 +19,7 @@ "native/jedit-echo-host/src/rope/window.rs", "native/jedit-echo-host/tests/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_basis.rs", + "native/jedit-echo-host/tests/support/replace_range_basis_witness.rs", "native/jedit-echo-host/tests/support/replace_range_contract.rs", "native/jedit-echo-host/tests/support/replace_range_consequence.rs", "native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs", @@ -26,7 +27,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "2c18f9fc895fcd070134eb59592e704940c6816a4dbe628ff71f3183442a7815" + "digestHex": "527209d4d6772ea31aaa9e90afe366e1dd26d0e2df569c713550f2552a1fc5cf" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index e6ea2184..d738127b 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -dbcc3767d71bbeb1de44c6599fd840385b368eb9116230dea408e9b4511a3ee2 +ff52164f8346299cd9a13417aeb7831764bf2a3fc1cb3a1b05afe14cba477cf1 diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs index 24f9b138..795bbb1b 100644 --- a/native/jedit-echo-host/tests/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -1,3 +1,5 @@ +#[path = "support/replace_range_basis_witness.rs"] +mod basis_witness; #[path = "support/replace_range_oracle.rs"] mod oracle_support; #[path = "support/resource_fixture.rs"] @@ -5,7 +7,9 @@ mod resource_fixture; use std::path::PathBuf; -use jedit_echo_host::records::{fact_type_id, BufferFact, HeadFact}; +use basis_witness::{ + assert_retained_basis, corrupt_foreign_canonical_head, corrupt_stale_ancestry, +}; use oracle_support::{ canonical_corpus_bytes, generate_corpus, BasisSetup, CaseSpec, ExpectedPosture, SemanticObstructionCode, @@ -53,6 +57,38 @@ fn basis_obstructions_name_real_retained_heads() { assert_retained_basis(cases, "foreign-basis", false); } +#[test] +fn basis_witness_rejects_false_stale_and_foreign_relationships() { + let corpus: serde_json::Value = serde_json::from_slice( + &std::fs::read(corpus_path()).expect("committed oracle corpus should exist"), + ) + .expect("committed oracle corpus should decode"); + + let mut stale_cases = corpus["cases"] + .as_array() + .expect("oracle cases should be an array") + .clone(); + corrupt_stale_ancestry(&mut stale_cases); + assert!( + std::panic::catch_unwind(|| assert_retained_basis(&stale_cases, "stale-basis", true)) + .is_err(), + "stale witness accepted a current Head without stale ancestry" + ); + + let mut foreign_cases = corpus["cases"] + .as_array() + .expect("oracle cases should be an array") + .clone(); + corrupt_foreign_canonical_head(&mut foreign_cases); + assert!( + std::panic::catch_unwind(|| { + assert_retained_basis(&foreign_cases, "foreign-basis", false); + }) + .is_err(), + "foreign witness accepted a Buffer that did not name the invocation Head" + ); +} + #[test] fn source_set_binds_native_dependency_selection() { let corpus: serde_json::Value = serde_json::from_slice( @@ -73,62 +109,6 @@ fn source_set_binds_native_dependency_selection() { } } -fn assert_retained_basis(cases: &[serde_json::Value], case_id: &str, same_buffer: bool) { - let case = cases - .iter() - .find(|case| case["id"] == case_id) - .unwrap_or_else(|| panic!("{case_id} should exist")); - let invocation_buffer_id = case["invocation"]["bufferId"] - .as_str() - .expect("invocation buffer should be a string"); - let invocation_basis_id = case["invocation"]["basisHeadId"] - .as_str() - .expect("invocation basis should be a string"); - let facts = case["basisFacts"] - .as_array() - .expect("basis facts should be an array"); - let head_type_id = hex::encode(fact_type_id::().as_bytes()); - let retained_head = facts - .iter() - .find(|fact| fact["nodeId"] == invocation_basis_id && fact["typeId"] == head_type_id) - .unwrap_or_else(|| panic!("{case_id} basis should be a retained Head fact")); - let head: HeadFact = serde_json::from_slice( - &hex::decode( - retained_head["attachmentBytesHex"] - .as_str() - .expect("head bytes should be hexadecimal"), - ) - .expect("head bytes should decode"), - ) - .expect("retained head should decode"); - let head_buffer_id = hex::encode(head.buffer_id.0); - assert_eq!( - head_buffer_id == invocation_buffer_id, - same_buffer, - "{case_id} buffer relationship drifted" - ); - - let buffer_type_id = hex::encode(fact_type_id::().as_bytes()); - let retained_buffer = facts - .iter() - .find(|fact| fact["nodeId"] == invocation_buffer_id && fact["typeId"] == buffer_type_id) - .unwrap_or_else(|| panic!("{case_id} target Buffer fact should be retained")); - let buffer: BufferFact = serde_json::from_slice( - &hex::decode( - retained_buffer["attachmentBytesHex"] - .as_str() - .expect("buffer bytes should be hexadecimal"), - ) - .expect("buffer bytes should decode"), - ) - .expect("retained buffer should decode"); - assert_ne!( - invocation_basis_id, - hex::encode(buffer.canonical_head_id.0), - "{case_id} must not use the current canonical head" - ); -} - fn corpus_path() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json") diff --git a/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs b/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs new file mode 100644 index 00000000..04992a71 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs @@ -0,0 +1,156 @@ +use jedit_echo_host::records::{fact_type_id, BufferFact, HeadFact}; + +pub fn assert_retained_basis(cases: &[serde_json::Value], case_id: &str, same_buffer: bool) { + let case = cases + .iter() + .find(|case| case["id"] == case_id) + .unwrap_or_else(|| panic!("{case_id} should exist")); + let invocation_buffer_id = case["invocation"]["bufferId"] + .as_str() + .expect("invocation buffer should be a string"); + let invocation_basis_id = case["invocation"]["basisHeadId"] + .as_str() + .expect("invocation basis should be a string"); + let facts = case["basisFacts"] + .as_array() + .expect("basis facts should be an array"); + let head_type_id = hex::encode(fact_type_id::().as_bytes()); + let retained_head = projected_fact( + facts, + invocation_basis_id, + &head_type_id, + "invocation basis Head", + ); + let head: HeadFact = decode_projected_fact(retained_head); + let head_buffer_id = hex::encode(head.buffer_id.0); + assert_eq!( + head_buffer_id == invocation_buffer_id, + same_buffer, + "{case_id} buffer relationship drifted" + ); + + let buffer_type_id = hex::encode(fact_type_id::().as_bytes()); + let retained_buffer = projected_fact( + facts, + invocation_buffer_id, + &buffer_type_id, + "target Buffer", + ); + let buffer: BufferFact = decode_projected_fact(retained_buffer); + let canonical_head_id = hex::encode(buffer.canonical_head_id.0); + assert_ne!( + invocation_basis_id, canonical_head_id, + "{case_id} must not use the current canonical head" + ); + + if same_buffer { + let current_head = projected_fact( + facts, + &canonical_head_id, + &head_type_id, + "current canonical Head", + ); + let current_head: HeadFact = decode_projected_fact(current_head); + assert_eq!( + hex::encode(current_head.buffer_id.0), + invocation_buffer_id, + "stale current Head must belong to the target Buffer" + ); + assert_eq!( + current_head.basis_head_id.map(|id| hex::encode(id.0)), + Some(invocation_basis_id.to_owned()), + "stale current Head must directly descend from the invocation basis" + ); + } else { + let foreign_buffer = + projected_fact(facts, &head_buffer_id, &buffer_type_id, "foreign Buffer"); + let foreign_buffer: BufferFact = decode_projected_fact(foreign_buffer); + assert_eq!( + hex::encode(foreign_buffer.canonical_head_id.0), + invocation_basis_id, + "foreign Buffer must canonically name the invocation basis Head" + ); + } +} + +pub fn corrupt_stale_ancestry(cases: &mut [serde_json::Value]) { + let case = cases + .iter_mut() + .find(|case| case["id"] == "stale-basis") + .expect("stale case should exist"); + let target_buffer_id = case["invocation"]["bufferId"] + .as_str() + .expect("target Buffer ID should be a string") + .to_owned(); + let facts = case["basisFacts"] + .as_array_mut() + .expect("basis facts should be an array"); + let buffer: BufferFact = decode_projected_fact( + facts + .iter() + .find(|fact| fact["nodeId"] == target_buffer_id) + .expect("target Buffer should be retained"), + ); + let current_head_id = hex::encode(buffer.canonical_head_id.0); + let current_head = facts + .iter_mut() + .find(|fact| fact["nodeId"] == current_head_id) + .expect("current Head should be retained"); + let mut head: HeadFact = decode_projected_fact(current_head); + head.basis_head_id = None; + current_head["attachmentBytesHex"] = + serde_json::Value::String(hex::encode(serde_json::to_vec(&head).unwrap())); +} + +pub fn corrupt_foreign_canonical_head(cases: &mut [serde_json::Value]) { + let case = cases + .iter_mut() + .find(|case| case["id"] == "foreign-basis") + .expect("foreign case should exist"); + let invocation_basis_id = case["invocation"]["basisHeadId"] + .as_str() + .expect("foreign Head ID should be a string") + .to_owned(); + let facts = case["basisFacts"] + .as_array_mut() + .expect("basis facts should be an array"); + let foreign_head: HeadFact = decode_projected_fact( + facts + .iter() + .find(|fact| fact["nodeId"] == invocation_basis_id) + .expect("foreign Head should be retained"), + ); + let foreign_buffer_id = hex::encode(foreign_head.buffer_id.0); + let foreign_buffer = facts + .iter_mut() + .find(|fact| fact["nodeId"] == foreign_buffer_id) + .expect("foreign Buffer should be retained"); + let mut buffer: BufferFact = decode_projected_fact(foreign_buffer); + buffer.canonical_head_id.0 = [0xA5; 32]; + foreign_buffer["attachmentBytesHex"] = + serde_json::Value::String(hex::encode(serde_json::to_vec(&buffer).unwrap())); +} + +fn projected_fact<'a>( + facts: &'a [serde_json::Value], + node_id: &str, + type_id: &str, + label: &str, +) -> &'a serde_json::Value { + facts + .iter() + .find(|fact| fact["nodeId"] == node_id && fact["typeId"] == type_id) + .unwrap_or_else(|| panic!("{label} should be retained")) +} + +fn decode_projected_fact(fact: &serde_json::Value) -> T { + serde_json::from_slice( + &hex::decode( + fact["attachmentBytesHex"] + .as_str() + .expect("fact bytes should be hexadecimal"), + ) + .expect("fact bytes should decode"), + ) + .expect("projected fact should decode") +} diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index d3939f8e..5a7679e1 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -46,6 +46,10 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ "native/jedit-echo-host/tests/support/replace_range_basis.rs", include_bytes!("replace_range_basis.rs"), ), + ( + "native/jedit-echo-host/tests/support/replace_range_basis_witness.rs", + include_bytes!("replace_range_basis_witness.rs"), + ), ( "native/jedit-echo-host/tests/support/replace_range_contract.rs", include_bytes!("replace_range_contract.rs"), From 6f4772af55b20484c715b37a10b7668092e54e91 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:51:47 -0700 Subject: [PATCH 22/63] Fix: bind retrospective artifact digests --- .../0158-replace-range-canonical-fact-law.md | 4 ++-- tests/replace-range-law-cycle.spec.mjs | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 96759e07..0f552e36 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -493,9 +493,9 @@ What changed from the design: for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `1c03efba1ba38ea17729385d49626795de74e7636444bef0b780f0ffb33fa12` + `ff52164f8346299cd9a13417aeb7831764bf2a3fc1cb3a1b05afe14cba477cf1` for the oracle. The oracle source-set digest is - `996125197ece197fb6fde1f48ace8fc62c58cfca8b0fac60e4e145a9ffcf6ea28`. + `527209d4d6772ea31aaa9e90afe366e1dd26d0e2df569c713550f2552a1fc5cf`. What the tests proved: diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index 02daec72..9b14633a 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -1,4 +1,5 @@ import assert from 'node:assert/strict'; +import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; @@ -16,6 +17,15 @@ const ORACLE_SUPPORT_PATH = path.join( 'tests', 'support', ); +const ORACLE_PATH = path.join( + process.cwd(), + 'contracts', + 'jedit', + 'lawpacks', + 'replace-range-v1', + 'replace-range-v1.oracle.json', +); +const ORACLE_DIGEST_PATH = `${ORACLE_PATH.slice(0, -'.json'.length)}.sha256`; function readDesign() { return fs.readFileSync(DESIGN_PATH, 'utf8'); @@ -38,6 +48,18 @@ test('DL-0158 retrospective pins implemented truth to a full commit SHA', () => ); }); +test('DL-0158 retrospective binds the exact oracle and source-set digests', () => { + const oracleBytes = fs.readFileSync(ORACLE_PATH); + const oracleDigest = crypto.createHash('sha256').update(oracleBytes).digest('hex'); + const publishedDigest = fs.readFileSync(ORACLE_DIGEST_PATH, 'utf8').trim(); + const sourceSetDigest = JSON.parse(oracleBytes).sourceSet.digestHex; + const retrospective = readDesign().split('## Retrospective')[1]; + + assert.equal(publishedDigest, oracleDigest); + assert.match(retrospective, new RegExp(`\\b${oracleDigest}\\b`)); + assert.match(retrospective, new RegExp(`\\b${sourceSetDigest}\\b`)); +}); + test('ReplaceRange oracle support modules stay within the Rust file budget', () => { const oversized = fs .readdirSync(ORACLE_SUPPORT_PATH) From 0f57cc54e3f4a873159c4b7e6e6d50c70024295a Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 20:54:21 -0700 Subject: [PATCH 23/63] Fix: pin final audit evidence --- .../0158-replace-range-canonical-fact-law.md | 20 +++++++++++++---- tests/replace-range-law-cycle.spec.mjs | 22 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 0f552e36..c11dba05 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -477,6 +477,7 @@ What changed from the design: retained stale-head and real foreign-head basis witnesses, without changing the production planner, fact bytes, identities, or routing. [`native/jedit-echo-host/tests/replace_range_oracle.rs#L43:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L43) + [`native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L48@d57e0d239c005898839a15041807580ed2dfe595`](https://github.com/flyingrobots/jedit/blob/d57e0d239c005898839a15041807580ed2dfe595/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L48) - Review tightened the declaration with a fully specified string codec, a corpus-only invocation schema and exact bytes, mechanical schema-to-writer field-order comparison, strict/atomic fixture updates, and a digest over the @@ -496,6 +497,9 @@ What changed from the design: `ff52164f8346299cd9a13417aeb7831764bf2a3fc1cb3a1b05afe14cba477cf1` for the oracle. The oracle source-set digest is `527209d4d6772ea31aaa9e90afe366e1dd26d0e2df569c713550f2552a1fc5cf`. +- The deliberate resource-update command now runs each exact writer serially + before the complete locked reader and conformance pass. + [`package.json#L9@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L9) What the tests proved: @@ -505,19 +509,27 @@ What the tests proved: bytes preserve `u64` above `i32::MAX`. [`native/jedit-echo-host/tests/replace_range_schema.rs#L89:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema.rs#L89) [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L295:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L295) -- Strict corpus conformance rejects unknown object members, terminal or patch - variants, and obstruction codes outside the ten-value semantic enum. - [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L356:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L356) +- Strict typed corpus conformance binds the exact envelope and source identity, + rejects unknown members and variants, and requires canonical member order, + framing, and terminal newline. + [`native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L120@9a309641edd4b809c37f5d055790de311e5d61f9`](https://github.com/flyingrobots/jedit/blob/9a309641edd4b809c37f5d055790de311e5d61f9/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L120) + [`native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L70@8112752b85862d065811fbe133fdfe8f9e8021c1`](https://github.com/flyingrobots/jedit/blob/8112752b85862d065811fbe133fdfe8f9e8021c1/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L70) - Corpus invocation conformance rejects identifiers and replacement bytes that violate their exact lowercase hexadecimal, width, or UTF-8 lexical laws. + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L396@c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c`](https://github.com/flyingrobots/jedit/blob/c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L396) - Six success and fourteen obstruction cases regenerate deterministically. A success yields the exact support, ordered patch, retained facts, metrics, and materialized consequence; an obstruction yields no `MutationPlan` and leaves the parent graph unchanged. [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190) [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258) + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L393@edd41a35058fea354d46a38421641b0c173e3faa`](https://github.com/flyingrobots/jedit/blob/edd41a35058fea354d46a38421641b0c173e3faa/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L393) - A retained-consequence consistency gate refuses mismatched Buffer, Head, - Rewrite, or Diff evidence before a committable result enters the corpus. + Rewrite, or Diff evidence before a committable result enters the corpus. It + selects Rewrite and Diff facts from the current patch and independently + checks each retained node record's declared type. + [`native/jedit-echo-host/tests/support/replace_range_consequence.rs#L113@475b6dcc0eae9edbf228221069ab3aab975be868`](https://github.com/flyingrobots/jedit/blob/475b6dcc0eae9edbf228221069ab3aab975be868/native/jedit-echo-host/tests/support/replace_range_consequence.rs#L113) + [`native/jedit-echo-host/tests/support/replace_range_consequence.rs#L242@6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8`](https://github.com/flyingrobots/jedit/blob/6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8/native/jedit-echo-host/tests/support/replace_range_consequence.rs#L242) - This is deterministic self-validation. It is not an independent verifier, all-input equivalence proof, Echo admission witness, runtime receipt, WAL, or recovery witness. diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index 9b14633a..7e1bc01b 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -26,6 +26,16 @@ const ORACLE_PATH = path.join( 'replace-range-v1.oracle.json', ); const ORACLE_DIGEST_PATH = `${ORACLE_PATH.slice(0, -'.json'.length)}.sha256`; +const FINAL_AUDIT_EVIDENCE_COMMITS = [ + ['WARP-qualified identifiers', 'edd41a35058fea354d46a38421641b0c173e3faa'], + ['invocation lexical validation', 'c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c'], + ['strict typed corpus validation', '9a309641edd4b809c37f5d055790de311e5d61f9'], + ['current-patch consequence selection', '475b6dcc0eae9edbf228221069ab3aab975be868'], + ['retained node-record typing', '6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8'], + ['canonical oracle bytes', '8112752b85862d065811fbe133fdfe8f9e8021c1'], + ['serialized resource updates', '13b16f2e7195458138865460c5948744ea8615ed'], + ['retained basis relationships', 'd57e0d239c005898839a15041807580ed2dfe595'], +]; function readDesign() { return fs.readFileSync(DESIGN_PATH, 'utf8'); @@ -48,6 +58,18 @@ test('DL-0158 retrospective pins implemented truth to a full commit SHA', () => ); }); +test('DL-0158 retrospective pins every final audit evidence class', () => { + const retrospective = readDesign().split('## Retrospective')[1]; + + for (const [evidenceClass, commit] of FINAL_AUDIT_EVIDENCE_COMMITS) { + assert.match( + retrospective, + new RegExp(`https://github\\.com/flyingrobots/jedit/blob/${commit}/`), + `${evidenceClass} must cite its immutable implementation evidence`, + ); + } +}); + test('DL-0158 retrospective binds the exact oracle and source-set digests', () => { const oracleBytes = fs.readFileSync(ORACLE_PATH); const oracleDigest = crypto.createHash('sha256').update(oracleBytes).digest('hex'); From 6228607cc69d53c123fcfb8f878753ebccbbaf8f Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:00:35 -0700 Subject: [PATCH 24/63] Fix: derive oracle obstruction semantics --- CHANGELOG.md | 3 ++ .../replace-range-v1.oracle.json | 3 +- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 4 +- .../support/replace_range_obstruction.rs | 45 +++++++++++++++++++ .../tests/support/replace_range_oracle.rs | 11 ++++- .../support/replace_range_oracle_tests.rs | 23 ++++++++++ .../tests/support/replace_range_source_set.rs | 4 ++ 8 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 native/jedit-echo-host/tests/support/replace_range_obstruction.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d9725b6..c54cc6b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Made the `ReplaceRange` oracle derive each semantic obstruction code from the + observed planner failure and canonical request coordinates. A fixture's + expected label can no longer determine the published semantic category. - Strengthened the `ReplaceRange` stale- and foreign-basis witnesses to prove the retained causal relationships they claim. A stale basis must be the direct ancestor of the target Buffer's canonical Head, while a foreign basis diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 0698262a..059d2f0b 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -23,11 +23,12 @@ "native/jedit-echo-host/tests/support/replace_range_contract.rs", "native/jedit-echo-host/tests/support/replace_range_consequence.rs", "native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs", + "native/jedit-echo-host/tests/support/replace_range_obstruction.rs", "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "527209d4d6772ea31aaa9e90afe366e1dd26d0e2df569c713550f2552a1fc5cf" + "digestHex": "56b0e7601495fdd27b86ff34048d9dfcf534431a0e74f72e284ee7bc26cb7c0e" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index d738127b..7ec0ef01 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -ff52164f8346299cd9a13417aeb7831764bf2a3fc1cb3a1b05afe14cba477cf1 +9695fef80c76352ba88cc3fbb990a32b9ae13aae5fa0dff580b52faf50f0cc54 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index c11dba05..f861f37a 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -494,9 +494,9 @@ What changed from the design: for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `ff52164f8346299cd9a13417aeb7831764bf2a3fc1cb3a1b05afe14cba477cf1` + `9695fef80c76352ba88cc3fbb990a32b9ae13aae5fa0dff580b52faf50f0cc54` for the oracle. The oracle source-set digest is - `527209d4d6772ea31aaa9e90afe366e1dd26d0e2df569c713550f2552a1fc5cf`. + `56b0e7601495fdd27b86ff34048d9dfcf534431a0e74f72e284ee7bc26cb7c0e`. - The deliberate resource-update command now runs each exact writer serially before the complete locked reader and conformance pass. [`package.json#L9@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L9) diff --git a/native/jedit-echo-host/tests/support/replace_range_obstruction.rs b/native/jedit-echo-host/tests/support/replace_range_obstruction.rs new file mode 100644 index 00000000..fdf87c58 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_obstruction.rs @@ -0,0 +1,45 @@ +use jedit_echo_host::error::HostError; + +use super::contract::SemanticObstructionCode; + +pub(super) fn semantic_obstruction( + error: &HostError, + start_byte: u64, + end_byte: u64, +) -> SemanticObstructionCode { + match error { + HostError::MissingFact(_) => SemanticObstructionCode::FactMissing, + HostError::MalformedFact(message) + if message == "head sequence overflow" || message == "buffer version overflow" => + { + SemanticObstructionCode::ArithmeticOverflow + } + HostError::MalformedFact(message) if message.contains("content hash does not match") => { + SemanticObstructionCode::ContentIdentityMismatch + } + HostError::MalformedFact(_) => SemanticObstructionCode::FactMalformed, + HostError::InvalidRequest(message) if message.starts_with("stale replace basis ") => { + SemanticObstructionCode::BasisNotCanonical + } + HostError::InvalidRequest(message) if message == "replace range is a no-op" => { + SemanticObstructionCode::NoOp + } + HostError::InvalidRequest(message) + if message == "replace offset splits a UTF-8 code point" => + { + SemanticObstructionCode::Utf8BoundaryInvalid + } + HostError::InvalidRequest(message) + if message.starts_with("replace range ") && start_byte > end_byte => + { + SemanticObstructionCode::RangeOrderInvalid + } + HostError::InvalidRequest(message) if message.starts_with("replace range ") => { + SemanticObstructionCode::RangeOutOfBounds + } + HostError::InvalidRequest(message) if message == "split exceeds empty rope" => { + SemanticObstructionCode::MalformedRope + } + other => panic!("oracle cannot classify semantic obstruction: {other}"), + } +} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index 545e53c2..cdc5e819 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -15,6 +15,8 @@ mod basis; mod consequence; #[path = "replace_range_contract.rs"] mod contract; +#[path = "replace_range_obstruction.rs"] +mod obstruction; #[path = "replace_range_source_set.rs"] mod source_set; #[cfg(test)] @@ -30,6 +32,7 @@ use contract::{ INVOCATION_SCHEMA_COORDINATE, OBSTRUCTED_PATCH_POSTURE, ORACLE_COORDINATE, ORACLE_SCHEMA_VERSION, ORACLE_WARP_LABEL, SEMANTIC_BASELINE_COMMIT, }; +use obstruction::semantic_obstruction; use source_set::{source_set, SourceSet}; #[derive(Clone, Copy)] @@ -230,6 +233,12 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { }, ) => { let (actual_class, actual_message) = error_projection(&error); + let actual_semantic_code = semantic_obstruction(&error, spec.start_byte, spec.end_byte); + assert_eq!( + actual_semantic_code, semantic_code, + "{} semantic obstruction", + spec.id + ); assert_eq!(actual_class, error_class, "{} error class", spec.id); assert!( actual_message.contains(message_fragment), @@ -237,7 +246,7 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { spec.id ); TerminalProjection::Obstructed { - semantic_code, + semantic_code: actual_semantic_code, legacy_error_class: error_class, legacy_message: actual_message, parent_graph_unchanged: before == project_store(&store), diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs index 10d11a67..2f0c7980 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs @@ -43,3 +43,26 @@ fn patch_projection_rejects_a_foreign_node_write() { project_op(&operation, expected); } + +#[test] +fn obstruction_projection_rejects_a_false_expected_semantic_code() { + let spec = CaseSpec { + id: "false-semantic-code", + purpose: "negative semantic-obstruction witness", + initial_text: "abc".to_owned(), + setup: BasisSetup::Plain, + start_byte: 2, + end_byte: 1, + replacement: "x".to_owned(), + expected: ExpectedPosture::Obstruction { + semantic_code: SemanticObstructionCode::RangeOutOfBounds, + error_class: "invalid-request", + message_fragment: "exceeds", + }, + }; + + assert!( + std::panic::catch_unwind(|| evaluate_case(make_warp_id("oracle-semantic"), spec)).is_err(), + "oracle accepted a semantic code that did not describe the observed failure" + ); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index 5a7679e1..571fa95c 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -62,6 +62,10 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ "native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs", include_bytes!("replace_range_consequence_tests.rs"), ), + ( + "native/jedit-echo-host/tests/support/replace_range_obstruction.rs", + include_bytes!("replace_range_obstruction.rs"), + ), ( "native/jedit-echo-host/tests/support/replace_range_oracle.rs", include_bytes!("replace_range_oracle.rs"), From 751ea54360f2633ee04f472bb18ad07c47cef61f Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:04:00 -0700 Subject: [PATCH 25/63] Fix: validate retained Buffer identity --- CHANGELOG.md | 3 ++ .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 4 +- .../support/replace_range_consequence.rs | 12 ++++- .../replace_range_consequence_tests.rs | 48 +++++++++++++++++++ 6 files changed, 66 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c54cc6b2..c44b1bdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Made retained `ReplaceRange` consequence validation recompute the keyed + Buffer identity in both the basis and result graphs. Mutually consistent + Buffer facts retained under the wrong node address now fail closed. - Made the `ReplaceRange` oracle derive each semantic obstruction code from the observed planner failure and canonical request coordinates. A fixture's expected label can no longer determine the published semantic category. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 059d2f0b..d52a7fb0 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -28,7 +28,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "56b0e7601495fdd27b86ff34048d9dfcf534431a0e74f72e284ee7bc26cb7c0e" + "digestHex": "1b538c15ae93bfc6f2660e333e350b741491e67f0f4c2a57c0563a14b2833170" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 7ec0ef01..166c9a85 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -9695fef80c76352ba88cc3fbb990a32b9ae13aae5fa0dff580b52faf50f0cc54 +f181eb6994aa54ee96aebb5dff574ea05be45aa5b909d96acf0e9a463d727c6e diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index f861f37a..85d75fc1 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -494,9 +494,9 @@ What changed from the design: for the schema, `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` for codec vectors, and - `9695fef80c76352ba88cc3fbb990a32b9ae13aae5fa0dff580b52faf50f0cc54` + `f181eb6994aa54ee96aebb5dff574ea05be45aa5b909d96acf0e9a463d727c6e` for the oracle. The oracle source-set digest is - `56b0e7601495fdd27b86ff34048d9dfcf534431a0e74f72e284ee7bc26cb7c0e`. + `1b538c15ae93bfc6f2660e333e350b741491e67f0f4c2a57c0563a14b2833170`. - The deliberate resource-update command now runs each exact writer serially before the complete locked reader and conformance pass. [`package.json#L9@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L9) diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence.rs b/native/jedit-echo-host/tests/support/replace_range_consequence.rs index 2ff2036d..8cd7e905 100644 --- a/native/jedit-echo-host/tests/support/replace_range_consequence.rs +++ b/native/jedit-echo-host/tests/support/replace_range_consequence.rs @@ -6,7 +6,7 @@ use jedit_echo_host::records::{ decode_fact, fact_id, fact_type_id, BufferFact, DiffFact, HeadFact, RewriteFact, EMPTY_ROOT_DIGEST_DOMAIN, }; -use jedit_echo_host::rope::{read_window, MutationPlan}; +use jedit_echo_host::rope::{buffer_node_id, read_window, MutationPlan}; use warp_core::{GraphStore, NodeId, TypeId, WarpId, WarpOp}; #[derive(Debug)] @@ -40,6 +40,11 @@ pub(super) fn validate_consequence( replacement, } = expectation; let basis_buffer: BufferFact = read_fact(basis, plan.buffer_id)?; + require_equal( + "basis Buffer keyed identity", + buffer_node_id(&basis_buffer.buffer_key), + plan.buffer_id, + )?; require_equal( "basis Buffer canonical head", NodeId::from(basis_buffer.canonical_head_id), @@ -47,6 +52,11 @@ pub(super) fn validate_consequence( )?; let basis_head: HeadFact = read_fact(basis, basis_head_id)?; let buffer: BufferFact = read_fact(next, plan.buffer_id)?; + require_equal( + "result Buffer keyed identity", + buffer_node_id(&buffer.buffer_key), + plan.buffer_id, + )?; require_equal( "result Buffer canonical head", NodeId::from(buffer.canonical_head_id), diff --git a/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs b/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs index 60b62830..59d0a112 100644 --- a/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs +++ b/native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs @@ -66,6 +66,42 @@ fn retained_consequence_rejects_node_and_atom_type_disagreement() { } } +#[test] +fn retained_consequence_rejects_a_misaddressed_buffer_key() { + let warp_id = warp_core::make_warp_id("oracle-buffer-key"); + let (basis, buffer_id, basis_head_id, _) = + make_basis(warp_id, "buffer-key", "abc", BasisSetup::Plain); + let plan = plan_replace(&basis, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mut delta = TickDelta::new(); + plan.emit(&mut delta); + let patch = delta.finalize(); + let mut next = basis.clone(); + apply_ops(&mut next, &patch); + + let mut misaddressed_basis = basis.clone(); + corrupt_buffer_key(&mut misaddressed_basis, buffer_id); + corrupt_buffer_key(&mut next, buffer_id); + + let error = validate_consequence( + &misaddressed_basis, + &next, + &plan, + &patch, + ReplaceExpectation { + basis_head_id, + start_byte: 1, + end_byte: 2, + replacement: "XY", + }, + ) + .expect_err("a Buffer retained under the wrong keyed identity must fail"); + assert!( + error.contains("Buffer keyed identity"), + "error was {error:?}" + ); +} + #[test] fn retained_consequence_selects_facts_from_the_current_edit() { let warp_id = warp_core::make_warp_id("oracle-retained-history"); @@ -176,3 +212,15 @@ fn retained_consequence_rejects_a_buffer_head_mismatch() { .expect_err("inconsistent retained consequence should fail"); assert!(error.contains("result Buffer canonical head")); } + +fn corrupt_buffer_key(store: &mut GraphStore, buffer_id: NodeId) { + let mut buffer: BufferFact = read_fact(store, buffer_id).expect("Buffer should decode"); + buffer.buffer_key = "deliberately-misaddressed".to_owned(); + store.set_node_attachment( + buffer_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(&buffer).expect("Buffer should encode").into(), + ))), + ); +} From 79a3b788d102c0b04fb47c1d3ce972dbab32a1cc Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:06:03 -0700 Subject: [PATCH 26/63] Fix: cover supplementary Unicode scalars --- CHANGELOG.md | 3 + .../replace-range-v1/codec-vectors-v1.json | 382 +++++++++--------- .../replace-range-v1/codec-vectors-v1.sha256 | 2 +- .../replace-range-v1/text-schema-v1.json | 4 +- .../replace-range-v1/text-schema-v1.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 4 +- .../tests/replace_range_schema_conformance.rs | 12 + .../tests/support/replace_range_schema.rs | 4 +- 8 files changed, 214 insertions(+), 199 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c44b1bdf..ad8c719f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Extended the canonical fact-string golden vector through the maximum Unicode + scalar U+10FFFF. Conformance now proves supplementary scalars remain literal + four-byte UTF-8 and cannot be emitted as surrogate escapes. - Made retained `ReplaceRange` consequence validation recompute the keyed Buffer identity in both the basis and result graphs. Mutually consistent Buffer facts retained under the wrong node address now fail closed. diff --git a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json index da73e4fc..15501e57 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json +++ b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.json @@ -8,49 +8,49 @@ "typeIdHex": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", "identity": { "kind": "keyed", - "bufferKey": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\"\\/猫

", - "nodeIdHex": "61bafe2a221bcba3367f69a64063e00d0cd7293aae29d7384dec1190f65f2dc7" + "bufferKey": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\"\\/猫

􏿿", + "nodeIdHex": "494aa40d2b2c6a6f7afd6423ad937ed0cc8f9ca7d01e1b94d7b61eefb2949e44" }, "value": { - "buffer_key": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\"\\/猫

", + "buffer_key": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f\"\\/猫

􏿿", "canonical_head_id": [ - 153, - 40, - 12, - 140, - 6, + 82, + 126, + 161, + 35, + 203, + 212, + 69, + 112, + 124, + 25, + 221, + 229, + 126, + 51, + 62, + 211, 137, - 1, - 46, - 230, - 232, - 107, - 235, - 142, - 50, - 148, 116, - 211, - 59, - 118, - 249, - 32, - 80, - 27, - 121, - 12, - 190, - 199, - 54, - 122, - 80, - 247, - 132 + 20, + 191, + 98, + 20, + 165, + 11, + 229, + 192, + 147, + 113, + 73, + 4, + 219, + 146 ], "projection_path": null, "version": 2147483648 }, - "canonicalBytesHex": "7b226275666665725f6b6579223a225c75303030305c75303030315c75303030325c75303030335c75303030345c75303030355c75303030365c75303030375c625c745c6e5c75303030625c665c725c75303030655c75303030665c75303031305c75303031315c75303031325c75303031335c75303031345c75303031355c75303031365c75303031375c75303031385c75303031395c75303031615c75303031625c75303031635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e280a9222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3135332c34302c31322c3134302c362c3133372c312c34362c3233302c3233322c3130372c3233352c3134322c35302c3134382c3131362c3231312c35392c3131382c3234392c33322c38302c32372c3132312c31322c3139302c3139392c35342c3132322c38302c3234372c3133325d2c2276657273696f6e223a323134373438333634387d" + "canonicalBytesHex": "7b226275666665725f6b6579223a225c75303030305c75303030315c75303030325c75303030335c75303030345c75303030355c75303030365c75303030375c625c745c6e5c75303030625c665c725c75303030655c75303030665c75303031305c75303031315c75303031325c75303031335c75303031345c75303031355c75303031365c75303031375c75303031385c75303031395c75303031615c75303031625c75303031635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e280a9f48fbfbf222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b38322c3132362c3136312c33352c3230332c3231322c36392c3131322c3132342c32352c3232312c3232392c3132362c35312c36322c3231312c3133372c3131362c32302c3139312c39382c32302c3136352c31312c3232392c3139322c3134372c3131332c37332c342c3231392c3134365d2c2276657273696f6e223a323134373438333634387d" }, { "fact": "Blob", @@ -250,7 +250,7 @@ "identity": { "kind": "content-addressed", "domainHex": "6a656469742e746578742e686561642e763100", - "nodeIdHex": "99280c8c0689012ee6e86beb8e329474d33b76f920501b790cbec7367a50f784" + "nodeIdHex": "527ea123cbd445707c19dde57e333ed3897414bf6214a50be5c093714904db92" }, "value": { "basis_head_id": [ @@ -288,38 +288,38 @@ 51 ], "buffer_id": [ - 97, - 186, - 254, - 42, - 34, - 27, - 203, - 163, - 54, - 127, - 105, - 166, - 64, - 99, - 224, + 73, + 74, + 164, 13, - 12, - 215, - 41, - 58, - 174, - 41, + 43, + 44, + 106, + 111, + 122, + 253, + 100, + 35, + 173, + 147, + 126, + 208, + 204, + 143, + 156, + 167, + 208, + 30, + 27, + 148, 215, - 56, - 77, - 236, - 17, - 144, - 246, - 95, - 45, - 199 + 182, + 30, + 239, + 178, + 148, + 158, + 68 ], "byte_length": 6, "line_count": 2, @@ -394,7 +394,7 @@ "sequence": 2147483648, "utf16_length": 4 }, - "canonicalBytesHex": "7b226275666665725f6964223a5b39372c3138362c3235342c34322c33342c32372c3230332c3136332c35342c3132372c3130352c3136362c36342c39392c3232342c31332c31322c3231352c34312c35382c3137342c34312c3231352c35362c37372c3233362c31372c3134342c3234362c39352c34352c3139395d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c22726f6f745f6e6f64655f6964223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c2273657175656e6365223a323134373438333634387d" + "canonicalBytesHex": "7b226275666665725f6964223a5b37332c37342c3136342c31332c34332c34342c3130362c3131312c3132322c3235332c3130302c33352c3137332c3134372c3132362c3230382c3230342c3134332c3135362c3136372c3230382c33302c32372c3134382c3231352c3138322c33302c3233392c3137382c3134382c3135382c36385d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c22726f6f745f6e6f64655f6964223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c22627974655f6c656e677468223a362c2275746631365f6c656e677468223a342c226c696e655f636f756e74223a322c22726f6f745f646967657374223a5b38342c36302c38362c32332c3137332c3136372c3137322c3130342c3230372c3233382c3230392c3133382c3130392c38302c37392c3138372c34392c3139362c3130352c35312c38382c3139312c39332c37352c3131302c36352c3230332c32372c34382c3138352c3230302c3232315d2c2273657175656e6365223a323134373438333634387d" }, { "fact": "Rewrite", @@ -403,7 +403,7 @@ "identity": { "kind": "content-addressed", "domainHex": "6a656469742e746578742e726577726974652e763100", - "nodeIdHex": "3722e6da72e3a3bed6d89963ad65194d3f6bf6e491c670d9ab474b7a9b5c2a31" + "nodeIdHex": "462c1003621e710d927f3d90342cf8cb2cd64abafbfba079e59cd5d75a524e50" }, "value": { "basis_head_id": [ @@ -441,78 +441,78 @@ 51 ], "buffer_id": [ - 97, - 186, - 254, - 42, - 34, - 27, - 203, - 163, - 54, - 127, - 105, - 166, - 64, - 99, - 224, + 73, + 74, + 164, 13, - 12, - 215, - 41, - 58, - 174, - 41, + 43, + 44, + 106, + 111, + 122, + 253, + 100, + 35, + 173, + 147, + 126, + 208, + 204, + 143, + 156, + 167, + 208, + 30, + 27, + 148, 215, - 56, - 77, - 236, - 17, - 144, - 246, - 95, - 45, - 199 + 182, + 30, + 239, + 178, + 148, + 158, + 68 ], "end_byte": 2147483652, "inserted_byte_length": 6, "next_head_id": [ - 153, - 40, - 12, - 140, - 6, + 82, + 126, + 161, + 35, + 203, + 212, + 69, + 112, + 124, + 25, + 221, + 229, + 126, + 51, + 62, + 211, 137, - 1, - 46, - 230, - 232, - 107, - 235, - 142, - 50, - 148, 116, - 211, - 59, - 118, - 249, - 32, - 80, - 27, - 121, - 12, - 190, - 199, - 54, - 122, - 80, - 247, - 132 + 20, + 191, + 98, + 20, + 165, + 11, + 229, + 192, + 147, + 113, + 73, + 4, + 219, + 146 ], "start_byte": 2147483648 }, - "canonicalBytesHex": "7b226275666665725f6964223a5b39372c3138362c3235342c34322c33342c32372c3230332c3136332c35342c3132372c3130352c3136362c36342c39392c3232342c31332c31322c3231352c34312c35382c3137342c34312c3231352c35362c37372c3233362c31372c3134342c3234362c39352c34352c3139395d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b3135332c34302c31322c3134302c362c3133372c312c34362c3233302c3233322c3130372c3233352c3134322c35302c3134382c3131362c3231312c35392c3131382c3234392c33322c38302c32372c3132312c31322c3139302c3139392c35342c3132322c38302c3234372c3133325d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a367d" + "canonicalBytesHex": "7b226275666665725f6964223a5b37332c37342c3136342c31332c34332c34342c3130362c3131312c3132322c3235332c3130302c33352c3137332c3134372c3132362c3230382c3230342c3134332c3135362c3136372c3230382c33302c32372c3134382c3231352c3138322c33302c3233392c3137382c3134382c3135382c36385d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b38322c3132362c3136312c33352c3230332c3231322c36392c3131322c3132342c32352c3232312c3232392c3132362c35312c36322c3231312c3133372c3131362c32302c3139312c39382c32302c3136352c31312c3232392c3139322c3134372c3131332c37332c342c3231392c3134365d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a367d" }, { "fact": "Diff", @@ -521,7 +521,7 @@ "identity": { "kind": "content-addressed", "domainHex": "6a656469742e746578742e646966662e763100", - "nodeIdHex": "2eeda507e54880b0ce929563be07c4fd74a543e453ee90c7120847def2912018" + "nodeIdHex": "9882cc76dcdee98cd11f4c820fb81ba0952c5dc2028bea60945401a7dbb22549" }, "value": { "basis_head_id": [ @@ -562,76 +562,76 @@ "end_byte": 2147483652, "inserted_byte_length": 6, "next_head_id": [ - 153, - 40, - 12, - 140, - 6, + 82, + 126, + 161, + 35, + 203, + 212, + 69, + 112, + 124, + 25, + 221, + 229, + 126, + 51, + 62, + 211, 137, - 1, - 46, - 230, - 232, - 107, - 235, - 142, - 50, - 148, 116, - 211, - 59, - 118, - 249, - 32, - 80, - 27, - 121, - 12, - 190, - 199, - 54, - 122, - 80, - 247, - 132 + 20, + 191, + 98, + 20, + 165, + 11, + 229, + 192, + 147, + 113, + 73, + 4, + 219, + 146 ], "rewrite_id": [ - 55, - 34, - 230, - 218, - 114, - 227, - 163, - 190, + 70, + 44, + 16, + 3, + 98, + 30, + 113, + 13, + 146, + 127, + 61, + 144, + 52, + 44, + 248, + 203, + 44, 214, - 216, - 153, - 99, - 173, - 101, - 25, - 77, - 63, - 107, - 246, - 228, - 145, - 198, - 112, - 217, - 171, - 71, - 75, - 122, - 155, - 92, - 42, - 49 + 74, + 186, + 251, + 251, + 160, + 121, + 229, + 156, + 213, + 215, + 90, + 82, + 78, + 80 ], "start_byte": 2147483648 }, - "canonicalBytesHex": "7b22726577726974655f6964223a5b35352c33342c3233302c3231382c3131342c3232372c3136332c3139302c3231342c3231362c3135332c39392c3137332c3130312c32352c37372c36332c3130372c3234362c3232382c3134352c3139382c3131322c3231372c3137312c37312c37352c3132322c3135352c39322c34322c34395d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b3135332c34302c31322c3134302c362c3133372c312c34362c3233302c3233322c3130372c3233352c3134322c35302c3134382c3131362c3231312c35392c3131382c3234392c33322c38302c32372c3132312c31322c3139302c3139392c35342c3132322c38302c3234372c3133325d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a362c2264656c657465645f627974655f6c656e677468223a31383434363734343037333730393535313631357d" + "canonicalBytesHex": "7b22726577726974655f6964223a5b37302c34342c31362c332c39382c33302c3131332c31332c3134362c3132372c36312c3134342c35322c34342c3234382c3230332c34342c3231342c37342c3138362c3235312c3235312c3136302c3132312c3232392c3135362c3231332c3231352c39302c38322c37382c38305d2c2262617369735f686561645f6964223a5b35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35312c35315d2c226e6578745f686561645f6964223a5b38322c3132362c3136312c33352c3230332c3231322c36392c3131322c3132342c32352c3232312c3232392c3132362c35312c36322c3231312c3133372c3131362c32302c3139312c39382c32302c3136352c31312c3232392c3139322c3134372c3131332c37332c342c3231392c3134365d2c2273746172745f62797465223a323134373438333634382c22656e645f62797465223a323134373438333635322c22696e7365727465645f627974655f6c656e677468223a362c2264656c657465645f627974655f6c656e677468223a31383434363734343037333730393535313631357d" } ] } diff --git a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 index 7d76cab4..8afee5f3 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/codec-vectors-v1.sha256 @@ -1 +1 @@ -903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb +1ac26477e1d6c08df49446627ad002e40bd214235ce93b563d62bb50e40dc14c diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json index 4f0ad14e..c8638351 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json @@ -26,8 +26,8 @@ "profile": "jedit.compact-serde-json.v1", "stringEscaping": { "goldenVector": { - "jsonStringUtf8Hex": "225c75303030305c75303030315c75303030325c75303030335c75303030345c75303030355c75303030365c75303030375c625c745c6e5c75303030625c665c725c75303030655c75303030665c75303031305c75303031315c75303031325c75303031335c75303031345c75303031355c75303031365c75303031375c75303031385c75303031395c75303031615c75303031625c75303031635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e280a922", - "sourceUtf8Hex": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f225c2fe78cabe280a8e280a9" + "jsonStringUtf8Hex": "225c75303030305c75303030315c75303030325c75303030335c75303030345c75303030355c75303030365c75303030375c625c745c6e5c75303030625c665c725c75303030655c75303030665c75303031305c75303031315c75303031325c75303031335c75303031345c75303031355c75303031365c75303031375c75303031385c75303031395c75303031615c75303031625c75303031635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e280a9f48fbfbf22", + "sourceUtf8Hex": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f225c2fe78cabe280a8e280a9f48fbfbf" }, "otherU0000ThroughU001f": "backslash-u00xx-lowercase-hex", "otherU0020ThroughU10ffff": "literal-utf8-unicode-scalars", diff --git a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 index 09946182..a1087063 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.sha256 @@ -1 +1 @@ -cb03407ff401033fd81312c31a1497519a2d1ae918ea4c19afd551bea1316a72 +fa5087c8fe72dc9c5f138f12b9498ab7f943061fc5ca735d4f68009c081eabc2 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 85d75fc1..f3ec2575 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -490,9 +490,9 @@ What changed from the design: [`native/jedit-echo-host/tests/support/replace_range_contract.rs#L3:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_contract.rs#L3) [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336) - The published SHA-256 digests are - `cb03407ff401033fd81312c31a1497519a2d1ae918ea4c19afd551bea1316a72` + `fa5087c8fe72dc9c5f138f12b9498ab7f943061fc5ca735d4f68009c081eabc2` for the schema, - `903947e1b25048b0bb0f3b3a473dd6441dfeb253fb588d112c28fcbceda083cb` + `1ac26477e1d6c08df49446627ad002e40bd214235ce93b563d62bb50e40dc14c` for codec vectors, and `f181eb6994aa54ee96aebb5dff574ea05be45aa5b909d96acf0e9a463d727c6e` for the oracle. The oracle source-set digest is diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index 4ad2079a..09cafcba 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -287,6 +287,18 @@ fn string_escape_golden_vector_matches_the_native_writer() { .expect("golden JSON should be hexadecimal"), ) .expect("golden JSON should decode"); + let supplementary_scalar = '\u{10ffff}'; + let supplementary_utf8 = supplementary_scalar.to_string(); + assert!( + source.contains(supplementary_scalar), + "golden source must cover the maximum supplementary Unicode scalar" + ); + assert!( + expected_json + .windows(supplementary_utf8.len()) + .any(|window| window == supplementary_utf8.as_bytes()), + "U+10FFFF must remain literal UTF-8 in canonical JSON" + ); assert_eq!(serde_json::to_vec(source).unwrap(), expected_json); assert_eq!(vectors["vectors"][0]["value"]["buffer_key"], source); } diff --git a/native/jedit-echo-host/tests/support/replace_range_schema.rs b/native/jedit-echo-host/tests/support/replace_range_schema.rs index e5ee141b..5d3387ae 100644 --- a/native/jedit-echo-host/tests/support/replace_range_schema.rs +++ b/native/jedit-echo-host/tests/support/replace_range_schema.rs @@ -20,7 +20,7 @@ pub const STRING_ESCAPE_VECTOR: &str = concat!( "\u{0008}\u{0009}\u{000a}\u{000b}\u{000c}\u{000d}\u{000e}\u{000f}", "\u{0010}\u{0011}\u{0012}\u{0013}\u{0014}\u{0015}\u{0016}\u{0017}", "\u{0018}\u{0019}\u{001a}\u{001b}\u{001c}\u{001d}\u{001e}\u{001f}", - "\"\\/猫\u{2028}\u{2029}" + "\"\\/猫\u{2028}\u{2029}\u{10ffff}" ); const STRING_ESCAPE_VECTOR_JSON_HEX: &str = concat!( "225c75303030305c75303030315c75303030325c75303030335c7530303034", @@ -29,7 +29,7 @@ const STRING_ESCAPE_VECTOR_JSON_HEX: &str = concat!( "3031325c75303031335c75303031345c75303031355c75303031365c753030", "31375c75303031385c75303031395c75303031615c75303031625c75303031", "635c75303031645c75303031655c75303031665c225c5c2fe78cabe280a8e2", - "80a922" + "80a9f48fbfbf22" ); pub fn expected_schema() -> Value { From f5482c4e7797e043c006adf1de293d4dfe85e13a Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:09:04 -0700 Subject: [PATCH 27/63] Fix: bind the published type-ID domain --- CHANGELOG.md | 3 ++ .../tests/replace_range_schema_conformance.rs | 34 ++++++++++++++++++- .../tests/support/replace_range_schema.rs | 3 +- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad8c719f..e224a9f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Bound the published fact type-ID domain to the pinned Echo `warp-core` + implementation for every declared Jedit fact. The schema keeps a named + publication constant without claiming ownership of Echo's identity law. - Extended the canonical fact-string golden vector through the maximum Unicode scalar U+10FFFF. Conformance now proves supplementary scalars remain literal four-byte UTF-8 and cannot be emitted as surrogate escapes. diff --git a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs index 09cafcba..59210fba 100644 --- a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs +++ b/native/jedit-echo-host/tests/replace_range_schema_conformance.rs @@ -4,7 +4,8 @@ use std::fmt; mod contract; use jedit_echo_host::records::{ - BlobFact, BranchFact, BufferFact, DiffFact, HeadFact, LeafFact, NodeIdBytes, RewriteFact, + fact_type_id, BlobFact, BranchFact, BufferFact, DiffFact, HeadFact, LeafFact, NodeIdBytes, + RewriteFact, TypedFact, }; use serde::de::{IgnoredAny, MapAccess, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; @@ -303,6 +304,37 @@ fn string_escape_golden_vector_matches_the_native_writer() { assert_eq!(vectors["vectors"][0]["value"]["buffer_key"], source); } +#[test] +fn schema_type_id_domain_matches_the_pinned_runtime() { + let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); + let domain = hex::decode( + schema["identityLaws"]["typeId"]["domainHex"] + .as_str() + .expect("type-ID domain should be hexadecimal"), + ) + .expect("type-ID domain should decode"); + + assert_type_id_domain::(&domain); + assert_type_id_domain::(&domain); + assert_type_id_domain::(&domain); + assert_type_id_domain::(&domain); + assert_type_id_domain::(&domain); + assert_type_id_domain::(&domain); + assert_type_id_domain::(&domain); +} + +fn assert_type_id_domain(domain: &[u8]) { + let mut digest = blake3::Hasher::new(); + digest.update(domain); + digest.update(T::TYPE_LABEL.as_bytes()); + assert_eq!( + digest.finalize().as_bytes(), + fact_type_id::().as_bytes(), + "{} type-ID domain drifted from pinned warp-core", + T::TYPE_LABEL + ); +} + #[test] fn oracle_invocation_bytes_follow_the_published_u64_codec() { let schema: Value = serde_json::from_slice(SCHEMA_BYTES).expect("schema should decode"); diff --git a/native/jedit-echo-host/tests/support/replace_range_schema.rs b/native/jedit-echo-host/tests/support/replace_range_schema.rs index 5d3387ae..4aa1da51 100644 --- a/native/jedit-echo-host/tests/support/replace_range_schema.rs +++ b/native/jedit-echo-host/tests/support/replace_range_schema.rs @@ -15,6 +15,7 @@ use contract::{ }; pub const SCHEMA_COORDINATE: &str = APPLICATION_SCHEMA_COORDINATE; +pub const PUBLISHED_TYPE_ID_DOMAIN: &[u8] = b"type:"; pub const STRING_ESCAPE_VECTOR: &str = concat!( "\u{0000}\u{0001}\u{0002}\u{0003}\u{0004}\u{0005}\u{0006}\u{0007}", "\u{0008}\u{0009}\u{000a}\u{000b}\u{000c}\u{000d}\u{000e}\u{000f}", @@ -86,7 +87,7 @@ pub fn expected_schema() -> Value { "identityLaws": { "typeId": { "algorithm": "blake3-256", - "domainHex": hex::encode(b"type:"), + "domainHex": hex::encode(PUBLISHED_TYPE_ID_DOMAIN), "material": "type-label-utf8" }, "bufferNode": { From 95f63d2c31847c3a4cc4bbea9152900470843d3a Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:10:37 -0700 Subject: [PATCH 28/63] Fix: bind every retrospective digest --- CHANGELOG.md | 3 +++ tests/replace-range-law-cycle.spec.mjs | 34 ++++++++++++++++++-------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e224a9f7..cdb117c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Bound every exact digest in the `ReplaceRange` retrospective to independently + hashed schema, codec-vector, and oracle bytes plus their committed sidecars. + The evidence ledger can no longer drift for only one artifact class. - Bound the published fact type-ID domain to the pinned Echo `warp-core` implementation for every declared Jedit fact. The schema keeps a named publication constant without claiming ownership of Echo's identity law. diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index 7e1bc01b..d0b7f79c 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -17,15 +17,18 @@ const ORACLE_SUPPORT_PATH = path.join( 'tests', 'support', ); -const ORACLE_PATH = path.join( +const LAWPACK_PATH = path.join( process.cwd(), 'contracts', 'jedit', 'lawpacks', 'replace-range-v1', - 'replace-range-v1.oracle.json', ); -const ORACLE_DIGEST_PATH = `${ORACLE_PATH.slice(0, -'.json'.length)}.sha256`; +const PUBLISHED_ARTIFACTS = [ + 'text-schema-v1.json', + 'codec-vectors-v1.json', + 'replace-range-v1.oracle.json', +]; const FINAL_AUDIT_EVIDENCE_COMMITS = [ ['WARP-qualified identifiers', 'edd41a35058fea354d46a38421641b0c173e3faa'], ['invocation lexical validation', 'c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c'], @@ -70,15 +73,26 @@ test('DL-0158 retrospective pins every final audit evidence class', () => { } }); -test('DL-0158 retrospective binds the exact oracle and source-set digests', () => { - const oracleBytes = fs.readFileSync(ORACLE_PATH); - const oracleDigest = crypto.createHash('sha256').update(oracleBytes).digest('hex'); - const publishedDigest = fs.readFileSync(ORACLE_DIGEST_PATH, 'utf8').trim(); - const sourceSetDigest = JSON.parse(oracleBytes).sourceSet.digestHex; +test('DL-0158 retrospective binds every published artifact digest', () => { const retrospective = readDesign().split('## Retrospective')[1]; + let oracleBytes; - assert.equal(publishedDigest, oracleDigest); - assert.match(retrospective, new RegExp(`\\b${oracleDigest}\\b`)); + for (const fileName of PUBLISHED_ARTIFACTS) { + const artifactPath = path.join(LAWPACK_PATH, fileName); + const artifactBytes = fs.readFileSync(artifactPath); + const actualDigest = crypto.createHash('sha256').update(artifactBytes).digest('hex'); + const sidecarPath = artifactPath.replace(/\.json$/, '.sha256'); + const publishedDigest = fs.readFileSync(sidecarPath, 'utf8').trim(); + + assert.equal(publishedDigest, actualDigest, `${fileName} sidecar drifted`); + assert.match(retrospective, new RegExp(`\\b${actualDigest}\\b`)); + if (fileName === 'replace-range-v1.oracle.json') { + oracleBytes = artifactBytes; + } + } + + assert.ok(oracleBytes, 'oracle artifact must be present in the published set'); + const sourceSetDigest = JSON.parse(oracleBytes).sourceSet.digestHex; assert.match(retrospective, new RegExp(`\\b${sourceSetDigest}\\b`)); }); From a8cabbd8d68c5345cdea5cba0bb59b956ec7be63 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:12:00 -0700 Subject: [PATCH 29/63] Fix: pin exact audit evidence locations --- CHANGELOG.md | 3 + .../0158-replace-range-canonical-fact-law.md | 5 +- tests/replace-range-law-cycle.spec.mjs | 174 ++++++++++++------ 3 files changed, 123 insertions(+), 59 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdb117c2..b6591e9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Tightened retrospective source citations from commit-level presence to exact + claim-bearing paths and lines, including distinct stale- and foreign-basis + anchors and the serialized update command itself. - Bound every exact digest in the `ReplaceRange` retrospective to independently hashed schema, codec-vector, and oracle bytes plus their committed sidecars. The evidence ledger can no longer drift for only one artifact class. diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index f3ec2575..41f0c76a 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -477,7 +477,8 @@ What changed from the design: retained stale-head and real foreign-head basis witnesses, without changing the production planner, fact bytes, identities, or routing. [`native/jedit-echo-host/tests/replace_range_oracle.rs#L43:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L43) - [`native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L48@d57e0d239c005898839a15041807580ed2dfe595`](https://github.com/flyingrobots/jedit/blob/d57e0d239c005898839a15041807580ed2dfe595/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L48) + [`native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L46@d57e0d239c005898839a15041807580ed2dfe595`](https://github.com/flyingrobots/jedit/blob/d57e0d239c005898839a15041807580ed2dfe595/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L46) + [`native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L64@d57e0d239c005898839a15041807580ed2dfe595`](https://github.com/flyingrobots/jedit/blob/d57e0d239c005898839a15041807580ed2dfe595/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L64) - Review tightened the declaration with a fully specified string codec, a corpus-only invocation schema and exact bytes, mechanical schema-to-writer field-order comparison, strict/atomic fixture updates, and a digest over the @@ -499,7 +500,7 @@ What changed from the design: `1b538c15ae93bfc6f2660e333e350b741491e67f0f4c2a57c0563a14b2833170`. - The deliberate resource-update command now runs each exact writer serially before the complete locked reader and conformance pass. - [`package.json#L9@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L9) + [`package.json#L10@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L10) What the tests proved: diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index d0b7f79c..89c19cff 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -1,59 +1,105 @@ -import assert from 'node:assert/strict'; -import crypto from 'node:crypto'; -import fs from 'node:fs'; -import path from 'node:path'; -import test from 'node:test'; +import assert from "node:assert/strict"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import path from "node:path"; +import test from "node:test"; const DESIGN_PATH = path.join( process.cwd(), - 'docs', - 'design', - '0158-replace-range-canonical-fact-law.md', + "docs", + "design", + "0158-replace-range-canonical-fact-law.md", ); const ORACLE_SUPPORT_PATH = path.join( process.cwd(), - 'native', - 'jedit-echo-host', - 'tests', - 'support', + "native", + "jedit-echo-host", + "tests", + "support", ); const LAWPACK_PATH = path.join( process.cwd(), - 'contracts', - 'jedit', - 'lawpacks', - 'replace-range-v1', + "contracts", + "jedit", + "lawpacks", + "replace-range-v1", ); const PUBLISHED_ARTIFACTS = [ - 'text-schema-v1.json', - 'codec-vectors-v1.json', - 'replace-range-v1.oracle.json', + "text-schema-v1.json", + "codec-vectors-v1.json", + "replace-range-v1.oracle.json", ]; -const FINAL_AUDIT_EVIDENCE_COMMITS = [ - ['WARP-qualified identifiers', 'edd41a35058fea354d46a38421641b0c173e3faa'], - ['invocation lexical validation', 'c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c'], - ['strict typed corpus validation', '9a309641edd4b809c37f5d055790de311e5d61f9'], - ['current-patch consequence selection', '475b6dcc0eae9edbf228221069ab3aab975be868'], - ['retained node-record typing', '6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8'], - ['canonical oracle bytes', '8112752b85862d065811fbe133fdfe8f9e8021c1'], - ['serialized resource updates', '13b16f2e7195458138865460c5948744ea8615ed'], - ['retained basis relationships', 'd57e0d239c005898839a15041807580ed2dfe595'], +const FINAL_AUDIT_EVIDENCE_LOCATIONS = [ + [ + "WARP-qualified identifiers", + "edd41a35058fea354d46a38421641b0c173e3faa", + "native/jedit-echo-host/tests/support/replace_range_oracle.rs", + 393, + ], + [ + "invocation lexical validation", + "c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c", + "native/jedit-echo-host/tests/replace_range_schema_conformance.rs", + 396, + ], + [ + "strict typed corpus validation", + "9a309641edd4b809c37f5d055790de311e5d61f9", + "native/jedit-echo-host/tests/replace_range_corpus_conformance.rs", + 120, + ], + [ + "current-patch consequence selection", + "475b6dcc0eae9edbf228221069ab3aab975be868", + "native/jedit-echo-host/tests/support/replace_range_consequence.rs", + 113, + ], + [ + "retained node-record typing", + "6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8", + "native/jedit-echo-host/tests/support/replace_range_consequence.rs", + 242, + ], + [ + "canonical oracle bytes", + "8112752b85862d065811fbe133fdfe8f9e8021c1", + "native/jedit-echo-host/tests/replace_range_corpus_conformance.rs", + 70, + ], + [ + "serialized resource updates", + "13b16f2e7195458138865460c5948744ea8615ed", + "package.json", + 10, + ], + [ + "retained stale-basis relationship", + "d57e0d239c005898839a15041807580ed2dfe595", + "native/jedit-echo-host/tests/support/replace_range_basis_witness.rs", + 46, + ], + [ + "retained foreign-basis relationship", + "d57e0d239c005898839a15041807580ed2dfe595", + "native/jedit-echo-host/tests/support/replace_range_basis_witness.rs", + 64, + ], ]; function readDesign() { - return fs.readFileSync(DESIGN_PATH, 'utf8'); + return fs.readFileSync(DESIGN_PATH, "utf8"); } -test('DL-0158 distinguishes committable evidence from obstruction evidence', () => { - const normalized = readDesign().replaceAll(/\s+/g, ' '); +test("DL-0158 distinguishes committable evidence from obstruction evidence", () => { + const normalized = readDesign().replaceAll(/\s+/g, " "); assert.match( normalized, /Successful cases name exact basis, input, support, patch, retained facts, and result\. Obstructed cases name exact basis, input, typed obstruction, no-plan posture, and unchanged-parent evidence\./, ); }); -test('DL-0158 retrospective pins implemented truth to a full commit SHA', () => { - const retrospective = readDesign().split('## Retrospective')[1]; +test("DL-0158 retrospective pins implemented truth to a full commit SHA", () => { + const retrospective = readDesign().split("## Retrospective")[1]; assert.match( retrospective, @@ -61,47 +107,61 @@ test('DL-0158 retrospective pins implemented truth to a full commit SHA', () => ); }); -test('DL-0158 retrospective pins every final audit evidence class', () => { - const retrospective = readDesign().split('## Retrospective')[1]; +test("DL-0158 retrospective pins every final audit evidence class", () => { + const retrospective = readDesign().split("## Retrospective")[1]; - for (const [evidenceClass, commit] of FINAL_AUDIT_EVIDENCE_COMMITS) { - assert.match( - retrospective, - new RegExp(`https://github\\.com/flyingrobots/jedit/blob/${commit}/`), - `${evidenceClass} must cite its immutable implementation evidence`, + for (const [ + evidenceClass, + commit, + sourcePath, + line, + ] of FINAL_AUDIT_EVIDENCE_LOCATIONS) { + const location = `https://github.com/flyingrobots/jedit/blob/${commit}/${sourcePath}#L${line}`; + assert.ok( + retrospective.includes(location), + `${evidenceClass} must cite ${location}`, ); } }); -test('DL-0158 retrospective binds every published artifact digest', () => { - const retrospective = readDesign().split('## Retrospective')[1]; +test("DL-0158 retrospective binds every published artifact digest", () => { + const retrospective = readDesign().split("## Retrospective")[1]; let oracleBytes; for (const fileName of PUBLISHED_ARTIFACTS) { const artifactPath = path.join(LAWPACK_PATH, fileName); const artifactBytes = fs.readFileSync(artifactPath); - const actualDigest = crypto.createHash('sha256').update(artifactBytes).digest('hex'); - const sidecarPath = artifactPath.replace(/\.json$/, '.sha256'); - const publishedDigest = fs.readFileSync(sidecarPath, 'utf8').trim(); + const actualDigest = crypto + .createHash("sha256") + .update(artifactBytes) + .digest("hex"); + const sidecarPath = artifactPath.replace(/\.json$/, ".sha256"); + const publishedDigest = fs.readFileSync(sidecarPath, "utf8").trim(); assert.equal(publishedDigest, actualDigest, `${fileName} sidecar drifted`); assert.match(retrospective, new RegExp(`\\b${actualDigest}\\b`)); - if (fileName === 'replace-range-v1.oracle.json') { + if (fileName === "replace-range-v1.oracle.json") { oracleBytes = artifactBytes; } } - assert.ok(oracleBytes, 'oracle artifact must be present in the published set'); + assert.ok( + oracleBytes, + "oracle artifact must be present in the published set", + ); const sourceSetDigest = JSON.parse(oracleBytes).sourceSet.digestHex; assert.match(retrospective, new RegExp(`\\b${sourceSetDigest}\\b`)); }); -test('ReplaceRange oracle support modules stay within the Rust file budget', () => { +test("ReplaceRange oracle support modules stay within the Rust file budget", () => { const oversized = fs .readdirSync(ORACLE_SUPPORT_PATH) .filter((name) => /^replace_range_.*\.rs$/.test(name)) .map((name) => { - const contents = fs.readFileSync(path.join(ORACLE_SUPPORT_PATH, name), 'utf8'); + const contents = fs.readFileSync( + path.join(ORACLE_SUPPORT_PATH, name), + "utf8", + ); return [name, contents.trimEnd().split(/\r?\n/).length]; }) .filter(([, lines]) => lines > 500); @@ -109,12 +169,12 @@ test('ReplaceRange oracle support modules stay within the Rust file budget', () assert.deepEqual(oversized, []); }); -test('ReplaceRange updater isolates writers before committed-resource readers', () => { +test("ReplaceRange updater isolates writers before committed-resource readers", () => { const packageJson = JSON.parse( - fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'), + fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8"), ); - const update = packageJson.scripts['lawpack:replace-range:update']; - const check = packageJson.scripts['lawpack:replace-range:check']; + const update = packageJson.scripts["lawpack:replace-range:update"]; + const check = packageJson.scripts["lawpack:replace-range:check"]; assert.match( update, @@ -126,10 +186,10 @@ test('ReplaceRange updater isolates writers before committed-resource readers', ); assert.match(update, /&& npm run lawpack:replace-range:check$/); for (const target of [ - 'replace_range_schema', - 'replace_range_oracle', - 'replace_range_schema_conformance', - 'replace_range_corpus_conformance', + "replace_range_schema", + "replace_range_oracle", + "replace_range_schema_conformance", + "replace_range_corpus_conformance", ]) { assert.match(check, new RegExp(`cargo test --locked .* --test ${target}`)); } From afd06f652aad7d6e8f389efec49e1ea1469eef88 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:16:45 -0700 Subject: [PATCH 30/63] Fix: verify the declared text consequence --- CHANGELOG.md | 3 +++ .../replace-range-v1.oracle.json | 2 +- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 4 ++-- .../tests/support/replace_range_oracle.rs | 21 ++++++++++++++++ .../support/replace_range_oracle_tests.rs | 24 +++++++++++++++++++ 6 files changed, 52 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6591e9e..f95d2900 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Added an independent byte-splice oracle for successful `ReplaceRange` cases. + Internally consistent retained graph evidence is refused unless its + materialized text equals the declared basis prefix, replacement, and suffix. - Tightened retrospective source citations from commit-level presence to exact claim-bearing paths and lines, including distinct stale- and foreign-basis anchors and the serialized update command itself. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index d52a7fb0..9cec46a3 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -28,7 +28,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "1b538c15ae93bfc6f2660e333e350b741491e67f0f4c2a57c0563a14b2833170" + "digestHex": "3cc55883551a8f1d2dc97c750eda3258819e308ae8c7ddc701567598a904ed26" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 166c9a85..0c34d07c 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -f181eb6994aa54ee96aebb5dff574ea05be45aa5b909d96acf0e9a463d727c6e +876722ea5eb2ff0d65037edd6b39ead814b25bc3468f6fc528040d6a98c5b01a diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 41f0c76a..695b47f1 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -495,9 +495,9 @@ What changed from the design: for the schema, `1ac26477e1d6c08df49446627ad002e40bd214235ce93b563d62bb50e40dc14c` for codec vectors, and - `f181eb6994aa54ee96aebb5dff574ea05be45aa5b909d96acf0e9a463d727c6e` + `876722ea5eb2ff0d65037edd6b39ead814b25bc3468f6fc528040d6a98c5b01a` for the oracle. The oracle source-set digest is - `1b538c15ae93bfc6f2660e333e350b741491e67f0f4c2a57c0563a14b2833170`. + `3cc55883551a8f1d2dc97c750eda3258819e308ae8c7ddc701567598a904ed26`. - The deliberate resource-update command now runs each exact writer serially before the complete locked reader and conformance pass. [`package.json#L10@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L10) diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index cdc5e819..49cc3098 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -313,6 +313,12 @@ fn success_projection( }, ) .unwrap_or_else(|error| panic!("{} retained consequence: {error}", spec.id)); + let expected_text = expected_materialized_text(spec); + assert_eq!( + retained.materialized_text, expected_text, + "{} materialized consequence violated the declared edit", + spec.id + ); let write_ids: BTreeSet<_> = first_plan.0.node_writes.iter().cloned().collect(); let created_node_ids = write_ids.difference(&basis_ids).cloned().collect(); let updated_node_ids = write_ids.intersection(&basis_ids).cloned().collect(); @@ -344,6 +350,21 @@ fn success_projection( } } +fn expected_materialized_text(spec: &CaseSpec) -> String { + let start = usize::try_from(spec.start_byte).expect("successful start should fit memory"); + let end = usize::try_from(spec.end_byte).expect("successful end should fit memory"); + assert!( + spec.initial_text.is_char_boundary(start) && spec.initial_text.is_char_boundary(end), + "successful oracle range must use UTF-8 boundaries" + ); + [ + &spec.initial_text[..start], + spec.replacement.as_str(), + &spec.initial_text[end..], + ] + .concat() +} + fn project_plan( plan: &MutationPlan, warp_id: warp_core::WarpId, diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs index 2f0c7980..3fbf5b34 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs @@ -66,3 +66,27 @@ fn obstruction_projection_rejects_a_false_expected_semantic_code() { "oracle accepted a semantic code that did not describe the observed failure" ); } + +#[test] +fn success_projection_rejects_a_consequence_that_violates_the_declared_edit() { + let warp_id = make_warp_id("oracle-expected-text"); + let (store, buffer_id, basis_head_id, _) = + make_basis(warp_id, "expected-text", "abc", BasisSetup::Plain); + let plan = plan_replace(&store, buffer_id, basis_head_id, 1, 2, "XY") + .expect("replacement should plan"); + let mismatched_spec = CaseSpec { + id: "false-materialized-text", + purpose: "negative materialized-consequence witness", + initial_text: "uvw".to_owned(), + setup: BasisSetup::Plain, + start_byte: 1, + end_byte: 2, + replacement: "XY".to_owned(), + expected: ExpectedPosture::Success, + }; + + assert!( + std::panic::catch_unwind(|| success_projection(&store, &plan, &mismatched_spec)).is_err(), + "oracle accepted a consequence that violated the declared text edit" + ); +} From b4606ce74ab37f8cc54d184a17c8eaeea2dff092 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 21:40:13 -0700 Subject: [PATCH 31/63] Fix: derive obstructions from planner reasons --- CHANGELOG.md | 7 +- .../replace-range-v1.oracle.json | 6 +- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 4 +- native/jedit-echo-host/src/rope.rs | 116 ++------ native/jedit-echo-host/src/rope/fault.rs | 65 +++++ native/jedit-echo-host/src/rope/replace.rs | 269 ++++++++++++++++++ native/jedit-echo-host/src/rope/tree.rs | 51 ++-- native/jedit-echo-host/src/rope/window.rs | 37 ++- .../tests/support/replace_range_contract.rs | 47 +-- .../tests/support/replace_range_legacy.rs | 44 +++ .../support/replace_range_obstruction.rs | 45 --- .../tests/support/replace_range_oracle.rs | 34 +-- .../support/replace_range_oracle_tests.rs | 32 +++ .../tests/support/replace_range_source_set.rs | 12 +- 15 files changed, 526 insertions(+), 245 deletions(-) create mode 100644 native/jedit-echo-host/src/rope/fault.rs create mode 100644 native/jedit-echo-host/src/rope/replace.rs create mode 100644 native/jedit-echo-host/tests/support/replace_range_legacy.rs delete mode 100644 native/jedit-echo-host/tests/support/replace_range_obstruction.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f95d2900..d4c108f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,9 +20,10 @@ - Made retained `ReplaceRange` consequence validation recompute the keyed Buffer identity in both the basis and result graphs. Mutually consistent Buffer facts retained under the wrong node address now fail closed. -- Made the `ReplaceRange` oracle derive each semantic obstruction code from the - observed planner failure and canonical request coordinates. A fixture's - expected label can no longer determine the published semantic category. +- Made the production `ReplaceRange` planner emit a closed typed obstruction + reason at each decision site while retaining its exact legacy `HostError`. + Oracle evidence now consumes that reason directly; diagnostic wording and + independently repeated request predicates cannot mint semantic categories. - Strengthened the `ReplaceRange` stale- and foreign-basis witnesses to prove the retained causal relationships they claim. A stale basis must be the direct ancestor of the target Buffer's canonical Head, while a foreign basis diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index 9cec46a3..cb553dcf 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -15,6 +15,8 @@ "native/jedit-echo-host/src/identity.rs", "native/jedit-echo-host/src/records.rs", "native/jedit-echo-host/src/rope.rs", + "native/jedit-echo-host/src/rope/fault.rs", + "native/jedit-echo-host/src/rope/replace.rs", "native/jedit-echo-host/src/rope/tree.rs", "native/jedit-echo-host/src/rope/window.rs", "native/jedit-echo-host/tests/replace_range_oracle.rs", @@ -23,12 +25,12 @@ "native/jedit-echo-host/tests/support/replace_range_contract.rs", "native/jedit-echo-host/tests/support/replace_range_consequence.rs", "native/jedit-echo-host/tests/support/replace_range_consequence_tests.rs", - "native/jedit-echo-host/tests/support/replace_range_obstruction.rs", + "native/jedit-echo-host/tests/support/replace_range_legacy.rs", "native/jedit-echo-host/tests/support/replace_range_oracle.rs", "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "3cc55883551a8f1d2dc97c750eda3258819e308ae8c7ddc701567598a904ed26" + "digestHex": "1c570ce54d50d0b6cf02d2881871bcaef32bf5326e60a8ef6bf711633eed4b7e" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index 0c34d07c..b3b1f693 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -876722ea5eb2ff0d65037edd6b39ead814b25bc3468f6fc528040d6a98c5b01a +70c570898195c00b120f6f5582d13ea12d15abccf7e458e869bde53bdf795a85 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 695b47f1..944ea7ee 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -495,9 +495,9 @@ What changed from the design: for the schema, `1ac26477e1d6c08df49446627ad002e40bd214235ce93b563d62bb50e40dc14c` for codec vectors, and - `876722ea5eb2ff0d65037edd6b39ead814b25bc3468f6fc528040d6a98c5b01a` + `70c570898195c00b120f6f5582d13ea12d15abccf7e458e869bde53bdf795a85` for the oracle. The oracle source-set digest is - `3cc55883551a8f1d2dc97c750eda3258819e308ae8c7ddc701567598a904ed26`. + `1c570ce54d50d0b6cf02d2881871bcaef32bf5326e60a8ef6bf711633eed4b7e`. - The deliberate resource-update command now runs each exact writer serially before the complete locked reader and conformance pass. [`package.json#L10@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L10) diff --git a/native/jedit-echo-host/src/rope.rs b/native/jedit-echo-host/src/rope.rs index e09f809e..6bafa5b2 100644 --- a/native/jedit-echo-host/src/rope.rs +++ b/native/jedit-echo-host/src/rope.rs @@ -1,3 +1,5 @@ +mod fault; +mod replace; mod tree; mod window; @@ -13,11 +15,14 @@ use crate::error::{HostError, HostResult}; use crate::identity::{hash_bytes, node_id_hex}; use crate::records::{ decode_fact, fact_bytes, fact_id, fact_type_id, BlobFact, BranchFact, BufferFact, - CheckpointFact, CheckpointReason, ContentAddressedFact, DiffFact, HeadFact, LeafFact, - NodeIdBytes, RewriteFact, TypedFact, BLOB_CONTENT_HASH_DOMAIN, BUFFER_NODE_ID_DOMAIN, + CheckpointFact, CheckpointReason, ContentAddressedFact, HeadFact, LeafFact, NodeIdBytes, + TypedFact, BLOB_CONTENT_HASH_DOMAIN, BUFFER_NODE_ID_DOMAIN, }; -use tree::{build_text, join, root_digest, root_metrics, split}; -use window::read_range_bytes; +use fault::{RopeFault, RopeResult}; +pub use replace::{ + plan_replace, plan_replace_with_reason, ReplaceRangeFailure, ReplaceRangeObstructionCode, +}; +use tree::{build_text, root_digest, root_metrics}; pub use window::{read_window, WindowLine, WindowProjection, WindowSupport}; pub const MAX_LEAF_BYTES: usize = 4096; @@ -164,9 +169,16 @@ impl<'a, T: GraphFacts> PlanContext<'a, T> { } fn verified_blob(&mut self, id: NodeId) -> HostResult { - let blob: BlobFact = self.read_fact(id)?; + self.verified_blob_fault(id) + .map_err(RopeFault::into_host_error) + } + + fn verified_blob_fault(&mut self, id: NodeId) -> RopeResult { + let blob: BlobFact = self + .read_fact(id) + .map_err(RopeFault::structural_dependency)?; if hash_bytes(BLOB_CONTENT_HASH_DOMAIN, &blob.bytes) != blob.content_hash { - return Err(HostError::MalformedFact(format!( + return Err(RopeFault::content_identity(format!( "blob {} content hash does not match", node_id_hex(id) ))); @@ -382,98 +394,6 @@ pub fn plan_create( Ok(finish_plan(context, buffer_id, head_id, &head, 0)) } -pub fn plan_replace( - source: &T, - buffer_id: NodeId, - expected_basis_head_id: NodeId, - start_byte: u64, - end_byte: u64, - insert_text: &str, -) -> HostResult { - let mut context = PlanContext::new(source); - let buffer: BufferFact = context.read_fact(buffer_id)?; - let basis_head_id = NodeId::from(buffer.canonical_head_id); - if basis_head_id != expected_basis_head_id { - return Err(HostError::InvalidRequest(format!( - "stale replace basis {}; canonical head is {}", - node_id_hex(expected_basis_head_id), - node_id_hex(basis_head_id) - ))); - } - let basis_head: HeadFact = context.read_fact(basis_head_id)?; - if start_byte > end_byte || end_byte > basis_head.byte_length { - return Err(HostError::InvalidRequest(format!( - "replace range {start_byte}..{end_byte} exceeds {} bytes", - basis_head.byte_length - ))); - } - let basis_root = basis_head.root_node_id.map(NodeId::from); - let current_bytes = read_range_bytes(&mut context, basis_root, start_byte, end_byte)?; - if current_bytes == insert_text.as_bytes() { - return Err(HostError::InvalidRequest( - "replace range is a no-op".to_owned(), - )); - } - let next_sequence = basis_head - .sequence - .checked_add(1) - .ok_or_else(|| HostError::MalformedFact("head sequence overflow".to_owned()))?; - let next_version = buffer - .version - .checked_add(1) - .ok_or_else(|| HostError::MalformedFact("buffer version overflow".to_owned()))?; - let (left, suffix) = split(&mut context, basis_root, start_byte)?; - let (_, right) = split(&mut context, suffix, end_byte - start_byte)?; - let inserted = build_text(&mut context, insert_text.as_bytes())?; - let left_with_insert = join(&mut context, left, inserted)?; - let root = join(&mut context, left_with_insert, right)?; - let metrics = root_metrics(&mut context, root)?; - let head = HeadFact { - buffer_id: buffer_id.into(), - basis_head_id: Some(basis_head_id.into()), - root_node_id: root.map(NodeIdBytes::from), - byte_length: metrics.byte_length, - utf16_length: metrics.utf16_length, - line_count: metrics.line_breaks + 1, - root_digest: root_digest(root), - sequence: next_sequence, - }; - let head_id = context.write_content_fact(&head)?; - let rewrite = RewriteFact { - buffer_id: buffer_id.into(), - basis_head_id: basis_head_id.into(), - next_head_id: head_id.into(), - start_byte, - end_byte, - inserted_byte_length: insert_text.len() as u64, - }; - let rewrite_id = context.write_content_fact(&rewrite)?; - context.write_content_fact(&DiffFact { - rewrite_id: rewrite_id.into(), - basis_head_id: basis_head_id.into(), - next_head_id: head_id.into(), - start_byte, - end_byte, - inserted_byte_length: insert_text.len() as u64, - deleted_byte_length: end_byte - start_byte, - })?; - context.write_fact_at( - buffer_id, - &BufferFact { - canonical_head_id: head_id.into(), - version: next_version, - ..buffer - }, - )?; - Ok(finish_plan( - context, - buffer_id, - head_id, - &head, - next_version, - )) -} - fn finish_plan( context: PlanContext<'_, T>, buffer_id: NodeId, diff --git a/native/jedit-echo-host/src/rope/fault.rs b/native/jedit-echo-host/src/rope/fault.rs new file mode 100644 index 00000000..d77baad3 --- /dev/null +++ b/native/jedit-echo-host/src/rope/fault.rs @@ -0,0 +1,65 @@ +use crate::error::HostError; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(super) enum RopeFaultKind { + FactMissing, + FactMalformed, + ContentIdentityMismatch, + InvalidUtf8Slice, + DeclaredRopeInconsistent, +} + +#[derive(Debug)] +pub(super) struct RopeFault { + kind: RopeFaultKind, + legacy: HostError, +} + +pub(super) type RopeResult = Result; + +impl RopeFault { + pub(super) fn structural_dependency(legacy: HostError) -> Self { + let kind = match legacy { + HostError::MissingFact(_) => RopeFaultKind::FactMissing, + HostError::MalformedFact(_) => RopeFaultKind::FactMalformed, + _ => RopeFaultKind::DeclaredRopeInconsistent, + }; + Self { kind, legacy } + } + + pub(super) fn fact_malformed(message: String) -> Self { + Self { + kind: RopeFaultKind::FactMalformed, + legacy: HostError::MalformedFact(message), + } + } + + pub(super) fn content_identity(message: String) -> Self { + Self { + kind: RopeFaultKind::ContentIdentityMismatch, + legacy: HostError::MalformedFact(message), + } + } + + pub(super) fn invalid_utf8_slice(message: String) -> Self { + Self { + kind: RopeFaultKind::InvalidUtf8Slice, + legacy: HostError::InvalidRequest(message), + } + } + + pub(super) fn declared_rope_inconsistent(message: String) -> Self { + Self { + kind: RopeFaultKind::DeclaredRopeInconsistent, + legacy: HostError::InvalidRequest(message), + } + } + + pub(super) fn into_parts(self) -> (RopeFaultKind, HostError) { + (self.kind, self.legacy) + } + + pub(super) fn into_host_error(self) -> HostError { + self.legacy + } +} diff --git a/native/jedit-echo-host/src/rope/replace.rs b/native/jedit-echo-host/src/rope/replace.rs new file mode 100644 index 00000000..6bf1b044 --- /dev/null +++ b/native/jedit-echo-host/src/rope/replace.rs @@ -0,0 +1,269 @@ +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use warp_core::NodeId; + +use crate::error::{HostError, HostResult}; +use crate::identity::node_id_hex; +use crate::records::{BufferFact, DiffFact, HeadFact, NodeIdBytes, RewriteFact}; + +use super::fault::{RopeFault, RopeFaultKind}; +use super::tree::{build_text, join, root_digest, root_metrics, split}; +use super::window::read_range_bytes; +use super::{finish_plan, GraphFacts, MutationPlan, PlanContext}; + +#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] +#[serde(rename_all = "kebab-case")] +pub enum ReplaceRangeObstructionCode { + RangeOrderInvalid, + RangeOutOfBounds, + Utf8BoundaryInvalid, + NoOp, + BasisNotCanonical, + ArithmeticOverflow, + FactMissing, + FactMalformed, + ContentIdentityMismatch, + MalformedRope, +} + +impl ReplaceRangeObstructionCode { + pub const ALL: [Self; 10] = [ + Self::RangeOrderInvalid, + Self::RangeOutOfBounds, + Self::Utf8BoundaryInvalid, + Self::NoOp, + Self::BasisNotCanonical, + Self::ArithmeticOverflow, + Self::FactMissing, + Self::FactMalformed, + Self::ContentIdentityMismatch, + Self::MalformedRope, + ]; + + pub const fn as_str(self) -> &'static str { + match self { + Self::RangeOrderInvalid => "range-order-invalid", + Self::RangeOutOfBounds => "range-out-of-bounds", + Self::Utf8BoundaryInvalid => "utf8-boundary-invalid", + Self::NoOp => "no-op", + Self::BasisNotCanonical => "basis-not-canonical", + Self::ArithmeticOverflow => "arithmetic-overflow", + Self::FactMissing => "fact-missing", + Self::FactMalformed => "fact-malformed", + Self::ContentIdentityMismatch => "content-identity-mismatch", + Self::MalformedRope => "malformed-rope", + } + } +} + +#[derive(Debug, Error)] +#[error("{legacy}")] +pub struct ReplaceRangeFailure { + reason: ReplaceRangeObstructionCode, + #[source] + legacy: HostError, +} + +impl ReplaceRangeFailure { + fn new(reason: ReplaceRangeObstructionCode, legacy: HostError) -> Self { + Self { reason, legacy } + } + + fn from_rope_fault(fault: RopeFault) -> Self { + let (kind, legacy) = fault.into_parts(); + let reason = match kind { + RopeFaultKind::FactMissing => ReplaceRangeObstructionCode::FactMissing, + RopeFaultKind::FactMalformed => ReplaceRangeObstructionCode::FactMalformed, + RopeFaultKind::ContentIdentityMismatch => { + ReplaceRangeObstructionCode::ContentIdentityMismatch + } + RopeFaultKind::InvalidUtf8Slice => ReplaceRangeObstructionCode::Utf8BoundaryInvalid, + RopeFaultKind::DeclaredRopeInconsistent => ReplaceRangeObstructionCode::MalformedRope, + }; + Self::new(reason, legacy) + } + + pub fn reason(&self) -> ReplaceRangeObstructionCode { + self.reason + } + + pub fn host_error(&self) -> &HostError { + &self.legacy + } + + pub fn into_host_error(self) -> HostError { + self.legacy + } +} + +pub fn plan_replace( + source: &T, + buffer_id: NodeId, + expected_basis_head_id: NodeId, + start_byte: u64, + end_byte: u64, + insert_text: &str, +) -> HostResult { + plan_replace_with_reason( + source, + buffer_id, + expected_basis_head_id, + start_byte, + end_byte, + insert_text, + ) + .map_err(ReplaceRangeFailure::into_host_error) +} + +pub fn plan_replace_with_reason( + source: &T, + buffer_id: NodeId, + expected_basis_head_id: NodeId, + start_byte: u64, + end_byte: u64, + insert_text: &str, +) -> Result { + let mut context = PlanContext::new(source); + let buffer: BufferFact = context + .read_fact(buffer_id) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let basis_head_id = NodeId::from(buffer.canonical_head_id); + if basis_head_id != expected_basis_head_id { + return Err(ReplaceRangeFailure::new( + ReplaceRangeObstructionCode::BasisNotCanonical, + HostError::InvalidRequest(format!( + "stale replace basis {}; canonical head is {}", + node_id_hex(expected_basis_head_id), + node_id_hex(basis_head_id) + )), + )); + } + let basis_head: HeadFact = context + .read_fact(basis_head_id) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + if start_byte > end_byte { + return Err(range_failure( + ReplaceRangeObstructionCode::RangeOrderInvalid, + start_byte, + end_byte, + basis_head.byte_length, + )); + } + if end_byte > basis_head.byte_length { + return Err(range_failure( + ReplaceRangeObstructionCode::RangeOutOfBounds, + start_byte, + end_byte, + basis_head.byte_length, + )); + } + let basis_root = basis_head.root_node_id.map(NodeId::from); + let current_bytes = read_range_bytes(&mut context, basis_root, start_byte, end_byte) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + if current_bytes == insert_text.as_bytes() { + return Err(ReplaceRangeFailure::new( + ReplaceRangeObstructionCode::NoOp, + HostError::InvalidRequest("replace range is a no-op".to_owned()), + )); + } + let next_sequence = basis_head.sequence.checked_add(1).ok_or_else(|| { + ReplaceRangeFailure::new( + ReplaceRangeObstructionCode::ArithmeticOverflow, + HostError::MalformedFact("head sequence overflow".to_owned()), + ) + })?; + let next_version = buffer.version.checked_add(1).ok_or_else(|| { + ReplaceRangeFailure::new( + ReplaceRangeObstructionCode::ArithmeticOverflow, + HostError::MalformedFact("buffer version overflow".to_owned()), + ) + })?; + let (left, suffix) = split(&mut context, basis_root, start_byte) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let (_, right) = split(&mut context, suffix, end_byte - start_byte) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let inserted = build_text(&mut context, insert_text.as_bytes()) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let left_with_insert = join(&mut context, left, inserted) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let root = join(&mut context, left_with_insert, right) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let metrics = root_metrics(&mut context, root) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let head = HeadFact { + buffer_id: buffer_id.into(), + basis_head_id: Some(basis_head_id.into()), + root_node_id: root.map(NodeIdBytes::from), + byte_length: metrics.byte_length, + utf16_length: metrics.utf16_length, + line_count: metrics.line_breaks + 1, + root_digest: root_digest(root), + sequence: next_sequence, + }; + let head_id = context + .write_content_fact(&head) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let rewrite = RewriteFact { + buffer_id: buffer_id.into(), + basis_head_id: basis_head_id.into(), + next_head_id: head_id.into(), + start_byte, + end_byte, + inserted_byte_length: insert_text.len() as u64, + }; + let rewrite_id = context + .write_content_fact(&rewrite) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + context + .write_content_fact(&DiffFact { + rewrite_id: rewrite_id.into(), + basis_head_id: basis_head_id.into(), + next_head_id: head_id.into(), + start_byte, + end_byte, + inserted_byte_length: insert_text.len() as u64, + deleted_byte_length: end_byte - start_byte, + }) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + context + .write_fact_at( + buffer_id, + &BufferFact { + canonical_head_id: head_id.into(), + version: next_version, + ..buffer + }, + ) + .map_err(RopeFault::structural_dependency) + .map_err(ReplaceRangeFailure::from_rope_fault)?; + Ok(finish_plan( + context, + buffer_id, + head_id, + &head, + next_version, + )) +} + +fn range_failure( + reason: ReplaceRangeObstructionCode, + start_byte: u64, + end_byte: u64, + basis_length: u64, +) -> ReplaceRangeFailure { + ReplaceRangeFailure::new( + reason, + HostError::InvalidRequest(format!( + "replace range {start_byte}..{end_byte} exceeds {basis_length} bytes" + )), + ) +} diff --git a/native/jedit-echo-host/src/rope/tree.rs b/native/jedit-echo-host/src/rope/tree.rs index 6ab8c454..fc1a69fa 100644 --- a/native/jedit-echo-host/src/rope/tree.rs +++ b/native/jedit-echo-host/src/rope/tree.rs @@ -6,6 +6,7 @@ use crate::records::{ BlobFact, BranchFact, LeafFact, BLOB_CONTENT_HASH_DOMAIN, EMPTY_ROOT_DIGEST_DOMAIN, }; +use super::fault::{RopeFault, RopeResult}; use super::{GraphFacts, NodeMetrics, PlanContext, RopeNode, MAX_LEAF_BYTES}; pub(super) fn build_text( context: &mut PlanContext<'_, T>, @@ -69,22 +70,23 @@ fn make_leaf_slice( leaf: &LeafFact, relative_start: u64, byte_length: u64, -) -> HostResult> { +) -> RopeResult> { if byte_length == 0 { return Ok(None); } let blob_id = NodeId::from(leaf.blob_id); - let bytes = context.blob_bytes(blob_id)?; + let bytes = context.verified_blob_fault(blob_id)?.bytes; let start = usize::try_from(leaf.byte_start + relative_start).map_err(|_| { - HostError::InvalidRequest("leaf offset exceeds addressable memory".to_owned()) + RopeFault::declared_rope_inconsistent("leaf offset exceeds addressable memory".to_owned()) + })?; + let end = usize::try_from(leaf.byte_start + relative_start + byte_length).map_err(|_| { + RopeFault::declared_rope_inconsistent("leaf end exceeds addressable memory".to_owned()) })?; - let end = usize::try_from(leaf.byte_start + relative_start + byte_length) - .map_err(|_| HostError::InvalidRequest("leaf end exceeds addressable memory".to_owned()))?; let slice = bytes.get(start..end).ok_or_else(|| { - HostError::MalformedFact(format!("leaf {} exceeds blob bounds", node_id_hex(blob_id))) + RopeFault::fact_malformed(format!("leaf {} exceeds blob bounds", node_id_hex(blob_id))) })?; let text = std::str::from_utf8(slice).map_err(|_| { - HostError::InvalidRequest("replace offset splits a UTF-8 code point".to_owned()) + RopeFault::invalid_utf8_slice("replace offset splits a UTF-8 code point".to_owned()) })?; let fact = LeafFact { blob_id: leaf.blob_id, @@ -93,25 +95,30 @@ fn make_leaf_slice( utf16_length: text.encode_utf16().count() as u64, line_breaks: slice.iter().filter(|byte| **byte == b'\n').count() as u64, }; - context.write_content_fact(&fact).map(Some) + context + .write_content_fact(&fact) + .map(Some) + .map_err(RopeFault::structural_dependency) } pub(super) fn split( context: &mut PlanContext<'_, T>, root: Option, offset: u64, -) -> HostResult<(Option, Option)> { +) -> RopeResult<(Option, Option)> { let Some(root_id) = root else { if offset == 0 { return Ok((None, None)); } - return Err(HostError::InvalidRequest( + return Err(RopeFault::declared_rope_inconsistent( "split exceeds empty rope".to_owned(), )); }; - let metrics = context.node_metrics(root_id)?; + let metrics = context + .node_metrics(root_id) + .map_err(RopeFault::structural_dependency)?; if offset > metrics.byte_length { - return Err(HostError::InvalidRequest( + return Err(RopeFault::declared_rope_inconsistent( "split exceeds rope length".to_owned(), )); } @@ -121,7 +128,10 @@ pub(super) fn split( if offset == metrics.byte_length { return Ok((Some(root_id), None)); } - match context.rope_node(root_id)? { + match context + .rope_node(root_id) + .map_err(RopeFault::structural_dependency)? + { RopeNode::Leaf(leaf) => Ok(( make_leaf_slice(context, &leaf, 0, offset)?, make_leaf_slice(context, &leaf, offset, leaf.byte_length - offset)?, @@ -129,15 +139,24 @@ pub(super) fn split( RopeNode::Branch(branch) => { let left = NodeId::from(branch.left); let right = NodeId::from(branch.right); - let left_length = context.node_metrics(left)?.byte_length; + let left_length = context + .node_metrics(left) + .map_err(RopeFault::structural_dependency)? + .byte_length; if offset < left_length { let (prefix, middle) = split(context, Some(left), offset)?; - Ok((prefix, join(context, middle, Some(right))?)) + Ok(( + prefix, + join(context, middle, Some(right)).map_err(RopeFault::structural_dependency)?, + )) } else if offset == left_length { Ok((Some(left), Some(right))) } else { let (middle, suffix) = split(context, Some(right), offset - left_length)?; - Ok((join(context, Some(left), middle)?, suffix)) + Ok(( + join(context, Some(left), middle).map_err(RopeFault::structural_dependency)?, + suffix, + )) } } } diff --git a/native/jedit-echo-host/src/rope/window.rs b/native/jedit-echo-host/src/rope/window.rs index b37de044..c8e5ca10 100644 --- a/native/jedit-echo-host/src/rope/window.rs +++ b/native/jedit-echo-host/src/rope/window.rs @@ -4,6 +4,7 @@ use crate::error::{HostError, HostResult}; use crate::identity::node_id_hex; use crate::records::{BufferFact, HeadFact}; +use super::fault::{RopeFault, RopeResult}; use super::{GraphFacts, PlanContext, RopeNode}; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] @@ -74,7 +75,8 @@ pub fn read_window( bounded_end, &mut bytes, &mut support, - )?; + ) + .map_err(RopeFault::into_host_error)?; } let (text, actual_end) = complete_utf8_prefix(bytes, start_byte, bounded_end)?; support.retain(|item| item.start_byte < actual_end); @@ -118,9 +120,8 @@ fn complete_utf8_prefix( let valid_length = utf8_error.valid_up_to(); let mut prefix = error.into_bytes(); prefix.truncate(valid_length); - let text = String::from_utf8(prefix).map_err(|_| { - HostError::InvalidRequest("window range is not UTF-8".to_owned()) - })?; + let text = String::from_utf8(prefix) + .map_err(|_| HostError::InvalidRequest("window range is not UTF-8".to_owned()))?; Ok((text, start_byte + valid_length as u64)) } } @@ -131,7 +132,7 @@ pub(super) fn read_range_bytes( root: Option, start_byte: u64, end_byte: u64, -) -> HostResult> { +) -> RopeResult> { let mut bytes = Vec::new(); let mut support = Vec::new(); if let Some(root_id) = root { @@ -157,16 +158,24 @@ fn collect_range( range_end: u64, output: &mut Vec, support: &mut Vec, -) -> HostResult<()> { - let metrics = context.node_metrics(node_id)?; +) -> RopeResult<()> { + let metrics = context + .node_metrics(node_id) + .map_err(RopeFault::structural_dependency)?; let node_end = node_start + metrics.byte_length; if range_end <= node_start || range_start >= node_end { return Ok(()); } - match context.rope_node(node_id)? { + match context + .rope_node(node_id) + .map_err(RopeFault::structural_dependency)? + { RopeNode::Branch(branch) => { let left = NodeId::from(branch.left); - let left_length = context.node_metrics(left)?.byte_length; + let left_length = context + .node_metrics(left) + .map_err(RopeFault::structural_dependency)? + .byte_length; collect_range( context, left, @@ -190,18 +199,20 @@ fn collect_range( let overlap_start = range_start.max(node_start); let overlap_end = range_end.min(node_end); let blob_id = NodeId::from(leaf.blob_id); - let blob = context.verified_blob(blob_id)?; + let blob = context.verified_blob_fault(blob_id)?; let relative_start = usize::try_from(leaf.byte_start + overlap_start - node_start) .map_err(|_| { - HostError::InvalidRequest("window offset exceeds memory".to_owned()) + RopeFault::declared_rope_inconsistent("window offset exceeds memory".to_owned()) })?; let relative_end = usize::try_from(leaf.byte_start + overlap_end - node_start) - .map_err(|_| HostError::InvalidRequest("window end exceeds memory".to_owned()))?; + .map_err(|_| { + RopeFault::declared_rope_inconsistent("window end exceeds memory".to_owned()) + })?; let slice = blob .bytes .get(relative_start..relative_end) .ok_or_else(|| { - HostError::MalformedFact(format!("leaf {} exceeds blob", node_id_hex(node_id))) + RopeFault::fact_malformed(format!("leaf {} exceeds blob", node_id_hex(node_id))) })?; output.extend_from_slice(slice); support.push(WindowSupport { diff --git a/native/jedit-echo-host/tests/support/replace_range_contract.rs b/native/jedit-echo-host/tests/support/replace_range_contract.rs index 0da95848..e53b0fb8 100644 --- a/native/jedit-echo-host/tests/support/replace_range_contract.rs +++ b/native/jedit-echo-host/tests/support/replace_range_contract.rs @@ -1,4 +1,4 @@ -use serde::{Deserialize, Serialize}; +pub use jedit_echo_host::rope::ReplaceRangeObstructionCode as SemanticObstructionCode; pub const ORACLE_SCHEMA_VERSION: u32 = 1; pub const ORACLE_COORDINATE: &str = "jedit.text.ReplaceRange.oracle@1"; @@ -10,51 +10,6 @@ pub const INDEPENDENCE_LIMIT: &str = "independent finite-corpus evidence begins pub const ORACLE_WARP_LABEL: &str = "jedit.replace-range.oracle.v1"; pub const OBSTRUCTED_PATCH_POSTURE: &str = "no-mutation-plan"; -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)] -#[serde(rename_all = "kebab-case")] -pub enum SemanticObstructionCode { - RangeOrderInvalid, - RangeOutOfBounds, - Utf8BoundaryInvalid, - NoOp, - BasisNotCanonical, - ArithmeticOverflow, - FactMissing, - FactMalformed, - ContentIdentityMismatch, - MalformedRope, -} - -impl SemanticObstructionCode { - pub const ALL: [Self; 10] = [ - Self::RangeOrderInvalid, - Self::RangeOutOfBounds, - Self::Utf8BoundaryInvalid, - Self::NoOp, - Self::BasisNotCanonical, - Self::ArithmeticOverflow, - Self::FactMissing, - Self::FactMalformed, - Self::ContentIdentityMismatch, - Self::MalformedRope, - ]; - - pub const fn as_str(self) -> &'static str { - match self { - Self::RangeOrderInvalid => "range-order-invalid", - Self::RangeOutOfBounds => "range-out-of-bounds", - Self::Utf8BoundaryInvalid => "utf8-boundary-invalid", - Self::NoOp => "no-op", - Self::BasisNotCanonical => "basis-not-canonical", - Self::ArithmeticOverflow => "arithmetic-overflow", - Self::FactMissing => "fact-missing", - Self::FactMalformed => "fact-malformed", - Self::ContentIdentityMismatch => "content-identity-mismatch", - Self::MalformedRope => "malformed-rope", - } - } -} - #[cfg(test)] mod tests { use std::collections::BTreeSet; diff --git a/native/jedit-echo-host/tests/support/replace_range_legacy.rs b/native/jedit-echo-host/tests/support/replace_range_legacy.rs new file mode 100644 index 00000000..144631f0 --- /dev/null +++ b/native/jedit-echo-host/tests/support/replace_range_legacy.rs @@ -0,0 +1,44 @@ +use jedit_echo_host::error::HostError; +use jedit_echo_host::rope::{plan_replace, ReplaceRangeFailure}; +use warp_core::{GraphStore, NodeId}; + +pub(super) fn error_projection(error: &HostError) -> (&'static str, String) { + match error { + HostError::MissingFact(message) => ("missing-fact", message.clone()), + HostError::MalformedFact(message) => ("malformed-fact", message.clone()), + HostError::InvalidRequest(message) => ("invalid-request", message.clone()), + other => panic!("oracle received unexpected host error: {other}"), + } +} + +#[allow(clippy::too_many_arguments)] +pub(super) fn assert_error_parity( + store: &GraphStore, + buffer_id: NodeId, + basis_head_id: NodeId, + start_byte: u64, + end_byte: u64, + replacement: &str, + case_id: &str, + typed: &ReplaceRangeFailure, +) { + let legacy = plan_replace( + store, + buffer_id, + basis_head_id, + start_byte, + end_byte, + replacement, + ) + .expect_err("typed obstruction must remain a legacy obstruction"); + assert_eq!( + error_projection(typed.host_error()), + error_projection(&legacy), + "{case_id} typed and legacy error projections diverged" + ); + assert_eq!( + typed.host_error().to_string(), + legacy.to_string(), + "{case_id} typed and legacy diagnostics diverged" + ); +} diff --git a/native/jedit-echo-host/tests/support/replace_range_obstruction.rs b/native/jedit-echo-host/tests/support/replace_range_obstruction.rs deleted file mode 100644 index fdf87c58..00000000 --- a/native/jedit-echo-host/tests/support/replace_range_obstruction.rs +++ /dev/null @@ -1,45 +0,0 @@ -use jedit_echo_host::error::HostError; - -use super::contract::SemanticObstructionCode; - -pub(super) fn semantic_obstruction( - error: &HostError, - start_byte: u64, - end_byte: u64, -) -> SemanticObstructionCode { - match error { - HostError::MissingFact(_) => SemanticObstructionCode::FactMissing, - HostError::MalformedFact(message) - if message == "head sequence overflow" || message == "buffer version overflow" => - { - SemanticObstructionCode::ArithmeticOverflow - } - HostError::MalformedFact(message) if message.contains("content hash does not match") => { - SemanticObstructionCode::ContentIdentityMismatch - } - HostError::MalformedFact(_) => SemanticObstructionCode::FactMalformed, - HostError::InvalidRequest(message) if message.starts_with("stale replace basis ") => { - SemanticObstructionCode::BasisNotCanonical - } - HostError::InvalidRequest(message) if message == "replace range is a no-op" => { - SemanticObstructionCode::NoOp - } - HostError::InvalidRequest(message) - if message == "replace offset splits a UTF-8 code point" => - { - SemanticObstructionCode::Utf8BoundaryInvalid - } - HostError::InvalidRequest(message) - if message.starts_with("replace range ") && start_byte > end_byte => - { - SemanticObstructionCode::RangeOrderInvalid - } - HostError::InvalidRequest(message) if message.starts_with("replace range ") => { - SemanticObstructionCode::RangeOutOfBounds - } - HostError::InvalidRequest(message) if message == "split exceeds empty rope" => { - SemanticObstructionCode::MalformedRope - } - other => panic!("oracle cannot classify semantic obstruction: {other}"), - } -} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle.rs b/native/jedit-echo-host/tests/support/replace_range_oracle.rs index 49cc3098..775fe39e 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle.rs @@ -1,8 +1,7 @@ use std::collections::BTreeSet; -use jedit_echo_host::error::HostError; use jedit_echo_host::records::{decode_fact, BufferFact}; -use jedit_echo_host::rope::{plan_replace, MutationPlan}; +use jedit_echo_host::rope::{plan_replace, plan_replace_with_reason, MutationPlan}; use serde::Serialize; use warp_core::{ make_warp_id, AttachmentOwner, AttachmentPlane, AttachmentValue, Footprint, GraphStore, NodeId, @@ -15,8 +14,8 @@ mod basis; mod consequence; #[path = "replace_range_contract.rs"] mod contract; -#[path = "replace_range_obstruction.rs"] -mod obstruction; +#[path = "replace_range_legacy.rs"] +mod legacy; #[path = "replace_range_source_set.rs"] mod source_set; #[cfg(test)] @@ -32,7 +31,7 @@ use contract::{ INVOCATION_SCHEMA_COORDINATE, OBSTRUCTED_PATCH_POSTURE, ORACLE_COORDINATE, ORACLE_SCHEMA_VERSION, ORACLE_WARP_LABEL, SEMANTIC_BASELINE_COMMIT, }; -use obstruction::semantic_obstruction; +use legacy::{assert_error_parity, error_projection}; use source_set::{source_set, SourceSet}; #[derive(Clone, Copy)] @@ -212,7 +211,7 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { serde_json::to_vec(&invocation).expect("oracle invocation should encode canonically"), ); let before = project_store(&store); - let first = plan_replace( + let first = plan_replace_with_reason( &store, buffer_id, basis_head_id, @@ -232,8 +231,18 @@ fn evaluate_case(warp_id: warp_core::WarpId, spec: CaseSpec) -> OracleCase { message_fragment, }, ) => { - let (actual_class, actual_message) = error_projection(&error); - let actual_semantic_code = semantic_obstruction(&error, spec.start_byte, spec.end_byte); + let (actual_class, actual_message) = error_projection(error.host_error()); + let actual_semantic_code = error.reason(); + assert_error_parity( + &store, + buffer_id, + basis_head_id, + spec.start_byte, + spec.end_byte, + &spec.replacement, + spec.id, + &error, + ); assert_eq!( actual_semantic_code, semantic_code, "{} semantic obstruction", @@ -475,12 +484,3 @@ fn project_op(op: &WarpOp, warp_id: warp_core::WarpId) -> PatchOpProjection { _ => panic!("ReplaceRange emitted an unsupported graph operation"), } } - -fn error_projection(error: &HostError) -> (&'static str, String) { - match error { - HostError::MissingFact(message) => ("missing-fact", message.clone()), - HostError::MalformedFact(message) => ("malformed-fact", message.clone()), - HostError::InvalidRequest(message) => ("invalid-request", message.clone()), - other => panic!("oracle received unexpected host error: {other}"), - } -} diff --git a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs index 3fbf5b34..34d80855 100644 --- a/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs +++ b/native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs @@ -44,6 +44,38 @@ fn patch_projection_rejects_a_foreign_node_write() { project_op(&operation, expected); } +#[test] +fn obstruction_semantics_come_from_the_typed_planner_reason() { + let warp_id = make_warp_id("oracle-typed-obstruction"); + let (store, buffer_id, basis_head_id, _) = + make_basis(warp_id, "typed-obstruction", "abc", BasisSetup::Plain); + let failure = plan_replace_with_reason(&store, buffer_id, basis_head_id, 2, 1, "x") + .expect_err("reversed range must obstruct"); + + assert_eq!(failure.reason(), SemanticObstructionCode::RangeOrderInvalid); +} + +#[test] +fn oracle_support_contains_no_diagnostic_semantic_classifier() { + let sources = [ + include_str!("replace_range_oracle.rs"), + include_str!("replace_range_legacy.rs"), + include_str!("replace_range_contract.rs"), + ]; + for forbidden in [ + "fn semantic_obstruction(", + "starts_with(\"replace range \")", + "starts_with(\"stale replace basis \")", + "contains(\"content hash does not match\")", + "start_byte > end_byte", + ] { + assert!( + sources.iter().all(|source| !source.contains(forbidden)), + "oracle support reintroduced diagnostic semantic classifier {forbidden:?}" + ); + } +} + #[test] fn obstruction_projection_rejects_a_false_expected_semantic_code() { let spec = CaseSpec { diff --git a/native/jedit-echo-host/tests/support/replace_range_source_set.rs b/native/jedit-echo-host/tests/support/replace_range_source_set.rs index 571fa95c..f4d9c10f 100644 --- a/native/jedit-echo-host/tests/support/replace_range_source_set.rs +++ b/native/jedit-echo-host/tests/support/replace_range_source_set.rs @@ -30,6 +30,14 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ "native/jedit-echo-host/src/rope.rs", include_bytes!("../../src/rope.rs"), ), + ( + "native/jedit-echo-host/src/rope/fault.rs", + include_bytes!("../../src/rope/fault.rs"), + ), + ( + "native/jedit-echo-host/src/rope/replace.rs", + include_bytes!("../../src/rope/replace.rs"), + ), ( "native/jedit-echo-host/src/rope/tree.rs", include_bytes!("../../src/rope/tree.rs"), @@ -63,8 +71,8 @@ const SOURCE_SET_FILES: &[(&str, &[u8])] = &[ include_bytes!("replace_range_consequence_tests.rs"), ), ( - "native/jedit-echo-host/tests/support/replace_range_obstruction.rs", - include_bytes!("replace_range_obstruction.rs"), + "native/jedit-echo-host/tests/support/replace_range_legacy.rs", + include_bytes!("replace_range_legacy.rs"), ), ( "native/jedit-echo-host/tests/support/replace_range_oracle.rs", From 9930d04bed048d69f54ef08d5f25f5c211efa199 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 22:03:25 -0700 Subject: [PATCH 32/63] Fix: refuse retained rope arithmetic overflow --- CHANGELOG.md | 5 + .../replace-range-v1.oracle.json | 453 +++++++++++++++++- .../replace-range-v1.oracle.sha256 | 2 +- .../0158-replace-range-canonical-fact-law.md | 6 +- native/jedit-echo-host/src/rope.rs | 9 +- native/jedit-echo-host/src/rope/fault.rs | 18 + native/jedit-echo-host/src/rope/replace.rs | 33 +- native/jedit-echo-host/src/rope/tree.rs | 206 ++++++-- native/jedit-echo-host/src/rope/window.rs | 126 +++-- .../tests/replace_range_oracle.rs | 88 ++++ .../tests/support/replace_range_basis.rs | 156 +++++- 11 files changed, 1001 insertions(+), 101 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4c108f4..8fc48b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +- Made every retained-rope coordinate and metric calculation used by + `ReplaceRange` fail with a typed arithmetic obstruction instead of panicking + in debug builds or wrapping in optimized builds. Adversarial leaf, branch, + and resulting-Head witnesses also prove legacy error parity and zero parent + mutation on refusal. - Added an independent byte-splice oracle for successful `ReplaceRange` cases. Internally consistent retained graph evidence is refused unless its materialized text equals the declared basis prefix, replacement, and suffix. diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json index cb553dcf..c0d1324e 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.json @@ -30,7 +30,7 @@ "native/jedit-echo-host/tests/support/replace_range_oracle_tests.rs", "native/jedit-echo-host/tests/support/replace_range_source_set.rs" ], - "digestHex": "1c570ce54d50d0b6cf02d2881871bcaef32bf5326e60a8ef6bf711633eed4b7e" + "digestHex": "6541ff04a5a97fd407f31479f9393293a937db12ab3705686525e49154f48083" }, "evidenceGrade": "deterministic-self-validation", "independenceLimit": "independent finite-corpus evidence begins only when a separately implemented Echo evaluator agrees", @@ -1960,6 +1960,457 @@ "parentGraphUnchanged": true, "patchPosture": "no-mutation-plan" } + }, + { + "id": "leaf-range-start-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "1ec26f91b0daf19174853e7d6b37663fd03a5cff05e7ce3eb34a74b2b0eeb142", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b33322c3133332c3232322c39312c3231372c34332c3138382c32352c3134382c39332c3233382c3230362c3136342c3235352c3130372c3234322c3233302c37302c33322c35362c3231342c3138302c36362c3134362c3138322c3233352c34342c3132312c3139362c3230322c3231332c3232335d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b36342c3138342c39382c3230312c3234302c31362c3137302c31362c3136352c33372c3233362c35332c3131352c3136352c39352c33312c3135392c3135392c312c3232342c35302c3234372c3233392c35362c31352c3137332c3134362c3134312c3135312c3131352c3132312c3137305d2c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b36342c3138342c39382c3230312c3234302c31362c3137302c31362c3136352c33372c3233362c35332c3131352c3136352c39352c33312c3135392c3135392c312c3232342c35302c3234372c3233392c35362c31352c3137332c3134362c3134312c3135312c3131352c3132312c3137305d2c2273657175656e6365223a307d" + }, + { + "nodeId": "2085de5bd92bbc19945deecea4ff6bf2e6462038d6b44292b6eb2c79c4cad5df", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226c6561662d72616e67652d73746172742d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b33302c3139342c3131312c3134352c3137362c3231382c3234312c3134352c3131362c3133332c36322c3132352c3130372c35352c3130322c36332c3230382c35382c39322c3235352c352c3233312c3230362c36322c3137392c37342c3131362c3137382c3137362c3233382c3137372c36365d2c2276657273696f6e223a307d" + }, + { + "nodeId": "40b862c9f010aa10a525ec3573a55f1f9f9f01e032f7ef380fad928d977379aa", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3138382c31372c332c352c352c3138392c38322c3135322c3130342c3137362c3130362c3139352c3233312c3132332c3138352c3132342c3230322c3233382c3232312c36342c3231392c3130392c3134322c35302c37352c3130362c3130382c3132302c3130312c3130302c35392c3135385d2c22627974655f7374617274223a31383434363734343037333730393535313631352c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "4eaac81b34c389cdaa75878a163bb5160102d8d90264f1a69ba8a859ea01ee0d", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b33322c3133332c3232322c39312c3231372c34332c3138382c32352c3134382c39332c3233382c3230362c3136342c3235352c3130372c3234322c3233302c37302c33322c35362c3231342c3138302c36362c3134362c3138322c3233352c34342c3132312c3139362c3230322c3231332c3232335d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3231312c3135382c3130302c3230302c3130302c34322c3230362c3131352c38392c38312c32382c3130342c3131332c3134312c38302c37312c3130332c35352c3230392c36382c37322c33392c35342c3136382c3138392c3233322c3134312c3133312c3233362c3139392c3232382c3135355d2c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3231312c3135382c3130302c3230302c3130302c34322c3230362c3131352c38392c38312c32382c3130342c3131332c3134312c38302c37312c3130332c35352c3230392c36382c37322c33392c35342c3136382c3138392c3233322c3134312c3133312c3233362c3139392c3232382c3135355d2c2273657175656e6365223a307d" + }, + { + "nodeId": "bc11030505bd529868b06ac3e77bb97ccaeedd40db6d8e324b6a6c7865643b9e", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3232372c3232372c33342c36362c3138332c3135332c32312c32332c3232352c3136362c3136372c3137372c3231322c3135382c32372c3233302c3232372c3135302c3131352c3131322c3135372c3137392c3138362c3137322c38372c3133332c3130342c3230332c3130392c36332c33362c3234325d2c226279746573223a5b39372c39385d7d" + }, + { + "nodeId": "d39e64c8642ace7359511c68718d50476737d144482736a8bde88d83ecc7e49b", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3138382c31372c332c352c352c3138392c38322c3135322c3130342c3137362c3130362c3139352c3233312c3132332c3138352c3132342c3230322c3233382c3232312c36342c3231392c3130392c3134322c35302c37352c3130362c3130382c3132302c3130312c3130302c35392c3135385d2c22627974655f7374617274223a302c22627974655f6c656e677468223a322c2275746631365f6c656e677468223a322c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "2085de5bd92bbc19945deecea4ff6bf2e6462038d6b44292b6eb2c79c4cad5df", + "basisHeadId": "1ec26f91b0daf19174853e7d6b37663fd03a5cff05e7ce3eb34a74b2b0eeb142", + "startByte": 1, + "endByte": 2, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2232303835646535626439326262633139393435646565636561346666366266326536343632303338643662343432393262366562326337396334636164356466222c226261736973486561644964223a2231656332366639316230646166313931373438353365376436623337363633666430336135636666303565376365336562333461373462326230656562313432222c22737461727442797465223a312c22656e6442797465223a322c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "leaf range start overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "leaf-range-end-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "6f8945859beec5438d4c51052425503894eb2714baff54c0407c1755921e040f", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a31383434363734343037333730393535313631352c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "8996ce4ad683cd9d23c897225e9b1765e5d2edeb2744c2ee8a06efc084eaff0c", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a226c6561662d72616e67652d656e642d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3139382c39312c31392c3234322c3136372c3231392c3138312c3233392c37322c3132312c31312c3231382c37372c3235302c32352c3231322c39382c3230312c3231302c3131322c3132382c3133382c39342c3233392c3233372c3235342c32382c3137312c36302c34352c3230382c3131385d2c2276657273696f6e223a307d" + }, + { + "nodeId": "8ac010bbd072db661bc6dcb94bac23c40656d9d6a98c6d5be8938a2d13d0ce8a", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3133372c3135302c3230362c37342c3231342c3133312c3230352c3135372c33352c3230302c3135312c33342c39342c3135352c32332c3130312c3232392c3231302c3233372c3233352c33392c36382c3139342c3233382c3133382c362c3233392c3139322c3133322c3233342c3235352c31325d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "a96e72ed93322ff2b4bb348b42ab2b5a46c84da8588a140d8f542b866d851481", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3234322c36302c3235322c3135302c3138342c3135382c3233322c3235302c3139342c322c3130312c32342c3133342c3234322c38362c3235332c342c3130302c32362c3138352c3133372c36332c3131372c3230322c3136342c3235302c38302c35322c3134392c3234362c39352c34365d2c226279746573223a5b39375d7d" + }, + { + "nodeId": "c65b13f2a7dbb5ef48790bda4dfa19d462c9d270808a5eefedfe1cab3c2dd076", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3133372c3135302c3230362c37342c3231342c3133312c3230352c3135372c33352c3230302c3135312c33342c39342c3135352c32332c3130312c3232392c3231302c3233372c3233352c33392c36382c3139342c3233382c3133382c362c3233392c3139322c3133322c3233342c3235352c31325d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3131312c3133372c36392c3133332c3135352c3233382c3139372c36372c3134312c37362c38312c352c33362c33372c38302c35362c3134382c3233352c33392c32302c3138362c3235352c38342c3139322c36342c3132342c32332c38352c3134362c33302c342c31355d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3131312c3133372c36392c3133332c3135352c3233382c3139372c36372c3134312c37362c38312c352c33362c33372c38302c35362c3134382c3233352c33392c32302c3138362c3235352c38342c3139322c36342c3132342c32332c38352c3134362c33302c342c31355d2c2273657175656e6365223a307d" + }, + { + "nodeId": "f1379d5f30391dd2f301f09f6e25950c8f88c9322db0f1939df0b432686e6e84", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "8996ce4ad683cd9d23c897225e9b1765e5d2edeb2744c2ee8a06efc084eaff0c", + "basisHeadId": "c65b13f2a7dbb5ef48790bda4dfa19d462c9d270808a5eefedfe1cab3c2dd076", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2238393936636534616436383363643964323363383937323235653962313736356535643265646562323734346332656538613036656663303834656166663063222c226261736973486561644964223a2263363562313366326137646262356566343837393062646134646661313964343632633964323730383038613565656665646665316361623363326464303736222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "leaf range end overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "rope-node-end-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "4d197711c188115df4eb1268a70574a93f16a2127a178a4e3cd1c99a76c5f043", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37302c3131302c39362c3234382c39382c3135302c35362c37372c31302c34392c3233342c3232342c33372c3139322c34322c34362c36362c3130342c33332c3231362c32342c3135352c34362c302c3130322c3137382c3231392c3230392c3234362c31332c38372c34315d2c226279746573223a5b39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39375d7d" + }, + { + "nodeId": "693ac326fba0d42f23410300d78cc689b68a672ab416d42f77a774ed9653551b", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132312c37332c3133322c39352c3134392c31332c3138332c3136362c37332c39312c36302c3139382c3134322c3131322c3137302c3231392c3139302c3230322c3232312c3139352c32392c3136332c3137362c3134382c3233382c31342c352c3131322c3234342c3134392c3234352c31345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234382c3235322c3131342c31322c3131312c38302c33332c31302c3234382c3138302c3130312c35362c39362c3138352c3233312c3137352c3230312c35312c3136342c36372c32342c3137342c37322c3131342c3231312c35302c3136322c3130362c3138372c3135332c3232382c38325d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234382c3235322c3131342c31322c3131312c38302c33332c31302c3234382c3138302c3130312c35362c39362c3138352c3233312c3137352c3230312c35312c3136342c36372c32342c3137342c37322c3131342c3231312c35302c3136322c3130362c3138372c3135332c3232382c38325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "7949845f950db7a6495b3cc68e70aadbbecaddc31da3b094ee0e0570f495f50e", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22726f70652d6e6f64652d656e642d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3130352c35382c3139352c33382c3235312c3136302c3231322c34372c33352c36352c332c302c3231352c3134302c3139382c3133372c3138322c3133382c3130332c34322c3138302c32322c3231322c34372c3131392c3136372c3131362c3233372c3135302c38332c38352c32375d2c2276657273696f6e223a307d" + }, + { + "nodeId": "82476c884c06397d5f00983caebb98952b275fcbee46d6619ce04753d540543a", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3132312c37332c3133322c39352c3134392c31332c3138332c3136362c37332c39312c36302c3139382c3134322c3131322c3137302c3231392c3139302c3230322c3232312c3139352c32392c3136332c3137362c3134382c3233382c31342c352c3131322c3234342c3134392c3234352c31345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3230332c35362c3233362c3139322c382c3137362c362c38312c3230332c3233372c3133372c35392c3132322c38342c38362c3134362c3137322c3136392c32382c3132342c3136352c37362c3136362c37382c3233352c3133392c36362c3132392c3132342c38372c3132302c33345d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3230332c35362c3233362c3139322c382c3137362c362c38312c3230332c3233372c3133372c35392c3132322c38342c38362c3134362c3137322c3136392c32382c3132342c3136352c37362c3136362c37382c3233352c3133392c36362c3132392c3132342c38372c3132302c33345d2c2273657175656e6365223a307d" + }, + { + "nodeId": "87bc414f79ec6ff8431cb551c434f17f9ea1959d63892ed83cc018341b497617", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3232372c3232332c31372c37332c35382c3138382c3137362c38392c35362c32362c3230332c3133372c37312c3138312c31382c3235342c3232322c34322c3136392c39352c3139312c3137372c312c3137372c3230392c39382c3134302c3133372c3230302c32332c3235312c3132395d2c226279746573223a5b39385d7d" + }, + { + "nodeId": "89d138e87e58cce6c0c8362e4924c259f45dd8811bc03fda8625e96244cf6e83", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b37372c32352c3131392c31372c3139332c3133362c31372c39332c3234342c3233352c31382c3130342c3136372c352c3131362c3136392c36332c32322c3136322c31382c3132322c32332c3133382c37382c36302c3230392c3230312c3135342c3131382c3139372c3234302c36375d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039362c2275746631365f6c656e677468223a343039362c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "8da3c1b53b66fea09ac5151a6f37310e71f930d0300fecb092718df6df700475", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3133352c3138382c36352c37392c3132312c3233362c3131312c3234382c36372c32382c3138312c38312c3139362c35322c3234312c3132372c3135382c3136312c3134392c3135372c39392c3133372c34362c3231362c36302c3139322c32342c35322c32372c37332c3131382c32335d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "cb38ecc008b00651cbed893b7a545692aca91c7ca54ca64eeb8b42817c577822", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3133372c3230392c35362c3233322c3132362c38382c3230342c3233302c3139322c3230302c35342c34362c37332c33362c3139342c38392c3234342c39332c3231362c3132392c32372c3139322c36332c3231382c3133342c33372c3233332c39382c36382c3230372c3131302c3133315d2c227269676874223a5b3134312c3136332c3139332c3138312c35392c3130322c3235342c3136302c3135342c3139372c32312c32362c3131312c35352c34392c31342c3131332c3234392c34382c3230382c34382c31352c3233362c3137362c3134362c3131332c3134312c3234362c3232332c3131322c342c3131375d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f627265616b73223a302c22686569676874223a327d" + }, + { + "nodeId": "f0186a0aed3263063e94052787baaac5c5bf267e7eaf563f0819fe9347422c40", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b37372c32352c3131392c31372c3139332c3133362c31372c39332c3234342c3233352c31382c3130342c3136372c352c3131362c3136392c36332c32322c3136322c31382c3132322c32332c3133382c37382c36302c3230392c3230312c3135342c3131382c3139372c3234302c36375d2c22627974655f7374617274223a302c22627974655f6c656e677468223a31383434363734343037333730393535313631352c2275746631365f6c656e677468223a343039362c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "f8fc720c6f50210af8b4653860b9e7afc933a44318ae4872d332a26abb99e452", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3234302c32342c3130362c31302c3233372c35302c39392c362c36322c3134382c352c33392c3133352c3138362c3137302c3139372c3139372c3139312c33382c3132362c3132362c3137352c38362c36332c382c32352c3235342c3134372c37312c36362c34342c36345d2c227269676874223a5b3134312c3136332c3139332c3138312c35392c3130322c3235342c3136302c3135342c3139372c32312c32362c3131312c35352c34392c31342c3131332c3234392c34382c3230382c34382c31352c3233362c3137362c3134362c3131332c3134312c3234362c3232332c3131322c342c3131375d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f627265616b73223a302c22686569676874223a327d" + } + ], + "invocation": { + "bufferId": "7949845f950db7a6495b3cc68e70aadbbecaddc31da3b094ee0e0570f495f50e", + "basisHeadId": "693ac326fba0d42f23410300d78cc689b68a672ab416d42f77a774ed9653551b", + "startByte": 0, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2237393439383435663935306462376136343935623363633638653730616164626265636164646333316461336230393465653065303537306634393566353065222c226261736973486561644964223a2236393361633332366662613064343266323334313033303064373863633638396236386136373261623431366434326637376137373465643936353335353162222c22737461727442797465223a302c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "rope node end overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "rope-byte-length-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "5d9c99890972997b22f2192981647b87328a825a5fd7ee7fb1bfa59bdc8d6ca0", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3138362c3132302c3235302c3136302c38392c32372c3137392c3234392c3234322c3234372c3138322c3131372c3231372c33352c3132322c39372c3135382c37372c3131352c3137372c3134302c3136322c372c3232332c36312c37322c3235312c3138352c3139342c3137382c37352c3234315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234372c3139302c3133372c3136352c33312c3139322c302c3138312c37342c3130332c3230312c3230312c3133372c3132372c3137312c3131332c322c3131352c3131302c3135332c39302c3135312c3234332c37332c33372c3130312c352c3132352c3232372c3136382c3135302c3232315d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234372c3139302c3133372c3136352c33312c3139322c302c3138312c37342c3130332c3230312c3230312c3133372c3132372c3137312c3131332c322c3131352c3131302c3135332c39302c3135312c3234332c37332c33372c3130312c352c3132352c3232372c3136382c3135302c3232315d2c2273657175656e6365223a307d" + }, + { + "nodeId": "a96e72ed93322ff2b4bb348b42ab2b5a46c84da8588a140d8f542b866d851481", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3234322c36302c3235322c3135302c3138342c3135382c3233322c3235302c3139342c322c3130312c32342c3133342c3234322c38362c3235332c342c3130302c32362c3138352c3133372c36332c3131372c3230322c3136342c3235302c38302c35322c3134392c3234362c39352c34365d2c226279746573223a5b39375d7d" + }, + { + "nodeId": "ba78faa0591bb3f9f2f7b675d9237a619e4d73b18ca207df3d48fbb9c2b24bf1", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22726f70652d627974652d6c656e6774682d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b39332c3135362c3135332c3133372c392c3131342c3135332c3132332c33342c3234322c32352c34312c3132392c3130302c3132332c3133352c35302c3133382c3133302c39302c39352c3231352c3233382c3132372c3137372c3139312c3136352c3135352c3232302c3134312c3130382c3136305d2c2276657273696f6e223a307d" + }, + { + "nodeId": "c695bc47c137d4bac03f1e8b3af3e4703585f5657ac5ff06dc0a18d07923e0fe", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3138362c3132302c3235302c3136302c38392c32372c3137392c3234392c3234322c3234372c3138322c3131372c3231372c33352c3132322c39372c3135382c37372c3131352c3137372c3134302c3136322c372c3232332c36312c37322c3235312c3138352c3139342c3137382c37352c3234315d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "f1379d5f30391dd2f301f09f6e25950c8f88c9322db0f1939df0b432686e6e84", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "f7be89a51fc000b54a67c9c9897fab7102736e995a97f3492565057de3a896dd", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a31383434363734343037333730393535313631352c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "ba78faa0591bb3f9f2f7b675d9237a619e4d73b18ca207df3d48fbb9c2b24bf1", + "basisHeadId": "5d9c99890972997b22f2192981647b87328a825a5fd7ee7fb1bfa59bdc8d6ca0", + "startByte": 0, + "endByte": 0, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2262613738666161303539316262336639663266376236373564393233376136313965346437336231386361323037646633643438666262396332623234626631222c226261736973486561644964223a2235643963393938393039373239393762323266323139323938313634376238373332386138323561356664376565376662316266613539626463386436636130222c22737461727442797465223a302c22656e6442797465223a302c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "rope byte length overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "rope-utf16-length-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "23178518ebd0f3cc19c0379fb231c873ffb3ba1590a49f4210297241c89ee736", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b39392c3132312c3133342c3137342c3234322c36302c3233302c3137362c39312c3134302c32362c3137332c3131362c3137362c3132372c3139382c3134302c3230372c35312c39392c3136332c32332c36312c3230392c34302c3234342c32382c3130322c31302c3136322c322c3134375d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b38372c3136362c3139352c3230382c37312c33312c3133312c3139322c31322c3133352c3230352c3231392c3139322c32352c35312c3137342c3130382c33342c3134392c3230372c3134312c362c3230352c3233352c3234382c37392c31312c3230382c3234302c3139342c3232362c3231315d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b38372c3136362c3139352c3230382c37312c33312c3133312c3139322c31322c3133352c3230352c3231392c3139322c32352c35312c3137342c3130382c33342c3134392c3230372c3134312c362c3230352c3233352c3234382c37392c31312c3230382c3234302c3139342c3232362c3231315d2c2273657175656e6365223a307d" + }, + { + "nodeId": "57a6c3d0471f83c00c87cddbc01933ae6c2295cf8d06cdebf84f0bd0f0c2e2d3", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a31383434363734343037333730393535313631352c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "637986aef23ce6b05b8c1aad74b07fc68ccf3363a3173dd128f41c660aa20293", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22726f70652d75746631362d6c656e6774682d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b33352c32332c3133332c32342c3233352c3230382c3234332c3230342c32352c3139322c35352c3135392c3137382c34392c3230302c3131352c3235352c3137392c3138362c32312c3134342c3136342c3135392c36362c31362c34312c3131342c36352c3230302c3135382c3233312c35345d2c2276657273696f6e223a307d" + }, + { + "nodeId": "9b3aea333b91abc30e907f586eed140670e9ab5bc0ea9b74e7fa0d6947f06e2b", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b39392c3132312c3133342c3137342c3234322c36302c3233302c3137362c39312c3134302c32362c3137332c3131362c3137362c3132372c3139382c3134302c3230372c35312c39392c3136332c32332c36312c3230392c34302c3234342c32382c3130322c31302c3136322c322c3134375d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "a96e72ed93322ff2b4bb348b42ab2b5a46c84da8588a140d8f542b866d851481", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3234322c36302c3235322c3135302c3138342c3135382c3233322c3235302c3139342c322c3130312c32342c3133342c3234322c38362c3235332c342c3130302c32362c3138352c3133372c36332c3131372c3230322c3136342c3235302c38302c35322c3134392c3234362c39352c34365d2c226279746573223a5b39375d7d" + }, + { + "nodeId": "f1379d5f30391dd2f301f09f6e25950c8f88c9322db0f1939df0b432686e6e84", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "637986aef23ce6b05b8c1aad74b07fc68ccf3363a3173dd128f41c660aa20293", + "basisHeadId": "23178518ebd0f3cc19c0379fb231c873ffb3ba1590a49f4210297241c89ee736", + "startByte": 0, + "endByte": 0, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2236333739383661656632336365366230356238633161616437346230376663363863636633333633613331373364643132386634316336363061613230323933222c226261736973486561644964223a2232333137383531386562643066336363313963303337396662323331633837336666623362613135393061343966343231303239373234316338396565373336222c22737461727442797465223a302c22656e6442797465223a302c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "rope UTF-16 length overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "rope-line-break-count-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "4c4fa5c3a463f6b3ecd465915c0c1b0fe92d46b0cc8f8c34adad892ddffa9df4", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22726f70652d6c696e652d627265616b2d636f756e742d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3138352c3234332c3139312c37382c3133332c3130362c35312c3139332c38352c37332c3130302c3130302c3136332c3130342c3136392c3130332c3134392c3231382c37392c3136362c3231352c3234372c3234312c3131332c3132372c33382c3139302c3133382c3233332c3234382c3137312c3130395d2c2276657273696f6e223a307d" + }, + { + "nodeId": "986fef2ab43313a6f9c3e65ea6108840eb6e4bbf3c09fc9c8a4c186ee05c2e4b", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a31383434363734343037333730393535313631357d" + }, + { + "nodeId": "a96e72ed93322ff2b4bb348b42ab2b5a46c84da8588a140d8f542b866d851481", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3234322c36302c3235322c3135302c3138342c3135382c3233322c3235302c3139342c322c3130312c32342c3133342c3234322c38362c3235332c342c3130302c32362c3138352c3133372c36332c3131372c3230322c3136342c3235302c38302c35322c3134392c3234362c39352c34365d2c226279746573223a5b39375d7d" + }, + { + "nodeId": "b9f3bf4e856a33c155496464a368a96795da4fa6d7f7f1717f26be8ae9f8ab6d", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b37362c37392c3136352c3139352c3136342c39392c3234362c3137392c3233362c3231322c3130312c3134352c39322c31322c32372c31352c3233332c34352c37302c3137362c3230342c3134332c3134302c35322c3137332c3137332c3133372c34352c3232332c3235302c3135372c3234345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3135322c3131312c3233392c34322c3138302c35312c31392c3136362c3234392c3139352c3233302c39342c3136362c31362c3133362c36342c3233352c3131302c37352c3139312c36302c392c3235322c3135362c3133382c37362c32342c3131302c3232342c39322c34362c37355d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3135322c3131312c3233392c34322c3138302c35312c31392c3136362c3234392c3139352c3233302c39342c3136362c31362c3133362c36342c3233352c3131302c37352c3139312c36302c392c3235322c3135362c3133382c37362c32342c3131302c3232342c39322c34362c37355d2c2273657175656e6365223a307d" + }, + { + "nodeId": "ec03f615037ebed20ce0056c34346f97f1cda0614be6cfc379b59fca72faa4be", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b37362c37392c3136352c3139352c3136342c39392c3234362c3137392c3233362c3231322c3130312c3134352c39322c31322c32372c31352c3233332c34352c37302c3137362c3230342c3134332c3134302c35322c3137332c3137332c3133372c34352c3232332c3235302c3135372c3234345d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "f1379d5f30391dd2f301f09f6e25950c8f88c9322db0f1939df0b432686e6e84", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "4c4fa5c3a463f6b3ecd465915c0c1b0fe92d46b0cc8f8c34adad892ddffa9df4", + "basisHeadId": "b9f3bf4e856a33c155496464a368a96795da4fa6d7f7f1717f26be8ae9f8ab6d", + "startByte": 0, + "endByte": 0, + "replacementUtf8Hex": "0a" + }, + "invocationBytesHex": "7b226275666665724964223a2234633466613563336134363366366233656364343635393135633063316230666539326434366230636338663863333461646164383932646466666139646634222c226261736973486561644964223a2262396633626634653835366133336331353534393634363461333638613936373935646134666136643766376631373137663236626538616539663861623664222c22737461727442797465223a302c22656e6442797465223a302c227265706c6163656d656e7455746638486578223a223061227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "rope line break count overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "rope-height-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "4ae29331fa57cf31c6101d73183e434d5b66ab9e38f6a3dbcd4767ef7d488d67", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3135302c3231372c3232342c35312c3233342c3139302c35302c3132352c3136322c3139382c37362c3132382c3137392c3136302c3231392c3133302c32362c3134372c3231392c33362c3234312c3234382c3235352c3136322c3138392c3134372c3134342c3135392c3233342c3134382c3136372c35355d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3230332c35362c3233362c3139322c382c3137362c362c38312c3230332c3233372c3133372c35392c3132322c38342c38362c3134362c3137322c3136392c32382c3132342c3136352c37362c3136362c37382c3233352c3133392c36362c3132392c3132342c38372c3132302c33345d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3230332c35362c3233362c3139322c382c3137362c362c38312c3230332c3233372c3133372c35392c3132322c38342c38362c3134362c3137322c3136392c32382c3132342c3136352c37362c3136362c37382c3233352c3133392c36362c3132392c3132342c38372c3132302c33345d2c2273657175656e6365223a307d" + }, + { + "nodeId": "4d197711c188115df4eb1268a70574a93f16a2127a178a4e3cd1c99a76c5f043", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b37302c3131302c39362c3234382c39382c3135302c35362c37372c31302c34392c3233342c3232342c33372c3139322c34322c34362c36362c3130342c33332c3231362c32342c3135352c34362c302c3130322c3137382c3231392c3230392c3234362c31332c38372c34315d2c226279746573223a5b39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39372c39375d7d" + }, + { + "nodeId": "87bc414f79ec6ff8431cb551c434f17f9ea1959d63892ed83cc018341b497617", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3232372c3232332c31372c37332c35382c3138382c3137362c38392c35362c32362c3230332c3133372c37312c3138312c31382c3235342c3232322c34322c3136392c39352c3139312c3137372c312c3137372c3230392c39382c3134302c3133372c3230302c32332c3235312c3132395d2c226279746573223a5b39385d7d" + }, + { + "nodeId": "89d138e87e58cce6c0c8362e4924c259f45dd8811bc03fda8625e96244cf6e83", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b37372c32352c3131392c31372c3139332c3133362c31372c39332c3234342c3233352c31382c3130342c3136372c352c3131362c3136392c36332c32322c3136322c31382c3132322c32332c3133382c37382c36302c3230392c3230312c3135342c3131382c3139372c3234302c36375d2c22627974655f7374617274223a302c22627974655f6c656e677468223a343039362c2275746631365f6c656e677468223a343039362c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "8da3c1b53b66fea09ac5151a6f37310e71f930d0300fecb092718df6df700475", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3133352c3138382c36352c37392c3132312c3233362c3131312c3234382c36372c32382c3138312c38312c3139362c35322c3234312c3132372c3135382c3136312c3134392c3135372c39392c3133372c34362c3231362c36302c3139322c32342c35322c32372c37332c3131382c32335d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + }, + { + "nodeId": "96d9e033eabe327da2c64c80b3a0db821a93db24f1f8ffa2bd93909fea94a737", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22726f70652d6865696768742d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3232302c34342c352c36382c35322c32392c38362c3230302c39312c32342c3131302c36312c3234312c3136342c39392c37352c38392c3233302c34322c37392c3138372c34362c3132372c3137332c35322c3136312c3135372c3232382c34362c3133382c3235302c39325d2c2276657273696f6e223a307d" + }, + { + "nodeId": "ab925ea3b919808c09a97bdba09018e187ae49702dfd6e645eeed2b5ae8fa396", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3133372c3230392c35362c3233322c3132362c38382c3230342c3233302c3139322c3230302c35342c34362c37332c33362c3139342c38392c3234342c39332c3231362c3132392c32372c3139322c36332c3231382c3133342c33372c3233332c39382c36382c3230372c3131302c3133315d2c227269676874223a5b3134312c3136332c3139332c3138312c35392c3130322c3235342c3136302c3135342c3139372c32312c32362c3131312c35352c34392c31342c3131332c3234392c34382c3230382c34382c31352c3233362c3137362c3134362c3131332c3134312c3234362c3232332c3131322c342c3131375d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f627265616b73223a302c22686569676874223a343239343936373239357d" + }, + { + "nodeId": "cb38ecc008b00651cbed893b7a545692aca91c7ca54ca64eeb8b42817c577822", + "typeId": "013a193bce05cad9da1c765bbe2c057c27f79aefdb980470a00c621f3d03b8f1", + "attachmentBytesHex": "7b226c656674223a5b3133372c3230392c35362c3233322c3132362c38382c3230342c3233302c3139322c3230302c35342c34362c37332c33362c3139342c38392c3234342c39332c3231362c3132392c32372c3139322c36332c3231382c3133342c33372c3233332c39382c36382c3230372c3131302c3133315d2c227269676874223a5b3134312c3136332c3139332c3138312c35392c3130322c3235342c3136302c3135342c3139372c32312c32362c3131312c35352c34392c31342c3131332c3234392c34382c3230382c34382c31352c3233362c3137362c3134362c3131332c3134312c3234362c3232332c3131322c342c3131375d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f627265616b73223a302c22686569676874223a327d" + }, + { + "nodeId": "dc2c0544341d56c85b186e3df1a4634b59e62a4fbb2e7fad34a19de42e8afa5c", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b3135302c3231372c3232342c35312c3233342c3139302c35302c3132352c3136322c3139382c37362c3132382c3137392c3136302c3231392c3133302c32362c3134372c3231392c33362c3234312c3234382c3235352c3136322c3138392c3134372c3134342c3135392c3233342c3134382c3136372c35355d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3137312c3134362c39342c3136332c3138352c32352c3132382c3134302c392c3136392c3132332c3231392c3136302c3134342c32342c3232352c3133352c3137342c37332c3131322c34352c3235332c3131302c3130302c39342c3233382c3231302c3138312c3137342c3134332c3136332c3135305d2c22627974655f6c656e677468223a343039372c2275746631365f6c656e677468223a343039372c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3137312c3134362c39342c3136332c3138352c32352c3132382c3134302c392c3136392c3132332c3231392c3136302c3134342c32342c3232352c3133352c3137342c37332c3131322c34352c3235332c3131302c3130302c39342c3233382c3231302c3138312c3137342c3134332c3136332c3135305d2c2273657175656e6365223a307d" + } + ], + "invocation": { + "bufferId": "96d9e033eabe327da2c64c80b3a0db821a93db24f1f8ffa2bd93909fea94a737", + "basisHeadId": "dc2c0544341d56c85b186e3df1a4634b59e62a4fbb2e7fad34a19de42e8afa5c", + "startByte": 0, + "endByte": 0, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2239366439653033336561626533323764613263363463383062336130646238323161393364623234663166386666613262643933393039666561393461373337222c226261736973486561644964223a2264633263303534343334316435366338356231383665336466316134363334623539653632613466626232653766616433346131396465343265386166613563222c22737461727442797465223a302c22656e6442797465223a302c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "right rope height overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } + }, + { + "id": "head-line-count-overflow", + "purpose": "typed no-plan ReplaceRange obstruction witness", + "basisFacts": [ + { + "nodeId": "3834267a48794a5184d11f15956f3ee7813f5a6c1f73e82d16b07ea7785cb2bc", + "typeId": "82c8f7ee9cfef2e1333ec0a4429cd75ae9e1a2e3fada99d82192851a9ddbcf1e", + "attachmentBytesHex": "7b226275666665725f6b6579223a22686561642d6c696e652d636f756e742d6f766572666c6f77222c2270726f6a656374696f6e5f70617468223a6e756c6c2c2263616e6f6e6963616c5f686561645f6964223a5b3135332c38302c38372c35322c36392c31372c35392c3130382c3137392c3130322c37352c37382c35392c3232322c3136302c39312c3234302c34392c3135352c3138372c3230322c3231332c34322c32372c34342c3136302c38352c3130372c36302c3233302c3133362c3138315d2c2276657273696f6e223a307d" + }, + { + "nodeId": "986fef2ab43313a6f9c3e65ea6108840eb6e4bbf3c09fc9c8a4c186ee05c2e4b", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a31383434363734343037333730393535313631357d" + }, + { + "nodeId": "9950573445113b6cb3664b4e3bdea05bf0319bbbcad52a1b2ca0556b3ce688b5", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b35362c35322c33382c3132322c37322c3132312c37342c38312c3133322c3230392c33312c32312c3134392c3131312c36322c3233312c3132392c36332c39302c3130382c33312c3131352c3233322c34352c32322c3137362c3132362c3136372c3132302c39322c3137382c3138385d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3135322c3131312c3233392c34322c3138302c35312c31392c3136362c3234392c3139352c3233302c39342c3136362c31362c3133362c36342c3233352c3131302c37352c3139312c36302c392c3235322c3135362c3133382c37362c32342c3131302c3232342c39322c34362c37355d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3135322c3131312c3233392c34322c3138302c35312c31392c3136362c3234392c3139352c3233302c39342c3136362c31362c3133362c36342c3233352c3131302c37352c3139312c36302c392c3235322c3135362c3133382c37362c32342c3131302c3232342c39322c34362c37355d2c2273657175656e6365223a307d" + }, + { + "nodeId": "a96e72ed93322ff2b4bb348b42ab2b5a46c84da8588a140d8f542b866d851481", + "typeId": "84d7b45a63c4bde2e3cbafc7c47df22f665bdaff0ab966daf4e1d453d9dccfd9", + "attachmentBytesHex": "7b22636f6e74656e745f68617368223a5b3234322c36302c3235322c3135302c3138342c3135382c3233322c3235302c3139342c322c3130312c32342c3133342c3234322c38362c3235332c342c3130302c32362c3138352c3133372c36332c3131372c3230322c3136342c3235302c38302c35322c3134392c3234362c39352c34365d2c226279746573223a5b39375d7d" + }, + { + "nodeId": "c0aad3a0e7023201dfa17be744492874130357ae068cd75225813f4da56679b9", + "typeId": "cbead59065296fb541ea52b3a9b7e19c40ae063150636e6d50eb9bcb4735314e", + "attachmentBytesHex": "7b226275666665725f6964223a5b35362c35322c33382c3132322c37322c3132312c37342c38312c3133322c3230392c33312c32312c3134392c3131312c36322c3233312c3132392c36332c39302c3130382c33312c3131352c3233322c34352c32322c3137362c3132362c3136372c3132302c39322c3137382c3138385d2c2262617369735f686561645f6964223a6e756c6c2c22726f6f745f6e6f64655f6964223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f636f756e74223a312c22726f6f745f646967657374223a5b3234312c35352c3135372c39352c34382c35372c32392c3231302c3234332c312c3234302c3135392c3131302c33372c3134392c31322c3134332c3133362c3230312c35302c34352c3137362c3234312c3134372c3135372c3234302c3138302c35302c3130342c3131302c3131302c3133325d2c2273657175656e6365223a307d" + }, + { + "nodeId": "f1379d5f30391dd2f301f09f6e25950c8f88c9322db0f1939df0b432686e6e84", + "typeId": "910a3066b739ebd0a1bbab7d3c13acec9189e47340a02b5b018ee251d06ea5b4", + "attachmentBytesHex": "7b22626c6f625f6964223a5b3136392c3131302c3131342c3233372c3134372c35302c34372c3234322c3138302c3138372c35322c3133392c36362c3137312c34332c39302c37302c3230302c37372c3136382c38382c3133382c32302c31332c3134332c38342c34332c3133342c3130392c3133332c32302c3132395d2c22627974655f7374617274223a302c22627974655f6c656e677468223a312c2275746631365f6c656e677468223a312c226c696e655f627265616b73223a307d" + } + ], + "invocation": { + "bufferId": "3834267a48794a5184d11f15956f3ee7813f5a6c1f73e82d16b07ea7785cb2bc", + "basisHeadId": "9950573445113b6cb3664b4e3bdea05bf0319bbbcad52a1b2ca0556b3ce688b5", + "startByte": 1, + "endByte": 1, + "replacementUtf8Hex": "78" + }, + "invocationBytesHex": "7b226275666665724964223a2233383334323637613438373934613531383464313166313539353666336565373831336635613663316637336538326431366230376561373738356362326263222c226261736973486561644964223a2239393530353733343435313133623663623336363462346533626465613035626630333139626262636164353261316232636130353536623363653638386235222c22737461727442797465223a312c22656e6442797465223a312c227265706c6163656d656e7455746638486578223a223738227d", + "terminal": { + "posture": "obstructed", + "semanticCode": "arithmetic-overflow", + "legacyErrorClass": "malformed-fact", + "legacyMessage": "head line count overflow", + "parentGraphUnchanged": true, + "patchPosture": "no-mutation-plan" + } } ] } diff --git a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 index b3b1f693..0c473a3c 100644 --- a/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 +++ b/contracts/jedit/lawpacks/replace-range-v1/replace-range-v1.oracle.sha256 @@ -1 +1 @@ -70c570898195c00b120f6f5582d13ea12d15abccf7e458e869bde53bdf795a85 +6e7d9ff396897940e16d8fa5936d7fd51b8c8915e2e9ea2fb2d3b5916a4224c9 diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index 944ea7ee..c35a6c5e 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -431,7 +431,7 @@ the declaration, codec vectors, corpus, and all sidecars, then proves check mode is clean. Inspect `contracts/jedit/lawpacks/replace-range-v1/text-schema-v1.json` for the exact fact, string, identity, rope, and corpus-invocation laws. Inspect -`replace-range-v1.oracle.json` for 20 self-contained cases with basis facts, +`replace-range-v1.oracle.json` for 28 self-contained cases with basis facts, exact invocation bytes, support, ordered patches, results, or typed no-patch obstructions. No editor, terminal rendering, external service, or sibling checkout is required. @@ -495,9 +495,9 @@ What changed from the design: for the schema, `1ac26477e1d6c08df49446627ad002e40bd214235ce93b563d62bb50e40dc14c` for codec vectors, and - `70c570898195c00b120f6f5582d13ea12d15abccf7e458e869bde53bdf795a85` + `6e7d9ff396897940e16d8fa5936d7fd51b8c8915e2e9ea2fb2d3b5916a4224c9` for the oracle. The oracle source-set digest is - `1c570ce54d50d0b6cf02d2881871bcaef32bf5326e60a8ef6bf711633eed4b7e`. + `6541ff04a5a97fd407f31479f9393293a937db12ab3705686525e49154f48083`. - The deliberate resource-update command now runs each exact writer serially before the complete locked reader and conformance pass. [`package.json#L10@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L10) diff --git a/native/jedit-echo-host/src/rope.rs b/native/jedit-echo-host/src/rope.rs index 6bafa5b2..c7ed9499 100644 --- a/native/jedit-echo-host/src/rope.rs +++ b/native/jedit-echo-host/src/rope.rs @@ -369,15 +369,20 @@ pub fn plan_create( "buffer {buffer_key} already exists" ))); } - let root = build_text(&mut context, initial_text.as_bytes())?; + let root = + build_text(&mut context, initial_text.as_bytes()).map_err(RopeFault::into_host_error)?; let metrics = root_metrics(&mut context, root)?; + let line_count = metrics + .line_breaks + .checked_add(1) + .ok_or_else(|| HostError::MalformedFact("head line count overflow".to_owned()))?; let head = HeadFact { buffer_id: buffer_id.into(), basis_head_id: None, root_node_id: root.map(NodeIdBytes::from), byte_length: metrics.byte_length, utf16_length: metrics.utf16_length, - line_count: metrics.line_breaks + 1, + line_count, root_digest: root_digest(root), sequence: 0, }; diff --git a/native/jedit-echo-host/src/rope/fault.rs b/native/jedit-echo-host/src/rope/fault.rs index d77baad3..a3dc384e 100644 --- a/native/jedit-echo-host/src/rope/fault.rs +++ b/native/jedit-echo-host/src/rope/fault.rs @@ -7,6 +7,7 @@ pub(super) enum RopeFaultKind { ContentIdentityMismatch, InvalidUtf8Slice, DeclaredRopeInconsistent, + ArithmeticOverflow, } #[derive(Debug)] @@ -55,6 +56,23 @@ impl RopeFault { } } + pub(super) fn arithmetic_overflow(message: &'static str) -> Self { + Self { + kind: RopeFaultKind::ArithmeticOverflow, + legacy: HostError::MalformedFact(message.to_owned()), + } + } + + pub(super) fn checked_add_u64(left: u64, right: u64, message: &'static str) -> RopeResult { + left.checked_add(right) + .ok_or_else(|| Self::arithmetic_overflow(message)) + } + + pub(super) fn checked_add_u32(left: u32, right: u32, message: &'static str) -> RopeResult { + left.checked_add(right) + .ok_or_else(|| Self::arithmetic_overflow(message)) + } + pub(super) fn into_parts(self) -> (RopeFaultKind, HostError) { (self.kind, self.legacy) } diff --git a/native/jedit-echo-host/src/rope/replace.rs b/native/jedit-echo-host/src/rope/replace.rs index 6bf1b044..593c03f8 100644 --- a/native/jedit-echo-host/src/rope/replace.rs +++ b/native/jedit-echo-host/src/rope/replace.rs @@ -79,6 +79,7 @@ impl ReplaceRangeFailure { } RopeFaultKind::InvalidUtf8Slice => ReplaceRangeObstructionCode::Utf8BoundaryInvalid, RopeFaultKind::DeclaredRopeInconsistent => ReplaceRangeObstructionCode::MalformedRope, + RopeFaultKind::ArithmeticOverflow => ReplaceRangeObstructionCode::ArithmeticOverflow, }; Self::new(reason, legacy) } @@ -159,6 +160,14 @@ pub fn plan_replace_with_reason( basis_head.byte_length, )); } + let deleted_byte_length = end_byte.checked_sub(start_byte).ok_or_else(|| { + range_failure( + ReplaceRangeObstructionCode::RangeOrderInvalid, + start_byte, + end_byte, + basis_head.byte_length, + ) + })?; let basis_root = basis_head.root_node_id.map(NodeId::from); let current_bytes = read_range_bytes(&mut context, basis_root, start_byte, end_byte) .map_err(ReplaceRangeFailure::from_rope_fault)?; @@ -182,27 +191,31 @@ pub fn plan_replace_with_reason( })?; let (left, suffix) = split(&mut context, basis_root, start_byte) .map_err(ReplaceRangeFailure::from_rope_fault)?; - let (_, right) = split(&mut context, suffix, end_byte - start_byte) + let (_, right) = split(&mut context, suffix, deleted_byte_length) .map_err(ReplaceRangeFailure::from_rope_fault)?; let inserted = build_text(&mut context, insert_text.as_bytes()) - .map_err(RopeFault::structural_dependency) - .map_err(ReplaceRangeFailure::from_rope_fault)?; - let left_with_insert = join(&mut context, left, inserted) - .map_err(RopeFault::structural_dependency) .map_err(ReplaceRangeFailure::from_rope_fault)?; + let left_with_insert = + join(&mut context, left, inserted).map_err(ReplaceRangeFailure::from_rope_fault)?; let root = join(&mut context, left_with_insert, right) - .map_err(RopeFault::structural_dependency) .map_err(ReplaceRangeFailure::from_rope_fault)?; let metrics = root_metrics(&mut context, root) .map_err(RopeFault::structural_dependency) .map_err(ReplaceRangeFailure::from_rope_fault)?; + let line_count = RopeFault::checked_add_u64(metrics.line_breaks, 1, "head line count overflow") + .map_err(ReplaceRangeFailure::from_rope_fault)?; + let inserted_byte_length = u64::try_from(insert_text.len()).map_err(|_| { + ReplaceRangeFailure::from_rope_fault(RopeFault::arithmetic_overflow( + "inserted byte length overflow", + )) + })?; let head = HeadFact { buffer_id: buffer_id.into(), basis_head_id: Some(basis_head_id.into()), root_node_id: root.map(NodeIdBytes::from), byte_length: metrics.byte_length, utf16_length: metrics.utf16_length, - line_count: metrics.line_breaks + 1, + line_count, root_digest: root_digest(root), sequence: next_sequence, }; @@ -216,7 +229,7 @@ pub fn plan_replace_with_reason( next_head_id: head_id.into(), start_byte, end_byte, - inserted_byte_length: insert_text.len() as u64, + inserted_byte_length, }; let rewrite_id = context .write_content_fact(&rewrite) @@ -229,8 +242,8 @@ pub fn plan_replace_with_reason( next_head_id: head_id.into(), start_byte, end_byte, - inserted_byte_length: insert_text.len() as u64, - deleted_byte_length: end_byte - start_byte, + inserted_byte_length, + deleted_byte_length, }) .map_err(RopeFault::structural_dependency) .map_err(ReplaceRangeFailure::from_rope_fault)?; diff --git a/native/jedit-echo-host/src/rope/tree.rs b/native/jedit-echo-host/src/rope/tree.rs index fc1a69fa..632548e6 100644 --- a/native/jedit-echo-host/src/rope/tree.rs +++ b/native/jedit-echo-host/src/rope/tree.rs @@ -11,23 +11,26 @@ use super::{GraphFacts, NodeMetrics, PlanContext, RopeNode, MAX_LEAF_BYTES}; pub(super) fn build_text( context: &mut PlanContext<'_, T>, bytes: &[u8], -) -> HostResult> { - let text = std::str::from_utf8(bytes) - .map_err(|error| HostError::InvalidRequest(format!("text is not UTF-8: {error}")))?; +) -> RopeResult> { + let text = std::str::from_utf8(bytes).map_err(|error| { + RopeFault::structural_dependency(HostError::InvalidRequest(format!( + "text is not UTF-8: {error}" + ))) + })?; if text.is_empty() { return Ok(None); } let mut roots = Vec::new(); let mut start = 0; while start < text.len() { - let mut end = (start + MAX_LEAF_BYTES).min(text.len()); + let mut end = start.saturating_add(MAX_LEAF_BYTES).min(text.len()); while end > start && !text.is_char_boundary(end) { end -= 1; } if end == start { - return Err(HostError::InvalidRequest( + return Err(RopeFault::structural_dependency(HostError::InvalidRequest( "unable to split UTF-8 leaf".to_owned(), - )); + ))); } roots.push(make_blob_leaf( context, @@ -45,24 +48,34 @@ pub(super) fn build_text( fn make_blob_leaf( context: &mut PlanContext<'_, T>, bytes: Vec, -) -> HostResult { - let text = std::str::from_utf8(&bytes) - .map_err(|error| HostError::InvalidRequest(format!("leaf is not UTF-8: {error}")))?; - let utf16_length = text.encode_utf16().count() as u64; - let line_breaks = bytes.iter().filter(|byte| **byte == b'\n').count() as u64; +) -> RopeResult { + let text = std::str::from_utf8(&bytes).map_err(|error| { + RopeFault::structural_dependency(HostError::InvalidRequest(format!( + "leaf is not UTF-8: {error}" + ))) + })?; + let utf16_length = u64::try_from(text.encode_utf16().count()) + .map_err(|_| RopeFault::arithmetic_overflow("leaf UTF-16 length overflow"))?; + let line_breaks = u64::try_from(bytes.iter().filter(|byte| **byte == b'\n').count()) + .map_err(|_| RopeFault::arithmetic_overflow("leaf line break count overflow"))?; let blob = BlobFact { content_hash: hash_bytes(BLOB_CONTENT_HASH_DOMAIN, &bytes), bytes, }; - let byte_length = blob.bytes.len() as u64; - let blob_id = context.write_content_fact(&blob)?; - context.write_content_fact(&LeafFact { - blob_id: blob_id.into(), - byte_start: 0, - byte_length, - utf16_length, - line_breaks, - }) + let byte_length = u64::try_from(blob.bytes.len()) + .map_err(|_| RopeFault::arithmetic_overflow("leaf byte length overflow"))?; + let blob_id = context + .write_content_fact(&blob) + .map_err(RopeFault::structural_dependency)?; + context + .write_content_fact(&LeafFact { + blob_id: blob_id.into(), + byte_start: 0, + byte_length, + utf16_length, + line_breaks, + }) + .map_err(RopeFault::structural_dependency) } fn make_leaf_slice( @@ -76,10 +89,14 @@ fn make_leaf_slice( } let blob_id = NodeId::from(leaf.blob_id); let bytes = context.verified_blob_fault(blob_id)?.bytes; - let start = usize::try_from(leaf.byte_start + relative_start).map_err(|_| { + let absolute_start = + RopeFault::checked_add_u64(leaf.byte_start, relative_start, "leaf range start overflow")?; + let absolute_end = + RopeFault::checked_add_u64(absolute_start, byte_length, "leaf range end overflow")?; + let start = usize::try_from(absolute_start).map_err(|_| { RopeFault::declared_rope_inconsistent("leaf offset exceeds addressable memory".to_owned()) })?; - let end = usize::try_from(leaf.byte_start + relative_start + byte_length).map_err(|_| { + let end = usize::try_from(absolute_end).map_err(|_| { RopeFault::declared_rope_inconsistent("leaf end exceeds addressable memory".to_owned()) })?; let slice = bytes.get(start..end).ok_or_else(|| { @@ -90,10 +107,12 @@ fn make_leaf_slice( })?; let fact = LeafFact { blob_id: leaf.blob_id, - byte_start: leaf.byte_start + relative_start, + byte_start: absolute_start, byte_length, - utf16_length: text.encode_utf16().count() as u64, - line_breaks: slice.iter().filter(|byte| **byte == b'\n').count() as u64, + utf16_length: u64::try_from(text.encode_utf16().count()) + .map_err(|_| RopeFault::arithmetic_overflow("leaf UTF-16 length overflow"))?, + line_breaks: u64::try_from(slice.iter().filter(|byte| **byte == b'\n').count()) + .map_err(|_| RopeFault::arithmetic_overflow("leaf line break count overflow"))?, }; context .write_content_fact(&fact) @@ -132,10 +151,17 @@ pub(super) fn split( .rope_node(root_id) .map_err(RopeFault::structural_dependency)? { - RopeNode::Leaf(leaf) => Ok(( - make_leaf_slice(context, &leaf, 0, offset)?, - make_leaf_slice(context, &leaf, offset, leaf.byte_length - offset)?, - )), + RopeNode::Leaf(leaf) => { + let suffix_length = leaf.byte_length.checked_sub(offset).ok_or_else(|| { + RopeFault::declared_rope_inconsistent( + "leaf split exceeds declared length".to_owned(), + ) + })?; + Ok(( + make_leaf_slice(context, &leaf, 0, offset)?, + make_leaf_slice(context, &leaf, offset, suffix_length)?, + )) + } RopeNode::Branch(branch) => { let left = NodeId::from(branch.left); let right = NodeId::from(branch.right); @@ -145,18 +171,17 @@ pub(super) fn split( .byte_length; if offset < left_length { let (prefix, middle) = split(context, Some(left), offset)?; - Ok(( - prefix, - join(context, middle, Some(right)).map_err(RopeFault::structural_dependency)?, - )) + Ok((prefix, join(context, middle, Some(right))?)) } else if offset == left_length { Ok((Some(left), Some(right))) } else { - let (middle, suffix) = split(context, Some(right), offset - left_length)?; - Ok(( - join(context, Some(left), middle).map_err(RopeFault::structural_dependency)?, - suffix, - )) + let right_offset = offset.checked_sub(left_length).ok_or_else(|| { + RopeFault::declared_rope_inconsistent( + "branch split offset underflow".to_owned(), + ) + })?; + let (middle, suffix) = split(context, Some(right), right_offset)?; + Ok((join(context, Some(left), middle)?, suffix)) } } } @@ -166,15 +191,24 @@ pub(super) fn join( context: &mut PlanContext<'_, T>, left: Option, right: Option, -) -> HostResult> { +) -> RopeResult> { let (Some(left_id), Some(right_id)) = (left, right) else { return Ok(left.or(right)); }; - let left_metrics = context.node_metrics(left_id)?; - let right_metrics = context.node_metrics(right_id)?; - if left_metrics.height > right_metrics.height + 1 { - let RopeNode::Branch(left_branch) = context.rope_node(left_id)? else { - return Err(HostError::MalformedFact( + let left_metrics = context + .node_metrics(left_id) + .map_err(RopeFault::structural_dependency)?; + let right_metrics = context + .node_metrics(right_id) + .map_err(RopeFault::structural_dependency)?; + let right_height_limit = + RopeFault::checked_add_u32(right_metrics.height, 1, "right rope height overflow")?; + if left_metrics.height > right_height_limit { + let RopeNode::Branch(left_branch) = context + .rope_node(left_id) + .map_err(RopeFault::structural_dependency)? + else { + return Err(RopeFault::fact_malformed( "unbalanced leaf height".to_owned(), )); }; @@ -185,9 +219,14 @@ pub(super) fn join( )?; return join(context, Some(NodeId::from(left_branch.left)), joined); } - if right_metrics.height > left_metrics.height + 1 { - let RopeNode::Branch(right_branch) = context.rope_node(right_id)? else { - return Err(HostError::MalformedFact( + let left_height_limit = + RopeFault::checked_add_u32(left_metrics.height, 1, "left rope height overflow")?; + if right_metrics.height > left_height_limit { + let RopeNode::Branch(right_branch) = context + .rope_node(right_id) + .map_err(RopeFault::structural_dependency)? + else { + return Err(RopeFault::fact_malformed( "unbalanced leaf height".to_owned(), )); }; @@ -201,12 +240,31 @@ pub(super) fn join( let branch = BranchFact { left: left_id.into(), right: right_id.into(), - byte_length: left_metrics.byte_length + right_metrics.byte_length, - utf16_length: left_metrics.utf16_length + right_metrics.utf16_length, - line_breaks: left_metrics.line_breaks + right_metrics.line_breaks, - height: left_metrics.height.max(right_metrics.height) + 1, + byte_length: RopeFault::checked_add_u64( + left_metrics.byte_length, + right_metrics.byte_length, + "rope byte length overflow", + )?, + utf16_length: RopeFault::checked_add_u64( + left_metrics.utf16_length, + right_metrics.utf16_length, + "rope UTF-16 length overflow", + )?, + line_breaks: RopeFault::checked_add_u64( + left_metrics.line_breaks, + right_metrics.line_breaks, + "rope line break count overflow", + )?, + height: RopeFault::checked_add_u32( + left_metrics.height.max(right_metrics.height), + 1, + "rope height overflow", + )?, }; - context.write_content_fact(&branch).map(Some) + context + .write_content_fact(&branch) + .map(Some) + .map_err(RopeFault::structural_dependency) } pub(super) fn root_metrics( @@ -230,3 +288,47 @@ pub(super) fn root_digest(root: Option) -> [u8; 32] { |id| *id.as_bytes(), ) } + +#[cfg(test)] +mod tests { + use warp_core::{make_warp_id, GraphStore}; + + use super::*; + use crate::records::NodeIdBytes; + + #[test] + fn join_refuses_left_height_overflow_with_structural_provenance() { + let store = GraphStore::new(make_warp_id("left-height-overflow")); + let mut context = PlanContext::new(&store); + let child = NodeIdBytes([0xA5; 32]); + let left = context + .write_content_fact(&BranchFact { + left: child, + right: child, + byte_length: 0, + utf16_length: 0, + line_breaks: 0, + height: u32::MAX, + }) + .expect("left fixture should identify"); + let right = context + .write_content_fact(&BranchFact { + left: child, + right: child, + byte_length: 0, + utf16_length: 0, + line_breaks: 0, + height: u32::MAX - 1, + }) + .expect("right fixture should identify"); + + let error = join(&mut context, Some(left), Some(right)) + .expect_err("left height overflow must obstruct joining"); + let (kind, legacy) = error.into_parts(); + assert_eq!(kind, super::super::fault::RopeFaultKind::ArithmeticOverflow); + assert!(matches!( + legacy, + HostError::MalformedFact(message) if message == "left rope height overflow" + )); + } +} diff --git a/native/jedit-echo-host/src/rope/window.rs b/native/jedit-echo-host/src/rope/window.rs index c8e5ca10..2f0bb4ca 100644 --- a/native/jedit-echo-host/src/rope/window.rs +++ b/native/jedit-echo-host/src/rope/window.rs @@ -88,7 +88,7 @@ pub fn read_window( } else { 0 }; - let lines = window_lines(&text, first_line, start_byte); + let lines = window_lines(&text, first_line, start_byte)?; Ok(WindowProjection { buffer_id: node_id_hex(buffer_id), basis_head_id: node_id_hex(basis_head_id), @@ -122,7 +122,13 @@ fn complete_utf8_prefix( prefix.truncate(valid_length); let text = String::from_utf8(prefix) .map_err(|_| HostError::InvalidRequest("window range is not UTF-8".to_owned()))?; - Ok((text, start_byte + valid_length as u64)) + let valid_length = u64::try_from(valid_length).map_err(|_| { + HostError::InvalidRequest("window UTF-8 prefix length overflow".to_owned()) + })?; + let actual_end = start_byte + .checked_add(valid_length) + .ok_or_else(|| HostError::InvalidRequest("window UTF-8 end overflow".to_owned()))?; + Ok((text, actual_end)) } } } @@ -162,7 +168,8 @@ fn collect_range( let metrics = context .node_metrics(node_id) .map_err(RopeFault::structural_dependency)?; - let node_end = node_start + metrics.byte_length; + let node_end = + RopeFault::checked_add_u64(node_start, metrics.byte_length, "rope node end overflow")?; if range_end <= node_start || range_start >= node_end { return Ok(()); } @@ -185,10 +192,12 @@ fn collect_range( output, support, )?; + let right_start = + RopeFault::checked_add_u64(node_start, left_length, "rope child start overflow")?; collect_range( context, NodeId::from(branch.right), - node_start + left_length, + right_start, range_start, range_end, output, @@ -200,14 +209,32 @@ fn collect_range( let overlap_end = range_end.min(node_end); let blob_id = NodeId::from(leaf.blob_id); let blob = context.verified_blob_fault(blob_id)?; - let relative_start = usize::try_from(leaf.byte_start + overlap_start - node_start) - .map_err(|_| { - RopeFault::declared_rope_inconsistent("window offset exceeds memory".to_owned()) - })?; - let relative_end = usize::try_from(leaf.byte_start + overlap_end - node_start) - .map_err(|_| { - RopeFault::declared_rope_inconsistent("window end exceeds memory".to_owned()) - })?; + let overlap_start_offset = overlap_start.checked_sub(node_start).ok_or_else(|| { + RopeFault::declared_rope_inconsistent( + "window overlap starts before its rope node".to_owned(), + ) + })?; + let overlap_end_offset = overlap_end.checked_sub(node_start).ok_or_else(|| { + RopeFault::declared_rope_inconsistent( + "window overlap ends before its rope node".to_owned(), + ) + })?; + let relative_start = RopeFault::checked_add_u64( + leaf.byte_start, + overlap_start_offset, + "leaf range start overflow", + )?; + let relative_end = RopeFault::checked_add_u64( + leaf.byte_start, + overlap_end_offset, + "leaf range end overflow", + )?; + let relative_start = usize::try_from(relative_start).map_err(|_| { + RopeFault::declared_rope_inconsistent("window offset exceeds memory".to_owned()) + })?; + let relative_end = usize::try_from(relative_end).map_err(|_| { + RopeFault::declared_rope_inconsistent("window end exceeds memory".to_owned()) + })?; let slice = blob .bytes .get(relative_start..relative_end) @@ -242,50 +269,89 @@ fn count_breaks_before( if offset <= left_metrics.byte_length { count_breaks_before(context, left, offset) } else { - Ok(left_metrics.line_breaks - + count_breaks_before( - context, - NodeId::from(branch.right), - offset - left_metrics.byte_length, - )?) + let right_offset = + offset + .checked_sub(left_metrics.byte_length) + .ok_or_else(|| { + HostError::MalformedFact("window branch offset underflow".to_owned()) + })?; + let right_breaks = + count_breaks_before(context, NodeId::from(branch.right), right_offset)?; + left_metrics + .line_breaks + .checked_add(right_breaks) + .ok_or_else(|| { + HostError::MalformedFact("window line count overflow".to_owned()) + }) } } RopeNode::Leaf(leaf) => { let blob = context.blob_bytes(NodeId::from(leaf.blob_id))?; let start = usize::try_from(leaf.byte_start) .map_err(|_| HostError::InvalidRequest("leaf start exceeds memory".to_owned()))?; - let end = usize::try_from(leaf.byte_start + offset) + let absolute_end = leaf.byte_start.checked_add(offset).ok_or_else(|| { + HostError::MalformedFact("window leaf offset overflow".to_owned()) + })?; + let end = usize::try_from(absolute_end) .map_err(|_| HostError::InvalidRequest("leaf offset exceeds memory".to_owned()))?; let bytes = blob.get(start..end).ok_or_else(|| { HostError::MalformedFact("line prefix exceeds leaf blob".to_owned()) })?; - Ok(bytes.iter().filter(|byte| **byte == b'\n').count() as u64) + u64::try_from(bytes.iter().filter(|byte| **byte == b'\n').count()) + .map_err(|_| HostError::MalformedFact("window line count overflow".to_owned())) } } } -fn window_lines(text: &str, first_line: u64, start_byte: u64) -> Vec { +fn window_lines(text: &str, first_line: u64, start_byte: u64) -> HostResult> { let mut lines = Vec::new(); let mut relative_start = 0usize; for (index, segment) in text.split_inclusive('\n').enumerate() { let content = segment.strip_suffix('\n').unwrap_or(segment); let content = content.strip_suffix('\r').unwrap_or(content); - let relative_end = relative_start + content.len(); + let relative_end = relative_start.checked_add(content.len()).ok_or_else(|| { + HostError::MalformedFact("window line byte length overflow".to_owned()) + })?; + let line_offset = u64::try_from(index) + .map_err(|_| HostError::MalformedFact("window line index overflow".to_owned()))?; + let line_number = first_line + .checked_add(line_offset) + .ok_or_else(|| HostError::MalformedFact("window line number overflow".to_owned()))?; + let relative_start_u64 = u64::try_from(relative_start) + .map_err(|_| HostError::MalformedFact("window line start overflow".to_owned()))?; + let relative_end_u64 = u64::try_from(relative_end) + .map_err(|_| HostError::MalformedFact("window line end overflow".to_owned()))?; lines.push(WindowLine { - line_number: first_line + index as u64, - start_byte: start_byte + relative_start as u64, - end_byte: start_byte + relative_end as u64, + line_number, + start_byte: start_byte.checked_add(relative_start_u64).ok_or_else(|| { + HostError::MalformedFact("window absolute line start overflow".to_owned()) + })?, + end_byte: start_byte.checked_add(relative_end_u64).ok_or_else(|| { + HostError::MalformedFact("window absolute line end overflow".to_owned()) + })?, text: content.to_owned(), }); - relative_start += segment.len(); + relative_start = relative_start + .checked_add(segment.len()) + .ok_or_else(|| HostError::MalformedFact("window segment length overflow".to_owned()))?; } if text.is_empty() || text.ends_with('\n') { + let line_offset = u64::try_from(lines.len()) + .map_err(|_| HostError::MalformedFact("window line index overflow".to_owned()))?; + let text_length = u64::try_from(text.len()) + .map_err(|_| HostError::MalformedFact("window text length overflow".to_owned()))?; + let line_number = first_line + .checked_add(line_offset) + .ok_or_else(|| HostError::MalformedFact("window line number overflow".to_owned()))?; + let end_byte = start_byte.checked_add(text_length).ok_or_else(|| { + HostError::MalformedFact("window absolute text end overflow".to_owned()) + })?; lines.push(WindowLine { - line_number: first_line + lines.len() as u64, - start_byte: start_byte + text.len() as u64, - end_byte: start_byte + text.len() as u64, + line_number, + start_byte: end_byte, + end_byte, text: String::new(), }); } - lines + Ok(lines) } diff --git a/native/jedit-echo-host/tests/replace_range_oracle.rs b/native/jedit-echo-host/tests/replace_range_oracle.rs index 795bbb1b..f8ac5ed1 100644 --- a/native/jedit-echo-host/tests/replace_range_oracle.rs +++ b/native/jedit-echo-host/tests/replace_range_oracle.rs @@ -307,6 +307,94 @@ fn cases() -> Vec { "invalid-request", "split exceeds empty rope", ), + obstruction( + "leaf-range-start-overflow", + "ab", + BasisSetup::LeafRangeStartOverflow, + 1, + 2, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "leaf range start overflow", + ), + obstruction( + "leaf-range-end-overflow", + "a", + BasisSetup::LeafRangeEndOverflow, + 0, + 1, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "leaf range end overflow", + ), + obstruction( + "rope-node-end-overflow", + &format!("{}b", "a".repeat(4096)), + BasisSetup::RopeNodeEndOverflow, + 0, + 1, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "rope node end overflow", + ), + obstruction( + "rope-byte-length-overflow", + "a", + BasisSetup::RopeByteLengthOverflow, + 0, + 0, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "rope byte length overflow", + ), + obstruction( + "rope-utf16-length-overflow", + "a", + BasisSetup::RopeUtf16LengthOverflow, + 0, + 0, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "rope UTF-16 length overflow", + ), + obstruction( + "rope-line-break-count-overflow", + "a", + BasisSetup::RopeLineBreakCountOverflow, + 0, + 0, + "\n", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "rope line break count overflow", + ), + obstruction( + "rope-height-overflow", + &format!("{}b", "a".repeat(4096)), + BasisSetup::RopeHeightOverflow, + 0, + 0, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "right rope height overflow", + ), + obstruction( + "head-line-count-overflow", + "a", + BasisSetup::HeadLineCountOverflow, + 1, + 1, + "x", + SemanticObstructionCode::ArithmeticOverflow, + "malformed-fact", + "head line count overflow", + ), ] } diff --git a/native/jedit-echo-host/tests/support/replace_range_basis.rs b/native/jedit-echo-host/tests/support/replace_range_basis.rs index 40238ab9..bcd9f3b8 100644 --- a/native/jedit-echo-host/tests/support/replace_range_basis.rs +++ b/native/jedit-echo-host/tests/support/replace_range_basis.rs @@ -1,6 +1,6 @@ use jedit_echo_host::records::{ - decode_fact, fact_bytes, fact_id, fact_type_id, BlobFact, BufferFact, HeadFact, LeafFact, - NodeIdBytes, + decode_fact, fact_bytes, fact_id, fact_type_id, BlobFact, BranchFact, BufferFact, + ContentAddressedFact, HeadFact, LeafFact, NodeIdBytes, TypedFact, }; use jedit_echo_host::rope::{plan_create, plan_replace}; use warp_core::{AttachmentValue, GraphStore, NodeId, NodeRecord, TickDelta, WarpOp}; @@ -14,6 +14,14 @@ pub enum BasisSetup { MissingBuffer, MalformedBuffer, BadBlobContentHash, + LeafRangeStartOverflow, + LeafRangeEndOverflow, + RopeNodeEndOverflow, + RopeByteLengthOverflow, + RopeUtf16LengthOverflow, + RopeLineBreakCountOverflow, + RopeHeightOverflow, + HeadLineCountOverflow, AboveGraphqlIntRange, StaleHead, ForeignHead, @@ -67,6 +75,43 @@ pub fn make_basis( ); } BasisSetup::BadBlobContentHash => corrupt_blob_hash(&mut store, head_id), + BasisSetup::LeafRangeStartOverflow => { + head_id = replace_root_leaf(&mut store, create.buffer_id, head_id, |leaf| { + leaf.byte_start = u64::MAX; + leaf.byte_length = 2; + }); + } + BasisSetup::LeafRangeEndOverflow => { + head_id = replace_root_leaf(&mut store, create.buffer_id, head_id, |leaf| { + leaf.byte_start = u64::MAX; + leaf.byte_length = 1; + }); + } + BasisSetup::RopeNodeEndOverflow => { + head_id = replace_root_left_leaf(&mut store, create.buffer_id, head_id, |leaf| { + leaf.byte_length = u64::MAX; + }); + } + BasisSetup::RopeByteLengthOverflow => { + head_id = replace_root_leaf(&mut store, create.buffer_id, head_id, |leaf| { + leaf.byte_length = u64::MAX; + }); + } + BasisSetup::RopeUtf16LengthOverflow => { + head_id = replace_root_leaf(&mut store, create.buffer_id, head_id, |leaf| { + leaf.utf16_length = u64::MAX; + }); + } + BasisSetup::RopeLineBreakCountOverflow | BasisSetup::HeadLineCountOverflow => { + head_id = replace_root_leaf(&mut store, create.buffer_id, head_id, |leaf| { + leaf.line_breaks = u64::MAX; + }); + } + BasisSetup::RopeHeightOverflow => { + head_id = replace_root_branch(&mut store, create.buffer_id, head_id, |branch| { + branch.height = u32::MAX; + }); + } BasisSetup::AboveGraphqlIntRange => { head_id = replace_head(&mut store, create.buffer_id, head_id, |head| { head.byte_length = i32::MAX as u64 + 1; @@ -198,6 +243,113 @@ fn corrupt_blob_hash(store: &mut GraphStore, head_id: NodeId) { ); } +fn replace_root_leaf( + store: &mut GraphStore, + buffer_id: NodeId, + head_id: NodeId, + mutate: impl FnOnce(&mut LeafFact), +) -> NodeId { + let root_id = root_node_id(store, head_id); + let next_root_id = replace_leaf(store, root_id, mutate); + replace_head_root(store, buffer_id, head_id, next_root_id) +} + +fn replace_root_left_leaf( + store: &mut GraphStore, + buffer_id: NodeId, + head_id: NodeId, + mutate: impl FnOnce(&mut LeafFact), +) -> NodeId { + let root_id = root_node_id(store, head_id); + let mut branch: BranchFact = decode_fact( + store + .node_attachment(&root_id) + .expect("branch attachment should exist"), + ) + .expect("root should decode as a branch"); + let next_left_id = replace_leaf(store, NodeId::from(branch.left), mutate); + branch.left = next_left_id.into(); + let next_root_id = insert_content_fact(store, &branch); + replace_head_root(store, buffer_id, head_id, next_root_id) +} + +fn replace_root_branch( + store: &mut GraphStore, + buffer_id: NodeId, + head_id: NodeId, + mutate: impl FnOnce(&mut BranchFact), +) -> NodeId { + let root_id = root_node_id(store, head_id); + let mut branch: BranchFact = decode_fact( + store + .node_attachment(&root_id) + .expect("branch attachment should exist"), + ) + .expect("root should decode as a branch"); + mutate(&mut branch); + let next_root_id = insert_content_fact(store, &branch); + replace_head_root(store, buffer_id, head_id, next_root_id) +} + +fn root_node_id(store: &GraphStore, head_id: NodeId) -> NodeId { + let head: HeadFact = decode_fact( + store + .node_attachment(&head_id) + .expect("head attachment should exist"), + ) + .expect("head should decode"); + NodeId::from(head.root_node_id.expect("fixture should have a rope root")) +} + +fn replace_leaf( + store: &mut GraphStore, + leaf_id: NodeId, + mutate: impl FnOnce(&mut LeafFact), +) -> NodeId { + let mut leaf: LeafFact = decode_fact( + store + .node_attachment(&leaf_id) + .expect("leaf attachment should exist"), + ) + .expect("leaf should decode"); + mutate(&mut leaf); + insert_content_fact(store, &leaf) +} + +fn replace_head_root( + store: &mut GraphStore, + buffer_id: NodeId, + head_id: NodeId, + root_id: NodeId, +) -> NodeId { + replace_head(store, buffer_id, head_id, |head| { + head.root_node_id = Some(root_id.into()); + head.root_digest = *root_id.as_bytes(); + }) +} + +fn insert_content_fact(store: &mut GraphStore, fact: &T) -> NodeId { + let node_id = fact_id(fact).expect("fixture fact should identify"); + store.insert_node( + node_id, + NodeRecord { + ty: fact_type_id::(), + }, + ); + set_fact_attachment(store, node_id, fact); + node_id +} + +fn set_fact_attachment(store: &mut GraphStore, node_id: NodeId, fact: &T) { + store.set_node_attachment( + node_id, + Some(AttachmentValue::Atom(warp_core::AtomPayload::new( + fact_type_id::(), + fact_bytes(fact).expect("fixture fact should encode").into(), + ))), + ); +} + #[cfg(test)] mod tests { use super::*; From 21772104754ae7234dac465f86adfe281601ff64 Mon Sep 17 00:00:00 2001 From: James Ross Date: Sun, 19 Jul 2026 23:05:42 -0700 Subject: [PATCH 33/63] Fix: exhaust retrospective evidence ledger --- CHANGELOG.md | 4 + .../0158-replace-range-canonical-fact-law.md | 137 ++++++--- tests/replace-range-law-cycle.spec.mjs | 280 ++++++++++++----- tests/support/replace-range-audit-ledger.mjs | 289 ++++++++++++++++++ 4 files changed, 595 insertions(+), 115 deletions(-) create mode 100644 tests/support/replace-range-audit-ledger.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fc48b87..9d8c6003 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- Made the DL-0158 final-audit ledger exhaustive. The release test now parses a + closed claim grammar, binds every named claim to its retrospective section + and complete ordered source-citation set, and rejects hidden, malformed, + missing, extra, or displaced evidence links. - Made every retained-rope coordinate and metric calculation used by `ReplaceRange` fail with a typed arithmetic obstruction instead of panicking in debug builds or wrapping in optimized builds. Adversarial leaf, branch, diff --git a/docs/design/0158-replace-range-canonical-fact-law.md b/docs/design/0158-replace-range-canonical-fact-law.md index c35a6c5e..480dd8af 100644 --- a/docs/design/0158-replace-range-canonical-fact-law.md +++ b/docs/design/0158-replace-range-canonical-fact-law.md @@ -473,24 +473,37 @@ Mitigations: What changed from the design: -- The planned native-schema corpus was implemented with 20 cases, separating - retained stale-head and real foreign-head basis witnesses, without - changing the production planner, fact bytes, identities, or routing. - [`native/jedit-echo-host/tests/replace_range_oracle.rs#L43:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L43) +- **Native schema corpus.** Native oracle generation runs the same case set + twice, requires byte-identical output, compares those bytes with the + committed oracle resource, and checks its SHA-256 sidecar. + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L22@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L22) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L23@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L23) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L24@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L24) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L30@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L30) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L31@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L31) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L35@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_oracle.rs#L35) +- **Retained stale-basis relationship.** The stale fixture retains the invoked + historical Head and checks its relationship to the current canonical Head. [`native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L46@d57e0d239c005898839a15041807580ed2dfe595`](https://github.com/flyingrobots/jedit/blob/d57e0d239c005898839a15041807580ed2dfe595/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L46) +- **Retained foreign-basis relationship.** The foreign fixture retains another + Buffer and checks that it canonically names the invoked foreign Head. [`native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L64@d57e0d239c005898839a15041807580ed2dfe595`](https://github.com/flyingrobots/jedit/blob/d57e0d239c005898839a15041807580ed2dfe595/native/jedit-echo-host/tests/support/replace_range_basis_witness.rs#L64) -- Review tightened the declaration with a fully specified string codec, a - corpus-only invocation schema and exact bytes, mechanical schema-to-writer - field-order comparison, strict/atomic fixture updates, and a digest over the - exact declared Jedit source set. These are proof-strengthening additions, - not a widening into the deferred Echo ABI. - [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86) -- Review replaced free-form obstruction labels with an exhaustive generated - enum and added a strict machine-readable corpus shape whose conformance path - rejects unknown members, variants, and semantic codes. - [`native/jedit-echo-host/tests/support/replace_range_contract.rs#L3:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_contract.rs#L3) - [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336) -- The published SHA-256 digests are +- **Schema and source-set contract.** Schema conformance mechanically compares + every declared fact-member order with canonical native-writer bytes. The + source-set implementation embeds an ordered path/byte list and hashes it + with domain-separated, length-framed SHA-256. + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86) + [`native/jedit-echo-host/tests/support/replace_range_source_set.rs#L5@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_source_set.rs#L5) + [`native/jedit-echo-host/tests/support/replace_range_source_set.rs#L70@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_source_set.rs#L70) +- **Obstruction schema.** Oracle support declares a closed ten-variant serde + obstruction enum and matching `ALL` list. Schema conformance freezes the same + ten wire values, while negative witnesses reject an unknown top-level member + and an invented semantic code. + [`native/jedit-echo-host/tests/support/replace_range_contract.rs#L3@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_contract.rs#L3) + [`native/jedit-echo-host/tests/support/replace_range_contract.rs#L20@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_contract.rs#L20) + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L336) + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L362@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L362) +- **Artifact digest ledger.** The published SHA-256 digests are `fa5087c8fe72dc9c5f138f12b9498ab7f943061fc5ca735d4f68009c081eabc2` for the schema, `1ac26477e1d6c08df49446627ad002e40bd214235ce93b563d62bb50e40dc14c` @@ -498,42 +511,84 @@ What changed from the design: `6e7d9ff396897940e16d8fa5936d7fd51b8c8915e2e9ea2fb2d3b5916a4224c9` for the oracle. The oracle source-set digest is `6541ff04a5a97fd407f31479f9393293a937db12ab3705686525e49154f48083`. -- The deliberate resource-update command now runs each exact writer serially + The ledger derives all four values from the committed artifacts. + [`tests/replace-range-law-cycle.spec.mjs#L76@95f63d2c31847c3a4cc4bbea9152900470843d3a`](https://github.com/flyingrobots/jedit/blob/95f63d2c31847c3a4cc4bbea9152900470843d3a/tests/replace-range-law-cycle.spec.mjs#L76) +- **Serialized resource updates.** The deliberate resource-update command runs + each exact writer serially before the complete locked reader and conformance pass. [`package.json#L10@13b16f2e7195458138865460c5948744ea8615ed`](https://github.com/flyingrobots/jedit/blob/13b16f2e7195458138865460c5948744ea8615ed/package.json#L10) +- **Exact audit locations.** The earlier audit gate declared nine evidence + class/full-commit/path/line tuples and required each corresponding GitHub + blob URL to occur somewhere in the retrospective. + [`tests/replace-range-law-cycle.spec.mjs#L32@a8cabbd8d68c5345cdea5cba0bb59b956ec7be63`](https://github.com/flyingrobots/jedit/blob/a8cabbd8d68c5345cdea5cba0bb59b956ec7be63/tests/replace-range-law-cycle.spec.mjs#L32) + [`tests/replace-range-law-cycle.spec.mjs#L110@a8cabbd8d68c5345cdea5cba0bb59b956ec7be63`](https://github.com/flyingrobots/jedit/blob/a8cabbd8d68c5345cdea5cba0bb59b956ec7be63/tests/replace-range-law-cycle.spec.mjs#L110) What the tests proved: -- Native Rust serialization regenerates all exact resources and identities; - every declared fact-member order equals the order observed from writer bytes; - the full string escape vector matches the native writer; and corpus invocation - bytes preserve `u64` above `i32::MAX`. - [`native/jedit-echo-host/tests/replace_range_schema.rs#L89:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema.rs#L89) - [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L295:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L295) -- Strict typed corpus conformance binds the exact envelope and source identity, - rejects unknown members and variants, and requires canonical member order, - framing, and terminal newline. +- **Native serialization.** Native Rust reconstructs the schema and + codec-vector resources, requires byte-for-byte equality with both committed + resources, checks their SHA-256 sidecars, and compares every declared + fact-member order with canonical native-writer bytes. + [`native/jedit-echo-host/tests/replace_range_schema.rs#L89@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema.rs#L89) + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86@9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L86) +- **Supplementary Unicode scalar.** The golden vector requires U+10FFFF to + remain literal four-byte UTF-8 and rejects surrogate-style canonical output. + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L290@79a3b788d102c0b04fb47c1d3ce972dbab32a1cc`](https://github.com/flyingrobots/jedit/blob/79a3b788d102c0b04fb47c1d3ce972dbab32a1cc/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L290) +- **Type-ID domain binding.** Conformance recomputes every declared fact type ID + from the published domain and compares it with pinned `warp-core` behavior. + [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L308@f5482c4e7797e043c006adf1de293d4dfe85e13a`](https://github.com/flyingrobots/jedit/blob/f5482c4e7797e043c006adf1de293d4dfe85e13a/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L308) +- **Strict typed corpus validation.** Strict conformance rejects drift in the + envelope identity and ordered source-set claim. [`native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L120@9a309641edd4b809c37f5d055790de311e5d61f9`](https://github.com/flyingrobots/jedit/blob/9a309641edd4b809c37f5d055790de311e5d61f9/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L120) +- **Canonical oracle bytes.** Raw-byte validation separately freezes canonical + object order, whitespace, framing, and terminal newline. [`native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L70@8112752b85862d065811fbe133fdfe8f9e8021c1`](https://github.com/flyingrobots/jedit/blob/8112752b85862d065811fbe133fdfe8f9e8021c1/native/jedit-echo-host/tests/replace_range_corpus_conformance.rs#L70) -- Corpus invocation conformance rejects identifiers and replacement bytes that +- **WARP-qualified identifiers.** Footprint and patch projections refuse a + foreign WARP instead of flattening its local IDs into the corpus WARP. + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L393@edd41a35058fea354d46a38421641b0c173e3faa`](https://github.com/flyingrobots/jedit/blob/edd41a35058fea354d46a38421641b0c173e3faa/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L393) +- **Invocation lexical validation.** Corpus invocation conformance rejects + identifiers and replacement bytes that violate their exact lowercase hexadecimal, width, or UTF-8 lexical laws. [`native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L396@c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c`](https://github.com/flyingrobots/jedit/blob/c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c/native/jedit-echo-host/tests/replace_range_schema_conformance.rs#L396) -- Six success and fourteen obstruction cases regenerate deterministically. A - success yields the exact support, ordered patch, retained facts, metrics, and - materialized consequence; an obstruction yields no `MutationPlan` and leaves - the parent graph unchanged. - [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L190) - [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258:9ffc0e4aa9313eba3774be970adbc1b976e5888a`](https://github.com/flyingrobots/jedit/blob/9ffc0e4aa9313eba3774be970adbc1b976e5888a/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L258) - [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L393@edd41a35058fea354d46a38421641b0c173e3faa`](https://github.com/flyingrobots/jedit/blob/edd41a35058fea354d46a38421641b0c173e3faa/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L393) -- A retained-consequence consistency gate refuses mismatched Buffer, Head, - Rewrite, or Diff evidence before a committable result enters the corpus. It - selects Rewrite and Diff facts from the current patch and independently - checks each retained node record's declared type. +- **Typed planner obstructions.** Production owns the closed operation reason, + exhaustively maps structural rope faults into it, and returns typed failures + from the detailed planner. The compatibility wrapper retains the legacy + `HostError`, and corpus parity re-runs that wrapper to compare legacy class, + message, and display. + [`native/jedit-echo-host/src/rope/replace.rs#L14@b4606ce74ab37f8cc54d184a17c8eaeea2dff092`](https://github.com/flyingrobots/jedit/blob/b4606ce74ab37f8cc54d184a17c8eaeea2dff092/native/jedit-echo-host/src/rope/replace.rs#L14) + [`native/jedit-echo-host/src/rope/replace.rs#L72@b4606ce74ab37f8cc54d184a17c8eaeea2dff092`](https://github.com/flyingrobots/jedit/blob/b4606ce74ab37f8cc54d184a17c8eaeea2dff092/native/jedit-echo-host/src/rope/replace.rs#L72) + [`native/jedit-echo-host/src/rope/replace.rs#L99@b4606ce74ab37f8cc54d184a17c8eaeea2dff092`](https://github.com/flyingrobots/jedit/blob/b4606ce74ab37f8cc54d184a17c8eaeea2dff092/native/jedit-echo-host/src/rope/replace.rs#L99) + [`native/jedit-echo-host/src/rope/replace.rs#L118@b4606ce74ab37f8cc54d184a17c8eaeea2dff092`](https://github.com/flyingrobots/jedit/blob/b4606ce74ab37f8cc54d184a17c8eaeea2dff092/native/jedit-echo-host/src/rope/replace.rs#L118) + [`native/jedit-echo-host/tests/support/replace_range_legacy.rs#L15@b4606ce74ab37f8cc54d184a17c8eaeea2dff092`](https://github.com/flyingrobots/jedit/blob/b4606ce74ab37f8cc54d184a17c8eaeea2dff092/native/jedit-echo-host/tests/support/replace_range_legacy.rs#L15) +- **Retained arithmetic refusal.** Checked helpers turn overflow into the typed + structural arithmetic fault. Named retained-rope fixtures exercise the path, + and corpus evaluation compares each observed reason with its expectation + while asserting that planning leaves the basis-store projection unchanged. + [`native/jedit-echo-host/src/rope/fault.rs#L59@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/src/rope/fault.rs#L59) + [`native/jedit-echo-host/src/rope/fault.rs#L66@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/src/rope/fault.rs#L66) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L310@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/tests/replace_range_oracle.rs#L310) + [`native/jedit-echo-host/tests/replace_range_oracle.rs#L387@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/tests/replace_range_oracle.rs#L387) + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L214@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L214) + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L223@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L223) + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L246@9930d04bed048d69f54ef08d5f25f5c211efa199`](https://github.com/flyingrobots/jedit/blob/9930d04bed048d69f54ef08d5f25f5c211efa199/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L246) +- **Declared-text consequence.** Successful projection independently derives + basis prefix plus replacement plus suffix and rejects an internally + consistent retained rope that does not realize that declared edit. + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L316@afd06f652aad7d6e8f389efec49e1ea1469eef88`](https://github.com/flyingrobots/jedit/blob/afd06f652aad7d6e8f389efec49e1ea1469eef88/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L316) +- **Keyed Buffer identity.** Retained-consequence validation recomputes the + keyed Buffer address from `buffer_key` in both basis and result graphs. + [`native/jedit-echo-host/tests/support/replace_range_consequence.rs#L42@751ea54360f2633ee04f472bb18ad07c47cef61f`](https://github.com/flyingrobots/jedit/blob/751ea54360f2633ee04f472bb18ad07c47cef61f/native/jedit-echo-host/tests/support/replace_range_consequence.rs#L42) +- **Current-patch consequence selection.** Rewrite and Diff evidence is selected + from the current patch and cross-linked before a committable result enters the + corpus. [`native/jedit-echo-host/tests/support/replace_range_consequence.rs#L113@475b6dcc0eae9edbf228221069ab3aab975be868`](https://github.com/flyingrobots/jedit/blob/475b6dcc0eae9edbf228221069ab3aab975be868/native/jedit-echo-host/tests/support/replace_range_consequence.rs#L113) +- **Retained node-record typing.** Every retained fact requires agreement + between the graph node record and atom attachment type before decoding. [`native/jedit-echo-host/tests/support/replace_range_consequence.rs#L242@6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8`](https://github.com/flyingrobots/jedit/blob/6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8/native/jedit-echo-host/tests/support/replace_range_consequence.rs#L242) -- This is deterministic self-validation. It is not an independent verifier, - all-input equivalence proof, Echo admission witness, runtime receipt, WAL, or - recovery witness. +- **Evidence-grade boundary.** The corpus explicitly calls itself deterministic + self-validation. It is not an independent verifier, all-input equivalence + proof, Echo admission witness, runtime receipt, WAL, or recovery witness. + [`native/jedit-echo-host/tests/support/replace_range_oracle.rs#L177@d87e7a1891cbefb6cf478bbc9b6a9ed87121b242`](https://github.com/flyingrobots/jedit/blob/d87e7a1891cbefb6cf478bbc9b6a9ed87121b242/native/jedit-echo-host/tests/support/replace_range_oracle.rs#L177) What remains open: diff --git a/tests/replace-range-law-cycle.spec.mjs b/tests/replace-range-law-cycle.spec.mjs index 89c19cff..23a06821 100644 --- a/tests/replace-range-law-cycle.spec.mjs +++ b/tests/replace-range-law-cycle.spec.mjs @@ -4,6 +4,12 @@ import fs from "node:fs"; import path from "node:path"; import test from "node:test"; +import { + CHANGED_SECTION, + FINAL_AUDIT_EVIDENCE_LOCATIONS, + PROVED_SECTION, +} from "./support/replace-range-audit-ledger.mjs"; + const DESIGN_PATH = path.join( process.cwd(), "docs", @@ -29,67 +35,119 @@ const PUBLISHED_ARTIFACTS = [ "codec-vectors-v1.json", "replace-range-v1.oracle.json", ]; -const FINAL_AUDIT_EVIDENCE_LOCATIONS = [ - [ - "WARP-qualified identifiers", - "edd41a35058fea354d46a38421641b0c173e3faa", - "native/jedit-echo-host/tests/support/replace_range_oracle.rs", - 393, - ], - [ - "invocation lexical validation", - "c0214b2fe52ef3a3cb2d09e24af9cd4a3258ca0c", - "native/jedit-echo-host/tests/replace_range_schema_conformance.rs", - 396, - ], - [ - "strict typed corpus validation", - "9a309641edd4b809c37f5d055790de311e5d61f9", - "native/jedit-echo-host/tests/replace_range_corpus_conformance.rs", - 120, - ], - [ - "current-patch consequence selection", - "475b6dcc0eae9edbf228221069ab3aab975be868", - "native/jedit-echo-host/tests/support/replace_range_consequence.rs", - 113, - ], - [ - "retained node-record typing", - "6c20d93ff94a8b0e24e6d3fc05492f99f27a56e8", - "native/jedit-echo-host/tests/support/replace_range_consequence.rs", - 242, - ], - [ - "canonical oracle bytes", - "8112752b85862d065811fbe133fdfe8f9e8021c1", - "native/jedit-echo-host/tests/replace_range_corpus_conformance.rs", - 70, - ], - [ - "serialized resource updates", - "13b16f2e7195458138865460c5948744ea8615ed", - "package.json", - 10, - ], - [ - "retained stale-basis relationship", - "d57e0d239c005898839a15041807580ed2dfe595", - "native/jedit-echo-host/tests/support/replace_range_basis_witness.rs", - 46, - ], - [ - "retained foreign-basis relationship", - "d57e0d239c005898839a15041807580ed2dfe595", - "native/jedit-echo-host/tests/support/replace_range_basis_witness.rs", - 64, - ], -]; function readDesign() { return fs.readFileSync(DESIGN_PATH, "utf8"); } +function retrospectiveClaims(design = readDesign()) { + const retrospectiveParts = design.split("## Retrospective"); + assert.equal(retrospectiveParts.length, 2, "expected one Retrospective"); + const changedParts = retrospectiveParts[1].split( + "What changed from the design:", + ); + assert.equal(changedParts.length, 2, "expected one changed-design marker"); + const openParts = changedParts[1].split("What remains open:"); + assert.equal(openParts.length, 2, "expected one remaining-work marker"); + + const claims = []; + let currentClaim; + let currentSection = CHANGED_SECTION; + let testsProvedMarkers = 0; + for (const line of openParts[0].split(/\r?\n/)) { + if (line === "") { + currentClaim = undefined; + continue; + } + if (line === "What the tests proved:") { + testsProvedMarkers += 1; + currentSection = PROVED_SECTION; + currentClaim = undefined; + continue; + } + if (line.startsWith("- ")) { + const match = + /^- \*\*(?[^*]+)\.\*\* (?\S.*)$/.exec(line); + assert.ok(match, `unlabeled retrospective claim: ${line}`); + assertPlainEvidenceProse(match.groups.claimText); + currentClaim = { + citations: [], + evidenceClass: match.groups.evidenceClass, + section: currentSection, + }; + claims.push(currentClaim); + continue; + } + + assert.ok(currentClaim, `unsupported retrospective syntax: ${line}`); + assert.doesNotMatch( + line, + /^\s*(?:#{1,6}\s|[-+*]\s|\d+[.)]\s|>|```|~~~|\||=+\s*$|(?:(?:\*\s*){3,}|(?:_\s*){3,}|(?:-\s*){3,})$)/, + `unsupported retrospective syntax: ${line}`, + ); + assert.match(line, /^ \S/, `unsupported retrospective syntax: ${line}`); + const citation = parseEvidenceCitation(line); + if (citation) { + currentClaim.citations.push(citation); + } + } + assert.equal(testsProvedMarkers, 1, "expected one tests-proved marker"); + return claims; +} + +function evidenceCitation([commit, sourcePath, line]) { + return { + label: `${sourcePath}#L${line}@${commit}`, + url: `https://github.com/flyingrobots/jedit/blob/${commit}/${sourcePath}#L${line}`, + }; +} + +function parseEvidenceCitation(line) { + const match = + /^ \[`(?