Skip to content

fix(extract): mask bare & in TSX JSX text so partial extraction stops (#2922) - #2934

Open
santhiprakash wants to merge 7 commits into
Graphify-Labs:v8from
santhiprakash:fix/tsx-jsx-text-ampersand
Open

fix(extract): mask bare & in TSX JSX text so partial extraction stops (#2922)#2934
santhiprakash wants to merge 7 commits into
Graphify-Labs:v8from
santhiprakash:fix/tsx-jsx-text-ampersand

Conversation

@santhiprakash

@santhiprakash santhiprakash commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

A bare & inside TSX JSX text (e.g. <div>VoIP & Chamadas</div>) is valid TSX — esbuild, tsc, and React all accept it — but tree-sitter-typescript's grammar requires & in JSX text to begin an HTML entity reference. The resulting ERROR node trips the partial-extraction path (#2551 / #2788) and, on the reporter's 3000-file TSX codebase, silently drops every function, class, and import from 31 files (1 %), all because UI labels use & as a natural-language connector (Conexões & Integrações, Configurações & Perfil, Privacidade & LGPD).

Fix

A small, context-tracking walker that masks bare & to a single ASCII space only in JSX text content. & is left untouched everywhere the grammar already accepts it:

  • inside JSX tag attribute values (<a href="/search?q=a&b=c">),
  • inside { ... } expression containers ({flag && <span/>}),
  • inside string literals and comments,
  • in TypeScript code where & is bitwise AND (0xff & 0x0f) or an intersection type (type X = A & B).

Already-formed entities (&amp;, &#NN;, &lt;, …) are passed through without double-masking.

A single-byte placeholder (ASCII space) is used instead of the entity &amp; so the transformed source stays the same length as the original file. This keeps tree-sitter byte offsets and source[start_byte:end_byte] slices aligned with the user's source, honoring the LanguageConfig.source_transform byte contract. Vue's non-script blanking uses the same offset-preserving approach.

The walker disambiguates JSX tag starts from TypeScript generic type-parameter openers by combining a previous-non-whitespace character set (operator/punctuation ⇒ JSX, alphanumeric ⇒ code) with a short keyword list (return/yield/new/as/typeof/void/delete) that flips < after an identifier into JSX context. <T>, <T,>, <T extends X>, <T = X>, and <T>(...) => ... are all treated as code (generic shape) so subsequent bitwise & in code is not masked.

Verification

  • tests/test_tsx_jsx_text_ampersand.py — 38 tests, all green. Covers the fixture (mixed JSX-text / JSX-attribute / expression-container / TS-code & in a single file), bare & in JSX text is silent, bitwise AND in TS code is preserved, && in JSX expression is preserved, existing &amp; passes through, mask is fully byte-preserving, every non-JSX-text & location is left intact, fast-path for empty/no-ampersand sources, multi-bare-ampersand JSX text runs.
  • tests/test_extract.py, tests/test_file_slice.py — 212 passed, 4 skipped. No regressions.
  • graphify update . re-extracted the repo and updated the local graph output.

Reproduction (before fix)

// with_amp.tsx — triggers "syntax errors" warning, first error at line 2
export function A() {
  return <div>VoIP & Chamadas</div>;
}

After the fix, A() extracts cleanly with no parse_errors.

Closes #2922.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a _mask_tsx_ampersands pre-parse pass in graphify/extract.py that escapes bare & to &amp; in JSX text content of TSX sources, using a context-stack walker with <-vs-generic disambiguation to leave & in code, strings, comments, and expression containers untouched. Wires the mask into the TSX extraction path so bare-ampersand JSX text no longer produces ERROR nodes and partial-extraction warnings. Adds fixture and unit tests covering ampersand masking and the silent-JSX-text behavior.

Worth a look

  • Template literal treated as plain string ignores ${} interpolation and nested JSXgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX ampersand mask stays in JSX text after closed tagsgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1569 functions depend on the 238 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_js() — 80 callers, 4 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • new: run_pipeline() — 8 callers, 13 callees
  • new: collect_files() — 17 callers, 6 callees
  • …and 23 more — each is listed as a finding

Verification — 1569 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1421 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify extract\_js.

The verifier did not have enough to check extract\_js, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 1 grounded finding(s) anchored inline below; 30 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/extract.py
return ''.join(out)


def extract_js(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regressionextract_js()

80 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@santhiprakash
santhiprakash force-pushed the fix/tsx-jsx-text-ampersand branch from d1d687b to 22ae0e9 Compare August 21, 2026 08:48

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds _mask_tsx_ampersands and its _TSX_ENTITY_RE/_TSX_LT_EXPR_PREV helpers to escape bare & in TSX JSX text to &amp; so tree-sitter parses cleanly instead of emitting ERROR nodes and dropping symbols (#2922), tracking string/comment/tag/expr/jsx_text contexts and disambiguating < as JSX tag vs. generic/comparison. Wires the masking into the TSX extraction path and adds a fixture plus tests covering entity preservation and &-masking elsewhere. Renames/renumbers several extractor engine internal helpers and rationale symbols.

Worth a look

  • Closing JSX tags never exit jsx_text stategraphify/extract.py:980 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX ampersand mask treats code after a JSX element as JSX textgraphify/extract.py:986 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Nested JSX in expression containers is not maskedgraphify/extract.py:995 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1836 functions depend on the 452 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1836 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1688 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

@santhiprakash
santhiprakash force-pushed the fix/tsx-jsx-text-ampersand branch from 22ae0e9 to 9a8827a Compare August 21, 2026 09:36

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 4 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a _mask_tsx_ampersands pre-parse pass in graphify/extract.py that rewrites bare & in JSX text to &amp; so tree-sitter's TSX grammar parses cleanly instead of emitting ERROR nodes and dropping symbols (#2922). Uses a context-stack walker that distinguishes JSX text from tags, expression containers, strings, comments, and TS code (bitwise &, generic <T> vs JSX tag disambiguation), and short-circuits when the source has no &. Adds tests/test_tsx_jsx_text_ampersand covering entity pass-through and related cases.

Worth a look

  • New test module ends with incomplete function definitiontests/test_tsx_jsx_text_ampersand.py:293 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • prev_code_keyword substring check via 'return' etc. can false-match identifier suffixesgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX ampersand mask rewrites regex literalsgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Fixture test mutates process-wide cwd during extractiontests/test_tsx_jsx_text_ampersand.py:69 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1843 functions depend on the 459 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 35 more — each is listed as a finding

Verification — 1843 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1695 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 1 grounded finding(s) anchored inline below; 42 more finding(s) on lines outside this diff (see the check run).

Comment thread graphify/extract.py
)


def _mask_tsx_ampersands(src: str) -> str:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression_mask_tsx_ampersands()

7 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

@santhiprakash
santhiprakash force-pushed the fix/tsx-jsx-text-ampersand branch from 9a8827a to 4486f5f Compare August 21, 2026 12:42

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a TSX-specific _mask_tsx_ampersands preprocessor (plus _TSX_ENTITY_RE/_TSX_LT_EXPR_PREV) in graphify/extract.py that escapes bare & to &amp; in JSX text content so tree-sitter parses cleanly instead of returning a partial tree (#2922). The masking walks a context stack (tags, strings, comments, expression containers, JSX text) and disambiguates < between JSX tags and generic type parameters to avoid touching bitwise & in code. Includes a new test_tsx_jsx_text_ampersand test.

Worth a look

  • TSX ampersand transform violates source_transform byte contractgraphify/extract.py · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Single-letter uppercase JSX tags are misclassified as genericsgraphify/extract.py:965 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1835 functions depend on the 451 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1835 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1687 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

@santhiprakash
santhiprakash force-pushed the fix/tsx-jsx-text-ampersand branch from 4486f5f to 8abb48b Compare August 21, 2026 15:55

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds _mask_tsx_ampersands (and supporting _TSX_ENTITY_RE / _TSX_LT_EXPR_PREV) to escape bare & to &amp; inside JSX text of TSX source so tree-sitter parses cleanly instead of producing ERROR nodes and dropping symbols (#2922). The masker walks a context stack (tags, expression containers, strings, comments, JSX text) with a < heuristic to distinguish JSX tags from generics/comparisons, leaving bitwise & in code untouched.

Worth a look

  • Incomplete statement leaves new test module syntactically invalidtests/test_tsx_jsx_text_ampersand.py:294 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Multi-character generic arrow type parameters are misclassified as JSXgraphify/extract.py:969 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1837 functions depend on the 453 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1837 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1689 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

…Graphify-Labs#2922)

tree-sitter-typescript requires & in JSX text (the run between >
and < inside a JSX element) to begin an HTML entity reference
(&amp;, &#NN;, &lt;, ...). A bare & produces an ERROR node
and the partial-extraction path surfaces a parse_errors warning (Graphify-Labs#2551,
Graphify-Labs#2788) — even though esbuild, tsc, and React all accept the file.

On the reporter's 3000-file TSX codebase, 31 files (~1 %) were silently
dropping every function, class, and import — UI labels like
"Conexões & Integrações" tripped the gate.

The fix is a context-tracking walker that masks bare & to &amp;
ONLY in JSX text. & inside JSX tag attribute values, { ... }
expression containers, string literals, comments, and TypeScript code
(where it is bitwise AND or an intersection type) is left untouched.

The walker disambiguates JSX tags from TypeScript generic type-parameter
openers (function f<T>, type Bar<T>, <T extends X>,
const pick = <T,>(x: T) => x) by combining a previous-non-whitespace
character set (operator/punctuation ⇒ JSX, alphanumeric ⇒ code) with a
short keyword list (return/yield/new/as/typeof/
void/delete) that flips < after an identifier into JSX
context. Already-formed entities (&amp;, &#NN;, &lt;, …) are
passed through; multi-ampersand JSX text runs are masked independently.

Tag lifecycle is tracked on the context stack: a closing tag pops the
element's jsx_text context (returning to code, an expression container,
or the parent element's text) and a self-closing tag never opens one, so
code following an element — bitwise & included — is never masked (the
first cut left jsx_text on the stack after </tag>, corrupting a later
"a & b" into "a &amp; b" and reintroducing a parse error). The same
tag/generic shape disambiguation runs inside JSX expression containers
with expression context forced on, so nested JSX
({ok ? <span>a & b</span> : null}) is masked like top-level JSX.

Ambiguous shapes — an identifier directly followed by ``>`` — are split
by what follows the ``>``: ``(`` opening a parameter list with an arrow
tail (``<T>(x: T) => x``, ``<TKey>(x: TKey): TKey => x``, function-type
positions) stays code, while ``<A>VoIP & Chamadas</A>`` — single-letter
uppercase components (icon/nav shorthand) and paren-initial JSX text
alike — mask their JSX text like any other tag. Misclassifying a
generic arrow would strand jsx_text and corrupt a later bitwise
``a & b`` into ``a &amp; b`` (a parse error — the very bug class this
mask removes), so the arrow-tail scan is bounded and uppercase-initial
is required for the generic reading. The bytes wrapper round-trips
with surrogateescape so non-UTF-8 files (latin-1 comments, legacy
encodings) keep their bytes exactly: the only byte-level change the
transform makes is the ``&`` -> ``&amp;`` insertion itself, never a
U+FFFD rewrite of unrelated bytes.

Adds tests/test_tsx_jsx_text_ampersand.py (38 tests, including regression
canaries for bitwise AND, JSX expression &&, existing &amp;,
arrow generics, as cast, JSX attribute &, code after a closed element,
self-closing and fragment lifecycle, nested JSX in expression
containers, single-letter components vs. single-letter generics, the
bytes-mask round-trip contract, and the fixture) and
tests/fixtures/tsx_jsx_text_ampersand.tsx (the real-world shape with
mixed JSX-text, JSX-attribute, expression-container, and TS-code &
in a single file).

The mask is wired through a new optional LanguageConfig.source_transform
bytes hook applied in _extract_generic's read path, so extract_js() stays
a pure suffix→config dispatch with a single file read: the TSX-specific
masking lives with _TSX_CONFIG, the walker is unchanged, and non-TSX
languages (source_transform unset) parse exactly as before. Vue SFCs with
lang="tsx" scripts share _TSX_CONFIG and now get the same mask.
@santhiprakash
santhiprakash force-pushed the fix/tsx-jsx-text-ampersand branch from 8abb48b to 459c5ce Compare August 21, 2026 16:10

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a TSX ampersand masking pass in graphify/extract.py: _mask_tsx_ampersands walks JSX source and rewrites bare & in JSX text to &amp; so tree-sitter's TSX grammar parses cleanly, backed by helpers _generic_arrow_tail, the _TSX_ENTITY_RE/_TSX_LT_EXPR_PREV tables, and <-disambiguation to keep bitwise & in TS code untouched. Wires the mask into the TSX extraction path and adds fixtures/tests under tests_test_tsx_jsx_text_ampersand covering nested JSX in expression containers, code after JSX elements, and component render cases.

Worth a look

  • Dangling pytest decorator leaves test module syntactically invalidtests/test_tsx_jsx_text_ampersand.py:313 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Single-letter JSX tags with extends-prefixed props are treated as genericsgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX ampersand mask rewrites regex literals as JSX textgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX source transform is not offset-preservinggraphify/extract.py:884 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • source_transform changes byte offsets, desyncing node positions from original sourcegraphify/extractors/engine.py:2812 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1839 functions depend on the 455 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1839 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1691 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

Replace the bare & -> &amp; rewrite with a single ASCII space

placeholder so the source_transform output stays the same length as the

original file. This preserves tree-sitter byte offsets and keeps

source[start_byte:end_byte] slices aligned with the user's source.

Existing entities (&amp;, &#NN;, etc.) are still passed through

unchanged; only bare ampersands in JSX text are masked. Tests and

docstrings are updated to assert the byte-preserving contract.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a TSX JSX-text & masking pass to graphify/extract.py (_mask_tsx_ampersands plus _generic_arrow_tail, _TSX_ENTITY_RE, _TSX_LT_EXPR_PREV), replacing bare & in JSX text with &amp; so tree-sitter-typescript parses cleanly instead of emitting ERROR nodes and partial trees. The walker tracks tag/expression/string/comment/jsx_text contexts and disambiguates < between JSX tags and generic type parameters/comparisons so bitwise & in code stays untouched. Includes new tsx_jsx_text_ampersand fixture and tests covering masked text, passed-through existing entities, and unmasked code after JSX elements.

Worth a look

  • TSX ampersand mask rewrites regex literal contentsgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1839 functions depend on the 455 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1839 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1691 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a TSX preprocessing pass that masks bare & in JSX text content before parsing, via new _mask_tsx_ampersands, _generic_arrow_tail, and supporting regex/char-set constants, to stop tree-sitter-typescript from producing ERROR nodes and partial extractions. Wires the mask into the TSX extraction path across the engine's language walkers and adds test_tsx_jsx_text_ampersand fixtures. The walker disambiguates JSX tags from generics, comparisons, regex literals, and bitwise-AND so only genuine JSX text runs are rewritten.

Worth a look

  • TSX source transform uses str despite source_transform receiving bytesgraphify/extract.py:899 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • b+1 index may read past end for lone '<' at buffer endgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • JSX attrs beginning with extends are misclassified as genericsgraphify/extract.py:1060 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX extraction now reports locations/snippets against transformed source instead of the original filegraphify/extractors/engine.py:2813 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX source transform shifts parser offsetsgraphify/extractors/engine.py:2813 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1839 functions depend on the 455 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1839 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1691 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

…in TSX mask

- Parse the masked TSX bytes but keep the original source bytes for
  downstream source[start_byte:end_byte] slices, so locations/snippets
  are reported against the user's file rather than the masked copy.
  The mask is byte-length-preserving, so offsets stay aligned.
- Treat `extends` as a JSX attribute when it is followed by `=`, `>`,
  `/`, or EOF; only classify it as a generic constraint when a real
  type expression follows.
- Use the already-safely-read `nxt` character for the upper-case check
  instead of re-indexing `src[b + 1]`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds _mask_tsx_ampersands (plus helpers _generic_arrow_tail and the _TSX_* classification tables) in graphify/extract.py, a state-machine walker that rewrites bare & in TSX JSX text to a byte-aligned placeholder so tree-sitter-typescript parses cleanly while leaving & in tags, expression containers, strings, comments, and TS code (bitwise AND) untouched. The <-disambiguation distinguishes JSX tags from generics/comparisons/generic-arrow functions to avoid stranding the walker in jsx_text mode. Adds tsx_jsx_text_ampersand / ..._component test fixtures.

Worth a look

  • surrogateescape round-trip is not guaranteed byte-length-preservinggraphify/extract.py · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX ampersand mask is not byte-length-preserving for multibyte JSX text, misaligning offsetsgraphify/extract.py · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX source transform uses str contract where extractor passes bytesgraphify/extract.py:900 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1839 functions depend on the 455 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1839 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1691 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

…ssion checks

The previous graphify-labs review raised three advisory high findings

about surrogateescape, multibyte JSX text, and the str/bytes contract.

These tests lock in the byte-preserving behavior that the implementation

already provides: the transform is bytes -> bytes, every non-& byte

round-trips unchanged (including non-UTF-8 surrogateescape bytes and

multibyte UTF-8), and only a bare & in JSX text becomes a single

ASCII space.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds TSX-specific preprocessing to extract.py that masks bare & in JSX text content before parsing, via a new _mask_tsx_ampersands walker (with helpers _generic_arrow_tail, _lt, and the _TSX_* regex/char-set constants) that distinguishes JSX tags from generics, comparisons, regex literals, strings, and expression containers. Wires the mask into the TSX extraction path so files with unescaped & parse cleanly instead of producing ERROR nodes and partial-extraction warnings (#2551, #2922). Includes test_tsx_jsx_text_ampersand_extract covering the new behavior.

Worth a look

  • TSX mask alters byte length for non-BMP chars, breaking offset alignmentgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1841 functions depend on the 457 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1841 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1693 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

…d mask

The latest graphify-labs review flagged an advisory that the TSX mask
alters byte length for non-BMP characters and breaks offset alignment.
Add a non-BMP (4-byte UTF-8 🚀) case to the byte-preservation
parametrize and an end-to-end extraction test to prove the mask stays
byte-length-preserving and that the file extracts silently.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a TSX preprocessing pass in graphify/extract.py that masks bare & in JSX text content so tree-sitter's strict TSX grammar parses cleanly instead of emitting ERROR nodes and dropping symbols (#2922). The core is _mask_tsx_ampersands, a context-stack walker with <-disambiguation helpers (_generic_arrow_tail) to distinguish JSX tags from generics/comparisons and leave bitwise & in code untouched. Wires the mask into the TSX extraction path and adds a regression test asserting code after a JSX element is not masked.

Worth a look

  • _generic_arrow_tail return-type branch uses find(';') which can span past arrowgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • TSX source transform violates byte-oriented source_transform contractgraphify/extract.py · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • source_transform contract omits required byte-length preservationgraphify/extractors/models.py:56 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1843 functions depend on the 459 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1843 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1695 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

…SX ampersand mask

- Replace the `src.find(';', j)` return-type bound in `_generic_arrow_tail`
  with a depth-aware scan that skips `;` inside object types and other nested
  delimiters, and skips `)` inside strings/regex in the parameter list.
- Update the stale ampersand-mask comment to describe the actual one-byte
  space placeholder and byte-alignment contract.
- Document the `LanguageConfig.source_transform` byte-length preservation
  requirement in `extractors/models.py`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds a TSX preprocessing pass in graphify/extract.py that masks bare & in JSX text content before parsing, so tree-sitter's strict TSX grammar no longer emits ERROR nodes and drops the file's symbols (#2922). The new _mask_tsx_ampersands walker distinguishes JSX text from tags, expression containers, strings, comments, regex, and generic type parameters (via _generic_arrow_tail and the </regex heuristics), replacing each bare & with a single space to keep byte offsets aligned. Includes tests/test_tsx_jsx_text_ampersand covering entity pass-through.

No blocking issues surfaced. 7 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 1848 functions depend on the 464 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract() — 477 callers, 42 callees
  • new: _rebuild_code() — 98 callers, 50 callees
  • new: _extract_generic() — 18 callers, 24 callees
  • new: extract_xaml() — 19 callers, 17 callees
  • new: extract_objc() — 27 callers, 9 callees
  • new: extract_js() — 80 callers, 3 callees
  • new: dispatch_command() — 2 callers, 119 callees
  • new: _get_extractor() — 26 callers, 6 callees
  • …and 34 more — each is listed as a finding

Verification — 1848 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1700 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify \_extract\_generic.

The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set

· 42 more finding(s) on lines outside this diff (see the check run).

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.

TSX: bare & in JSX text node triggers partial extraction (tree-sitter-javascript rejects valid JSX)

1 participant