make boundary crossings attributable to the construct that caused them

The beat reported which function crossed a boundary, never which decorator
put the beat there. So the graph accumulated boundary events with no
attribution, and no construct could be measured — "is this decorator
earning its keep" stayed an argument instead of a traversal.

engram_boundary_beat now takes the construct and carries it on the bus as
{"construct":"..."}. The injection point, the beat, and the accumulation
already existed; only the attribution was missing.

Also pins a known defect as a test: codegen calls fn_has_decorator for
exactly three names (manager, accessor, route). Twelve others parse, attach,
and compile to nothing — including @authenticate (6 uses), @authorize (3),
@rate_limit (3) and @validate (2), which look like protection and are not.
decorator-authenticate-compiles-to-nothing asserts that @authenticate emits
byte-identical C to no decorator at all, so fixing it will be a visible flip.

Verified: compiler self-hosts byte-identically, 86/86 native compiler tests
pass, emitted C carries the construct for both @manager and @accessor.
This commit is contained in:
bigmerge
2026-08-17 04:53:40 -05:00
parent 409bf57341
commit dcaa77d77b
5 changed files with 83 additions and 12 deletions
+11 -2
View File
@@ -3223,8 +3223,17 @@ fn cg_fn(stmt: Map<String, Any>) -> Void {
// strengthen (self-activity) + a dharma bus event so a decorated op
// self-reports with ZERO hand-written instrumentation in its body. (VBD role
// = the topmost decorator; write it topmost when stacking with @route.)
if fn_has_decorator(stmt, "manager") || fn_has_decorator(stmt, "accessor") {
emit_line(" engram_boundary_beat(EL_STR(" + c_str_lit(fn_name) + "));")
// The beat carries the CONSTRUCT that caused it, not only the fn that beat.
// Without it the graph accumulates boundary events with no way to attribute
// them to the decorator responsible so no construct can ever be measured,
// and "is this decorator earning its keep" stays an argument instead of a
// query. One parameter is the whole difference.
if fn_has_decorator(stmt, "manager") {
emit_line(" engram_boundary_beat(EL_STR(" + c_str_lit(fn_name) + "), EL_STR(" + c_str_lit("manager") + "));")
} else {
if fn_has_decorator(stmt, "accessor") {
emit_line(" engram_boundary_beat(EL_STR(" + c_str_lit(fn_name) + "), EL_STR(" + c_str_lit("accessor") + "));")
}
}
// Seed declared with parameter names so reassignment works
let decl = native_list_empty()