program-context-protocol 0.12.4__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.
- pcp/__init__.py +3 -0
- pcp/assertions.py +152 -0
- pcp/attest.py +111 -0
- pcp/build_loop_bypass.py +76 -0
- pcp/build_report.py +54 -0
- pcp/capture.py +339 -0
- pcp/cli.py +104 -0
- pcp/commands/__init__.py +0 -0
- pcp/commands/amend.py +283 -0
- pcp/commands/architect_review.py +291 -0
- pcp/commands/architecture_justification.py +164 -0
- pcp/commands/audit.py +371 -0
- pcp/commands/build.py +4523 -0
- pcp/commands/build_plan.py +153 -0
- pcp/commands/build_status.py +83 -0
- pcp/commands/capture.py +72 -0
- pcp/commands/check.py +584 -0
- pcp/commands/context.py +151 -0
- pcp/commands/control_audit_cmd.py +54 -0
- pcp/commands/correct_objective.py +160 -0
- pcp/commands/dashboard.py +732 -0
- pcp/commands/deploy.py +199 -0
- pcp/commands/deploy_check.py +134 -0
- pcp/commands/design_audit.py +323 -0
- pcp/commands/diff.py +153 -0
- pcp/commands/diff_reduce.py +355 -0
- pcp/commands/docs.py +538 -0
- pcp/commands/doctor.py +820 -0
- pcp/commands/escalations_cmd.py +64 -0
- pcp/commands/gate.py +209 -0
- pcp/commands/import_project.py +404 -0
- pcp/commands/init.py +1634 -0
- pcp/commands/install_hook.py +283 -0
- pcp/commands/install_skill.py +48 -0
- pcp/commands/kickoff.py +772 -0
- pcp/commands/narrative_lint.py +54 -0
- pcp/commands/objective_conflicts_cmd.py +68 -0
- pcp/commands/pm.py +504 -0
- pcp/commands/pressure_test_cmd.py +72 -0
- pcp/commands/provenance.py +313 -0
- pcp/commands/prune.py +179 -0
- pcp/commands/report.py +49 -0
- pcp/commands/run_log_cmd.py +122 -0
- pcp/commands/scan.py +346 -0
- pcp/commands/self_update.py +125 -0
- pcp/commands/status.py +180 -0
- pcp/commands/takeover.py +55 -0
- pcp/commands/telemetry_cmd.py +167 -0
- pcp/commands/validate_module.py +153 -0
- pcp/commands/validate_strategy.py +413 -0
- pcp/commands/verify.py +166 -0
- pcp/commands/verify_syntax_fix.py +74 -0
- pcp/commands/watch.py +372 -0
- pcp/config_audit.py +141 -0
- pcp/context_map.py +124 -0
- pcp/control_audit.py +159 -0
- pcp/coupling.py +178 -0
- pcp/coverage_audit.py +77 -0
- pcp/decision_log.py +134 -0
- pcp/discovery/__init__.py +0 -0
- pcp/discovery/clusters.py +124 -0
- pcp/discovery/graph.py +110 -0
- pcp/discovery/scanner.py +109 -0
- pcp/escalations.py +193 -0
- pcp/evidence.py +30 -0
- pcp/evidence_chain.py +56 -0
- pcp/impact.py +164 -0
- pcp/install_approvals.py +44 -0
- pcp/integrity_audit.py +176 -0
- pcp/librarian.py +89 -0
- pcp/llm/__init__.py +0 -0
- pcp/llm/client.py +183 -0
- pcp/llm/coding_agent_contract.py +104 -0
- pcp/llm/harness/__init__.py +12 -0
- pcp/llm/harness/agy.py +121 -0
- pcp/llm/harness/agy_coding_loop.py +180 -0
- pcp/llm/harness/claude.py +241 -0
- pcp/llm/ledger.py +47 -0
- pcp/narrative_lint.py +229 -0
- pcp/nav_graph.py +226 -0
- pcp/objective_conflicts.py +129 -0
- pcp/operational.py +70 -0
- pcp/orphaned_work.py +262 -0
- pcp/pcp_dir.py +35 -0
- pcp/pcp_status.py +313 -0
- pcp/policy.py +81 -0
- pcp/pressure_test.py +196 -0
- pcp/qa.py +445 -0
- pcp/run_log.py +225 -0
- pcp/schema/__init__.py +0 -0
- pcp/schema/ci_rules.schema.json +106 -0
- pcp/schema/controls.schema.json +39 -0
- pcp/schema/module_acceptance.schema.json +144 -0
- pcp/schema/module_spec.schema.json +78 -0
- pcp/schema/sdlc_phase.schema.json +52 -0
- pcp/schema/validator.py +77 -0
- pcp/skill_data/pcp/SKILL.md +1897 -0
- pcp/spec_write.py +269 -0
- pcp/spend.py +77 -0
- pcp/symbols.py +86 -0
- pcp/telemetry.py +308 -0
- pcp/uat.py +271 -0
- pcp/version_drift.py +222 -0
- program_context_protocol-0.12.4.dist-info/METADATA +123 -0
- program_context_protocol-0.12.4.dist-info/RECORD +109 -0
- program_context_protocol-0.12.4.dist-info/WHEEL +4 -0
- program_context_protocol-0.12.4.dist-info/entry_points.txt +2 -0
- program_context_protocol-0.12.4.dist-info/licenses/LICENSE-APACHE +202 -0
- program_context_protocol-0.12.4.dist-info/licenses/LICENSE-MIT +21 -0
|
@@ -0,0 +1,1897 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pcp
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: "Program Context Protocol — full autonomous software factory. PM describes vision; PCP runs structured discovery, generates BRD, translates to modular specs, builds via the Workflow tool's native parallelism (pcp build-plan computes the schedule, the harness governs concurrency), runs all gates (TDD, architect-review, CI), auto-fixes failures, merges, deploys. PM only touches: vision input, visual approvals, escalations. Invoke with /pcp."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /pcp
|
|
8
|
+
|
|
9
|
+
Autonomous software factory. PM describes what to build. PCP builds it.
|
|
10
|
+
|
|
11
|
+
**Three modes:**
|
|
12
|
+
1. `/pcp new` — Vision workshop → BRD → spec scaffold
|
|
13
|
+
2. `/pcp build` — Parallel autonomous build (concurrency governed by the Workflow tool, not a manual count)
|
|
14
|
+
3. `/pcp status` — Project health across all projects
|
|
15
|
+
|
|
16
|
+
**The contract:** PM inputs vision and approves milestones. PCP handles everything else. "Build" means deployed and verified — not written.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
/pcp new # start vision workshop → BRD → .pcp/ scaffold (greenfield)
|
|
24
|
+
/pcp new --from-brd <file> # skip workshop, generate .pcp/ from existing BRD
|
|
25
|
+
/pcp import "<description>" # brownfield: graphify → clusters → draft specs → PM review
|
|
26
|
+
/pcp build # build next wave via pcp build-plan + Workflow tool
|
|
27
|
+
/pcp build --module <name> # build one module
|
|
28
|
+
/pcp build --all # build entire backlog, full wave scheduling
|
|
29
|
+
# (no --agents flag: Workflow's own concurrency cap
|
|
30
|
+
# governs how many run at once, not a manual number)
|
|
31
|
+
/pcp status # current project: phase, %, CI, deferred queue
|
|
32
|
+
/pcp status --all # all projects under ~/Claude-code/
|
|
33
|
+
/pcp watch # watch Railway + GitHub Actions
|
|
34
|
+
/pcp fix # diagnose + fix current CI failure
|
|
35
|
+
/pcp approve <module>/<id> # unblock a deferred criterion (manual/visual)
|
|
36
|
+
/pcp feedback <module>/<id> "<text>" # give feedback on a visual criterion, rebuild
|
|
37
|
+
/pcp accept-adr <id> # accept a DRAFT ADR written autonomously
|
|
38
|
+
/pcp override-adr <id> "<decision>" # override PCP's autonomous architecture decision
|
|
39
|
+
/pcp review <module> # architect-review on module spec
|
|
40
|
+
/pcp review <type> # architecture | logic | design | security | qa — see REVIEW ROUTER below
|
|
41
|
+
/pcp diff # target vs current gap
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Language rule — non-negotiable:** Everything the PM reads is in product language. Technical terms (module, spec, coupling, AST, CI, YAML, schema, criterion, pre-commit, branch, commit hash, coverage %) never appear in PM-facing output. Translate before presenting. Technical language is internal — it lives in files and logs, not in conversation.
|
|
45
|
+
|
|
46
|
+
| Technical | Product language |
|
|
47
|
+
|---|---|
|
|
48
|
+
| "3 CI failures" | "3 things broke — fixing now" |
|
|
49
|
+
| "coupling violation: payments ↔ webhooks" | "payments and notifications are too tangled — I'm separating them" |
|
|
50
|
+
| "acceptance criterion BF_001 pending" | "still working on: [feature name]" |
|
|
51
|
+
| "coverage 72%" | "28% of what you described isn't built yet" |
|
|
52
|
+
| "merge conflict on feat/auth" | "two parts of the build clashed — resolving" |
|
|
53
|
+
| "pcp validate-strategy failed" | "the build plan doesn't fully cover what you described — adjusting" |
|
|
54
|
+
|
|
55
|
+
**Unattended operation:** PCP runs fully autonomously while you are away. Escalations go to Slack — not to a blocking prompt. `/pcp watch` monitors CI and Railway for failures and auto-fixes them. When you return, run `/pcp` to see what was built, what is deferred, and what needs your input. Build never stops for input that can be deferred.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## REVIEW ROUTER — the PM should never need to remember a command name
|
|
60
|
+
|
|
61
|
+
Real gap found dogfooding: PCP has grown ~32 CLI commands across the whole lifecycle
|
|
62
|
+
(architecture, logic-tier decisions, design/UI, security, QA, deploy). A PM asking to
|
|
63
|
+
"review the UI strategy" or "check the security posture" shouldn't need to know the
|
|
64
|
+
exact command (`design-audit`, `architecture-justification`, `check`...) exists, let
|
|
65
|
+
alone spell it correctly. This section is PCP's own lookup table — read it, don't guess
|
|
66
|
+
a command name, and don't tell the PM the technical name unless they ask (same
|
|
67
|
+
Language Rule as above applies here too).
|
|
68
|
+
|
|
69
|
+
### Intent → command lookup
|
|
70
|
+
|
|
71
|
+
| PM says something like... | Run this |
|
|
72
|
+
|---|---|
|
|
73
|
+
| "review the architecture" / "does this still make sense structurally" / "check coupling" | `pcp architect-review` (advisory, per-diff), `pcp validate-strategy` (coverage_score + coupling_score, Pass 2), `pcp validate-module <name>` (Pass 1) |
|
|
74
|
+
| "review the logic" / "are we using the right tool for this" / "check build-vs-buy" / "did we reinvent something that already exists" | `pcp architecture-justification` — rolls up every criterion's `logic_tier` (the 6-rung deterministic→deep-think ladder) and `build_vs_buy` decision |
|
|
75
|
+
| "review the design" / "review the UI" / "is this discoverable" / "does this look consistent across screens" | `pcp design-audit` (Feature Exposure Ladder — how discoverable each UI-facing criterion actually is) — for the design-thinking pass on one specific screen, use the `pcp-ui-design` skill (`~/.claude/skills/pcp-ui-design/SKILL.md`), not this rollup |
|
|
76
|
+
| "review security" / "any secrets" / "check for vulnerabilities" | `pcp check` (Layer 1 — deterministic, hard block: hardcoded secrets, eval/exec, disabled TLS, string-built SQL, plus any project-specific `hard_block` rules in `ci_rules.yaml`) |
|
|
77
|
+
| "check before merging" / "PR review" / "does this align with what we asked for" | `pcp gate` (Layer 2 — LLM alignment score, advisory, never hard-blocks) |
|
|
78
|
+
| "are we ready to deploy" / "check the deploy gate" | `pcp deploy-check` (Layer 3 — SDLC phase exit criteria, hard block) |
|
|
79
|
+
| "review QA" / "how's the build going" / "any flaky retries" / "what's this costing" | `pcp telemetry` (per-module retries, QA error rate, cost, languages), `pcp provenance` (full audit-evidence document — SSDF crosswalk, bypass ledger, chain integrity) |
|
|
80
|
+
| "any dead code" / "bloat check" | `pcp audit` (advisory, never blocks) |
|
|
81
|
+
| "what's the overall status" / "show me everything" | `pcp dashboard` — single HTML, 5 tabs (Overview / Objective & Gaps / Audit Trail / Architecture Justification / Design), already visualizes most of the rows above. Open this first before running things piecemeal. |
|
|
82
|
+
| "what's blocked or bypassed" | `pcp report` (bypass history, coverage score, gate outcomes) |
|
|
83
|
+
| "give me a plain-English status" | `pcp status --pm` |
|
|
84
|
+
| "what changed in this module" / "module history" / "drift ledger for X" | `pcp docs [--module <name>]` — per-module vision/BRD/built/changelog, `changelog.md` is a drift ledger with a computed drift score (in-flight spec changes, bypasses, retries) |
|
|
85
|
+
| "clean up old logs" / "disk is full" / "prune old evidence" | `pcp prune [--evidence-days N] [--transcript-days N]` — deletes stale `.pcp/evidence/`/`.pcp/transcripts/` past a retention window; no-op unless configured, `--dry-run` to preview, mandatory confirmation unless `--yes` |
|
|
86
|
+
|
|
87
|
+
### Full command reference, by lifecycle stage (all current as of this skill's own version — re-check `pcp --help` if something here feels stale)
|
|
88
|
+
|
|
89
|
+
**Setup / onboarding**
|
|
90
|
+
- `pcp init` — scaffold `.pcp/`
|
|
91
|
+
- `pcp kickoff <vision.md>` — generate specs from a vision doc
|
|
92
|
+
- `pcp import "<description>"` — brownfield onboarding (graphify clusters → draft specs)
|
|
93
|
+
- `pcp takeover` — end-to-end: preflight + kickoff + build everything pending
|
|
94
|
+
- `pcp doctor` — check/configure environment tool integrations
|
|
95
|
+
- `pcp install-hook` / `pcp install-skill` — one-time wiring
|
|
96
|
+
|
|
97
|
+
**Strategy / architecture**
|
|
98
|
+
- `pcp validate-strategy` — coverage_score + coupling_score (Pass 2: modules vs objective)
|
|
99
|
+
- `pcp validate-module <name>` — module vs objective/decomposition (Pass 1)
|
|
100
|
+
- `pcp architect-review` — architecture principle review against persona + KB (advisory)
|
|
101
|
+
- `pcp architecture-justification [--json]` — `logic_tier`/`build_vs_buy` rollup
|
|
102
|
+
|
|
103
|
+
**Design** (see CLAUDE.md's "PCP Design as a Hard Constraint" for the full 5-stage lifecycle this fits into)
|
|
104
|
+
- `pcp design-audit [--json]` — Feature Exposure Ladder rollup
|
|
105
|
+
- skill `pcp-ui-design` — design-thinking pass on one UI-facing criterion, establishes/reuses `.pcp/design_system.md`
|
|
106
|
+
|
|
107
|
+
**Build**
|
|
108
|
+
- `pcp build [--module <name> | --all]` — autonomous coding loops per pending criterion
|
|
109
|
+
- `pcp pm "<intent>"` — translate natural-language feature intent to spec/acceptance changes
|
|
110
|
+
|
|
111
|
+
**Gates — the 3-tier system**
|
|
112
|
+
- `pcp check` — Layer 1, pre-commit, deterministic, hard block, no LLM
|
|
113
|
+
- `pcp gate` — Layer 2, PR, LLM alignment score, advisory
|
|
114
|
+
- `pcp deploy-check` — Layer 3, deploy, SDLC phase-exit criteria, hard block
|
|
115
|
+
|
|
116
|
+
**QA / audit / evidence**
|
|
117
|
+
- `pcp telemetry` — build-efficiency rollup (retries, QA error rate, cost, languages)
|
|
118
|
+
- `pcp provenance [--json]` — audit-evidence document (SSDF crosswalk, bypass ledger, chain integrity)
|
|
119
|
+
- `pcp audit` — dead-code/bloat scan, advisory, never blocks
|
|
120
|
+
- `pcp report` — bypass history, coverage score, gate outcomes
|
|
121
|
+
- `pcp capture [--transcript-file <path>]` — classify a session transcript into BRD/decision-log drift
|
|
122
|
+
|
|
123
|
+
**Status / drift**
|
|
124
|
+
- `pcp scan [--coverage]` — regenerate `.pcp/current_state.md`
|
|
125
|
+
- `pcp diff` — target vs current state gap
|
|
126
|
+
- `pcp status [--pm] [--rescan] [--print]` — `pcp.md` snapshot, or a plain-English PM report
|
|
127
|
+
- `pcp dashboard` — unified 5-tab HTML view (see above)
|
|
128
|
+
- `pcp context` — dump `.pcp/` context for LLM consumption at session start
|
|
129
|
+
- `pcp docs [--module <name>]` — per-module doc kit (vision/BRD/built/changelog); `changelog.md` is a chronological drift ledger with a computed drift score, not just a build log
|
|
130
|
+
|
|
131
|
+
**Deploy / ops**
|
|
132
|
+
- `pcp deploy [--yes] [--rollout N]` — checklist → Layer 3 gate → approval → trigger → smoke test → auto-rollback
|
|
133
|
+
- `pcp watch [--once] [--interval N]` — continuous CI/deploy monitoring, auto-fix on failure
|
|
134
|
+
- `pcp prune [--evidence-days N] [--transcript-days N] [--dry-run] [--yes]` — retention cleanup for `.pcp/evidence/`/`.pcp/transcripts/` (the two directories that actually grow unbounded); does nothing unless a retention window is configured or passed; mandatory confirmation like `pcp deploy`, `--yes` opts out
|
|
135
|
+
|
|
136
|
+
**Narrow / utility**
|
|
137
|
+
- `pcp verify-syntax-fix <file>` — deterministic SAFE/UNSAFE verdict for a protected-path syntax-only edit (used by the `verify-syntax-fix` PreToolUse hook, not typically invoked directly by a PM)
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## BROWNFIELD IMPORT (`/pcp import "<description>"`)
|
|
142
|
+
|
|
143
|
+
For existing codebases with no `.pcp/` context. PM provides one paragraph describing what the codebase delivers — that's the only required input.
|
|
144
|
+
|
|
145
|
+
### Step 1 — Write objective.md
|
|
146
|
+
|
|
147
|
+
PM description → `.pcp/objective.md`. Done immediately. No workshop needed.
|
|
148
|
+
|
|
149
|
+
### Step 2 — Graphify cluster detection
|
|
150
|
+
|
|
151
|
+
Invoke `/graphify` on the entire codebase. Input: all source files (imports, function calls, class references, shared state). Output: knowledge graph with cluster detection.
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
Clusters = tightly coupled file groups = natural module candidates
|
|
155
|
+
Cross-cluster edges = inter-module dependencies = contract boundaries
|
|
156
|
+
Dense cross-cluster edges = coupling violations (flagged immediately)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Why graphify instead of directory scan: flat repos with 300 files across mixed dirs have no meaningful directory structure. Graphify finds real boundaries from actual code relationships, not assumed from folder names.
|
|
160
|
+
|
|
161
|
+
### Step 3 — Present module map to PM
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
Graphify found 7 clusters:
|
|
165
|
+
|
|
166
|
+
auth-cluster auth.py, session.py, jwt_utils.py, bcrypt_helper.py
|
|
167
|
+
payments-cluster stripe_client.py, charge.py, refund.py, invoice.py
|
|
168
|
+
webhooks-cluster webhook.py, event_queue.py, retry.py
|
|
169
|
+
api-cluster routes.py, middleware.py, serializers.py
|
|
170
|
+
db-cluster models.py, migrations/, connection.py
|
|
171
|
+
tasks-cluster celery.py, workers/, scheduler.py
|
|
172
|
+
shared config.py, utils.py, constants.py ← infrastructure, not a module
|
|
173
|
+
|
|
174
|
+
Suggested module names (from PM description + file names):
|
|
175
|
+
auth-cluster → auth
|
|
176
|
+
payments-cluster → payments
|
|
177
|
+
webhooks-cluster → webhooks
|
|
178
|
+
api-cluster → api-gateway
|
|
179
|
+
db-cluster → data-layer
|
|
180
|
+
tasks-cluster → task-runner
|
|
181
|
+
|
|
182
|
+
Cross-cluster edges (coupling violations — fix in Wave 0):
|
|
183
|
+
payments ↔ webhooks: 14 direct calls
|
|
184
|
+
api-gateway ↔ db-cluster: direct model access (bypasses data-layer)
|
|
185
|
+
|
|
186
|
+
Rename, split, or merge any modules before I generate specs.
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Wait for PM review. Iterate until approved.
|
|
190
|
+
|
|
191
|
+
### Step 4 — Baseline scan
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
pcp scan # current_state.md = existing code as-is
|
|
195
|
+
pcp check --baseline # catalog pre-existing violations → .pcp/baseline_violations.yaml
|
|
196
|
+
# NOT blocking. Counting what's already broken.
|
|
197
|
+
pcp validate-strategy # coverage score + coupling score on day 1
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Report to PM:
|
|
201
|
+
```
|
|
202
|
+
Baseline: <project>
|
|
203
|
+
|
|
204
|
+
Objective coverage: 72% (28% of your description has no code yet)
|
|
205
|
+
Coupling score: 0.3 (high — pivots will be expensive)
|
|
206
|
+
Pre-existing violations: 23 MOD_001, 4 circular deps, 11 SEC_001
|
|
207
|
+
Test coverage: 34%
|
|
208
|
+
|
|
209
|
+
Recommended wave order:
|
|
210
|
+
Wave 0 — characterization tests + decouple (no new features)
|
|
211
|
+
Wave 1+ — new features per objective gaps
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Step 5 — Generate draft `.pcp/`
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
.pcp/objective.md ← from PM description (immutable now)
|
|
218
|
+
.pcp/architecture.md ← detected stack + patterns
|
|
219
|
+
.pcp/strategy/decomposition.md ← approved cluster→module map
|
|
220
|
+
.pcp/strategy/modules/<name>/spec.yaml ← _generated: true, PM must review
|
|
221
|
+
.pcp/strategy/modules/<name>/acceptance.yaml ← mined from test files if exist, else empty
|
|
222
|
+
.pcp/baseline_violations.yaml ← pre-existing violations, shrinks as Wave 0 completes
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
All generated spec files have `_generated: true`. PM reviews each, removes the flag when correct. Once flag removed: spec is immutable — agents never modify it.
|
|
226
|
+
|
|
227
|
+
### Step 6 — Auto-generate Wave 0 criteria
|
|
228
|
+
|
|
229
|
+
From baseline scan, PCP generates brownfield-specific acceptance criteria:
|
|
230
|
+
|
|
231
|
+
```yaml
|
|
232
|
+
# auto-generated in each module's acceptance.yaml
|
|
233
|
+
- id: BF_001
|
|
234
|
+
description: "Write characterization tests for <module> (golden master — no code changes)"
|
|
235
|
+
check: test_passes
|
|
236
|
+
test: "tests/characterization/test_<module>_baseline.py"
|
|
237
|
+
status: pending
|
|
238
|
+
|
|
239
|
+
- id: BF_002 # only if coupling violations exist
|
|
240
|
+
description: "Remove direct <moduleA>→<moduleB> calls, route through interface"
|
|
241
|
+
check: ast_pattern
|
|
242
|
+
pattern: "from ...<moduleB> import" # must not appear in moduleA after fix
|
|
243
|
+
status: pending
|
|
244
|
+
|
|
245
|
+
- id: BF_003 # one per god module detected
|
|
246
|
+
description: "Extract <domain> from <god_module> into standalone boundary"
|
|
247
|
+
check: file_exists
|
|
248
|
+
target: "src/<domain>/__init__.py"
|
|
249
|
+
status: pending
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Wave 0 has no new features. Code behaves identically before and after. Tests prove it.
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### Brownfield TDD — three criterion types
|
|
257
|
+
|
|
258
|
+
| Type | Code exists? | TDD behavior |
|
|
259
|
+
|---|---|---|
|
|
260
|
+
| **Existing behavior** | Yes, working | Write tests → GREEN immediately. If RED → code broken, fix it. |
|
|
261
|
+
| **New feature** | No | Normal TDD: RED → write code → GREEN |
|
|
262
|
+
| **Refactor/decouple** | Yes, but wrong shape | Characterization tests first → refactor → tests still GREEN |
|
|
263
|
+
|
|
264
|
+
Criterion agent detects which type before writing any code:
|
|
265
|
+
```bash
|
|
266
|
+
# Check if criterion target already exists
|
|
267
|
+
ls <target_file> 2>/dev/null && echo "EXISTS" || echo "NEW"
|
|
268
|
+
# Check if tests already pass
|
|
269
|
+
pytest tests/test_<criterion>.py -q 2>/dev/null && echo "GREEN" || echo "RED"
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
### Brownfield `pcp check` — staged-only mode
|
|
275
|
+
|
|
276
|
+
Existing code has violations everywhere. Full-codebase check would block immediately.
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
# Greenfield pcp check: entire codebase
|
|
280
|
+
# Brownfield pcp check: staged changes only (new code must be clean, old code tracked separately)
|
|
281
|
+
git diff --staged | pcp check --staged-only
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Violations in `baseline_violations.yaml` are excluded from block gate. As Wave 0 decoupling criteria complete, violations are removed from baseline. When `baseline_violations.yaml` reaches zero: project automatically switches to full greenfield `pcp check` rules.
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### Full brownfield lifecycle
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
/pcp import "<description>"
|
|
292
|
+
→ objective.md written
|
|
293
|
+
→ /graphify → clusters → module map → PM approves
|
|
294
|
+
→ baseline scan → current_state.md + baseline_violations.yaml
|
|
295
|
+
→ draft .pcp/ generated, PM reviews specs
|
|
296
|
+
↓
|
|
297
|
+
Wave 0 (brownfield only — no new features)
|
|
298
|
+
→ characterization tests per module
|
|
299
|
+
→ decouple cross-cluster violations
|
|
300
|
+
→ extract god modules
|
|
301
|
+
→ baseline_violations.yaml shrinks toward zero
|
|
302
|
+
↓
|
|
303
|
+
Wave 1+ (same as greenfield)
|
|
304
|
+
→ TDD for new features / objective gaps
|
|
305
|
+
→ parallel agents, CI gates, merge coordinator
|
|
306
|
+
↓
|
|
307
|
+
baseline_violations.yaml = empty
|
|
308
|
+
→ full greenfield rules apply, brownfield mode retired
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## PHASE 1: VISION WORKSHOP (`/pcp new`)
|
|
314
|
+
|
|
315
|
+
Triggered by `/pcp new` in any directory (with or without existing `.pcp/`).
|
|
316
|
+
|
|
317
|
+
### Step 1 — Product Discovery Interview
|
|
318
|
+
|
|
319
|
+
Target user: technically aware, product-thinking, non-coding. PCP is their engineering team. They are the PM/founder. All questions are product questions — no technical jargon. PCP makes all technology decisions autonomously after the interview.
|
|
320
|
+
|
|
321
|
+
Ask ONE AT A TIME. Wait for answers. Do not batch. Follow up on vague answers before moving on.
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
1. What problem are you solving? Tell me about a real moment when you
|
|
325
|
+
or someone you know ran into this problem.
|
|
326
|
+
|
|
327
|
+
2. Who is the person with this problem? What do they do today to
|
|
328
|
+
work around it — before your product exists?
|
|
329
|
+
|
|
330
|
+
3. Walk me through your product step by step from the user's point
|
|
331
|
+
of view. What happens when they first open it? What do they do next?
|
|
332
|
+
|
|
333
|
+
4. What are the 3-5 things this product MUST do for it to be useful?
|
|
334
|
+
(outcomes, not features — what does the user achieve?)
|
|
335
|
+
|
|
336
|
+
5. Who creates content or data in your product? Who views or uses it?
|
|
337
|
+
(e.g. "sellers list products, buyers browse and purchase")
|
|
338
|
+
|
|
339
|
+
6. Do users need accounts? How do they sign up or log in?
|
|
340
|
+
(e.g. "sign in with Google", "email + password", "invite-only")
|
|
341
|
+
|
|
342
|
+
7. Is money involved? How does it work?
|
|
343
|
+
(subscriptions, one-time payment, marketplace take-rate, free)
|
|
344
|
+
|
|
345
|
+
8. Does your product connect to anything the user already uses?
|
|
346
|
+
(e.g. "pulls from their Google Calendar", "sends Slack messages")
|
|
347
|
+
|
|
348
|
+
9. What is explicitly NOT in version 1?
|
|
349
|
+
(what are you leaving out on purpose)
|
|
350
|
+
|
|
351
|
+
10. Show me "done": describe the moment you demo this to someone and
|
|
352
|
+
they say "yes, this is exactly what I need."
|
|
353
|
+
|
|
354
|
+
11. Is there a deadline or launch event driving timing?
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Follow-up questions based on answers — no fixed limit. Keep going until the picture is complete. If the user mentions something ambiguous ("some kind of dashboard"), drill in: "What exactly does that show? Who looks at it? How often?"
|
|
358
|
+
|
|
359
|
+
After all answers, synthesise in product language only:
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
Here's what I understood:
|
|
363
|
+
|
|
364
|
+
Problem: <one line — the pain>
|
|
365
|
+
User: <one line — who they are and what they do today>
|
|
366
|
+
Core outcomes:
|
|
367
|
+
- <what user achieves, not feature name>
|
|
368
|
+
- ...
|
|
369
|
+
User flows:
|
|
370
|
+
- <flow 1: actor does X → sees Y → achieves Z>
|
|
371
|
+
- <flow 2: ...>
|
|
372
|
+
Accounts: <yes/no, how>
|
|
373
|
+
Money: <yes/no, model>
|
|
374
|
+
Integrations: <external systems named>
|
|
375
|
+
Out of scope: <list>
|
|
376
|
+
Done looks like: <demo scenario>
|
|
377
|
+
Timeline: <constraint or "ship when ready">
|
|
378
|
+
|
|
379
|
+
Is this right? Tell me anything I missed or got wrong.
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Iterate until PM says "yes" / "correct" / "go". Do not proceed until confirmed.
|
|
383
|
+
|
|
384
|
+
After confirmed: PCP decides all technology internally. Do NOT ask about stack, framework, database, deployment target, or hosting. Infer from what's being built:
|
|
385
|
+
- Web app with auth + data → modern web stack, Railway deploy
|
|
386
|
+
- API-only product → lightweight backend, Railway deploy
|
|
387
|
+
- Mobile-first → cross-platform mobile
|
|
388
|
+
- Desktop tool → native desktop
|
|
389
|
+
Present the choice to PM in one plain sentence after BRD is written: "I'll build this as a web app you can use in any browser, hosted so anyone with the link can access it." No technical details unless PM asks.
|
|
390
|
+
|
|
391
|
+
### Step 2 — Generate BRD
|
|
392
|
+
|
|
393
|
+
Using confirmed answers, generate a structured BRD as `brd.md` in the project root.
|
|
394
|
+
|
|
395
|
+
BRD sections — product language throughout, no technical terms visible to PM:
|
|
396
|
+
1. Problem Statement
|
|
397
|
+
2. Target User (persona + current workaround)
|
|
398
|
+
3. Core User Flows (step-by-step, from user's point of view)
|
|
399
|
+
4. Features (what the product does — plain English, no tech)
|
|
400
|
+
5. Out of Scope
|
|
401
|
+
6. How Users Access It (web / mobile / desktop — one sentence)
|
|
402
|
+
7. Money Model (if any)
|
|
403
|
+
8. Integrations (external services)
|
|
404
|
+
9. Success: what "done" looks like in observable, testable terms
|
|
405
|
+
10. Open Questions
|
|
406
|
+
|
|
407
|
+
PCP appends internally (not shown to PM):
|
|
408
|
+
- Technology stack decision (PCP's choice, not PM's)
|
|
409
|
+
- Architecture principles (derived from product requirements)
|
|
410
|
+
- Module decomposition (internal planning)
|
|
411
|
+
|
|
412
|
+
After writing `brd.md`, present module breakdown in product language:
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
BRD written.
|
|
416
|
+
|
|
417
|
+
Here's how I'll build this — each part does one job:
|
|
418
|
+
|
|
419
|
+
User accounts → sign-up, login, profile
|
|
420
|
+
[feature name] → <what it does for the user, one line>
|
|
421
|
+
[feature name] → <what it does for the user, one line>
|
|
422
|
+
Payments → <if applicable>
|
|
423
|
+
...
|
|
424
|
+
|
|
425
|
+
Does this cover everything? Tell me if anything's missing or wrong
|
|
426
|
+
before I start building.
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Never use: module, spec, acceptance criteria, coupling, AST, CI, YAML, schema — in anything the PM reads.
|
|
430
|
+
|
|
431
|
+
Wait for PM approval. Iterate until PM says "yes" / "looks right" / "go".
|
|
432
|
+
|
|
433
|
+
### Step 3 — Generate `.pcp/` Scaffold
|
|
434
|
+
|
|
435
|
+
Once BRD and module list are approved:
|
|
436
|
+
|
|
437
|
+
**3a. Write core files.**
|
|
438
|
+
```
|
|
439
|
+
.pcp/objective.md — distilled from BRD problem + use cases
|
|
440
|
+
.pcp/target_state.md — distilled from BRD "done looks like"
|
|
441
|
+
.pcp/architecture.md — from BRD technical constraints + architecture principles
|
|
442
|
+
.pcp/ci_rules.yaml — derive rules from architecture principles
|
|
443
|
+
.pcp/SDLC_phase.yaml — phases: planning → alpha → beta → v1
|
|
444
|
+
.pcp/architect_persona.md — derive BLOCK/WARN/NOTE rules from architecture principles
|
|
445
|
+
.pcp/strategy/decomposition.md
|
|
446
|
+
.pcp/strategy/dependency_map.md
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
**3b. For each module: generate spec + acceptance.**
|
|
450
|
+
```
|
|
451
|
+
.pcp/strategy/modules/<module>/spec.yaml
|
|
452
|
+
.pcp/strategy/modules/<module>/acceptance.yaml
|
|
453
|
+
.pcp/strategy/modules/<module>/plan.md
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Every module spec must include these non-negotiable criteria regardless of module type:
|
|
457
|
+
- One criterion for structured logging (`check: ast_pattern` targeting `logger.` or `logging.` or `log.`)
|
|
458
|
+
- One criterion for error handling (`check: test_passes` on a failure-path test)
|
|
459
|
+
- For modules with external interfaces: one criterion for health/readiness endpoint or contract schema
|
|
460
|
+
|
|
461
|
+
Acceptance criteria must be MEASURABLE. For each criterion, choose the right check type:
|
|
462
|
+
- `check: file_exists` — verifiable by path
|
|
463
|
+
- `check: ast_pattern` — verifiable by grep/regex
|
|
464
|
+
- `check: dom_contains` — verifiable by Playwright selector
|
|
465
|
+
- `check: url_responds` — verifiable by HTTP status
|
|
466
|
+
- `check: railway_deploy` — verifiable by Railway API
|
|
467
|
+
- `check: github_actions` — verifiable by gh CLI
|
|
468
|
+
- `check: visual` — requires PM visual approval
|
|
469
|
+
- `check: manual` — requires PM explicit confirmation
|
|
470
|
+
- `check: test_passes` — verifiable by running named test
|
|
471
|
+
|
|
472
|
+
Avoid `check: manual` where a programmatic check is possible.
|
|
473
|
+
|
|
474
|
+
**3c. Run `pcp validate-strategy`.**
|
|
475
|
+
```bash
|
|
476
|
+
pcp validate-strategy
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
If coverage < 80%: identify gap, add module or expand spec, re-run. Iterate until ≥ 80%.
|
|
480
|
+
|
|
481
|
+
**3d. Confirm with PM — product language only.**
|
|
482
|
+
|
|
483
|
+
```
|
|
484
|
+
Ready to build.
|
|
485
|
+
|
|
486
|
+
Here's what I'm building and in what order:
|
|
487
|
+
|
|
488
|
+
Round 1 (starting now, all in parallel):
|
|
489
|
+
User accounts — sign-up, login, profile management
|
|
490
|
+
[feature] — <plain English>
|
|
491
|
+
[feature] — <plain English>
|
|
492
|
+
|
|
493
|
+
Round 2 (starts when Round 1 is complete):
|
|
494
|
+
[feature depending on Round 1]
|
|
495
|
+
...
|
|
496
|
+
|
|
497
|
+
Estimated time to first working version: <N> hours.
|
|
498
|
+
I'll update you when each part is ready to test.
|
|
499
|
+
|
|
500
|
+
Say /pcp build --all to start, or tell me to change anything first.
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Never surface: module names, criterion IDs, coverage %, coupling score, YAML structure, CI status codes, branch names, commit hashes — in anything the PM reads. Translate all build status to product language before presenting.
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## PHASE 2: PARALLEL BUILD
|
|
508
|
+
|
|
509
|
+
### Dependency Analysis
|
|
510
|
+
|
|
511
|
+
Before spawning agents, read `.pcp/strategy/dependency_map.md`.
|
|
512
|
+
|
|
513
|
+
Build the dependency graph:
|
|
514
|
+
- Modules with no dependencies → Wave 1
|
|
515
|
+
- Modules whose deps are all in Wave 1 → Wave 2
|
|
516
|
+
- And so on
|
|
517
|
+
|
|
518
|
+
Example output:
|
|
519
|
+
```
|
|
520
|
+
Wave 1 (no deps, build in parallel):
|
|
521
|
+
auth, logging, config-service
|
|
522
|
+
|
|
523
|
+
Wave 2 (deps: Wave 1 complete):
|
|
524
|
+
payments, notifications, api-gateway, admin-dashboard
|
|
525
|
+
|
|
526
|
+
Wave 3 (deps: Wave 2 complete):
|
|
527
|
+
reporting, billing-reconciliation
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Stage 1 — Pre-Build Spec Review (per module, before any criterion agent spawns)
|
|
531
|
+
|
|
532
|
+
Run `pcp architect-review` against the module spec before touching code. Catches design-level violations when they're still cheap to fix — not after 8 criterion agents have built the wrong thing.
|
|
533
|
+
|
|
534
|
+
```bash
|
|
535
|
+
for module in <wave_modules>; do
|
|
536
|
+
pcp architect-review --spec .pcp/strategy/modules/$module/spec.yaml 2>&1
|
|
537
|
+
done
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
**Results:**
|
|
541
|
+
- `BLOCK` → **do not spawn any agents for this module**. Spec must be fixed first. Write to deferred queue, notify PM: "spec for `<module>` violates architect persona: `<finding>`. Fix spec before build can start." Continue build on other modules.
|
|
542
|
+
- `WARN` → spawn agents, but include the warning in every criterion brief: "Persona WARN on this module: `<warning>`. Account for it in your implementation."
|
|
543
|
+
- Clean → proceed normally.
|
|
544
|
+
|
|
545
|
+
Spec files are human-**authorized** — a build agent never fixes a spec violation on its own. Route it to the PM, who applies it through the gated write path for that file (`pcp correct-objective` / `pcp pm` / `pcp amend`): the change is proposed, the PM sees a real diff, approves, then it's written. "Authorized" means unattended writes are forbidden, not that the PM hand-types the diff.
|
|
546
|
+
|
|
547
|
+
### Branch Isolation Protocol
|
|
548
|
+
|
|
549
|
+
Each parallel agent works on its own feature branch + git worktree.
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
# For each module being built in parallel:
|
|
553
|
+
git worktree add "../<project>-<module>" -b "feat/<module>"
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
This gives each agent:
|
|
557
|
+
- Isolated working directory
|
|
558
|
+
- Own branch (no conflicts with other agents)
|
|
559
|
+
- Full copy of the repo at that point
|
|
560
|
+
|
|
561
|
+
Worktree cleanup after merge:
|
|
562
|
+
```bash
|
|
563
|
+
git worktree remove "../<project>-<module>"
|
|
564
|
+
git branch -d feat/<module>
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### Agent Sizing Principle — Non-Negotiable
|
|
568
|
+
|
|
569
|
+
**One agent per criterion. Not one agent per module.**
|
|
570
|
+
|
|
571
|
+
Why: a module with 8 criteria accumulates 8 × (code + tests + CI output + error logs) in one context window. By criterion 4 the context is saturated. The agent starts hallucinating file contents, missing earlier test failures, and losing track of what was committed. This is why context blows up during builds.
|
|
572
|
+
|
|
573
|
+
The fix: each criterion is an independent job. Agent starts clean, does exactly one criterion, commits, exits. Orchestrator spawns the next agent with a fresh context for the next criterion.
|
|
574
|
+
|
|
575
|
+
**Depth limit: one. Non-negotiable, reference-pattern borrowed from Grok Build's subagent model 2026-07-16** (its `spawn_subagent` tool hard-errors if a subagent tries to call it again — "maximum nesting depth is one"). A criterion agent MUST NOT call the `Agent` or `Workflow` tool itself. Only the top-level orchestrator session spawns criterion agents. This is enforced here as an explicit instruction, not a harness-level denial the way Grok's Rust runtime enforces it structurally — be explicit about that gap rather than implying a guarantee that doesn't exist. Why it matters: a criterion agent that spawns its own helper subagent reintroduces exactly the unbounded-growth failure the orchestrator-session-bound section below exists to prevent, one level down and outside the orchestrator's own turn-count/checkpoint visibility.
|
|
576
|
+
|
|
577
|
+
**Context budget per agent (strict):**
|
|
578
|
+
```
|
|
579
|
+
objective.md — always included (short)
|
|
580
|
+
architecture.md — always included (short)
|
|
581
|
+
module/spec.yaml — always included (this module only)
|
|
582
|
+
ONE criterion entry — from acceptance.yaml, this criterion only
|
|
583
|
+
architect_persona.md — BLOCK rules section only, not full document
|
|
584
|
+
Relevant ADRs — ONLY ADRs whose domain matches files this criterion touches
|
|
585
|
+
(check ADR title vs file paths — if no match, omit)
|
|
586
|
+
Prior criterion note — 1 line: "<module>/<prev-id> done. commit: <hash>. added: <what>"
|
|
587
|
+
(not the code, not the tests, just the summary)
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
**Excluded from every agent brief:**
|
|
591
|
+
- Other modules' specs
|
|
592
|
+
- All criteria except the current one
|
|
593
|
+
- Full KB domain files (load snippet only if directly relevant)
|
|
594
|
+
- Full ci_rules.yaml (agent runs `pcp check` — doesn't need to read rules)
|
|
595
|
+
- Full build loop protocol (summarised to 7 steps in the agent brief)
|
|
596
|
+
|
|
597
|
+
### Execution Engine — `pcp build-plan` + Workflow tool
|
|
598
|
+
|
|
599
|
+
**Rewritten 2026-07-30, real incident.** `pcp build`'s Python execution engine
|
|
600
|
+
(worktree-per-criterion, merge-then-retry-on-conflict) and this skill's own
|
|
601
|
+
Workflow-based execution were two INDEPENDENT orchestration implementations of
|
|
602
|
+
the same job, reinventing coordination the harness already provides natively.
|
|
603
|
+
Measured cost on Project O: 5 of 5 completed `query-eval-harness`
|
|
604
|
+
criteria collided on merge in one run, 99% of that run's spend sat on the
|
|
605
|
+
conflicted criteria, two criteria were still stuck mid-retry two hours in. Root
|
|
606
|
+
cause, read directly out of the colliding file: every criterion in a module
|
|
607
|
+
adds one method to a shared facade class in `__init__.py` and removes one entry
|
|
608
|
+
from a shared `_PENDING` dict — small, non-overlapping in MEANING, but landing
|
|
609
|
+
at the same insertion point in the same file, so git's diff calls it a conflict
|
|
610
|
+
where nothing semantically conflicts. No criterion ever declares `__init__.py`
|
|
611
|
+
as its `target`, so a target-only scheduler can never see this collision coming.
|
|
612
|
+
|
|
613
|
+
**The split now:** Python plans, the harness executes. `pcp build-plan
|
|
614
|
+
[--module X]` computes the schedule (module waves, criterion waves, each
|
|
615
|
+
module's `shared_surface_files`) exactly as before — same
|
|
616
|
+
`_compute_criterion_waves`/dependency logic, nothing new — and emits it as JSON.
|
|
617
|
+
It spawns nothing and writes nothing. Run it first, always:
|
|
618
|
+
|
|
619
|
+
```bash
|
|
620
|
+
pcp build-plan --module <name> # or omit --module for the whole program
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Each module in the plan carries `shared_surface_files`: its own
|
|
624
|
+
`src/modules/<module>/__init__.py`, plus any file a `MOD_A00x` criterion in that
|
|
625
|
+
module declares as `target` (the app-registry entry, the interface file).
|
|
626
|
+
**Every criterion in the module is scheduled as implicitly touching these**,
|
|
627
|
+
regardless of what its own `target` says — an `A00x` criterion's own new file
|
|
628
|
+
IS genuinely disjoint from a sibling's own new file (real parallelism is safe
|
|
629
|
+
there), but both still touch the shared facade to register.
|
|
630
|
+
|
|
631
|
+
**Two different things, two different primitives — do not conflate them:**
|
|
632
|
+
|
|
633
|
+
1. **Each criterion's own file(s) — build in real `parallel()`.** True
|
|
634
|
+
independence; worktree isolation is warranted here (Workflow's own
|
|
635
|
+
guidance: use isolation ONLY when agents would otherwise conflict — this is
|
|
636
|
+
exactly that case, criteria genuinely writing different files at once).
|
|
637
|
+
2. **`shared_surface_files` — single writer, never parallel edits.** A worker
|
|
638
|
+
agent NEVER edits a module's `__init__.py`/interface/registry directly — it
|
|
639
|
+
returns a small STRUCTURED registration request instead (method name,
|
|
640
|
+
delegate-to call, `_PENDING` key to drop — the same shape every criterion in
|
|
641
|
+
this module already needs, visible in any existing `__init__.py` of this
|
|
642
|
+
kind). One step applies all pending requests to the shared file(s), in
|
|
643
|
+
order, after (or as) each worker finishes. Since only that one step ever
|
|
644
|
+
writes those files, there is nothing to merge and nothing to conflict —
|
|
645
|
+
not "conflicts resolved faster", genuinely no conflict is possible.
|
|
646
|
+
Prefer a **deterministic** apply step over another agent call for this: the
|
|
647
|
+
edit shape (add one method with a fixed pattern, remove one dict key) is
|
|
648
|
+
regular enough not to need judgment. Escalate to an actual agent step only
|
|
649
|
+
when a shared-file edit genuinely can't be expressed as that structured
|
|
650
|
+
request (e.g. two criteria both want to restructure the same class
|
|
651
|
+
differently) — rare, not the routine case.
|
|
652
|
+
|
|
653
|
+
`depends_on` still means ORDER only (declared → later wave, absent/empty →
|
|
654
|
+
wave 0, runs together) — `pcp build-plan`'s `criterion_waves` already reflects
|
|
655
|
+
this, don't re-derive it.
|
|
656
|
+
|
|
657
|
+
```javascript
|
|
658
|
+
export const meta = {
|
|
659
|
+
name: 'pcp-wave',
|
|
660
|
+
description: 'Build one module wave from pcp build-plan\'s output',
|
|
661
|
+
phases: [
|
|
662
|
+
{ title: 'Build' },
|
|
663
|
+
{ title: 'Gate' },
|
|
664
|
+
{ title: 'Register' }, // the single-writer step — see above
|
|
665
|
+
]
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// plan = JSON.parse(shell(`pcp build-plan --module ${moduleName}`))
|
|
669
|
+
// One pipeline() call per criterion wave (plan.modules[i].criterion_waves[w]) —
|
|
670
|
+
// waves run in order, criteria within a wave run together.
|
|
671
|
+
for (const wave of plan.modules[0].criterion_waves) {
|
|
672
|
+
const results = await pipeline(
|
|
673
|
+
wave,
|
|
674
|
+
// Stage 1: build ONLY this criterion's own file(s) — never the shared surface.
|
|
675
|
+
criterion => agent(buildBrief(criterion, plan.modules[0].shared_surface_files), {
|
|
676
|
+
label: `build:${criterion.id}`, phase: 'Build',
|
|
677
|
+
isolation: wave.length > 1 ? 'worktree' : undefined, // isolation only when it's buying something
|
|
678
|
+
}),
|
|
679
|
+
// Stage 2: gate, streams in as each build finishes — no barrier.
|
|
680
|
+
(buildResult, criterion) => agent(
|
|
681
|
+
`Run pcp gate on ${criterion.id}. commit: ${buildResult.commit}. advisory only.`,
|
|
682
|
+
{ label: `gate:${criterion.id}`, phase: 'Gate' },
|
|
683
|
+
),
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
// Stage 3: single-writer registration — a REAL barrier, deliberately.
|
|
687
|
+
// Every criterion in this wave returned a registration request; apply them
|
|
688
|
+
// to shared_surface_files ONE AT A TIME, here, the only place these files
|
|
689
|
+
// are ever touched. Deterministic template insertion where the shape
|
|
690
|
+
// allows (see module docstring); escalate to agent() only if a request
|
|
691
|
+
// can't be expressed that way.
|
|
692
|
+
phase('Register')
|
|
693
|
+
for (const r of results.filter(Boolean)) {
|
|
694
|
+
await applyRegistration(r.registrationRequest, plan.modules[0].shared_surface_files)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**The `buildBrief(criterion, sharedSurfaceFiles)` function** — compact, no bloat:
|
|
700
|
+
```
|
|
701
|
+
Build criterion ${criterion.id}: "${criterion.description}"
|
|
702
|
+
Module: ${criterion.module} | check: ${criterion.check}
|
|
703
|
+
Working dir: worktree for feat/${criterion.module}
|
|
704
|
+
|
|
705
|
+
## Objective
|
|
706
|
+
${objective_md} ← full (it's short)
|
|
707
|
+
|
|
708
|
+
## Architecture
|
|
709
|
+
${architecture_md} ← full (it's short)
|
|
710
|
+
|
|
711
|
+
## Module spec
|
|
712
|
+
${module_spec_yaml} ← full (this module only)
|
|
713
|
+
|
|
714
|
+
## Persona BLOCK rules
|
|
715
|
+
${persona_block_section} ← BLOCK section only, not full document
|
|
716
|
+
|
|
717
|
+
${relevant_adr ? `## Relevant ADR\n${relevant_adr}` : ''}
|
|
718
|
+
${criterion.prevCommit ? `## Prior work\n${criterion.module}/${criterion.prevId} done. commit: ${criterion.prevCommit}. added: ${criterion.prevSummary}.` : ''}
|
|
719
|
+
|
|
720
|
+
## Files you may NOT edit — single-writer, someone else applies these
|
|
721
|
+
${sharedSurfaceFiles.join('\n')}
|
|
722
|
+
If your criterion needs a new export/registration entry in one of these, do NOT
|
|
723
|
+
touch the file. Instead include a registrationRequest in your return value (see
|
|
724
|
+
below) describing exactly what to add. This is not a style preference — two
|
|
725
|
+
agents editing these files at once is how a real incident happened (2026-07-30,
|
|
726
|
+
5 of 5 criteria in one module collided on merge because everyone edited the
|
|
727
|
+
same shared facade class directly). The file will be updated for you, after you
|
|
728
|
+
finish, by the one step that's allowed to touch it.
|
|
729
|
+
|
|
730
|
+
## Steps (in order, no skipping)
|
|
731
|
+
1. Write tests → verify RED
|
|
732
|
+
2. Write code → verify GREEN (your own file(s) only — never a file listed above)
|
|
733
|
+
3. lint → pcp check → pcp architect-review --staged
|
|
734
|
+
4. Fix all BLOCK findings
|
|
735
|
+
5. commit: feat(${criterion.module}): ${criterion.id} — ${criterion.description}
|
|
736
|
+
6. echo "${criterion.module}:${criterion.id}:$(git rev-parse HEAD)" >> .pcp/.build_progress
|
|
737
|
+
|
|
738
|
+
Return: {
|
|
739
|
+
commit: "<hash>", summary: "<one line what was added>", escalation: null | "<what>",
|
|
740
|
+
registrationRequest: null | {
|
|
741
|
+
file: "<one of the files listed above>",
|
|
742
|
+
method_name: "<name>", delegates_to: "<module.path.function>",
|
|
743
|
+
pending_key_to_remove: "<key, or null if this module has no _PENDING dict>"
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
Do NOT read other modules. Do NOT continue to next criterion. Exit after step 6.
|
|
747
|
+
Do NOT call the Agent or Workflow tool yourself — depth limit is one level, you are the leaf.
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
### Escalation Bubbling
|
|
751
|
+
|
|
752
|
+
When a subagent hits an escalation trigger, it returns it to the orchestrator.
|
|
753
|
+
Orchestrator collects all escalations, presents them to PM as a batch:
|
|
754
|
+
|
|
755
|
+
```
|
|
756
|
+
Build in progress — 3 escalations need your input:
|
|
757
|
+
|
|
758
|
+
1. exports/A004 (check: manual)
|
|
759
|
+
Verify: run export against a test dataset, confirm output matches spec
|
|
760
|
+
Action: test and say "exports A004 verified"
|
|
761
|
+
|
|
762
|
+
2. payments
|
|
763
|
+
Architecture decision needed: which webhook signing scheme for the payment provider?
|
|
764
|
+
Options: (a) HMAC-SHA256 shared secret (b) provider SDK-managed verification
|
|
765
|
+
Action: say "pcp decision: payment-webhook-signing = <choice>"
|
|
766
|
+
|
|
767
|
+
3. notifications
|
|
768
|
+
Missing API credential for the push-notification provider
|
|
769
|
+
Requires provider dashboard signup, 1-2 day lead time
|
|
770
|
+
Action: provision and say "push credential added"
|
|
771
|
+
|
|
772
|
+
Build continues on all non-blocked modules.
|
|
773
|
+
Reply to each escalation by number to unblock.
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
PM responds, orchestrator unblocks the relevant agents.
|
|
777
|
+
|
|
778
|
+
---
|
|
779
|
+
|
|
780
|
+
## SINGLE-AGENT BUILD LOOP PROTOCOL
|
|
781
|
+
|
|
782
|
+
**Scope: one criterion per agent session.** This section describes what one agent does for one criterion. Agents do not loop. They do not continue to the next criterion. One criterion → commit → exit. The orchestrator spawns the next agent.
|
|
783
|
+
|
|
784
|
+
Why: each criterion accumulates code + test output + CI logs + error messages in context. Looping through multiple criteria in one session causes context saturation by criterion 3-4. The agent starts missing earlier failures, misremembering file contents, and losing spec alignment. One agent per criterion eliminates this entirely.
|
|
785
|
+
|
|
786
|
+
**Completion Definition — all must be true before marking done:**
|
|
787
|
+
- Tests written that FAIL before code exists (red proven)
|
|
788
|
+
- Tests passing after code written (green proven)
|
|
789
|
+
- Full regression suite green (no regressions)
|
|
790
|
+
- Build succeeds (compiled languages)
|
|
791
|
+
- Lint clean
|
|
792
|
+
- Secret scan clean — no credentials in staged files
|
|
793
|
+
- `pcp check` clean (no BLOCK)
|
|
794
|
+
- `pcp architect-review --staged` clean (no BLOCK)
|
|
795
|
+
- Acceptance `check:` type passes (file_exists / test_passes / dom_contains / manual)
|
|
796
|
+
- Self-QA: code matches criterion description exactly
|
|
797
|
+
- `acceptance.yaml` status updated to `complete` for this criterion
|
|
798
|
+
- Checkpoint written: `echo "<module>:<id>:<git-hash>" >> .pcp/.build_progress`
|
|
799
|
+
|
|
800
|
+
**Not required per-criterion (run at wave merge, not per agent):**
|
|
801
|
+
- CI green (runs on PR)
|
|
802
|
+
- Staging smoke test (runs after wave merge)
|
|
803
|
+
- Production smoke test (runs after staging)
|
|
804
|
+
- `pcp scan` (runs after all criteria complete)
|
|
805
|
+
|
|
806
|
+
### Module pre-flight (once per module, before first criterion agent — run by orchestrator)
|
|
807
|
+
|
|
808
|
+
**Dep audit** — dependencies checked once per module, not per criterion:
|
|
809
|
+
```bash
|
|
810
|
+
# Python
|
|
811
|
+
safety check 2>/dev/null || pip-audit 2>/dev/null
|
|
812
|
+
|
|
813
|
+
# JavaScript/TypeScript
|
|
814
|
+
npm audit --audit-level=high 2>/dev/null
|
|
815
|
+
|
|
816
|
+
# Rust
|
|
817
|
+
cargo audit 2>/dev/null
|
|
818
|
+
```
|
|
819
|
+
High-severity CVE: fix dependency version in manifest before spawning any criterion agents. Criterion agents inherit the fixed manifest.
|
|
820
|
+
|
|
821
|
+
**Spec security review** (Stage 1 — security dimension):
|
|
822
|
+
Does spec describe auth model, input validation, data classification for user-facing or API modules?
|
|
823
|
+
```bash
|
|
824
|
+
pcp architect-review --module <name> --fail-on-block
|
|
825
|
+
```
|
|
826
|
+
BLOCK on spec → do not spawn criterion agents. Notify PM. Continue other modules.
|
|
827
|
+
|
|
828
|
+
### Per-criterion agent pre-flight (once per agent, takes 30 seconds)
|
|
829
|
+
|
|
830
|
+
**Spec lock.** Hash all spec files. Store in `.pcp/.build_lock`.
|
|
831
|
+
```bash
|
|
832
|
+
find .pcp/strategy/modules -name "*.yaml" | sort | xargs sha256sum > .pcp/.build_lock
|
|
833
|
+
```
|
|
834
|
+
If `.pcp/.build_lock` already exists and differs: specs changed during build — escalate before continuing.
|
|
835
|
+
|
|
836
|
+
**Install pre-commit hooks** if not present:
|
|
837
|
+
```bash
|
|
838
|
+
# Secret scanning
|
|
839
|
+
pip install detect-secrets 2>/dev/null || true
|
|
840
|
+
pre-commit install 2>/dev/null || true
|
|
841
|
+
# If no pre-commit config, add minimal secret hook:
|
|
842
|
+
if [ ! -f .pre-commit-config.yaml ]; then
|
|
843
|
+
cat > .pre-commit-config.yaml << 'EOF'
|
|
844
|
+
repos:
|
|
845
|
+
- repo: https://github.com/Yelp/detect-secrets
|
|
846
|
+
rev: v1.4.0
|
|
847
|
+
hooks:
|
|
848
|
+
- id: detect-secrets
|
|
849
|
+
EOF
|
|
850
|
+
fi
|
|
851
|
+
```
|
|
852
|
+
|
|
853
|
+
**Conflict pre-check** (for parallel builds on feature branches):
|
|
854
|
+
```bash
|
|
855
|
+
BRANCH=$(git branch --show-current)
|
|
856
|
+
git fetch origin main --quiet
|
|
857
|
+
CONFLICTS=$(git merge-tree $(git merge-base HEAD origin/main) HEAD origin/main 2>/dev/null | grep -c "<<<<<<" || echo 0)
|
|
858
|
+
if [ "$CONFLICTS" -gt "0" ]; then
|
|
859
|
+
echo "CONFLICT: $CONFLICTS conflicting sections vs main. Rebase before continuing."
|
|
860
|
+
# attempt auto-rebase
|
|
861
|
+
git rebase origin/main 2>/dev/null || echo "REBASE_NEEDED"
|
|
862
|
+
fi
|
|
863
|
+
```
|
|
864
|
+
If rebase fails: escalate to orchestrator.
|
|
865
|
+
|
|
866
|
+
### For your assigned criterion (one only — stop after commit):
|
|
867
|
+
|
|
868
|
+
**1. Announce:** `Building <module>/<id> — <description>`
|
|
869
|
+
|
|
870
|
+
**2. Context already loaded in your brief.** Do NOT speculatively read other modules' files, other criteria, or the full KB. Read only files directly relevant to implementing this criterion. If you need an ADR that wasn't in your brief: read it, note it, do not load the rest.
|
|
871
|
+
|
|
872
|
+
**3. Resolve dependencies.** Check packages exist before writing imports. Add to manifest first.
|
|
873
|
+
|
|
874
|
+
**4. Write tests first (TDD).**
|
|
875
|
+
Tests must FAIL before code exists. Verify red before writing code.
|
|
876
|
+
```bash
|
|
877
|
+
pytest tests/test_<module>.py -x 2>&1 | tail -5 # Python
|
|
878
|
+
npx jest <module>.test.ts 2>&1 | tail -5 # TypeScript
|
|
879
|
+
cargo test <module> 2>&1 | tail -5 # Rust
|
|
880
|
+
swift test --filter <Module>Tests 2>&1 | tail -5 # Swift
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
**5. Write code.** Ground in spec.yaml constraints + architecture.md + persona BLOCK rules + ADRs.
|
|
884
|
+
If architectural decision needed that's not in ADRs: STOP, return escalation, do not guess.
|
|
885
|
+
|
|
886
|
+
**6. Build check (compiled languages).**
|
|
887
|
+
```bash
|
|
888
|
+
xcodebuild build -scheme <S> -destination 'generic/platform=macOS' 2>&1 | grep -E "error:|BUILD" | tail -5
|
|
889
|
+
cargo build 2>&1 | tail -10
|
|
890
|
+
npx tsc --noEmit 2>&1 | tail -10
|
|
891
|
+
```
|
|
892
|
+
Fix until build passes.
|
|
893
|
+
|
|
894
|
+
**7. Tests green + regression.**
|
|
895
|
+
One run — unit tests for this module with coverage, plus full suite to catch regressions. Not two separate runs.
|
|
896
|
+
```bash
|
|
897
|
+
pytest --tb=short -q --cov=src/<module> --cov-report=term-missing # Python
|
|
898
|
+
npx jest --coverage 2>/dev/null # TypeScript (all tests)
|
|
899
|
+
cargo test 2>/dev/null # Rust (all tests)
|
|
900
|
+
swift test 2>/dev/null # Swift
|
|
901
|
+
```
|
|
902
|
+
Fix until all pass. Cannot proceed with broken existing tests.
|
|
903
|
+
|
|
904
|
+
**9. QA self-review.** Re-read criterion description. Ask:
|
|
905
|
+
- Does my code actually do what this says?
|
|
906
|
+
- Will `check:` type pass on my code?
|
|
907
|
+
- Any BLOCK rule from persona violated?
|
|
908
|
+
- Any ADR boundary touched without following the ADR pattern?
|
|
909
|
+
|
|
910
|
+
**10. `pcp check`** — fix any BLOCK before proceeding.
|
|
911
|
+
|
|
912
|
+
**11. `pcp architect-review --staged`** — fix BLOCK, fix WARN, log NOTE.
|
|
913
|
+
|
|
914
|
+
**11a. Lint gate.**
|
|
915
|
+
```bash
|
|
916
|
+
# Python
|
|
917
|
+
black --check src/ tests/ 2>/dev/null && ruff check src/ 2>/dev/null
|
|
918
|
+
# TypeScript/JS
|
|
919
|
+
npx eslint src/ --max-warnings 0 2>/dev/null && npx prettier --check src/ 2>/dev/null
|
|
920
|
+
# Rust
|
|
921
|
+
cargo fmt --check 2>/dev/null && cargo clippy -- -D warnings 2>/dev/null
|
|
922
|
+
# Swift
|
|
923
|
+
swiftformat --lint Sources/ 2>/dev/null
|
|
924
|
+
# C
|
|
925
|
+
# clang-format check handled by pcp check AST rules
|
|
926
|
+
```
|
|
927
|
+
Lint failures → fix code, re-run. Do not commit with lint violations.
|
|
928
|
+
|
|
929
|
+
**11b. Secret scan.**
|
|
930
|
+
```bash
|
|
931
|
+
detect-secrets scan --baseline .secrets.baseline 2>/dev/null || \
|
|
932
|
+
git diff --staged | grep -E "(password|secret|api_key|token|private_key)\s*=\s*['\"][^'\"]{8,}" -i
|
|
933
|
+
```
|
|
934
|
+
If secrets detected: STOP. Remove secret. Add to `.env.example` instead. Commit clean version.
|
|
935
|
+
Never commit credentials, tokens, or API keys.
|
|
936
|
+
|
|
937
|
+
**11c. SAST (code patterns only — dep audit runs once at module pre-flight, not per criterion).**
|
|
938
|
+
```bash
|
|
939
|
+
# Python
|
|
940
|
+
bandit -r src/ -ll 2>/dev/null # SAST, skip low-severity
|
|
941
|
+
|
|
942
|
+
# Any: semgrep (if installed)
|
|
943
|
+
semgrep --config=auto src/ --error 2>/dev/null
|
|
944
|
+
```
|
|
945
|
+
SAST finding: fix code. If false positive: add `# nosec` / `// nosemgrep` with comment explaining why.
|
|
946
|
+
Dependency CVEs are checked once at module pre-flight — not repeated here.
|
|
947
|
+
|
|
948
|
+
**11d. Error handling test coverage.**
|
|
949
|
+
At least one test per criterion must test a failure path — what happens when:
|
|
950
|
+
- Input is malformed or missing
|
|
951
|
+
- Downstream dependency is unavailable
|
|
952
|
+
- Timeout occurs
|
|
953
|
+
- Permission is denied
|
|
954
|
+
|
|
955
|
+
If criterion spec has no failure mode described: derive from spec constraints and common sense.
|
|
956
|
+
|
|
957
|
+
**11e. ~~Contract validation~~**
|
|
958
|
+
Moved to post-module-complete (after ALL criteria for this module pass), just before PR creation. Validating a contract after one criterion when the module has 8 criteria is premature — the output isn't representative yet. Skip here.
|
|
959
|
+
|
|
960
|
+
**12. E2E (only if criterion check type is NOT dom_contains / url_responds).**
|
|
961
|
+
If criterion has `check: dom_contains` or `check: url_responds` — skip this step. Step 16 IS the E2E for those types.
|
|
962
|
+
If criterion has `check: manual` or `check: test_passes` with integration scope:
|
|
963
|
+
```bash
|
|
964
|
+
npx playwright test tests/e2e/<module>.spec.ts 2>/dev/null # web
|
|
965
|
+
tests/integration/<module>_integration.sh 2>/dev/null # CLI/API
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
**13. Commit.**
|
|
969
|
+
```bash
|
|
970
|
+
git add <specific files>
|
|
971
|
+
git commit -m "<type>(<module>): <what>"
|
|
972
|
+
```
|
|
973
|
+
No Co-Authored-By. No attribution. Specific files only, never `git add -A`.
|
|
974
|
+
|
|
975
|
+
**14. Push.**
|
|
976
|
+
```bash
|
|
977
|
+
git push origin feat/<module>
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
**15. Watch CI.**
|
|
981
|
+
```bash
|
|
982
|
+
BRANCH=$(git branch --show-current)
|
|
983
|
+
RUN_ID=$(gh run list --branch "$BRANCH" --limit 1 --json databaseId --jq '.[0].databaseId')
|
|
984
|
+
gh run watch "$RUN_ID" --exit-status
|
|
985
|
+
```
|
|
986
|
+
Failure → auto-fix loop (max 3 retries, exponential backoff: wait 2s, 4s, 8s between retries).
|
|
987
|
+
|
|
988
|
+
**Auto-fix loop:**
|
|
989
|
+
```bash
|
|
990
|
+
gh run view "$RUN_ID" --log-failed | head -300
|
|
991
|
+
# diagnose → fix code → re-run tests locally → lint → secret scan → commit → push → watch CI
|
|
992
|
+
```
|
|
993
|
+
After 3 retries still failing: return escalation to orchestrator.
|
|
994
|
+
|
|
995
|
+
**15a. ~~Staging deploy~~**
|
|
996
|
+
Moved to wave merge — one staging deploy after all wave modules merge to main. Deploying to staging after every criterion (8 deploys for an 8-criterion module) is waste. Skip here.
|
|
997
|
+
|
|
998
|
+
**16. Visual/URL checks.**
|
|
999
|
+
- `check: dom_contains` → Playwright selector, automated
|
|
1000
|
+
- `check: url_responds` → curl HTTP status, automated
|
|
1001
|
+
- `check: visual` → screenshot + return to orchestrator → PM approves
|
|
1002
|
+
|
|
1003
|
+
**16b. Production smoke test** (after Railway prod deploy, if `check: railway_deploy`):
|
|
1004
|
+
```bash
|
|
1005
|
+
# Wait for prod deploy
|
|
1006
|
+
for i in $(seq 1 30); do
|
|
1007
|
+
STATUS=$(railway status --json 2>/dev/null | python3 -c \
|
|
1008
|
+
"import sys,json; print(json.load(sys.stdin).get('deploymentStatus','unknown'))")
|
|
1009
|
+
[ "$STATUS" = "SUCCESS" ] && break
|
|
1010
|
+
[ "$STATUS" = "FAILED" ] && echo "PROD_DEPLOY_FAILED" && break
|
|
1011
|
+
sleep 30
|
|
1012
|
+
done
|
|
1013
|
+
|
|
1014
|
+
# Smoke test production
|
|
1015
|
+
PROD_URL=$(railway domain 2>/dev/null | head -1)
|
|
1016
|
+
if [ -n "$PROD_URL" ]; then
|
|
1017
|
+
HTTP=$(curl -s -o /dev/null -w "%{http_code}" "https://$PROD_URL/health" 2>/dev/null)
|
|
1018
|
+
echo "Production smoke: HTTP $HTTP"
|
|
1019
|
+
if [ "$HTTP" != "200" ]; then
|
|
1020
|
+
echo "PRODUCTION_DOWN — initiating rollback"
|
|
1021
|
+
railway rollback 2>/dev/null || echo "manual rollback needed"
|
|
1022
|
+
echo "PROD_SMOKE_FAILED"
|
|
1023
|
+
fi
|
|
1024
|
+
fi
|
|
1025
|
+
```
|
|
1026
|
+
If production smoke fails: auto-rollback, escalate immediately. Never leave production broken silently.
|
|
1027
|
+
|
|
1028
|
+
**17. Checkpoint + exit.**
|
|
1029
|
+
```bash
|
|
1030
|
+
# Update acceptance.yaml: set this criterion's status to complete
|
|
1031
|
+
# Write checkpoint (orchestrator reads this to know where to resume)
|
|
1032
|
+
HASH=$(git rev-parse HEAD)
|
|
1033
|
+
echo "<module>:<criterion-id>:$HASH" >> .pcp/.build_progress
|
|
1034
|
+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) COMPLETE <module>/<criterion-id> $HASH" >> .pcp/audit.log
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
Report to orchestrator: `DONE <module>/<criterion-id> | commit <hash> | next: <next-criterion-id or WAVE_COMPLETE>`
|
|
1038
|
+
|
|
1039
|
+
**Stop here. Do not continue to the next criterion.** The orchestrator spawns a fresh agent for the next criterion with a clean context window. This is how context limits are prevented — not by checkpointing mid-loop, but by never looping in the first place.
|
|
1040
|
+
|
|
1041
|
+
---
|
|
1042
|
+
|
|
1043
|
+
## MERGE COORDINATOR PROTOCOL
|
|
1044
|
+
|
|
1045
|
+
After each wave completes, orchestrator runs merge sequence:
|
|
1046
|
+
|
|
1047
|
+
```bash
|
|
1048
|
+
for module in <wave_modules>; do
|
|
1049
|
+
# Pre-merge: rebase feature branch on main to surface conflicts before PR
|
|
1050
|
+
git -C "../$(basename $(pwd))-$module" fetch origin main --quiet
|
|
1051
|
+
git -C "../$(basename $(pwd))-$module" rebase origin/main 2>/dev/null
|
|
1052
|
+
if [ $? -ne 0 ]; then
|
|
1053
|
+
echo "CONFLICT: $module cannot rebase cleanly — escalating"
|
|
1054
|
+
continue # skip this module, escalate, continue others
|
|
1055
|
+
fi
|
|
1056
|
+
|
|
1057
|
+
# Contract validation — runs here (module complete, all criteria done, full output available)
|
|
1058
|
+
if [ -f ".pcp/contracts/${module}_output.schema.json" ]; then
|
|
1059
|
+
echo "Validating $module output contract..."
|
|
1060
|
+
python3 -m pytest "tests/contracts/test_${module}_contract.py" -q 2>/dev/null || \
|
|
1061
|
+
echo "CONTRACT_FAIL: $module — escalating"
|
|
1062
|
+
else
|
|
1063
|
+
# Generate contract schema from actual module output if it has dependents
|
|
1064
|
+
grep -q "$module" .pcp/strategy/dependency_map.md 2>/dev/null && \
|
|
1065
|
+
echo "WARN: $module has dependents but no contract schema — generate .pcp/contracts/${module}_output.schema.json"
|
|
1066
|
+
fi
|
|
1067
|
+
|
|
1068
|
+
# Create PR
|
|
1069
|
+
gh pr create \
|
|
1070
|
+
--base main \
|
|
1071
|
+
--head "feat/$module" \
|
|
1072
|
+
--title "feat($module): complete all acceptance criteria" \
|
|
1073
|
+
--body "$(pcp status --module $module --markdown 2>/dev/null || echo 'Module complete')"
|
|
1074
|
+
|
|
1075
|
+
# Layer 2 gate (advisory, not blocking)
|
|
1076
|
+
pcp gate --branch "feat/$module" 2>/dev/null
|
|
1077
|
+
|
|
1078
|
+
# Merge
|
|
1079
|
+
gh pr merge "feat/$module" --merge --auto
|
|
1080
|
+
|
|
1081
|
+
# Cleanup worktree
|
|
1082
|
+
git worktree remove "../$(basename $(pwd))-$module" 2>/dev/null || true
|
|
1083
|
+
git branch -d "feat/$module" 2>/dev/null || true
|
|
1084
|
+
done
|
|
1085
|
+
|
|
1086
|
+
# Integration test after all wave merges
|
|
1087
|
+
git checkout main && git pull
|
|
1088
|
+
npm test 2>/dev/null || pytest --tb=short -q 2>/dev/null || cargo test 2>/dev/null || swift test 2>/dev/null || make test 2>/dev/null
|
|
1089
|
+
|
|
1090
|
+
# Staging deploy + smoke (once per wave, not per criterion)
|
|
1091
|
+
if grep -q "STAGING_URL\|staging" .pcp/architecture.md 2>/dev/null; then
|
|
1092
|
+
railway up --environment staging 2>/dev/null
|
|
1093
|
+
for i in $(seq 1 20); do
|
|
1094
|
+
STATUS=$(railway status --environment staging --json 2>/dev/null | python3 -c \
|
|
1095
|
+
"import sys,json; print(json.load(sys.stdin).get('deploymentStatus','unknown'))" 2>/dev/null)
|
|
1096
|
+
[ "$STATUS" = "SUCCESS" ] && break
|
|
1097
|
+
[ "$STATUS" = "FAILED" ] && echo "STAGING_DEPLOY_FAILED" && break
|
|
1098
|
+
sleep 30
|
|
1099
|
+
done
|
|
1100
|
+
STAGING_URL=$(railway domain --environment staging 2>/dev/null | head -1)
|
|
1101
|
+
[ -n "$STAGING_URL" ] && \
|
|
1102
|
+
HTTP=$(curl -s -o /dev/null -w "%{http_code}" "https://$STAGING_URL/health" 2>/dev/null) && \
|
|
1103
|
+
echo "Staging smoke: $HTTP" && \
|
|
1104
|
+
[ "$HTTP" != "200" ] && echo "STAGING_SMOKE_FAILED — fix before starting next wave"
|
|
1105
|
+
fi
|
|
1106
|
+
|
|
1107
|
+
# Validate strategy coverage still holds after wave
|
|
1108
|
+
pcp validate-strategy
|
|
1109
|
+
# If coverage dropped or coupling violations increased: flag to PM before starting next wave
|
|
1110
|
+
|
|
1111
|
+
# Stage 3 — Wave-level architect review
|
|
1112
|
+
# Reviews ALL files changed since this wave started (not just per-criterion diffs)
|
|
1113
|
+
# Catches emergent violations: individual modules clean, but combined they violate a principle
|
|
1114
|
+
# WAVE_START_COMMIT is written to .pcp/.active_workflow at wave start
|
|
1115
|
+
WAVE_BASE=$(python3 -c "import yaml; d=yaml.safe_load(open('.pcp/.active_workflow')); print(d.get('wave_start_commit','main'))" 2>/dev/null || echo "main")
|
|
1116
|
+
pcp architect-review --base $WAVE_BASE --fail-on-block 2>&1
|
|
1117
|
+
# BLOCK findings here → do NOT start next wave. Fix before proceeding.
|
|
1118
|
+
# WARN findings → log, include in next wave's module briefs, proceed.
|
|
1119
|
+
|
|
1120
|
+
# Audit log
|
|
1121
|
+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) WAVE_COMPLETE modules=[<wave_modules>]" >> .pcp/audit.log
|
|
1122
|
+
```
|
|
1123
|
+
|
|
1124
|
+
If integration test fails after merge: bisect to find which module caused regression.
|
|
1125
|
+
```bash
|
|
1126
|
+
# Bisect: revert merges one by one until green
|
|
1127
|
+
git log --oneline -10 # find merge commits
|
|
1128
|
+
git revert <merge-commit> --no-edit # revert suspect, re-run tests
|
|
1129
|
+
```
|
|
1130
|
+
Revert culprit module, open escalation, continue next wave without that module.
|
|
1131
|
+
|
|
1132
|
+
---
|
|
1133
|
+
|
|
1134
|
+
## PERSISTENCE
|
|
1135
|
+
|
|
1136
|
+
PCP build state survives session end, session crash, and laptop close.
|
|
1137
|
+
|
|
1138
|
+
### State files (all in `.pcp/`, all committed to git)
|
|
1139
|
+
|
|
1140
|
+
```
|
|
1141
|
+
.pcp/.active_workflow — Workflow run ID + current wave state (written at build start, deleted at completion)
|
|
1142
|
+
.pcp/.build_progress — append-only log: <module>:<criterion-id>:<git-hash> per completed criterion
|
|
1143
|
+
.pcp/deferred_queue.yaml — blocked/deferred items (manual, visual, architecture decisions)
|
|
1144
|
+
.pcp/notifications.log — escalations sent when PM was away
|
|
1145
|
+
.pcp/audit.log — timestamped full audit trail
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
### Writing state on build start
|
|
1149
|
+
|
|
1150
|
+
Immediately after launching a Workflow, write `.pcp/.active_workflow`:
|
|
1151
|
+
```yaml
|
|
1152
|
+
workflow_run_id: <Workflow tool returned run ID>
|
|
1153
|
+
started: <ISO timestamp>
|
|
1154
|
+
wave: 1
|
|
1155
|
+
wave_modules: [module-a, module-b, module-c]
|
|
1156
|
+
wave_start_commit: <git rev-parse HEAD at wave start>
|
|
1157
|
+
criteria_total: 24
|
|
1158
|
+
criteria_done: 0
|
|
1159
|
+
last_updated: <ISO timestamp>
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
Update `criteria_done` and `last_updated` as each agent checkpoint is written to `.build_progress`.
|
|
1163
|
+
|
|
1164
|
+
Delete `.pcp/.active_workflow` when the full build completes cleanly.
|
|
1165
|
+
|
|
1166
|
+
### Two resume paths
|
|
1167
|
+
|
|
1168
|
+
**Hot resume (same Claude session, Workflow run ID still valid):**
|
|
1169
|
+
```
|
|
1170
|
+
Workflow({
|
|
1171
|
+
scriptPath: '.pcp/.workflow_script.js',
|
|
1172
|
+
resumeFromRunId: '<run_id from .active_workflow>'
|
|
1173
|
+
})
|
|
1174
|
+
```
|
|
1175
|
+
Completed agents return cached results instantly. Build continues from where it stopped.
|
|
1176
|
+
|
|
1177
|
+
**Cold resume (new Claude session, run ID expired):**
|
|
1178
|
+
```
|
|
1179
|
+
# Read which criteria are already done
|
|
1180
|
+
done = parse .pcp/.build_progress # set of "<module>:<id>" strings
|
|
1181
|
+
|
|
1182
|
+
# Compute pending criteria
|
|
1183
|
+
pending = all criteria in acceptance.yaml files WHERE status != complete
|
|
1184
|
+
AND "<module>:<id>" NOT IN done
|
|
1185
|
+
|
|
1186
|
+
# Start fresh Workflow with only pending criteria
|
|
1187
|
+
```
|
|
1188
|
+
Cold resume rebuilds the work list from file state, not from memory. Idempotent — re-running a criterion that's already committed is safe (agent sees the commit, marks done immediately).
|
|
1189
|
+
|
|
1190
|
+
---
|
|
1191
|
+
|
|
1192
|
+
## ORCHESTRATOR SESSION BOUND — NON-NEGOTIABLE
|
|
1193
|
+
|
|
1194
|
+
**Found 2026-07-01** after a real incident: an orchestrator session ran 36 hours, 1725 turns, 133M cache_read tokens, in one continuous session — no cap ever fired because nothing was watching the orchestrator itself.
|
|
1195
|
+
|
|
1196
|
+
The Agent Sizing Principle bounds *subagents* (one criterion, fresh context, exit). It does not bound the **orchestrator** — this session, the one running `/pcp build --all` or `/pcp watch` — which dispatches, polls CI/Railway, and handles escalations for the entire build or monitoring run. Left unbounded, the orchestrator's own transcript accumulates every poll cycle, every dispatch decision, every dashboard render, forever. That's the same context-saturation failure the per-criterion design exists to prevent, one level up, and it was missed because "runs indefinitely" (see `/pcp watch` above) was read as license for one session to stay open the whole time. It isn't — indefinite describes the monitoring/build *job*, not the *session*.
|
|
1197
|
+
|
|
1198
|
+
**Hard caps, enforced by the orchestrator on itself:**
|
|
1199
|
+
|
|
1200
|
+
- **`/pcp watch`:** stop the poll loop after 200 iterations (~15h at the default 270s interval — matches `PCP_WATCH_MAX_ITERATIONS` in the Python CLI; keep both in sync if either changes). On hitting the cap: write `.pcp/.watch_last_poll` as usual, notify PM (`"pcp watch: reached iteration cap (200) — restart with /pcp watch to keep monitoring"`), then end the session. Do not silently keep looping past it.
|
|
1201
|
+
- **`/pcp build --all`:** at every wave boundary (already a checkpoint — `.active_workflow` is written there), check this session's own turn count / elapsed time. Past ~4 hours or a large turn count, checkpoint and hand off instead of continuing into the next wave: tell PM `"wave <N> complete, checkpointing here — run /pcp build --resume in a fresh session for the remaining waves."` Cold resume (above) already rebuilds the work list from `.build_progress` idempotently — handoff is safe, so there's no reason to keep one session alive just to avoid restarting it.
|
|
1202
|
+
- **General rule:** if the orchestrator ever notices its own session is running long (large turn count, many hours elapsed, cache growing), that is itself a trigger to checkpoint and hand off — do not wait for a wave/poll boundary if it's clearly overdue.
|
|
1203
|
+
|
|
1204
|
+
---
|
|
1205
|
+
|
|
1206
|
+
## ON INVOCATION (bare `/pcp`)
|
|
1207
|
+
|
|
1208
|
+
**Step 1 — Find project root.** Walk up from cwd looking for `.pcp/`. If not found: offer to start vision workshop.
|
|
1209
|
+
|
|
1210
|
+
**Step 2 — Check for active workflow first.**
|
|
1211
|
+
```bash
|
|
1212
|
+
[ -f .pcp/.active_workflow ] && cat .pcp/.active_workflow
|
|
1213
|
+
```
|
|
1214
|
+
If `.active_workflow` exists:
|
|
1215
|
+
```
|
|
1216
|
+
Active build found.
|
|
1217
|
+
Started: <started>
|
|
1218
|
+
Wave: <wave> — <wave_modules>
|
|
1219
|
+
Progress: <criteria_done>/<criteria_total>
|
|
1220
|
+
|
|
1221
|
+
Options:
|
|
1222
|
+
(1) Resume — /pcp build --resume (hot: reattach to Workflow run, cold: rebuild from .build_progress)
|
|
1223
|
+
(2) Status — show what completed so far
|
|
1224
|
+
(3) Abandon — /pcp build --abandon (clears .active_workflow, keeps .build_progress)
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
**Step 3 — Load into working memory.** Read: objective.md, architecture.md, SDLC_phase.yaml. Do NOT load all ADRs upfront — load them lazily when a build agent needs them.
|
|
1228
|
+
|
|
1229
|
+
**Step 4 — Scan.**
|
|
1230
|
+
```bash
|
|
1231
|
+
pcp scan
|
|
1232
|
+
```
|
|
1233
|
+
|
|
1234
|
+
**Step 5 — CI + deferred queue.**
|
|
1235
|
+
```bash
|
|
1236
|
+
gh run list --limit 3 --json status,name,conclusion,updatedAt 2>/dev/null
|
|
1237
|
+
railway status 2>/dev/null || true
|
|
1238
|
+
# deferred items waiting for PM
|
|
1239
|
+
[ -f .pcp/deferred_queue.yaml ] && grep "status: deferred" .pcp/deferred_queue.yaml | wc -l
|
|
1240
|
+
```
|
|
1241
|
+
|
|
1242
|
+
**Step 6 — Present dashboard.**
|
|
1243
|
+
```
|
|
1244
|
+
Project: <name>
|
|
1245
|
+
Phase: <phase>
|
|
1246
|
+
Progress: X/Y criteria complete (Z%)
|
|
1247
|
+
|
|
1248
|
+
Waves:
|
|
1249
|
+
Wave 1 — <modules> [complete / in-progress / pending]
|
|
1250
|
+
Wave 2 — <modules> [pending]
|
|
1251
|
+
|
|
1252
|
+
CI: GitHub Actions <status> | Railway <status>
|
|
1253
|
+
Deferred: <N> items waiting for you (run /pcp status for details)
|
|
1254
|
+
Agents: 25 available
|
|
1255
|
+
|
|
1256
|
+
/pcp build --all start full parallel build
|
|
1257
|
+
/pcp build --resume resume active build
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
---
|
|
1261
|
+
|
|
1262
|
+
## MULTI-PROJECT STATUS (`/pcp status --all`)
|
|
1263
|
+
|
|
1264
|
+
```bash
|
|
1265
|
+
find ~/Claude-code -name "objective.md" -path "*/.pcp/*" 2>/dev/null | sed 's|/.pcp/objective.md||'
|
|
1266
|
+
```
|
|
1267
|
+
|
|
1268
|
+
For each project: read SDLC_phase.yaml + latest pcp.md + .active_workflow if present.
|
|
1269
|
+
|
|
1270
|
+
```
|
|
1271
|
+
All Projects — <date>
|
|
1272
|
+
|
|
1273
|
+
project-a alpha 0% Wave 1 ready CI: — [no active build]
|
|
1274
|
+
project-b alpha 17% Wave 1 ready CI: green [no active build]
|
|
1275
|
+
project-c alpha 34% Wave 2 active CI: failing [BUILDING: 8/24 done]
|
|
1276
|
+
project-d planning 0% needs /pcp new [no active build]
|
|
1277
|
+
|
|
1278
|
+
Agents: 25 available
|
|
1279
|
+
Building: project-c (resume with: cd project-c && /pcp build --resume)
|
|
1280
|
+
Most urgent: project-c CI failing — /pcp fix (in project-c dir)
|
|
1281
|
+
```
|
|
1282
|
+
|
|
1283
|
+
---
|
|
1284
|
+
|
|
1285
|
+
## `/pcp watch` — CI AND RAILWAY MONITORING
|
|
1286
|
+
|
|
1287
|
+
Triggered by `/pcp watch`. Uses `ScheduleWakeup` to run a polling loop every 270 seconds (stays within prompt cache TTL). Runs until PM says `/pcp stop` OR the iteration cap below is hit — "runs indefinitely" describes the monitoring job, not license for one session to stay open forever. See ORCHESTRATOR SESSION BOUND.
|
|
1288
|
+
|
|
1289
|
+
### What it monitors (every poll cycle)
|
|
1290
|
+
|
|
1291
|
+
**1. GitHub Actions — CLI**
|
|
1292
|
+
```bash
|
|
1293
|
+
gh run list --limit 10 --json databaseId,status,conclusion,name,updatedAt,headBranch \
|
|
1294
|
+
--jq '.[] | select(.conclusion == "failure" or .status == "in_progress")'
|
|
1295
|
+
```
|
|
1296
|
+
Failed run → proceed to auto-fix (see below).
|
|
1297
|
+
|
|
1298
|
+
**2. Railway — CLI**
|
|
1299
|
+
```bash
|
|
1300
|
+
railway status --json 2>/dev/null
|
|
1301
|
+
```
|
|
1302
|
+
Failed deploy → proceed to auto-fix.
|
|
1303
|
+
|
|
1304
|
+
**3. Railway logs (proactive scan for errors)**
|
|
1305
|
+
```bash
|
|
1306
|
+
railway logs --tail 50 2>/dev/null | grep -iE "error|exception|crash|oom|timeout" | tail -10
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
### Auto-fix loop (triggered by any failure source)
|
|
1310
|
+
|
|
1311
|
+
```
|
|
1312
|
+
1. Fetch full failure log
|
|
1313
|
+
gh run view <run-id> --log-failed | head -500
|
|
1314
|
+
OR: railway logs --deployment <id> | head -500
|
|
1315
|
+
|
|
1316
|
+
2. Diagnose: what failed?
|
|
1317
|
+
- Test failure → identify which test, which assertion
|
|
1318
|
+
- Build failure → identify which file, which error
|
|
1319
|
+
- Deploy failure → identify which service, which config
|
|
1320
|
+
- Dependency error → identify which package, which version
|
|
1321
|
+
|
|
1322
|
+
3. Fix:
|
|
1323
|
+
- Code fix → edit file → run tests locally → commit → push
|
|
1324
|
+
- Config fix → edit railway.toml or Dockerfile → commit → push
|
|
1325
|
+
- Dep fix → update manifest → commit → push
|
|
1326
|
+
|
|
1327
|
+
4. Watch new run:
|
|
1328
|
+
RUN_ID=$(gh run list --branch $(git branch --show-current) --limit 1 --json databaseId --jq '.[0].databaseId')
|
|
1329
|
+
gh run watch $RUN_ID --exit-status
|
|
1330
|
+
|
|
1331
|
+
5. On resolution:
|
|
1332
|
+
slack-notify "PCP fixed: <what failed> → <what was fixed>. CI green. Commit: <hash>"
|
|
1333
|
+
Update .pcp/.watch_last_poll
|
|
1334
|
+
|
|
1335
|
+
6. On 3 failed fix attempts:
|
|
1336
|
+
slack-notify "PCP stuck on <failure>. 3 fix attempts failed. Needs you."
|
|
1337
|
+
Add to deferred queue, continue monitoring other projects
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
### State file
|
|
1341
|
+
```bash
|
|
1342
|
+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .pcp/.watch_last_poll
|
|
1343
|
+
```
|
|
1344
|
+
Read on each cycle to know which emails/runs are new since last check.
|
|
1345
|
+
|
|
1346
|
+
### `/pcp watch --all`
|
|
1347
|
+
Monitors ALL projects under `~/Claude-code/` in a single loop. Checks each project's CI + Railway + shared email inbox. One loop, all projects.
|
|
1348
|
+
|
|
1349
|
+
---
|
|
1350
|
+
|
|
1351
|
+
## UNATTENDED AUTONOMY PROTOCOL
|
|
1352
|
+
|
|
1353
|
+
**Rule: escalation ≠ stop.** Every blocked item goes to the deferred queue + Slack notification. Build continues on everything that doesn't depend on the blocked item. Only two things are true stops: missing production credentials with no workaround, and production incident.
|
|
1354
|
+
|
|
1355
|
+
### Notification setup (read once at session start)
|
|
1356
|
+
|
|
1357
|
+
Read `.pcp/config.yaml` for notification config:
|
|
1358
|
+
```yaml
|
|
1359
|
+
notifications:
|
|
1360
|
+
slack_command: slack-notify # default: ~/bin/slack-notify
|
|
1361
|
+
channel: "#pcp-builds" # default channel
|
|
1362
|
+
webhook: "" # optional webhook URL
|
|
1363
|
+
on: [deferred, blocked, complete, error, production-incident]
|
|
1364
|
+
```
|
|
1365
|
+
If config absent: use `slack-notify` with default channel if available, else write to `.pcp/notifications.log`.
|
|
1366
|
+
|
|
1367
|
+
Every notification:
|
|
1368
|
+
```bash
|
|
1369
|
+
slack-notify "PCP [<project>/<module>]: <one-line summary>. Build continuing." 2>/dev/null || \
|
|
1370
|
+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) <summary>" >> .pcp/notifications.log
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
### Deferred queue
|
|
1374
|
+
|
|
1375
|
+
File: `.pcp/deferred_queue.yaml` — created if absent, appended atomically.
|
|
1376
|
+
|
|
1377
|
+
```yaml
|
|
1378
|
+
deferred:
|
|
1379
|
+
- id: "<module>/<criterion-id>"
|
|
1380
|
+
type: manual | visual | architecture-decision | blocked-ci | blocked-secret
|
|
1381
|
+
summary: "<one sentence what is needed>"
|
|
1382
|
+
detail: "<exact instruction for PM>"
|
|
1383
|
+
screenshot: "<path if visual>"
|
|
1384
|
+
draft_adr: "<path if architecture-decision>"
|
|
1385
|
+
blocked_since: "<ISO timestamp>"
|
|
1386
|
+
retry_at: "<ISO timestamp, for blocked-ci>"
|
|
1387
|
+
unblocks: ["<module>/<id>", ...] # criteria waiting on this
|
|
1388
|
+
status: deferred | resolved
|
|
1389
|
+
```
|
|
1390
|
+
|
|
1391
|
+
### How each blocker is handled autonomously
|
|
1392
|
+
|
|
1393
|
+
**Architecture decision not in ADRs:**
|
|
1394
|
+
1. Apply heuristics: existing ADRs, architecture.md principles, language/framework best practices
|
|
1395
|
+
2. Make the decision and write `.pcp/kb/adr/ADR-<N>-<slug>-DRAFT.md`
|
|
1396
|
+
3. Continue building with that decision
|
|
1397
|
+
4. Add to deferred queue (type: architecture-decision)
|
|
1398
|
+
5. Notify: "Made autonomous architecture decision: <decision>. Draft ADR written. Approve with `/pcp accept-adr <N>` or override with `/pcp override-adr <N> <your-decision>`"
|
|
1399
|
+
6. If PM overrides later: revert affected code, rebuild with correct decision
|
|
1400
|
+
|
|
1401
|
+
**`check: manual` criterion:**
|
|
1402
|
+
1. Write code that satisfies the criterion (treat as `check: file_exists` for the implementation)
|
|
1403
|
+
2. Add to deferred queue. Record `deferred_at` timestamp.
|
|
1404
|
+
3. Notify: "Manual verification needed: <what to check and how>"
|
|
1405
|
+
4. Continue all other criteria
|
|
1406
|
+
5. When PM says `/pcp approve <module>/<id>`: mark complete, log intervention:
|
|
1407
|
+
```python
|
|
1408
|
+
_log_intervention(pcp_dir, {
|
|
1409
|
+
"type": "manual_approval",
|
|
1410
|
+
"module": module, "criterion_id": criterion_id,
|
|
1411
|
+
"criterion_description": description,
|
|
1412
|
+
"deferred_at": deferred_at, "resolved_at": now,
|
|
1413
|
+
"time_to_resolve_minutes": elapsed,
|
|
1414
|
+
"feedback": None, "outcome": "approved"
|
|
1415
|
+
})
|
|
1416
|
+
```
|
|
1417
|
+
|
|
1418
|
+
**`check: visual` criterion:**
|
|
1419
|
+
1. Start dev server
|
|
1420
|
+
2. Try `dom_contains` fallback if selector is determinable from spec — if passes, auto-approve (no intervention log entry — fully automated)
|
|
1421
|
+
3. If no auto-check: take screenshot → `.pcp/previews/<module>-<id>-<ts>.png`
|
|
1422
|
+
4. Add to deferred queue. Record `deferred_at` timestamp.
|
|
1423
|
+
5. Notify with screenshot path: "Visual approval needed. Open: `open .pcp/previews/<file>`. Approve: `/pcp approve <module>/<id>`"
|
|
1424
|
+
6. Continue all other criteria
|
|
1425
|
+
7. On `/pcp approve`: log intervention. On `/pcp feedback "<text>"`: log with feedback field, rebuild.
|
|
1426
|
+
|
|
1427
|
+
**CI failing after 3 retries:**
|
|
1428
|
+
1. Mark criterion `blocked-ci` in deferred queue with `retry_at: <now + 30min>`
|
|
1429
|
+
2. Notify: "CI blocked after 3 retries. Last error: <summary>. Auto-retry in 30min."
|
|
1430
|
+
3. Continue building all criteria that don't depend on this one
|
|
1431
|
+
4. At retry time (if still in same session): attempt again silently
|
|
1432
|
+
5. If resolved: mark complete, notify success
|
|
1433
|
+
|
|
1434
|
+
**Missing env var / secret:**
|
|
1435
|
+
1. Detect which variable is missing from error log
|
|
1436
|
+
2. Check `.env.example` — if variable is documented there, note its purpose
|
|
1437
|
+
3. Add to deferred queue (type: blocked-secret)
|
|
1438
|
+
4. Write to `.pcp/pending_secrets.md`: exact variable name, where to set it (Railway dashboard / `.env`), why it's needed
|
|
1439
|
+
5. Notify: "Missing: `<VAR_NAME>` in <environment>. Set it and run `/pcp build --module <name>`"
|
|
1440
|
+
6. Continue all code-only criteria for this module (only deploy criteria are blocked by missing secrets)
|
|
1441
|
+
|
|
1442
|
+
**Merge conflict (auto-resolve):**
|
|
1443
|
+
1. Identify conflicting files
|
|
1444
|
+
2. For each conflict, apply resolution priority:
|
|
1445
|
+
- Spec files (`.pcp/`): NEVER auto-resolve — always escalate
|
|
1446
|
+
- Config files (package.json versions): take higher version
|
|
1447
|
+
- Code files: apply ADR priority — if ADR governs this file's domain, its rules win
|
|
1448
|
+
- Lock files (package-lock.json, Cargo.lock): regenerate (`npm install` / `cargo update`)
|
|
1449
|
+
3. If auto-resolve succeeds: commit resolution, continue
|
|
1450
|
+
4. If genuinely ambiguous (both branches modified same business logic): add to deferred queue, isolate module, continue others
|
|
1451
|
+
5. Notify only if couldn't auto-resolve
|
|
1452
|
+
|
|
1453
|
+
**Integration test regression after wave merge:**
|
|
1454
|
+
1. Identify culprit via bisect (revert merges one by one until green)
|
|
1455
|
+
2. Revert culprit module's merge: `git revert <merge-commit> --no-edit`
|
|
1456
|
+
3. Mark module as `blocked-regression` in deferred queue
|
|
1457
|
+
4. Continue next wave without the reverted module
|
|
1458
|
+
5. Notify: "<module> reverted — integration test failed. Investigating. Build continues."
|
|
1459
|
+
6. Diagnose the regression and write a fix plan to `.pcp/regression_<module>.md`
|
|
1460
|
+
|
|
1461
|
+
**Production smoke test failed:**
|
|
1462
|
+
1. Execute rollback immediately: `railway rollback` or `git revert + railway up`
|
|
1463
|
+
2. Notify immediately (this is a production incident — highest priority Slack message)
|
|
1464
|
+
3. Pause ALL deploy-related criteria
|
|
1465
|
+
4. Continue code-only criteria in other modules
|
|
1466
|
+
5. Write incident log to `.pcp/incidents/<ts>.md`
|
|
1467
|
+
6. Do NOT auto-redeploy to production — wait for PM to investigate
|
|
1468
|
+
|
|
1469
|
+
**Spec drift detected mid-build:**
|
|
1470
|
+
1. Re-read new spec
|
|
1471
|
+
2. Evaluate impact: does the change invalidate already-completed criteria?
|
|
1472
|
+
3. If backward-compatible (additive): adopt new spec, update draft ADR if needed, continue
|
|
1473
|
+
4. If breaking (removes or contradicts completed work): add to deferred queue, notify PM with diff
|
|
1474
|
+
5. Never silently build against an old spec
|
|
1475
|
+
|
|
1476
|
+
**Context window near limit:**
|
|
1477
|
+
This should never happen in normal operation — one agent per criterion means context never accumulates. If it does happen (agent was incorrectly briefed to loop through multiple criteria):
|
|
1478
|
+
1. Stop immediately at the current criterion boundary
|
|
1479
|
+
2. Commit whatever is staged with `wip(<module>/<id>): partial - context limit`
|
|
1480
|
+
3. Write handoff to `.pcp/.handoff.md`: "Resume: /pcp build --module <name> --from <criterion-id>"
|
|
1481
|
+
4. Notify: "Context limit — agent was running too many criteria. Resuming with corrected single-criterion model."
|
|
1482
|
+
Prevention: orchestrator must never brief an agent with "complete ALL criteria". Always "complete criterion <id> only".
|
|
1483
|
+
|
|
1484
|
+
### When PM returns (`/pcp` after being away)
|
|
1485
|
+
|
|
1486
|
+
Read: `.pcp/.build_progress` + `.pcp/deferred_queue.yaml` + `.pcp/pending_secrets.md`
|
|
1487
|
+
|
|
1488
|
+
Present:
|
|
1489
|
+
```
|
|
1490
|
+
Built while you were away:
|
|
1491
|
+
✓ <module> <n>/<total> criteria complete
|
|
1492
|
+
✓ <module> complete
|
|
1493
|
+
→ <module> <n>/<total> (CI retry pending at <time>)
|
|
1494
|
+
|
|
1495
|
+
Waiting for you (<n> items):
|
|
1496
|
+
1. <module>/<id> — <type>: <one-line what's needed>
|
|
1497
|
+
→ /pcp approve <module>/<id>
|
|
1498
|
+
|
|
1499
|
+
2. <module>/<id> — architecture decision
|
|
1500
|
+
Draft ADR at .pcp/kb/adr/ADR-<N>-DRAFT.md (recommendation: <decision>)
|
|
1501
|
+
→ /pcp accept-adr <N> OR /pcp override-adr <N> "<your decision>"
|
|
1502
|
+
|
|
1503
|
+
3. <module>/<id> — visual approval
|
|
1504
|
+
Screenshot: .pcp/previews/<file> (open automatically)
|
|
1505
|
+
→ /pcp approve <module>/<id>
|
|
1506
|
+
|
|
1507
|
+
Cannot unblock autonomously:
|
|
1508
|
+
⚠ <VAR_NAME> missing — set in Railway dashboard, see .pcp/pending_secrets.md
|
|
1509
|
+
⚠ <External approval> — see .pcp/pending_actions.md
|
|
1510
|
+
|
|
1511
|
+
Overall: <X>/<Y> criteria complete (<Z>%). Continue? (/pcp build --all resumes)
|
|
1512
|
+
```
|
|
1513
|
+
|
|
1514
|
+
### True stops (only 2 categories)
|
|
1515
|
+
|
|
1516
|
+
| Category | Why PCP cannot self-resolve |
|
|
1517
|
+
|---|---|
|
|
1518
|
+
| Production credentials (API keys, DB passwords, OAuth secrets) | Must never generate or guess real credentials. Security boundary. |
|
|
1519
|
+
| Regulatory / external approval (OAuth app review, app store approval, entitlement from vendor) | External to PCP's control. Cannot be automated. |
|
|
1520
|
+
|
|
1521
|
+
For both: write to `.pcp/pending_actions.md` with exact instructions. Build everything that doesn't depend on them.
|
|
1522
|
+
|
|
1523
|
+
---
|
|
1524
|
+
|
|
1525
|
+
## ESCALATION RULES
|
|
1526
|
+
|
|
1527
|
+
**No blocking prompts. Build never stops for deferrable input.**
|
|
1528
|
+
|
|
1529
|
+
| Situation | PCP Action |
|
|
1530
|
+
|---|---|
|
|
1531
|
+
| Architecture decision not in ADRs | Self-decide → write DRAFT ADR → continue → notify async |
|
|
1532
|
+
| `check: manual` | Defer → notify → continue |
|
|
1533
|
+
| `check: visual` | Try dom_contains fallback → screenshot → defer → notify → continue |
|
|
1534
|
+
| CI fail after 3 retries | Defer with 30min retry → notify → continue other criteria |
|
|
1535
|
+
| Missing env var / secret | Defer deploy criteria only → write pending_secrets.md → notify → continue code |
|
|
1536
|
+
| Merge conflict (resolvable via ADR priority) | Auto-resolve → continue |
|
|
1537
|
+
| Merge conflict (ambiguous logic) | Isolate module → defer → notify → continue other modules |
|
|
1538
|
+
| Integration regression after merge | Auto-bisect → revert culprit → defer → notify → continue next wave |
|
|
1539
|
+
| Spec drift (backward-compatible) | Adopt new spec → continue |
|
|
1540
|
+
| Spec drift (breaking change to completed work) | Defer → notify with diff → continue unaffected modules |
|
|
1541
|
+
| Context near limit | Checkpoint → commit WIP → write handoff note → notify |
|
|
1542
|
+
| `pcp check` BLOCK after 2 self-fix attempts | Defer → notify with finding → continue unblocked criteria |
|
|
1543
|
+
| Spec file modified by agent | Never auto-resolve → defer immediately → notify |
|
|
1544
|
+
| Production smoke test failed | Rollback immediately → notify URGENT → pause deploy criteria → continue code-only |
|
|
1545
|
+
| **Missing production credentials** | TRUE STOP for that module's deploy — write to pending_actions.md, continue all else |
|
|
1546
|
+
| **External regulatory/vendor approval** | TRUE STOP for dependent criteria — write to pending_actions.md, continue all else |
|
|
1547
|
+
|
|
1548
|
+
Slack notification format (every deferred item):
|
|
1549
|
+
```
|
|
1550
|
+
⚠ PCP [<project>] <module>/<id>
|
|
1551
|
+
Type: <manual | visual | architecture-decision | blocked-ci | blocked-secret>
|
|
1552
|
+
Summary: <one sentence what happened>
|
|
1553
|
+
Your action: <exact command — /pcp approve, /pcp accept-adr, set env var, etc.>
|
|
1554
|
+
Build: continuing on <other modules/criteria>
|
|
1555
|
+
```
|
|
1556
|
+
|
|
1557
|
+
---
|
|
1558
|
+
|
|
1559
|
+
## MODULARITY PROTOCOL — NON-NEGOTIABLE
|
|
1560
|
+
|
|
1561
|
+
**Philosophy baked in at every layer.**
|
|
1562
|
+
|
|
1563
|
+
Vibe coders pivot. Features get dropped. Modules get added. The codebase must absorb this without surgery.
|
|
1564
|
+
|
|
1565
|
+
**The rule:** Every module is a guest in the codebase. It can be removed by deleting its directory. It can be added without touching existing modules. Nothing else should know about it except the application registry.
|
|
1566
|
+
|
|
1567
|
+
---
|
|
1568
|
+
|
|
1569
|
+
### Layer 1: Application structure (enforced on every project)
|
|
1570
|
+
|
|
1571
|
+
When generating the project scaffold, PCP always creates this structure regardless of language:
|
|
1572
|
+
|
|
1573
|
+
```
|
|
1574
|
+
src/
|
|
1575
|
+
main.<ext> ← orchestrator: loads modules from registry. knows nothing about modules.
|
|
1576
|
+
interfaces/ ← typed contracts. the ONLY thing modules are allowed to share.
|
|
1577
|
+
I<Module>.ts/.py ← interface per module (what it exposes to the world)
|
|
1578
|
+
modules/
|
|
1579
|
+
<module>/
|
|
1580
|
+
index.<ext> ← public surface: exports only what interface requires
|
|
1581
|
+
src/ ← implementation: never imported by any other module
|
|
1582
|
+
tests/
|
|
1583
|
+
feature_flag.env ← FEATURE_<MODULE>_ENABLED=false (default off)
|
|
1584
|
+
core/ ← infrastructure only: logging, config, db connection
|
|
1585
|
+
← core is NOT a module. modules may depend on core. never on each other.
|
|
1586
|
+
```
|
|
1587
|
+
|
|
1588
|
+
Language-specific patterns:
|
|
1589
|
+
- **TypeScript/JS**: modules register via `app.register(AuthModule)` — no `import { doAuth } from '../auth/src/service'`
|
|
1590
|
+
- **Python**: modules register in `app.py` via plugin pattern — no cross-module `from auth.service import ...`
|
|
1591
|
+
- **Rust**: modules are crate features — `[features] auth = []`
|
|
1592
|
+
- **Swift**: modules are Swift packages or targets — no target-to-target source imports
|
|
1593
|
+
|
|
1594
|
+
---
|
|
1595
|
+
|
|
1596
|
+
### Layer 2: Auto-generated modularity rules in `ci_rules.yaml`
|
|
1597
|
+
|
|
1598
|
+
PCP adds these to EVERY project's `ci_rules.yaml` during `/pcp new`:
|
|
1599
|
+
|
|
1600
|
+
```yaml
|
|
1601
|
+
# Modularity rules — generated by PCP, do not remove
|
|
1602
|
+
- id: MOD_001
|
|
1603
|
+
check: ast_pattern
|
|
1604
|
+
description: "No direct cross-module implementation imports"
|
|
1605
|
+
pattern: "from \\.\\.(/\\.\\.)?/[^/]+/src/" # catches ../../other-module/src/
|
|
1606
|
+
severity: hard_block
|
|
1607
|
+
message: "Modules must communicate through interfaces/, not by importing each other's src/"
|
|
1608
|
+
|
|
1609
|
+
- id: MOD_002
|
|
1610
|
+
check: ast_pattern
|
|
1611
|
+
description: "No hardcoded module names in application orchestrator"
|
|
1612
|
+
pattern: "require\\(['\"]\\.\\./" # direct require of module internals
|
|
1613
|
+
severity: hard_block
|
|
1614
|
+
|
|
1615
|
+
- id: MOD_003
|
|
1616
|
+
check: file_exists
|
|
1617
|
+
description: "Every module must have a public interface file"
|
|
1618
|
+
target: "src/interfaces/I{module}.ts" # checked per module
|
|
1619
|
+
severity: hard_block
|
|
1620
|
+
|
|
1621
|
+
- id: MOD_004
|
|
1622
|
+
check: ast_pattern
|
|
1623
|
+
description: "No shared global mutable state between modules"
|
|
1624
|
+
pattern: "global\\s+\\w+\\s*=" # language-specific, adapt per stack
|
|
1625
|
+
severity: hard_block
|
|
1626
|
+
|
|
1627
|
+
- id: MOD_005
|
|
1628
|
+
check: file_exists
|
|
1629
|
+
description: "Every module must have a feature flag"
|
|
1630
|
+
target: "src/modules/{module}/feature_flag.env"
|
|
1631
|
+
severity: hard_block
|
|
1632
|
+
```
|
|
1633
|
+
|
|
1634
|
+
---
|
|
1635
|
+
|
|
1636
|
+
### Layer 3: Auto-generated modularity BLOCK rules in `architect_persona.md`
|
|
1637
|
+
|
|
1638
|
+
PCP adds these to EVERY project's architect_persona.md:
|
|
1639
|
+
|
|
1640
|
+
```markdown
|
|
1641
|
+
## Modularity Invariants (always enforced, not project-specific)
|
|
1642
|
+
|
|
1643
|
+
BLOCK:
|
|
1644
|
+
- Any direct import of another module's `src/` directory
|
|
1645
|
+
- Any module that imports from another module's `index` except through the interface type
|
|
1646
|
+
- Any global mutable state shared between modules (singleton patterns, global stores)
|
|
1647
|
+
- Any module that directly instantiates another module (use dependency injection via registry)
|
|
1648
|
+
- Feature code shipped without a feature flag (all new modules default off)
|
|
1649
|
+
- A module whose tests require another module to be running (tests must be isolated)
|
|
1650
|
+
|
|
1651
|
+
WARN:
|
|
1652
|
+
- A module with more than 3 dependencies in dependency_map.md (God module risk)
|
|
1653
|
+
- A module that owns more than 3 database tables (God module risk)
|
|
1654
|
+
- A module that directly modifies another module's database tables
|
|
1655
|
+
|
|
1656
|
+
LENIENT:
|
|
1657
|
+
- Module-internal design patterns (each module can use its own patterns)
|
|
1658
|
+
- Module test framework choices (each module can use its preferred test tool)
|
|
1659
|
+
```
|
|
1660
|
+
|
|
1661
|
+
---
|
|
1662
|
+
|
|
1663
|
+
### Layer 4: Mandatory acceptance criteria on every module
|
|
1664
|
+
|
|
1665
|
+
PCP auto-adds these criteria to EVERY module's `acceptance.yaml`:
|
|
1666
|
+
|
|
1667
|
+
```yaml
|
|
1668
|
+
# Modularity criteria — added by PCP to every module, do not remove
|
|
1669
|
+
- id: MOD_A001
|
|
1670
|
+
description: "Module can be dropped without breaking other modules"
|
|
1671
|
+
check: test_passes
|
|
1672
|
+
test: "tests/modularity/test_drop_<module>.sh"
|
|
1673
|
+
notes: "Delete or disable module, run full test suite of remaining modules — must pass"
|
|
1674
|
+
|
|
1675
|
+
- id: MOD_A002
|
|
1676
|
+
description: "Module registers through application interface, not via direct import"
|
|
1677
|
+
check: ast_pattern
|
|
1678
|
+
target: "src/main.<ext>"
|
|
1679
|
+
pattern: "register\\(<Module>\\)|plugin\\(<Module>\\)|mount\\(<Module>\\)"
|
|
1680
|
+
|
|
1681
|
+
- id: MOD_A003
|
|
1682
|
+
description: "Module has a feature flag, default off"
|
|
1683
|
+
check: file_exists
|
|
1684
|
+
target: "src/modules/<module>/feature_flag.env"
|
|
1685
|
+
|
|
1686
|
+
- id: MOD_A004
|
|
1687
|
+
description: "Module interface file exists and is typed"
|
|
1688
|
+
check: file_exists
|
|
1689
|
+
target: "src/interfaces/I<Module>.<ext>"
|
|
1690
|
+
```
|
|
1691
|
+
|
|
1692
|
+
---
|
|
1693
|
+
|
|
1694
|
+
### Layer 5: Drop test in every build cycle
|
|
1695
|
+
|
|
1696
|
+
After completing ALL criteria for a module, run the drop test before marking the module done:
|
|
1697
|
+
|
|
1698
|
+
```bash
|
|
1699
|
+
# Drop test — verify module can be removed without breaking others
|
|
1700
|
+
echo "Running drop test for <module>..."
|
|
1701
|
+
|
|
1702
|
+
# Temporarily disable module
|
|
1703
|
+
git stash -- src/modules/<module>/
|
|
1704
|
+
|
|
1705
|
+
# Run full test suite of ALL OTHER modules
|
|
1706
|
+
pytest --ignore=src/modules/<module>/ -q 2>/dev/null || \
|
|
1707
|
+
npm test -- --testPathIgnorePatterns="modules/<module>" 2>/dev/null || \
|
|
1708
|
+
cargo test --features "$(grep -v '<module>' Cargo.toml | grep 'features' | ...)" 2>/dev/null
|
|
1709
|
+
|
|
1710
|
+
DROP_EXIT=$?
|
|
1711
|
+
|
|
1712
|
+
# Restore module
|
|
1713
|
+
git stash pop
|
|
1714
|
+
|
|
1715
|
+
if [ $DROP_EXIT -ne 0 ]; then
|
|
1716
|
+
echo "DROP TEST FAILED: removing <module> breaks other modules"
|
|
1717
|
+
echo "Cross-module coupling detected — must fix before marking module complete"
|
|
1718
|
+
# Identify which tests failed → locate the coupling → fix it
|
|
1719
|
+
fi
|
|
1720
|
+
```
|
|
1721
|
+
|
|
1722
|
+
Drop test failure = cross-module coupling found. Fix the coupling before marking the module complete. Add the coupling fix to the current criterion's implementation.
|
|
1723
|
+
|
|
1724
|
+
---
|
|
1725
|
+
|
|
1726
|
+
### Layer 6: `pcp validate-strategy` extended — coupling check
|
|
1727
|
+
|
|
1728
|
+
In addition to coverage, `pcp validate-strategy` now also checks:
|
|
1729
|
+
|
|
1730
|
+
```
|
|
1731
|
+
Coupling analysis:
|
|
1732
|
+
✓ auth → no implementation imports from other modules
|
|
1733
|
+
✓ payment → no implementation imports from other modules
|
|
1734
|
+
✗ dashboard → imports directly from auth/src/session.ts (COUPLING)
|
|
1735
|
+
✗ billing → bidirectional dependency with payment (CIRCULAR)
|
|
1736
|
+
|
|
1737
|
+
Coupling violations: 2
|
|
1738
|
+
→ Fix: dashboard must use IAuthModule interface, not auth/src directly
|
|
1739
|
+
→ Fix: billing/payment circular dep — extract shared type to interfaces/
|
|
1740
|
+
```
|
|
1741
|
+
|
|
1742
|
+
If coupling violations exist: cannot proceed to next wave. Fix coupling first.
|
|
1743
|
+
|
|
1744
|
+
---
|
|
1745
|
+
|
|
1746
|
+
### Layer 7: Module add/drop commands
|
|
1747
|
+
|
|
1748
|
+
```
|
|
1749
|
+
/pcp add-module <name> "<one-line purpose>"
|
|
1750
|
+
→ generates spec.yaml, acceptance.yaml, interface file, feature flag, test scaffold
|
|
1751
|
+
→ adds to dependency_map.md with no deps by default
|
|
1752
|
+
→ runs pcp validate-strategy to check coverage impact
|
|
1753
|
+
→ ready to /pcp build --module <name>
|
|
1754
|
+
|
|
1755
|
+
/pcp drop-module <name>
|
|
1756
|
+
→ runs drop test to verify safe removal
|
|
1757
|
+
→ if safe: removes module directory, updates decomposition.md, updates dependency_map.md
|
|
1758
|
+
→ runs pcp validate-strategy to check coverage impact of removal
|
|
1759
|
+
→ removes from acceptance tracking
|
|
1760
|
+
→ if unsafe: shows which modules depend on it, recommends decoupling steps first
|
|
1761
|
+
|
|
1762
|
+
/pcp pivot "<new direction>"
|
|
1763
|
+
→ runs pcp validate-strategy with new context
|
|
1764
|
+
→ shows: which modules are still relevant, which can be dropped, what new modules are needed
|
|
1765
|
+
→ generates add/drop plan for PM to approve
|
|
1766
|
+
→ executes plan after approval
|
|
1767
|
+
```
|
|
1768
|
+
|
|
1769
|
+
---
|
|
1770
|
+
|
|
1771
|
+
### Why this matters for vibe coders
|
|
1772
|
+
|
|
1773
|
+
Without this:
|
|
1774
|
+
```
|
|
1775
|
+
PM: "actually let's drop payments for now and add a referral system"
|
|
1776
|
+
Dev: *3 days of refactoring* "ok done"
|
|
1777
|
+
```
|
|
1778
|
+
|
|
1779
|
+
With PCP modularity protocol:
|
|
1780
|
+
```
|
|
1781
|
+
PM: "actually let's drop payments for now and add a referral system"
|
|
1782
|
+
PCP: /pcp drop-module payment → drop test passes → removed
|
|
1783
|
+
/pcp add-module referral "track and reward user referrals" → scaffold generated
|
|
1784
|
+
/pcp build --module referral → builds autonomously
|
|
1785
|
+
Time: hours, not days. Zero surgery on existing code.
|
|
1786
|
+
```
|
|
1787
|
+
|
|
1788
|
+
---
|
|
1789
|
+
|
|
1790
|
+
## CONTEXT DRIFT PREVENTION
|
|
1791
|
+
|
|
1792
|
+
Before writing code for any criterion:
|
|
1793
|
+
1. Re-read spec.yaml constraints
|
|
1794
|
+
2. Confirm tech stack vs architecture.md
|
|
1795
|
+
3. Internalize architect_persona.md BLOCK rules — know the rules before writing a line
|
|
1796
|
+
4. Check if files touch any ADR boundary — if yes, follow that ADR's pattern exactly
|
|
1797
|
+
5. Never make a new architectural decision without first checking ADRs + persona
|
|
1798
|
+
|
|
1799
|
+
If a decision is genuinely new: self-decide using best practices, document as DRAFT ADR, continue building. Do not stop.
|
|
1800
|
+
|
|
1801
|
+
---
|
|
1802
|
+
|
|
1803
|
+
## HONESTY RULES
|
|
1804
|
+
|
|
1805
|
+
- Never mark `status: complete` without every Completion Definition item passing
|
|
1806
|
+
- Never commit with `pcp check` BLOCK
|
|
1807
|
+
- Never skip regression suite
|
|
1808
|
+
- Never skip secret scan — no credentials ever reach git
|
|
1809
|
+
- Never invent CI or Railway status — read from CLI output
|
|
1810
|
+
- Never spawn more agents than modules needing work
|
|
1811
|
+
- Never silently skip a deferred item — always write to deferred_queue.yaml + notify
|
|
1812
|
+
- Never auto-resolve a spec file conflict unattended — specs are human-approved; defer to the PM, then apply via `pcp correct-objective` / `pcp pm` / `pcp amend` (diff shown, PM approves, then written)
|
|
1813
|
+
- Never generate or guess production credentials — security boundary, always stop and notify
|
|
1814
|
+
- Never redeploy to production after rollback without PM confirmation
|
|
1815
|
+
- If Playwright not installed and `check: visual` required: notify and defer rather than skip
|
|
1816
|
+
- If `pcp` CLI not installed: fetch the latest wheel from GitHub Releases and `pip install` it (see the top-level `SKILL.md`'s Install step — there is no PyPI package yet)
|
|
1817
|
+
- Draft ADRs are labelled DRAFT — never present them as accepted decisions
|
|
1818
|
+
- If a module's build is completely blocked (all criteria depend on a true-stop item): say so clearly in status, move to next module
|
|
1819
|
+
- If git worktree fails: fall back to sequential build on main branch, tell PM
|
|
1820
|
+
|
|
1821
|
+
---
|
|
1822
|
+
|
|
1823
|
+
## INTERVENTION LOGGING
|
|
1824
|
+
|
|
1825
|
+
Every human interaction is logged to `.pcp/intervention_log.yaml`. This is the learning signal. Metadata only — no code, no secrets, no project content.
|
|
1826
|
+
|
|
1827
|
+
### `_log_intervention(pcp_dir, entry)` — called on every human touchpoint
|
|
1828
|
+
|
|
1829
|
+
```python
|
|
1830
|
+
import yaml
|
|
1831
|
+
from datetime import datetime, timezone
|
|
1832
|
+
from pathlib import Path
|
|
1833
|
+
|
|
1834
|
+
def _log_intervention(pcp_dir: Path, entry: dict) -> None:
|
|
1835
|
+
log_path = pcp_dir / "intervention_log.yaml"
|
|
1836
|
+
existing = []
|
|
1837
|
+
if log_path.exists():
|
|
1838
|
+
data = yaml.safe_load(log_path.read_text()) or {}
|
|
1839
|
+
existing = data.get("interventions", [])
|
|
1840
|
+
entry["logged_at"] = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1841
|
+
existing.append(entry)
|
|
1842
|
+
log_path.write_text(yaml.dump({"interventions": existing}, default_flow_style=False))
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
### Log on every human touchpoint
|
|
1846
|
+
|
|
1847
|
+
| Trigger | type field | Extra fields |
|
|
1848
|
+
|---|---|---|
|
|
1849
|
+
| `/pcp approve <module>/<id>` (manual) | `manual_approval` | module, criterion_id, criterion_description, time_to_resolve_minutes, outcome |
|
|
1850
|
+
| `/pcp approve <module>/<id>` (visual) | `visual_approval` | module, criterion_id, screenshot_path, time_to_resolve_minutes, outcome |
|
|
1851
|
+
| `/pcp feedback <module>/<id> "<text>"` | `visual_feedback` | module, criterion_id, feedback (text), retest_triggered: true |
|
|
1852
|
+
| `/pcp accept-adr <id>` | `adr_accepted` | adr_id, topic, time_to_resolve_minutes |
|
|
1853
|
+
| `/pcp override-adr <id> "<decision>"` | `adr_overridden` | adr_id, topic, original_decision, override_decision, time_to_resolve_minutes |
|
|
1854
|
+
| Escalation resolved (PM replied) | `escalation_resolved` | reason, module, deferred_duration_minutes, resolution |
|
|
1855
|
+
| UAT retest cycle completes | `uat_retest` | scenario, retest_count, root_cause, time_to_pass_minutes |
|
|
1856
|
+
| Deploy gate: migration approved | `migration_approved` | migration_file, time_to_resolve_minutes |
|
|
1857
|
+
| Deploy gate: rollout % decided | `rollout_decision` | module, rollout_pct, reason |
|
|
1858
|
+
|
|
1859
|
+
### Schema — intervention_log.yaml
|
|
1860
|
+
|
|
1861
|
+
```yaml
|
|
1862
|
+
interventions:
|
|
1863
|
+
- logged_at: "2026-06-29T14:23:00Z"
|
|
1864
|
+
type: visual_approval
|
|
1865
|
+
module: payments
|
|
1866
|
+
criterion_id: PAY_005
|
|
1867
|
+
criterion_description: "Payment confirmation screen looks correct"
|
|
1868
|
+
time_to_resolve_minutes: 3
|
|
1869
|
+
outcome: approved
|
|
1870
|
+
|
|
1871
|
+
- logged_at: "2026-06-29T15:10:00Z"
|
|
1872
|
+
type: visual_feedback
|
|
1873
|
+
module: auth
|
|
1874
|
+
criterion_id: AUTH_003
|
|
1875
|
+
feedback: "Button is too small on mobile"
|
|
1876
|
+
retest_triggered: true
|
|
1877
|
+
|
|
1878
|
+
- logged_at: "2026-06-29T16:00:00Z"
|
|
1879
|
+
type: adr_overridden
|
|
1880
|
+
adr_id: ADR-003
|
|
1881
|
+
topic: database_choice
|
|
1882
|
+
original_decision: "SQLite for simplicity"
|
|
1883
|
+
override_decision: "PostgreSQL — need concurrent writes"
|
|
1884
|
+
time_to_resolve_minutes: 5
|
|
1885
|
+
```
|
|
1886
|
+
|
|
1887
|
+
### Daily aggregation (runs via cron)
|
|
1888
|
+
|
|
1889
|
+
Cron reads all intervention logs across all PCP projects, aggregates patterns, sends to Slack, writes to `~/.pcp/global_learning.yaml`:
|
|
1890
|
+
|
|
1891
|
+
```
|
|
1892
|
+
Most frequent manual criteria: auth flows (OAuth) — 4/5 projects
|
|
1893
|
+
Longest to resolve: missing API keys — avg 127 min deferred
|
|
1894
|
+
Most UAT retests: payment edge cases — avg 2.8 retests/scenario
|
|
1895
|
+
Most overridden ADRs: database choice — 3/5 overridden
|
|
1896
|
+
→ Next automation targets: OAuth verification, pre-flight API key check, payment edge case criteria templates
|
|
1897
|
+
```
|