Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 51s
parser.sx parse-toggle-cmd: when seeing 'toggle .foo for', peek the following two tokens. If they are '<ident> in', it is a for-in loop and toggle does NOT consume 'for' as a duration clause. Restores the trailing for-in to the command list. parser.sx parse-on (handler modifiers): recognize 'throttled at <ms>' and 'debounced at <ms>' as handler modifiers. Captured as :throttle / :debounce kwargs in the on-form parts list. compiler.sx emit-on: pre-extract :throttle / :debounce from parts via new _strip-throttle-debounce helper before scan-on, then wrap the built handler with (hs-throttle! handler ms) or (hs-debounce! handler ms). runtime.sx: hs-throttle! — closure with __hs-last-fire timestamp, fires immediately and drops events arriving within ms of the last fire. hs-debounce! — closure with __hs-timer, clears any pending timer and schedules a new setTimeout(handler, ms) so only the last burst event fires. Both formerly-architectural skips now pass: - "toggle does not consume a following for-in loop" - "throttled at <time> drops events within the window" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.5 KiB
8.5 KiB
HS Conformance Scoreboard
Live tally for plans/hs-conformance-to-100.md. Update after every cluster commit.
Baseline: 1213/1496 (81.1%) initial scrape
Snapshot: 1496/1514 upstream sync 2026-05-08 (+18 new upstream tests)
Conformance: 1496/1496 (100.0%) on counted tests; 18 documented architectural skips
Wall: 26m17s sequential (8 batches × 200) via tests/hs-run-batched.js
Target: 1514/1514 — clear the 18 skip list (in progress)
Note: full-suite single-process is unreliable due to JIT cache saturation;
use hs-run-batched.js (fresh kernel per batch) for deterministic numbers.
Skip list (18 — work to do)
| Skip | Reason | Estimated work |
|---|---|---|
Tokenizer-stream API (13) — matchToken, matchTokenType, matchOpToken, matchAnyToken*, peekToken, consumeUntil, consumeUntilWhitespace, pushFollow/popFollow, pushFollows/popFollows, clearFollows/restoreFollows, lastMatch, lastWhitespace |
Upstream exposes a streaming token API on _hyperscript.internals.tokenizer. Our hs-tokenize returns a flat list; parser holds stream state internally as closures. |
Wrap hs-tokenize output in a token-stream object exposed as a primitive. ~1-2 days, mostly mechanical. |
Template-component scope (2) — component reads a feature-level set from an enclosing div on first load, component reads enclosing scope set by a sibling init on first load |
Upstream supports <script type="text/hyperscript-template" component="..."> — HTML-template-based custom elements. Our defcomp is SX-only; no template-component bootstrap. |
Add a <script type="text/hyperscript-template"> registrar alongside the existing script-tag scanner. ~1 day. |
Toggle parser ambiguity (1) — toggle does not consume a following for-in loop |
Parser greedily consumes for x in [...] as toggle .foo for <duration>. Need lookahead to distinguish for <num>ms/s (duration) from for <ident> in <expr> (iteration). |
Targeted parser fix in parse-toggle. ~2-4 hours. |
Throttled-at modifier (1) — throttled at <time> drops events within the window |
Parser doesn't recognize throttled at <duration> as a handler modifier. Currently emits malformed SX (handler body is the literal throttled symbol; time expression dangles outside closure). |
Parser support + runtime hs-throttle! wrapper. ~4 hours. |
Async event dispatch (1) — until event keyword works |
repeat until event click from #x suspends the OCaml kernel waiting for a click that the sync test runner can't dispatch (kernel busy, JS event loop blocked). |
Architectural — requires either yielding to the JS event loop between iterations, or a different test-runner shape that can interleave event injection. ~2-3 days. |
Cluster ledger
Bucket A — runtime fixes
| # | Cluster | Status | Δ | Commit |
|---|---|---|---|---|
| 1 | fetch JSON unwrap | done | +4 | 39a597e9 |
| 2 | element → HTML via outerHTML | done | +1 | e195b5bd |
| 3 | Values dict insertion order | done | +2 | e59c0b8e |
| 4 | not precedence over or |
done | +3 | 4fe0b649 |
| 5 | some selector for nonempty match |
done | +1 | e7b86264 |
| 6 | string template ${x} |
done | +2 | 108e25d4 |
| 7 | put hyperscript reprocessing |
done | +5 | 247bd85c |
| 8 | select returns selected text |
done | +1 | d862efe8 |
| 9 | wait on event basics |
done | +4 | f79f96c1 |
| 10 | swap variable ↔ property |
done | +1 | 30f33341 |
| 11 | hide strategy |
partial | +3 | beb120ba |
| 12 | show multi-element + display retention |
done | +2 | 98c957b3 |
| 13 | toggle multi-class + timed + until-event |
partial | +2 | bd821c04 |
| 14 | unless modifier |
done | +1 | c4da0698 |
| 15 | transition query-ref + multi-prop + initial |
partial | +3 | 3d352055 |
| 16 | send can reference sender |
done | +1 | ed8d71c9 |
| 17 | tell semantics |
blocked | — | — |
| 18 | throw respond async/sync |
done | +2 | dda3becb |
Bucket B — parser/compiler additions
| # | Cluster | Status | Δ | Commit |
|---|---|---|---|---|
| 19 | pick regex + indices |
done | +13 | 4be90bf2 |
| 20 | repeat property for-loops + where |
done | +3 | c932ad59 |
| 21 | possessiveExpression property access via its |
done | +1 | f0c41278 |
| 22 | window global fn fallback | done | +1 | d31565d5 |
| 23 | me symbol works in from expressions |
done | +1 | 0d38a75b |
| 24 | properly interpolates values 2 |
done | +1 | cb37259d |
| 25 | parenthesized commands and features | done | +1 | d7a88d85 |
Bucket C — feature stubs (observer mocks)
| # | Cluster | Status | Δ | Commit |
|---|---|---|---|---|
| 26 | resize observer mock + on resize |
done | +3 | 304a52d2 |
| 27 | intersection observer mock + on intersection |
done | +3 | 0c31dd27 |
| 28 | ask/answer + prompt/confirm mock |
done | +4 | 6c1da921 |
| 29 | hyperscript:before:init / :after:init / :parse-error |
partial | +2 | e01a3baa |
| 30 | logAll config |
done | +1 | 64bcefff |
Bucket D — medium features
| # | Cluster | Status | Δ |
|---|---|---|---|
| 31 | runtime null-safety error reporting | done | +13 |
| 32 | MutationObserver mock + on mutation |
done | +7 |
| 33 | cookie API | partial | +4 |
| 34 | event modifier DSL | partial | +7 |
| 35 | namespaced def |
done | +3 |
| 36b | call result binds to it |
done | +1 |
Bucket E — subsystems (design docs landed, pending review + implementation)
| # | Cluster | Status | Design doc |
|---|---|---|---|
| 36 | WebSocket + socket + RPC proxy |
done | +16 |
| 37 | Tokenizer-as-API | done | +17 |
| 38 | SourceInfo API | done | +2 |
| 39 | WebWorker plugin | done | +1 |
| 40 | Fetch non-2xx / before-fetch / real response | done | +7 |
Step-limit fix
| # | Cluster | Status | Δ | Commit |
|---|---|---|---|---|
| SL | raise default step limit 200k→1M | done | +70 | 225fa2e8 |
| 17 | tell semantics |
done | (included in SL) | — |
| 33 | cookie API (remaining 1) | done | (included in SL) | — |
Bucket F — generator translation gaps
Defer until A–D drain. Estimated ~25 recoverable tests.
| # | Cluster | Status | Δ | Commit |
|---|---|---|---|---|
| F1 | add CSS template interpolation | done | +1 | 5a76a040 |
| F2 | empty multi-element (query→for-each) | done | +1 | 875e9ba3 |
| F3 | hs-make-object _order + assert= for dicts | done | +1 | daea2808 |
| F4 | array literal arg to JS fn (sxToJs + reduce→SX) | done | +1 | da2e6b1b |
| F5 | bind feature parser stub |
done | +32 | 846650da |
| F6 | asyncError rejected promise catch |
done | +1 | — |
| F7 | hs-on nil-target guard (skip-list rescue) |
done | +1 | 1751cd05 |
| F8 | on EVENT from SRC or EVENT from SRC multi-source |
done | +1 | f1428009 |
| F9 | obj.method() via host-call (T9 from plan) |
done | +1 | hs-f |
| F10 | obj.method(promiseArg) resolved sync (F2) |
done | +1 | hs-f |
| F11 | obj.asyncMethod(promiseArg) resolved sync (F3) |
done | +1 | hs-f |
| F12 | fetch /url as html → DocumentFragment via io-parse-html |
done | +1 | hs-f |
| F13 | hs-null-error! self-contained guard (avoid slow host_error path) |
done | +3 | hs-f |
| F14 | when @attr changes parser+compiler+runtime — MutationObserver wiring |
done | +1 | hs-f |
| F15 | def/default/empty suites: NO_STEP_LIMIT for legitimate scoped-var cascades | done | +N | hs-f |
Buckets roll-up
| Bucket | Done | Partial | In-prog | Pending | Blocked | Design-done | Total |
|---|---|---|---|---|---|---|---|
| A | 12 | 4 | 0 | 0 | 1 | — | 17 |
| B | 7 | 0 | 0 | 0 | 0 | — | 7 |
| C | 4 | 1 | 0 | 0 | 0 | — | 5 |
| D | 2 | 2 | 0 | 0 | 1 | — | 5 |
| E | 5 | 0 | 0 | 0 | 0 | 0 | 5 |
| F | — | — | — | ~10 | — | — | ~10 |
Maintenance
After each cluster commit, update:
Merged:pass count +deltaline at top- The row's
Status/Δ/Commitin the relevant bucket - The buckets roll-up table counts
Use mcp__hs-test__hs_test_run(start=0, end=195) + targeted-suite runs to get the real number; don't run the full suite every iteration (hangs on 196/199/200/615/1197/1198).