agentforge-framework 0.2.2__tar.gz → 0.2.3__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.
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/PKG-INFO +23 -4
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/README.md +22 -3
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/pyproject.toml +1 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/__init__.py +1 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/config.py +14 -1
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/issues.py +32 -10
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/project.py +8 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/repo.py +15 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/runtime.py +46 -11
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/PKG-INFO +23 -4
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_config.py +25 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_issues.py +38 -9
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_project.py +6 -4
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_runtime.py +56 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/LICENSE +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/setup.cfg +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/.claude-plugin/plugin.json +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/architect.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/decomposer.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/implementer.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/orchestrator.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/reviewer.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/security.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/tester.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/cli.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/extractors/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/extractors/base.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/extractors/python.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/extractors/sql.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/extractors/yaml.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/prompt.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/resolver.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/commands.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/contracts.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/gates.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/plan_format.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/process.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/registry.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/router.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/skills.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/workflow.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/plugins/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/plugins/databricks/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/plugins/pyspark/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/plugins/python/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/plugins/sql/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/providers/__init__.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/providers/base.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/providers/claude.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/providers/codex.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/MANIFEST.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/NOTICE +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/domain-modeling/ADR-FORMAT.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/domain-modeling/CONTEXT-FORMAT.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/domain-modeling/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/domain-modeling/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/grill-with-docs/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/grilling/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/grilling/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/to-spec/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/to-spec/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/to-tickets/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/to-tickets/agents/openai.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/evals/fixtures/silhouette/human_reference.json +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/_lang.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/banned_phrase_scan.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/calibrate_pairs.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/calibrate_score.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/check_packs.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/check_suggestions.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/contribute.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/diff_check.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/extract_constraints.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/harvest_classify.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/harvest_samples.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/readability_metrics.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/refresh_status.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/silhouette_scan.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/structure_scan.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/suggest.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/validate_preservation.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/voice_card.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/voice_profile.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/voice_score.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/unslop/scripts/wiki_sync.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/write-plainly/SKILL.md +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/workflows/bugfix.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/workflows/feature.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/workflows/review.yaml +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/SOURCES.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/dependency_links.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/entry_points.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/requires.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/top_level.txt +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_agents.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_architect.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_cli.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_context.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_contracts.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_decompose.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_docs.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_execution_posture.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_gates.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_interview.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_plan_format.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_plugins.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_providers.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_reviewer.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_security.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_skills.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/tests/test_tester.py +0 -0
- {agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/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.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A modular framework for coordinating specialized software agents.
|
|
5
5
|
Author: Yash Mhatre
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -145,7 +145,7 @@ without notice. See
|
|
|
145
145
|
```console
|
|
146
146
|
$ pip install agentforge-framework
|
|
147
147
|
$ agentforge --version
|
|
148
|
-
agentforge 0.2.
|
|
148
|
+
agentforge 0.2.3
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
That is the whole of it. The two routes below the naming note are for a machine
|
|
@@ -169,7 +169,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
|
|
|
169
169
|
and install the file:
|
|
170
170
|
|
|
171
171
|
```console
|
|
172
|
-
$ pip install agentforge_framework-0.2.
|
|
172
|
+
$ pip install agentforge_framework-0.2.3-py3-none-any.whl
|
|
173
173
|
```
|
|
174
174
|
|
|
175
175
|
Publishing a release is a wizard rather than a checklist:
|
|
@@ -200,6 +200,14 @@ What each release contains is in [CHANGELOG.md](CHANGELOG.md).
|
|
|
200
200
|
|
|
201
201
|
AgentForge never touches a model API and handles no credentials of its own. Whatever your coding-agent CLI is already authenticated with is what a Run costs.
|
|
202
202
|
|
|
203
|
+
### Editors and IDEs
|
|
204
|
+
|
|
205
|
+
AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE is not a Provider and its assistant is not one either.
|
|
206
|
+
|
|
207
|
+
**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.
|
|
208
|
+
|
|
209
|
+
**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.
|
|
210
|
+
|
|
203
211
|
## Commands
|
|
204
212
|
|
|
205
213
|
| Command | What it does |
|
|
@@ -331,10 +339,21 @@ code rather than the verdict about the old code.
|
|
|
331
339
|
|
|
332
340
|
Read [`CONTEXT.md`](CONTEXT.md) before writing anything, and [`docs/adr/`](docs/adr/) for the decisions that constrain it.
|
|
333
341
|
|
|
334
|
-
##
|
|
342
|
+
## Changing AgentForge
|
|
343
|
+
|
|
344
|
+
Clone it and install that clone editable. Never patch the installed copy.
|
|
335
345
|
|
|
336
346
|
```console
|
|
347
|
+
$ git clone https://github.com/yashmhatre/AgentForge.git
|
|
348
|
+
$ cd AgentForge
|
|
337
349
|
$ pip install -e ".[dev]"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Editing `site-packages/agentforge_framework/` appears to work and is the one change nothing will catch: the local install diverges from what ships, so the thing that works is not the thing anybody else gets, and the fix is lost the next time the package is upgraded. If a Run is failing badly enough that patching the install looks like the way forward, that is a bug worth filing — [#101](https://github.com/yashmhatre/AgentForge/issues/101) is what happened the last time somebody reached for it.
|
|
353
|
+
|
|
354
|
+
## Tests
|
|
355
|
+
|
|
356
|
+
```console
|
|
338
357
|
$ pytest
|
|
339
358
|
```
|
|
340
359
|
|
|
@@ -127,7 +127,7 @@ without notice. See
|
|
|
127
127
|
```console
|
|
128
128
|
$ pip install agentforge-framework
|
|
129
129
|
$ agentforge --version
|
|
130
|
-
agentforge 0.2.
|
|
130
|
+
agentforge 0.2.3
|
|
131
131
|
```
|
|
132
132
|
|
|
133
133
|
That is the whole of it. The two routes below the naming note are for a machine
|
|
@@ -151,7 +151,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
|
|
|
151
151
|
and install the file:
|
|
152
152
|
|
|
153
153
|
```console
|
|
154
|
-
$ pip install agentforge_framework-0.2.
|
|
154
|
+
$ pip install agentforge_framework-0.2.3-py3-none-any.whl
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
Publishing a release is a wizard rather than a checklist:
|
|
@@ -182,6 +182,14 @@ What each release contains is in [CHANGELOG.md](CHANGELOG.md).
|
|
|
182
182
|
|
|
183
183
|
AgentForge never touches a model API and handles no credentials of its own. Whatever your coding-agent CLI is already authenticated with is what a Run costs.
|
|
184
184
|
|
|
185
|
+
### Editors and IDEs
|
|
186
|
+
|
|
187
|
+
AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE is not a Provider and its assistant is not one either.
|
|
188
|
+
|
|
189
|
+
**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.
|
|
190
|
+
|
|
191
|
+
**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.
|
|
192
|
+
|
|
185
193
|
## Commands
|
|
186
194
|
|
|
187
195
|
| Command | What it does |
|
|
@@ -313,10 +321,21 @@ code rather than the verdict about the old code.
|
|
|
313
321
|
|
|
314
322
|
Read [`CONTEXT.md`](CONTEXT.md) before writing anything, and [`docs/adr/`](docs/adr/) for the decisions that constrain it.
|
|
315
323
|
|
|
316
|
-
##
|
|
324
|
+
## Changing AgentForge
|
|
325
|
+
|
|
326
|
+
Clone it and install that clone editable. Never patch the installed copy.
|
|
317
327
|
|
|
318
328
|
```console
|
|
329
|
+
$ git clone https://github.com/yashmhatre/AgentForge.git
|
|
330
|
+
$ cd AgentForge
|
|
319
331
|
$ pip install -e ".[dev]"
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Editing `site-packages/agentforge_framework/` appears to work and is the one change nothing will catch: the local install diverges from what ships, so the thing that works is not the thing anybody else gets, and the fix is lost the next time the package is upgraded. If a Run is failing badly enough that patching the install looks like the way forward, that is a bug worth filing — [#101](https://github.com/yashmhatre/AgentForge/issues/101) is what happened the last time somebody reached for it.
|
|
335
|
+
|
|
336
|
+
## Tests
|
|
337
|
+
|
|
338
|
+
```console
|
|
320
339
|
$ pytest
|
|
321
340
|
```
|
|
322
341
|
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/config.py
RENAMED
|
@@ -41,6 +41,13 @@ class Config:
|
|
|
41
41
|
#: Command in the glossary's sense: nothing infers anything from it.
|
|
42
42
|
test_suite: tuple[str, ...] = DEFAULT_TEST_SUITE
|
|
43
43
|
|
|
44
|
+
#: Whether the Context Pack comment publishes the symbols and import graph
|
|
45
|
+
#: it resolved, or only their counts. Off by default: the Issue already
|
|
46
|
+
#: carries the pack's file paths in the frozen Plan, and nothing else on it
|
|
47
|
+
#: carries private symbol names. A tracker whose audience matches the
|
|
48
|
+
#: code's turns this on. See ADR-0024.
|
|
49
|
+
publish_pack_inventory: bool = False
|
|
50
|
+
|
|
44
51
|
def capability_for(self, provider: str) -> CapabilityTier:
|
|
45
52
|
return self.provider_capabilities.get(provider, CapabilityTier.FRAGMENT)
|
|
46
53
|
|
|
@@ -55,7 +62,13 @@ def load_config(root: Path | str) -> Config:
|
|
|
55
62
|
capabilities = dict(DEFAULT_CAPABILITIES)
|
|
56
63
|
for name, provider in (data.get("providers") or {}).items():
|
|
57
64
|
capabilities[str(name)] = CapabilityTier(provider["capability_tier"])
|
|
58
|
-
return Config(
|
|
65
|
+
return Config(
|
|
66
|
+
provider_capabilities=capabilities,
|
|
67
|
+
test_suite=_test_suite(data),
|
|
68
|
+
publish_pack_inventory=bool(
|
|
69
|
+
(data.get("context") or {}).get("publish_inventory", False)
|
|
70
|
+
),
|
|
71
|
+
)
|
|
59
72
|
|
|
60
73
|
|
|
61
74
|
def _test_suite(data: dict) -> tuple[str, ...]:
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/issues.py
RENAMED
|
@@ -381,6 +381,7 @@ def render_context_comment(
|
|
|
381
381
|
context: ContextPack,
|
|
382
382
|
plugins: Sequence[tuple[str, str]] = (),
|
|
383
383
|
skipped: Sequence[str] = (),
|
|
384
|
+
publish_inventory: bool = False,
|
|
384
385
|
) -> str:
|
|
385
386
|
"""The pack this Run's Agents were handed, recorded before the first one ran.
|
|
386
387
|
|
|
@@ -390,6 +391,14 @@ def render_context_comment(
|
|
|
390
391
|
invocation (ADR-0010), so this is a record of a Run rather than a contract
|
|
391
392
|
the next one reads back.
|
|
392
393
|
|
|
394
|
+
`publish_inventory` decides whether the resolved symbols and import graph
|
|
395
|
+
are named or only counted. Off by default: the Issue already carries this
|
|
396
|
+
pack's file paths in the frozen Plan, so the file list discloses nothing
|
|
397
|
+
new, while private symbol names and the graph between modules appear
|
|
398
|
+
nowhere else on the tracker (ADR-0024). The counts stay either way, because
|
|
399
|
+
a pack that resolved more or less than a reader expected is most of what
|
|
400
|
+
this comment is read for.
|
|
401
|
+
|
|
393
402
|
`plugins` and `skipped` are the Plugins that answered for this Run and the
|
|
394
403
|
ones that raised while being asked. Both are named for the same reason the
|
|
395
404
|
pack is: a prompt that grew, or one that did not grow when a reader expected
|
|
@@ -418,16 +427,29 @@ def render_context_comment(
|
|
|
418
427
|
lines += [f"**Files ({len(context.files)}):**", ""]
|
|
419
428
|
lines += [f"- `{path}`" for path in context.files]
|
|
420
429
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
430
|
+
inventory = (("Symbols", context.symbols), ("Reaches for", context.references))
|
|
431
|
+
if publish_inventory:
|
|
432
|
+
for label, values in inventory:
|
|
433
|
+
if values:
|
|
434
|
+
lines += [
|
|
435
|
+
"",
|
|
436
|
+
f"<details><summary>{label} ({len(values)})</summary>",
|
|
437
|
+
"",
|
|
438
|
+
", ".join(f"`{value}`" for value in values),
|
|
439
|
+
"",
|
|
440
|
+
"</details>",
|
|
441
|
+
]
|
|
442
|
+
elif any(values for _, values in inventory):
|
|
443
|
+
lines += [
|
|
444
|
+
"",
|
|
445
|
+
(
|
|
446
|
+
"Symbol and reference counts are in the heading; the names are not "
|
|
447
|
+
"published. The Issue already carries this pack's file paths in the "
|
|
448
|
+
"frozen Plan, and a reader diagnosing a Run reads them against the "
|
|
449
|
+
"repository. Set `context.publish_inventory: true` where the tracker's "
|
|
450
|
+
"audience is the code's audience (ADR-0024)."
|
|
451
|
+
),
|
|
452
|
+
]
|
|
431
453
|
|
|
432
454
|
if context.conventions:
|
|
433
455
|
lines += ["", "**Conventions:**", ""]
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/project.py
RENAMED
|
@@ -142,6 +142,14 @@ gates:
|
|
|
142
142
|
# The argument vector the `tests` Gate runs, in this repository.
|
|
143
143
|
# {because}
|
|
144
144
|
suite: [{suite}]
|
|
145
|
+
|
|
146
|
+
context:
|
|
147
|
+
# Whether the Context Pack comment names the symbols and imports it resolved,
|
|
148
|
+
# or only counts them (ADR-0024). A Run posts that comment to the Issue, and
|
|
149
|
+
# a tracker can have a wider audience than the code — so the names are off
|
|
150
|
+
# unless this repository says otherwise. The file list is published either
|
|
151
|
+
# way: the frozen Plan on the Issue already carries it.
|
|
152
|
+
publish_inventory: false
|
|
145
153
|
"""
|
|
146
154
|
|
|
147
155
|
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/repo.py
RENAMED
|
@@ -145,6 +145,21 @@ class Repository:
|
|
|
145
145
|
self._git("push", "--set-upstream", "origin", branch)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
def unclaimed(committed: Iterable[str], declared: Iterable[str]) -> tuple[str, ...]:
|
|
149
|
+
"""Of the paths committed, the ones nothing in the Run said it would touch.
|
|
150
|
+
|
|
151
|
+
ADR-0015 commits every change to a tracked file however it arrived, and its
|
|
152
|
+
reasoning holds as long as the only writers are the Agents and the commands
|
|
153
|
+
they were allowed to run. A second agent editing the same checkout breaks
|
|
154
|
+
that — its half-finished work is committed into the Run's branch and
|
|
155
|
+
attributed to a Role, and nothing about the file on disk says otherwise
|
|
156
|
+
(#101). This does not change what is committed; it names what nothing
|
|
157
|
+
claimed, so the disclosure reaches the human at Sign-off.
|
|
158
|
+
"""
|
|
159
|
+
named = {_normalize(path) for path in declared}
|
|
160
|
+
return tuple(path for path in committed if _normalize(path) not in named)
|
|
161
|
+
|
|
162
|
+
|
|
148
163
|
def _normalize(path: str) -> str:
|
|
149
164
|
r"""A declared path in the spelling `git status` uses.
|
|
150
165
|
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/runtime.py
RENAMED
|
@@ -24,7 +24,7 @@ from ..agents.decomposer import Approver, Decomposed, Decomposer, slice_task
|
|
|
24
24
|
from ..agents.orchestrator import Exchange, Interviewer, Orchestrator, Planned
|
|
25
25
|
from ..context.resolver import resolve_pack
|
|
26
26
|
from ..providers import DEFAULT_PROVIDER, get_provider
|
|
27
|
-
from .config import load_config
|
|
27
|
+
from .config import Config, load_config
|
|
28
28
|
from .contracts import (
|
|
29
29
|
AgentResult,
|
|
30
30
|
ContextPack,
|
|
@@ -64,7 +64,13 @@ from .registry import (
|
|
|
64
64
|
fragments_for,
|
|
65
65
|
gates_for,
|
|
66
66
|
)
|
|
67
|
-
from .repo import
|
|
67
|
+
from .repo import (
|
|
68
|
+
PreconditionFailed,
|
|
69
|
+
Repository,
|
|
70
|
+
branch_for_issue,
|
|
71
|
+
open_repository,
|
|
72
|
+
unclaimed,
|
|
73
|
+
)
|
|
68
74
|
from .workflow import Workflow, WorkflowError, load_workflow
|
|
69
75
|
|
|
70
76
|
|
|
@@ -144,7 +150,9 @@ class Forge:
|
|
|
144
150
|
|
|
145
151
|
# --- preconditions -----------------------------------------------------
|
|
146
152
|
|
|
147
|
-
def _prepare(
|
|
153
|
+
def _prepare(
|
|
154
|
+
self, allow_commands: bool = False
|
|
155
|
+
) -> tuple[Repository, GitHub, object, Config]:
|
|
148
156
|
"""Every check that can fail for free, before anything is spent.
|
|
149
157
|
|
|
150
158
|
Absent git, absent remote, absent `gh`, absent coding-agent CLI. A Run
|
|
@@ -157,11 +165,12 @@ class Forge:
|
|
|
157
165
|
raise RunFailed(str(exc)) from exc
|
|
158
166
|
|
|
159
167
|
github = GitHub(self.runner, repo.root)
|
|
168
|
+
config = load_config(repo.root)
|
|
160
169
|
provider = get_provider(
|
|
161
170
|
self.provider_name,
|
|
162
171
|
self.runner,
|
|
163
172
|
allow_commands=allow_commands,
|
|
164
|
-
config=
|
|
173
|
+
config=config,
|
|
165
174
|
)
|
|
166
175
|
try:
|
|
167
176
|
github.preflight()
|
|
@@ -169,7 +178,7 @@ class Forge:
|
|
|
169
178
|
except (IssueError, RuntimeError) as exc:
|
|
170
179
|
raise RunFailed(str(exc)) from exc
|
|
171
180
|
|
|
172
|
-
return repo, github, provider
|
|
181
|
+
return repo, github, provider, config
|
|
173
182
|
|
|
174
183
|
# --- agentforge plan ---------------------------------------------------
|
|
175
184
|
|
|
@@ -208,7 +217,7 @@ class Forge:
|
|
|
208
217
|
the caller supplies an approver that always says yes when the human
|
|
209
218
|
asked for exactly that.
|
|
210
219
|
"""
|
|
211
|
-
repo, github, provider = self._prepare()
|
|
220
|
+
repo, github, provider, _config = self._prepare()
|
|
212
221
|
|
|
213
222
|
# Only when interviewing: every stage is told to change nothing, and two
|
|
214
223
|
# extra `git status` calls on an unattended pass buy nothing.
|
|
@@ -368,7 +377,7 @@ class Forge:
|
|
|
368
377
|
off already suppresses them, and a Run with neither cannot say which of
|
|
369
378
|
the two moved the total. See ADR-0016 for the three conditions.
|
|
370
379
|
"""
|
|
371
|
-
repo, github, provider = self._prepare(allow_commands=allow_commands)
|
|
380
|
+
repo, github, provider, config = self._prepare(allow_commands=allow_commands)
|
|
372
381
|
|
|
373
382
|
if repo.is_dirty():
|
|
374
383
|
raise RunFailed(
|
|
@@ -466,7 +475,10 @@ class Forge:
|
|
|
466
475
|
github.post_comment(
|
|
467
476
|
number,
|
|
468
477
|
render_context_comment(
|
|
469
|
-
state.context,
|
|
478
|
+
state.context,
|
|
479
|
+
contributions(activation),
|
|
480
|
+
activation.skipped,
|
|
481
|
+
publish_inventory=config.publish_pack_inventory,
|
|
470
482
|
),
|
|
471
483
|
)
|
|
472
484
|
role = resolve_role(step.role)
|
|
@@ -535,14 +547,22 @@ class Forge:
|
|
|
535
547
|
)
|
|
536
548
|
|
|
537
549
|
changed = repo.changed_files()
|
|
550
|
+
declared = _declared_surface(state, results)
|
|
538
551
|
committed = repo.commit_declared(
|
|
539
552
|
f"{issue.title}\n\nImplements #{number} via AgentForge.",
|
|
540
|
-
|
|
553
|
+
declared,
|
|
541
554
|
)
|
|
542
555
|
# Left in the working tree on purpose (ADR-0015), and named rather
|
|
543
556
|
# than dropped: the human at Sign-off is the only one who can say
|
|
544
557
|
# whether an undeclared file was an Agent's work or its suite's.
|
|
545
558
|
left = tuple(path for path in changed if path not in committed)
|
|
559
|
+
# The other half of the same disclosure. A tracked file is committed
|
|
560
|
+
# however it changed (ADR-0015), which is right when the only writers
|
|
561
|
+
# are the Agents and their commands, and is how a second agent sharing
|
|
562
|
+
# the checkout gets its work committed and attributed to a Role (#101).
|
|
563
|
+
# Naming these is the difference between a data-loss bug and a line at
|
|
564
|
+
# Sign-off.
|
|
565
|
+
unclaimed_paths = unclaimed(committed, declared)
|
|
546
566
|
base = github.default_branch()
|
|
547
567
|
# An empty working tree is only a failure when the branch has nothing on
|
|
548
568
|
# it either. Plenty of Runs legitimately write nothing here: an audit
|
|
@@ -573,7 +593,7 @@ class Forge:
|
|
|
573
593
|
repo.push(branch)
|
|
574
594
|
url = github.open_draft_pr(
|
|
575
595
|
title=issue.title,
|
|
576
|
-
body=_pr_body(number, state, results, committed, left),
|
|
596
|
+
body=_pr_body(number, state, results, committed, left, unclaimed_paths),
|
|
577
597
|
head=branch,
|
|
578
598
|
base=base,
|
|
579
599
|
)
|
|
@@ -761,7 +781,7 @@ def _end(github: GitHub, issue: Issue, state: RunState) -> RunState:
|
|
|
761
781
|
return state
|
|
762
782
|
|
|
763
783
|
|
|
764
|
-
def _pr_body(number: int, state: RunState, results, committed, left=()) -> str:
|
|
784
|
+
def _pr_body(number: int, state: RunState, results, committed, left=(), unclaimed=()) -> str:
|
|
765
785
|
lines = [
|
|
766
786
|
f"Closes #{number}.",
|
|
767
787
|
"",
|
|
@@ -791,6 +811,21 @@ def _pr_body(number: int, state: RunState, results, committed, left=()) -> str:
|
|
|
791
811
|
"",
|
|
792
812
|
]
|
|
793
813
|
lines += [f"- `{path}`" for path in left]
|
|
814
|
+
if unclaimed:
|
|
815
|
+
lines += [
|
|
816
|
+
"",
|
|
817
|
+
"## Committed, but no Agent claimed them",
|
|
818
|
+
"",
|
|
819
|
+
(
|
|
820
|
+
"These are in the diff. Git already tracked them, so AgentForge committed "
|
|
821
|
+
"them however they changed (ADR-0015) — but neither the Plan nor any Agent "
|
|
822
|
+
"Result named them, so nothing in this Run says they are its work. Read "
|
|
823
|
+
"them before you sign off. Another agent or a person editing this checkout "
|
|
824
|
+
"while the Run was going is the reason worth ruling out."
|
|
825
|
+
),
|
|
826
|
+
"",
|
|
827
|
+
]
|
|
828
|
+
lines += [f"- `{path}`" for path in unclaimed]
|
|
794
829
|
lines += [
|
|
795
830
|
"",
|
|
796
831
|
"---",
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentforge-framework
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: A modular framework for coordinating specialized software agents.
|
|
5
5
|
Author: Yash Mhatre
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -145,7 +145,7 @@ without notice. See
|
|
|
145
145
|
```console
|
|
146
146
|
$ pip install agentforge-framework
|
|
147
147
|
$ agentforge --version
|
|
148
|
-
agentforge 0.2.
|
|
148
|
+
agentforge 0.2.3
|
|
149
149
|
```
|
|
150
150
|
|
|
151
151
|
That is the whole of it. The two routes below the naming note are for a machine
|
|
@@ -169,7 +169,7 @@ from the [latest release](https://github.com/yashmhatre/AgentForge/releases/late
|
|
|
169
169
|
and install the file:
|
|
170
170
|
|
|
171
171
|
```console
|
|
172
|
-
$ pip install agentforge_framework-0.2.
|
|
172
|
+
$ pip install agentforge_framework-0.2.3-py3-none-any.whl
|
|
173
173
|
```
|
|
174
174
|
|
|
175
175
|
Publishing a release is a wizard rather than a checklist:
|
|
@@ -200,6 +200,14 @@ What each release contains is in [CHANGELOG.md](CHANGELOG.md).
|
|
|
200
200
|
|
|
201
201
|
AgentForge never touches a model API and handles no credentials of its own. Whatever your coding-agent CLI is already authenticated with is what a Run costs.
|
|
202
202
|
|
|
203
|
+
### Editors and IDEs
|
|
204
|
+
|
|
205
|
+
AgentForge is a terminal tool. It runs fine from an IDE's terminal, but the IDE is not a Provider and its assistant is not one either.
|
|
206
|
+
|
|
207
|
+
**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.
|
|
208
|
+
|
|
209
|
+
**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.
|
|
210
|
+
|
|
203
211
|
## Commands
|
|
204
212
|
|
|
205
213
|
| Command | What it does |
|
|
@@ -331,10 +339,21 @@ code rather than the verdict about the old code.
|
|
|
331
339
|
|
|
332
340
|
Read [`CONTEXT.md`](CONTEXT.md) before writing anything, and [`docs/adr/`](docs/adr/) for the decisions that constrain it.
|
|
333
341
|
|
|
334
|
-
##
|
|
342
|
+
## Changing AgentForge
|
|
343
|
+
|
|
344
|
+
Clone it and install that clone editable. Never patch the installed copy.
|
|
335
345
|
|
|
336
346
|
```console
|
|
347
|
+
$ git clone https://github.com/yashmhatre/AgentForge.git
|
|
348
|
+
$ cd AgentForge
|
|
337
349
|
$ pip install -e ".[dev]"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Editing `site-packages/agentforge_framework/` appears to work and is the one change nothing will catch: the local install diverges from what ships, so the thing that works is not the thing anybody else gets, and the fix is lost the next time the package is upgraded. If a Run is failing badly enough that patching the install looks like the way forward, that is a bug worth filing — [#101](https://github.com/yashmhatre/AgentForge/issues/101) is what happened the last time somebody reached for it.
|
|
353
|
+
|
|
354
|
+
## Tests
|
|
355
|
+
|
|
356
|
+
```console
|
|
338
357
|
$ pytest
|
|
339
358
|
```
|
|
340
359
|
|
|
@@ -17,6 +17,31 @@ def test_missing_config_uses_the_documented_provider_capability_defaults(tmp_pat
|
|
|
17
17
|
assert not (tmp_path / ".agentforge").exists(), "the loader is read-only"
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
def _config_file(tmp_path, text: str):
|
|
21
|
+
config_dir = tmp_path / ".agentforge"
|
|
22
|
+
config_dir.mkdir(exist_ok=True)
|
|
23
|
+
(config_dir / "config.yaml").write_text(text, encoding="utf-8")
|
|
24
|
+
return load_config(tmp_path)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_the_pack_inventory_is_not_published_unless_the_project_says_so():
|
|
28
|
+
"""ADR-0024. The safe direction is the default because the disclosure is
|
|
29
|
+
the unrecoverable half: a comment on a public tracker is published."""
|
|
30
|
+
assert load_config("/nowhere").publish_pack_inventory is False
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_a_project_whose_tracker_matches_its_code_publishes_the_inventory(tmp_path):
|
|
34
|
+
config = _config_file(tmp_path, "context:\n publish_inventory: true\n")
|
|
35
|
+
|
|
36
|
+
assert config.publish_pack_inventory is True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_a_project_can_say_no_as_explicitly_as_it_can_say_yes(tmp_path):
|
|
40
|
+
config = _config_file(tmp_path, "context:\n publish_inventory: false\n")
|
|
41
|
+
|
|
42
|
+
assert config.publish_pack_inventory is False
|
|
43
|
+
|
|
44
|
+
|
|
20
45
|
def test_capability_tiers_are_read_from_the_shared_config_file(tmp_path):
|
|
21
46
|
config_dir = tmp_path / ".agentforge"
|
|
22
47
|
config_dir.mkdir()
|
|
@@ -721,23 +721,52 @@ def test_a_run_where_nobody_reported_anything_says_nobody_reported_anything():
|
|
|
721
721
|
# --- the pack, recorded where a Run is diagnosed ---------------------------
|
|
722
722
|
|
|
723
723
|
|
|
724
|
+
A_PACK = ContextPack(
|
|
725
|
+
files=("src/loader.py",),
|
|
726
|
+
symbols=("src/loader.py::fetch",),
|
|
727
|
+
references=("requests",),
|
|
728
|
+
conventions=("no new dependencies",),
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
|
|
724
732
|
def test_the_run_log_records_the_pack_the_agents_were_shown():
|
|
725
733
|
"""A Run that went wrong is diagnosed against what its Agents could see."""
|
|
726
|
-
comment = render_context_comment(
|
|
727
|
-
ContextPack(
|
|
728
|
-
files=("src/loader.py",),
|
|
729
|
-
symbols=("src/loader.py::fetch",),
|
|
730
|
-
references=("requests",),
|
|
731
|
-
conventions=("no new dependencies",),
|
|
732
|
-
)
|
|
733
|
-
)
|
|
734
|
+
comment = render_context_comment(A_PACK)
|
|
734
735
|
|
|
735
736
|
assert "### Context Pack" in comment
|
|
736
737
|
assert "`src/loader.py`" in comment
|
|
737
|
-
assert "`src/loader.py::fetch`" in comment
|
|
738
738
|
assert "no new dependencies" in comment
|
|
739
739
|
|
|
740
740
|
|
|
741
|
+
def test_the_counts_survive_the_names_not_being_published():
|
|
742
|
+
"""What the comment is read for. A pack that resolved more or less than the
|
|
743
|
+
reader expected is visible without naming anything (ADR-0024)."""
|
|
744
|
+
comment = render_context_comment(A_PACK)
|
|
745
|
+
|
|
746
|
+
assert "1 file, 1 symbol, 1 reference" in comment
|
|
747
|
+
assert "src/loader.py::fetch" not in comment
|
|
748
|
+
assert "requests" not in comment
|
|
749
|
+
assert "publish_inventory" in comment, "a reader who wants the names is told how"
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def test_the_inventory_is_published_when_the_repository_asks_for_it():
|
|
753
|
+
"""A tracker whose audience is the code's audience loses nothing by it."""
|
|
754
|
+
comment = render_context_comment(A_PACK, publish_inventory=True)
|
|
755
|
+
|
|
756
|
+
assert "`src/loader.py::fetch`" in comment
|
|
757
|
+
assert "`requests`" in comment
|
|
758
|
+
assert "<details><summary>Symbols (1)</summary>" in comment
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def test_a_pack_with_no_symbols_explains_nothing_it_withheld():
|
|
762
|
+
"""The note is a disclosure about withheld names. A pack that resolved none
|
|
763
|
+
has nothing to withhold, and saying so would be noise."""
|
|
764
|
+
comment = render_context_comment(ContextPack(files=("src/loader.py",)))
|
|
765
|
+
|
|
766
|
+
assert "publish_inventory" not in comment
|
|
767
|
+
assert "`src/loader.py`" in comment
|
|
768
|
+
|
|
769
|
+
|
|
741
770
|
def test_a_run_with_no_pack_says_that_it_is_the_control():
|
|
742
771
|
"""The measurement in story twenty needs two Runs and a way to tell which
|
|
743
772
|
of them was which."""
|
|
@@ -117,6 +117,7 @@ def test_the_written_file_round_trips_through_load_config(tmp_path):
|
|
|
117
117
|
config = load_config(tmp_path)
|
|
118
118
|
assert config.test_suite == context.test_suite
|
|
119
119
|
assert config.capability_for("codex") is context.capability_tier
|
|
120
|
+
assert config.publish_pack_inventory is False
|
|
120
121
|
|
|
121
122
|
|
|
122
123
|
def test_an_argument_with_a_dash_in_it_survives_the_round_trip(tmp_path):
|
|
@@ -138,12 +139,13 @@ def test_the_file_says_which_values_were_detected_and_which_were_defaulted():
|
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
def test_the_file_carries_no_key_nothing_reads():
|
|
141
|
-
"""`docs/PLAN.md` promised a config owning plugin activation
|
|
142
|
-
|
|
143
|
-
so activation is printed and the file says why
|
|
142
|
+
"""`docs/PLAN.md` promised a config owning plugin activation, and the file
|
|
143
|
+
holds only what `load_config` reads. A key that has no effect is a lie told
|
|
144
|
+
to whoever edits it, so activation is printed and the file says why
|
|
145
|
+
(ADR-0020). Every key here arrived with its reader."""
|
|
144
146
|
written = render_config(a_context(plugins=("sql", "databricks")))
|
|
145
147
|
|
|
146
|
-
assert set(yaml.safe_load(written)) == {"providers", "gates"}
|
|
148
|
+
assert set(yaml.safe_load(written)) == {"providers", "gates", "context"}
|
|
147
149
|
assert "databricks" not in written
|
|
148
150
|
# The absence is explained where somebody looking for the key will look.
|
|
149
151
|
assert "no `plugins:` key here to edit" in written
|
|
@@ -1903,6 +1903,62 @@ def test_the_pull_request_lists_only_what_was_committed():
|
|
|
1903
1903
|
assert "loader.cpython-311.pyc" not in body.split("## Left uncommitted")[0]
|
|
1904
1904
|
|
|
1905
1905
|
|
|
1906
|
+
#: A second agent editing the same checkout while the Run is going. The Run's
|
|
1907
|
+
#: own file is modified as expected; `src/telemetry.py` is somebody else's
|
|
1908
|
+
#: half-finished work, and git tracks it, so ADR-0015 commits it either way.
|
|
1909
|
+
CONCURRENT_EDIT = " M src/loader.py\n M src/telemetry.py\n"
|
|
1910
|
+
|
|
1911
|
+
|
|
1912
|
+
def test_a_committed_file_no_agent_claimed_is_named_in_the_pull_request():
|
|
1913
|
+
"""The concurrency hazard in #101. Antigravity's agent and a Run share one
|
|
1914
|
+
checkout; ADR-0015 commits every change to a tracked file however it
|
|
1915
|
+
arrived, so the other agent's work lands in the branch attributed to a Role.
|
|
1916
|
+
Nothing about the file on disk says otherwise, so the Run says it instead."""
|
|
1917
|
+
runner = a_runner()
|
|
1918
|
+
runner.script("git", "status", "--porcelain", stdout=["", CONCURRENT_EDIT])
|
|
1919
|
+
runner.script("claude", stdout=agent_says("completed", "retry added"))
|
|
1920
|
+
|
|
1921
|
+
forge(runner).implement(12, allow_commands=True)
|
|
1922
|
+
|
|
1923
|
+
assert "src/telemetry.py" in staged(runner), "ADR-0015 still commits it"
|
|
1924
|
+
body = runner.argument_after("--body", "gh", "pr", "create")
|
|
1925
|
+
unclaimed_section = body.split("## Committed, but no Agent claimed them")[1]
|
|
1926
|
+
assert "- `src/telemetry.py`" in unclaimed_section
|
|
1927
|
+
assert "- `src/loader.py`" not in unclaimed_section, (
|
|
1928
|
+
"the Role declared this one; naming it would bury the one that matters"
|
|
1929
|
+
)
|
|
1930
|
+
|
|
1931
|
+
|
|
1932
|
+
def test_a_run_whose_every_file_was_declared_says_nothing_about_unclaimed_ones():
|
|
1933
|
+
"""The section is a disclosure, not a fixture of the body. An ordinary Run
|
|
1934
|
+
that names what it changed reads exactly as it did before."""
|
|
1935
|
+
runner = a_runner()
|
|
1936
|
+
runner.script("git", "status", "--porcelain", stdout=["", " M src/loader.py\n"])
|
|
1937
|
+
runner.script("claude", stdout=agent_says("completed", "retry added"))
|
|
1938
|
+
|
|
1939
|
+
forge(runner).implement(12, allow_commands=True)
|
|
1940
|
+
|
|
1941
|
+
body = runner.argument_after("--body", "gh", "pr", "create")
|
|
1942
|
+
assert "no Agent claimed" not in body
|
|
1943
|
+
|
|
1944
|
+
|
|
1945
|
+
def test_a_role_that_spelled_its_path_the_windows_way_still_claimed_the_file():
|
|
1946
|
+
"""A Role reports `src\\telemetry.py` and git answers `src/telemetry.py`.
|
|
1947
|
+
Comparing those literally would accuse an Agent of somebody else's edit on
|
|
1948
|
+
every Windows Run, which is the platform this hazard was found on."""
|
|
1949
|
+
runner = a_runner()
|
|
1950
|
+
runner.script("git", "status", "--porcelain", stdout=["", CONCURRENT_EDIT])
|
|
1951
|
+
runner.script(
|
|
1952
|
+
"claude",
|
|
1953
|
+
stdout=agent_says("completed", "retry added", files=("src\\telemetry.py",)),
|
|
1954
|
+
)
|
|
1955
|
+
|
|
1956
|
+
forge(runner).implement(12, allow_commands=True)
|
|
1957
|
+
|
|
1958
|
+
body = runner.argument_after("--body", "gh", "pr", "create")
|
|
1959
|
+
assert "no Agent claimed" not in body
|
|
1960
|
+
|
|
1961
|
+
|
|
1906
1962
|
def test_what_was_left_behind_is_named_rather_than_dropped():
|
|
1907
1963
|
"""A build artifact is the usual reason and an Agent writing outside its
|
|
1908
1964
|
Step is the one worth reading, and only a human can tell them apart."""
|
|
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
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/agents/tester.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
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/context/prompt.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/__init__.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/commands.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/contracts.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/gates.py
RENAMED
|
File without changes
|
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/process.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/registry.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/router.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/skills.py
RENAMED
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/core/workflow.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/providers/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agentforge_framework-0.2.2 → agentforge_framework-0.2.3}/src/agentforge_framework/skills/NOTICE
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|