Repository navigation
Track undeclared effect expectations at dispatch sites - #7276
Merged
Merged
Conversation
Found by audit while scoping the three measured gate flips, not by a red. The FalsifierCadenceJob lane shipped with a witness projector and no gate projector, while witness_entries_from_enrollments drops CommitSpecGate rows by construction. So a gate moved to surfaces: [FalsifierCadenceJob] projected into nothing -- it left the per-PR floor and arrived on no lane -- and the bare-deenrollment wall could not catch it, because that wall filters on enrollment_is_witness_claim, which answers false for exactly this row kind. A gate was therefore the one enrollment shape whose de-enrollment was invisible: a one-line surface edit that reads in the diff identically to a legitimate re-home. That is the shape the lane exists to forbid, in the one row kind the lane never covered -- and the next three planned edits were all gates. Three pieces, landing together because any two without the third re-open it: - project_falsifier_gates: the missing gate projector. - gunbc_falsifier_gate_batch: the executing consumer, so the surface is not enrollment-by-illusion. Appends nothing at landing (no gate rides the cadence yet), so the batch chain is byte-identical until a flip lands. - gate_enrollment_is_scheduled / roster_has_no_bare_deenrolled_gate: the wall, with the surface asymmetry reasoned rather than assumed -- the regen job counts for gates (it executes them) and never for witnesses (it does not), so one predicate could not serve both without wronging one kind. Proven discriminating by execution, not by inspection: widening the gate predicate to credit the pre-push hook reds the new RED control (gauntlet_lane_gate_bare_deenrollment_RED_control_holds FAIL), and reverting greens it. Both new claims are enrolled on the roster in the same change -- the file lives under long/, which discovery excludes, so the roster is its only execution path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013GELyMsZrGgZRrxte2TCsk
source_root_ingest (427s) and self_host_realized_comparison (223s) leave the per-PR floor for the 4-hour falsifier cadence. Basis is the [gate-warm-cost] receipt the floor already emits: a real measurement at the grain the placement law requires for a GATE, which is why these could be priced when the 36 CommitWitnessClaim rows still cannot -- the TSV measures gates, and the witness-side blocker is per-ROW receipts. Conflating those grains would be the fabrication the placement law exists to forbid. Both are orders above the 5s fast-lane budget, so Gauntlet is not a preference but the only admissible placement; no signature can make a 427s check fast-lane-eligible. Expected recovery ~650s of the 36.5min PR floor. THE THIRD GATE IS NOT FLIPPED. CheapClaimPoolGate (75s) is a declared member of gunbc_ci_cheap_floor_gate_membership, making it both a DataDependsOn predecessor of the compile root and a floor_resource_anchor whose exclusion from corpus->gate serialization is what that list's own note says avoids a scheduler deadlock. Moving it is a scheduler rewire, not a surface edit, for 10% of the measured prize. It stays per-PR pending an operator call. Three couplings the flip surfaced, each fixed at its authority: - Clamp params are index-aligned and hand-maintained, so two rows had to go. Which two was NOT the obvious answer: verified by execution that emit_host sits at batch index 4 both before and after, so the dead rows are the last two (600s/420s), not the 120s/600s pair the schedule order suggests. The length witness proves the count and cannot prove the mapping -- recorded, because a plausible-but-wrong deletion here is silent. - The source-ingest ordering witness asserted a SCHEDULER property against a spec derived from the live roster, so the flip made it unsatisfiable. Made synthetic instead of deleted, exactly as heavy_pair_spec already does for a deleted gate: dropping it would have bought a planner coverage loss that has nothing to do with where the gate runs. - witness_gate_roster_matches_coproduct_arms bag-equates enrolled gates with every arm of the Gate coproduct -- the construction-shaped "no gate is unenrolled", stronger than the per-row wall because it reds even when no roster row was edited. It went red for exactly the right reason: its universe of executing surfaces was two and there are now three. Extended by projecting the cadence gates into the bag, NOT by dropping the flipped arms or slackening to subset -- either would have silenced the wall in the same motion that made it informative. Extension proven rather than asserted: enrolling EmitHostGate nowhere reds BOTH the totality witness and the new gate wall, and restoring greens both. ci.yml confirmed unaffected (heal produces no diff; its 8 source_root_ingest matches are the discover_source_root_ingest bin name). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013GELyMsZrGgZRrxte2TCsk
Operator ruling 2026-07-25: "we should never have an undeclared expectation; get rid of the fallback." The boundary used to close the gap itself -- declared_expectation.unwrap_or(ExpectSuccess) -- which is textbook absorbing fallback (DESIGN 5). The missing declaration was answered with a plausible one INSIDE the callee, so it left no trace: the frequency of undeclared sites was zero by construction and the deficit could never rank for fixing. eval_service_call now takes ExpectationDeclaration = Declared(ExpectedOutcome) | UntracedDefault, so absence is a value the caller must construct rather than a default the callee manufactures. Per the operator guardrail this is a construction wall, NOT a refusal sweep: UntracedDefault resolves to the same ExpectSuccess -- no behaviour change, no floor-time refusal on sites nobody has traced -- but is typed, located and COUNTED, surfaced as [expectation-frontier]. The frontier is real, not notional: 340 service-op call sites in dag/ + src/v2 against 16 carrying `expect:`, and one ordinary witness run reports 67 undeclared Filesystem.Read dispatches. 324 mechanical annotations are deliberately NOT in this change -- re-arming is evidence-gated, because a speculative ExpectFailure silences a real fault. The three sites that looked like migration debt are not. :5162, :5222 and :7493 are interpreter-OWN seams with no .dag call node to carry `expect:`, so an explicit Declared there is the correct form, not a leftover default. One of them did hide a real finding, recorded rather than fixed: resolve_env_var_token swallows failure into None, the classic outcome-is-data tell -- but its None already means BOTH "variable unset" and "dispatch broke", so re-arming it to OutcomeIsData would trade a crude loud arm for a silent conflation. The fix is a pair (split the consumer's None, then re-arm), which is its own change. Witnessed by a present-and-absent pair, both live on the bin-wet lane: an entry with undeclared effects MUST emit the receipt, one whose effects all declare MUST NOT. Either claim alone is satisfiable by a broken counter -- one that always fires passes the first, one that never fires passes the second -- so neither is dropped. Measured differentially before enrolling: silent on the annotated entry, "1 site(s), 67 dispatch(es)" on the unannotated one. Subject roots are witness_layer_roots, not compile_clean_source_roots: the latter appends src/v1, putting v1.std.core.Node and v2.std.node.Node in one pool, and the subject then dies on ~100 ambiguous-reference errors before dispatching a single effect -- a child that fails to resolve emits no receipt and would red this witness for a reason unrelated to the mechanism. Found by execution, not by reading. Dissolve-on: the counted frontier reaches zero corpus-wide, at which point the UntracedDefault arm deletes and an undeclared site becomes a hard typed refusal -- absence unwritable rather than merely counted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013GELyMsZrGgZRrxte2TCsk
CI run 30188211737 redded "floor resolve count 1 differs from declared 3:
duplicate-computation debt changed". The floor step itself SUCCEEDED
(floor_outcome=success); what fired is the post-floor receipt gate, which
exists precisely to force a conscious update when the debt moves.
This is the flip's benefit surfacing as a receipt. ci_floor_resolve_receipt_note
states the law that predicts it: resolves_total = 1 (the batch-1 whole-tree
materialization) + the entry classes whose closure ESCAPES it. Both flipped
gates were such entries -- SourceRootIngestGate on
floor_source_root_ingest_gate_entry and SelfHostReadsRealBytesGate on
floor_self_host_realized_comparison_gate_entry, each a RunnableSingleClaim on
its own entry file paying its own cold closure resolve. Re-homing them to the
falsifier cadence removed both, leaving the anchor resolve alone.
So the drop is the ratchet direction that note already names ("collapses the
per-entry resolves back toward 1, lowering this row consciously"), and it is a
wall-clock-independent MEASUREMENT of what the flips bought: two whole-tree
cold resolves per PR, gone.
Recorded as Receipt 6 with the distinction that matters kept explicit: the
escape set is empty today by ROSTER, not by construction, so this does NOT
discharge the terminal condition -- the persistent content-keyed store still
owes its own permanent pin at 1. And 1 is now live debt: if either gate returns
to the per-PR surface, or any new escaping entry class enrolls, this gate reds
on the run that does it and the number goes back up consciously, exactly as it
came down here.
ci.yml regenerated via main_wet so the emitted gate compares against 1 (the
drift gate would otherwise red on the stale "-ne 3"), rather than leaving it
for the heal job to push.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013GELyMsZrGgZRrxte2TCsk
…ll-and-gate-gauntlet
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Jul 26, 2026
Main added this wall (#7276) against roadmap_belt_actuate_witnesses / generated_artifact_drift_witnesses after sweep A deleted those aggregators. Point at surviving effect-bearing leaves instead. Co-authored-by: Cursor <cursoragent@cursor.com>
3 tasks
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Jul 26, 2026
Main added this wall (#7276) against roadmap_belt_actuate_witnesses / generated_artifact_drift_witnesses after sweep A deleted those aggregators. Point at surviving effect-bearing leaves instead. Co-authored-by: Cursor <cursoragent@cursor.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Jul 26, 2026
Main added this wall (#7276) against roadmap_belt_actuate_witnesses / generated_artifact_drift_witnesses after sweep A deleted those aggregators. Point at surviving effect-bearing leaves instead. Co-authored-by: Cursor <cursoragent@cursor.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Jul 26, 2026
Main added this wall (#7276) against roadmap_belt_actuate_witnesses / generated_artifact_drift_witnesses after sweep A deleted those aggregators. Point at surviving effect-bearing leaves instead. Co-authored-by: Cursor <cursoragent@cursor.com>
briansrls
pushed a commit
that referenced
this pull request
Jul 26, 2026
* WIP: test umbrella dissolution * Fix regen drift: register hygiene in layout and emit schedule U3 note. rustfmt places test_module_hygiene alphabetically; seed-retained intrinsic registration keeps self-compile from dropping the mod. Co-authored-by: Cursor <cursoragent@cursor.com> * Restore fallback_arm_census falsifier substrate-long-lane enrollment. Review 43070: umbrella dissolution had dropped the five Class-C nightly rows (and exclusion) with no re-home, leaving the long-lane witnesses with zero executing consumers. Co-authored-by: Cursor <cursoragent@cursor.com> * Enrollment: retarget roadmap_belt_actuate bin_wet to file-grain. Umbrella dissolution promoted 37 leaf test fns; the wet roster still named the deleted aggregator, so discovery deferred them with zero wet consumers (UnexecutedDeferredWitness ~38). File-grain empty function expands via expand_explicit_entries — same pattern as other dissolved enrollments. Co-authored-by: Cursor <cursoragent@cursor.com> * Admission: expand file-grain bin_wet keys to leaf test fns. Phase 0(b) matched deferred rows against the unexpanded `entry::` consumer key from `f: ""`, so roadmap_belt's 38 leaf witnesses still looked unexecuted after the wet roster retarget. Expand empty function the same way execution already does (enumerate_entry_test_fns). Co-authored-by: Cursor <cursoragent@cursor.com> * fmt: wrap file-grain admission expand assertion. Co-authored-by: Cursor <cursoragent@cursor.com> * Known-RED: re-enroll english_emit ingest-round-trip leaf. Umbrella dissolution dropped probe_red for english_emit_add_emit_ingest_round_trip_holds and left only prose_holds — the discriminating S2 ingest control named in known_red_frontier_note. Promote the ingest leaf to test fn, enroll it, dissolve the aggregator, and promote the sibling serialize/grammar leaves so they stay live. Co-authored-by: Cursor <cursoragent@cursor.com> * Drop logic_truth_tables_all umbrella with its known-RED probe. Enrollment already removed the aggregator probe_red row; the excluded file still declared the test fn, so Phase 0(b) admission refused an unexecuted deferred witness. Leaves stay the three known-RED controls. Co-authored-by: Cursor <cursoragent@cursor.com> * Align logic_ground_truth exclusion dissolve-on count with three leaves. Co-authored-by: Cursor <cursoragent@cursor.com> * Retarget no_fake_anomalies at a surviving belt leaf. roadmap_belt_actuate_witnesses was dissolved; the anomaly-glyph wall still named the umbrella as its passing corpus entry. Co-authored-by: Cursor <cursoragent@cursor.com> * Fix three discovery reds exposed by umbrella dissolution. Restore the readback roster ratchet at length 5; invert the transport RestNetwork hermetic RED into a green leaf; treat file-grain wet enrollment (function: "") as matching any backing function so emit coverage SelfEmittedNative rows stay backed after f: "" retargets. Co-authored-by: Cursor <cursoragent@cursor.com> * Replace illegal // comments in emit_coverage_frontier.dag. .dag has no slash-comment syntax; the prior note panicked parse (expected expression, found Slash) and failed regen/heal. Co-authored-by: Cursor <cursoragent@cursor.com> * Re-home english_emit serialize/grammar helpers under known-red leaf. matches_serialize and grammar_inverse were wrongly promoted to test fn with no roster consumer (file is OfflineLocalRecipe). Demote to plain fns and force-eval them from english_emit_add_ingest_round_trip_holds, matching main's helper shape without an Offline-orphan pair. Co-authored-by: Cursor <cursoragent@cursor.com> * Restore mock-only commit enrollments for classical_not and native_only. File-grain check_fns: [] pulled wet equals_eval / native-run legs into the per-PR execution corpus; those files intentionally enroll only the mock-shape aggregates (real compile/run stays on the wet nightly lane). Co-authored-by: Cursor <cursoragent@cursor.com> * Retarget expectation_frontier off dissolved umbrella names. Main added this wall (#7276) against roadmap_belt_actuate_witnesses / generated_artifact_drift_witnesses after sweep A deleted those aggregators. Point at surviving effect-bearing leaves instead. Co-authored-by: Cursor <cursoragent@cursor.com> * WIP: test umbrella dissolution * Roster ct_render_rust_applied_type_qualified_base_test scaffold blob. Coverage witness compiler_tests_rust_blobs_are_all_rostered reds when a ct_ blob lands unmarked; this PR's touch pulled that live-tree check into the affected set and exposed the unrostered #7269 render blob. Co-authored-by: Cursor <cursoragent@cursor.com> * WIP: test umbrella dissolution * Make witness-admission scaffold receipt rg-count truthful. Review 43319: bare-token `rg … == 1` was false (header/literal/test multi-hit). Pin the declaration with IDENT+TYPE_ANN split across comment lines so the contiguous pattern matches once — only the const declaration. Co-authored-by: Cursor <cursoragent@cursor.com> * Keep join on meet_join cold roster and ledger (review 43336). Umbrella dissolution left only meet enrolled; join is a peer primary agreement witness and must stay on the native-cache cold falsifier and proactive verification ledger. Co-authored-by: Cursor <cursoragent@cursor.com> * Dedupe ct_render_rust_applied_type scaffold roster row (review 43345). Main already carried two identical declarations and roster slots; this PR had added a third. Keep a single LanguageSourceScaffoldRow and one roster entry for the blob. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Brian Searls <briansearls1@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces
ExpectationDeclarationto make undeclared effect expectations observable and countable at their dispatch sites, replacing silentunwrap_ordefaults with typed, located tracking. This enables prioritization and shrinking of the frontier of sites that have not yet declared their expectation.Key Changes
New type
ExpectationDeclaration: Distinguishes betweenDeclared(ExpectedOutcome)andUntracedDefault, making the absence of a declaration a first-class value rather than a silent default.Expectation frontier tracking: Added thread-local counter
UNTRACED_EXPECTATION_SITESthat recordsservice.op→ dispatch count for every site that dispatches without declaringexpect:. Exposed viauntraced_expectation_frontier()for observability.Updated dispatch boundary:
eval_service_callnow takesExpectationDeclarationinstead of bareExpectedOutcome. The resolution from declaration to outcome happens at the boundary and is counted, making the substitution typed, located, and observable.Interpreter seams annotated: Three internal dispatch sites (
eval_method_call,resolve_env_var_token,eval_filesystem_read_builtin) now explicitly constructExpectationDeclaration::Declared(ExpectedOutcome::ExpectSuccess)with rationale comments explaining why each is notOutcomeIsData.Receipt emission:
claim_batchnow emits[expectation-frontier]to stderr when undeclared sites exist, showing site count, total dispatch count, and per-site breakdown.Test coverage: New witness
expectation_frontier_witness_test.dagvalidates that:CI integration: Updated
commit_workflow.dagto project gates ontoFalsifierCadenceJobsurface and added corresponding gate-half wall predicates (roster_has_no_bare_deenrolled_gate,live_roster_has_no_bare_deenrolled_gate) mirroring the witness-half enforcement.Implementation Details
The change is deliberately not a behavior change:
UntracedDefaultresolves to the sameExpectSuccessthat the priorunwrap_orproduced. What changes is observability — the substitution is now typed, located, and counted, so the frontier is measurable and can be shrunk under evidence rather than remaining invisible by construction.The dissolution condition is corpus-wide: when
untraced_expectation_frontier()returns empty, every effect that ran declared its expectation, and theUntracedDefaultarm can be deleted as a hard typed refusal at the boundary.Per DESIGN §5, this is an absorbing fallback made interim and observable: a site re-arms to
Declaredonly when someone has established what it expects, never speculatively (a wrongExpectFailurewould silence a real fault).https://claude.ai/code/session_013GELyMsZrGgZRrxte2TCsk