guard-cli 0.1.0rc1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.4
2
+ Name: guard-cli
3
+ Version: 0.1.0rc1
4
+ Summary: Graph-powered safety layer for AI coding agents built on FalkorDB
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: falkordb>=1.0.0
8
+ Provides-Extra: dev
9
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
10
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
11
+ Requires-Dist: coverage>=7.0.0; extra == "dev"
12
+
13
+ # Guard: Graph-Powered Safety Layer for AI Coding Agents
14
+
15
+ > **A high-velocity, graph-native regression prevention engine built on FalkorDB.**
16
+ > Guard selects the exact minimal subset of tests to run after an AI coding agent modifies code, measures a multi-factor **Trust Score**, and deterministically triggers hard fallbacks to full test suites when graph confidence drops.
17
+
18
+ ---
19
+
20
+ ## 1. Problem Statement & Motivation
21
+
22
+ When AI coding agents (Claude Code, Cursor, Codex, OpenCode) modify complex codebases, running the entire test suite on every step slows iteration cycles to a crawl. Conversely, naive heuristics used by developers or agents to speed things up introduce catastrophic silent escapes:
23
+
24
+ - **Same-File Heuristic (`same-file`)**: Only runs tests in the matching test file (e.g., changes in `src/flask/helpers.py` $\to$ run `tests/test_helpers.py`). In real production architectures, foundational helpers, middleware, and lifecycle hooks are heavily exercised across disparate modules (`test_basic.py`, `test_blueprints.py`, `test_templating.py`). On the held-out benchmark, **`same-file` suffered a 17.6% Escape Rate** (3 missed failures).
25
+ - **Agent Guessing (`naive-guess`)**: LLMs predicting test paths based on file names or semantic similarity produce identical blind spots and hallucinate coverage.
26
+ - **Run-All Suite (`run-all`)**: 100% safe, but destroys developer velocity and agent autonomy on non-trivial suites.
27
+
28
+ **Guard's Core Thesis**: Code relationships and test executions form a graph topology. By indexing static AST relationships and per-test dynamic coverage contexts inside **FalkorDB**, Guard computes exact dependency subgraphs, measures empirical test confidence, dynamically widens around ambiguous dynamic dispatch, and enforces hard overrides when evidence is absent.
29
+
30
+ ---
31
+
32
+ ## 2. Verified Benchmark Results (Held-Out Evaluation)
33
+
34
+ Guard was evaluated against **`pallets/flask`** (494 tests) across 24 held-out mutants under a frozen protocol (`evals/split.json`, `PLAN.md`).
35
+
36
+ All metrics are evaluated and reported as **Paired Metrics** (Escape Rate $\leftrightarrow$ Fallback Rate $\leftrightarrow$ Savings Ratio):
37
+
38
+ | Strategy / Engine | Escape Rate (Paired) | Savings Ratio (Paired) | Fallback Rate (Paired) | Killable Evaluated | Escapes |
39
+ | :--- | :---: | :---: | :---: | :---: | :---: |
40
+ | **`run-all`** (Ground Truth) | 0.0% (0/17) | 0.0% | 100.0% (24/24) | 17 | None |
41
+ | **`same-file`** (Heuristic) | 17.6% (3/17) | 77.3% | 0.0% (0/24) | 17 | `m08`, `m16`, `m30` |
42
+ | **`naive-guess`** (Agent Guess) | 17.6% (3/17) | 77.5% | 0.0% (0/24) | 17 | `m08`, `m16`, `m30` |
43
+ | **`guard` (Full System)** | **0.0% (0/17)** | **67.3%** | **25.0% (6/24)** | 17 | **0 (Zero)** |
44
+ | *Ablation 1: `guard-no-confidence`* | 0.0% (0/17) | 77.1% | 20.8% (5/24) | 17 | None |
45
+ | *Ablation 2: `guard-no-widening`* | 0.0% (0/17) | 73.3% | 25.0% (6/24) | 17 | None |
46
+
47
+ > **Key Result**: Guard achieved a **0.0% Escape Rate** while delivering **67.3% Test-Time Savings** compared to `run-all`, completely eliminating the 17.6% escape rate of `same-file`.
48
+
49
+ ### Case Study: Root Cause of `m08` Escape Under `same-file`
50
+ - **Mutant `m08`** inverted an `if not` statement in `src/flask/helpers.py:45`.
51
+ - `same-file` and `naive-guess` selected `tests/test_helpers.py` (30 tests) $\to$ **0 failures reported (Escape)**.
52
+ - `guard` traversed FalkorDB `COVERED_BY` edges and detected dependent execution paths in `tests/test_basic.py`, `tests/test_blueprints.py`, and `tests/test_templating.py` (26 tests) $\to$ **Caught failure immediately (0.0% escape)**.
53
+
54
+ ---
55
+
56
+ ## 3. Architecture & System Design
57
+
58
+ Guard operates as a deterministic, graph-native verification pipeline connecting static code structure, dynamic coverage telemetry, and autonomous agent loops:
59
+
60
+ 1. **AST Diff Analyzer (`guard.thin_slice`)**: Inspects unified git diffs, identifies modified lines, and resolves them to exact function boundaries while flagging test file modifications.
61
+ 2. **FalkorDB Graph Engine (`guard.db`)**: Maintains structural call/import relationships and empirical per-test coverage contexts (`COVERED_BY`) in FalkorDB for sub-millisecond Cypher graph traversals.
62
+ 3. **Decision Engine (`guard.decision`)**: Pre-emptively checks hard safety overrides (uncovered functions, dynamic dispatch, touched tests), computes the multi-factor Trust Score, and routes execution to the appropriate policy level.
63
+ 4. **Self-Healing Feedback Loop (`guard.feedback`)**: Injects high-confidence `miss_repair` edges whenever a missed failure is detected, continuously hardening the graph against future escapes.
64
+
65
+
66
+ ### 3.1. FalkorDB Graph Schema
67
+
68
+ The graph layer leverages FalkorDB for low-latency Cypher queries and multi-hop graph traversals:
69
+
70
+ #### Nodes
71
+ - `(:File {path})`: Source files and test files in the repository.
72
+ - `(:Function {id, file, name, qualname, start, end, has_dynamic_dispatch, no_evidence})`: Function and method definitions.
73
+ - `(:Test {id, file, name, qualname, start, end, flake_rate})`: Pytest test units.
74
+ - `(:Run {id, timestamp, commit_sha, duration_ms, total_tests, passed, failed})`: Historical test execution runs.
75
+
76
+ #### Edges
77
+ - `(:Function)-[:DEFINED_IN]->(:File)`: Static structural hierarchy.
78
+ - `(:Test)-[:DEFINED_IN]->(:File)`: Static test placement.
79
+ - `(:Function)-[:CALLS {source: 'static', confidence: 0.6, last_verified_sha}]->(:Function)`: Multi-hop static call graph.
80
+ - `(:File)-[:IMPORTS {source: 'static', confidence: 0.6}]->(:File)`: Module import topology.
81
+ - `(:Function)-[:COVERED_BY {source: 'coverage'|'miss_repair', confidence, evidence_count, last_verified_sha}]->(:Test)`: Verified dynamic execution evidence connecting modified functions directly to covering tests.
82
+ - `(:Test)-[:FAILED_IN {source: 'test_run', error_message}]->(:Run)`: Historical test failure records.
83
+
84
+ ---
85
+
86
+ ## 4. Decision Engine & Trust Model
87
+
88
+ Guard evaluates code changes using a strict two-stage process: **Hard Safety Overrides** followed by the **Empirical Trust Score Policy**.
89
+
90
+ ### 4.1. Hard Safety Overrides (Pre-Emptive Gating)
91
+ Before computing continuous scores, Guard enforces three non-bypassable guardrails:
92
+ 1. **Test Files Touched**: If any test file is modified or added, Guard forces `run-all` with a safety warning (modifying assertions could invalidate test isolation).
93
+ 2. **Missing Evidence (`no_evidence`)**: If any modified function lacks dynamic coverage evidence in FalkorDB, Guard automatically falls back to `run-all` (never guess when unmapped).
94
+ 3. **Dynamic Dispatch Flagged**: If AST analysis flags dynamic dispatch (`getattr`, `setattr`, `eval`, `**kwargs` dispatch), Guard automatically triggers **Dynamic Widening**, expanding the candidate test set to include all module tests.
95
+
96
+ ### 4.2. Trust Score Formula
97
+ When no hard overrides force `run-all`, Guard computes the composite trust metric:
98
+
99
+ $$\text{Trust} = 0.5 \cdot \text{EvidenceRatio} + 0.3 \cdot \text{Freshness} + 0.2 \cdot (1 - \text{UncertainRatio})$$
100
+
101
+ - $\text{EvidenceRatio}$: Mean of the highest covering-edge confidence per modified function ($0.0$ for functions without covering edges).
102
+ - $\text{Freshness}$: Proportion of evidence edges whose `last_verified_sha` matches the current commit SHA.
103
+ - $\text{UncertainRatio}$: Proportion of modified functions flagged with dynamic dispatch patterns.
104
+
105
+ ### 4.3. Three-Level Execution Policy
106
+ - **$\text{Trust} \ge 0.8$ (`subset`)**: Execute only the directly covering tests mapped via high-confidence `COVERED_BY` edges.
107
+ - **$0.5 \le \text{Trust} < 0.8$ (`medium`)**: Execute the covering subset plus all tests associated with the modified modules.
108
+ - **$\text{Trust} < 0.5$ (`run-all`)**: Enforce full test suite fallback.
109
+
110
+ ---
111
+
112
+ ## 5. Self-Healing Feedback Loop
113
+
114
+ When an unexpected regression or missed test failure occurs:
115
+ 1. The failure event is captured via `guard feedback --function <func_id> --test <test_id>`.
116
+ 2. Guard writes a synthetic repair edge `(:Function)-[:COVERED_BY {source: 'miss_repair', confidence: 1.0}]->(:Test)`.
117
+ 3. The `no_evidence` flag on the function is cleared.
118
+ 4. Future decisions for this function or its callers automatically select the repaired test, preventing recurring escapes.
119
+
120
+ ---
121
+
122
+ ## 6. Installation & Quickstart
123
+
124
+ ### Prerequisites
125
+ - Python 3.10+
126
+ - Docker & Docker Compose (for FalkorDB)
127
+
128
+ ### 1. Start FalkorDB
129
+ ```bash
130
+ docker compose up -d
131
+ # FalkorDB is live on localhost:6379
132
+ # Web UI is accessible on http://localhost:3000
133
+ ```
134
+
135
+ ### 2. Install Guard
136
+ ```bash
137
+ pip install -e .
138
+ # Or install with dev dependencies:
139
+ pip install -e ".[dev]"
140
+ ```
141
+
142
+ ### 3. Ingest Target Repository
143
+ ```bash
144
+ # 1. Ingest static AST topology into FalkorDB
145
+ guard ingest-static /path/to/target/repo --include-tests
146
+
147
+ # 2. Ingest per-test coverage contexts (runs pytest with --cov-context=test)
148
+ guard ingest-coverage /path/to/target/repo --runs 3
149
+
150
+ # 3. View database statistics
151
+ guard stats
152
+ ```
153
+
154
+ ---
155
+
156
+ ## 7. CLI Reference
157
+
158
+ ### `guard run`
159
+ Executes agent pre-completion test verification on the current working tree diff:
160
+ ```bash
161
+ guard run --repo /path/to/target/repo
162
+ ```
163
+ *Exits with code 0 on success, or code 1 if tests fail or trust is breached, blocking autonomous agents from committing broken code.*
164
+
165
+ ### `guard check`
166
+ Computes the test selection decision without executing tests (outputs JSON or human-readable format):
167
+ ```bash
168
+ guard check --repo /path/to/target/repo --json
169
+ ```
170
+
171
+ ### `guard callers`
172
+ Traverses multi-hop static call graph in FalkorDB up to $N$ hops:
173
+ ```bash
174
+ guard callers "src/flask/app.py::Flask.dispatch_request" --hops 4 --min-confidence 0.6
175
+ ```
176
+
177
+ ### `guard covering-tests`
178
+ Queries all tests covering a specific function ordered by edge confidence:
179
+ ```bash
180
+ guard covering-tests "src/flask/helpers.py::url_for"
181
+ ```
182
+
183
+ ### `guard feedback`
184
+ Repairs the graph after a missed failure:
185
+ ```bash
186
+ guard feedback --function "src/flask/helpers.py::url_for" --test "tests/test_helpers.py::test_url_for"
187
+ ```
188
+
189
+ ---
190
+
191
+ ## 8. Architectural Trade-offs & Limits
192
+
193
+ ### What Graph-Native Architecture Enables
194
+ - **Deterministic Traceability**: Every skipped test has an explicit, queryable reason (`guard check --json` outputs edge justifications for all known tests).
195
+ - **Cross-Module Awareness**: Identifies deep, non-obvious test linkages across package boundaries that escape keyword search or lexical matching.
196
+ - **Fail-Safe Convergence**: Combines static structure with empirical dynamic execution contexts so that gaps trigger defensive full runs rather than silent failures.
197
+
198
+ ### Known Limits and Edge Cases
199
+ 1. **Dynamic Python Metaprogramming**: Heavy dynamic dispatch (`getattr(module, var)()`, custom metaclasses, dynamic decorators) cannot be completely resolved via static AST. *Mitigation*: Guard detects dynamic syntax patterns and triggers dynamic widening or full fallbacks.
200
+ 2. **Initial Ingestion Cost**: Generating fine-grained per-test coverage contexts requires an initial baseline run (`pytest --cov-context=test`), which can incur a 1.5x–2.0x slowdown during graph ingestion.
201
+ 3. **Ecosystem Scope**: Currently tuned for Python repositories using `pytest`.
202
+
203
+ ---
204
+
205
+ ## 9. Verification & Quality Invariants
206
+
207
+ Guard development adheres to strict engineering rules (`PLAN.md` R1–R9):
208
+ - **Frozen Benchmarks**: Acceptance claims and split files are immutable and verified through independent verifier runs (`reports/verify_t*.md`).
209
+ - **Zero Flake Tolerance**: Flaky tests are detected during ingest runs and excluded from dynamic evidence edges.
210
+ - **Complete Test Coverage**: Pure decision logic, parsers, and graph writers are covered with unit and integration tests under `tests/`.
@@ -0,0 +1,198 @@
1
+ # Guard: Graph-Powered Safety Layer for AI Coding Agents
2
+
3
+ > **A high-velocity, graph-native regression prevention engine built on FalkorDB.**
4
+ > Guard selects the exact minimal subset of tests to run after an AI coding agent modifies code, measures a multi-factor **Trust Score**, and deterministically triggers hard fallbacks to full test suites when graph confidence drops.
5
+
6
+ ---
7
+
8
+ ## 1. Problem Statement & Motivation
9
+
10
+ When AI coding agents (Claude Code, Cursor, Codex, OpenCode) modify complex codebases, running the entire test suite on every step slows iteration cycles to a crawl. Conversely, naive heuristics used by developers or agents to speed things up introduce catastrophic silent escapes:
11
+
12
+ - **Same-File Heuristic (`same-file`)**: Only runs tests in the matching test file (e.g., changes in `src/flask/helpers.py` $\to$ run `tests/test_helpers.py`). In real production architectures, foundational helpers, middleware, and lifecycle hooks are heavily exercised across disparate modules (`test_basic.py`, `test_blueprints.py`, `test_templating.py`). On the held-out benchmark, **`same-file` suffered a 17.6% Escape Rate** (3 missed failures).
13
+ - **Agent Guessing (`naive-guess`)**: LLMs predicting test paths based on file names or semantic similarity produce identical blind spots and hallucinate coverage.
14
+ - **Run-All Suite (`run-all`)**: 100% safe, but destroys developer velocity and agent autonomy on non-trivial suites.
15
+
16
+ **Guard's Core Thesis**: Code relationships and test executions form a graph topology. By indexing static AST relationships and per-test dynamic coverage contexts inside **FalkorDB**, Guard computes exact dependency subgraphs, measures empirical test confidence, dynamically widens around ambiguous dynamic dispatch, and enforces hard overrides when evidence is absent.
17
+
18
+ ---
19
+
20
+ ## 2. Verified Benchmark Results (Held-Out Evaluation)
21
+
22
+ Guard was evaluated against **`pallets/flask`** (494 tests) across 24 held-out mutants under a frozen protocol (`evals/split.json`, `PLAN.md`).
23
+
24
+ All metrics are evaluated and reported as **Paired Metrics** (Escape Rate $\leftrightarrow$ Fallback Rate $\leftrightarrow$ Savings Ratio):
25
+
26
+ | Strategy / Engine | Escape Rate (Paired) | Savings Ratio (Paired) | Fallback Rate (Paired) | Killable Evaluated | Escapes |
27
+ | :--- | :---: | :---: | :---: | :---: | :---: |
28
+ | **`run-all`** (Ground Truth) | 0.0% (0/17) | 0.0% | 100.0% (24/24) | 17 | None |
29
+ | **`same-file`** (Heuristic) | 17.6% (3/17) | 77.3% | 0.0% (0/24) | 17 | `m08`, `m16`, `m30` |
30
+ | **`naive-guess`** (Agent Guess) | 17.6% (3/17) | 77.5% | 0.0% (0/24) | 17 | `m08`, `m16`, `m30` |
31
+ | **`guard` (Full System)** | **0.0% (0/17)** | **67.3%** | **25.0% (6/24)** | 17 | **0 (Zero)** |
32
+ | *Ablation 1: `guard-no-confidence`* | 0.0% (0/17) | 77.1% | 20.8% (5/24) | 17 | None |
33
+ | *Ablation 2: `guard-no-widening`* | 0.0% (0/17) | 73.3% | 25.0% (6/24) | 17 | None |
34
+
35
+ > **Key Result**: Guard achieved a **0.0% Escape Rate** while delivering **67.3% Test-Time Savings** compared to `run-all`, completely eliminating the 17.6% escape rate of `same-file`.
36
+
37
+ ### Case Study: Root Cause of `m08` Escape Under `same-file`
38
+ - **Mutant `m08`** inverted an `if not` statement in `src/flask/helpers.py:45`.
39
+ - `same-file` and `naive-guess` selected `tests/test_helpers.py` (30 tests) $\to$ **0 failures reported (Escape)**.
40
+ - `guard` traversed FalkorDB `COVERED_BY` edges and detected dependent execution paths in `tests/test_basic.py`, `tests/test_blueprints.py`, and `tests/test_templating.py` (26 tests) $\to$ **Caught failure immediately (0.0% escape)**.
41
+
42
+ ---
43
+
44
+ ## 3. Architecture & System Design
45
+
46
+ Guard operates as a deterministic, graph-native verification pipeline connecting static code structure, dynamic coverage telemetry, and autonomous agent loops:
47
+
48
+ 1. **AST Diff Analyzer (`guard.thin_slice`)**: Inspects unified git diffs, identifies modified lines, and resolves them to exact function boundaries while flagging test file modifications.
49
+ 2. **FalkorDB Graph Engine (`guard.db`)**: Maintains structural call/import relationships and empirical per-test coverage contexts (`COVERED_BY`) in FalkorDB for sub-millisecond Cypher graph traversals.
50
+ 3. **Decision Engine (`guard.decision`)**: Pre-emptively checks hard safety overrides (uncovered functions, dynamic dispatch, touched tests), computes the multi-factor Trust Score, and routes execution to the appropriate policy level.
51
+ 4. **Self-Healing Feedback Loop (`guard.feedback`)**: Injects high-confidence `miss_repair` edges whenever a missed failure is detected, continuously hardening the graph against future escapes.
52
+
53
+
54
+ ### 3.1. FalkorDB Graph Schema
55
+
56
+ The graph layer leverages FalkorDB for low-latency Cypher queries and multi-hop graph traversals:
57
+
58
+ #### Nodes
59
+ - `(:File {path})`: Source files and test files in the repository.
60
+ - `(:Function {id, file, name, qualname, start, end, has_dynamic_dispatch, no_evidence})`: Function and method definitions.
61
+ - `(:Test {id, file, name, qualname, start, end, flake_rate})`: Pytest test units.
62
+ - `(:Run {id, timestamp, commit_sha, duration_ms, total_tests, passed, failed})`: Historical test execution runs.
63
+
64
+ #### Edges
65
+ - `(:Function)-[:DEFINED_IN]->(:File)`: Static structural hierarchy.
66
+ - `(:Test)-[:DEFINED_IN]->(:File)`: Static test placement.
67
+ - `(:Function)-[:CALLS {source: 'static', confidence: 0.6, last_verified_sha}]->(:Function)`: Multi-hop static call graph.
68
+ - `(:File)-[:IMPORTS {source: 'static', confidence: 0.6}]->(:File)`: Module import topology.
69
+ - `(:Function)-[:COVERED_BY {source: 'coverage'|'miss_repair', confidence, evidence_count, last_verified_sha}]->(:Test)`: Verified dynamic execution evidence connecting modified functions directly to covering tests.
70
+ - `(:Test)-[:FAILED_IN {source: 'test_run', error_message}]->(:Run)`: Historical test failure records.
71
+
72
+ ---
73
+
74
+ ## 4. Decision Engine & Trust Model
75
+
76
+ Guard evaluates code changes using a strict two-stage process: **Hard Safety Overrides** followed by the **Empirical Trust Score Policy**.
77
+
78
+ ### 4.1. Hard Safety Overrides (Pre-Emptive Gating)
79
+ Before computing continuous scores, Guard enforces three non-bypassable guardrails:
80
+ 1. **Test Files Touched**: If any test file is modified or added, Guard forces `run-all` with a safety warning (modifying assertions could invalidate test isolation).
81
+ 2. **Missing Evidence (`no_evidence`)**: If any modified function lacks dynamic coverage evidence in FalkorDB, Guard automatically falls back to `run-all` (never guess when unmapped).
82
+ 3. **Dynamic Dispatch Flagged**: If AST analysis flags dynamic dispatch (`getattr`, `setattr`, `eval`, `**kwargs` dispatch), Guard automatically triggers **Dynamic Widening**, expanding the candidate test set to include all module tests.
83
+
84
+ ### 4.2. Trust Score Formula
85
+ When no hard overrides force `run-all`, Guard computes the composite trust metric:
86
+
87
+ $$\text{Trust} = 0.5 \cdot \text{EvidenceRatio} + 0.3 \cdot \text{Freshness} + 0.2 \cdot (1 - \text{UncertainRatio})$$
88
+
89
+ - $\text{EvidenceRatio}$: Mean of the highest covering-edge confidence per modified function ($0.0$ for functions without covering edges).
90
+ - $\text{Freshness}$: Proportion of evidence edges whose `last_verified_sha` matches the current commit SHA.
91
+ - $\text{UncertainRatio}$: Proportion of modified functions flagged with dynamic dispatch patterns.
92
+
93
+ ### 4.3. Three-Level Execution Policy
94
+ - **$\text{Trust} \ge 0.8$ (`subset`)**: Execute only the directly covering tests mapped via high-confidence `COVERED_BY` edges.
95
+ - **$0.5 \le \text{Trust} < 0.8$ (`medium`)**: Execute the covering subset plus all tests associated with the modified modules.
96
+ - **$\text{Trust} < 0.5$ (`run-all`)**: Enforce full test suite fallback.
97
+
98
+ ---
99
+
100
+ ## 5. Self-Healing Feedback Loop
101
+
102
+ When an unexpected regression or missed test failure occurs:
103
+ 1. The failure event is captured via `guard feedback --function <func_id> --test <test_id>`.
104
+ 2. Guard writes a synthetic repair edge `(:Function)-[:COVERED_BY {source: 'miss_repair', confidence: 1.0}]->(:Test)`.
105
+ 3. The `no_evidence` flag on the function is cleared.
106
+ 4. Future decisions for this function or its callers automatically select the repaired test, preventing recurring escapes.
107
+
108
+ ---
109
+
110
+ ## 6. Installation & Quickstart
111
+
112
+ ### Prerequisites
113
+ - Python 3.10+
114
+ - Docker & Docker Compose (for FalkorDB)
115
+
116
+ ### 1. Start FalkorDB
117
+ ```bash
118
+ docker compose up -d
119
+ # FalkorDB is live on localhost:6379
120
+ # Web UI is accessible on http://localhost:3000
121
+ ```
122
+
123
+ ### 2. Install Guard
124
+ ```bash
125
+ pip install -e .
126
+ # Or install with dev dependencies:
127
+ pip install -e ".[dev]"
128
+ ```
129
+
130
+ ### 3. Ingest Target Repository
131
+ ```bash
132
+ # 1. Ingest static AST topology into FalkorDB
133
+ guard ingest-static /path/to/target/repo --include-tests
134
+
135
+ # 2. Ingest per-test coverage contexts (runs pytest with --cov-context=test)
136
+ guard ingest-coverage /path/to/target/repo --runs 3
137
+
138
+ # 3. View database statistics
139
+ guard stats
140
+ ```
141
+
142
+ ---
143
+
144
+ ## 7. CLI Reference
145
+
146
+ ### `guard run`
147
+ Executes agent pre-completion test verification on the current working tree diff:
148
+ ```bash
149
+ guard run --repo /path/to/target/repo
150
+ ```
151
+ *Exits with code 0 on success, or code 1 if tests fail or trust is breached, blocking autonomous agents from committing broken code.*
152
+
153
+ ### `guard check`
154
+ Computes the test selection decision without executing tests (outputs JSON or human-readable format):
155
+ ```bash
156
+ guard check --repo /path/to/target/repo --json
157
+ ```
158
+
159
+ ### `guard callers`
160
+ Traverses multi-hop static call graph in FalkorDB up to $N$ hops:
161
+ ```bash
162
+ guard callers "src/flask/app.py::Flask.dispatch_request" --hops 4 --min-confidence 0.6
163
+ ```
164
+
165
+ ### `guard covering-tests`
166
+ Queries all tests covering a specific function ordered by edge confidence:
167
+ ```bash
168
+ guard covering-tests "src/flask/helpers.py::url_for"
169
+ ```
170
+
171
+ ### `guard feedback`
172
+ Repairs the graph after a missed failure:
173
+ ```bash
174
+ guard feedback --function "src/flask/helpers.py::url_for" --test "tests/test_helpers.py::test_url_for"
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 8. Architectural Trade-offs & Limits
180
+
181
+ ### What Graph-Native Architecture Enables
182
+ - **Deterministic Traceability**: Every skipped test has an explicit, queryable reason (`guard check --json` outputs edge justifications for all known tests).
183
+ - **Cross-Module Awareness**: Identifies deep, non-obvious test linkages across package boundaries that escape keyword search or lexical matching.
184
+ - **Fail-Safe Convergence**: Combines static structure with empirical dynamic execution contexts so that gaps trigger defensive full runs rather than silent failures.
185
+
186
+ ### Known Limits and Edge Cases
187
+ 1. **Dynamic Python Metaprogramming**: Heavy dynamic dispatch (`getattr(module, var)()`, custom metaclasses, dynamic decorators) cannot be completely resolved via static AST. *Mitigation*: Guard detects dynamic syntax patterns and triggers dynamic widening or full fallbacks.
188
+ 2. **Initial Ingestion Cost**: Generating fine-grained per-test coverage contexts requires an initial baseline run (`pytest --cov-context=test`), which can incur a 1.5x–2.0x slowdown during graph ingestion.
189
+ 3. **Ecosystem Scope**: Currently tuned for Python repositories using `pytest`.
190
+
191
+ ---
192
+
193
+ ## 9. Verification & Quality Invariants
194
+
195
+ Guard development adheres to strict engineering rules (`PLAN.md` R1–R9):
196
+ - **Frozen Benchmarks**: Acceptance claims and split files are immutable and verified through independent verifier runs (`reports/verify_t*.md`).
197
+ - **Zero Flake Tolerance**: Flaky tests are detected during ingest runs and excluded from dynamic evidence edges.
198
+ - **Complete Test Coverage**: Pure decision logic, parsers, and graph writers are covered with unit and integration tests under `tests/`.
@@ -0,0 +1,2 @@
1
+ """Guard: Graph-powered safety layer for AI coding agents."""
2
+ __version__ = "0.1.0rc1"
@@ -0,0 +1,119 @@
1
+ """Agent integration and test execution runner (Task T7).
2
+
3
+ Provides `guard run`: an agent calls this command before declaring work complete.
4
+ - Calls `guard check` on proposed changes.
5
+ - If trust is low or changes are uncovered (fallback), forces a full test suite run.
6
+ - If trust is high, runs only the selected test subset.
7
+ - Blocks task completion if any test fails.
8
+ """
9
+ from __future__ import annotations
10
+ import os
11
+ import sys
12
+ import time
13
+ import subprocess
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from guard.thin_slice import guard_check
18
+
19
+ REPO_ROOT = Path("target_repos/flask").resolve()
20
+
21
+
22
+ def apply_patch(diff_file: Path) -> bool:
23
+ """Applies patch to target repo."""
24
+ proc = subprocess.run(["git", "apply", str(diff_file)], cwd=str(REPO_ROOT), capture_output=True, text=True)
25
+ if proc.returncode != 0:
26
+ patch_proc = subprocess.run(["patch", "-p1", "-i", str(diff_file)], cwd=str(REPO_ROOT), capture_output=True)
27
+ return patch_proc.returncode == 0
28
+ return True
29
+
30
+
31
+ def reset_repo():
32
+ """Restores target repo to clean state."""
33
+ subprocess.run(["git", "checkout", "--", "."], cwd=str(REPO_ROOT), capture_output=True)
34
+ subprocess.run(["git", "clean", "-fdx"], cwd=str(REPO_ROOT), capture_output=True)
35
+
36
+
37
+ def count_collected_tests(test_targets: list[str]) -> int:
38
+ """Counts how many test items are collected for the targets."""
39
+ cmd = [sys.executable, "-m", "pytest", "--collect-only", "-q"] + test_targets
40
+ proc = subprocess.run(cmd, cwd=str(REPO_ROOT), capture_output=True, text=True)
41
+ lines = proc.stdout.strip().splitlines()
42
+ for line in reversed(lines):
43
+ if "test" in line:
44
+ parts = line.split()
45
+ for p in parts:
46
+ if p.isdigit():
47
+ return int(p)
48
+ return len(test_targets)
49
+
50
+
51
+ def run_agent_guard(
52
+ diff_file: str | Path | None = None,
53
+ repo_path: str | Path | None = None,
54
+ graph_name: str = "guard",
55
+ ) -> tuple[dict[str, Any], int]:
56
+ """Runs Guard check and executes tests according to trust level.
57
+
58
+ Returns:
59
+ (report_dict, exit_code)
60
+ """
61
+ repo = Path(repo_path).resolve() if repo_path else REPO_ROOT
62
+ diff_path = Path(diff_file).resolve() if diff_file else None
63
+
64
+ # 1. Run guard check
65
+ check_res = guard_check(
66
+ diff_file=diff_path,
67
+ repo_path=repo,
68
+ graph_name=graph_name,
69
+ )
70
+
71
+ forced_full = check_res.is_fallback or (check_res.status == "run-all")
72
+ tests_to_run = ["tests"] if forced_full else check_res.selected_tests
73
+
74
+ # 2. If a patch file was passed, apply it temporarily for test execution
75
+ applied = False
76
+ if diff_path and diff_path.exists():
77
+ reset_repo()
78
+ applied = apply_patch(diff_path)
79
+ if not applied:
80
+ return {
81
+ "status": "error",
82
+ "forced_full": True,
83
+ "fallback_reason": "Failed to apply patch diff",
84
+ "changed_functions": [],
85
+ "tests_run": [],
86
+ "tests_run_count": 0,
87
+ "duration_s": 0.0,
88
+ "tests_passed": False,
89
+ "exit_code": 1,
90
+ "output_summary": "Error: Failed to apply patch cleanly to target repo.",
91
+ }, 1
92
+
93
+ try:
94
+ # Count collected tests
95
+ test_count = count_collected_tests(tests_to_run)
96
+
97
+ # 3. Execute pytest
98
+ cmd = [sys.executable, "-m", "pytest", "-q", "--tb=short"] + tests_to_run
99
+ start = time.time()
100
+ proc = subprocess.run(cmd, cwd=str(repo), capture_output=True, text=True)
101
+ duration_s = time.time() - start
102
+
103
+ passed = (proc.returncode == 0)
104
+ report = {
105
+ "status": check_res.status,
106
+ "forced_full": forced_full,
107
+ "fallback_reason": check_res.fallback_reason,
108
+ "changed_functions": check_res.changed_functions,
109
+ "tests_run": tests_to_run,
110
+ "tests_run_count": test_count,
111
+ "duration_s": round(duration_s, 3),
112
+ "tests_passed": passed,
113
+ "exit_code": proc.returncode,
114
+ "output_summary": proc.stdout.strip().splitlines()[-1] if proc.stdout.strip() else "",
115
+ }
116
+ return report, proc.returncode
117
+ finally:
118
+ if applied:
119
+ reset_repo()