# Refactor-plan benchmark policy
The reproducible harness lives in `tools/benchmarks`. It gates correctness before latency and keeps four result classes separate: cached legacy validation, JSON parse plus legacy validation, end-to-end validation/fingerprinting, and cached validated fingerprint lookup.
The direct predecessor is public npm `weavatrix-refactor@0.1.5`. Its validator is a useful common-subset baseline, but its `planHash` removes `createdAt` and hashes insertion-order `JSON.stringify` output. That hash is retained only as migration context; it is not byte-equivalent to the Rust JCS contract and is never included in an equal-contract speed claim.
The fingerprint reference is pinned Node `canonicalize@3.0.0`. A correctness run must prove byte-for-byte canonical JSON equality and equal domain-separated SHA-256 digests before any timing is recorded. The end-to-end Node row includes npm validation, canonicalization, and hashing; the Rust row calls `validate_consumer_plan`. Because Rust additionally validates the new evidence/operation contract, that row is descriptive and speed-claim ineligible. Cached digest lookup is measured separately with equal semantics. An unchecked canonicalize/hash-only row is omitted because the crate intentionally exposes no such route. Adversarial validation also checks bounds, path aliases, overlaps, unsafe numbers, and duplicate member names.
## Decode policy
Every Rust path in the harness decodes legacy envelopes into `EditPlan`, which materializes undeclared members. `weavatrix-edit` also offers `DeclaredEditPlan`, a decode that skips them structurally while accepting and rejecting exactly the same documents. It is not used anywhere in this repository, by decision rather than by oversight.
The measured `parse_validate` section is paired against `JSON.parse` plus the npm validator, and `JSON.parse` has no declared-only mode. The valid workloads carry undeclared members at the plan, file, and edit level, so decoding declared-only on the Rust side alone would remove real work from inside the timed region rather than perform it faster. `parse_validate` is also the class where Rust currently loses, so that substitution would flip losing rows without the code getting faster. The `check` gate and the `legacy_validate` cached plan must decode identically to the measured path, and the adversarial `probeLegacy` classifier must decode what the product decodes; changing any one of them alone would leave the measured path ungated or the corpus classified by an instrument the product does not use. The scope note "equal only for the declared valid common subset" scopes which documents both sides accept, not which members either side retains, and is not license to decode less.
The library has the stronger reason. The edit envelope declares only `schemaVersion`, `operation`, `files`, and `completeness`, so the whole v1 annotation set — `createdAt`, `graphRevision`, `completenessProof`, `uncertainReferences`, `notModified`, `warnings`, `followUp`, `syntaxCheck` — arrives as undeclared members. A declared-only decode would erase all of it, and `RefactorPlan::from_text_edit_plan` would return `Ok` with empty evidence, a changed fingerprint, and a vacuous extension budget, reporting nothing. `tests/declared_decode.rs` pins all three consequences.
Publication-quality rows use warmed persistent processes, in-worker timing, seeded interleaving, five warmups, 30 recorded samples, at least 250 ms of adaptive inner loops per sample, raw sample retention, and 95% bootstrap median intervals. A 2x statement is allowed only for an individual row whose conservative `JavaScript p25 / Rust p75` ratio is at least 2.0. There is no universal product ranking, and the stronger validated Rust JCS operation is not labeled equal-cost to the Node canonicalization reference.
Windows and Ubuntu CI execute the short smoke profile solely to verify portability and correctness. Publication performance is run only after source/machine freeze and with no concurrent full benchmark.