agentic-dev-loop 1.0.0a30__tar.gz → 1.0.0a32__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 (95) hide show
  1. {agentic_dev_loop-1.0.0a30/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a32}/PKG-INFO +31 -5
  2. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/README.md +30 -4
  3. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/pyproject.toml +2 -1
  4. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/core/config.py +16 -0
  5. agentic_dev_loop-1.0.0a32/src/agentic_dev_loop/engine/agent_error_guide.md +6 -0
  6. agentic_dev_loop-1.0.0a32/src/agentic_dev_loop/engine/agent_error_guide.py +26 -0
  7. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/stages.py +99 -45
  8. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32/src/agentic_dev_loop.egg-info}/PKG-INFO +31 -5
  9. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop.egg-info/SOURCES.txt +4 -0
  10. agentic_dev_loop-1.0.0a32/tests/test_035_finalize_nudge.py +211 -0
  11. agentic_dev_loop-1.0.0a32/tests/test_agent_error_guide.py +184 -0
  12. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/LICENSE +0 -0
  13. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/setup.cfg +0 -0
  14. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/__init__.py +0 -0
  15. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/core/__init__.py +0 -0
  16. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/core/paths.py +0 -0
  17. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/core/results.py +0 -0
  18. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/core/verdict.py +0 -0
  19. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/__init__.py +0 -0
  20. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/actions.py +0 -0
  21. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/agents.py +0 -0
  22. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/cli.py +0 -0
  23. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/deps.py +0 -0
  24. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/dryrun.py +0 -0
  25. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
  26. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
  27. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
  28. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
  29. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/graph.py +0 -0
  30. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
  31. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/isolation.py +0 -0
  32. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/llm.py +0 -0
  33. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/loaders.py +0 -0
  34. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/mounts.py +0 -0
  35. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/outputs.py +0 -0
  36. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/presets.py +0 -0
  37. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/proc.py +0 -0
  38. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/registry.py +0 -0
  39. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
  40. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
  41. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/secrets.py +0 -0
  42. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
  43. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/engine/sync_back.py +0 -0
  44. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/py.typed +0 -0
  45. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/viz/__init__.py +0 -0
  46. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/viz/__main__.py +0 -0
  47. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop/viz/server.py +0 -0
  48. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
  49. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
  50. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
  51. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
  52. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_028_declared_caches.py +0 -0
  53. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_029_merge_on_conflict.py +0 -0
  54. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_032_stop_when_green.py +0 -0
  55. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_033_brief_step.py +0 -0
  56. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_actions.py +0 -0
  57. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_agents.py +0 -0
  58. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_aidlc.py +0 -0
  59. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_cli.py +0 -0
  60. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_config.py +0 -0
  61. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_deps.py +0 -0
  62. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_dry_run.py +0 -0
  63. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_e2e_fixtures.py +0 -0
  64. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_egress_proxy.py +0 -0
  65. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_graph.py +0 -0
  66. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_hostboundary.py +0 -0
  67. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_hostboundary_integration.py +0 -0
  68. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_isolation.py +0 -0
  69. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_isolation_integration.py +0 -0
  70. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_isolation_pbt.py +0 -0
  71. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_llm.py +0 -0
  72. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_loaders.py +0 -0
  73. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_module_contract.py +0 -0
  74. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_outputs.py +0 -0
  75. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_outputs_pbt.py +0 -0
  76. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_proc.py +0 -0
  77. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_publish.py +0 -0
  78. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_qa_prompt.py +0 -0
  79. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_registry.py +0 -0
  80. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_repo_ascii.py +0 -0
  81. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_results.py +0 -0
  82. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_sdk_compat.py +0 -0
  83. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_secrets.py +0 -0
  84. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_secrets_integration.py +0 -0
  85. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_secrets_pipeline.py +0 -0
  86. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_source_resolution.py +0 -0
  87. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_stages.py +0 -0
  88. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_step_workspace.py +0 -0
  89. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_sync_back.py +0 -0
  90. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_trigger_script.py +0 -0
  91. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_unit5_config.py +0 -0
  92. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_unit5_pbt.py +0 -0
  93. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_unit5_pure.py +0 -0
  94. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/tests/test_verdict.py +0 -0
  95. {agentic_dev_loop-1.0.0a30 → agentic_dev_loop-1.0.0a32}/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.0a30
3
+ Version: 1.0.0a32
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
@@ -224,6 +224,8 @@ steps:
224
224
  agent: qa
225
225
  artifact: qa-report.md
226
226
  verdicts: { pass: PASS, fail: FAIL }
227
+ max_iterations: 100
228
+ finalize_nudge_turns: 10
227
229
  on_pass: commit
228
230
  on_fail: { goto: dev, retry: true }
229
231
 
@@ -239,6 +241,7 @@ steps:
239
241
  | `brief_inputs:` | Optional agent-step input spec: `{ include: [<paths/globs>], env_facts: [<strings>] }`. The engine resolves the include list against the workspace, passes the resolved docs + include contents + env facts as that step's task message, and regenerates the step only when those inputs change. A produces-only agent step such as `brief` is the documented pattern for per-run context digestion. |
240
242
  | `stop_when:` | Optional agent-step termination: `{ green_command: <argv regex>, post_green_turns: <int, default 2> }`. When the agent's most recent terminal observation matching `green_command` exits 0 and no workspace writes have happened since, the engine injects one nudge turn, allows `post_green_turns` further turns, then closes the conversation. Absent = run to finish. |
241
243
  | `terminal_timeout:` | Optional agent-step per-action timeout in seconds (gt 0). `SandboxTerminalExecutor` uses it when the agent leaves the terminal action timeout unset, overriding the SDK tool's default. `None` = current behavior. |
244
+ | `finalize_nudge_turns:` | Optional agent-step final-response nudge (gt 0). Requires `max_iterations:` and must be less than it. At `max_iterations - finalize_nudge_turns` completed turns the engine injects one advisory turn ("`N turns remain: produce your final response now`"), exactly once; the agent may still exhaust its turns and existing stuck/unclear handling applies unchanged. Absent = current behavior. Provisional pending one consumer run (revert if noisy or the raised cap inflates cost). |
242
245
  | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
243
246
  | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox before the step runs (command or agent steps; network ON; stamp-cached, re-runs when the pipeline's lockfiles change); `network: true` lets a command/agent step reach the hosts its `egress:` allowlist names (default off) - see [Per-stage networking](#per-stage-networking). |
244
247
  | `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). |
@@ -421,9 +424,10 @@ an agent burned budget learning it mid-run:
421
424
  the dev agent's next context; the dev prompt makes every listed issue a
422
425
  required fix.
423
426
  - **No git mutations, ever.** The `commit` step is the single commit point.
424
- - **`max_iterations` per role.** qa is bounded probing (60 in both
425
- reference repos); dev is open-ended construction and stays uncapped
426
- (the engine default).
427
+ - **`max_iterations` per role.** qa is bounded probing (100 in this
428
+ repo's dogfood loop, with `finalize_nudge_turns: 10` so it never ends
429
+ silently at the cap - both provisional); dev is open-ended construction
430
+ and stays uncapped (the engine default).
427
431
 
428
432
  ### The pipeline file
429
433
 
@@ -438,7 +442,7 @@ adapt per repo.
438
442
  | 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 |
439
443
  | conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
440
444
  | review | agent | ro, offline | static conformance verdict |
441
- | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
445
+ | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 100` + `finalize_nudge_turns: 10` (provisional); inject secrets only if the probes need them |
442
446
  | commit | action | host-side | the single commit point |
443
447
 
444
448
  Adaptation checklist for a new repo:
@@ -541,6 +545,28 @@ dotnet restore --locked-mode # installs exactly packages.lock.json
541
545
  Keep the lockfile tracked (do not gitignore it) and re-run the locked restore
542
546
  to prove it holds from a clean cache before setting `deps_policy: locked`.
543
547
 
548
+ ## Agent-facing error guide
549
+
550
+ The engine bundles an always-on guide for the engine error codes an agent
551
+ can actually observe while running terminal commands. The guide lives at
552
+ `src/agentic_dev_loop/engine/agent_error_guide.md`, versioned with the engine
553
+ code that emits the codes, and is injected into every agent step's task
554
+ context at assembly time. There is no opt-in and no opt-out in v1; command
555
+ and action steps never receive it because no agent reads them.
556
+
557
+ Each agent-visible code has exactly one table row, shaped like this:
558
+
559
+ | Code | Meaning | Correct response |
560
+ | --- | --- | --- |
561
+ | AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
562
+
563
+ The guide is consumer-agnostic: it explains the general mechanism and never
564
+ names a consumer or a consumer domain rule ID. If an ID must appear inside a
565
+ quoted engine message, the placeholder `<domain-rule-id>` is used. Any engine
566
+ change that adds or changes an agent-visible error code must update the guide
567
+ in the same commit; the test suite enforces the pairing by parsing the
568
+ agent-visible `SandboxError` raise sites against the bundled table.
569
+
544
570
  ## Host boundary (security model)
545
571
 
546
572
  The orchestrator, tool layer, and the `commit` action run in the **host
@@ -182,6 +182,8 @@ steps:
182
182
  agent: qa
183
183
  artifact: qa-report.md
184
184
  verdicts: { pass: PASS, fail: FAIL }
185
+ max_iterations: 100
186
+ finalize_nudge_turns: 10
185
187
  on_pass: commit
186
188
  on_fail: { goto: dev, retry: true }
187
189
 
@@ -197,6 +199,7 @@ steps:
197
199
  | `brief_inputs:` | Optional agent-step input spec: `{ include: [<paths/globs>], env_facts: [<strings>] }`. The engine resolves the include list against the workspace, passes the resolved docs + include contents + env facts as that step's task message, and regenerates the step only when those inputs change. A produces-only agent step such as `brief` is the documented pattern for per-run context digestion. |
198
200
  | `stop_when:` | Optional agent-step termination: `{ green_command: <argv regex>, post_green_turns: <int, default 2> }`. When the agent's most recent terminal observation matching `green_command` exits 0 and no workspace writes have happened since, the engine injects one nudge turn, allows `post_green_turns` further turns, then closes the conversation. Absent = run to finish. |
199
201
  | `terminal_timeout:` | Optional agent-step per-action timeout in seconds (gt 0). `SandboxTerminalExecutor` uses it when the agent leaves the terminal action timeout unset, overriding the SDK tool's default. `None` = current behavior. |
202
+ | `finalize_nudge_turns:` | Optional agent-step final-response nudge (gt 0). Requires `max_iterations:` and must be less than it. At `max_iterations - finalize_nudge_turns` completed turns the engine injects one advisory turn ("`N turns remain: produce your final response now`"), exactly once; the agent may still exhaust its turns and existing stuck/unclear handling applies unchanged. Absent = current behavior. Provisional pending one consumer run (revert if noisy or the raised cap inflates cost). |
200
203
  | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
201
204
  | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox before the step runs (command or agent steps; network ON; stamp-cached, re-runs when the pipeline's lockfiles change); `network: true` lets a command/agent step reach the hosts its `egress:` allowlist names (default off) - see [Per-stage networking](#per-stage-networking). |
202
205
  | `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). |
@@ -379,9 +382,10 @@ an agent burned budget learning it mid-run:
379
382
  the dev agent's next context; the dev prompt makes every listed issue a
380
383
  required fix.
381
384
  - **No git mutations, ever.** The `commit` step is the single commit point.
382
- - **`max_iterations` per role.** qa is bounded probing (60 in both
383
- reference repos); dev is open-ended construction and stays uncapped
384
- (the engine default).
385
+ - **`max_iterations` per role.** qa is bounded probing (100 in this
386
+ repo's dogfood loop, with `finalize_nudge_turns: 10` so it never ends
387
+ silently at the cap - both provisional); dev is open-ended construction
388
+ and stays uncapped (the engine default).
385
389
 
386
390
  ### The pipeline file
387
391
 
@@ -396,7 +400,7 @@ adapt per repo.
396
400
  | 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 |
397
401
  | conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
398
402
  | review | agent | ro, offline | static conformance verdict |
399
- | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
403
+ | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 100` + `finalize_nudge_turns: 10` (provisional); inject secrets only if the probes need them |
400
404
  | commit | action | host-side | the single commit point |
401
405
 
402
406
  Adaptation checklist for a new repo:
@@ -499,6 +503,28 @@ dotnet restore --locked-mode # installs exactly packages.lock.json
499
503
  Keep the lockfile tracked (do not gitignore it) and re-run the locked restore
500
504
  to prove it holds from a clean cache before setting `deps_policy: locked`.
501
505
 
506
+ ## Agent-facing error guide
507
+
508
+ The engine bundles an always-on guide for the engine error codes an agent
509
+ can actually observe while running terminal commands. The guide lives at
510
+ `src/agentic_dev_loop/engine/agent_error_guide.md`, versioned with the engine
511
+ code that emits the codes, and is injected into every agent step's task
512
+ context at assembly time. There is no opt-in and no opt-out in v1; command
513
+ and action steps never receive it because no agent reads them.
514
+
515
+ Each agent-visible code has exactly one table row, shaped like this:
516
+
517
+ | Code | Meaning | Correct response |
518
+ | --- | --- | --- |
519
+ | AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
520
+
521
+ The guide is consumer-agnostic: it explains the general mechanism and never
522
+ names a consumer or a consumer domain rule ID. If an ID must appear inside a
523
+ quoted engine message, the placeholder `<domain-rule-id>` is used. Any engine
524
+ change that adds or changes an agent-visible error code must update the guide
525
+ in the same commit; the test suite enforces the pairing by parsing the
526
+ agent-visible `SandboxError` raise sites against the bundled table.
527
+
502
528
  ## Host boundary (security model)
503
529
 
504
530
  The orchestrator, tool layer, and the `commit` action run in the **host
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentic-dev-loop"
7
- version = "1.0.0a30"
7
+ version = "1.0.0a32"
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"
@@ -68,6 +68,7 @@ where = ["src"]
68
68
 
69
69
  [tool.setuptools.package-data]
70
70
  agentic_dev_loop = ["py.typed"]
71
+ "agentic_dev_loop.engine" = ["*.md"]
71
72
  "agentic_dev_loop.engine.seccomp" = ["*.json"]
72
73
 
73
74
  [tool.pytest.ini_options]
@@ -387,6 +387,7 @@ class StepConfig(BaseModel):
387
387
  timeout: float | None = Field(default=None, gt=0) # seconds, command steps only
388
388
  terminal_timeout: float | None = Field(default=None, gt=0) # seconds, agent steps only (032)
389
389
  stop_when: StopWhenSpec | None = None # agent steps only (032)
390
+ finalize_nudge_turns: int | None = Field(default=None, gt=0) # agent steps only (035)
390
391
  brief_inputs: BriefInputsSpec | None = None # agent steps only (033)
391
392
  env: dict[str, str | None] | None = None # NFR-1: null = inherit host value
392
393
  setup: str | None = None # BR-13: command steps only
@@ -460,6 +461,21 @@ class StepConfig(BaseModel):
460
461
  raise ValueError(
461
462
  f"step {self.name!r}: 'stop_when' requires 'agent'"
462
463
  )
464
+ if self.finalize_nudge_turns is not None and self.agent is None:
465
+ raise ValueError(
466
+ f"step {self.name!r}: 'finalize_nudge_turns' requires 'agent'"
467
+ )
468
+ if self.finalize_nudge_turns is not None and self.max_iterations is None:
469
+ raise ValueError(
470
+ f"step {self.name!r}: 'finalize_nudge_turns' requires "
471
+ "'max_iterations'"
472
+ )
473
+ if (self.finalize_nudge_turns is not None
474
+ and self.finalize_nudge_turns >= self.max_iterations):
475
+ raise ValueError(
476
+ f"step {self.name!r}: 'finalize_nudge_turns' must be less "
477
+ "than 'max_iterations'"
478
+ )
463
479
  if self.brief_inputs is not None and self.agent is None:
464
480
  raise ValueError(
465
481
  f"step {self.name!r}: 'brief_inputs' requires 'agent'"
@@ -0,0 +1,6 @@
1
+ | Code | Meaning | Correct response |
2
+ | --- | --- | --- |
3
+ | AE-SBX-008 | The sandbox refused a git mutation command. Only the single commit step may change git state; agents never commit, branch, tag, or otherwise mutate git. | Do not run the command or any git mutation again. Continue without touching git and leave committing to the commit step. |
4
+ | AE-SBX-010 | The sandbox could not create the execution container for the command, so the command did not run. | The command is not the problem: retry once; if it persists, mention the code in your final response and stop. |
5
+ | AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
6
+ | AE-SBX-018 | The network egress proxy for this step stopped, so the sandbox refused the command rather than run without enforcement. | The command is not the problem: do not retry it, mention the code in your final response, and stop. |
@@ -0,0 +1,26 @@
1
+ """Bundled agent-facing error guide (034).
2
+
3
+ The guide table is an engine-owned resource, versioned with the engine so the
4
+ codes and their guidance cannot drift. Every agent step's task context gets
5
+ the table injected at assembly time; command and action steps never do (no
6
+ agent reads them).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from importlib.resources import files
12
+
13
+ GUIDE_RESOURCE = "agent_error_guide.md"
14
+ GUIDE_HEADING = "Agent-facing error guide"
15
+
16
+
17
+ def load_guide() -> str:
18
+ """The bundled guide table text."""
19
+ return (files("agentic_dev_loop.engine")
20
+ .joinpath(GUIDE_RESOURCE)
21
+ .read_text(encoding="utf-8"))
22
+
23
+
24
+ def guide_section() -> str:
25
+ """The guide block injected into an agent step's task context."""
26
+ return f"## {GUIDE_HEADING}\n\n{load_guide()}"
@@ -32,6 +32,7 @@ from typing import TYPE_CHECKING
32
32
  from langgraph.graph import END
33
33
  from openhands.sdk import Conversation
34
34
 
35
+ from .agent_error_guide import guide_section
35
36
  from .agents import load_agent, register_sandbox_terminal
36
37
  from agentic_dev_loop.core.config import TERMINALS, PipelineConfig, Route, StepConfig, route_target
37
38
  from agentic_dev_loop.core.paths import _has_wildcards
@@ -57,49 +58,79 @@ LAST_ERROR = "last-error.md"
57
58
  ARTIFACT_CONTEXT_CAP = 24_000 # chars per artifact injected into a step's context
58
59
 
59
60
 
60
- class _StopWhenController:
61
+ class _TurnPauseController:
62
+ """Turn-level pause callback (032): pause the SDK run loop after every
63
+ agent-sourced event so the caller can drive one turn at a time. The
64
+ finalize nudge (035) reuses this hook without any stop-when state."""
65
+
66
+ def __init__(self) -> None:
67
+ self.conversation = None
68
+
69
+ def attach(self, conversation) -> None:
70
+ self.conversation = conversation
71
+
72
+ def on_event(self, event) -> None:
73
+ if self.conversation is None:
74
+ return
75
+ if getattr(event, "source", None) == "agent":
76
+ from openhands.sdk.conversation.state import (
77
+ ConversationExecutionStatus,
78
+ )
79
+ self.conversation.state.execution_status = (
80
+ ConversationExecutionStatus.PAUSED
81
+ )
82
+
83
+
84
+ class _FinalizeNudgeController(_TurnPauseController):
85
+ """Finalize nudge (035): template text, never step-named. The turn loop
86
+ asks for the message when the conversation reaches the configured index;
87
+ ``nudged`` guards against re-injection on a same-turn retry."""
88
+
89
+ _TEMPLATE = "{n} turns remain: produce your final response now"
90
+
91
+ def __init__(self, turns: int) -> None:
92
+ super().__init__()
93
+ self.turns = turns
94
+ self.nudged = False
95
+
96
+ def message(self) -> str:
97
+ return self._TEMPLATE.format(n=self.turns)
98
+
99
+
100
+ class _StopWhenController(_TurnPauseController):
61
101
  """Turn-level stop-when-green observation (032).
62
102
 
63
103
  Pure per-event state machine - no knowledge of how the conversation is
64
104
  driven. ``on_event`` is a SDK conversation callback: it records the most
65
105
  recent matching terminal observation and any subsequent file-editor
66
- write, and pauses the conversation after every agent-sourced event so the
67
- caller can drive one turn at a time.
106
+ write, and (via the base pause) pauses the conversation after every
107
+ agent-sourced event so the caller can drive one turn at a time.
68
108
  """
69
109
 
70
110
  NUDGE = "Step complete: produce your final response now"
71
111
  _WRITE_COMMANDS = frozenset({"create", "str_replace", "insert", "undo_edit"})
72
112
 
73
113
  def __init__(self, spec) -> None:
114
+ super().__init__()
74
115
  self._pattern = re.compile(spec.green_command)
75
116
  self.post_green_turns = spec.post_green_turns
76
117
  self.green_hit = False
77
118
  self.writes_since_green = False
78
119
  self.nudged = False
79
120
  self.post_nudge_turns = 0
80
- self.conversation = None
81
-
82
- def attach(self, conversation) -> None:
83
- self.conversation = conversation
84
121
 
85
122
  @property
86
123
  def green_signal(self) -> bool:
87
124
  return self.green_hit and not self.writes_since_green
88
125
 
89
126
  def on_event(self, event) -> None:
127
+ super().on_event(event)
90
128
  if self.conversation is None:
91
129
  return
92
130
  from openhands.sdk.event import ObservationEvent
93
131
 
94
132
  if isinstance(event, ObservationEvent):
95
133
  self._record_observation(event)
96
- if getattr(event, "source", None) == "agent":
97
- from openhands.sdk.conversation.state import (
98
- ConversationExecutionStatus,
99
- )
100
- self.conversation.state.execution_status = (
101
- ConversationExecutionStatus.PAUSED
102
- )
103
134
 
104
135
  def on_terminal_result(self, command: str | None, exit_code: int | None,
105
136
  wrote_workspace: bool) -> None:
@@ -159,6 +190,10 @@ class Runtime:
159
190
  controller = None
160
191
  if step is not None and step.stop_when is not None:
161
192
  controller = _StopWhenController(step.stop_when)
193
+ finalize_controller = None
194
+ if step is not None and step.finalize_nudge_turns is not None:
195
+ finalize_controller = _FinalizeNudgeController(
196
+ step.finalize_nudge_turns)
162
197
 
163
198
  if self.sandbox is not None:
164
199
  if step is not None:
@@ -181,15 +216,22 @@ class Runtime:
181
216
  secrets=(step_secret_env(self.cfg, step, self.secrets)
182
217
  if step is not None else None))
183
218
  agent = load_agent(self.agents_dir, agent_name, self.llm)
184
- callbacks = [controller.on_event] if controller is not None else None
219
+ callbacks = []
220
+ if controller is not None:
221
+ callbacks.append(controller.on_event)
222
+ if finalize_controller is not None:
223
+ callbacks.append(finalize_controller.on_event)
185
224
  conv = Conversation(agent=agent, workspace=str(self.workspace),
186
225
  max_iteration_per_run=max_iterations,
187
- callbacks=callbacks)
226
+ callbacks=callbacks or None)
188
227
  if controller is not None:
189
228
  controller.attach(conv)
229
+ if finalize_controller is not None:
230
+ finalize_controller.attach(conv)
190
231
  conv.send_message(message)
191
- if controller is not None:
192
- self._run_stop_when_loop(conv, controller, max_iterations)
232
+ if controller is not None or finalize_controller is not None:
233
+ self._run_stop_when_loop(conv, controller, max_iterations,
234
+ finalize_controller)
193
235
  else:
194
236
  conv.run()
195
237
 
@@ -230,15 +272,19 @@ class Runtime:
230
272
  if conv is not None:
231
273
  conv.close()
232
274
 
233
- def _run_stop_when_loop(self, conv, controller: _StopWhenController,
234
- max_iterations: int) -> None:
235
- """Drive one turn at a time so a green signal can inject the nudge
236
- and enforce the post-nudge turn budget. The controller's callback
237
- pauses the SDK run loop after every agent-sourced event, so each
238
- ``conv.run()`` here advances exactly one agent turn."""
275
+ def _run_stop_when_loop(self, conv,
276
+ controller: _StopWhenController | None,
277
+ max_iterations: int,
278
+ finalize_controller: _FinalizeNudgeController | None = None) -> None:
279
+ """Drive one turn at a time so turn-level hooks can inject their
280
+ nudges and enforce budgets. A controller's callback pauses the SDK
281
+ run loop after every agent-sourced event, so each ``conv.run()`` here
282
+ advances exactly one agent turn."""
239
283
  from openhands.sdk.conversation.state import ConversationExecutionStatus
240
284
 
241
285
  turns = 0
286
+ nudge_at = (max_iterations - finalize_controller.turns
287
+ if finalize_controller is not None else None)
242
288
  while True:
243
289
  conv.run()
244
290
  status = conv.state.execution_status
@@ -252,17 +298,24 @@ class Runtime:
252
298
  conv.state.execution_status = ConversationExecutionStatus.ERROR
253
299
  break
254
300
 
255
- if not controller.nudged and controller.green_signal:
256
- conv.send_message(controller.NUDGE)
257
- controller.nudged = True
258
- if controller.post_green_turns == 0:
259
- break
301
+ if (finalize_controller is not None and turns == nudge_at
302
+ and not finalize_controller.nudged):
303
+ conv.send_message(finalize_controller.message())
304
+ finalize_controller.nudged = True
260
305
  continue
261
306
 
262
- if controller.nudged:
263
- controller.post_nudge_turns += 1
264
- if controller.post_nudge_turns >= controller.post_green_turns:
265
- break
307
+ if controller is not None:
308
+ if not controller.nudged and controller.green_signal:
309
+ conv.send_message(controller.NUDGE)
310
+ controller.nudged = True
311
+ if controller.post_green_turns == 0:
312
+ break
313
+ continue
314
+
315
+ if controller.nudged:
316
+ controller.post_nudge_turns += 1
317
+ if controller.post_nudge_turns >= controller.post_green_turns:
318
+ break
266
319
 
267
320
 
268
321
  def prompt_dir(runtime: Runtime) -> str:
@@ -364,12 +417,12 @@ def _artifact_file(runtime: Runtime, step_name: str) -> str | None:
364
417
  def build_context(runtime: Runtime, docs: dict[str, str], subject: str,
365
418
  notes: list[str] | None = None,
366
419
  artifacts: dict[str, str] | None = None) -> str:
367
- """Assemble the attempt's shared message: subject, doc bundle, the
368
- canonical dependency-policy paragraph (021), the expected-output-paths
369
- section generated from step defs (a prompt merely naming files lets agents
370
- skip them), accumulated stage artifacts (027), and previous-attempt
371
- notes."""
372
- parts = [f"You are working on {subject}.\n"]
420
+ """Assemble the attempt's shared message: the always-on agent-facing error
421
+ guide (034), subject, doc bundle, the canonical dependency-policy paragraph
422
+ (021), the expected-output-paths section generated from step defs (a prompt
423
+ merely naming files lets agents skip them), accumulated stage artifacts
424
+ (027), and previous-attempt notes."""
425
+ parts = [f"You are working on {subject}.\n", guide_section()]
373
426
  for name, content in docs.items():
374
427
  parts.append(f"## {name}\n\n{content}")
375
428
  if artifacts:
@@ -541,13 +594,14 @@ def brief_inputs_hash(runtime: Runtime, step: StepConfig,
541
594
  def build_brief_context(runtime: Runtime, step: StepConfig,
542
595
  docs: dict[str, str],
543
596
  doc_sources: dict[str, str] | None = None) -> str:
544
- """Assemble the brief agent's task message (033): the resolved unit/plan
545
- docs (re-read from disk where sources are known), the closed
546
- ``brief_inputs.include`` file set, and the env-facts block. Prior verdicts
547
- and feedback are deliberately absent - the notes channel delivers them per
548
- attempt."""
597
+ """Assemble the brief agent's task message (033): the always-on
598
+ agent-facing error guide (034), the resolved unit/plan docs (re-read from
599
+ disk where sources are known), the closed ``brief_inputs.include`` file
600
+ set, and the env-facts block. Prior verdicts and feedback are deliberately
601
+ absent - the notes channel delivers them per attempt."""
549
602
  sections, env_facts = _brief_sections(runtime, step, docs, doc_sources)
550
- parts = [f"## {name}\n\n{content}" for name, content in sections.items()]
603
+ parts = [guide_section()]
604
+ parts += [f"## {name}\n\n{content}" for name, content in sections.items()]
551
605
  if env_facts:
552
606
  parts.append("## Environment facts\n\n"
553
607
  + "\n".join(f"- {fact}" for fact in env_facts))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 1.0.0a30
3
+ Version: 1.0.0a32
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
@@ -224,6 +224,8 @@ steps:
224
224
  agent: qa
225
225
  artifact: qa-report.md
226
226
  verdicts: { pass: PASS, fail: FAIL }
227
+ max_iterations: 100
228
+ finalize_nudge_turns: 10
227
229
  on_pass: commit
228
230
  on_fail: { goto: dev, retry: true }
229
231
 
@@ -239,6 +241,7 @@ steps:
239
241
  | `brief_inputs:` | Optional agent-step input spec: `{ include: [<paths/globs>], env_facts: [<strings>] }`. The engine resolves the include list against the workspace, passes the resolved docs + include contents + env facts as that step's task message, and regenerates the step only when those inputs change. A produces-only agent step such as `brief` is the documented pattern for per-run context digestion. |
240
242
  | `stop_when:` | Optional agent-step termination: `{ green_command: <argv regex>, post_green_turns: <int, default 2> }`. When the agent's most recent terminal observation matching `green_command` exits 0 and no workspace writes have happened since, the engine injects one nudge turn, allows `post_green_turns` further turns, then closes the conversation. Absent = run to finish. |
241
243
  | `terminal_timeout:` | Optional agent-step per-action timeout in seconds (gt 0). `SandboxTerminalExecutor` uses it when the agent leaves the terminal action timeout unset, overriding the SDK tool's default. `None` = current behavior. |
244
+ | `finalize_nudge_turns:` | Optional agent-step final-response nudge (gt 0). Requires `max_iterations:` and must be less than it. At `max_iterations - finalize_nudge_turns` completed turns the engine injects one advisory turn ("`N turns remain: produce your final response now`"), exactly once; the agent may still exhaust its turns and existing stuck/unclear handling applies unchanged. Absent = current behavior. Provisional pending one consumer run (revert if noisy or the raised cap inflates cost). |
242
245
  | `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
243
246
  | `env:` / `setup:` / `network:` | `env:` declares step vars (fixed safe base + declared vars only); `setup:` provisions the sandbox before the step runs (command or agent steps; network ON; stamp-cached, re-runs when the pipeline's lockfiles change); `network: true` lets a command/agent step reach the hosts its `egress:` allowlist names (default off) - see [Per-stage networking](#per-stage-networking). |
244
247
  | `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). |
@@ -421,9 +424,10 @@ an agent burned budget learning it mid-run:
421
424
  the dev agent's next context; the dev prompt makes every listed issue a
422
425
  required fix.
423
426
  - **No git mutations, ever.** The `commit` step is the single commit point.
424
- - **`max_iterations` per role.** qa is bounded probing (60 in both
425
- reference repos); dev is open-ended construction and stays uncapped
426
- (the engine default).
427
+ - **`max_iterations` per role.** qa is bounded probing (100 in this
428
+ repo's dogfood loop, with `finalize_nudge_turns: 10` so it never ends
429
+ silently at the cap - both provisional); dev is open-ended construction
430
+ and stays uncapped (the engine default).
427
431
 
428
432
  ### The pipeline file
429
433
 
@@ -438,7 +442,7 @@ adapt per repo.
438
442
  | 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 |
439
443
  | conformance | command | network + `presets: [package-registries]` | repo-specific fitness checks (the consumer repo: jscpd + DDD rules) |
440
444
  | review | agent | ro, offline | static conformance verdict |
441
- | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 60`; inject secrets only if the probes need them |
445
+ | qa | agent | per repo (see table above) | live-behaviour probes; `max_iterations: 100` + `finalize_nudge_turns: 10` (provisional); inject secrets only if the probes need them |
442
446
  | commit | action | host-side | the single commit point |
443
447
 
444
448
  Adaptation checklist for a new repo:
@@ -541,6 +545,28 @@ dotnet restore --locked-mode # installs exactly packages.lock.json
541
545
  Keep the lockfile tracked (do not gitignore it) and re-run the locked restore
542
546
  to prove it holds from a clean cache before setting `deps_policy: locked`.
543
547
 
548
+ ## Agent-facing error guide
549
+
550
+ The engine bundles an always-on guide for the engine error codes an agent
551
+ can actually observe while running terminal commands. The guide lives at
552
+ `src/agentic_dev_loop/engine/agent_error_guide.md`, versioned with the engine
553
+ code that emits the codes, and is injected into every agent step's task
554
+ context at assembly time. There is no opt-in and no opt-out in v1; command
555
+ and action steps never receive it because no agent reads them.
556
+
557
+ Each agent-visible code has exactly one table row, shaped like this:
558
+
559
+ | Code | Meaning | Correct response |
560
+ | --- | --- | --- |
561
+ | AE-SBX-016 | The loop refused to sync an edit because the real repo changed underneath on a path this workspace also touches; the file in this workspace is stale and its edits will not be applied. | The command is not the problem: retry once, do not edit or work around the named path, and mention the code in your final response if it persists. |
562
+
563
+ The guide is consumer-agnostic: it explains the general mechanism and never
564
+ names a consumer or a consumer domain rule ID. If an ID must appear inside a
565
+ quoted engine message, the placeholder `<domain-rule-id>` is used. Any engine
566
+ change that adds or changes an agent-visible error code must update the guide
567
+ in the same commit; the test suite enforces the pairing by parsing the
568
+ agent-visible `SandboxError` raise sites against the bundled table.
569
+
544
570
  ## Host boundary (security model)
545
571
 
546
572
  The orchestrator, tool layer, and the `commit` action run in the **host
@@ -16,6 +16,8 @@ src/agentic_dev_loop/core/results.py
16
16
  src/agentic_dev_loop/core/verdict.py
17
17
  src/agentic_dev_loop/engine/__init__.py
18
18
  src/agentic_dev_loop/engine/actions.py
19
+ src/agentic_dev_loop/engine/agent_error_guide.md
20
+ src/agentic_dev_loop/engine/agent_error_guide.py
19
21
  src/agentic_dev_loop/engine/agents.py
20
22
  src/agentic_dev_loop/engine/cli.py
21
23
  src/agentic_dev_loop/engine/deps.py
@@ -47,7 +49,9 @@ tests/test_028_declared_caches.py
47
49
  tests/test_029_merge_on_conflict.py
48
50
  tests/test_032_stop_when_green.py
49
51
  tests/test_033_brief_step.py
52
+ tests/test_035_finalize_nudge.py
50
53
  tests/test_actions.py
54
+ tests/test_agent_error_guide.py
51
55
  tests/test_agents.py
52
56
  tests/test_aidlc.py
53
57
  tests/test_cli.py