Skip to content

conformance: dispose the declared cases and verify full trees across carriers against released bitruntime. - #57

Merged
jm9e merged 2 commits into
mainfrom
conformance/wiretree-disposition
Sep 26, 2026
Merged

jm9e merged 2 commits into
mainfrom
conformance/wiretree-disposition

Conversation

@jm9e

@jm9e jm9e commented Sep 26, 2026

Copy link
Copy Markdown
Member

Refs #29 and #39. This does not close #29: consumer acceptance (bitsystem3#11, bittree#53) and bitruntime's production facilities (bitruntime#15) remain.

What this adds

conformance/wiretree/cases.json: an independent full-tree family for decision 0012. The expectations are written from the contract, decision 0012 and the pinned bitwire/1 profile, never recorded from an implementation. Drivers get their inputs with expectations withheld, and keys arrive as exact-byte hex.

Family Cases Evidence
structure 18 own capability identity, exact empty/binary/slash/Unicode keys, complete children, missing vs. present-but-refusing, selection chains, both reconstruction directions, complete cuts, retained and reset state, substitution, alteration, and refusal of duplicate keys, missing children and cycles. Every construction also shows that neither the input nor the returned parts can change the tree.
bridge 1 AsAddressed carries the exact UTF-8 image; ill-formed segments are refused before any primitive; binary keys are unreachable; the bridge is send-only
carrier 7 a far tree served on a dispatcher and a near tree whose own Wires send at far paths: routing, reconstruction on either side, a relay through Forward, access through an addressed Mount, a late reply and a requester's cancel after the far node was replaced, teardown with the borrowed endpoint still usable

conformance/wiretree/disposition.json maps all 39 historical declared cases, the 19 recorded gaps and both limitations. 37 cases have current counterparts. Ten keep a historical addressed part:

  • suffix delivery into opaque access;
  • path-observing guards;
  • addressed views.

Two of the ten (guard-around-composite and guarded-child-complete-access) are addressed-only. The historical fixture and both ledgers are unchanged. They are pinned by SHA-256, and check.mjs fails if they change without the disposition being revisited.

Results against the released artifacts

Pins are unchanged from #52:

  • Go: github.com/Bitspark/bitruntime v0.2.0.
  • TypeScript: @bitspark/bitruntime 0.2.0, the release asset.
  • Contract: bitwire v0.3.0 at f825f3f.
reference (test-only) production (bitruntime trees)
Go, structure and bridge 19/19 19/19
Go, carrier: local, peer/0, peer/1 7/7 on each 7/7 on each
TypeScript, structure and bridge 19/19 19/19
TypeScript, carrier: local, peer/0, peer/1 7/7 on each 7/7 on each
  • Toolchain: go1.26.3 with the race detector, and Node 24 checked by TypeScript 7.0.2.
  • Unlawful realizations: all seven are rejected: fallback, wrapping-own, normalizing, fabricating, latin1-bridge, incomplete-children and retargeting-serve.
  • Existing families: all unchanged and still green.

Findings

  • bitwire/1 refuses an empty root path, so a served tree sits under a nonempty prefix, ["t"].
  • A carrier path names a position, not a node, so the near tree binds each position separately.
  • Across a carrier, a far path without a node answers method_not_found, and a refusing far own answers internal. Both codes come from the pinned profile. A binary key cannot be named over the carrier.
  • Captured cancellation survives far-side replacement only when serving goes through an invocation-aware dispatcher with per-node routes. Serving through the tree current at delivery re-routes the cancel, and the suite detects it.
  • bitruntime v0.2.0 has no public facility for two adapters, so they are test-only here and belong on bitruntime#15:
    • bind: a Wire at an AddressedWire path;
    • serve: a tree's nodes on a dispatcher.

Checks

  • node scripts/check.mjs: green, including the new scripts/wiretree.test.mjs.
  • node scripts/conformance-runtime.mjs: green, as above.

…carriers against released bitruntime.

Adds conformance/wiretree, a separately identified family for decision 0012 with
independently authored expectations: 18 structural cases, 1 addressed-bridge
case and 7 carrier cases. Go and TypeScript drivers run it against the released
bitruntime v0.2.0 through a test-only reference interpreter and through
bitruntime's own Compose/Select/Send/AsAddressed, on the local pair and on real
WebSockets in both directions. All 26 cases pass in both languages and both
realizations. Seven deliberately unlawful TypeScript realizations are each
rejected.

disposition.json maps every one of the 39 historical declared cases, the 19
recorded production gaps and both limitations to current cases or to an
explicit historical addressed limitation (suffix delivery, path-observing
interception, addressed views). The historical fixture and both gap ledgers are
unchanged and pinned by digest.

Carrier composition uses two test-only adapters, bind and serve, because
bitruntime v0.2.0 has no public facility for either; bitruntime#15 tracks them.

Refs #29, #39.
- The mounted-carrier case now reaches an own value at [] through the mount;
  the relay also carries the doubly empty path; the late reply arrives after
  the serving composition is torn down, as in the historical cases.
- A U+FFFD child and a fixture-defined ill-formed segment (UTF-16 units and
  UTF-8 bytes) make lossy bridges observable in both languages; a lossy-bridge
  mutant is added.
- Refusing primitives record their own invocation; missing is no invocation
  plus refusal; bridge refusals without a primitive are "unreached".
- A driver-implemented acyclic child is accepted, so cycle refusal is not
  vacuous.
- Exact parts check children() and decompose() by count and contents; the
  selection law covers chains that fail part-way; Go checks every structural
  method on the bridge; every event has its own return capability; borrowed
  endpoints are checked after the dispatcher, forwarding and mount are released.
- Each mutant must fail the case aimed at it.
- The carrier model moves from docs/composition.md to the family README as a
  test model; internal is documented as the serving adapter's choice.
- The contract states that derived sending on a missing path is refused.
- Disposition notes cover every sharpened meaning and name the carrier gap
  members met only through the test-only adapters.
@jm9e

jm9e commented Sep 26, 2026

Copy link
Copy Markdown
Member Author

Review record

An independent review agent re-derived every expected trace by hand. It found no data races and confirmed that the counts and instance numbers were right. All of its findings are resolved in ac88970:

Finding Resolution
mounted-carrier-reconstruction was mapped to a mount case with a root whose own refuses, and never reached an own value through the mount carrier-mount-reconstruction now uses root and delivers to its own value at []
bridgeInvalid couldn't detect lossy conversion, and Go and TS sent different segments A U+FFFD child was added. The ill-formed segment is in the fixture as UTF-16 code units plus UTF-8 bytes. A lossy-bridge mutant is added and rejected.
"refused" was inferred from a thrown error Refusing primitives record their own invocation. missing means no invocation plus refusal. Bridge-level refusals are unreached. Violations get names of their own, such as refusedWithoutOwn, fallback and missingAdmitted.
The contract doc stated the test adapters' behavior as law The carrier model moved into the family README as a test model. internal is documented as the serving adapter's choice (thrown in TS, core.Respond in Go).
cycle-refused passed vacuously Added foreign-child-accepted: an acyclic child implemented by the driver is accepted, selected and rebuilt
Missing-path refusal was stricter than the contract text The contract now states that derived sending on a missing path is refused. bitruntime already does this, and no declaration changes.
Mutants had to fail only something Each mutant has a named target case that must be among its failures
Driver asymmetries (parts checks, sendOnly, a shared Go event return, borrowedUsable with compositions attached, partial selection-law check) Both drivers now check children() and decompose() by count and contents, including after mutation. Go checks every structural method. Each event has its own return. Borrowed endpoints are checked after the dispatcher, forward and mount are released. The selection law covers chains that fail part-way.
Meaning changes and dropped steps not noted The disposition notes every sharpened meaning. The relay carries ["",""]. The late reply follows teardown. The carrier members of the origin gap are named as met only through test-only adapters.

Re-run locally at ac88970:

  • node scripts/check.mjs passes.
  • node scripts/conformance-runtime.mjs passes.
    • Both languages and both realizations pass all 27 cases: 20 structure and bridge, plus 7 carrier cases on each of three carriers.
    • All eight mutants fail the case aimed at them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wire: specify and verify Deixis composition and reconstruction laws.

1 participant