aepkit 0.6.0__tar.gz → 0.7.0__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.
- {aepkit-0.6.0 → aepkit-0.7.0}/CHANGELOG.md +58 -9
- {aepkit-0.6.0/src/aepkit.egg-info → aepkit-0.7.0}/PKG-INFO +26 -12
- {aepkit-0.6.0 → aepkit-0.7.0}/README.md +21 -8
- {aepkit-0.6.0 → aepkit-0.7.0}/SKILL.md +13 -7
- {aepkit-0.6.0 → aepkit-0.7.0}/pyproject.toml +9 -4
- {aepkit-0.6.0/src/aepkit/skill → aepkit-0.7.0}/references/autonomous-mode.md +5 -1
- {aepkit-0.6.0/src/aepkit/skill → aepkit-0.7.0}/references/failure-modes-and-example.md +1 -1
- {aepkit-0.6.0/src/aepkit/skill → aepkit-0.7.0}/references/handoffs.md +1 -1
- aepkit-0.7.0/src/aepkit/__init__.py +1 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/cli.py +17 -7
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/install_skill.py +1 -1
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/lint.py +26 -19
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/negcontrol.py +23 -6
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/scaffold.py +4 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/skill/SKILL.md +13 -7
- {aepkit-0.6.0 → aepkit-0.7.0/src/aepkit/skill}/references/autonomous-mode.md +5 -1
- {aepkit-0.6.0 → aepkit-0.7.0/src/aepkit/skill}/references/failure-modes-and-example.md +1 -1
- {aepkit-0.6.0 → aepkit-0.7.0/src/aepkit/skill}/references/handoffs.md +1 -1
- aepkit-0.7.0/src/aepkit/status.py +314 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/templates/execution-guide.md +6 -3
- {aepkit-0.6.0 → aepkit-0.7.0/src/aepkit.egg-info}/PKG-INFO +26 -12
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit.egg-info/requires.txt +4 -3
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_install_skill.py +39 -40
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_lint.py +87 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_negcontrol.py +42 -1
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_scaffold.py +8 -0
- aepkit-0.7.0/tests/test_status.py +322 -0
- aepkit-0.6.0/src/aepkit/__init__.py +0 -1
- aepkit-0.6.0/src/aepkit/status.py +0 -122
- aepkit-0.6.0/tests/test_status.py +0 -112
- {aepkit-0.6.0 → aepkit-0.7.0}/LICENSE +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/MANIFEST.in +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/references/tasks-and-gates.md +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/setup.cfg +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/__main__.py +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/py.typed +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/skill/references/tasks-and-gates.md +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/templates/compliance-log.md +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit/templates/plan-of-record.md +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit.egg-info/SOURCES.txt +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit.egg-info/dependency_links.txt +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit.egg-info/entry_points.txt +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/src/aepkit.egg-info/top_level.txt +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/run.py +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_cli.py +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_readme_claims.py +0 -0
- {aepkit-0.6.0 → aepkit-0.7.0}/tests/test_skill_sync.py +0 -0
|
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-11-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `aepkit status --json` prints the status reading as one JSON object: each ID with its
|
|
14
|
+
phase, kind, effective state, verdict, latest report, executor (a status board column
|
|
15
|
+
named `Executor`, `Worker`, `Agent`, `Owner` or `Assignee`) and any board/header
|
|
16
|
+
conflict; the phases with their titles from `execution-guide.md` and the plan's phase
|
|
17
|
+
table, an optional `Weight` column, and phases the guide has not broken down; IDs the
|
|
18
|
+
guide lists that the log has no entry for yet, marked `inLog: false`. The text output
|
|
19
|
+
is unchanged.
|
|
20
|
+
- `aeprogress`, an optional Claude Code mod in `integrations/claude-code/aeprogress/`:
|
|
21
|
+
a pane with the run's progress per phase, who is working on which ID (Claude Code
|
|
22
|
+
subagents, Orca workers, or any harness through a command), what awaits audit and what
|
|
23
|
+
needs attention, opened with `/aeprogress`. `.claude-plugin/marketplace.json` makes the
|
|
24
|
+
repository installable with
|
|
25
|
+
`/plugin install aeprogress --marketplace tBeltty/auditor-executor-protocol`.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- `SKILL.md`, `references/handoffs.md`, `references/autonomous-mode.md` and
|
|
29
|
+
`references/failure-modes-and-example.md` spoke of "a plain `PASS`" on one task. `PASS`
|
|
30
|
+
is not a verdict (the verdicts are `APPROVED`, `CONDITIONAL` and `REJECTED`), and a log
|
|
31
|
+
that used it showed the task as awaiting audit in `aepkit status`. The four sentences
|
|
32
|
+
now say `APPROVED`.
|
|
33
|
+
|
|
10
34
|
## [0.6.0] - 2026-10-24
|
|
11
35
|
|
|
12
36
|
### Added
|
|
@@ -29,6 +53,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
29
53
|
(`pipx install aepkit`, `aepkit`, `python -m aepkit`) to eliminate naming collisions with
|
|
30
54
|
external `auditkit` packages and binaries.
|
|
31
55
|
|
|
56
|
+
## [0.5.1] - 2026-10-04
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
- `negcontrol --file` without `--break-cmd` is now a usage error (exit 2). The backup was taken
|
|
60
|
+
of a file already broken by hand, so a correct `--restore-cmd` was overwritten with the broken
|
|
61
|
+
copy and blamed for it.
|
|
62
|
+
- `negcontrol` fails the control when the test times out with the protection removed: a hang is
|
|
63
|
+
not a caught break. It used to print `OK` with a warning.
|
|
64
|
+
- `negcontrol --file <directory>` and `auditkit init <existing file>` print an error and exit 2
|
|
65
|
+
instead of crashing with a traceback.
|
|
66
|
+
- The execution guide's reporting format puts the verify output in a fenced block, so a report
|
|
67
|
+
written as the template shows no longer fails `lint`.
|
|
68
|
+
- `lint` no longer cuts a report short at a `#`, `---` or `**Field:**` line inside pasted output.
|
|
69
|
+
- `lint`'s phase-order check reads verdicts the way `status` does: the latest report header wins,
|
|
70
|
+
and a status board verdict that disagrees with it is a conflict, not an approval.
|
|
71
|
+
- `install-skill` auto-detects Claude Code through `CLAUDECODE`, the variable Claude Code sets.
|
|
72
|
+
- The install-skill tests no longer depend on the agent environment they run in.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
- The release workflow runs the full CI before building and publishing.
|
|
76
|
+
- CI pins ruff, mypy, build and twine through the `dev` extra, and smoke-tests the built wheel in
|
|
77
|
+
a clean environment.
|
|
78
|
+
- README links are absolute, so they work on PyPI; `auditkit --help` and the installed skill
|
|
79
|
+
link to GitHub instead of "the repo root".
|
|
80
|
+
- `--annex-threshold` is documented as a total with a per-phase breakdown, which is what it
|
|
81
|
+
checks.
|
|
82
|
+
- `SECURITY.md` lists 0.5.x as supported; `CONTRIBUTING.md` documents the release steps.
|
|
83
|
+
|
|
32
84
|
## [0.5.0] - 2026-10-02
|
|
33
85
|
|
|
34
86
|
### Added
|
|
@@ -85,9 +137,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
85
137
|
requires Python 3.11+.
|
|
86
138
|
|
|
87
139
|
### Fixed
|
|
88
|
-
- `AGENTS.md` imported itself and said nothing, because `GEMINI.md` was a symlink to it. It is
|
|
89
|
-
now a short, generic agent guide that points to `CONTRIBUTING.md`; tool-specific instruction
|
|
90
|
-
files (`CLAUDE.md`, `GEMINI.md`) are no longer tracked.
|
|
91
140
|
- README: the compliance log template, `install-skill` detection and `--global`/`--dest`, every
|
|
92
141
|
command's options, exit codes, and the BSD-only `sed -i ''` in the `negcontrol` example now match
|
|
93
142
|
the CLI.
|
|
@@ -193,7 +242,7 @@ Fixes from an independent review of 0.3.0.
|
|
|
193
242
|
field is missing, empty, or only a template placeholder. The protocol already records
|
|
194
243
|
these as `FAILED`; the check makes that mechanical.
|
|
195
244
|
- README: install `auditkit` without cloning via `pipx install git+https://github.com/tBeltty/auditor-executor-protocol`.
|
|
196
|
-
- Conventional Commits specification documented in `CONTRIBUTING.md
|
|
245
|
+
- Conventional Commits specification documented in `CONTRIBUTING.md`.
|
|
197
246
|
- `SKILL.md`: optional Guided/Autonomous operating mode, with the autonomy charter, per-task
|
|
198
247
|
model-tier assignment, the per-task cycle, and an Auditor-brief template for starting an
|
|
199
248
|
autonomous run.
|
|
@@ -218,8 +267,6 @@ Fixes from an independent review of 0.3.0.
|
|
|
218
267
|
- `auditkit install-skill` copies `references/` beside `SKILL.md`. Destinations that hold a
|
|
219
268
|
single file (Cursor `.mdc`, or a `--dest` not named `SKILL.md`) get one bundled file.
|
|
220
269
|
- The packaged skill moved from `src/auditkit/templates/SKILL.md` to `src/auditkit/skill/`.
|
|
221
|
-
- `GEMINI.md` imports `AGENTS.md` instead of duplicating it, so the agent rules have one
|
|
222
|
-
source of truth.
|
|
223
270
|
- Bumped `actions/checkout` and `actions/setup-python` to v7 in GitHub Actions CI workflow.
|
|
224
271
|
- Configured grouped updates in `.github/dependabot.yml` to bundle action and pip dependency bumps.
|
|
225
272
|
|
|
@@ -250,7 +297,6 @@ Fixes from an independent review of 0.3.0.
|
|
|
250
297
|
- 90%+ test coverage enforcement across the repository.
|
|
251
298
|
- GitHub Actions CI workflow hardening: explicit token permissions, dedicated lint/format/typecheck job, package distribution verification, and weekly Dependabot configuration.
|
|
252
299
|
- Open-source governance files: `SECURITY.md`, `CONTRIBUTING.md`, `CHANGELOG.md`, PR and issue templates.
|
|
253
|
-
- Agent rule specifications (`AGENTS.md`, `GEMINI.md`) establishing mandatory changelog updates and test verification gates on every task.
|
|
254
300
|
|
|
255
301
|
### Changed
|
|
256
302
|
- Conformed all Python code to Ruff formatting and PEP 8 import conventions.
|
|
@@ -269,8 +315,10 @@ Fixes from an independent review of 0.3.0.
|
|
|
269
315
|
- Multi-framework agent installation support for Antigravity, Claude Code, and Cursor.
|
|
270
316
|
- Zero-dependency test runner (`tests/run.py`).
|
|
271
317
|
|
|
272
|
-
[Unreleased]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.
|
|
273
|
-
[0.
|
|
318
|
+
[Unreleased]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.7.0...HEAD
|
|
319
|
+
[0.7.0]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.6.0...v0.7.0
|
|
320
|
+
[0.6.0]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.5.1...v0.6.0
|
|
321
|
+
[0.5.1]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.5.0...v0.5.1
|
|
274
322
|
[0.5.0]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.4.0...v0.5.0
|
|
275
323
|
[0.4.0]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.3.9...v0.4.0
|
|
276
324
|
[0.3.9]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.3.8...v0.3.9
|
|
@@ -284,3 +332,4 @@ Fixes from an independent review of 0.3.0.
|
|
|
284
332
|
[0.3.1]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.3.0...v0.3.1
|
|
285
333
|
[0.3.0]: https://github.com/tBeltty/auditor-executor-protocol/compare/v0.2.0...v0.3.0
|
|
286
334
|
[0.2.0]: https://github.com/tBeltty/auditor-executor-protocol/releases/tag/v0.2.0
|
|
335
|
+
[0.1.0]: https://github.com/tBeltty/auditor-executor-protocol/tree/7e9701cdacf732fa63b28428eedd9dcc7250fa2f
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aepkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Mechanical support for the Auditor/Executor protocol: scaffold, lint, and negative-control the document set for a multi-phase, multi-agent execution.
|
|
5
5
|
Author-email: tBeltty <jhonatan@tbelt.online>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,9 +30,10 @@ Requires-Dist: pytest-cov>=4.0; extra == "test"
|
|
|
30
30
|
Provides-Extra: dev
|
|
31
31
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
32
32
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
33
|
-
Requires-Dist: ruff
|
|
34
|
-
Requires-Dist: mypy
|
|
35
|
-
Requires-Dist: build
|
|
33
|
+
Requires-Dist: ruff==0.16.10; extra == "dev"
|
|
34
|
+
Requires-Dist: mypy==2.4.0; extra == "dev"
|
|
35
|
+
Requires-Dist: build==1.6.1; extra == "dev"
|
|
36
|
+
Requires-Dist: twine==7.0.0; extra == "dev"
|
|
36
37
|
Dynamic: license-file
|
|
37
38
|
|
|
38
39
|
# Auditor/Executor Protocol
|
|
@@ -43,13 +44,13 @@ Dynamic: license-file
|
|
|
43
44
|
[](https://opensource.org/licenses/MIT)
|
|
44
45
|
[](https://github.com/astral-sh/ruff)
|
|
45
46
|
[](https://mypy-lang.org/)
|
|
46
|
-
[](pyproject.toml)
|
|
47
|
+
[](https://github.com/tBeltty/auditor-executor-protocol/blob/main/pyproject.toml)
|
|
47
48
|
|
|
48
49
|
A two-role protocol for running multi-phase work through AI agents without the plan drifting into open-ended discussion and without "all tests pass" masquerading as verification.
|
|
49
50
|
|
|
50
51
|
The **Auditor** defines what "done" means and proves it independently. The **Executor** implements one numbered task at a time and logs command output. Neither role crosses into the other.
|
|
51
52
|
|
|
52
|
-
[`SKILL.md`](SKILL.md) specifies the protocol core; [`references/`](references
|
|
53
|
+
[`SKILL.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/SKILL.md) specifies the protocol core; [`references/`](https://github.com/tBeltty/auditor-executor-protocol/tree/main/references) holds the parts an agent loads only when it reaches that step (task and gate writing, handoff templates, Autonomous mode, failure modes and a worked example). `aepkit` is a zero-dependency Python CLI that automates scaffolding, drift linting, negative controls, status tallies, and installing the skill into your agent.
|
|
53
54
|
|
|
54
55
|
---
|
|
55
56
|
|
|
@@ -147,13 +148,15 @@ Inspect open tasks and tallied verdicts:
|
|
|
147
148
|
aepkit status docs/<task-name>
|
|
148
149
|
```
|
|
149
150
|
|
|
151
|
+
`aepkit status --json docs/<task-name>` prints the same reading as JSON, with each ID's phase, effective state and executor, and the phases with their titles from `execution-guide.md` (and weights from an optional `Weight` column in the plan's phase table), for scripts and tools to use instead of parsing the log.
|
|
152
|
+
|
|
150
153
|
Cross-check document consistency:
|
|
151
154
|
|
|
152
155
|
```bash
|
|
153
156
|
aepkit lint docs/<task-name>
|
|
154
157
|
```
|
|
155
158
|
|
|
156
|
-
`aepkit lint` catches an empty guide, tasks without a report line, missing report entries, log entries with no matching task, `DONE` reports whose verify output is not pasted in a code block, gates without a stated negative control (hypothetical or waived ones do not count), work reported in a phase before every earlier phase is `APPROVED`, unfilled opportunity/shaping/non-goal fields in `plan-of-record.md` once work is reported `DONE` or audited, open rows in the deferred items ledger for a phase being closed, duplicated log paragraphs, and annex buildup past `--annex-threshold` (default 6,
|
|
159
|
+
`aepkit lint` catches an empty guide, tasks without a report line, missing report entries, log entries with no matching task, `DONE` reports whose verify output is not pasted in a code block, gates without a stated negative control (hypothetical or waived ones do not count), work reported in a phase before every earlier phase is `APPROVED`, unfilled opportunity/shaping/non-goal fields in `plan-of-record.md` once work is reported `DONE` or audited, open rows in the deferred items ledger for a phase being closed, duplicated log paragraphs, and annex buildup past `--annex-threshold` (default 6 in total, with a per-phase breakdown). It checks that evidence is present and in the right form, not that it is genuine; the Auditor's re-run does that.
|
|
157
160
|
|
|
158
161
|
### 5. Run a Negative Control
|
|
159
162
|
|
|
@@ -169,9 +172,20 @@ aepkit negcontrol \
|
|
|
169
172
|
|
|
170
173
|
The `sed -i ''` form above is BSD/macOS sed; on GNU/Linux use `sed -i 's/.../.../' file`.
|
|
171
174
|
|
|
172
|
-
`negcontrol` backs up the target file, applies the break mutation, confirms the test fails, restores the file with byte-level verification, confirms the test passes, and outputs a transcript ready for `compliance-log.md`. Without `--file`, pass `--restore-cmd` to undo the break yourself; with both, the backup still wins if the file is not byte-identical afterwards. `--timeout` limits each command in seconds.
|
|
175
|
+
`negcontrol` backs up the target file, applies the break mutation, confirms the test fails, restores the file with byte-level verification, confirms the test passes, and outputs a transcript ready for `compliance-log.md`. Without `--file`, pass `--restore-cmd` to undo the break yourself; with both, the backup still wins if the file is not byte-identical afterwards. `--file` needs `--break-cmd`, because the backup must be taken before the break. `--timeout` limits each command in seconds, and a test that times out instead of failing fails the control: a hang is not a caught break.
|
|
176
|
+
|
|
177
|
+
A failure only proves something if it is the failure you meant. A syntax error, a missing import or a mistyped test command also makes the test exit non-zero. `--expect` takes a regular expression that the failing run's output must match, and the control fails if it does not. Without `--expect`, output that looks like a syntax, import, collection or command-not-found error is flagged with a `WARNING` next to the `OK`, and you need to read the transcript to confirm the reason.
|
|
178
|
+
|
|
179
|
+
### Optional: Progress Pane for Claude Code
|
|
180
|
+
|
|
181
|
+
`aeprogress` is a Claude Code mod that keeps a pane beside the chat with the run's overall percentage, a bar per phase, who is working on which task ID (Claude Code subagents, Orca workers, or any other harness through a command of your own), what awaits audit and what needs attention. It reads the log through `aepkit status --json` and refreshes after each turn, and every 20 seconds while the pane is open. The protocol does not depend on it.
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
/plugin install aeprogress --marketplace tBeltty/auditor-executor-protocol
|
|
185
|
+
/aeprogress docs/<task-name>
|
|
186
|
+
```
|
|
173
187
|
|
|
174
|
-
|
|
188
|
+
It works in the terminal and in the desktop app's local sessions. Claude Code's mod API is early access, so a Claude Code release can break it. Setup, options and how the percentage is computed: [integrations/claude-code/aeprogress](https://github.com/tBeltty/auditor-executor-protocol/tree/main/integrations/claude-code/aeprogress).
|
|
175
189
|
|
|
176
190
|
---
|
|
177
191
|
|
|
@@ -183,7 +197,7 @@ A failure only proves something if it is the failure you meant. A syntax error,
|
|
|
183
197
|
| `aepkit install-skill [dir]` | `--agent`, `--global`, `--dest`, `--force` | Provision `SKILL.md` and `references/` into Antigravity, Claude Code, or Cursor |
|
|
184
198
|
| `aepkit lint <dir>` | `--annex-threshold` | Cross-check IDs both ways, flag `DONE` without evidence, detect missing negative controls, and flag log rot |
|
|
185
199
|
| `aepkit negcontrol` | `--test-cmd` (required), `--file`, `--break-cmd`, `--restore-cmd`, `--expect`, `--timeout` | Run automated backup, break, fail, restore, and pass cycle |
|
|
186
|
-
| `aepkit status <dir>` | | Combine status board verdicts with reports; list everything not `APPROVED` |
|
|
200
|
+
| `aepkit status <dir>` | `--json` | Combine status board verdicts with reports; list everything not `APPROVED` |
|
|
187
201
|
|
|
188
202
|
`python -m aepkit` works the same as `aepkit`. Errors go to stderr.
|
|
189
203
|
|
|
@@ -212,11 +226,11 @@ pytest
|
|
|
212
226
|
|
|
213
227
|
## Contributing
|
|
214
228
|
|
|
215
|
-
Contributions are welcome. [`CONTRIBUTING.md`](CONTRIBUTING.md) covers the development setup, the quality gates, and the PR checklist; participation follows the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). Report vulnerabilities privately as described in [`SECURITY.md`](SECURITY.md). Changes are listed in [`CHANGELOG.md`](CHANGELOG.md).
|
|
229
|
+
Contributions are welcome. [`CONTRIBUTING.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/CONTRIBUTING.md) covers the development setup, the quality gates, and the PR checklist; participation follows the [`CODE_OF_CONDUCT.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/CODE_OF_CONDUCT.md). Report vulnerabilities privately as described in [`SECURITY.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/SECURITY.md). Changes are listed in [`CHANGELOG.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/CHANGELOG.md).
|
|
216
230
|
|
|
217
231
|
## License
|
|
218
232
|
|
|
219
|
-
MIT (see [`LICENSE`](LICENSE)).
|
|
233
|
+
MIT (see [`LICENSE`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/LICENSE)).
|
|
220
234
|
|
|
221
235
|
---
|
|
222
236
|
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
[](https://github.com/astral-sh/ruff)
|
|
8
8
|
[](https://mypy-lang.org/)
|
|
9
|
-
[](pyproject.toml)
|
|
9
|
+
[](https://github.com/tBeltty/auditor-executor-protocol/blob/main/pyproject.toml)
|
|
10
10
|
|
|
11
11
|
A two-role protocol for running multi-phase work through AI agents without the plan drifting into open-ended discussion and without "all tests pass" masquerading as verification.
|
|
12
12
|
|
|
13
13
|
The **Auditor** defines what "done" means and proves it independently. The **Executor** implements one numbered task at a time and logs command output. Neither role crosses into the other.
|
|
14
14
|
|
|
15
|
-
[`SKILL.md`](SKILL.md) specifies the protocol core; [`references/`](references
|
|
15
|
+
[`SKILL.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/SKILL.md) specifies the protocol core; [`references/`](https://github.com/tBeltty/auditor-executor-protocol/tree/main/references) holds the parts an agent loads only when it reaches that step (task and gate writing, handoff templates, Autonomous mode, failure modes and a worked example). `aepkit` is a zero-dependency Python CLI that automates scaffolding, drift linting, negative controls, status tallies, and installing the skill into your agent.
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -110,13 +110,15 @@ Inspect open tasks and tallied verdicts:
|
|
|
110
110
|
aepkit status docs/<task-name>
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
`aepkit status --json docs/<task-name>` prints the same reading as JSON, with each ID's phase, effective state and executor, and the phases with their titles from `execution-guide.md` (and weights from an optional `Weight` column in the plan's phase table), for scripts and tools to use instead of parsing the log.
|
|
114
|
+
|
|
113
115
|
Cross-check document consistency:
|
|
114
116
|
|
|
115
117
|
```bash
|
|
116
118
|
aepkit lint docs/<task-name>
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
`aepkit lint` catches an empty guide, tasks without a report line, missing report entries, log entries with no matching task, `DONE` reports whose verify output is not pasted in a code block, gates without a stated negative control (hypothetical or waived ones do not count), work reported in a phase before every earlier phase is `APPROVED`, unfilled opportunity/shaping/non-goal fields in `plan-of-record.md` once work is reported `DONE` or audited, open rows in the deferred items ledger for a phase being closed, duplicated log paragraphs, and annex buildup past `--annex-threshold` (default 6,
|
|
121
|
+
`aepkit lint` catches an empty guide, tasks without a report line, missing report entries, log entries with no matching task, `DONE` reports whose verify output is not pasted in a code block, gates without a stated negative control (hypothetical or waived ones do not count), work reported in a phase before every earlier phase is `APPROVED`, unfilled opportunity/shaping/non-goal fields in `plan-of-record.md` once work is reported `DONE` or audited, open rows in the deferred items ledger for a phase being closed, duplicated log paragraphs, and annex buildup past `--annex-threshold` (default 6 in total, with a per-phase breakdown). It checks that evidence is present and in the right form, not that it is genuine; the Auditor's re-run does that.
|
|
120
122
|
|
|
121
123
|
### 5. Run a Negative Control
|
|
122
124
|
|
|
@@ -132,9 +134,20 @@ aepkit negcontrol \
|
|
|
132
134
|
|
|
133
135
|
The `sed -i ''` form above is BSD/macOS sed; on GNU/Linux use `sed -i 's/.../.../' file`.
|
|
134
136
|
|
|
135
|
-
`negcontrol` backs up the target file, applies the break mutation, confirms the test fails, restores the file with byte-level verification, confirms the test passes, and outputs a transcript ready for `compliance-log.md`. Without `--file`, pass `--restore-cmd` to undo the break yourself; with both, the backup still wins if the file is not byte-identical afterwards. `--timeout` limits each command in seconds.
|
|
137
|
+
`negcontrol` backs up the target file, applies the break mutation, confirms the test fails, restores the file with byte-level verification, confirms the test passes, and outputs a transcript ready for `compliance-log.md`. Without `--file`, pass `--restore-cmd` to undo the break yourself; with both, the backup still wins if the file is not byte-identical afterwards. `--file` needs `--break-cmd`, because the backup must be taken before the break. `--timeout` limits each command in seconds, and a test that times out instead of failing fails the control: a hang is not a caught break.
|
|
138
|
+
|
|
139
|
+
A failure only proves something if it is the failure you meant. A syntax error, a missing import or a mistyped test command also makes the test exit non-zero. `--expect` takes a regular expression that the failing run's output must match, and the control fails if it does not. Without `--expect`, output that looks like a syntax, import, collection or command-not-found error is flagged with a `WARNING` next to the `OK`, and you need to read the transcript to confirm the reason.
|
|
140
|
+
|
|
141
|
+
### Optional: Progress Pane for Claude Code
|
|
142
|
+
|
|
143
|
+
`aeprogress` is a Claude Code mod that keeps a pane beside the chat with the run's overall percentage, a bar per phase, who is working on which task ID (Claude Code subagents, Orca workers, or any other harness through a command of your own), what awaits audit and what needs attention. It reads the log through `aepkit status --json` and refreshes after each turn, and every 20 seconds while the pane is open. The protocol does not depend on it.
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
/plugin install aeprogress --marketplace tBeltty/auditor-executor-protocol
|
|
147
|
+
/aeprogress docs/<task-name>
|
|
148
|
+
```
|
|
136
149
|
|
|
137
|
-
|
|
150
|
+
It works in the terminal and in the desktop app's local sessions. Claude Code's mod API is early access, so a Claude Code release can break it. Setup, options and how the percentage is computed: [integrations/claude-code/aeprogress](https://github.com/tBeltty/auditor-executor-protocol/tree/main/integrations/claude-code/aeprogress).
|
|
138
151
|
|
|
139
152
|
---
|
|
140
153
|
|
|
@@ -146,7 +159,7 @@ A failure only proves something if it is the failure you meant. A syntax error,
|
|
|
146
159
|
| `aepkit install-skill [dir]` | `--agent`, `--global`, `--dest`, `--force` | Provision `SKILL.md` and `references/` into Antigravity, Claude Code, or Cursor |
|
|
147
160
|
| `aepkit lint <dir>` | `--annex-threshold` | Cross-check IDs both ways, flag `DONE` without evidence, detect missing negative controls, and flag log rot |
|
|
148
161
|
| `aepkit negcontrol` | `--test-cmd` (required), `--file`, `--break-cmd`, `--restore-cmd`, `--expect`, `--timeout` | Run automated backup, break, fail, restore, and pass cycle |
|
|
149
|
-
| `aepkit status <dir>` | | Combine status board verdicts with reports; list everything not `APPROVED` |
|
|
162
|
+
| `aepkit status <dir>` | `--json` | Combine status board verdicts with reports; list everything not `APPROVED` |
|
|
150
163
|
|
|
151
164
|
`python -m aepkit` works the same as `aepkit`. Errors go to stderr.
|
|
152
165
|
|
|
@@ -175,11 +188,11 @@ pytest
|
|
|
175
188
|
|
|
176
189
|
## Contributing
|
|
177
190
|
|
|
178
|
-
Contributions are welcome. [`CONTRIBUTING.md`](CONTRIBUTING.md) covers the development setup, the quality gates, and the PR checklist; participation follows the [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). Report vulnerabilities privately as described in [`SECURITY.md`](SECURITY.md). Changes are listed in [`CHANGELOG.md`](CHANGELOG.md).
|
|
191
|
+
Contributions are welcome. [`CONTRIBUTING.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/CONTRIBUTING.md) covers the development setup, the quality gates, and the PR checklist; participation follows the [`CODE_OF_CONDUCT.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/CODE_OF_CONDUCT.md). Report vulnerabilities privately as described in [`SECURITY.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/SECURITY.md). Changes are listed in [`CHANGELOG.md`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/CHANGELOG.md).
|
|
179
192
|
|
|
180
193
|
## License
|
|
181
194
|
|
|
182
|
-
MIT (see [`LICENSE`](LICENSE)).
|
|
195
|
+
MIT (see [`LICENSE`](https://github.com/tBeltty/auditor-executor-protocol/blob/main/LICENSE)).
|
|
183
196
|
|
|
184
197
|
---
|
|
185
198
|
|
|
@@ -188,7 +188,6 @@ done before task 1 is drafted:**
|
|
|
188
188
|
| Rule | Look for |
|
|
189
189
|
|---|---|
|
|
190
190
|
| Version bump per change | A version field (`package.json`, `pyproject.toml`, a `VERSION` file) and any script that already bumps it |
|
|
191
|
-
| No AI co-author trailers | `git log --grep="Co-Authored-By"` — existing trailers (or their consistent absence) |
|
|
192
191
|
| Fetch-before-push | Multiple contributors or branches in `git log`/`git branch -a` — a solo, single-branch repo needs this less |
|
|
193
192
|
| Quality gate green before push | CI config (`.github/workflows/`, `.gitlab-ci.yml`) or a `test`/`lint`/`build` script already wired together |
|
|
194
193
|
| i18n/locale parity | A locales/translations directory with more than one language file |
|
|
@@ -299,7 +298,7 @@ report's job is to point at the evidence, not to argue for a verdict.
|
|
|
299
298
|
for the whole feature.
|
|
300
299
|
8. **Before sending the verdict, check what it's missing, not just what it says.** This
|
|
301
300
|
applies to every verdict, not only a full-phase `APPROVED` or a `CONDITIONAL`/
|
|
302
|
-
`REJECTED` with an obvious remediation — a plain `
|
|
301
|
+
`REJECTED` with an obvious remediation — a plain `APPROVED` on one task, mid-phase, with
|
|
303
302
|
something else already outstanding (an earlier remediation, the next task) needs the
|
|
304
303
|
same discipline: it does not end the message on its own. If anything is left to do —
|
|
305
304
|
a remediation handoff, the next task's handoff, or a re-statement of a handoff already
|
|
@@ -423,7 +422,7 @@ If `aepkit` is installed, prefer it over doing these by hand:
|
|
|
423
422
|
compliance log in both directions, flag `DONE` reports with no pasted verify output
|
|
424
423
|
(output counts only inside a fenced code block; prose such as "all tests pass" or a lone
|
|
425
424
|
"n/a" is not output), flag work reported in a phase before every earlier phase is
|
|
426
|
-
`APPROVED`, flag near-duplicate paragraphs, warn on annex count per phase, flag gates with no
|
|
425
|
+
`APPROVED`, flag near-duplicate paragraphs, warn on annex count (listed per phase), flag gates with no
|
|
427
426
|
stated negative control (it must name the action that removes the protection and the
|
|
428
427
|
failure that follows; a negated, deferred, hypothetical, or waived mention such as "n/a",
|
|
429
428
|
"TBD", or "we could remove it" does not count), and—once work has started (`DONE` or a
|
|
@@ -437,12 +436,19 @@ If `aepkit` is installed, prefer it over doing these by hand:
|
|
|
437
436
|
file up, runs the break command, runs the test (expects failure), restores from the
|
|
438
437
|
backup, runs the test again (expects success), and prints a paste-ready transcript.
|
|
439
438
|
With `--restore-cmd`, the file must still end byte-identical to the backup or it is
|
|
440
|
-
restored from it
|
|
439
|
+
restored from it. `--file` needs `--break-cmd`, since the backup must come before the
|
|
440
|
+
break. `--timeout` bounds each command, and a test that times out instead of failing
|
|
441
|
+
fails the control. Pass `--expect "<regex>"` with the
|
|
441
442
|
failure the break should cause (an assertion message, a 403): a test that fails for
|
|
442
443
|
another reason, such as a syntax error or a missing import, then fails the control
|
|
443
444
|
instead of passing it. Without `--expect`, such output is only flagged as a warning.
|
|
444
445
|
- `aepkit status <dir>` — combines the status board verdicts with the latest report
|
|
445
446
|
per ID and prints what's still open. Only `APPROVED` closes an item; a `DONE` with no
|
|
446
|
-
verdict is listed as awaiting audit.
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
verdict is listed as awaiting audit. `--json` prints the same reading, with the phases
|
|
448
|
+
from the guide and the plan, for tools to use instead of parsing the log themselves.
|
|
449
|
+
- Optional, Claude Code only: the `aeprogress` mod keeps a pane beside the chat with the
|
|
450
|
+
run's progress per phase and who is working on which ID (subagents, Orca workers, or
|
|
451
|
+
any harness through a command). The protocol does not depend on it. See
|
|
452
|
+
`integrations/claude-code/aeprogress/` in the repository.
|
|
453
|
+
|
|
454
|
+
Install instructions: https://github.com/tBeltty/auditor-executor-protocol#quickstart
|
|
@@ -45,12 +45,15 @@ test = [
|
|
|
45
45
|
"pytest>=7.0",
|
|
46
46
|
"pytest-cov>=4.0",
|
|
47
47
|
]
|
|
48
|
+
# Exact pins, so a new linter rule or build-tool release cannot turn CI red or change a
|
|
49
|
+
# published build without a commit; Dependabot bumps them.
|
|
48
50
|
dev = [
|
|
49
51
|
"pytest>=7.0",
|
|
50
52
|
"pytest-cov>=4.0",
|
|
51
|
-
"ruff
|
|
52
|
-
"mypy
|
|
53
|
-
"build
|
|
53
|
+
"ruff==0.16.10",
|
|
54
|
+
"mypy==2.4.0",
|
|
55
|
+
"build==1.6.1",
|
|
56
|
+
"twine==7.0.0",
|
|
54
57
|
]
|
|
55
58
|
|
|
56
59
|
[project.scripts]
|
|
@@ -68,6 +71,8 @@ where = ["src"]
|
|
|
68
71
|
[tool.ruff]
|
|
69
72
|
line-length = 100
|
|
70
73
|
target-version = "py311"
|
|
74
|
+
# agent-produced run directories and experiment scripts are data, not package code
|
|
75
|
+
extend-exclude = ["benchmarks"]
|
|
71
76
|
|
|
72
77
|
[tool.ruff.lint]
|
|
73
78
|
select = ["E", "F", "W", "I", "UP", "B", "PL", "RUF"]
|
|
@@ -82,7 +87,7 @@ ignore = [
|
|
|
82
87
|
]
|
|
83
88
|
|
|
84
89
|
[tool.ruff.lint.per-file-ignores]
|
|
85
|
-
"tests/*" = ["RUF001"
|
|
90
|
+
"tests/*" = ["RUF001"]
|
|
86
91
|
|
|
87
92
|
[tool.mypy]
|
|
88
93
|
python_version = "3.11"
|
|
@@ -47,6 +47,10 @@ it from a governance decision in the plan of record. It has four parts:
|
|
|
47
47
|
made: <decision-entry IDs, or "none">. Next: <phase/task already launched>.
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
Between reports, an owner on Claude Code can follow the run in the optional
|
|
51
|
+
`aeprogress` pane (`integrations/claude-code/aeprogress/` in the repository), which
|
|
52
|
+
reads the compliance log and never messages the Auditor. The run does not depend on it.
|
|
53
|
+
|
|
50
54
|
Harness permission prompts that need the owner go in **one batched request**, not one
|
|
51
55
|
at a time. When every phase is `APPROVED` and the plan of record declares no further
|
|
52
56
|
phase, say so as its own line — see "Closing the run" in `SKILL.md`.
|
|
@@ -91,7 +95,7 @@ each handoff. The shape that has worked here:
|
|
|
91
95
|
its first task.
|
|
92
96
|
|
|
93
97
|
Do not end a turn between a verdict and the next launch unless something on the
|
|
94
|
-
critical-risk list is open. A run that stops to report
|
|
98
|
+
critical-risk list is open. A run that stops to report an `APPROVED` and wait is Guided
|
|
95
99
|
mode by accident.
|
|
96
100
|
|
|
97
101
|
### Starting the Auditor (the Auditor brief)
|
|
@@ -23,7 +23,7 @@ this file.
|
|
|
23
23
|
- **Silent model inheritance.** A subagent launched without an explicit model runs on
|
|
24
24
|
the Auditor's own model. Nothing fails; the rule is just broken on every task. Pass
|
|
25
25
|
it every time.
|
|
26
|
-
- **Guided by habit.** In Autonomous mode, the Auditor ends a turn after
|
|
26
|
+
- **Guided by habit.** In Autonomous mode, the Auditor ends a turn after an `APPROVED` to
|
|
27
27
|
"report," or asks the owner a question the charter already lets it decide. Each one
|
|
28
28
|
costs the owner a message and adds nothing.
|
|
29
29
|
- **A defect ships and nobody owns re-checking its blast radius.** A task changes what
|
|
@@ -120,7 +120,7 @@ Auditor's own rule 4 ("The Auditor is bound by rule 4 too" in `SKILL.md`).
|
|
|
120
120
|
|
|
121
121
|
**No verdict ends the loop by itself if anything is left to run — and "anything left to
|
|
122
122
|
run" is not only "a phase remains" or "a remediation order is needed."** It also covers
|
|
123
|
-
the most common case of all: a plain `
|
|
123
|
+
the most common case of all: a plain `APPROVED` on one task, mid-phase, with something else
|
|
124
124
|
already outstanding — an earlier remediation, the next task in line. That case has no
|
|
125
125
|
name of its own in the Verdicts table of `SKILL.md`, which is exactly why it's the easiest one
|
|
126
126
|
to ship without a handoff: closing with a status sentence that *describes* what's still
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.0"
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""aepkit — mechanical support for the Auditor/Executor protocol.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
The protocol itself is SKILL.md:
|
|
4
|
+
https://github.com/tBeltty/auditor-executor-protocol/blob/main/SKILL.md
|
|
5
|
+
|
|
6
|
+
This CLI operationalizes the parts of it that are checklist work, not judgment:
|
|
7
|
+
scaffolding the document set, cross-checking it for drift, running a negative control
|
|
8
|
+
end to end, and reporting status.
|
|
7
9
|
"""
|
|
8
10
|
|
|
9
11
|
from __future__ import annotations
|
|
@@ -15,7 +17,9 @@ from . import __version__, install_skill, lint, negcontrol, scaffold, status
|
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
def build_parser() -> argparse.ArgumentParser:
|
|
18
|
-
parser = argparse.ArgumentParser(
|
|
20
|
+
parser = argparse.ArgumentParser(
|
|
21
|
+
prog="aepkit", description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
|
|
22
|
+
)
|
|
19
23
|
parser.add_argument("--version", action="version", version=f"aepkit {__version__}")
|
|
20
24
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
21
25
|
|
|
@@ -39,7 +43,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
39
43
|
type=int,
|
|
40
44
|
metavar="N",
|
|
41
45
|
default=lint.DEFAULT_ANNEX_THRESHOLD,
|
|
42
|
-
help="flag more annexes than this,
|
|
46
|
+
help="flag more annexes than this in total, listed per phase (default: %(default)s)",
|
|
43
47
|
)
|
|
44
48
|
|
|
45
49
|
p_neg = sub.add_parser("negcontrol", help="run a backup/break/test/restore/test cycle")
|
|
@@ -85,6 +89,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
85
89
|
"status", help="tally verdicts and reports in the compliance log; only APPROVED closes"
|
|
86
90
|
)
|
|
87
91
|
p_status.add_argument("target_dir", help="directory holding compliance-log.md")
|
|
92
|
+
p_status.add_argument(
|
|
93
|
+
"--json",
|
|
94
|
+
dest="as_json",
|
|
95
|
+
action="store_true",
|
|
96
|
+
help="print the reading as JSON, with phases from the execution guide and the plan",
|
|
97
|
+
)
|
|
88
98
|
|
|
89
99
|
p_install = sub.add_parser(
|
|
90
100
|
"install-skill", help="provision SKILL.md and references/ into an agent skills directory"
|
|
@@ -137,7 +147,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
137
147
|
expect=args.expect,
|
|
138
148
|
)
|
|
139
149
|
if args.command == "status":
|
|
140
|
-
return status.run(args.target_dir)
|
|
150
|
+
return status.run(args.target_dir, as_json=args.as_json)
|
|
141
151
|
return install_skill.run(
|
|
142
152
|
target_dir=args.target_dir,
|
|
143
153
|
agent=args.agent,
|
|
@@ -61,7 +61,7 @@ def _detect_agent(target: Path) -> str:
|
|
|
61
61
|
# 2. Check runtime environment variables
|
|
62
62
|
if os.environ.get("ANTIGRAVITY_AGENT") or os.environ.get("GEMINI_CLI"):
|
|
63
63
|
return "antigravity"
|
|
64
|
-
if os.environ.get(
|
|
64
|
+
if any(os.environ.get(v) for v in ("CLAUDECODE", "CLAUDE_CODE", "CLAUDE_PROJECT_DIR")):
|
|
65
65
|
return "claude"
|
|
66
66
|
if os.environ.get("CURSOR_PROJECT_DIR"):
|
|
67
67
|
return "cursor"
|
|
@@ -13,6 +13,8 @@ import sys
|
|
|
13
13
|
from collections import Counter, defaultdict
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
+
from .status import VERDICTS, _cells, board_verdicts
|
|
17
|
+
|
|
16
18
|
REPORT_ID_RE = re.compile(r"\*\*Report:\*\*\s*`?([A-Za-z0-9_.\-]+)`?")
|
|
17
19
|
GUIDE_HEADER_RE = re.compile(r"^###\s+([A-Za-z0-9_.\-]+)\s+[\u2014\u2013\-]\s*(.*)$", re.MULTILINE)
|
|
18
20
|
LOG_HEADER_RE = re.compile(
|
|
@@ -95,7 +97,6 @@ FENCE_LINE_RE = re.compile(r"^\s*(```|~~~)[\w-]*\s*$", re.MULTILINE)
|
|
|
95
97
|
FENCED_BLOCK_RE = re.compile(r"^\s*(```|~~~)[\w-]*\s*\n(.*?)^\s*\1\s*$", re.MULTILINE | re.DOTALL)
|
|
96
98
|
PHASE_RE = re.compile(r"^P(\d+)-", re.IGNORECASE)
|
|
97
99
|
DEFERRED_PHASE_RE = re.compile(r"\b(?:P|Phase\s*)(\d+)\b", re.IGNORECASE)
|
|
98
|
-
VERDICTS = {"APPROVED", "CONDITIONAL", "REJECTED"}
|
|
99
100
|
DETERMINISTIC_BYPASS_RE = re.compile(
|
|
100
101
|
r"\b(?:n/?a|not\s+applicable)\b[^\n]{0,60}\b"
|
|
101
102
|
r"(?:deterministic|bug\s*fix|fix|compliance|security|patch|migration)\b",
|
|
@@ -170,6 +171,11 @@ def _is_gate(task_id: str, title: str) -> bool:
|
|
|
170
171
|
return bool(GATE_ID_RE.search(task_id)) or "gate" in title.lower()
|
|
171
172
|
|
|
172
173
|
|
|
174
|
+
def _mask_fences(text: str) -> str:
|
|
175
|
+
"""text with every fenced block blanked out, keeping offsets and line breaks."""
|
|
176
|
+
return FENCED_BLOCK_RE.sub(lambda b: re.sub(r"[^\n]", " ", b.group(0)), text)
|
|
177
|
+
|
|
178
|
+
|
|
173
179
|
def _done_without_evidence(log_text: str) -> list[str]:
|
|
174
180
|
"""IDs reported DONE whose **Verify output:** field is missing, empty, or only defers.
|
|
175
181
|
|
|
@@ -183,15 +189,16 @@ def _done_without_evidence(log_text: str) -> list[str]:
|
|
|
183
189
|
if m.group(2).upper() != "DONE":
|
|
184
190
|
continue
|
|
185
191
|
rest = log_text[m.end() :]
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
# A "#", "---" or "**Field:**" line inside pasted output does not end the report.
|
|
193
|
+
masked = _mask_fences(rest)
|
|
194
|
+
end = SECTION_END_RE.search(masked)
|
|
195
|
+
body_end = end.start() if end else len(rest)
|
|
196
|
+
field = VERIFY_FIELD_RE.search(masked, 0, body_end)
|
|
189
197
|
if not field:
|
|
190
198
|
missing.append(m.group(1))
|
|
191
199
|
continue
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
evidence = after[: nxt.start()] if nxt else after
|
|
200
|
+
nxt = NEXT_FIELD_RE.search(masked, field.end(), body_end)
|
|
201
|
+
evidence = rest[field.end() : nxt.start() if nxt else body_end]
|
|
195
202
|
blocks = [
|
|
196
203
|
PLACEHOLDER_RE.sub("", b.group(2)).strip() for b in FENCED_BLOCK_RE.finditer(evidence)
|
|
197
204
|
]
|
|
@@ -202,18 +209,23 @@ def _done_without_evidence(log_text: str) -> list[str]:
|
|
|
202
209
|
|
|
203
210
|
def _phase_order_violations(guide_text: str, log_text: str) -> list[str]:
|
|
204
211
|
"""IDs reported DONE in a phase that started before every earlier phase was APPROVED."""
|
|
205
|
-
from .status import _board_verdicts
|
|
206
|
-
|
|
207
212
|
ids = [task_id for task_id, _, _ in _guide_sections(guide_text) if TASK_ID_RE.search(task_id)]
|
|
208
|
-
|
|
213
|
+
board = {k: v for k, v in board_verdicts(log_text).items() if v in VERDICTS}
|
|
214
|
+
# As in `aepkit status`: the last report header per ID wins, and a board verdict that
|
|
215
|
+
# disagrees with the header verdict is a conflict, which does not count as APPROVED.
|
|
216
|
+
header_verdicts: dict[str, str] = {}
|
|
209
217
|
reports: dict[str, str] = {}
|
|
210
218
|
for m in LOG_HEADER_RE.finditer(log_text):
|
|
211
219
|
task_id, value = m.group(1), m.group(2).upper()
|
|
212
220
|
ids.append(task_id)
|
|
213
221
|
if value in VERDICTS:
|
|
214
|
-
|
|
222
|
+
header_verdicts[task_id] = value
|
|
215
223
|
else:
|
|
216
224
|
reports[task_id] = value
|
|
225
|
+
verdicts = {**header_verdicts, **board}
|
|
226
|
+
for task_id, value in header_verdicts.items():
|
|
227
|
+
if board.get(task_id, value) != value:
|
|
228
|
+
verdicts[task_id] = "CONFLICT"
|
|
217
229
|
phases: defaultdict[int, set[str]] = defaultdict(set)
|
|
218
230
|
for task_id in ids:
|
|
219
231
|
found = PHASE_RE.match(task_id)
|
|
@@ -291,9 +303,7 @@ def _plan_gaps(plan_text: str) -> list[str]:
|
|
|
291
303
|
def _open_deferred_items(plan_text: str, known_ids: set[str], log_text: str) -> list[str]:
|
|
292
304
|
"""Flag deferred-ledger rows whose closing task ID does not exist or remains empty when
|
|
293
305
|
its target phase is being closed."""
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
verdicts = {k: v for k, v in _board_verdicts(log_text).items() if v in VERDICTS}
|
|
306
|
+
verdicts = {k: v for k, v in board_verdicts(log_text).items() if v in VERDICTS}
|
|
297
307
|
reports: dict[str, str] = {}
|
|
298
308
|
for m in LOG_HEADER_RE.finditer(log_text):
|
|
299
309
|
tid, val = m.group(1), m.group(2).upper()
|
|
@@ -476,10 +486,8 @@ def run(target_dir: str, annex_threshold: int = DEFAULT_ANNEX_THRESHOLD) -> int:
|
|
|
476
486
|
# 3b. Plan of record: opportunity/shaping/non-goals (once work starts) and deferred items.
|
|
477
487
|
plan_path = target / "plan-of-record.md"
|
|
478
488
|
if plan_path.is_file():
|
|
479
|
-
from .status import _board_verdicts
|
|
480
|
-
|
|
481
489
|
plan_text = _read(plan_path)
|
|
482
|
-
board =
|
|
490
|
+
board = board_verdicts(log_text)
|
|
483
491
|
work_started = any(
|
|
484
492
|
v == "DONE" or v in VERDICTS for v in (*log_status.values(), *board.values())
|
|
485
493
|
)
|
|
@@ -528,8 +536,7 @@ def run(target_dir: str, annex_threshold: int = DEFAULT_ANNEX_THRESHOLD) -> int:
|
|
|
528
536
|
else:
|
|
529
537
|
phase_counts["General"] += 1
|
|
530
538
|
|
|
531
|
-
|
|
532
|
-
if len(annex_files) > annex_threshold or phase_exceeded:
|
|
539
|
+
if len(annex_files) > annex_threshold:
|
|
533
540
|
problems += 1
|
|
534
541
|
print(
|
|
535
542
|
f"[annex count] {len(annex_files)} annexes exceeds the threshold of "
|