forgedock 1.0.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.
Files changed (44) hide show
  1. package/LICENSE +244 -0
  2. package/README.md +251 -0
  3. package/bin/forgedock.mjs +232 -0
  4. package/commands/analytics.md +777 -0
  5. package/commands/audit-agents.md +410 -0
  6. package/commands/audit.md +410 -0
  7. package/commands/autopilot.md +432 -0
  8. package/commands/cleanup.md +264 -0
  9. package/commands/deploy-info.md +292 -0
  10. package/commands/failure-recon.md +375 -0
  11. package/commands/forge-stats.md +183 -0
  12. package/commands/geo-audit.md +343 -0
  13. package/commands/incident-response.md +384 -0
  14. package/commands/issue.md +506 -0
  15. package/commands/milestone.md +599 -0
  16. package/commands/orchestrate.md +1044 -0
  17. package/commands/pipeline-health.md +2343 -0
  18. package/commands/qa-sweep.md +237 -0
  19. package/commands/quality-gate.md +777 -0
  20. package/commands/review-pr-agents.md +1711 -0
  21. package/commands/review-pr-staging.md +325 -0
  22. package/commands/review-pr.md +888 -0
  23. package/commands/rollback.md +253 -0
  24. package/commands/security-audit.md +529 -0
  25. package/commands/sync-ecosystem.md +527 -0
  26. package/commands/validate.md +131 -0
  27. package/commands/work-on/build/architect.md +291 -0
  28. package/commands/work-on/build/context.md +349 -0
  29. package/commands/work-on/build/implement.md +280 -0
  30. package/commands/work-on/build/validate.md +292 -0
  31. package/commands/work-on/build.md +262 -0
  32. package/commands/work-on/close.md +350 -0
  33. package/commands/work-on/decompose.md +254 -0
  34. package/commands/work-on/investigate.md +437 -0
  35. package/commands/work-on/review.md +245 -0
  36. package/commands/work-on-monolithic.md +298 -0
  37. package/commands/work-on.md +1089 -0
  38. package/docs/CODEX.md +92 -0
  39. package/docs/WORKFLOW.md +104 -0
  40. package/package.json +45 -0
  41. package/scripts/verify-env-vars.sh +201 -0
  42. package/scripts/verify-host-headers.sh +117 -0
  43. package/scripts/verify-route-registration.sh +118 -0
  44. package/scripts/verify-sops-chain.sh +163 -0
@@ -0,0 +1,599 @@
1
+ ---
2
+ description: Create, manage, and ship milestones — the top-level planning layer for feature development
3
+ argument-hint: [create <name> | status | ship <slug> | sync <slug>]
4
+ ---
5
+
6
+ # /milestone — Milestone Lifecycle Manager
7
+
8
+ **Input**: $ARGUMENTS
9
+
10
+ Milestones are the top-level planning unit. They group related issues into a shippable feature set. Each milestone gets its own long-lived Git branch (`milestone/{slug}`) where feature PRs accumulate. When the milestone is ready, it ships to `main` as a single reviewed merge.
11
+
12
+ **Hierarchy**: Milestone → Issues → Sub-issues (optional decomposition)
13
+
14
+ **You have access to ALL tools** — Task tool, Skill tool, sub-agents, everything.
15
+
16
+ ## Multi-Repo Support
17
+
18
+ Milestones can span the AlterLab ecosystem. Project context (repo map, paths, conventions) comes from each project's `CLAUDE.md`.
19
+
20
+ ### Cross-Repo Milestones
21
+
22
+ Some milestones (e.g., "BYOS Authenticated Scraping") require changes across multiple repos — the platform, SDKs, MCP server, etc. For these:
23
+
24
+ - **The milestone is created in the primary repo** (usually `RapierCraft/AlterLab`)
25
+ - **Issues are created in the repo where the code lives** — e.g., MCP server issues go to `RapierCraft/alterlab-mcp-server`
26
+ - **Each repo gets its own milestone branch** (`milestone/{slug}`) if it has issues in the milestone
27
+ - **Issues reference the parent milestone** in their body: `Part of cross-repo milestone: **{TITLE}** (primary: RapierCraft/AlterLab)`
28
+
29
+ ### Repo Prefixes (same as `/work-on`)
30
+
31
+ | Prefix | Repo |
32
+ |--------|------|
33
+ | _(none)_ | `RapierCraft/AlterLab` (default) |
34
+ | `mcp:` | `RapierCraft/alterlab-mcp-server` |
35
+ | `n8n:` | `RapierCraft/n8n-nodes-alterlab` |
36
+
37
+ When decomposing a milestone, tag each proposed issue with its target repo. Use `-R` flag for non-default repos.
38
+
39
+ ---
40
+
41
+ ## Command Router
42
+
43
+ Parse `$ARGUMENTS` to determine which action to take:
44
+
45
+ | Input Pattern | Action |
46
+ |--------------|--------|
47
+ | `create <name>` or just a descriptive phrase | → **Create Milestone** |
48
+ | `status` (no args, or "status") | → **Show All Milestones** |
49
+ | `status <slug>` | → **Show Milestone Detail** |
50
+ | `ship <slug>` | → **Ship Milestone** |
51
+ | `sync <slug>` | → **Sync Milestone Branch** |
52
+ | `close <slug>` | → **Close Milestone** |
53
+
54
+ ---
55
+
56
+ ## Action: Create Milestone
57
+
58
+ ### Step 1: Parse the milestone scope
59
+
60
+ The user provides a name or description. Extract:
61
+ - **Title**: Short, descriptive (e.g., "LLM Extraction Platform", "API Expansion v1")
62
+ - **Slug**: Lowercase, hyphenated, max 40 chars (e.g., `llm-extraction-platform`)
63
+ - **Description**: What this milestone delivers, extracted from user's input
64
+
65
+ ### Step 2: Create the GitHub milestone
66
+
67
+ ```bash
68
+ gh api repos/{owner}/{repo}/milestones --method POST \
69
+ --field title="{TITLE}" \
70
+ --field description="{DESCRIPTION}" \
71
+ --field state="open"
72
+ ```
73
+
74
+ Capture the milestone number from the response.
75
+
76
+ ### Step 3: Create the milestone branch
77
+
78
+ ```bash
79
+ cd {REPO_PATH} # default: /home/mrdubey/projects/ScraperAPI/alterlab
80
+ git fetch origin main
81
+ git branch milestone/{slug} origin/main
82
+ git push origin milestone/{slug}
83
+ ```
84
+
85
+ ### Step 4: Scope decomposition
86
+
87
+ Use a Task sub-agent to analyze the milestone scope and propose issues:
88
+
89
+ > You are planning the scope for milestone "{TITLE}" on the AlterLab platform.
90
+ >
91
+ > **Milestone description**: {DESCRIPTION}
92
+ >
93
+ > **Your job**: Break this milestone into concrete, implementable GitHub issues. Each issue should be:
94
+ > - Independently shippable (produces a working PR on its own)
95
+ > - Specific enough for the `/work-on` pipeline to pick up and implement
96
+ > - Ordered by dependencies (if B needs A's API, A comes first)
97
+ >
98
+ > **AlterLab ecosystem context**:
99
+ > - **Platform** (default): FastAPI backend (services/api/, services/worker/), Next.js frontend (web/), Docker microservices, PostgreSQL, Redis
100
+ > - **Python SDK** (sdk/python/): Async client, httpx, pydantic
101
+ > - **Node SDK** (sdk/node/): TypeScript client, axios
102
+ > - **MCP Server** (alterlab-mcp-server/ — SEPARATE REPO): MCP tools for AI assistants, TypeScript, @modelcontextprotocol/sdk
103
+ > - **n8n Node** (n8n-nodes-alterlab/ — SEPARATE REPO): n8n community node, TypeScript
104
+ > - **Chrome Extension** (tools/chrome-extension/): Cookie capture for BYOS
105
+ >
106
+ > Check the existing codebase to understand current architecture before proposing changes.
107
+ >
108
+ > **Output format**: Numbered list of proposed issues, each with:
109
+ > - **Title**: Actionable, specific (e.g., "Add per-token billing engine for LLM extraction")
110
+ > - **Repo**: Which repo this issue belongs to (`alterlab`, `mcp`, `n8n`, or platform default)
111
+ > - **Type**: `feature`, `bug`, `refactor`, `infra`
112
+ > - **Priority**: P1-P3
113
+ > - **Size**: S (1-2 files), M (3-5 files), L (6+ files)
114
+ > - **Dependencies**: Which other issues must be done first (by number)
115
+ > - **Scope**: Key files/modules affected
116
+ > - **Acceptance criteria**: 2-3 testable requirements
117
+
118
+ ### Step 5: Review with user
119
+
120
+ Present the proposed issues to the user. Ask:
121
+ - "Does this scope look right? Should I add/remove/modify any issues?"
122
+ - Wait for confirmation before creating.
123
+
124
+ ### Step 6: Create issues and assign to milestone
125
+
126
+ For each approved issue, create it in the **correct repo** based on the scope analysis:
127
+
128
+ ```bash
129
+ # For AlterLab issues (default):
130
+ gh issue create \
131
+ --title "{fix|feat|refactor}: {issue_title}" \
132
+ --label "{type},{priority}" \
133
+ --milestone "{TITLE}" \
134
+ --body "$(cat <<'BODY_EOF'
135
+ ## Problem
136
+
137
+ {1-3 sentences: what needs to be built or fixed for this milestone. What's missing or broken.}
138
+
139
+ ## Root Cause (if known)
140
+
141
+ {Why this needs to change — architecture gap, missing feature, technical debt. If a new feature: "New capability required for {MILESTONE_TITLE}."}
142
+
143
+ ## Affected Files
144
+
145
+ Files that need changes:
146
+ 1. `{filepath}` — {what needs to change}
147
+ 2. `{filepath}` — {what needs to change}
148
+
149
+ ## Acceptance Criteria
150
+
151
+ - [ ] {Specific, testable criterion}
152
+ - [ ] {Specific, testable criterion}
153
+ - [ ] No regression in {related feature}
154
+
155
+ ## Context
156
+
157
+ Part of milestone: **{MILESTONE_TITLE}**
158
+ **Type**: {feature|bug|refactor|infra}
159
+
160
+ ## Dependencies
161
+
162
+ {Either "None" or "Depends on #{ISSUE_NUMBER} — {reason}"}
163
+ BODY_EOF
164
+ )"
165
+
166
+ # For satellite repo issues (MCP, n8n) — create in the satellite repo:
167
+ # Note: GitHub milestones are per-repo, so satellite issues reference the milestone by name only
168
+ gh issue create -R {SATELLITE_REPO} \
169
+ --title "{fix|feat|refactor}: {issue_title}" \
170
+ --label "{type},{priority}" \
171
+ --body "$(cat <<'BODY_EOF'
172
+ ## Problem
173
+
174
+ {1-3 sentences: what needs to be built or fixed in this satellite repo for the milestone.}
175
+
176
+ ## Root Cause (if known)
177
+
178
+ {Why this needs to change. If a new feature: "New capability required for {MILESTONE_TITLE}."}
179
+
180
+ ## Affected Files
181
+
182
+ Files that need changes:
183
+ 1. `{filepath}` — {what needs to change}
184
+ 2. `{filepath}` — {what needs to change}
185
+
186
+ ## Acceptance Criteria
187
+
188
+ - [ ] {Specific, testable criterion}
189
+ - [ ] {Specific, testable criterion}
190
+
191
+ ## Context
192
+
193
+ Part of cross-repo milestone: **{MILESTONE_TITLE}** (primary: RapierCraft/AlterLab)
194
+ **Type**: {feature|bug|refactor|infra}
195
+
196
+ ## Dependencies
197
+
198
+ {Either "None" or "Depends on #{ISSUE_NUMBER} — {reason}"}
199
+ BODY_EOF
200
+ )"
201
+ ```
202
+
203
+ ### Step 6B: Add issues to Project board
204
+
205
+ For each created issue, add it to the GitHub Project board with initial fields. Reference `~/projects/forge/docs/WORKFLOW.md` → "Project Board Integration" for field IDs.
206
+
207
+ ```bash
208
+ for ISSUE_NUM in {created_issue_numbers}; do
209
+ ISSUE_URL="https://github.com/{GH_REPO}/issues/${ISSUE_NUM}"
210
+ ITEM_ID=$(gh project item-add 1 --owner RapierCraft --url "$ISSUE_URL" --format json --jq '.id' 2>/dev/null)
211
+ if [ -n "$ITEM_ID" ]; then
212
+ gh project item-edit --project-id PVT_kwHOCx3gR84BSK2L --id "$ITEM_ID" --field-id PVTSSF_lAHOCx3gR84BSK2Lzg_yF6E --single-select-option-id f75ad846 2>/dev/null || true # Status=Todo
213
+ gh project item-edit --project-id PVT_kwHOCx3gR84BSK2L --id "$ITEM_ID" --field-id PVTSSF_lAHOCx3gR84BSK2Lzg_yF98 --single-select-option-id 4ff6f9e6 2>/dev/null || true # Lane=Feature
214
+ gh project item-edit --project-id PVT_kwHOCx3gR84BSK2L --id "$ITEM_ID" --field-id PVTSSF_lAHOCx3gR84BSK2Lzg_yF-o --single-select-option-id {COMPONENT_OPTION_ID} 2>/dev/null || true # Component (from repo — see WORKFLOW.md mapping)
215
+ gh project item-edit --project-id PVT_kwHOCx3gR84BSK2L --id "$ITEM_ID" --field-id PVTSSF_lAHOCx3gR84BSK2Lzg_yF8o --single-select-option-id {PRIORITY_OPTION_ID} 2>/dev/null || true # Priority (from label)
216
+ fi
217
+ done
218
+ ```
219
+
220
+ ### Step 7: Report
221
+
222
+ ```
223
+ ## Created: Milestone "{TITLE}"
224
+
225
+ Branch: `milestone/{slug}`
226
+ Issues: {count} created
227
+
228
+ | # | Issue | Type | Priority | Size | Depends On |
229
+ |---|-------|------|----------|------|------------|
230
+ | 1 | #{N} — {title} | {type} | {P} | {S/M/L} | — |
231
+ | 2 | #{N} — {title} | {type} | {P} | {S/M/L} | #{dep} |
232
+ ...
233
+
234
+ **Next**: Run `/work-on next` to start on the first issue, or `/work-on #{N}` for a specific one.
235
+ Issues with this milestone will automatically PR to `milestone/{slug}`.
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Action: Show All Milestones (status)
241
+
242
+ ```bash
243
+ # List open milestones with progress
244
+ gh api repos/{owner}/{repo}/milestones --jq '.[] | select(.state == "open") | {
245
+ title: .title,
246
+ number: .number,
247
+ open_issues: .open_issues,
248
+ closed_issues: .closed_issues,
249
+ due_on: (.due_on // "no due date"),
250
+ description: (.description[:100])
251
+ }'
252
+ ```
253
+
254
+ Format as a table:
255
+
256
+ ```
257
+ ## Active Milestones
258
+
259
+ | Milestone | Progress | Open | Closed | Due |
260
+ |-----------|----------|------|--------|-----|
261
+ | LLM Extraction Platform | ████░░░░ 40% | 6 | 4 | Apr 15 |
262
+ | Scraper Quality v1 | ████████░ 85% | 2 | 11 | — |
263
+ | API Expansion | ░░░░░░░░ 0% | 3 | 0 | — |
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Action: Show Milestone Detail (status <slug>)
269
+
270
+ ```bash
271
+ # Find milestone by slug (match against title, case-insensitive)
272
+ MILESTONE=$(gh api repos/{owner}/{repo}/milestones --jq '.[] | select(.title | ascii_downcase | gsub(" "; "-") | startswith("{slug}"))')
273
+
274
+ # List issues in this milestone
275
+ gh issue list --milestone "{TITLE}" --state all --json number,title,state,labels --jq '.[] | {number, title, state, labels: [.labels[].name]}'
276
+ ```
277
+
278
+ Show detailed progress with per-issue status.
279
+
280
+ ---
281
+
282
+ ## Action: Ship Milestone
283
+
284
+ **This is the human-gated deployment step.** When the user says "ship {slug}", they've decided this feature set is ready for production.
285
+
286
+ ### Step 1: Pre-flight check
287
+
288
+ ```bash
289
+ # Check milestone branch exists
290
+ git fetch origin milestone/{slug}
291
+
292
+ # Check for open issues still in the milestone
293
+ OPEN_COUNT=$(gh issue list --milestone "{TITLE}" --state open --json number --jq '. | length')
294
+ ```
295
+
296
+ If there are open issues, warn the user:
297
+ ```
298
+ ⚠ Milestone "{TITLE}" still has {OPEN_COUNT} open issues:
299
+ {list them}
300
+
301
+ Ship anyway? Open issues will remain assigned to the milestone.
302
+ ```
303
+
304
+ Wait for confirmation.
305
+
306
+ ### Step 2: Sync milestone branch with staging
307
+
308
+ Before shipping, ensure the milestone branch is up to date with `staging` (picks up any fast-lane fixes that accumulated since the milestone branch was created):
309
+
310
+ ```bash
311
+ cd {REPO_PATH} # default: /home/mrdubey/projects/ScraperAPI/alterlab
312
+ git fetch origin staging milestone/{slug}
313
+ git checkout milestone/{slug}
314
+ git merge origin/staging --no-edit
315
+ # If conflicts, report to user and STOP — do not auto-resolve
316
+ git push origin milestone/{slug}
317
+ git checkout staging
318
+ ```
319
+
320
+ ### Step 2.5: Pre-Merge Hunk-Loss Audit (MANDATORY before creating PR)
321
+
322
+ **WHY THIS EXISTS**: When a long-lived milestone branch is merged into staging, git hunks that exist in staging but were NOT touched by the milestone can be silently dropped. This happened in a 20h window that produced 13 regression-fix PRs (27% of total pipeline throughput) restoring lost code: postgres tuning params, completion_refund sweep, credit_ledger columns, browser flags, pool guards, Redis config, crawl TTL refresh, challenge_strategy guard. Each regression required investigation → new issue → new PR → review → merge — consuming nearly a third of pipeline capacity on damage repair instead of forward progress.
323
+
324
+ This step identifies "at-risk hunks" — staging-only content in files the milestone also modifies — and absorbs them into the milestone branch via rebase before the PR is created. After the rebase, even a squash merge will preserve all staging content because it's now part of the milestone branch's commits.
325
+
326
+ ```bash
327
+ cd {REPO_PATH}
328
+ git fetch origin staging milestone/{slug}
329
+
330
+ echo "=== Pre-Merge Hunk-Loss Audit ==="
331
+
332
+ # Files the milestone branch changes vs staging
333
+ MILESTONE_FILES=$(git diff --name-only origin/staging...origin/milestone/{slug})
334
+
335
+ if [ -z "$MILESTONE_FILES" ]; then
336
+ echo "No files changed by milestone vs staging — nothing to audit. Proceeding to PR creation."
337
+ else
338
+ # What does staging have in those files that the milestone branch doesn't carry?
339
+ # NOTE: $MILESTONE_FILES is intentionally unquoted here — word-splitting is required to pass
340
+ # each filename as a separate path argument to git diff. Quoting would collapse the newline-
341
+ # separated list into a single argument matching no real path, silently producing an empty diff.
342
+ # Filenames with spaces are not present in this codebase (AlterLab/Forge use standard paths).
343
+ AT_RISK=$(git diff origin/milestone/{slug}...origin/staging -- $MILESTONE_FILES 2>/dev/null)
344
+ AT_RISK_COUNT=$(echo "$AT_RISK" | grep -c '^@@' 2>/dev/null || echo 0)
345
+
346
+ if [ "$AT_RISK_COUNT" -eq 0 ]; then
347
+ echo "Hunk-loss audit: CLEAN — no at-risk staging hunks in milestone-modified files."
348
+ else
349
+ echo "AT-RISK HUNKS: $AT_RISK_COUNT staging hunks in milestone-modified files will be lost without rebase."
350
+ echo "Affected files:"
351
+ echo "$AT_RISK" | grep '^--- a/' | sed 's|^--- a/||'
352
+
353
+ echo "Rebasing milestone/{slug} onto origin/staging to absorb at-risk hunks..."
354
+ git checkout milestone/{slug}
355
+ git rebase origin/staging
356
+
357
+ REBASE_EXIT=$?
358
+ if [ "$REBASE_EXIT" -ne 0 ]; then
359
+ # Capture conflicting files BEFORE aborting (after abort, unmerged state is gone)
360
+ CONFLICTING_FILES=$(git diff --name-only --diff-filter=U 2>/dev/null)
361
+ [ -z "$CONFLICTING_FILES" ] && CONFLICTING_FILES="unknown — check git status after resolving manually"
362
+
363
+ # Abort the failed rebase and return to a clean state
364
+ git rebase --abort 2>/dev/null || true
365
+ git checkout staging
366
+
367
+ # Truncate AT_RISK to safe size for issue body (avoid shell quoting issues with large diffs)
368
+ AT_RISK_SNIPPET=$(echo "$AT_RISK" | head -60 | sed "s/'/'\\\\''/g")
369
+
370
+ gh issue create -R {GH_REPO} \
371
+ --title "fix(milestone): rebase conflict during hunk-loss audit for milestone/{slug}" \
372
+ --body "$(cat <<ISSUE_EOF
373
+ ## Problem
374
+
375
+ The pre-merge hunk-loss audit for milestone \`{slug}\` detected $AT_RISK_COUNT at-risk staging hunks, then attempted to rebase \`milestone/{slug}\` onto \`origin/staging\` to absorb them. The rebase conflicted and was aborted. Manual resolution is required before the milestone can ship.
376
+
377
+ ## Root Cause
378
+
379
+ Divergent changes between \`milestone/{slug}\` and \`origin/staging\` cannot be automatically rebased. Conflicting files must be resolved manually.
380
+
381
+ ## Affected Files
382
+
383
+ Files with rebase conflicts:
384
+ \`\`\`
385
+ ${CONFLICTING_FILES}
386
+ \`\`\`
387
+
388
+ **At-risk diff** (staging content that would be lost):
389
+ \`\`\`diff
390
+ ${AT_RISK_SNIPPET}
391
+ \`\`\`
392
+
393
+ ## Acceptance Criteria
394
+
395
+ - [ ] Rebase conflict in milestone/{slug} resolved manually
396
+ - [ ] \`git push origin milestone/{slug} --force-with-lease\` succeeds
397
+ - [ ] \`/milestone ship {slug}\` completes without conflict
398
+
399
+ ## Context
400
+
401
+ Resolve the rebase conflict manually:
402
+ \`\`\`bash
403
+ git checkout milestone/{slug}
404
+ git rebase origin/staging
405
+ # resolve conflicts in the files listed above
406
+ git rebase --continue
407
+ git push origin milestone/{slug} --force-with-lease
408
+ \`\`\`
409
+ Then re-run \`/milestone ship {slug}\`.
410
+
411
+ <!-- AUTO-CREATED: hunk-loss audit rebase conflict -->
412
+ ISSUE_EOF
413
+ )" \
414
+ --label "bug" --label "priority:P0" --label "needs-human"
415
+
416
+ echo "STOP: Rebase conflict. Created issue for manual resolution. Do NOT proceed to PR creation."
417
+ echo "Run '/milestone ship {slug}' again after resolving the conflict."
418
+ exit 1
419
+ fi
420
+
421
+ # Rebase succeeded — push the rebased branch
422
+ git push origin milestone/{slug} --force-with-lease
423
+ echo "Rebase complete. Milestone branch now includes all $AT_RISK_COUNT at-risk staging hunks."
424
+ git checkout staging
425
+ fi
426
+ fi
427
+ ```
428
+
429
+ ### Step 3: Create shipping PR
430
+
431
+ ```bash
432
+ # Get the full diff summary (computed after Step 2.5 rebase, so it reflects the final state)
433
+ DIFF_STATS=$(git diff origin/staging...origin/milestone/{slug} --stat)
434
+ COMMIT_LOG=$(git log origin/staging..origin/milestone/{slug} --oneline)
435
+
436
+ gh pr create \
437
+ --base staging \
438
+ --head milestone/{slug} \
439
+ --title "Ship: {MILESTONE_TITLE}" \
440
+ --body "$(cat <<'PR_EOF'
441
+ ## Milestone: {MILESTONE_TITLE}
442
+
443
+ {MILESTONE_DESCRIPTION}
444
+
445
+ ## Issues Included
446
+ {list of all closed issues in this milestone with PR references}
447
+
448
+ ## Hunk-Loss Audit
449
+ {If Step 2.5 rebase ran: "✓ Rebase completed — {AT_RISK_COUNT} at-risk staging hunks absorbed into milestone branch before this PR was created. All staging content is preserved."}
450
+ {If Step 2.5 found no at-risk hunks: "✓ Clean — no at-risk staging hunks detected. Safe to merge."}
451
+
452
+ > ⚠ **IMPORTANT: Merge this PR using a MERGE COMMIT, not squash.** Squash-merging a milestone PR can silently drop staging-only hunks in files the milestone touches. Use the "Create a merge commit" option in the GitHub UI, or `gh pr merge {PR_NUMBER} --merge`.
453
+
454
+ ## Diff Summary
455
+ {DIFF_STATS}
456
+
457
+ ## Commits
458
+ {COMMIT_LOG}
459
+ PR_EOF
460
+ )"
461
+ ```
462
+
463
+ ### Step 4: Run staging review
464
+
465
+ Use the `/review-pr` skill to review the shipping PR. This is a comprehensive review of the entire feature set:
466
+
467
+ ```
468
+ Skill(skill="review-pr", args="{PR_NUMBER}")
469
+ ```
470
+
471
+ ### Step 5: Report — PR is ready for user to merge
472
+
473
+ ```
474
+ ## Ready to Ship: {MILESTONE_TITLE}
475
+
476
+ PR #{PR_NUMBER}: milestone/{slug} → staging
477
+ - {X} issues resolved
478
+ - {Y} files changed
479
+ - Review: {PASSED/FINDINGS}
480
+
481
+ **Merging this PR lands the milestone on staging.** To deploy to production, merge `staging → main` via the GitHub web UI when ready.
482
+ PR link: {PR_URL}
483
+ ```
484
+
485
+ **STOP here.** The agent does NOT merge this PR. The user merges it manually via the GitHub web UI when they're ready to deploy.
486
+
487
+ If the user explicitly says "merge it" or "deploy it" in the chat, THEN proceed to Step 6.
488
+
489
+ ### Step 6: Merge and cleanup (ONLY with explicit user authorization)
490
+
491
+ **Only execute this step if the user explicitly authorizes the merge in the current conversation.** The user may say things like "merge it", "ship it", "land it on staging". Without this explicit authorization, STOP at Step 5.
492
+
493
+ **CRITICAL: NEVER use `--squash` for milestone PRs.** Squash-merging collapses all milestone commits into one and can silently discard staging-only hunks in files the milestone touches — even after Step 2.5 rebase, if someone squashes manually via the GitHub UI, staging content can be lost. Always use `--merge` (merge commit). If the repository policy forces squash-only merges, the Step 2.5 rebase (which absorbs at-risk hunks into the milestone branch) is the only safe mitigation — but a merge commit is strongly preferred.
494
+
495
+ **If the PR was rejected or closed without merging**: do nothing here. Milestone stays open, all issues remain assigned to it, and the milestone branch stays intact. The code those issues reference only exists on the milestone branch — moving them to fast lane would be wrong. Log the rejection and stop:
496
+
497
+ ```
498
+ Shipping attempt for "{MILESTONE_TITLE}" was rejected (PR #{PR_NUMBER} closed without merging).
499
+ Milestone stays open. Issues remain assigned. Branch milestone/{slug} preserved.
500
+ Run /milestone ship {slug} again when the milestone is ready for another shipping attempt.
501
+ ```
502
+
503
+ **If the PR merged successfully**, run the following:
504
+
505
+ ```bash
506
+ gh pr merge {PR_NUMBER} --merge
507
+
508
+ # Step 6A: Promote remaining open issues to fast lane
509
+ # Query open issues still on this milestone (per_page=100 to avoid silent truncation beyond default 30)
510
+ OPEN_ISSUES=$(gh api "repos/{owner}/{repo}/issues?milestone={MS_NUMBER}&state=open&per_page=100" \
511
+ --jq '.[].number')
512
+ OPEN_COUNT=$(echo "$OPEN_ISSUES" | grep -c '[0-9]' 2>/dev/null || echo 0)
513
+
514
+ if [ "$OPEN_COUNT" -gt 0 ]; then
515
+ echo "Promoting $OPEN_COUNT open issue(s) to fast lane (removing milestone assignment)..."
516
+ for ISSUE_NUM in $OPEN_ISSUES; do
517
+ gh api "repos/{owner}/{repo}/issues/$ISSUE_NUM" -X PATCH --field milestone=null
518
+ echo " Demoted #$ISSUE_NUM → fast lane"
519
+ done
520
+ else
521
+ echo "No open issues remain on the milestone — nothing to demote."
522
+ fi
523
+
524
+ # Step 6B: Close the milestone
525
+ gh api repos/{owner}/{repo}/milestones/{MILESTONE_NUMBER} --method PATCH --field state="closed"
526
+
527
+ # Step 6C: Delete the milestone branch
528
+ git push origin --delete milestone/{slug}
529
+ git branch -D milestone/{slug} 2>/dev/null
530
+ ```
531
+
532
+ Report:
533
+ ```
534
+ ## Shipped: {MILESTONE_TITLE}
535
+
536
+ Merged to staging. Milestone branch deleted.
537
+ {If OPEN_COUNT > 0: "{OPEN_COUNT} open issue(s) moved to fast lane: #{list}. They will target staging on next /work-on run."}
538
+ {If OPEN_COUNT == 0: "No open issues remaining — clean close."}
539
+
540
+ To deploy: merge `staging → main` via GitHub web UI when ready.
541
+ ```
542
+
543
+ ---
544
+
545
+ ## Action: Sync Milestone Branch
546
+
547
+ Syncs the milestone branch with the latest `main` to pick up fast-lane fixes. Run periodically on long-lived milestones.
548
+
549
+ ```bash
550
+ cd {REPO_PATH} # default: /home/mrdubey/projects/ScraperAPI/alterlab
551
+ git fetch origin main milestone/{slug}
552
+ git checkout milestone/{slug}
553
+ git merge origin/main --no-edit
554
+ # If conflicts, report to user and STOP
555
+ git push origin milestone/{slug}
556
+ git checkout main
557
+ ```
558
+
559
+ Report: "Synced milestone/{slug} with latest main. {N} new commits from main incorporated."
560
+
561
+ ---
562
+
563
+ ## Action: Close Milestone (without shipping)
564
+
565
+ For milestones that are abandoned or superseded:
566
+
567
+ ```bash
568
+ gh api repos/{owner}/{repo}/milestones/{MILESTONE_NUMBER} --method PATCH --field state="closed"
569
+
570
+ # Optionally delete the branch
571
+ git push origin --delete milestone/{slug} 2>/dev/null
572
+ ```
573
+
574
+ Open issues remain open but lose their milestone assignment. They can be reassigned to a new milestone.
575
+
576
+ ---
577
+
578
+ ## Assigning Existing Issues to Milestones
579
+
580
+ When the user says "add #123 to milestone X":
581
+
582
+ ```bash
583
+ # Find milestone number
584
+ MILESTONE_NUMBER=$(gh api repos/{owner}/{repo}/milestones --jq '.[] | select(.title | ascii_downcase | contains("{slug}")) | .number')
585
+
586
+ # Assign issue to milestone
587
+ gh issue edit {NUMBER} --milestone "{TITLE}"
588
+ ```
589
+
590
+ This automatically makes the issue a feature-lane issue — next time `/work-on` picks it up, the PR will target `milestone/{slug}`.
591
+
592
+ ---
593
+
594
+ ## Error Handling
595
+
596
+ - **Milestone already exists**: Check first with `gh api repos/{owner}/{repo}/milestones --jq '.[] | select(.title == "{TITLE}")'`
597
+ - **Branch already exists**: Reuse it, don't recreate
598
+ - **Merge conflicts on sync/ship**: Report to user, do NOT auto-resolve. List conflicting files.
599
+ - **No issues in milestone**: Warn user — empty milestones are valid but unusual