@pennyfarthing/core 11.2.2 → 11.3.2
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.
- package/README.md +3 -3
- package/package.json +1 -1
- package/packages/core/dist/cli/commands/doctor-legacy.test.js +2 -2
- package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
- package/packages/core/dist/cli/commands/doctor.d.ts +63 -0
- package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/doctor.js +280 -43
- package/packages/core/dist/cli/commands/doctor.js.map +1 -1
- package/packages/core/dist/cli/commands/init.d.ts +12 -0
- package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/init.js +45 -0
- package/packages/core/dist/cli/commands/init.js.map +1 -1
- package/packages/core/dist/cli/commands/pyproject-install.test.d.ts +19 -0
- package/packages/core/dist/cli/commands/pyproject-install.test.d.ts.map +1 -0
- package/packages/core/dist/cli/commands/pyproject-install.test.js +261 -0
- package/packages/core/dist/cli/commands/pyproject-install.test.js.map +1 -0
- package/packages/core/dist/cli/commands/update-consolidation.test.js +14 -6
- package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
- package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
- package/packages/core/dist/cli/commands/update.js +5 -1
- package/packages/core/dist/cli/commands/update.js.map +1 -1
- package/packages/core/dist/cli/index.js +2 -0
- package/packages/core/dist/cli/index.js.map +1 -1
- package/packages/core/dist/cli/utils/python.d.ts +1 -0
- package/packages/core/dist/cli/utils/python.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/python.js +22 -1
- package/packages/core/dist/cli/utils/python.js.map +1 -1
- package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts +17 -0
- package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/settings-hook-migration.test.js +382 -0
- package/packages/core/dist/cli/utils/settings-hook-migration.test.js.map +1 -0
- package/packages/core/dist/cli/utils/settings-pf-wrapper.test.d.ts +16 -0
- package/packages/core/dist/cli/utils/settings-pf-wrapper.test.d.ts.map +1 -0
- package/packages/core/dist/cli/utils/settings-pf-wrapper.test.js +377 -0
- package/packages/core/dist/cli/utils/settings-pf-wrapper.test.js.map +1 -0
- package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
- package/packages/core/dist/cli/utils/settings.js +15 -2
- package/packages/core/dist/cli/utils/settings.js.map +1 -1
- package/packages/core/dist/server/paths.d.ts.map +1 -1
- package/packages/core/dist/server/paths.js +6 -0
- package/packages/core/dist/server/paths.js.map +1 -1
- package/packages/core/dist/server/settings.d.ts.map +1 -1
- package/packages/core/dist/server/settings.js +5 -0
- package/packages/core/dist/server/settings.js.map +1 -1
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js +7 -5
- package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -1
- package/packages/core/dist/workflow/workflow-graph-validation.d.ts +65 -0
- package/packages/core/dist/workflow/workflow-graph-validation.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-graph-validation.js +190 -0
- package/packages/core/dist/workflow/workflow-graph-validation.js.map +1 -0
- package/packages/core/dist/workflow/workflow-graph-validation.test.d.ts +18 -0
- package/packages/core/dist/workflow/workflow-graph-validation.test.d.ts.map +1 -0
- package/packages/core/dist/workflow/workflow-graph-validation.test.js +706 -0
- package/packages/core/dist/workflow/workflow-graph-validation.test.js.map +1 -0
- package/packages/core/dist/workflow/workflow-migration.test.js +6 -5
- package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
- package/pennyfarthing-dist/agents/dev.md +4 -2
- package/pennyfarthing-dist/agents/devops.md +2 -10
- package/pennyfarthing-dist/agents/reviewer-preflight.md +4 -5
- package/pennyfarthing-dist/agents/sm.md +4 -17
- package/pennyfarthing-dist/commands/pf-health-check.md +30 -11
- package/pennyfarthing-dist/gates/{confidence-sm.md → confidence.md} +16 -17
- package/pennyfarthing-dist/gates/dev-exit.md +75 -0
- package/pennyfarthing-dist/gates/merge-ready.md +49 -0
- package/pennyfarthing-dist/gates/release-ready.md +95 -0
- package/pennyfarthing-dist/gates/reviewer-preflight-check.md +90 -0
- package/pennyfarthing-dist/gates/sm-setup-exit.md +82 -0
- package/pennyfarthing-dist/guides/agent-behavior.md +88 -30
- package/pennyfarthing-dist/guides/gates.md +7 -2
- package/pennyfarthing-dist/scripts/lib/find-root.sh +5 -0
- package/pennyfarthing-dist/scripts/lib/run-pf.sh +7 -0
- package/pennyfarthing-dist/skills/pf-settings/skill.md +42 -0
- package/pennyfarthing-dist/skills/skill-registry.yaml +15 -0
- package/pennyfarthing-dist/templates/pyproject.toml +27 -0
- package/pennyfarthing-dist/workflows/bdd-tandem.yaml +7 -3
- package/pennyfarthing-dist/workflows/bdd.yaml +7 -3
- package/pennyfarthing-dist/workflows/installation-check/steps/step-01-foundation.md +77 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-02-commands.md +82 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-03-hooks.md +121 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-04-scripts.md +83 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-05-layout.md +81 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-06-legacy.md +94 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-07-tools.md +80 -0
- package/pennyfarthing-dist/workflows/installation-check/steps/step-08-summary.md +99 -0
- package/pennyfarthing-dist/workflows/installation-check/workflow.yaml +47 -0
- package/pennyfarthing-dist/workflows/tdd-tandem.yaml +7 -3
- package/pennyfarthing-dist/workflows/tdd.yaml +7 -3
- package/pennyfarthing-dist/workflows/trivial.yaml +7 -3
- package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bc/cli.py +21 -0
- package/pennyfarthing_scripts/bc/focus.py +1 -0
- package/pennyfarthing_scripts/bc/split.py +52 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bikerack/context_meter_footer.py +53 -3
- package/pennyfarthing_scripts/bikerack/tui.py +202 -8
- package/pennyfarthing_scripts/bmad/__init__.py +1 -0
- package/pennyfarthing_scripts/bmad/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bmad/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bmad/__pycache__/parser.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bmad/__pycache__/sync.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/bmad/__pycache__/test_parser.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/bmad/__pycache__/test_sync.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/bmad/cli.py +197 -0
- package/pennyfarthing_scripts/bmad/importer.py +200 -0
- package/pennyfarthing_scripts/bmad/parser.py +233 -0
- package/pennyfarthing_scripts/bmad/sync.py +464 -0
- package/pennyfarthing_scripts/bmad/test_parser.py +253 -0
- package/pennyfarthing_scripts/bmad/test_sync.py +223 -0
- package/pennyfarthing_scripts/cli.py +10 -0
- package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/__init__.py +0 -0
- package/pennyfarthing_scripts/settings/__pycache__/__init__.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/__pycache__/settings.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/settings/cli.py +55 -0
- package/pennyfarthing_scripts/settings/settings.py +98 -0
- package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
- package/pennyfarthing_scripts/tests/__pycache__/test_workflow_list_team.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +17 -16
- package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +45 -47
- package/pennyfarthing_scripts/tests/test_workflow_list_team.py +0 -4
- package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
- package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
<gate name="reviewer-preflight-check" model="haiku">
|
|
2
|
+
|
|
3
|
+
<purpose>
|
|
4
|
+
Composite gate for reviewer preflight. Extends tests-pass with code smell
|
|
5
|
+
detection and error boundary checks. The reviewer-preflight subagent runs
|
|
6
|
+
this gate before the Reviewer does critical analysis.
|
|
7
|
+
</purpose>
|
|
8
|
+
|
|
9
|
+
<ref gate="gates/tests-pass" />
|
|
10
|
+
|
|
11
|
+
<check name="no-debug-code">
|
|
12
|
+
No debug artifacts in changed files.
|
|
13
|
+
Search changed files (`git diff --name-only develop...HEAD`) for:
|
|
14
|
+
- `console.log` (not guarded by `process.env.NODE_ENV`)
|
|
15
|
+
- `dangerouslySetInnerHTML`
|
|
16
|
+
- `.skip(` (test skips)
|
|
17
|
+
- `TODO` / `FIXME`
|
|
18
|
+
Count by category. None found = pass.
|
|
19
|
+
</check>
|
|
20
|
+
|
|
21
|
+
<check name="error-boundaries">
|
|
22
|
+
UI components have error boundaries (UI repos only).
|
|
23
|
+
For React component files in diff, verify error boundary wrapping.
|
|
24
|
+
Skip for non-UI repos.
|
|
25
|
+
</check>
|
|
26
|
+
|
|
27
|
+
<pass>
|
|
28
|
+
Run all checks from `gates/tests-pass` (test-suite, working-tree, branch-status),
|
|
29
|
+
then run no-debug-code and error-boundaries.
|
|
30
|
+
|
|
31
|
+
If ALL pass, return:
|
|
32
|
+
|
|
33
|
+
```yaml
|
|
34
|
+
GATE_RESULT:
|
|
35
|
+
status: pass
|
|
36
|
+
gate: reviewer-preflight-check
|
|
37
|
+
message: "Preflight clear: tests green, no smells, boundaries present"
|
|
38
|
+
checks:
|
|
39
|
+
- name: test-suite
|
|
40
|
+
status: pass
|
|
41
|
+
detail: "{passed}/{total} passing ({source: cached|fresh})"
|
|
42
|
+
- name: working-tree
|
|
43
|
+
status: pass
|
|
44
|
+
detail: "No uncommitted changes"
|
|
45
|
+
- name: branch-status
|
|
46
|
+
status: pass
|
|
47
|
+
detail: "On branch {branch}"
|
|
48
|
+
- name: no-debug-code
|
|
49
|
+
status: pass
|
|
50
|
+
detail: "No debug patterns in {N} changed files"
|
|
51
|
+
- name: error-boundaries
|
|
52
|
+
status: pass
|
|
53
|
+
detail: "Error boundaries present (or N/A for non-UI)"
|
|
54
|
+
```
|
|
55
|
+
</pass>
|
|
56
|
+
|
|
57
|
+
<fail>
|
|
58
|
+
If ANY check fails, report all results. Note: code smells are advisory (YELLOW)
|
|
59
|
+
rather than blocking (RED) — the Reviewer makes the final call.
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
GATE_RESULT:
|
|
63
|
+
status: fail
|
|
64
|
+
gate: reviewer-preflight-check
|
|
65
|
+
message: "Preflight issues: {summary}"
|
|
66
|
+
checks:
|
|
67
|
+
- name: test-suite
|
|
68
|
+
status: pass | fail
|
|
69
|
+
detail: "{test results or failure list}"
|
|
70
|
+
- name: working-tree
|
|
71
|
+
status: pass | fail
|
|
72
|
+
detail: "{clean or list of uncommitted files}"
|
|
73
|
+
- name: branch-status
|
|
74
|
+
status: pass | fail
|
|
75
|
+
detail: "{branch info}"
|
|
76
|
+
- name: no-debug-code
|
|
77
|
+
status: pass | fail
|
|
78
|
+
detail: "{clean or smell counts by category}"
|
|
79
|
+
- name: error-boundaries
|
|
80
|
+
status: pass | fail
|
|
81
|
+
detail: "{present, missing list, or N/A}"
|
|
82
|
+
recovery:
|
|
83
|
+
- "Fix failing tests before review"
|
|
84
|
+
- "Commit or stash uncommitted changes"
|
|
85
|
+
- "Remove debug code: {locations}"
|
|
86
|
+
- "Add error boundaries to: {components}"
|
|
87
|
+
```
|
|
88
|
+
</fail>
|
|
89
|
+
|
|
90
|
+
</gate>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<gate name="sm-setup-exit" model="haiku">
|
|
2
|
+
|
|
3
|
+
<purpose>
|
|
4
|
+
Verify SM has completed story setup before handing off to the next agent.
|
|
5
|
+
Without a properly configured session file, the next agent cannot function.
|
|
6
|
+
Extracts the inline pre-handoff checklist from sm.md.
|
|
7
|
+
</purpose>
|
|
8
|
+
|
|
9
|
+
<pass>
|
|
10
|
+
Run these checks in order:
|
|
11
|
+
|
|
12
|
+
1. **session-exists:** Check `.session/{story-id}-session.md` exists.
|
|
13
|
+
```bash
|
|
14
|
+
ls .session/{story-id}-session.md
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
2. **session-fields-set:** Read session file and verify:
|
|
18
|
+
- `**Workflow:**` field present and non-empty
|
|
19
|
+
- `**Phase:**` field present and set to `setup`
|
|
20
|
+
|
|
21
|
+
3. **story-context-exists:** Verify:
|
|
22
|
+
- `sprint/context/context-epic-{N}.md` exists (extract epic number from story ID)
|
|
23
|
+
- Session file contains technical approach section
|
|
24
|
+
- Session file contains acceptance criteria
|
|
25
|
+
|
|
26
|
+
4. **branch-created:** For each repo in session `**Repos:**`:
|
|
27
|
+
- Run `git branch --show-current`
|
|
28
|
+
- Confirm not on `main` or `develop`
|
|
29
|
+
|
|
30
|
+
If ALL pass, return:
|
|
31
|
+
|
|
32
|
+
```yaml
|
|
33
|
+
GATE_RESULT:
|
|
34
|
+
status: pass
|
|
35
|
+
gate: sm-setup-exit
|
|
36
|
+
message: "Session {story-id} ready. Workflow: {workflow}, Branch: {branch}"
|
|
37
|
+
checks:
|
|
38
|
+
- name: session-exists
|
|
39
|
+
status: pass
|
|
40
|
+
detail: ".session/{story-id}-session.md exists"
|
|
41
|
+
- name: session-fields-set
|
|
42
|
+
status: pass
|
|
43
|
+
detail: "Workflow: {workflow}, Phase: setup"
|
|
44
|
+
- name: story-context-exists
|
|
45
|
+
status: pass
|
|
46
|
+
detail: "Epic context and story ACs present"
|
|
47
|
+
- name: branch-created
|
|
48
|
+
status: pass
|
|
49
|
+
detail: "Branch {branch} created in {repos}"
|
|
50
|
+
```
|
|
51
|
+
</pass>
|
|
52
|
+
|
|
53
|
+
<fail>
|
|
54
|
+
If ANY check fails, report all results:
|
|
55
|
+
|
|
56
|
+
```yaml
|
|
57
|
+
GATE_RESULT:
|
|
58
|
+
status: fail
|
|
59
|
+
gate: sm-setup-exit
|
|
60
|
+
message: "Setup incomplete: {summary}"
|
|
61
|
+
checks:
|
|
62
|
+
- name: session-exists
|
|
63
|
+
status: pass | fail
|
|
64
|
+
detail: "{exists or missing}"
|
|
65
|
+
- name: session-fields-set
|
|
66
|
+
status: pass | fail
|
|
67
|
+
detail: "{fields present or missing fields list}"
|
|
68
|
+
- name: story-context-exists
|
|
69
|
+
status: pass | fail
|
|
70
|
+
detail: "{context present or missing sections}"
|
|
71
|
+
- name: branch-created
|
|
72
|
+
status: pass | fail
|
|
73
|
+
detail: "{branch status per repo}"
|
|
74
|
+
recovery:
|
|
75
|
+
- "Run sm-setup to create session file"
|
|
76
|
+
- "Set Workflow and Phase fields in session"
|
|
77
|
+
- "Write story context with technical approach and ACs"
|
|
78
|
+
- "Create feature branch: git checkout -b feat/{story-slug}"
|
|
79
|
+
```
|
|
80
|
+
</fail>
|
|
81
|
+
|
|
82
|
+
</gate>
|
|
@@ -101,46 +101,104 @@ See `.pennyfarthing/guides/tandem-protocol.md` for full protocol details.
|
|
|
101
101
|
---
|
|
102
102
|
|
|
103
103
|
<team-mode>
|
|
104
|
-
## Team Mode
|
|
104
|
+
## Team Mode — Phase-Scoped Native Teams
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
**team lead** and spawns teammates for parallel collaboration within that phase.
|
|
106
|
+
Team mode enables parallel agent collaboration within a single workflow phase using Claude Code's native Agent Teams. The phase agent is always the **team lead**; spawned agents are **teammates**. Teams are created at phase start and destroyed before handoff.
|
|
108
107
|
|
|
109
|
-
|
|
108
|
+
**Prerequisites:**
|
|
109
|
+
- Workflow phase has a `team:` block in its YAML definition
|
|
110
|
+
- `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` env var is set
|
|
111
|
+
- Interactive session (not `-p` mode)
|
|
112
|
+
- Feature detection passes (`pf detect teams` or equivalent)
|
|
110
113
|
|
|
111
|
-
|
|
112
|
-
to create a phase-scoped team named after the phase (e.g., `"green-phase"`).
|
|
114
|
+
**If prerequisites are not met:** Fall back to solo execution (optionally with tandem consultation). No error — team mode is always optional.
|
|
113
115
|
|
|
114
|
-
###
|
|
116
|
+
### Detection
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
runs `pf agent start {agent}` for full Prime activation. Keep spawn prompts
|
|
118
|
-
under 500 tokens — teammates auto-load CLAUDE.md, MCP servers, and skills.
|
|
118
|
+
On activation, check the workflow YAML for a `team:` block on your phase:
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
```yaml
|
|
121
|
+
phases:
|
|
122
|
+
- name: green
|
|
123
|
+
agent: dev
|
|
124
|
+
team:
|
|
125
|
+
teammates:
|
|
126
|
+
- agent: architect
|
|
127
|
+
task: "Review implementation approach and patterns"
|
|
128
|
+
- agent: tea
|
|
129
|
+
task: "Verify tests stay green, flag regressions"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If `team:` is present and prerequisites are met, you are the **team lead** for this phase.
|
|
133
|
+
|
|
134
|
+
### Team Lead Responsibilities
|
|
135
|
+
|
|
136
|
+
**On phase entry:**
|
|
137
|
+
|
|
138
|
+
1. **Create the team:**
|
|
139
|
+
```
|
|
140
|
+
TeamCreate("phase-{PHASE}-{STORY_ID}")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
2. **Spawn teammates** per workflow YAML config. Each teammate gets a spawn prompt:
|
|
144
|
+
```
|
|
145
|
+
Task(team_name="phase-{PHASE}-{STORY_ID}", name="{agent}",
|
|
146
|
+
prompt="Run `pf agent start {agent}`. Story: {STORY_ID}.
|
|
147
|
+
{task assignment from YAML}")
|
|
148
|
+
```
|
|
149
|
+
Teammates auto-load CLAUDE.md, MCP servers, and skills from the working directory. Spawn prompts only need the activation command and task assignment — keep under 500 tokens.
|
|
150
|
+
|
|
151
|
+
3. **Coordinate via SendMessage.** Use `SendMessage` for all intra-phase communication with teammates:
|
|
152
|
+
- Assign work: `SendMessage(to="{teammate}", message="Implement the adapter pattern for...")`
|
|
153
|
+
- Check status: `SendMessage(to="{teammate}", message="Status on your review?")`
|
|
154
|
+
- Broadcast: `SendMessage(message="Shifting approach — using strategy pattern instead")`
|
|
155
|
+
|
|
156
|
+
4. **Do your own work.** You are still the primary implementer. Teammates assist — they don't replace you.
|
|
157
|
+
|
|
158
|
+
**Before exit protocol:**
|
|
121
159
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
160
|
+
5. **Shut down all teammates.** This is mandatory before starting the normal exit protocol:
|
|
161
|
+
```
|
|
162
|
+
SendMessage(to="{teammate}", message="Phase complete. Shut down.")
|
|
163
|
+
```
|
|
164
|
+
Wait for teammates to go idle, then:
|
|
165
|
+
```
|
|
166
|
+
TeamDelete("phase-{PHASE}-{STORY_ID}")
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
6. **Proceed with normal exit protocol** (assessment → resolve-gate → complete-phase → marker).
|
|
170
|
+
|
|
171
|
+
### Teammate Responsibilities
|
|
172
|
+
|
|
173
|
+
When spawned as a teammate (you receive a task via spawn prompt, not a phase handoff):
|
|
126
174
|
|
|
127
|
-
|
|
175
|
+
1. **Recognize you are a teammate, not the lead.** You do not own the phase. You do not run the exit protocol. You do not emit handoff markers.
|
|
128
176
|
|
|
129
|
-
|
|
130
|
-
- Communicate via SendMessage (DMs to lead or other teammates)
|
|
131
|
-
- Go idle when their assigned task is complete
|
|
132
|
-
- Respond to shutdown requests with shutdown_response
|
|
177
|
+
2. **Activate normally** via `pf agent start {agent}`. Read the session file for story context.
|
|
133
178
|
|
|
134
|
-
|
|
179
|
+
3. **Communicate via SendMessage.** All collaboration with the lead and other teammates uses `SendMessage`:
|
|
180
|
+
- Report findings: `SendMessage(to="lead", message="Found coupling issue in...")`
|
|
181
|
+
- Ask questions: `SendMessage(to="lead", message="Should I use the existing adapter?")`
|
|
182
|
+
- Share status: `SendMessage(to="lead", message="Review complete. 2 issues found.")`
|
|
135
183
|
|
|
136
|
-
|
|
137
|
-
1. Send `shutdown_request` to all teammates via SendMessage
|
|
138
|
-
2. Wait for `shutdown_response` from each teammate
|
|
139
|
-
3. Run `TeamDelete` to clean up the team
|
|
140
|
-
4. Then proceed with normal handoff (resolve-gate → complete-phase → marker)
|
|
184
|
+
4. **Go idle when your task is done.** Once your assigned task is complete, send a final status message and wait. Do not start new work unprompted.
|
|
141
185
|
|
|
142
|
-
|
|
143
|
-
|
|
186
|
+
5. **Respond to shutdown requests.** When the lead sends a shutdown message, wrap up immediately. Save any observations to the session file or sidecar, then stop.
|
|
187
|
+
|
|
188
|
+
### Communication Channels
|
|
189
|
+
|
|
190
|
+
| Channel | Scope | Used For |
|
|
191
|
+
|---------|-------|----------|
|
|
192
|
+
| `SendMessage` | Intra-phase (within team) | Lead ↔ teammate collaboration, status updates, task assignment |
|
|
193
|
+
| Reflector markers (`<!-- CYCLIST:... -->`) | Inter-phase (between phases) | Handoff from one phase agent to the next — **unchanged** |
|
|
194
|
+
| Session file | Cross-phase persistence | Story state, assessments, ACs — written by lead only in team mode |
|
|
195
|
+
| Sidecar files | Agent learning | Teammates may write to their own sidecar with file locking |
|
|
196
|
+
|
|
197
|
+
<critical>
|
|
198
|
+
**Never use SendMessage for inter-phase handoff.** Markers and `pf handoff` are the only way to transition between phases. SendMessage is for real-time collaboration within a phase.
|
|
199
|
+
|
|
200
|
+
**Never use markers for intra-phase communication.** Markers are routing signals for Cyclist UI and the handoff system. They have no meaning inside a team.
|
|
201
|
+
</critical>
|
|
144
202
|
</team-mode>
|
|
145
203
|
|
|
146
204
|
---
|
|
@@ -167,8 +225,8 @@ Handoff between phases is unchanged.
|
|
|
167
225
|
## Exit Protocol
|
|
168
226
|
|
|
169
227
|
1. Write assessment to session
|
|
170
|
-
2.
|
|
171
|
-
3.
|
|
228
|
+
2. **If team mode active:** Shut down all teammates via `SendMessage`, then `TeamDelete`. Wait for cleanup before proceeding.
|
|
229
|
+
3. Terminate tandem backseat (if active)
|
|
172
230
|
4. `"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh handoff resolve-gate {story-id} {workflow} {phase}` → RESOLVE_RESULT
|
|
173
231
|
5. If blocked → report error, STOP
|
|
174
232
|
6. If skip → jump to step 8
|
|
@@ -15,7 +15,12 @@ Gates live in `pennyfarthing-dist/gates/` and are referenced by workflow YAML fi
|
|
|
15
15
|
| **tests-pass** | `gates/tests-pass.md` | Verify all tests pass, working tree clean, correct branch | Dev → Reviewer transitions |
|
|
16
16
|
| **tests-fail** | `gates/tests-fail.md` | Verify tests are RED (failing) with AC coverage | TEA → Dev transitions |
|
|
17
17
|
| **approval** | `gates/approval.md` | Verify reviewer has issued explicit APPROVED verdict | Reviewer → SM transitions |
|
|
18
|
-
| **confidence
|
|
18
|
+
| **confidence** | `gates/confidence.md` | Check if user instruction is ambiguous | Any agent entry gate |
|
|
19
|
+
| **dev-exit** | `gates/dev-exit.md` | Composite: tests-pass + no debug code | Dev → Reviewer transitions |
|
|
20
|
+
| **sm-setup-exit** | `gates/sm-setup-exit.md` | Session file, fields, context, branch created | SM → next agent transitions |
|
|
21
|
+
| **merge-ready** | `gates/merge-ready.md` | No open non-draft PRs | SM new work gate |
|
|
22
|
+
| **release-ready** | `gates/release-ready.md` | Composite: tests-pass + build, version, changelog | DevOps pre-deploy |
|
|
23
|
+
| **reviewer-preflight-check** | `gates/reviewer-preflight-check.md` | Composite: tests-pass + code smells, error boundaries | Reviewer preflight |
|
|
19
24
|
|
|
20
25
|
## Gate File Format
|
|
21
26
|
|
|
@@ -96,7 +101,7 @@ Extended evaluation criteria can live in `gates/evaluations/`:
|
|
|
96
101
|
|
|
97
102
|
| File | Purpose |
|
|
98
103
|
|------|---------|
|
|
99
|
-
| `evaluations/confidence-sm.md` |
|
|
104
|
+
| `evaluations/confidence-sm.md` | Historical evaluation of SM confidence gate (led to agent-agnostic `confidence` gate) |
|
|
100
105
|
|
|
101
106
|
## Creating Custom Gates
|
|
102
107
|
|
|
@@ -29,6 +29,11 @@ if [[ -n "$_caller_script" ]]; then
|
|
|
29
29
|
_pkg="${_pkg%/.pennyfarthing}"
|
|
30
30
|
if [[ "$_pkg" == */node_modules/* ]]; then
|
|
31
31
|
PROJECT_ROOT="${_pkg%/node_modules/*}"
|
|
32
|
+
elif [[ -d "$_pkg/.pennyfarthing" ]]; then
|
|
33
|
+
PROJECT_ROOT="$_pkg"
|
|
34
|
+
elif [[ -d "$_pkg/../.pennyfarthing" ]]; then
|
|
35
|
+
# Dogfooding: pennyfarthing/ inlined inside orchestrator
|
|
36
|
+
PROJECT_ROOT="$(cd "$_pkg/.." && pwd -P)"
|
|
32
37
|
else
|
|
33
38
|
PROJECT_ROOT="$_pkg"
|
|
34
39
|
fi
|
|
@@ -18,15 +18,22 @@ _pf_project=""
|
|
|
18
18
|
if [[ -f "$PROJECT_ROOT/pennyfarthing/pyproject.toml" ]]; then
|
|
19
19
|
# Dogfooding: inlined framework repo
|
|
20
20
|
_pf_project="$PROJECT_ROOT/pennyfarthing"
|
|
21
|
+
elif [[ -f "$PROJECT_ROOT/node_modules/@pennyfarthing/core/pyproject.toml" ]]; then
|
|
22
|
+
# Symlink install: npm package has full Python source
|
|
23
|
+
_pf_project="$PROJECT_ROOT/node_modules/@pennyfarthing/core"
|
|
21
24
|
elif [[ -f "$PROJECT_ROOT/pyproject.toml" ]]; then
|
|
22
25
|
# Consumer: project-level pyproject.toml with pennyfarthing-scripts dep
|
|
23
26
|
_pf_project="$PROJECT_ROOT"
|
|
27
|
+
elif [[ -f "$PROJECT_ROOT/.pennyfarthing/pyproject.toml" ]]; then
|
|
28
|
+
# Consumer: auto-generated by pennyfarthing init
|
|
29
|
+
_pf_project="$PROJECT_ROOT/.pennyfarthing"
|
|
24
30
|
fi
|
|
25
31
|
|
|
26
32
|
if [[ -z "$_pf_project" ]]; then
|
|
27
33
|
echo "Error: No pyproject.toml found for pennyfarthing-scripts" >&2
|
|
28
34
|
echo "Expected: $PROJECT_ROOT/pennyfarthing/pyproject.toml (dogfooding)" >&2
|
|
29
35
|
echo " or: $PROJECT_ROOT/pyproject.toml (consumer)" >&2
|
|
36
|
+
echo " or: $PROJECT_ROOT/.pennyfarthing/pyproject.toml (auto-generated)" >&2
|
|
30
37
|
exit 1
|
|
31
38
|
fi
|
|
32
39
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pf-settings
|
|
3
|
+
description: |
|
|
4
|
+
View and manage .pennyfarthing/config.local.yaml settings.
|
|
5
|
+
Get, set, and show configuration values using dot-path notation.
|
|
6
|
+
args: "[show|get|set] [key] [value]"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# /pf-settings - Configuration Settings
|
|
10
|
+
|
|
11
|
+
View and manage `.pennyfarthing/config.local.yaml` settings.
|
|
12
|
+
|
|
13
|
+
## Quick Reference
|
|
14
|
+
|
|
15
|
+
| Command | CLI | Purpose |
|
|
16
|
+
|---------|-----|---------|
|
|
17
|
+
| `/pf-settings show` | `pf.sh settings show` | Pretty-print all settings |
|
|
18
|
+
| `/pf-settings get <key>` | `pf.sh settings get <key>` | Get value by dot-path |
|
|
19
|
+
| `/pf-settings set <key> <value>` | `pf.sh settings set <key> <value>` | Set value by dot-path |
|
|
20
|
+
|
|
21
|
+
## Examples
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Show all interesting settings (theme, workflow, display, split, last_panel)
|
|
25
|
+
pf.sh settings show
|
|
26
|
+
|
|
27
|
+
# Get a specific value
|
|
28
|
+
pf.sh settings get theme # → fifth-element
|
|
29
|
+
pf.sh settings get workflow.relay_mode # → True
|
|
30
|
+
pf.sh settings get display.colorPreset # → catppuccin
|
|
31
|
+
|
|
32
|
+
# Set a value (auto-coerces bool/int)
|
|
33
|
+
pf.sh settings set workflow.bell_mode false
|
|
34
|
+
pf.sh settings set workflow.relay_mode true
|
|
35
|
+
pf.sh settings set display.colorPreset monokai
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- Dot-path notation traverses nested keys: `workflow.relay_mode` → `workflow: { relay_mode: ... }`
|
|
41
|
+
- Value coercion: `true`/`false` → bool, numeric strings → int, else string
|
|
42
|
+
- `show` skips large blobs (layout, bikerack_layout, panels, theme_characters) for readability
|
|
@@ -216,6 +216,21 @@ skills:
|
|
|
216
216
|
related_skills: []
|
|
217
217
|
keywords: [grants, scopes, tools, runtime, security]
|
|
218
218
|
|
|
219
|
+
pf-settings:
|
|
220
|
+
name: pf-settings
|
|
221
|
+
description: View and manage .pennyfarthing/config.local.yaml settings
|
|
222
|
+
category: tools
|
|
223
|
+
tags: [configuration, settings, config]
|
|
224
|
+
version: "1.0.0"
|
|
225
|
+
command_group: settings
|
|
226
|
+
examples:
|
|
227
|
+
- context: View all settings
|
|
228
|
+
invocation: /pf-settings show
|
|
229
|
+
- context: Get a specific setting
|
|
230
|
+
invocation: /pf-settings get workflow.relay_mode
|
|
231
|
+
related_skills: [pf-theme, pf-bc]
|
|
232
|
+
keywords: [theme, relay, bell, permission, display, config]
|
|
233
|
+
|
|
219
234
|
pf-persona-benchmark:
|
|
220
235
|
name: pf-persona-benchmark
|
|
221
236
|
description: Run benchmarks to compare persona effectiveness
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Auto-generated by pennyfarthing init — provides Python dependencies for hooks.
|
|
2
|
+
# This file enables `uv run --project` to resolve pennyfarthing_scripts.
|
|
3
|
+
# Safe to customize: init/update will not overwrite this file once created.
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pennyfarthing-scripts"
|
|
7
|
+
version = "0.0.0"
|
|
8
|
+
description = "Python utilities for Pennyfarthing agent orchestration"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
|
|
11
|
+
dependencies = [
|
|
12
|
+
"pyyaml>=6.0",
|
|
13
|
+
"ruamel.yaml>=0.18",
|
|
14
|
+
"httpx>=0.28",
|
|
15
|
+
"click>=8.0",
|
|
16
|
+
"pydriller>=2.6",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
pf = "pennyfarthing_scripts.cli:main"
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["setuptools>=68.0"]
|
|
24
|
+
build-backend = "setuptools.build_meta"
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.packages.find]
|
|
27
|
+
include = ["pennyfarthing_scripts*"]
|
|
@@ -24,6 +24,10 @@ workflow:
|
|
|
24
24
|
- name: setup
|
|
25
25
|
agent: sm
|
|
26
26
|
output: [session_file, branches, story_context]
|
|
27
|
+
gate:
|
|
28
|
+
file: gates/sm-setup-exit
|
|
29
|
+
type: sm_setup_exit
|
|
30
|
+
condition: Session file created with workflow, phase, context, and branch
|
|
27
31
|
|
|
28
32
|
- name: design
|
|
29
33
|
agent: ux-designer
|
|
@@ -54,9 +58,9 @@ workflow:
|
|
|
54
58
|
input: [failing_tests, design_spec, story_context]
|
|
55
59
|
output: [implementation, passing_tests]
|
|
56
60
|
gate:
|
|
57
|
-
file: gates/
|
|
58
|
-
type:
|
|
59
|
-
condition:
|
|
61
|
+
file: gates/dev-exit
|
|
62
|
+
type: dev_exit
|
|
63
|
+
condition: Tests green, tree clean, no debug code, correct branch
|
|
60
64
|
tandem:
|
|
61
65
|
partner: ux-designer
|
|
62
66
|
scope: file-watch
|
|
@@ -13,6 +13,10 @@ workflow:
|
|
|
13
13
|
- name: setup
|
|
14
14
|
agent: sm
|
|
15
15
|
output: [session_file, branches, story_context]
|
|
16
|
+
gate:
|
|
17
|
+
file: gates/sm-setup-exit
|
|
18
|
+
type: sm_setup_exit
|
|
19
|
+
condition: Session file created with workflow, phase, context, and branch
|
|
16
20
|
|
|
17
21
|
- name: design
|
|
18
22
|
agent: ux-designer
|
|
@@ -37,9 +41,9 @@ workflow:
|
|
|
37
41
|
input: [failing_tests, design_spec, story_context]
|
|
38
42
|
output: [implementation, passing_tests]
|
|
39
43
|
gate:
|
|
40
|
-
file: gates/
|
|
41
|
-
type:
|
|
42
|
-
condition:
|
|
44
|
+
file: gates/dev-exit
|
|
45
|
+
type: dev_exit
|
|
46
|
+
condition: Tests green, tree clean, no debug code, correct branch
|
|
43
47
|
|
|
44
48
|
- name: review
|
|
45
49
|
agent: reviewer
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Step 1: Foundation Check
|
|
2
|
+
|
|
3
|
+
<step-meta>
|
|
4
|
+
step: 1
|
|
5
|
+
name: foundation
|
|
6
|
+
workflow: installation-check
|
|
7
|
+
agent: devops
|
|
8
|
+
gate: false
|
|
9
|
+
next: step-02-commands
|
|
10
|
+
</step-meta>
|
|
11
|
+
|
|
12
|
+
<purpose>
|
|
13
|
+
Verify the core Pennyfarthing installation exists and is intact. This checks the manifest, core framework directories, symlinks, and file integrity — the foundation everything else depends on.
|
|
14
|
+
</purpose>
|
|
15
|
+
|
|
16
|
+
<prerequisites>
|
|
17
|
+
- Project has been initialized with `pennyfarthing init`
|
|
18
|
+
- Running from the project root directory
|
|
19
|
+
</prerequisites>
|
|
20
|
+
|
|
21
|
+
<instructions>
|
|
22
|
+
1. Run the doctor command for the installation category
|
|
23
|
+
2. For each result, explain what it checks and why it matters:
|
|
24
|
+
- **manifest/exists**: The manifest tracks installed version and file hashes. Without it, Pennyfarthing can't detect drift or apply updates.
|
|
25
|
+
- **core/* directories**: These contain agents, commands, guides, skills, personas, and scripts. Missing directories mean broken agent invocation.
|
|
26
|
+
- **symlink/* checks** (symlink mode only): Symlinks to node_modules keep files in sync with the installed package version.
|
|
27
|
+
- **core/integrity**: Detects locally modified framework files that may conflict with updates.
|
|
28
|
+
- **core/completeness**: Flags missing files that should have been installed.
|
|
29
|
+
3. For any failures, explain the impact and offer remediation
|
|
30
|
+
4. Present the collaboration menu
|
|
31
|
+
</instructions>
|
|
32
|
+
|
|
33
|
+
<actions>
|
|
34
|
+
- Run: `pennyfarthing doctor --json --category installation`
|
|
35
|
+
- Check: manifest.json exists at `.pennyfarthing/manifest.json`
|
|
36
|
+
- Check: Core directories exist under `.claude/pennyfarthing/` or via symlinks
|
|
37
|
+
</actions>
|
|
38
|
+
|
|
39
|
+
<output>
|
|
40
|
+
Present results in a clear table format:
|
|
41
|
+
|
|
42
|
+
```markdown
|
|
43
|
+
## Foundation Check Results
|
|
44
|
+
|
|
45
|
+
| Check | Status | Detail |
|
|
46
|
+
|-------|--------|--------|
|
|
47
|
+
| manifest | ... | ... |
|
|
48
|
+
| core files | ... | ... |
|
|
49
|
+
| symlinks | ... | ... |
|
|
50
|
+
|
|
51
|
+
### Issues Found
|
|
52
|
+
[Explain each failure/warning with remediation steps]
|
|
53
|
+
```
|
|
54
|
+
</output>
|
|
55
|
+
|
|
56
|
+
<collaboration-menu>
|
|
57
|
+
- **[F] Fix** - Run `pennyfarthing doctor --fix --category installation` to auto-repair
|
|
58
|
+
- **[E] Explain** - Deep dive on a specific check result
|
|
59
|
+
- **[C] Continue** - Proceed to Commands & Skills check
|
|
60
|
+
- **[R] Recheck** - Re-run after manual changes
|
|
61
|
+
</collaboration-menu>
|
|
62
|
+
|
|
63
|
+
<next-step>
|
|
64
|
+
After reviewing foundation results, proceed to step-02-commands.md for Commands & Skills verification.
|
|
65
|
+
</next-step>
|
|
66
|
+
|
|
67
|
+
## Failure Modes
|
|
68
|
+
|
|
69
|
+
- Running from wrong directory (not project root)
|
|
70
|
+
- Manifest missing entirely (needs `pennyfarthing init`)
|
|
71
|
+
- Broken symlinks after `node_modules` cleanup without reinstall
|
|
72
|
+
|
|
73
|
+
## Success Metrics
|
|
74
|
+
|
|
75
|
+
- All manifest and core file checks pass
|
|
76
|
+
- User understands each check's purpose
|
|
77
|
+
- Any failures have clear remediation path
|