agentic-dev-loop 0.15.2__py3-none-any.whl → 0.15.4__py3-none-any.whl

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.
@@ -11,11 +11,26 @@ and file-agnostic.
11
11
  from __future__ import annotations
12
12
 
13
13
  import re
14
+ import unicodedata
15
+
16
+
17
+ _GLYPH_FOLD = str.maketrans({
18
+ "\u2010": "-", "\u2011": "-", "\u2012": "-",
19
+ "\u2013": "-", "\u2014": "-", "\u2015": "-",
20
+ "\u2018": "'", "\u2019": "'",
21
+ "\u201c": '"', "\u201d": '"',
22
+ })
23
+
24
+
25
+ def _fold_glyphs(text: str) -> str:
26
+ """Fold compatibility glyphs and collapse whitespace, preserving case."""
27
+ folded = unicodedata.normalize("NFKC", text).translate(_GLYPH_FOLD)
28
+ return " ".join(folded.split())
14
29
 
15
30
 
16
31
  def _normalize(text: str) -> str:
17
- """Case-fold and collapse whitespace - the matching normal form."""
18
- return " ".join(text.split()).casefold()
32
+ """Case-folded, glyph-folded, whitespace-collapsed matching form."""
33
+ return _fold_glyphs(text).casefold()
19
34
 
20
35
 
21
36
  def _normalize_line(line: str) -> str:
@@ -23,7 +38,7 @@ def _normalize_line(line: str) -> str:
23
38
 
24
39
  A compliant digest writes anchors as headings on their own line, wrapped
25
40
  in ``**`` and (for the four section headings) prefixed with ``##``. List
26
- bullets are tolerated; the whole-line comparison is what makes a mid-line
41
+ bullets are tolerated; the line-prefix comparison is what makes a mid-line
27
42
  prose mention fail."""
28
43
  stripped = line.strip().replace("**", "")
29
44
  stripped = re.sub(r"^#+\s*", "", stripped)
@@ -67,19 +82,27 @@ def missing_anchors(brief_text: str, anchors: list[str],
67
82
  """Return every required item the captured brief fails to cover.
68
83
 
69
84
  Required section headings (from the configured ``brief_inputs.sections``)
70
- and anchors are matched line-anchored (case-insensitive,
71
- whitespace-collapsed, ``**`` decoration stripped). Env facts are matched
72
- verbatim, exactly as handed to the brief agent. An empty list means the
85
+ and anchors are matched as line prefixes (case-insensitive,
86
+ glyph-folded, whitespace-collapsed, decoration stripped): an item is
87
+ covered when it starts a line, so content may follow on that line. A
88
+ mid-line mention does not count. Env facts are matched as glyph-folded,
89
+ whitespace-collapsed, case-sensitive substrings. An empty list means the
73
90
  digest passes coverage."""
74
91
  missing: list[str] = []
75
- lines = {_normalize_line(line) for line in brief_text.splitlines()}
92
+ lines = [_normalize_line(line) for line in brief_text.splitlines()]
93
+ lines = [line for line in lines if line]
94
+
95
+ def covered(item: str) -> bool:
96
+ return any(line.startswith(item) for line in lines)
97
+
76
98
  for section in sections:
77
- if _normalize(section) not in lines:
99
+ if not covered(_normalize(section)):
78
100
  missing.append(section)
79
101
  for anchor in anchors:
80
- if anchor not in lines:
102
+ if not covered(anchor):
81
103
  missing.append(anchor)
104
+ folded_brief = _fold_glyphs(brief_text)
82
105
  for fact in env_facts:
83
- if fact not in brief_text:
106
+ if _fold_glyphs(fact) not in folded_brief:
84
107
  missing.append(fact)
85
108
  return missing
@@ -26,21 +26,24 @@ a compact brief that the dev and review agents reuse on every attempt.
26
26
  attempt. Do not invent them and do not re-derive them.
27
27
  - The repo-rules digest is structural: one line per top-level section of each
28
28
  repo-rule input. Markdown anchors are the `##` headings: keep each heading
29
- verbatim, then compress that section's rules into the one line that follows
30
- it. YAML anchors are each `- name:` entry under `categories:` plus the
29
+ verbatim at the start of its own line, then compress that section's rules
30
+ into the text that follows on that line (after a colon) or on the next
31
+ line. YAML anchors are each `- name:` entry under `categories:` plus the
31
32
  `enforcement_levels` preamble: keep each name verbatim. No top-level section
32
33
  or category may be dropped; merging is allowed only between a section and
33
- its own subsections. The anchor line holds the heading and nothing else; the
34
- compressed rules go on the following line:
34
+ its own subsections:
35
35
 
36
36
  - general_principles
37
37
  Simplest thing that works; defer decisions to the last responsible moment.
38
38
 
39
- A line like `general_principles: Simplest thing that works; ...` does not
40
- satisfy coverage; the heading must be the only text on its line.
39
+ A line like `general_principles: Simplest thing that works; ...` passes too:
40
+ the heading must start its line.
41
41
  - Keep the environment-facts entries verbatim: each fact and the behavior it
42
42
  implies travel together.
43
- - ASCII only. Do not use em dash characters.
43
+ - ASCII only, except that a required heading, env fact, or configured section
44
+ heading must be reproduced character-exact: when such an item contains a
45
+ non-ASCII character, keep that exact character, never transliterate it. Do
46
+ not introduce em dashes or other non-ASCII characters of your own.
44
47
  - If the task message carries a `## Coverage retry` section, regenerate the
45
48
  brief so every listed item appears, verbatim where the contract requires
46
49
  verbatim. No exploration: the listed items are the only correction signal.
@@ -15,7 +15,7 @@ from __future__ import annotations
15
15
  from importlib.resources import files
16
16
 
17
17
  BRIEF_PROMPT_RESOURCE = "brief_prompt.md"
18
- BRIEF_PROMPT_VERSION = "1"
18
+ BRIEF_PROMPT_VERSION = "2"
19
19
 
20
20
 
21
21
  def load_template() -> str:
@@ -36,9 +36,9 @@ def _output_block(sections: list[str]) -> str:
36
36
  "compressed content for that section (write `None` when a section has "
37
37
  "nothing):\n\n"
38
38
  f"{headings}\n\n"
39
- f"The section headings must appear exactly as written above: {names}. "
40
- "Do not shorten or rename them, and do not merge a section with "
41
- "another section."
39
+ "The section headings must appear at the start of their own lines, as "
40
+ f"written above: {names}. Do not shorten or rename them, and do not "
41
+ "merge a section with another section."
42
42
  )
43
43
 
44
44
 
@@ -10,10 +10,11 @@ opinionated LangGraph + OpenHands SDK pipeline engine written in Python.
10
10
  ## WORKFLOW
11
11
 
12
12
  1. Read the plan documents and README.md in the context below.
13
- 2. If the context contains a *Previous attempt feedback* section, treat every
14
- issue listed there as a required fix from the pipeline's earlier stages -
15
- address each one before anything else, and verify each fix landed (re-read
16
- the edited file) before moving on.
13
+ 2. If the context contains a *Previous attempt feedback* section or a
14
+ *Mechanical gate output* section, treat every issue listed there as a
15
+ required fix from the pipeline's earlier stages - address each one before
16
+ anything else, and verify each fix landed (re-read the edited file) before
17
+ moving on. A mechanical entry names the attempt that produced it.
17
18
  3. Check for prior-attempt residue before implementing. The pipeline syncs
18
19
  every lane's edits back into this repo's working tree after each terminal
19
20
  action, so an earlier attempt that died at a later gate can leave the full
@@ -66,10 +67,23 @@ opinionated LangGraph + OpenHands SDK pipeline engine written in Python.
66
67
  exit code alone.
67
68
  - You do not need to prove anything here: fill in the verification matrix,
68
69
  write your final response, and stop.
69
- - If the gate fails, you are retried with the failing output in your notes
70
- under *Previous attempt feedback*. Find the root cause - a regression in
71
- your change, a stale test pinning old behavior, or a genuine library bug -
72
- and fix it, never the symptom.
70
+ - If the gate fails, you are retried with the failing output under
71
+ *Mechanical gate output* and the exit code under *Verified state* (command
72
+ gates do not write *Previous attempt feedback*). Find the root cause - a
73
+ regression in your change, a stale test pinning old behavior, or a genuine
74
+ library bug - and fix it, never the symptom.
75
+
76
+ ## AUDIT GATE
77
+
78
+ - The restore step re-runs the install and a dependency audit on every pass.
79
+ A failing audit means a pin in the dependency manifest carries known CVEs;
80
+ it is a required fix, never "pre-existing" or "unrelated".
81
+ - Fix it with a manifest edit, never a package-manager command (agent steps
82
+ are offline). Follow the floor-pin pattern: pin the vulnerable resolved
83
+ package to a CVE-free version, or pin the intermediate package to the last
84
+ release that admits it, so a fresh resolve lands on the fixed version.
85
+ - Restore re-audits the bumped pins on the next pass, so the loop
86
+ self-corrects; report the audit as fixed only after that gate passes.
73
87
 
74
88
  ## TEST-FIRST
75
89
 
@@ -81,7 +95,8 @@ opinionated LangGraph + OpenHands SDK pipeline engine written in Python.
81
95
 
82
96
  - The moment your final response is written AND the full suite has passed
83
97
  locally (`.venv-sandbox/bin/python -m pytest tests/ -q` exits 0) AND every
84
- *Previous attempt feedback* item is addressed, the step is COMPLETE. Stop.
98
+ *Previous attempt feedback* and *Mechanical gate output* item is addressed,
99
+ the step is COMPLETE. Stop.
85
100
  - Do not re-read files, re-run checks, or `cat`/`tail`/`grep` files you just
86
101
  wrote to confirm what already passed. A green suite is a green suite:
87
102
  re-running it buys nothing, since the mechanical test gate re-runs the
@@ -446,7 +446,10 @@ def build_context(runtime: Runtime, docs: dict[str, str], subject: str,
446
446
  own mechanical section (process fact, never an agent claim), and only
447
447
  engine-verified facts (command exit codes, brief coverage, commit outcome)
448
448
  appear in the verified section. Nothing in the claims section is ever
449
- routed on."""
449
+ routed on. The mechanical heading binds every failure it carries as a
450
+ required fix: command gates (test, restore) never write the notes channel,
451
+ so without that binding a retried dev agent has no required-fix trigger
452
+ (plan 065)."""
450
453
  parts = [f"You are working on {subject}.\n", guide_section()]
451
454
  for name, content in docs.items():
452
455
  parts.append(f"## {name}\n\n{content}")
@@ -470,7 +473,8 @@ def build_context(runtime: Runtime, docs: dict[str, str], subject: str,
470
473
  parts.extend(claims)
471
474
  if mechanical_notes:
472
475
  parts.append("## Mechanical gate output "
473
- "(pipeline-captured process output, not agent claims)")
476
+ "(pipeline-captured process output, not agent claims; "
477
+ "each failure here is a required fix)")
474
478
  parts.extend(mechanical_notes)
475
479
  verified_lines = [f"- {name}: {fact}" for name, fact in (verified or {}).items()]
476
480
  parts.append(
@@ -678,16 +682,11 @@ def build_brief_context(runtime: Runtime, step: StepConfig,
678
682
  parts.append(
679
683
  "## Coverage retry\n\n"
680
684
  "The captured brief is missing the following required items. "
681
- "Regenerate the digest so every listed item occupies its own "
682
- "line, exactly as listed: the item alone on the line, no "
683
- "trailing colon and no content. Put the compressed content on "
684
- "the following line:\n\n"
685
+ "Regenerate the digest so every listed item starts its own line; "
686
+ "compressed content may follow on that line or the next line.\n\n"
685
687
  "- general_principles\n"
686
688
  " Simplest thing that works; defer decisions to the last "
687
689
  "responsible moment.\n\n"
688
- "A line like `general_principles: Simplest thing that works; "
689
- "...` does not satisfy coverage; the item must be the only "
690
- "text on its line:\n\n"
691
690
  + coverage_note)
692
691
  return "\n\n".join(parts)
693
692
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-dev-loop
3
- Version: 0.15.2
3
+ Version: 0.15.4
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
@@ -29,6 +29,8 @@ Requires-Dist: pydantic>=2.0
29
29
  Requires-Dist: PyYAML>=6.0
30
30
  Requires-Dist: openhands-sdk~=1.17.0
31
31
  Requires-Dist: openhands-tools~=1.17.0
32
+ Requires-Dist: browser-use==0.11.13
33
+ Requires-Dist: pillow>=13.0.0
32
34
  Requires-Dist: agent-client-protocol<1.0.0
33
35
  Requires-Dist: langgraph>=1.2
34
36
  Requires-Dist: langgraph-checkpoint-sqlite>=3.0
@@ -560,10 +562,11 @@ Conventions that make a good agent file:
560
562
  - **dev enumerates its impossible set and stops at it.** List the commands
561
563
  the sandbox cannot run (offline package fetches, browser downloads), one
562
564
  attempt per command - keeps the productive loop and kills doomed retries.
563
- - **Failure output returns as *Previous attempt feedback*.** Steps route
564
- `on_fail: { goto: dev, retry: true }`, so gate failure output lands in the
565
- dev agent's next context; the dev prompt makes every listed issue a
566
- required fix.
565
+ - **Failure output is a required fix in dev's next context.** Agent verdict
566
+ steps land their failing output under *Previous attempt feedback*; command
567
+ gates (test, restore) land it under *Mechanical gate output*, with the exit
568
+ code under *Verified state*. Steps route `on_fail: { goto: dev, retry: true }`,
569
+ and the dev prompt makes every listed failure a required fix.
567
570
  - **No git mutations, ever.** The `commit` step is the single commit point.
568
571
 
569
572
  ### The pipeline file
@@ -15,9 +15,9 @@ agentic_dev_loop/engine/agent_error_guide.md,sha256=PL8WbDWs6W0TUf0AMz1a6Dh0YbfV
15
15
  agentic_dev_loop/engine/agent_error_guide.py,sha256=RI64yC9kzlYabKSj78F2gY_qmYjnZlCi-3ImQBZEJu4,798
16
16
  agentic_dev_loop/engine/agents.py,sha256=I5WbVB-mCteY-JOqPY2tGnlqP5WSpCb7SNqYKegU_jY,13707
17
17
  agentic_dev_loop/engine/allowlist.py,sha256=IrtWNyNNojMXGPDUJCLLcdlWm4Fn6yNwD4VvlMme6wM,2453
18
- agentic_dev_loop/engine/brief_coverage.py,sha256=7wL8aP1_dBl62hh0IyZ60bpN4-hfOOsQDKURmeLi8uo,3356
19
- agentic_dev_loop/engine/brief_prompt.md,sha256=0MmA4xNdeJCtjoZxjE3iNm5J_k_uCqrajOXxR5hyzYM,2464
20
- agentic_dev_loop/engine/brief_prompt.py,sha256=nHkGFdyWkZcZyDEZHAsLIO3pUzpw2nsCHk8QYwqH8J4,1904
18
+ agentic_dev_loop/engine/brief_coverage.py,sha256=E76bIX6CVWBd41-vgAeU5DYBLwybmpBFMxX6AhKFe-4,4150
19
+ agentic_dev_loop/engine/brief_prompt.md,sha256=s69XFuxN94bzDo_YADE0sT3sYI9bYIpzrkbByyIq2PE,2661
20
+ agentic_dev_loop/engine/brief_prompt.py,sha256=NlGUnBI2gk37YisxnVr7P0WcuCq1bMM7zcROQV76UKI,1929
21
21
  agentic_dev_loop/engine/cli.py,sha256=1W20QTPEzyJCnGnE58_pa4TiZeJPAb4YX38WR3odNW8,20276
22
22
  agentic_dev_loop/engine/completion.py,sha256=JguMetYsXKm02Rmq0o360oNt4ElXlKXzFILhv-P7xg8,1939
23
23
  agentic_dev_loop/engine/containers.py,sha256=r2iVEzEkF422OTZEUFnROT92b2HLRfrMtvfDbYPGcvY,3541
@@ -41,7 +41,7 @@ agentic_dev_loop/engine/registry.py,sha256=qepez9iU-dBE-yG67qI7Q7Ol89NsERHy34eoQ
41
41
  agentic_dev_loop/engine/run_status.py,sha256=d_jw_3hT1oEqG8j5hDfq7XT71hZyoKhgJDjNYd88ZIE,12144
42
42
  agentic_dev_loop/engine/run_status_command.md,sha256=PhusrtsgcCGHTIEb7xQvcK46i-NdZIHi_zEG-MbsGx0,1357
43
43
  agentic_dev_loop/engine/secrets.py,sha256=xdxw-JwmgDkiLdJkBchym9UADZiQ5M4NagmdJhToQYI,8386
44
- agentic_dev_loop/engine/stages.py,sha256=_g5K7jg53UK2i1KeE2uM8dau-siyILaWK-vhrE2nvGY,63319
44
+ agentic_dev_loop/engine/stages.py,sha256=8oi1RKfFQznbflJrW5GXwd8AQOL47qzDlvVvZgX-cuE,63324
45
45
  agentic_dev_loop/engine/step_workspace.py,sha256=YyaZWk8ozc_v7AFBt63QAn4VFL-a1wvhzNvnbascYYw,7937
46
46
  agentic_dev_loop/engine/sync_back.py,sha256=Mc6YAAhc_Iydz-7Ht3OkyGekaa6s1HitCsOlIepDfOc,13942
47
47
  agentic_dev_loop/engine/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -55,7 +55,7 @@ agentic_dev_loop/engine/onboarding/templates.py,sha256=4vlu4SXY2I5BJboHnmdCHxW-l
55
55
  agentic_dev_loop/engine/onboarding/upgrade.py,sha256=5WiqLlef30yg78XRYZQWyHcYIIsCNRoxbv_UL2bnAxY,2701
56
56
  agentic_dev_loop/engine/onboarding/recipes/dotnet-node.yaml,sha256=Uqq__RbPbZ3NuNtW04-CAdY-mARP3oQHzt2byTZMz_g,3743
57
57
  agentic_dev_loop/engine/onboarding/recipes/python.yaml,sha256=42nvggXaQ4rrPAlNjrYnLeD6czuwchYVRnrFziefRKA,2491
58
- agentic_dev_loop/engine/onboarding/templates/dev.agent.md,sha256=GdbENdOrWSJ8FKWqf97Z9JsjnEZuC9p0RHTNR6Cmuk8,6460
58
+ agentic_dev_loop/engine/onboarding/templates/dev.agent.md,sha256=37sjR-WXdTZWQ6baPN4Tn_DjDWWPuTurg3-fxZm20rU,7339
59
59
  agentic_dev_loop/engine/onboarding/templates/env.example,sha256=o_BzGtkXsRKjiKeWpsz4mVqPnHstETBF-PsvwJawN8g,532
60
60
  agentic_dev_loop/engine/onboarding/templates/qa.agent.md,sha256=uA_7xJZKsW3L5UFvvR-u2lHfiXcRgvhnTdFaUjmAVZE,10389
61
61
  agentic_dev_loop/engine/onboarding/templates/review.agent.md,sha256=_88SgIlXLSDowpRloZl985zq1v4O9b7NaUXEt1-9u-U,2069
@@ -70,9 +70,9 @@ agentic_dev_loop/viz/layout.py,sha256=yXBT96rRGgrNm1aumZ6A8SZCeqiT6akknc1GMJn2Hh
70
70
  agentic_dev_loop/viz/page.py,sha256=JkiJS6ixCfRLG6EVS2Av2aEU8YfSF20QViSvzYdx1sk,1278233
71
71
  agentic_dev_loop/viz/runs_view.py,sha256=EaXHvH1y7TL4fcXzj7X913ckdTPv6R21HD1ivGwKE0c,6780
72
72
  agentic_dev_loop/viz/server.py,sha256=X65h6gyx8NCtl32_NGN_01zsTjoF3Nzd5Y3NdfxjhjY,23371
73
- agentic_dev_loop-0.15.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
74
- agentic_dev_loop-0.15.2.dist-info/METADATA,sha256=mKBN26e7EpAdaWoKqaRtnorKJauckML4-Hhm4eHKmaU,33600
75
- agentic_dev_loop-0.15.2.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
76
- agentic_dev_loop-0.15.2.dist-info/entry_points.txt,sha256=Us59oHX6NBy-7ZN3LW5w3HAjnvOPJBfgNiv6ftnmHWw,70
77
- agentic_dev_loop-0.15.2.dist-info/top_level.txt,sha256=n1ktP1gXxGN6DJAnACR0mt276GRJB9PVDezf9DVPwaw,17
78
- agentic_dev_loop-0.15.2.dist-info/RECORD,,
73
+ agentic_dev_loop-0.15.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
74
+ agentic_dev_loop-0.15.4.dist-info/METADATA,sha256=0wuRMEsNVFQ_DKhHGvu1_v7fBzDNthBz-4Hut37P7bA,33807
75
+ agentic_dev_loop-0.15.4.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
76
+ agentic_dev_loop-0.15.4.dist-info/entry_points.txt,sha256=Us59oHX6NBy-7ZN3LW5w3HAjnvOPJBfgNiv6ftnmHWw,70
77
+ agentic_dev_loop-0.15.4.dist-info/top_level.txt,sha256=n1ktP1gXxGN6DJAnACR0mt276GRJB9PVDezf9DVPwaw,17
78
+ agentic_dev_loop-0.15.4.dist-info/RECORD,,