Select the credential envelope explicitly, and declare the provenance facts the binary knows - #6
Conversation
… order CredentialGetResponse was #[serde(untagged)] with Direct declared before Wrapped, so a frame carrying both a wrapped result.error and top-level success fields decoded as a SUCCESS and discarded the error — fetch() would return a CredentialToken built from the stray bytes and hand it to a provider call. Claustrum cannot emit that frame today (their outcome is an enum whose variants cannot coexist), but the envelope choice rested on declaration order rather than on a decision, and it failed toward serving a credential. result now wins whenever present; the unwrapped form stays accepted as a documented fallback rather than a coincidence of ordering. Also pins the missing-code frame, which was rejected only because code is non-Option — an accident of the type rather than an intention. Adding #[serde(default)] later now reddens a test instead of silently widening what synapse accepts from the vault. Refs cortexkit#3
provenance was blanket-None with a comment about release scripts not stamping CK_BUILD_* yet. That reasoning holds for the build facts and not for the other two: wire_crate_version is a compile-time constant of the linked subc-protocol crate, and the newest migration this binary carries is a fact a daemon can compare against a store's actual version to spot a stale binary directly. Blanket-None where a field is knowable wastes the field. Uses the SDK helper, which stamps wire_crate_version from the linked crate and maps an absent build fact to field omission rather than to a sentinel string -- a present, well-formed value stops the reader asking. newest_schema_version derives from the migration list rather than restating it: a literal keeps reporting the old number the first time a migration is appended, which is the drift this field exists to catch. The test deliberately does not restate the schema number. Asserting a derived value against the same derivation would make the test agree with the code by construction and pass whatever the migration list said.
There was a problem hiding this comment.
1 issue found across 3 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="crates/synapse-module/src/lib.rs">
<violation number="1" location="crates/synapse-module/src/lib.rs:14046">
P3: The `store_schema_version` assertion only checks that the value parses to a number greater than 0, so it can never catch a regressed derivation that still yields a positive value. With the current single-migration list (version 1), even changing `newest_schema_version()` to `.min()`, `.last()`, or a hardcoded `1` keeps the asserted bound true and this test passes, which contradicts the PR's mutation-testing note that derivation mutations around schema versioning are detectable. This is the only assertion that exercises `newest_schema_version()`, so a broken derivation is effectively unobserved.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| assert!( | ||
| schema_version | ||
| .parse::<u32>() | ||
| .is_ok_and(|version| version > 0), |
There was a problem hiding this comment.
P3: The store_schema_version assertion only checks that the value parses to a number greater than 0, so it can never catch a regressed derivation that still yields a positive value. With the current single-migration list (version 1), even changing newest_schema_version() to .min(), .last(), or a hardcoded 1 keeps the asserted bound true and this test passes, which contradicts the PR's mutation-testing note that derivation mutations around schema versioning are detectable. This is the only assertion that exercises newest_schema_version(), so a broken derivation is effectively unobserved.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/synapse-module/src/lib.rs, line 14046:
<comment>The `store_schema_version` assertion only checks that the value parses to a number greater than 0, so it can never catch a regressed derivation that still yields a positive value. With the current single-migration list (version 1), even changing `newest_schema_version()` to `.min()`, `.last()`, or a hardcoded `1` keeps the asserted bound true and this test passes, which contradicts the PR's mutation-testing note that derivation mutations around schema versioning are detectable. This is the only assertion that exercises `newest_schema_version()`, so a broken derivation is effectively unobserved.</comment>
<file context>
@@ -14002,6 +14010,48 @@ mod tests {
+ assert!(
+ schema_version
+ .parse::<u32>()
+ .is_ok_and(|version| version > 0),
+ "store_schema_version must be a real migration number, got {schema_version:?}"
+ );
</file context>
|
Merged, both commits. Review notes: I mutation-verified the envelope precedence test independently (reintroduced a top-level-first selection; your test failed naming the right invariant) and ran the full battery + clippy + fmt + banlist on the branch — 386 green. The provenance commit is the part I'd single out: declaring wire_crate_version and the derived newest-migration number while leaving build facts absent is the honest-declaration doctrine applied better than our own blanket None was, and the test's refusal to restate the derived value is the right self-discipline. The missing-code pin (type-accident promoted to guarantee) closes residual 1 properly. Thanks — this sets the bar for external contributions here. |
The manifest-side assertion only checks that store_schema_version parses to a positive number, so a regressed derivation that still returns a positive value is invisible there. Verified by mutation: changing newest_schema_version to .min() left all 22 tests in the area green. The fix is not to restate the derivation in the test -- that would make the test agree with the code by construction and pass whatever the function did. It asserts the two bounds the result must satisfy: no migration exceeds it, and it is a version the list actually contains. .last() stays green while the list is sorted, because it then returns the same value as .max(). That is an equivalent mutant rather than a gap, and the first bound catches it as soon as the list is unsorted -- verified, not asserted, by reversing the iteration order. Reported by cubic on #6. The finding was right; its stated reason was not -- the list holds 12 migrations, not one, and .min() survives because 1 is still greater than 0.
Opened at your invitation on #3. Two commits, separable — take either alone.
1.
fix(vault): envelope selection by decision, not by variant order — closes residual 3 of #3CredentialGetResponsewas#[serde(untagged)]withDirectdeclared beforeWrapped. Untagged enums are tried in declaration order, so a frame carrying both a wrappedresult.errorand top-level success fields decoded as a SUCCESS and discarded the error —fetch()would return aCredentialTokenbuilt from the stray bytes and hand it to a provider call.resultnow wins whenever present. The unwrapped form stays accepted, as a documented fallback rather than a coincidence of ordering.Scope, stated honestly: claustrum cannot emit that frame today. Their
GetOutcomeis an enum whose variants cannot coexist, confirmed from their types, so this is a latent hazard rather than a live bug. What made it worth fixing is the direction it fails in — toward serving a credential — and that the envelope choice rested on declaration order rather than on a decision.Also pins the missing-
codeframe. It was already rejected, but only becausecodeis non-Option— an accident of the type rather than an intention. Adding#[serde(default)]later now reddens a test instead of silently widening what synapse accepts from the vault.Three tests: the defect, the fallback, and the missing-
codeframe.2.
feat(manifest): declare provenance facts instead of blanket-Noneprovenance: Nonecarried a comment about release scripts not stampingCK_BUILD_*yet. That reasoning holds for the build facts and not for the other two:wire_crate_versionis a compile-time constant of the linkedsubc-protocolcrate.Uses the SDK helper, which stamps
wire_crate_versionfrom the linked crate and maps an absent build fact to field omission rather than to a sentinel string. Build facts stay absent.newest_schema_version()derives from the migration list rather than restating it — a literal keeps reporting the old number the first time a migration is appended, which is the drift this field exists to catch. It usesmaxrather thanlastso it does not rest on the list happening to stay sorted.The test deliberately does not restate the schema number. Asserting a derived value against the same derivation would make the test agree with the code by construction and pass whatever the migration list said.
Mutation proof
Each test was verified capable of failing, with a positive control asserting the mutation target existed exactly once before editing — a substitution that silently matches nothing is indistinguishable from a test that catches nothing.
Direct-before-WrappedFAIL wrapped_error_wins_over_stray_top_level_success_fields— stray top-level success fields must not outrank a wrapped error; the other four vault tests stayed greenprovenancereverted toNoneFAIL manifest_provenance_declares_only_facts_this_binary_knows— an SDK module always has at least one honest provenance factbuild_provenance(None, None, None))FAIL— a module with a migration list can state its newest migrationwire_crate_versionset to synapse's own crate versionFAIL—left: Some("0.0.0"),right: Some("0.14.0")The vault mutation left the other four tests green, so that test discriminates rather than merely triggering. Every mutation was reverted and the file confirmed byte-identical to its pre-mutation copy.
One control fired for real: a second mutation stacked on an unreverted first was refused by its own positive control (
target not found exactly once (count=0)) instead of running and reporting a meaningless pass.Gate
Run at the pushed sha
adfa363— a gate result is a claim about a tree, not about a diff — inside a network namespace with egress dead by DNS name and raw IP, loopback positive control live before and after:Scope caveat: 4 crates (
synapse-core,synapse-engine-ort,synapse-module,owned-decode-worker). CI runs 6 —synapse-worker-llamapulls a llama.cpp C++ build andsynapse-worker-decodeis macOS-gated, neither reachable here.Build note for reviewers:
masternow requiressubc-protocol0.14.0 forself_signals. This was built against an isolated sibling checkout at that version rather than by moving the shared fleet tree;Cargo.lockis untouched, which is the check that the sibling set was right rather than silently downgrading.Not included
_ => PauseJobis unchanged — contract-backed per the resolution on #3, and claustrum has since published the widening rule that makes it correct.Residuals 1 and 2 of #3 are untouched. They are judgement calls about your error vocabulary rather than mechanical fixes. Residual 1 did stop being hypothetical, though: claustrum deployed
706e257today, socredential.geton a signing-key handle now returnskind_not_gettable— a vault-side wrong-verb condition that synapse still reports to consumers ascredential_config_invalid.Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Fixes vault credential envelope selection so a wrapped error can't be misread as a credential success, and declares the provenance facts synapse actually knows instead of blanket-None.
Bug Fixes
resultkey's presence rather than untagged variant order; claustrum can't emit the mixed frame today, but the old code failed toward serving a credential.codeon a credential error as malformed; it was rejected by the type, not by intent.New Features
wire_crate_versionfrom the linkedsubc-protocolcrate and the newest migration version; build facts stay absent.newest_schema_version()derives from the migration list viamaxso appending a migration can't stale the reported number.Written for commit adfa363. Summary will update on new commits.