forgedock 1.0.21 → 1.1.1

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 (76) hide show
  1. package/README.md +227 -174
  2. package/bin/cinema.mjs +320 -0
  3. package/bin/demo.mjs +386 -0
  4. package/bin/engine/phases.mjs +148 -0
  5. package/bin/engine/projector.mjs +32 -0
  6. package/bin/engine/reconcile.mjs +11 -0
  7. package/bin/engine/runlog.mjs +79 -0
  8. package/bin/engine/state.mjs +27 -0
  9. package/bin/engine-cli.mjs +50 -0
  10. package/bin/engine.mjs +138 -0
  11. package/bin/forgedock.mjs +693 -1393
  12. package/bin/init-detect.mjs +95 -0
  13. package/bin/journey.mjs +783 -0
  14. package/bin/runner.mjs +847 -0
  15. package/bin/settings-hook.mjs +112 -0
  16. package/bin/tui.mjs +6 -3
  17. package/commands/autopilot.md +4 -4
  18. package/commands/cleanup.md +85 -39
  19. package/commands/deploy-info.md +11 -5
  20. package/commands/design-bench.md +1 -0
  21. package/commands/design-render-critique-loop.md +1 -0
  22. package/commands/design.md +1 -0
  23. package/commands/forge-stats.md +2 -1
  24. package/commands/issue.md +1 -1
  25. package/commands/optimize.md +1 -1
  26. package/commands/orchestrate.md +346 -34
  27. package/commands/quality-gate.md +168 -80
  28. package/commands/review-pr-agents.md +69 -45
  29. package/commands/review-pr.md +54 -6
  30. package/commands/scope.md +11 -3
  31. package/commands/security-audit.md +24 -16
  32. package/commands/sync-ecosystem.md +2 -2
  33. package/commands/work-on/build/architect.md +47 -3
  34. package/commands/work-on/build/context.md +36 -5
  35. package/commands/work-on/build/implement.md +16 -4
  36. package/commands/work-on/build/validate.md +54 -8
  37. package/commands/work-on/build.md +27 -9
  38. package/commands/work-on/close.md +131 -3
  39. package/commands/work-on/investigate.md +24 -1
  40. package/commands/work-on/review.md +24 -0
  41. package/commands/work-on-monolithic.md +1 -0
  42. package/commands/work-on.md +271 -11
  43. package/docs/BRAND.md +6 -2
  44. package/docs/CONFIG.md +39 -34
  45. package/docs/site/.vitepress/config.mjs +85 -0
  46. package/docs/site/annotations-explained.md +55 -0
  47. package/docs/site/command-learning-path.md +104 -0
  48. package/docs/site/command-reference.md +3 -0
  49. package/docs/site/getting-started.md +42 -13
  50. package/docs/site/index.md +1 -1
  51. package/docs/site/troubleshooting.md +208 -0
  52. package/docs/spec/forge-protocol-v1.md +768 -0
  53. package/docs/superpowers/plans/2026-07-03-cinematic-onboarding-tui.md +2339 -0
  54. package/docs/superpowers/plans/2026-07-03-durable-execution-engine.md +1223 -0
  55. package/docs/superpowers/specs/2026-07-03-cinematic-onboarding-tui-design.md +157 -0
  56. package/docs/superpowers/specs/2026-07-03-durable-execution-engine-design.md +194 -0
  57. package/examples/forgedock-demo/README.md +113 -0
  58. package/examples/forgedock-demo/bootstrap.sh +125 -0
  59. package/examples/forgedock-demo/issues/01-bug-delete-missing-auth.md +37 -0
  60. package/examples/forgedock-demo/issues/02-feature-filter-notes.md +37 -0
  61. package/examples/forgedock-demo/issues/03-refactor-router-table.md +33 -0
  62. package/examples/forgedock-demo/issues/04-perf-index-lookup.md +35 -0
  63. package/examples/forgedock-demo/issues/05-docs-api-reference.md +39 -0
  64. package/examples/forgedock-demo/labels.json +21 -0
  65. package/examples/forgedock-demo/package.json +16 -0
  66. package/examples/forgedock-demo/scripts/smoke.js +61 -0
  67. package/examples/forgedock-demo/src/auth.js +28 -0
  68. package/examples/forgedock-demo/src/db.js +66 -0
  69. package/examples/forgedock-demo/src/routes/notes.js +64 -0
  70. package/examples/forgedock-demo/src/server.js +79 -0
  71. package/package.json +13 -3
  72. package/scripts/verify-env-vars.sh +18 -2
  73. package/scripts/verify-host-headers.sh +4 -4
  74. package/scripts/verify-route-registration.sh +2 -2
  75. package/scripts/worktree-lifecycle.sh +202 -0
  76. package/templates/forge.yaml.minimal +44 -0
@@ -81,6 +81,30 @@ Do NOT push this branch. Human review required to identify the source of the mer
81
81
  fi
82
82
  ```
83
83
 
84
+ ## Phase R1: Non-Empty Commit Guard (MANDATORY — run before push) <!-- Added: forge#1305 -->
85
+
86
+ Before pushing, verify the branch has at least one commit ahead of the PR base. This is the last-line defense against the phantom-commit hazard: a session that resumed from a partial FORGE:BUILDER comment (without `:COMPLETE`) would have skipped the commit step and could otherwise push an empty branch.
87
+
88
+ ```bash
89
+ cd {WORKTREE_PATH}
90
+ # Count commits on this branch that are not reachable from origin/{PR_BASE}
91
+ COMMIT_COUNT=$(git rev-list --count HEAD ^origin/{PR_BASE} 2>/dev/null || echo "0")
92
+ if [ "$COMMIT_COUNT" -eq 0 ]; then
93
+ gh issue comment {NUMBER} {GH_FLAG} --body "## Push Blocked — No Commits Ahead of Base
94
+
95
+ Branch \`{BRANCH}\` has 0 commits ahead of \`origin/{PR_BASE}\`. Pushing this branch would create an empty PR.
96
+
97
+ **Likely cause**: Build was interrupted after the FORGE:BUILDER comment was posted (implement.md Phase I6) but before the commit was created (validate.md Phase V5). The branch was pushed with no implementation on it.
98
+
99
+ **Resolution**: Delete this branch, re-run \`/work-on {NUMBER}\` to restart the build phase. The partial FORGE:BUILDER comment (lacking \`FORGE:BUILDER:COMPLETE\`) will be detected and deleted, and the build will restart cleanly.
100
+
101
+ <!-- FORGE:PUSH_BLOCKED_EMPTY_BRANCH -->"
102
+ gh issue edit {NUMBER} {GH_FLAG} --add-label "needs-human"
103
+ exit 1
104
+ fi
105
+ echo "Commit count ahead of origin/{PR_BASE}: $COMMIT_COUNT — OK to push"
106
+ ```
107
+
84
108
  ## Phase R1: Push Branch
85
109
 
86
110
  ```bash
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  description: "[BENCHMARK] Monolithic work-on — single-prompt pipeline with no Skill sub-phase boundaries"
3
3
  argument-hint: [issue number]
4
+ install: internal
4
5
  ---
5
6
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
6
7
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -96,6 +96,74 @@ Satellite repos (those without a `staging` branch) receive fast-lane PRs directl
96
96
 
97
97
  ## Phase 0: Resolve Issue & Load Context
98
98
 
99
+ ### 0.0: Pre-Flight Checks (MANDATORY — run before any other Phase 0 step)
100
+
101
+ Validate the environment before the pipeline spends tokens. Each check fails fast with an actionable error and a pointer to the troubleshooting guide (`docs/site/troubleshooting.md`). Run all checks; report every failure, then STOP if any HARD check fails. <!-- Added: forge#1149 -->
102
+
103
+ ```bash
104
+ PREFLIGHT_FAILED=0
105
+
106
+ # Check 1 — forge.yaml present (HARD)
107
+ if [ ! -f forge.yaml ]; then
108
+ echo "ERROR: forge.yaml not found in the repository root."
109
+ echo " Fix: run \`npx forgedock init\` to generate one, or copy forge.yaml.example."
110
+ echo " See: docs/site/troubleshooting.md#1-forgeyaml-not-found"
111
+ PREFLIGHT_FAILED=1
112
+ fi
113
+
114
+ # Check 2 — yq installed; forge.yaml is valid YAML (HARD, only if present)
115
+ if [ -f forge.yaml ]; then
116
+ if ! command -v yq >/dev/null 2>&1; then
117
+ echo "ERROR: yq is not installed. The pipeline requires yq to parse forge.yaml."
118
+ echo " Fix: install yq — https://github.com/mikefarah/yq#install"
119
+ echo " See: docs/site/troubleshooting.md#2-forgeyaml-has-a-syntax-error"
120
+ PREFLIGHT_FAILED=1
121
+ elif ! yq '.' forge.yaml >/dev/null 2>&1; then
122
+ echo "ERROR: forge.yaml has a YAML syntax error."
123
+ echo " Fix: run \`yq '.' forge.yaml\` to locate the offending line, then correct the indentation/quoting."
124
+ echo " See: docs/site/troubleshooting.md#2-forgeyaml-has-a-syntax-error"
125
+ PREFLIGHT_FAILED=1
126
+ fi
127
+ fi
128
+
129
+ # Check 3 — gh CLI authenticated (HARD)
130
+ if ! gh auth status >/dev/null 2>&1; then
131
+ echo "ERROR: gh CLI is not authenticated. The pipeline cannot read or write GitHub state."
132
+ echo " Fix: run \`gh auth login\` (ensure repo scope), then \`gh auth status\` to confirm."
133
+ echo " See: docs/site/troubleshooting.md#3-gh-cli-not-authenticated"
134
+ PREFLIGHT_FAILED=1
135
+ fi
136
+
137
+ # Check 4 — workflow labels exist on the repo (SOFT — warn, auto-recoverable)
138
+ if [ -f forge.yaml ] && gh auth status >/dev/null 2>&1; then
139
+ GH_REPO_PF="$(yq -r '.project.owner + "/" + .project.repo' forge.yaml 2>/dev/null)"
140
+ if [ -n "$GH_REPO_PF" ] && ! gh label list -R "$GH_REPO_PF" --search "workflow:" 2>/dev/null | grep -q "workflow:"; then
141
+ echo "WARNING: ForgeDock workflow:* labels not found on $GH_REPO_PF."
142
+ echo " Fix: run \`npx forgedock labels setup\` (or \`--repo $GH_REPO_PF\`) to bootstrap them."
143
+ echo " See: docs/site/troubleshooting.md#9-missing-workflow-labels"
144
+ fi
145
+ fi
146
+
147
+ # Check 5 — GitHub API rate limit headroom (SOFT — warn)
148
+ if gh auth status >/dev/null 2>&1; then
149
+ RL_REMAINING="$(gh api rate_limit --jq '.resources.core.remaining' 2>/dev/null || echo '')"
150
+ if [ -n "$RL_REMAINING" ] && [ "$RL_REMAINING" -lt 100 ] 2>/dev/null; then
151
+ RL_RESET="$(gh api rate_limit --jq '.resources.core.reset' 2>/dev/null)"
152
+ echo "WARNING: GitHub API rate limit low ($RL_REMAINING remaining; resets at epoch $RL_RESET)."
153
+ echo " Fix: wait for the reset, reduce orchestration parallelism, or use a higher-limit PAT."
154
+ echo " See: docs/site/troubleshooting.md#10-github-api-rate-limit-exceeded"
155
+ fi
156
+ fi
157
+
158
+ if [ "$PREFLIGHT_FAILED" -eq 1 ]; then
159
+ echo "Pre-flight checks failed. Resolve the errors above and re-run /work-on {NUMBER}."
160
+ echo "Full recovery guide: docs/site/troubleshooting.md"
161
+ exit 1
162
+ fi
163
+ ```
164
+
165
+ Worktree/branch-already-exists and stale-label conditions are surfaced later (Phase 3E worktree creation and the `## Error Handling` section) with their own recovery guidance in `docs/site/troubleshooting.md`.
166
+
99
167
  ### 0A: Parse input
100
168
  Extract project prefix and issue number. If `next`/`pick`: list open issues sorted by priority, skip `needs-human` and `workflow:decomposed`, pick highest priority.
101
169
 
@@ -424,7 +492,16 @@ Use `forge.yaml → review.tech_stack` and the issue domain labels to identify e
424
492
  grep -rn '\.field_name\s*=' services/ # Attribute assignments
425
493
  ```
426
494
  If the field is written with different types in different code paths (e.g. dict in the standard path, string in the auth-gated path), document ALL variants. The fix must handle every variant — not just the one on the primary investigated code path. A type guard like `or {}` only protects against falsy values; a non-empty string is truthy and bypasses it.
427
- 4. Git blame — trace when/why the relevant code was written
495
+ 4. Git blame — trace when/why the relevant code was written. Run bounded, local commands (no network round-trip):
496
+ ```bash
497
+ # Introducing commit for each affected file (first commit that added it)
498
+ git log --reverse --format='%h %an %ad %s' --date=short -- {affected_file} | head -1
499
+ # Last-touch commit (most recent change)
500
+ git log -1 --format='%h %an %ad %s' --date=short -- {affected_file}
501
+ # Line-level blame for a specific suspect hunk, if the issue names one
502
+ git blame -L {start},{end} -- {affected_file}
503
+ ```
504
+ Record the introducing commit and last-touch commit for each primary affected file — this feeds the mandatory **History findings** field in Phase 1C.
428
505
  4.5. **Rogue commit pre-state comparison (conditional)**: If the issue body references a specific commit as rogue, bad, or unintended (e.g., "rogue commit `abc1234`", "bad commit", "this was never intended"), MUST run `git show {commit}^:{file}` to see the file before that commit. Compare the pre-commit state against the current file. Any block present in the current file but absent in the pre-commit state was introduced by that commit chain and is a candidate for full reversion — not just partial editing. Report the delta (pre vs. current) in the investigation report. Do NOT assume surrounding code near a named import/bug is correct simply because the issue only named a specific sub-problem. (Ref: forge#278 — investigator confirmed the broken import but never ran `git show 18a3a2cf3^:batch.py`; the surrounding 50-line feature gate was also rogue and was preserved by the fix PR, causing a P1 access regression for all non-Scale users)
429
506
  5. Domain context discovery (narrow scope only, 1–5 files):
430
507
  ```bash
@@ -432,6 +509,14 @@ Use `forge.yaml → review.tech_stack` and the issue domain labels to identify e
432
509
  gh issue list -R {GH_REPO} --state closed --limit 8 --search "{function_name}"
433
510
  ```
434
511
  Keep only file/function-level overlap. Max 5 related issues.
512
+
513
+ **Pickaxe pass (prior fix / regression detection)** — bounded to one pass, capped at 5 hits: search for prior additions/removals of the suspected symbol or literal string named in the issue, independent of whether that fix was ever linked to a filed issue:
514
+ ```bash
515
+ git log -S"{suspected_symbol_or_string}" --oneline -- {affected_files} | head -5
516
+ # Use -G instead of -S when the target is a regex pattern rather than a literal string
517
+ git log -G"{pattern}" --oneline -- {affected_files} | head -5
518
+ ```
519
+ Any hit here is a candidate prior fix or reintroduced defect — read the commit body (`git show {hash}`) to confirm before citing it. Feed confirmed hits into the History findings field and let them inform the verdict (e.g. a defect being reintroduced raises severity).
435
520
  6. Determine root cause
436
521
  7. Identify affected files — full list of files that need changes
437
522
  7.5. **Sibling Pattern Sweep** *(conditional — when the bug is a condition, gated function call, or field presence check)*: After identifying the affected files, grep for the same pattern in sibling files within the same directory. The issue spec may name only the file where the error was first observed — but the same commit or PR that introduced the bug often applied it uniformly across related handlers.
@@ -475,6 +560,12 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:INVESTIGATOR -->
475
560
  ### Evidence
476
561
  {specific findings — function names, line numbers, behavior observed}
477
562
 
563
+ ### History Findings
564
+ **Introducing commit**: {hash — author — date — subject, per primary affected file}
565
+ **Last touched**: {hash — author — date — subject}
566
+ **Pickaxe hits (prior fixes / regressions)**: {commit(s) found via \`git log -S\`/\`-G\`, or 'None found' — max 5}
567
+ {This field is MANDATORY — populate from the git blame + pickaxe commands in step 4/5. If a file is newly created (no history), write 'New file — no history.'}
568
+
478
569
  ### Recommendation
479
570
  {what to build/fix, concrete and actionable}
480
571
 
@@ -744,7 +835,17 @@ esac
744
835
 
745
836
  <!-- FORGE:PHASE_COMPLETE — Entering Phase 3 (Build). See Universal Phase Dispatcher: sub-phases 3A–3M execute in sequence. No sub-phase completion is terminal. -->
746
837
 
747
- **Skip if**: `<!-- FORGE:BUILDER -->` exists.
838
+ **Skip if**: `<!-- FORGE:BUILDER:COMPLETE -->` is present in a BUILDER comment. <!-- Added: forge#1305 — require completion marker, not mere presence of BUILDER annotation -->
839
+
840
+ **Partial build detection**: If `<!-- FORGE:BUILDER -->` exists BUT `<!-- FORGE:BUILDER:COMPLETE -->` is ABSENT → the build was interrupted after implement.md Phase I6 (comment posted) but before validate.md Phase V5 (commit). Delete the partial comment and restart Phase 3 from the top: <!-- Added: forge#1305 -->
841
+ ```bash
842
+ PARTIAL_ID=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
843
+ --jq '[.[] | select(.body | contains("FORGE:BUILDER") and (contains("FORGE:BUILDER:COMPLETE") | not))] | last | .id // ""')
844
+ if [ -n "$PARTIAL_ID" ]; then
845
+ gh api repos/{GH_REPO}/issues/comments/$PARTIAL_ID -X DELETE
846
+ echo "Deleted partial FORGE:BUILDER comment (no FORGE:BUILDER:COMPLETE) — restarting build"
847
+ fi
848
+ ```
748
849
 
749
850
  **CRITICAL: You MUST execute ALL sub-phases 3A–3M in order. Sub-phases 3C.5 (context) and 3C.6 (architect) are skipped ONLY for TRIVIAL tasks and Investigation tasks — see Phase 3B for classification. For STANDARD and COMPLEX tasks they post mandatory `FORGE:CONTEXT` and `FORGE:ARCHITECT` comments that Phase 3F reads as its primary input. Skipping them without a TRIVIAL/Investigation classification degrades build quality and causes review findings. After each sub-phase, continue to the next — no sub-phase is terminal.**
750
851
 
@@ -766,9 +867,9 @@ gh issue view {NUMBER} {GH_FLAG} --json number,title,body,labels,state,milestone
766
867
  gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
767
868
  --jq '.[] | select(.body | contains("FORGE:INVESTIGATOR")) | .body'
768
869
 
769
- # Check if build already completed
870
+ # Check if build already completed (require FORGE:BUILDER:COMPLETE — not just FORGE:BUILDER)
770
871
  gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
771
- --jq '.[] | select(.body | contains("FORGE:BUILDER")) | .body'
872
+ --jq '.[] | select(.body | contains("FORGE:BUILDER:COMPLETE")) | .body'
772
873
 
773
874
  # Check for existing COMPLEXITY_BAND from a prior run (resume path)
774
875
  EXISTING_FAST_PATH=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
@@ -889,7 +990,12 @@ Run these queries (20s timeout each, 2 min total budget):
889
990
 
890
991
  **C1: Past Review Findings on These Files**
891
992
  ```bash
892
- for file in {AFFECTED_FILES}; do
993
+ # {AFFECTED_FILES} is a space-separated argument (see --files contract) — split
994
+ # explicitly on IFS=' ' into an array instead of a bare `for file in {AFFECTED_FILES}`,
995
+ # which word-splits on the shell's default IFS (space, tab, AND newline) and
996
+ # would corrupt any path containing a space.
997
+ IFS=' ' read -ra AFFECTED_FILES_ARR <<< "{AFFECTED_FILES}"
998
+ for file in "${AFFECTED_FILES_ARR[@]}"; do
893
999
  basename=$(basename "$file" .py)
894
1000
  gh issue list -R {GH_REPO} --state closed --label "review-finding" \
895
1001
  --search "$basename" --limit 10 \
@@ -908,6 +1014,20 @@ git log --oneline -30 -- {AFFECTED_FILES} | grep -oE '#[0-9]+' | sort -u | head
908
1014
  # For each issue: fetch title + root cause, keep only bug/fix/review-finding labeled. Max 5.
909
1015
  ```
910
1016
 
1017
+ **Direct commit-body read (bounded, prefer over `gh api` when it already answers "why")**: read the top 5 commit subjects+bodies on the affected files directly — local git is near-free relative to `gh api` round-trips, and commit bodies often explain the "why" without needing to fetch a linked issue at all:
1018
+ ```bash
1019
+ git log -5 --format='%h %ad %s%n%b' --date=short -- {AFFECTED_FILES}
1020
+ ```
1021
+ If a commit body fully explains a prior bug/fix (common for squashed or fix-up commits with no `#NNN` reference), use it directly as a "Past Bug in This Module" entry — do not require a linked GitHub issue to exist.
1022
+
1023
+ **Pickaxe pass (has this exact area been fixed before?)** — one bounded pass, capped at 5 hits, keyed on the suspected symbol/string from the Builder Contract or investigation report:
1024
+ ```bash
1025
+ git log -S"{suspected_symbol_or_string}" --oneline -- {AFFECTED_FILES} | head -5
1026
+ # Use -G instead of -S for regex patterns
1027
+ git log -G"{pattern}" --oneline -- {AFFECTED_FILES} | head -5
1028
+ ```
1029
+ Any hit is a candidate prior fix or reintroduced defect for this exact code area — read `git show {hash}` to confirm scope before including it in the output. This catches regressions the issue-number harvest above misses (e.g. a defect fixed via a squashed commit with no `#NNN` reference).
1030
+
911
1031
  **C3: Related Code Paths** (callers/importers of FUNCTION_NAMES)
912
1032
  ```bash
913
1033
  for fn in {FUNCTION_NAMES}; do
@@ -934,7 +1054,8 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:CONTEXT -->
934
1054
  {past review-finding issues}
935
1055
 
936
1056
  ### Past Bugs in This Module
937
- {closed bug issues from git log mining}
1057
+ {closed bug issues from git log mining, PLUS pickaxe-derived findings (commits with no linked issue, or commit
1058
+ bodies read directly per the C2 direct-commit-body step)}
938
1059
 
939
1060
  ### Related Code Paths (must stay consistent)
940
1061
  {files that import/call changed functions}
@@ -1070,6 +1191,14 @@ esac
1070
1191
  **Determine source branch**:
1071
1192
  - Review-finding → parse `**Code branch**: \`{branch}\`` from issue body; branch from `origin/{branch}`
1072
1193
  - **Milestone review-finding hybrid lane** (Code branch matches `milestone/*`): High-risk lane. NEVER use `git merge` to resolve conflicts — use `git rebase` or `git cherry-pick` only. If conflicts can't be resolved without merge, post comment, add `needs-human`, STOP.
1194
+ - **Missing ref fallback**: After parsing, verify the Code branch still exists on remote. If not (e.g., the source PR's head branch was deleted post-merge before a corrected stamp took effect), fall back to `PR_BASE` (the lane default) and note the fallback:
1195
+ ```bash
1196
+ SOURCE_BRANCH="{CODE_BRANCH_FROM_ISSUE_BODY}"
1197
+ if ! git ls-remote --exit-code origin "$SOURCE_BRANCH" >/dev/null 2>&1; then
1198
+ echo "WARNING: Code branch '$SOURCE_BRANCH' not found on remote — falling back to PR_BASE '$PR_BASE'"
1199
+ SOURCE_BRANCH="$PR_BASE"
1200
+ fi
1201
+ ```
1073
1202
  - Feature lane (has milestone) → branch from `origin/{PR_BASE}` (PR_BASE now set above)
1074
1203
  - Fast lane (no milestone) → branch from `origin/{PR_BASE}` (PR_BASE = `{STAGING_BRANCH}`)
1075
1204
 
@@ -1178,6 +1307,11 @@ if iteration == max_iterations AND not PASS:
1178
1307
 
1179
1308
  All tool commands are read from `forge.yaml → verification.commands`. When a key is absent, the step logs `SKIPPED — not configured in verification.commands` and continues rather than silently passing.
1180
1309
 
1310
+ **Track skipped checks** — initialize before any check runs:
1311
+ ```bash
1312
+ VERIFICATION_SKIPPED_CHECKS=""
1313
+ ```
1314
+
1181
1315
  **Python**:
1182
1316
  ```bash
1183
1317
  cd {WORKTREE_PATH}
@@ -1187,6 +1321,7 @@ if [ -n "$PYTHON_FORMAT" ]; then
1187
1321
  eval "$PYTHON_FORMAT" 2>&1
1188
1322
  else
1189
1323
  echo "SKIPPED — python.format not configured in verification.commands"
1324
+ VERIFICATION_SKIPPED_CHECKS="${VERIFICATION_SKIPPED_CHECKS:+$VERIFICATION_SKIPPED_CHECKS, }python.format"
1190
1325
  fi
1191
1326
 
1192
1327
  # Compile check always runs (no config needed — catches syntax errors)
@@ -1206,6 +1341,7 @@ if [ -n "$TS_FORMAT" ]; then
1206
1341
  eval "$TS_FORMAT" 2>&1
1207
1342
  else
1208
1343
  echo "SKIPPED — typescript.format not configured in verification.commands"
1344
+ VERIFICATION_SKIPPED_CHECKS="${VERIFICATION_SKIPPED_CHECKS:+$VERIFICATION_SKIPPED_CHECKS, }typescript.format"
1209
1345
  fi
1210
1346
 
1211
1347
  if [ -n "$TS_TYPECHECK" ]; then
@@ -1216,6 +1352,7 @@ elif [ -n "$TS_BUILD" ]; then
1216
1352
  TS_EXIT=$?
1217
1353
  else
1218
1354
  echo "SKIPPED — typescript.typecheck and typescript.build not configured in verification.commands"
1355
+ VERIFICATION_SKIPPED_CHECKS="${VERIFICATION_SKIPPED_CHECKS:+$VERIFICATION_SKIPPED_CHECKS, }typescript.typecheck/build"
1219
1356
  TS_EXIT=0
1220
1357
  fi
1221
1358
  ```
@@ -1257,10 +1394,11 @@ Skip if no changed Python files contain DB engine/session/pool patterns.
1257
1394
 
1258
1395
  ```bash
1259
1396
  cd {WORKTREE_PATH}
1260
- for f in $(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.py$'); do
1397
+ while IFS= read -r f; do
1398
+ [ -z "$f" ] && continue
1261
1399
  grep -qE "create_async_engine|AsyncSession|connect_args|pool_size|prepared_statement|engine_from_config|sessionmaker" "$f" 2>/dev/null && \
1262
1400
  echo "DB CONFIG CHANGE DETECTED in: $f"
1263
- done
1401
+ done < <(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.py$')
1264
1402
  ```
1265
1403
 
1266
1404
  Advisory only — does not block build. Check for lambda/callable in connect_args (the exact bug class from PR #14391).
@@ -1335,7 +1473,7 @@ Stage all changes and commit:
1335
1473
  ```bash
1336
1474
  cd {WORKTREE_PATH}
1337
1475
  git add -u
1338
- git commit -m "fix({SCOPE}): {description} (#{NUMBER})"
1476
+ git commit -s -m "fix({SCOPE}): {description} (#{NUMBER})"
1339
1477
  ```
1340
1478
 
1341
1479
  Conventional prefix: `fix`/`feat`/`refactor`/`docs`. Reference `#{NUMBER}` in message.
@@ -1356,12 +1494,20 @@ Check off completed items, mark phases complete, add PR references.
1356
1494
 
1357
1495
  ### 3M: Post implementation comment
1358
1496
  ```bash
1497
+ # Compute verification status from VERIFICATION_SKIPPED_CHECKS (set in Phase 3H)
1498
+ if [ -z "$VERIFICATION_SKIPPED_CHECKS" ]; then
1499
+ VERIFICATION_STATUS="✅ All configured verification commands passed"
1500
+ else
1501
+ VERIFICATION_STATUS="⚠ Verification NOT run: ${VERIFICATION_SKIPPED_CHECKS} — verification.commands not configured for these checks"
1502
+ fi
1503
+
1359
1504
  gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:BUILDER -->
1360
1505
  ## Implementation Complete
1361
1506
 
1362
1507
  **Branch**: \`{BRANCH}\`
1363
1508
  **Commits**: {COMMIT_SHA(S)}
1364
1509
  **Files changed**: {COUNT}
1510
+ **Verification Status**: ${VERIFICATION_STATUS}
1365
1511
 
1366
1512
  ### Approach
1367
1513
  {what was built, key decisions}
@@ -1649,7 +1795,14 @@ fi
1649
1795
 
1650
1796
  ## Phase 7: Summary & Trajectory
1651
1797
 
1652
- ### 7A: Report
1798
+ ### 7A: Report + Pipeline Summary Card
1799
+
1800
+ Output the terse report, then render the shareable **Pipeline Summary Card** — the shareable
1801
+ moment a developer screenshots. Gather real stats (commits, additions/deletions, PR target,
1802
+ review summary, elapsed time) and render exactly as specified in `work-on/close.md` Phase C4.5
1803
+ (`C4.5a` stats gathering → `C4.5b` box-drawing card to stdout → `C4.5c` machine-readable twin).
1804
+ This inline path and the delegated `close.md` path MUST produce an identical card.
1805
+
1653
1806
  ```
1654
1807
  ## Done: #{NUMBER} — {TITLE}
1655
1808
  - Investigation: {VERDICT} ({CONFIDENCE})
@@ -1658,6 +1811,98 @@ fi
1658
1811
  - Files changed: {COUNT}
1659
1812
  ```
1660
1813
 
1814
+ Then print the card to stdout (inner width 51; truncate long titles with `…`; missing stats
1815
+ render `—`; pipeline line reflects the actual terminal state — merged / decomposed / invalid /
1816
+ blocked; draft PRs append `(draft)`):
1817
+
1818
+ ```
1819
+ ╔═══════════════════════════════════════════════════╗
1820
+ ║ ForgeDock Pipeline Complete ║
1821
+ ╠═══════════════════════════════════════════════════╣
1822
+ ║ ║
1823
+ ║ Issue: #{NUMBER} — {TITLE} ║
1824
+ ║ Pipeline: investigate → architect → build → ║
1825
+ ║ review → merge ✓ ║
1826
+ ║ Commits: {COMMITS} ({ADDITIONS} additions, {DELETIONS} deletions) ║
1827
+ ║ PR: #{PR_NUMBER} (merged to {PR_BASE}) ║
1828
+ ║ Review: {REVIEW_SUMMARY} ║
1829
+ ║ Time: {ELAPSED} ║
1830
+ ║ ║
1831
+ ╚═══════════════════════════════════════════════════╝
1832
+ ```
1833
+
1834
+ **Gather real stats** (C4.5a — this block MUST run on the inline path to populate card variables;
1835
+ do NOT rely on the cross-reference to `close.md` alone):
1836
+
1837
+ ```bash
1838
+ PR_STATS=$(gh pr view {PR_NUMBER} {GH_FLAG} --json commits,additions,deletions,baseRefName,isDraft 2>/dev/null)
1839
+ COMMITS=$(echo "$PR_STATS" | jq -r '(.commits | length) // empty' 2>/dev/null); COMMITS=${COMMITS:-—}
1840
+ ADDITIONS=$(echo "$PR_STATS" | jq -r '.additions // empty' 2>/dev/null); ADDITIONS=${ADDITIONS:-—}
1841
+ DELETIONS=$(echo "$PR_STATS" | jq -r '.deletions // empty' 2>/dev/null); DELETIONS=${DELETIONS:-—}
1842
+ PR_TARGET=$(echo "$PR_STATS" | jq -r '.baseRefName // empty' 2>/dev/null); PR_TARGET=${PR_TARGET:-{PR_BASE}}
1843
+ IS_DRAFT=$(echo "$PR_STATS" | jq -r '.isDraft // false' 2>/dev/null)
1844
+
1845
+ REVIEW_BODIES=$(gh pr view {PR_NUMBER} {GH_FLAG} --json reviews,comments \
1846
+ --jq '[.reviews[].body // ""] + [.comments[].body // ""] | .[]' 2>/dev/null)
1847
+ # NOTE: `grep -c` already prints `0` on no match (and exits non-zero) — do NOT add
1848
+ # `|| echo 0`, which would append a second line ("0\n0") and break the arithmetic
1849
+ # and `--argjson` below. Swallow the non-zero exit with `|| true`, then default.
1850
+ APPROVED=$(echo "$REVIEW_BODIES" | grep -cE 'APPROVED:' 2>/dev/null || true); APPROVED=${APPROVED:-0}
1851
+ CHANGES=$(echo "$REVIEW_BODIES" | grep -cE 'CHANGES REQUESTED:' 2>/dev/null || true); CHANGES=${CHANGES:-0}
1852
+ TOTAL_AGENTS=$((APPROVED + CHANGES))
1853
+ BLOCKERS=$(echo "$REVIEW_BODIES" | grep -ciE 'blocker|merge.?block' 2>/dev/null || true); BLOCKERS=${BLOCKERS:-0}
1854
+ if [ "$TOTAL_AGENTS" -gt 0 ]; then
1855
+ REVIEW_SUMMARY="${APPROVED}/${TOTAL_AGENTS} agents passed, ${BLOCKERS} blockers"
1856
+ else
1857
+ REVIEW_SUMMARY="—" # review data unavailable (e.g. review skipped)
1858
+ fi
1859
+
1860
+ FIRST_TS=$(gh api repos/{GH_REPO}/issues/{NUMBER}/comments \
1861
+ --jq '[.[] | select(.body | contains("FORGE:")) | .created_at] | sort | .[0] // empty' 2>/dev/null)
1862
+ if [ -n "$FIRST_TS" ]; then
1863
+ START_EPOCH=$(date -u -d "$FIRST_TS" +%s 2>/dev/null \
1864
+ || python3 -c "import sys,datetime; ts=sys.argv[1].rstrip('Z'); print(int(datetime.datetime.fromisoformat(ts+'+00:00').timestamp()))" "$FIRST_TS" 2>/dev/null \
1865
+ || echo "")
1866
+ NOW_EPOCH=$(date -u +%s)
1867
+ if [ -n "$START_EPOCH" ]; then
1868
+ ELAPSED_SECS=$((NOW_EPOCH - START_EPOCH))
1869
+ ELAPSED=$(printf '%dm %02ds' $((ELAPSED_SECS / 60)) $((ELAPSED_SECS % 60)))
1870
+ else ELAPSED="—"; ELAPSED_SECS=0; fi
1871
+ else ELAPSED="—"; ELAPSED_SECS=0; fi
1872
+
1873
+ case "{TERMINAL_STATE}" in
1874
+ decomposed) PIPELINE_LINE="investigate → decompose ⏹"; CARD_STATUS="decomposed" ;;
1875
+ invalid) PIPELINE_LINE="investigate → invalid ✗"; CARD_STATUS="invalid" ;;
1876
+ blocked) PIPELINE_LINE="investigate → build → blocked ⚠"; CARD_STATUS="blocked" ;;
1877
+ *) PIPELINE_LINE="investigate → architect → build → review → merge ✓"; CARD_STATUS="merged" ;;
1878
+ esac
1879
+ [ "$IS_DRAFT" = "true" ] && PIPELINE_LINE="${PIPELINE_LINE} (draft)"
1880
+ ```
1881
+
1882
+ **Build the machine-readable twin** (C4.5c — MUST run this block to assign `CARD_JSON` before
1883
+ Phase 7B embeds it; the cross-reference to `close.md` above is insufficient on the inline path): <!-- forge#1178 -->
1884
+
1885
+ ```bash
1886
+ CARD_JSON=$(jq -nc \
1887
+ --argjson issue {NUMBER} \
1888
+ --arg title "{TITLE}" \
1889
+ --arg status "$CARD_STATUS" \
1890
+ --arg pipeline "$PIPELINE_LINE" \
1891
+ --arg pr "{PR_NUMBER}" \
1892
+ --arg target "$PR_TARGET" \
1893
+ --arg commits "$COMMITS" --arg adds "$ADDITIONS" --arg dels "$DELETIONS" \
1894
+ --arg review "$REVIEW_SUMMARY" --argjson blockers "${BLOCKERS:-0}" \
1895
+ --argjson elapsed "${ELAPSED_SECS:-0}" \
1896
+ '{issue:$issue, title:$title, status:$status, pipeline:$pipeline,
1897
+ pr:($pr|tonumber? // null), pr_target:$target,
1898
+ commits:($commits|tonumber? // null),
1899
+ additions:($adds|tonumber? // null),
1900
+ deletions:($dels|tonumber? // null),
1901
+ review:$review, blockers:$blockers, elapsed_seconds:$elapsed}')
1902
+ ```
1903
+
1904
+ `CARD_JSON` is now set and embedded in the trajectory comment by 7B.
1905
+
1661
1906
  ### 7B: Trajectory Log (MANDATORY)
1662
1907
 
1663
1908
  **Review-presence check** (run before filling in Phase 4-5 row): <!-- Added: forge#381 -->
@@ -1676,6 +1921,13 @@ This check is **audit-only** — it annotates the trajectory for visibility. It
1676
1921
  Post `<!-- FORGE:TRAJECTORY -->` comment with phase-by-phase results table:
1677
1922
 
1678
1923
  ```bash
1924
+ # Compute verification row from VERIFICATION_SKIPPED_CHECKS (set in Phase 3H)
1925
+ if [ -z "$VERIFICATION_SKIPPED_CHECKS" ]; then
1926
+ VERIFICATION_ROW="✅ Ran"
1927
+ else
1928
+ VERIFICATION_ROW="⚠ Skipped — verification.commands not configured for: ${VERIFICATION_SKIPPED_CHECKS}"
1929
+ fi
1930
+
1679
1931
  gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:TRAJECTORY -->
1680
1932
  ## Pipeline Trajectory — #{NUMBER}
1681
1933
 
@@ -1686,14 +1938,22 @@ gh issue comment {NUMBER} {GH_FLAG} --body "<!-- FORGE:TRAJECTORY -->
1686
1938
  | Phase 2: Decomposition | ⏭ Skipped | {reason} |
1687
1939
  | Phase 3: Build | ✅ Complete | Branch: \`{BRANCH}\` |
1688
1940
  | Phase 3G: Quality Gate | ✅ Gate passed | {iterations} iterations |
1941
+ | Phase 3H: Verification | ${VERIFICATION_ROW} | |
1689
1942
  | Phase 4–5: Review + PR | {REVIEW_ROW} | PR #{PR_NUMBER} → \`{PR_BASE}\` |
1690
1943
  | Phase 6: Close | ✅ Complete | Issue closed |
1691
1944
 
1692
1945
  **Decisions**: {key decisions}
1693
1946
  **Anomalies**: {anomalies or None}
1694
- **Pipeline completed**: {TIMESTAMP}"
1947
+ **Pipeline completed**: {TIMESTAMP}
1948
+
1949
+ <!-- FORGE:CARD ${CARD_JSON} -->"
1695
1950
  ```
1696
1951
 
1952
+ Append the `<!-- FORGE:CARD {...} -->` block (machine-readable twin from 7A / close.md C4.5c)
1953
+ as the last line of the trajectory comment. It is HTML-comment-wrapped so it stays hidden in
1954
+ the rendered view but greppable for platform consumption (`/orchestrate` Phase 6 reads it for
1955
+ per-issue cards). Additive — does not affect existing `FORGE:TRAJECTORY` table consumers.
1956
+
1697
1957
  ### 7C: Graph Decision Record (MANDATORY when PR exists)
1698
1958
 
1699
1959
  **Skip if**: `{PR_NUMBER}` is empty (investigation-only tasks with no PR) OR `<!-- FORGE:DECISION_RECORD -->` already posted on the PR.
package/docs/BRAND.md CHANGED
@@ -6,7 +6,10 @@ Status of reserved and blocked brand handles across platforms.
6
6
 
7
7
  | Domain | Status |
8
8
  |--------|--------|
9
- | `forgedock.dev` | **Owned** — canonical marketing and App URL |
9
+ | `forgedock.dev` | **Unverified** — live content (2026-07-04) is a third-party product unrelated to ForgeDock; registrar verification pending. Do not use as a public-facing link until resolved. |
10
+ | `forgedock.io` | **Parked** — Porkbun parked page, HTTP-only (HTTPS does not connect). Not controlled by RapierCraft Studios. Do not use as a public-facing link. |
11
+
12
+ > **Rule**: No public ForgeDock surface (npm homepage, repo description, plugin manifests, docs) may point at a domain until its live content is verified as controlled by RapierCraft Studios.
10
13
 
11
14
  ## npm
12
15
 
@@ -45,7 +48,8 @@ Social handle registration is an operational task. Reserve handles before public
45
48
 
46
49
  | Platform | Action |
47
50
  |----------|--------|
48
- | Domain | Done |
51
+ | Domain (forgedock.dev) | Unverified — registrar check pending; do not use as live surface |
52
+ | Domain (forgedock.io) | Parked — not controlled by RapierCraft Studios; do not use as live surface |
49
53
  | npm | Done |
50
54
  | GitHub org slug | Blocked — use variant for App slug |
51
55
  | Social handles | Reserve before L1 public launch |
package/docs/CONFIG.md CHANGED
@@ -12,18 +12,26 @@ echo "forge.yaml" >> .gitignore # if your credentials path is sensitive
12
12
 
13
13
  ---
14
14
 
15
- ## CLI Flags for `init`
15
+ ## CLI Flags
16
16
 
17
- `npx forgedock init` accepts two optional flags that change how configuration is generated:
17
+ `--verbose` and `--fast` apply to both `npx forgedock` (the full install journey) and `npx forgedock init` (config-only regeneration); `--manual` and `--minimal` apply to `init` only:
18
18
 
19
19
  | Flag | Behavior |
20
20
  |------|----------|
21
- | `--manual` | Skips AI autopilot enrichment entirely. Presents the annotated review screen with detection baseline values only (no AI-enriched suggestions). Detection still runs to provide defaults — you review and edit the raw inferred values on the same single-screen interface. |
22
- | `--verbose` | Surfaces each field's detection source and confidence rationale during the init flow. Helpful for understanding where ForgeDock inferred values from (e.g. `git remote`, `package.json`, `gh api`). |
21
+ | `--manual` | `npx forgedock init` only. Skips the annotated review screen and asks plain, one-field-at-a-time prompts instead, pre-filled with the detected values as defaults. |
22
+ | `--minimal` | `npx forgedock init` only. Skips detection enrichment and the review screen entirely, and writes a short `forge.yaml` containing only the three required sections (`project`, `paths`, `branches`) with detected values. |
23
+ | `--verbose` | Shows each field's detection source and confidence rationale on the review screen (e.g. `git remote`, `package.json`, `gh api`), instead of only flagging low-confidence fields. |
24
+ | `--fast` | Skips the animation/motion frames and jumps straight to the static screens. |
23
25
 
24
- The two flags can be combined: `npx forgedock init --manual --verbose` shows the annotated review screen with detection baseline values and field provenance at each entry.
26
+ **Environment variables:**
25
27
 
26
- The default (`npx forgedock init` with no flags) remains the zero-question autopilot: AI enrichment fills in everything it can, and you review the result on a single annotated screen.
28
+ | Variable | Behavior |
29
+ |----------|----------|
30
+ | `FORGE_NO_MOTION=1` | Same effect as `--fast` — disables animation frames. Color is unaffected. |
31
+ | `NO_COLOR=1` | Disables ANSI color output only. Motion is unaffected (monochrome choreography). |
32
+ | Non-TTY / piped output (or `CI=1`) | Plain sequential log: no color **and** no animation. |
33
+
34
+ The default (no flags) is the annotated review screen: detection runs, AI enrichment fills in what it can (when an API key is available), and you review the result on a single screen — Enter accepts everything, low-confidence fields are flagged with a `# TODO(forgedock:<field>)` comment if left unedited.
27
35
 
28
36
  ---
29
37
 
@@ -686,46 +694,43 @@ Downstream consumers read `field.value` for the raw value and `field.confidence`
686
694
 
687
695
  ---
688
696
 
689
- ## CLAUDE.md Integration
697
+ ## SessionStart Hook Integration
690
698
 
691
- ForgeDock can inject a managed usage block into your project's `CLAUDE.md` so every Claude Code session opened in the repo automatically knows that ForgeDock drives development here and which commands to use.
699
+ ForgeDock does **not** write anything into your project's `CLAUDE.md` or `AGENTS.md`. Instead, `npx forgedock` registers a `SessionStart` hook in `~/.claude/settings.json` — a small script that runs at the start of every Claude Code session and prints ForgeDock's context straight to that session, without touching any file in your repo.
692
700
 
693
701
  ### How It Works
694
702
 
695
- Running `npx forgedock init` or `npx forgedock integrate` writes a marker-bounded block into `CLAUDE.md`:
696
-
697
- ```
698
- <!-- BEGIN FORGEDOCK -->
699
- ## ForgeDock — Autonomous Development Pipeline
700
- ...command index and conventions...
701
- <!-- END FORGEDOCK -->
702
- ```
703
+ The hook resolves the current directory's state and reacts accordingly:
703
704
 
704
- The block is **idempotent** — re-running replaces only the managed section and leaves all other `CLAUDE.md` content untouched. If `CLAUDE.md` does not exist, it is created.
705
+ | State | Behavior |
706
+ |-------|----------|
707
+ | `managed-active` (`forge.yaml` present, not opted out) | Prints a summary of `forge.yaml` (project, repo, branches) plus the available pipeline commands |
708
+ | `managed-active`, no `forge.yaml` yet | Suggests running `npx forgedock init` |
709
+ | `managed-optedout` | Completely silent — no output |
710
+ | `unmanaged` | Prints a one-time, suppressible nudge to run `npx forgedock enable` |
705
711
 
706
- If `AGENTS.md` already exists in the project root, the same block is mirrored into it.
712
+ The hook entry is registered idempotently: re-installing (`npx forgedock update`) will not duplicate it, and unrelated hooks or settings in `~/.claude/settings.json` are left untouched. If `settings.json` is malformed, the installer skips writing rather than risk corrupting it.
707
713
 
708
714
  ### Commands
709
715
 
710
716
  | Command | Action |
711
717
  |---------|--------|
712
- | `npx forgedock init` | Generates `forge.yaml` **and** injects the CLAUDE.md block |
713
- | `npx forgedock integrate` | Injects/refreshes the block without modifying `forge.yaml` |
718
+ | `npx forgedock` / `npx forgedock install` | Registers the SessionStart hook (along with installing commands and writing `forge.yaml`) |
719
+ | `npx forgedock update` | Re-registers the hook (relink only — does not touch `forge.yaml`) |
720
+ | `npx forgedock uninstall` | Removes the hook entry from `settings.json` |
714
721
 
715
722
  ### Opting Out
716
723
 
717
- To prevent ForgeDock from managing the block, remove the `<!-- BEGIN FORGEDOCK -->` / `<!-- END FORGEDOCK -->` markers from `CLAUDE.md`. Without the markers, subsequent runs will append a new block rather than replacing one — so if you want to opt out permanently, delete or omit the markers **and** do not run `integrate` again.
718
-
719
- Alternatively, keep the markers but edit the content between them freely — ForgeDock will replace that section on the next run, so any manual edits inside the markers will be overwritten.
720
-
721
- ### Re-generating
722
-
723
- The command index inside the block is auto-generated from the `description:` frontmatter in each `commands/*.md` file. To refresh it after a ForgeDock update:
724
+ Per-directory, independent of the global hook registration. Each command acts on the current working directory — run it inside the project directory:
724
725
 
725
726
  ```bash
726
- npx forgedock integrate
727
+ npx forgedock disable # the current directory goes silent (managed-optedout)
728
+ npx forgedock enable # re-activate the current directory
729
+ npx forgedock status # show the current directory's resolved state
727
730
  ```
728
731
 
732
+ See [Per-Directory State Registry](#per-directory-state-registry) below for how opt-out state is tracked.
733
+
729
734
  ---
730
735
 
731
736
  ## Per-Directory State Registry
@@ -788,22 +793,22 @@ If you downgrade to a build older than PR #467 **and** your project is accessed
788
793
  - The one-time "Enable ForgeDock here?" nudge may reappear once.
789
794
  - No data is lost. No crash. Fail-open behaviour holds throughout.
790
795
 
791
- **Recovery**: after downgrading, re-apply your opt-out with the older build:
796
+ **Recovery**: after downgrading, re-apply your opt-out with the older build (run inside the project directory):
792
797
 
793
798
  ```bash
794
- npx forgedock disable [dir]
799
+ npx forgedock disable
795
800
  ```
796
801
 
797
802
  This re-writes the entry under the key form the older build expects.
798
803
 
799
804
  ### Managing Opt-Out State
800
805
 
801
- Use the `forgedock enable` and `forgedock disable` commands to add or remove a directory from the opt-out set:
806
+ Use the `forgedock enable` and `forgedock disable` commands to add or remove a directory from the opt-out set. Each command acts on the current working directory by default — run it inside the project directory, or pass an explicit path:
802
807
 
803
808
  ```bash
804
- npx forgedock enable [dir] # Remove directory from opt-out set (default: cwd)
805
- npx forgedock disable [dir] # Add directory to opt-out set (default: cwd)
806
- npx forgedock status [dir] # Show resolved state for a directory
809
+ npx forgedock enable [dir] # Remove a directory from the opt-out set (default: cwd)
810
+ npx forgedock disable [dir] # Add a directory to the opt-out set (default: cwd)
811
+ npx forgedock status [dir] # Show a directory's resolved state (default: cwd)
807
812
  ```
808
813
 
809
814
  ---