feat: React frontend IR — TSX routing + component semantic lowering - #5
Open
enoch3712 wants to merge 56 commits into
Open
feat: React frontend IR — TSX routing + component semantic lowering#5enoch3712 wants to merge 56 commits into
enoch3712 wants to merge 56 commits into
Conversation
Design for replacing model-generated IL with deterministic AST/tree-sitter compilation: tiered emitter (measured 6.5x at L2 on 3.7k-line file), per-symbol source-map handles, verify verb for latent-fault trapping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ClassDef branch in parse_python now computes decorator span consistently with function handling: start includes decorator lines, slice_sha8 spans from decorator to end, and Symbol.decorators field is populated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restructure expr_risk into two passes: first check Call nodes (against _RISK_CALL) and withitem nodes for call-based risks (regex/crypto/ concurrency) and return immediately if found. Only after verify no call-based risks, check for ast.BinOp math risks. This ensures that foo(a) ** hashlib.sha256(a).digest() correctly returns "crypto" instead of "math", since call-based security risks must outrank math risks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… --tier Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…check Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sts, INTENT redaction Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
redact_line used the strict SECRET_ASSIGNMENT adjacency pattern as its cut trigger, so lines like "Authorization: Bearer eyJ..." passed through unredacted (auth is 9 chars from the colon, and JWTs don't match SECRET_VALUE prefixes). Reintroduce the broad SECRET_NAME keyword pattern for redact_line's trigger (over-redaction there is harmless); commit_file's enrichment gate keeps the strict SECRET_VALUE/SECRET_ASSIGNMENT check so INTENT prose mentioning "token" or "bearer" is still accepted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
At L2 a bare CALL line carries no call name (CALLS already lists them), yet it accounted for 23% of L2 page bytes on a 3,704-line corpus file. L2 now emits only risk-tagged CALL lines (!FAULT positions must stay); L3 dataflow is unchanged. Measured L2 on the corpus file: 4.5x -> 5.8x. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TSX grammar routing fix + React semantic lowering (COMPONENT/PROPS/ STATE/EFFECT/EVENT/RENDER) as KERN-IL extension via kern_react.py adapter post-pass. Tiers L1-L3 mapped, ambiguity faults enumerated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the byte-identical no-op regression test (plain JS with no JSX stays frontend=tree-sitter, no COMPONENT symbols emitted) and a "React frontend (tsx/jsx)" section in docs/deterministic-compiler.md covering dialect routing, the COMPONENT vocabulary (copied verbatim from the design spec), tier behavior, and the six fault markers. Corpus run (L2, KERN's own app/ + a fresh shallow clone of vercel/commerce, node_modules excluded): files=69 crashes=0 components=61 faults=8 ratio min=1.9 median=3.3 max=10.1 Zero crashes on first run; no compiler fixes were needed. Full suite: 154 tests, all passing (153 baseline + 1 new). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
component_lines dropped s.flow and s.effects entirely: non-hook body logic (guards, computed consts, calls) vanished at every tier, violating the spec line 'non-hook, non-render statements flow through the existing L3 flow-op rendering unchanged'. Extract the flow-op loop from _function_lines into a shared flow_lines helper (function output stays byte-identical), then render body flow at L3 — skipping CALL ops whose callee is a hook, since those already surface as STATE/CTX/REF/HOOK/ EFFECT heads — and the EFFECTS provenance line at L2+, after EVENT lines and before faults/RENDER. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_returns_jsx required the return value to BE a JSX node, so 'return loading ? <Spinner/> : <Content/>' and '=> ok && <b>hi</b>' silently stayed kind=function. New _jsx_bearing helper accepts, one level deep (deterministic), a ternary whose consequence or alternative is JSX and a '&&' binary whose right is JSX — on both the arrow expression-body path and return statements. _extract_render counts these shapes as JSX-bearing for last-return-wins selection and routes them through _lower_expr, which already lowers both to IF/ELSE nodes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 'IF open > UserDetails' inline branch in _render_lines returned
via continue without walking the inlined child's dropped descendants,
so e.g. a spread-props risk on '<div {...rest}/>' inside the child
never reached the FAULT-BEFORE footer at L2. New _collect_faults
helper walks child.children recursively before the continue.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Only SECRET_VALUE-shaped tokens were redacted on the new component surfaces; the Python frontend already redacts by name hint. Now, using SECRET_NAME/sanitize_string imported from kern_compile (safe: the reverse import is lazy, no module-exec cycle): - PROPS: object_assignment_pattern defaults of secret-named props - STATE/CTX/HOOK/useReducer: value text bound to a secret-named binding - JSX attributes: secret-named attr values render as name=<REDACTED …> - COMPONENT head: signature text scrubbed of secret-named string defaults (_scrub_named_secrets) - L3 body flow ops: details scrubbed the same way, so RET <input apiKey="…"/> cannot re-leak what RENDER redacts Plain-function output untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- _extract_hooks unwraps parenthesized expression statements so '(on && useHook());' faults conditional-hook like the bare form - _jsx_attrs docstring: event_attrs are (attr_name, value_node) pairs, not stale triples - docs: generator= line documents lang=tsx alongside frontend=tree-sitter+react; React section and tier table describe the EFFECTS provenance line (L2+) and L3 non-hook body flow Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-grafts the React frontend adapter onto main's hardened deterministic compiler (PR #4 squash e4b122d + cache-contract hardening). - Adopted main's parse_tsjs(text, typescript=..., tsx=...) bool API and per-grammar tsjs_available(typescript=..., tsx=...); our interim dialect="js"|"ts"|"tsx" parameter is dropped everywhere. - kern_compile.py: main's version taken as base; re-grafted Symbol.react field, fn_nodes capture in function_symbol, memo/forwardRef unwrap in the lexical_declaration walker, post-walk kern_react.lower_components call (JSX-capable grammars only: javascript + tsx), flow_lines extraction from _function_lines, component kind in propagate, and the emit_il COMPONENT branch. - kern_cache.py: main's version taken wholesale; "component" added to ADDRESSABLE_SYMBOL_KINDS so verify addresses components. - kern_react.py: component head line now uses semantic8-or-slice8 handles to match main's verify contract. - tests/test_react.py ported to the bool API (68 tests); main's expanded suites taken as-is, including tests/test_tsjs.py. - docs/deterministic-compiler.md: 3-way merged; React grammar-routing prose rewritten for the bool API and lang=typescript header value. Byte-identity verified: merged compiler emits identical IL to origin/main's compiler for component-free TS/JS and Python at L1/L2/L3. Full suite: 229 tests OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Source drift only: ADDRESSABLE_SYMBOL_KINDS gained "component" (+3-4 tokens per tier). Values match CI's 3.13 regeneration exactly. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Stacked on #4. Adds a React frontend to the deterministic compiler:
.tsxwas parsed with the plain TypeScript grammar (JSX = syntax error, files degraded to parse-error IR).parse_tsjsnow takesdialect="js"|"ts"|"tsx"and routes.tsxtolanguage_tsx().skills/kern/scripts/kern_react.py): post-pass on the same tree-sitter tree upgrades capitalized JSX-returning functions (incl.memo/forwardRef, ternary/&&returns) tokind="component"with PROPS / STATE / CTX / REF / HOOK / EFFECT / EVENT / RENDER payloads.IF x > Childinlining, L3 full attrs + body flow + effect bodies. Spans, slice hashes, verify, cache, redaction inherited from KERN-IL/0.2 — one format.&&/ternary/paren-guarded), dynamic-component, spread-props, render-prop, render-truncated — inline and in the FAULT-BEFORE footer at every tier.Verification
tests/test_react.py)app/+ vercel/commerce — 69 files, 0 crashes, 61 components, 8 faults, L2 ratios min 1.9 / median 3.3 / max 10.1Spec / docs
docs/superpowers/specs/2026-07-11-react-frontend-ir-design.mddocs/deterministic-compiler.md— new React frontend sectionFollow-up debt (tracked in review ledger): RET call-arg secret shape + scrub-before-cap, orphan ELSE for one-sided ternary, RAISES line for components,
…+Ntruncation count, render-prop flow summary.🤖 Generated with Claude Code