build_provenance() and PROVENANCE_SENTINELS live in subc-client-rs (lib.rs:54). Half the fleet cannot call them, and the half that cannot is precisely the half that hand-constructs ManifestProvenance today.
Raised by Astrocyte during their Tier-1 adoption; the census and source verification below are mine.
Census — measured, not recalled
TRANSPORT-DIRECT cannot call the helper
astrocyte · broca · cerebellum · claustrum · insula
client-rs LINKED can call it
aft · wernicke · thalamus · magic-context · prefrontal
Five and five. Every module in the left column links subc-protocol + subc-transport and never subc-client-rs.
A methodology note, because the first run of this census was wrong. My initial pass resolved repo paths by find $HOME -name <repo> and took the first hit, which landed on ~/.local/share/cortexkit/<repo> — data directories with no Cargo.toml. Claustrum, aft and magic-context came back "no subc dep", which is false for all three. That is the absent-vs-examined collapse this repo already fixed once in fleet-pulse.sh (#57): a repo the classifier never looked at is indistinguishable from one it looked at and found clean. The numbers above are from a re-run scoped to source roots, with unexamined reported as unexamined.
The rails are function-internal, so hand-construction silently skips both
pub fn build_provenance(...) -> ManifestProvenance {
ManifestProvenance {
build_git_sha: normalize_provenance_fact(build_git_sha), // sentinel filter
build_lock_digest: normalize_provenance_fact(build_lock_digest),
wire_crate_version: Some(SUBC_PROTOCOL_CRATE_VERSION.to_string()), // auto-fill
store_schema_version: normalize_provenance_fact(store_schema_version),
}
}
Neither rail is enforced by the type. A transport-direct consumer constructing ManifestProvenance directly gets a struct that compiles, serialises, and passes review — while publishing "unknown" as a fact and whatever wire_crate_version it decided to write.
Both failures are already on the board rather than hypothetical:
The function is already pure
Verified at f52c1309 — build_provenance touches exactly three things:
ManifestProvenance subc-protocol/src/manifest.rs
SUBC_PROTOCOL_CRATE_VERSION subc-protocol/src/lib.rs
normalize_provenance_fact private, local, no deps
Zero client-specific dependencies. It is in subc-client-rs by history, not by requirement.
Proposal
Move build_provenance + PROVENANCE_SENTINELS + normalize_provenance_fact into subc-protocol, re-export from subc-client-rs for source compatibility. No new dependency edges in either direction — everything the function touches already lives in the destination crate.
Astrocyte's framing is the right one: a safety rail in a crate the consumer does not link is a rail that does not exist for them. The five transport-direct modules are not declining to follow the contract; they have no reachable way to follow it.
The alternative — telling five modules to add a client-rs edge to reach one pure function — was already refused once on cost grounds when Insula hit this on 08-26, and refused correctly. Adding a client dependency to a transport-direct consumer to obtain a string-normalisation helper inverts the dependency direction for no benefit.
Interim posture
Astrocyte declares nothing until this is ruled — Unverifiable is honest, whereas a hand-rolled struct is the well-formed-lie shape one level up: not a placeholder inside a field, but a whole declaration that looks compliant and bypassed both rails.
That posture is worth generalising to the other four while this is open. Under the current shape a transport-direct module can only promise to follow the contract; it cannot be held to it by anything the compiler checks.
Venue
Happy to take the PR. It is a move plus a re-export plus a version cascade — mechanical. I would land it after #82 clears rather than stacking a second change on subc-control/subc-protocol while that one is in review.
build_provenance()andPROVENANCE_SENTINELSlive insubc-client-rs(lib.rs:54). Half the fleet cannot call them, and the half that cannot is precisely the half that hand-constructsManifestProvenancetoday.Raised by Astrocyte during their Tier-1 adoption; the census and source verification below are mine.
Census — measured, not recalled
Five and five. Every module in the left column links
subc-protocol+subc-transportand neversubc-client-rs.A methodology note, because the first run of this census was wrong. My initial pass resolved repo paths by
find $HOME -name <repo>and took the first hit, which landed on~/.local/share/cortexkit/<repo>— data directories with noCargo.toml. Claustrum, aft and magic-context came back "no subc dep", which is false for all three. That is the absent-vs-examined collapse this repo already fixed once infleet-pulse.sh(#57): a repo the classifier never looked at is indistinguishable from one it looked at and found clean. The numbers above are from a re-run scoped to source roots, with unexamined reported as unexamined.The rails are function-internal, so hand-construction silently skips both
Neither rail is enforced by the type. A transport-direct consumer constructing
ManifestProvenancedirectly gets a struct that compiles, serialises, and passes review — while publishing"unknown"as a fact and whateverwire_crate_versionit decided to write.Both failures are already on the board rather than hypothetical:
store_schema_version: 1fromRECORD_SCHEMA_VERSION, a wrong-domain fill, by hand-constructing.wire_crate_versiondeclared as0.7.0from a provider crate versus0.13.0fromsubc-protocol. Same field, two vocabularies, silently incomparable. The auto-fill exists exactly to make that unrepresentable, and it only fires for callers of the helper.The function is already pure
Verified at
f52c1309—build_provenancetouches exactly three things:Zero client-specific dependencies. It is in
subc-client-rsby history, not by requirement.Proposal
Move
build_provenance+PROVENANCE_SENTINELS+normalize_provenance_factintosubc-protocol, re-export fromsubc-client-rsfor source compatibility. No new dependency edges in either direction — everything the function touches already lives in the destination crate.Astrocyte's framing is the right one: a safety rail in a crate the consumer does not link is a rail that does not exist for them. The five transport-direct modules are not declining to follow the contract; they have no reachable way to follow it.
The alternative — telling five modules to add a client-rs edge to reach one pure function — was already refused once on cost grounds when Insula hit this on 08-26, and refused correctly. Adding a client dependency to a transport-direct consumer to obtain a string-normalisation helper inverts the dependency direction for no benefit.
Interim posture
Astrocyte declares nothing until this is ruled —
Unverifiableis honest, whereas a hand-rolled struct is the well-formed-lie shape one level up: not a placeholder inside a field, but a whole declaration that looks compliant and bypassed both rails.That posture is worth generalising to the other four while this is open. Under the current shape a transport-direct module can only promise to follow the contract; it cannot be held to it by anything the compiler checks.
Venue
Happy to take the PR. It is a move plus a re-export plus a version cascade — mechanical. I would land it after #82 clears rather than stacking a second change on
subc-control/subc-protocolwhile that one is in review.