Files
rose-ash/plans/designs/f13-step-limit-and-meta.md
giles 985671cd76 hs: query targets, prolog hook, loop scripts, new plans, WASM regen
Hyperscript compiler/runtime:
- query target support in set/fire/put commands
- hs-set-prolog-hook! / hs-prolog-hook / hs-prolog in runtime
- runtime log-capture cleanup

Scripts: sx-loops-up/down, sx-hs-e-up/down, sx-primitives-down
Plans: datalog, elixir, elm, go, koka, minikanren, ocaml, hs-bucket-f,
       designs (breakpoint, null-safety, step-limit, tell, cookies, eval,
       plugin-system)
lib/prolog/hs-bridge.sx: initial hook-based bridge draft
lib/common-lisp/tests/runtime.sx: CL runtime tests

WASM: regenerate sx_browser.bc.js from updated hs sources

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 09:19:56 +00:00

6.8 KiB
Raw Blame History

F13 — Step Limit + meta.caller (+5 → 100%)

Five tests currently timeout or produce wrong values due to two root causes: step budget exhaustion and a missing meta implementation.

Tests

# Suite Test Failure
198 hs-upstream-core/runtime has proper stack from event handler wrong-value: meta.caller returns "" instead of an object with .meta.feature.type = "onFeature"
200 hs-upstream-core/runtime hypertrace is reasonable TIMEOUT (15s, step limit)
615 hs-upstream-expressions/in query template returns values TIMEOUT (37s, step limit)
1197 hs-upstream-repeat repeat forever works TIMEOUT (step limit)
1198 hs-upstream-repeat repeat forever works w/o keyword TIMEOUT (step limit)

Root cause A — Step limit (tests 200, 615, 1197, 1198)

The runner sets HS_STEP_LIMIT=200000. Every CEK step consumed by any expression in a test — including the double compilation warm-up guard blocks that appear before the actual DOM test — counts against this shared budget.

repeat forever (1197, 1198)

The loop body terminates in exactly 5 iterations (if retVal == 5 then return). This is bounded, not infinite. The step budget is exhausted before the loop runs because two eval-expr-cek compilation warm-up calls each consume tens of thousands of steps.

Fix: each warm-up guard compiles and discards a HS function definition. Those calls are defensive (wrapped in guard that swallows errors). We do NOT need to run the compiled code — the warm-up's purpose is just to ensure the compiler doesn't crash, not to consume steps. The step counter should not tick during compilation (compilation is a pure transform, not evaluation). If that's impractical to gate, raise HS_STEP_LIMIT to 2000000 (10×).

hypertrace is reasonable (200)

Defines bar() → calls baz() → throws. Simple call chain. The "hypertrace" in the test name implies the HS runtime trace recorder is active during the test. If trace recording is on globally, every CEK step generates a trace entry allocation. Fix: confirm whether trace recording is always-on in the test runner and disable it by default (trace should only be on when explicitly requested). Alternatively raise step limit.

query template returns values (615)

Uses <${"p"}/> — a CSS query selector built from a template string. Takes 37 seconds. Likely the template selector evaluation triggers repeated DOM scanning or expensive string construction per step. Fix: profile with hs_test_run verbose=true to identify which step is slow. If it's a regex compilation per-call, cache it. If step limit only, raise to 2M.

Unified fix: raise HS_STEP_LIMIT to 2000000

The simplest fix that unblocks all four timeout tests. In tests/hs-run-filtered.js, change the default step limit. Per-test overrides can still be set via HS_STEP_LIMIT env var for debugging.

If the query template test is still slow at 2M steps (37s × 10 = 370s, which would be unacceptable), that test needs a separate performance fix — cache the compiled regex/query from the template string rather than rebuilding it on every access.


Root cause B — meta.caller not implemented (test 198)

The HS meta object is available inside any function call. It exposes:

  • meta.caller — the calling context object
  • meta.caller.meta.feature.type — the HS feature type of the caller (e.g. "onFeature" when called from an on click handler)

Test script:

def bar()
  log meta.caller
  return meta.caller
end

Triggered via on click put bar().meta.feature.type into my.innerHTML. Expects "onFeature" in innerHTML. Currently gets "".

What meta needs

meta is a dict-like object injected into every function's execution context at call time. Minimum fields for this test:

meta = {
  :caller  <the calling context — a dict with its own :meta field>
  :element <the element the script is attached to>
}

meta.caller.meta.feature.type must return "onFeature" when called from an on event handler. The feature type string "onFeature" is already used internally (event handler features are tagged with this type).

Implementation

In lib/hyperscript/runtime.sx, at the point where a HS def function is called:

  1. Build a meta dict:

    {:caller calling-context :element current-element}
    

    where calling-context is the current runtime context dict (which includes its own :meta field with :feature {:type "onFeature"} for event handlers).

  2. Bind meta in the function's execution env.

  3. Ensure event handler contexts carry {:meta {:feature {:type "onFeature"}}}.

This is an additive change — nothing currently uses meta, so no regression risk.


Implementation checklist

Step A — Raise step limit

  1. In tests/hs-run-filtered.js, change default HS_STEP_LIMIT from 200000 to 2000000.
  2. Run tests 11971198: hs_test_run(start=1197, end=1199) — expect 2/2.
  3. Run test 615: hs_test_run(start=615, end=616) — expect 1/1 or note if still too slow.
  4. Run test 200: hs_test_run(start=200, end=201) — expect 1/1.

Step B — meta.caller (test 198)

  1. sx_find_all in lib/hyperscript/runtime.sx for where def functions are called / where event handler contexts are constructed.
  2. Add meta dict construction at call time; bind in function env.
  3. Ensure on handler context carries {:meta {:feature {:type "onFeature"}}}.
  4. Run test 198: hs_test_run(start=198, end=199) — expect 1/1.

Step C — Query template performance (if still slow after step A)

  1. Profile hs_test_run(start=615, end=616, step_limit=2000000, verbose=true).
  2. If the CSS template query <${"p"}/> rebuilds on every call, add a memoize cache keyed on the template result string.
  3. Rerun — expect < 5s.

Step D — Full suite verification

  1. Run all ranges with raised step limit:
    • hs_test_run(start=0, end=201, step_limit=2000000)
    • hs_test_run(start=201, end=616, step_limit=2000000)
    • hs_test_run(start=616, end=1200, step_limit=2000000)
    • hs_test_run(start=1200, end=1496, step_limit=2000000)
  2. Confirm all previously-passing tests still pass.
  3. Commit: HS: raise step limit to 2M + meta.caller for onFeature stack (+5)

Risk

  • Step limit raise: May make test suite slower overall (more steps to exhaust before timeout). But if tests pass quickly the limit is never reached. The 37s query-template test is the only real concern — if it genuinely needs 2M steps × (time per step), it needs a performance fix too.
  • meta.caller: Additive binding in function scope. Zero regression risk. The only complexity is constructing the right shape for the calling context chain — but since only one test exercises this and the shape is simple, the risk is low.