Skip to content

Commit 0ece4d6

Browse files
author
Your Name
committed
feat(docs): deep code analysis engine with 5 supporting modules
changes: - file: runner.py area: analyzer modified: [_run_tool] - file: app.py area: cli modified: [plan_validate, plan_run] - file: formatters.py area: cli modified: [output_json, _build_model_row, output_rich] - file: strategy_commands.py area: cli modified: [verify_strategy, add_strategy_commands, validate_strategy] - file: ticket_pruner.py area: core added: [_filter_list, prune_planfile_tickets, _make_backup, _matches, _prune_sprints, _filter_dict_of_tickets, +3 more] - file: workflows.py area: core added: [_step_plan_prune_stale] modified: [_step_plan_validate] - file: test_ticket_pruner.py area: test added: [_write, _planfile_for_cli, _read] new_tests: 11 testing: new_tests: 11 scenarios: - prune_handles_backlog_dict_with_tickets - prune_handles_missing_planfile - prune_with_no_backup_does_not_create_bak - prune_handles_dict_tasks_section - plan_validate_cli_prune_stale_only - prune_walks_sprints_and_backlog - workflow_plan_prune_stale_uses_previous_validate_output - workflow_plan_prune_stale_noop_when_nothing_to_remove - prune_removes_top_level_tasks_and_creates_backup - prune_returns_noop_when_ticket_ids_empty # +1 more dependencies: flow: "test_ticket_pruner→app→planfile" - app.py -> planfile.py - test_ticket_pruner.py -> app.py stats: lines: "+1071/-187 (net +884)" files: 13 complexity: "Large structural change (normalized)"
1 parent e43f805 commit 0ece4d6

20 files changed

Lines changed: 1099 additions & 195 deletions

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@
129129
- Update README.md with model tier configuration examples
130130
- Update examples/planfile/README.md with backend system documentation
131131

132+
## [0.1.76] - 2026-04-26
133+
134+
### Docs
135+
- Update README.md
136+
- Update docs/TESTQL_INTEGRATION.md
137+
- Update docs/llx-tools.md
138+
139+
### Test
140+
- Update tests/test_ticket_pruner.py
141+
142+
### Other
143+
- Update llx/analysis/runner.py
144+
- Update llx/cli/app.py
145+
- Update llx/cli/formatters.py
146+
- Update llx/cli/strategy_commands.py
147+
- Update llx/commands/_patch_apply.py
148+
- Update llx/planfile/ticket_pruner.py
149+
- Update llx/workflows.py
150+
- Update planfile.yaml
151+
132152
## [0.1.75] - 2026-04-26
133153

134154
### Docs

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@
33
**Intelligent LLM model router driven by real code metrics.**
44

55
[![PyPI](https://img.shields.io/pypi/v/llx)](https://pypi.org/project/llx/)
6-
[![Version](https://img.shields.io/badge/version-0.1.75-blue)](https://pypi.org/project/llx/)
6+
[![Version](https://img.shields.io/badge/version-0.1.76-blue)](https://pypi.org/project/llx/)
77
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
88
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://python.org)
99

1010

1111
## AI Cost Tracking
1212

13-
![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.75-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
13+
![PyPI](https://img.shields.io/badge/pypi-costs-blue) ![Version](https://img.shields.io/badge/version-0.1.76-blue) ![Python](https://img.shields.io/badge/python-3.9+-blue) ![License](https://img.shields.io/badge/license-Apache--2.0-green)
1414
![AI Cost](https://img.shields.io/badge/AI%20Cost-$7.50-orange) ![Human Time](https://img.shields.io/badge/Human%20Time-35.2h-blue) ![Model](https://img.shields.io/badge/Model-openrouter%2Fqwen%2Fqwen3--coder--next-lightgrey)
1515

16-
- 🤖 **LLM usage:** $7.5000 (99 commits)
17-
- 👤 **Human dev:** ~$3521 (35.2h @ $100/h, 30min dedup)
16+
- 🤖 **LLM usage:** $7.5000 (100 commits)
17+
- 👤 **Human dev:** ~$3523 (35.2h @ $100/h, 30min dedup)
1818

1919
Generated on 2026-04-26 using [openrouter/qwen/qwen3-coder-next](https://openrouter.ai/qwen/qwen3-coder-next)
2020

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.75
1+
0.1.76

docs/TESTQL_INTEGRATION.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# LLX TestQL Integration
2+
3+
This guide documents how **llx** bridges **TestQL** DSL validation with **planfile** ticket generation, upsert, and multi-backend synchronization.
4+
5+
## Overview
6+
7+
TestQL scenarios describe expected API behavior in a declarative DSL. When a scenario fails, llx can:
8+
9+
1. **Validate** the scenario via the TestQL CLI.
10+
2. **Generate** planfile tickets for each failure.
11+
3. **Upsert** tickets into `planfile.yaml` with identity-aware deduplication.
12+
4. **Sync** tickets to `TODO.md` and configured integrations (GitHub, GitLab, Jira).
13+
14+
All of this is available both as a standalone CLI command and as a workflow step.
15+
16+
---
17+
18+
## `llx plan testql` — Standalone Command
19+
20+
Run a TestQL scenario and optionally create/sync tickets in one shot.
21+
22+
```bash
23+
llx plan testql SCENARIO [OPTIONS]
24+
```
25+
26+
### Arguments
27+
28+
- `SCENARIO` — Path to the `.testql.toon.yaml` scenario file.
29+
30+
### Options
31+
32+
| Option | Default | Description |
33+
|--------|---------|-------------|
34+
| `--strategy`, `-s` | `planfile.yaml` | Target planfile YAML |
35+
| `--project`, `-p` | `.` | Project root directory |
36+
| `--url` | `http://localhost:8101` | TestQL service base URL |
37+
| `--dry-run`, `-d` | `False` | Parse/validate only; do not execute |
38+
| `--create-tickets` / `--no-create-tickets` | `True` | Create planfile tickets for failures |
39+
| `--sync` / `--no-sync` | `True` | Sync tickets to TODO.md and integrations |
40+
| `--max-tickets` | `25` | Max tickets generated from one run |
41+
| `--testql-bin` | `testql` | TestQL CLI executable name/path |
42+
| `--testql-repo-path` | `/home/tom/github/oqlos/testql` | Fallback local repo path |
43+
| `--output-yaml`, `-o` || Save full payload to a YAML file |
44+
45+
### Examples
46+
47+
```bash
48+
# Basic validation + ticket generation + sync
49+
llx plan testql testql-scenarios/api-smoke.testql.toon.yaml
50+
51+
# Dry-run: validate without creating tickets
52+
llx plan testql testql-scenarios/api-smoke.testql.toon.yaml --dry-run
53+
54+
# Custom project and strategy
55+
llx plan testql scenarios/smoke.testql.toon.yaml -p ./my-api -s my-api/planfile.yaml
56+
57+
# Save results for CI inspection
58+
llx plan testql scenarios/smoke.testql.toon.yaml -o testql-results.yaml
59+
```
60+
61+
### Output
62+
63+
The command prints a summary to stderr and writes a YAML payload to stdout:
64+
65+
```yaml
66+
scenario: scenarios/smoke.testql.toon.yaml
67+
strategy: planfile.yaml
68+
project: /home/tom/projects/my-api
69+
dry_run: false
70+
validation:
71+
ok: false
72+
passed: 2
73+
failed: 1
74+
exit_code: 1
75+
errors:
76+
- "step 'health-check': status mismatch"
77+
tickets:
78+
generated: 1
79+
created: 1
80+
skipped: 0
81+
created_ticket_ids:
82+
- TQL-a1b2c3d4e5
83+
sync:
84+
sync_order:
85+
- markdown
86+
- github
87+
integrations:
88+
- integration: markdown
89+
created: 1
90+
updated: 0
91+
skipped: 0
92+
failed: 0
93+
- integration: github
94+
created: 1
95+
updated: 0
96+
skipped: 0
97+
failed: 0
98+
```
99+
100+
If validation fails (`ok: false`), the CLI exits with code `1`.
101+
102+
---
103+
104+
## Workflow Step — `testql`
105+
106+
In llx workflow YAML, use the `testql` step to embed TestQL validation inside a pipeline.
107+
108+
```yaml
109+
steps:
110+
- name: validate-api
111+
type: testql
112+
params:
113+
scenario: testql-scenarios/api-smoke.testql.toon.yaml
114+
strategy: planfile.yaml
115+
project: .
116+
url: http://localhost:8101
117+
dry_run: false
118+
create_tickets: true
119+
sync_targets: true
120+
max_tickets: 25
121+
testql_bin: testql
122+
testql_repo_path: /home/tom/github/oqlos/testql
123+
fail_on_failure: true
124+
```
125+
126+
### Parameters
127+
128+
| Parameter | Default | Description |
129+
|-----------|---------|-------------|
130+
| `scenario` | **required** | Path to `.testql.toon.yaml` |
131+
| `strategy` | `planfile.yaml` | Target planfile YAML |
132+
| `project` | `.` | Project root directory |
133+
| `url` | `http://localhost:8101` | TestQL service URL |
134+
| `dry_run` | `false` | Parse/validate only |
135+
| `create_tickets` | `true` | Create tickets for failures |
136+
| `sync_targets` | `true` | Sync to TODO.md and integrations |
137+
| `max_tickets` | `25` | Ticket cap per run |
138+
| `testql_bin` | `testql` | TestQL CLI executable |
139+
| `testql_repo_path` | `/home/tom/github/oqlos/testql` | Fallback repo path |
140+
| `fail_on_failure` | `true` | Mark step as failed when checks fail |
141+
142+
---
143+
144+
## Pre-flight Freshness Validation (`llx plan run` & `llx plan validate`)
145+
146+
Before executing tasks, `llx plan run` can run a **prefact-driven freshness check** to detect stale tickets and optionally skip or cancel them.
147+
148+
### `llx plan run` Options
149+
150+
| Option | Default | Description |
151+
|--------|---------|-------------|
152+
| `--validate` / `--no-validate` | `True` | Pre-flight: scan code and skip stale tickets |
153+
| `--cancel-stale` | `False` | Mark stale tickets as canceled in planfile |
154+
| `--prefact-yaml` | — | Explicit prefact.yaml for the scan |
155+
| `--prefact-bin` | — | Custom prefact executable name/path |
156+
157+
### Examples
158+
159+
```bash
160+
# Run with pre-flight freshness check (default)
161+
llx plan run planfile.yaml
162+
163+
# Cancel stale tickets before running
164+
llx plan run planfile.yaml --cancel-stale
165+
166+
# Disable pre-flight validation
167+
llx plan run planfile.yaml --no-validate
168+
```
169+
170+
### `llx plan validate` — Dedicated Freshness Command
171+
172+
```bash
173+
# Validate ticket freshness and print markdown report
174+
llx plan validate planfile.yaml
175+
176+
# Cancel stale tickets in the planfile
177+
llx plan validate planfile.yaml --cancel-stale
178+
179+
# Exit non-zero when stale tickets are found
180+
llx plan validate planfile.yaml --fail-on-stale
181+
182+
# Require a successful prefact scan
183+
llx plan validate planfile.yaml --require-scan --fail-on-stale
184+
```
185+
186+
The freshness report contains:
187+
188+
```yaml
189+
scan:
190+
backend: prefact
191+
ok: true
192+
stale_ticket_ids:
193+
- TICKET-42
194+
- TICKET-99
195+
stale: 2
196+
current: 15
197+
unknown: 3
198+
```
199+
200+
---
201+
202+
## Identity-Aware Ticket Deduplication
203+
204+
When upserting TestQL tickets into `planfile.yaml`, the system collects **identity keys** from each ticket to prevent duplicates across integrations:
205+
206+
- `local_id` — ticket `id` or `ticket_id`
207+
- `external_id` — `external_id` or integration-specific IDs (`github_id`, `gitlab_id`, `jira_id`)
208+
- `external_key` — `external_key`, `key`, or integration-specific keys
209+
- `external_url` — `external_url`, `url`, `issue_url`, `ticket_url`, or integration-specific URLs
210+
- `source` and `external_refs` dictionaries
211+
212+
If an incoming ticket shares any identity key with an existing task, it is **skipped** instead of recreated.
213+
214+
---
215+
216+
## End-to-End Example
217+
218+
```bash
219+
# 1. Start the API under test
220+
python -m my_api &
221+
222+
# 2. Run TestQL validation, create tickets, sync to GitHub
223+
llx plan testql testql-scenarios/api-smoke.testql.toon.yaml --strategy planfile.yaml --sync
224+
225+
# 3. Execute planfile tasks, skipping stale tickets and canceling them
226+
llx plan run planfile.yaml --cancel-stale --format markdown
227+
228+
# 4. Validate remaining tickets are still fresh
229+
llx plan validate planfile.yaml --fail-on-stale
230+
```
231+
232+
---
233+
234+
**LLX TestQL Integration** — Validate APIs, generate tickets, and keep your backlog in sync. 🚀

docs/llx-tools.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ llx-tools docker restore --backup-dir ./backups/20240325
567567

568568
### Documentation
569569
- [llx Main Documentation](../README.md)
570+
- [TestQL Integration](TESTQL_INTEGRATION.md)
570571
- [Docker Integration](../examples/docker/README.md)
571572
- [AI Tools Examples](../examples/ai-tools/README.md)
572573
- [VS Code + RooCode](../examples/vscode-roocode/README.md)

llx/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
model = select_model(metrics)
1515
"""
1616

17-
__version__ = "0.1.75"
17+
__version__ = "0.1.76"
1818

1919
from llx.analysis.collector import ProjectMetrics, analyze_project
2020
from llx.llm import DEFAULT_MAX_TOKENS, LLM, LLMResponse, get_api_key, get_llm, get_llm_model

llx/analysis/runner.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Run external analysis tools and collect fresh output.
22
3-
Lesson from preLLM: query() function (CC=27) mixed tool invocation
3+
Lesson from preLLM: query() function mixed tool invocation
44
with result processing. Here each tool is a separate function.
55
"""
66

@@ -13,6 +13,7 @@
1313
# Constants for tool execution
1414
DEFAULT_TIMEOUT_SECONDS = 120
1515
MAX_ERROR_LENGTH = 500
16+
SUCCESS_EXIT_CODE = 0
1617

1718

1819
@dataclass
@@ -37,7 +38,7 @@ def _run_tool(
3738
output_dir.mkdir(parents=True, exist_ok=True)
3839
try:
3940
result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
40-
if result.returncode == 0:
41+
if result.returncode == SUCCESS_EXIT_CODE:
4142
return ToolResult(tool=tool, success=True, output_dir=output_dir)
4243
return ToolResult(tool=tool, success=False, error=result.stderr[:MAX_ERROR_LENGTH])
4344
except subprocess.TimeoutExpired:

0 commit comments

Comments
 (0)