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.
- package/LICENSE +244 -0
- package/README.md +251 -0
- package/bin/forgedock.mjs +232 -0
- package/commands/analytics.md +777 -0
- package/commands/audit-agents.md +410 -0
- package/commands/audit.md +410 -0
- package/commands/autopilot.md +432 -0
- package/commands/cleanup.md +264 -0
- package/commands/deploy-info.md +292 -0
- package/commands/failure-recon.md +375 -0
- package/commands/forge-stats.md +183 -0
- package/commands/geo-audit.md +343 -0
- package/commands/incident-response.md +384 -0
- package/commands/issue.md +506 -0
- package/commands/milestone.md +599 -0
- package/commands/orchestrate.md +1044 -0
- package/commands/pipeline-health.md +2343 -0
- package/commands/qa-sweep.md +237 -0
- package/commands/quality-gate.md +777 -0
- package/commands/review-pr-agents.md +1711 -0
- package/commands/review-pr-staging.md +325 -0
- package/commands/review-pr.md +888 -0
- package/commands/rollback.md +253 -0
- package/commands/security-audit.md +529 -0
- package/commands/sync-ecosystem.md +527 -0
- package/commands/validate.md +131 -0
- package/commands/work-on/build/architect.md +291 -0
- package/commands/work-on/build/context.md +349 -0
- package/commands/work-on/build/implement.md +280 -0
- package/commands/work-on/build/validate.md +292 -0
- package/commands/work-on/build.md +262 -0
- package/commands/work-on/close.md +350 -0
- package/commands/work-on/decompose.md +254 -0
- package/commands/work-on/investigate.md +437 -0
- package/commands/work-on/review.md +245 -0
- package/commands/work-on-monolithic.md +298 -0
- package/commands/work-on.md +1089 -0
- package/docs/CODEX.md +92 -0
- package/docs/WORKFLOW.md +104 -0
- package/package.json +45 -0
- package/scripts/verify-env-vars.sh +201 -0
- package/scripts/verify-host-headers.sh +117 -0
- package/scripts/verify-route-registration.sh +118 -0
- 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
|