prizmkit 1.1.124 → 1.1.126

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/bundled/VERSION.json +3 -3
  2. package/bundled/adapters/claude/command-adapter.js +4 -6
  3. package/bundled/dev-pipeline/prizmkit_runtime/reset.py +49 -3
  4. package/bundled/dev-pipeline/prizmkit_runtime/runners.py +75 -14
  5. package/bundled/dev-pipeline/prizmkit_runtime/runtime_helper.py +37 -0
  6. package/bundled/dev-pipeline/prizmkit_runtime/task_checkout.py +267 -0
  7. package/bundled/dev-pipeline/scripts/generate-recovery-prompt.py +4 -4
  8. package/bundled/dev-pipeline/templates/bootstrap-prompt.md +1 -1
  9. package/bundled/dev-pipeline/templates/bootstrap-tier3.md +5 -16
  10. package/bundled/dev-pipeline/templates/bugfix-bootstrap-prompt.md +6 -14
  11. package/bundled/dev-pipeline/templates/refactor-bootstrap-prompt.md +8 -4
  12. package/bundled/dev-pipeline/templates/sections/bugfix-phase-review.md +9 -8
  13. package/bundled/dev-pipeline/templates/sections/phase-review-agent.md +12 -15
  14. package/bundled/dev-pipeline/templates/sections/phase-review-full.md +11 -16
  15. package/bundled/dev-pipeline/templates/sections/refactor-phase-review.md +9 -8
  16. package/bundled/dev-pipeline/tests/test_generate_bootstrap_prompt.py +8 -4
  17. package/bundled/dev-pipeline/tests/test_generate_bugfix_prompt.py +1 -1
  18. package/bundled/dev-pipeline/tests/test_generate_refactor_prompt.py +1 -1
  19. package/bundled/dev-pipeline/tests/test_python_runner_parity.py +206 -3
  20. package/bundled/dev-pipeline/tests/test_runtime_helper.py +36 -0
  21. package/bundled/dev-pipeline/tests/test_unified_cli.py +21 -12
  22. package/bundled/skills/_metadata.json +4 -4
  23. package/bundled/skills/prizmkit-code-review/SKILL.md +130 -103
  24. package/bundled/skills/prizmkit-code-review/assets/reviewer-role.json +3 -5
  25. package/bundled/skills/prizmkit-code-review/references/review-report-template.md +89 -38
  26. package/bundled/skills/prizmkit-code-review/references/reviewer-agent-prompt.md +44 -62
  27. package/bundled/skills/prizmkit-code-review/scripts/render_review_report.py +249 -143
  28. package/package.json +1 -1
  29. package/bundled/skills/prizmkit-code-review/references/reviewer-execution-protocol.md +0 -179
  30. package/bundled/skills/prizmkit-code-review/references/reviewer-workspace-protocol.md +0 -118
  31. package/bundled/skills/prizmkit-code-review/scripts/check_loop.py +0 -380
  32. package/bundled/skills/prizmkit-code-review/scripts/workspace_snapshot.py +0 -222
@@ -1,179 +0,0 @@
1
- # Reviewer Execution Protocol
2
-
3
- This protocol is the mandatory execution contract for `/prizmkit-code-review`. It defines semantic guarantees rather than platform tool, agent, workflow, or command names.
4
-
5
- ## Terms
6
-
7
- | Term | Meaning |
8
- |---|---|
9
- | Orchestrator | The single Main Agent that controls rounds, filters findings, and applies accepted fixes |
10
- | Reviewer | The single independent read-only review authority for one round |
11
- | Downstream execution unit | Any separately scheduled or executed worker, reviewer, task, workflow child, team member, remote job, or equivalent delegated execution |
12
- | Delegation-equivalent capability | Any capability that can create, schedule, resume, continue, request, or coordinate downstream execution |
13
- | Orchestration re-entry | Entering a process that can delegate, fan out, or become another review orchestrator |
14
- | Capability attestation | Launch-time evidence that the Reviewer environment satisfies the mandatory capability boundary |
15
- | Completed round | A round whose Reviewer output passed execution and workspace validation |
16
- | Convergence | A completed round leaves zero accepted findings |
17
- | Safety fuse | The internal ten-round automation limit |
18
-
19
- ## Normative Invariants
20
-
21
- ```yaml
22
- reviewer_execution:
23
- sole_review_authority_per_round: true
24
- fresh_execution_per_round: true
25
- observational_workspace_access: true
26
- mutation_capability: unavailable
27
- delegation_equivalent_capability: unavailable
28
- orchestration_reentry_capability: unavailable
29
- max_reviewers_per_round: 1
30
- max_descendant_execution_units: 0
31
- max_delegation_depth: 0
32
- normal_termination: convergence
33
- internal_safety_review_limit: 10
34
- ```
35
-
36
- Additional invariants:
37
-
38
- 1. Only the Main Agent starts a Reviewer or the next round.
39
- 2. A Reviewer cannot create, request, continue, resume, refresh, replace, or coordinate another execution unit.
40
- 3. A Reviewer cannot invoke an equivalent review orchestrator.
41
- 4. A Reviewer performs the complete review itself and cannot delegate a subset.
42
- 5. If the review does not fit its execution context, it returns `REVIEW_INFRASTRUCTURE_BLOCKED` rather than delegating.
43
- 6. Reviewer output is unusable until execution and workspace validation both pass.
44
- 7. Every round uses a fresh execution identity. Reviewer sessions are never reused.
45
- 8. A Reviewer receives no prior findings, verdicts, filtering decisions, repair descriptions, or reasoning.
46
- 9. The Main Agent applies accepted fixes directly; it does not create a separate Fixer or Dev Agent.
47
-
48
- ## Capability Attestation
49
-
50
- Before every launch, establish this claim with platform-native evidence:
51
-
52
- ```yaml
53
- capability_attestation:
54
- mutation_capability: unavailable
55
- delegation_equivalent_capability: unavailable
56
- downstream_execution_creation: unavailable
57
- orchestration_reentry_capability: unavailable
58
- review_execution: foreground-single-authority
59
- status: VERIFIED
60
- ```
61
-
62
- A prompt promise is not evidence. The selected execution environment must actually remove the prohibited capabilities through a capability-scoped role, restricted tool surface, sandbox policy, launch wrapper, capability token, or equivalent mechanism.
63
-
64
- If the platform cannot establish the boundary:
65
-
66
- ```text
67
- Do not launch Reviewer
68
- → verdict: NEEDS_FIXES
69
- → blockerCode: REVIEW_INFRASTRUCTURE_BLOCKED
70
- ```
71
-
72
- ## One-Reviewer Topology
73
-
74
- Required topology:
75
-
76
- ```text
77
- Main Agent
78
- ├── Round 1: one fresh Reviewer, no descendants
79
- ├── Round 2: one fresh Reviewer, no descendants
80
- ├── ...
81
- └── Round N: one fresh Reviewer, no descendants
82
- ```
83
-
84
- The Main Agent owns all loop state. No Reviewer may start its successor.
85
-
86
- ## Execution Validation
87
-
88
- Validate topology before consuming output:
89
-
90
- 1. Exactly one fresh Reviewer started in the active round.
91
- 2. Capability attestation is `VERIFIED`.
92
- 3. Observed descendant execution units equal zero.
93
- 4. Observed delegation depth equals zero.
94
- 5. Orchestration status is `COMPLIANT`.
95
- 6. Execution evidence is sufficient to support those claims.
96
-
97
- The Reviewer's own compliance declaration is supplementary. It never replaces launch-time attestation or platform-observed evidence.
98
-
99
- ## Runtime Violation
100
-
101
- A violation exists when a Reviewer creates or requests downstream execution, re-enters orchestration, causes more than one Reviewer in a round, exceeds zero delegation depth, or makes compliance unverifiable.
102
-
103
- On violation:
104
-
105
- 1. Stop the Reviewer and identifiable descendants on a best-effort basis.
106
- 2. Discard the complete Reviewer output.
107
- 3. Do not consume or filter findings.
108
- 4. Do not count the round as completed.
109
- 5. Do not retry or start another Reviewer.
110
- 6. Terminate Code Review.
111
- 7. Return `NEEDS_FIXES` with `REVIEW_ORCHESTRATION_VIOLATION`.
112
-
113
- Cleanup success is not required to declare failure.
114
-
115
- ## Complete Independent Review
116
-
117
- Every valid round reviews the complete current implementation against the complete applicable specification, plan, rules, contracts, callers, dependents, and test evidence. Later rounds are not finding reconciliation or fix-only verification.
118
-
119
- A fresh Reviewer may independently rediscover an unresolved defect or find a defect earlier Reviewers missed. Historical findings are intentionally withheld to avoid anchoring.
120
-
121
- ## Convergence and Safety Fuse
122
-
123
- Normal completion occurs only when a usable completed round leaves zero accepted findings:
124
-
125
- - the Reviewer reports no findings; or
126
- - the Main Agent rejects every finding with concrete evidence.
127
-
128
- When accepted findings remain, the Main Agent repairs the accepted batch and starts a fresh complete round.
129
-
130
- The internal limit is ten Reviewer executions. Rounds one through ten may return `PASS` when they converge. If round ten leaves accepted findings:
131
-
132
- ```text
133
- Do not apply another automatic repair
134
- Do not start round 11
135
- → verdict: NEEDS_FIXES
136
- → blockerCode: REVIEW_SAFETY_LIMIT_REACHED
137
- ```
138
-
139
- ## External Verdicts and Blockers
140
-
141
- External verdicts:
142
-
143
- ```text
144
- PASS
145
- NEEDS_FIXES
146
- ```
147
-
148
- Diagnostic blocker codes:
149
-
150
- ```text
151
- REVIEW_INFRASTRUCTURE_BLOCKED
152
- REVIEW_ORCHESTRATION_VIOLATION
153
- WORKSPACE_MISMATCH
154
- REVIEW_SAFETY_LIMIT_REACHED
155
- INVALID_REVIEW_RESULT
156
- ```
157
-
158
- A blocker code is metadata, not a third verdict.
159
-
160
- ## Non-Normative Recognition Examples
161
-
162
- These examples help identify prohibited semantics but do not define compliance:
163
-
164
- - starting another Agent to inspect a subsystem;
165
- - invoking a Workflow that fans out workers;
166
- - entering another code-review Skill;
167
- - asking a task runner or Main Agent to create a helper on the Reviewer's behalf;
168
- - creating a remote review job;
169
- - resuming a stopped helper or successor execution.
170
-
171
- A future capability with a different name remains prohibited when it creates or coordinates downstream execution.
172
-
173
- ## Platform Mapping
174
-
175
- A platform mapping is valid only when its actual launch configuration proves all normative capabilities.
176
-
177
- For a current platform that supports project-defined subagent roles and a strict capability allowlist, a role containing only observational file-inspection capabilities can satisfy the launch boundary. Omitting downstream-execution and skill-orchestration capabilities is required; a deny instruction in prose is insufficient.
178
-
179
- Platforms without an equivalent enforceable role or launch restriction are unsupported for independent Reviewer execution and fail closed. Platform support may be added later through canonical adapters or runtime launchers without changing this protocol.
@@ -1,118 +0,0 @@
1
- # Reviewer Workspace Protocol
2
-
3
- Use this protocol for every `/prizmkit-code-review` round. Workspace equivalence and execution compliance are independent requirements; both must pass before Reviewer output is usable.
4
-
5
- ## Invariants
6
-
7
- 1. **Complete logical view**: represent staged, unstaged, untracked, deleted, and renamed changes plus required unchanged callers and dependents.
8
- 2. **Currency**: capture a new snapshot after every accepted repair batch.
9
- 3. **Equivalence**: the Reviewer's logical view matches the captured Main-Agent workspace even when physical location or transport differs.
10
- 4. **Determinism**: the same repository state and inputs produce the same manifest identity.
11
- 5. **Verifiability**: missing, extra, stale, mixed, or unverifiable content produces `WORKSPACE_MISMATCH`.
12
- 6. **Execution separation**: a matched workspace does not prove the non-delegating execution boundary, and execution compliance does not prove workspace equivalence.
13
- 7. **Main-Agent ownership**: only the Main Agent refreshes snapshots or launches successor Reviewers.
14
-
15
- ## Deterministic Manifest
16
-
17
- Prefer `${SKILL_DIR}/scripts/workspace_snapshot.py` when Python and Git are available.
18
-
19
- The manifest contains:
20
-
21
- ```text
22
- protocol version
23
- repository identity
24
- baseline identity
25
- snapshot identity
26
- path inventory
27
- change type
28
- staged and unstaged composition
29
- rename and delete state
30
- content identity for available blobs
31
- supporting-context identity
32
- ```
33
-
34
- Use path-safe Git output so spaces, Unicode, quoting, renames, and deletions remain unambiguous. A prose summary is not a deterministic manifest.
35
-
36
- ## Review Payload
37
-
38
- Provide both:
39
-
40
- 1. the complete manifest used for verification;
41
- 2. a reviewable representation of every manifest entry.
42
-
43
- A representation may be workspace files, complete content, patches plus required base content, or equivalent materialization. Diff summaries alone are insufficient for new files or findings that depend on surrounding code.
44
-
45
- Supporting unchanged context must be clearly distinguished from changed content.
46
-
47
- ## Content-Addressed Reuse
48
-
49
- Round one establishes the complete logical snapshot. Later rounds may transfer only changed blobs while referring to unchanged content by stable identity:
50
-
51
- ```text
52
- current logical workspace
53
- = unchanged content-addressed blobs
54
- + current-round changed blobs
55
- + current complete manifest
56
- ```
57
-
58
- Reuse optimizes transport, never review scope. Every Reviewer remains responsible for the complete current change.
59
-
60
- ## Reviewer Verification
61
-
62
- Before normal review, verify:
63
-
64
- 1. repository and baseline identity;
65
- 2. every path and state in the manifest is represented;
66
- 3. no unexplained changed path exists outside the manifest;
67
- 4. staged, unstaged, untracked, deleted, and renamed states are preserved;
68
- 5. content identities match;
69
- 6. supporting-context identities match;
70
- 7. the snapshot belongs to the active round.
71
-
72
- On success, return `Snapshot Verification: VERIFIED` and proceed.
73
-
74
- On failure:
75
-
76
- ```text
77
- ### Result: WORKSPACE_MISMATCH
78
-
79
- ### Snapshot Verification
80
- - Status: FAILED
81
- - Identity: <expected and observed identity>
82
- - Mismatch: <missing, extra, stale, mixed, or unverifiable entries>
83
- - Required action: <what snapshot preparation failed>
84
- ```
85
-
86
- Do not emit ordinary findings after a mismatch.
87
-
88
- ## Transport Preparation and Failure Boundary
89
-
90
- Before launch, snapshot transport may use:
91
-
92
- 1. the preferred strategy;
93
- 2. at most one alternate strategy.
94
-
95
- Both attempts occur before a Reviewer starts. Do not spend the one-Reviewer budget on transport discovery.
96
-
97
- After launch, any mismatch makes the Reviewer output unusable and terminates Code Review:
98
-
99
- ```text
100
- Discard output
101
- Do not launch a replacement Reviewer
102
- → verdict: NEEDS_FIXES
103
- → blockerCode: WORKSPACE_MISMATCH
104
- ```
105
-
106
- This prevents workspace repair from becoming another unbounded Reviewer loop.
107
-
108
- ## Round Refresh
109
-
110
- After the Main Agent applies accepted fixes:
111
-
112
- 1. retain prior identities only as internal report evidence;
113
- 2. capture a new complete manifest and payload;
114
- 3. reuse unchanged blobs by content identity where supported;
115
- 4. create a fresh Reviewer execution identity;
116
- 5. repeat workspace verification.
117
-
118
- Never reuse a Reviewer session or send historical findings to the fresh Reviewer.
@@ -1,380 +0,0 @@
1
- #!/usr/bin/env python3
2
- """Strict execution and convergence gate for prizmkit-code-review protocol v2."""
3
-
4
- from __future__ import annotations
5
-
6
- import json
7
- import sys
8
- from typing import Any
9
-
10
- PROTOCOL_VERSION = 2
11
- REVIEW_SAFETY_LIMIT = 10
12
-
13
- CAPABILITY_FIELDS = {
14
- "mutation_capability",
15
- "delegation_equivalent_capability",
16
- "downstream_execution_creation",
17
- "orchestration_reentry_capability",
18
- "review_execution",
19
- "status",
20
- }
21
- PREFLIGHT_FIELDS = {
22
- "protocol_version",
23
- "gate_stage",
24
- "round",
25
- "reviewers_started_this_round",
26
- "reviewers_started_total",
27
- "fresh_reviewer",
28
- "workspace_snapshot_ready",
29
- "capability_attestation",
30
- }
31
- RESULT_BASE_FIELDS = {
32
- "protocol_version",
33
- "gate_stage",
34
- "filtering_done",
35
- "round",
36
- "reviewers_started_this_round",
37
- "reviewers_started_total",
38
- "fresh_reviewer",
39
- "descendant_execution_units_observed",
40
- "delegation_depth_observed",
41
- "capability_attestation",
42
- "orchestration_status",
43
- "workspace_status",
44
- "review_complete",
45
- "reviewer_result",
46
- "findings_count",
47
- }
48
- RESULT_FILTER_FIELDS = {"accepted_count", "rejected_count"}
49
-
50
-
51
- class ProtocolError(ValueError):
52
- """Raised when a payload violates the strict protocol schema."""
53
-
54
-
55
- def _is_int(value: Any) -> bool:
56
- return isinstance(value, int) and not isinstance(value, bool)
57
-
58
-
59
- def _require_exact_fields(data: dict[str, Any], required: set[str]) -> None:
60
- missing = sorted(required - data.keys())
61
- unknown = sorted(data.keys() - required)
62
- if missing:
63
- raise ProtocolError(f"missing required fields: {', '.join(missing)}")
64
- if unknown:
65
- raise ProtocolError(f"unknown fields: {', '.join(unknown)}")
66
-
67
-
68
- def _require_bool(data: dict[str, Any], name: str) -> bool:
69
- value = data[name]
70
- if not isinstance(value, bool):
71
- raise ProtocolError(f"{name} must be a boolean")
72
- return value
73
-
74
-
75
- def _require_count(data: dict[str, Any], name: str) -> int:
76
- value = data[name]
77
- if not _is_int(value) or value < 0:
78
- raise ProtocolError(f"{name} must be a non-negative integer")
79
- return value
80
-
81
-
82
- def _require_round(data: dict[str, Any]) -> int:
83
- current_round = _require_count(data, "round")
84
- if not 1 <= current_round <= REVIEW_SAFETY_LIMIT:
85
- raise ProtocolError(
86
- f"round must be between 1 and {REVIEW_SAFETY_LIMIT}"
87
- )
88
- return current_round
89
-
90
-
91
- def _require_enum(data: dict[str, Any], name: str, allowed: set[str]) -> str:
92
- value = data[name]
93
- if value not in allowed:
94
- choices = ", ".join(sorted(allowed))
95
- raise ProtocolError(f"{name} must be one of: {choices}")
96
- return value
97
-
98
-
99
- def _validate_protocol_header(data: dict[str, Any], stage: str) -> None:
100
- if data["protocol_version"] != PROTOCOL_VERSION:
101
- raise ProtocolError(
102
- f"protocol_version must be {PROTOCOL_VERSION}"
103
- )
104
- if data["gate_stage"] != stage:
105
- raise ProtocolError(f"gate_stage must be {stage}")
106
-
107
-
108
- def _validate_attestation(value: Any) -> bool:
109
- if not isinstance(value, dict):
110
- raise ProtocolError("capability_attestation must be an object")
111
- _require_exact_fields(value, CAPABILITY_FIELDS)
112
-
113
- unavailable_fields = (
114
- "mutation_capability",
115
- "delegation_equivalent_capability",
116
- "downstream_execution_creation",
117
- "orchestration_reentry_capability",
118
- )
119
- for name in unavailable_fields:
120
- _require_enum(value, name, {"available", "unavailable"})
121
- _require_enum(
122
- value,
123
- "review_execution",
124
- {"foreground-single-authority", "unsupported"},
125
- )
126
- _require_enum(value, "status", {"FAILED", "VERIFIED"})
127
-
128
- return (
129
- value["status"] == "VERIFIED"
130
- and value["review_execution"] == "foreground-single-authority"
131
- and all(value[name] == "unavailable" for name in unavailable_fields)
132
- )
133
-
134
-
135
- def _response(
136
- *,
137
- action: str,
138
- verdict: str | None,
139
- blocker_code: str | None = None,
140
- launch_authorized: bool = False,
141
- result_usable: bool = False,
142
- end_loop: bool = False,
143
- reason: str,
144
- ) -> dict[str, Any]:
145
- return {
146
- "action": action,
147
- "launchAuthorized": launch_authorized,
148
- "resultUsable": result_usable,
149
- "endLoop": end_loop,
150
- "verdict": verdict,
151
- "blockerCode": blocker_code,
152
- "reason": reason,
153
- }
154
-
155
-
156
- def check_preflight(data: dict[str, Any]) -> dict[str, Any]:
157
- """Authorize one safe Reviewer launch or return a fail-closed blocker."""
158
- _require_exact_fields(data, PREFLIGHT_FIELDS)
159
- _validate_protocol_header(data, "preflight")
160
- current_round = _require_round(data)
161
- started_this_round = _require_count(data, "reviewers_started_this_round")
162
- started_total = _require_count(data, "reviewers_started_total")
163
- fresh_reviewer = _require_bool(data, "fresh_reviewer")
164
- snapshot_ready = _require_bool(data, "workspace_snapshot_ready")
165
- attested = _validate_attestation(data["capability_attestation"])
166
-
167
- if started_this_round != 0:
168
- raise ProtocolError(
169
- "preflight requires reviewers_started_this_round to equal 0"
170
- )
171
- if started_total != current_round - 1:
172
- raise ProtocolError(
173
- "preflight reviewers_started_total must equal round - 1"
174
- )
175
- if not fresh_reviewer:
176
- return _response(
177
- action="STOP",
178
- verdict="NEEDS_FIXES",
179
- blocker_code="REVIEW_INFRASTRUCTURE_BLOCKED",
180
- end_loop=True,
181
- reason="Reviewer execution is not fresh",
182
- )
183
- if not snapshot_ready:
184
- return _response(
185
- action="STOP",
186
- verdict="NEEDS_FIXES",
187
- blocker_code="REVIEW_INFRASTRUCTURE_BLOCKED",
188
- end_loop=True,
189
- reason="Workspace snapshot is not ready",
190
- )
191
- if not attested:
192
- return _response(
193
- action="STOP",
194
- verdict="NEEDS_FIXES",
195
- blocker_code="REVIEW_INFRASTRUCTURE_BLOCKED",
196
- end_loop=True,
197
- reason="Mandatory Reviewer capability boundary is not verified",
198
- )
199
-
200
- return _response(
201
- action="AUTHORIZE_LAUNCH",
202
- verdict=None,
203
- launch_authorized=True,
204
- reason=f"Round {current_round} may launch one fresh Reviewer",
205
- )
206
-
207
-
208
- def _validate_result_structure(data: dict[str, Any]) -> tuple[int, int, bool]:
209
- filtering_done = _require_bool(data, "filtering_done")
210
- required_fields = RESULT_BASE_FIELDS | (
211
- RESULT_FILTER_FIELDS if filtering_done else set()
212
- )
213
- _require_exact_fields(data, required_fields)
214
- _validate_protocol_header(data, "result")
215
-
216
- current_round = _require_round(data)
217
- started_this_round = _require_count(data, "reviewers_started_this_round")
218
- started_total = _require_count(data, "reviewers_started_total")
219
- fresh_reviewer = _require_bool(data, "fresh_reviewer")
220
- _require_count(data, "descendant_execution_units_observed")
221
- _require_count(data, "delegation_depth_observed")
222
- _require_bool(data, "review_complete")
223
- findings_count = _require_count(data, "findings_count")
224
- reviewer_result = _require_enum(
225
- data, "reviewer_result", {"NEEDS_FIXES", "PASS"}
226
- )
227
- _require_enum(
228
- data,
229
- "orchestration_status",
230
- {"COMPLIANT", "UNVERIFIABLE", "VIOLATION"},
231
- )
232
- _require_enum(data, "workspace_status", {"MATCHED", "MISMATCH"})
233
- _validate_attestation(data["capability_attestation"])
234
-
235
- if started_this_round != 1:
236
- raise ProtocolError(
237
- "result requires exactly one Reviewer started in the round"
238
- )
239
- if started_total != current_round:
240
- raise ProtocolError("result reviewers_started_total must equal round")
241
- if not fresh_reviewer:
242
- raise ProtocolError("result requires a fresh Reviewer execution")
243
- if reviewer_result == "PASS" and findings_count != 0:
244
- raise ProtocolError("PASS requires findings_count to equal 0")
245
- if reviewer_result == "NEEDS_FIXES" and findings_count == 0:
246
- raise ProtocolError("NEEDS_FIXES requires at least one finding")
247
-
248
- if filtering_done:
249
- accepted_count = _require_count(data, "accepted_count")
250
- rejected_count = _require_count(data, "rejected_count")
251
- if accepted_count + rejected_count != findings_count:
252
- raise ProtocolError(
253
- "accepted_count + rejected_count must equal findings_count"
254
- )
255
- if reviewer_result == "PASS" and (accepted_count or rejected_count):
256
- raise ProtocolError("PASS cannot contain finding classifications")
257
-
258
- return current_round, findings_count, filtering_done
259
-
260
-
261
- def check_result(data: dict[str, Any]) -> dict[str, Any]:
262
- """Validate topology and workspace before filtering, then decide convergence."""
263
- current_round, _, filtering_done = _validate_result_structure(data)
264
- attested = _validate_attestation(data["capability_attestation"])
265
-
266
- if (
267
- not attested
268
- or data["descendant_execution_units_observed"] != 0
269
- or data["delegation_depth_observed"] != 0
270
- or data["orchestration_status"] != "COMPLIANT"
271
- ):
272
- return _response(
273
- action="STOP",
274
- verdict="NEEDS_FIXES",
275
- blocker_code="REVIEW_ORCHESTRATION_VIOLATION",
276
- end_loop=True,
277
- reason="Reviewer execution topology is non-compliant or unverifiable",
278
- )
279
-
280
- if data["workspace_status"] != "MATCHED":
281
- return _response(
282
- action="STOP",
283
- verdict="NEEDS_FIXES",
284
- blocker_code="WORKSPACE_MISMATCH",
285
- end_loop=True,
286
- reason="Reviewer workspace does not match the authorized snapshot",
287
- )
288
-
289
- if not data["review_complete"]:
290
- return _response(
291
- action="STOP",
292
- verdict="NEEDS_FIXES",
293
- blocker_code="INVALID_REVIEW_RESULT",
294
- end_loop=True,
295
- reason="Reviewer output is incomplete",
296
- )
297
-
298
- if not filtering_done:
299
- if data["reviewer_result"] == "PASS":
300
- return _response(
301
- action="PASS",
302
- verdict="PASS",
303
- result_usable=True,
304
- end_loop=True,
305
- reason=f"Round {current_round} completed with no findings",
306
- )
307
- return _response(
308
- action="AUTHORIZE_FILTERING",
309
- verdict=None,
310
- result_usable=True,
311
- reason=f"Round {current_round} findings are authorized for filtering",
312
- )
313
-
314
- if data["accepted_count"] == 0:
315
- return _response(
316
- action="PASS",
317
- verdict="PASS",
318
- result_usable=True,
319
- end_loop=True,
320
- reason=f"Round {current_round} converged with zero accepted findings",
321
- )
322
-
323
- if current_round == REVIEW_SAFETY_LIMIT:
324
- return _response(
325
- action="STOP",
326
- verdict="NEEDS_FIXES",
327
- blocker_code="REVIEW_SAFETY_LIMIT_REACHED",
328
- result_usable=True,
329
- end_loop=True,
330
- reason="Accepted findings remain at the internal safety limit",
331
- )
332
-
333
- return _response(
334
- action="CONTINUE",
335
- verdict=None,
336
- result_usable=True,
337
- reason=(
338
- f"Round {current_round} has {data['accepted_count']} accepted "
339
- "findings; repair and start a fresh complete review"
340
- ),
341
- )
342
-
343
-
344
- def check_gate(data: Any) -> dict[str, Any]:
345
- """Validate a strict v2 payload and dispatch to the requested stage."""
346
- if not isinstance(data, dict):
347
- raise ProtocolError("input must be a JSON object")
348
- stage = data.get("gate_stage")
349
- if stage == "preflight":
350
- return check_preflight(data)
351
- if stage == "result":
352
- return check_result(data)
353
- raise ProtocolError("gate_stage must be preflight or result")
354
-
355
-
356
- def main() -> int:
357
- try:
358
- data = json.load(sys.stdin)
359
- result = check_gate(data)
360
- except (json.JSONDecodeError, ProtocolError) as error:
361
- print(
362
- json.dumps(
363
- _response(
364
- action="STOP",
365
- verdict="NEEDS_FIXES",
366
- blocker_code="INVALID_REVIEW_RESULT",
367
- end_loop=True,
368
- reason=f"Invalid protocol payload: {error}",
369
- ),
370
- sort_keys=True,
371
- )
372
- )
373
- return 1
374
-
375
- print(json.dumps(result, sort_keys=True))
376
- return 0
377
-
378
-
379
- if __name__ == "__main__":
380
- sys.exit(main())