Files
Open_Ultra_SAST/.kiro/specs/tree-sitter-overlay-extra/research.md
T
Marius CiepluchandClaude Opus 4.8 39a1873e5b docs: plain-English pass over the active specs
Ran the humanizer rules over 79 prose files in the active specs (brief,
design, requirements, research and narrative docs). Removed em dashes,
significance inflation, forced rule-of-three, bold-label list items and
filler. Skipped tasks.md checklists and the retired specs (harnessx-*,
openrouter-sast-harness, unseen-repo-evaluation).

Every number, path, command, hash and identifier was preserved: checked
each file against HEAD for drift in numbers, inline code and code fences,
zero drift across all 79 files.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-10-12 18:16:41 +02:00

10 KiB
Raw Blame History

Gap Analysis: tree-sitter-overlay-extra

Requirements are generated but not approved. Analysis proceeds to inform design; it does not treat requirements as locked.

Steering product.md / tech.md / structure.md are absent. Context is the overlay package, harness_ext extra pattern, pyproject.toml, CI, and PyPI tree-sitter bindings.


1. Current State

Assets that already exist

Asset Role today
src/openultrasast/semantic/ir.py FileIR / Bind / CallSite; Python ast extractor (~170 lines); parse_tree_sitter_cli always returns None
src/openultrasast/semantic/engines.py Probe: env flag, importlib.util.find_spec("tree_sitter"), or tree-sitter CLI on PATH
src/openultrasast/semantic/taint.py Language-agnostic taint on FileIR (names, constants, fact tables)
src/openultrasast/semantic/overlay.py Dispositions; already consumes parse_file
src/openultrasast/ruleset/semantic/*.toml JS/C/Java facts already written
src/openultrasast/harness_ext.py Pattern for optional extra: find_spec, lazy import, mypy override, cold-import tests
pyproject.toml dependencies = []; only extra is harnessx
tests/test_semantic_overlay.py JS unsupported without probe; JS IR only via monkeypatch, not a real grammar

Conventions

  • Optional native/heavy deps stay off the default install; capability probe must not import at module load (harness_ext, Docker sandbox probe).
  • mypy ignore_missing_imports for extras (harnessx.*).
  • pytest marker already exists for docker; extra-enabled tests would follow that skip pattern.
  • CI checks job: uv sync --group dev then pytest, no optional extras.

Integration surfaces

  • parse_file(path, text, language) -> FileIR is the only seam taint/overlay need.
  • tree_sitter_available() is a presence probe, not "grammar produced IR". Host CLI on PATH makes it True even with no PyPI wheels (this environment has /home/linuxbrew/.../tree-sitter and no languages).
  • Pair eval already falls back to inventory when a language never promotes/demotes.

2. Requirements Feasibility

Req Need Gap
1 Extra-free quick Empty core deps; no import of parser wheels at load Constraint met. Missing extra cannot currently break quick because there is no extra. Must keep it that way.
2 Structured parse for Py/JS/C/C++/Java Real CST → FileIR; JS promote; C/Java demote on constant/sanitizer Missing. CLI parse never builds FileIR. No [semantic] extra. No CST walker.
3 Fail closed language_unsupported / parse_failed; Python ast last resort Mostly present via parse_file fallback. Syntax-error Python already parse_failed. JS/C/Java without IR already language_unsupported.
4 One shared extract path Not a visitor per language; CLI without extra must not fake success Missing walker. CLI stub already returns None (does not fake success). Probe can still report “available” without IR — constraint for Req 4.3 wording vs tree_sitter_available().
5 Overlay contracts No LLM parse; Joern not required; corroboration; promotions-only sandbox; gate inventory-only Present in overlay/prove/gate. Do not reopen.
6 Dual test matrix Extra-free always; extra-on JS + C/Java; skip if extra absent Partial. Extra-free tests exist. Extra-on tests are mocked. No pytest marker. CI does not install a semantic extra.

Complexity signal: external native-wheel extra + one algorithmic CST walk + pytest/CI matrix. Not CRUD.

Research needed (design phase, not blockers):

  1. Pin compatible tree-sitter + tree-sitter-{python,javascript,c,cpp,java} wheel ABIs (Language(mod.language()) vs older build_library). PyPI: tree-sitter 0.26 (2026-06-30), tree-sitter-javascript 0.25.
  2. Per-language CST node names for assignment, call, identifier, string, function (JS assignment_expression vs C assignment_expression vs Java assignment_expression vs Python assignment). Shared walker needs a small node-kind table, not four visitors.
  3. C++: separate tree-sitter-cpp vs C grammar; preprocess maps .cpp to cpp.
  4. Whether default CI should add a second job uv sync --extra semantic or only skip-marked tests on the extra-free job.

3. Implementation Approach Options

Option A: Extend ir.py / engines.py only

Put extra pins in pyproject.toml, implement API parse + CST walk inside ir.py, tighten tree_sitter_available().

  • Pros: Few files; parse_file stays the seam.
  • Cons: ir.py is already ~277 lines (Python ast + dead CLI). A walker + language table will bloat it and mix packaging probe with extract.

Option B: New components only

New semantic/cst.py + semantic/extra.py; leave ir.py as Python ast; overlay calls a new entrypoint.

  • Pros: Clean extra guard.
  • Cons: Two parse entrypoints; easy to fork parse_file consumers.
  • New: semantic/cst.py: one walker: tree-sitter Node → Bind / CallSite / FunctionIR using a node-kind map.
  • New or thin: extra guard modeled on harness_ext (has_semantic_extra, lazy grammar load, never import wheels at module load).
  • Extend: pyproject.toml [project.optional-dependencies] semantic = [...]; mypy override tree_sitter*; pytest marker semantic; CI extra-free job unchanged; optional second job or skip.
  • Extend: parse_file prefers extra CST when a grammar loads; else Python ast; else language_unsupported. Delete or stop calling parse_tree_sitter_cli as a success path (Req 4.3).
  • Do not extend: taint.py, overlay.py, facts, gate, pair catalog.

Trade-offs: Extra files, but ir.py shrinks (drop dead CLI), taint stays language-agnostic, extra follows an existing packaging seam.

4. Effort and Risk

  • Effort: M (3–7 days). Extra pins + walker + dual tests. Overlay/taint reuse. Not a new adjudicator.
  • Risk: Medium. Native wheels and grammar ABI pins; CST node-name drift across languages; CI without extra must stay green. Mitigate with skip markers and a version pin table in design.

5. Design-phase recommendations

  • Prefer Option C.
  • Treat FileIR as frozen contract; do not add dispositions.
  • Decide probe semantics: tree_sitter_available should mean "can produce FileIR for this language", not "CLI binary exists".
  • Carry research items 1–4 into design (ABI pins, node-kind map, cpp package, CI job vs skip).
  • Do not pull Joern, pair harvest, or CWE-121 into this spec.

Summary (design discovery)

  • Feature: tree-sitter-overlay-extra
  • Discovery Scope: Extension of parse_file / overlay IR; optional extra packaging
  • Key Findings:
    • Official PyPI wheels: tree-sitter (0.26), tree-sitter-python, tree-sitter-javascript (0.25), tree-sitter-c (0.24.x), tree-sitter-cpp (0.23.4), tree-sitter-java. API is Parser(Language(mod.language())); build_library is gone.
    • FileIR is the only overlay seam; taint does not need language visitors.
    • Host CLI presence must not count as a successful overlay parse (Req 4.3).

Research Log

PyPI ABI and load API

Probe vs grammar

  • Context: tree_sitter_available() is true when CLI is on PATH with zero grammars.
  • Findings: CLI parse cannot populate FileIR without a walker. Extra Python API is the product parse.
  • Implications: Split has_semantic_extra() from per-language grammar_for. Remove CLI as a success path.

Architecture Pattern Evaluation

Option Description Strengths Risks Notes
A Extend ir.py only Walker + extra inside ir.py Few files Bloats 277-line module Rejected
B New entrypoint Overlay calls cst.py directly Clean extra Forks parse_file Rejected
C Hybrid Extra guard + cst walker; parse_file dispatch Matches harness_ext; FileIR frozen Node-kind table Selected

Design Decisions

Decision: Adopt official grammar wheels, build one walker

  • Context: Avoid per-language AST visitors (Req 4).
  • Alternatives: tree-sitter CLI sexp; py-tree-sitter-languages (unmaintained); hand-rolled JS/C parsers.
  • Selected: openultrasast[semantic] = tree-sitter plus python/javascript/c/cpp/java grammar packages. One CST walk keyed by a node-kind map.
  • Rationale: Wheels are MIT, maintained, match in-scope languages. Taint stays language-agnostic.
  • Trade-offs: Native wheels in the extra; ABI pins. Core install stays empty.
  • Follow-up: Verify Parser constructor against the pinned tree-sitter minor at implement time.

Decision: Extra-free CI stays default; extra-on is a second job plus skip marker

  • Context: Req 6 dual matrix.
  • Selected: pytest semantic marker skips when extra absent. GitHub Actions adds a job uv sync --extra semantic --group dev that runs marked tests. Default checks job unchanged.
  • Rationale: Default CI must not fail without wheels. Extra-on tests must actually run somewhere.
  • Trade-offs: Two pytest invocations in CI.

Decision: has_error implies parse_failed

  • Context: tree-sitter is error-tolerant; Req 3.4 wants fail-closed on syntax errors.
  • Selected: If tree.root_node.has_error (or ERROR children), return FileIR(parse_ok=False, reason=parse_failed).
  • Rationale: Partial IR would invite false demote.

Synthesis

  • Generalization: One parse_file dispatch; extra is a grammar source, not a second overlay.
  • Build vs adopt: Adopt tree-sitter wheels; build only the FileIR projection (binds/calls).
  • Simplification: No CLI sexp walker; no second parse API; no taint/overlay changes.

Risks & Mitigations

  • Grammar ABI mismatch: pin extras together; extra-on CI job catches import/parse failure.
  • Node-type drift: table in cst.py; tests assert JS eval and C printf on real trees.
  • CLI still on PATH: must not set parse_ok.

References