agentic-dev-loop 1.0.0a16__tar.gz → 1.0.0a26__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.

Potentially problematic release.


This version of agentic-dev-loop might be problematic. Click here for more details.

Files changed (90) hide show
  1. {agentic_dev_loop-1.0.0a16/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a26}/PKG-INFO +178 -22
  2. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/README.md +177 -21
  3. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/pyproject.toml +1 -1
  4. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/core/__init__.py +2 -2
  5. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/core/config.py +694 -0
  6. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/core/paths.py +69 -0
  7. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/actions.py +1 -1
  8. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/agents.py +25 -5
  9. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/dryrun.py +3 -1
  10. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/engine/egress_policy.py +120 -0
  11. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/engine/egress_proxy.py +264 -0
  12. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/graph.py +23 -9
  13. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/hostboundary.py +1 -1
  14. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/isolation.py +674 -22
  15. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/engine/mounts.py +63 -0
  16. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/engine/presets.py +50 -0
  17. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/registry.py +15 -0
  18. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/stages.py +160 -86
  19. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/engine/step_workspace.py +176 -0
  20. agentic_dev_loop-1.0.0a26/src/agentic_dev_loop/engine/sync_back.py +275 -0
  21. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26/src/agentic_dev_loop.egg-info}/PKG-INFO +178 -22
  22. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop.egg-info/SOURCES.txt +15 -0
  23. agentic_dev_loop-1.0.0a26/tests/test_028_declared_caches.py +259 -0
  24. agentic_dev_loop-1.0.0a26/tests/test_029_merge_on_conflict.py +97 -0
  25. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_actions.py +1 -1
  26. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_agents.py +27 -1
  27. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_config.py +3 -3
  28. agentic_dev_loop-1.0.0a26/tests/test_egress_proxy.py +205 -0
  29. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_graph.py +35 -26
  30. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_hostboundary_integration.py +38 -17
  31. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_isolation.py +879 -30
  32. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_isolation_integration.py +99 -4
  33. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_qa_prompt.py +8 -0
  34. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_registry.py +22 -1
  35. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_repo_ascii.py +4 -0
  36. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_sdk_compat.py +18 -0
  37. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_secrets.py +4 -4
  38. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_stages.py +131 -14
  39. agentic_dev_loop-1.0.0a26/tests/test_step_workspace.py +159 -0
  40. agentic_dev_loop-1.0.0a26/tests/test_sync_back.py +350 -0
  41. agentic_dev_loop-1.0.0a26/tests/test_unit5_config.py +173 -0
  42. agentic_dev_loop-1.0.0a26/tests/test_unit5_pbt.py +93 -0
  43. agentic_dev_loop-1.0.0a26/tests/test_unit5_pure.py +196 -0
  44. agentic_dev_loop-1.0.0a16/src/agentic_dev_loop/core/config.py +0 -329
  45. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/LICENSE +0 -0
  46. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/setup.cfg +0 -0
  47. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/__init__.py +0 -0
  48. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/core/results.py +0 -0
  49. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/core/verdict.py +0 -0
  50. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/__init__.py +0 -0
  51. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/cli.py +0 -0
  52. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/deps.py +0 -0
  53. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  54. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  55. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/llm.py +0 -0
  56. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/loaders.py +0 -0
  57. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/outputs.py +0 -0
  58. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/proc.py +0 -0
  59. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  60. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  61. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/engine/secrets.py +0 -0
  62. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/py.typed +0 -0
  63. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/viz/__init__.py +0 -0
  64. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/viz/__main__.py +0 -0
  65. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop/viz/server.py +0 -0
  66. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  67. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  68. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
  69. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  70. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_aidlc.py +0 -0
  71. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_cli.py +0 -0
  72. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_deps.py +0 -0
  73. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_dry_run.py +0 -0
  74. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_e2e_fixtures.py +0 -0
  75. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_hostboundary.py +0 -0
  76. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_isolation_pbt.py +0 -0
  77. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_llm.py +0 -0
  78. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_loaders.py +0 -0
  79. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_module_contract.py +0 -0
  80. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_outputs.py +0 -0
  81. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_outputs_pbt.py +0 -0
  82. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_proc.py +0 -0
  83. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_publish.py +0 -0
  84. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_results.py +0 -0
  85. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_secrets_integration.py +0 -0
  86. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_secrets_pipeline.py +0 -0
  87. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_source_resolution.py +0 -0
  88. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_trigger_script.py +0 -0
  89. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_verdict.py +0 -0
  90. {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a26}/tests/test_viz.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 1.0.0a16
3
+ Version: 1.0.0a26
4
4
  Summary: Turn an LLM into a gated, sandboxed, resumable construction loop over your codebase.
5
5
  Author: Luke Wilson
6
6
  License-Expression: Apache-2.0
@@ -72,15 +72,17 @@ flowchart LR
72
72
  | Feature | What it gives you |
73
73
  | --- | --- |
74
74
  | **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. |
75
- | **Kernel-level sandboxing** | Agent work and command stages run isolated from your machine: [no network access **by default** - authors opt specific stages in](#per-stage-networking), no writes outside your workspace. No sandbox available → the run stops instead of degrading. |
75
+ | **Kernel-level sandboxing** | Agent work and command stages run isolated from your machine: [no network access **by default** - authors opt specific stages in](#per-stage-networking), writes only to declared `mounts:`, and no access to `.git` (steps run against a fresh export). No sandbox available → the run stops instead of degrading. |
76
76
  | **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. |
77
77
  | **Live graph visualisation** | Follow every run in the browser: stage status, pass/fail verdicts, retry cycles, and live console output as it happens. |
78
78
  | **Extensible** | Plug in your own actions, tools, and document loaders to adapt the loop to your projects. |
79
79
 
80
80
  The fixed policies *are* the product: fail fast on missing config/secrets, no
81
81
  silent fallbacks, verdicts only from artifacts, bounded retries, per-attempt
82
- artifact hygiene, a single clean-worktree-gated commit point, and no default
83
- LLM timeout (agent runs are long).
82
+ artifact hygiene, a single clean-worktree-gated commit point, no default LLM
83
+ timeout (agent runs are long), and fail fast on a broken sandbox - daemon-side
84
+ exec failures are classified and stop the run without an agent retry loop,
85
+ while a startup reaper reclaims containers leaked by a crashed run.
84
86
 
85
87
  ## Installation
86
88
 
@@ -93,16 +95,17 @@ LLM timeout (agent runs are long).
93
95
  | 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. |
94
96
 
95
97
  ```bash
96
- pip install "agentic-dev-loop==1.0.0a9"
98
+ pip install "agentic-dev-loop==1.0.0a17"
97
99
  agentic-dev-loop --version
98
100
  ```
99
101
 
100
102
  The canonical consumer install is **pipx**, which puts the engine's venv
101
- *outside* the workspace - the sandbox mount set is exactly `{workspace}`, so an
102
- out-of-workspace install is unmounted and never agent-writable:
103
+ *outside* the workspace - the sandbox mounts only the step's declared `mounts:`
104
+ from a fresh export, so an out-of-workspace install is unmounted and never
105
+ agent-writable:
103
106
 
104
107
  ```bash
105
- pipx install "agentic-dev-loop==1.0.0a9"
108
+ pipx install "agentic-dev-loop==1.0.0a17"
106
109
  ```
107
110
 
108
111
  If you install with `uv` shortly after a release, pass `--refresh` - PyPI
@@ -111,7 +114,7 @@ reuses a pre-upload snapshot of the index for up to 10 minutes, reporting
111
114
  "no version of agentic-dev-loop==X.Y.Z" for a version that is already live:
112
115
 
113
116
  ```bash
114
- uv pip install --refresh "agentic-dev-loop==1.0.0a9"
117
+ uv pip install --refresh "agentic-dev-loop==1.0.0a17"
115
118
  ```
116
119
 
117
120
  On the first run the engine pulls the **digest-pinned** dev image
@@ -211,13 +214,16 @@ steps:
211
214
  | `agent:` / `command:` / `action:` | Exactly one per step. Agents live in `agents_dir` as `<name>.agent.md`; commands run sandboxed (mechanical, never an LLM call). `commit` is the sole host-side built-in action; consumer actions are sandbox-executed, command-shaped scripts (see [Host boundary](#host-boundary-security-model)). |
212
215
  | `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs verdict-parsed routing. The **last** `VERDICT:` line wins; stale artifacts are deleted at each attempt start. |
213
216
  | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
214
- | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox (command steps, network ON, stamp-cached); `network: true` grants egress to a command or agent step (default off) - see [Per-stage networking](#per-stage-networking). |
217
+ | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox (command steps, network ON, stamp-cached); `network: true` lets a command/agent step reach the hosts its `egress:` allowlist names (default off) - see [Per-stage networking](#per-stage-networking). |
218
+ | `mounts:` / `egress:` | Per-step workspace isolation. `mounts:` lists `{path, mode}` entries (`rw`/`ro`, default `ro`) and is **strict-by-default** - empty/missing mounts nothing; only declared-rw paths sync back to the repo. `egress:` is `{class, presets, targets}` (`class: browser` = broad-but-logged; otherwise a strict allowlist). See [Per-stage networking](#per-stage-networking) and [docs/setup.md](docs/setup.md). |
215
219
  | `secrets:` (top-level) / step `secrets:` | Declares secret **refs only - never values**; each step opts in to the refs it needs. Resolved host-side and injected into that step's exec env only. See [Secrets](#secrets--refs-only-never-values). |
216
220
  | `isolation:` (optional) | Configures the sandbox - image override, resource caps, `allow_git_mutations`, `seccomp_profile`. It can never disable it. See [browser sandbox](docs/browser-sandbox.md). |
217
221
  | `state_dir` | Defaults to `~/.agentic-dev-loop/runs/<pipeline-hash>/` - artifacts and checkpoints live outside the sandbox mount. |
218
222
  | `extensions_dir` | Where consumer `extensions:` modules resolve - defaults to `~/.agentic-dev-loop/extensions`, **outside** the workspace. Workspace-resident extension/tool modules are refused at load. |
219
223
  | `deps_policy` | `locked` (default) or `open`. `locked` = the human-owned lockfile is the source of truth and the agent may not add dependencies; `open` = the bootstrap protocol (the agent may add exact-pinned manifest entries). See [Dependency policy](#dependency-policy-locked-by-default). |
220
224
  | `lockfiles` | The pipeline's declared lockfile names (workspace-relative), injected into agent context and named by the missing-dependency fail-fast. Required (non-empty) when `deps_policy: locked` - a `locked` pipeline with no lockfiles fails fast at load. |
225
+ | `caches:` / `sync_excludes:` | Top-level run-wide lists. `caches:` names repo-relative toolchain cache dirs bind-mounted rw from the real workspace (auto-created if missing); `sync_excludes:` names scratch paths skipped from both the export copy and sync-back (wildcard `*` spans separators, so `**/bin/**` matches a `/bin/` segment at any depth). Both default to `[]`. See [docs/setup.md](docs/setup.md). |
226
+ | `merge_on_conflict:` | Top-level run-wide list of append-style logs (same path vocabulary as `sync_excludes:`, default `[]`). A concurrent host edit to a listed path is union-merged instead of raising: the host file wins and the sandbox's export-only lines are appended in order; both sides converge and the run continues. See [docs/setup.md](docs/setup.md). |
221
227
 
222
228
  ### Secrets (`secrets:`) - refs only, never values
223
229
 
@@ -231,7 +237,7 @@ secrets: # refs only - never values
231
237
  key: KINDIE_CLIENT_SECRET # provider-specific locator
232
238
  r2-keys:
233
239
  from: env
234
- key: YOSK_R2_KEYS
240
+ key: R2_KEYS
235
241
 
236
242
  steps:
237
243
  - name: qa
@@ -262,26 +268,176 @@ steps:
262
268
 
263
269
  The sandbox is network-off by default. A step that legitimately needs egress -
264
270
  a QA step that curls a service it booted, an agent that must hit an API or
265
- fetch package metadata mid-conversation - declares it explicitly:
271
+ fetch package metadata mid-conversation - declares it explicitly, and names
272
+ exactly what it may reach:
266
273
 
267
274
  ```yaml
268
275
  steps:
269
276
  - name: fetch-docs
270
277
  command: curl -fsSL https://example.com/spec.json -o spec.json
271
- network: true # egress for this step only
278
+ mounts: [{ path: ".", mode: rw }] # the cwd + a place to write spec.json
279
+ network: true # egress for this step only
280
+ egress: { targets: [example.com] } # ...and only these hosts
281
+
282
+ - name: restore
283
+ command: .venv-sandbox/bin/pip install -e ".[dev]"
284
+ mounts: [{ path: ".", mode: rw }]
285
+ network: true
286
+ egress: { presets: [package-registries] } # pypi/npm/NuGet/cargo + VCS
272
287
  ```
273
288
 
274
289
  - **Default off.** Every step runs with `--network none` unless it declares
275
- `network: true`. A networked step runs in a second, otherwise-identical
276
- sandbox container (`--read-only`, `--cap-drop ALL`, single workspace mount)
277
- on the Docker bridge network - the default network-none container is always
278
- created too, so no infrastructure work happens mid-run.
290
+ `network: true`.
291
+ - **A networked step that writes output must also declare a writable mount.**
292
+ `mounts:` is strict-by-default: with no `mounts:` a step mounts nothing, so
293
+ a command like `curl ... -o spec.json` has no writable cwd to write into.
294
+ Declare `mounts: [{ path: ".", mode: rw }]` (or a narrower rw path) for any
295
+ networked step that needs to produce files.
296
+ - **`network: true` is now scoped by the `egress:` allowlist.** A networked
297
+ step's traffic is forced through an engine-run per-policy egress proxy
298
+ (injected via `HTTP_PROXY`/`HTTPS_PROXY` and reached through the
299
+ `host.docker.internal` host-gateway); a request to an undeclared host is
300
+ denied and logged as an enforcement event. The engine never adds an
301
+ undeclared target. `egress: { class: browser }` is the explicit
302
+ broad-but-logged class (every connection logged, nothing filtered); command
303
+ and agent steps default to a strict allowlist.
279
304
  - **Command, agent, and sandbox-action steps** may declare it. The host-side
280
305
  built-in `commit` action never takes it (refused once the registry is known).
281
- - **Author opt-in is the control.** A networked step gives its agent raw
282
- egress for that stage; the default stays network-off everywhere. Reserve it
283
- for stages that need it (QA, integration). The residual risk is documented
284
- in [docs/setup.md](docs/setup.md).
306
+ - **Author opt-in is the control.** A networked step grants its agent egress
307
+ to exactly the declared hosts for that stage; the default stays network-off
308
+ everywhere. Reserve it for stages that need it (QA, integration). The
309
+ residual risks are documented in [docs/setup.md](docs/setup.md).
310
+
311
+ ### Sync-back conflicts (host edits during a run)
312
+
313
+ Only declared-`rw` paths sync back to the real repo after a step. If the host
314
+ also edits a declared-`rw` path while the step is running, sync-back prints
315
+ `[isolation] sync-back conflict: ...` and raises `AE-SBX-016`; the host's
316
+ version wins, the baseline is re-snapshotted, and the next step (even
317
+ `echo hello`) runs clean instead of re-raising forever. For append-style logs
318
+ edited by both sides (an audit log, say), declare them in the top-level
319
+ `merge_on_conflict:` list: those paths are union-merged - the host keeps its
320
+ lines, the sandbox's new lines are appended in order, both sides converge, and
321
+ the run continues with `[isolation] merged concurrent edits: ...` on the
322
+ console.
323
+
324
+
325
+ ## Authoring a loop for your own repo
326
+
327
+ A consumer repo needs two things written by hand - a pipeline file and the
328
+ agent files it names - both committed before the first run (the host-boundary
329
+ check refuses dirty tooling, and the `commit` step never commits it). Two
330
+ reference layouts ship today: this repo dogfoods the engine on itself
331
+ (python + pytest, no frontend, CLI-only qa), and the consumer repo carries
332
+ the wider shape (.NET + npm frontend + a networked browser-probing qa step).
333
+
334
+ ```
335
+ <repo>/agentic-dev-loop/pipeline.yaml
336
+ <repo>/agentic-dev-loop/sdk_agents/dev.agent.md
337
+ <repo>/agentic-dev-loop/sdk_agents/review.agent.md
338
+ <repo>/agentic-dev-loop/sdk_agents/qa.agent.md
339
+ ```
340
+
341
+ ### The agent files
342
+
343
+ Each `sdk_agents/<name>.agent.md` is a markdown prompt plus YAML frontmatter:
344
+ `name` matches a step's `agent:` value; `tools` lists the tool names the step
345
+ may use (`terminal` always; a repo can add more via extensions, e.g.
346
+ `figma`).
347
+
348
+ ```markdown
349
+ ---
350
+ name: review # matches a step's `agent:` value
351
+ tools: [terminal]
352
+ ---
353
+ # Review Agent
354
+ ## WORKFLOW # numbered steps
355
+ ## RULES # absolute never/always rules
356
+ ## SANDBOX NOTES # the sandbox truth for THIS step
357
+ ## OUTPUT # the verdict contract
358
+ ```
359
+
360
+ Three roles, three contracts:
361
+
362
+ | Role | Verdict contract | Typical sandbox |
363
+ | --- | --- | --- |
364
+ | dev | none - the final response is captured as the `produces:` summary | rw mount, offline |
365
+ | review | `VERDICT: APPROVED` / `VERDICT: NEEDS CHANGES` on the first content line | ro mount, offline |
366
+ | qa | `VERDICT: PASS` / `VERDICT: FAIL`; map `unverifiable: UNVERIFIABLE` if you want a third way | shaped by the repo: rw + network + `class: browser` when it boots servers and drives a browser (the consumer repo); ro + offline when it probes the CLI only (this repo) |
367
+
368
+ The conventions that make a good agent file - each one below exists because
369
+ an agent burned budget learning it mid-run:
370
+
371
+ - **State the sandbox truth in the prompt.** Every agent file carries a
372
+ `SANDBOX NOTES` section naming exactly what its step allows: mounts ro or
373
+ rw, network on or off, and which gates have already run. Never make the
374
+ agent discover it.
375
+ - **Gates are the source of truth - cite the record, never re-run.** A
376
+ command the test gate, the editor-tests gate, or the dev summary reports
377
+ as run is coverage evidence, cited as `covered by <test>` or the gate
378
+ record. Review and qa never re-run builds or suites; qa maps every
379
+ acceptance criterion to existing coverage and probes only the remainder.
380
+ - **dev enumerates its impossible set and stops at it.** The dev prompt
381
+ says: stop the moment your next step needs a command this sandbox cannot
382
+ run, and lists the set (package-manager fetches after a new pin, npm or
383
+ vitest offline, playwright downloads, database-category tests), one
384
+ attempt per command. That keeps the productive edit/build/test loop and
385
+ kills doomed retries. The set is toolchain-specific - write yours; this
386
+ repo's is a single bullet (no package managers), the consumer repo's is
387
+ four.
388
+ - **Failure output returns as *Previous attempt feedback*.** Steps route
389
+ `on_fail: { goto: dev, retry: true }`, so gate failure output lands in
390
+ the dev agent's next context; the dev prompt makes every listed issue a
391
+ required fix.
392
+ - **No git mutations, ever.** The `commit` step is the single commit point.
393
+ - **`max_iterations` per role.** qa is bounded probing (60 in both
394
+ reference repos); dev is open-ended construction and stays uncapped
395
+ (the engine default).
396
+
397
+ ### The pipeline file
398
+
399
+ The full field reference is above; this is the stage lineup and what to
400
+ adapt per repo.
401
+
402
+ | Stage | Kind | Typical shape | Why |
403
+ | --- | --- | --- | --- |
404
+ | dev | agent | rw, offline | does the work; edits are untracked |
405
+ | restore | command | network + `presets: [package-registries]` | fetches what dev pinned (`deps_policy: open`) or the locked install; also the audit escalation point |
406
+ | test | command | offline is fine (warm cache) | exit code is the verdict; the source of truth |
407
+ | editor-tests | command | network + `presets: [package-registries]` | frontend suites that cannot run offline (`npm ci && npm test`). A separate step, never merged into `test`: the step name is the failure diagnosis, its timeout does not contend the backend suite's, and a retry re-runs only its own chain |
408
+ | conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
409
+ | review | agent | ro, offline | static conformance verdict |
410
+ | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
411
+ | commit | action | host-side | the single commit point |
412
+
413
+ Adaptation checklist for a new repo:
414
+
415
+ 1. `llm:` - model, `base_url`, `api_key_env`.
416
+ 2. `isolation:` - toolchain image override (the consumer repo pins a
417
+ toolchain image by digest) and `seccomp_profile: bundled:chromium-userns`
418
+ only if a stage runs Chromium.
419
+ 3. `caches:` - toolchain cache dirs as rw bind-mounts shared across runs
420
+ (`.nuget-packages`, `qa/node_modules`, `qa/.browsers`,
421
+ `frontend-editor/node_modules`); caches are never synced back.
422
+ 4. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
423
+ 5. `secrets:` - refs only, resolved from host env; each step opts in to the
424
+ refs it needs.
425
+ 6. `egress:` - `presets: [package-registries]` for fetch steps;
426
+ `class: browser` (broad-but-logged) for browser-tier qa only.
427
+ 7. `deps_policy:` and `lockfiles:` - see
428
+ [Dependency policy](#dependency-policy-locked-by-default).
429
+ 8. The dev prompt's impossible set - write it for your toolchain.
430
+ 9. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
431
+ docs: shared context for every agent step.
432
+
433
+ Validate before the first run - dry-run loads and prints the resolved plan
434
+ with no LLM, no Docker, no state writes:
435
+
436
+ ```bash
437
+ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run
438
+ ```
439
+
440
+ Then commit the tooling and run for real.
285
441
 
286
442
  ## Dependency policy (locked by default)
287
443
 
@@ -407,7 +563,7 @@ the loop is building.
407
563
  ```bash
408
564
  git clone git@github.com:lukedawilson/agentic-dev-loop.git
409
565
  cd agentic-dev-loop
410
- pipx install "agentic-dev-loop==1.0.0a9" # engine venv OUTSIDE the workspace (self-check)
566
+ pipx install "agentic-dev-loop==1.0.0a17" # engine venv OUTSIDE the workspace (self-check)
411
567
  export OPENAI_API_KEY=... # set your LLM key in the host env (no .env in the repo)
412
568
  python -m pytest tests/ -q # fast suite (in-repo .venv is for tests only)
413
569
  python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
@@ -30,15 +30,17 @@ flowchart LR
30
30
  | Feature | What it gives you |
31
31
  | --- | --- |
32
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 **by default** - authors opt specific stages in](#per-stage-networking), no writes outside your workspace. No sandbox available → the run stops instead of degrading. |
33
+ | **Kernel-level sandboxing** | Agent work and command stages run isolated from your machine: [no network access **by default** - authors opt specific stages in](#per-stage-networking), writes only to declared `mounts:`, and no access to `.git` (steps run against a fresh export). No sandbox available → the run stops instead of degrading. |
34
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
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
36
  | **Extensible** | Plug in your own actions, tools, and document loaders to adapt the loop to your projects. |
37
37
 
38
38
  The fixed policies *are* the product: fail fast on missing config/secrets, no
39
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).
40
+ artifact hygiene, a single clean-worktree-gated commit point, no default LLM
41
+ timeout (agent runs are long), and fail fast on a broken sandbox - daemon-side
42
+ exec failures are classified and stop the run without an agent retry loop,
43
+ while a startup reaper reclaims containers leaked by a crashed run.
42
44
 
43
45
  ## Installation
44
46
 
@@ -51,16 +53,17 @@ LLM timeout (agent runs are long).
51
53
  | 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
54
 
53
55
  ```bash
54
- pip install "agentic-dev-loop==1.0.0a9"
56
+ pip install "agentic-dev-loop==1.0.0a17"
55
57
  agentic-dev-loop --version
56
58
  ```
57
59
 
58
60
  The canonical consumer install is **pipx**, which puts the engine's venv
59
- *outside* the workspace - the sandbox mount set is exactly `{workspace}`, so an
60
- out-of-workspace install is unmounted and never agent-writable:
61
+ *outside* the workspace - the sandbox mounts only the step's declared `mounts:`
62
+ from a fresh export, so an out-of-workspace install is unmounted and never
63
+ agent-writable:
61
64
 
62
65
  ```bash
63
- pipx install "agentic-dev-loop==1.0.0a9"
66
+ pipx install "agentic-dev-loop==1.0.0a17"
64
67
  ```
65
68
 
66
69
  If you install with `uv` shortly after a release, pass `--refresh` - PyPI
@@ -69,7 +72,7 @@ reuses a pre-upload snapshot of the index for up to 10 minutes, reporting
69
72
  "no version of agentic-dev-loop==X.Y.Z" for a version that is already live:
70
73
 
71
74
  ```bash
72
- uv pip install --refresh "agentic-dev-loop==1.0.0a9"
75
+ uv pip install --refresh "agentic-dev-loop==1.0.0a17"
73
76
  ```
74
77
 
75
78
  On the first run the engine pulls the **digest-pinned** dev image
@@ -169,13 +172,16 @@ steps:
169
172
  | `agent:` / `command:` / `action:` | Exactly one per step. Agents live in `agents_dir` as `<name>.agent.md`; commands run sandboxed (mechanical, never an LLM call). `commit` is the sole host-side built-in action; consumer actions are sandbox-executed, command-shaped scripts (see [Host boundary](#host-boundary-security-model)). |
170
173
  | `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs verdict-parsed routing. The **last** `VERDICT:` line wins; stale artifacts are deleted at each attempt start. |
171
174
  | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
172
- | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox (command steps, network ON, stamp-cached); `network: true` grants egress to a command or agent step (default off) - see [Per-stage networking](#per-stage-networking). |
175
+ | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox (command steps, network ON, stamp-cached); `network: true` lets a command/agent step reach the hosts its `egress:` allowlist names (default off) - see [Per-stage networking](#per-stage-networking). |
176
+ | `mounts:` / `egress:` | Per-step workspace isolation. `mounts:` lists `{path, mode}` entries (`rw`/`ro`, default `ro`) and is **strict-by-default** - empty/missing mounts nothing; only declared-rw paths sync back to the repo. `egress:` is `{class, presets, targets}` (`class: browser` = broad-but-logged; otherwise a strict allowlist). See [Per-stage networking](#per-stage-networking) and [docs/setup.md](docs/setup.md). |
173
177
  | `secrets:` (top-level) / step `secrets:` | Declares secret **refs only - never values**; each step opts in to the refs it needs. Resolved host-side and injected into that step's exec env only. See [Secrets](#secrets--refs-only-never-values). |
174
178
  | `isolation:` (optional) | Configures the sandbox - image override, resource caps, `allow_git_mutations`, `seccomp_profile`. It can never disable it. See [browser sandbox](docs/browser-sandbox.md). |
175
179
  | `state_dir` | Defaults to `~/.agentic-dev-loop/runs/<pipeline-hash>/` - artifacts and checkpoints live outside the sandbox mount. |
176
180
  | `extensions_dir` | Where consumer `extensions:` modules resolve - defaults to `~/.agentic-dev-loop/extensions`, **outside** the workspace. Workspace-resident extension/tool modules are refused at load. |
177
181
  | `deps_policy` | `locked` (default) or `open`. `locked` = the human-owned lockfile is the source of truth and the agent may not add dependencies; `open` = the bootstrap protocol (the agent may add exact-pinned manifest entries). See [Dependency policy](#dependency-policy-locked-by-default). |
178
182
  | `lockfiles` | The pipeline's declared lockfile names (workspace-relative), injected into agent context and named by the missing-dependency fail-fast. Required (non-empty) when `deps_policy: locked` - a `locked` pipeline with no lockfiles fails fast at load. |
183
+ | `caches:` / `sync_excludes:` | Top-level run-wide lists. `caches:` names repo-relative toolchain cache dirs bind-mounted rw from the real workspace (auto-created if missing); `sync_excludes:` names scratch paths skipped from both the export copy and sync-back (wildcard `*` spans separators, so `**/bin/**` matches a `/bin/` segment at any depth). Both default to `[]`. See [docs/setup.md](docs/setup.md). |
184
+ | `merge_on_conflict:` | Top-level run-wide list of append-style logs (same path vocabulary as `sync_excludes:`, default `[]`). A concurrent host edit to a listed path is union-merged instead of raising: the host file wins and the sandbox's export-only lines are appended in order; both sides converge and the run continues. See [docs/setup.md](docs/setup.md). |
179
185
 
180
186
  ### Secrets (`secrets:`) - refs only, never values
181
187
 
@@ -189,7 +195,7 @@ secrets: # refs only - never values
189
195
  key: KINDIE_CLIENT_SECRET # provider-specific locator
190
196
  r2-keys:
191
197
  from: env
192
- key: YOSK_R2_KEYS
198
+ key: R2_KEYS
193
199
 
194
200
  steps:
195
201
  - name: qa
@@ -220,26 +226,176 @@ steps:
220
226
 
221
227
  The sandbox is network-off by default. A step that legitimately needs egress -
222
228
  a QA step that curls a service it booted, an agent that must hit an API or
223
- fetch package metadata mid-conversation - declares it explicitly:
229
+ fetch package metadata mid-conversation - declares it explicitly, and names
230
+ exactly what it may reach:
224
231
 
225
232
  ```yaml
226
233
  steps:
227
234
  - name: fetch-docs
228
235
  command: curl -fsSL https://example.com/spec.json -o spec.json
229
- network: true # egress for this step only
236
+ mounts: [{ path: ".", mode: rw }] # the cwd + a place to write spec.json
237
+ network: true # egress for this step only
238
+ egress: { targets: [example.com] } # ...and only these hosts
239
+
240
+ - name: restore
241
+ command: .venv-sandbox/bin/pip install -e ".[dev]"
242
+ mounts: [{ path: ".", mode: rw }]
243
+ network: true
244
+ egress: { presets: [package-registries] } # pypi/npm/NuGet/cargo + VCS
230
245
  ```
231
246
 
232
247
  - **Default off.** Every step runs with `--network none` unless it declares
233
- `network: true`. A networked step runs in a second, otherwise-identical
234
- sandbox container (`--read-only`, `--cap-drop ALL`, single workspace mount)
235
- on the Docker bridge network - the default network-none container is always
236
- created too, so no infrastructure work happens mid-run.
248
+ `network: true`.
249
+ - **A networked step that writes output must also declare a writable mount.**
250
+ `mounts:` is strict-by-default: with no `mounts:` a step mounts nothing, so
251
+ a command like `curl ... -o spec.json` has no writable cwd to write into.
252
+ Declare `mounts: [{ path: ".", mode: rw }]` (or a narrower rw path) for any
253
+ networked step that needs to produce files.
254
+ - **`network: true` is now scoped by the `egress:` allowlist.** A networked
255
+ step's traffic is forced through an engine-run per-policy egress proxy
256
+ (injected via `HTTP_PROXY`/`HTTPS_PROXY` and reached through the
257
+ `host.docker.internal` host-gateway); a request to an undeclared host is
258
+ denied and logged as an enforcement event. The engine never adds an
259
+ undeclared target. `egress: { class: browser }` is the explicit
260
+ broad-but-logged class (every connection logged, nothing filtered); command
261
+ and agent steps default to a strict allowlist.
237
262
  - **Command, agent, and sandbox-action steps** may declare it. The host-side
238
263
  built-in `commit` action never takes it (refused once the registry is known).
239
- - **Author opt-in is the control.** A networked step gives its agent raw
240
- egress for that stage; the default stays network-off everywhere. Reserve it
241
- for stages that need it (QA, integration). The residual risk is documented
242
- in [docs/setup.md](docs/setup.md).
264
+ - **Author opt-in is the control.** A networked step grants its agent egress
265
+ to exactly the declared hosts for that stage; the default stays network-off
266
+ everywhere. Reserve it for stages that need it (QA, integration). The
267
+ residual risks are documented in [docs/setup.md](docs/setup.md).
268
+
269
+ ### Sync-back conflicts (host edits during a run)
270
+
271
+ Only declared-`rw` paths sync back to the real repo after a step. If the host
272
+ also edits a declared-`rw` path while the step is running, sync-back prints
273
+ `[isolation] sync-back conflict: ...` and raises `AE-SBX-016`; the host's
274
+ version wins, the baseline is re-snapshotted, and the next step (even
275
+ `echo hello`) runs clean instead of re-raising forever. For append-style logs
276
+ edited by both sides (an audit log, say), declare them in the top-level
277
+ `merge_on_conflict:` list: those paths are union-merged - the host keeps its
278
+ lines, the sandbox's new lines are appended in order, both sides converge, and
279
+ the run continues with `[isolation] merged concurrent edits: ...` on the
280
+ console.
281
+
282
+
283
+ ## Authoring a loop for your own repo
284
+
285
+ A consumer repo needs two things written by hand - a pipeline file and the
286
+ agent files it names - both committed before the first run (the host-boundary
287
+ check refuses dirty tooling, and the `commit` step never commits it). Two
288
+ reference layouts ship today: this repo dogfoods the engine on itself
289
+ (python + pytest, no frontend, CLI-only qa), and the consumer repo carries
290
+ the wider shape (.NET + npm frontend + a networked browser-probing qa step).
291
+
292
+ ```
293
+ <repo>/agentic-dev-loop/pipeline.yaml
294
+ <repo>/agentic-dev-loop/sdk_agents/dev.agent.md
295
+ <repo>/agentic-dev-loop/sdk_agents/review.agent.md
296
+ <repo>/agentic-dev-loop/sdk_agents/qa.agent.md
297
+ ```
298
+
299
+ ### The agent files
300
+
301
+ Each `sdk_agents/<name>.agent.md` is a markdown prompt plus YAML frontmatter:
302
+ `name` matches a step's `agent:` value; `tools` lists the tool names the step
303
+ may use (`terminal` always; a repo can add more via extensions, e.g.
304
+ `figma`).
305
+
306
+ ```markdown
307
+ ---
308
+ name: review # matches a step's `agent:` value
309
+ tools: [terminal]
310
+ ---
311
+ # Review Agent
312
+ ## WORKFLOW # numbered steps
313
+ ## RULES # absolute never/always rules
314
+ ## SANDBOX NOTES # the sandbox truth for THIS step
315
+ ## OUTPUT # the verdict contract
316
+ ```
317
+
318
+ Three roles, three contracts:
319
+
320
+ | Role | Verdict contract | Typical sandbox |
321
+ | --- | --- | --- |
322
+ | dev | none - the final response is captured as the `produces:` summary | rw mount, offline |
323
+ | review | `VERDICT: APPROVED` / `VERDICT: NEEDS CHANGES` on the first content line | ro mount, offline |
324
+ | qa | `VERDICT: PASS` / `VERDICT: FAIL`; map `unverifiable: UNVERIFIABLE` if you want a third way | shaped by the repo: rw + network + `class: browser` when it boots servers and drives a browser (the consumer repo); ro + offline when it probes the CLI only (this repo) |
325
+
326
+ The conventions that make a good agent file - each one below exists because
327
+ an agent burned budget learning it mid-run:
328
+
329
+ - **State the sandbox truth in the prompt.** Every agent file carries a
330
+ `SANDBOX NOTES` section naming exactly what its step allows: mounts ro or
331
+ rw, network on or off, and which gates have already run. Never make the
332
+ agent discover it.
333
+ - **Gates are the source of truth - cite the record, never re-run.** A
334
+ command the test gate, the editor-tests gate, or the dev summary reports
335
+ as run is coverage evidence, cited as `covered by <test>` or the gate
336
+ record. Review and qa never re-run builds or suites; qa maps every
337
+ acceptance criterion to existing coverage and probes only the remainder.
338
+ - **dev enumerates its impossible set and stops at it.** The dev prompt
339
+ says: stop the moment your next step needs a command this sandbox cannot
340
+ run, and lists the set (package-manager fetches after a new pin, npm or
341
+ vitest offline, playwright downloads, database-category tests), one
342
+ attempt per command. That keeps the productive edit/build/test loop and
343
+ kills doomed retries. The set is toolchain-specific - write yours; this
344
+ repo's is a single bullet (no package managers), the consumer repo's is
345
+ four.
346
+ - **Failure output returns as *Previous attempt feedback*.** Steps route
347
+ `on_fail: { goto: dev, retry: true }`, so gate failure output lands in
348
+ the dev agent's next context; the dev prompt makes every listed issue a
349
+ required fix.
350
+ - **No git mutations, ever.** The `commit` step is the single commit point.
351
+ - **`max_iterations` per role.** qa is bounded probing (60 in both
352
+ reference repos); dev is open-ended construction and stays uncapped
353
+ (the engine default).
354
+
355
+ ### The pipeline file
356
+
357
+ The full field reference is above; this is the stage lineup and what to
358
+ adapt per repo.
359
+
360
+ | Stage | Kind | Typical shape | Why |
361
+ | --- | --- | --- | --- |
362
+ | dev | agent | rw, offline | does the work; edits are untracked |
363
+ | restore | command | network + `presets: [package-registries]` | fetches what dev pinned (`deps_policy: open`) or the locked install; also the audit escalation point |
364
+ | test | command | offline is fine (warm cache) | exit code is the verdict; the source of truth |
365
+ | editor-tests | command | network + `presets: [package-registries]` | frontend suites that cannot run offline (`npm ci && npm test`). A separate step, never merged into `test`: the step name is the failure diagnosis, its timeout does not contend the backend suite's, and a retry re-runs only its own chain |
366
+ | conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
367
+ | review | agent | ro, offline | static conformance verdict |
368
+ | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
369
+ | commit | action | host-side | the single commit point |
370
+
371
+ Adaptation checklist for a new repo:
372
+
373
+ 1. `llm:` - model, `base_url`, `api_key_env`.
374
+ 2. `isolation:` - toolchain image override (the consumer repo pins a
375
+ toolchain image by digest) and `seccomp_profile: bundled:chromium-userns`
376
+ only if a stage runs Chromium.
377
+ 3. `caches:` - toolchain cache dirs as rw bind-mounts shared across runs
378
+ (`.nuget-packages`, `qa/node_modules`, `qa/.browsers`,
379
+ `frontend-editor/node_modules`); caches are never synced back.
380
+ 4. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
381
+ 5. `secrets:` - refs only, resolved from host env; each step opts in to the
382
+ refs it needs.
383
+ 6. `egress:` - `presets: [package-registries]` for fetch steps;
384
+ `class: browser` (broad-but-logged) for browser-tier qa only.
385
+ 7. `deps_policy:` and `lockfiles:` - see
386
+ [Dependency policy](#dependency-policy-locked-by-default).
387
+ 8. The dev prompt's impossible set - write it for your toolchain.
388
+ 9. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
389
+ docs: shared context for every agent step.
390
+
391
+ Validate before the first run - dry-run loads and prints the resolved plan
392
+ with no LLM, no Docker, no state writes:
393
+
394
+ ```bash
395
+ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run
396
+ ```
397
+
398
+ Then commit the tooling and run for real.
243
399
 
244
400
  ## Dependency policy (locked by default)
245
401
 
@@ -365,7 +521,7 @@ the loop is building.
365
521
  ```bash
366
522
  git clone git@github.com:lukedawilson/agentic-dev-loop.git
367
523
  cd agentic-dev-loop
368
- pipx install "agentic-dev-loop==1.0.0a9" # engine venv OUTSIDE the workspace (self-check)
524
+ pipx install "agentic-dev-loop==1.0.0a17" # engine venv OUTSIDE the workspace (self-check)
369
525
  export OPENAI_API_KEY=... # set your LLM key in the host env (no .env in the repo)
370
526
  python -m pytest tests/ -q # fast suite (in-repo .venv is for tests only)
371
527
  python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "1.0.0a16"
7
+ version = "1.0.0a26"
8
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"
@@ -6,13 +6,13 @@ test fails if this package imports engine/viz or heavy deps.
6
6
 
7
7
  from .config import (PipelineConfig, StepConfig, LlmConfig, IsolationConfig,
8
8
  VerdictSpec, Route, route_target, load_config,
9
- default_state_dir, TERMINALS)
9
+ default_state_dir, TERMINALS, MountSpec, EgressSpec)
10
10
  from .results import Result
11
11
  from .verdict import Verdict, VerdictError, parse_verdict, RETRY
12
12
 
13
13
  __all__ = [
14
14
  "PipelineConfig", "StepConfig", "LlmConfig", "IsolationConfig",
15
15
  "VerdictSpec", "Route", "route_target", "load_config",
16
- "default_state_dir", "TERMINALS",
16
+ "default_state_dir", "TERMINALS", "MountSpec", "EgressSpec",
17
17
  "Result", "Verdict", "VerdictError", "parse_verdict", "RETRY",
18
18
  ]