Repository navigation
Transport binding spine: one target-neutral semantic binding for all four transports, then Filesystem bindings + Rust renderer to restore the 03_ingest board - #8945
Closed
gunbai-bot[bot] wants to merge 2 commits into
Conversation
…s and a Rust renderer: the 03_ingest board emits again #8858 deleted three per-target file renderers that each fabricated a read of `base_path + "/" + op_name` for EVERY operation, and replaced them with a typed refusal. It named its own next rung precisely: the class climbs when a real consumer needs a file transport emitted, the repair is DESIGN section 3's -- bind a realization handler -- and NOT a fourth per-target renderer. The consumer arrived. `src/v2/compiler/03_ingest.dag`'s closure reaches `extdeps.filesystem.filesystem_io`, so the whole Rust self-host board sat behind five refusals. MEASURED, both on this branch's own authority with mirrors regenerated from it (never main's mirrors against this branch's .dag -- that hybrid is unmeasurable): BEFORE abf7194 emit exit=1, files=0, file-transport refusals=5 AFTER emit exit=0, files=176, file-transport refusals=0 THE DEFECT WAS SEMANTIC INFORMATION LOSS, ONE FRAME ABOVE THE BRANCH. Rest and shell received the raw transport node and re-derived its meaning; local received only a spelling and an indent depth; file refused. Three targets independently deciding what an operation MEANS is the N x M trap -- the N x M defect is not that each target renders differently, which is correct, but that each decides. CUT A -- one target-neutral binding. `bind_operation_transport` classifies once, in the frame that still holds the declaration, before it narrows to (op_name, depth), and yields RestBound / ShellBound / FileBound / LocalBound / BindingRefused{cause}. Both dispatchers -- `emit_unified_transport_dispatch` (Go, Python) and `emit_transport_call` (Rust) -- now match on that binding and do target selection ONLY. Direct target-side transport classification: zero. `child_from_key` and `unwrap_single_field_product` move out of the Rust backend so the declared output projection is read target-neutrally rather than forked. It is a closed total function, not a handler roster: nothing declares an independent handler, so an open registration surface would have no consumer. THE LOCAL HOLE IS CLOSED POSITIVELY. `is_local_transport` was defined negatively ("not rest, not shell, not file"), so a malformed transport BECAME local and emitted a direct call by omission -- which also made both dispatchers' "unrecognized transport kind" arm unreachable, so the check that existed to catch this could never fire. `LocalBound` now requires a canonically empty transport; nonempty-and-unclaimed refuses. Verified `local_transport_node` IS canonically empty, so every existing local operation is unaffected by construction. CUT B -- Filesystem bound, Rust renders it. The five operations bind from the declaration: verb, resolved path template, payload input, declared result projection. `WriteOwnerOnly` is NOT an alias for `Write` -- create_new + mode 0600, and a non-Unix host REFUSES rather than degrading to a plain write. Go and Python have no file renderer and refuse as the backend-capability gap they are, never falling back to the deleted renderers. KEYED ON THE DECLARATION, NOT ON THE FIVE OPERATION NAMES. Neither name nor transport alone determines the operation -- `Write` and `Read` both carry a file transport with no verb -- but the pair does: explicit `verb:` row, else the content-param convention. That is exactly what the working interpreter (`dispatch_file`) already does, so the rule now has ONE authority read by both realizations instead of two agreeing by coincidence. Hardcoding `extdeps.filesystem.filesystem_io`'s operation names into the compiler would put one extdeps module's identity inside it -- the section 3 authority inversion. THE WALL IS NOT RETIRED. `TransportEmissionNotModeled` still refuses a target with no file renderer, and any operation the binding cannot bind -- now carrying the EXACT cause rather than one generic sentence. Its permanent regression control is retargeted rather than deleted (section 4b(4)): the well-formed fixture flips from expecting 3 refusals to expecting 0, and four new unbindable fixtures carry the discriminating RED. The omitted-path row is the sharp one. The parser SUBSTITUTES an empty string literal when `path:` is absent, so a binding that trusted what it was handed would emit a write to "" and report it as a successful realization. The binding therefore refuses a path template that is present but empty, not merely absent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…duct
A file operation declaring exactly ONE output field refused as if it had declared
no channel at all, so `output { success: Bool from "write_success" }` -- a
perfectly ordinary declaration -- could not be emitted.
THE CAUSE. `bind_file_result_fields` called `unwrap_single_field_product` before
reading each child's `from` key. That helper collapses a one-field product to the
field's OWN type, and a scalar is not a product, so the child list went empty and
the binder reported FileTransportUndeclaredResultChannel.
WHY IT SURVIVED THE FIRST PASS. Every one of `extdeps.filesystem.filesystem_io`'s
five operations declares two or more output fields, so the real consumer that
motivated this work never exercised the single-field path. The fixture in the
regression control does, which is what caught it -- the discriminating RED
earning its enrolment on the first change after it was written.
WHY THE HELPER IS RIGHT WHERE IT IS AND WRONG HERE. `emit_shell_return` unwraps
deliberately, because it has a defensible default to fall back to: an unkeyed
shell output means stdout. A file operation has no such default -- that is the
entire reason this binder refuses instead of guessing -- so it must read the
declaration as authored. The binder now reads the resolved type directly and
derives `single_field` from the child count, which also makes the one-field case
project `Ok(expr)` rather than a one-tuple.
`unwrap_single_field_product` moves back to the Rust backend: the previous commit
relocated it to the shared layer for this binder, and with the binder no longer
calling it that move was churn with no consumer. `child_from_key` stays shared --
it is genuinely read target-neutrally.
Named products refuse rather than being tuple-projected: the emitted signature
would carry the type NAME, and a positional tuple would not match it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6 tasks
briansrls
marked this pull request as ready for review
August 23, 2026 00:17
Contributor
Author
|
Superseded by #8957. Same architectural change, regenerated onto #8954's closed transport roster. #8957 is the canonical head: it carries the stage0 mirrors regenerated for the cut, so the construction wall exists in the compiler that actually runs rather than only in the authored source. This branch predates that and is nonmergeable. Closing rather than leaving two open PRs with the same title, so the wrong head cannot be reviewed or composed against. — sent from witty-carp-775 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Auto-opened by session-dashboard for session
witty-carp-775.Pushing to
session/witty-carp-775advances this PR.Worker attestation
Before flipping this PR to ready for review, confirm each item:
npm test,cargo test) and the result.Closes #Ndirective.Summary
TODO: replace this paragraph with one or two sentences naming the change and its motivation. Reviewers read this first.
Test plan