forgedock 1.26.0 → 1.28.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/README.md +4 -1
- package/bin/bootstrap.mjs +55 -0
- package/bin/engine/outcome-codes.mjs +13 -0
- package/bin/engine/phases.mjs +38 -2
- package/bin/engine/telemetry-seam.mjs +41 -6
- package/bin/engine/telemetry-seam.test.mjs +35 -0
- package/bin/engine-cli.mjs +616 -54
- package/bin/engine.mjs +13 -5
- package/bin/forgedock.mjs +169 -39
- package/bin/gc.mjs +580 -0
- package/bin/init-detect.mjs +68 -3
- package/bin/init-enrich.mjs +3 -0
- package/bin/init-repo.mjs +213 -0
- package/bin/journey.mjs +278 -43
- package/bin/lean/admission.mjs +33 -3
- package/bin/lean/batch.mjs +24 -5
- package/bin/lean/batch.test.mjs +54 -4
- package/bin/lean/checks.mjs +199 -0
- package/bin/lean/checks.test.mjs +183 -0
- package/bin/lean/conformance.mjs +235 -37
- package/bin/lean/conformance.test.mjs +143 -5
- package/bin/lean/credentials.mjs +29 -0
- package/bin/lean/depcache.mjs +312 -0
- package/bin/lean/depcache.test.mjs +298 -0
- package/bin/lean/drift.mjs +198 -0
- package/bin/lean/drift.test.mjs +223 -0
- package/bin/lean/effects.mjs +178 -16
- package/bin/lean/execution.e2e.test.mjs +81 -0
- package/bin/lean/execution.mjs +431 -0
- package/bin/lean/execution.test.mjs +583 -0
- package/bin/lean/heavy.mjs +289 -0
- package/bin/lean/heavy.test.mjs +207 -0
- package/bin/lean/lean.mjs +877 -151
- package/bin/lean/lean.test.mjs +1798 -28
- package/bin/lean/ledger.mjs +92 -0
- package/bin/lean/ledger.test.mjs +100 -0
- package/bin/lean/limits.mjs +58 -0
- package/bin/lean/limits.test.mjs +150 -0
- package/bin/lean/orchestrate.mjs +321 -19
- package/bin/lean/prompts.mjs +125 -17
- package/bin/lean/refactor.mjs +276 -0
- package/bin/lean/refactor.test.mjs +359 -0
- package/bin/lean/render.mjs +25 -6
- package/bin/lean/review-trigger.mjs +134 -0
- package/bin/lean/review-trigger.test.mjs +104 -0
- package/bin/lean/sandbox-provider.contract.test.mjs +257 -0
- package/bin/lean/sandbox-remote.mjs +142 -0
- package/bin/lean/sched.mjs +221 -0
- package/bin/lean/sched.test.mjs +310 -0
- package/bin/lean/schedd.mjs +247 -0
- package/bin/lean/schedd.test.mjs +180 -0
- package/bin/lean/toolchain.mjs +228 -0
- package/bin/lean/toolchain.test.mjs +154 -0
- package/bin/lean/verify.mjs +124 -0
- package/bin/lean/verify.test.mjs +118 -0
- package/bin/lean/worktree.mjs +69 -0
- package/bin/lean/worktree.test.mjs +141 -0
- package/bin/runner.mjs +53 -10
- package/bin/stats/github-source.mjs +1 -1
- package/bin/stats/index.mjs +14 -2
- package/bin/stats/query.mjs +72 -0
- package/bin/stats/stats.test.mjs +54 -0
- package/commands/adopt.md +3 -0
- package/commands/audit.md +5 -8
- package/commands/autopilot.md +2 -0
- package/commands/bootstrap.md +35 -0
- package/commands/cleanup.md +7 -22
- package/commands/forge-stats.md +2 -1
- package/commands/forgedock-init.md +125 -6
- package/commands/issue.md +89 -830
- package/commands/milestone.md +55 -6
- package/commands/orchestrate.md +10 -2
- package/commands/review-pr.md +2 -2
- package/commands/upgrade-deps.md +3 -3
- package/commands/work-on/build/validate.md +4 -0
- package/commands/work-on/close.md +2 -0
- package/commands/work-on/decompose.md +24 -6
- package/docs/CI.md +3 -3
- package/docs/CONFIG.md +227 -3
- package/docs/OUTCOME-CODES.md +11 -0
- package/docs/TELEMETRY.md +1 -1
- package/docs/site/command-reference.md +22 -0
- package/docs/site/getting-started.md +2 -0
- package/docs/site/start-a-new-project.md +327 -0
- package/package.json +1 -1
- package/scripts/affected-files-check.sh +91 -0
- package/scripts/affected-files-check.test.sh +80 -0
- package/scripts/bootstrap-create.mjs +393 -0
- package/scripts/bootstrap-validate.mjs +321 -0
- package/scripts/calibration.mjs +116 -0
- package/scripts/check-devdocs-freshness.sh +125 -0
- package/scripts/check-devdocs-freshness.test.sh +130 -0
- package/scripts/check-spec-markers.sh +3 -0
- package/scripts/check-spec-size.sh +29 -11
- package/scripts/check-spec-size.test.sh +27 -4
- package/scripts/ci-local/lib.mjs +8 -1
- package/scripts/ci-local/parallelism.test.mjs +27 -0
- package/scripts/ci-local/run.mjs +16 -5
- package/scripts/ci-local/scoping.mjs +82 -0
- package/scripts/ci-local.test.sh +14 -2
- package/scripts/fixtures/symbol-index/other/sample.rb +3 -0
- package/scripts/fixtures/symbol-index/py/pkg/__init__.py +1 -0
- package/scripts/fixtures/symbol-index/py/pkg/a.py +5 -0
- package/scripts/fixtures/symbol-index/py/pkg/b.py +14 -0
- package/scripts/fixtures/symbol-index/py/pkg/c.py +1 -0
- package/scripts/fixtures/symbol-index/py/sample.py +41 -0
- package/scripts/fixtures/symbol-index/ts/sample.ts +13 -0
- package/scripts/fixtures/symbol-move/gen_big.py +201 -0
- package/scripts/fixtures/symbol-move-ts/gen_big.mjs +56 -0
- package/scripts/forge-root.test.sh +1 -1
- package/scripts/hotspots.sh +100 -0
- package/scripts/hotspots.test.sh +101 -0
- package/scripts/issue-create.sh +197 -0
- package/scripts/issue-create.test.sh +166 -0
- package/scripts/maintenance-scan.sh +185 -0
- package/scripts/maintenance-scan.test.sh +89 -0
- package/scripts/milestone-drift.sh +206 -0
- package/scripts/milestone-drift.test.sh +120 -0
- package/scripts/northstar-docs.test.sh +68 -0
- package/scripts/refactor-verify.sh +368 -0
- package/scripts/refactor-verify.test.sh +202 -0
- package/scripts/review-prepare.sh +11 -9
- package/scripts/review-prepare.test.sh +14 -0
- package/scripts/structure-delta.sh +155 -0
- package/scripts/structure-delta.test.sh +136 -0
- package/scripts/symbol-index.mjs +253 -0
- package/scripts/symbol-index.py +354 -0
- package/scripts/symbol-index.test.sh +119 -0
- package/scripts/symbol-move-ts.test.sh +161 -0
- package/scripts/symbol-move.mjs +379 -0
- package/scripts/symbol-move.py +383 -0
- package/scripts/symbol-move.test.sh +168 -0
- package/templates/bootstrap/interview.md +34 -0
- package/templates/bootstrap/issue-body.md +42 -0
- package/templates/bootstrap/needs-human-issue.md +45 -0
- package/templates/bootstrap/stack-profiles.md +34 -0
- package/templates/bootstrap/technical-spike.md +75 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Start a New Project with ForgeDock"
|
|
3
|
+
description: "Go from a one-line idea to a validated, orchestrate-ready GitHub backlog with /bootstrap: interview, spike doc, dry-run preview with estimated cost, creation and the /orchestrate hand-off. Includes a worked example."
|
|
4
|
+
keywords: ["forgedock bootstrap", "new project backlog", "claude code new project", "ai project setup", "orchestrate"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Start a New Project with ForgeDock
|
|
8
|
+
|
|
9
|
+
[Getting Started](./getting-started.md) assumes a repo that already has issues. This guide covers a brand-new project. You start with an idea, `/bootstrap` turns it into a backlog of GitHub issues in build order, and `/orchestrate` builds that backlog one milestone at a time.
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
idea ──▶ /bootstrap ──▶ spike doc + plan ──▶ dry-run preview ──▶ backlog on GitHub ──▶ /orchestrate per milestone
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
All the GitHub writes happen in one deterministic script (`scripts/bootstrap-create.mjs`). The agent does the judgment work: the interview, the defaults and the plan. The script validates the plan and creates it. Flags are listed in the [command reference](./command-reference.md#bootstrap).
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- A GitHub repo for the new project (it can be empty). Set it in `forge.yaml` (`project.owner` / `project.repo`, from `npx forgedock init`) or pass `--repo OWNER/NAME`.
|
|
22
|
+
- GitHub CLI (`gh`) authenticated (`gh auth login`).
|
|
23
|
+
- Node.js 18 or higher.
|
|
24
|
+
- Claude Code with ForgeDock installed for `/bootstrap`, or just `npx forgedock` for the headless path.
|
|
25
|
+
|
|
26
|
+
## 1. From idea to `/bootstrap`
|
|
27
|
+
|
|
28
|
+
Start from a free-text idea, or from docs you already have:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
/bootstrap "a shared grocery list for households" # in a Claude Code session
|
|
32
|
+
/bootstrap docs/idea.md # start from existing docs
|
|
33
|
+
/bootstrap "<idea>" --dry-run # preview only, no GitHub writes
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Without an interactive session:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx forgedock bootstrap "a shared grocery list for households" --dry-run
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Headless mode can't ask you questions. It takes the recommended option for each one and lists it under "Here's what I assumed", so check that list before you create anything.
|
|
43
|
+
|
|
44
|
+
## 2. What `/bootstrap` does
|
|
45
|
+
|
|
46
|
+
1. **Interview.** It covers the aspects in `templates/bootstrap/interview.md` (users, core jobs, scope, platforms, stack, auth, data and tenancy, integrations, payments, compliance, deployment, budget and more) and skips anything your idea already answers.
|
|
47
|
+
2. **Fills the blanks.** Every unanswered aspect gets a sensible default with a one-line rationale. All of them appear in one editable "Here's what I assumed" list.
|
|
48
|
+
3. **Asks only genuine doubts.** These are choices that change architecture or cost, such as native vs PWA, data residency or self-hosted vs managed DB, plus the product name and repo owner, which are always asked. It asks at most 4 multiple-choice questions per batch, each with a recommended option.
|
|
49
|
+
4. **Writes the spike doc** to `docs/engineering/technical-spike.md` (or the path in `--spike <path>`). It must have the sections Stack, Repo layout, Runtime topology, Data model, Security and privacy, Design system and quality gates, Route/API map, Credentials and Milestone plan. Each section needs a `**Rationale**:` line.
|
|
50
|
+
5. **Writes a plan JSON** with milestones in build order (foundation first), a `SPIKE` parent issue, and sub-issues in the pipeline issue template. Each sub-issue lists planned files and acceptance criteria with `[type:*]` tags.
|
|
51
|
+
- Every credential, account or external input gets its own **needs-human** issue. Code issues may mention it, but they never depend on it. A mock keeps dev and CI green until the real thing exists.
|
|
52
|
+
6. **Validates** the plan with `scripts/bootstrap-validate.mjs` and fixes it until it passes. Nothing is ever created from an invalid plan.
|
|
53
|
+
|
|
54
|
+
## 3. Preview the backlog (dry run)
|
|
55
|
+
|
|
56
|
+
If you already have a plan file, you can preview it without an LLM call or any GitHub access:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx forgedock bootstrap --plan plan.json --dry-run
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The preview lists each milestone with its `milestone/<slug>` branch, the issues in build order and their dependencies, a DAG summary, and an **estimated agent cost**. The estimate is a flat rate per build issue: `$3` by default, or `costPerIssueUsd` in the plan, or `--cost-per-issue <usd>`. The spike and needs-human issues are excluded. It's a planning estimate, not a measured figure.
|
|
63
|
+
|
|
64
|
+
The spike doc has to exist on disk even for a dry run. If it's missing a section or a rationale, the script prints the error and writes nothing.
|
|
65
|
+
|
|
66
|
+
## 4. Create the backlog
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx forgedock bootstrap --plan plan.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This creates:
|
|
73
|
+
|
|
74
|
+
- the spike parent issue
|
|
75
|
+
- the milestones, plus a `milestone/<slug>` branch for each
|
|
76
|
+
- every sub-issue, with a `**Parent**` link to the spike, the plan's guardrails and domain rules added to its Context, and <code v-pre>#{{KEY}}</code> placeholders resolved to real issue numbers
|
|
77
|
+
- a board item for each issue (Status=Todo, Priority, Component)
|
|
78
|
+
- `FORGE:BOOTSTRAP_PLAN` and `FORGE:DECOMPOSED` comments on the spike, which is then labelled `workflow:decomposed`
|
|
79
|
+
|
|
80
|
+
Exit codes: `0` created (or previewed), `1` invalid plan or spike doc (nothing written), `2` usage error, `3` GitHub error.
|
|
81
|
+
|
|
82
|
+
**Resume by re-running.** GitHub is the state. Issues are reused by exact title plus their `**Parent**` link, milestones by title, branches if they already exist, and comments if their marker is already there. After a network or rate-limit failure (exit `3`), run the same command again.
|
|
83
|
+
|
|
84
|
+
## 5. The hand-off
|
|
85
|
+
|
|
86
|
+
Creation ends with a hand-off in three parts:
|
|
87
|
+
|
|
88
|
+
- **Created**: links to the build issues.
|
|
89
|
+
- **Needs a human**: links to the credential and account issues. Do these when you can. They block nothing, because the code issues use mocks until then.
|
|
90
|
+
- **Next commands**: one `/orchestrate milestone "<title>"` per milestone, in build order.
|
|
91
|
+
|
|
92
|
+
Run the orchestrate commands in that order. Each one builds its milestone's issues through the normal `/work-on` pipeline.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Worked example: Shelf, a shared grocery list
|
|
97
|
+
|
|
98
|
+
The idea: *"Shelf, a shared grocery list for households. Members invite each other by email and see items checked off live."*
|
|
99
|
+
|
|
100
|
+
### Assumptions and doubts
|
|
101
|
+
|
|
102
|
+
Headless, `/bootstrap` takes the recommended options and lists them:
|
|
103
|
+
|
|
104
|
+
```text
|
|
105
|
+
Here's what I assumed
|
|
106
|
+
- Platform: responsive web app (PWA later): a household list is used on phones, and a PWA avoids app-store review.
|
|
107
|
+
- Stack: Next.js + TypeScript + Postgres (Prisma): the closest preset for a small web app.
|
|
108
|
+
- Auth: email magic link: members already arrive through an email invite.
|
|
109
|
+
- Email: Resend, behind a mock mailer in dev and CI.
|
|
110
|
+
- Hosting: one web service plus managed Postgres.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### The spike doc
|
|
114
|
+
|
|
115
|
+
`docs/engineering/technical-spike.md` has every required section, each with a rationale:
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
# Shelf — technical spike
|
|
119
|
+
|
|
120
|
+
## Stack
|
|
121
|
+
Next.js (App Router) + TypeScript, Postgres via Prisma, pnpm.
|
|
122
|
+
**Rationale**: one language end to end; the closest stack preset for a small web app.
|
|
123
|
+
|
|
124
|
+
## Repo layout
|
|
125
|
+
Single app: `app/`, `lib/`, `prisma/`.
|
|
126
|
+
**Rationale**: one deployable; no monorepo until a second app exists.
|
|
127
|
+
|
|
128
|
+
## Runtime topology
|
|
129
|
+
One web service plus managed Postgres.
|
|
130
|
+
**Rationale**: the cheapest setup that still gives live updates for a few households.
|
|
131
|
+
|
|
132
|
+
## Data model
|
|
133
|
+
`Household`, `Member`, `List`, `Item`; every row carries `householdId`.
|
|
134
|
+
**Rationale**: the household is the sharing and access boundary.
|
|
135
|
+
|
|
136
|
+
## Security and privacy
|
|
137
|
+
Members only ever read their own household's rows; invites expire after 7 days.
|
|
138
|
+
**Rationale**: grocery lists are personal data.
|
|
139
|
+
|
|
140
|
+
## Design system and quality gates
|
|
141
|
+
shadcn/ui; `pnpm build`, lint and unit tests in CI.
|
|
142
|
+
**Rationale**: accessible defaults and fast feedback.
|
|
143
|
+
|
|
144
|
+
## Route/API map
|
|
145
|
+
`/api/lists`, `/api/invites`.
|
|
146
|
+
**Rationale**: one route per milestone feature.
|
|
147
|
+
|
|
148
|
+
## Credentials
|
|
149
|
+
Resend API key (needs-human); the mock mailer is used until it exists.
|
|
150
|
+
**Rationale**: agents cannot create third-party accounts.
|
|
151
|
+
|
|
152
|
+
## Milestone plan
|
|
153
|
+
1. Foundation: scaffold, schema. 2. Sharing: list API, invites.
|
|
154
|
+
**Rationale**: sharing needs the schema first.
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### The plan
|
|
158
|
+
|
|
159
|
+
Two milestones, a spike, four build issues and one needs-human issue. `INVITES` mentions <code v-pre>#{{RESEND_KEY}}</code> but doesn't depend on it, so it can be built and tested against the mock mailer.
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"labels": [
|
|
164
|
+
"frontend",
|
|
165
|
+
"backend",
|
|
166
|
+
"infra",
|
|
167
|
+
"needs-human"
|
|
168
|
+
],
|
|
169
|
+
"milestones": [
|
|
170
|
+
{
|
|
171
|
+
"key": "foundation",
|
|
172
|
+
"title": "Foundation"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"key": "sharing",
|
|
176
|
+
"title": "Sharing"
|
|
177
|
+
}
|
|
178
|
+
],
|
|
179
|
+
"spikeDoc": "docs/engineering/technical-spike.md",
|
|
180
|
+
"guardrails": [
|
|
181
|
+
"no email is sent without an explicit invite"
|
|
182
|
+
],
|
|
183
|
+
"domainRules": [
|
|
184
|
+
"one household per list"
|
|
185
|
+
],
|
|
186
|
+
"issues": [
|
|
187
|
+
{
|
|
188
|
+
"key": "SPIKE",
|
|
189
|
+
"title": "spike: Shelf stack, layout and milestone plan",
|
|
190
|
+
"milestone": "foundation",
|
|
191
|
+
"labels": [
|
|
192
|
+
"infra"
|
|
193
|
+
],
|
|
194
|
+
"deps": [],
|
|
195
|
+
"body": "## Problem\n\nDecide Shelf's stack, repo layout and milestone plan.\n\n## Root Cause (if known)\n\nGreenfield: not built yet.\n\n## Affected Files\n\n1. `docs/engineering/technical-spike.md` — new\n\n## Acceptance Criteria\n\n- [ ] Spike doc covers every section with a rationale [type:manual]\n\n## Context\n\n**Parent**: none (spike)\n\n## Dependencies\n\nNone.\n"
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"key": "SCAFFOLD",
|
|
199
|
+
"title": "scaffold: Next.js app with pnpm and CI",
|
|
200
|
+
"milestone": "foundation",
|
|
201
|
+
"labels": [
|
|
202
|
+
"frontend",
|
|
203
|
+
"infra"
|
|
204
|
+
],
|
|
205
|
+
"deps": [
|
|
206
|
+
"SPIKE"
|
|
207
|
+
],
|
|
208
|
+
"priority": "p0",
|
|
209
|
+
"body": "## Problem\n\nScaffold the Next.js app with pnpm and a CI workflow.\n\n## Root Cause (if known)\n\nGreenfield: not built yet.\n\n## Affected Files\n\n1. `package.json` — new\n2. `app/layout.tsx` — new\n3. `.github/workflows/ci.yml` — new\n\n## Acceptance Criteria\n\n- [ ] `pnpm build` succeeds [type:unit]\n\n## Context\n\n**Parent**: #{{SPIKE}}\n\n## Dependencies\n\nDepends on #{{SPIKE}} — needs the stack decision\n"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"key": "DB",
|
|
213
|
+
"title": "db: Postgres schema for households, lists and items",
|
|
214
|
+
"milestone": "foundation",
|
|
215
|
+
"labels": [
|
|
216
|
+
"backend"
|
|
217
|
+
],
|
|
218
|
+
"deps": [
|
|
219
|
+
"SCAFFOLD"
|
|
220
|
+
],
|
|
221
|
+
"priority": "p0",
|
|
222
|
+
"body": "## Problem\n\nPostgres schema for households, lists and items via Prisma.\n\n## Root Cause (if known)\n\nGreenfield: not built yet.\n\n## Affected Files\n\n1. `prisma/schema.prisma` — new\n\n## Acceptance Criteria\n\n- [ ] A member reads only their own household's lists [type:unit]\n\n## Context\n\n**Parent**: #{{SPIKE}}\n\n## Dependencies\n\nDepends on #{{SCAFFOLD}} — needs the app and package setup\n"
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"key": "LISTS_API",
|
|
226
|
+
"title": "lists: Shared list API with live updates",
|
|
227
|
+
"milestone": "sharing",
|
|
228
|
+
"labels": [
|
|
229
|
+
"backend"
|
|
230
|
+
],
|
|
231
|
+
"deps": [
|
|
232
|
+
"DB"
|
|
233
|
+
],
|
|
234
|
+
"priority": "p1",
|
|
235
|
+
"body": "## Problem\n\nAPI to add, check off and remove items, with live updates for everyone in the household.\n\n## Root Cause (if known)\n\nGreenfield: not built yet.\n\n## Affected Files\n\n1. `app/api/lists/route.ts` — new\n\n## Acceptance Criteria\n\n- [ ] Checking an item off shows up for a second member [type:api]\n\n## Context\n\n**Parent**: #{{SPIKE}}\n\n## Dependencies\n\nDepends on #{{DB}} — needs the schema\n"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"key": "INVITES",
|
|
239
|
+
"title": "invites: Email invites to join a household",
|
|
240
|
+
"milestone": "sharing",
|
|
241
|
+
"labels": [
|
|
242
|
+
"backend"
|
|
243
|
+
],
|
|
244
|
+
"deps": [
|
|
245
|
+
"LISTS_API"
|
|
246
|
+
],
|
|
247
|
+
"priority": "p1",
|
|
248
|
+
"body": "## Problem\n\nInvite a member by email. Sending uses Resend; the key comes from #{{RESEND_KEY}}, and until it exists dev and CI use the mock mailer.\n\n## Root Cause (if known)\n\nGreenfield: not built yet.\n\n## Affected Files\n\n1. `app/api/invites/route.ts` — new\n2. `lib/mailer.ts` — new\n\n## Acceptance Criteria\n\n- [ ] An invite link adds the member to the household [type:api]\n\n## Context\n\n**Parent**: #{{SPIKE}}\n\n## Dependencies\n\nDepends on #{{LISTS_API}} — needs households and lists\n"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"key": "RESEND_KEY",
|
|
252
|
+
"title": "resend: Create the Resend API key and store it",
|
|
253
|
+
"milestone": "sharing",
|
|
254
|
+
"labels": [
|
|
255
|
+
"needs-human"
|
|
256
|
+
],
|
|
257
|
+
"deps": [],
|
|
258
|
+
"needsHuman": true,
|
|
259
|
+
"body": "## Problem\n\nCreate the Resend API key that #{{INVITES}} uses to send email.\n\n### Setup steps\n\n1. Create an API key at https://resend.com/api-keys with sending access only.\n2. Store it as the `RESEND_API_KEY` deploy secret.\n\n### Environment variables\n\n- `RESEND_API_KEY` — Resend sending key\n\n### Secret handling\n\nNever commit the key or paste it into issues. Until it exists, dev and CI use the mock mailer.\n\n## Root Cause (if known)\n\nGreenfield: not built yet.\n\n## Affected Files\n\n1. `docs/setup/resend.md` — new\n\n## Acceptance Criteria\n\n- [ ] Key stored as a deploy secret [type:manual]\n- [ ] The real mailer replaces the mock in staging [type:manual]\n\n## Context\n\n**Parent**: #{{SPIKE}}\n\n## Dependencies\n\nNone.\n"
|
|
260
|
+
}
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
`node scripts/bootstrap-validate.mjs plan.json` prints `bootstrap-validate: OK (6 issues, 2 milestones)`.
|
|
266
|
+
|
|
267
|
+
### The dry-run preview
|
|
268
|
+
|
|
269
|
+
`npx forgedock bootstrap --plan plan.json --dry-run --repo acme/shelf` prints:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
## Bootstrap plan preview (dry run: nothing written to GitHub)
|
|
273
|
+
|
|
274
|
+
Spike: spike: Shelf stack, layout and milestone plan — doc `docs/engineering/technical-spike.md`
|
|
275
|
+
|
|
276
|
+
### 1. Foundation (`milestone/foundation`, 2 issues)
|
|
277
|
+
- scaffold: Next.js app with pnpm and CI
|
|
278
|
+
- db: Postgres schema for households, lists and items — depends on SCAFFOLD
|
|
279
|
+
|
|
280
|
+
### 2. Sharing (`milestone/sharing`, 3 issues)
|
|
281
|
+
- lists: Shared list API with live updates — depends on DB
|
|
282
|
+
- invites: Email invites to join a household — depends on LISTS_API
|
|
283
|
+
- resend: Create the Resend API key and store it [needs-human]
|
|
284
|
+
|
|
285
|
+
DAG: 5 sub-issues (4 build, 1 needs-human), 3 dependency edges, 2 milestones.
|
|
286
|
+
Estimated agent cost: ~$12.00 (4 build issues × $3/issue; needs-human and spike excluded).
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Create and hand off
|
|
290
|
+
|
|
291
|
+
`npx forgedock bootstrap --plan plan.json --repo acme/shelf` creates the backlog and prints the hand-off. The issue numbers below are illustrative:
|
|
292
|
+
|
|
293
|
+
```text
|
|
294
|
+
## Bootstrap hand-off
|
|
295
|
+
|
|
296
|
+
Spike: #1 spike: Shelf stack, layout and milestone plan — https://github.com/acme/shelf/issues/1
|
|
297
|
+
|
|
298
|
+
### Created
|
|
299
|
+
- #2 scaffold: Next.js app with pnpm and CI — https://github.com/acme/shelf/issues/2
|
|
300
|
+
- #3 db: Postgres schema for households, lists and items — https://github.com/acme/shelf/issues/3
|
|
301
|
+
- #4 lists: Shared list API with live updates — https://github.com/acme/shelf/issues/4
|
|
302
|
+
- #5 invites: Email invites to join a household — https://github.com/acme/shelf/issues/5
|
|
303
|
+
|
|
304
|
+
### Needs a human
|
|
305
|
+
- #6 resend: Create the Resend API key and store it — https://github.com/acme/shelf/issues/6
|
|
306
|
+
|
|
307
|
+
### Next commands (in build order)
|
|
308
|
+
1. /orchestrate milestone "Foundation"
|
|
309
|
+
2. /orchestrate milestone "Sharing"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Then:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
/orchestrate milestone "Foundation" # scaffold, then the schema
|
|
316
|
+
/orchestrate milestone "Sharing" # list API, then invites (against the mock mailer until #6 is done)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
If creation fails halfway, for example at issue #4, re-run `npx forgedock bootstrap --plan plan.json --repo acme/shelf`. Issues #1–#3 are reused and creation carries on from #4.
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Next
|
|
324
|
+
|
|
325
|
+
- [Command Reference: `/bootstrap`](./command-reference.md#bootstrap)
|
|
326
|
+
- [How It Works](./how-it-works.md): what `/orchestrate` and `/work-on` do with each issue
|
|
327
|
+
- [Troubleshooting & Recovery](./troubleshooting.md)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgedock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
4
4
|
"description": "Autonomous development pipeline for coding agents. GitHub as institutional memory, with native Claude Code and OpenCode entrypoints.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"forgedock": "bin/forgedock.mjs"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
3
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
4
|
+
#
|
|
5
|
+
# affected-files-check.sh — /issue Phase 3E rule 2: are the draft's Affected Files real? (forge#4172)
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# affected-files-check.sh <body-file> [--ref <git-ref>]... [--spike <spike-doc>]
|
|
9
|
+
#
|
|
10
|
+
# <body-file> issue body (markdown); paths are the backticked tokens containing '/' or '.'
|
|
11
|
+
# inside its `## Affected Files` section (up to the next `## ` heading).
|
|
12
|
+
# --ref git ref to check existence against (repeatable). Default: origin/staging then
|
|
13
|
+
# origin/main; refs that do not resolve are skipped. Run from the target repo.
|
|
14
|
+
# --spike greenfield mode: a path that exists at no ref still passes as PLANNED when it
|
|
15
|
+
# equals an entry of the spike doc's repo-layout section, or starts with an entry
|
|
16
|
+
# ending in '/'. The section is the first heading matching /repo layout/i; its
|
|
17
|
+
# entries are backticked paths plus the lines of fenced blocks (tree glyphs and
|
|
18
|
+
# `#` comments stripped). Entries are full repo-relative paths: indentation-based
|
|
19
|
+
# nesting is not reconstructed.
|
|
20
|
+
#
|
|
21
|
+
# Output: one `OK <path>` | `PLANNED <path>` | `MISSING <path>` line per path.
|
|
22
|
+
# Exit: 0 no MISSING | 1 any MISSING, or no paths found | 2 usage error (no body file,
|
|
23
|
+
# --spike file absent, spike doc without a repo-layout section or entries).
|
|
24
|
+
# Deterministic, no LLM. bash 3.2 portable.
|
|
25
|
+
|
|
26
|
+
set -u
|
|
27
|
+
BODY_FILE=""
|
|
28
|
+
SPIKE=""
|
|
29
|
+
REFS=""
|
|
30
|
+
while [ $# -gt 0 ]; do
|
|
31
|
+
case "$1" in
|
|
32
|
+
--ref) [ -n "${2:-}" ] || { echo "affected-files-check: --ref requires a value" >&2; exit 2; }
|
|
33
|
+
REFS="$REFS
|
|
34
|
+
$2"; shift 2 ;;
|
|
35
|
+
--spike) [ -n "${2:-}" ] || { echo "affected-files-check: --spike requires a value" >&2; exit 2; }
|
|
36
|
+
SPIKE="$2"; shift 2 ;;
|
|
37
|
+
--*) echo "affected-files-check: unknown argument: $1" >&2; exit 2 ;;
|
|
38
|
+
*) [ -z "$BODY_FILE" ] || { echo "affected-files-check: more than one body file" >&2; exit 2; }
|
|
39
|
+
BODY_FILE="$1"; shift ;;
|
|
40
|
+
esac
|
|
41
|
+
done
|
|
42
|
+
[ -n "$BODY_FILE" ] && [ -r "$BODY_FILE" ] || { echo "usage: affected-files-check.sh <body-file> [--ref <git-ref>]... [--spike <spike-doc>]" >&2; exit 2; }
|
|
43
|
+
[ -n "$REFS" ] || REFS="
|
|
44
|
+
origin/staging
|
|
45
|
+
origin/main"
|
|
46
|
+
|
|
47
|
+
# Backticked path-like tokens from the `## Affected Files` section.
|
|
48
|
+
PATHS=$(awk '/^## /{ins=($0 ~ /^## Affected Files[[:space:]]*$/); next} ins' "$BODY_FILE" \
|
|
49
|
+
| grep -oE '`[^` ]+`' | tr -d '`' | grep -E '[/.]' | sed 's|^\./||; s|:[0-9][0-9,-]*$||' | awk '!seen[$0]++' || true)
|
|
50
|
+
|
|
51
|
+
LAYOUT=""
|
|
52
|
+
if [ -n "$SPIKE" ]; then
|
|
53
|
+
[ -r "$SPIKE" ] || { echo "affected-files-check: spike doc not found: $SPIKE" >&2; exit 2; }
|
|
54
|
+
grep -qiE '^#+[[:space:]].*repo layout' "$SPIKE" || { echo "affected-files-check: spike doc has no 'Repo layout' section: $SPIKE" >&2; exit 2; }
|
|
55
|
+
LAYOUT=$(awk '
|
|
56
|
+
/^[[:space:]]*```/ { fence=!fence; next }
|
|
57
|
+
!fence && /^#+[[:space:]]/ { if (ins) exit; if (tolower($0) ~ /repo layout/) ins=1; next }
|
|
58
|
+
!ins { next }
|
|
59
|
+
fence { sub(/#.*/, ""); gsub(/├──|└──|│|─/, ""); gsub(/^[[:space:]]+|[[:space:]]+$/, ""); if ($0 != "") print; next }
|
|
60
|
+
{ while (match($0, /`[^` ]+`/)) { print substr($0, RSTART+1, RLENGTH-2); $0 = substr($0, RSTART+RLENGTH) } }
|
|
61
|
+
' "$SPIKE" | sed 's|^\./||' | grep -E '[/.]' | awk '!seen[$0]++' || true)
|
|
62
|
+
[ -n "$LAYOUT" ] || { echo "affected-files-check: spike doc 'Repo layout' section declares no paths: $SPIKE" >&2; exit 2; }
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
[ -n "$PATHS" ] || { echo "MISSING (no backticked paths in ## Affected Files)"; exit 1; }
|
|
66
|
+
|
|
67
|
+
exists_at_ref() { # path -> 0 when it exists at any resolvable ref
|
|
68
|
+
printf '%s\n' "$REFS" | while IFS= read -r r; do
|
|
69
|
+
[ -n "$r" ] || continue
|
|
70
|
+
git cat-file -e "$r:$1" 2>/dev/null && { echo y; break; }
|
|
71
|
+
done | grep -q y
|
|
72
|
+
}
|
|
73
|
+
in_layout() { # path -> 0 when it equals a declared entry or sits under a declared directory
|
|
74
|
+
printf '%s\n' "$LAYOUT" | while IFS= read -r e; do
|
|
75
|
+
[ -n "$e" ] || continue
|
|
76
|
+
if [ "$1" = "$e" ]; then echo y; break; fi
|
|
77
|
+
case "$e" in */) case "$1" in "$e"*) echo y; break ;; esac ;; esac
|
|
78
|
+
done | grep -q y
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
FAIL=0
|
|
82
|
+
while IFS= read -r p; do
|
|
83
|
+
[ -n "$p" ] || continue
|
|
84
|
+
if exists_at_ref "$p"; then echo "OK $p"
|
|
85
|
+
elif [ -n "$LAYOUT" ] && in_layout "$p"; then echo "PLANNED $p"
|
|
86
|
+
else echo "MISSING $p"; FAIL=1
|
|
87
|
+
fi
|
|
88
|
+
done <<EOF
|
|
89
|
+
$PATHS
|
|
90
|
+
EOF
|
|
91
|
+
exit $FAIL
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# affected-files-check.test.sh — tests for scripts/affected-files-check.sh (forge#4172)
|
|
3
|
+
#
|
|
4
|
+
# Builds a throwaway git repo under $TMPDIR; no network or GitHub access.
|
|
5
|
+
# Usage: bash scripts/affected-files-check.test.sh
|
|
6
|
+
# Exit code: 0 if all assertions pass, 1 if any fail.
|
|
7
|
+
#
|
|
8
|
+
# SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
|
|
9
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
10
|
+
|
|
11
|
+
set -uo pipefail
|
|
12
|
+
|
|
13
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
|
+
CHECK="$SCRIPT_DIR/affected-files-check.sh"
|
|
15
|
+
T=$(mktemp -d "${TMPDIR:-/tmp}/afc-test.XXXXXX")
|
|
16
|
+
trap 'rm -rf "$T"' EXIT
|
|
17
|
+
|
|
18
|
+
PASS=0; FAIL=0
|
|
19
|
+
expect() { # label expected actual
|
|
20
|
+
if [ "$2" = "$3" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); printf 'FAIL %s\n expected: %s\n actual: %s\n' "$1" "$2" "$3"; fi
|
|
21
|
+
}
|
|
22
|
+
run() { # args... -> "<exit>|<stdout lines joined by ;>"
|
|
23
|
+
local out rc
|
|
24
|
+
out=$(cd "$T/repo" && bash "$CHECK" "$@" 2>/dev/null); rc=$?
|
|
25
|
+
printf '%s|%s' "$rc" "$(printf '%s' "$out" | tr '\n' ';')"
|
|
26
|
+
}
|
|
27
|
+
body() { # name lines... -> body file path
|
|
28
|
+
local f="$T/$1.md"; shift
|
|
29
|
+
{ printf '## Problem\n\nx `not/listed.ts`\n\n## Affected Files\n\n'; printf '%s\n' "$@"; printf '\n## Acceptance Criteria\n\n- [ ] y\n'; } > "$f"
|
|
30
|
+
printf '%s' "$f"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
mkdir -p "$T/repo/src"
|
|
34
|
+
git -C "$T/repo" init -q -b main
|
|
35
|
+
printf 'x\n' > "$T/repo/src/existing.ts"
|
|
36
|
+
git -C "$T/repo" add -A && git -C "$T/repo" -c user.name=t -c user.email=t@t commit -qm init
|
|
37
|
+
|
|
38
|
+
cat > "$T/spike.md" <<'EOF'
|
|
39
|
+
# Technical spike
|
|
40
|
+
|
|
41
|
+
## Repo layout
|
|
42
|
+
|
|
43
|
+
Apps live in `apps/web/` and the shared schema is `packages/db/schema.sql`.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
apps/api/ # backend
|
|
47
|
+
├── apps/api/src/main.ts
|
|
48
|
+
└── docs/README.md
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Data model
|
|
52
|
+
|
|
53
|
+
`packages/billing/` is mentioned outside the layout section.
|
|
54
|
+
EOF
|
|
55
|
+
printf '# Spike\n\n## Stack\n\n`apps/web/`\n' > "$T/nolayout.md"
|
|
56
|
+
|
|
57
|
+
R="--ref main"
|
|
58
|
+
|
|
59
|
+
# Brownfield (no --spike): unchanged semantics — existing OK, missing MISSING.
|
|
60
|
+
expect "brownfield existing path" "0|OK src/existing.ts" "$(run "$(body b1 '1. `src/existing.ts`: the file')" $R)"
|
|
61
|
+
expect "brownfield missing path" "1|MISSING apps/web/page.tsx" "$(run "$(body b2 '1. `apps/web/page.tsx`: planned')" $R)"
|
|
62
|
+
expect "brownfield ignores paths outside the section" "0|OK src/existing.ts" "$(run "$(body b3 '- `src/existing.ts`')" $R)"
|
|
63
|
+
expect "brownfield unresolvable default refs -> MISSING" "1|MISSING src/existing.ts" "$(run "$(body b4 '- `src/existing.ts`')")"
|
|
64
|
+
|
|
65
|
+
# Greenfield (--spike): planned paths validated against the declared layout.
|
|
66
|
+
expect "greenfield path under declared dir" "0|PLANNED apps/web/app/page.tsx" "$(run "$(body g1 '- `apps/web/app/page.tsx`')" $R --spike "$T/spike.md")"
|
|
67
|
+
expect "greenfield path equal to declared file" "0|PLANNED packages/db/schema.sql" "$(run "$(body g2 '- `packages/db/schema.sql`')" $R --spike "$T/spike.md")"
|
|
68
|
+
expect "greenfield path from fenced tree" "0|PLANNED apps/api/src/main.ts;PLANNED apps/api/routes.ts" "$(run "$(body g3 '- `apps/api/src/main.ts`' '- `apps/api/routes.ts`')" $R --spike "$T/spike.md")"
|
|
69
|
+
expect "greenfield path outside layout" "1|PLANNED apps/web/x.ts;MISSING packages/billing/x.ts" "$(run "$(body g4 '- `apps/web/x.ts`' '- `packages/billing/x.ts`')" $R --spike "$T/spike.md")"
|
|
70
|
+
expect "greenfield existing path still OK" "0|OK src/existing.ts" "$(run "$(body g5 '- `src/existing.ts`')" $R --spike "$T/spike.md")"
|
|
71
|
+
expect "greenfield file prefix is not a directory" "1|MISSING packages/db/schema.sql.bak" "$(run "$(body g6 '- `packages/db/schema.sql.bak`')" $R --spike "$T/spike.md")"
|
|
72
|
+
|
|
73
|
+
# Usage errors.
|
|
74
|
+
expect "spike doc missing" "2|" "$(run "$(body u1 '- `apps/web/x.ts`')" $R --spike "$T/absent.md")"
|
|
75
|
+
expect "spike doc without repo layout" "2|" "$(run "$(body u2 '- `apps/web/x.ts`')" $R --spike "$T/nolayout.md")"
|
|
76
|
+
expect "no body file" "2|" "$(run)"
|
|
77
|
+
expect "body without Affected Files paths" "1|MISSING (no backticked paths in ## Affected Files)" "$(run "$(body e1 'Files to be identified during investigation.')" $R)"
|
|
78
|
+
|
|
79
|
+
echo "affected-files-check.test.sh: $PASS passed, $FAIL failed"
|
|
80
|
+
[ "$FAIL" -eq 0 ]
|