Some checks failed
CI / skinny-install (aco) (push) Successful in 53s
CI / lint-test (push) Successful in 1m15s
CI / skinny-install (api) (push) Successful in 26s
CI / skinny-install (bcda) (push) Successful in 29s
CI / skinny-install (bib) (push) Successful in 31s
CI / skinny-install (bls) (push) Successful in 27s
CI / skinny-install (ccw) (push) Successful in 30s
CI / skinny-install (cli) (push) Successful in 30s
CI / skinny-install (cms) (push) Successful in 26s
CI / skinny-install (conf) (push) Successful in 26s
CI / skinny-install (opps) (push) Successful in 28s
CI / skinny-install (perf) (push) Successful in 29s
CI / skinny-install (pfs) (push) Successful in 27s
Infra CI / notebooks (push) Successful in 7s
Infra CI / zotero (push) Successful in 7s
CI / skinny-install (rex) (push) Successful in 27s
Infra CI / docs (push) Failing after 9s
Infra CI / api (push) Successful in 8s
Infra CI / mc (push) Successful in 6s
Package Supply Chain / pkg-supply-chain (push) Successful in 52s
CI / lint-test (pull_request) Successful in 1m18s
CI / skinny-install (aco) (pull_request) Successful in 44s
CI / skinny-install (api) (pull_request) Successful in 24s
CI / skinny-install (bcda) (pull_request) Successful in 32s
CI / skinny-install (bib) (pull_request) Successful in 30s
CI / skinny-install (bls) (pull_request) Successful in 25s
CI / skinny-install (ccw) (pull_request) Successful in 23s
CI / skinny-install (cli) (pull_request) Successful in 30s
CI / skinny-install (cms) (pull_request) Successful in 27s
CI / skinny-install (conf) (pull_request) Successful in 30s
CI / skinny-install (opps) (pull_request) Successful in 29s
CI / skinny-install (perf) (pull_request) Successful in 32s
CI / skinny-install (pfs) (pull_request) Successful in 33s
Infra CI / notebooks (pull_request) Successful in 7s
Infra CI / zotero (pull_request) Successful in 6s
Infra CI / docs (pull_request) Failing after 6s
CI / skinny-install (rex) (pull_request) Successful in 27s
Infra CI / api (pull_request) Successful in 6s
Infra CI / mc (pull_request) Successful in 6s
4-layer architecture for tracking coverage as semantic AST nodes rather than raw line numbers: parse → enrich → runtime → plan. Stable node identities, Ruff/ty diagnostic attachment, coverage mapping, priority scoring heuristic, and drift-resistant rolling state persistence. 39 tests, ruff clean.
120 lines
2.9 KiB
Python
120 lines
2.9 KiB
Python
"""Semantic node model.
|
|
|
|
Each meaningful AST construct becomes a ``SemanticNode`` with a stable
|
|
identity derived from its module path, qualified symbol name, node kind,
|
|
and ordinal position — never from line numbers.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import enum
|
|
import hashlib
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
class NodeKind(enum.Enum):
|
|
"""Taxonomy of tracked AST constructs."""
|
|
|
|
FUNCTION = "function"
|
|
METHOD = "method"
|
|
CLASS = "class"
|
|
BRANCH_IF = "branch_if"
|
|
BRANCH_ELSE = "branch_else"
|
|
BRANCH_ELIF = "branch_elif"
|
|
MATCH_CASE = "match_case"
|
|
EXCEPT_HANDLER = "except_handler"
|
|
FOR_LOOP = "for_loop"
|
|
WHILE_LOOP = "while_loop"
|
|
WITH_BLOCK = "with_block"
|
|
RETURN = "return"
|
|
RAISE = "raise"
|
|
ASSERT = "assert"
|
|
|
|
|
|
def _make_node_id(
|
|
module: str,
|
|
symbol_context: list[str],
|
|
kind: NodeKind,
|
|
ordinal: int,
|
|
) -> str:
|
|
"""Build a stable, human-readable node identity string.
|
|
|
|
Example: ``app.config::load_settings::branch_if[1]``
|
|
"""
|
|
parts = [module]
|
|
if symbol_context:
|
|
parts.append("::".join(symbol_context))
|
|
parts.append(f"{kind.value}[{ordinal}]")
|
|
return "::".join(parts)
|
|
|
|
|
|
@dataclass
|
|
class Span:
|
|
"""Byte-offset span within a source file (line numbers as metadata only)."""
|
|
|
|
start_line: int
|
|
end_line: int
|
|
start_col: int = 0
|
|
end_col: int = 0
|
|
|
|
|
|
@dataclass
|
|
class RuntimeInfo:
|
|
"""Runtime coverage data attached after test execution."""
|
|
|
|
hit: bool = False
|
|
hit_count: int = 0
|
|
tests_seen: list[str] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class SemanticNode:
|
|
"""A single tracked semantic unit in the codebase."""
|
|
|
|
node_id: str
|
|
kind: NodeKind
|
|
span: Span
|
|
module: str
|
|
symbol_context: list[str] = field(default_factory=list)
|
|
source_hash: str = ""
|
|
|
|
# Enrichment: Ruff diagnostics
|
|
ruff_codes: list[str] = field(default_factory=list)
|
|
|
|
# Enrichment: ty diagnostics
|
|
ty_codes: list[str] = field(default_factory=list)
|
|
|
|
# Runtime coverage
|
|
runtime: RuntimeInfo = field(default_factory=RuntimeInfo)
|
|
|
|
# Planner state
|
|
status: str = "unexplored"
|
|
priority: float = 0.0
|
|
attempts: int = 0
|
|
intent_tags: list[str] = field(default_factory=list)
|
|
|
|
@staticmethod
|
|
def build(
|
|
module: str,
|
|
symbol_context: list[str],
|
|
kind: NodeKind,
|
|
ordinal: int,
|
|
span: Span,
|
|
source_fragment: str = "",
|
|
) -> SemanticNode:
|
|
"""Construct a node with a stable ID and optional source hash."""
|
|
node_id = _make_node_id(module, symbol_context, kind, ordinal)
|
|
src_hash = ""
|
|
if source_fragment:
|
|
src_hash = hashlib.sha1(
|
|
source_fragment.encode(), usedforsecurity=False
|
|
).hexdigest()[:12]
|
|
return SemanticNode(
|
|
node_id=node_id,
|
|
kind=kind,
|
|
span=span,
|
|
module=module,
|
|
symbol_context=list(symbol_context),
|
|
source_hash=src_hash,
|
|
)
|