Repository navigation
Service config values face the reference judgment: per-field, with the unjudged set typed and counted - #9028
Conversation
…h the unjudged set typed and counted
infer_item sent every service `config { .. }` property to
infer_auth_source_properties, which inferred a property IF ITS NAME WAS
`svc_auth_source` and otherwise returned it unchanged with `diagnostics: []`.
Five of the six fields were therefore never asked a question of any kind.
The headline specimen: `auth: Bearer`. `Bearer` is a declared variant of
`AuthScheme` in dag/std/types.dag, and the files using it already carry
`import std.types { .., AuthScheme }`. 23 occurrences of a correct reference
to a declared, imported coproduct, never once checked — no missing
declaration, no missing import, no grammar question. Only the traversal.
- infer_service_config_properties judges per FIELD, because a config block is
not one kind of thing: `endpoint:` is a module-level `data` reference,
`auth:` a declared variant, `auth_input:` a service-scoped input with no
binder kind, `requests:` an ordinary literal.
- The unjudged set stops being silent. A field the judgment does not yet reach
emits ServiceConfigReferenceJudgmentDeferred — located, carrying its own
next-rung trigger, non-blocking (DESIGN §4b FrontierAccepted). The empty
diagnostic list destroyed the frequency signal; this restores it.
- `retry:` is DELETED at the root — field, accessor, parse arm and 21 corpus
blocks. service_config_retry had zero callers, and neither realization
retries: 05_emit_rust.dag has no occurrence of retry or backoff and the
interpreter's only match is an unrelated doc comment. It was also a §3 layer
inversion — `max_attempts: 3` is our policy authored inside a cited upstream
module. rate_limit is deliberately kept: a published request ceiling is a
fact about the dependency.
- 8 `auth: None` lines deleted. `auth` is already `Node?`, so absence already
means no auth; a NoAuth variant would be a second spelling of one fact.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
Produced by `claim_executor --required-regen` from the modified .dag authority, not hand-edited: the mirror is generated, and hand-adding the variant to make the build pass is the laundering the regen gate exists to refuse. The regen refused exactly where expected and named the three drifted mirrors — v1_compiler_infer.rs, v1_compiler_parse.rs, v1_std_core.rs — which is the gate working, not a defect. The full .dag pipeline ran clean ahead of the emit (frontend 7s, normalize 429ms, reconcile 16s, analyses 384ms, emit 3m), so the authority edits typecheck as well as parse. Bootstrap ordering, since it is why the first CI run could not have been green: the committed seed emits the mirror with the OLD enum, so a diff that adds a CompilerDiagnostic variant AND consumes it from hand-Rust in the same PR must build the seed with the hand-Rust arm stripped, regen, then restore the arm. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
compile_clean_diagnostic_histogram_key matches CompilerDiagnostic exhaustively with no wildcard, twice — once for the class label, once for the subject name. That is the right shape (a new variant must fail to compile HERE rather than silently inherit whichever arm a `_` happened to name), and it duly refused. The subject NAME keys on the config FIELD, not the referenced spelling: the burn-down this histogram feeds is the list of fields still awaiting the reference judgment, so keying on the referenced name would spread one unjudged field across a row per service that happens to use a different word in it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
Three arms now, and the third is what running it produced. `item.properties`
is not a service-config-only channel: 02_parse.dag mints `namespace_root` (a
module-scope String literal) and `sole_constructor` (a Bool literal on a type)
onto the same list. Measured on four extdeps entries with the deferral flipped
to blocking so it would print, the previous revision reported those as DEFERRED
SERVICE CONFIG FIELDS — five `namespace_root` on gcp/iam.dag alone.
Wrong twice: wrong category, and wrong claim, because those values are
parser-minted literals with no authored reference, so no reference judgment is
owed at all. Not-applicable rendered as unjudged is the conflation this
function exists to remove, recreated one layer out inside the fix for it.
service_config_field_judgment is now total over the closed set
service_config_properties mints — FieldJudged | FieldDeferred { trigger } |
FieldCarriesNoReference — so a new service-config field fails to compile HERE
rather than inheriting whichever arm a wildcard named.
ServiceConfigPropertyResult replaces the anonymous record, for a grammatical
reason as well as a modelling one: `=>` followed by `{` opens a BLOCK, so an
anonymous record literal as a match-arm body is a parse error at its first
field colon.
Mirrors regenerated; only v1_compiler_infer.rs drifted.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
…t forced it
infer_property_values (transport properties) and infer_service_config_properties
(service config) both fold one property into a typed node plus its diagnostics.
That is one concept, and it now has one authority: PropertyInferenceResult,
named for the concept rather than for the caller that surfaced it.
WHY IT IS NAMED RATHER THAN ANONYMOUS, two independent reasons:
Grammatically, `=>` followed by `{` opens a BLOCK, so an anonymous record
literal as a match-arm body is a parse error at its first field colon
(located by byte offset, not line).
Structurally, an anonymous record with these exact fields already existed in
infer_property_values. Declaring a named type of the same shape beside it made
the emitter bind the anonymous one to the named type while still emitting
POSITIONAL `.0`/`.1` accessors for it — so a function this diff never touched
stopped compiling. That is action at a distance through structural coincidence,
and it is loud rather than silent, which is the only thing keeping it from
being a miscompile. Two spellings of one shape is the §3 fork; this is what the
fork cost, so both folds now carry the same name rather than one being renamed
around the collision.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
…give one fixture the import the wall now requires
THE FLOOR CAUGHT A DEFECT IN MY OWN CLEANUP. 15 witness failures, 14 of them
`no field 'auth' on type ''` across cache_layer_planner,
hermetic_fixture_realization, reconcile_in_process_cache and
floor_drain_retention.
Cause: the regex that removed `auth: None` matched ANY line in the corpus, and
six of its eight hits were not service config at all — they are `auth` fields of
`CacheInterfaceCatalogFacts` record literals in dag/extdeps/realization/, where
`None` is a legitimate declared variant of that field's type. Restored, all six.
This is the same class as the defect in `infer_service_config_properties` two
commits ago, arriving through my TOOLING instead of my code: a channel whose
denominator is not what its name suggests. `auth:` is not a service-config
spelling, and a line-oriented regex has no way to know which record it sits in.
Both halves of this PR now carry the same lesson.
The two docker deletions stand: those ARE service configs, and `None` is not a
declared AuthScheme variant, so with `svc_auth` judged they would refuse.
THE FIFTEENTH FAILURE, `rest_axes_emit_dual_auth_refuses`, is a true positive of
the new wall rather than collateral. Its fixture is a standalone module string
carrying `auth: Bearer` with no import, so the newly-judged field correctly
refuses an unresolved name and the emit produces no file for the witness to
read. Its three sibling fixtures in the adjacent witness already carry
`import std.types { AuthScheme }`; this one now does too. The witness's subject
— dual auth refusing at emit — is unchanged and can now be reached.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
|
HOLD — do not merge during the #9102 → #8282 window. Computed against #8282's changed-file set: this PR intersects it on 28 file(s), including:
Under the operator's #9059 ruling — "not a category judgment about emission work; it is a direct subject-overlap constraint" — an intersecting PR must not land between the prerequisite (#9102) and the cut cohort (#8282): it alters the cut's conflict set and invalidates its prepared subject. Nothing is wrong with this change and its approvals stand. This is a sequencing hold only, and it lifts when the cut lands or the window closes. Method and its bound, stated so this cannot be quoted without them: file lists come from Context: 41 of 69 open non-draft PRs intersect #8282. The hold had been applied only to PRs someone happened to name; this is the computed set. Two of us have already been caught not applying it to our own PRs. — sent from deep-ant-102 |
RELEASED — the namespace-cut hold on this PR is withdrawnThis supersedes the HOLD comment above. Normal merge policy resumes for this PR. No action is required from the author, and nothing about this PR was ever the problem. Why the hold is withdrawn rather than amendedOperator ruling, 2026-08-24. Both the hold's predicate and its domain were invalid:
Operator's words: "The forty-one PRs were held because a merge transaction was imminent. That transaction no longer exists. The possibility of a future transaction is not a present hold." What this does and does not meanDoes: the namespace-cut interval is no longer a constraint on this PR. Does not: mean this PR must merge. Ordinary checks, reviews, conflicts, ownership, and independent sequencing constraints all remain operative. #8282 itself remains excluded and stays draft. If this PR touches
|
…d discriminator Review 55463 (codex/gpt-5.6-sol) is correct and this is the fix rather than a softened claim. `service_config_field_judgment` was an if/else-if chain over string literals whose else-arm classified ANY unrecognised property as carrying no reference — so a field added to the parser without an arm here routed silently to `diagnostics: []`. That recreates the fail-open this PR exists to close, one level up, and the comment asserted the opposite as a guarantee. It was also the failure mode this PR already documents twice: total at the level examined, blind one level down. The match WAS total over `ServiceConfigFieldJudgment`; the distinction that decides the outcome is the FIELD SET, and that was string-matched with an else. - `ServiceConfigField` is now a coproduct in v1.std.core, and `service_config_properties` derives its minted spellings from `service_config_field_property_name` — so the property names have one authority instead of a copy at the mint and a copy in the judgment. - The judgment matches that coproduct TOTALLY, so a new variant forces an arm both there and in the name function. DECLARED RESIDUE, stated rather than implied: `service_config_known_fields` is a hand-authored roster, so a variant absent from it is unreachable from the judgment and lands in `FieldCarriesNoReference`. Next-rung trigger is a variant-enumeration primitive; until one exists the roster is the single place that must be edited and every other site refuses. Mirrors regenerated; drift was exactly v1_compiler_infer.rs and v1_std_core.rs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EmTGqfFuvRFLcrUBZ3sx6q
|
Review 55463 is correct, and The finding was exactly right. It is also, embarrassingly, the failure mode this PR already documents twice: total at the level examined, blind one level down. The match was total over The fix, taking the "typed property discriminator" option:
Declared residue, stated rather than implied, since the same overclaim twice would be worse than the first: I did not take the "make unknown authored service fields defer" option, and the reason is a measured one rather than a preference: Mirrors regenerated; drift was exactly — sent from merry-wolf-442 |
…t either side #9028 touched cli_run.rs and the expected-red roster, so both sides had edits. cli_run.rs auto-merged; the roster conflicted. RESOLVED BY IDENTITY SETS, not by taking a side. Measured at the three merge stages: base=201, ours=102, theirs=192. Neither side ADDED a row (ours-minus-base = 0, theirs-minus-base = 0), main removed 9, this branch removed 99, and the two removal sets are DISJOINT. The correct result is therefore the intersection, 93 rows -- taking ours would have resurrected main's 9, taking theirs would have resurrected this branch's 99, and both would be a stale exemption readmitted in silence. Built by starting from THEIRS and applying this branch's 99 removals, so main's own edits survive by construction rather than by re-application. All 99 applied, 0 unapplied. THE REBUILD IS GUARDED THIS TIME. It rewrites a Cons chain only when every head in it is a quoted string literal; the chunk AGGREGATOR, whose heads are function calls, is passed through untouched. That guard is the repair for the defect this branch already committed once -- an unscoped rebuild blanked the aggregator to `Empty {}` and the roster evaluated to zero identities. RECOVERED A DROP THE REBUILD CAUSED: starting from theirs silently lost this branch's 29-line annotation recording the 82-row correction, because main never carried it. Re-applied from stage :2. That is the same take-one-side-wholesale failure as the roster itself, in prose, and it was found by checking rather than by review. Verified: 0 conflict markers, 93 rows (= |ours n theirs|), 22 chunk functions, no duplicate heads, every chain line brace-balanced, annotation present, both sides' cli_run.rs changes intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…projection Main advanced (#9028, #9057, #9070) and both sides had edited the recurring-failure- modes paragraph. Two conflicting paths, and they are not peers: dag/gunbc/design_document.dag AUTHORITY -- resolved by hand DESIGN.md PROJECTION -- regenerated, never hand-merged The authority resolution takes main's paragraph and re-applies my one sentence onto it, so the merged line is byte-identical to main's with only the declared-type-position census sentence swapped in -- verified programmatically rather than by eye, and it is the only line in that file differing from origin/main. Nothing of main's was dropped. DESIGN.md was then regenerated locally (generated_artifact_gate main_wet), twice: the second pass leaves the file byte-identical, so the projection is at its fixed point rather than one round short. It was never opened in a merge tool. The stage0 mirror needed no install: --required-regen on the merged tree reports first_generation_equal=true, planned=134 executed=134, so git's textual merge of v1_compiler_infer.rs already equals the emission of the merged authorities. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
main changed src/v1/02_parse.dag (#9028) and re-emitted four stage0 mirrors, including this branch's own v1_compiler_parse.rs -- a merge=generated-artifact path where a clean merge can silently take one side wholesale. Controls taken before the merge say nothing about the tree that ships, so both were re-taken: required-regen: first_generation_equal=true planned=134 executed=134, exit 0 heads-reading-differential: compared=3886 divergent=0 narrowed=0 regressed=0 full 10009 ms / heads 4995 ms = 2.00x The .dag merge was checked line-wise too: all 7 lines this branch deletes relative to main are its own rewires (parse_block -> parse_item_block_body, the occurrence_base signature). None of #9028 was dropped. Denominator moved 3880 -> 3886 (main added modules); the ratio did not. Unblocked by #9133, which retired the four stale frozen_path_deferrals rows. That refusal was never this branch's: both rosters are static declarations and this diff touches neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… a heads reading of the same grammar, 2x on the corpus parse (#9131) * The pool census read every function body to throw it away 82ms later: a heads reading of the same grammar, 2x on the corpus parse `pool_parse` builds full function bodies for all 3875 corpus modules and `census_heads_module_node` replaces every one of them with a shared stand-in 82ms later. The attribution probe measured that as 7.15s of the row's 14.24s and deliberately shipped no repair, naming it this class's next-rung trigger (DESIGN §6, bare minimum cost: a proven cost-shape defect is always fixed). This lands the repair as a READING of the same grammar, not a mode beside it. `ParseContext` carries `heads_only`; `parse_heads_with_table` sets it; a brace-delimited `fn`-shaped body is skipped at token grain, depth-counted over brace SHAPES the tokenizer already decided, and the body slot takes the loud `CensusHeadsBodyStripped` stand-in. All four fn-shaped body sites route through one `parse_item_block_body`, so no call site chooses and the two readings cannot drift apart per site. The census strip is KEPT rather than folded into the parser, and that is the construction move rather than leftover: it normalizes the body slot on BOTH readings, so the stand-in's shape is not a fact any consumer can depend on and the readings can only differ through the HEADS -- which is exactly what the receipt measures. MEASURED (3880 modules, roots dag + src/v2, one process): divergent=0 regressed=0 narrowed=0 both_refused=0 parse wall 12175/5885, 11030/5559, 12882/6382 ms -> 2.07x / 1.98x / 2.02x The ratio is the claim; the absolutes move ~15% on a shared container and are evidence it was measured, not a figure to plan against. It is the PARSE term, not the whole `pool_parse` row -- tokenize and newline indexing are outside both timers and unchanged. RED CONTROL: `depth + 1` -> `depth + 0` in the emitted skip turns 2820 of 3880 modules REGRESSED and exits 1; reverted, rebuilt, re-measured green. SELF-HOST: `--required-regen` first_generation_equal=true over 134 outputs. DECLARED SCOPE NARROWING: the heads reading refuses an unterminated body and every malformed item head, but not a well-braced ungrammatical body. That question is owned by the required run's .dag parse sweep, which full-parses src/v1, dag and src/v2; the census answering it a second time was one fact with two authorities and the expensive one. `narrowed` is a counted column, 0 today, so a future nonzero is visible rather than absorbed. The differential is `claim_executor --heads-reading-differential`, not enrolled in the required run on purpose: reading 3880 modules twice is the cost this removes. Next-rung trigger is a diff-proportional form of the same check. Receipt: docs/probes/heads_reading_of_the_grammar_2026-08-24.md Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * Merge origin/main: re-take both controls on the merged tree main changed src/v1/02_parse.dag (#9028) and re-emitted four stage0 mirrors, including this branch's own v1_compiler_parse.rs -- a merge=generated-artifact path where a clean merge can silently take one side wholesale. Controls taken before the merge say nothing about the tree that ships, so both were re-taken: required-regen: first_generation_equal=true planned=134 executed=134, exit 0 heads-reading-differential: compared=3886 divergent=0 narrowed=0 regressed=0 full 10009 ms / heads 4995 ms = 2.00x The .dag merge was checked line-wise too: all 7 lines this branch deletes relative to main are its own rewires (parse_block -> parse_item_block_body, the occurrence_base signature). None of #9028 was dropped. Denominator moved 3880 -> 3886 (main added modules); the ratio did not. Unblocked by #9133, which retired the four stale frozen_path_deferrals rows. That refusal was never this branch's: both rosters are static declarations and this diff touches neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: gunbc-ci-auto-heal <gunbc-ci-auto-heal@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
What was wrong
infer_itemsent every serviceconfig { .. }property toinfer_auth_source_properties, which inferred a property if its name wassvc_auth_sourceand otherwise returned it unchanged withdiagnostics: [].Five of the six config fields were therefore never asked a question of any kind
— not "does this name resolve", not "does it resolve to one thing", not
anything. The function name declared the narrowing honestly; nothing else did,
and no count existed.
That else-arm is DESIGN §5's absorbing fallback in its quietest form:
not-applicable rendered as nothing-wrong. It reads as "nothing to do for this
property" and means "this property is not judged".
The headline specimen — verifiable in two greps
auth: Bearer.Beareris a declared variant ofAuthSchemeindag/std/types.dag(
Bearer | Header { name } | Basic { username } | ApiKey).import std.types { .., AuthScheme }—extdeps/github/pulls,extdeps/github/gists,extdeps/sec/edgar_rest,extdeps/tailscale/acl_apiall list it explicitly.23 occurrences (21
Bearer, 2Header) of a correct reference to a declared,imported coproduct, never once checked. No missing declaration, no missing
import, no ambiguity, no grammar question — every confounder removed. The
defect is the traversal and nothing else.
What this changes
The judgment is per FIELD, not per block. A config block is not one kind of
thing. Inside a single block:
endpoint: default_api_baseis a module-levelreference to a real
datarow;auth: Bearera declared variant;auth_input: access_tokennames a service-scoped input whose binder kind nothing tracks;requests: 5000an ordinary literal. So "walk the block" and "walk none of it"are wrong for the same reason — both answer at block grain a question decided
per field.
infer_service_config_propertiesjudgessvc_endpoint,svc_authandsvc_auth_source.The unjudged set stops being silent. A field the judgment does not yet reach
emits
ServiceConfigReferenceJudgmentDeferred— typed, located, carrying itsown next-rung trigger, non-blocking (DESIGN §4b
FrontierAccepted: the phaseresult is still
Accepted). The empty diagnostic list destroyed the deficit'sfrequency by construction; this restores it. The variant is added to
cli_run.rs's advisory allowlist deliberately: a non-blocking variant absentfrom that list is counted by neither predicate, which is the
frontier-claiming-to-be-counted failure that file's own comment names.
Two deferrals, each with its trigger stated in the diagnostic:
svc_auth_inputsvc_rate_limitperandscopeare bare identifiers with no declaration; judged once grounded as cited rows in the extdeps module that publishes the limitTwo deletions, and why they are deletions rather than groundings
retry:is deleted at the root — theServiceConfigfield, the parse arm,service_config_properties' parameter, theservice_config_retryaccessor, and21 corpus blocks.
service_config_retryhad no callers anywherein the tree.
05_emit_rust.dagcontains no occurrence ofretryorbackoff.v1_interpreter.rs(16,976 lines, the REST realization) has onematch for either word and it is an unrelated doc comment. Neither
realization retries anything. Nothing in this PR changes that, and nobody
should read a runtime into the carrier's removal.
max_attempts: 3is our policy authored inside a citedupstream module. §3 puts business policy in the workflow layer and names the
tell exactly: a literal carried where a parameter should be received. So
grounding these atoms would have landed a §3 inversion inside the diff
repairing a §3 gap.
rate_limitis deliberately kept: GitHub publishes its 5000/hour corelimit, so a request ceiling is a fact about the dependency and belongs in
extdeps. Its grounding is the deferral row above, not this PR.8
auth: Nonelines deleted.AuthSchemehas noNonevariant, butauthis already
Node?— absence already is the representation for "this servicehas no auth", and six services already demonstrate it. A
NoAuthvariant wouldbe two spellings of one fact (§3) with the arm reading as
not-applicable-rendered-as-a-value.
Ordering: why the traversal is not simply switched on for every field
svc_auth_inputandsvc_rate_limitare deferred rather than judged becauseneither has a referent to resolve to.
auth_input:names a service-scopedinput and the binder authority carries no service/operation parameter kind;
per:andscope:are bare identifiers with no declaration anywhere in thecorpus. Turning the judgment on for those fields today would produce reds with
no repair available to the author — 32 occurrences whose only closing move
would be to quote the word, which is string anemia and is exactly the
substitution this work exists to prevent.
So each is a declared, counted deferral carrying the grounding that closes it.
The three fields whose values ARE already grounded are judged now.
Measured, with the discriminating RED
Instrument:
gunbc compile --entry <file> --target dag, scoped to one module and itstransitive import closure. Every closure carries 52 pre-existing hard diagnostics from
dag/test/manual/command_runner_local_argv_receipt_test.dag(annotation-names-no-subject),excluded by name below so they cannot mask the subject.
On
dag/extdeps/github/checks.dag:auth: Bearer→auth: NotADeclaredSchemeundefined variable 'NotADeclaredScheme' (dag/extdeps/github/checks.dag:3784-3802)52 → 53 → 52, and the delta is exactly one located refusal naming the mutated token. The
svc_authfield is judged now and demonstrably was not before.Two results that settle open questions:
auth: Bearerproduces no diagnostic atbaseline, so all 23
Bearer/Headeroccurrences resolve — the declaration and the importwere both present the whole time and only the judgment was missing. And
endpoint: production_api_base, declared in bothextdeps.ebay.ebayandextdeps.tcgplayer.tcgplayer, compiles clean from both sides with no ambiguity refusal:listed imports bind ahead of the bare-name census, so there is no misresolution here.
An advisory arm measured by a hard-diagnostic instrument reads zero in both worlds
Worth stating generally, because the whole non-blocking population sits in this trap and
this PR adds a sixth member to it.
gunbc compileprints hard diagnostics only. Thedeferral is non-blocking by design. So that instrument reports zero deferrals whether the
arm fires on every service in the corpus or never fires at all — the zero carries no
information, and reading it as "clean" would have been a vacuous green.
The way out: flip the variant to blocking in a scratch build so the same occurrences
become printable, and count those. That is what produced the table below — and what caught
the defect in the section after it.
extdeps/github/checks.dagsvc_rate_limit1extdeps/cloud/gcp/iam.dagsvc_auth_input3,svc_rate_limit1extdeps/ebay/oauth.dagsvc_rate_limit1extdeps/tcgplayer/catalog.dagsvc_auth_input1A related instrument failure from the same night, same family: the first run of this
measurement reported nothing because it piped through
head -40, and the 52 pre-existingdiagnostics consumed the window. A filter that truncates — like a log that buffers — renders
"it did not happen" and "you could not see it happen" identically.
What that measurement caught in this change itself
The counts above also carried
namespace_root(5 ongcp/iam.dag) andsole_constructor,reported by an earlier revision of this function as deferred service config fields.
They are nothing of the sort.
item.propertiesis not a service-config channel.02_parse.dagmintsnamespace_root(amodule-scope
Stringliteral) andsole_constructor(aBoolliteral on a type) onto thesame list the
config { }block writes to. So the arm was wrong in two directions, and thesecond is worse than the miscategorisation: those values are parser-minted literals with
no authored reference in them, so no reference judgment is owed at all. Rendering
not-applicable as unjudged is precisely the conflation this function exists to remove,
recreated one layer out inside the fix for it.
The reviewable question this generalises to — and it is askable without running
anything, which is why it is worth writing down: what else lands on this channel?
item.propertiesreads like "this item's properties" and is in fact a shared mint targetfor at least three unrelated producers. The channel's denominator is not what its name
suggests, and that is true of every
List<Node>side-channel in the tree.Fixed as shape, not filter:
service_config_field_judgmentis a total classificationreturning
FieldJudged | FieldDeferred { trigger } | FieldCarriesNoReferenceover theclosed set
service_config_propertiesmints, so a new service-config field fails to compilethere rather than silently inheriting whichever arm a wildcard happened to name.
The floor caught a defect in this PR's own cleanup
Worth recording, because 14 of 15 failures were mine and the 15th was the change working.
The first floor run reported
failed=15. Fourteen wereno field 'auth' on type ''acrosscache_layer_planner,hermetic_fixture_realization,reconcile_in_process_cacheandfloor_drain_retention— caused by theauth: Noneremoval, which used a line regexthat matched any such line in the corpus. Six of its eight hits were not service config at
all: they are
authfields ofCacheInterfaceCatalogFactsrecord literals underdag/extdeps/realization/, whereNoneis a legitimate declared variant of that field'stype. All six restored; only the two genuine docker service configs remain removed.
That is the same class as the
namespace_rootdefect above — a name is not a denominator —arriving through tooling instead of code. The population had already been enumerated
correctly with a brace-matching parser scoped to
config { }blocks; the edit then used asecond, unscoped mechanism, and the two were never asserted to agree. Two mechanisms over
one population is a second representation of the denominator, and it decays silently at the
seam while both halves look correct in isolation.
The verification that catches it is a structural diff over the whole diff, not a count
over the rows you meant to touch — a census can only count what it can see. Classify every
removed and added line and assert every loss is in the intended population and nothing is
gained unintentionally. Run on this PR:
retryblocks removedauth: Noneremoved (both docker service configs)The fifteenth failure,
rest_axes_emit_dual_auth_refuses, is a true positive of the newwall. Its fixture is a standalone module source string carrying
auth: Bearerwith noimport, so the newly-judged field correctly refuses an unresolved name and the emit produces
no file for the witness to read. Three sibling fixtures in the adjacent witness (
re1d,re1d_header,re3b) already carryimport std.types { AuthScheme }and this one alone didnot — an asymmetry, which is evidence, as against forming an opinion about whether the wall
should have fired. It now carries the same import; the witness's subject is unchanged. The
judgment was not relaxed and the witness was not marked expected-red.
Final state
required-ci: phases_run=4 failed=0, and the floor executed rather than passing vacuously:Mirrors are converged against the authority after merging main.
One hole named rather than covered
FieldCarriesNoReferenceemits nothing, and that is not a silent arm to be closed — it isgenuinely not-applicable, with its reason stated where the arm is. But if a future property
minted onto
Node.propertiesdoes carry an authored reference, it lands in that arm andgoes unjudged. That is a real hole. It is not one this brief covers, and it belongs to
whoever owns the
Node.propertieschannel — the shape is declared insrc/v1/00_core.dagand the non-service producers are minted in
src/v1/02_parse.dag, so the repair sits withthat channel's shape rather than with the service-config judgment.
Widening this diagnostic to cover it would have been the absorbing fallback wearing the
fix's clothes.
Ordering: why the traversal is not simply switched on for every field
svc_auth_inputandsvc_rate_limitare deferred rather than judged becauseneither has a referent to resolve to.
auth_input:names a service-scopedinput and the binder authority carries no service/operation parameter kind;
per:andscope:are bare identifiers with no declaration anywhere in thecorpus. Turning the judgment on for those fields today would produce reds with
no repair available to the author — 32 occurrences whose only closing move
would be to quote the word, which is string anemia and is exactly the
substitution this work exists to prevent.
So each is a declared, counted deferral carrying the grounding that closes it.
The three fields whose values ARE already grounded are judged now.
The deferral must not become the conflation one layer out
A counted deferral is only an improvement if nothing downstream reads it as a
pass. Two things keep that honest here, and a reviewer should check both rather
than take the count as coverage:
pass: nothing has established that '' names anything" — so a reader who
meets one occurrence cannot mistake it for a clean judgment.
that service config references were checked. It asserts that the three judged
fields were. If any consumer's else-arm ever treats "no blocking diagnostic"
as "this position was checked", the deferral has recreated the original
conflation at one remove — harder to see the second time, because the count
exists and looks like coverage.
v1 seed admission
00_core.dag,02_parse.dagand04_infer.dagare v1 seed files under thefreeze. Admitted under the purpose test in
gunbc.v1_maintenance_standingv1_seed_standing: the change serves the v2 self-host program by closing areference-resolution gap in ordinary semantic acceptance. It adds no growth
surface to the seed — it removes one (
retry).Build-surface finding, recorded here so the next lane can find it
Not a fact about this change; a fact about the build surface, and it cost two dead runs
before I understood it. Searchable phrase:
ERROR: Remote run exceeded timeout (45m0s).ctrl-builddispatches BuildBuddy with--timeout ${CTRL_BUILD_TIMEOUT:-45m}. A stage0seed regen (
claim_executor --required-regen) does not fit in 45 minutes, and neitherdoes a whole-tree
compile_clean_diagnostic_histogram. Both of mine were killed mid-run.Pass
ctrl-build --remote --timeout 3hfor anything that emits or compiles the corpus.The reason it costs two runs rather than one is the same conflation this PR is about, one
layer down: the remote log is fully buffered, so nothing streams until the run ends.
"This run is still going" and "this run was killed twenty minutes ago" render identically
for the run's entire life. Print
date -utimestamps between steps instead of pipingthrough
tail, which buffers away the very progress that would distinguish them.Two smaller traps in the same neighbourhood, both self-inflicted here:
top, so
git checkout -- <file>on the runner restores your commit, notmain. Itcannot undo an edit you already committed — which is exactly what the bootstrap ordering
below needs. Strip the hunk by pattern instead.
echo "$(ts) build rc=$?"reports the exit status of the$(...)substitution, not thebuild. A failed build cheerfully printed
rc=0directly aboveBUILD_FAILED.Bootstrap ordering (why the first CI run could not have been green)
This diff adds a
CompilerDiagnosticvariant to the.dagauthority and consumes itfrom hand-written
cli_run.rs. The committedv1_std_core.rsis the generated mirror ofthat authority, so until it is regenerated the hand-Rust references a variant the mirror
does not contain —
error[E0599]: no variant named ServiceConfigReferenceJudgmentDeferred.The fix is a regen, and the regenerated mirrors are part of this PR. The mirror is
generated: hand-adding the variant to it to make the build pass is the laundering the regen
gate exists to refuse, and the next regen would revert it.
Scope held
No
dag/test/claimexpected-red rosters touched. #9020'sNamesNothingKnowndisposition unchanged; the refusal belongs in ordinary semantic acceptance, not
in a workflow preparation helper.