Skip to content
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7dc50d0
feat(edit): evaluate frozen constraints read-only
aimasteracc Aug 12, 2026
7d340e9
fix(edit): bind frozen constraint evidence
aimasteracc Aug 12, 2026
3d3c7f8
test(edit): preserve portable snapshot seams
aimasteracc Aug 12, 2026
22afcfa
fix(edit): close frozen constraint review gaps
aimasteracc Aug 12, 2026
bb1df70
fix(index): bind reusable capability scope
aimasteracc Aug 12, 2026
688e4d5
fix(edit): require exact constraint authority
aimasteracc Aug 12, 2026
5c7aad7
test(edit): preserve config capture seams
aimasteracc Aug 12, 2026
4b2a448
test(edit): emulate portable config evidence
aimasteracc Aug 12, 2026
e46914a
test(edit): complete portable oracle seams
aimasteracc Aug 12, 2026
813e1dc
fix(edit): harden read-only constraint authority
aimasteracc Aug 12, 2026
4c2847b
fix(edit): enforce bounded constraint reads
aimasteracc Aug 12, 2026
55f7e8d
fix(edit): bound response materialization
aimasteracc Aug 12, 2026
29fd21b
test(edit): scope read-only errors by platform
aimasteracc Aug 12, 2026
bab41f8
test(edit): isolate facade snapshot contract
aimasteracc Aug 12, 2026
b53c5ea
fix(edit): certify bounded cross-platform constraints
aimasteracc Aug 12, 2026
12c6db7
fix(edit): close portable constraint gaps
aimasteracc Aug 12, 2026
0cfb5d1
fix(edit): normalize portable index errors
aimasteracc Aug 12, 2026
a70fc6b
test(edit): align portable snapshot contracts
aimasteracc Aug 12, 2026
34f6473
fix(edit): normalize corrupt portable indexes
aimasteracc Aug 12, 2026
28f063c
fix(edit): close portable constraint authority
aimasteracc Aug 12, 2026
fe4815f
test(edit): make portable oracles cross-platform
aimasteracc Aug 12, 2026
178b52d
fix(edit): close frozen constraint authority
aimasteracc Aug 13, 2026
4c1d981
test(index): align portable Windows contracts
aimasteracc Aug 13, 2026
bb38b2d
fix(cache): close final constraint gates
aimasteracc Aug 13, 2026
f86af1d
fix(snapshot): close final review gates
aimasteracc Aug 13, 2026
313ea8f
fix(snapshot): close cross-platform review gates
aimasteracc Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ TSA ships curated workflows under `.claude/skills/tsa-*/`:

Each skill ships an `allowed-tools` subset + procedure recipe + decision-surface schema, so the agent doesn't have to triage 8 tools on every question.

### 323 CLI flags
### 324 CLI flags

Superset of CodeGraph's CLI surface. Highlights:

Expand Down
2 changes: 1 addition & 1 deletion README_ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ TSA は `.claude/skills/tsa-*/` 下にキュレーション済みワークフロ

各 skill は `allowed-tools` ツール サブセット + 手順レシピ + 決定面スキーマを同梱し、エージェントは 8 個のツールから毎回選別する必要がありません。

### 323 の CLI フラグ
### 324 の CLI フラグ

CodeGraph の CLI の厳密な上位互換。主なもの:

Expand Down
2 changes: 1 addition & 1 deletion README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ TSA 在 `.claude/skills/tsa-*/` 下提供精选工作流:

每个 skill 都带 `allowed-tools` 工具子集 + 操作流程 + 决策面 schema,agent 不必在 8 个工具间反复挑选。

### 323 个 CLI flag
### 324 个 CLI flag

CodeGraph CLI 的严格超集。亮点:

Expand Down
4 changes: 2 additions & 2 deletions benchmarks/codegraph_compare/readme_claim_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
re.compile(r"13 言語は `?pipeline_registered|13 种语言为 `?pipeline_registered"),
re.compile(r"5 言語 gap|5 语言 gap"),
re.compile(r"1 ワークフロー|一个工作流"),
re.compile(r"\b323 CLI flags\b", re.IGNORECASE),
re.compile(r"323 の CLI フラグ|323 个 CLI flag", re.IGNORECASE),
re.compile(r"\b324 CLI flags\b", re.IGNORECASE),
re.compile(r"324 の CLI フラグ|324 个 CLI flag", re.IGNORECASE),
re.compile(r"\b(?:FTS5|BM25)\b"),
re.compile(r"\bE[0-4]\b"),
re.compile(r"\bE2E\b", re.IGNORECASE),
Expand Down
1 change: 1 addition & 0 deletions docs/CODEMAPS/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Categories of CLI surface:
- `--call-graph` — caller/callee graph

### Code Quality
- `--check-constraints [--constraints-read-only]` — evaluate architecture constraints; read-only mode forwards `persist=false` and never updates violation rows
- `--code-patterns` — smell detection
- `--refactor` — concrete refactor recipes
- `--outline` — hierarchical outline (package → class → method, no bodies)
Expand Down
2 changes: 1 addition & 1 deletion docs/api/facade-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Reading the tables:
| --- | --- | --- | --- |
| `ast_diff` | `diff_snapshot_id`, `file_path`, `include_node_bodies`, `language`, `mode`, `new_file`, `new_ref`, `new_source`, `old_file`, `old_ref`, `old_source`, `output_format` | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--ast-diff` |
| `classify` | `diff_snapshot_id`, `file_path`, `hunk_cap`, `include_ast_nodes`, `language`, `mode`, `new_ref`, `new_source`, `old_ref`, `old_source`, `output_format` | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--semantic-classify` |
| `constraints` | `output_format`, `path_filter`, `severity_min` | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--check-constraints` |
| `constraints` | `diff_snapshot_id`, `output_format`, `path_filter`, `persist`, `scope_paths`, `severity_min` | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--check-constraints` |
| `guard` | `modification_type`*, `symbol`*, `file_path` | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--modification-guard` |
| `impact` | `agent_summary_only`, `capture_diff_snapshot`, `compact_only`, `include_tests`, `mode`, `output_format`, `pr_url`, `resource_profile`, `scope_mode`, `scope_paths` — `capture_diff_snapshot` is an explicit boolean producer available only to same-process POSIX consumers | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--change-impact` |
| `pr` | `include_call_graph`, `mode`, `output_format`, `pr_url` | `success`*, `verdict`*, `agent_summary`, `error` + action payload | `--pr-review` |
Expand Down
6 changes: 6 additions & 0 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,9 @@ uv run tree-sitter-analyzer large_file.java --query-key methods --filter "public
# 4. Extract only the lines you need
uv run tree-sitter-analyzer large_file.java --partial-read --start-line 100 --end-line 150
```

### Constraint evaluation

`--check-constraints` evaluates the project architecture rules. Add
`--constraints-read-only` to forward `persist=false`, opening an existing cache
read-only and leaving its schema and violation rows unchanged.
4 changes: 2 additions & 2 deletions tests/contracts/test_language_support_inventory_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ def test_translated_readmes_reject_unregistered_quantitative_marketing(
(
"Python 3.10 以上",
"8 MCP ツール",
"### 323 の CLI フラグ",
"### 324 の CLI フラグ",
"22 言語プラグイン",
"13 は `pipeline_registered`",
"3 は `index_admitted`",
Expand All @@ -471,7 +471,7 @@ def test_translated_readmes_reject_unregistered_quantitative_marketing(
(
"需要 Python 3.10+",
"8 个 MCP 工具",
"### 323 个 CLI flag",
"### 324 个 CLI flag",
"22 个语言插件",
"13 个为 `pipeline_registered`",
"3 个为 `index_admitted`",
Expand Down
11 changes: 4 additions & 7 deletions tests/integration/test_diff_snapshot_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,19 +245,16 @@ def test_frozen_scope_inventory_does_not_admit_post_capture_mutation(


@POSIX_SNAPSHOT_TEST
def test_staged_symlink_records_unsupported_source_kind(tmp_path: Path) -> None:
# PR #1252 review thread 3746878582.
def test_staged_symlink_cannot_claim_shared_authoritative_generation(
tmp_path: Path,
) -> None:
root = _repo(tmp_path)
(root / "module.py").symlink_to("old.py")
_git(root, "add", "module.py")

result = snapshots.DiffSnapshotRegistry().create(str(root), "staged", [])

assert result["success"] is True
record = next(
item for item in result["changed_records"] if item["path"] == "module.py"
)
assert (record["new_kind"], record["new_mode"]) == ("symlink", "120000")
assert result == {"success": False, "error_code": "SOURCE_SCOPE_UNSAFE"}


def test_entry_parts_rejects_malformed_git_header() -> None:
Expand Down
58 changes: 58 additions & 0 deletions tests/integration/test_diff_snapshot_constraint_planes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Integration coverage for staged snapshot constraint/source planes."""

from __future__ import annotations

import subprocess
from pathlib import Path

import tree_sitter_analyzer.diff_snapshot_registry as snapshots
from tests.unit._diff_snapshot_support import POSIX_SNAPSHOT_TEST, make_repo


def _git(root: Path, *args: str) -> None:
subprocess.run(["git", *args], cwd=root, check=True, capture_output=True)


def _repo(tmp_path: Path) -> Path:
return make_repo(tmp_path)


@POSIX_SNAPSHOT_TEST
def test_staged_snapshot_constraint_config_comes_from_index_plane(
tmp_path: Path,
) -> None:
# PR #1254 review 3765536002: staged constraints are index-plane evidence.
root = _repo(tmp_path)
config = root / "architectural-constraints.yml"
config.write_bytes(b"version: 1\nconstraints: []\n")
_git(root, "add", config.name)
config.write_bytes(b"version: 1\nconstraints: [invalid-worktree]\n")
registry = snapshots.DiffSnapshotRegistry()

created = registry.create(str(root), "staged", [])
consumer, error = registry.acquire(str(created["diff_snapshot_id"]), str(root))

assert error is None
assert consumer is not None
assert consumer.snapshot.constraint_config_path == config.name
assert consumer.snapshot.constraint_config_data == b"version: 1\nconstraints: []\n"
assert consumer.snapshot.staged_config_matches_worktree is False
consumer.release()


@POSIX_SNAPSHOT_TEST
def test_staged_snapshot_records_source_plane_divergence(tmp_path: Path) -> None:
# PR #1254 review 3765536016: live graphs cannot represent dirty staged sources.
root = _repo(tmp_path)
(root / "old.py").write_text("value = 2\n")
_git(root, "add", "old.py")
(root / "old.py").write_text("value = 3\n")
registry = snapshots.DiffSnapshotRegistry()

created = registry.create(str(root), "staged", [])
consumer, error = registry.acquire(str(created["diff_snapshot_id"]), str(root))

assert error is None
assert consumer is not None
assert consumer.snapshot.staged_source_matches_worktree is False
consumer.release()
6 changes: 5 additions & 1 deletion tests/integration/test_diff_snapshot_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,11 @@ def oracle(*args, deadline=None):
monkeypatch.setattr(snapshots.time, "monotonic", lambda: 100.0)
monkeypatch.setattr(snapshots, "oracle_generation", oracle)
consumer, error = registry.acquire(str(created["diff_snapshot_id"]), str(tmp_path))
assert (consumer, error, deadlines) == (None, "DIFF_SNAPSHOT_EXPIRED", [135.0])
assert (consumer, error, deadlines) == (
None,
"DIFF_SNAPSHOT_EXPIRED",
[35.0, 35.0],
)


def test_strict_scope_binds_only_scoped_change_and_valid_scope(monkeypatch):
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/_diff_snapshot_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,56 @@ def fake_oracle(project_root, mode="diff", *, deadline=None, manifest=None):
return "sg_test", identity

monkeypatch.setattr(snapshots, "oracle_generation", fake_oracle)
monkeypatch.setattr(
snapshots, "shared_source_generation", lambda *_a, **_k: "sg_test"
)
from types import SimpleNamespace

import tree_sitter_analyzer.index_source_snapshot as source_snapshot
from tree_sitter_analyzer.source_oracle import SafePath

monkeypatch.setattr(
source_snapshot,
"capture_current_source_snapshot",
lambda *_a, **_k: SimpleNamespace(
state="exact", generation="sg_test", reason=None
),
)

original_safe_workspace_path = snapshots.safe_workspace_path

def fake_safe_workspace_path(_root, relative, **kwargs):
try:
return original_safe_workspace_path(_root, relative, **kwargs)
except Exception as exc:
if "WORKSPACE_UNSUPPORTED" not in str(exc):
raise
target = root / relative
if not target.exists():
return SafePath(data=None, metadata=(b"missing",), kind="missing")
if target.is_dir():
return SafePath(data=None, metadata=(b"directory",), kind="directory")
if not target.is_file():
return SafePath(data=None, metadata=(b"unsafe",), kind="unsafe")
data = target.read_bytes()
return SafePath(
data=data,
metadata=(b"test," + str(len(data)).encode("ascii"),),
kind="file",
)

monkeypatch.setattr(snapshots, "safe_workspace_path", fake_safe_workspace_path)
import tree_sitter_analyzer.source_oracle as source_oracle

monkeypatch.setattr(source_oracle, "safe_workspace_path", fake_safe_workspace_path)
monkeypatch.setattr(
snapshots,
"frozen_index_constraint_config",
lambda *_a, **_k: (None, None, ()),
)
monkeypatch.setattr(
snapshots, "frozen_index_sources_match_worktree", lambda *_a, **_k: True
)
monkeypatch.setattr(
snapshots,
"capture_inventory",
Expand Down
59 changes: 59 additions & 0 deletions tests/unit/cli/test_argument_parser_builder_constraint_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""Focused parser coverage for constraint-check CLI options."""

import argparse

import pytest

from tree_sitter_analyzer.cli.argument_parser_builder import (
_add_mcp_equivalent_options,
)


def test_constraints_read_only_option_sets_exact_destination() -> None:
parser = argparse.ArgumentParser()
_add_mcp_equivalent_options(parser)

args = parser.parse_args(["--constraints-read-only"])

assert args.constraints_read_only is True


class TestFullIndexMCPEquivalentOptions:
"""Regression: CLI --full-index-mode choices must match MCP tool valid modes.

Dogfood-found bug: argparse exposed {rebuild,stats,clear} but CodeGraphFullIndexTool
only accepts {full,incremental}. Using TSA on TSA to discover and verify the fix.
"""

def _make_parser(self):
p = argparse.ArgumentParser()
_add_mcp_equivalent_options(p)
return p

def test_full_index_mode_default_is_incremental(self):
"""Default mode must match MCP tool default ('incremental')."""
parser = self._make_parser()
args = parser.parse_args(["--full-index"])
assert args.full_index_mode == "incremental"

def test_full_index_mode_accepts_full(self):
parser = self._make_parser()
args = parser.parse_args(["--full-index", "--full-index-mode", "full"])
assert args.full_index_mode == "full"

def test_full_index_mode_accepts_incremental(self):
parser = self._make_parser()
args = parser.parse_args(["--full-index", "--full-index-mode", "incremental"])
assert args.full_index_mode == "incremental"

def test_full_index_mode_rejects_rebuild(self):
"""'rebuild' was the old invalid choice — must now be rejected."""
parser = self._make_parser()
with pytest.raises(SystemExit):
parser.parse_args(["--full-index", "--full-index-mode", "rebuild"])

def test_full_index_mode_rejects_stats(self):
"""'stats' was an old invalid choice — must now be rejected."""
parser = self._make_parser()
with pytest.raises(SystemExit):
parser.parse_args(["--full-index", "--full-index-mode", "stats"])
41 changes: 0 additions & 41 deletions tests/unit/cli/test_argument_parser_builder_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,44 +489,3 @@ def test_agent_workflow(self):
_add_agent_workflow_options(parser)
args = parser.parse_args(["--agent-workflow"])
assert args.agent_workflow is True


class TestFullIndexMCPEquivalentOptions:
"""Regression: CLI --full-index-mode choices must match MCP tool valid modes.

Dogfood-found bug: argparse exposed {rebuild,stats,clear} but CodeGraphFullIndexTool
only accepts {full,incremental}. Using TSA on TSA to discover and verify the fix.
"""

def _make_parser(self):
p = argparse.ArgumentParser()
_add_mcp_equivalent_options(p)
return p

def test_full_index_mode_default_is_incremental(self):
"""Default mode must match MCP tool default ('incremental')."""
parser = self._make_parser()
args = parser.parse_args(["--full-index"])
assert args.full_index_mode == "incremental"

def test_full_index_mode_accepts_full(self):
parser = self._make_parser()
args = parser.parse_args(["--full-index", "--full-index-mode", "full"])
assert args.full_index_mode == "full"

def test_full_index_mode_accepts_incremental(self):
parser = self._make_parser()
args = parser.parse_args(["--full-index", "--full-index-mode", "incremental"])
assert args.full_index_mode == "incremental"

def test_full_index_mode_rejects_rebuild(self):
"""'rebuild' was the old invalid choice — must now be rejected."""
parser = self._make_parser()
with pytest.raises(SystemExit):
parser.parse_args(["--full-index", "--full-index-mode", "rebuild"])

def test_full_index_mode_rejects_stats(self):
"""'stats' was an old invalid choice — must now be rejected."""
parser = self._make_parser()
with pytest.raises(SystemExit):
parser.parse_args(["--full-index", "--full-index-mode", "stats"])
Loading
Loading