aepkit 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aepkit/__init__.py +1 -0
- aepkit/__main__.py +7 -0
- aepkit/cli.py +151 -0
- aepkit/install_skill.py +126 -0
- aepkit/lint.py +549 -0
- aepkit/negcontrol.py +234 -0
- aepkit/py.typed +0 -0
- aepkit/scaffold.py +40 -0
- aepkit/skill/SKILL.md +448 -0
- aepkit/skill/references/autonomous-mode.md +144 -0
- aepkit/skill/references/failure-modes-and-example.md +103 -0
- aepkit/skill/references/handoffs.md +133 -0
- aepkit/skill/references/tasks-and-gates.md +84 -0
- aepkit/status.py +122 -0
- aepkit/templates/compliance-log.md +37 -0
- aepkit/templates/execution-guide.md +75 -0
- aepkit/templates/plan-of-record.md +79 -0
- aepkit-0.6.0.dist-info/METADATA +223 -0
- aepkit-0.6.0.dist-info/RECORD +23 -0
- aepkit-0.6.0.dist-info/WHEEL +5 -0
- aepkit-0.6.0.dist-info/entry_points.txt +2 -0
- aepkit-0.6.0.dist-info/licenses/LICENSE +21 -0
- aepkit-0.6.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Failure Modes and Worked Example
|
|
2
|
+
|
|
3
|
+
Part of the `auditor-executor-protocol` skill. Load when auditing a delivery or looking for a filled-in run. Section names in
|
|
4
|
+
quotes ("Rules of engagement", "Auditing", ...) refer to `SKILL.md` unless they are in
|
|
5
|
+
this file.
|
|
6
|
+
|
|
7
|
+
## Failure modes seen in practice
|
|
8
|
+
|
|
9
|
+
- **Self-expansion.** The Executor implements phases marked "do not start." Work
|
|
10
|
+
arrives with no checkpoints between phases; a defect in an early one gets built on
|
|
11
|
+
before anyone looks.
|
|
12
|
+
- **Zero stops.** An empty "Blocked" section across dozens of tasks means ambiguities
|
|
13
|
+
were resolved silently, not that none existed.
|
|
14
|
+
- **Letter over intent.** The task says "add a check for case X"; a check appears, it
|
|
15
|
+
passes, and the condition X exists to detect is routed around by fixture ordering or
|
|
16
|
+
test isolation. Anticipate this by naming the expected failure in the task itself.
|
|
17
|
+
- **Evidence-free `DONE`.** Treat as `FAILED`. Say so in the reporting rules up front.
|
|
18
|
+
- **The Auditor shrinks the goal.** Asked for a whole outcome, the plan delivers a
|
|
19
|
+
slice of it and calls the rest "follow-up," backed by a risk/ROI argument. Shrinking
|
|
20
|
+
the goal is never authorized; splitting it into phases is fine, as long as the goal
|
|
21
|
+
is met. Big goals get more phases, never a smaller goal — later phases are part of
|
|
22
|
+
the run, not a follow-up.
|
|
23
|
+
- **Silent model inheritance.** A subagent launched without an explicit model runs on
|
|
24
|
+
the Auditor's own model. Nothing fails; the rule is just broken on every task. Pass
|
|
25
|
+
it every time.
|
|
26
|
+
- **Guided by habit.** In Autonomous mode, the Auditor ends a turn after a `PASS` to
|
|
27
|
+
"report," or asks the owner a question the charter already lets it decide. Each one
|
|
28
|
+
costs the owner a message and adds nothing.
|
|
29
|
+
- **A defect ships and nobody owns re-checking its blast radius.** A task changes what
|
|
30
|
+
an existing field means; every other reader of that field is now a latent bug, and
|
|
31
|
+
the person who finds it is usually a different, unrelated task that happens to hit
|
|
32
|
+
it — not a re-audit of the original task. Grep for every reader before signing off,
|
|
33
|
+
not after something breaks.
|
|
34
|
+
- **A change primes something to fire on its own before anyone can see or stop it.** A
|
|
35
|
+
migration or config change that alters what triggers an automated write can leave
|
|
36
|
+
the system armed to act — at scale, on real data — the moment its ordinary trigger
|
|
37
|
+
next runs, with no one having pressed a button and no UI yet built to see or cancel
|
|
38
|
+
it. Check the state the change leaves behind, not only the correctness of the new
|
|
39
|
+
code path.
|
|
40
|
+
|
|
41
|
+
## Worked example
|
|
42
|
+
|
|
43
|
+
Everything below is fictional and sanitized. It shows the shape of each artifact, not
|
|
44
|
+
content to copy. Names, commands and risks in a real run come from that project.
|
|
45
|
+
|
|
46
|
+
**Plan:** "Notifications as a standalone service" in a web shop. Goal set by the owner:
|
|
47
|
+
every notification leaves the monolith. Acceptance criteria AC1-AC4. Phases P0-P5, with
|
|
48
|
+
P0-P2 expanded into tasks and P3-P5 fixed in scope, expanded by the Auditor at each gate.
|
|
49
|
+
Mode: Autonomous (governance decision D9 in the plan of record).
|
|
50
|
+
|
|
51
|
+
**Rules-of-engagement block (item 8)**, as it would appear in each handoff:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
- One task at a time.
|
|
55
|
+
- Verification = run the command and the negative controls, not read the code.
|
|
56
|
+
- Fetch and compare against the remote branch before starting and before pushing.
|
|
57
|
+
- <project's quality-gate command> exits 0 before any push.
|
|
58
|
+
- Every commit to the main branch bumps the version with <project's version script>.
|
|
59
|
+
- No AI tool co-authorship on commits.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**Auditor brief (abridged):**
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
ROLE: Auditor of plan "Notifications as a standalone service". You do not write feature
|
|
66
|
+
code. You run this in Autonomous mode: launch Executors as subagents, audit each
|
|
67
|
+
delivery by re-running it, resolve their questions, expand P3-P5 once the previous gate
|
|
68
|
+
is APPROVED. Follow the auditor-executor-protocol skill.
|
|
69
|
+
|
|
70
|
+
AUTONOMY (D9; "Coordination and autonomy" section of the guide):
|
|
71
|
+
- You decide anything the documents leave open; amend decisions by annex with evidence.
|
|
72
|
+
You never amend AC1-AC4 or reduce scope.
|
|
73
|
+
- BLOCKED → decision entry in the log; the task continues.
|
|
74
|
+
- Permissions needing the owner: one batched request. Short report per closed phase.
|
|
75
|
+
- ONLY reasons to interrupt the owner:
|
|
76
|
+
1. Reducing or reinterpreting AC1-AC4.
|
|
77
|
+
2. Deleting production data without a verified, restorable backup.
|
|
78
|
+
3. A real email or SMS sent to a customer.
|
|
79
|
+
4. Charges, refunds, or billing changes not proven equivalent.
|
|
80
|
+
5. Production down after a deploy with no successful rollback.
|
|
81
|
+
6. An exposed secret.
|
|
82
|
+
7. Force-push or disabling CI.
|
|
83
|
+
|
|
84
|
+
EXECUTOR MODEL ASSIGNMENT: small/fast tier for P0-T1, P1-T3, P2-T4 (listed commands,
|
|
85
|
+
bump, gate, push). Mid tier for the rest. Top tier never. Model explicit on every
|
|
86
|
+
launch; if a small-tier task goes off-script, relaunch the whole task on the mid tier.
|
|
87
|
+
|
|
88
|
+
STATE: clean tree, plan committed and pushed. First task: P0-T1 on the small/fast tier.
|
|
89
|
+
|
|
90
|
+
GOAL (non-negotiable): no notification leaves the monolith. RUN COMPLETE only with
|
|
91
|
+
P5-G1 APPROVED, which proves AC1-AC4.
|
|
92
|
+
|
|
93
|
+
WHAT'S VERIFIED AND WHAT ISN'T:
|
|
94
|
+
- Verified (command in the plan): all 14 send sites in the inventory.
|
|
95
|
+
- Not verified: whether the SMS provider accepts idempotency keys (P2-T2 measures it).
|
|
96
|
+
|
|
97
|
+
TRAPS: "doesn't send" checks that pass because the mock was never registered; negative
|
|
98
|
+
controls restored with a command that wipes other uncommitted work; a small-tier task
|
|
99
|
+
that patches an unexpected surprise itself instead of escalating.
|
|
100
|
+
|
|
101
|
+
FIRST STEP: read the three documents, confirm the log is empty, launch P0-T1 on the
|
|
102
|
+
small/fast tier.
|
|
103
|
+
```
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Handoffs
|
|
2
|
+
|
|
3
|
+
Part of the `auditor-executor-protocol` skill. Load when handing a task to an Executor or writing a remediation order. Section names in
|
|
4
|
+
quotes ("Rules of engagement", "Auditing", ...) refer to `SKILL.md` unless they are in
|
|
5
|
+
this file.
|
|
6
|
+
|
|
7
|
+
## Handing off a task (Auditor)
|
|
8
|
+
|
|
9
|
+
Writing the execution guide is not the handoff. The Executor's session starts cold — it
|
|
10
|
+
has none of the investigation behind the guide, and "read the execution guide, task
|
|
11
|
+
`<ID>`" is a pointer to an instruction, not the instruction itself. Producing the actual,
|
|
12
|
+
paste-ready message for a fresh Executor session is Auditor work, not something left for
|
|
13
|
+
whoever is relaying the plan to assemble by hand.
|
|
14
|
+
|
|
15
|
+
**The rules-of-engagement block in the template below is item 8, settled when the run
|
|
16
|
+
started** ("Rules of engagement" in `SKILL.md`) — not re-derived here, and not re-asked per
|
|
17
|
+
task. If you're about to write a handoff and item 8 is still blank, that's the actual
|
|
18
|
+
problem: go settle it before drafting the message, don't paste a rule set from a
|
|
19
|
+
different project or a different run to fill the gap. A rule copied in from elsewhere
|
|
20
|
+
either asserts something false about this codebase or hands the Executor a stop it has
|
|
21
|
+
no way to satisfy.
|
|
22
|
+
|
|
23
|
+
### Handoff template
|
|
24
|
+
|
|
25
|
+
One task per message, addressed to a fresh Executor session with no shared context. Fill
|
|
26
|
+
every section — do not leave "see the execution guide" where a fact belongs.
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
ROLE: Executor. Work from <EXECUTION_GUIDE> (Phase <n>, `<TASK-ID>`) only; report in
|
|
30
|
+
<COMPLIANCE_LOG>, section `<TASK-ID>` (already exists, empty).
|
|
31
|
+
|
|
32
|
+
[Autonomous mode only:] You are the Auditor's subagent. Questions go as BLOCKED in the
|
|
33
|
+
log and in your final report; you do not address the owner.
|
|
34
|
+
|
|
35
|
+
REPORTED STATE: <what is already DONE in the log, and whether it was audited or only
|
|
36
|
+
self-reported — do not conflate the two>. Do not reopen <prior tasks>.
|
|
37
|
+
|
|
38
|
+
TASK: <TASK-ID> — <imperative title>
|
|
39
|
+
Source: <EXECUTION_GUIDE>, section "<TASK-ID>" in full.
|
|
40
|
+
|
|
41
|
+
Goal: <one sentence — what is true after this that was not before>.
|
|
42
|
+
|
|
43
|
+
Key finding (already verified, do not re-check it): <the concrete fact driving this
|
|
44
|
+
task, with the file/line/command that established it>.
|
|
45
|
+
|
|
46
|
+
Decided points, not reopenable without evidence that contradicts them:
|
|
47
|
+
1. <decision>
|
|
48
|
+
2. <decision>
|
|
49
|
+
...
|
|
50
|
+
|
|
51
|
+
Failure to avoid explicitly: <the specific mistake a context-free Executor would make by
|
|
52
|
+
reflex — copying a neighboring task's filter that doesn't apply here, re-deriving a
|
|
53
|
+
fact that was already established and getting it wrong, etc.>.
|
|
54
|
+
|
|
55
|
+
Verify (literal):
|
|
56
|
+
<exact command>
|
|
57
|
+
|
|
58
|
+
Success, minimum: <what the check(s) must prove, in terms of cases — not "it passes">.
|
|
59
|
+
|
|
60
|
+
Report: <TASK-ID>, in <COMPLIANCE_LOG>, section already created.
|
|
61
|
+
|
|
62
|
+
RULES OF ENGAGEMENT (this run's — confirmed at the start, not a default):
|
|
63
|
+
<the block settled above>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`Key finding` and `Failure to avoid` exist because a fresh Executor has no memory of the
|
|
67
|
+
investigation behind the task — it will make exactly the mistake full context would have
|
|
68
|
+
prevented. Naming the specific reflex to avoid is cheaper than an Executor discovering it
|
|
69
|
+
mid-task.
|
|
70
|
+
|
|
71
|
+
Do not paste a rules-of-engagement block from one project's run into another's handoff
|
|
72
|
+
without re-confirming it applies. It is a per-run artifact, not part of this skill.
|
|
73
|
+
|
|
74
|
+
## Remediation handoff (Auditor)
|
|
75
|
+
|
|
76
|
+
A verdict is not the deliverable — a well-evidenced `CONDITIONAL` or `REJECTED` that
|
|
77
|
+
ends in prose still leaves the Executor with nothing to act on. This is the
|
|
78
|
+
`Remediation order (annex)` from "The document set": write it, and hand it off the same
|
|
79
|
+
way the first task was handed off, not as a narrative the reader has to translate into
|
|
80
|
+
next steps themselves.
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
ROLE: Executor. Work from this remediation only; the rest of <EXECUTION_GUIDE> is
|
|
84
|
+
unaffected unless named below. Report in <COMPLIANCE_LOG>, appended under <GATE-ID> —
|
|
85
|
+
do not overwrite the original entry.
|
|
86
|
+
|
|
87
|
+
MODEL: <the original task's, or the next tier up if this is an escalation>.
|
|
88
|
+
|
|
89
|
+
AUDIT RESULT: <Phase/Task> — <CONDITIONAL | REJECTED>. <one line: what was
|
|
90
|
+
independently re-verified and passed, so the Executor knows what not to touch>.
|
|
91
|
+
|
|
92
|
+
BLOCKED: <GATE-ID> — <what's actually wrong, in the Auditor's own re-run terms, not a
|
|
93
|
+
restatement of what the original delivery claimed>.
|
|
94
|
+
|
|
95
|
+
Root cause (verified — carries the command that established it, not reasoned from the
|
|
96
|
+
code): <the mechanism, with file/line and the falsifying probe or command that proved
|
|
97
|
+
it wrong>.
|
|
98
|
+
|
|
99
|
+
Candidate fix — a recommendation, not a decided point; the Auditor has not run it:
|
|
100
|
+
<the shape of a fix. Mark explicitly as unverified — the Executor confirms it, and may
|
|
101
|
+
find a better one, per "a factual claim always is reopenable">.
|
|
102
|
+
|
|
103
|
+
Do not touch: <what already passed and must not be disturbed by this fix — the other
|
|
104
|
+
approved gates or tasks in this delivery>.
|
|
105
|
+
|
|
106
|
+
Verify (literal): <the exact re-run command(s), including re-running the falsifying
|
|
107
|
+
probe that caught this — it must now pass>.
|
|
108
|
+
|
|
109
|
+
Success, minimum: <what must be true afterward — the probe that failed now passes, the
|
|
110
|
+
original suite stays green, nothing named under "Do not touch" moved>.
|
|
111
|
+
|
|
112
|
+
Report: <GATE-ID>, in <COMPLIANCE_LOG>, as a remediation entry.
|
|
113
|
+
|
|
114
|
+
RULES OF ENGAGEMENT: <this run's block>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The "candidate fix, not a decided point" framing matters — the Auditor found the defect
|
|
118
|
+
by running a probe, not by running the fix. Presenting it as settled would violate the
|
|
119
|
+
Auditor's own rule 4 ("The Auditor is bound by rule 4 too" in `SKILL.md`).
|
|
120
|
+
|
|
121
|
+
**No verdict ends the loop by itself if anything is left to run — and "anything left to
|
|
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 `PASS` on one task, mid-phase, with something else
|
|
124
|
+
already outstanding — an earlier remediation, the next task in line. That case has no
|
|
125
|
+
name of its own in the Verdicts table of `SKILL.md`, which is exactly why it's the easiest one
|
|
126
|
+
to ship without a handoff: closing with a status sentence that *describes* what's still
|
|
127
|
+
pending, instead of resending the pasteable block for it. Only a genuine "nothing left"
|
|
128
|
+
closes without a handoff. Otherwise: send the outstanding remediation, or the next
|
|
129
|
+
phase's first task, or the handoff already sent and not yet acted on — with the literal
|
|
130
|
+
template from "Remediation handoff" or "Handing off a task" above, in the same message
|
|
131
|
+
as the verdict, never as a sentence describing it. The Executor's next session starts
|
|
132
|
+
cold no matter which verdict just landed; a one-line summary of what's still owed is
|
|
133
|
+
exactly the kind of pointer-not-an-instruction this whole document exists to close.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Writing Tasks and Gates
|
|
2
|
+
|
|
3
|
+
Part of the `auditor-executor-protocol` skill. Load when writing a task or a gate. Section names in
|
|
4
|
+
quotes ("Rules of engagement", "Auditing", ...) refer to `SKILL.md` unless they are in
|
|
5
|
+
this file.
|
|
6
|
+
|
|
7
|
+
## Writing a task (Auditor)
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
### <TASK-ID> — <imperative, one line>
|
|
11
|
+
|
|
12
|
+
**Goal:** one sentence. What is true after this that was not before.
|
|
13
|
+
**Files:** every path the Executor should read or change.
|
|
14
|
+
**Blast radius:** every caller or reader of anything a step renames, empties, deletes, or
|
|
15
|
+
redefines — including a later task's planned work. Empty if genuinely none; never left
|
|
16
|
+
blank.
|
|
17
|
+
**Steps:** numbered. Exact enough that two Executors produce the same thing.
|
|
18
|
+
**Verify:** the literal command, and the output that counts as success.
|
|
19
|
+
**Report:** the task ID.
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Rules for the steps:
|
|
23
|
+
|
|
24
|
+
- Encode decisions, do not re-open them. "Polling, not a push channel — this is
|
|
25
|
+
decided, believing otherwise is a stop, not a choice to make while implementing."
|
|
26
|
+
- **Name the failure you expect.** If a field might get overwritten, if a type behaves
|
|
27
|
+
differently across two environments, if a cap is a layout decision and not a data
|
|
28
|
+
one — say so in the task. Most bad output comes from an ambiguity the task's author
|
|
29
|
+
already saw and didn't write down.
|
|
30
|
+
- State what must *not* change alongside what must.
|
|
31
|
+
- **The Blast radius field is not optional decoration — write it before the step, not
|
|
32
|
+
after something breaks.** When a task renames or redefines what an existing field or
|
|
33
|
+
column means, the task text must include the command to find every other reader of
|
|
34
|
+
it, and the Executor must run it and account for every hit, not only the call sites
|
|
35
|
+
the task's file list happened to name. When a step would empty, delete, or replace
|
|
36
|
+
shared code, check whether a *later* task in the same phase was planned to still call
|
|
37
|
+
it — if so, the step is "leave a delegating stub," not "empty it," until the task that
|
|
38
|
+
owns that caller lands. A task's file list is a lower bound on its blast radius,
|
|
39
|
+
discovered by the person who wrote the task before the work started — not the whole
|
|
40
|
+
of it, discovered later by whoever happens to hit the stale or broken reader next.
|
|
41
|
+
- **When a task changes what triggers an automatic action** (a scheduler, a background
|
|
42
|
+
job, a materializer, anything that can fire without a human pressing a button), the
|
|
43
|
+
task must require checking — before shipping — what state the change leaves the
|
|
44
|
+
system in and whether anything is now primed to fire destructively on its next
|
|
45
|
+
ordinary trigger. Passing tests prove the new code path is correct; they do not
|
|
46
|
+
prove nothing is about to run against real data the moment it gets the chance.
|
|
47
|
+
|
|
48
|
+
## Writing a gate (Auditor)
|
|
49
|
+
|
|
50
|
+
A gate is a claim that can be proven false. "Tests pass" is not a gate. "The
|
|
51
|
+
visibility check was observed failing with the filter removed" is.
|
|
52
|
+
|
|
53
|
+
Every gate names how it is proven. Include, whenever the phase produces a security,
|
|
54
|
+
privacy, or financial-integrity boundary, a **negative control**: the Executor must
|
|
55
|
+
remove the protection, observe the check fail, restore it, and observe it pass. A
|
|
56
|
+
check never seen failing has not been verified. It must also fail for the right
|
|
57
|
+
reason: a syntax error or a missing import fails just as loudly, so the control names
|
|
58
|
+
the failure it expects (the assertion, the status code) and the Auditor checks the
|
|
59
|
+
output shows it. `aepkit negcontrol` runs this sequence and produces a paste-ready
|
|
60
|
+
transcript; `--expect "<regex>"` makes it reject a failure whose output does not match.
|
|
61
|
+
|
|
62
|
+
**A gate defined by more than one command must be re-run in full, not cited from
|
|
63
|
+
whichever half was last checked.** A compound gate closed in one phase by running only
|
|
64
|
+
one of its checks, then cited as "already satisfied" in later phase gates without ever
|
|
65
|
+
re-running the rest, can let real violations accumulate for phases before anyone
|
|
66
|
+
notices. If a gate's own definition lists multiple checks, every phase that claims it as
|
|
67
|
+
satisfied re-runs all of them — a partial citation from an earlier phase does not close
|
|
68
|
+
a compound gate.
|
|
69
|
+
|
|
70
|
+
**A shared test fixture that always uses a simplified shape hides bugs that only appear
|
|
71
|
+
with a realistic one.** A helper that builds request URLs, headers, or IDs for tests
|
|
72
|
+
should default to a realistic shape (a base URL with its own path prefix, a populated
|
|
73
|
+
auth header, a non-empty ID) rather than the minimal shape that's easiest to write —
|
|
74
|
+
otherwise every test built on it passes for the wrong reason. When gating a feature that
|
|
75
|
+
touches an external contract, prove it against at least one realistically-shaped
|
|
76
|
+
fixture, not only the simplified one most unit tests use.
|
|
77
|
+
|
|
78
|
+
**When a decision restricts what one channel may carry, check every other channel
|
|
79
|
+
available for the same call.** A rule like "this value travels only via the field
|
|
80
|
+
scoped for it, never the generic payload" is not proven by testing the transport where
|
|
81
|
+
that rule was enforced — check whether the same data can leak through a sibling channel
|
|
82
|
+
on a transport the restriction was never applied to, especially one that leaves your own
|
|
83
|
+
infrastructure for a third party. A restriction proven on one channel or transport is
|
|
84
|
+
not proven on all of them.
|
aepkit/status.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Tally the compliance log and print what is still open.
|
|
2
|
+
|
|
3
|
+
Two sources are combined per task/gate ID:
|
|
4
|
+
|
|
5
|
+
- the Auditor's verdict: the "Verdict" column of the status board table, or a report
|
|
6
|
+
header whose status is a verdict (APPROVED, CONDITIONAL, REJECTED);
|
|
7
|
+
- the Executor's latest report header (DONE, BLOCKED, FAILED, PENDING).
|
|
8
|
+
|
|
9
|
+
Only APPROVED closes an item. A DONE report without a verdict is self-reported, not
|
|
10
|
+
audited, so it is listed as awaiting audit. When the board and a report header carry
|
|
11
|
+
different verdicts, the item is listed as a conflict and stays open.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
import sys
|
|
18
|
+
from collections import Counter
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
LOG_HEADER_RE = re.compile(
|
|
22
|
+
r"^###\s+([A-Za-z0-9_.\-]+)\s+[\u2014\u2013\-]\s+([A-Za-z_]+)\s*$", re.MULTILINE
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
VERDICTS = {"APPROVED", "CONDITIONAL", "REJECTED"}
|
|
26
|
+
CLOSED = "APPROVED"
|
|
27
|
+
AWAITING_AUDIT = "AWAITING AUDIT"
|
|
28
|
+
CONFLICT = "CONFLICT"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _cells(line: str) -> list[str]:
|
|
32
|
+
return [c.strip().strip("`*").strip() for c in line.strip().strip("|").split("|")]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _board_verdicts(text: str) -> dict[str, str]:
|
|
36
|
+
"""ID -> verdict from the first table whose header has "ID" and "Verdict" columns."""
|
|
37
|
+
lines = text.splitlines()
|
|
38
|
+
for i, line in enumerate(lines):
|
|
39
|
+
if not line.lstrip().startswith("|"):
|
|
40
|
+
continue
|
|
41
|
+
header = [c.lower() for c in _cells(line)]
|
|
42
|
+
if "id" not in header or "verdict" not in header:
|
|
43
|
+
continue
|
|
44
|
+
id_col, verdict_col = header.index("id"), header.index("verdict")
|
|
45
|
+
verdicts: dict[str, str] = {}
|
|
46
|
+
for row in lines[i + 1 :]:
|
|
47
|
+
if not row.lstrip().startswith("|"):
|
|
48
|
+
break
|
|
49
|
+
cells = _cells(row)
|
|
50
|
+
if len(cells) <= max(id_col, verdict_col) or set(cells[id_col]) <= set("-: "):
|
|
51
|
+
continue
|
|
52
|
+
verdicts[cells[id_col]] = cells[verdict_col].upper()
|
|
53
|
+
return verdicts
|
|
54
|
+
return {}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def run(target_dir: str) -> int:
|
|
58
|
+
target = Path(target_dir)
|
|
59
|
+
log_path = target / "compliance-log.md"
|
|
60
|
+
if not log_path.exists():
|
|
61
|
+
print(f"error: {log_path} not found.", file=sys.stderr)
|
|
62
|
+
return 2
|
|
63
|
+
|
|
64
|
+
text = log_path.read_text(encoding="utf-8")
|
|
65
|
+
|
|
66
|
+
# IDs in first-seen order; for headers, the last occurrence wins (resubmissions).
|
|
67
|
+
order: dict[str, None] = {}
|
|
68
|
+
reports: dict[str, str] = {}
|
|
69
|
+
header_verdicts: dict[str, str] = {}
|
|
70
|
+
last_header_id = ""
|
|
71
|
+
board = _board_verdicts(text)
|
|
72
|
+
for task_id in board:
|
|
73
|
+
order.setdefault(task_id, None)
|
|
74
|
+
for m in LOG_HEADER_RE.finditer(text):
|
|
75
|
+
task_id, value = m.group(1), m.group(2).upper()
|
|
76
|
+
order.setdefault(task_id, None)
|
|
77
|
+
last_header_id = task_id
|
|
78
|
+
if value in VERDICTS:
|
|
79
|
+
header_verdicts[task_id] = value
|
|
80
|
+
else:
|
|
81
|
+
reports[task_id] = value
|
|
82
|
+
|
|
83
|
+
if not order:
|
|
84
|
+
print("no task entries found in compliance-log.md.")
|
|
85
|
+
return 0
|
|
86
|
+
|
|
87
|
+
states: dict[str, str] = {}
|
|
88
|
+
conflicts: dict[str, str] = {}
|
|
89
|
+
for task_id in order:
|
|
90
|
+
board_verdict = board.get(task_id, "")
|
|
91
|
+
board_verdict = board_verdict if board_verdict in VERDICTS else ""
|
|
92
|
+
header_verdict = header_verdicts.get(task_id, "")
|
|
93
|
+
verdict = board_verdict or header_verdict
|
|
94
|
+
report = reports.get(task_id, "PENDING")
|
|
95
|
+
if board_verdict and header_verdict and board_verdict != header_verdict:
|
|
96
|
+
states[task_id] = CONFLICT
|
|
97
|
+
conflicts[task_id] = (
|
|
98
|
+
f" (status board: {board_verdict}, report header: {header_verdict})"
|
|
99
|
+
)
|
|
100
|
+
elif verdict:
|
|
101
|
+
states[task_id] = verdict
|
|
102
|
+
elif report == "DONE":
|
|
103
|
+
states[task_id] = AWAITING_AUDIT
|
|
104
|
+
else:
|
|
105
|
+
states[task_id] = report
|
|
106
|
+
|
|
107
|
+
counts = Counter(states.values())
|
|
108
|
+
print("Status board:")
|
|
109
|
+
for state, n in sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])):
|
|
110
|
+
print(f" {state:<15} {n}")
|
|
111
|
+
|
|
112
|
+
open_items = [(tid, st) for tid, st in states.items() if st != CLOSED]
|
|
113
|
+
print(f"\n{len(open_items)} open (not APPROVED):")
|
|
114
|
+
for tid, st in open_items:
|
|
115
|
+
report_of_item = reports.get(tid)
|
|
116
|
+
detail = f" (report: {report_of_item})" if report_of_item and st in VERDICTS else ""
|
|
117
|
+
detail = conflicts.get(tid, detail)
|
|
118
|
+
print(f" - {tid}: {st}{detail}")
|
|
119
|
+
|
|
120
|
+
last_id = last_header_id or next(reversed(order))
|
|
121
|
+
print(f"\nMost recent entry: {last_id} ({states[last_id]})")
|
|
122
|
+
return 0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Compliance Log
|
|
2
|
+
|
|
3
|
+
> **Evidence of record.** One report and one verdict per task/gate ID. Pre-generate an
|
|
4
|
+
> empty row per ID from the execution guide so nothing gets skipped silently.
|
|
5
|
+
>
|
|
6
|
+
> Maintained by the Auditor after every verdict. Compact repeated open conditions
|
|
7
|
+
> periodically instead of only appending — see the protocol's note on log maintenance.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Status board
|
|
12
|
+
|
|
13
|
+
| ID | Delivery | Verdict | Notes |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| P0-T1 | | `PENDING` | |
|
|
16
|
+
| P0-G1 | | `PENDING` | |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Open conditions
|
|
21
|
+
|
|
22
|
+
None yet.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Reports
|
|
27
|
+
|
|
28
|
+
### P0-T1 — PENDING
|
|
29
|
+
**Changed:**
|
|
30
|
+
**Verify output:**
|
|
31
|
+
**Observations:**
|
|
32
|
+
**Decisions made without stopping:**
|
|
33
|
+
|
|
34
|
+
### P0-G1 — PENDING
|
|
35
|
+
**Verify output:**
|
|
36
|
+
**Observations:**
|
|
37
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Execution Guide
|
|
2
|
+
|
|
3
|
+
> **You are the Executor.** The Auditor wrote this file and will re-run every gate
|
|
4
|
+
> independently. Read `plan-of-record.md` once for context, then work only from here.
|
|
5
|
+
> Report every task in `compliance-log.md`.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Rules of engagement
|
|
10
|
+
|
|
11
|
+
1. **One task at a time, in order.** No batching. Do not start a phase whose
|
|
12
|
+
predecessor is not marked `APPROVED` in the compliance log.
|
|
13
|
+
2. **Never invent a value the codebase declares.** Read it. Needing to ask for one
|
|
14
|
+
means a file was skipped.
|
|
15
|
+
3. **Never guess a cause. Observe it.** Read the log, run the query, print the value.
|
|
16
|
+
4. **Verification is running the thing, not reading the code.** "I reviewed it and it
|
|
17
|
+
looks correct" is reported as `FAILED`.
|
|
18
|
+
5. **Scope is the task text.** Unrelated problems go in *Observations*. Do not fix
|
|
19
|
+
them.
|
|
20
|
+
6. **Stop and ask** when this document contradicts the code, when a task needs a
|
|
21
|
+
decision this document does not make, or when a previously passing check starts
|
|
22
|
+
failing for reasons unrelated to your change. If you resolve an ambiguity without
|
|
23
|
+
stopping, log the decision. A decision in the plan of record is settled; a factual
|
|
24
|
+
claim never is — if this document states something the code contradicts, the
|
|
25
|
+
evidence outranks the order.
|
|
26
|
+
7. **When the system cannot do what a task asks, that is the deliverable.** Report it
|
|
27
|
+
and stop. A task that ends in a well-argued finding is a success.
|
|
28
|
+
8. Project-specific rules that apply to every task — list them here, cited from their
|
|
29
|
+
source rather than restated:
|
|
30
|
+
-
|
|
31
|
+
-
|
|
32
|
+
|
|
33
|
+
## Reporting format
|
|
34
|
+
|
|
35
|
+
For each task, append to `compliance-log.md`:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
### <TASK-ID> — DONE | BLOCKED | FAILED
|
|
39
|
+
**Changed:** <paths>
|
|
40
|
+
**Verify output:**
|
|
41
|
+
<pasted, literal, unedited command output>
|
|
42
|
+
**Observations:** <or "none">
|
|
43
|
+
**Decisions made without stopping:** <or "none">
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
A `DONE` with no pasted output is recorded as `FAILED`.
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
# Phase 0 — <name>
|
|
51
|
+
|
|
52
|
+
### P0-T1 — <imperative, one line>
|
|
53
|
+
|
|
54
|
+
**Goal:**
|
|
55
|
+
**Files:**
|
|
56
|
+
**Steps:**
|
|
57
|
+
1.
|
|
58
|
+
|
|
59
|
+
**Expected failure to avoid:**
|
|
60
|
+
|
|
61
|
+
**Verify:**
|
|
62
|
+
```bash
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Report:** `P0-T1`
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### P0-G1 — Gate: <claim under test>
|
|
70
|
+
|
|
71
|
+
**Proven by:** <the check that proves the claim>
|
|
72
|
+
|
|
73
|
+
**Negative control:** <how you remove the protection, which check fails, and that it passes again once restored>
|
|
74
|
+
|
|
75
|
+
**Report:** `P0-G1`
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# {{PROJECT_NAME}} — Plan of Record
|
|
2
|
+
|
|
3
|
+
> **Status:** draft. This document holds the *what* and the *why*. Nobody implements
|
|
4
|
+
> from this file. The executable instructions live in `execution-guide.md`; evidence
|
|
5
|
+
> goes in `compliance-log.md`.
|
|
6
|
+
|
|
7
|
+
**Target version / milestone:**
|
|
8
|
+
**Operating mode:** <Guided | Autonomous>
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Why this exists (opportunity framing)
|
|
13
|
+
|
|
14
|
+
What problem is this solving, and why now. If several requests trace back to one
|
|
15
|
+
underlying gap, name the gap here rather than listing the requests as unrelated.
|
|
16
|
+
|
|
17
|
+
- **Target outcome:** <measurable behavior, metric, or system state that changes when this is done>
|
|
18
|
+
- **Problem / friction:** <verified gap or failure mode in the current system, without prescribing the feature yet>
|
|
19
|
+
- **Cost of doing nothing:** <concrete impact of not doing this work or deferring it>
|
|
20
|
+
|
|
21
|
+
## 2. What the audit of the current system found
|
|
22
|
+
|
|
23
|
+
| Finding | Evidence |
|
|
24
|
+
|---|---|
|
|
25
|
+
| | |
|
|
26
|
+
|
|
27
|
+
State each finding with the command or artifact that established it, not from memory.
|
|
28
|
+
|
|
29
|
+
## 3. Opportunity shaping & decisions (settled — reopening one is a stop, not a choice)
|
|
30
|
+
|
|
31
|
+
For open-design or new-capability work, compare at least three distinct approaches before
|
|
32
|
+
locking the architecture. For deterministic bugfixes, security patches, schema migrations,
|
|
33
|
+
or strict compliance work, mark the shaping and assumption fields as
|
|
34
|
+
`N/A (Deterministic Fix/Compliance)` and go straight to `D1`.
|
|
35
|
+
|
|
36
|
+
- **Option A (minimal / existing tooling / config-only):** <approach, cost, and limits — or `N/A (Deterministic Fix/Compliance)`>
|
|
37
|
+
- **Option B (balanced / pragmatic implementation):** <approach, cost, and limits — or `N/A (Deterministic Fix/Compliance)`>
|
|
38
|
+
- **Option C (full architectural / custom build):** <approach, cost, and limits — or `N/A (Deterministic Fix/Compliance)`>
|
|
39
|
+
- **Selected option & rationale:** <which option wins and why the other two were rejected — or `N/A (Deterministic Fix/Compliance)`>
|
|
40
|
+
- **Riskiest assumption & first-phase validation:** <the primary value, usability, feasibility, or viability assumption that could invalidate the plan, and how the first phase (`P0` or `P1`) tests it before building the rest — or `N/A (Deterministic Fix/Compliance)`>
|
|
41
|
+
|
|
42
|
+
### D1 — <decision name>
|
|
43
|
+
|
|
44
|
+
What was decided, what alternative was rejected and why, what failure this prevents.
|
|
45
|
+
|
|
46
|
+
## 4. What this deliberately does not do
|
|
47
|
+
|
|
48
|
+
Scope boundaries the Auditor is holding, so an Executor (or a future Auditor) doesn't
|
|
49
|
+
drift into rebuilding something that was cut on purpose.
|
|
50
|
+
|
|
51
|
+
- **Explicit non-goals:** <what is deliberately out of scope for this run>
|
|
52
|
+
|
|
53
|
+
## 5. Phases
|
|
54
|
+
|
|
55
|
+
When the plan carries an unverified technical or product assumption, the first phase
|
|
56
|
+
(`P0` or `P1`) must test that riskiest assumption before building secondary scaffolding.
|
|
57
|
+
|
|
58
|
+
| Phase | Delivers | Risk |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| | | |
|
|
61
|
+
|
|
62
|
+
## 6. Deferred items ledger
|
|
63
|
+
|
|
64
|
+
| ID | What | Deferred to | Closed by |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| | | | |
|
|
67
|
+
|
|
68
|
+
Every decision or annex that pushes work to a future phase gets a row here in the same
|
|
69
|
+
edit. An open row (`Closed by` without a task ID) for a phase being closed is a stop.
|
|
70
|
+
|
|
71
|
+
## 7. Request traceability
|
|
72
|
+
|
|
73
|
+
| Origin request | Where it lands |
|
|
74
|
+
|---|---|
|
|
75
|
+
| | |
|
|
76
|
+
|
|
77
|
+
## 8. Standards this work is held to
|
|
78
|
+
|
|
79
|
+
Cite the project's own conventions rather than restating them here.
|