1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages

Add incremental adapter compiler boundary

This commit is contained in:
Andraxion 2026-07-25 19:08:39 -04:00
parent 82b3b90521
commit 696b62f9f8
20 changed files with 1592 additions and 122 deletions

View file

@ -13,7 +13,15 @@ from contextlib import contextmanager
from pathlib import Path
from .errors import DocForgeError
from .models import Edge, Node, ProjectService, ProjectSnapshot
from .models import (
BuildReportingProject,
Edge,
IncrementalStateProject,
Node,
ProjectService,
ProjectSnapshot,
ProjectState,
)
from .project import project_root_fingerprint
INDEX_SCHEMA_VERSION = 1
@ -86,6 +94,11 @@ class ProjectIndex:
def build(self) -> dict[str, object]:
snapshot = self.project.load()
status = _status(snapshot)
build_report = (
self.project.build_report() if isinstance(self.project, BuildReportingProject) else None
)
if build_report is not None and build_report.get("mode") != "incremental":
build_report = None
cache_root = snapshot.descriptor.cache_root
cache_root.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(
@ -183,9 +196,16 @@ class ProjectIndex:
except Exception:
temporary.unlink(missing_ok=True)
raise
return {**status, "database": str(self.path)}
result: dict[str, object] = {**status, "database": str(self.path)}
if build_report is not None:
result["build"] = build_report
return result
def check(self) -> dict[str, object]:
if isinstance(self.project, IncrementalStateProject):
state = self.project.incremental_state()
if state is not None:
return self._check_incremental_state(state)
snapshot = self.project.load()
expected = _status(snapshot)
with _read_connection(self.path) as connection:
@ -233,6 +253,64 @@ class ProjectIndex:
raise DocForgeError("invalid_index", "Derived index rows do not match source")
return {**expected, "database": str(self.path)}
def _check_incremental_state(self, state: ProjectState) -> dict[str, object]:
"""Validate a published index against cheap current source identity."""
descriptor = self.project.descriptor
identity = {
"project_id": descriptor.project_id,
"project_root_fingerprint": project_root_fingerprint(descriptor.root),
"revision": state.revision,
"source_hash": state.source_hash,
"index_schema_version": INDEX_SCHEMA_VERSION,
"adapter": descriptor.adapter,
}
with _read_connection(self.path) as connection:
application_id = connection.execute("PRAGMA application_id").fetchone()[0]
schema_version = connection.execute("PRAGMA user_version").fetchone()[0]
if application_id != APPLICATION_ID or schema_version != INDEX_SCHEMA_VERSION:
raise DocForgeError("invalid_index", "Derived index has an unsupported schema")
metadata = dict(connection.execute("SELECT key, value FROM metadata"))
for key, expected in identity.items():
if metadata.get(key) != str(expected):
raise DocForgeError(
"stale_index", "Derived index does not match canonical source", field=key
)
integrity = connection.execute("PRAGMA integrity_check").fetchone()
if integrity is None or integrity[0] != "ok":
raise DocForgeError("invalid_index", "Derived index failed SQLite integrity check")
indexed_nodes = tuple(
_row_to_node(row)
for row in connection.execute("SELECT * FROM nodes ORDER BY node_id")
)
indexed_edges = tuple(
Edge(*row)
for row in connection.execute(
"SELECT source_id, relation, target_id FROM edges "
"ORDER BY source_id, relation, target_id"
)
)
node_hash = _node_hash(indexed_nodes)
edge_hash = _edge_hash(indexed_edges)
fts_count = connection.execute("SELECT COUNT(*) FROM node_fts").fetchone()[0]
if (
metadata.get("node_hash") != node_hash
or metadata.get("edge_hash") != edge_hash
or metadata.get("node_count") != str(len(indexed_nodes))
or metadata.get("edge_count") != str(len(indexed_edges))
or fts_count != len(indexed_nodes)
):
raise DocForgeError("invalid_index", "Derived index rows do not match metadata")
return {
**identity,
"node_hash": node_hash,
"node_count": len(indexed_nodes),
"edge_hash": edge_hash,
"edge_count": len(indexed_edges),
"status": "ok",
"database": str(self.path),
}
def get_node(self, node_id: str) -> dict[str, object]:
checked = self.check()
with _read_connection(self.path) as connection: