Repository navigation
YAML ingest/emit: bounded-subset correct-meaning contract; retire the action-use line projection - #11730
Conversation
…jection scaffold on its carrier; close two projection holes extdeps.languages.yaml.ingest: - YamlLine table: each line's indent, text and ignorability computed once, read by every parse function (was re-derived several times per line and per level). - yaml_all_digits stops at the first non-digit by branching (the eager && recursed to the end of every plain value -- the operand-demand divergence recorded as realization_arms_diverge_on_whether_the_program_refuses). - block literals collected as a slice (end found by one field test per line). - mapping/sequence loops fetch the line record once per iteration. Measured (claim_batch eval_steps): witnesses.yml 127,947 -> 58,073; fleet-converge.yml 296,572 -> 138,484 (budget 72,300 per claim). gunbc.action_use_admission: - realized_workflow_projection_frontier_rows marks the line projection as the operator-approved scaffold (#11663) with its dissolution trigger, folded by gunbc.census_closure_frontier. - uses-key detection ignores whitespace before the colon (`uses :`), and a quoted key carrying an escape refuses (`"us\u0065s":`) -- the projection cannot decode it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nd the action-use census reads every workflow through it extdeps.languages.yaml is now a bounded-subset reader and writer whose contract is meaning, not text: every document ingest_yaml_source ACCEPTS denotes, under the YAML 1.2.2 core schema, exactly the YamlValue it returns, and every construct outside the declared subset is REFUSED with the line it sits on -- never reinterpreted as a string, never dropped. The writer is the same contract turned around: ingest_yaml_source(emit_yaml(v)) == v, or a refusal naming the path it could not write. WHAT WAS WRONG (source audit on #11663, comment 5743133113; each verified against the code before it was fixed). The reader had no refusal arm -- unrecognized text became a string -- so `{uses: x}` was a string, a single-quoted scalar kept its quotes, `\n` and `\q` were neither decoded nor refused, a quoted key kept its quotes, `key: # c` became the string "# c", a `|` literal lost the line break it clips, and a flow sequence split on the literal ", " (so `[a,b]` was one element and `["a, b", c]` split inside the quotes). The writer filtered a literal's empty lines out, wrote `|` for text with no final break, and wrote the string "123" unquoted, which reads back as an int. WHY THE WITNESSES DID NOT SEE IT: they compared serialize(parse(x)) with serialize(expected), and the round trip was emit(parse(emit(v))) == emit(v). Both compare the WRITER's text, so a loss in the writer masked the same loss in the reader -- the "blank lines are kept" claim could not fail for that reason -- and a type change the writer re-spells identically passed. Rostered as gunbc.recurring_failure_mode bounded_reader_reinterprets_what_it_does_not_model. THE SUBSET is declared in the reader's module header, construct by construct, with its refusals. Accepted: the core schema (null, bool, int in decimal/0o/0x, float with exponent and .inf/.nan, otherwise string); both quote styles on one line, with every escape section 5.7 names; quoted keys; one-line flow sequences and the empty flow mapping; `|`, `|-` and `|+` with auto-detected indentation; block collections at any increasing indentation, compact `- key: value` items, compact nested `- - x`, and a sequence at its key's own column; comments and empty lines between nodes. Refused, each with its own located reason: folded scalars, indentation indicators, anchors, aliases, tags, directives, document markers, explicit `?` keys, flow mappings, nested or multi-line flow collections, multi-line plain and quoted scalars, a comment after a value, a duplicate key, a tab in leading whitespace, a line ending in whitespace, and every character YAML's printable set excludes or this reader cannot tell from whitespace. THE WITNESSES NOW COMPARE DECODED STRUCTURE. test.claim.yaml_ingest_witness compares parse(text) with hand-written values, one conformance claim per construct, each with a red control that must name its line and reason; test.claim.yaml_emit_witness compares parse(emit(v)) with v over strings and multi-line texts built to break plain-versus-quoted and chomping, plus the writer's refusals by path. Four planted reader defects (clip drops its break, unknown escapes pass, duplicate keys admit, plain text never resolves) and three planted writer defects (empty lines filtered, always `|`, digit strings unquoted) each turned their own control red. THE CENSUS READS EVERY EXECUTED FILE THROUGH THE MODELED READER, and the line projection is deleted with its frontier row (gunbc.action_use_admission realized_workflow_action_uses, uses_line_reading, uses_value_text, uses_site_of, realized_workflow_projection_frontier_rows). A use is taken from where GitHub reads one -- jobs.<id>.uses and jobs.<id>.steps[<i>].uses -- so the site is where the key sits, a quoted or escaped `uses` key is decoded and read, and a script line spelled like a `uses:` key is script content. test.claim.action_use_admission_witness carries one claim per workflow file, joined to the directory listing by identity in both directions, so a workflow added without a claim refuses by name. THE BUDGET IS A CONSTRAINT, AND IT IS MET WITHOUT A SECOND READER. The census claim over the largest committed workflow (.github/workflows/fleet-converge.yml, 912 lines) measures 67,692 eval steps against v2.workflow.required_floor's 72,300 per new witness, re-derived by claim_batch on that claim. The reader was 138,483 steps on that file before this change and is ~61,000 after, reading it correctly: a block is split into its entries by marking the line breaks at its own indentation with a sentinel the document is already refused for, so deeper lines and literal bodies are never walked line by line. Three cost-shape defects found on the way are fixed at their owning links: admission admitted every occurrence of a use where the question is per (site, text) (realized_distinct_uses), the manifest join scanned every reading per use (action_manifest_readings_by_producer), and std.types commit_sha_text_holds ran a lambda per character of every 40-character head. THE COMMITTED WORKFLOWS ARE REGENERATED, and the diff is only what the old writer got wrong: 46 `run: |` become `|-` (the scripts carry no final line break) and 11 become `|+` (they end in a blank line the old writer dropped); MALLOC_ARENA_MAX and fetch-depth are quoted, because they are STRINGS in the model and unquoted they read back as ints. A DUPLICATED CONTRACT EPOCH IS NOW THE READER'S REFUSAL, not a count downstream: an ingested mapping cannot hold a key twice, so gunbc.required_ci_epoch_observation RequiredCiEpochDuplicated is deleted as the lower-rung handling the climb obsoletes (DESIGN section 4b), and its control stays, asserting the reader's located refusal. NOT IN THIS COMMIT: gunbc.os_install_emit autoinstall_user_data still calls the writer's old total entry point, so this tree does not typecheck yet. Its refusal has to travel as a typed outcome to the srv3 install-media build input (parent ruling), and #11731 is rewriting that chain now; sleek-ferret-265 and I agreed #11731 lands first and this branch rebases onto it. The PR stays draft until then. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # .github/workflows/fleet-converge.yml # .github/workflows/heal-publish.yml # .github/workflows/witnesses.yml # dag/extdeps/languages/yaml/ingest.dag
…e reader keeps `.inf`/`.nan` numeric WHY THIS FOLLOWS THE READER/WRITER CHANGE. emit_yaml refuses a value it cannot write with its meaning, and gunbc.os_install_emit autoinstall_user_data renders cloud-init's user-data -- bytes that are written into the seeded ISO's NoCloud seed AND hashed into the media's identity. So the refusal has nowhere to be absorbed: a document the writer will not write leaves no media to name (parent ruling on #11730, arm A: carry it as a typed outcome to the consumers). WHAT NOW CARRIES IT - gunbc.os_install_emit autoinstall_user_data returns AutoinstallUserData = Rendered | Refused, the refusal naming the value's path through yaml_emit_refusal_text. - gunbc.srv3_seeded_install_media_artifact: srv3_seeded_install_media is Srv3SeededInstallMedia = Derived { artifact } | Refused { reason }. The body-dependent rows (the user-data body, the content hash, the artifact row) live INSIDE the derived arm, because each is a function of the rendered bytes. - THE INSTALL PATH STAYS TOTAL, and that is a modelling fix rather than a workaround: the seeded media's file name is made of the stock point release and the host it seeds, neither of which is seeded content. extdeps.provisioning.ubuntu_seeded_install_media now names those two facts (ubuntu_seeded_install_media_install_path_for / _filename_for) and the row-taking spellings are derived from them, so srv3's install path -- which the NBD serve intents, the actuator scope and the diagnostic all read -- no longer depends on a derivation that can refuse. - gunbc.host_effect_realize refuses the remaster into NotConverged with the derivation's reason; gunbc.srv3_os_install_diagnostic exposes srv3_os_install_attempt() = Intended { intent } | Unavailable { reason } over srv3_os_install_attempt_intent_for, so no intent names the content hash of something that was never written; gunbc.generated_artifact_emit routes the refusal into ArtifactGenerationRefused, which it already had. THE DIAGNOSTIC WITNESS SUPPLIES ITS HASH AND INTENT AT THE BOUNDARY. Its claims discriminate the diagnostic fold over an observation, not the media's derivation, so deriving the pair per claim would re-execute the renderer and the hash once per claim (DESIGN section 3's standing rule). The pairing obligation is one claim that runs the REAL route -- the_production_attempt_intent_carries_the_derived_media_hash -- joining the production derivation's hash to the production intent's, and asserting the rendered body is a cloud-config. A DEFECT MY OWN CONFORMANCE CLAIM CAUGHT, worth recording because it is this change's own failure class. While cutting the reader's cost I let the numeric fast path skip the core-schema word map -- but `.inf`, `.nan` and their signed spellings START with numeric characters, so `.NaN` came back a string: a bounded reader reinterpreting what it does model. core_schema_resolves_null_bool_int_float went red on the spot. yaml_resolve_numeric_grammar now consults the word map on its miss path, and the word map stays the single authority for those spellings. MEASURED AFTER MERGING main, whose fleet-converge authority grew the largest committed workflow from 912 to 973 lines: the census claim over it is 70,897 eval steps against the 72,300 new-witness budget (claim_batch on test.claim.action_use_admission_witness). The margin is 1,403 steps, about 2%, and it is honest to say that is thin: the next growth of that workflow's authority will need another cut at the reader's per-entry cost, which is where 619 of those entries are paid. Cuts that landed here: chunking a block without the sentinel pass when it holds no deeper line, reading a nested `key:` before splitting the line, one indentation test per block instead of two, and screening a literal's blank lines instead of counting them per line. main's `first`/`last` now return an optional (#11626), which the reader reads as `join(xs.take(n: 1), "")` -- that element or "" -- rather than unwrapping an optional per line. main's new gunbc.compiler_gate_workflow, which now emits witnesses.yml, routes the writer's refusal like the other generators; the census claims name the actions each file actually carries after that move (checkout and setup-rust-toolchain in witnesses.yml, upload-artifact in heal.yml). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…plan records the round-trip oracle names The claim over the largest committed workflow sits a low single-digit percentage under the floor's new-witness budget, and its subject -- a generated workflow -- grows from authorities other lanes edit. That is a standing fact about the claim, not a fact about this change, so it is recorded on gunbc.action_use_admission realized_census_cost_standing with the instrument that re-derives it, the remedy (cut the reader's per-entry cost, where the ~619-unit largest file spends it), and the remedies that are refused: a raised budget, a second reader, a declared cost drop, or dropping a file from the census. A witness joins the row's subject to a file the census actually reads. gunbc.plans.emission_ingestion_inverse said yaml was emit-only and failed the round-trip oracle. It no longer is: the reader reads a declared bounded subset, the writer decides its shapes through that reader's own predicates, and test.claim.yaml_emit_witness holds ingest(emit(v)) == v over decoded values. The record now says so, and says what it still does not buy -- the writer is a shared reading of the subset, not a row-driven inverse. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # .github/workflows/fleet-converge.yml # .github/workflows/witnesses.yml # dag/gunbc/witness/compiler_gate_workflow.dag
# Conflicts: # .github/workflows/heal-publish.yml # .github/workflows/heal.yml # .github/workflows/witnesses.yml # dag/gunbc/witness/compiler_gate_workflow.dag
main's #11791 grew .github/workflows/fleet-converge.yml, and the census claim over it measured 74,002 eval steps against the new-witness budget of 72,300 -- over. gunbc.action_use_admission realized_census_cost_standing names the only admissible remedy for exactly this, and refuses the others: cut the reader's per-entry cost, never raise the budget, add a second reader, or declare a drop. This is that cut. It is 74,002 -> 70,436, measured per step by claim_batch over the same claim. WHERE THE COST WAS, MEASURED RATHER THAN GUESSED. A scratch probe parsed documents of one repeated shape each and divided: a one-line `key: value` entry costs about 87 eval steps, a two-line step mapping under a sequence about 243. The committed workflows are mostly step lists, so the sequence and multi-line paths carry the claim, not the one-line path. Two of the four cuts below were aimed at the one-line path before that probe ran and returned 1,828 and 30 steps respectively; the probe is why the last two went where they did. The cuts, in the order they were measured: - ONE MAP LOOKUP FOR THE KEY'S START, replacing a scan of the refused starts plus an unconditional core-schema word lookup. Only the six starts a core-schema word can begin with consult the word map, and the value half is not computed until the entry is known to have one `: `. - NESTED IFS WHERE THE TESTS WERE CONJOINED. `&&` and `||` evaluate both sides, so `yaml_entry_multiline` paid all five of its tests to learn what the first already decided -- on every literal block and every sequence item. The order is now cheapest-and-most-selective first. (The eager-`&&` question itself belongs to gunbc.recurring_failure_mode realization_arms_diverge_on_whether_the_program_refuses; this module only stops depending on short-circuiting it does not have.) - ONE PASS INSTEAD OF THREE over each block's entries and items. Both block folds built an intermediate list of results that the accepted path -- every chunk of every committed workflow -- never inspected, so that the refusal walk could be handed it. The refusal walks now re-read the chunk they are about to refuse on, and a refused sequence item is detected the way a refused mapping entry already was: from the join sentinel it produces, which no accepted scalar can hold because the reader refuses U+0001 anywhere in a document. The contract is unchanged and is still established by execution: the 24 ingest conformance claims, the 9 emit round-trip claims and all 33 census claims pass, each red control still red. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The row named the one-line entry path as where the next cut goes. A probe over documents of one repeated shape each says otherwise: the committed workflows are step lists, and a two-line step mapping under a sequence costs about 243 eval steps against about 87 for a one-line entry, so the sequence and multi-line paths carry the claim. An authority that points a future reader at the wrong path is worse than one that says nothing, so the row now names the measured paths and tells the next author to re-measure rather than trust the sentence. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rkaround at it Review 69056 approved this PR and named one thing worth fixing: the sixteen nested `replace` calls in std.types commit_sha_text_holds route around a language-layer cost -- one interpreter evaluation per lambda in `all(chars(...))` -- in the most-shared type module, with no row anywhere. That is DESIGN section 4b(2): a class below its ceiling with no named trigger, so it never ranks for climbing. The row is its own class rather than a receipt on nested_membership_scan_where_a_map_primitive_exists, because that class is a QUADRATIC shape repaired by a better data structure. Here both forms are linear and the program's shape is already correct: only the evaluator's constant differs, so there is no algorithmic defect to point at and the repair is in the evaluator, not the module. The row carries a second instance from this same change -- the `&&` chain in the YAML reader re-authored as nested ifs -- which is what makes it a class rather than an incident, and its trigger names the capability that returns commit_sha_text_holds to the form that says what it means. The declaration now names the row instead of only explaining its own cost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he instrument Review 69075: the cost standing carried "about 87 eval steps" and "about 243" in a NonEmptyStr data row and in its comment, twelve lines after the same row asserts that the numbers are not transcribed because DESIGN section 6 says to name the instrument. That is the section's own failure mode, and the reviewer is right that it makes the row's advice rot the moment the reader changes. The figures are gone from both places. Deleting them alone would have left the remedy unable to say WHICH path to cut, which is the thing the row exists to say -- so the probe that produced them is now a committed instrument, test.probe.yaml_shape_cost_probe, and the remedy names it and says to run it before cutting. The probe parses documents of one repeated shape each, so the difference between two readings says which path a corpus of that shape spends in. That instruction is not ceremony: the first two cuts made against an unmeasured guess about which path carried the cost returned almost nothing, and this probe is what redirected them. The probe also records the trap that caught its own first version -- repeated keys are a duplicate, so a naive generator measures a refusal rather than a read. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Both findings in review 69075 are correct. One is fixed; the other is escalated rather than decided by me, which is the point the review makes. Transcribed figures — fixed in
|
| attempt | result |
|---|---|
| key start-class map + hoisted value split | −1,828 |
nested ifs for eager && on the multi-line path |
−883 |
| one pass instead of three in both block folds | −825 |
| short-circuiting chunk/item/value predicates | −30 |
scan_to_eol for first-line extraction |
not callable — contract row in std.primitives, no interpreter declaration |
index_by for the key map |
not bound for List<YamlKeyValue> (the known index_by registry algebra gap) |
consolidating yaml_entry_general onto shared helpers |
+1,135 — reverted |
The last row is the useful one: an interpreted call costs its arguments and its body, so factoring a hot path into shared helpers loses. That is why the two key predicates in the reader deliberately do not call each other.
Measured, reproduced, against the 72,300 new-witness budget: 70,436 with the std.types spelling, 73,754 without it — over by 1,454. The spelling is worth 3,318 steps at two different merge bases.
I also corrected a figure of my own: the PR body said 69,496. That reading was taken at an earlier merge base and I could not reproduce it; the reproducible pair is the one above, and I will fix the body.
One hypothesis falsified cleanly, since it is the interesting part. I expected the eager && in commit_sha_text_holds — head.length() == 40 && all(chars(...)) — to be walking forty characters for every @v4 tag. Guarding it with a nested if changed the figure by zero: every ref this census validates is already forty hex, because the admission requires commit pinning. The per-character walk is genuine work, not waste, so there is no free repair hiding there.
What remains is restructuring so a sequence item stops re-entering the full block setup for a two-line compact mapping. That is a real change to extdeps.languages.yaml.ingest with a yield I cannot bound in advance, and it deserves its own PR and review rather than being appended here.
So the choice — admit the std.types spelling externally with the filed row as its trigger, or hold this PR until that restructuring lands — is escalated to my parent. Per §5 the admission is not mine to grant in the diff that needs it, and if it is granted I will record on the carrier who granted it rather than leave the row reading as self-authorised.
Separate, and not part of that question
On the last green head the floor lane printed required-ci: FAILED PHASE floor refused ... ChangedWitnessObservationFailed with phases_failed=1, and step 8 still exited 0 — so the job and the aggregating lane both reported success. The cause is that editing dag/std/types.dag makes the base side unreconstructable, so the changed-witness sublane refuses to plan. The consequence worth noting here: on that head CI was not judging the budget claim this PR is about. I will confirm it reproduces before filing it as its own finding.
— sent from still-lynx-398
…an external ruling Review 69075 refused this correctly: the diff landed a respelling of std.types commit_sha_text_holds AND wrote the row admitting it, which DESIGN section 5 separates -- an author who can write a scaffold can equally write a row claiming it was approved. The admission is now external (operator ruling, stern-carp-604, 2026-09-20), and it comes with three conditions, all of which make the change stand on better ground than it did. THE JUSTIFICATION IS THE PREDICATE'S OWN COST SHAPE, NOT A BUDGET SOMEWHERE ELSE. A per-character recursion in a hot, widely-shared predicate is the bare-minimum-cost defect DESIGN section 6 makes standing: it is fixed regardless of the realized n, because n is not a time-stable fact. This predicate is what every caller admitting a pinned ref runs, and nothing bounds how many refs a caller holds tomorrow. A claim budget elsewhere in the tree fitting afterwards is a CONSEQUENCE, and the annotation now says so and names what the spelling computes, so a reader does not have to reconstruct "forty lowercase hex digits" from sixteen replace calls. THE EQUALITY IS THE CLAIM, AND IT IS HOMED IN STD. The new test.claim.std_commit_sha_text_witness keeps the per-character spelling as an oracle and claims the two answer the same on every input either can be given, with a red per refusal cause: the lengths either side of forty, uppercase, the characters adjacent to both ranges on both sides, and the empty string. Nothing in that module asserts a step count -- a step count is a property of the evaluator, and the contract is the answer. The equality's red is reachable and was executed: respelling one stripped character from `f` to `F` turns it red, so it discriminates rather than decorating. THE SELF-WRITTEN ROW IS DELETED, ruling cited in its place. The margin row on gunbc.action_use_admission stands and is sharper: it now says the remedy is the structural reader cut and that this is its own change -- each sequence item re-enters the whole block setup for what is often a two-line compact mapping -- and it refuses one more remedy by name, so this respelling cannot be read as a precedent for buying margin with a second one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Review 69118 read head The measurement you asked for
Both spellings, current head, minutes apart, after the
So the hypothesis that dedup removed the pressure is falsified. You were right that the class row's own sentence ("forty lambda evaluations for every head on every admission") overstated it — after dedup it is roughly ten distinct heads per file, not every occurrence. But ten forty-character walks is still ~4,200 eval steps, which is more than the whole margin. The row that said "every admission" is deleted, so that overstatement goes with it. I could not have shown you this by reading, which is the point your The two DESIGN legsYou were right on both, and neither is answered by argument:
What the change now owes, and pays
Its red is reachable and I executed it: respelling one stripped character from On the remedy pointing elsewhereYou are right that — sent from still-lynx-398 |
Review 69135 found that dag/test/probe/yaml_shape_cost_probe.dag carries no imports while naming five things from other modules, in a directory the floor excludes from strict resolution -- so nothing would ever tell me. Checking it turned up a second defect the review did not reach: the committed file has NO `test` markers at all. Floor discovery and claim_batch both key on that marker, so the tool this row named as the way to run the instrument reported nothing whatsoever and exited 0. The artifact designated as the authority for the census-cost remedy could not produce a measurement. The repair is not to add the imports. dag/test/probe/ is documented as MUST-NOT-RESOLVE: every module there is a designed refusal, consumed as SOURCE TEXT by gunbc.compile_diagnostic_census census_of(probe_source(..)) rather than as a module in a closure, and each declares that in its header. A runnable instrument in that directory is wrong however well it is spelled, and making it resolve would push it against the convention the exclusion exists to protect. So it is deleted. What review 69075 actually asked for -- stop transcribing figures, name what re-derives them -- still holds, and the row still carries no numbers: it names claim_batch against the floor's budget, which is a live entry point, and states the METHOD for locating the expensive path in one sentence. DESIGN section 6 draws the line at whether a measurement is worth an entry point; this one is re-derived in minutes from the claim vocabulary that already exists, so it is a method and this row now says so, including why no probe stands beside it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review 69151: the claim named "every executed workflow file has a census claim" only joined the hand-authored roster to the directory listing. It never looked for the per-file claim. The hole has exactly the shape this census exists to close: a lane adds .github/workflows/new.yml, the claim reds, the author adds the NAME to the roster, the claim greens -- and that file's `uses:` are admitted by nobody while a claim asserting per-file coverage reports SUCCESS. A silent widen (DESIGN section 5) under a name the evidence did not establish (section 4b rung honesty). The claim now reads this module's own test declarations through the floor's own scanner -- v2.workflow.floor_naming_hygiene floor_discovery_scan_test_decl_names, the same producer floor discovery uses, rather than a second scanner minted here -- and requires, for each rostered file, a claim under the name that file derives. Adding a roster row without its claim reds. PROVEN BY EXECUTION, because a coverage claim whose red nobody has seen is the thing under review here: renaming census_heal_uses_are_modeled_and_ executed_exactly so heal.yml keeps its roster row and loses its claim turns this claim RED, and the rest of the file stays green (32 pass, 1 fail, and the one failure is this claim). Restored, it is green at a cost well inside the new-witness budget. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # .github/workflows/fleet-converge.yml # dag/extdeps/languages/yaml/ingest.dag # dag/gunbc/action_use_admission.dag # dag/gunbc/recurring_failure_mode/provider_substituted_action_runtime.dag # dag/test/claim/action_use_admission_witness_test.dag
|
Status: held, not abandoned. The authoring session (still-lynx-398) archived in the wind-down and this PR re-homed to me. I am recording its exact state rather than forcing it in, because it cannot land honestly today. Two independent reasons, both measured: 1. The census claim is over its ceiling at this head. I had approved exactly one bounded cut for this (the sequence/multi-line path the measurement already located, ~1,035 steps needed). It was never made before the session archived. 2. Merging main now conflicts with a concurrent rewrite of the same subject. #11731 (CENSUS-IMAGE 0A) landed the seeded-image derivation and rewrote the srv3 chain this PR's emitter-refusal plumbing was written against: 26 conflict hunks across 8 files, including What is done and worth keeping (all on this branch, checks green): What the next session must do, in order: rebase onto main after #11875 (the parse repair) lands; re-derive the writer's refusal path against #11731's seeded-image design rather than re-applying this branch's srv3 hunks; make the one bounded reader cut and re-derive the census cost with Carried as roadmap node — sent from stern-carp-604 |
Operator wind-down (2026-09-20): step back to v1 performance and v2 migration; record progress and remaining items as project items. - action-use-yaml-reader-contract: the bounded-subset YAML contract and the deletion of the action-use line projection (#11730, held). - required-lane-exit-on-refused-phase: the required lane exits 0 on a refused phase, with runs 35503853026 and 35510547600 as receipts (#11861). - workflow-census-claim-budget-margin: cut the reader's per-entry cost so the census is not one unrelated workflow edit from red. - action-runtime-epoch-observation: nothing reads the epoch date against a clock, and a runner-host override is unobserved. - namespace-relocation-batch-grain: one relocation should not cost one admission file per consuming declaration. ROADMAP.md regenerated through gunbc.roadmap_authority expected_roadmap_md. HISTORY NOTE: this branch is rebuilt on origin/main. The previous head carried a merge made with -s ours, which recorded main as merged while keeping this side of every file -- silently reverting main's changes in 17 unrelated files. That merge is discarded rather than fixed forward, so nothing of main's is lost. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Operator wind-down (2026-09-20): step back to v1 performance and v2 migration; record progress and remaining items as project items. - action-use-yaml-reader-contract: the bounded-subset YAML contract and the deletion of the action-use line projection (#11730, held). - required-lane-exit-on-refused-phase: the required lane exits 0 on a refused phase, with runs 35503853026 and 35510547600 as receipts (#11861). - workflow-census-claim-budget-margin: cut the reader's per-entry cost so the census is not one unrelated workflow edit from red. - action-runtime-epoch-observation: nothing reads the epoch date against a clock, and a runner-host override is unobserved. - namespace-relocation-batch-grain: one relocation should not cost one admission file per consuming declaration. Rebuilt on current main (other lanes keep appending nodes at the same point); ROADMAP.md regenerated through gunbc.roadmap_authority expected_roadmap_md. Verified like for like: main 51 PASS, mine 51 PASS, identical FAIL set, 0 errors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Operator wind-down (2026-09-20): step back to v1 performance and v2 migration; record progress and remaining items as project items. - action-use-yaml-reader-contract: the bounded-subset YAML contract and the deletion of the action-use line projection (#11730, held). - required-lane-exit-on-refused-phase: the required lane exits 0 on a refused phase, with runs 35503853026 and 35510547600 as receipts (#11861). - workflow-census-claim-budget-margin: cut the reader's per-entry cost so the census is not one unrelated workflow edit from red. - action-runtime-epoch-observation: nothing reads the epoch date against a clock, and a runner-host override is unobserved. - namespace-relocation-batch-grain: one relocation should not cost one admission file per consuming declaration. Rebuilt on current main again (sixth time: other lanes keep appending nodes at the same point). The five rows are byte-identical to the head verified at d5dc91b -- main 51 PASS, mine 51 PASS, identical FAIL set, 0 errors -- and ROADMAP.md is regenerated on this head through expected_roadmap_md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…kflow projections from their authorities Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ost shape Both spellings are linear; the gain is the interpreter's per-character lambda cost. Say so, keep the operator ruling, and name the dissolution trigger: that cost fixed at its source (review 70343). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…te fleet-converge.yml from its authority Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#11730 changed this file's import, so its identity entered the changed set and the floor refused with changed_witness_planned_without_terminal_verdict (run 35816045125): the file sat in bin_witness_wet_entries, which has had no executing consumer since 2026-08-15. Reclassify it LocalRepoWetLane and enrol it in local_repo_wet_schedule, as its sibling fetch file was on 2026-09-09. Measured wet on this tree before enrolment: it holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…yed on their complete inputs The accelerator walk returns the pair a mapping holds, reads one-line, nested and literal entries directly, and hands every other chunk to the general reading, which is now also the only refusal reading (the multi-line read dispatcher is deleted). Each routing decision is a function of exactly what it reads -- an entry's first line, an item's head, a block's indentation, a mapping's key sequence, a flow sequence's inner text -- so a shape a document repeats is decided once. Measured with test-local repeated-shape claims (distinct content per repetition) and a per-line step profile: census_fleet_converge 95,843 -> 76,936; slope 1,013 -> 830 steps per added step item. All YAML ingest (26) and emit (11) witnesses unchanged and green. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
realized_jobs_reading flattens each job into its use readings in document order instead of folding a stop flag through every step and copying the accumulated uses at every use; the first refusal in that order is the one the stopping scan reported. realized_distinct_uses pushes instead of copying. The block split decides its chunks in one expression. action_use_admission witnesses 33/33, yaml ingest 26/26, emit 11/11. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…side the reader cut Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…g against an independent reader a_real_uses_line_respelled_outside_the_subset_refuses_and_respelled_inside_it_reads_the_same rewrites heal-publish.yml's first uses: line into a flow sequence, an alias and an empty value (each must make the file unreadable, never an empty or unchanged population) and into a quoted key and a spaced colon (the same YAML key, so exactly the original population). A mutation moving quoted_key into the must-refuse set turns it red. The three literal_*_chomping claims cover |, |- and |+ with 0, 1 and 3 trailing empty lines, at document end and before a key, over a body with a more-indented line; every expected value was produced by the npm package yaml v2 parsing the same bytes as YAML 1.2. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…he fleet-converge census and record the allowance model fleet-converge.yml is regenerated from its authority. The census over it is rostered in v2.workflow.required_floor corpus_census_roster and judged against corpus_census_eval_step_allowance: measured at this tree, the census's own parse reads 1,098 entries, the derived budget is 97,840, and the claim performs 76,337 steps (about 69.5 per entry against the declared 80). realized_census_cost_standing now names that judge and records the per-entry model as ruling-admitted (stern-carp-604, 2026-09-23, operator default-approval on timeout); raising the budget, its rate included, stays refused. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
briansrls
left a comment
There was a problem hiding this comment.
APPROVED FOR MERGE on exact head be779362b29e1746c937fd3a71b1c1cd2357e1f7.
No additional direct-exit native bundle is required. The earlier direct-exit concern was specifically that piping claim_batch through a filter could preserve the filter's status instead of the producer's. That ambiguity is no longer carrying the merge conclusion: the exact-head required CI run completed successfully, and the required floor executed census_fleet_converge_uses_are_modeled_and_executed_exactly as a changed witness on the real acceptance path. It derived entry_count=1098, budget_steps=97840, then admitted the measured eval_steps=76337 — 21,503 steps of margin — under the separately merged #12134 per-entry authority.
The remaining checklist is discharged: #12087 restores truthful base reconstruction for the std.types edit; route controls prove unsupported forms refuse while quoted/spaced spellings of the same YAML key preserve the same action population; the 18 block-scalar cases are checked against the independent npm yaml v2 implementation; ingest/emit/action-census/allowance claim bundles are green; scratch probes are absent; GitHub reports CLEAN; and an exact-head source approval already exists.
A separate native wrapper around the same claim execution would duplicate the evidence route rather than close a remaining gap. This approval is for this SHA; a source change requires re-verdict.
…rge.yml from its authority The merge queue ejected be77936 on generated-artifact agreement: main moved that workflow's authority after the approved head, so the composed revision carried stale generated bytes. This commit is the merge and the regeneration only. Census at this tree: 76,804 eval steps against a derived budget of 98,560 (1,107 entries). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Re review 70622, which asks the merger to confirm the |
…rge.yml from its authority The merge queue ejected 676b743 on generated-artifact agreement again (#12011 changed that workflow on main after it). Merge and regeneration only. Census at this tree: 77,869 eval steps against a derived budget of 101,200 (1,140 entries). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… 70673)
The accelerated block walk carried a refused entry, sequence item or flow
entry as a value holding U+0001 and found refusals by scanning for it. A
document whose decoded content legitimately held U+0001 (a double-quoted
\x01) was then refused as if an item had been: in-band signalling that
DESIGN section 5 forbids and the tell of this PR's own row
bounded_reader_reinterprets_what_it_does_not_model.
YamlEntryRead becomes YamlEntryAccepted | YamlEntryRefused { line, reason };
yaml_entry, yaml_item_read and yaml_flow_plain_entry return typed readings;
blocks find a refusal by counting accepted readings against chunks and
locate it in the readings already made (no re-read). yaml_block_pair,
yaml_entry_read and every sentinel comparison are deleted; U+0001 survives
only as yaml_line_join_mark, source-text punctuation no value, key or
refusal can hold.
Discriminating reds: a_decoded_u0001_is_content_as_an_item_a_value_and_a_key
fails on the previous reader and passes here;
red_a_flow_fast_path_refusal_is_typed_without_the_document_precheck calls
the flow readers directly. yaml ingest 31/31, emit 11/11,
action_use_admission 34/34, corpus_census_allowance 5/5. Census 82,717
eval steps (was 77,869) against a derived budget of 101,200.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review 70673's finding is correct and is fixed at d8994b4: refusals are now a typed arm ( — sent from tidy-ant-630 |
briansrls
left a comment
There was a problem hiding this comment.
APPROVED FOR MERGE on exact head d8994b4df4a0710c485bc20f7da2a1e9ecbee6ef.
I re-reviewed the complete source delta from parent 983ac5aaa94d942d1e72a2da0102cd3f727d13c0; it changes only dag/extdeps/languages/yaml/ingest.dag and dag/test/claim/yaml_ingest_witness_test.dag. No source blocker remains.
The in-band refusal channel is removed end-to-end:
- mapping entries now carry
YamlEntryAccepted | YamlEntryRefused { line, reason }; - sequence item readers carry
YamlBlockResult; - flow-entry fast paths carry
YamlScalarResult; - their aggregators derive accepted populations from those typed outcomes and scan the same outcomes for the first refusal.
No caller now compares a semantic key or value against U+0001, and no refusal is reconstructed by re-reading a value. The direct typed paths preserve first-refusal ordering and line accounting, and every accelerator delegates to the same canonical lower-level reader with the same arguments. U+0001 remains only as yaml_line_join_mark, private punctuation inserted into raw source after the document precheck; raw U+0001 is refused while decoded \x01 is ordinary semantic content.
The new decoded-U+0001 claim discriminates the former live defect across sequence item, mapping value and mapping key. The precheck-bypass red directly exercises the flow accelerators and establishes typed refusal without relying on whole-document screening.
Exact-head CI is fully green: compiler, clippy, both native emit-build targets, required floor, and aggregate witnesses. The floor completed measurement publication/adjudication and generated-artifact agreement. The corpus census is 82,717 steps against a derived 101,200-step allowance for 1,140 entries, approximately 18% margin.
Nonblocking metadata: the PR body still reports the prior 29/29 ingest count and prior 76,337/97,840 census. Refresh it to 31/31 and 82,717/101,200, and remove the stale “still open” line; a body-only edit does not move the source SHA.
This approval is exact-head scoped. Any subsequent source movement requires re-review.
…s YamlEmitRefused arm (main's #11730 replaced serialize_yaml) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main's #11730 also changed std.types; the merge conflicted in both mirrors. Regenerated by --required-regen from the merged .dag; a second regen is byte-identical. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Summary
extdeps.languages.yamlwas an emit-only projection with a reader that reinterpreted whatever it did not model as a string. This makes the pair a bounded-subset reader/writer with a correct-meaning contract: every document the reader accepts has its correct YAML 1.2.2 meaning within a declared subset, and everything outside that subset is refused with a located reason — never reinterpreted, never silently dropped. The writer is the same contract turned around: for every value it emits,ingest_yaml_source(emit(v)) == v, and a value it cannot write that way is refused with the path to it.That then makes the reader affordable enough for
gunbc.action_use_admission's census to read every committed.github/workflowsfile through it, which is the trigger that retires the line projectionrealized_workflow_action_usesand its dissolution rowrealized_workflow_projection_frontier_rows. Both are deleted here.Every wrong-meaning case in the source audit (#11663, comment 5743133113) is fixed and each has a claim with a red control:
123is an int,yesis a string) and unrecognized text no longer silently becomes a string;[a,b]is two elements;["a, b", c]is not split inside the quotes);''decoded; double-quoted escapes decode and an invalid escape like\qrefuses;|clips to exactly one trailing newline,|-strips,|+keeps; empty lines inside a literal are kept (the emitter used to filter them out — a semantic loss).Why the witnesses are new rather than edited
The old ingest claims compared
serialize(parse(x)) == serialize(expected), so a loss in the writer hid a loss in the reader — the "blank lines are kept" test could not fail for that reason. The old round-trip helper comparedemit(parse(emit(v))) == emit(v), which a type change (YamlString "123"→YamlInt) passes. Both comparisons are gone:test.claim.yaml_ingest_witness— 24 claims comparingingest_yaml_source(text)against a hand-written value, one construct at a time, each paired with a refusal that must name its line and its reason.test.claim.yaml_emit_witness— 9 claims comparing decoded structure,ingest_yaml_source(emit(v)) == v, over 39 tricky strings and 13 multi-line strings as values, keys and flow entries.Refusal travels rather than being swallowed
serialize_yamlbecameemit_yaml -> EmittedYaml | YamlEmitRefused { path, reason }, so the five workflow generators, the compiler gate, and the srv3 seeded-install-media chain carry a typed outcome to their consumers instead of a string.gunbc.required_ci_epoch_observation'sRequiredCiEpochDuplicatedarm is deleted — the reader now refuses a duplicate mapping key, so that state is unreachable (DESIGN §4b: dissolution on climb).The budget: what was cut, what the floor is, and why this PR is held
Judged against a line, not a point.
.github/workflows/fleet-converge.ymlis now 135,149 bytes, up from 89,935 when this reader was first fitted. It is a generated file other lanes grow, so a flat budget turns this required claim red on someone else's change. On merged main the claim measured 95,843 against the flat 72,300.After the cuts below it measures 76,337. That is the measured floor of every remedy the cost standing admits, and it is still over the flat budget. A 2026-09-23 ruling admitted a per-entry allowance for claims whose subject is a whole corpus file. The ruling is an operator default-approval on timeout, not a deliberated ruling. #12134 landed that allowance: budget = base + per_entry × entry_count, where the census's own parse derives the count, and a claim over the per-entry rate still goes red. This PR adds the census to
v2.workflow.required_floorcorpus_census_roster.Measured at this head: 1,140 entries, derived budget 101,200, census 82,717 steps. That is about 18% margin, and about 72.6 steps per entry against the declared 80.
realized_census_cost_standingnow names that judge and records the model as ruling-admitted. "Raising the claim's eval-step budget", per-entry rate included, stays a refused remedy.What was cut (real work, never the population).
yaml_entryreturns the pair a mapping holds. It reads one-line, nested and literal entries directly and hands every other chunk toyaml_entry_general. That function is now the only refusal reading: the separate multi-line read dispatcher andyaml_literal_entryare deleted.yaml_first_line_route)yaml_item_head_opens_mapping)yaml_indent_marks)yaml_key_sequence_is_clean, with an exact per-key fallback when a decoded key holds a control character)yaml_flow_inner_is_plain)yaml_line_lead)realized_jobs_readingflattens each job into its use readings in document order. It no longer threads a stop flag through every step or copies the accumulated uses at every use; the first refusal in that order is the one the stopping scan reported.realized_distinct_usespushes instead of copying.gunbc.action_use_admissionrealized_census_cost_standing'sremedynow names both repairs.Repeated-shape slope. Measured over workflow step items of one shape with distinct content per repetition, between 20 and 40 items. Identical repetitions are memoized by the evaluator and read as nearly free, which makes a naive repeated-shape measurement misleading. The cost per added step item went from about 1,013 to about 830 steps.
Why the floor is where it is. Every evaluated expression node costs one step. The remaining cost is per distinct line and per block, and each is near its node floor:
yaml_lookup's per-field filter: about 4 per fieldGoing well under 72,300 would need a new string builtin, which is Rust seed growth and not done here, or one of the standing row's refused remedies. The subject also keeps growing, so any constant cut is spent again.
The instrument, not the figures. Every number here is re-derived by
claim_batch --source-root dag --source-root src/v2 --entry dag/test/claim/action_use_admission_witness_test.dag --functions census_fleet_converge_uses_are_modeled_and_executed_exactly, read from its[witness]line. The per-component split came from a temporary per-line step counter in the seed interpreter. It was a local instrument only and is not committed.The
std.typespredicatestd.typescommit_sha_text_holdsis respelled as sixteen whole-string removals. Both that and the per-character spelling are linear, so this buys a constant factor in the interpreter, not a cost-shape repair (review 70343). The comment says so, and it names the dissolution trigger: the per-character evaluation cost fixed at its source. It was admitted by an operator ruling (stern-carp-604, 2026-09-20), not by this diff.test.claim.std_commit_sha_text_witnesskeeps the per-character spelling as an oracle, with a failing case for each refusal cause.What CI establishes
The floor's whole-blob
dag/std/types.dagkernel guard used to refuse this PR's changed-witness planning. That refusal was a defect, and #12087 fixed it; this branch integrates main after #12087. The floor now plans and runs the changed witnesses. Two things it found on this branch:install_media_remaster_ensure_grub_cmdline_inserts_by_real_executionhad no executing lane. It moves to the local-repo wet lane, holds when run wet, and its sibling fetch file made the same move on 2026-09-09.emit-build's E0573PointerWidthred is inherited from main and owned by #12070.Test plan
Run with a seed built from this tree:
claim_batch --entry dag/test/claim/yaml_ingest_witness_test.dag: 31/31claim_batch --entry dag/test/claim/yaml_emit_witness_test.dag: 11/11claim_batch --entry dag/test/claim/action_use_admission_witness_test.dag: 34/34, with the census claim at 82,717 against its derived budget of 101,200claim_batch --entry dag/test/claim/corpus_census_allowance_witness_test.dag: 5/5claim_batch --wet --entry dag/test/claim/srv3/srv3_seeded_install_media_real_execution_witness_test.dag: PASSsrc/v2/test/claim/local_repo_wet_terminal_test.dag: all passgunbc run --entry dag/gunbc/instruments/generated_artifact_gate.dag --function main: no driftLanding-checklist evidence added:
a_real_uses_line_respelled_outside_the_subset_refuses_and_respelled_inside_it_reads_the_samerewritesheal-publish.yml's firstuses:line into five spellings.quoted_escaped_and_spaced_uses_keys_are_read_and_script_text_is_notalready pins that meaning.quoted_keyinto the must-refuse set turns the claim red.literal_{clip,strip,keep}_chomping_matches_the_independent_reader_for_every_trailing_blank_countcover|,|-and|+, each with 0, 1 and 3 trailing empty lines, at document end and before a following key, over a body with a more-indented line. All 18 expected values were produced by the npm packageyamlv2 parsing the same bytes as YAML 1.2, and are written as literals.dag/test/claim/scratchprobe/*residue: the repeated-shape probes were local and are deleted.🤖 Generated with Claude Code