agentforge-framework 0.2.4__tar.gz → 0.2.5__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.
Files changed (119) hide show
  1. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/PKG-INFO +25 -11
  2. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/README.md +24 -10
  3. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/pyproject.toml +1 -1
  4. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/__init__.py +1 -1
  5. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/cli.py +11 -6
  6. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/gates.py +35 -3
  7. agentforge_framework-0.2.5/src/agentforge_framework/core/project.py +445 -0
  8. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/repo.py +14 -0
  9. agentforge_framework-0.2.5/src/agentforge_framework/core/runlock.py +223 -0
  10. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/runtime.py +54 -0
  11. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework.egg-info/PKG-INFO +25 -11
  12. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework.egg-info/SOURCES.txt +2 -0
  13. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_cli.py +25 -0
  14. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_gates.py +74 -0
  15. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_project.py +183 -1
  16. agentforge_framework-0.2.5/tests/test_runlock.py +189 -0
  17. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_runtime.py +27 -2
  18. agentforge_framework-0.2.4/src/agentforge_framework/core/project.py +0 -270
  19. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/LICENSE +0 -0
  20. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/setup.cfg +0 -0
  21. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/.claude-plugin/plugin.json +0 -0
  22. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/__init__.py +0 -0
  23. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/architect.py +0 -0
  24. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/decomposer.py +0 -0
  25. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/implementer.py +0 -0
  26. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/orchestrator.py +0 -0
  27. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/reviewer.py +0 -0
  28. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/security.py +0 -0
  29. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/agents/tester.py +0 -0
  30. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/__init__.py +0 -0
  31. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/extractors/__init__.py +0 -0
  32. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/extractors/base.py +0 -0
  33. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/extractors/python.py +0 -0
  34. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/extractors/sql.py +0 -0
  35. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/extractors/yaml.py +0 -0
  36. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/prompt.py +0 -0
  37. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/context/resolver.py +0 -0
  38. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/__init__.py +0 -0
  39. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/commands.py +0 -0
  40. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/config.py +0 -0
  41. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/contracts.py +0 -0
  42. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/issues.py +0 -0
  43. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/plan_format.py +0 -0
  44. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/process.py +0 -0
  45. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/registry.py +0 -0
  46. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/router.py +0 -0
  47. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/skills.py +0 -0
  48. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/core/workflow.py +0 -0
  49. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/plugins/__init__.py +0 -0
  50. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/plugins/databricks/__init__.py +0 -0
  51. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/plugins/pyspark/__init__.py +0 -0
  52. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/plugins/python/__init__.py +0 -0
  53. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/plugins/sql/__init__.py +0 -0
  54. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/providers/__init__.py +0 -0
  55. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/providers/base.py +0 -0
  56. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/providers/claude.py +0 -0
  57. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/providers/codex.py +0 -0
  58. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/MANIFEST.yaml +0 -0
  59. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/NOTICE +0 -0
  60. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/domain-modeling/ADR-FORMAT.md +0 -0
  61. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/domain-modeling/CONTEXT-FORMAT.md +0 -0
  62. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/domain-modeling/SKILL.md +0 -0
  63. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/domain-modeling/agents/openai.yaml +0 -0
  64. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/grill-with-docs/SKILL.md +0 -0
  65. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/grilling/SKILL.md +0 -0
  66. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/grilling/agents/openai.yaml +0 -0
  67. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/to-spec/SKILL.md +0 -0
  68. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/to-spec/agents/openai.yaml +0 -0
  69. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/to-tickets/SKILL.md +0 -0
  70. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/to-tickets/agents/openai.yaml +0 -0
  71. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/SKILL.md +0 -0
  72. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/evals/fixtures/silhouette/human_reference.json +0 -0
  73. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/_lang.py +0 -0
  74. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/banned_phrase_scan.py +0 -0
  75. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/calibrate_pairs.py +0 -0
  76. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/calibrate_score.py +0 -0
  77. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/check_packs.py +0 -0
  78. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/check_suggestions.py +0 -0
  79. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/contribute.py +0 -0
  80. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/diff_check.py +0 -0
  81. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/extract_constraints.py +0 -0
  82. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/harvest_classify.py +0 -0
  83. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/harvest_samples.py +0 -0
  84. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/readability_metrics.py +0 -0
  85. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/refresh_status.py +0 -0
  86. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/silhouette_scan.py +0 -0
  87. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/structure_scan.py +0 -0
  88. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/suggest.py +0 -0
  89. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/validate_preservation.py +0 -0
  90. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/voice_card.py +0 -0
  91. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/voice_profile.py +0 -0
  92. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/voice_score.py +0 -0
  93. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/unslop/scripts/wiki_sync.py +0 -0
  94. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/skills/write-plainly/SKILL.md +0 -0
  95. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/workflows/bugfix.yaml +0 -0
  96. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/workflows/feature.yaml +0 -0
  97. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework/workflows/review.yaml +0 -0
  98. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework.egg-info/dependency_links.txt +0 -0
  99. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework.egg-info/entry_points.txt +0 -0
  100. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework.egg-info/requires.txt +0 -0
  101. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/src/agentforge_framework.egg-info/top_level.txt +0 -0
  102. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_agents.py +0 -0
  103. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_architect.py +0 -0
  104. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_config.py +0 -0
  105. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_context.py +0 -0
  106. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_contracts.py +0 -0
  107. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_decompose.py +0 -0
  108. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_docs.py +0 -0
  109. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_execution_posture.py +0 -0
  110. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_interview.py +0 -0
  111. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_issues.py +0 -0
  112. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_plan_format.py +0 -0
  113. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_plugins.py +0 -0
  114. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_providers.py +0 -0
  115. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_reviewer.py +0 -0
  116. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_security.py +0 -0
  117. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_skills.py +0 -0
  118. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_tester.py +0 -0
  119. {agentforge_framework-0.2.4 → agentforge_framework-0.2.5}/tests/test_workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentforge-framework
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: A modular framework for coordinating specialized software agents.
5
5
  Author: Yash Mhatre
6
6
  License-Expression: Apache-2.0
@@ -26,9 +26,9 @@ A human states a Task. The Orchestrator grills them on it, writes a spec, cuts t
26
26
 
27
27
  ## Status
28
28
 
29
- The Workflow runtime now runs multiple Roles in order. The default `feature`
30
- Workflow invokes the Implementer, the Tester, Security, and then the Reviewer,
31
- posting each Agent Result to the Issue before starting the next Step.
29
+ A Workflow runs its Roles in order. The default `feature` Workflow invokes the
30
+ Implementer, the Tester, Security, and then the Reviewer, posting each Agent
31
+ Result to the Issue before starting the next Step.
32
32
 
33
33
  ```console
34
34
  $ agentforge plan "add a retry to the loader"
@@ -104,13 +104,15 @@ cannot hold it — `codex exec` discards its approval flag — so a denied Run o
104
104
  `codex` is refused before it starts rather than run under a guarantee nothing
105
105
  enforces (ADR-0007, amended). Security, the Reviewer, and the Architect need no such flag —
106
106
  auditing, reviewing, and designing are reading. All six Roles `CONTEXT.md` names
107
- now run. Plugins have landed, and with them the data-engineering ones: a
107
+ run. Plugins carry the data-engineering conventions: a
108
108
  repository whose files import `pyspark` has its Roles told to write DataFrame
109
109
  expressions rather than RDDs, and a Databricks workspace gets Unity Catalog
110
110
  naming and the Delta MERGE idioms in the prompts that write code and the
111
111
  workspace's own posture in the one that audits it. A repository that matches
112
- neither hears neither. `agentforge init` is still to come. See
113
- [`docs/PLAN.md`](docs/PLAN.md).
112
+ neither hears neither. `agentforge init` inspects a repository and writes its
113
+ `.agentforge/config.yaml`; the file is optional, and
114
+ [Project configuration](#project-configuration) below says what it holds. What
115
+ is still to come is in [`docs/PLAN.md`](docs/PLAN.md).
114
116
 
115
117
  Before the first Role is invoked, AgentForge resolves a Context Pack from the
116
118
  frozen plan — the files it names, the symbols and imports inside them, the
@@ -148,7 +150,7 @@ without notice. See
148
150
  ```console
149
151
  $ pip install agentforge-framework
150
152
  $ agentforge --version
151
- agentforge 0.2.4
153
+ agentforge 0.2.5
152
154
  ```
153
155
 
154
156
  That is the whole of it. The two routes below the naming note are for a machine
@@ -172,7 +174,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
172
174
  and install the file:
173
175
 
174
176
  ```console
175
- $ pip install agentforge_framework-0.2.4-py3-none-any.whl
177
+ $ pip install agentforge_framework-0.2.5-py3-none-any.whl
176
178
  ```
177
179
 
178
180
  Publishing a release is a wizard rather than a checklist:
@@ -209,7 +211,7 @@ AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE
209
211
 
210
212
  **Antigravity IDE cannot be a Provider** ([#101](https://github.com/yashmhatre/AgentForge/issues/101)). Checked by running it, so nobody has to derive it again: `antigravity-ide chat` accepts a prompt and reads stdin, which is the right shape for the argument vector — but it is a window launcher. Piping a prompt to it returns exit 0 immediately with `Reading from stdin via: …\code-stdin-XXXX` on stdout and nothing else, ever; the answer goes to a GUI session. A Provider adapter has to read the Agent's result off stdout, so there is nothing for `parse_output` to parse. The same is true of any editor CLI of this shape.
211
213
 
212
- **Do not run another agent against a checkout while a Run is going.** A Run commits every change to a file git already tracks, however it got there ([ADR-0015](docs/adr/0015-a-run-commits-what-it-declared.md)) — so a second agent's half-finished edits are committed into the Run's branch and attributed to a Role. Files nothing in the Run claimed are listed in the pull request body ([ADR-0023](docs/adr/0023-a-commit-names-what-nothing-claimed.md)), which makes it visible at Sign-off but does not prevent it. Use a separate clone.
214
+ **Do not run another agent against a checkout while a Run is going.** A Run commits every change to a file git already tracks, however it got there ([ADR-0015](docs/adr/0015-a-run-commits-what-it-declared.md)) — so a second agent's half-finished edits are committed into the Run's branch and attributed to a Role. Files nothing in the Run claimed are listed in the pull request body ([ADR-0023](docs/adr/0023-a-commit-names-what-nothing-claimed.md)), which makes it visible at Sign-off but does not prevent it. Use a separate clone. A second `agentforge implement` is refused outright rather than merely disclosed — a Run holds its checkout for the duration ([ADR-0026](docs/adr/0026-a-run-holds-the-checkout-it-was-pointed-at.md)) — but nothing else consults that lock, so an IDE assistant is still yours to keep away.
213
215
 
214
216
  ## Commands
215
217
 
@@ -277,9 +279,21 @@ if the repository has no GitHub remote, because ADR-0002 makes that a
277
279
  precondition for every Run. Re-running never clobbers a config you have edited:
278
280
  it reports what differs and writes nothing, and `--force` replaces it.
279
281
 
282
+ A Python suite is written pinned to the interpreter that will run it —
283
+ `[".venv/bin/python", "-m", "pytest"]` rather than `pytest` — whenever the
284
+ repository has a virtualenv to pin it to, and init checks that the interpreter
285
+ can run pytest before writing the line. Bare `pytest` is whichever one PATH
286
+ answers with, which in a project with a venv is the one the project does not
287
+ use: the suite then fails on imports that are installed and reads as a broken
288
+ repository rather than a misconfigured one. The interpreter is named relative to
289
+ the repository root, so the file is still right on somebody else's clone, and
290
+ init looks up to two directories down for the project — a suite that lives in
291
+ `subproject/tests/` is found, and named.
292
+
280
293
  The file is not a precondition. Without one, the documented Provider capability
281
294
  defaults are Claude `native` and every other Provider `fragment`, and the
282
- `tests` Gate runs `pytest`.
295
+ `tests` Gate runs `pytest` — the case the pinning above exists to avoid, so a
296
+ repository with a venv is better off with a file.
283
297
 
284
298
  ```yaml
285
299
  providers:
@@ -8,9 +8,9 @@ A human states a Task. The Orchestrator grills them on it, writes a spec, cuts t
8
8
 
9
9
  ## Status
10
10
 
11
- The Workflow runtime now runs multiple Roles in order. The default `feature`
12
- Workflow invokes the Implementer, the Tester, Security, and then the Reviewer,
13
- posting each Agent Result to the Issue before starting the next Step.
11
+ A Workflow runs its Roles in order. The default `feature` Workflow invokes the
12
+ Implementer, the Tester, Security, and then the Reviewer, posting each Agent
13
+ Result to the Issue before starting the next Step.
14
14
 
15
15
  ```console
16
16
  $ agentforge plan "add a retry to the loader"
@@ -86,13 +86,15 @@ cannot hold it — `codex exec` discards its approval flag — so a denied Run o
86
86
  `codex` is refused before it starts rather than run under a guarantee nothing
87
87
  enforces (ADR-0007, amended). Security, the Reviewer, and the Architect need no such flag —
88
88
  auditing, reviewing, and designing are reading. All six Roles `CONTEXT.md` names
89
- now run. Plugins have landed, and with them the data-engineering ones: a
89
+ run. Plugins carry the data-engineering conventions: a
90
90
  repository whose files import `pyspark` has its Roles told to write DataFrame
91
91
  expressions rather than RDDs, and a Databricks workspace gets Unity Catalog
92
92
  naming and the Delta MERGE idioms in the prompts that write code and the
93
93
  workspace's own posture in the one that audits it. A repository that matches
94
- neither hears neither. `agentforge init` is still to come. See
95
- [`docs/PLAN.md`](docs/PLAN.md).
94
+ neither hears neither. `agentforge init` inspects a repository and writes its
95
+ `.agentforge/config.yaml`; the file is optional, and
96
+ [Project configuration](#project-configuration) below says what it holds. What
97
+ is still to come is in [`docs/PLAN.md`](docs/PLAN.md).
96
98
 
97
99
  Before the first Role is invoked, AgentForge resolves a Context Pack from the
98
100
  frozen plan — the files it names, the symbols and imports inside them, the
@@ -130,7 +132,7 @@ without notice. See
130
132
  ```console
131
133
  $ pip install agentforge-framework
132
134
  $ agentforge --version
133
- agentforge 0.2.4
135
+ agentforge 0.2.5
134
136
  ```
135
137
 
136
138
  That is the whole of it. The two routes below the naming note are for a machine
@@ -154,7 +156,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
154
156
  and install the file:
155
157
 
156
158
  ```console
157
- $ pip install agentforge_framework-0.2.4-py3-none-any.whl
159
+ $ pip install agentforge_framework-0.2.5-py3-none-any.whl
158
160
  ```
159
161
 
160
162
  Publishing a release is a wizard rather than a checklist:
@@ -191,7 +193,7 @@ AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE
191
193
 
192
194
  **Antigravity IDE cannot be a Provider** ([#101](https://github.com/yashmhatre/AgentForge/issues/101)). Checked by running it, so nobody has to derive it again: `antigravity-ide chat` accepts a prompt and reads stdin, which is the right shape for the argument vector — but it is a window launcher. Piping a prompt to it returns exit 0 immediately with `Reading from stdin via: …\code-stdin-XXXX` on stdout and nothing else, ever; the answer goes to a GUI session. A Provider adapter has to read the Agent's result off stdout, so there is nothing for `parse_output` to parse. The same is true of any editor CLI of this shape.
193
195
 
194
- **Do not run another agent against a checkout while a Run is going.** A Run commits every change to a file git already tracks, however it got there ([ADR-0015](docs/adr/0015-a-run-commits-what-it-declared.md)) — so a second agent's half-finished edits are committed into the Run's branch and attributed to a Role. Files nothing in the Run claimed are listed in the pull request body ([ADR-0023](docs/adr/0023-a-commit-names-what-nothing-claimed.md)), which makes it visible at Sign-off but does not prevent it. Use a separate clone.
196
+ **Do not run another agent against a checkout while a Run is going.** A Run commits every change to a file git already tracks, however it got there ([ADR-0015](docs/adr/0015-a-run-commits-what-it-declared.md)) — so a second agent's half-finished edits are committed into the Run's branch and attributed to a Role. Files nothing in the Run claimed are listed in the pull request body ([ADR-0023](docs/adr/0023-a-commit-names-what-nothing-claimed.md)), which makes it visible at Sign-off but does not prevent it. Use a separate clone. A second `agentforge implement` is refused outright rather than merely disclosed — a Run holds its checkout for the duration ([ADR-0026](docs/adr/0026-a-run-holds-the-checkout-it-was-pointed-at.md)) — but nothing else consults that lock, so an IDE assistant is still yours to keep away.
195
197
 
196
198
  ## Commands
197
199
 
@@ -259,9 +261,21 @@ if the repository has no GitHub remote, because ADR-0002 makes that a
259
261
  precondition for every Run. Re-running never clobbers a config you have edited:
260
262
  it reports what differs and writes nothing, and `--force` replaces it.
261
263
 
264
+ A Python suite is written pinned to the interpreter that will run it —
265
+ `[".venv/bin/python", "-m", "pytest"]` rather than `pytest` — whenever the
266
+ repository has a virtualenv to pin it to, and init checks that the interpreter
267
+ can run pytest before writing the line. Bare `pytest` is whichever one PATH
268
+ answers with, which in a project with a venv is the one the project does not
269
+ use: the suite then fails on imports that are installed and reads as a broken
270
+ repository rather than a misconfigured one. The interpreter is named relative to
271
+ the repository root, so the file is still right on somebody else's clone, and
272
+ init looks up to two directories down for the project — a suite that lives in
273
+ `subproject/tests/` is found, and named.
274
+
262
275
  The file is not a precondition. Without one, the documented Provider capability
263
276
  defaults are Claude `native` and every other Provider `fragment`, and the
264
- `tests` Gate runs `pytest`.
277
+ `tests` Gate runs `pytest` — the case the pinning above exists to avoid, so a
278
+ repository with a venv is better off with a file.
265
279
 
266
280
  ```yaml
267
281
  providers:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agentforge-framework"
7
- version = "0.2.4"
7
+ version = "0.2.5"
8
8
  description = "A modular framework for coordinating specialized software agents."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  """AgentForge — coordination of specialized software agents through reusable workflows."""
2
2
 
3
- __version__ = "0.2.4"
3
+ __version__ = "0.2.5"
@@ -512,7 +512,7 @@ def _run_init(args: argparse.Namespace, runner=None) -> int:
512
512
  from .core.config import load_config
513
513
  from .core.contracts import Plan
514
514
  from .core.process import SubprocessRunner
515
- from .core.project import config_path, detect, differences, render_config
515
+ from .core.project import config_path, detect, differences, render_config, verified
516
516
  from .core.registry import activate
517
517
  from .core.repo import PreconditionFailed, open_repository
518
518
  from .providers import DEFAULT_PROVIDER, PROVIDERS
@@ -531,11 +531,14 @@ def _run_init(args: argparse.Namespace, runner=None) -> int:
531
531
  return 2
532
532
 
533
533
  active = activate(Plan(summary=""), repo.root)
534
- context = detect(
535
- repo.root,
536
- provider,
537
- tracked=repo.tracked_files(),
538
- plugins=tuple(plugin.name for plugin in active.plugins),
534
+ context = verified(
535
+ detect(
536
+ repo.root,
537
+ provider,
538
+ tracked=repo.tracked_files(),
539
+ plugins=tuple(plugin.name for plugin in active.plugins),
540
+ ),
541
+ runner,
539
542
  )
540
543
 
541
544
  print(f"Repository: {repo.root}")
@@ -544,6 +547,8 @@ def _run_init(args: argparse.Namespace, runner=None) -> int:
544
547
  suite = " ".join(context.test_suite)
545
548
  where = context.suite_detected or "not detected, so this is the documented default"
546
549
  print(f" Suite: `{suite}` — {where}")
550
+ if context.suite_note:
551
+ print(f" {context.suite_note}")
547
552
  print(f" Plugins: {', '.join(context.plugins) or 'none by root marker'}")
548
553
  print(
549
554
  " printed, not written: which Plugins answer is decided per Run\n"
@@ -110,6 +110,11 @@ def human(context: GateContext) -> GateEntry:
110
110
  #: nothing collected; none of those say anything about the repository.
111
111
  SUITE_FAILED = 1
112
112
 
113
+ #: What `python -m` prints when the runner it was asked for is not installed in
114
+ #: that interpreter. It exits 1 doing it, which is the one status that otherwise
115
+ #: means the suite ran and reported on the code.
116
+ MODULE_MISSING = "No module named"
117
+
113
118
  #: How much of a failing suite reaches the Run Log. The end of it: a test runner
114
119
  #: puts its summary last, and a comment nobody scrolls to the bottom of is a
115
120
  #: comment nobody reads.
@@ -138,11 +143,12 @@ def tests(context: GateContext) -> GateEntry:
138
143
  runs. A Gate is not an Agent: the suite is the one the project declared, its
139
144
  exit status is read rather than interpreted, and no model chose either.
140
145
  """
141
- suite = load_config(context.root).test_suite
142
- rendered = " ".join(suite)
146
+ declared = load_config(context.root).test_suite
147
+ rendered = " ".join(declared)
148
+ suite = _resolved(declared, context.root)
143
149
 
144
150
  if not context.runner.has_binary(suite[0]):
145
- return _cannot_run(rendered, f"{suite[0]!r} is not installed or not on PATH")
151
+ return _cannot_run(rendered, f"{declared[0]!r} is not installed or not on PATH")
146
152
 
147
153
  try:
148
154
  result = context.runner.run(suite, cwd=context.root)
@@ -160,6 +166,13 @@ def tests(context: GateContext) -> GateEntry:
160
166
  summary=f"`{rendered}` passed.",
161
167
  )
162
168
 
169
+ if result.returncode == SUITE_FAILED and MODULE_MISSING in result.stderr:
170
+ # An interpreter that has no pytest exits 1, which is the status a suite
171
+ # spends on real failures. The interpreter says so on standard error and
172
+ # a failing suite reports its failures on standard output, so the two are
173
+ # told apart there rather than by the status they share.
174
+ return _cannot_run(rendered, f"{declared[0]!r} has no test runner installed")
175
+
163
176
  if result.returncode == SUITE_FAILED:
164
177
  return GateEntry(
165
178
  kind="",
@@ -181,6 +194,25 @@ def tests(context: GateContext) -> GateEntry:
181
194
  )
182
195
 
183
196
 
197
+ def _resolved(suite: tuple[str, ...], root: Path) -> tuple[str, ...]:
198
+ """A suite whose first word is a path, resolved against the repository.
199
+
200
+ `agentforge init` pins a Python suite to the project's own virtualenv and
201
+ writes that interpreter relative to the root, because the file it writes gets
202
+ committed and an absolute path is right on one machine only. Resolving it
203
+ here is what makes the relative form work at all: a relative program name is
204
+ resolved against the child process's directory on POSIX and the parent's on
205
+ Windows, so leaving it to the OS would run on one platform and not the other.
206
+
207
+ A suite naming a plain command is returned untouched, PATH lookup and all.
208
+ """
209
+ program = Path(suite[0])
210
+ if program.is_absolute() or len(program.parts) == 1:
211
+ return suite
212
+ candidate = root / program
213
+ return (str(candidate), *suite[1:]) if candidate.is_file() else suite
214
+
215
+
184
216
  def _cannot_run(rendered: str, reason: str) -> GateEntry:
185
217
  """The suite never started, which is not a report on the code.
186
218