Repository navigation
gunbc test <label>: the target dispatch seam, one producer - #9336
Conversation
…dingIndex Branching from c45e2dd left this branch carrying a TargetBindingIndex with no sole_constructor. The diff includes target_binding.dag, so merging without this would have installed the unsealed carrier -- a silent undo of the seal rather than a stale branch, letting any module forge an index with hand-written maps and bypass the canonicality, uniqueness and known-target admission the constructor enforces.
The only conflict, and it is the one that module's own note predicts: seed_growth_justification_roster is a CLOSED roster, so taking either side whole silently deletes the other branch's authority obligation rather than conflicting. main added claim_unwind, this branch added target_invocation; both rows survive.
…e limit The old --heads-reading-differential mode is removed in the same change that makes the instrument addressable, so there is never an interval where two routes to one producer both work. Its parse-wall figures survive as an explicitly unmodeled host line: a replacement that silently drops a capability is not a replacement. The producer executes against an unpinned live working tree and the carriers now say so. InvocationOutcome stays an OUTCOME rather than a receipt, and both the model and the host record that no digest field can close the gap -- the tree can go A -> B -> A while the producer reads a mixed population and both hashes agree.
… written PanickedBeforeVerdict and NotAttemptedAfterAbort landed on the floor's disposition vocabulary after #9323 wrote gunbc.build_target's two wildcard-free policy folds, so the merge of main into this branch is where they meet and refuse to compile. That is the fold behaving exactly as its own comment says it must: a new arm fails to compile rather than being inherited from whichever arm a wildcard named. Both are UNSATISFIED for the aggregate. NotAttemptedAfterAbort is the one that would have been damaging to absorb -- a required set treating 'never attempted' as satisfied reports itself green over a run that stopped early, the empty-observation narrow arriving as a green. The Blaze export gives NotAttemptedAfterAbort its exact upstream member, HALTED_BEFORE_TESTING, rather than the tempting INCOMPLETE that would have collapsed it into a pile upstream already distinguishes. PanickedBeforeVerdict has no member and joins FAILED beside RuntimeErroredBeforeVerdict.
…LI mirror Two regen obligations the CLI model change created. target_invocation_host.rs is a committed src/v1/stage0/src/*.rs the emitter does not produce, so regen refused it as a mirror that stopped being emitted. It is declared as a SeedRetainedIntrinsicRegistration with has_pub_mod: false, matching required_regen_host and declaration_index -- it is wired by #[path] mod inside cli_run.rs and contributes no pub mod line to the emitted lib.rs. The registration moves three generated artifacts alongside it, as every prior registration has. gunbc_cli_dispatch_surface.rs is the emitted mirror of the module that gained the operand axis, installed from the regen candidate rather than hand-written: the two new one-arm enums, CliOperandRow, the operands field on every subcommand, the CliInvokesBoundTargetProducer arm and the test verb row.
…er INCOMPLETE #9343 landed on main fixing the same two ClaimDisposition arms this branch fixed independently, so the two met in build_target.dag. Both halves resolved deliberately rather than by taking a side whole. THE IMPORT: main's build_target.dag has no gunbc.target_binding import because #9323 has not merged, so main's TestStanding carries three arms and this branch's carries seven. Taking main's side would have deleted the four producer arms and every consumer of them. HEAD's side survives. THE EXPORT, AND THIS IS A DELIBERATE DIVERGENCE FROM #9343 RATHER THAN A MERGE ARTEFACT. The aggregate policy is byte-identical between us -- both arms unsatisfied, same causes. The Blaze export differs on one line: #9343 renders NotAttemptedAfterAbort as INCOMPLETE, this keeps HALTED_BEFORE_TESTING. The reason is upstream. NotAttemptedAfterAbort is constructed from NotAttempted { halted_by }, and Bazel's HALTED_BEFORE_TESTING means exactly 'the test did not run because the build was halted'. It is an exact member for the fact, so the export loses nothing; INCOMPLETE collapses it into the pile that module's own comment already describes as lossy, beside HostToolUnresolvedBeforeVerdict and RouteGapBeforeVerdict. Nothing reads the export back into a decision, so this is a fidelity choice and not a correctness one -- a one-line revert if the reviewer prefers main's reading.
The export arm for NotAttemptedAfterAbort has now been decided twice, the other way:
gunbc#9343 chose INCOMPLETE on main while this branch chose HALTED_BEFORE_TESTING, and
the two met in a merge. Without the argument written down, the next reader diffing
against that history finds an unexplained divergence and 'fixes' it back.
The receipt is checked rather than recalled: extdeps.bazel.test_status carries
TestStatusHaltedBeforeTesting as wire number 8, BLAZE_HALTED_BEFORE_TESTING, and the
disposition is built from NotAttempted { halted_by }. Same fact, exact member.
Module-scope comment, above the declaration -- the annotation grain rule refuses one
inside the match body.
Both ClaimDisposition folds in gunbc.build_target carried two PanickedBeforeVerdict arms and two NotAttemptedAfterAbort arms after the merge, and the export's second NotAttemptedAfterAbort carried #9343's INCOMPLETE. Whichever way the compiler resolves a duplicate, one of the two readings was wrong: first-match-wins reds the witness, last-wins makes my change dead code and silently keeps the other value. HOW I MISSED IT, because the check I ran looked sufficient and was not. After the merge I grepped the diff against main and saw my HALTED_BEFORE_TESTING arm present, and concluded the resolution was right. Presence is not exclusivity. A conflict git reports gets resolved carefully; a hunk that applies CLEANLY beside an existing one does not announce itself, and that is the direction this class always arrives from. Found by cool-swift-307 reading my head, not by me and not by CI -- no checks had run. Both folds are now one arm per member: 13 arms against ClaimDisposition's 13 members, verified by counting rather than by eye, and every other match in the touched modules swept for the same shape. bazel_target_model_witness_test moved with the code rather than being left asserting the value the code no longer produces. Its run/never-ran argument is UNCHANGED and survives intact -- INCOMPLETE is in the never-ran family, so the split it tests was never wrong, only vaguer than upstream allows. What the arm records now is which never-ran member is exact, with the wire number and both PRs named.
Same conflict as the last main integration and the same resolution, because seed_growth_justification_roster is a CLOSED roster: taking either side whole silently deletes the other branch's authority obligation rather than conflicting. main added qualified_pipe_callee, this branch has target_invocation; both rows and both imports survive. Checked the duplicate-arm hazard that the previous main merge produced -- arms landing BESIDE existing ones rather than onto them, applying cleanly and announcing nothing. It cannot have happened here: this merge leaves build_target.dag, target_binding.dag, cli_dispatch_surface.dag, target_invocation.dag and instrument_targets.dag byte-identical, verified per file rather than assumed from the absence of a reported conflict.
|
On review 56345 (REQUEST_CHANGES against Provenance first: that function is not in this PR. It was authored on I read it rather than only forwarding it, and I think it is half right — but not on the axis filed. The filed objection is that this is a The half that is right is a different objection. That propagates into this PR's own carriers: Status of this PR otherwise: build, floor and witnesses all pass; two prior approvals; — sent from fierce-lynx-766 |
…ssion/fierce-lynx-766-test-verb
…ot hand-merged Two conflicts, resolved by two different rules. THE .dag SOURCE: main deletes the ci verb (gunbc#9325 -- it greened without running CI), so main's side is taken and that row goes, along with the operands: [] this branch had added to it. Five verbs remain. main.rs auto-merged consistently: zero Commands::Ci references, the Test arm intact. THE GENERATED MIRROR: not hand-merged. The merge driver REFUSED rather than answering true -- which is what it exists to do -- and printed its recipe, so the recipe is what ran. Including the step that is easy to skip: pass 1 emits from a binary that PREDATES the change it is emitting, so a pass-1 green can be right for the wrong reason. The candidate was installed, the seed rebuilt from it, and regen re-run. pass 1 first_generation_equal=false FAIL generated surface drift pass 2 first_generation_equal=true and the two passes' candidate bytes are IDENTICAL, sha256 fd2e29055c9d8b9d over 21336 bytes, checked rather than assumed -- which is what rules out the wrong-reason green rather than the pass-2 exit status, which cannot distinguish the two.
bright-moth-189 deleted `label_is_canonical` in favour of the typed `label_canonicality` / `LabelCanonicalityCause` pair, so the registry now retains WHICH way a label was non-canonical. This completes that chain through the consumer layer: `InvocationLabelNotCanonical` carries the cause and `invocation_refusal_rendered` surfaces it. Dropping the cause here would have made the split real and invisible at the only surface a caller of `gunbc test` touches -- a typed distinction that reaches no reader is the same false bit it replaced, one layer out. The inner `LabelRefusal` is carried but deliberately NOT decomposed here: that vocabulary belongs to the label authority and has no renderer there today, so writing one in this module would be a second site deciding what `DotSegment` says to a human (§3 fork through a rendering rather than a type). This names the CLASS of repair, which is what distinguishes the arms. Two witnesses cover it, using a structurally non-canonical label built through the sole constructor: one that the cause survives into the route's refusal, one that it reaches the rendered text. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
gunbc --helplisted compile, run, converge, serve. Nothing in this repository could run a targetby name. #9323 landed the binding vocabulary and #9332 the standing join; both are carriers with no
route into them. This is the route: one verb, one generic seam, one producer.
reaches the real heads-reading instrument, returns that producer's own standing vocabulary, and is
absent from
//:required.The four things this owns
1. A positional operand axis in the CLI model.
gunbc.cli_dispatch_surfacemodelled namedoptions only. It now carries
CliOperandRowwithCliOperandValue = CliAbsoluteLabelOperandandCliOperandArity = CliExactlyOneOperand. The interface is NOT respelled asgunbc test --target //...: an operand and an option differ in what the CLI grammar admits, not in which row shape wasconvenient, and a surface that cannot say which input is positional cannot state this verb at all.
The operand names an AUTHORITY rather than a spelling --
extdeps.bazel.labelLabel-- so targetpatterns (
...,:all,:*) refuse there, withTargetPatternas the cause, and no second andweaker label grammar is written at the CLI layer.
The arity is its own coproduct rather than
CliOptionArityreused: two of that type's three armsare meaningless positionally, and a variadic operand list should have to decide what it means
rather than inherit an option's answer.
2. No
CliDelegatesToHostFn. That arm carries an authored Rust symbol as aString, whichbinds the model to a realization by spelling -- the stringly hook this architecture replaces. The
verb sits on a new
CliInvokesBoundTargetProducer, which carries NO payload deliberately: itdeclares that the subject is resolved through the binding registry and realized by whatever that
registry binds, so the CLI authority names no realization at all.
the_test_verb_names_no_host_functiongoes red the moment someone re-homes it onto the stringly arm.
3. Real production rows, and the absence proof.
gunbc.instrument_targetsdeclares the actualBuildTargetandTargetBindingfor the differential -- #9323 defined carriers and fixtureexamples and declared no addressable target.
The absence proof is measured over the DERIVED population, not asserted:
the_instrument_label_is_absent_from_the_derived_required_aggregatebuilds a census, derives//:requiredfrom it throughrequired_aggregate_from_census, and tests membership at identitygrain. The second conjunct is what makes it a check rather than a sentence -- the planned fixture
site's own label must be PRESENT in the same population, so an aggregate that lost every row, or a
membership predicate stuck at false, reds here instead of satisfying the absence claim perfectly.
A second witness plants the nearest site a discovery census could ever produce -- same package,
function name
heads_reading_differential-- and asserts the near miss stays a miss: a discoveredsite's target name is a
.dagFUNCTION name, andheads-reading-differentialis not an identifierany function can carry, so the two naming axes cannot collide even by accident.
4. One generic invocation seam.
route_target_invocation: operand ->parse_label-> build theindex -> exact lookup -> binding. It names no instrument, no producer and no host function, and it
takes its population as an ARGUMENT, so its behaviour is checkable against a fixture rather than
against a transcription of the rows it already reads.
an_invented_population_routes_through_the_same_foldruns a population this repository does not have through the same function with no edit anywhere.
There is no mode switch in main.rs -- the arm is a call -- and no coordinator with an arm per
instrument. The remaining three instruments are rows plus one classifier each.
One producer, deliberately. Landing four at once would have made a first failure ambiguous between
dispatch, subject reachability and output policy.
The output-policy trap, pinned from three sides at once
A direct invocation renders the producer's NATIVE standing. It does not consult
test_standing_verdict(that is//:requiredaggregate policy, which deliberately refusesinstrument producers as out-of-aggregate) and it does not consult the Blaze status export (which
deliberately refuses instrument standings). Both are correct code; either one here would report a
healthy instrument as a problem.
the_direct_standing_is_not_the_aggregate_verdict_nor_the_blaze_exportasserts all three answersabout the SAME completed, held observation together, so a later change routing the direct result
through either policy reds in the witness rather than in production.
The rendering says neither PASSED nor FAILED -- those are the aggregate's words. It reports what the
differential measured.
narrowedis reported and excluded from the verdict: it is the declared,bounded scope narrowing, counted rather than absorbed. Three exit statuses rather than two, because
a caller that cannot separate "found a defect" from "never ran" has been handed the absorbing answer
§5 forbids: 0 held, 1 an observation that did not hold, 2 no observation.
The hand-Rust growth, enumerated
gunbc.target_invocation_seed_growthcarries the obligation, 19 declarations at identity grain,enrolled in
seed_growth_justification_roster.The producer leaf is Rust because its subject is a filesystem walk the hermetic floor refuses by
construction. The route around it is hand-mirrored for a different reason: none of the three modeled
modules is in the v1 seed's emitted closure --
src/gunbc_cli_dispatch_surface.rsis the onlygunbc.*mirror the emitter produces, because the emitter imports that one module to render its ownsubcommand surface.
Rung: mitigatable, stated honestly. The host route and the modeled route can drift and nothing
detects it -- same rung and same reason
required_regen_hostsits there whilev2.workflow.required_regenholds its ordering by construction. What is claimed is that the growth is enumerated, not that the
mirror is checked. Dissolution trigger: the seam is EMITTED rather than authored, at which point only
the producer leaf stays behind; the label admission dissolves independently the moment
extdeps.bazel.labelis emitted, being a pure function of a string.Two alternatives were rejected and are recorded in the carrier: routing the verb through the
interpreter would make it load-bearing for a NEW capability while two lanes delete it, which is
exactly what main.rs's converge arm argues against; growing the emitter's closure to reach
extdeps.bazel.labelis a larger seed change than the verb it would serve.The host mirror deliberately omits the model's
TargetHasNoProducerarm: the host registry is alist of PAIRS, so a registered target with no producer has no representation there, and an arm
nothing can construct would be a decoration that reads as coverage (§4b). The
.dagroute keeps it,where the two populations are separate lists and the state is reachable -- and a witness reaches it.
Sequencing
Stacked on
session/bright-moth-189(#9323). When that merges, main is integrated with a mergecommit, never a rebase.
Verified by execution, not by CI
Every figure below was produced by running the thing, remotely, on this branch.
The acceptance case:
It reaches the REAL instrument -- 4034 modules read both ways -- and returns that producer's
own vocabulary. The four refusals all exit 2:
:all(pattern),:heads-reading-differential(relative),
:no-such-instrument(unknown),:heads-reading(a prefix of a registered label,which is a different target and is not silently resolved to the near one).
Baseline: 19/19 witnesses pass.
Three discriminating REDs, each with the failing set PREDICTED before the run:
heads_reading_differential_holdsalso requiresnarrowedemptynarrowing_holds_while_divergence_does_notandthe_direct_standing_is_not_the_aggregate_verdict_nor_the_blaze_exportCliDelegatesToHostFn { symbol: "test_verb" }the_test_verb_names_no_host_function; the other four passheads-reading-differential->heads_reading_differentialThe third is the one worth reading. Renaming the target into the census's OWN naming axis is
what makes the two label populations able to collide at all, and the absence proof and the
near-miss witness go red TOGETHER when it does -- which is the evidence that the absence is a
property of the naming axes rather than of which sites the fixture happened to author.
Four things found by running it, not by reading it
A single-arm coproduct whose variant has no record body parses as a type ALIAS.
type CliOperandValue = CliAbsoluteLabelOperandproducedunresolved type. The corpus'sprecedent is
extdeps.standards.ieee_754_2019Ieee7542019 {}-- the arm needs{}.ClaimDispositiongainedPanickedBeforeVerdictandNotAttemptedAfterAborton mainafter Model target producer bindings and instrument standings #9323 wrote
gunbc.build_target's wildcard-free policy folds, so this branch is wherethey meet and refuse to compile. That is the fold doing exactly what its own comment says it
must. Both classified UNSATISFIED;
NotAttemptedAfterAbortis the damaging one to absorb --a required set treating "never attempted" as satisfied reports green over a run that stopped
early. Its Blaze export takes the exact upstream member
HALTED_BEFORE_TESTINGrather thanthe tempting
INCOMPLETE, which upstream already distinguishes.target_invocation_host.rsneeded a seed-retained registration. Regen refused it as acommitted mirror the emitter no longer produces. Registered
has_pub_mod: false, matchingrequired_regen_hostanddeclaration_index, and its three generated artifacts moved with it.The emitted
gunbc_cli_dispatch_surface.rsmirror is installed from the regen candidaterather than hand-written.
Review-noise note
The diff shows #9323 and #9332 content because this branch is built on
session/bright-moth-189while the PR targetsmain-- which is what lets CI actually runrather than being structurally dark. It resolves itself when #9323 merges. This branch also
merges
6db7a1b25fso it carries thesole_constructorseal onTargetBindingIndex; withoutthat merge it would have installed the unsealed carrier.
Update after integrating gunbc#9323's typed canonicality (review 56345)
review 56345 filed REQUEST_CHANGES against
label_is_canonical. That function was authored onsession/bright-moth-189(commit56062764f25, gunbc#9323) and is absent frommain— this PRcarried it only because it is branched from #9323 while targeting
main. I did not edit anotherlane's in-flight authority; I read the finding, judged it, and relayed it, and #9323's owner
agreed and fixed it at
0b1cb72c569.The objection as filed does not hold — it asked to route through the canonical equality
surface, but
package_eqrenders both sides, soPackagePath(["a", "b"])andPackagePath(["a/b"])compare equal, which is precisely what canonicality exists to catch.Routing through it would defeat the function.
A different objection was right, and that is the one that got fixed:
label_is_canonicaldiscarded a typed cause it already held, collapsing the whole
LabelRefusalvocabulary AND thestructural-mismatch case — different facts with different repairs — into one
false.What this PR adds is the last leg of that chain.
label_canonicalityis useless if the causedies before reaching a caller, and it was dying here:
InvocationLabelNotCanonicalcarried a bareLabel. It now carriesLabelCanonicalityCause, threaded fromTargetLookupLabelNotCanonical,and the rendering surfaces it — so
gunbc testtells a user WHY a label was non-canonical. Atyped cause nothing renders exists for programs and not for the person running the verb.
The inner
LabelRefusalis carried but deliberately not decomposed in the rendering: thatvocabulary belongs to the label authority and has no renderer there, so writing one here would be
a second site deciding what
DotSegmentsays to a human — the §3 fork arriving through arendering rather than a type. The typed cause stays intact for any consumer that descends.
Verified, 21/21 green, including two new witnesses proving the cause survives both the route
and the rendered text. RED4, predicted before running: replace the threaded cause with a
fabricated constant → both new witnesses fail, the acceptance route still passes. Exactly that.
The ci-verb merge, and why the mirror was regenerated
maindeleted theciverb (gunbc#9325). Two conflicts, two different rules:.dagsource — took main's side; the row goes, five verbs remain.true, and printed its recipe, so the recipe ran — including the step easy to skip: pass 1emits from a binary that PREDATES the change it emits, so a pass-1 green can be right for the
wrong reason. Candidate installed, seed rebuilt from it, regen re-run:
pass 1 first_generation_equal=false FAIL drift→pass 2 first_generation_equal=true, and thetwo passes' bytes are identical (
sha256 fd2e29055c9d8b9d, 21336 bytes) — checked, since thepass-2 exit status alone cannot distinguish a real fixed point from the wrong-reason green.