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

167 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)
5. `sx_find_all` in `lib/hyperscript/runtime.sx` for where `def` functions are
called / where event handler contexts are constructed.
6. Add `meta` dict construction at call time; bind in function env.
7. Ensure `on` handler context carries `{:meta {:feature {:type "onFeature"}}}`.
8. Run test 198: `hs_test_run(start=198, end=199)` — expect 1/1.
### Step C — Query template performance (if still slow after step A)
9. Profile `hs_test_run(start=615, end=616, step_limit=2000000, verbose=true)`.
10. If the CSS template query `<${"p"}/>` rebuilds on every call, add a memoize
cache keyed on the template result string.
11. Rerun — expect < 5s.
### Step D — Full suite verification
12. 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)`
13. Confirm all previously-passing tests still pass.
14. 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.