Repository navigation
Description
Priority: P2
Area: Updater resolver
Repository / target branch: wavect/semaprax / wavect/v090
Reviewed commit: 5428b0805fa3cf35859c843a53e30c3252ef378c
Review date: 6 October 2026
Failure and impact
The update resolver accepts malformed range comparators and expands a zero-major caret range too broadly. An explicit channel range:^0.0.1 selects release v0.0.9 when both v0.0.1 and v0.0.9 are available. Under the conventional caret compatibility rule, the upper bound is <0.0.2 for that fully specified range.
The same parser accepts range:>=1.2.3.4, range:>=1.bad.3, and range:>=1.2.bad. Invalid components silently become zero, while trailing components are ignored. A mistyped constraint therefore becomes a different valid update policy instead of being rejected.
This affects revision selection for both skill and adapter sources. It does not by itself bypass adapter approval or artifact validation; compatible skill content may be eligible for automatic activation under the already approved policy. The defect is that the resolver does not enforce the operator's written constraint reliably.
Exact source and expected semantics
updates/resolve.rs:89–125 parses optional numeric fields through Option and then calls unwrap_or(0). That conflates omitted fields with malformed fields. It also never checks whether a fourth field remains. The caret branch uses minor + 1 whenever major is zero, including 0.0.patch.
The documented channel contract is the highest stable release satisfying the supplied comparator: HARNESS-UPDATES-V1.md:21–30. The ordinary caret convention advances the leftmost nonzero component; Cargo's primary documentation and examples state this behavior. Even if the project chooses a deliberately narrower range grammar, malformed numeric text and extra components must not silently turn into another policy.
Executed reproduction
The probe links directly against the unchanged semaprax_harness library. It registers two synthetic releases with the production MemoryFetcher, then calls the public Channel::parse and resolve::resolve APIs:
versions.add_release(REPO, "v0.0.1", &"1".repeat(40), false);
versions.add_release(REPO, "v0.0.9", &"9".repeat(40), false);
let channel = Channel::parse("range:^0.0.1").unwrap();
let selected = resolve::resolve(&versions, REPO, &channel, "main").unwrap();Observed:
caret_range=^0.0.1 selected_version=v0.0.9
malformed_range=range:>=1.2.3.4 accepted=true
malformed_range=range:>=1.bad.3 accepted=true
malformed_range=range:>=1.2.bad accepted=true
See repros/updates/repro_updates.rs and evidence/updates-evidence.txt. This was a public-API test with a deterministic in-memory upstream, with no remote release or installation modified. The reproduction asserts the current undesired behavior; it must be converted to expected rejection/selection in the owning tests.
Focused fix
Use a well-tested SemVer range implementation already suitable for the project, or make this small parser strict and completely consume its input. Distinguish an absent numeric component from an invalid one, define the supported partial-version syntax, and implement caret/tilde bounds consistently with the documented grammar. Check bound arithmetic so extremely large valid numeric components cannot panic or wrap when computing an exclusive upper bound.
Keep the existing explicitly supported comma-separated conjunction grammar, stable-release filtering, pinned commits, head resolution, and moved-tag detection. This is not a request to add every npm or Cargo range feature. If an external parser supports a wider grammar, explicitly admit only what Semaprax intends to support and document it.
Acceptance criteria
-
^0.0.1accepts0.0.1and refuses0.0.2/0.0.9; resolution picks the highest actually satisfying release. -
^0.2.3stays below0.3.0, and^1.2.3stays below2.0.0. - Invalid numeric text, empty required fields, extra components, and unsupported suffixes return a bounded
SPX-HPU001diagnostic. - Invalid input is rejected before upstream requests or update-state mutation.
- Document and test partial versions and tilde bounds; preserve supported comparator conjunctions.
- Numeric boundary cases cannot overflow in debug or release builds.
- Prerelease/draft exclusion and exact-commit/head channels continue to satisfy their existing contracts.
Existing issue boundary and implementation handoff
#442 (HN-05) is the feature predecessor. The retrieved backlog contained no issue describing these parser failures. Read AGENTS.md and HARNESS-UPDATES-V1.md; extend the existing resolver/update tests with direct parsing and actual highest-release resolution cases. Keep this separate from transaction serialization: a single-threaded resolver reproduces the bug.
Activity
harness updates: lock state writers and parse ranges strictly
wsdt commentedon Oct 6, 2026
Fixed in a028b4d (pushed to main). Range components are parsed strictly (non-empty digits, no sign/leading zero, fits u64, full consumption) with SPX-HPU001 before any fetch or state write; caret bounds follow the zero-major rule (^0.0.1 <0.0.2, ^0.2.3 <0.3.0, ^1.2.3 <2.0.0, ^0.0 <0.1.0, ^0 <1.0.0), tilde bounds documented; overflowing bounds rejected instead of wrapping. Grammar documented in HARNESS-UPDATES-V1. Evidence: resolve.rs unit tests over every bound and 21 malformed inputs, plus hp_dv15_* integration tests proving bad ranges make no upstream request.