agentic-dev-loop 1.0.0a1__tar.gz → 1.0.0a3__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. agentic_dev_loop-1.0.0a3/PKG-INFO +239 -0
  2. agentic_dev_loop-1.0.0a3/README.md +200 -0
  3. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/pyproject.toml +7 -2
  4. agentic_dev_loop-1.0.0a3/src/agentic_dev_loop/__init__.py +1 -0
  5. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/isolation.py +18 -15
  6. agentic_dev_loop-1.0.0a3/src/agentic_dev_loop.egg-info/PKG-INFO +239 -0
  7. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop.egg-info/requires.txt +1 -0
  8. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_isolation.py +27 -12
  9. agentic_dev_loop-1.0.0a1/PKG-INFO +0 -343
  10. agentic_dev_loop-1.0.0a1/README.md +0 -305
  11. agentic_dev_loop-1.0.0a1/src/agentic_dev_loop/__init__.py +0 -1
  12. agentic_dev_loop-1.0.0a1/src/agentic_dev_loop.egg-info/PKG-INFO +0 -343
  13. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/LICENSE +0 -0
  14. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/setup.cfg +0 -0
  15. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/actions.py +0 -0
  16. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/agents.py +0 -0
  17. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/cli.py +0 -0
  18. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/config.py +0 -0
  19. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/dryrun.py +0 -0
  20. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/extensions/__init__.py +0 -0
  21. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/extensions/aidlc.py +0 -0
  22. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/graph.py +0 -0
  23. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/llm.py +0 -0
  24. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/loaders.py +0 -0
  25. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/outputs.py +0 -0
  26. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/proc.py +0 -0
  27. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/py.typed +0 -0
  28. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/registry.py +0 -0
  29. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/results.py +0 -0
  30. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/stages.py +0 -0
  31. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop/viz.py +0 -0
  32. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
  33. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  34. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  35. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  36. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_actions.py +0 -0
  37. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_agents.py +0 -0
  38. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_aidlc.py +0 -0
  39. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_cli.py +0 -0
  40. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_config.py +0 -0
  41. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_dry_run.py +0 -0
  42. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_e2e_fixtures.py +0 -0
  43. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_graph.py +0 -0
  44. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_isolation_integration.py +0 -0
  45. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_isolation_pbt.py +0 -0
  46. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_llm.py +0 -0
  47. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_loaders.py +0 -0
  48. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_outputs.py +0 -0
  49. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_outputs_pbt.py +0 -0
  50. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_proc.py +0 -0
  51. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_publish.py +0 -0
  52. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_registry.py +0 -0
  53. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_results.py +0 -0
  54. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_sdk_compat.py +0 -0
  55. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_source_resolution.py +0 -0
  56. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_stages.py +0 -0
  57. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_verdict.py +0 -0
  58. {agentic_dev_loop-1.0.0a1 → agentic_dev_loop-1.0.0a3}/tests/test_viz.py +0 -0
@@ -0,0 +1,239 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentic-dev-loop
3
+ Version: 1.0.0a3
4
+ Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
+ Author: Luke Wilson
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/lukedawilson/agentic-dev-loop
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Classifier: Operating System :: POSIX :: Linux
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Topic :: Software Development
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Software Development :: Quality Assurance
23
+ Classifier: Topic :: Security
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: openhands-sdk~=1.17.0
29
+ Requires-Dist: openhands-tools~=1.17.0
30
+ Requires-Dist: agent-client-protocol<1.0.0
31
+ Requires-Dist: langgraph>=1.2
32
+ Requires-Dist: langgraph-checkpoint-sqlite>=3.0
33
+ Requires-Dist: pydantic>=2.0
34
+ Requires-Dist: PyYAML>=6.0
35
+ Provides-Extra: dev
36
+ Requires-Dist: hypothesis>=6.0; extra == "dev"
37
+ Requires-Dist: pytest>=8.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # agentic-dev-loop
41
+
42
+ > **Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.**
43
+
44
+ Describe your pipeline in one YAML file — agent steps, mechanical test gates, and
45
+ trusted actions — and `agentic-dev-loop` runs it end to end: agent and command steps get
46
+ kernel-level sandboxing via Docker, verdicts route the graph, every boundary is
47
+ checkpointed, and nothing gets committed until every gate passes.
48
+
49
+ ```mermaid
50
+ flowchart LR
51
+ dev["agent: dev"] --> test{{"test gate"}} --> review["agent: review"] --> qa["agent: qa"] --> commit["action: commit"]
52
+ test -- fail --> dev
53
+ review -- fail --> dev
54
+ qa -- fail --> dev
55
+ ```
56
+
57
+ - **A tool, not a wiring project.** One CLI entry point, opinionated defaults,
58
+ fixed safety policies. You write the pipeline and the agent definitions —
59
+ nothing else.
60
+ - **Deterministic.** Same pipeline + same state → same routing. Verdicts are
61
+ parsed from files, retries are bounded, nothing is best-effort.
62
+ - **Sandboxed by default.** The agent's shell and every command step run inside
63
+ an always-on Docker sandbox. There is no opt-out.
64
+
65
+ ---
66
+
67
+ ## Core features
68
+
69
+ | Feature | What it gives you |
70
+ | --- | --- |
71
+ | **Stage-gated agent loop** | Agents do the work, test stages verify it, and the pipeline only advances when each stage passes its gates — looping back with full context when one fails. |
72
+ | **Kernel-level sandboxing** | Agent work and command stages run isolated from your machine: no network access, no writes outside your workspace. No sandbox available → the run stops instead of degrading. |
73
+ | **Crash-safe resume** | Interrupt a run at any time and pick it up later — finished stages stay finished, and you resume from exactly where it stopped. |
74
+ | **Live graph visualisation** | Follow every run in the browser: stage status, pass/fail verdicts, retry cycles, and live console output as it happens. |
75
+ | **Extensible** | Plug in your own actions, tools, and document loaders to adapt the loop to your projects. |
76
+
77
+ The fixed policies *are* the product: fail fast on missing config/secrets, no
78
+ silent fallbacks, verdicts only from artifacts, bounded retries, per-attempt
79
+ artifact hygiene, a single clean-worktree-gated commit point, and no default
80
+ LLM timeout (agent runs are long).
81
+
82
+ ## Installation
83
+
84
+ **Prerequisites**
85
+
86
+ | Requirement | Detail |
87
+ | --- | --- |
88
+ | Python | ≥ 3.12 |
89
+ | Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works — Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, Linux-containers mode is required. |
90
+ | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set — a missing key fails fast, naming it. |
91
+
92
+ ```bash
93
+ pip install agentic-dev-loop==1.0.0a3 # pin the current alpha release explicitly
94
+ agentic-dev-loop --version
95
+ ```
96
+
97
+ On the first run the engine pulls the **digest-pinned** dev image
98
+ (`ghcr.io/lukedawilson/agentic-dev-loop/dev`) and verifies its digest — the
99
+ supply-chain check refuses to continue on a mismatch. If you're running
100
+ pre-publish tooling, point `AGENTIC_DEV_LOOP_DEV_IMAGE` at your locally built
101
+ image. Engine setup, first-run details, the full `pipeline.yaml` contract, and
102
+ troubleshooting for every fail-fast code live in
103
+ [docs/setup.md](docs/setup.md).
104
+
105
+ ## Usage
106
+
107
+ ```bash
108
+ export OPENAI_API_KEY=... # whatever api_key_env names
109
+ agentic-dev-loop pipeline.yaml # run it
110
+ agentic-dev-loop pipeline.yaml --dry-run # validate first — no LLM, no Docker
111
+ agentic-dev-loop pipeline.yaml --resume <thread-id> --max-attempts 5
112
+ ```
113
+
114
+ | Flag | Effect |
115
+ | --- | --- |
116
+ | `--dry-run` | Full load-and-validate pass, then print the resolved plan. No LLM, no Docker, no state writes, no API key. Exit `0` on a clean load. |
117
+ | `--resume <thread-id>` | Continue the latest checkpoint of a previous run (crash, Ctrl-C, exhaustion) with all accumulated notes intact. |
118
+ | `--max-attempts <n>` | Override the pipeline's `max_attempts`. |
119
+ | `--refresh` | Force re-provision of every `setup:` hook (bypasses the stamp cache). |
120
+ | `--clean-stamps` | Delete cached provision stamps and exit. |
121
+ | `--no-viz` / `--viz-port <port>` | Suppress the live graph view / set a preferred port (default 8321; falls back to a free port when busy). |
122
+ | extension flags | Document-loader flags (e.g. `--plan`, `--aidlc-unit`) appear once the pipeline's extensions are loaded — `agentic-dev-loop PIPELINE --help` shows them. |
123
+
124
+ Exit code is `0` on pipeline success, `1` otherwise.
125
+
126
+ **What a run looks like** (the shipped self-loop example):
127
+
128
+ ![The live graph view — pipeline stages with verdict badges, retry cycles, and per-stage consoles](docs/viz-screenshot.png)
129
+
130
+ 1. **dev** — the dev agent works with full context (loaded docs + shared files).
131
+ Its edits are untracked; only declared `produces:` files are hygiene-tracked.
132
+ 2. **test** — a mechanical command step runs the suite inside the sandbox.
133
+ Exit 0 moves on; anything else rewinds to `dev` with the failing output.
134
+ 3. **review / qa** — verdict agents write artifacts whose `VERDICT:` lines route
135
+ the graph. `fail` rewinds to `dev` and consumes an attempt.
136
+ 4. **commit** — the single commit point fires only when all gates passed and
137
+ the worktree was clean at start.
138
+ 5. **success** — committed; or **failure** when attempts are exhausted.
139
+
140
+ ### pipeline.yaml reference
141
+
142
+ ```yaml
143
+ name: my-construction-loop
144
+ extensions:
145
+ - agentic_dev_loop.extensions.aidlc:AidlcExtension # bundled AI-DLC doc loader
146
+ additional_files:
147
+ - ../README.md # shared context for every agent step
148
+
149
+ llm:
150
+ model: openai/deepseek-v4-pro
151
+ base_url: https://api.deepseek.com
152
+ api_key_env: OPENAI_API_KEY
153
+
154
+ max_attempts: 10 # CLI --max-attempts overrides
155
+ agents_dir: sdk_agents # your <name>.agent.md files
156
+
157
+ steps:
158
+ - name: dev
159
+ agent: dev
160
+ produces: implementation-summary.md
161
+ on_pass: test
162
+
163
+ - name: test
164
+ command: .venv-sandbox/bin/python -m pytest tests/ -q -m "not docker"
165
+ setup: python -m venv .venv-sandbox && .venv-sandbox/bin/pip install -e ".[dev]"
166
+ timeout: 600
167
+ on_pass: review
168
+ on_fail: { goto: dev, retry: true }
169
+
170
+ - name: review
171
+ agent: review
172
+ artifact: review-findings.md
173
+ verdicts: { pass: APPROVED, fail: NEEDS CHANGES }
174
+ on_pass: qa
175
+ on_fail: { goto: dev, retry: true }
176
+
177
+ - name: qa
178
+ agent: qa
179
+ artifact: qa-report.md
180
+ verdicts: { pass: PASS, fail: FAIL }
181
+ on_pass: commit
182
+ on_fail: { goto: dev, retry: true }
183
+
184
+ - name: commit
185
+ action: commit
186
+ params: { message: "feat: construct {subject} (agent dev loop)" }
187
+ ```
188
+
189
+ | Key | Notes |
190
+ | --- | --- |
191
+ | `agent:` / `command:` / `action:` | Exactly one per step. Agents live in `agents_dir` as `<name>.agent.md`; commands run sandboxed (mechanical, never an LLM call); actions are registered host-side functions. |
192
+ | `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs verdict-parsed routing. The **last** `VERDICT:` line wins; stale artifacts are deleted at each attempt start. |
193
+ | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
194
+ | `env:` / `setup:` (command steps) | Declared env inside the sandbox (fixed safe base + declared vars only); `setup:` provisions the sandbox (network ON, stamp-cached, `--refresh` forces). |
195
+ | `isolation:` (optional) | Configures the sandbox — image override, resource caps, `allow_git_mutations`. It can never disable it. |
196
+ | `state_dir` | Defaults to `~/.agentic-dev-loop/runs/<pipeline-hash>/` — artifacts and checkpoints live outside the sandbox mount. |
197
+
198
+ ## Development environment
199
+
200
+ ```bash
201
+ git clone git@github.com:lukedawilson/agentic-dev-loop.git
202
+ cd agentic-dev-loop
203
+ ./setup-venv.sh # idempotent .venv setup (never touches bare pip)
204
+ # create .env with your LLM key (loaded by trigger-agentic-dev-loop.sh, never committed):
205
+ # echo 'OPENAI_API_KEY=...' > .env
206
+ python -m pytest tests/ -q # fast suite
207
+ python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
208
+ ./trigger-agentic-dev-loop.sh --dry-run # run the shipped pipeline against this repo
209
+ ./run-demo.sh # viz demo — scripted run replay, no pipeline needed
210
+ ```
211
+
212
+ - `setup-venv.sh` is safe to re-run and picks the right interpreter even on
213
+ machines with exotic Python installations.
214
+ - Docker-marked integration tests need a local daemon; CI runs them on Linux.
215
+ - The shipped pipeline (`agentic-dev-loop/pipeline.yaml`) **dogfoods the tool on
216
+ its own repo** — dev → test → review → qa → commit, with the engine's own
217
+ three agent definitions in `sdk_agents/`.
218
+ - CI: `ci.yml` (unit + integration suite, ubuntu + windows),
219
+ `publish-image.yml` (multi-arch dev image, amd64 + arm64), `release.yml`
220
+ (PyPI publishing).
221
+
222
+ ## Contributing
223
+
224
+ - **Read [AGENTS.md](AGENTS.md) first** — it is the instruction set for both
225
+ human and agent contributors (default mode: the user drives, ask before
226
+ coding, TDD, no silent fallbacks).
227
+ - Development is structured by **AI-DLC** — the lifecycle workflow in
228
+ `.aidlc/aidlc-rules/aws-aidlc-rules/` with working docs (plans, units, state)
229
+ in `aidlc-docs/`. The bundled document loader autodetects the current unit
230
+ from `aidlc-state.md` via `--aidlc-unit`, or load any plan with `--plan`.
231
+ - OpenCode commands over the workflow: `/unit-status`, `/epic-status`,
232
+ `/ticket-details`, `/update-subtask`, `/apply-aidlc-patches`, and more.
233
+ - Working extension samples (actions, tools, document loaders) ship as
234
+ `tests/sample_ext.py`.
235
+
236
+ ---
237
+
238
+ Built on [OpenHands SDK](https://github.com/OpenHands/software-agent-sdk) and
239
+ [LangGraph](https://github.com/langchain-ai/langgraph).
@@ -0,0 +1,200 @@
1
+ # agentic-dev-loop
2
+
3
+ > **Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.**
4
+
5
+ Describe your pipeline in one YAML file — agent steps, mechanical test gates, and
6
+ trusted actions — and `agentic-dev-loop` runs it end to end: agent and command steps get
7
+ kernel-level sandboxing via Docker, verdicts route the graph, every boundary is
8
+ checkpointed, and nothing gets committed until every gate passes.
9
+
10
+ ```mermaid
11
+ flowchart LR
12
+ dev["agent: dev"] --> test{{"test gate"}} --> review["agent: review"] --> qa["agent: qa"] --> commit["action: commit"]
13
+ test -- fail --> dev
14
+ review -- fail --> dev
15
+ qa -- fail --> dev
16
+ ```
17
+
18
+ - **A tool, not a wiring project.** One CLI entry point, opinionated defaults,
19
+ fixed safety policies. You write the pipeline and the agent definitions —
20
+ nothing else.
21
+ - **Deterministic.** Same pipeline + same state → same routing. Verdicts are
22
+ parsed from files, retries are bounded, nothing is best-effort.
23
+ - **Sandboxed by default.** The agent's shell and every command step run inside
24
+ an always-on Docker sandbox. There is no opt-out.
25
+
26
+ ---
27
+
28
+ ## Core features
29
+
30
+ | Feature | What it gives you |
31
+ | --- | --- |
32
+ | **Stage-gated agent loop** | Agents do the work, test stages verify it, and the pipeline only advances when each stage passes its gates — looping back with full context when one fails. |
33
+ | **Kernel-level sandboxing** | Agent work and command stages run isolated from your machine: no network access, no writes outside your workspace. No sandbox available → the run stops instead of degrading. |
34
+ | **Crash-safe resume** | Interrupt a run at any time and pick it up later — finished stages stay finished, and you resume from exactly where it stopped. |
35
+ | **Live graph visualisation** | Follow every run in the browser: stage status, pass/fail verdicts, retry cycles, and live console output as it happens. |
36
+ | **Extensible** | Plug in your own actions, tools, and document loaders to adapt the loop to your projects. |
37
+
38
+ The fixed policies *are* the product: fail fast on missing config/secrets, no
39
+ silent fallbacks, verdicts only from artifacts, bounded retries, per-attempt
40
+ artifact hygiene, a single clean-worktree-gated commit point, and no default
41
+ LLM timeout (agent runs are long).
42
+
43
+ ## Installation
44
+
45
+ **Prerequisites**
46
+
47
+ | Requirement | Detail |
48
+ | --- | --- |
49
+ | Python | ≥ 3.12 |
50
+ | Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works — Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, Linux-containers mode is required. |
51
+ | LLM API key | The env var named by your pipeline's `api_key_env` (e.g. `OPENAI_API_KEY`) must be set — a missing key fails fast, naming it. |
52
+
53
+ ```bash
54
+ pip install agentic-dev-loop==1.0.0a3 # pin the current alpha release explicitly
55
+ agentic-dev-loop --version
56
+ ```
57
+
58
+ On the first run the engine pulls the **digest-pinned** dev image
59
+ (`ghcr.io/lukedawilson/agentic-dev-loop/dev`) and verifies its digest — the
60
+ supply-chain check refuses to continue on a mismatch. If you're running
61
+ pre-publish tooling, point `AGENTIC_DEV_LOOP_DEV_IMAGE` at your locally built
62
+ image. Engine setup, first-run details, the full `pipeline.yaml` contract, and
63
+ troubleshooting for every fail-fast code live in
64
+ [docs/setup.md](docs/setup.md).
65
+
66
+ ## Usage
67
+
68
+ ```bash
69
+ export OPENAI_API_KEY=... # whatever api_key_env names
70
+ agentic-dev-loop pipeline.yaml # run it
71
+ agentic-dev-loop pipeline.yaml --dry-run # validate first — no LLM, no Docker
72
+ agentic-dev-loop pipeline.yaml --resume <thread-id> --max-attempts 5
73
+ ```
74
+
75
+ | Flag | Effect |
76
+ | --- | --- |
77
+ | `--dry-run` | Full load-and-validate pass, then print the resolved plan. No LLM, no Docker, no state writes, no API key. Exit `0` on a clean load. |
78
+ | `--resume <thread-id>` | Continue the latest checkpoint of a previous run (crash, Ctrl-C, exhaustion) with all accumulated notes intact. |
79
+ | `--max-attempts <n>` | Override the pipeline's `max_attempts`. |
80
+ | `--refresh` | Force re-provision of every `setup:` hook (bypasses the stamp cache). |
81
+ | `--clean-stamps` | Delete cached provision stamps and exit. |
82
+ | `--no-viz` / `--viz-port <port>` | Suppress the live graph view / set a preferred port (default 8321; falls back to a free port when busy). |
83
+ | extension flags | Document-loader flags (e.g. `--plan`, `--aidlc-unit`) appear once the pipeline's extensions are loaded — `agentic-dev-loop PIPELINE --help` shows them. |
84
+
85
+ Exit code is `0` on pipeline success, `1` otherwise.
86
+
87
+ **What a run looks like** (the shipped self-loop example):
88
+
89
+ ![The live graph view — pipeline stages with verdict badges, retry cycles, and per-stage consoles](docs/viz-screenshot.png)
90
+
91
+ 1. **dev** — the dev agent works with full context (loaded docs + shared files).
92
+ Its edits are untracked; only declared `produces:` files are hygiene-tracked.
93
+ 2. **test** — a mechanical command step runs the suite inside the sandbox.
94
+ Exit 0 moves on; anything else rewinds to `dev` with the failing output.
95
+ 3. **review / qa** — verdict agents write artifacts whose `VERDICT:` lines route
96
+ the graph. `fail` rewinds to `dev` and consumes an attempt.
97
+ 4. **commit** — the single commit point fires only when all gates passed and
98
+ the worktree was clean at start.
99
+ 5. **success** — committed; or **failure** when attempts are exhausted.
100
+
101
+ ### pipeline.yaml reference
102
+
103
+ ```yaml
104
+ name: my-construction-loop
105
+ extensions:
106
+ - agentic_dev_loop.extensions.aidlc:AidlcExtension # bundled AI-DLC doc loader
107
+ additional_files:
108
+ - ../README.md # shared context for every agent step
109
+
110
+ llm:
111
+ model: openai/deepseek-v4-pro
112
+ base_url: https://api.deepseek.com
113
+ api_key_env: OPENAI_API_KEY
114
+
115
+ max_attempts: 10 # CLI --max-attempts overrides
116
+ agents_dir: sdk_agents # your <name>.agent.md files
117
+
118
+ steps:
119
+ - name: dev
120
+ agent: dev
121
+ produces: implementation-summary.md
122
+ on_pass: test
123
+
124
+ - name: test
125
+ command: .venv-sandbox/bin/python -m pytest tests/ -q -m "not docker"
126
+ setup: python -m venv .venv-sandbox && .venv-sandbox/bin/pip install -e ".[dev]"
127
+ timeout: 600
128
+ on_pass: review
129
+ on_fail: { goto: dev, retry: true }
130
+
131
+ - name: review
132
+ agent: review
133
+ artifact: review-findings.md
134
+ verdicts: { pass: APPROVED, fail: NEEDS CHANGES }
135
+ on_pass: qa
136
+ on_fail: { goto: dev, retry: true }
137
+
138
+ - name: qa
139
+ agent: qa
140
+ artifact: qa-report.md
141
+ verdicts: { pass: PASS, fail: FAIL }
142
+ on_pass: commit
143
+ on_fail: { goto: dev, retry: true }
144
+
145
+ - name: commit
146
+ action: commit
147
+ params: { message: "feat: construct {subject} (agent dev loop)" }
148
+ ```
149
+
150
+ | Key | Notes |
151
+ | --- | --- |
152
+ | `agent:` / `command:` / `action:` | Exactly one per step. Agents live in `agents_dir` as `<name>.agent.md`; commands run sandboxed (mechanical, never an LLM call); actions are registered host-side functions. |
153
+ | `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs verdict-parsed routing. The **last** `VERDICT:` line wins; stale artifacts are deleted at each attempt start. |
154
+ | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
155
+ | `env:` / `setup:` (command steps) | Declared env inside the sandbox (fixed safe base + declared vars only); `setup:` provisions the sandbox (network ON, stamp-cached, `--refresh` forces). |
156
+ | `isolation:` (optional) | Configures the sandbox — image override, resource caps, `allow_git_mutations`. It can never disable it. |
157
+ | `state_dir` | Defaults to `~/.agentic-dev-loop/runs/<pipeline-hash>/` — artifacts and checkpoints live outside the sandbox mount. |
158
+
159
+ ## Development environment
160
+
161
+ ```bash
162
+ git clone git@github.com:lukedawilson/agentic-dev-loop.git
163
+ cd agentic-dev-loop
164
+ ./setup-venv.sh # idempotent .venv setup (never touches bare pip)
165
+ # create .env with your LLM key (loaded by trigger-agentic-dev-loop.sh, never committed):
166
+ # echo 'OPENAI_API_KEY=...' > .env
167
+ python -m pytest tests/ -q # fast suite
168
+ python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
169
+ ./trigger-agentic-dev-loop.sh --dry-run # run the shipped pipeline against this repo
170
+ ./run-demo.sh # viz demo — scripted run replay, no pipeline needed
171
+ ```
172
+
173
+ - `setup-venv.sh` is safe to re-run and picks the right interpreter even on
174
+ machines with exotic Python installations.
175
+ - Docker-marked integration tests need a local daemon; CI runs them on Linux.
176
+ - The shipped pipeline (`agentic-dev-loop/pipeline.yaml`) **dogfoods the tool on
177
+ its own repo** — dev → test → review → qa → commit, with the engine's own
178
+ three agent definitions in `sdk_agents/`.
179
+ - CI: `ci.yml` (unit + integration suite, ubuntu + windows),
180
+ `publish-image.yml` (multi-arch dev image, amd64 + arm64), `release.yml`
181
+ (PyPI publishing).
182
+
183
+ ## Contributing
184
+
185
+ - **Read [AGENTS.md](AGENTS.md) first** — it is the instruction set for both
186
+ human and agent contributors (default mode: the user drives, ask before
187
+ coding, TDD, no silent fallbacks).
188
+ - Development is structured by **AI-DLC** — the lifecycle workflow in
189
+ `.aidlc/aidlc-rules/aws-aidlc-rules/` with working docs (plans, units, state)
190
+ in `aidlc-docs/`. The bundled document loader autodetects the current unit
191
+ from `aidlc-state.md` via `--aidlc-unit`, or load any plan with `--plan`.
192
+ - OpenCode commands over the workflow: `/unit-status`, `/epic-status`,
193
+ `/ticket-details`, `/update-subtask`, `/apply-aidlc-patches`, and more.
194
+ - Working extension samples (actions, tools, document loaders) ship as
195
+ `tests/sample_ext.py`.
196
+
197
+ ---
198
+
199
+ Built on [OpenHands SDK](https://github.com/OpenHands/software-agent-sdk) and
200
+ [LangGraph](https://github.com/langchain-ai/langgraph).
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "1.0.0a1"
8
- description = "Opinionated LangGraph engine for OpenHands agent pipelines"
7
+ version = "1.0.0a3"
8
+ description = "Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
11
11
  license = "Apache-2.0"
@@ -34,6 +34,11 @@ classifiers = [
34
34
  dependencies = [
35
35
  "openhands-sdk ~=1.17.0",
36
36
  "openhands-tools ~=1.17.0",
37
+ # openhands-sdk declares agent-client-protocol>=0.8.1 with NO upper bound;
38
+ # the 1.0.0rc pre-releases drop acp.helpers (breaks sdk 1.17.0 imports).
39
+ # Without this cap, --pre installs resolve to the broken rc. See AGENTS.md
40
+ # error-handling rules + COMPAT-SHIMS.md for the SDK-compat pattern.
41
+ "agent-client-protocol<1.0.0",
37
42
  "langgraph>=1.2",
38
43
  "langgraph-checkpoint-sqlite>=3.0",
39
44
  "pydantic>=2.0",
@@ -0,0 +1 @@
1
+ """Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase."""
@@ -282,12 +282,14 @@ def host_user_flag() -> str | None:
282
282
 
283
283
  def docker_create_payload(*, run_id: str, image: str, profile: EffectiveProfile,
284
284
  workspace: str) -> list[str]:
285
- """Exact ``docker create`` argv for the execution container (§3.3, KD-8).
285
+ """Exact ``docker run -d --rm`` argv for the execution container (§3.3, KD-8).
286
286
  Single mount {workspace} (Q1); --network none, --read-only, --cap-drop ALL;
287
287
  --user host uid:gid (host-owned bind mounts, AE-SBX-009 root cause) with
288
- HOME=/tmp (the rootfs is read-only; /tmp is a 1777 tmpfs)."""
288
+ HOME=/tmp (the rootfs is read-only; /tmp is a 1777 tmpfs). ``--rm`` lets the
289
+ daemon auto-remove the container the instant it exits, however it exits
290
+ (D1) — a SIGKILLed CLI never reaches close()."""
289
291
  payload = [
290
- "docker", "create", "--name", f"{run_id}-exec",
292
+ "docker", "run", "--detach", "--rm", "--name", f"{run_id}-exec",
291
293
  "--network", "none", "--read-only", "--cap-drop", "ALL",
292
294
  "--memory", profile.memory, "--cpus", str(profile.cpus),
293
295
  "--tmpfs", "/tmp", "--tmpfs", "/root",
@@ -452,20 +454,17 @@ class DockerBackend(Sandbox):
452
454
  self._verify_digest()
453
455
 
454
456
  def create_execution_container(self) -> None:
455
- """Eager create + start (Q2) — never lazy."""
457
+ """Eager create + start via ``docker run -d --rm`` (Q2, D1) — never
458
+ lazy; ``run -d`` is create + start in one call, so there is no
459
+ separate ``docker start``."""
456
460
  payload = docker_create_payload(
457
461
  run_id=self._run_id, image=self._image, profile=self._profile,
458
462
  workspace=self._workspace)
459
- create = self._runner.run(payload, timeout=60)
460
- if create.exit_code != 0:
461
- raise SandboxError(
462
- f"AE-SBX-010: container create failed: "
463
- f"{create.stderr.strip() or create.stdout.strip()}")
464
- start = self._runner.run(["docker", "start", self._container], timeout=60)
465
- if start.exit_code != 0:
463
+ res = self._runner.run(payload, timeout=60)
464
+ if res.exit_code != 0:
466
465
  raise SandboxError(
467
- f"AE-SBX-010: container start failed: "
468
- f"{start.stderr.strip() or start.stdout.strip()}")
466
+ f"AE-SBX-010: container run failed: "
467
+ f"{res.stderr.strip() or res.stdout.strip()}")
469
468
  self._started = True
470
469
 
471
470
  def provision(self, setup_cmd: str, stamp_key: str, stamp_dir: str,
@@ -513,10 +512,14 @@ class DockerBackend(Sandbox):
513
512
  return result
514
513
 
515
514
  def close(self) -> None:
516
- """Best-effort stop + rm; idempotent (BR-8.3)."""
515
+ """Best-effort stop + rm; idempotent (BR-8.3). With ``--rm`` the
516
+ daemon may already have auto-removed the container by the time close()
517
+ runs, so a nonzero stop/rm exit code ("no such container") is treated
518
+ as success, never raised (D2)."""
517
519
  if not self._started:
518
520
  return
519
521
  self._runner.run(["docker", "stop", "-t", "5", self._container], timeout=60)
522
+ # rm -f returns rc != 0 when --rm already removed it — ignore, never raise.
520
523
  self._runner.run(["docker", "rm", "-f", self._container], timeout=60)
521
524
  self._started = False
522
525
 
@@ -639,7 +642,7 @@ def clean_stamps(state_dir: Path) -> int:
639
642
 
640
643
 
641
644
  # ---------------------------------------------------------------------------
642
- # Eager startup (Q2) — probe → image → create/start → provision all setups
645
+ # Eager startup (Q2) — probe → image → run -d --rm → provision all setups
643
646
  # ---------------------------------------------------------------------------
644
647
 
645
648
  _LOCKFILE_NAMES = ("package-lock.json", "requirements.txt")