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.
Files changed (137) hide show
  1. package/README.md +4 -1
  2. package/bin/bootstrap.mjs +55 -0
  3. package/bin/engine/outcome-codes.mjs +13 -0
  4. package/bin/engine/phases.mjs +38 -2
  5. package/bin/engine/telemetry-seam.mjs +41 -6
  6. package/bin/engine/telemetry-seam.test.mjs +35 -0
  7. package/bin/engine-cli.mjs +616 -54
  8. package/bin/engine.mjs +13 -5
  9. package/bin/forgedock.mjs +169 -39
  10. package/bin/gc.mjs +580 -0
  11. package/bin/init-detect.mjs +68 -3
  12. package/bin/init-enrich.mjs +3 -0
  13. package/bin/init-repo.mjs +213 -0
  14. package/bin/journey.mjs +278 -43
  15. package/bin/lean/admission.mjs +33 -3
  16. package/bin/lean/batch.mjs +24 -5
  17. package/bin/lean/batch.test.mjs +54 -4
  18. package/bin/lean/checks.mjs +199 -0
  19. package/bin/lean/checks.test.mjs +183 -0
  20. package/bin/lean/conformance.mjs +235 -37
  21. package/bin/lean/conformance.test.mjs +143 -5
  22. package/bin/lean/credentials.mjs +29 -0
  23. package/bin/lean/depcache.mjs +312 -0
  24. package/bin/lean/depcache.test.mjs +298 -0
  25. package/bin/lean/drift.mjs +198 -0
  26. package/bin/lean/drift.test.mjs +223 -0
  27. package/bin/lean/effects.mjs +178 -16
  28. package/bin/lean/execution.e2e.test.mjs +81 -0
  29. package/bin/lean/execution.mjs +431 -0
  30. package/bin/lean/execution.test.mjs +583 -0
  31. package/bin/lean/heavy.mjs +289 -0
  32. package/bin/lean/heavy.test.mjs +207 -0
  33. package/bin/lean/lean.mjs +877 -151
  34. package/bin/lean/lean.test.mjs +1798 -28
  35. package/bin/lean/ledger.mjs +92 -0
  36. package/bin/lean/ledger.test.mjs +100 -0
  37. package/bin/lean/limits.mjs +58 -0
  38. package/bin/lean/limits.test.mjs +150 -0
  39. package/bin/lean/orchestrate.mjs +321 -19
  40. package/bin/lean/prompts.mjs +125 -17
  41. package/bin/lean/refactor.mjs +276 -0
  42. package/bin/lean/refactor.test.mjs +359 -0
  43. package/bin/lean/render.mjs +25 -6
  44. package/bin/lean/review-trigger.mjs +134 -0
  45. package/bin/lean/review-trigger.test.mjs +104 -0
  46. package/bin/lean/sandbox-provider.contract.test.mjs +257 -0
  47. package/bin/lean/sandbox-remote.mjs +142 -0
  48. package/bin/lean/sched.mjs +221 -0
  49. package/bin/lean/sched.test.mjs +310 -0
  50. package/bin/lean/schedd.mjs +247 -0
  51. package/bin/lean/schedd.test.mjs +180 -0
  52. package/bin/lean/toolchain.mjs +228 -0
  53. package/bin/lean/toolchain.test.mjs +154 -0
  54. package/bin/lean/verify.mjs +124 -0
  55. package/bin/lean/verify.test.mjs +118 -0
  56. package/bin/lean/worktree.mjs +69 -0
  57. package/bin/lean/worktree.test.mjs +141 -0
  58. package/bin/runner.mjs +53 -10
  59. package/bin/stats/github-source.mjs +1 -1
  60. package/bin/stats/index.mjs +14 -2
  61. package/bin/stats/query.mjs +72 -0
  62. package/bin/stats/stats.test.mjs +54 -0
  63. package/commands/adopt.md +3 -0
  64. package/commands/audit.md +5 -8
  65. package/commands/autopilot.md +2 -0
  66. package/commands/bootstrap.md +35 -0
  67. package/commands/cleanup.md +7 -22
  68. package/commands/forge-stats.md +2 -1
  69. package/commands/forgedock-init.md +125 -6
  70. package/commands/issue.md +89 -830
  71. package/commands/milestone.md +55 -6
  72. package/commands/orchestrate.md +10 -2
  73. package/commands/review-pr.md +2 -2
  74. package/commands/upgrade-deps.md +3 -3
  75. package/commands/work-on/build/validate.md +4 -0
  76. package/commands/work-on/close.md +2 -0
  77. package/commands/work-on/decompose.md +24 -6
  78. package/docs/CI.md +3 -3
  79. package/docs/CONFIG.md +227 -3
  80. package/docs/OUTCOME-CODES.md +11 -0
  81. package/docs/TELEMETRY.md +1 -1
  82. package/docs/site/command-reference.md +22 -0
  83. package/docs/site/getting-started.md +2 -0
  84. package/docs/site/start-a-new-project.md +327 -0
  85. package/package.json +1 -1
  86. package/scripts/affected-files-check.sh +91 -0
  87. package/scripts/affected-files-check.test.sh +80 -0
  88. package/scripts/bootstrap-create.mjs +393 -0
  89. package/scripts/bootstrap-validate.mjs +321 -0
  90. package/scripts/calibration.mjs +116 -0
  91. package/scripts/check-devdocs-freshness.sh +125 -0
  92. package/scripts/check-devdocs-freshness.test.sh +130 -0
  93. package/scripts/check-spec-markers.sh +3 -0
  94. package/scripts/check-spec-size.sh +29 -11
  95. package/scripts/check-spec-size.test.sh +27 -4
  96. package/scripts/ci-local/lib.mjs +8 -1
  97. package/scripts/ci-local/parallelism.test.mjs +27 -0
  98. package/scripts/ci-local/run.mjs +16 -5
  99. package/scripts/ci-local/scoping.mjs +82 -0
  100. package/scripts/ci-local.test.sh +14 -2
  101. package/scripts/fixtures/symbol-index/other/sample.rb +3 -0
  102. package/scripts/fixtures/symbol-index/py/pkg/__init__.py +1 -0
  103. package/scripts/fixtures/symbol-index/py/pkg/a.py +5 -0
  104. package/scripts/fixtures/symbol-index/py/pkg/b.py +14 -0
  105. package/scripts/fixtures/symbol-index/py/pkg/c.py +1 -0
  106. package/scripts/fixtures/symbol-index/py/sample.py +41 -0
  107. package/scripts/fixtures/symbol-index/ts/sample.ts +13 -0
  108. package/scripts/fixtures/symbol-move/gen_big.py +201 -0
  109. package/scripts/fixtures/symbol-move-ts/gen_big.mjs +56 -0
  110. package/scripts/forge-root.test.sh +1 -1
  111. package/scripts/hotspots.sh +100 -0
  112. package/scripts/hotspots.test.sh +101 -0
  113. package/scripts/issue-create.sh +197 -0
  114. package/scripts/issue-create.test.sh +166 -0
  115. package/scripts/maintenance-scan.sh +185 -0
  116. package/scripts/maintenance-scan.test.sh +89 -0
  117. package/scripts/milestone-drift.sh +206 -0
  118. package/scripts/milestone-drift.test.sh +120 -0
  119. package/scripts/northstar-docs.test.sh +68 -0
  120. package/scripts/refactor-verify.sh +368 -0
  121. package/scripts/refactor-verify.test.sh +202 -0
  122. package/scripts/review-prepare.sh +11 -9
  123. package/scripts/review-prepare.test.sh +14 -0
  124. package/scripts/structure-delta.sh +155 -0
  125. package/scripts/structure-delta.test.sh +136 -0
  126. package/scripts/symbol-index.mjs +253 -0
  127. package/scripts/symbol-index.py +354 -0
  128. package/scripts/symbol-index.test.sh +119 -0
  129. package/scripts/symbol-move-ts.test.sh +161 -0
  130. package/scripts/symbol-move.mjs +379 -0
  131. package/scripts/symbol-move.py +383 -0
  132. package/scripts/symbol-move.test.sh +168 -0
  133. package/templates/bootstrap/interview.md +34 -0
  134. package/templates/bootstrap/issue-body.md +42 -0
  135. package/templates/bootstrap/needs-human-issue.md +45 -0
  136. package/templates/bootstrap/stack-profiles.md +34 -0
  137. package/templates/bootstrap/technical-spike.md +75 -0
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: AI-powered forge.yaml config generator — scans codebase, queries GitHub, auto-fills all optional sections from detection
3
- argument-hint: "[--preserve | --interactive | --section <name>]"
3
+ argument-hint: "[--preserve | --interactive | --section <name> | --domain-labels <a,b,c>]"
4
4
  ---
5
5
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
6
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -23,13 +23,15 @@ You complete the `forge.yaml` configuration that `npx forgedock init` started. T
23
23
  | (none) | Fill all optional sections — overwrite existing values with newly detected ones, skip sections with nothing detected |
24
24
  | `--preserve` | Keep existing active section values unchanged — only fill sections that are not yet configured |
25
25
  | `--interactive` | Ask confirmation for every detected value and present menus for optional features |
26
- | `--section <name>` | Fill only one section: `repos`, `project_board`, `services`, `review`, or `verification` |
26
+ | `--section <name>` | Fill only one section: `repos`, `project_board`, `services`, `review`, `verification`, or `labels` |
27
+ | `--domain-labels <a,b,c>` | Domain labels to bootstrap on the repo (e.g. `frontend,backend,infra,design,legal-gated`) in addition to `bin/labels.json`. Overrides the list Phase 5E derives from the spec. |
27
28
 
28
29
  Parse `$ARGUMENTS` and set:
29
30
  ```
30
31
  PRESERVE = true if --preserve present
31
32
  INTERACTIVE = true if --interactive present (restores full questionnaire behavior for all sections)
32
33
  TARGET_SECTION = value from --section <name>, or "all"
34
+ DOMAIN_LABELS = value from --domain-labels <a,b,c>, or "" (derived in Phase 5E)
33
35
  ```
34
36
 
35
37
  If both `--preserve` and `--interactive` are present, `--interactive` takes precedence (`PRESERVE = false`).
@@ -37,7 +39,7 @@ If both `--preserve` and `--interactive` are present, `--interactive` takes prec
37
39
  If `--section` was provided, validate the value immediately:
38
40
 
39
41
  ```bash
40
- ALLOWED_SECTIONS="repos project_board services review verification"
42
+ ALLOWED_SECTIONS="repos project_board services review verification labels"
41
43
  if [ "$TARGET_SECTION" != "all" ]; then
42
44
  VALID=false
43
45
  for s in $ALLOWED_SECTIONS; do
@@ -46,7 +48,7 @@ if [ "$TARGET_SECTION" != "all" ]; then
46
48
  if [ "$VALID" = "false" ]; then
47
49
  echo "Error: '$TARGET_SECTION' is not a valid section name."
48
50
  echo ""
49
- echo "Valid sections: repos, project_board, services, review, verification"
51
+ echo "Valid sections: repos, project_board, services, review, verification, labels"
50
52
  echo ""
51
53
  echo "Usage: /forgedock-init --section <name>"
52
54
  exit 1
@@ -248,6 +250,17 @@ Build `DETECTED_ANALYTICS` list from findings:
248
250
  - `DETECTED_GA4=true` if GA4 references found
249
251
  - If none found, `DETECTED_ANALYTICS_EMPTY=true`
250
252
 
253
+ ### 2G: Boundary config check
254
+
255
+ Core work-on blocks a new package.json dependency between workspace packages that its contract does not name, and only reports import growth along existing ones (`scripts/structure-delta.sh`). An allow-all boundary rule makes that report the only signal, so suggest tightening it once.
256
+
257
+ ```bash
258
+ # eslint-plugin-boundaries, dependency-cruiser, Nx depConstraints (repo root only)
259
+ ls "$REPO_ROOT"/.eslintrc* "$REPO_ROOT"/eslint.config.* "$REPO_ROOT"/.dependency-cruiser.* "$REPO_ROOT"/nx.json 2>/dev/null | head -10
260
+ ```
261
+
262
+ Read each file found. Set `BOUNDARY_ALLOW_ALL=true` when a rule allows everything: an eslint-plugin-boundaries rule `{ from: [X], allow: ['*'] }` (or an `allow` list naming every element type), a dependency-cruiser config with no `forbidden` rules, or Nx `onlyDependOnLibsWithTags: ['*']`. Record the file and the rule as `BOUNDARY_ALLOW_ALL_AT`. Suggestion only: never edit the config. If no config is found, or the rules are scoped, leave it unset.
263
+
251
264
  ---
252
265
 
253
266
  ## Phase 3: GitHub Queries
@@ -262,6 +275,28 @@ gh auth status 2>&1
262
275
 
263
276
  If not authenticated, skip all GitHub queries and note which sections will need manual completion.
264
277
 
278
+ **Projects v2 scope check.** Board discovery and creation need the `read:project` (read) or `project` (read + write) OAuth scope, which `gh auth login` does not grant by default. Without it, `gh project list` fails — and that failure must NOT be reported as "zero boards":
279
+
280
+ ```bash
281
+ BOARD_SCOPE_MISSING=false
282
+ SCOPES=$(gh auth status 2>&1 | grep -i "Token scopes:" || true)
283
+ # Classic OAuth tokens list their scopes; a token with neither project scope cannot touch boards.
284
+ if [ -n "$SCOPES" ] && ! echo "$SCOPES" | grep -qE "'(read:)?project'"; then
285
+ BOARD_SCOPE_MISSING=true
286
+ fi
287
+ # Fine-grained / App tokens list no scopes — probe instead and match the missing-scope error.
288
+ if [ "$BOARD_SCOPE_MISSING" = "false" ] && gh project list --owner "$OWNER" --limit 1 2>&1 >/dev/null | grep -qiE "required scopes|read:project|INSUFFICIENT_SCOPES"; then
289
+ BOARD_SCOPE_MISSING=true
290
+ fi
291
+ if [ "$BOARD_SCOPE_MISSING" = "true" ]; then
292
+ echo "Your GitHub token lacks the read:project/project scope needed for Projects v2 boards."
293
+ echo "Grant it, then re-run /forgedock-init --section project_board:"
294
+ echo " gh auth refresh -h github.com -s read:project,project"
295
+ fi
296
+ ```
297
+
298
+ When `BOARD_SCOPE_MISSING=true`, skip Phases 3B, 3C, 4B and 5F; the Phase 5D summary shows `✗ project_board — token lacks read:project/project scope (run: gh auth refresh -h github.com -s read:project,project)`. Label bootstrap (Phase 5E) does not need this scope and still runs.
299
+
265
300
  ### 3B: Project board discovery
266
301
 
267
302
  ```bash
@@ -312,6 +347,7 @@ Store results in `SIBLING_REPOS`. Assess satellite signals for each repo — str
312
347
  - **Auto-detected value** → use it directly. No confirmation prompt.
313
348
  - **Genuine ambiguity** (e.g., multiple project boards, multiple candidates for the same field) → ask once with the detected candidates listed.
314
349
  - **Nothing detected** → skip the section silently. Do NOT offer a menu or ask "do you want X?".
350
+ Exception: `project_board` — with no board found, Phase 4B offers to create one (a new project has none to detect).
315
351
  - **`--interactive` flag** → override the above and ask confirmation/selection for every section, even when detection found a clear answer.
316
352
 
317
353
  ### 4A: repos section
@@ -346,8 +382,16 @@ Store results in `SIBLING_REPOS`. Assess satellite signals for each repo — str
346
382
 
347
383
  Which board should ForgeDock use? Enter number, or press Enter to skip:
348
384
  ```
349
- - Zero project boards found AND `INTERACTIVE=false` → skip this section silently.
350
- - `INTERACTIVE=true` → present all boards and ask which one to use.
385
+ - Zero project boards found → offer to create one (Phase 5F provisions it after `forge.yaml` is written):
386
+ ```
387
+ No Projects v2 board found under {OWNER}.
388
+ Create "{REPO} ForgeDock" with the ForgeDock fields (Status, Lane, Priority, Workflow, Component) and link it to {OWNER}/{REPO}? [Y/n]
389
+ ```
390
+ Set `BOARD_ACTION=create` on yes, else skip the section.
391
+ - Board(s) found but you are linking, not auto-using one (multiple boards, or `INTERACTIVE=true`) → the menu above also offers `[n] Create a new "{REPO} ForgeDock" board`. Picking an existing board sets `BOARD_ACTION=link`; picking new sets `BOARD_ACTION=create`. The single auto-used board is `BOARD_ACTION=link`.
392
+ - `BOARD_SCOPE_MISSING=true` (Phase 3A) → skip; the scope hint was already printed.
393
+
394
+ Whichever path is taken, Phase 5F makes sure the board has every ForgeDock field before the `project_board` IDs are written.
351
395
 
352
396
  ### 4C: services section
353
397
 
@@ -453,6 +497,7 @@ project_board:
453
497
  lane:
454
498
  fast: "{FAST_OPTION_ID}"
455
499
  feature: "{FEATURE_OPTION_ID}"
500
+ sync: "{SYNC_OPTION_ID}"
456
501
  priority:
457
502
  p0: "{P0_OPTION_ID}"
458
503
  p1: "{P1_OPTION_ID}"
@@ -499,6 +544,13 @@ verification:
499
544
  - "{SERVICE_PREFIX}"
500
545
  ```
501
546
 
547
+ **env** (optional, off by default; never auto-filled): lanes already read the repo's toolchain pins (`packageManager`, `.nvmrc`, `.node-version`, `.tool-versions`, `engines.node`) at lane setup. Write this section only when the user asks for it (`--interactive`). `toolchain` overrides the pinned package manager (or `off`); `setup` runs once per fresh lane worktree before the builder, e.g. a dev-DB reconcile script. See docs/CONFIG.md → `env`.
548
+ ```yaml
549
+ env:
550
+ toolchain: "{PM}@{VERSION}"
551
+ setup: "{SETUP_COMMAND}"
552
+ ```
553
+
502
554
  ### 5C: Section merge strategy
503
555
 
504
556
  For each optional section that is ALREADY ACTIVE in forge.yaml (detected in Phase 1C):
@@ -562,6 +614,69 @@ forge.yaml updated:
562
614
 
563
615
  If nothing changed for an overwritten section (detected values match existing values), show `(no change)` instead of a diff.
564
616
 
617
+ Add one line for each provisioning step below, e.g. `✓ project_board — created "Acme ForgeDock" (#3), linked to acme/app, 4 fields added` and `✓ labels — 36 ForgeDock labels + domain labels: frontend, backend`.
618
+
619
+ ### 5E: Bootstrap labels (after forge.yaml is written)
620
+
621
+ Runs on every invocation unless `--section` targets some other section. With `--preserve` it still runs: label creation is idempotent.
622
+
623
+ **Domain labels.** If `DOMAIN_LABELS` was supplied (`--domain-labels`), use it verbatim. Otherwise derive it from the spec — the Phase 2 scan, `CLAUDE.md` and `README.md`:
624
+ - web/UI framework (React, Next.js, Vue, Svelte, Angular, `app/`/`frontend/` dirs) → `frontend`
625
+ - server/API code (Express, FastAPI, Django, Rails, Go/Rust services, `api/`/`backend/`/`server/` dirs) → `backend`
626
+ - `Dockerfile`, `docker-compose*`, Terraform, Kubernetes/Helm manifests, deployment workflows → `infra`
627
+ - a design system, Figma links, Storybook, or `design/` dir → `design`
628
+ - legal, compliance, licensing or regulatory gating described in the spec → `legal-gated`
629
+
630
+ Ask once — listing the derived names — only when the spec is ambiguous (or `INTERACTIVE=true`); otherwise use the derived list directly. An empty list is fine.
631
+
632
+ ```bash
633
+ if [ -n "$DOMAIN_LABELS" ]; then
634
+ npx forgedock labels setup --repo "${OWNER}/${REPO}" --domain "$DOMAIN_LABELS"
635
+ else
636
+ npx forgedock labels setup --repo "${OWNER}/${REPO}"
637
+ fi
638
+ ```
639
+
640
+ This creates or updates every label in `bin/labels.json` plus one `Domain: <name>` label per domain name (known domains get fixed colors). Failures are reported, not fatal.
641
+
642
+ ### 5F: Create or link the Projects v2 board (after forge.yaml is written)
643
+
644
+ Run only when Phase 4B set `BOARD_ACTION` (and `BOARD_SCOPE_MISSING=false`).
645
+
646
+ **Create** (`BOARD_ACTION=create`):
647
+ ```bash
648
+ CREATED=$(gh project create --owner "$OWNER" --title "${REPO} ForgeDock" --format json)
649
+ PROJECT_NUMBER=$(echo "$CREATED" | jq -r '.number')
650
+ PROJECT_ID=$(echo "$CREATED" | jq -r '.id')
651
+ ```
652
+
653
+ **Link** to the repo (both actions; idempotent — an "already linked" error is fine):
654
+ ```bash
655
+ gh project link "$PROJECT_NUMBER" --owner "$OWNER" --repo "${OWNER}/${REPO}" 2>/dev/null || true
656
+ ```
657
+
658
+ **Ensure ForgeDock fields.** `Status` is built into every Projects v2 board (Todo / In Progress / Done) — map it, never create it. Create each other field that is missing (case-insensitive name match):
659
+
660
+ ```bash
661
+ EXISTING=$(gh project field-list "$PROJECT_NUMBER" --owner "$OWNER" --format json | jq -r '.fields[].name | ascii_downcase')
662
+ COMPONENT_OPTIONS="${DOMAIN_LABELS:-core}"
663
+ # One "field name|comma-separated options" row per ForgeDock field
664
+ while IFS='|' read -r FIELD_NAME FIELD_OPTIONS; do
665
+ echo "$EXISTING" | grep -qx "$(echo "$FIELD_NAME" | tr '[:upper:]' '[:lower:]')" && continue
666
+ gh project field-create "$PROJECT_NUMBER" --owner "$OWNER" --name "$FIELD_NAME" \
667
+ --data-type SINGLE_SELECT --single-select-options "$FIELD_OPTIONS"
668
+ done <<EOF
669
+ Lane|Fast,Feature,Sync
670
+ Priority|P0,P1,P2,P3
671
+ Workflow|Investigating,Building,In Review,Merged
672
+ Component|${COMPONENT_OPTIONS}
673
+ EOF
674
+ ```
675
+
676
+ Component options come from the Phase 5E domain-label list (fallback `core`), so board components and domain labels line up.
677
+
678
+ **Write the IDs.** Re-run the Phase 3C field/option queries against `PROJECT_NUMBER`, then write the `project_board` section (Phase 5B schema) into `forge.yaml`, replacing any previous `project_board` block, and keep `forge.yaml.bak` from 5D. If any `gh project` call fails with a missing-scope error, print `gh auth refresh -h github.com -s read:project,project` and stop this step.
679
+
565
680
  ---
566
681
 
567
682
  ## Phase 6: Validation
@@ -626,6 +741,9 @@ forge.yaml is ready.
626
741
  Validation results:
627
742
  {validation output from 6A–6D}
628
743
 
744
+ Board: {created "{TITLE}" (#{N}) | linked "{TITLE}" (#{N}) | skipped — token lacks read:project/project scope, run: gh auth refresh -h github.com -s read:project,project}
745
+ Labels: {N} ForgeDock labels + domain labels: {DOMAIN_LABELS or "none"}
746
+
629
747
  Next steps:
630
748
  1. If forge.yaml contains sensitive paths (credentials file, local paths):
631
749
  echo "forge.yaml" >> .gitignore
@@ -633,6 +751,7 @@ Next steps:
633
751
  /work-on next
634
752
  3. If any WARNING appeared above, edit forge.yaml to fix the flagged values.
635
753
  4. Backup file: forge.yaml.bak (delete when satisfied)
754
+ {If BOUNDARY_ALLOW_ALL (2G): "5. Your boundary config allows every package to import every package ({BOUNDARY_ALLOW_ALL_AT}). Consider tightening it per package once its design settles: core work-on reports import growth but only blocks undeclared package.json edges."}
636
755
 
637
756
  ForgeDock commands now use your project's config from forge.yaml.
638
757
  ```