Repository navigation
XL-2 LoweringOccurrenceProjection 3b: census consumes occurrence roles; typed excluded populations; typed missing paths; repin (stacked on #12317) - #12334
Conversation
…red from the tag token construct_tag_edge built a record-literal tag / pattern constructor atom with the enclosing capture's occurrence. It now takes the tag's own terminal and uses node_lowered_from, so the tag keeps its minted occurrence. Adds a controlled-fixture claim: 9 conserved before, 11 after (the two tags). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ir own occurrence The chain reader reduced segment tokens to Symbols, so qualified_name_spine_node could only build synthetic segment atoms. Segments are now carried as atoms lowered from their tokens (node_lowered_from) on the value route (postfix chain) and the type route (one generic segment walker in dag.dag), and the spine is built by qualified_name_spine_of_atoms (the symbol builder is defined through it). Field projections and method selectors reuse the segment atom. Fixture claim: 4 conserved before, 10 after (the six segments). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…binders and labels) v2.compiler.occurrence_role records std.occurrence_identity OccurrenceRole and OccurrenceCategory for the names a production binds or labels, by dispatching to the decoders that already find each name. The dispatch table is admitted against the grammar in both directions (typed refusal naming the unlisted or unknown production); binder productions whose reader has not landed are listed and counted, never skipped. Planted controls: an added name-holding production and a removed fn_decl row each refuse by name. Ceiling recorded as an RFM row with its trigger: the parser records choice arms. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…'no binder' Readers answer NameRead = NamesFound | NoNameHere | NameReadRefused. Only a positional argument is NoNameHere; a decoder refusal or a missing captured child is counted per production in OccurrenceRoles.reader_refused. Planted control: an fn_decl shell with no name spine counts one refusal and records no role (mutation back to the silent arm turns it red). Addresses review 71392. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d authority, not an unlanded doc Addresses review 71404. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…1-tag-sources # Conflicts: # src/v2/test/claim/namespace_xl0/reference_conservation_test.dag
…as unkeyed The emitted-identity join moves into admission; the admitted table carries the keyed dispatch the walk uses. Planted control replaces fn_decl's emitted atom with a non-atom node; mutation back to the silent drop turns it red. Addresses review 71409. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…es' into session/tidy-otter-111-spine-segments # Conflicts: # src/v2/compiler/body_lowering_fold.dag # src/v2/test/claim/namespace_xl0/reference_conservation_test.dag
…y-otter-111-occurrence-roles
…block) The table's own admission check refused the merged grammar: unlisted dag_production_input_block, dag_production_output_block; unknown dag_production_io_block. Both new productions carry names and have no reader yet, so they are listed as NameRoleNotYetRead, as io_block was. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…les; typed excluded populations; typed missing paths; repin conservation_against_tree reads v2.compiler.occurrence_role roles: an erased atom that is a declaration or a non-module-scope reference is counted in role_excluded, a header segment in header_channel, and locus_erased keeps only references. The role producer's gaps are carried into every report. The XL-2 rehearsal gains HeaderSegmentsNotRefusalSites and NestingScopedReferencesUncounted (with a nesting control). The sample census types a missing path, and the sample is repinned by its rule, which reproduces the original list exactly at fec339d. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…12314 landed) Resolved to main's tree plus this PR's own five files (the occurrence-role module, its claims, the named-argument node decoder, the RFM row, one warm-enrolment row): every other difference was the pre-squash PR2 content that #12314 landed in final form, including the emit fix and the cast-node re-application. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…us-roles Resolved to the 3a branch's tree (main + 3a) plus this PR's own six files. The one conflict was imports in v2.compiler.reference_conservation: main moved list_snoc_item to std.algebra; kept main's placement and added this PR's occurrence-role imports. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… keeps its cause
reference_conservation_path_observation now observes each listed path through
extdeps.filesystem.filesystem_io filesystem_file_observation, which establishes
absence from a successful listing. SamplePathMissing is only that (a stale pin);
a listed-but-unreadable file is SamplePathUnreadable { cause } with the host's
error; listing/read contradiction and a malformed subject are their own arms.
Run for real: a missing file, a chmod-000 file and a real module render
missing / unreadable (Permission denied) / measured. Addresses review 71731.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Addressed review 71731 in a5a3a4d. Missing and unreadable are no longer conflated. The census now observes each listed path through the existing authority for exactly this distinction:
Run for real over a missing file, a — sent from tidy-otter-111 |
|
Re review 71744's non-blocking remark, about the repinned list having no re-deriving entry point. Agreed: the rule is stated exactly, and it was verified by reproducing the original 315-path list at A faithful entry point needs a whole-tree walk plus per-file sizes from
— sent from tidy-otter-111 |
Resolved to main's tree plus this PR's own six files; every other difference was the pre-squash 3a content that #12317 landed in final form. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ys only its walk Floor refused e3b89a9 with five reference_conservation claims completed over the 72300-step new-witness budget (73035..90288): each re-derived the dag grammar root, re-ran table admission over every production, and re-derived the emitted symbol list inside its own verdict. Those are nullary, language-only values: dag_name_table_admission and dag_name_production_emitted_symbols are now enrolled WARM in v2.workflow.floor_pure_producer_share beside dag_declared_token_classes, and dag_occurrence_roles walks against the admitted table (occurrence_roles_of_admitted). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
briansrls
left a comment
There was a problem hiding this comment.
HOLD / REQUEST_CHANGES at exact head 2ed6b27.
Two boundary defects prevent approval. Neither requires reopening the occurrence-role design or completing the ten NameRoleNotYetRead readers.
- [P2] Preserve the directory identity when splitting a sample path — src/v2/compiler/reference_conservation_census.dag:84-96.
sample_path_parts("/tmp/rc/probe.dag") produces directory="tmp/rc", name="probe.dag": the leading empty component is discarded, so the absolute parent becomes relative. reference_conservation_path_observation then lists that different directory but reads the ORIGINAL absolute path. A readable file can consequently be reported unreadable or observations-disagree. More importantly, when the wrong relative directory exists and omits the name, an existing absolute file whose read fails with permission denied reaches FilesystemFileAbsent and then SamplePathMissing; the original host error is discarded. That recreates the absent-versus-unreadable conflation that review 71731 repaired, through mismatched subjects rather than through the filesystem fold itself.
The same helper gives a bare "probe.dag" an empty directory, so filesystem_listing_observation returns FilesystemDirectorySubjectRefused even though a basename is a valid current-directory read. "./probe.dag" works differently from "probe.dag".
Please preserve the original parent/root, use "." for a basename's parent, and ensure listing membership and file read refer to the same subject. Add controls for a basename, an absolute path, and an absolute unreadable file beside an unrelated empty relative shadow directory. The last case must preserve the host cause and must not become SamplePathMissing. This is a correction at this caller; it does not require a repository-wide filesystem migration.
- [P2] Do not render unobserved role gaps as zero on normalization refusal — src/v2/compiler/reference_conservation.dag:1085-1094.
After parsing succeeds, dag_occurrence_roles is called only inside the Accepted normalization arm. The Rejected arm instead calls conservation_against_refusal directly; that starts from report_from_population, where role_not_yet_read and role_reader_refused are both zero, and neither is populated on that route. Thus a parsed binder-bearing module which normalization refuses loses the role-gap observation entirely. The same route never consults role-table admission, so a table refusal cannot produce OccurrenceRoleTableUnadmitted there. The promised 'gaps carried into every report' and typed-unmeasured table refusal currently hold only for normalized-success reports.
Please obtain/admit roles once after successful parsing and propagate their gap counts into both normalized-success and normalized-refusal reports. Preserve normalization's own refusal accounting; this is not a request to reclassify a normalization refusal as successful conservation. A refused role table must retain a typed unmeasured disposition rather than disappear behind that other branch. Add a parsed binder-bearing subject with a deliberately refused normalization outcome and assert its gap counts survive, plus a refused-role-table/normalization-refusal control.
Evidence and scope: the exact-head workflow 36324332195 reports success for floor, compiler, clippy, emit-build and witnesses. Those results do not exercise the two counterexamples above. I independently checked the path splitter with a direct Python transliteration and exercised the filesystem observations under an unprivileged local process: the requested absolute file existed, its read failed with errno 13, while the incorrectly selected relative directory listed successfully without that entry. This was a source/host-boundary reproduction, NOT execution of the .dag census. I did not run new .dag/native tests or mutation tests, and I did not independently rerun the sample-selection rule.
The existing-category reuse, typed header/nesting exclusions, and nullary warm specialization are not the reasons for this HOLD. The requested fixes concern subject fidelity and report completeness at the new boundaries. Also retain the existing caveat that locus_erased still contains binders whose roles are not yet read; the PR's own fixture explicitly has three of those.
Head rechecked unchanged before submission. Latest PR metadata now reports mergeable=false against main bcbf786, so CLEAN is no longer confirmed by that read. No enqueue or merge performed. After the fixes, rebind the review to the new exact head and require its merge-group candidate to pass.
| } | ||
|
|
||
| fn sample_path_parts(path: String) -> SamplePathParts { | ||
| fold_list(xs: split(s: path, delimiter: "/"), empty: SamplePathParts { directory: "", name: "" }, cons: fn(acc, part) { |
There was a problem hiding this comment.
[P2] The splitter changes the listing subject. /tmp/rc/probe.dag becomes directory tmp/rc, but the subsequent Read still uses /tmp/rc/probe.dag. With an empty relative shadow directory and an existing unreadable absolute file, the listing says absent and the read fails, so this caller reports SamplePathMissing and discards PermissionDenied. A bare probe.dag also produces directory "", which the listing-subject admission refuses instead of treating as .. Preserve the absolute parent/root and use . for basenames; exercise the mixed-directory unreadable case so the absence claim is about the path actually requested.
| match subject.normalized { | ||
| Accepted { value: tree, diagnostics: _ } => | ||
| conservation_against_tree(path: subject.path, population: population, tree: tree) | ||
| match dag_occurrence_roles(artifact: artifact) { |
There was a problem hiding this comment.
[P2] The role observation/admission runs only when normalization succeeds. A parsed module whose normalization is Rejected goes straight to conservation_against_refusal, retaining the zero role_not_yet_read/role_reader_refused defaults from report_from_population; it also never surfaces a refused role table as OccurrenceRoleTableUnadmitted. Please carry the role observation across both normalization outcomes, preserving normalization-refusal accounting and typed unmeasured role-table failures. A parsed binder-bearing specimen with an injected normalization refusal should retain its nonzero role-gap count.
…ion does Addresses GitHub review 5330814496. (1) sample_path_parts kept neither an absolute path's root nor '.' for a bare name, so the census listed one directory and read a file in another. It now keeps the parent as spelled (/tmp/rc, /, .). The classification is a pure sample_path_observation_of(path, listing, read) that refuses a listing of any directory other than the path's parent as a subject mismatch, so absence can never be read off the wrong directory. Controls: absolute, root, bare, relative splits; unreadable absolute file -> SamplePathUnreadable with cause; a listing of the unrelated empty relative dir -> SamplePathSubjectRefused, never Missing; positive absence control. The splits are red on the old split. (2) Roles are read right after a successful parse, and the refusal arm carries the role gaps beside normalization's own refusal accounting, or records OccurrenceRoleTableUnadmitted. reference_conservation_of_subject_admitted takes the admission as a parameter. Controls: a parsed binder-bearing module whose normalization refuses reports role_not_yet_read=1 (0 on the old code, refusal accounting identical); a planted refused table beside a normalization refusal surfaces as unmeasured. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Addressed GitHub review 5330814496 (both inline findings) in b4fceb9. 1.
2. Roles were not observed when normalization refused.
— sent from tidy-otter-111 |
|
Re review 71809's non-blocking note, on the Agreed it is weaker than a typed discriminator, but this PR did not introduce it. It is the census population's existing encoding: The typed version is a change to — sent from tidy-otter-111 |
briansrls
left a comment
There was a problem hiding this comment.
APPROVE-MERGE at exact head b4fceb9, through the merge queue only. Review 5330814496's two blockers are satisfied.
-
Path-subject fidelity: sample_path_parts preserves the absolute parent/root and uses '.' for a basename. The pure observation boundary checks that the supplied listing is for that very parent before using it as evidence. An empty relative shadow directory cannot establish absence of the requested unreadable absolute file: the mismatch is SamplePathSubjectRefused. With the correct parent, read refusal retains its host cause; genuinely established absence remains SamplePathMissing. The split, wrong-directory, unreadable and positive-missing controls discriminate the former implementation.
-
Report completeness: roles are now observed immediately after successful parsing and consumed by both normalization outcomes. A refused normalization keeps its original authored/refused/dropped accounting while receiving the role-gap observation. A refused role table remains typed unmeasured on that route instead of disappearing behind normalization refusal. The parsed binder-bearing/refused-normalization control and planted-table-refusal control exercise these joins rather than merely checking record construction.
I reviewed the source delta and execution evidence. Exact-head workflow 36333451979 has all five jobs successful, including the floor and its changed-witness qualification. No new local .dag/native test, mutation, sample-selection reproduction, or census run was performed by me.
Scope remains PR3b's census consumption, not completion of every role reader or the real-tree XL-2 rehearsal. In particular, locus_erased still includes binders whose role is not yet read; the explicit role-gap counters must accompany any interpretation of that count. The header/nesting exclusions and open XL-6 question remain qualified as before. The warm specialization shares language-only derived facts, not claim verdicts, and does not raise the claim budget.
This supersedes the previous HOLD. Require a passing merge_group candidate against then-current main; do not direct-merge or bypass checks.
Superseded by APPROVE review 5331373952 on exact head b4fceb9. The path splitter and listing-parent join now preserve the requested subject, and role gaps/table refusal survive normalization refusal with discriminating controls.
XL-2
LoweringOccurrenceProjection, PR 3b: the census consumes occurrence roles; typed excluded populations; typed missing paths; repinStacked on #12317 (3a), which is stacked on #12314 and #12305. Retarget each to
mainas the one below it lands. Design: #12302, §3–§4. XL-2 manager rulings: (1) binders go in a typed counted excluded population; (2) headers getHeaderSegmentsNotRefusalSiteswith the nesting caveat; (3) nesting-scoped references get their own typed record; (4) deleted sample paths get a typed disposition, plus a repin.1. The census reads roles (the consumer #12317 declared)
v2.compiler.reference_conservationconservation_against_treenow takes the roles thatv2.compiler.occurrence_roledag_occurrence_rolesproduces for the module.An atom whose locus is erased is sent by
erased_atom_dispositionto exactly one count:header_channel: a module-header segment (item −1). This is HeaderSegmentsNotRefusalSites.role_excluded: the atom has a recordedDeclarationRole, or aReferenceRolewhose categorystd.occurrence_identityoccurrence_category_module_scope_exposure_verdictcalls not module-scope (a field label or an argument label).locus_erased: otherwise. It now means exactly "a reference lost its locus".The rule is not restated: the census asks the existing verdict function. An atom with no occurrence and no pool copy is still dropped, whatever its role.
The producer's gaps are carried into every report as
role_not_yet_readandrole_reader_refused. A binder the producer cannot read yet stays inlocus_erasedand is also counted there. A role table the grammar does not admit gives a typedunmeasuredentry,OccurrenceRoleTableUnadmitted. It never reports every atom as a reference.The summary line renders all four new numbers.
2. The rehearsal's excluded populations (
gunbc.namespace_xl2_rehearsal_censusXl2ExcludedPopulation)This adds two arms beside
Xl2KernelCanonicalReferencesUncounted:HeaderSegmentsNotRefusalSites. No resolve refusal is located at a header segment. This was measured on this composition with a module whose header names a nonexistent path. Its atoms are counted by the censusheader_channel.NestingScopedReferencesUncounted. A module nested under a provider's namespace resolves that provider's names with its import stripped, so the residual cannot see the reference, exactly as for kernel names. It is uncounted for the same reason: the resolved arm keeps no tree. The trigger for counting it is that the census carries which scope resolved each reference.nestingrun in the shared warm producer,xl2_nested_reference_survives_the_strip_holds. The provider and the nested module both resolve after the strip, with no residual.3. Typed missing paths and the repin (
v2.compiler.reference_conservation_census)reference_conservation_path_observationobserves each listed path throughextdeps.filesystem.filesystem_iofilesystem_file_observation. That function establishes absence from a successful listing, never from a failed read (review 71731). Its result is one of:SamplePathMissing: the parent directory's listing establishes the file is not there. Only this arm means the pin is stale.SamplePathUnreadable { cause }: the file is listed, but its read failed. The host's error is kept, because an unreadable file calls for the opposite fix: investigate the environment, do not repin.SamplePathObservationsDisagree/SamplePathSubjectRefused: the listing and the read contradict each other, or the path is malformed.SamplePathMeasured/SamplePathGrammarUnprepared: as before.The rendered census line is derived from the type. Run for real: a missing file, a
chmod 000file and a real module rendermissing ...,unreadable ... Permission denied (os error 13)and a measured report.Repin. The sample's rule is now stated exactly: the share is over all
.dagfiles. That wording was ambiguous before, and I fixed it by reproduction: applying the rule atfec339d561regenerates the original 315-path list with zero differences. Re-applied at main8fcd8e77b8, it gives 316 paths, and all of them exist on this branch.Carrier wording for
gunbc.compiler_frontend_program_status(proposed, for the next carrier update; not edited here)Replace the whole
whystring of theLoweringOccurrenceProjectionarm, not only its closing "No fix has merged.". The current opening sentences describe the tag and spine defects in the present tense, and #12305 and #12314 have fixed both. The proposed text:The arm stays
PrerequisiteOutstanding. Adapted from calm-heron-346's draft on #12314.Evidence (local, seed-run interpreter)
v2.test.claim.namespace_xl0.reference_conservation, all 19 claims run. 16 are true. The 3 false ones are the same 3 that were false before PR 1: if-arm call argument, list-literal argument, repeated spelling. CI's floor is the authority on those.conserved + locus_erased + role_excluded + header_channel == authored, with every part non-zero. The tag and spine fixtures assert their conserved counts; their erased atoms are now summed across the three channels.the_clean_control_counts_its_header_segments_in_the_header_channel_holds(3 segments).binders_and_labels_are_role_excluded_not_locus_erased_holds. On the tag fixture it asserts role_excluded=5 (the type, the two fn names, the record-literal label and the pattern label), header=3, locus_erased=3 and not_yet_read=3; the last two are the parameter and field-declaration binders.erased_atom_dispositionanswerErasedReferencefor every role turns the new claim false. The file was restored byte-identical.test.claim.namespace_xl2_rehearsal_census, all 8 claims true. The walls claim now checks the three excluded arms by identity, not by count. The newxl2_nested_reference_survives_the_strip_holdspasses.reference_conservation_census_for_pathsover a missing file, achmod 000file anddag/examples/blackjack/hand.dag. It rendersmissing ..., thenunreadable ... Permission denied (os error 13), then a measured report (authored=8 conserved=2 locus_erased=2 role_excluded=1 header_channel=3 role_not_yet_read=1).NoSuchFunction): all touched modules.io_blockwithinput_blockandoutput_block. That is fixed in XL-2 LoweringOccurrenceProjection 3a: v2 occurrence-role producer, grammar-admitted table (stacked on #12314) #12317. Without the check, those productions would have been read silently as references.🤖 Generated with Claude Code