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]>
10 KiB
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_importsfor extras (harnessx.*). - pytest marker already exists for
docker; extra-enabled tests would follow that skip pattern. - CI
checksjob:uv sync --group devthen pytest, no optional extras.
Integration surfaces
parse_file(path, text, language) -> FileIRis 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-sitterand 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):
- Pin compatible
tree-sitter+tree-sitter-{python,javascript,c,cpp,java}wheel ABIs (Language(mod.language())vs olderbuild_library). PyPI:tree-sitter0.26 (2026-06-30),tree-sitter-javascript0.25. - Per-language CST node names for assignment, call, identifier, string, function (JS
assignment_expressionvs Cassignment_expressionvs Javaassignment_expressionvs Pythonassignment). Shared walker needs a small node-kind table, not four visitors. - C++: separate
tree-sitter-cppvs C grammar;preprocessmaps.cpptocpp. - Whether default CI should add a second job
uv sync --extra semanticor 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_filestays the seam. - Cons:
ir.pyis 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_fileconsumers.
Option C: Hybrid (recommended for design)
- New:
semantic/cst.py: one walker: tree-sitterNode→Bind/CallSite/FunctionIRusing 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 overridetree_sitter*; pytest markersemantic; CI extra-free job unchanged; optional second job or skip. - Extend:
parse_fileprefers extra CST when a grammar loads; else Python ast; elselanguage_unsupported. Delete or stop callingparse_tree_sitter_clias 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
FileIRas frozen contract; do not add dispositions. - Decide probe semantics:
tree_sitter_availableshould 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 isParser(Language(mod.language()));build_libraryis gone. FileIRis the only overlay seam; taint does not need language visitors.- Host CLI presence must not count as a successful overlay parse (Req 4.3).
- Official PyPI wheels:
Research Log
PyPI ABI and load API
- Context: Pin wheels that work on Python 3.11+ without compiling grammars in CI.
- Sources Consulted: https://pypi.org/project/tree-sitter/ ; https://pypi.org/project/tree-sitter-javascript/ ; https://pypi.org/project/tree-sitter-cpp/ ; py-tree-sitter 0.23 release notes (Language capsule, Parser constructor).
- Findings: Grammars ship abi3 wheels. Load with
Language(tree_sitter_<lang>.language())thenParser(language). Do not useLanguage.build_library. - Implications: Extra lists core + five grammar packages. mypy ignores
tree_sitter*.
Probe vs grammar
- Context:
tree_sitter_available()is true when CLI is on PATH with zero grammars. - Findings: CLI parse cannot populate
FileIRwithout a walker. Extra Python API is the product parse. - Implications: Split
has_semantic_extra()from per-languagegrammar_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-sitterplus 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-sitterminor 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
semanticmarker skips when extra absent. GitHub Actions adds a jobuv sync --extra semantic --group devthat runs marked tests. Defaultchecksjob 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), returnFileIR(parse_ok=False, reason=parse_failed). - Rationale: Partial IR would invite false demote.
Synthesis
- Generalization: One
parse_filedispatch; 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
- https://pypi.org/project/tree-sitter/
- https://github.com/tree-sitter/py-tree-sitter/releases
src/openultrasast/harness_ext.pyextra guard pattern