Record the whole-cut Soul pass: the hop is keyed wrong and the runtimes disagree
Cut 1 may not merge. Row identity becomes (schemaId, recordKey), the hop behaves identically in both runtimes, the error message obeys its own schema, and cultnet-rs becomes mutable by cargo-mutants. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b688cb2091
commit
618b7b562b
1 changed files with 105 additions and 2 deletions
|
|
@ -671,8 +671,111 @@ edit `src/` at the same time.
|
|||
five-field bodies with an HMAC-SHA256 digest, and they differ in base64
|
||||
flavour and separators. That is correct and stays. Rust's own doc said so
|
||||
first.
|
||||
- **To check:** pass B reports three Caching failures as pre-existing, where
|
||||
pass A reported one. Soul settles it against `main`.
|
||||
- **Settled by Soul:** three Caching failures, identical at `b3d9cf7` and
|
||||
`52082ec`. All three expect a write to a read-only directory to fail, and
|
||||
the container is root. **Environmental.** Pass B's count was right.
|
||||
|
||||
**Soul's whole-cut pass, 2026-09-22** (Opus, at `52082ec`). **Cut 1 may not
|
||||
merge.**
|
||||
|
||||
- **Held:** Networking 238 and 2 skips, Mesh 255, cultnet-rs 213. R-Q holds
|
||||
(both replica applies, the startup rebuild, and a shard-spanning refusal).
|
||||
R-O holds against a stranger and across a restart, and the astral key
|
||||
round-trips. R-H's digest covers every selection term. R-R holds and is
|
||||
honest. R-A holds. R-F's door is inside `EvaluateAll`, which every path
|
||||
reaches. The canonical-number door is anchored `\A..\z`.
|
||||
- **S-1, high: the hop indexes rows by record key alone.** `byKey`
|
||||
(`CultNetSelectionEvaluator.cs:176-178`) and Rust's `HashMap<&str, &R>`
|
||||
(`selection.rs:1457`) both keep the last row at a key. CultCache keys are
|
||||
unique **per schema**, and the Mesh tests already put two schemas at one
|
||||
key. With rows in one order the selection refuses valid data as
|
||||
`reference_outside_target`; in the other order it succeeds. Under `cited`
|
||||
the index is a set of bare keys, so a row of one schema counts as cited
|
||||
because something cites another schema at the same key.
|
||||
- **S-2, high: R-T's `cites` half did not land in C#.** C# filters edges by
|
||||
the citation's key (`:355`) **before** `EnsureWithinDeclaredTarget` (`:359`);
|
||||
Rust checks first. A many-reference declared to one leaf but holding an edge
|
||||
into another is refused by Rust and accepted by C#.
|
||||
- **S-3, high: a selection with both hops loses its `cites` edges in Rust.**
|
||||
`selection.rs:1541` computes one direction for every edge; C# anchors each
|
||||
edge by its own hop. No vector covers both hops.
|
||||
- **S-4, high: the error message fails its own schema.** Nothing decodes real
|
||||
`CultNetErrorMessage` bytes against `cultnet.error.schema.json`.
|
||||
`routingHint` is undeclared under `additionalProperties: false`, and nil
|
||||
`details.asOf`, `details.current`, `code` and `details` all fail their
|
||||
declared types. The header schema shows the nullable pattern was understood
|
||||
and simply not applied here.
|
||||
- **S-5, medium: Stryker leaves 85 survivors** over the cut's two core files:
|
||||
396 mutants, 310 killed, 85 survived, **75 with no coverage at all**, score
|
||||
66%. The old "every entry killed" was an artifact of a hand-picked list.
|
||||
- **S-6, medium: the cursor is malleable in its own position.** The digest
|
||||
covers the selection but not the body's `asOf`, `ordinal`, `schemaId` or
|
||||
`recordKey`. A caller holding one valid cursor can rewrite its position and
|
||||
reuse the digest.
|
||||
- **S-7, medium: R-I's claim is false.** The vectors compare ids, `matched`,
|
||||
`hasNext` and edges. They never compare page bytes, `asOf`, `next`, or a
|
||||
refusal's code, and `rs-written.json` carries no projection vector at all.
|
||||
- **S-8, low: the out-of-target null branch is dead.** D11 refuses a bare
|
||||
`ICultRecordRef` in both scalar and many form, so every surviving shape has
|
||||
a non-null target.
|
||||
- **S-9, low: `asOf` is the maximum across all shards**, so a single-shard
|
||||
page advertises another shard's number, and an unrelated write invalidates
|
||||
a live cursor.
|
||||
- **S-10, low: `MiniJsonSchemaValidator` silently ignores** `minimum`,
|
||||
`maximum`, `maxLength`, `maxItems`, `anyOf`, `allOf`, `if/then/else`,
|
||||
`uniqueItems` and `format`. The committed schemas use `minimum` 34 times,
|
||||
including `limit`'s bound. Its `pattern` also runs through .NET `Regex`,
|
||||
whose `$` accepts a trailing newline.
|
||||
- **S-11, low:** the v1 subscribe refuses a hop before running the door and
|
||||
always names field `cites`.
|
||||
- **S-12, low:** the fixture's prose still calls the alias vectors a
|
||||
cross-runtime defect, which the alias port fixed. Rust's port also drops
|
||||
`SchemaVersion` and `CompatibleSchemaIds`, so R-E is two rules agreeing on a
|
||||
subset.
|
||||
- **cargo-mutants cannot run on `cultnet-rs` at all.** `schema_discovery.rs`
|
||||
uses `include_str!("../../../contracts/…")`, which escapes the package, so
|
||||
the tool's copied tree fails its baseline build. **There is no mutation
|
||||
evidence for the Rust half**, and the package's own source shape is why.
|
||||
|
||||
**Self's rulings for fix batch 4, 2026-09-22:**
|
||||
|
||||
- **R-V (S-1). A row's identity is `(schemaId, recordKey)`, never the key
|
||||
alone.** Both runtimes index rows and incoming edges by the pair. `RecordRef`
|
||||
already carries both. Tests: two schemas at one key, in both row orders,
|
||||
give the same answer; `cited` does not match a row of another schema at the
|
||||
same key.
|
||||
- **R-W (S-2 and S-3). The hop behaves identically in both runtimes.** The
|
||||
target check runs **before** any key filtering, and each edge is anchored by
|
||||
its own hop, so a selection carrying both `cites` and `cited` returns both
|
||||
sets. C# adopts Rust's ordering for the first; Rust adopts C#'s per-edge
|
||||
anchoring for the second. Vectors cover both.
|
||||
- **R-X (S-4 and S-10). A message that fails its own schema is a defect.**
|
||||
`cultnet.error.schema.json` declares `routingHint` and every nullable field
|
||||
the way the header schema does. A test decodes real bytes for every code.
|
||||
`MiniJsonSchemaValidator` **refuses a schema containing a keyword it does
|
||||
not implement**, rather than ignoring it, and implements `minimum`,
|
||||
`maximum`, `maxLength` and `maxItems`. Its `pattern` anchors so a trailing
|
||||
newline cannot pass.
|
||||
- **R-Y (S-6).** The cursor's HMAC covers the body as well as the selection,
|
||||
so the position cannot be rewritten. Test: a rewritten ordinal or key is
|
||||
refused.
|
||||
- **R-Z (S-7).** The vectors compare **page bytes**, plus `asOf`, `next` and a
|
||||
refusal's code, and carry a header-projection and a document-projection
|
||||
vector on both sides. Where a field is legitimately per-server, such as a
|
||||
cursor, the vector states that and compares the rest.
|
||||
- **R-AA (S-5).** Triage all 85 survivors by name. The 75 uncovered mutants
|
||||
mean whole regions have no test; write behavioural tests for them. A
|
||||
survivor that is genuinely equivalent gets a one-line reason in the report.
|
||||
- **R-AB. Make `cultnet-rs` self-contained enough to mutate.** Replace the
|
||||
escaping `include_str!` with a build script that stages the contracts into
|
||||
`OUT_DIR`, or another route that keeps every source path inside the package.
|
||||
Prove it by running cargo-mutants on the crate.
|
||||
- **S-8:** delete the dead branch.
|
||||
- **S-9:** `asOf` is the watermark of the shards the selection actually
|
||||
covers.
|
||||
- **S-11:** run the door first, and name the field that is actually set.
|
||||
- **S-12:** correct the fixture's prose, and state Rust's alias reduction
|
||||
where R-E is described.
|
||||
|
||||
**Ledger correction.** Commits 0 and 1 came in at about twice the §14
|
||||
estimate:
|
||||
|
|
|
|||
Loading…
Reference in a new issue