agentic-dev-loop 1.0.0a43__tar.gz → 1.0.0a44__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.0a43/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a44}/PKG-INFO +2 -1
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/README.md +1 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/pyproject.toml +1 -1
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/config.py +12 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/isolation.py +35 -12
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44/src/agentic_dev_loop.egg-info}/PKG-INFO +2 -1
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_config.py +31 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_isolation.py +49 -2
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_isolation_integration.py +19 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/LICENSE +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/setup.cfg +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/paths.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/results.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/verdict.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/actions.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/agent_error_guide.md +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/agent_error_guide.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/agents.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/allowlist.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/brief_coverage.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/cli.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/containers.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/deps.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/dryrun.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/egress_policy.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/egress_proxy.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/extensions/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/extensions/aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/forbidden.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/graph.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/llm.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/loaders.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/mounts.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/outputs.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/presets.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/proc.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/registry.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/seccomp/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/seccomp/chromium-userns.json +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/secrets.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/stages.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/step_workspace.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/sync_back.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/py.typed +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/viz/__init__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/viz/__main__.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/viz/server.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/SOURCES.txt +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/dependency_links.txt +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/entry_points.txt +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/requires.txt +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/top_level.txt +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_028_declared_caches.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_029_merge_on_conflict.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_033_brief_step.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_035_finalize_nudge.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_036_banned_commands_and_lane_boundary.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_041_brief_coverage.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_actions.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_agent_error_guide.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_agents.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_aidlc.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_cli.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_containers.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_deps.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_dry_run.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_e2e_fixtures.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_egress_proxy.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_graph.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_hostboundary.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_hostboundary_integration.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_isolation_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_llm.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_loaders.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_module_contract.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_outputs.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_outputs_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_proc.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_publish.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_qa_prompt.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_registry.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_repo_ascii.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_results.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_sdk_compat.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_secrets.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_secrets_integration.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_secrets_pipeline.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_source_resolution.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_stages.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_step_workspace.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_sync_back.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_trigger_script.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_unit5_config.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_unit5_pbt.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_unit5_pure.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_verdict.py +0 -0
- {agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_viz.py +0 -0
{agentic_dev_loop-1.0.0a43/src/agentic_dev_loop.egg-info → agentic_dev_loop-1.0.0a44}/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.0a44
|
|
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
|
|
@@ -255,6 +255,7 @@ steps:
|
|
|
255
255
|
| `custom_prompt:` | Planned (planning-docs/040). Optional agent-step value: literal text, or a path whose file contents are read at load time, appended verbatim to the agent prompt at context assembly (after the prompt, before the task/artifacts). A value that looks like a path but names no file is a validation error. Agent steps only. |
|
|
256
256
|
| `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
|
|
257
257
|
| `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). |
|
|
258
|
+
| `setup_probe:` | Optional consumer-authored in-container probe run on a warm `setup:` stamp instead of the venv probe. Exit 0 trusts the stamp (skips); any nonzero re-provisions with network on (fail toward freshness). The probe text is NOT part of the stamp key, so a probe-only change needs `--refresh` or `--clean-stamps` to take effect. Example (asserting a warmed esbuild binary is Linux-shaped): `file frontend-editor/node_modules/esbuild/bin/esbuild | grep -q ELF`. |
|
|
258
259
|
| `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). |
|
|
259
260
|
| `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). |
|
|
260
261
|
| `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). |
|
|
@@ -211,6 +211,7 @@ steps:
|
|
|
211
211
|
| `custom_prompt:` | Planned (planning-docs/040). Optional agent-step value: literal text, or a path whose file contents are read at load time, appended verbatim to the agent prompt at context assembly (after the prompt, before the task/artifacts). A value that looks like a path but names no file is a validation error. Agent steps only. |
|
|
212
212
|
| `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
|
|
213
213
|
| `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). |
|
|
214
|
+
| `setup_probe:` | Optional consumer-authored in-container probe run on a warm `setup:` stamp instead of the venv probe. Exit 0 trusts the stamp (skips); any nonzero re-provisions with network on (fail toward freshness). The probe text is NOT part of the stamp key, so a probe-only change needs `--refresh` or `--clean-stamps` to take effect. Example (asserting a warmed esbuild binary is Linux-shaped): `file frontend-editor/node_modules/esbuild/bin/esbuild | grep -q ELF`. |
|
|
214
215
|
| `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
216
|
| `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
217
|
| `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). |
|
|
@@ -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.0a44"
|
|
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"
|
|
@@ -362,6 +362,7 @@ class StepConfig(BaseModel):
|
|
|
362
362
|
custom_prompt: str | None = None # agent steps only (040); file path resolved at load
|
|
363
363
|
env: dict[str, str | None] | None = None # NFR-1: null = inherit host value
|
|
364
364
|
setup: str | None = None # BR-13: command steps only
|
|
365
|
+
setup_probe: str | None = None # 045: consumer probe replacing the venv probe on a warm stamp; requires `setup`; probe text is NOT part of the stamp key (a probe-only change needs --refresh/--clean-stamps)
|
|
365
366
|
forbidden_commands: list[str] = [] # banned-command regex deny-list (036 D1)
|
|
366
367
|
network: bool = False # per-stage egress (command/agent + sandbox actions; host-side actions reject at registry time)
|
|
367
368
|
# unit-5: per-step mount granularity + egress policy. Empty mounts =
|
|
@@ -420,6 +421,13 @@ class StepConfig(BaseModel):
|
|
|
420
421
|
raise ValueError("custom_prompt must be a non-empty string")
|
|
421
422
|
return v
|
|
422
423
|
|
|
424
|
+
@field_validator("setup_probe")
|
|
425
|
+
@classmethod
|
|
426
|
+
def _nonempty_setup_probe(cls, v: str | None) -> str | None:
|
|
427
|
+
if v is not None and not v.strip():
|
|
428
|
+
raise ValueError("setup_probe must be a non-empty string")
|
|
429
|
+
return v
|
|
430
|
+
|
|
423
431
|
@model_validator(mode="after")
|
|
424
432
|
def _check_shape(self) -> "StepConfig":
|
|
425
433
|
for key in ("agent", "action", "command"):
|
|
@@ -494,6 +502,10 @@ class StepConfig(BaseModel):
|
|
|
494
502
|
raise ValueError(
|
|
495
503
|
f"step {self.name!r}: 'setup' is only allowed on 'command' or 'agent' steps"
|
|
496
504
|
)
|
|
505
|
+
if self.setup_probe is not None and self.setup is None:
|
|
506
|
+
raise ValueError(
|
|
507
|
+
f"step {self.name!r}: 'setup_probe' requires 'setup'"
|
|
508
|
+
)
|
|
497
509
|
# `network` is NOT rejected here: the schema can't tell a host-side
|
|
498
510
|
# built-in action (e.g. `commit`, which must never take egress) from
|
|
499
511
|
# a sandbox-executed consumer action (which may, like a command step).
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/isolation.py
RENAMED
|
@@ -534,11 +534,19 @@ def venv_probe_command() -> str:
|
|
|
534
534
|
)
|
|
535
535
|
|
|
536
536
|
|
|
537
|
+
def probe_argv(probe_cmd: str, container: str, workspace: str) -> list[str]:
|
|
538
|
+
"""``docker exec`` argv that runs an arbitrary in-container probe command
|
|
539
|
+
(045) inside the already-running execution container against the
|
|
540
|
+
bind-mounted workspace. ``probe_cmd`` is a consumer-authored shell command
|
|
541
|
+
string passed verbatim to ``/bin/sh -c``."""
|
|
542
|
+
return ["docker", "exec", "-w", workspace, container,
|
|
543
|
+
"/bin/sh", "-c", probe_cmd]
|
|
544
|
+
|
|
545
|
+
|
|
537
546
|
def venv_probe_argv(container: str, workspace: str) -> list[str]:
|
|
538
547
|
"""``docker exec`` argv that runs the venv probe inside the (already
|
|
539
548
|
running) execution container against the bind-mounted workspace."""
|
|
540
|
-
return
|
|
541
|
-
"/bin/sh", "-c", venv_probe_command()]
|
|
549
|
+
return probe_argv(venv_probe_command(), container, workspace)
|
|
542
550
|
|
|
543
551
|
|
|
544
552
|
def venv_probe_state(stdout: str, exit_code: int | None) -> str:
|
|
@@ -1133,7 +1141,7 @@ class DockerBackend(Sandbox):
|
|
|
1133
1141
|
self._sync_excludes)
|
|
1134
1142
|
|
|
1135
1143
|
def provision(self, setup_cmd: str, stamp_key: str, stamp_dir: str,
|
|
1136
|
-
force: bool) -> None:
|
|
1144
|
+
force: bool, setup_probe: str | None = None) -> None:
|
|
1137
1145
|
"""Run one ``setup:`` hook in the provision container (network ON),
|
|
1138
1146
|
stamp-cached. Nonzero exit → AE-SBX-009, no stamp written.
|
|
1139
1147
|
|
|
@@ -1143,13 +1151,21 @@ class DockerBackend(Sandbox):
|
|
|
1143
1151
|
broken in the Linux container. A ``broken`` probe removes the venv
|
|
1144
1152
|
(fail loud if removal is impossible) and falls through to re-provision;
|
|
1145
1153
|
``valid``/``missing``/``unknown`` keep the stamp skip.
|
|
1154
|
+
|
|
1155
|
+
045: when ``setup_probe`` is set it replaces the venv probe for this
|
|
1156
|
+
hook. Exit 0 trusts the stamp (skip); any nonzero - a broken artifact
|
|
1157
|
+
or a failed probe run - falls through to re-provision (fail toward
|
|
1158
|
+
freshness).
|
|
1146
1159
|
"""
|
|
1147
1160
|
if not force and read_stamp(Path(stamp_dir), stamp_key):
|
|
1148
|
-
if
|
|
1161
|
+
if setup_probe is not None:
|
|
1162
|
+
if self._probe(setup_probe).exit_code == 0:
|
|
1163
|
+
return
|
|
1164
|
+
else:
|
|
1165
|
+
if self._probe_venv() != "broken":
|
|
1166
|
+
return
|
|
1149
1167
|
remove_sandbox_venv(self._workspace,
|
|
1150
1168
|
sandbox_venv_dir=self._sandbox_venv_dir)
|
|
1151
|
-
else:
|
|
1152
|
-
return
|
|
1153
1169
|
payload = docker_provision_payload(
|
|
1154
1170
|
run_id=self._run_id, image=self._image, workspace=self._workspace,
|
|
1155
1171
|
setup_cmd=setup_cmd, sandbox_venv_dir=self._sandbox_venv_dir,
|
|
@@ -1162,13 +1178,18 @@ class DockerBackend(Sandbox):
|
|
|
1162
1178
|
f"- stamp not written (docs/setup.md §5)")
|
|
1163
1179
|
write_stamp(Path(stamp_dir), stamp_key)
|
|
1164
1180
|
|
|
1181
|
+
def _probe(self, probe_cmd: str) -> CmdResult:
|
|
1182
|
+
"""Run an arbitrary in-container probe command (045). The workdir
|
|
1183
|
+
follows the export root when one is materialised, else the workspace."""
|
|
1184
|
+
workdir = self._export_root or self._workspace
|
|
1185
|
+
return self._runner.run(
|
|
1186
|
+
probe_argv(probe_cmd, self._container, workdir), timeout=30)
|
|
1187
|
+
|
|
1165
1188
|
def _probe_venv(self) -> str:
|
|
1166
1189
|
"""Run the in-container venv probe and classify it (016 self-heal).
|
|
1167
1190
|
With an export the venv is mounted at ``{export}/.venv-sandbox``, so
|
|
1168
1191
|
the probe workdir follows the export root."""
|
|
1169
|
-
|
|
1170
|
-
res = self._runner.run(
|
|
1171
|
-
venv_probe_argv(self._container, workdir), timeout=30)
|
|
1192
|
+
res = self._probe(venv_probe_command())
|
|
1172
1193
|
return venv_probe_state(res.stdout, res.exit_code)
|
|
1173
1194
|
|
|
1174
1195
|
# -- execution ----------------------------------------------------------
|
|
@@ -1903,7 +1924,8 @@ def _declared_setups(cfg: PipelineConfig, workspace: Path):
|
|
|
1903
1924
|
if step.setup is not None and (
|
|
1904
1925
|
step.command is not None or step.agent is not None
|
|
1905
1926
|
):
|
|
1906
|
-
yield step.setup, setup_stamp_key(step.setup, hashes)
|
|
1927
|
+
yield (step.setup, setup_stamp_key(step.setup, hashes),
|
|
1928
|
+
step.setup_probe)
|
|
1907
1929
|
|
|
1908
1930
|
|
|
1909
1931
|
def isolation_state_dir(cfg: PipelineConfig) -> Path:
|
|
@@ -2017,10 +2039,11 @@ def startup_sandbox(cfg: PipelineConfig, *, workspace: Path,
|
|
|
2017
2039
|
_announce("execution container started")
|
|
2018
2040
|
if refresh is None:
|
|
2019
2041
|
refresh = bool(cfg.isolation and cfg.isolation.refresh)
|
|
2020
|
-
for setup_cmd, stamp_key in _declared_setups(cfg, workspace):
|
|
2042
|
+
for setup_cmd, stamp_key, setup_probe in _declared_setups(cfg, workspace):
|
|
2021
2043
|
_announce(f"provisioning setup (stamp {stamp_key[:8]})")
|
|
2022
2044
|
backend.provision(setup_cmd, stamp_key,
|
|
2023
|
-
str(iso_state_dir / "stamps"), force=refresh
|
|
2045
|
+
str(iso_state_dir / "stamps"), force=refresh,
|
|
2046
|
+
setup_probe=setup_probe)
|
|
2024
2047
|
_announce("sandbox ready")
|
|
2025
2048
|
return backend
|
|
2026
2049
|
except BaseException:
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44/src/agentic_dev_loop.egg-info}/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.0a44
|
|
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
|
|
@@ -255,6 +255,7 @@ steps:
|
|
|
255
255
|
| `custom_prompt:` | Planned (planning-docs/040). Optional agent-step value: literal text, or a path whose file contents are read at load time, appended verbatim to the agent prompt at context assembly (after the prompt, before the task/artifacts). A value that looks like a path but names no file is a validation error. Agent steps only. |
|
|
256
256
|
| `on_pass:` / `on_fail:` | A step name, terminal `success`/`failure`, or `{goto: X, retry: true}`. No loops happen unless you write them. |
|
|
257
257
|
| `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). |
|
|
258
|
+
| `setup_probe:` | Optional consumer-authored in-container probe run on a warm `setup:` stamp instead of the venv probe. Exit 0 trusts the stamp (skips); any nonzero re-provisions with network on (fail toward freshness). The probe text is NOT part of the stamp key, so a probe-only change needs `--refresh` or `--clean-stamps` to take effect. Example (asserting a warmed esbuild binary is Linux-shaped): `file frontend-editor/node_modules/esbuild/bin/esbuild | grep -q ELF`. |
|
|
258
259
|
| `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). |
|
|
259
260
|
| `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). |
|
|
260
261
|
| `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). |
|
|
@@ -596,6 +596,37 @@ class TestStepEnvAndSetup:
|
|
|
596
596
|
assert rt.env == {"A": "x", "B": None}
|
|
597
597
|
|
|
598
598
|
|
|
599
|
+
class TestSetupProbe:
|
|
600
|
+
def test_defaults_none(self, tmp_path):
|
|
601
|
+
assert _load(tmp_path, _base()).steps[0].setup_probe is None
|
|
602
|
+
|
|
603
|
+
def test_parses_with_setup(self, tmp_path):
|
|
604
|
+
data = _base()
|
|
605
|
+
data["steps"] = [{"name": "t", "command": "make",
|
|
606
|
+
"setup": "make warm-cache",
|
|
607
|
+
"setup_probe": "file node_modules/esbuild/bin/esbuild | grep -q ELF",
|
|
608
|
+
"on_pass": "success"}]
|
|
609
|
+
step = _load(tmp_path, data).steps[0]
|
|
610
|
+
assert step.setup_probe == (
|
|
611
|
+
"file node_modules/esbuild/bin/esbuild | grep -q ELF")
|
|
612
|
+
|
|
613
|
+
def test_requires_setup(self, tmp_path):
|
|
614
|
+
data = _base()
|
|
615
|
+
data["steps"] = [{"name": "t", "command": "make",
|
|
616
|
+
"setup_probe": "file node_modules/esbuild/bin/esbuild | grep -q ELF",
|
|
617
|
+
"on_pass": "success"}]
|
|
618
|
+
with pytest.raises(ValidationError,
|
|
619
|
+
match="'setup_probe' requires 'setup'"):
|
|
620
|
+
_load(tmp_path, data)
|
|
621
|
+
|
|
622
|
+
def test_empty_rejected(self, tmp_path):
|
|
623
|
+
data = _base()
|
|
624
|
+
data["steps"] = [{"name": "t", "command": "make", "setup": "make warm-cache",
|
|
625
|
+
"setup_probe": " ", "on_pass": "success"}]
|
|
626
|
+
with pytest.raises(ValidationError, match="non-empty"):
|
|
627
|
+
_load(tmp_path, data)
|
|
628
|
+
|
|
629
|
+
|
|
599
630
|
class TestStepNetwork:
|
|
600
631
|
def test_network_defaults_false(self, tmp_path):
|
|
601
632
|
step = _load(tmp_path, _base()).steps[0]
|
|
@@ -33,7 +33,7 @@ from agentic_dev_loop.engine.isolation import (
|
|
|
33
33
|
probe_docker, process_start, read_stamp, reap_orphaned_containers,
|
|
34
34
|
remove_sandbox_venv, resolve_effective_profile, resolve_image_ref,
|
|
35
35
|
resolve_sandbox_env, resolve_sandbox_venv_dir, select_backend,
|
|
36
|
-
setup_stamp_key, translate_mount_path, venv_probe_argv,
|
|
36
|
+
probe_argv, setup_stamp_key, translate_mount_path, venv_probe_argv,
|
|
37
37
|
venv_probe_command, venv_probe_state, write_stamp,
|
|
38
38
|
)
|
|
39
39
|
|
|
@@ -282,6 +282,16 @@ class TestVenvProbe:
|
|
|
282
282
|
assert venv_probe_state(stdout, exit_code) == expected
|
|
283
283
|
|
|
284
284
|
|
|
285
|
+
class TestProbeArgv:
|
|
286
|
+
def test_assembly(self):
|
|
287
|
+
argv = probe_argv("file .warmed | grep -q ELF", "c1", "/ws")
|
|
288
|
+
assert argv[1] == "exec"
|
|
289
|
+
assert argv[2] == "-w"
|
|
290
|
+
assert argv[3] == "/ws"
|
|
291
|
+
assert argv[4] == "c1"
|
|
292
|
+
assert argv[5:] == ["/bin/sh", "-c", "file .warmed | grep -q ELF"]
|
|
293
|
+
|
|
294
|
+
|
|
285
295
|
class TestRemoveSandboxVenv:
|
|
286
296
|
def test_removes_broken_symlink_dir(self, tmp_path):
|
|
287
297
|
ws = tmp_path / "ws"
|
|
@@ -971,6 +981,39 @@ class TestProvisionStampsAudit:
|
|
|
971
981
|
assert "x" * 8 in rolled
|
|
972
982
|
|
|
973
983
|
|
|
984
|
+
class TestSetupProbeProvision:
|
|
985
|
+
def test_probe_exit_zero_skips(self, tmp_path):
|
|
986
|
+
runner = FakeRunner(ok(""))
|
|
987
|
+
backend = _backend(tmp_path, runner)
|
|
988
|
+
stamp_dir = tmp_path / "stamps"
|
|
989
|
+
write_stamp(stamp_dir, "key1")
|
|
990
|
+
backend.provision("make warm-cache", "key1", str(stamp_dir),
|
|
991
|
+
force=False, setup_probe="file .warmed | grep -q ELF")
|
|
992
|
+
assert [c for c in runner.calls if c[1] == "exec"]
|
|
993
|
+
assert not [c for c in runner.calls if c[1] == "run"]
|
|
994
|
+
|
|
995
|
+
def test_probe_nonzero_reprovisions(self, tmp_path):
|
|
996
|
+
runner = FakeRunner(fail(1, "not ELF"), ok(""))
|
|
997
|
+
backend = _backend(tmp_path, runner)
|
|
998
|
+
stamp_dir = tmp_path / "stamps"
|
|
999
|
+
write_stamp(stamp_dir, "key1")
|
|
1000
|
+
backend.provision("make warm-cache", "key1", str(stamp_dir),
|
|
1001
|
+
force=False, setup_probe="file .warmed | grep -q ELF")
|
|
1002
|
+
assert [c for c in runner.calls if c[1] == "run"]
|
|
1003
|
+
assert read_stamp(stamp_dir, "key1")
|
|
1004
|
+
|
|
1005
|
+
def test_probe_run_failure_reprovisions(self, tmp_path):
|
|
1006
|
+
# A probe that cannot run (exec failure) is treated as a broken artifact.
|
|
1007
|
+
runner = FakeRunner(fail(125, "no such container"), ok(""))
|
|
1008
|
+
backend = _backend(tmp_path, runner)
|
|
1009
|
+
stamp_dir = tmp_path / "stamps"
|
|
1010
|
+
write_stamp(stamp_dir, "key1")
|
|
1011
|
+
backend.provision("make warm-cache", "key1", str(stamp_dir),
|
|
1012
|
+
force=False, setup_probe="file .warmed | grep -q ELF")
|
|
1013
|
+
assert [c for c in runner.calls if c[1] == "run"]
|
|
1014
|
+
assert read_stamp(stamp_dir, "key1")
|
|
1015
|
+
|
|
1016
|
+
|
|
974
1017
|
class TestStartupSandbox:
|
|
975
1018
|
"""Eager startup sequence (Q2): probe → image → create/start → provision
|
|
976
1019
|
all declared setups - with zero infra operations left for the run phase."""
|
|
@@ -2298,14 +2341,18 @@ def test_declared_setups_includes_agent_steps(tmp_path):
|
|
|
2298
2341
|
{"name": "dev", "agent": "dev",
|
|
2299
2342
|
"setup": "cd frontend-editor && npm ci", "on_pass": "success"},
|
|
2300
2343
|
{"name": "t", "command": "make", "setup": "echo warm",
|
|
2344
|
+
"setup_probe": "file .warmed | grep -q ELF",
|
|
2301
2345
|
"on_pass": "success"},
|
|
2302
2346
|
],
|
|
2303
2347
|
}
|
|
2304
2348
|
p = tmp_path / "pipeline.yaml"
|
|
2305
2349
|
p.write_text(yaml.safe_dump(data))
|
|
2306
2350
|
cfg = load_config(p)
|
|
2307
|
-
|
|
2351
|
+
declared = list(_declared_setups(cfg, tmp_path))
|
|
2352
|
+
assert {s for s, _k, _p in declared} == {
|
|
2308
2353
|
"cd frontend-editor && npm ci", "echo warm"}
|
|
2354
|
+
assert {p for _s, _k, p in declared} == {
|
|
2355
|
+
None, "file .warmed | grep -q ELF"}
|
|
2309
2356
|
|
|
2310
2357
|
|
|
2311
2358
|
def test_provision_payload_mounts_declared_caches():
|
|
@@ -224,6 +224,25 @@ class TestVenvSelfHealE2E:
|
|
|
224
224
|
assert read_stamp(stamp_dir, key)
|
|
225
225
|
|
|
226
226
|
|
|
227
|
+
class TestSetupProbeE2E:
|
|
228
|
+
"""045 repro: a warm stamp plus a consumer-authored probe that fails inside
|
|
229
|
+
the container must re-provision (remove-free - the setup hook re-warms)
|
|
230
|
+
instead of blind-skipping a poisoned artifact."""
|
|
231
|
+
|
|
232
|
+
def test_consumer_probe_failure_reprovisions(self, backend, tmp_path):
|
|
233
|
+
b, ws = backend
|
|
234
|
+
(ws / "warmed").write_text("poisoned")
|
|
235
|
+
stamp_dir = tmp_path / "stamps"
|
|
236
|
+
key = "warm-probe"
|
|
237
|
+
write_stamp(stamp_dir, key)
|
|
238
|
+
|
|
239
|
+
b.provision("printf fresh > warmed", key, str(stamp_dir),
|
|
240
|
+
force=False, setup_probe="grep -q fresh warmed")
|
|
241
|
+
|
|
242
|
+
assert (ws / "warmed").read_text() == "fresh"
|
|
243
|
+
assert read_stamp(stamp_dir, key)
|
|
244
|
+
|
|
245
|
+
|
|
227
246
|
class TestRelocatedVenvE2E:
|
|
228
247
|
"""022: `sandbox_venv_dir` relocates the sandbox venv outside the workspace.
|
|
229
248
|
The outside dir is bind-mounted at `{ws}/.venv-sandbox` in BOTH containers,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/results.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/core/verdict.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/actions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/agents.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/allowlist.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/containers.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/dryrun.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/egress_policy.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/egress_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/forbidden.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/graph.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/hostboundary.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/loaders.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/mounts.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/outputs.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/presets.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/secrets.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/stages.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/engine/sync_back.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/viz/__init__.py
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop/viz/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/requires.txt
RENAMED
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/src/agentic_dev_loop.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentic_dev_loop-1.0.0a43 → agentic_dev_loop-1.0.0a44}/tests/test_hostboundary_integration.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|