Skip to content

feat: React frontend IR — TSX routing + component semantic lowering - #5

Open
enoch3712 wants to merge 56 commits into
mainfrom
design/react-frontend-ir
Open

feat: React frontend IR — TSX routing + component semantic lowering#5
enoch3712 wants to merge 56 commits into
mainfrom
design/react-frontend-ir

Conversation

@enoch3712

Copy link
Copy Markdown
Owner

Summary

Stacked on #4. Adds a React frontend to the deterministic compiler:

  • TSX grammar routing fix: .tsx was parsed with the plain TypeScript grammar (JSX = syntax error, files degraded to parse-error IR). parse_tsjs now takes dialect="js"|"ts"|"tsx" and routes .tsx to language_tsx().
  • React adapter (skills/kern/scripts/kern_react.py): post-pass on the same tree-sitter tree upgrades capitalized JSX-returning functions (incl. memo/forwardRef, ternary/&& returns) to kind="component" with PROPS / STATE / CTX / REF / HOOK / EFFECT / EVENT / RENDER payloads.
  • Tiered emission: L1 head-only, L2 heads + components-only render tree with IF x > Child inlining, L3 full attrs + body flow + effect bodies. Spans, slice hashes, verify, cache, redaction inherited from KERN-IL/0.2 — one format.
  • Explicit faults: aliased-hook, conditional-hook (incl. &&/ternary/paren-guarded), dynamic-component, spread-props, render-prop, render-truncated — inline and in the FAULT-BEFORE footer at every tier.
  • Secret redaction parity: name-hinted literals scrubbed on all new surfaces (props defaults, state inits, JSX attrs, head signature, EVENT actions, effect/body flow).
  • No-op guarantee: non-React files emit byte-identical IR (verified against base commit).

Verification

  • 171/171 tests (68 new in tests/test_react.py)
  • Corpus: KERN app/ + vercel/commerce — 69 files, 0 crashes, 61 components, 8 faults, L2 ratios min 1.9 / median 3.3 / max 10.1
  • Subagent-driven: per-task spec+quality reviews, final whole-branch review verdict: Ready to merge

Spec / docs

  • docs/superpowers/specs/2026-07-11-react-frontend-ir-design.md
  • docs/deterministic-compiler.md — new React frontend section

Follow-up debt (tracked in review ledger): RET call-arg secret shape + scrub-before-cap, orphan ELSE for one-sided ternary, RAISES line for components, …+N truncation count, render-prop flow summary.

🤖 Generated with Claude Code

enoch3712 and others added 30 commits July 11, 2026 14:40
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>
enoch3712 and others added 22 commits July 11, 2026 18:39
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>
@enoch3712
enoch3712 changed the base branch from design/deterministic-il-compiler to main July 11, 2026 21:01
enoch3712 and others added 4 commits July 11, 2026 23:15
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant