@pareto4/pp-core 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/agents/pp-code-fixer.md +9 -5
- package/agents/pp-code-reviewer.md +1 -0
- package/agents/pp-codebase-mapper.md +3 -0
- package/agents/pp-debugger.md +3 -2
- package/agents/pp-executor.md +16 -7
- package/agents/pp-integration-checker.md +1 -0
- package/agents/pp-phase-researcher.md +3 -1
- package/agents/pp-plan-checker.md +3 -0
- package/agents/pp-planner.md +5 -1
- package/agents/pp-research-synthesizer.md +1 -1
- package/agents/pp-ui-auditor.md +1 -0
- package/agents/pp-ui-researcher.md +3 -1
- package/agents/pp-verifier.md +10 -2
- package/commands/pp-add-tests.md +1 -1
- package/commands/pp-debug.md +1 -0
- package/commands/pp-init.md +4 -3
- package/commands/pp-quick.md +3 -0
- package/commands/pp-review-backlog.md +2 -1
- package/gemini-extension.json +1 -1
- package/package.json +1 -1
- package/pp-core/bin/lib/capability-registry.cjs +2 -2
- package/pp-core/bin/lib/check-command-router.cjs +60 -20
- package/pp-core/bin/lib/commands.cjs +79 -4
- package/pp-core/bin/lib/compose-bindings.cjs +97 -14
- package/pp-core/bin/lib/discovery-rollup.cjs +9 -6
- package/pp-core/bin/lib/discovery-screens.cjs +20 -2
- package/pp-core/bin/lib/discovery-sweep.cjs +12 -3
- package/pp-core/bin/lib/discovery-vault-check.cjs +37 -3
- package/pp-core/bin/lib/init-contract.cjs +48 -7
- package/pp-core/bin/lib/vault-coordinates.cjs +1 -1
- package/pp-core/bin/lib/vault-index.cjs +18 -5
- package/pp-core/bin/lib/vault-update.cjs +4 -0
- package/pp-core/bin/lib/vault.cjs +2 -2
- package/pp-core/bin/pp-tools.cjs +10 -4
- package/pp-core/org-profile.json +1 -1
- package/pp-core/references/autonomous-best-effort.md +3 -2
- package/pp-core/references/autonomous-lifecycle.md +1 -0
- package/pp-core/references/autonomous-smart-discuss.md +5 -1
- package/pp-core/references/discovery-id-conventions.md +35 -2
- package/pp-core/references/execute-mvp-tdd.md +3 -3
- package/pp-core/references/git-integration.md +57 -33
- package/pp-core/references/git-planning-commit.md +31 -9
- package/pp-core/references/phase-argument-parsing.md +1 -0
- package/pp-core/references/planner-gap-closure.md +1 -0
- package/pp-core/references/planner-revision.md +3 -1
- package/pp-core/references/planner-tdd.md +4 -4
- package/pp-core/references/planning-config.md +3 -3
- package/pp-core/references/shell-portability.md +28 -0
- package/pp-core/references/tdd.md +20 -16
- package/pp-core/references/worktree-path-safety.md +1 -0
- package/pp-core/templates/milestone.md +5 -5
- package/pp-core/workflows/add-backlog.md +1 -1
- package/pp-core/workflows/add-tests.md +5 -2
- package/pp-core/workflows/add-todo.md +3 -1
- package/pp-core/workflows/ai-integration-phase.md +2 -1
- package/pp-core/workflows/audit-fix.md +4 -3
- package/pp-core/workflows/audit-milestone.md +1 -0
- package/pp-core/workflows/autonomous.md +6 -0
- package/pp-core/workflows/check-todos.md +2 -1
- package/pp-core/workflows/cleanup.md +3 -1
- package/pp-core/workflows/code-review-fix.md +12 -5
- package/pp-core/workflows/code-review.md +15 -1
- package/pp-core/workflows/complete-milestone.md +13 -3
- package/pp-core/workflows/debug.md +2 -0
- package/pp-core/workflows/diagnose-issues.md +1 -1
- package/pp-core/workflows/discovery-sprint/session-13-gates.md +1 -1
- package/pp-core/workflows/discovery-sprint/sessions/01-kick-off.md +51 -7
- package/pp-core/workflows/discovery-sprint/sessions/02-as-is.md +5 -3
- package/pp-core/workflows/discovery-sprint/sessions/03-to-be.md +10 -8
- package/pp-core/workflows/discovery-sprint/sessions/04-ux-design.md +11 -2
- package/pp-core/workflows/discovery-sprint/sessions/05-architecture.md +5 -5
- package/pp-core/workflows/discovery-sprint/sessions/06-security.md +3 -3
- package/pp-core/workflows/discovery-sprint/sessions/07-identity.md +2 -2
- package/pp-core/workflows/discovery-sprint/sessions/08-integration.md +14 -2
- package/pp-core/workflows/discovery-sprint/sessions/09-qa.md +2 -2
- package/pp-core/workflows/discovery-sprint/sessions/10-infrastructure.md +10 -10
- package/pp-core/workflows/discovery-sprint/sessions/11-roadmap.md +11 -8
- package/pp-core/workflows/discovery-sprint/sessions/12-delivery-model.md +21 -18
- package/pp-core/workflows/discovery-sprint/sessions/13-wrap-up.md +5 -3
- package/pp-core/workflows/discovery-sprint/templates/context-template.md +9 -3
- package/pp-core/workflows/discovery-sprint/vault-context.md +14 -3
- package/pp-core/workflows/discovery-sprint.md +11 -8
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -0
- package/pp-core/workflows/discuss-phase-assumptions.md +5 -3
- package/pp-core/workflows/discuss-phase.md +8 -3
- package/pp-core/workflows/docs-update.md +1 -0
- package/pp-core/workflows/eval-review.md +2 -1
- package/pp-core/workflows/execute-phase/steps/check-query-gate.md +7 -6
- package/pp-core/workflows/execute-phase/steps/cross-ai-delegation.md +1 -0
- package/pp-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -0
- package/pp-core/workflows/execute-phase/steps/vault-fetch.md +12 -0
- package/pp-core/workflows/execute-phase.md +19 -7
- package/pp-core/workflows/execute-plan.md +18 -7
- package/pp-core/workflows/explore.md +1 -1
- package/pp-core/workflows/forensics.md +4 -2
- package/pp-core/workflows/health.md +1 -0
- package/pp-core/workflows/import.md +2 -1
- package/pp-core/workflows/ingest-docs.md +1 -0
- package/pp-core/workflows/init/steps/client-context-capture.md +1 -0
- package/pp-core/workflows/init/steps/github-provisioning.md +145 -0
- package/pp-core/workflows/init/steps/gitlab-provisioning.md +18 -4
- package/pp-core/workflows/init/steps/reachability-outcome.md +3 -1
- package/pp-core/workflows/init.md +56 -66
- package/pp-core/workflows/map-codebase.md +5 -1
- package/pp-core/workflows/milestone-summary.md +2 -1
- package/pp-core/workflows/mvp-phase.md +1 -0
- package/pp-core/workflows/new-milestone.md +9 -5
- package/pp-core/workflows/new-project.md +9 -8
- package/pp-core/workflows/new-workspace.md +1 -0
- package/pp-core/workflows/next.md +2 -1
- package/pp-core/workflows/pause-work.md +6 -4
- package/pp-core/workflows/plan-milestone-gaps.md +3 -1
- package/pp-core/workflows/plan-phase/steps/chunked-planning.md +1 -1
- package/pp-core/workflows/plan-phase.md +21 -6
- package/pp-core/workflows/plan-review-convergence.md +2 -0
- package/pp-core/workflows/plant-seed.md +4 -2
- package/pp-core/workflows/pr-branch.md +2 -0
- package/pp-core/workflows/progress.md +5 -0
- package/pp-core/workflows/quick.md +11 -4
- package/pp-core/workflows/reapply-patches.md +3 -0
- package/pp-core/workflows/remove-phase.md +1 -1
- package/pp-core/workflows/resume-project.md +3 -0
- package/pp-core/workflows/review.md +7 -1
- package/pp-core/workflows/secure-phase.md +2 -1
- package/pp-core/workflows/session-report.md +1 -0
- package/pp-core/workflows/ship.md +8 -1
- package/pp-core/workflows/sketch-wrap-up.md +1 -1
- package/pp-core/workflows/sketch.md +3 -2
- package/pp-core/workflows/spec-phase.md +2 -1
- package/pp-core/workflows/spike-wrap-up.md +1 -1
- package/pp-core/workflows/spike.md +4 -3
- package/pp-core/workflows/sync-skills.md +5 -2
- package/pp-core/workflows/thread.md +3 -2
- package/pp-core/workflows/transition.md +6 -0
- package/pp-core/workflows/ui-phase.md +3 -1
- package/pp-core/workflows/ui-review.md +2 -1
- package/pp-core/workflows/ultraplan-phase.md +1 -0
- package/pp-core/workflows/undo.md +14 -10
- package/pp-core/workflows/update.md +5 -0
- package/pp-core/workflows/validate-phase.md +6 -3
- package/pp-core/workflows/vault-review.md +2 -1
- package/pp-core/workflows/vault-write-back.md +5 -0
- package/pp-core/workflows/verify-phase.md +5 -0
- package/pp-core/workflows/verify-work.md +8 -1
- package/scripts/prompt-injection-scan.sh +1 -1
- package/scripts/sync-runtime-launcher.cjs +115 -5
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Gated GitHub provisioning — H-2..H-5 (D-05, D-09, D-10, D-11, D-12, D-13)
|
|
2
|
+
|
|
3
|
+
Lazily Read by `pp-core/workflows/init.md` § "Gated GitLab/GitHub provisioning" — Step 3's
|
|
4
|
+
other sub-path — and ONLY when BOTH of that section's gates hold: `.platform-bindings.json`
|
|
5
|
+
is present AND `resolveProvider` answers `github`. Both gates, G-1's one-source-per-value
|
|
6
|
+
config reads and the empty-group guard stay in `init.md`; this file is the GitHub create
|
|
7
|
+
recipe those guards protect, the counterpart of `gitlab-provisioning.md` step for step.
|
|
8
|
+
`$GROUP` (the GitHub organization), `$SLUG`, `$VIS`, `$DESC` and `$HOST` are the variables
|
|
9
|
+
G-1 has already set. Every `gh` call names the host through `GH_HOST`, so a GitHub
|
|
10
|
+
Enterprise host in the binding is honored and `github.com` is the default:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
GH_HOSTNAME="${HOST:-github.com}"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
#### H-2. Auth verify-only (D-11)
|
|
17
|
+
|
|
18
|
+
`/pp-init` **never establishes auth and never reads or writes the user's token.**
|
|
19
|
+
Secrets stay in the environment or OS keyring. Only verify that a session exists:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gh auth status --hostname "$GH_HOSTNAME" >/dev/null 2>&1 || {
|
|
23
|
+
echo "Not authenticated to GitHub ($GH_HOSTNAME). Run: gh auth login --hostname $GH_HOSTNAME"
|
|
24
|
+
exit 1
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If this check fails, stop immediately. Do not proceed to probe or create.
|
|
29
|
+
Never run `gh auth login` from within `/pp-init`.
|
|
30
|
+
|
|
31
|
+
#### H-3. Existence probe → adopt-if-exists (D-09, D-12)
|
|
32
|
+
|
|
33
|
+
Pre-check with a read-only probe before any create attempt. The create command is **not
|
|
34
|
+
idempotent** — re-running it against an existing repository fails, and a create whose
|
|
35
|
+
remote-add step fails has still created the repository. The probe avoids this landmine.
|
|
36
|
+
|
|
37
|
+
This is one self-contained block: it sets `PROJECT_EXISTS` and, on the exists path,
|
|
38
|
+
adopts (links the remote). It NEVER creates — create lives in its own H-4 block, gated
|
|
39
|
+
on `PROJECT_EXISTS=false`, so the create invocation can never fall inside the exists
|
|
40
|
+
branch.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
if GH_HOST="$GH_HOSTNAME" gh repo view "$GROUP/$SLUG" --json name >/dev/null 2>&1; then
|
|
44
|
+
# Repository already exists — ADOPT: link remote only, skip create (D-12)
|
|
45
|
+
PROJECT_EXISTS=true
|
|
46
|
+
echo "GitHub repository $GROUP/$SLUG already exists — adopting (linking remote, skipping create)."
|
|
47
|
+
# Match the remote scheme gh itself uses on the create path — honor the user's
|
|
48
|
+
# `gh config get git_protocol` (https default) instead of hardcoding SSH.
|
|
49
|
+
GIT_PROTO=$(gh config get git_protocol --host "$GH_HOSTNAME" 2>/dev/null || echo https)
|
|
50
|
+
if [ "$GIT_PROTO" = "ssh" ]; then
|
|
51
|
+
ADOPT_URL="git@$GH_HOSTNAME:$GROUP/$SLUG.git"
|
|
52
|
+
else
|
|
53
|
+
ADOPT_URL="https://$GH_HOSTNAME/$GROUP/$SLUG.git"
|
|
54
|
+
fi
|
|
55
|
+
git remote get-url origin >/dev/null 2>&1 || git remote add origin "$ADOPT_URL"
|
|
56
|
+
echo "Remote 'origin' → $ADOPT_URL"
|
|
57
|
+
else
|
|
58
|
+
# Repository not found — defer to H-4 for confirm-then-create
|
|
59
|
+
PROJECT_EXISTS=false
|
|
60
|
+
echo "GitHub repository $GROUP/$SLUG not found — will confirm before create."
|
|
61
|
+
fi
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
If the repository exists (`PROJECT_EXISTS=true`), the remote is already linked above
|
|
65
|
+
(skipping create entirely) — proceed straight to H-5 reconciliation. The adopt path does
|
|
66
|
+
not show a create confirmation — it only links the remote, which is a local-only
|
|
67
|
+
operation and can be undone with `git remote remove origin`.
|
|
68
|
+
|
|
69
|
+
#### H-4. Per-action confirmation, then create (D-05, D-09, D-10, SAFE-01)
|
|
70
|
+
|
|
71
|
+
**Only run this step when `PROJECT_EXISTS=false`** (the existence probe in H-3 found no
|
|
72
|
+
repository). If `PROJECT_EXISTS=true`, skip H-4 entirely — the repository was already
|
|
73
|
+
adopted — and go to H-5. When creating, **show the exact command and target before
|
|
74
|
+
firing**. The user must confirm explicitly. Nothing is batched. (D-05)
|
|
75
|
+
|
|
76
|
+
Present to the user (via `AskUserQuestion` or TEXT_MODE numbered list):
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
About to create GitHub repository:
|
|
80
|
+
Command: gh repo create "$GROUP/$SLUG"
|
|
81
|
+
Host: $GH_HOSTNAME
|
|
82
|
+
Organization: $GROUP
|
|
83
|
+
Repository: $GROUP/$SLUG
|
|
84
|
+
Visibility: private (enforced --private)
|
|
85
|
+
Requested: $VIS (from project.visibility — see note below)
|
|
86
|
+
Source: this directory (--source .) — nothing is pushed
|
|
87
|
+
Remote name: origin
|
|
88
|
+
|
|
89
|
+
Proceed? [y/N]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`$VIS` (from G-1) is surfaced in the confirmation so the requested visibility is never
|
|
93
|
+
silently ignored (WR-01). pp-core always **creates private** regardless; a non-private
|
|
94
|
+
repository must be a deliberate, separate post-create change.
|
|
95
|
+
|
|
96
|
+
Only after the user confirms — and only with `PROJECT_EXISTS=false` — run the locked
|
|
97
|
+
create recipe (D-09, D-10). This is a standalone block: it carries no `if`/`else`/`fi`, so
|
|
98
|
+
it can only ever run as the deliberate, confirmed not-exists path:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
GH_HOST="$GH_HOSTNAME" gh repo create "$GROUP/$SLUG" \
|
|
102
|
+
--description "$DESC" \
|
|
103
|
+
--private \
|
|
104
|
+
--source . \
|
|
105
|
+
--remote origin
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Visibility hard-default (D-10):** the create invocation always carries `--private` and
|
|
109
|
+
never `--public` or `--internal`. `VIS` is read in G-1 and **surfaced in the H-4
|
|
110
|
+
confirmation** (WR-01) so a `public`/`internal` request is visible to the user — but
|
|
111
|
+
pp-core still creates the repository **private**. Applying a non-private visibility is a
|
|
112
|
+
deliberate, separate post-create action (e.g. `gh repo edit --visibility …`) the operator
|
|
113
|
+
performs knowingly; `/pp-init` never auto-relaxes visibility. The create pushes nothing
|
|
114
|
+
(no `--push`): the first push is Step 3's own confirmed push, and the repository's default
|
|
115
|
+
branch is whatever that push sends.
|
|
116
|
+
|
|
117
|
+
#### H-5. Post-create reconciliation (D-09)
|
|
118
|
+
|
|
119
|
+
After the create (or after the adopt path), re-probe existence. A non-zero exit from
|
|
120
|
+
`gh repo create` does **not** mean the repository was not created — the repository is
|
|
121
|
+
created before `--remote origin` is added, so an `origin` that already exists fails the
|
|
122
|
+
command after the create succeeded. The `repo view` probe is the source of truth, not the
|
|
123
|
+
exit code:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
if GH_HOST="$GH_HOSTNAME" gh repo view "$GROUP/$SLUG" --json name >/dev/null 2>&1; then
|
|
127
|
+
echo "Reconciled: GitHub repository $GROUP/$SLUG confirmed."
|
|
128
|
+
else
|
|
129
|
+
echo "WARN: gh repo view $GROUP/$SLUG returned non-zero — verify the repository was created before continuing."
|
|
130
|
+
fi
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If reconciliation warns, do not auto-retry. Prompt the user to check GitHub directly
|
|
134
|
+
before proceeding to Step 4 (handoff routing).
|
|
135
|
+
|
|
136
|
+
**Security invariants — landmine guards:**
|
|
137
|
+
- Always use the organization-qualified `$GROUP/$SLUG` — never a bare slug (T-08-04, D-09).
|
|
138
|
+
A bare slug creates under the user's personal account, not the client organization.
|
|
139
|
+
G-1's empty-group guard is what actually enforces this before any create attempt.
|
|
140
|
+
- Always pass `--private` explicitly — never `--public` or `--internal` (T-08-05, D-10).
|
|
141
|
+
- Always confirm immediately before each outward action — nothing batched (T-08-06, D-05).
|
|
142
|
+
- Verify-only auth via `gh auth status` — never run `gh auth login`, never read or write
|
|
143
|
+
the user's token (T-08-08, D-11). Bindings hold only non-secret routing data.
|
|
144
|
+
- Always reconcile create with a `gh repo view` probe — a non-zero create exit does NOT
|
|
145
|
+
mean nothing happened (T-08-09, D-09).
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Gated GitLab provisioning — G-2..G-5 (D-05, D-09, D-10, D-11, D-12, D-13)
|
|
2
2
|
|
|
3
|
-
Lazily Read by `pp-core/workflows/init.md` § "Gated GitLab/
|
|
3
|
+
Lazily Read by `pp-core/workflows/init.md` § "Gated GitLab/GitHub provisioning" — Step 3's
|
|
4
4
|
other sub-path — and ONLY when BOTH of that section's gates hold: `.platform-bindings.json`
|
|
5
5
|
is present AND `resolveProvider` answers `gitlab`. Both gates, G-1's one-source-per-value
|
|
6
|
-
config reads
|
|
7
|
-
|
|
6
|
+
config reads and the empty-group guard stay in `init.md`; this file is the create recipe
|
|
7
|
+
those guards protect, and ends with its security invariants (moved here from `init.md`
|
|
8
|
+
beside `github-provisioning.md`'s own). Extracted from `init.md` (07-10) so the
|
|
8
9
|
always-loaded workflow stays under its DEFAULT size cap — relocation, not deletion: this
|
|
9
10
|
text is the authority for how `/pp-init` provisions a GitLab project, and `$GROUP`, `$SLUG`,
|
|
10
11
|
`$VIS`, `$DESC` and `$HOST` are the variables G-1 has already set.
|
|
@@ -26,7 +27,7 @@ Never run `glab auth login` from within `/pp-init`.
|
|
|
26
27
|
|
|
27
28
|
#### G-3. Existence probe → adopt-if-exists (D-09, D-12)
|
|
28
29
|
|
|
29
|
-
Pre-check with a read-only probe before any create attempt.
|
|
30
|
+
Pre-check with a read-only probe before any create attempt. The create command is **not
|
|
30
31
|
idempotent** — re-running it against an existing project errors but may still produce side
|
|
31
32
|
effects (the GitLab project is created before the remote-add step that then fails). The
|
|
32
33
|
probe avoids this landmine.
|
|
@@ -128,3 +129,16 @@ fi
|
|
|
128
129
|
|
|
129
130
|
If reconciliation warns, do not auto-retry. Prompt the user to check GitLab directly
|
|
130
131
|
before proceeding to Step 4 (handoff routing).
|
|
132
|
+
|
|
133
|
+
**Security invariants — landmine guards:**
|
|
134
|
+
- Always use the group-qualified `$GROUP/$SLUG` path — never a bare slug (T-08-04, D-09).
|
|
135
|
+
A bare slug creates under the user's personal namespace, not the client group. G-1's
|
|
136
|
+
empty-group guard in `init.md` is what actually enforces this before any create attempt.
|
|
137
|
+
- Always pass `--private` explicitly — never inherit `glab`'s default `--internal`
|
|
138
|
+
visibility (T-08-05, D-10). The `--internal` flag is what makes this dangerous; the
|
|
139
|
+
locked recipe must carry `--private` and must NOT emit `--internal`.
|
|
140
|
+
- Always confirm immediately before each outward action — nothing batched (T-08-06, D-05).
|
|
141
|
+
- Verify-only auth via `glab auth status` — never run `glab auth login`, never read
|
|
142
|
+
or write the user's token (T-08-08, D-11). Bindings hold only non-secret routing data.
|
|
143
|
+
- Always reconcile create with a `glab repo view` probe — a non-zero create exit does
|
|
144
|
+
NOT mean nothing happened (T-08-09, D-09).
|
|
@@ -16,7 +16,9 @@ the probe, or this step to the user.
|
|
|
16
16
|
named ref exists, and the client vault landed with a written `SUMMARY.md` (INIT-02).
|
|
17
17
|
Continue to Step 3. A `shared` line on that card is information about the fallback
|
|
18
18
|
tier (OD-5) — it is probed and shown, never required; an unreachable shared vault does
|
|
19
|
-
not fail the proof and is not in the recorded note.
|
|
19
|
+
not fail the proof and is not in the recorded note. A `platform` line reading
|
|
20
|
+
`unreachable (not required)` is information too: the platform repo is not required, and
|
|
21
|
+
the record states what was measured.
|
|
20
22
|
|
|
21
23
|
**(c) `PP_INIT_PROBE=unbound` or `PP_INIT_PROBE=fail`** — **STOP. `/pp-init` is not
|
|
22
24
|
complete (INIT-03).** Do not continue to Step 3, and do not re-run the probe hoping for a
|
|
@@ -11,8 +11,8 @@ D-13 (gate on .platform-bindings.json via the Phase 7 reader — Step 2 now comp
|
|
|
11
11
|
writes this file too, not only reads it, Phase 34 INIT-01).
|
|
12
12
|
|
|
13
13
|
Provisioning is provider-aware (Phase 34 D-11/D-13): `resolveProvider` is the sole
|
|
14
|
-
authority for which provisioning path runs. The gated
|
|
15
|
-
|
|
14
|
+
authority for which provisioning path runs. The gated create recipe runs only when it
|
|
15
|
+
resolves to `gitlab` (`glab`, G-2…G-5) or `github` (`gh`, H-2…H-5); under any other provider (`azure` today) it is
|
|
16
16
|
**skipped, not failed**, and the run falls through to this same generic remote-wiring
|
|
17
17
|
path so the user still leaves with a wired remote (Phase 34 INIT-03).
|
|
18
18
|
|
|
@@ -31,13 +31,6 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|
|
31
31
|
|
|
32
32
|
<process>
|
|
33
33
|
|
|
34
|
-
## Shim hoist
|
|
35
|
-
|
|
36
|
-
Resolve `pp_run` before any step — required for the platform-bindings preflight (D-13).
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
```
|
|
40
|
-
|
|
41
34
|
## Text mode (resolve before any prompt — WR-02)
|
|
42
35
|
|
|
43
36
|
Resolve `TEXT_MODE` here, **before Step 0**, because the Step 0 drift prompt is the first
|
|
@@ -91,13 +84,13 @@ step is skipped entirely.
|
|
|
91
84
|
|
|
92
85
|
## 1. Worktree-aware git init
|
|
93
86
|
|
|
94
|
-
Parse the init signals by running the query below. `init.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
87
|
+
Parse the init signals by running the query below. `init.ingest-docs` takes no argument
|
|
88
|
+
and emits the git-state signals this step needs (`has_git`, `in_nested_subdir`,
|
|
89
|
+
`git_worktree_root`) from the same #3491 reader `init.new-project` uses. Never use
|
|
90
|
+
`init.execute-phase ""`: it refuses an empty phase, and the `{}` fallback would disable the guard.
|
|
98
91
|
|
|
99
92
|
```bash
|
|
100
|
-
INIT=$(pp_run query init.
|
|
93
|
+
INIT=$(pp_run query init.ingest-docs 2>/dev/null || echo '{}')
|
|
101
94
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
102
95
|
```
|
|
103
96
|
|
|
@@ -177,7 +170,12 @@ its booleans, never in `$?`, and its `error` prose is never echoed (D-07-07). Th
|
|
|
177
170
|
runs only *after* a clean probe, so a project that cannot reach its vault never ends up
|
|
178
171
|
with a half-populated cache (D-07-05).
|
|
179
172
|
|
|
173
|
+
The verdict is the client vault's. The platform repo is probed and shown, but a client
|
|
174
|
+
project may have no access to it, so an unreachable platform repo reads `(not required)`
|
|
175
|
+
and does not fail the proof.
|
|
176
|
+
|
|
180
177
|
```bash
|
|
178
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
181
179
|
# D-07-01 + OD-1 + INIT-01: three signals, all read through the resolvers every other path
|
|
182
180
|
# uses. Anything but the literal "true" is a fail-inert default.
|
|
183
181
|
PP_INIT_PROBE=""
|
|
@@ -219,26 +217,25 @@ if [ "$VAULT_ON" = "true" ] && [ "$PROFILE_RESOLVED" = "true" ]; then
|
|
|
219
217
|
PROBE=$(pp_run query vault probe --raw 2>/dev/null || echo '{"targets":[]}')
|
|
220
218
|
# R3-CR-02 + R3-WR-02: ONE evaluation of the target set, read by every arm
|
|
221
219
|
# below. FOUR hand-copied de-dup pipelines stood here and had already
|
|
222
|
-
# diverged
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
#
|
|
228
|
-
#
|
|
229
|
-
# tier's identity when two tiers name one coordinate
|
|
230
|
-
# the
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
238
|
-
#
|
|
239
|
-
#
|
|
240
|
-
|
|
241
|
-
PROBE_EVAL=$(printf '%s' "$PROBE" | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const t=(JSON.parse(d).targets||[]).filter(x=>x&&x.configured===true);const key=x=>[x.repo,x.ref,x.host].join('|');const names=new Set(t.map(x=>x.name));const seen=new Set();const req=t.filter(x=>x.name!=='shared'&&!seen.has(key(x))&&seen.add(key(x)));const pick=new Map();for(const x of t){const k=key(x);const p=pick.get(k);if(!p||(p.name==='shared'&&x.name!=='shared'))pick.set(k,x);}const shown=t.filter(x=>pick.get(key(x))===x);const cfg={client:'client.vaultRepo',platform:'platform.repo'};const gone=Object.keys(cfg).filter(n=>!names.has(n));const ok=names.has('client')&&names.has('platform')&&req.length>0&&req.every(x=>x.reachable===true&&x.refFound===true);const sick=req.filter(x=>!(x.reachable===true&&x.refFound===true)).map(x=>String(x.name)+' '+String(x.repoSafe)+' @ '+String(x.ref)+' '+(x.reachable===true?'ref not found':'unreachable'));const all=gone.map(n=>n+' not configured').concat(sick);const lines=shown.map(x=>' '+String(x.name).padEnd(9)+String(x.repoSafe)+' @ '+((x.name==='platform'&&x.ref==='HEAD')?'(unpinned)':String(x.ref))+' '+(x.reachable===true?(x.refFound===true?'reachable, ref found':'ref not found'):'unreachable')+(x.name==='shared'?' (fallback tier, not required)':'')).concat(gone.map(n=>' '+n.padEnd(9)+'(not configured) missing — '+cfg[n]+' is absent from .platform-bindings.json'));process.stdout.write([ok?'pass':'fail',String(shown.length>0&&shown.every(x=>x.reachable!==true)),'failed='+(all.length?all.join('; '):'probe payload unreadable')].concat(lines).join('\n'));}catch{process.stdout.write('fail\nfalse\nfailed=probe payload unreadable');}")
|
|
220
|
+
# diverged, so every arm reads this one line. Only the CLIENT vault decides
|
|
221
|
+
# the verdict: it is what the fetch below lands. The platform repo is shown
|
|
222
|
+
# and recorded but not required (a client project may have no access to
|
|
223
|
+
# it), so an unreachable platform line carries "(not required)", like the
|
|
224
|
+
# fallback tier (OD-5 Option A: shared is probed and shown, out of the
|
|
225
|
+
# verdict and out of the note's sick list). A binding with no platform.repo
|
|
226
|
+
# at all still fails as "not configured". The DISPLAY de-dup keeps a
|
|
227
|
+
# non-shared tier's identity when two tiers name one coordinate, so a
|
|
228
|
+
# platform colliding with the fallback is still shown as platform. The
|
|
229
|
+
# all-down flag counts EVERY distinct remote, fallback included, ON
|
|
230
|
+
# PURPOSE: it drives the offline candidate below, and a remote that
|
|
231
|
+
# answered is evidence the network is up. CR-02: a target with no
|
|
232
|
+
# coordinate gets its own "missing" line — without it the D-07-02 case
|
|
233
|
+
# printed a card whose every line read healthy under a fail verdict;
|
|
234
|
+
# measured, not a diagnosis (D-07-07). EMITS: line 1 verdict, 2 all-down,
|
|
235
|
+
# 3 note, 4+ card; a card line can never carry a newline of its own
|
|
236
|
+
# (redactCloneTarget collapses the control class at the source, R2-WR-03),
|
|
237
|
+
# so the split below is safe.
|
|
238
|
+
PROBE_EVAL=$(printf '%s' "$PROBE" | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const t=(JSON.parse(d).targets||[]).filter(x=>x&&x.configured===true);const key=x=>[x.repo,x.ref,x.host].join('|');const names=new Set(t.map(x=>x.name));const healthy=x=>x.reachable===true&&x.refFound===true;const req=t.filter(x=>x.name==='client');const pick=new Map();for(const x of t){const k=key(x);const p=pick.get(k);if(!p||(p.name==='shared'&&x.name!=='shared'))pick.set(k,x);}const shown=t.filter(x=>pick.get(key(x))===x);const cfg={client:'client.vaultRepo',platform:'platform.repo'};const gone=Object.keys(cfg).filter(n=>!names.has(n));const ok=names.has('client')&&names.has('platform')&&req.length>0&&req.every(healthy);const sick=req.filter(x=>!healthy(x)).map(x=>String(x.name)+' '+String(x.repoSafe)+' @ '+String(x.ref)+' '+(x.reachable===true?'ref not found':'unreachable'));const all=gone.map(n=>n+' not configured').concat(sick);const lines=shown.map(x=>' '+String(x.name).padEnd(9)+String(x.repoSafe)+' @ '+((x.name==='platform'&&x.ref==='HEAD')?'(unpinned)':String(x.ref))+' '+(x.reachable===true?(x.refFound===true?'reachable, ref found':'ref not found'):'unreachable')+(x.name==='shared'?' (fallback tier, not required)':(x.name==='platform'&&!healthy(x)?' (not required)':''))).concat(gone.map(n=>' '+n.padEnd(9)+'(not configured) missing — '+cfg[n]+' is absent from .platform-bindings.json'));process.stdout.write([ok?'pass':'fail',String(shown.length>0&&shown.every(x=>x.reachable!==true)),'failed='+(all.length?all.join('; '):'probe payload unreadable')].concat(lines).join('\n'));}catch{process.stdout.write('fail\nfalse\nfailed=probe payload unreadable');}")
|
|
242
239
|
PROBE_VERDICT=$(printf '%s' "$PROBE_EVAL" | sed -n '1p')
|
|
243
240
|
PROBE_ALL_DOWN=$(printf '%s' "$PROBE_EVAL" | sed -n '2p')
|
|
244
241
|
PROBE_NOTE=$(printf '%s' "$PROBE_EVAL" | sed -n '3p')
|
|
@@ -257,6 +254,9 @@ if [ "$VAULT_ON" = "true" ] && [ "$PROFILE_RESOLVED" = "true" ]; then
|
|
|
257
254
|
FETCH_COUNT=$(printf '%s' "$FETCH" | node -e "const d=require('fs').readFileSync(0,'utf-8');try{process.stdout.write(String((JSON.parse(d).files||[]).length));}catch{process.stdout.write('0');}")
|
|
258
255
|
FETCH_SUMMARY=$(printf '%s' "$FETCH" | node -e "const d=require('fs').readFileSync(0,'utf-8');try{process.stdout.write(String(JSON.parse(d).summaryWritten===true));}catch{process.stdout.write('false');}")
|
|
259
256
|
PLATFORM_REPO=$(printf '%s' "$PROBE" | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const x=(JSON.parse(d).targets||[]).find(y=>y&&y.name==='platform');process.stdout.write(String((x&&x.repoSafe)||''));}catch{process.stdout.write('');}")
|
|
257
|
+
# The platform repo is not required (a client may have no access to it): its measured
|
|
258
|
+
# state goes on the card and into the record, it never decides the verdict.
|
|
259
|
+
PLATFORM_STATE=$(printf '%s' "$PROBE" | node -e "const d=require('fs').readFileSync(0,'utf-8');try{const x=(JSON.parse(d).targets||[]).find(y=>y&&y.name==='platform');process.stdout.write(x&&x.reachable===true?(x.refFound===true?'reachable':'ref not found (not required)'):'unreachable (not required)');}catch{process.stdout.write('unreachable (not required)');}")
|
|
260
260
|
# Never the probe's internal HEAD placeholder — an unpinned platform says so.
|
|
261
261
|
PLATFORM_TAG=$(pp_run query platform-bindings --pick platform.tag 2>/dev/null)
|
|
262
262
|
if [ "$FETCH_TIER" = "client" ] && [ "$FETCH_SUMMARY" = "true" ] && { [ "$FETCH_SOURCE" = "fetched" ] || [ "$FETCH_SOURCE" = "cache" ]; }; then
|
|
@@ -264,13 +264,13 @@ if [ "$VAULT_ON" = "true" ] && [ "$PROFILE_RESOLVED" = "true" ]; then
|
|
|
264
264
|
# card prints. A refused write used to be a warning under a PASSED card, and PASSED
|
|
265
265
|
# routes to "continue to Step 3" — so "unrecorded" was defined to continue. It is a
|
|
266
266
|
# failure of the step, kind FAIL_KIND=record. Markers unchanged: pass/unbound/fail.
|
|
267
|
-
if pp_run query init-contract --record-probe "passed=platform ${PLATFORM_REPO} @ ${PLATFORM_TAG:-(unpinned)}; vault ${FETCH_REPO} @ ${FETCH_REF} tier ${FETCH_TIER} ${FETCH_COUNT} files" >/dev/null 2>&1; then
|
|
267
|
+
if pp_run query init-contract --record-probe "passed=platform ${PLATFORM_REPO} @ ${PLATFORM_TAG:-(unpinned)} ${PLATFORM_STATE}; vault ${FETCH_REPO} @ ${FETCH_REF} tier ${FETCH_TIER} ${FETCH_COUNT} files" >/dev/null 2>&1; then
|
|
268
268
|
PP_INIT_PROBE=pass
|
|
269
269
|
echo "Reachability proof: PASSED"
|
|
270
270
|
# OD-3 + CR-01: the DISPLAY-SAFE coordinate (repoSafe), ref, tier and booleans
|
|
271
271
|
# only. Credentials are stripped at the source, so a PAT in the binding reaches
|
|
272
272
|
# neither this card nor the record. Never url, never error.
|
|
273
|
-
printf '%s\n' " platform ${PLATFORM_REPO} @ ${PLATFORM_TAG:-(unpinned)}
|
|
273
|
+
printf '%s\n' " platform ${PLATFORM_REPO} @ ${PLATFORM_TAG:-(unpinned)} ${PLATFORM_STATE}"
|
|
274
274
|
printf '%s\n' " vault ${FETCH_REPO} @ ${FETCH_REF} reachable, ref found — tier ${FETCH_TIER}, sha ${FETCH_SHA}, ${FETCH_COUNT} files, SUMMARY.md written (${FETCH_SOURCE})"
|
|
275
275
|
printf '%s\n' "$PROBE_LINES" | grep '^ shared ' || true
|
|
276
276
|
echo "PP_INIT_PROBE=pass"
|
|
@@ -353,7 +353,7 @@ outright. Take THIS generic sub-path when
|
|
|
353
353
|
bindings ARE present, take the gated GitLab/Azure sub-path below instead — that section is
|
|
354
354
|
still part of Step 3, not a separate optional step. **The provider answer never decides
|
|
355
355
|
whether Step 3 runs** — inside the gated sub-path it only decides whether the
|
|
356
|
-
|
|
356
|
+
`glab` or `gh` create recipe fires or falls through to this same generic code below.
|
|
357
357
|
|
|
358
358
|
On the generic path:
|
|
359
359
|
|
|
@@ -393,14 +393,14 @@ gets its own per-action confirmation immediately before it fires — nothing bat
|
|
|
393
393
|
step makes **ZERO `gh`/`glab`/provider-API calls**. The push target is the user-supplied
|
|
394
394
|
URL only.
|
|
395
395
|
|
|
396
|
-
### Gated GitLab/
|
|
396
|
+
### Gated GitLab/GitHub provisioning (Step 3's other sub-path — bindings present)
|
|
397
397
|
|
|
398
398
|
This is Step 3's other sub-path, not a separate step. It runs **instead of** the generic
|
|
399
399
|
remote-wiring path above when
|
|
400
400
|
`pp_run query platform-bindings --pick present 2>/dev/null` equals `"true"`. Within it,
|
|
401
|
-
G-1 always runs first to source coordinates and apply the two guards below;
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
G-1 always runs first to source coordinates and apply the two guards below; a create
|
|
402
|
+
recipe runs **only when the resolved provider is `gitlab` or `github`** — under any
|
|
403
|
+
other provider (`azure` today) it is skipped, not failed, and the
|
|
404
404
|
run falls through to Step 3's generic remote-wiring path above instead (D-11, D-13).
|
|
405
405
|
Implements D-05 (per-action confirmation), D-09 (existence probe → adopt-if-exists), D-10
|
|
406
406
|
(private by default), D-11 (auth verify-only; gated on provider), D-12 (adopt-if-exists
|
|
@@ -442,8 +442,9 @@ comes from, skip G-2 through G-5 entirely, and fall through to Step 3's generic
|
|
|
442
442
|
wiring:
|
|
443
443
|
|
|
444
444
|
```bash
|
|
445
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
445
446
|
if [ -z "$GROUP" ] || [ -z "$SLUG" ]; then
|
|
446
|
-
echo "Skipping
|
|
447
|
+
echo "Skipping repository provisioning: $( [ -z "$GROUP" ] && echo "GROUP (org profile seed provisioning group)" || echo "SLUG (project.slug)" ) is empty — falling through to Step 3's generic remote wiring."
|
|
447
448
|
fi
|
|
448
449
|
```
|
|
449
450
|
|
|
@@ -452,35 +453,24 @@ fi
|
|
|
452
453
|
|
|
453
454
|
```bash
|
|
454
455
|
PROVIDER=$(pp_run query compose-bindings --offline --pick provider 2>/dev/null)
|
|
455
|
-
if [ "$PROVIDER" != "gitlab" ]; then
|
|
456
|
-
echo "Skipping
|
|
456
|
+
if [ "$PROVIDER" != "gitlab" ] && [ "$PROVIDER" != "github" ]; then
|
|
457
|
+
echo "Skipping repository provisioning for provider \"$PROVIDER\" — falling through to Step 3's generic remote wiring instead."
|
|
457
458
|
fi
|
|
458
459
|
```
|
|
459
460
|
|
|
460
|
-
Continue
|
|
461
|
-
|
|
461
|
+
Continue only when the empty-group guard above did not skip **and** `PROVIDER`
|
|
462
|
+
is `gitlab` or `github`. Otherwise stop here and go to Step 3's generic remote wiring (`:283-312`)
|
|
462
463
|
instead — do not fail, do not error, do not prompt for a missing CLI. "Skipped, not
|
|
463
464
|
failed" is satisfied *and* the user still leaves with a wired remote, which a bare skip
|
|
464
465
|
would not give them; pp-core makes zero provider-API calls it has no client for.
|
|
465
466
|
|
|
466
|
-
When both conditions hold, **Read `pp-core/workflows/init/steps/gitlab-provisioning.md`
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
- Always use the group-qualified `$GROUP/$SLUG` path — never a bare slug (T-08-04, D-09).
|
|
474
|
-
A bare slug creates under the user's personal namespace, not the client group. G-1's
|
|
475
|
-
empty-group guard above is what actually enforces this before any create attempt.
|
|
476
|
-
- Always pass `--private` explicitly — never inherit `glab`'s default `--internal`
|
|
477
|
-
visibility (T-08-05, D-10). The `--internal` flag is what makes this dangerous; the
|
|
478
|
-
locked recipe must carry `--private` and must NOT emit `--internal`.
|
|
479
|
-
- Always confirm immediately before each outward action — nothing batched (T-08-06, D-05).
|
|
480
|
-
- Verify-only auth via `glab auth status` — never run `glab auth login`, never read
|
|
481
|
-
or write the user's token (T-08-08, D-11). Bindings hold only non-secret routing data.
|
|
482
|
-
- Always reconcile create with a `glab repo view` probe — a non-zero create exit does
|
|
483
|
-
NOT mean nothing happened (T-08-09, D-09).
|
|
467
|
+
When both conditions hold, **Read `pp-core/workflows/init/steps/gitlab-provisioning.md`
|
|
468
|
+
(provider `gitlab`) or `pp-core/workflows/init/steps/github-provisioning.md` (provider
|
|
469
|
+
`github`) and follow it.** Each owns, for its
|
|
470
|
+
CLI, auth verify-only, the existence probe → adopt-if-exists, per-action confirmation then
|
|
471
|
+
the locked `--private` create, post-create reconciliation, and its security invariants
|
|
472
|
+
(landmine guards: group-qualified path, explicit `--private`, verify-only auth). Under any
|
|
473
|
+
other provider read neither — fall through to Step 3's generic remote wiring above.
|
|
484
474
|
|
|
485
475
|
### Gated client-context capture (Pareto projects — bindings present)
|
|
486
476
|
|
|
@@ -555,9 +545,9 @@ legitimately not exist — the session declines or composes nothing on the inert
|
|
|
555
545
|
|
|
556
546
|
```bash
|
|
557
547
|
if [ -f .platform-bindings.json ]; then
|
|
558
|
-
pp_run query commit "docs: initialize project" --files .planning/config.json .platform-bindings.json
|
|
548
|
+
pp_run query commit "docs(planning): initialize project" --files .planning/config.json .platform-bindings.json
|
|
559
549
|
else
|
|
560
|
-
pp_run query commit "docs: initialize project" --files .planning/config.json
|
|
550
|
+
pp_run query commit "docs(planning): initialize project" --files .planning/config.json
|
|
561
551
|
fi
|
|
562
552
|
```
|
|
563
553
|
|
|
@@ -327,6 +327,7 @@ Continue to verify_output.
|
|
|
327
327
|
Verify all documents created successfully:
|
|
328
328
|
|
|
329
329
|
```bash
|
|
330
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
330
331
|
ls -la .planning/codebase/
|
|
331
332
|
wc -l .planning/codebase/*.md
|
|
332
333
|
```
|
|
@@ -346,6 +347,7 @@ Continue to scan_for_secrets.
|
|
|
346
347
|
Run secret pattern detection:
|
|
347
348
|
|
|
348
349
|
```bash
|
|
350
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
349
351
|
# Check for common API key patterns in generated docs
|
|
350
352
|
grep -E '(sk-[a-zA-Z0-9]{20,}|sk_live_[a-zA-Z0-9]+|sk_test_[a-zA-Z0-9]+|ghp_[a-zA-Z0-9]{36}|gho_[a-zA-Z0-9]{36}|glpat-[a-zA-Z0-9_-]+|AKIA[A-Z0-9]{16}|xox[baprs]-[a-zA-Z0-9-]+|-----BEGIN.*PRIVATE KEY|eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.)' .planning/codebase/*.md 2>/dev/null && SECRETS_FOUND=true || SECRETS_FOUND=false
|
|
351
353
|
```
|
|
@@ -379,7 +381,8 @@ Continue to commit_codebase_map.
|
|
|
379
381
|
Commit the codebase map:
|
|
380
382
|
|
|
381
383
|
```bash
|
|
382
|
-
|
|
384
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
385
|
+
pp_run query commit "docs(planning): map existing codebase" --files .planning/codebase/*.md
|
|
383
386
|
```
|
|
384
387
|
|
|
385
388
|
Continue to offer_next.
|
|
@@ -390,6 +393,7 @@ Present completion summary and next steps.
|
|
|
390
393
|
|
|
391
394
|
**Get line counts:**
|
|
392
395
|
```bash
|
|
396
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
393
397
|
wc -l .planning/codebase/*.md
|
|
394
398
|
```
|
|
395
399
|
|
|
@@ -78,6 +78,7 @@ git tag -l "v${VERSION}" | head -1
|
|
|
78
78
|
```
|
|
79
79
|
If the tag exists:
|
|
80
80
|
```bash
|
|
81
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
81
82
|
git log v${VERSION} --oneline | wc -l
|
|
82
83
|
git diff --stat $(git log --format=%H --reverse v${VERSION} | head -1)..v${VERSION}
|
|
83
84
|
```
|
|
@@ -190,7 +191,7 @@ mkdir -p .planning/reports
|
|
|
190
191
|
|
|
191
192
|
Write the summary, then commit:
|
|
192
193
|
```bash
|
|
193
|
-
pp_run query commit "docs(v${VERSION}
|
|
194
|
+
pp_run query commit "docs(planning): generate v${VERSION} milestone summary for onboarding" --files \
|
|
194
195
|
".planning/reports/MILESTONE_SUMMARY-v${VERSION}.md"
|
|
195
196
|
```
|
|
196
197
|
|
|
@@ -99,6 +99,7 @@ If any of the three answers is empty or whitespace-only, error and re-prompt tha
|
|
|
99
99
|
**Validate via the centralized User Story validator.** The verb owns the canonical regex `/^As a .+, I want to .+, so that .+\.$/` and surfaces per-error guidance:
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
102
103
|
USER_STORY_RESULT=$(pp_run query user-story.validate --story "$USER_STORY")
|
|
103
104
|
if [ "$(echo "$USER_STORY_RESULT" | jq -r '.valid')" != "true" ]; then
|
|
104
105
|
echo "$USER_STORY_RESULT" | jq -r '.errors[]' >&2
|
|
@@ -55,6 +55,7 @@ If the flag is absent, keep the current behavior of continuing phase numbering f
|
|
|
55
55
|
Check `.planning/seeds/` for seed files that match the milestone goals gathered in step 2.
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
58
59
|
ls .planning/seeds/SEED-*.md 2>/dev/null
|
|
59
60
|
```
|
|
60
61
|
|
|
@@ -217,7 +218,7 @@ pp_run query phases.clear --confirm
|
|
|
217
218
|
```
|
|
218
219
|
|
|
219
220
|
```bash
|
|
220
|
-
pp_run query commit "docs: start milestone v[X.Y] [Name]" --files .planning/PROJECT.md .planning/STATE.md
|
|
221
|
+
pp_run query commit "docs(planning): start milestone v[X.Y] [Name]" --files .planning/PROJECT.md .planning/STATE.md
|
|
221
222
|
```
|
|
222
223
|
|
|
223
224
|
## 7. Load Context and Resolve Models
|
|
@@ -256,6 +257,7 @@ If `--reset-phase-numbers` is active:
|
|
|
256
257
|
If `phase_dir_count > 0`, let the engine archive what the closed milestones name — never move phase directories by hand:
|
|
257
258
|
|
|
258
259
|
```bash
|
|
260
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
259
261
|
ARCHIVED=$(pp_run query phases.archive-closed ${GSD_WS})
|
|
260
262
|
```
|
|
261
263
|
|
|
@@ -368,7 +370,7 @@ Commit after writing.
|
|
|
368
370
|
**Synthesizer output self-heal (#222) — verify SUMMARY.md materialized:** The synthesizer's canonical output is `.planning/research/SUMMARY.md` on disk; its brief structured return (`## SYNTHESIS COMPLETE` plus a few `###` confirmation lines) is NOT the file content. A known LLM false-refusal (issue #222) sometimes makes the agent return the full SUMMARY.md document inline — fabricating a write restriction (e.g. "the runtime is blocking file writes") — instead of writing the file. Prompt hardening alone does not fully eliminate it, so the orchestrator MUST absorb the failure deterministically before spawning `pp-roadmapper`:
|
|
369
371
|
|
|
370
372
|
1. Verify `.planning/research/SUMMARY.md` exists AND is substantive — non-empty, and free of any leftover `<!-- gsd:write-continue -->` continuation sentinel (which marks a truncated/incomplete write). You may validate with `pp-tools verify-summary .planning/research/SUMMARY.md` — it exits 0 regardless, so check its JSON `passed` field (`"passed": false` means missing or invalid), not the process exit code. If it passes, continue normally.
|
|
371
|
-
2. If it is MISSING or invalid AND the synthesizer's return message contains the FULL SUMMARY.md document — recognizable by the template's top-level markers `# Project Research Summary`, `## Key Findings`, `## Implications for Roadmap`, and `## Sources`, not merely the brief `## SYNTHESIS COMPLETE` confirmation — the false-refusal fired: write that returned document to `.planning/research/SUMMARY.md` with the Write tool, then commit ALL research artifacts the synthesizer owns (it commits on behalf of the four researchers) with `
|
|
373
|
+
2. If it is MISSING or invalid AND the synthesizer's return message contains the FULL SUMMARY.md document — recognizable by the template's top-level markers `# Project Research Summary`, `## Key Findings`, `## Implications for Roadmap`, and `## Sources`, not merely the brief `## SYNTHESIS COMPLETE` confirmation — the false-refusal fired: write that returned document to `.planning/research/SUMMARY.md` with the Write tool, then commit ALL research artifacts the synthesizer owns (it commits on behalf of the four researchers) with `pp_run query commit "docs(planning): complete project research" --files .planning/research/` unless they are already committed. Log `⚠ #222 self-heal: synthesizer returned SUMMARY.md inline without writing it; orchestrator persisted the file.`
|
|
372
374
|
3. If it is MISSING or invalid AND the return is only a brief confirmation (no full SUMMARY document to recover), the synthesizer genuinely failed — surface the error and stop; do NOT spawn `pp-roadmapper` against a missing or incomplete SUMMARY.md.
|
|
373
375
|
|
|
374
376
|
This guarantees `pp-roadmapper` (which lists SUMMARY.md as required reading) never runs against a missing or truncated SUMMARY.md.
|
|
@@ -456,7 +458,7 @@ If "adjust": Return to scoping.
|
|
|
456
458
|
|
|
457
459
|
**Commit requirements:**
|
|
458
460
|
```bash
|
|
459
|
-
pp_run query commit "docs: define milestone v[X.Y] requirements" --files .planning/REQUIREMENTS.md
|
|
461
|
+
pp_run query commit "docs(planning): define milestone v[X.Y] requirements" --files .planning/REQUIREMENTS.md
|
|
460
462
|
```
|
|
461
463
|
|
|
462
464
|
## 10. Create Roadmap
|
|
@@ -542,7 +544,7 @@ Success criteria:
|
|
|
542
544
|
|
|
543
545
|
**Commit roadmap** (after approval):
|
|
544
546
|
```bash
|
|
545
|
-
pp_run query commit "docs: create milestone v[X.Y] roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md
|
|
547
|
+
pp_run query commit "docs(planning): create milestone v[X.Y] roadmap ([N] phases)" --files .planning/ROADMAP.md .planning/STATE.md .planning/REQUIREMENTS.md
|
|
546
548
|
```
|
|
547
549
|
|
|
548
550
|
## 10.5. Link Pending Todos to Roadmap Phases
|
|
@@ -551,6 +553,7 @@ After roadmap approval, scan pending todos against the newly approved phases. Fo
|
|
|
551
553
|
|
|
552
554
|
**Check for pending todos:**
|
|
553
555
|
```bash
|
|
556
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
554
557
|
PENDING_TODOS=$(ls .planning/todos/pending/*.md 2>/dev/null | head -50)
|
|
555
558
|
```
|
|
556
559
|
|
|
@@ -585,7 +588,8 @@ files: [existing]
|
|
|
585
588
|
|
|
586
589
|
**If any todos were linked:**
|
|
587
590
|
```bash
|
|
588
|
-
|
|
591
|
+
[ -z "${ZSH_VERSION:-}" ] || emulate -R sh
|
|
592
|
+
pp_run query commit "docs(planning): tag [count] pending todos with resolves_phase after milestone v[X.Y] roadmap" --files .planning/todos/pending/*.md
|
|
589
593
|
```
|
|
590
594
|
|
|
591
595
|
Print a summary:
|