Repository navigation
Decide whether Filesystem service operations are transport-bound before repairing the two emitter path defects - #8858
Conversation
… dispatch dropped the declaration, three targets copied the result
`transport file` operations emitted code that does not compile in any target.
Measured by compiling a three-operation fixture through rust, python and go:
Rust `write` declares -> Result<(bool), _> and returns Ok(content), a String
Go declares (struct{ bool }, error), returns string, calls os.ReadFile
without importing "os", and `path :=` redeclares the parameter
Python body at the wrong indent, Tuple unbound
Every operation -- Write, Delete, List, WriteOwnerOnly -- emitted a READ,
ignored the declared `path:` template AND its own `path` argument, and Write
dropped `content` entirely. That is DESIGN section 5's fabricated plausible
output: below the ladder, not a low rung on it.
THE ROOT IS SEMANTIC INFORMATION LOSS AT THE DISPATCH BOUNDARY, not three
renderer defects. emit_unified_transport_dispatch handed `render_file` only
(op_name, depth) -- alone among the transport arms in NOT receiving the
transport node -- so no renderer could read `path:` or `verb:` however it was
written. The declaration carries a verb, a path template, input bindings and an
output contract; the dispatch supplied a name and an indent depth. The three
target copies (Rust emit_file_call, Python emit_py_file_call, Go
emit_go_file_call) are downstream of one defect rather than three peers, which
is why all three are deleted rather than the Rust one repaired.
THIS REMOVES NO CAPABILITY. There was never a working file emission to remove;
the absence was spelled as output instead of as a refusal. The class climbs
from below-floor fabrication to a typed, located, counted refusal.
The refusal is minted ONCE, before target-specific rendering, in emit_artifact:
a module declaring a file-transport operation emits NO file and yields
TransportEmissionNotModeled { transport_kind, service, operation,
declaring_module, target, missing_realization_fact, span }. Counted is the
point (section 5): a refusal whose frequency is observable is how we learn a
real consumer arrived. The dispatch arm still refuses rather than being deleted
-- is_local_transport is DEFINED as "not rest, not shell, not file", so
removing the arm would route a file transport into render_local and fabricate a
direct function call by omission.
The census mirrors the dispatch's rest -> shell -> file precedence rather than
testing is_file_transport alone, so the two agree by construction rather than
by coincidence.
SEVERITY: mechanism confirmed, no victim. Production reach is zero -- six
operations across two modules, no emission site, no witness asserting these
strings, and zero hand-maintained callers under src/v1/stage0/src/bin.
Mechanism reach is NONZERO: the fixture in the new witness executes the path.
NEXT-RUNG TRIGGER (section 4b(2) -- waiting on a named event, not stalled): the
class climbs when a real consumer needs a file transport emitted, and the
refusal is what reports that day. The repair is then the section 3 one --
interface shape and transport are two facts, so emitters bind a realization
handler rather than each inlining one. THREADING THE PATH ALONE DOES NOT CLOSE
IT: the deleted renderers also dropped `content` and returned a String against
a declared bool, neither of which is a path problem. Deliberately unbuilt today
(section 6, displaced cost): building the binding for a surface with no
consumer is the purity trap.
EVIDENCE, by execution:
- discriminator witness, 4/4 PASS: three file ops refuse with exactly 3;
rest+shell refuse with 0; rest+shell clean of ALL blocking diagnostics;
a mixed module refuses ONLY the file operation. The last row pins the
refusal to the operation, so a wall poisoning siblings goes red there while
the file-only row stays green. It stays enrolled after the climb
(section 4b(4)): deleting the evidence with the machinery would recreate
specification-without-execution one rung up.
- required-regen: drift is exactly the six mirrors of the six edited .dag
modules, no more; mirrors installed from the candidate, never hand-edited.
- emitted artifacts read directly: TransportEmissionNotModeled present in
v1_std_core.rs; emit_file_call / emit_py_file_call / emit_go_file_call at
zero across all three emitter mirrors.
- seed projection receipt re-derived, not carried forward: cli_run.rs fn
census FLAT at 847 on both main and this branch; numstat 7 added / 0
removed; 0 added lines declaring a fn. The variant is blocking, so
is_interpreter_blocking_diagnostic's `_ => true` is correct and no
compile_clean_diagnostic_is_advisory entry is owed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI red is pre-existing on main, not this change — and the floor executed this PR's witness
Failure-set parity, at identity grain rather than by count. The 16 failures on this PR are set-identical to main's at the merge base All 16 are The floor counters show this PR's witness ran and passed:
+4 planned and +4 passed, with failures flat — exactly the four rows of That is stronger evidence than the local run reported in the description: these rows executed under the required floor on CI, not just under a local I am not merging (temporary policy: operator merges manually). Flagging that main is red at HEAD as a separate matter, since it is not this PR's to fix and it affects anything else merging today. — sent from still-badger-464 |
The question came first
The brief posed a binary — are Filesystem operations bound to a fixture handler (in which case teaching the emitter a real path would break the one thing it does correctly), or is the emitted code an attempted transport (in which case the two reported defects are real template defects)?
Neither. Established by compiling a three-operation
transport filefixture throughgunbc compileat all three targets and reading the output, rather than by reading the templates:Write(path, content)-> Result<(bool), _>, returnsOk(content)— aString(struct{ bool }, error), returnsstring, callsos.ReadFilewithout importingos, andpath :=redeclares the parameterTupleunboundEvery operation — Write, Delete, List, WriteOwnerOnly — emits a read, ignores the declared
path:template and its ownpathargument, and Write dropscontententirely.Not a fixture binding, positively: nothing binds
base_pathto a fixture root (Rust defaults".", Python requires it from the caller, Go has no constructor at all). And the same operations are consumed as real filesystem effects through the interpreter, which reads the declaration correctly —dispatch_filesubstitutes the path template and dispatches onverb. A fixture handler that returns the wrong type and cannot be built is not serving a fixture.The root
Not three renderer defects — semantic information loss at the dispatch boundary.
emit_unified_transport_dispatchtakes:The declaration carries a verb, a path template, input bindings and an output contract. The dispatch supplies a name and an indent depth. No file renderer could read
path:orverb:however it was written — one signature defect wearing three costumes. There was a third copy beyond the two in the brief: Go.This removes no capability
There was never a working file emission to remove. Emitted code that fails to typecheck in Rust, fails to compile in Go for two independent reasons, and binds an undefined name in Python is the absence of the capability, currently spelled as output rather than as a refusal. The class climbs from below-floor fabrication (§5) to a typed, located, counted refusal.
The repair
One refusal, minted before target-specific rendering in
emit_artifact, so one authority serves all three targets:TransportEmissionNotModeled { transport_kind, service, operation, declaring_module, target, missing_realization_fact, span }A module declaring a file-transport operation emits no file and yields a counted diagnostic. Counted is the point (§5) — a refusal whose frequency is observable is how we learn a real consumer arrived; its absence is why this decayed unnoticed.
The dispatch arm still refuses rather than being deleted:
is_local_transportis defined as "not rest, not shell, not file", so removing the arm would route a file transport intorender_localand fabricate a direct function call by omission.The census mirrors the dispatch's rest → shell → file precedence rather than testing
is_file_transportalone, so the two agree by construction rather than by coincidence (§3).Severity: mechanism confirmed, no victim
src/v1/stage0/src/bin/.Next-rung trigger (§4b(2) — waiting on a named event, not stalled)
The class climbs when a real consumer needs a file transport emitted, and the refusal is what reports that day. The repair is then the §3 one: interface shape and transport are two facts, so emitters bind a realization handler rather than each inlining one.
Threading the path alone does not close it, and the trigger says so in its own text — the deleted renderers also dropped
contentand returned a String against a declared bool, neither of which is a path problem. The interface must carry the verb, the resolved path template, the operation input bindings, the payload/content input, the declared output type, and the failure/effect semantics.Deliberately unbuilt today (§6, displaced cost): building the full binding for a surface with no consumer is the purity trap.
Evidence, by execution
Discriminator witness (
dag/test/claim/transport_emission_not_modeled_witness_test.dag), run in both directions. The RED arm is executed, not reasoned: the same four rows were run against aclaim_batchbuilt from a detachedorigin/mainworktree — a compiler with no such wall, whosev1_std_core.rscontains zero occurrences ofTransportEmissionNotModeled.The two discriminators go red on the baseline and green here. Rows 2 and 3 passing on both sides is the correct result, not a weakness: they are positive controls whose job is to stay green, and one that flipped would mean the dispatch had been broken for every transport. Without them the file cannot distinguish "the file arm refuses" from "the dispatch is broken for everything". The mixed row pins the refusal to the operation, so a wall that poisoned siblings goes red there while the file-only row stays green. The outcome was predicted before the run rather than read back from it.
The witness stays enrolled after the climb (§4b(4)) — deleting the evidence with the machinery would recreate specification-without-execution one rung up.
required-regen: drift is exactly the six mirrors of the six edited
.dagmodules — no more, no less. Mirrors installed from<candidate>/src/withcp, never hand-edited.Emitted artifacts read directly, not assumed:
TransportEmissionNotModeledpresent inv1_std_core.rs;emit_file_call/emit_py_file_call/emit_go_file_callat zero across all three emitter mirrors.Seed projection receipt, re-derived rather than carried forward (as
compiler_diagnostic_seed_projection_noterequires):cli_run.rsfn census flat at 847 on both main and this branch; numstat 7 added / 0 removed; 0 added lines declaring a fn. The variant is blocking, sois_interpreter_blocking_diagnostic's_ => trueis correct and nocompile_clean_diagnostic_is_advisoryentry is owed.Also confirmed by execution: the annotation-grain refusal fired on this change's own first draft (five hard diagnostics for a
//block inside a declaration body, which DESIGN §4c does not admit) and was fixed by moving it to module grain.Two notes for reviewers
An instrument hazard worth knowing.
gunbc compile --source-dir Xis silently ignored whenever any--source-rootis passed (main.rs:else if let Some(dir) = source_dir), and adding--source-root src/v1does not help either — v1 modules land in the name-census-only bucket, indexed but not compiled. A deliberately undefined symbol planted in05_emit.dagproduced zero new diagnostics under both invocations. There is nogunbc compileinvocation that typecheckssrc/v1; the regen path is the instrument. Whole-tree resolve overdag+src/v2sits at ~26.4k diagnostics on green main — baseline corpus red, never a signal about a change.A stale figure I did not touch.
compiler_diagnostic_seed_projection_noterecords acli_run.rsfn census of 747; the command it names now yields 847 on main. That figure certifies a different lane's diff, so I re-derived my own rather than overwriting another lane's receipt — flagging it rather than silently fixing or ignoring it.🤖 Generated with Claude Code