tdd-cli 0.1.0__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.
Files changed (58) hide show
  1. tdd_cli-0.1.0/.gitignore +9 -0
  2. tdd_cli-0.1.0/CHANGELOG.md +42 -0
  3. tdd_cli-0.1.0/LICENSE +21 -0
  4. tdd_cli-0.1.0/PKG-INFO +391 -0
  5. tdd_cli-0.1.0/README.md +363 -0
  6. tdd_cli-0.1.0/SECURITY.md +29 -0
  7. tdd_cli-0.1.0/examples/claude-code-hooks/README.md +44 -0
  8. tdd_cli-0.1.0/examples/claude-code-hooks/bash_hook.py +68 -0
  9. tdd_cli-0.1.0/examples/claude-code-hooks/stop_hook.py +46 -0
  10. tdd_cli-0.1.0/examples/plan.md +160 -0
  11. tdd_cli-0.1.0/examples/skills/tdd-drive/README.md +30 -0
  12. tdd_cli-0.1.0/examples/skills/tdd-drive/SKILL.md +92 -0
  13. tdd_cli-0.1.0/examples/skills/tdd-handoff/README.md +43 -0
  14. tdd_cli-0.1.0/examples/skills/tdd-handoff/SKILL.md +555 -0
  15. tdd_cli-0.1.0/pyproject.toml +60 -0
  16. tdd_cli-0.1.0/src/tddcli/__init__.py +6 -0
  17. tdd_cli-0.1.0/src/tddcli/adapters/__init__.py +48 -0
  18. tdd_cli-0.1.0/src/tddcli/adapters/base.py +119 -0
  19. tdd_cli-0.1.0/src/tddcli/adapters/pytest_adapter.py +179 -0
  20. tdd_cli-0.1.0/src/tddcli/adapters/vitest_adapter.py +170 -0
  21. tdd_cli-0.1.0/src/tddcli/advance.py +423 -0
  22. tdd_cli-0.1.0/src/tddcli/cli.py +1043 -0
  23. tdd_cli-0.1.0/src/tddcli/config.py +255 -0
  24. tdd_cli-0.1.0/src/tddcli/contract.py +237 -0
  25. tdd_cli-0.1.0/src/tddcli/envelope.py +96 -0
  26. tdd_cli-0.1.0/src/tddcli/fleet.py +128 -0
  27. tdd_cli-0.1.0/src/tddcli/gitutil.py +138 -0
  28. tdd_cli-0.1.0/src/tddcli/identity.py +82 -0
  29. tdd_cli-0.1.0/src/tddcli/leases.py +118 -0
  30. tdd_cli-0.1.0/src/tddcli/ledger.py +433 -0
  31. tdd_cli-0.1.0/src/tddcli/machine.py +390 -0
  32. tdd_cli-0.1.0/src/tddcli/render.py +275 -0
  33. tdd_cli-0.1.0/src/tddcli/snapshot.py +90 -0
  34. tdd_cli-0.1.0/src/tddcli/staging.py +130 -0
  35. tdd_cli-0.1.0/tests/conftest.py +155 -0
  36. tdd_cli-0.1.0/tests/test_artifact_regeneration.py +76 -0
  37. tdd_cli-0.1.0/tests/test_baseline_integrity.py +148 -0
  38. tdd_cli-0.1.0/tests/test_config_and_staging.py +166 -0
  39. tdd_cli-0.1.0/tests/test_config_drift.py +91 -0
  40. tdd_cli-0.1.0/tests/test_contract.py +139 -0
  41. tdd_cli-0.1.0/tests/test_doctor_attribution.py +75 -0
  42. tdd_cli-0.1.0/tests/test_end_to_end.py +310 -0
  43. tdd_cli-0.1.0/tests/test_example_plan.py +69 -0
  44. tdd_cli-0.1.0/tests/test_fleet.py +164 -0
  45. tdd_cli-0.1.0/tests/test_heartbeat.py +175 -0
  46. tdd_cli-0.1.0/tests/test_init_detection.py +63 -0
  47. tdd_cli-0.1.0/tests/test_pin_cycles.py +119 -0
  48. tdd_cli-0.1.0/tests/test_progress.py +151 -0
  49. tdd_cli-0.1.0/tests/test_project_commands.py +126 -0
  50. tdd_cli-0.1.0/tests/test_python_env_managers.py +84 -0
  51. tdd_cli-0.1.0/tests/test_refactor_cycles.py +115 -0
  52. tdd_cli-0.1.0/tests/test_release_surface.py +146 -0
  53. tdd_cli-0.1.0/tests/test_run_claim.py +240 -0
  54. tdd_cli-0.1.0/tests/test_single_project_repo.py +122 -0
  55. tdd_cli-0.1.0/tests/test_snapshot_and_identity.py +106 -0
  56. tdd_cli-0.1.0/tests/test_stub_hint.py +102 -0
  57. tdd_cli-0.1.0/tests/test_vitest_adapter.py +78 -0
  58. tdd_cli-0.1.0/tests/test_worker_leases.py +185 -0
@@ -0,0 +1,9 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ build/
8
+ *.egg-info/
9
+ .claude/worktrees/
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and the project adheres to [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-08-08
10
+
11
+ Initial release.
12
+
13
+ - Ledger-backed TDD process controller: phase is derived from observed test
14
+ execution, never asserted by the caller.
15
+ - Plan contracts in YAML front-matter, hashed at the committed blob.
16
+ - Standard, pin, and contract cycle kinds.
17
+ - pytest and vitest adapters. The pytest adapter runs suites through the
18
+ project's environment manager (`uv.lock`, `poetry.lock`, `Pipfile`,
19
+ `pdm.lock`, or `[tool.poetry]`), falling back to bare `pytest`; an explicit
20
+ `test_command` always wins.
21
+ - Single-project repositories (`root = "."`) and monorepos alike; `tdd init`
22
+ scaffolds the registry from evidence and reports directories it could not
23
+ match instead of guessing.
24
+ - Phase-derived staging and committing; artifact regeneration in separate commits.
25
+ - Machine-wide worker leases so concurrent agents share cores.
26
+ - `tdd fleet`: read-only view of every agent's run on a repository.
27
+ - Fidelity metrics, typed blockers, friction-log rendering.
28
+ - `tdd --version`; every JSON envelope carries `envelope_version`.
29
+ - Ledger schema versioning: older ledgers are migrated forward on open; a
30
+ ledger written by a newer tdd-cli is refused with a clear error.
31
+ - Third-party adapters via the `tddcli.adapters` entry-point group
32
+ (built-in names cannot be shadowed).
33
+ - Windows is refused at startup (`reason: "unsupported_platform"`); use WSL.
34
+ - `docs/harness-integration.md`: the contract for writing a driving skill
35
+ against any harness — the envelope, the closed verb set, and authoring rules.
36
+ - Example Claude Code integrations: Stop + PreToolUse hooks
37
+ (`examples/claude-code-hooks/`), a driving skill (`examples/skills/tdd-drive/`),
38
+ and a plan-hardening skill (`examples/skills/tdd-handoff/`).
39
+ - `examples/plan.md`: a complete example plan exercising every cycle kind and
40
+ the full front-matter vocabulary, registered by the test suite so it cannot
41
+ drift from the contract parser.
42
+ - SECURITY.md documenting the command-execution trust model.
tdd_cli-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 geuben
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tdd_cli-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,391 @@
1
+ Metadata-Version: 2.4
2
+ Name: tdd-cli
3
+ Version: 0.1.0
4
+ Summary: Ledger-backed TDD process controller for autonomous coding agents
5
+ Project-URL: Homepage, https://github.com/geuben/tdd-cli
6
+ Project-URL: Repository, https://github.com/geuben/tdd-cli
7
+ Project-URL: Issues, https://github.com/geuben/tdd-cli/issues
8
+ Project-URL: Changelog, https://github.com/geuben/tdd-cli/blob/main/CHANGELOG.md
9
+ Author: geuben
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,ai,pytest,tdd,testing,vitest,workflow
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Quality Assurance
24
+ Classifier: Topic :: Software Development :: Testing
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: pyyaml>=6.0
27
+ Description-Content-Type: text/markdown
28
+
29
+ # tdd-cli
30
+
31
+ A ledger-backed TDD process controller for autonomous coding agents.
32
+
33
+ Process state is **derived from observed test execution**, never asserted by the caller.
34
+ There is no command that accepts a phase, and no file an agent can edit to claim progress
35
+ it has not made.
36
+
37
+ Implements [`tdd-cli-prd.md`](./docs/PRD.md). Requirement ids (`R9.14`, `§6.2`) in the source
38
+ refer to that document.
39
+
40
+ ## Why
41
+
42
+ An agent instructed to follow TDD will report that it did. The usual ways to hold it to
43
+ that — prompt rules, a checklist, a state file in the worktree — all share one flaw: the
44
+ record of progress is written by the same agent it is meant to constrain. That flaw
45
+ produces four failure classes, reliably: state that is corrupted or edited to claim
46
+ progress never made; "the test failed first" as an unverifiable self-report; runs that
47
+ stop silently mid-plan; and no record comparable across runs, plans, or models.
48
+
49
+ This tool removes the agent from the reporting path. It runs the suites itself, computes
50
+ every phase transition from what the tests observably did, and records the whole run in a
51
+ ledger the agent cannot reach — which is also what makes the friction logs and metrics at
52
+ the end trustworthy.
53
+
54
+ ## Install
55
+
56
+ ```sh
57
+ pip install tdd-cli # or: uv tool install tdd-cli
58
+ tdd --help
59
+ ```
60
+
61
+ From source:
62
+
63
+ ```sh
64
+ uv sync
65
+ uv run tdd --help
66
+ ```
67
+
68
+ ## Quick start
69
+
70
+ ```sh
71
+ tdd init # scaffold tdd.toml from detected projects — then review it
72
+ tdd doctor # environment preflight
73
+ tdd plan register tasks/my-plan.md
74
+ tdd run start --plan tasks/my-plan.md
75
+ tdd advance # the only command that changes phase
76
+ ```
77
+
78
+ Every command emits JSON with a `next_action`. That verb is the single authority on control
79
+ flow — skills describe *how* to do the work and must never contain stopping instructions.
80
+ [`docs/harness-integration.md`](./docs/harness-integration.md) specifies the verb set and how
81
+ to write such a skill; [`examples/skills/tdd-drive/`](./examples/skills/tdd-drive/) is a
82
+ runnable one for Claude Code. Its planning-side counterpart,
83
+ [`examples/skills/tdd-handoff/`](./examples/skills/tdd-handoff/), hardens a
84
+ draft plan and authors its contract before the run starts.
85
+
86
+ ## Configuration
87
+
88
+ `tdd.toml` at the worktree root. Roots are declared, never discovered by scanning for marker
89
+ files: two projects can share a marker, and directory-listing order must not decide which
90
+ suite runs.
91
+
92
+ A single-project repository declares the worktree root itself:
93
+
94
+ ```toml
95
+ [project.app]
96
+ root = "."
97
+ adapter = "pytest"
98
+ test_paths = ["tests/"]
99
+ ```
100
+
101
+ A monorepo declares one project per root:
102
+
103
+ ```toml
104
+ [project.backend]
105
+ root = "backend"
106
+ adapter = "pytest"
107
+ test_paths = ["tests/"]
108
+ lint = ["ruff check"]
109
+ typecheck = ["mypy ."]
110
+
111
+ [project.frontend]
112
+ root = "frontend"
113
+ adapter = "vitest"
114
+ test_paths = ["**/__tests__/**", "**/*.test.ts"]
115
+ typecheck = ["tsc --noEmit"]
116
+
117
+ [artifact.openapi]
118
+ path = "schema/openapi.json"
119
+ produced_by = "backend"
120
+ regenerate = "uv run python -m app.export_openapi"
121
+ consumed_by = ["frontend"]
122
+
123
+ [artifact.api_client]
124
+ path = "frontend/generated"
125
+ produced_by = "artifact.openapi" # artifacts chain
126
+ regenerate = "npm --prefix codegen run generate"
127
+ check = "npm --prefix codegen run check"
128
+ consumed_by = ["frontend"]
129
+ generated = true # excluded from authorship accounting
130
+ ```
131
+
132
+ A generator that is never hand-edited (`codegen`) is an artifact regeneration command, not a
133
+ project. It has no tests and no cycles.
134
+
135
+ ## Sharing cores between concurrent agents
136
+
137
+ Several agents running tdd-cli on one machine (each in its own worktree) face a bad
138
+ trade: a fixed worker count in the test command either oversubscribes the box when
139
+ agents run together or serialises every suite when an agent is alone. Instead, declare
140
+ where the worker count goes and let the tool compute it:
141
+
142
+ ```toml
143
+ [project.backend]
144
+ test_command = "uv run pytest -n {workers}"
145
+
146
+ [project.frontend]
147
+ test_command = "npx vitest run --maxWorkers={workers}"
148
+ ```
149
+
150
+ Each suite invocation takes a lease in a machine-wide directory (`~/.cache/tdd-cli/leases`,
151
+ override with `TDD_LEASE_DIR`) held for the duration of the run, and receives
152
+ `max(1, cores // live_leases)` workers: one agent gets the whole machine, four agents get a
153
+ quarter each. Leases whose process has died, or older than a suite could legitimately run,
154
+ are swept — a crash never throttles the machine.
155
+
156
+ `{workers}` is opt-in per project; without it the declared command runs verbatim, but the
157
+ budget is still exported as `TDD_WORKERS` for commands that prefer to read it themselves,
158
+ and the lease is still held so other agents account for the running suite. Set
159
+ `TDD_CORE_BUDGET` to cap the total below `os.cpu_count()` and keep headroom for the agents
160
+ themselves.
161
+
162
+ The split is computed at lease acquisition: an agent arriving mid-run takes the smaller
163
+ share immediately, and the earlier agent's share corrects on its next invocation. Per-file
164
+ collection stays serial — collection is cheap and xdist adds startup cost per file.
165
+
166
+ ## Watching every agent at once
167
+
168
+ The ledger is one database per repository, shared by all worktrees, so every agent's
169
+ progress is already in one place. `tdd fleet` reads it:
170
+
171
+ ```sh
172
+ tdd fleet # one line per active run, plus in-flight baselines and executing suites
173
+ tdd fleet --json # the same as a machine envelope
174
+ ```
175
+
176
+ Each run line carries the worktree, plan, cycle N of M, phase, and the age of the newest
177
+ suite invocation — a stale age is the signal for a wedged agent. Baselines still being
178
+ collected are listed separately, and the worker-lease directory is read (never modified)
179
+ to show how many suites are executing right now and each one's share of the cores.
180
+
181
+ The command is safe to run while agents are mid-run from any worktree on any branch: it
182
+ opens the ledger with SQLite's read-only mode, so it is structurally incapable of creating,
183
+ migrating, or writing the database, and it requires no `tdd.toml`, plan, or active run.
184
+
185
+ ## Plan contracts
186
+
187
+ The plan carries its own contract in YAML front-matter, so a planning agent needs no
188
+ integration with this tool. The contract is hashed at the **committed blob**, so editing
189
+ front-matter mid-run raises `plan_blob_changed`.
190
+
191
+ ```yaml
192
+ ---
193
+ cycles:
194
+ - n: 1
195
+ project: backend
196
+ title: "unmapped exception is not swallowed"
197
+ test: "tests/test_map.py::test_unmapped_is_not_swallowed"
198
+ stub_expected: ["app/exception_map.py"]
199
+ commit_red: "test: unmapped exception is not swallowed"
200
+ commit_green: "feat: domain exception map skeleton"
201
+ - n: 8
202
+ project: backend
203
+ pin_cycle: true # characterisation; passes on arrival by design
204
+ test: "tests/test_keys.py::test_enrol_maps_signature_error_to_422"
205
+ - n: 12
206
+ projects: ["backend", "frontend"]
207
+ contract_cycle: true # breaking change: no intermediate green state
208
+ tests:
209
+ - "backend::tests/test_openapi.py::test_upload_body_schema"
210
+ - "frontend::services/__tests__/upload.test.ts > matches contract"
211
+ annotation_keys: ["literal_detail_handlers_kept"]
212
+ ---
213
+ ```
214
+
215
+ Absent front-matter is legitimate — the run proceeds as `undeclared` with
216
+ `--allow-undeclared`, and fidelity metrics are unavailable. **Malformed** front-matter
217
+ hard-fails registration: it is almost always a defect in the planning process, and that
218
+ signal must surface rather than degrade silently.
219
+
220
+ [`examples/plan.md`](./examples/plan.md) is a complete plan — every cycle kind, the full
221
+ front-matter vocabulary, and the body structure (context, verified repo facts, per-cycle
222
+ expected failures) that lets an agent execute it without conversation context. The test
223
+ suite registers it, so it cannot drift from the contract parser.
224
+
225
+ Producing a plan of that shape is itself a process.
226
+ [`examples/skills/tdd-handoff/`](./examples/skills/tdd-handoff/) is a Claude
227
+ Code skill that takes a draft plan, verifies its claims against the codebase, probes each
228
+ cycle's RED path empirically, assigns cycle kinds, and authors the contract — gated on
229
+ `tdd plan register` succeeding with the intended cycle count and kind breakdown.
230
+
231
+ ## The friction log
232
+
233
+ `tdd log render` projects the ledger into a markdown friction log — the feedback channel
234
+ back to the **planning** process. It reports plan fidelity (declared vs delivered vs
235
+ skipped vs never-reached cycles, human interventions) and, per cycle: the target, suite
236
+ runs by phase, the first-run outcome against expectation, sensitivity checks, commits,
237
+ and integrity events.
238
+
239
+ Every observable fact in it is projected from recorded events. The agent that did the
240
+ work cannot compose it — that is what makes it worth reading, and why the log is
241
+ rendered, never written. Judgement enters in exactly two ways:
242
+
243
+ - **Per cycle, through `tdd annotate`** — rendered inline in the cycle it concerns.
244
+ Beyond keys the plan requires via `annotation_keys`, these keys are reserved for
245
+ judgement agents volunteer: `plan_defect`, `friction_note`, `red_expectation`,
246
+ `commit_shape_deviation`, `test_setup_smell`, `unplanned_change`, `new_work_raised`.
247
+ `plan_defect` is the one that matters most: it records where the plan and the codebase
248
+ disagreed, which is precisely what the next plan needs to know.
249
+ - **Per run, as prose appended below the rendered document.** Legitimate and expected —
250
+ post-run narrative (CI failures, patterns noticed) has no cycle to attach to. But it
251
+ is unverified: an auditor should trust the projected sections and read appended
252
+ narrative as the agent's opinion.
253
+
254
+ `tdd metrics` is the quantitative companion: attempts per cycle, RED-first violation
255
+ rate, fidelity, blockers, interventions. Cross-plan aggregates are deliberately labelled
256
+ non-comparable — cycle difficulty varies too much — so compare runs of the same contract
257
+ only (e.g. the same plan executed by two models).
258
+
259
+ The loop closes when the rendered log is committed alongside the plan and read before
260
+ the next plan is written.
261
+
262
+ ## Adapters
263
+
264
+ `pytest` and `vitest` are built in. The pytest adapter runs the suite through the
265
+ project's own environment manager, detected from its marker files — `uv.lock`,
266
+ `poetry.lock`, `Pipfile`, `pdm.lock`, or `[tool.poetry]` in `pyproject.toml` — checked at
267
+ the project root first, then the worktree root (workspace layouts keep one lockfile at the
268
+ top). With no marker, the active environment's bare `pytest` runs. An explicit
269
+ `test_command` always wins.
270
+
271
+ Third-party adapters register under the
272
+ `tddcli.adapters` entry-point group:
273
+
274
+ ```toml
275
+ [project.entry-points."tddcli.adapters"]
276
+ cargo = "tddcli_cargo:CargoAdapter"
277
+ ```
278
+
279
+ The class must implement `tddcli.adapters.base.Adapter`. Built-in names cannot be
280
+ shadowed: a plugin named `pytest` is ignored, so what "observed test execution" means for
281
+ existing configs can never change underneath them.
282
+
283
+ ## Platform support
284
+
285
+ Linux and macOS. Windows is refused at startup with `reason: "unsupported_platform"` —
286
+ worker leases and process-liveness checks are POSIX-only. Use WSL.
287
+
288
+ Every JSON envelope carries `envelope_version`; consumers should check it rather than
289
+ assuming the shape is stable across releases. See also [SECURITY.md](./SECURITY.md) for
290
+ the trust model: running `tdd` executes the repository's declared commands.
291
+
292
+ ## Cycle kinds
293
+
294
+ **Standard / contract** — `AWAITING_TEST → AWAITING_IMPL → AWAITING_REFACTOR → CLOSED`
295
+
296
+ **Pin** — `AWAITING_PIN → SENSITIVITY_REQUIRED → AWAITING_REFACTOR → CLOSED`
297
+
298
+ A pin characterises existing behaviour before deleting or restructuring it, so its test
299
+ passes on arrival by design and its sensitivity check is mandatory. Pins are excluded from
300
+ the RED-first violation metric; a *standard* cycle that passes on arrival remains a violation
301
+ and is never reclassified as a pin.
302
+
303
+ ## What the tool does, that agents do not
304
+
305
+ - **Stages and commits.** The staged set is derived from the phase: RED takes tests and
306
+ declared stubs, GREEN takes the rest. This makes "implementation written during RED" an
307
+ exact, language-independent detection with no source parsing.
308
+ - **Regenerates stale artifacts**, in their own commit, so hand-written and generated changes
309
+ stay separately reviewable.
310
+ - **Resolves executor identity** from the session transcript. It is never an argument.
311
+ - **Runs the close sweep** over the cycle's projects plus anything downstream of an artifact
312
+ it touched — not every project every time.
313
+
314
+ ## Commands
315
+
316
+ | Command | Purpose |
317
+ |---|---|
318
+ | `tdd init` / `tdd doctor` | scaffold config; environment preflight |
319
+ | `tdd plan register <path>` | parse and hash the contract |
320
+ | `tdd run start --plan <path>` | capture baselines, resolve executor, open cycle 1 |
321
+ | `tdd status` | position and `next_action`; safe any time |
322
+ | `tdd advance [--retry]` | run suites, compute the transition, commit |
323
+ | `tdd cycle skip --reason` | sanctioned path for a cycle the plan got wrong |
324
+ | `tdd sensitivity begin\|check\|end` | prove a passing test can fail; verify restore |
325
+ | `tdd annotate --key --value` | attach judgement to the current cycle |
326
+ | `tdd blocker --kind --detail` | typed blocker; releases the stop hook |
327
+ | `tdd resume [--unblock --note]` | reconstruct position; human intervention |
328
+ | `tdd log render [--out]` | project the ledger into a friction log |
329
+ | `tdd metrics` | fidelity, attempts, violations, interventions |
330
+ | `tdd fleet [--json]` | all active runs across every worktree; read-only |
331
+
332
+ ## Running a long baseline
333
+
334
+ `run start` probes every project's suite before a run exists (R9.5a), and on a real project
335
+ that can take minutes — well past an agent harness's default Bash timeout. If the command
336
+ appears to hang or time out, **do not re-run it**: the probe is still making progress in the
337
+ background, and a second `run start` against the same worktree is refused with
338
+ `reason: "baseline_in_progress"` — retrying on timeout just stacks refusals on top of a
339
+ baseline that was never stuck. In order of preference:
340
+
341
+ 1. **Background it.** Run `tdd run start` in the background if your harness supports it. The
342
+ heartbeat (`baseline_captured` / `project_completed` lines on stderr) lands in the task log
343
+ as each project finishes, and most harnesses re-invoke the agent when a backgrounded command
344
+ exits — a real completion callback, with no timeout ceiling.
345
+ 2. **Raise the timeout.** Claude Code's Bash tool takes an explicit `timeout` (default 120000ms,
346
+ max 600000ms). A baseline that takes 3–8 minutes fits inside ten.
347
+ 3. **Poll.** `tdd progress` (and `tdd status`) report `collecting_baseline` with per-project
348
+ counters and elapsed time while a baseline is in flight, with `next_action.verb ==
349
+ "await_baseline"` — the fallback for an agent that inherited a run it did not start itself.
350
+
351
+ ## Storage
352
+
353
+ One SQLite ledger **per repository**, in `~/.local/share/tdd-cli/` (override with
354
+ `TDD_LEDGER_HOME`), keyed by the common git dir. Never inside the worktree, never resolved
355
+ from the current directory, never committed. `worktree_path` is a column, so concurrent runs
356
+ in separate worktrees are isolated without a pruned worktree orphaning its history.
357
+
358
+ ## Enforcement boundary
359
+
360
+ The CLI cannot compel an agent — only the harness can.
361
+
362
+ **Hard gates here:** phase is never caller-supplied; a cycle cannot close over a stale
363
+ artifact; a passed-on-arrival cycle cannot close without a verified sensitivity check;
364
+ `advance` refuses an unchanged tree unless `--retry`.
365
+
366
+ **Recorded, never blocked:** non-stub writes during RED, undeclared file touches, scope
367
+ divergence, extra attempts. Prevention rules with edge cases produce false denials, and a
368
+ blocked agent improvises around them — putting it right back in the reporting path the
369
+ tool exists to keep it out of.
370
+
371
+ **Delegated to hooks:** a Stop hook that queries `tdd status` and refuses to let an agent
372
+ stop while a run is live; a Bash hook redirecting bare `pytest`/`vitest` through `tdd advance`.
373
+ Ready-made Claude Code implementations of both live in
374
+ [`examples/claude-code-hooks/`](./examples/claude-code-hooks/).
375
+
376
+ **Delegated to the skill:** how to respond to each `next_action` verb — writing the test,
377
+ the stub, the implementation. [`docs/harness-integration.md`](./docs/harness-integration.md)
378
+ is the contract for writing one against any harness.
379
+
380
+ ## Development
381
+
382
+ ```sh
383
+ uv run pytest
384
+ uv run ruff check src tests
385
+ ```
386
+
387
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
388
+
389
+ ## License
390
+
391
+ [MIT](./LICENSE)