Skip to content

#915's write-path gate arms per EigsState and monotonically: one predicate read — or a bare "report" string — anywhere in an interpreter state arms entropy bookkeeping for every assignment in every module #1046

Description

@InauguralPhysicist

Found by the phugoid consumer (rung-3 autopilot), blind-critic rounds 10–11, 2026-08-25, eigenscript v0.41.0 / EigenScript@301026d. Gap ledger: phugoid GAPS.md G7.

Corrected 2026-08-25. The first version of this issue said the arming was module-granular and suggested splitting observer-reading code into its own module. Both were wrong — round 11 refuted them from the source and the runtime. See the correction comment; this body is the corrected version.

Summary

#915 (merged as PR #1034) makes observed scalar writes nearly free when a program uses no observer features. obs_needed is a single flag on EigsState (src/eigenscript.h:561), set in compile_ast (src/compiler.c:3717) and documented there as "Monotonic: a later unit that reads the observer turns it on for good."

So arming is process-wide and monotonic: one verdict read anywhere in the process — in a function that is never called, in a different file, in an eagerly-resolved load_file target (obs_gate_resolve_static_loads), or as a bare string constant in the constant pool — arms entropy bookkeeping for every assignment in every module of that program.

Reproduction

wlib.eigs is a 200k-frame observed write loop containing no observer construct of any kind:

define run_w(n) as:
    local u is 0.0
    local w is 0.0
    local i is 0
    loop while i < n:
        u is 280.0 + (sin of (i * 0.01))
        w is 2.0 + (cos of (i * 0.01))
        i is i + 1
    print of f"w {i}"
run_w of (200000)

reader_mod.eigs holds a function nobody calls:

define never_called(z) as:
    if oscillating of z:
        return 1
    return 0

and ctrl_mod.eigs is identical with if z > 0.0:. Four mains load these in combination. n=5 medians, two rounds, with the gate’s own verdict for wlib:

main EIGS_OBS_GATE_STATS=1 for wlib 1 2
A: loads wlib only unobserved 0.153 0.151
B: also loads reader_mod.eigs (different file, dead read) observed 0.219 0.219
C: also loads ctrl_mod.eigs (numeric control) unobserved 0.155 0.154
D: msg is "report" at module level, no observer construct observed 0.223 0.223

Row B: a read-free module compiles as observed because a different file held a read that never executes. Row C: the numeric comparison in the same position costs nothing, so this is arming, not parse cost or function-table size. Row D: const_pool_names_observer scans for bare names, so a string literal "report" costs +48% on a program that uses no observer feature.

Why it matters to a consumer

phugoid rung 3 is a closed-loop autopilot that polls observer verdicts as its supervisory layer — the intended use of the feature. Its profile gate measured what it believed was intrinsic observed-write cost at +44% over an unobserved floor. It is not: neutering only the four predicate reads in a function the measured variant never enters drops that to ~1.03, and forcing the gate open for both programs (EIGS_OBS_FORCE=1) collapses the differential entirely, showing the whole spread is arming. Against a genuinely read-free program an observed scalar write is indistinguishable from the unobserved floor.

That inverted a published finding. An early probe reported "observed scalar writes are essentially free"; the consumer could not reproduce it, withdrew it as wrong, and carried the withdrawal for six review rounds. Both measurements were correct — different regimes, and nobody had named the regime variable.

Practical consequence: unobserved: is the only workaround. Restructuring does not help — a consumer cannot escape by moving verdict reads into their own module, because the flag is process-wide.

What would resolve it

Adding scope where today there is one process-wide bit — per-module at least, per-binding ideally. A fix would also need to cover obs_gate_resolve_static_loads’s eager resolution of literal load_file targets, the always-conservative OP_IMPORT path, and the constant-pool name scan (a program whose only "observer content" is the string "report" should not pay).

The consumer pins this live as a write/noread > 1.15 arm with a planted fault, so when this is fixed upstream that gate fails and tells us to re-attribute and close G7.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions