Skip to content

Shadow mismatch paths: port to Go, Rust and Python and land the shared observations, vectors and mutants #243

Closed

Description

@lan17

Agent: tracking issue for the work design/2026-10-24-shadow-mismatch-paths.md plans after its TypeScript reference. On 2026-10-24 the maintainer chose to land all of it in #242, which closes this issue, once he is satisfied with the TypeScript reference.

Ports (Plan step 3, in #242)

Each port follows the TypeScript semantics, with its driver and docs.

  • Go: ShadowPolicy.MismatchLogging, Operation.MismatchDiff, MismatchEntry, the warning fields, the shadowMismatchLogging event and its exporters, config_unknown_field.
  • Rust: ShadowPolicy.mismatch_logging, Operation::mismatch_diff, the warning fields, the event and exporters, config_unknown_field. ShadowPolicy gains a public field, so struct literals that list every field stop compiling; the Rust docs/upgrading.md entry names it. The pull request carries no ! and no breaking-change footer, by the maintainer's choice.
  • Python: shadow={"mismatch_logging": ...}, shadow_mismatch_diff=, the warning fields, the event, config_unknown_field.

With the last port (Plan step 4, in #242)

  • The hand-authored mismatchPathVectors group in formal/catalogs/protocol-vectors.json, mapped in semantic-cases.json as protocol/mismatchPathVectors/*, replayed by every port, and the new CONTRACTS.md row. The TypeScript cases in typescript/test/shadow-log-json.test.ts are written as (cached, source) -> { entries, count } pairs so they can move there unchanged.
  • The observation record: mismatchWarning gains optional mismatchPathsJson, mismatchPathCount, diffJson and loggingOutcome, and a shadowMismatchLogging event kind joins formal/guides/PORTING.md and formal/replay/protocol.schema.json. Every port compares warning events exactly against the shared scenarios, so until every port emits these fields the TypeScript driver records only the seven shared fields (sharedWarningFields in typescript/test/formal/behavior-driver.ts); remove that projection here.
  • Re-encode the logPolicy choices in formal/replay/features.mjs and the fixed scenarios in behavioral-scenarios.json for each diff level and for value, so the corpus exercises both leaves and the shorthand's expansion.
  • Mutants for the built-in differ (the array-length rule, the root cases) and for the hook's fail-closed behavior, in mutations.json (TypeScript and Go) and rust-mutations.json. The catalog requires each mutant to name a semantic case backed by a scheduled Quint check; the differ has no Quint model, so mapping its mutants to the vector group may need the catalog to accept vector-backed cases.
  • Full validation tier on the final head.

Activity

  1. lan17 commented on Oct 24, 2026

    @lan17
    OwnerAuthor

    Agent: two caveats from the review of the TypeScript differ in #242, for whoever designs the mismatchPathVectors group:

    • Clamped sides are rendering text. A diffJson side over 1 KiB becomes that port's clipped native-JSON text of the subvalue, so its member order and escaping follow each port's renderer (V8 puts integer-like keys first, Go's json.Marshal sorts map keys, Python keeps insertion order). C60 keeps exact truncation text as B03/X01, so vectors should assert only that such a side is a string ending in ...[truncated], or avoid clipped sides, rather than pin TypeScript's text.
    • Numbers. The TypeScript walk compares parsed JSON numbers as doubles, so [1.0] against [1] and out-of-range exponents yield no entries, while serde_json::Value distinguishes 1.0 from 1. Vectors should stay within integers of magnitude up to 2^53 and canonical number forms unless the record decides how ports compare numbers.
  2. lan17 commented on Oct 25, 2026

    @lan17
    OwnerAuthor

    Agent: two model notes for Plan step 4, when the logPolicy choices and fixed scenarios are re-encoded for each diff level and for value. The maintainer chose (2026-10-24) not to model warning content or the shadowMismatchLogging metric in Quint; native tests in each port cover them.

    • The generated choices come from dialcache-shadow-conformance.qnt (setLogging, the init codes), so adding them is a model edit, not only a driver fixture change.
    • If a choice pairs an invalid leaf with a valid one beside it (for example a logMismatches: true default under a runtime { diff: "bogus" }), the invariant invalidLoggingCannotBeCaptured fails as written, because value stays on while a config error is recorded. It would need restating per leaf.

    Separately, the model records an invalid-logging config error before the capacity check that drops a job (kernel/shadow.qnt), while TypeScript resolves logging only after that check. No current profile reaches it; it predates this work.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions