Commit Graph

4 Commits

Author SHA1 Message Date
bigmerge c741cfe928 EXPERIMENT: prohibition becomes a query over emitted relations
I said prohibition could not move because "a #error has no runtime". That
conflated two separable things: WHEN a violation is detected (build time --
correct, and unchanged) and WHERE the rule and the checker live (the compiler
-- assumed).

A prohibition is a containment relation over the call graph. So codegen now
records what it saw:

    sneaky   calls raw_sql
    allowed  calls raw_sql
    allowed  calls @repository
    repository calls prohibits:raw_sql

and tools/check/prohibitions.sh decides, at build time, outside the compiler.

PREDICTIONS AND RESULTS
  P1 codegen can emit the call graph it already walks   TRUE
  P2 the check becomes a query outside the compiler     TRUE
  P3 all prohibition decisions leave codegen            TRUE  zero #errors now
  P4 violations still caught at build time              TRUE  exit=1
  P5 codegen drops below the 4661 baseline              FALSE 4962, +301

P5 is the finding. The TRAVERSAL is irreducible -- you must walk the AST to
find calls, and those ~120 lines do not move no matter who decides. What is not
irreducible is the rule (which names) or the decision (#error). Those left. I
predicted the whole 223 lines would go because I had not separated walking from
adjudicating.

Still compiled, and measured rather than assumed: the capability-tier system
(cap_check_call, is_self_formation_call, is_dharma_call, is_llm_call,
cap_record_violation, emit_cap_violations) is 76 lines of the same shape --
prohibits_WITHIN rather than prohibits_outside, so the checker needs the
opposite polarity to absorb it.

98/98 native, 4/4 prohibition_query.sh, 7/7 seam_binding.sh, fixpoint ok.
2026-08-17 09:11:48 -05:00
bigmerge bc2f26ddfc EXPERIMENT: invocation control resolves at runtime
ISHIKAWA: why did wraps_body need compile-time knowledge? Because the wrapper
called the target directly. If the wrapper calls through the seam instead, the
seam can call the body itself, and a construct bound after the build decides
how and whether to invoke it.

PREDICTIONS AND RESULTS
  P1 wrap becomes runtime-bindable                  TRUE   body x3 -> 21,
                                                           never invoked -> 111
  P2 codegen shrinks                                TRUE   5042 -> 4977
  P3 cost 5-10% from an indirect call on every fn   TRUE   0.36s -> 0.39s, ~8%
  P4 zero-param fns break on the empty struct       TRUE   empty struct is a GNU
                                                           extension, empty init
                                                           is C23. Fixed with a
                                                           char field.
  P5 fixpoint holds                                 TRUE

PROCESS FAILURE worth recording: my first patch silently did not apply because
I dropped the assert on the string replacement. The build then failed with
"undeclared identifier __thunk_noargs", which I nearly attributed to the
empty-struct prediction. The guard that would have caught it existed and I
removed it -- the same shape as every other defect found tonight.

Removed: declare_wrap, decorator_wrap, cg_wrap_target, cg_wrap_construct,
params_to_call_args, and the wraps_body scanner branch.

prohibits_outside is now the ONLY construct kind left at compile time, and it
cannot move: a #error has no runtime.
2026-08-17 09:06:05 -05:00
bigmerge 285166c25c EXPERIMENT: emit the wrapper unconditionally, so exit binds at runtime too
ISHIKAWA: why did exit injection still need compile-time knowledge? Because the
body-helper wrapper was only emitted when codegen already knew an exit
construct existed. The wrapper being conditional was the cause, not the wrapper
being necessary.

PREDICTIONS AND RESULTS
  P1 exit becomes runtime-bindable                    TRUE  returns 14, bound
                                                            after the build
  P2 codegen shrinks                                  TRUE  5094 -> 5044
  P3 cost 5-15% from a call frame on every fn         FALSE 0.37s -> 0.38s, ~3%
  P4 fixpoint holds                                   TRUE

Every fn now gets a body helper and a wrapper. It has to be unconditional:
early returns must route through something for an exit construct to observe
them, and codegen cannot know which fns will be bound after the binary exists.

Removed with the machinery: declare_exit, decorator_exit, cg_exit_target,
cg_exit_construct, and the injects_at_exit scanner branch.

Two controls failed and were rewritten rather than repaired --
no-exit-construct-emits-no-wrapper asserted the optimisation this removes, so
it is now inverted. The integration harness gained a seventh assertion: an exit
construct declared after the build replaces the result.

99/99 native, 7/7 integration, fixpoint gen2==gen3.
2026-08-17 09:01:55 -05:00
bigmerge 8bbb750c2c control the claim that cannot be unit tested
The seam's whole claim is that a construct declared AFTER a binary exists
applies to that already-built program. compile_capture only sees emitted text,
so it structurally cannot check this: it needs a built binary, a linked target,
and an environment. Verified by hand until now, which is the standing problem
this session has been about.

tests/integration/seam_binding.sh builds a probe from El source containing no
construct at all, links a target that El never references, and asserts:

  ok  unbound program is unaffected
  ok  a construct declared AFTER the build applies
  ok  a construct declared after the build can REFUSE
  ok  an unlinked target is skipped, not fatal
  ok  a binding for a different fn does not fire
  ok  two constructs compose on one crossing

  6 assertions, 6 passed, 0 failed

The eight controls that failed after the strip were replaced, not repaired.
They asserted compile-time emission of capability that moved to runtime;
contorting them would have kept an assertion whose subject no longer exists.
Three took their place, asserting the emitted shape, and the behaviour they
used to cover is now the integration harness's job -- which is the honest
division, since the shape and the behaviour are no longer the same fact.

99/99 native compiler tests pass. Fixpoint holds.
2026-08-17 08:48:57 -05:00