Foundation Closure B1b: the services' coupled seat, the empty relation, and the derived watch set - #10
Conversation
…rite one What this does not establish: a private field excludes SIBLINGS, not descendants. `E0451` stops `establish.rs` beside the guard, every other module in the services, and every crate downstream — it does not stop a module declared INSIDE the guard, which would construct as freely as the mint does. So a `#[cfg(test)] mod` under any of these guards would reopen exactly what the guard closes, the six guard files say so at their own declarations, and the reversal that carries the claim is testpak's, from outside the crate, where the exclusion is total. Six services families spelled band 00's coupled seat `report` and spelled it `pub`. The name was accidental: every one of those six field docs already explained the seat using the word "body", the closure struct's own summary line reads "The closure refusal family body", and core spells the same seat `body` in twenty-one places. The name is now the one the prose was already using. The visibility was the defect. The coupled seat keeps a carry and its posture together; it does not, by itself, keep a body and the seam that established it together. A one-field record whose one field is public is a record any holder can write, so a body one pass produced could be seated under a refusal no pass ever raised — and the record would read exactly like one a seam returned. The six seats are private, each home's mint moved to the `type_guard.rs` that is `types.rs`'s own child and can therefore name the seat, and each home exposes one borrowed reader for the consumers that exist: the diagnostics projection, the proof surface, and testpak's own lanes. Borrowed and never owned, for the reason band 00 borrows its carry. The refusal home gains the `type_guard.rs` it previously declared it did not need. Its README said no seat was private and therefore no invariant nucleus existed; one seat is private now, so the nucleus exists, and `establish.rs` — which held nothing but the three mints — is that file under its real name. The reversal: `a-services-refusal-body-reseated-by-literal.rs` builds a real body through the public guarded road and writes it into a second refusal by literal. It refuses with `E0451`. Restoring `pub` on the seat makes trybuild report "Expected test case to fail to compile, but it succeeded", so the fixture is proving the visibility rather than sitting green beside it. Denominator: the coupling gate reads seat TYPES and struct visibility, not field names or field visibility, so it reads 27 coupled / 27 declared before and after — measured on both trees rather than argued. No canonical byte moves: no field name enters any encoding, and the identity golden vectors and testpak's independent transcript lane are untouched and pass unchanged. Deleted: `macros/macroc/src/refusal/establish.rs`; the six `pub` field declarations; the five sibling-module body mints in `establish.rs`/`prove.rs` and the five `refused` helpers that called them across a module boundary; and the three module-doc claims that the body road "lives in type_guard.rs" while it did not. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…l one Unproven by this commit: nothing here stops a LATER road from inventing a third shape for the same state. The claim is closed over the two roads that exist — the deriving one and the single-cause one — and it is closed by a lane that compares them, not by a type that would make a second shape unbuildable. What would carry it structurally is a road whose material cannot be empty, and the one seam that reaches this road holds a `NonEmptyBounded` carry it currently flattens to a `Vec` before handing over; recovering that shape is a change to the diagnostics seam rather than to this mint, and it is not made here. `RelatedSet::derived_over(family, &[])` derived a whole-body commitment over empty material and called the result `Complete`. `RelatedSet::nothing_enumerated` represents the same state — the road looked and there was nothing to enumerate — carrying no identity at all. Two distinguishable values for one state: two diagnostics that both enumerated nothing compared UNEQUAL, and a reader who learned to recognize one shape did not recognize the other. Worse at the identity level, because the derived commitment's preimage is the empty framing, which is the same 32 bytes for every empty set at that family — a name for the state rather than for the refusal. Empty material now routes to the canonical value. It routes rather than refuses, and the reason is the one `PlannedMembership::complete` already records: a refusal here would hand every caller an error branch that the one real caller cannot reach and therefore has no honest value to fill, and a caller with no honest value writes the nearest one. That is how a second representation arrives in the first place. `nothing_enumerated` is documented as what it was already ruled to be: a lawful empty relation, not a missing set, not a set that failed to build, and not a truncation that dropped everything — a truncation names a bound and a non-zero count, and this names neither because neither happened. The seat is testpak's, because this is a behavioural claim about the services and the services cannot judge it: a producer comparing its own two roads agrees for the reason it exists. Two lanes land in the independent file. The population is the DOMAIN rather than a sample — all 256 family tags are asked, so a routing that held for the tag this file happens to use and failed for another is caught here rather than by whoever met the other tag first. The reversal names the value instead of describing it: the lane's own encoder rebuilds the whole-body commitment over empty material from the published grammar, and requires that no road hands it out. Reversal activation, executed: deleting the routing turns both new lanes red — "family 0 answers empty material with a second representation" and "the derived road still carries a whole-body commitment over empty material". The other seven lanes in that file stay GREEN under the same deletion, which is the evidence that only the empty case moved: every non-empty derivation, the truncation posture, the independent re-derivation, and the crafted aliasing case are byte-identical either way. The identity golden vectors and the transcript lane are untouched and pass unchanged; the coupling denominator reads 27 coupled / 27 declared. Deleted: the second representation. No check, no assertion, and no law is added in its place — the road that produced it no longer produces it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Not closed by this commit, and it is a departure from what the docket asked for. The pattern, the instantiation, the two typed arguments and the cited owner facts are still unwatched. Watching them needs a roster seat each, and the trigger roster's cardinality IS the declared invalidation magnitude — `InvalidationLimit` is nine because "one trigger per kind is all that can be watched". Minting a kind per anchor is the mirroring roster the ruling forbids and pushes the set past its own bound; the alternative is one generic dependency-key seat plus a wider magnitude, which adds an identity vocabulary to the plane and moves a declared limit, and a limit moves only behind a realistic positive control and a first-over-limit negative one. That is a fork with an owner, and this commit does not take it. What it does instead is make the gap COUNTED rather than remembered. The defect was not the four triggers. It was that they were a roster written at a call site, standing beside a context declared elsewhere, kept in step by whoever remembered. Two such rosters existed and had already drifted differently — and neither drift was visible from either site. `ProjectionContext::watch_set` derives the shared half from the context's own seats. It destructures the context exhaustively, so the population is read off the declaration rather than listed: sources reach a cause trigger, graph a graph trigger, profile and generator their own, target the contract trigger where one is bound, and `profile_version` is bound aside with the reason — a version is not an identity and every roster seat watches one. `plan_scope_guard_stamp` destructures `ScopeGuardStampAnchors` exhaustively for the same reason, and each of its eleven bindings now says where its anchor reaches the plan: the context to the watch set, pattern and instance and the two arguments to the kind content, the three origin nodes to the trail, the stamped unit to the membership, the traced subject and the owner facts to the decision trace. Two holes closed with seats the roster already declared. The target binding was never watched, so a plan bound to a host contract carried no trigger for the contract it was bound to while `TargetContractChanged` sat unused — invisible because every context in the tree is target-free, and the old control's demo context was one of them. And the watch set is deduplicated: an expansion-time context is decided against the same capture that caused it, so its cause and graph triggers are one trigger, and the derivation home's roster skipped the graph one by hand to avoid stating a kind twice. That knowledge now lives in the derivation instead of at the call site. Reversals, all four executed. Planting a seat on `ProjectionContext` refuses at `planning/anchor.rs` with `E0027: pattern does not mention field`; planting one on `ScopeGuardStampAnchors` refuses the same way at `pattern_stamp/plan.rs` — the completeness claim is the compiler's, and the laws are positive controls beside it. Making the target contribute no trigger reddens `every_context_identity_is_watched` and nothing else. Removing the deduplication reddens `the_watch_set_never_states_one_kind_twice` AND `derive_refusal::the_one_road_closes_before_it_emits`, which is the evidence that the dedup is load-bearing at a production plan site rather than only in a control. Denominators, measured rather than argued. Both existing plan sites produce the same SET as before — the pattern-stamp law still reads four triggers, the derivation law still reads three — so no plan identity moves and no canonical byte moves. Every pre-existing test passes unchanged. The coupling gate reads 27 coupled / 27 declared. The fifth trigger only ever appears for a target-bound context, and the tree has none. Deleted: both hand-written `vec![…]` watch rosters, and the claim in `pattern_stamp/plan.rs` that the watch set "covers the SHARED CONTEXT completely" — which was false for the target binding at the moment it was written. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 88 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (36)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b8f42a1666
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -19,7 +33,7 @@ impl ProjectionPlanning { | |||
| /// refusing never needs an error road of its own. | |||
| pub fn established(issue: ProjectionPlanningIssue) -> Self { | |||
There was a problem hiding this comment.
Restrict refusal constructors to establishing seams
Because this constructor remains pub, any downstream crate can still mint ProjectionPlanning from an arbitrary issue; it can also reconstruct an existing body by cloning the issues exposed through body() and calling the public co_established. The new private field and compile-fail fixture therefore prevent only struct-literal syntax, not the forged or reseated refusal bodies this change claims to make unrepresentable. Make these construction roads crate-restricted and expose only constructors tied to the actual establishing operations.
AGENTS.md reference: AGENTS.md:L89-L97
Useful? React with 👍 / 👎.
| CauseAnchoring::Declarations(declared) => InvalidationTrigger::SourceDeclarationChanged { | ||
| watched: *declared.first(), | ||
| }, |
There was a problem hiding this comment.
Multi-source causes lose invalidation coverage
CauseAnchoring::Declarations can contain more than one source declaration, but this branch emits SourceDeclarationChanged only for declared.first(). A plan built through plan_scope_guard_stamp retains all source identities while invalidating only when the first one changes; changes to later sources can leave a dependent plan appearing current. Represent every declared source in the invalidation set, or reject multi-source contexts until the invalidation contract can do so.
Artifacts
Single-source baseline test output
- Executed the one-source pattern-stamp plan test and recorded that the sole carried source has a matching source-declaration trigger, establishing the baseline.
Two-source invalidation test output
- Executed the two-source pattern-stamp plan test and printed two carried source identities with only the first source-declaration trigger, confirming the omission.
- Captured the complete Rust test source containing the review-only single-source baseline and multi-source reproduction harnesses.
- Captured the executed source excerpt showing the exact multi-source assertions and printed observed values.
Ran code and verified through T-Rex
Prompt To Fix With AI
This is a comment left during a code review.
Path: macros/macroc/src/planning/anchor.rs
Line: 79-81
Comment:
**Multi-source causes lose invalidation coverage**
`CauseAnchoring::Declarations` can contain more than one source declaration, but this branch emits `SourceDeclarationChanged` only for `declared.first()`. A plan built through `plan_scope_guard_stamp` retains all source identities while invalidating only when the first one changes; changes to later sources can leave a dependent plan appearing current. Represent every declared source in the invalidation set, or reject multi-source contexts until the invalidation contract can do so.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Foundation Closure B1b — services-crate type residue. Three ruled repairs against
macros/macroc/, one commit each, branched fromorigin/main(f1f1f08).Lead with what is not closed
Repair 3 departs from the docket, deliberately. The docket asked that
pattern,instance,guard_name,scope_typeandowner_factsstop leaving a cached plan stale. They are still unwatched. Watching them needs a roster seat each, and the trigger roster's cardinality is the declared invalidation magnitude —InvalidationLimit = 9, documented as "the trigger roster's own cardinality, since one trigger per kind is all that can be watched". Minting a kind per anchor is the mirroring roster the ruling forbids and pushes the set past its own bound; the alternative is one generic dependency-key seat plus a wider magnitude, which adds an identity vocabulary to the plane and moves a declared limit — and a limit moves only behind a realistic positive control and a first-over-limit negative one. That fork has an owner and this branch does not take it. What it does instead is make the gap counted by the compiler rather than remembered by prose.Repair 1's exclusion is layered, not total. A private field stops every sibling module and every downstream crate (
E0451); it does not stop a module declared inside the guard. No#[cfg(test)]module stands under any of the six guards, each guard file says so at its own declaration, and the reversal is owned by testpak from outside the crate.Repair 2 is closed by a lane, not by a type. Nothing stops a later road inventing a third shape for "nothing was enumerated". What would carry it structurally is a mint whose material cannot be empty; the one seam that reaches it holds a
NonEmptyBoundedcarry it flattens to aVecfirst, and recovering that shape is a change to the diagnostics seam rather than to this mint.The three repairs
1 — the coupled seat is named
body, and the six seats go private. Six services families spelled band 00'sAdmittedPrefixseatreport, and spelled itpub. The rename follows the prose that was already there; the visibility was the defect — a one-field record whose one field is public is a record any holder can write, so a body one pass produced could be seated under a refusal no pass ever raised. Each home's mint moved into thetype_guard.rsthat istypes.rs's own child and can name the seat; each home exposes one borrowed reader. Therefusal/home gained thetype_guard.rsit had declared it did not need.2 —
derived_over(family, &[])routes to the canonical empty relation. It derived a whole-body commitment over empty material and called itComplete, whilenothing_enumerated()meant the same state with no identities. It routes rather than refuses, for the reasonPlannedMembership::completealready records: a refusal would hand every caller a branch the one real caller cannot reach.3 — the watch set is derived from the context. The defect was not the four triggers; it was that they were a roster written at a call site, standing beside a context declared elsewhere. Two such rosters existed and had already drifted differently.
ProjectionContext::watch_setnow derives the shared half by destructuring the context exhaustively, andplan_scope_guard_stampdestructures its anchors exhaustively. Two holes closed with seats the roster already declared: the target binding was never watched whileTargetContractChangedsat unused, and the set is now deduplicated (an expansion-time context is decided against the same capture that caused it).Reversals, all executed
pubon a body seatProjectionContextE0027atplanning/anchor.rsScopeGuardStampAnchorsE0027atpattern_stamp/plan.rsevery_context_identity_is_watchedred, alonereadme-obligations-joinFAIL, count drops 16→15Denominators — measured on both trees
cargo xtask qualify— all 7 stages hold on acargo cleanbuild of the committed tree.🤖 Generated with Claude Code
Greptile Summary
This change centralizes invalidation-watch derivation and improves target-binding coverage, but plans with multiple declared source causes watch only the first source. A later source can change without invalidating the plan that depends on it, so this should be corrected before merge.
Confidence Score: 4/5
Merge is not safe until multi-source plans invalidate when any contributing source declaration changes.
One verified correctness finding remains: the invalidation derivation omits every declared source after the first.
Files Needing Attention: macros/macroc/src/planning/anchor.rs
What T-Rex did
Prompt To Fix All With AI
Reviews (2): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile