feat(observer): raw-step signals (#422) + trajectory snapshots across calls (#421)#540
Merged
Merged
Conversation
… calls (#421) The observer pair, designed together because they interlock: #422 makes the value-channel classifier honest, #421 makes it reachable from the other side of a call. #422 — relative normalization (dv/(1+|v|)) erased exactly two classes, both misreading as ACCEPTING regimes: additive/polynomial runaway read converged/stable, and sub-deadband perpetual oscillation read stable. The value channel now keeps a parallel raw-step window and asks one structural question — are the steps non-vanishing (recent-half mean >= half the older-half mean, above a 4*eps*(1+|v|) fp-noise floor)? Non-vanishing same-sign => diverging (first use of the label on the value channel; geometric runaway upgrades from 'moving' too). Non-vanishing alternating => oscillating. Decaying steps settle as before — damped oscillation still reads converged. Contracts upgraded: expect_converging fails FAST on mid-run diverging, invariant_stable throws on it, and lib/contract.eigs's documented blind-spot caveats are deleted because the instrument no longer has them. #421 — observer state is binding-identity; a passed value arrives with no history. New special form 'trajectory of x' (OP_TRAJECTORY_SLOT/NAME, ABI-appended) snapshots the binding's windows into a transparent dict; new builtin 'classify of t' / 'classify of [t, "entropy"]' classifies it through the SAME slot machinery report_value/report use. classify of a non-snapshot raises type_mismatch — a bare value never silently classifies. lib/contract.eigs gains the snapshot-form expect_regime; lint.c learns 'trajectory' as a special form (E003 + shadow set). Suite [50j2] (test_trajectory.eigs, 20 checks) pins both issue repros red-then-green, the no-regressions, the call-boundary crossing, both channels, and the loud error paths. test_lint.sh's feature-rich fixture renamed its 'classify' fn — a true-positive W013 now that the name is a registered builtin (consumer sweep note: check for the same at the next pin bump). SPEC.md + COMPARISON.md gain byte-pinned examples; OBSERVER.md documents the raw-step signal and the snapshot model; BUILTINS.md gate green (214 builtins documented). Release suite 2725/2725; ASan+detect_leaks 2725/2725, leak tally 0; both tiers (JIT on / EIGS_JIT_OFF) verified on the new tests. Closes #422 Closes #421 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GdDAVfZHgoQ2UNP63FYUXm
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.
The pair
Designed together because they interlock: #422 makes the value-channel classifier honest; #421 makes it reachable from the other side of a call boundary. Closes #421, closes #422 — the last two observer-semantics issues from the 2026-07-04 survey.
#422 — the two blind spots, one missing signal
Relative normalization (
Δv/(1+|v|)) erased exactly two classes, and both misread as accepting regimes:x → x + 5000seeded at 4e5 (linear runaway)stable/convergeddivergingx → -xseeded at 4e-4 (perpetual oscillation)stable/convergedoscillatingconvergedoscillatingx → 2x(geometric runaway)movingdivergingconvergedconverged(unchanged)The insight: both blind spots are the same missing question. The slot now keeps a parallel window of raw steps and asks whether they are non-vanishing (recent-half mean ≥ half the older-half mean, above a
4·ε·(1+|v|)fp-noise floor). Non-vanishing same-sign steps sum without bound →diverging; non-vanishing alternating steps →oscillating; decaying steps settle exactly as before. The relative step stays the primary contract.Contracts upgraded to match:
expect_convergingfails fast on a mid-rundiverging,invariant_stablethrows on it, and lib/contract.eigs's documented blind-spot caveats are deleted because the instrument no longer has them.#421 — trajectories as values
Observer state is binding-identity, so a value passed into a function arrives with no history — the naive two-arg contract could never work. Now:
trajectory of x(special form, two ABI-appended opcodes) snapshots the binding's observer windows into a plain, inspectable dictclassify of t/classify of [t, "entropy"](builtin, sandbox-allowed) classifies the snapshot through the same slot machineryreport_value/reportuse — no mirror to driftclassifyof a non-snapshot raisestype_mismatch: a bare value never silently classifies as "no trajectory"expect_regime of [trajectory of x, expected, msg]trajectoryas a special form (E003 + the shadow set)Verification
tests/test_trajectory.eigs, 20 checks), plus no-regression pins for damped/settled/halving, both channels from one snapshot, the loud error paths, andexpect_regimedetect_leaks=12725/2725, leak tally 0; both tiers verified (JIT on /EIGS_JIT_OFF)test_lint.sh's fixture renamed itsclassifyfunction — a true-positive W013 now that the name is a registered builtin. Bump-train note: the next consumer sweep should check for user-definedclassify/trajectoryshadows.aot_rtobserver mirror picks this up at the next EIGS_REF bump, per the Observer surface coherence: implement or remove 'how'; decide |x|=1.0; fold in #383 #412 precedent.🤖 Generated with Claude Code
https://claude.ai/code/session_01GdDAVfZHgoQ2UNP63FYUXm