agentic-dev-loop 1.0.0a16__tar.gz → 1.0.0a27__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.
- {agentic_dev_loop-1.0.0a16/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a27}/PKG-INFO +194 -24
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/README.md +193 -23
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/pyproject.toml +1 -1
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/core/__init__.py +2 -2
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/core/config.py +694 -0
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/core/paths.py +69 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/actions.py +1 -1
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/agents.py +25 -5
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/dryrun.py +3 -1
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/engine/egress_policy.py +120 -0
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/engine/egress_proxy.py +264 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/graph.py +23 -9
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/hostboundary.py +1 -1
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/isolation.py +674 -22
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/engine/mounts.py +63 -0
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/engine/presets.py +50 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/proc.py +7 -35
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/registry.py +15 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/stages.py +160 -86
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/engine/step_workspace.py +176 -0
- agentic_dev_loop-1.0.0a27/src/agentic_dev_loop/engine/sync_back.py +275 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27/src/agentic_dev_loop.egg-info}/PKG-INFO +194 -24
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop.egg-info/SOURCES.txt +15 -0
- agentic_dev_loop-1.0.0a27/tests/test_028_declared_caches.py +259 -0
- agentic_dev_loop-1.0.0a27/tests/test_029_merge_on_conflict.py +97 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_actions.py +3 -4
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_agents.py +27 -1
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_config.py +3 -3
- agentic_dev_loop-1.0.0a27/tests/test_egress_proxy.py +205 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_graph.py +35 -26
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_hostboundary_integration.py +38 -17
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_isolation.py +879 -30
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_isolation_integration.py +99 -4
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_proc.py +7 -41
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_qa_prompt.py +8 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_registry.py +22 -1
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_repo_ascii.py +4 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_sdk_compat.py +18 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_secrets.py +4 -4
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_stages.py +133 -16
- agentic_dev_loop-1.0.0a27/tests/test_step_workspace.py +159 -0
- agentic_dev_loop-1.0.0a27/tests/test_sync_back.py +350 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_trigger_script.py +4 -21
- agentic_dev_loop-1.0.0a27/tests/test_unit5_config.py +173 -0
- agentic_dev_loop-1.0.0a27/tests/test_unit5_pbt.py +93 -0
- agentic_dev_loop-1.0.0a27/tests/test_unit5_pure.py +196 -0
- agentic_dev_loop-1.0.0a16/src/agentic_dev_loop/core/config.py +0 -329
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/LICENSE +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/setup.cfg +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/cli.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/llm.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/engine/secrets.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/viz/server.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_cli.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_deps.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_llm.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_publish.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_results.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_verdict.py +0 -0
- {agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/tests/test_viz.py +0 -0
{agentic_dev_loop-1.0.0a16/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a27}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-dev-loop
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0a27
|
|
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,31 @@ 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
|
|
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,
|
|
83
|
-
|
|
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.
|
|
86
|
+
|
|
87
|
+
## Supported platforms
|
|
88
|
+
|
|
89
|
+
The engine is plain Linux under the hood and runs wherever a compliant
|
|
90
|
+
Docker engine does:
|
|
91
|
+
|
|
92
|
+
| Platform | Support |
|
|
93
|
+
| --- | --- |
|
|
94
|
+
| **Linux** | Native. Docker Engine, OrbStack, Colima, or podman with docker compat. |
|
|
95
|
+
| **macOS** | Native. Docker Desktop, OrbStack, or Colima. |
|
|
96
|
+
| **Windows** | Via **WSL2** (Ubuntu distro). Enable Docker Desktop's WSL integration (or run Docker Engine inside the distro) and keep the repo on the WSL filesystem (`~/dev/...`), not `/mnt/c` - bind-mounting a 9p path into containers breaks permissions and is slow. |
|
|
97
|
+
|
|
98
|
+
Native Windows (outside WSL2) is not supported: every Windows-specific code
|
|
99
|
+
path and test was removed, and CI runs on Ubuntu only.
|
|
84
100
|
|
|
85
101
|
## Installation
|
|
86
102
|
|
|
@@ -89,20 +105,21 @@ LLM timeout (agent runs are long).
|
|
|
89
105
|
| Requirement | Detail |
|
|
90
106
|
| --- | --- |
|
|
91
107
|
| Python | ≥ 3.12 |
|
|
92
|
-
| Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows,
|
|
108
|
+
| Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
|
|
93
109
|
| 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
110
|
|
|
95
111
|
```bash
|
|
96
|
-
pip install "agentic-dev-loop==1.0.
|
|
112
|
+
pip install "agentic-dev-loop==1.0.0a17"
|
|
97
113
|
agentic-dev-loop --version
|
|
98
114
|
```
|
|
99
115
|
|
|
100
116
|
The canonical consumer install is **pipx**, which puts the engine's venv
|
|
101
|
-
*outside* the workspace - the sandbox
|
|
102
|
-
out-of-workspace install is unmounted and never
|
|
117
|
+
*outside* the workspace - the sandbox mounts only the step's declared `mounts:`
|
|
118
|
+
from a fresh export, so an out-of-workspace install is unmounted and never
|
|
119
|
+
agent-writable:
|
|
103
120
|
|
|
104
121
|
```bash
|
|
105
|
-
pipx install "agentic-dev-loop==1.0.
|
|
122
|
+
pipx install "agentic-dev-loop==1.0.0a17"
|
|
106
123
|
```
|
|
107
124
|
|
|
108
125
|
If you install with `uv` shortly after a release, pass `--refresh` - PyPI
|
|
@@ -111,7 +128,7 @@ reuses a pre-upload snapshot of the index for up to 10 minutes, reporting
|
|
|
111
128
|
"no version of agentic-dev-loop==X.Y.Z" for a version that is already live:
|
|
112
129
|
|
|
113
130
|
```bash
|
|
114
|
-
uv pip install --refresh "agentic-dev-loop==1.0.
|
|
131
|
+
uv pip install --refresh "agentic-dev-loop==1.0.0a17"
|
|
115
132
|
```
|
|
116
133
|
|
|
117
134
|
On the first run the engine pulls the **digest-pinned** dev image
|
|
@@ -211,13 +228,16 @@ steps:
|
|
|
211
228
|
| `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
229
|
| `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs verdict-parsed routing. The **last** `VERDICT:` line wins; stale artifacts are deleted at each attempt start. |
|
|
213
230
|
| `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`
|
|
231
|
+
| `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). |
|
|
232
|
+
| `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
233
|
| `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
234
|
| `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
235
|
| `state_dir` | Defaults to `~/.agentic-dev-loop/runs/<pipeline-hash>/` - artifacts and checkpoints live outside the sandbox mount. |
|
|
218
236
|
| `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
237
|
| `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
238
|
| `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. |
|
|
239
|
+
| `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). |
|
|
240
|
+
| `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
241
|
|
|
222
242
|
### Secrets (`secrets:`) - refs only, never values
|
|
223
243
|
|
|
@@ -231,7 +251,7 @@ secrets: # refs only - never values
|
|
|
231
251
|
key: KINDIE_CLIENT_SECRET # provider-specific locator
|
|
232
252
|
r2-keys:
|
|
233
253
|
from: env
|
|
234
|
-
key:
|
|
254
|
+
key: R2_KEYS
|
|
235
255
|
|
|
236
256
|
steps:
|
|
237
257
|
- name: qa
|
|
@@ -262,26 +282,176 @@ steps:
|
|
|
262
282
|
|
|
263
283
|
The sandbox is network-off by default. A step that legitimately needs egress -
|
|
264
284
|
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
|
|
285
|
+
fetch package metadata mid-conversation - declares it explicitly, and names
|
|
286
|
+
exactly what it may reach:
|
|
266
287
|
|
|
267
288
|
```yaml
|
|
268
289
|
steps:
|
|
269
290
|
- name: fetch-docs
|
|
270
291
|
command: curl -fsSL https://example.com/spec.json -o spec.json
|
|
271
|
-
|
|
292
|
+
mounts: [{ path: ".", mode: rw }] # the cwd + a place to write spec.json
|
|
293
|
+
network: true # egress for this step only
|
|
294
|
+
egress: { targets: [example.com] } # ...and only these hosts
|
|
295
|
+
|
|
296
|
+
- name: restore
|
|
297
|
+
command: .venv-sandbox/bin/pip install -e ".[dev]"
|
|
298
|
+
mounts: [{ path: ".", mode: rw }]
|
|
299
|
+
network: true
|
|
300
|
+
egress: { presets: [package-registries] } # pypi/npm/NuGet/cargo + VCS
|
|
272
301
|
```
|
|
273
302
|
|
|
274
303
|
- **Default off.** Every step runs with `--network none` unless it declares
|
|
275
|
-
`network: true`.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
304
|
+
`network: true`.
|
|
305
|
+
- **A networked step that writes output must also declare a writable mount.**
|
|
306
|
+
`mounts:` is strict-by-default: with no `mounts:` a step mounts nothing, so
|
|
307
|
+
a command like `curl ... -o spec.json` has no writable cwd to write into.
|
|
308
|
+
Declare `mounts: [{ path: ".", mode: rw }]` (or a narrower rw path) for any
|
|
309
|
+
networked step that needs to produce files.
|
|
310
|
+
- **`network: true` is now scoped by the `egress:` allowlist.** A networked
|
|
311
|
+
step's traffic is forced through an engine-run per-policy egress proxy
|
|
312
|
+
(injected via `HTTP_PROXY`/`HTTPS_PROXY` and reached through the
|
|
313
|
+
`host.docker.internal` host-gateway); a request to an undeclared host is
|
|
314
|
+
denied and logged as an enforcement event. The engine never adds an
|
|
315
|
+
undeclared target. `egress: { class: browser }` is the explicit
|
|
316
|
+
broad-but-logged class (every connection logged, nothing filtered); command
|
|
317
|
+
and agent steps default to a strict allowlist.
|
|
279
318
|
- **Command, agent, and sandbox-action steps** may declare it. The host-side
|
|
280
319
|
built-in `commit` action never takes it (refused once the registry is known).
|
|
281
|
-
- **Author opt-in is the control.** A networked step
|
|
282
|
-
|
|
283
|
-
for stages that need it (QA, integration). The
|
|
284
|
-
in [docs/setup.md](docs/setup.md).
|
|
320
|
+
- **Author opt-in is the control.** A networked step grants its agent egress
|
|
321
|
+
to exactly the declared hosts for that stage; the default stays network-off
|
|
322
|
+
everywhere. Reserve it for stages that need it (QA, integration). The
|
|
323
|
+
residual risks are documented in [docs/setup.md](docs/setup.md).
|
|
324
|
+
|
|
325
|
+
### Sync-back conflicts (host edits during a run)
|
|
326
|
+
|
|
327
|
+
Only declared-`rw` paths sync back to the real repo after a step. If the host
|
|
328
|
+
also edits a declared-`rw` path while the step is running, sync-back prints
|
|
329
|
+
`[isolation] sync-back conflict: ...` and raises `AE-SBX-016`; the host's
|
|
330
|
+
version wins, the baseline is re-snapshotted, and the next step (even
|
|
331
|
+
`echo hello`) runs clean instead of re-raising forever. For append-style logs
|
|
332
|
+
edited by both sides (an audit log, say), declare them in the top-level
|
|
333
|
+
`merge_on_conflict:` list: those paths are union-merged - the host keeps its
|
|
334
|
+
lines, the sandbox's new lines are appended in order, both sides converge, and
|
|
335
|
+
the run continues with `[isolation] merged concurrent edits: ...` on the
|
|
336
|
+
console.
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
## Authoring a loop for your own repo
|
|
340
|
+
|
|
341
|
+
A consumer repo needs two things written by hand - a pipeline file and the
|
|
342
|
+
agent files it names - both committed before the first run (the host-boundary
|
|
343
|
+
check refuses dirty tooling, and the `commit` step never commits it). Two
|
|
344
|
+
reference layouts ship today: this repo dogfoods the engine on itself
|
|
345
|
+
(python + pytest, no frontend, CLI-only qa), and the consumer repo carries
|
|
346
|
+
the wider shape (.NET + npm frontend + a networked browser-probing qa step).
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
<repo>/agentic-dev-loop/pipeline.yaml
|
|
350
|
+
<repo>/agentic-dev-loop/sdk_agents/dev.agent.md
|
|
351
|
+
<repo>/agentic-dev-loop/sdk_agents/review.agent.md
|
|
352
|
+
<repo>/agentic-dev-loop/sdk_agents/qa.agent.md
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### The agent files
|
|
356
|
+
|
|
357
|
+
Each `sdk_agents/<name>.agent.md` is a markdown prompt plus YAML frontmatter:
|
|
358
|
+
`name` matches a step's `agent:` value; `tools` lists the tool names the step
|
|
359
|
+
may use (`terminal` always; a repo can add more via extensions, e.g.
|
|
360
|
+
`figma`).
|
|
361
|
+
|
|
362
|
+
```markdown
|
|
363
|
+
---
|
|
364
|
+
name: review # matches a step's `agent:` value
|
|
365
|
+
tools: [terminal]
|
|
366
|
+
---
|
|
367
|
+
# Review Agent
|
|
368
|
+
## WORKFLOW # numbered steps
|
|
369
|
+
## RULES # absolute never/always rules
|
|
370
|
+
## SANDBOX NOTES # the sandbox truth for THIS step
|
|
371
|
+
## OUTPUT # the verdict contract
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Three roles, three contracts:
|
|
375
|
+
|
|
376
|
+
| Role | Verdict contract | Typical sandbox |
|
|
377
|
+
| --- | --- | --- |
|
|
378
|
+
| dev | none - the final response is captured as the `produces:` summary | rw mount, offline |
|
|
379
|
+
| review | `VERDICT: APPROVED` / `VERDICT: NEEDS CHANGES` on the first content line | ro mount, offline |
|
|
380
|
+
| 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) |
|
|
381
|
+
|
|
382
|
+
The conventions that make a good agent file - each one below exists because
|
|
383
|
+
an agent burned budget learning it mid-run:
|
|
384
|
+
|
|
385
|
+
- **State the sandbox truth in the prompt.** Every agent file carries a
|
|
386
|
+
`SANDBOX NOTES` section naming exactly what its step allows: mounts ro or
|
|
387
|
+
rw, network on or off, and which gates have already run. Never make the
|
|
388
|
+
agent discover it.
|
|
389
|
+
- **Gates are the source of truth - cite the record, never re-run.** A
|
|
390
|
+
command the test gate, the editor-tests gate, or the dev summary reports
|
|
391
|
+
as run is coverage evidence, cited as `covered by <test>` or the gate
|
|
392
|
+
record. Review and qa never re-run builds or suites; qa maps every
|
|
393
|
+
acceptance criterion to existing coverage and probes only the remainder.
|
|
394
|
+
- **dev enumerates its impossible set and stops at it.** The dev prompt
|
|
395
|
+
says: stop the moment your next step needs a command this sandbox cannot
|
|
396
|
+
run, and lists the set (package-manager fetches after a new pin, npm or
|
|
397
|
+
vitest offline, playwright downloads, database-category tests), one
|
|
398
|
+
attempt per command. That keeps the productive edit/build/test loop and
|
|
399
|
+
kills doomed retries. The set is toolchain-specific - write yours; this
|
|
400
|
+
repo's is a single bullet (no package managers), the consumer repo's is
|
|
401
|
+
four.
|
|
402
|
+
- **Failure output returns as *Previous attempt feedback*.** Steps route
|
|
403
|
+
`on_fail: { goto: dev, retry: true }`, so gate failure output lands in
|
|
404
|
+
the dev agent's next context; the dev prompt makes every listed issue a
|
|
405
|
+
required fix.
|
|
406
|
+
- **No git mutations, ever.** The `commit` step is the single commit point.
|
|
407
|
+
- **`max_iterations` per role.** qa is bounded probing (60 in both
|
|
408
|
+
reference repos); dev is open-ended construction and stays uncapped
|
|
409
|
+
(the engine default).
|
|
410
|
+
|
|
411
|
+
### The pipeline file
|
|
412
|
+
|
|
413
|
+
The full field reference is above; this is the stage lineup and what to
|
|
414
|
+
adapt per repo.
|
|
415
|
+
|
|
416
|
+
| Stage | Kind | Typical shape | Why |
|
|
417
|
+
| --- | --- | --- | --- |
|
|
418
|
+
| dev | agent | rw, offline | does the work; edits are untracked |
|
|
419
|
+
| restore | command | network + `presets: [package-registries]` | fetches what dev pinned (`deps_policy: open`) or the locked install; also the audit escalation point |
|
|
420
|
+
| test | command | offline is fine (warm cache) | exit code is the verdict; the source of truth |
|
|
421
|
+
| 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 |
|
|
422
|
+
| conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
|
|
423
|
+
| review | agent | ro, offline | static conformance verdict |
|
|
424
|
+
| qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
|
|
425
|
+
| commit | action | host-side | the single commit point |
|
|
426
|
+
|
|
427
|
+
Adaptation checklist for a new repo:
|
|
428
|
+
|
|
429
|
+
1. `llm:` - model, `base_url`, `api_key_env`.
|
|
430
|
+
2. `isolation:` - toolchain image override (the consumer repo pins a
|
|
431
|
+
toolchain image by digest) and `seccomp_profile: bundled:chromium-userns`
|
|
432
|
+
only if a stage runs Chromium.
|
|
433
|
+
3. `caches:` - toolchain cache dirs as rw bind-mounts shared across runs
|
|
434
|
+
(`.nuget-packages`, `qa/node_modules`, `qa/.browsers`,
|
|
435
|
+
`frontend-editor/node_modules`); caches are never synced back.
|
|
436
|
+
4. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
|
|
437
|
+
5. `secrets:` - refs only, resolved from host env; each step opts in to the
|
|
438
|
+
refs it needs.
|
|
439
|
+
6. `egress:` - `presets: [package-registries]` for fetch steps;
|
|
440
|
+
`class: browser` (broad-but-logged) for browser-tier qa only.
|
|
441
|
+
7. `deps_policy:` and `lockfiles:` - see
|
|
442
|
+
[Dependency policy](#dependency-policy-locked-by-default).
|
|
443
|
+
8. The dev prompt's impossible set - write it for your toolchain.
|
|
444
|
+
9. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
|
|
445
|
+
docs: shared context for every agent step.
|
|
446
|
+
|
|
447
|
+
Validate before the first run - dry-run loads and prints the resolved plan
|
|
448
|
+
with no LLM, no Docker, no state writes:
|
|
449
|
+
|
|
450
|
+
```bash
|
|
451
|
+
agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Then commit the tooling and run for real.
|
|
285
455
|
|
|
286
456
|
## Dependency policy (locked by default)
|
|
287
457
|
|
|
@@ -407,7 +577,7 @@ the loop is building.
|
|
|
407
577
|
```bash
|
|
408
578
|
git clone git@github.com:lukedawilson/agentic-dev-loop.git
|
|
409
579
|
cd agentic-dev-loop
|
|
410
|
-
pipx install "agentic-dev-loop==1.0.
|
|
580
|
+
pipx install "agentic-dev-loop==1.0.0a17" # engine venv OUTSIDE the workspace (self-check)
|
|
411
581
|
export OPENAI_API_KEY=... # set your LLM key in the host env (no .env in the repo)
|
|
412
582
|
python -m pytest tests/ -q # fast suite (in-repo .venv is for tests only)
|
|
413
583
|
python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
|
|
@@ -423,7 +593,7 @@ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run # run the shipped pi
|
|
|
423
593
|
- The shipped pipeline (`agentic-dev-loop/pipeline.yaml`) **dogfoods the tool on
|
|
424
594
|
its own repo** - dev → test → review → qa → commit, with the engine's own
|
|
425
595
|
three agent definitions in `sdk_agents/`.
|
|
426
|
-
- CI: `ci.yml` (unit + integration suite, ubuntu
|
|
596
|
+
- CI: `ci.yml` (unit + integration suite, ubuntu),
|
|
427
597
|
`publish-image.yml` (multi-arch dev image, amd64 + arm64), `release.yml`
|
|
428
598
|
(PyPI publishing).
|
|
429
599
|
|
|
@@ -30,15 +30,31 @@ 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
|
|
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,
|
|
41
|
-
|
|
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.
|
|
44
|
+
|
|
45
|
+
## Supported platforms
|
|
46
|
+
|
|
47
|
+
The engine is plain Linux under the hood and runs wherever a compliant
|
|
48
|
+
Docker engine does:
|
|
49
|
+
|
|
50
|
+
| Platform | Support |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| **Linux** | Native. Docker Engine, OrbStack, Colima, or podman with docker compat. |
|
|
53
|
+
| **macOS** | Native. Docker Desktop, OrbStack, or Colima. |
|
|
54
|
+
| **Windows** | Via **WSL2** (Ubuntu distro). Enable Docker Desktop's WSL integration (or run Docker Engine inside the distro) and keep the repo on the WSL filesystem (`~/dev/...`), not `/mnt/c` - bind-mounting a 9p path into containers breaks permissions and is slow. |
|
|
55
|
+
|
|
56
|
+
Native Windows (outside WSL2) is not supported: every Windows-specific code
|
|
57
|
+
path and test was removed, and CI runs on Ubuntu only.
|
|
42
58
|
|
|
43
59
|
## Installation
|
|
44
60
|
|
|
@@ -47,20 +63,21 @@ LLM timeout (agent runs are long).
|
|
|
47
63
|
| Requirement | Detail |
|
|
48
64
|
| --- | --- |
|
|
49
65
|
| 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,
|
|
66
|
+
| Docker engine | Engine API ≥ 1.43 (Engine 24). Any compliant engine works - Docker Desktop, OrbStack, Colima, Rancher Desktop, podman docker-compat. On Windows, run under WSL2: the repo must live on the WSL filesystem, not /mnt/c. |
|
|
51
67
|
| 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
68
|
|
|
53
69
|
```bash
|
|
54
|
-
pip install "agentic-dev-loop==1.0.
|
|
70
|
+
pip install "agentic-dev-loop==1.0.0a17"
|
|
55
71
|
agentic-dev-loop --version
|
|
56
72
|
```
|
|
57
73
|
|
|
58
74
|
The canonical consumer install is **pipx**, which puts the engine's venv
|
|
59
|
-
*outside* the workspace - the sandbox
|
|
60
|
-
out-of-workspace install is unmounted and never
|
|
75
|
+
*outside* the workspace - the sandbox mounts only the step's declared `mounts:`
|
|
76
|
+
from a fresh export, so an out-of-workspace install is unmounted and never
|
|
77
|
+
agent-writable:
|
|
61
78
|
|
|
62
79
|
```bash
|
|
63
|
-
pipx install "agentic-dev-loop==1.0.
|
|
80
|
+
pipx install "agentic-dev-loop==1.0.0a17"
|
|
64
81
|
```
|
|
65
82
|
|
|
66
83
|
If you install with `uv` shortly after a release, pass `--refresh` - PyPI
|
|
@@ -69,7 +86,7 @@ reuses a pre-upload snapshot of the index for up to 10 minutes, reporting
|
|
|
69
86
|
"no version of agentic-dev-loop==X.Y.Z" for a version that is already live:
|
|
70
87
|
|
|
71
88
|
```bash
|
|
72
|
-
uv pip install --refresh "agentic-dev-loop==1.0.
|
|
89
|
+
uv pip install --refresh "agentic-dev-loop==1.0.0a17"
|
|
73
90
|
```
|
|
74
91
|
|
|
75
92
|
On the first run the engine pulls the **digest-pinned** dev image
|
|
@@ -169,13 +186,16 @@ steps:
|
|
|
169
186
|
| `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
187
|
| `produces:` / `artifact:` + `verdicts:` | Hygiene-tracked outputs vs verdict-parsed routing. The **last** `VERDICT:` line wins; stale artifacts are deleted at each attempt start. |
|
|
171
188
|
| `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`
|
|
189
|
+
| `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). |
|
|
190
|
+
| `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
191
|
| `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
192
|
| `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
193
|
| `state_dir` | Defaults to `~/.agentic-dev-loop/runs/<pipeline-hash>/` - artifacts and checkpoints live outside the sandbox mount. |
|
|
176
194
|
| `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
195
|
| `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
196
|
| `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. |
|
|
197
|
+
| `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). |
|
|
198
|
+
| `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
199
|
|
|
180
200
|
### Secrets (`secrets:`) - refs only, never values
|
|
181
201
|
|
|
@@ -189,7 +209,7 @@ secrets: # refs only - never values
|
|
|
189
209
|
key: KINDIE_CLIENT_SECRET # provider-specific locator
|
|
190
210
|
r2-keys:
|
|
191
211
|
from: env
|
|
192
|
-
key:
|
|
212
|
+
key: R2_KEYS
|
|
193
213
|
|
|
194
214
|
steps:
|
|
195
215
|
- name: qa
|
|
@@ -220,26 +240,176 @@ steps:
|
|
|
220
240
|
|
|
221
241
|
The sandbox is network-off by default. A step that legitimately needs egress -
|
|
222
242
|
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
|
|
243
|
+
fetch package metadata mid-conversation - declares it explicitly, and names
|
|
244
|
+
exactly what it may reach:
|
|
224
245
|
|
|
225
246
|
```yaml
|
|
226
247
|
steps:
|
|
227
248
|
- name: fetch-docs
|
|
228
249
|
command: curl -fsSL https://example.com/spec.json -o spec.json
|
|
229
|
-
|
|
250
|
+
mounts: [{ path: ".", mode: rw }] # the cwd + a place to write spec.json
|
|
251
|
+
network: true # egress for this step only
|
|
252
|
+
egress: { targets: [example.com] } # ...and only these hosts
|
|
253
|
+
|
|
254
|
+
- name: restore
|
|
255
|
+
command: .venv-sandbox/bin/pip install -e ".[dev]"
|
|
256
|
+
mounts: [{ path: ".", mode: rw }]
|
|
257
|
+
network: true
|
|
258
|
+
egress: { presets: [package-registries] } # pypi/npm/NuGet/cargo + VCS
|
|
230
259
|
```
|
|
231
260
|
|
|
232
261
|
- **Default off.** Every step runs with `--network none` unless it declares
|
|
233
|
-
`network: true`.
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
262
|
+
`network: true`.
|
|
263
|
+
- **A networked step that writes output must also declare a writable mount.**
|
|
264
|
+
`mounts:` is strict-by-default: with no `mounts:` a step mounts nothing, so
|
|
265
|
+
a command like `curl ... -o spec.json` has no writable cwd to write into.
|
|
266
|
+
Declare `mounts: [{ path: ".", mode: rw }]` (or a narrower rw path) for any
|
|
267
|
+
networked step that needs to produce files.
|
|
268
|
+
- **`network: true` is now scoped by the `egress:` allowlist.** A networked
|
|
269
|
+
step's traffic is forced through an engine-run per-policy egress proxy
|
|
270
|
+
(injected via `HTTP_PROXY`/`HTTPS_PROXY` and reached through the
|
|
271
|
+
`host.docker.internal` host-gateway); a request to an undeclared host is
|
|
272
|
+
denied and logged as an enforcement event. The engine never adds an
|
|
273
|
+
undeclared target. `egress: { class: browser }` is the explicit
|
|
274
|
+
broad-but-logged class (every connection logged, nothing filtered); command
|
|
275
|
+
and agent steps default to a strict allowlist.
|
|
237
276
|
- **Command, agent, and sandbox-action steps** may declare it. The host-side
|
|
238
277
|
built-in `commit` action never takes it (refused once the registry is known).
|
|
239
|
-
- **Author opt-in is the control.** A networked step
|
|
240
|
-
|
|
241
|
-
for stages that need it (QA, integration). The
|
|
242
|
-
in [docs/setup.md](docs/setup.md).
|
|
278
|
+
- **Author opt-in is the control.** A networked step grants its agent egress
|
|
279
|
+
to exactly the declared hosts for that stage; the default stays network-off
|
|
280
|
+
everywhere. Reserve it for stages that need it (QA, integration). The
|
|
281
|
+
residual risks are documented in [docs/setup.md](docs/setup.md).
|
|
282
|
+
|
|
283
|
+
### Sync-back conflicts (host edits during a run)
|
|
284
|
+
|
|
285
|
+
Only declared-`rw` paths sync back to the real repo after a step. If the host
|
|
286
|
+
also edits a declared-`rw` path while the step is running, sync-back prints
|
|
287
|
+
`[isolation] sync-back conflict: ...` and raises `AE-SBX-016`; the host's
|
|
288
|
+
version wins, the baseline is re-snapshotted, and the next step (even
|
|
289
|
+
`echo hello`) runs clean instead of re-raising forever. For append-style logs
|
|
290
|
+
edited by both sides (an audit log, say), declare them in the top-level
|
|
291
|
+
`merge_on_conflict:` list: those paths are union-merged - the host keeps its
|
|
292
|
+
lines, the sandbox's new lines are appended in order, both sides converge, and
|
|
293
|
+
the run continues with `[isolation] merged concurrent edits: ...` on the
|
|
294
|
+
console.
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
## Authoring a loop for your own repo
|
|
298
|
+
|
|
299
|
+
A consumer repo needs two things written by hand - a pipeline file and the
|
|
300
|
+
agent files it names - both committed before the first run (the host-boundary
|
|
301
|
+
check refuses dirty tooling, and the `commit` step never commits it). Two
|
|
302
|
+
reference layouts ship today: this repo dogfoods the engine on itself
|
|
303
|
+
(python + pytest, no frontend, CLI-only qa), and the consumer repo carries
|
|
304
|
+
the wider shape (.NET + npm frontend + a networked browser-probing qa step).
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
<repo>/agentic-dev-loop/pipeline.yaml
|
|
308
|
+
<repo>/agentic-dev-loop/sdk_agents/dev.agent.md
|
|
309
|
+
<repo>/agentic-dev-loop/sdk_agents/review.agent.md
|
|
310
|
+
<repo>/agentic-dev-loop/sdk_agents/qa.agent.md
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### The agent files
|
|
314
|
+
|
|
315
|
+
Each `sdk_agents/<name>.agent.md` is a markdown prompt plus YAML frontmatter:
|
|
316
|
+
`name` matches a step's `agent:` value; `tools` lists the tool names the step
|
|
317
|
+
may use (`terminal` always; a repo can add more via extensions, e.g.
|
|
318
|
+
`figma`).
|
|
319
|
+
|
|
320
|
+
```markdown
|
|
321
|
+
---
|
|
322
|
+
name: review # matches a step's `agent:` value
|
|
323
|
+
tools: [terminal]
|
|
324
|
+
---
|
|
325
|
+
# Review Agent
|
|
326
|
+
## WORKFLOW # numbered steps
|
|
327
|
+
## RULES # absolute never/always rules
|
|
328
|
+
## SANDBOX NOTES # the sandbox truth for THIS step
|
|
329
|
+
## OUTPUT # the verdict contract
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Three roles, three contracts:
|
|
333
|
+
|
|
334
|
+
| Role | Verdict contract | Typical sandbox |
|
|
335
|
+
| --- | --- | --- |
|
|
336
|
+
| dev | none - the final response is captured as the `produces:` summary | rw mount, offline |
|
|
337
|
+
| review | `VERDICT: APPROVED` / `VERDICT: NEEDS CHANGES` on the first content line | ro mount, offline |
|
|
338
|
+
| 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) |
|
|
339
|
+
|
|
340
|
+
The conventions that make a good agent file - each one below exists because
|
|
341
|
+
an agent burned budget learning it mid-run:
|
|
342
|
+
|
|
343
|
+
- **State the sandbox truth in the prompt.** Every agent file carries a
|
|
344
|
+
`SANDBOX NOTES` section naming exactly what its step allows: mounts ro or
|
|
345
|
+
rw, network on or off, and which gates have already run. Never make the
|
|
346
|
+
agent discover it.
|
|
347
|
+
- **Gates are the source of truth - cite the record, never re-run.** A
|
|
348
|
+
command the test gate, the editor-tests gate, or the dev summary reports
|
|
349
|
+
as run is coverage evidence, cited as `covered by <test>` or the gate
|
|
350
|
+
record. Review and qa never re-run builds or suites; qa maps every
|
|
351
|
+
acceptance criterion to existing coverage and probes only the remainder.
|
|
352
|
+
- **dev enumerates its impossible set and stops at it.** The dev prompt
|
|
353
|
+
says: stop the moment your next step needs a command this sandbox cannot
|
|
354
|
+
run, and lists the set (package-manager fetches after a new pin, npm or
|
|
355
|
+
vitest offline, playwright downloads, database-category tests), one
|
|
356
|
+
attempt per command. That keeps the productive edit/build/test loop and
|
|
357
|
+
kills doomed retries. The set is toolchain-specific - write yours; this
|
|
358
|
+
repo's is a single bullet (no package managers), the consumer repo's is
|
|
359
|
+
four.
|
|
360
|
+
- **Failure output returns as *Previous attempt feedback*.** Steps route
|
|
361
|
+
`on_fail: { goto: dev, retry: true }`, so gate failure output lands in
|
|
362
|
+
the dev agent's next context; the dev prompt makes every listed issue a
|
|
363
|
+
required fix.
|
|
364
|
+
- **No git mutations, ever.** The `commit` step is the single commit point.
|
|
365
|
+
- **`max_iterations` per role.** qa is bounded probing (60 in both
|
|
366
|
+
reference repos); dev is open-ended construction and stays uncapped
|
|
367
|
+
(the engine default).
|
|
368
|
+
|
|
369
|
+
### The pipeline file
|
|
370
|
+
|
|
371
|
+
The full field reference is above; this is the stage lineup and what to
|
|
372
|
+
adapt per repo.
|
|
373
|
+
|
|
374
|
+
| Stage | Kind | Typical shape | Why |
|
|
375
|
+
| --- | --- | --- | --- |
|
|
376
|
+
| dev | agent | rw, offline | does the work; edits are untracked |
|
|
377
|
+
| restore | command | network + `presets: [package-registries]` | fetches what dev pinned (`deps_policy: open`) or the locked install; also the audit escalation point |
|
|
378
|
+
| test | command | offline is fine (warm cache) | exit code is the verdict; the source of truth |
|
|
379
|
+
| 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 |
|
|
380
|
+
| conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
|
|
381
|
+
| review | agent | ro, offline | static conformance verdict |
|
|
382
|
+
| qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
|
|
383
|
+
| commit | action | host-side | the single commit point |
|
|
384
|
+
|
|
385
|
+
Adaptation checklist for a new repo:
|
|
386
|
+
|
|
387
|
+
1. `llm:` - model, `base_url`, `api_key_env`.
|
|
388
|
+
2. `isolation:` - toolchain image override (the consumer repo pins a
|
|
389
|
+
toolchain image by digest) and `seccomp_profile: bundled:chromium-userns`
|
|
390
|
+
only if a stage runs Chromium.
|
|
391
|
+
3. `caches:` - toolchain cache dirs as rw bind-mounts shared across runs
|
|
392
|
+
(`.nuget-packages`, `qa/node_modules`, `qa/.browsers`,
|
|
393
|
+
`frontend-editor/node_modules`); caches are never synced back.
|
|
394
|
+
4. `sync_excludes:` - build output trees (`**/bin/**`, `**/obj/**`).
|
|
395
|
+
5. `secrets:` - refs only, resolved from host env; each step opts in to the
|
|
396
|
+
refs it needs.
|
|
397
|
+
6. `egress:` - `presets: [package-registries]` for fetch steps;
|
|
398
|
+
`class: browser` (broad-but-logged) for browser-tier qa only.
|
|
399
|
+
7. `deps_policy:` and `lockfiles:` - see
|
|
400
|
+
[Dependency policy](#dependency-policy-locked-by-default).
|
|
401
|
+
8. The dev prompt's impossible set - write it for your toolchain.
|
|
402
|
+
9. `additional_files:` - the repo's `AGENTS.md`, coding standards, design
|
|
403
|
+
docs: shared context for every agent step.
|
|
404
|
+
|
|
405
|
+
Validate before the first run - dry-run loads and prints the resolved plan
|
|
406
|
+
with no LLM, no Docker, no state writes:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Then commit the tooling and run for real.
|
|
243
413
|
|
|
244
414
|
## Dependency policy (locked by default)
|
|
245
415
|
|
|
@@ -365,7 +535,7 @@ the loop is building.
|
|
|
365
535
|
```bash
|
|
366
536
|
git clone git@github.com:lukedawilson/agentic-dev-loop.git
|
|
367
537
|
cd agentic-dev-loop
|
|
368
|
-
pipx install "agentic-dev-loop==1.0.
|
|
538
|
+
pipx install "agentic-dev-loop==1.0.0a17" # engine venv OUTSIDE the workspace (self-check)
|
|
369
539
|
export OPENAI_API_KEY=... # set your LLM key in the host env (no .env in the repo)
|
|
370
540
|
python -m pytest tests/ -q # fast suite (in-repo .venv is for tests only)
|
|
371
541
|
python -m pytest tests/ -q -m "not docker" # skip engine-dependent tests
|
|
@@ -381,7 +551,7 @@ agentic-dev-loop agentic-dev-loop/pipeline.yaml --dry-run # run the shipped pi
|
|
|
381
551
|
- The shipped pipeline (`agentic-dev-loop/pipeline.yaml`) **dogfoods the tool on
|
|
382
552
|
its own repo** - dev → test → review → qa → commit, with the engine's own
|
|
383
553
|
three agent definitions in `sdk_agents/`.
|
|
384
|
-
- CI: `ci.yml` (unit + integration suite, ubuntu
|
|
554
|
+
- CI: `ci.yml` (unit + integration suite, ubuntu),
|
|
385
555
|
`publish-image.yml` (multi-arch dev image, amd64 + arm64), `release.yml`
|
|
386
556
|
(PyPI publishing).
|
|
387
557
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentic-dev-loop"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.0a27"
|
|
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"
|
{agentic_dev_loop-1.0.0a16 → agentic_dev_loop-1.0.0a27}/src/agentic_dev_loop/core/__init__.py
RENAMED
|
@@ -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
|
]
|