Repository navigation
Refuse casting an optional at the compiler: the leaked-Optional construction wall - #8804
Merged
Merged
Conversation
A cast does not eliminate absence -- it re-types the node and leaves the
value alone -- so `reading as String` over a `NonEmptyStr?` rendered the
WRAPPER, and srv5/srv6 receipts carried
`probed_at=Present { value: 2026-08-20T21:11:38Z }`.
`gunbc.clock_read` `probed_at_word` fixed the fourteen sites and
`test.claim.probed_at_word_witness_test` recorded the honest rung:
mitigatable, because reverting any one of those sites left both witnesses
green. This is that carrier's declared next-rung trigger, built as a
compiler refusal rather than a lens: membership is decidable from the
inferred cardinality alone.
`OptionalCastNotEliminated { source_type, target_type, span }` is fired
from the ExprCast arm of inference (`optional_cast_diags`), beside the
existing sole-constructor and where-refinement cast walls. It refuses a
cast off an optional source for EVERY target, not only the text targets
that made the defect visible, because the loss is the elimination that
never happened rather than the target's spelling; a cast whose target is
itself optional is not this class and is left to `validate_cast`.
Evidence: `test.claim.optional_cast_wall_witness_test` -- RED arm is the
reverted production call site, GREEN arms are the sanctioned
`probed_at_word(reading: now)` form and a cast off a REQUIRED source of
the same type, which is what makes the RED discriminating rather than a
wall that refuses every cast.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The wall's first whole-corpus run reported 30 sites across 11 files, every one of them a cast that re-typed an Optional instead of eliminating it: `fields.first() as String` in the /proc/mounts parse, `required.first()` reaching a sensor name inside converge receipt text, an unset `BMC_HOST` dialled as the literal host `Absent`, and `shell.Env.Get(...).value as Secret` minting a credential whose contents were the word `Absent`. Each site is eliminated with a match rather than silenced. Where a caller has already refused the absent case one line above, the arm is a loud word (`probed_at_word`'s precedent) so the failure can never read as data; where a refusal already exists downstream -- an empty required field, a failing `parse_int`, an existing refusal-detail message -- absence routes into it rather than minting a second refusal beside it. One bound is stated rather than closed: `extdeps.shell.credentials` `env_credential` now yields the EMPTY secret when the variable is unset, which every consumer must reject, but the honest shape is `token: Secret?` carried to the caller -- that changes the pattern's declared surface and `GitHubAuthToken.token` with it, and is a separate change from the wall that surfaced the site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`env_credential` now answers `token: Secret?` and `github_token()` answers a `GitHubAuthTokenRead` coproduct, so `GitHubAuthToken.token` KEEPS its non-optional `Secret`: a value of that type still means a token was actually read, and the refusal names the variable, because "no token" and "the wrong variable" are different problems and only the second is fixable by editing the auth source. This reverses the deferral the previous commit declared, on a measurement rather than a preference. That commit substituted the empty secret and argued "every consumer must reject it" -- an unverified universal. The checkable version is better: `env_credential` has exactly one consumer (`github_token`) and that consumer has ZERO callers, so the empty secret could not reach anything today AND the migration cost is at its global minimum now. Every caller added before the fix would have inherited a silent empty credential -- the same forged-credential class this branch exists to remove, one layer down. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbai-bot Bot
pushed a commit
that referenced
this pull request
Aug 22, 2026
…ually and restore two silent losses
MY OWN SURVEY HAD SEEN THESE AND MY CLASSIFICATION ABSORBED THEM. I reported "41
declarations on main and absent here", explained the bucket by naming four members
(class_b_import_closure_overlay, declared_import_closure_binding,
compile_clean_diagnostic_policy and their witnesses), and let the other thirty-seven
inherit that verdict. A count with four named members explains four; the rest are assumed.
Re-run at declaration grain against current main with the instrument corrected -- an
alias RHS is not a declaration, and qualification is normalized before diffing, which
takes a false 301 down to a real 107 -- and every member enumerated:
95 import machinery the cut deletes by design (12 wholly-absent modules plus
v1.compiler.resolve's 10 import-resolution decls and infer_env's 4 listed-import
predicates), each now individually accounted rather than bucket-explained
3 main's #8788 rust_scalar_checkpoint_* -- arriving in this merge
5 deliberate branch decisions with their own commits (v2.std.collection.List
de-forked as a nickname for std.types.List; ownership_movable retired with a receipt)
2 REAL SILENT LOSSES, restored here:
test.claim.runner_slot_provision.fixture_five_slot_deploy (#8401, ancestor)
test.claim.roadmap_belt_actuate_witness.live_tree_disposition (#8804, ancestor)
2 gunbc.non_fold_residue class_b rows, by design
Both losses came from take-one-side-whole resolutions on commits ALREADY in the branch,
so no merge would ever have re-offered them. Also reverted 141 further in-string
qualifications this pass introduced.
regen: exit 0, first_generation_equal=true, 131 modules.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.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.
The wall found 30 live leaked-Optional casts across 11 files on its first whole-corpus run, and all 30 are eliminated with a
match— none silenced, none exempted, no roster. Among them: an unsetBMC_HOSTwas being dialled at a host whose name was the literal textAbsent;shell.Env.Get(...).value as Secretminted a credential whose contents were the wordAbsent; andrequired.first() as Stringwould have writtenrequired tach health is absent: Present { value: fan0 }into a converge receipt — the identical defect this wall was commissioned for, in a module nobody was looking at.What this is
probed_at_word's declared next-rung trigger, built. A cast does not eliminate absence — it re-types the node and leaves the value alone — soreading as Stringover aNonEmptyStr?rendered the wrapper, and srv5/srv6 receipts carriedprobed_at=Present { value: 2026-08-20T21:11:38Z }. #8377 fixed the fourteen sites;test.claim.probed_at_word_witness_testrecorded the honest rung (mitigatable — reverting any one of those sites left both witnesses green). This makes the reverted spelling not compile.OptionalCastNotEliminated { source_type, target_type, span }is a typed located refusal fired from theExprCastarm of inference (src/v1/04_infer.dagoptional_cast_diags), beside the existing sole-constructor and where-refinement cast walls.The two open questions in the brief
Cast judgment, or the wider "Optional in a text position"? The cast judgment — and wider than text within it: it refuses a cast off an optional source for every target, because the loss is the elimination that never happened, not the target's spelling. A cast whose target is itself optional is not this class and is left to
validate_cast. The corpus settled it: of the 30 live sites the wall found, 6 have a non-text target (NonEmptyStr,Secret), so a text-only wall would have left a fifth of the measured population writable — includingshell.Env.Get(...).value as Secret, which minted a credential whose contents were the wordAbsent. Interpolation and String-typed argument positions are not covered here; they are different sites with different judgments, and that bound is written on theprobed_at_wordcarrier rather than left to be assumed away.Which path? Source →
.dag-acceptance. The refusal sits in inference, so a refused module never reaches emission or interpretation — but that is a consequence of where the wall sits, not a second measurement, and it is not claimed as one. The interpreter'sDisplay for Valuestill rendersPresent { value: … }for optionals reaching text by a route the wall does not judge.The corpus census — 30 live sites, not zero
The brief asked for the number plainly.
Named observation, because sibling lanes running corpus-wide censuses will otherwise each rediscover it:
gunbc compilewith no--entrycannot be used as a corpus census instrument. It refuses withWholeCorpusCompileBudgetBelowMeasuredDemand— both in a session container and on a BuildBuddy runner. That is a refusal, not a measurement: the two numbers it prints (a 7.1 GiB readable budget against 7.5 GiB of measured demand) are one sample under one host's pressure and should not be treated as stable. Locally, before that wall existed, the same run was SIGKILLed in reconcile — which is exactly why the refusal exists, since a count grepped from a killed process is a memorial rather than a census. What does work: CI's own required floor resolves the whole tree (modules_resolved=3813) and prints every typed diagnostic withpath:line:col. That is where this census came from, on the first push:dag/extdeps/filesystem/linux.dagdag/gunbc/bmc_fan_converge.dagdag/test/claim/spark_serving_runtime_install_witness_test.dagdag/test/claim/secret_rotation_witness_test.dagdag/test/claim/commit_check_demand_witness_test.dagdag/extdeps/bmc/openbmc_fan_control.dagdag/gunbc/tools/bmc_first_contact.dagdag/gunbc/systemctl_list_units.dagdag/gunbc/spark/serving_observe.dagdag/gunbc/roadmap_dispatch_actuator.dagdag/extdeps/shell/credentials.dagEvery one is eliminated with a match in the second commit, not silenced. Where the caller already refuses the absent case one line above, the arm is a loud word (
probed_at_word's precedent) so a failure can never read as data; where a refusal already exists downstream — an empty required field, a failingparse_int, an existing refusal-detail message — absence routes into it rather than minting a second refusal beside it. Worth naming individually: an unsetBMC_HOSTwas being dialled as the literal hostAbsent;required.first() as Stringwould have writtenrequired tach health is absent: Present { value: fan0 }into a converge receipt — the identical defect to the one this wall was commissioned for, in a module nobody was looking at.The credential site carries its absence, it does not paper over it.
env_credentialanswerstoken: Secret?, andgithub_token()answers aGitHubAuthTokenReadcoproduct — soGitHubAuthToken.tokenkeeps its non-optionalSecretand a value of that type still means a token was actually read. An earlier revision of this branch substituted the empty secret and deferred the surface change on the reasoning that "every consumer must reject it"; that is an unverified universal and the deferral was reversed on a measurement instead:env_credentialhas exactly one consumer (github_token) and that consumer has zero callers — the three grep hits are prose. So the empty secret could not reach anything today, which is the checkable version of the claim, and the migration cost is at its global minimum now rather than after the first caller inherits a silent empty credential.Rung
Structurally guaranteed on the source →
.dag-acceptance path — the invalid state is refused at acceptance by derived structure, not detected by an enrolled mechanism. Not claimed for any other path: emission and interpretation are untouched by consequence of where the arm sits, and that consequence is not a second measurement.The v1 seed change, classified rather than assumed
First, what the seed diff actually is. The hand-authored change is the
.dagauthority,src/v1/04_infer.dag. The 49 lines insrc/v1/stage0/src/v1_compiler_infer.rsare the emitter's output — installed fromtarget/stage0-regen-candidate, verified byrequired-regen: first_generation_equal=true. No seed Rust was grown by hand: that file changed because the authority changed and the emitter re-derived it, which is the self-host mechanism working rather than someone editing a frozen file.The authority is still in the seed, so the standing carrier
gunbc.v1_maintenance_standingv1_seed_standing(SemanticsFrozen { maintenance: MaintenanceActive }) applies and the classification is owed.Claimed:
SupportsV2SelfHostProgram { instance: DefectRepairDiagnosticsMayMove }.Why that class and not "this is a safety improvement" — which would be growth for its own sake, exactly what the freeze refuses. The seed is not merely missing a check here. Its
ExprCastarm affirmatively stampsinferred: Resolved { node: target_type }on the cast node, so it asserts that the value has the target type when it does not, and the interpreter then rendersPresent { value: … }into a field the type system says is aString. That is a specific incorrect behavior the seed exhibits today, evidenced the wayv1_maintenance_defect_repair_notedemands: 30 live sites and two srv5/srv6 production receipts. The repair moves diagnostics, which is what the class name anticipates. On the governing 2026-08-20 purpose test it also passes on its own terms — v1 is the compiler that compiles the v2 corpus, and every one of the 30 sites is in that corpus, two of them in v2's own witness tests.The counter-reading, stated because I would rather it be argued than discovered:
v1_maintenance_defect_repair_notedraws the line as "the seed FAILING TO DO something it never did isSeedFeatureCompletion, refused", and v1 never performed an optional-cast judgment. Read that way this is a refused class, and refusals dominate every admission. My argument against that reading is the paragraph above — the defect is a wrong assertion the seed makes, not an absent one it omits — and I think it is the better reading, but it is a reading. If the standing's owner or a reviewer lands onSeedFeatureCompletion, refusal dominates and the seed half of this PR needs re-deciding rather than defending; I am not treating my own classification as the verdict (v1_maintenance_rung_noteputs this carrier at mitigatable precisely because the classification is authored, not derived).Evidence
dag/test/claim/optional_cast_wall_witness_test.dag, run on a post-regen binary (claim_batch, all four PASS):w_reverted_probed_at_call_site_is_refused: the reverted production call site (clock_now_probed_at() as String) is refused.w_sanctioned_probed_at_call_site_is_accepted:probed_at_word(reading: now)still compiles.w_required_source_cast_is_accepted: a cast off a required source of the same type still compiles. This is what makes the RED discriminating rather than a wall that refuses every cast.w_optional_cast_to_a_non_text_target_is_refused: the non-text half of the class.All four stay enrolled (§4b(4)); the two
probed_at_wordwitnesses stay enrolled as permanent controls on the rendering authority itself. Its rung note is rewritten to say what changed and what is still writable.Test plan
claim_batch --entry dag/test/claim/optional_cast_wall_witness_test.dag --functions …→ 4/4 PASS.claim_executor --required-regen→ mirror regenerated from the.dagauthority (the hand-written seed patch was replaced by the emitted bytes);cargo fmt --all --checkclean.68e3ffa—parse OK 50 file(s),regen first_generation_equal=true, floormodules_resolved=3813 planned=10253 executed=10253 terminal=10253 passed=9946 failed=0. Zero remaining leaked-Optional casts in the tree.