policyflow 0.1.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.
Files changed (67) hide show
  1. policyflow/__init__.py +36 -0
  2. policyflow/agent_execution.py +301 -0
  3. policyflow/api.py +125 -0
  4. policyflow/assets/agents/architecture-agent.md +50 -0
  5. policyflow/assets/agents/planning-agent.md +46 -0
  6. policyflow/assets/agents/qa-agent.md +46 -0
  7. policyflow/assets/agents/review-agent.md +51 -0
  8. policyflow/assets/agents/senior-dev-agent.md +49 -0
  9. policyflow/assets/docs/concepts.md +30 -0
  10. policyflow/assets/docs/extraction-notes.md +10 -0
  11. policyflow/assets/docs/getting-started.md +282 -0
  12. policyflow/assets/docs/governance-enforcement-roadmap.md +58 -0
  13. policyflow/assets/docs/human-in-the-loop.md +26 -0
  14. policyflow/assets/docs/overview.md +19 -0
  15. policyflow/assets/docs/project-context-contract.md +42 -0
  16. policyflow/assets/docs/public-api.md +87 -0
  17. policyflow/assets/docs/release-and-upgrade.md +125 -0
  18. policyflow/assets/docs/runner-contract.md +104 -0
  19. policyflow/assets/docs/schema-compatibility.md +99 -0
  20. policyflow/assets/examples/policyflow.github-governed.yml +29 -0
  21. policyflow/assets/examples/policyflow.minimal.yml +7 -0
  22. policyflow/assets/examples/project-context.yml +26 -0
  23. policyflow/assets/github/ISSUE_TEMPLATE/architecture-change.yml +92 -0
  24. policyflow/assets/github/ISSUE_TEMPLATE/bugfix.yml +97 -0
  25. policyflow/assets/github/ISSUE_TEMPLATE/feature.yml +97 -0
  26. policyflow/assets/github/PULL_REQUEST_TEMPLATE.md +57 -0
  27. policyflow/assets/github/workflows/policyflow-governance.yml +84 -0
  28. policyflow/assets/prompts/architecture-agent.prompt.md +11 -0
  29. policyflow/assets/prompts/codex-feature-prompt.template.md +20 -0
  30. policyflow/assets/prompts/codex-review-prompt.template.md +18 -0
  31. policyflow/assets/prompts/planning-agent.prompt.md +11 -0
  32. policyflow/assets/prompts/qa-agent.prompt.md +11 -0
  33. policyflow/assets/prompts/review-agent.prompt.md +11 -0
  34. policyflow/assets/prompts/senior-dev-agent.prompt.md +11 -0
  35. policyflow/assets/rules/agent-handoff-contracts.md +36 -0
  36. policyflow/assets/rules/confidence-governance.md +25 -0
  37. policyflow/assets/rules/escalation-rules.md +20 -0
  38. policyflow/assets/rules/qa-checklist.md +10 -0
  39. policyflow/assets/rules/review-checklist.md +11 -0
  40. policyflow/assets/rules/risk-classification.md +41 -0
  41. policyflow/assets/rules/risk-review-matrix.md +57 -0
  42. policyflow/assets/workflows/templates/architecture-change-workflow.template.yml +220 -0
  43. policyflow/assets/workflows/templates/bugfix-workflow.template.yml +194 -0
  44. policyflow/assets/workflows/templates/feature-workflow.template.yml +263 -0
  45. policyflow/assets/workflows/templates/governance-fields.example.yml +48 -0
  46. policyflow/assets/workflows/templates/handoff-fields.example.yml +63 -0
  47. policyflow/assets/workflows/templates/low-risk-workflow.template.yml +151 -0
  48. policyflow/bootstrap.py +378 -0
  49. policyflow/cli.py +382 -0
  50. policyflow/codex_runner.py +188 -0
  51. policyflow/consumer_config.py +119 -0
  52. policyflow/doctor.py +358 -0
  53. policyflow/exceptions.py +9 -0
  54. policyflow/github_approval.py +89 -0
  55. policyflow/models.py +321 -0
  56. policyflow/reporting.py +192 -0
  57. policyflow/runtime.py +263 -0
  58. policyflow/schemas.py +57 -0
  59. policyflow/sync.py +135 -0
  60. policyflow/validator.py +897 -0
  61. policyflow/workflow_generator.py +228 -0
  62. policyflow-0.1.0.dist-info/METADATA +450 -0
  63. policyflow-0.1.0.dist-info/RECORD +67 -0
  64. policyflow-0.1.0.dist-info/WHEEL +5 -0
  65. policyflow-0.1.0.dist-info/entry_points.txt +2 -0
  66. policyflow-0.1.0.dist-info/licenses/LICENSE +21 -0
  67. policyflow-0.1.0.dist-info/top_level.txt +1 -0
policyflow/__init__.py ADDED
@@ -0,0 +1,36 @@
1
+ """PolicyFlow lightweight governance validator."""
2
+
3
+ from policyflow.api import (
4
+ WorkflowDocument,
5
+ WorkflowValidationError,
6
+ audit_workflows,
7
+ block_workflow_phase,
8
+ complete_workflow_phase,
9
+ get_workflow_status,
10
+ inspect_workflow,
11
+ record_workflow_handoff,
12
+ start_workflow_phase,
13
+ validate_github_approvals,
14
+ validate_pr_body,
15
+ validate_workflow,
16
+ validate_workflow_data,
17
+ )
18
+
19
+ __all__ = [
20
+ "WorkflowDocument",
21
+ "WorkflowValidationError",
22
+ "__version__",
23
+ "audit_workflows",
24
+ "block_workflow_phase",
25
+ "complete_workflow_phase",
26
+ "get_workflow_status",
27
+ "inspect_workflow",
28
+ "record_workflow_handoff",
29
+ "start_workflow_phase",
30
+ "validate_github_approvals",
31
+ "validate_pr_body",
32
+ "validate_workflow",
33
+ "validate_workflow_data",
34
+ ]
35
+
36
+ __version__ = "0.1.0"
@@ -0,0 +1,301 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ import sys
6
+ import tempfile
7
+ from copy import deepcopy
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+ import yaml
12
+
13
+ from policyflow.exceptions import WorkflowValidationError
14
+ from policyflow.runtime import (
15
+ PHASE_AGENT_MAP,
16
+ block_phase,
17
+ complete_phase,
18
+ load_workflow_raw,
19
+ record_handoff,
20
+ save_workflow_raw,
21
+ start_phase,
22
+ )
23
+ from policyflow.validator import validate_workflow_data
24
+
25
+
26
+ DEFAULT_RUNNER_CONFIG = Path("policyflow.runners.yml")
27
+ PHASE_EVIDENCE_KEYS = {
28
+ "planning": "planning",
29
+ "architecture-check": "architecture-check",
30
+ "review": "review",
31
+ "qa": "qa",
32
+ "approval": "approval",
33
+ }
34
+ PHASE_CONTRACT_KEYS = {
35
+ "planning": "planning",
36
+ "architecture-check": "architecture-check",
37
+ "implementation": "implementation",
38
+ "review": "review",
39
+ "qa": "qa",
40
+ }
41
+
42
+
43
+ def run_phase_with_runner(
44
+ workflow_path: Path, phase: str, runner_config_path: Path | None = None
45
+ ) -> None:
46
+ if phase == "approval":
47
+ raise WorkflowValidationError(
48
+ ["Phase 'approval' remains human-driven and cannot be run via agent execution."]
49
+ )
50
+
51
+ config_path = runner_config_path or DEFAULT_RUNNER_CONFIG
52
+ runner_config = _load_runner_config(config_path)
53
+ runner_name = str(runner_config.get("default_runner", "")).strip()
54
+ runners = runner_config.get("runners")
55
+
56
+ if not runner_name:
57
+ raise WorkflowValidationError(["Runner config must declare default_runner."])
58
+ if not isinstance(runners, dict) or runner_name not in runners:
59
+ raise WorkflowValidationError(
60
+ [f"Runner config must declare runner settings for '{runner_name}'."]
61
+ )
62
+
63
+ runner = runners[runner_name]
64
+ if not isinstance(runner, dict):
65
+ raise WorkflowValidationError(
66
+ [f"Runner definition for '{runner_name}' must be a mapping."]
67
+ )
68
+
69
+ start_phase(workflow_path, phase)
70
+
71
+ try:
72
+ raw = load_workflow_raw(workflow_path)
73
+ payload = _build_execution_payload(raw, workflow_path, phase, runner, config_path)
74
+ result = _execute_runner(runner, payload, workflow_path, config_path)
75
+ _apply_runner_result(workflow_path, phase, result)
76
+ except WorkflowValidationError as exc:
77
+ _block_phase_on_failure(workflow_path, phase, exc.errors[0])
78
+ raise
79
+ except Exception as exc: # pragma: no cover - defensive catch for subprocess/json edge cases
80
+ _block_phase_on_failure(workflow_path, phase, str(exc))
81
+ raise WorkflowValidationError([str(exc)]) from exc
82
+
83
+
84
+ def _load_runner_config(path: Path) -> dict[str, Any]:
85
+ if not path.exists():
86
+ raise WorkflowValidationError([f"Runner config file not found: {path}"])
87
+
88
+ try:
89
+ data = yaml.safe_load(path.read_text(encoding="utf-8"))
90
+ except yaml.YAMLError as exc:
91
+ raise WorkflowValidationError([f"Invalid runner config YAML: {exc}"]) from exc
92
+
93
+ if not isinstance(data, dict):
94
+ raise WorkflowValidationError(
95
+ ["Runner config file must contain a top-level YAML mapping."]
96
+ )
97
+
98
+ return data
99
+
100
+
101
+ def _build_execution_payload(
102
+ raw: dict[str, Any],
103
+ workflow_path: Path,
104
+ phase: str,
105
+ runner: dict[str, Any],
106
+ config_path: Path,
107
+ ) -> dict[str, Any]:
108
+ handoffs = raw.get("handoffs") or []
109
+ inbound_handoff = None
110
+ for handoff in handoffs:
111
+ if (
112
+ isinstance(handoff, dict)
113
+ and handoff.get("to_phase") == phase
114
+ and handoff.get("status") in {"pending", "completed"}
115
+ ):
116
+ inbound_handoff = handoff
117
+ break
118
+
119
+ return {
120
+ "workflow_path": str(workflow_path),
121
+ "workflow": raw,
122
+ "phase": phase,
123
+ "owner_agent": PHASE_AGENT_MAP.get(phase),
124
+ "prompt_text": _load_phase_asset(runner, "prompt_paths", phase, config_path),
125
+ "agent_text": _load_phase_asset(runner, "agent_paths", phase, config_path),
126
+ "handoff": inbound_handoff,
127
+ }
128
+
129
+
130
+ def _load_phase_asset(
131
+ runner: dict[str, Any], config_key: str, phase: str, config_path: Path
132
+ ) -> str | None:
133
+ phase_map = runner.get(config_key)
134
+ if not isinstance(phase_map, dict):
135
+ return None
136
+ configured_path = phase_map.get(phase)
137
+ if not isinstance(configured_path, str) or not configured_path.strip():
138
+ return None
139
+
140
+ candidate = Path(configured_path)
141
+ if candidate.exists():
142
+ return candidate.read_text(encoding="utf-8")
143
+
144
+ cwd_candidate = Path.cwd() / configured_path
145
+ if cwd_candidate.exists():
146
+ return cwd_candidate.read_text(encoding="utf-8")
147
+
148
+ config_candidate = config_path.parent / configured_path
149
+ if config_candidate.exists():
150
+ return config_candidate.read_text(encoding="utf-8")
151
+
152
+ raise WorkflowValidationError(
153
+ [f"Runner asset for phase '{phase}' not found: {configured_path}"]
154
+ )
155
+
156
+
157
+ def _execute_runner(
158
+ runner: dict[str, Any],
159
+ payload: dict[str, Any],
160
+ workflow_path: Path,
161
+ config_path: Path,
162
+ ) -> dict[str, Any]:
163
+ command = runner.get("command")
164
+ if not isinstance(command, list) or not command:
165
+ raise WorkflowValidationError(["Runner command must be a non-empty list."])
166
+
167
+ with tempfile.TemporaryDirectory() as temp_dir_name:
168
+ temp_dir = Path(temp_dir_name)
169
+ input_path = temp_dir / "policyflow-agent-input.json"
170
+ output_path = temp_dir / "policyflow-agent-output.json"
171
+ input_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
172
+
173
+ substitutions = {
174
+ "workflow_path": str(workflow_path),
175
+ "phase": payload["phase"],
176
+ "input_path": str(input_path),
177
+ "output_path": str(output_path),
178
+ "python_executable": sys.executable,
179
+ }
180
+
181
+ resolved_command = [
182
+ _format_command_token(token, substitutions) for token in command
183
+ ]
184
+
185
+ completed = subprocess.run(
186
+ resolved_command,
187
+ cwd=Path.cwd(),
188
+ text=True,
189
+ capture_output=True,
190
+ check=False,
191
+ )
192
+
193
+ if completed.returncode != 0:
194
+ stderr = completed.stderr.strip()
195
+ stdout = completed.stdout.strip()
196
+ message = stderr or stdout or f"runner exited with code {completed.returncode}"
197
+ raise WorkflowValidationError([f"Agent runner failed: {message}"])
198
+
199
+ if not output_path.exists():
200
+ raise WorkflowValidationError(
201
+ [f"Agent runner did not produce output JSON: {output_path}"]
202
+ )
203
+
204
+ try:
205
+ result = json.loads(output_path.read_text(encoding="utf-8"))
206
+ except json.JSONDecodeError as exc:
207
+ raise WorkflowValidationError(
208
+ [f"Agent runner produced invalid JSON: {exc}"]
209
+ ) from exc
210
+
211
+ if not isinstance(result, dict):
212
+ raise WorkflowValidationError(
213
+ ["Agent runner output must be a top-level JSON object."]
214
+ )
215
+
216
+ return result
217
+
218
+
219
+ def _format_command_token(token: Any, substitutions: dict[str, str]) -> str:
220
+ if not isinstance(token, str):
221
+ raise WorkflowValidationError(["Runner command entries must be strings."])
222
+ return token.format(**substitutions)
223
+
224
+
225
+ def _apply_runner_result(workflow_path: Path, phase: str, result: dict[str, Any]) -> None:
226
+ output_phase = str(result.get("phase", "")).strip()
227
+ if output_phase != phase:
228
+ raise WorkflowValidationError(
229
+ [f"Agent runner output phase must match requested phase: {phase}"]
230
+ )
231
+
232
+ output_owner = str(result.get("owner_agent", "")).strip()
233
+ expected_owner = PHASE_AGENT_MAP.get(phase)
234
+ if output_owner != expected_owner:
235
+ raise WorkflowValidationError(
236
+ [f"Agent runner output owner_agent must match expected owner: {expected_owner}"]
237
+ )
238
+
239
+ status = str(result.get("status", "")).strip()
240
+ if status not in {"completed", "blocked"}:
241
+ raise WorkflowValidationError(
242
+ ["Agent runner output status must be 'completed' or 'blocked'."]
243
+ )
244
+
245
+ if status == "blocked":
246
+ blockers = result.get("blockers")
247
+ if isinstance(blockers, list) and blockers:
248
+ reason = str(blockers[0])
249
+ else:
250
+ reason = str(result.get("summary", "")).strip() or "agent runner blocked phase"
251
+ _block_phase_on_failure(workflow_path, phase, reason)
252
+ raise WorkflowValidationError([f"Agent runner blocked phase '{phase}': {reason}"])
253
+
254
+ raw = load_workflow_raw(workflow_path)
255
+ updated = deepcopy(raw)
256
+ _apply_phase_updates(updated, phase, result)
257
+ validate_workflow_data(updated)
258
+ save_workflow_raw(workflow_path, updated)
259
+
260
+ complete_phase(workflow_path, phase)
261
+
262
+ handoff = result.get("handoff")
263
+ if isinstance(handoff, dict):
264
+ to_phase = handoff.get("to_phase")
265
+ required_inputs = handoff.get("required_inputs")
266
+ produced_outputs = handoff.get("produced_outputs")
267
+ if (
268
+ isinstance(to_phase, str)
269
+ and isinstance(required_inputs, list)
270
+ and isinstance(produced_outputs, list)
271
+ ):
272
+ record_handoff(
273
+ workflow_path,
274
+ phase,
275
+ to_phase,
276
+ [str(item) for item in required_inputs],
277
+ [str(item) for item in produced_outputs],
278
+ [],
279
+ [],
280
+ )
281
+
282
+
283
+ def _apply_phase_updates(raw: dict[str, Any], phase: str, result: dict[str, Any]) -> None:
284
+ contract_key = PHASE_CONTRACT_KEYS.get(phase)
285
+ contract_updates = result.get("contract_updates")
286
+ if contract_key and isinstance(contract_updates, dict):
287
+ raw.setdefault("contracts", {})
288
+ raw["contracts"][contract_key] = contract_updates
289
+
290
+ evidence_key = PHASE_EVIDENCE_KEYS.get(phase)
291
+ evidence_updates = result.get("evidence_updates")
292
+ if evidence_key and isinstance(evidence_updates, dict):
293
+ raw.setdefault("evidence", {})
294
+ raw["evidence"][evidence_key] = evidence_updates
295
+
296
+
297
+ def _block_phase_on_failure(workflow_path: Path, phase: str, reason: str) -> None:
298
+ try:
299
+ block_phase(workflow_path, phase, reason)
300
+ except WorkflowValidationError:
301
+ pass
policyflow/api.py ADDED
@@ -0,0 +1,125 @@
1
+ """Stable public Python API for PolicyFlow consumers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from policyflow.exceptions import WorkflowValidationError
9
+ from policyflow.github_approval import validate_github_pr_approvals
10
+ from policyflow.models import WorkflowDocument
11
+ from policyflow.reporting import audit_directory, workflow_status
12
+ from policyflow.runtime import (
13
+ block_phase,
14
+ complete_phase,
15
+ record_handoff,
16
+ start_phase,
17
+ )
18
+ from policyflow.validator import (
19
+ inspect_workflow_file,
20
+ validate_pull_request,
21
+ validate_workflow_data as _validate_workflow_data,
22
+ validate_workflow_file,
23
+ )
24
+
25
+ __all__ = [
26
+ "WorkflowDocument",
27
+ "WorkflowValidationError",
28
+ "audit_workflows",
29
+ "block_workflow_phase",
30
+ "complete_workflow_phase",
31
+ "get_workflow_status",
32
+ "inspect_workflow",
33
+ "record_workflow_handoff",
34
+ "start_workflow_phase",
35
+ "validate_github_approvals",
36
+ "validate_pr_body",
37
+ "validate_workflow",
38
+ "validate_workflow_data",
39
+ ]
40
+
41
+
42
+ def inspect_workflow(path: str | Path) -> tuple[WorkflowDocument, list[str]]:
43
+ """Validate a workflow file and return the normalized document plus warnings."""
44
+
45
+ return inspect_workflow_file(path)
46
+
47
+
48
+ def validate_workflow(path: str | Path) -> WorkflowDocument:
49
+ """Validate a workflow file and return its normalized workflow document."""
50
+
51
+ return validate_workflow_file(path)
52
+
53
+
54
+ def validate_workflow_data(raw_data: dict[str, Any]) -> WorkflowDocument:
55
+ """Validate an in-memory workflow mapping."""
56
+
57
+ return _validate_workflow_data(raw_data)
58
+
59
+
60
+ def validate_pr_body(
61
+ workflow_path: str | Path, pr_body_path: str | Path
62
+ ) -> WorkflowDocument:
63
+ """Validate a PR body markdown file against a workflow file."""
64
+
65
+ return validate_pull_request(workflow_path, pr_body_path)
66
+
67
+
68
+ def validate_github_approvals(
69
+ workflow_path: str | Path, pr_body_path: str | Path, reviews_path: str | Path
70
+ ) -> WorkflowDocument:
71
+ """Validate GitHub review metadata against workflow approval requirements."""
72
+
73
+ return validate_github_pr_approvals(workflow_path, pr_body_path, reviews_path)
74
+
75
+
76
+ def get_workflow_status(path: str | Path) -> dict[str, Any]:
77
+ """Return workflow status and merge-readiness data."""
78
+
79
+ return workflow_status(Path(path))
80
+
81
+
82
+ def audit_workflows(directory: str | Path) -> dict[str, Any]:
83
+ """Return an audit payload for all workflow files under a directory."""
84
+
85
+ return audit_directory(Path(directory))
86
+
87
+
88
+ def start_workflow_phase(path: str | Path, phase: str) -> None:
89
+ """Start a pending workflow phase and persist runtime state."""
90
+
91
+ start_phase(Path(path), phase)
92
+
93
+
94
+ def complete_workflow_phase(path: str | Path, phase: str) -> None:
95
+ """Complete an in-progress workflow phase and persist runtime state."""
96
+
97
+ complete_phase(Path(path), phase)
98
+
99
+
100
+ def block_workflow_phase(path: str | Path, phase: str, reason: str) -> None:
101
+ """Block a workflow phase with a reason and persist runtime state."""
102
+
103
+ block_phase(Path(path), phase, reason)
104
+
105
+
106
+ def record_workflow_handoff(
107
+ path: str | Path,
108
+ from_phase: str,
109
+ to_phase: str,
110
+ required_inputs: list[str],
111
+ produced_outputs: list[str],
112
+ blockers: list[str] | None = None,
113
+ override_refs: list[str] | None = None,
114
+ ) -> None:
115
+ """Record a workflow phase handoff with concrete artifacts."""
116
+
117
+ record_handoff(
118
+ Path(path),
119
+ from_phase,
120
+ to_phase,
121
+ required_inputs,
122
+ produced_outputs,
123
+ blockers,
124
+ override_refs,
125
+ )
@@ -0,0 +1,50 @@
1
+ # Architecture Agent
2
+
3
+ ## Purpose
4
+
5
+ Protect architecture boundaries and keep the workflow aligned with the target project's operating model.
6
+
7
+ ## Responsibilities
8
+
9
+ - review architecture context and boundaries
10
+ - confirm risk and review depth
11
+ - identify approval needs
12
+ - define implementation constraints
13
+
14
+ ## Accepted Inputs
15
+
16
+ - issue brief
17
+ - acceptance criteria
18
+ - non-goals
19
+ - initial risk level
20
+ - protected areas touched
21
+ - confidence summary
22
+ - escalation flags
23
+
24
+ ## Required Outputs
25
+
26
+ - architecture assessment
27
+ - approved scope
28
+ - module boundaries
29
+ - contract impact
30
+ - risk review decision
31
+ - required reviews
32
+ - implementation constraints
33
+
34
+ ## Forbidden Actions
35
+
36
+ - waive mandatory reviews
37
+ - approve speculative architecture
38
+ - normalize undocumented coupling
39
+
40
+ ## Escalation Triggers
41
+
42
+ - contract ownership unclear
43
+ - boundary conflict
44
+ - protected area touched without approval path
45
+ - risk needs upgrade
46
+
47
+ ## Handoff Target
48
+
49
+ - senior-dev-agent
50
+ - human approval first when required
@@ -0,0 +1,46 @@
1
+ # Planning Agent
2
+
3
+ ## Purpose
4
+
5
+ Turn an approved request into a bounded workflow with clear scope, non-goals, risk, approvals, and handoffs.
6
+
7
+ ## Responsibilities
8
+
9
+ - read target-project context first
10
+ - classify risk
11
+ - choose the smallest workflow that still satisfies governance rules
12
+ - define scope, non-goals, affected modules, and escalation points
13
+
14
+ ## Accepted Inputs
15
+
16
+ - approved issue or request
17
+ - target-project context
18
+ - relevant governance rules
19
+
20
+ ## Required Outputs
21
+
22
+ - issue brief
23
+ - acceptance criteria
24
+ - non-goals
25
+ - initial risk level
26
+ - protected areas touched
27
+ - confidence summary
28
+ - escalation flags
29
+
30
+ ## Forbidden Actions
31
+
32
+ - implement changes directly
33
+ - downgrade risk to avoid review
34
+ - reduce required reviews below the matrix
35
+
36
+ ## Escalation Triggers
37
+
38
+ - risk unclear
39
+ - protected area touched
40
+ - conflicting architecture signals
41
+ - scope ambiguity
42
+
43
+ ## Handoff Target
44
+
45
+ - architecture-agent
46
+ - senior-dev-agent on LOW-risk fast path only
@@ -0,0 +1,46 @@
1
+ # QA Agent
2
+
3
+ ## Purpose
4
+
5
+ Validate acceptance criteria, evidence quality, and merge readiness.
6
+
7
+ ## Responsibilities
8
+
9
+ - check scope and acceptance criteria against the actual diff
10
+ - confirm validation evidence
11
+ - confirm approval compliance
12
+ - record residual risks and merge readiness
13
+
14
+ ## Accepted Inputs
15
+
16
+ - review approval
17
+ - residual risk
18
+ - QA focus areas
19
+ - test expectations
20
+ - approval evidence when required
21
+
22
+ ## Required Outputs
23
+
24
+ - QA report
25
+ - quality gate status
26
+ - unresolved risks
27
+ - approval required
28
+ - merge readiness
29
+
30
+ ## Forbidden Actions
31
+
32
+ - declare success without evidence
33
+ - bypass approval requirements
34
+ - weaken quality gates to force completion
35
+
36
+ ## Escalation Triggers
37
+
38
+ - validation evidence missing
39
+ - approval ambiguity
40
+ - confidence conflicts with evidence
41
+ - unresolved critical risk remains
42
+
43
+ ## Handoff Target
44
+
45
+ - human approval when required
46
+ - merge readiness when all gates are satisfied
@@ -0,0 +1,51 @@
1
+ # Review Agent
2
+
3
+ ## Purpose
4
+
5
+ Review for regressions, scope drift, architecture issues, and missing validation.
6
+
7
+ ## Responsibilities
8
+
9
+ - compare the diff with issue, workflow, and rules
10
+ - check protected areas and contract safety
11
+ - identify missing tests or documentation
12
+ - decide whether the change returns for fixes or can move to QA
13
+
14
+ ## Accepted Inputs
15
+
16
+ - implementation summary
17
+ - changed files
18
+ - test summary
19
+ - docs updates
20
+ - known limitations
21
+ - unresolved questions
22
+
23
+ ## Required Outputs
24
+
25
+ - review findings
26
+ - required fixes
27
+ - severity
28
+ - approval status
29
+ - review approval
30
+ - residual risk
31
+ - QA focus areas
32
+ - test expectations
33
+
34
+ ## Forbidden Actions
35
+
36
+ - rewrite scope during review
37
+ - waive approvals
38
+ - hand off to QA while blocking findings remain
39
+
40
+ ## Escalation Triggers
41
+
42
+ - hidden protected-area impact
43
+ - risk understated
44
+ - evidence incomplete
45
+ - fix loop does not converge
46
+
47
+ ## Handoff Target
48
+
49
+ - senior-dev-agent
50
+ - qa-agent
51
+ - human approval if risk or approval path changes
@@ -0,0 +1,49 @@
1
+ # Senior Dev Agent
2
+
3
+ ## Purpose
4
+
5
+ Implement the smallest approved change while preserving reviewability and evidence.
6
+
7
+ ## Responsibilities
8
+
9
+ - follow the approved workflow
10
+ - keep changes local and explicit
11
+ - add or update tests when approved logic changes require them
12
+ - record assumptions and validation
13
+
14
+ ## Accepted Inputs
15
+
16
+ - architecture assessment when required
17
+ - approved scope
18
+ - module boundaries
19
+ - contract impact
20
+ - risk review decision
21
+ - required reviews
22
+ - implementation constraints
23
+
24
+ ## Required Outputs
25
+
26
+ - implementation summary
27
+ - changed files
28
+ - test summary
29
+ - docs updates
30
+ - known limitations
31
+ - unresolved questions
32
+
33
+ ## Forbidden Actions
34
+
35
+ - expand scope
36
+ - bypass approval
37
+ - change protected areas without authorization
38
+
39
+ ## Escalation Triggers
40
+
41
+ - scope no longer fits approved workflow
42
+ - protected area becomes implicated
43
+ - validation blocked
44
+ - architecture conflict appears during implementation
45
+
46
+ ## Handoff Target
47
+
48
+ - review-agent
49
+ - architecture-agent or human approval if governance conditions change