forgedock 1.2.0 → 1.4.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 (78) hide show
  1. package/README.md +8 -2
  2. package/bin/batch-runner.mjs +31 -2
  3. package/bin/engine/admission.mjs +294 -0
  4. package/bin/engine/admission.test.mjs +265 -0
  5. package/bin/engine/invariants.mjs +17 -4
  6. package/bin/engine/phases.mjs +358 -42
  7. package/bin/engine/resolve.mjs +256 -0
  8. package/bin/engine/resolve.test.mjs +149 -0
  9. package/bin/engine/state.mjs +31 -7
  10. package/bin/engine-cli.mjs +138 -2
  11. package/bin/engine.mjs +414 -21
  12. package/bin/forgedock.mjs +552 -44
  13. package/bin/hooks/interactive-engine.mjs +50 -24
  14. package/bin/hooks/pre-tool-use.mjs +189 -19
  15. package/bin/journey.mjs +109 -11
  16. package/bin/labels.json +5 -0
  17. package/bin/runner.mjs +321 -10
  18. package/bin/watch-utils.mjs +255 -0
  19. package/commands/cleanup.md +7 -3
  20. package/commands/forge-stats.md +5 -4
  21. package/commands/issue.md +5 -1
  22. package/commands/orchestrate/config.md +41 -1
  23. package/commands/orchestrate/phase-1-resolve.md +234 -8
  24. package/commands/orchestrate/phase-3-dependency.md +27 -18
  25. package/commands/orchestrate/phase-4-execution.md +223 -22
  26. package/commands/qa-sweep.md +17 -0
  27. package/commands/quality-gate.md +6 -6
  28. package/commands/replay.md +16 -2
  29. package/commands/review-pr-agents/spec-cli.md +179 -0
  30. package/commands/review-pr.md +65 -0
  31. package/commands/upgrade-deps.md +523 -0
  32. package/commands/work-on/build/validate.md +61 -1
  33. package/commands/work-on/build.md +9 -1
  34. package/commands/work-on/decompose.md +10 -1
  35. package/commands/work-on/investigate.md +22 -4
  36. package/commands/work-on/remediate.md +2 -0
  37. package/commands/work-on/review.md +40 -1
  38. package/commands/work-on.md +19 -4
  39. package/docs/AIDER.md +264 -0
  40. package/docs/AUTOPILOT-CRON.md +130 -0
  41. package/docs/CONFIG.md +156 -0
  42. package/docs/CURSOR.md +135 -0
  43. package/docs/OPENCODE.md +312 -0
  44. package/docs/demo.gif +0 -0
  45. package/docs/eval/README.md +4 -4
  46. package/docs/launch/show-hn.md +3 -3
  47. package/docs/site/architecture.md +180 -0
  48. package/docs/site/command-reference.md +17 -0
  49. package/docs/site/configuration.md +243 -0
  50. package/docs/site/for-companies.md +4 -4
  51. package/docs/site/forge-annotation-protocol.md +1 -1
  52. package/docs/site/github-is-the-memory.md +2 -2
  53. package/docs/site/how-it-works.md +1 -1
  54. package/docs/site/public/llms-full.txt +380 -0
  55. package/docs/site/public/llms.txt +35 -0
  56. package/docs/spec/eval-run-result.md +8 -4
  57. package/docs/spec/label-state-machine.md +9 -0
  58. package/docs/spec-graph-schema.md +3 -3
  59. package/package.json +3 -3
  60. package/packages/protocol/README.md +75 -0
  61. package/packages/protocol/src/card.js +114 -0
  62. package/packages/protocol/src/cli.js +4 -77
  63. package/packages/protocol/src/emit.js +5 -11
  64. package/packages/protocol/src/html-comment-escape.js +68 -0
  65. package/packages/protocol/src/index.js +3 -1
  66. package/packages/protocol/src/parse.js +19 -11
  67. package/packages/protocol/src/phases.js +105 -0
  68. package/packages/protocol/src/types.js +18 -1
  69. package/packages/protocol/src/validate.js +11 -0
  70. package/scripts/check-admission-parity.mjs +167 -0
  71. package/scripts/check-command-side-effects.sh +376 -0
  72. package/scripts/check-phase-registry-drift.mjs +96 -0
  73. package/scripts/check-spec-markers.sh +274 -0
  74. package/scripts/classify-lane.sh +29 -7
  75. package/scripts/forge-run.sh +353 -0
  76. package/templates/cross-repo-trigger.yml +282 -0
  77. package/templates/cursor/forge.mdc +148 -0
  78. package/templates/github-actions/forgedock-autopilot.yml +113 -0
@@ -0,0 +1,274 @@
1
+ #!/usr/bin/env bash
2
+ # SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
3
+ # SPDX-License-Identifier: AGPL-3.0-or-later
4
+ #
5
+ # check-spec-markers.sh — Validate FORGE: annotation markers in commands/**/*.md and
6
+ # detect unsubstituted {PLACEHOLDER} tokens in CI workflow files.
7
+ #
8
+ # Command specs contain two classes of spec-as-code marker defects:
9
+ #
10
+ # 1. Non-registry FORGE: markers — a typo or invented marker (e.g. FORGE:INVESTIAGOR,
11
+ # FORGE:NOTES) in a command spec will cause agents to look for a comment annotation
12
+ # that is never emitted, silently breaking pipeline state detection. (Ref: forge#633)
13
+ #
14
+ # 2. Unsubstituted {PLACEHOLDER} tokens in CI workflow YAML — a template variable like
15
+ # {GH_REPO} or {NUMBER} left unsubstituted in a workflow inline script means every
16
+ # CI run executes a malformed command that fails silently or produces wrong output.
17
+ # (Ref: forge#318; note: {PLACEHOLDER} tokens in commands/*.md are intentional
18
+ # spec notation and are NOT flagged by this check.)
19
+ #
20
+ # This script:
21
+ # 1. Scans all *.md files in commands/ for <!-- FORGE:XXXX --> annotations.
22
+ # 2. Validates XXXX against the known registry (see MARKER_REGISTRY below).
23
+ # 3. Scans .github/workflows/*.yml for unsubstituted {PLACEHOLDER} tokens in run: blocks.
24
+ #
25
+ # Registry: covers all reserved types from packages/protocol/src/types.js
26
+ # (RESERVED_TYPE_NAMES) plus all operational/pipeline markers used throughout the
27
+ # ForgeDock spec corpus. Compound markers (FORGE:TYPE-SUBTYPE, FORGE:TYPE:SUBTYPE)
28
+ # are validated by their primary type (the part before the first - or :).
29
+ # Update the registry when adding new marker types.
30
+ #
31
+ # Allowlist: add <!-- allowlist:check-spec-markers --> on the same line as the marker
32
+ # to suppress that specific hit.
33
+ #
34
+ # Usage:
35
+ # check-spec-markers.sh [<commands_dir> [<repo_root>]]
36
+ # commands_dir: path to the commands/ directory (default: ./commands)
37
+ # repo_root: path to repo root for workflow scanning (default: .)
38
+ #
39
+ # Exit codes:
40
+ # 0 no violations found
41
+ # 1 one or more violations found (listed to stderr)
42
+ # 2 usage / dependency error (commands_dir not found)
43
+ #
44
+ # <!-- Added: forge#1609 -->
45
+
46
+ set -euo pipefail
47
+
48
+ COMMANDS_DIR="${1:-./commands}"
49
+ REPO_ROOT="${2:-.}"
50
+
51
+ if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
52
+ sed -n '2,/^set -/p' "$0" | grep '^#' | sed 's/^# *//'
53
+ exit 0
54
+ fi
55
+
56
+ if [ ! -d "$COMMANDS_DIR" ]; then
57
+ echo "ERROR: commands directory not found: $COMMANDS_DIR" >&2
58
+ echo "Usage: $0 [<commands_dir> [<repo_root>]]" >&2
59
+ exit 2
60
+ fi
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # FORGE: marker registry
64
+ #
65
+ # Sources:
66
+ # - packages/protocol/src/types.js — RESERVED_TYPE_NAMES (reserved lifecycle types)
67
+ # - Operational pipeline markers used in commands/**/*.md specs
68
+ #
69
+ # Compound markers (FORGE:REVIEW-AGENT:material-change, FORGE:PHASE:COMPLETE) are
70
+ # validated by their PRIMARY type only (the part before the first - or : after FORGE:).
71
+ # Add entries for each primary type; sub-types are unrestricted.
72
+ #
73
+ # Update this list when new marker types are added to the protocol or spec corpus.
74
+ # Keep sorted alphabetically for readability.
75
+ # ---------------------------------------------------------------------------
76
+ MARKER_REGISTRY="
77
+ ACCEPTANCE_GATE
78
+ ADR_EXTRACTED
79
+ ANCESTRY_FAILED
80
+ ARCHITECT
81
+ AUDIT
82
+ AUTOPILOT_CYCLE
83
+ AUTOPILOT_GATE
84
+ AUTOPILOT_IMPACT
85
+ BATCHABLE
86
+ BATCH_ID
87
+ BATCH_MEMBERS
88
+ BENCH_SCORECARD
89
+ BLOCKED_ON_HUMAN_MERGE
90
+ BUILDER
91
+ CALIBRATION_CHECK
92
+ CARD
93
+ CHECKPOINT
94
+ CLAIM
95
+ CLAIM_RELEASED
96
+ CONTEXT
97
+ CONTRACT
98
+ COORD_ISSUE
99
+ CRITIQUE
100
+ DECISION_RECORD
101
+ DECOMPOSED
102
+ DESIGN
103
+ DISPATCHER
104
+ DOSSIER_UPDATED
105
+ FAST_PATH
106
+ FINDING_SOURCE
107
+ FIX_CI
108
+ GATE_FAILED
109
+ GATE_FAILURE
110
+ GATE_PASS
111
+ HEARTBEAT
112
+ INDEP_VERIFY
113
+ INDEP_VERIFY_FAIL
114
+ INDEP_VERIFY_PASS
115
+ INVESTIGATOR
116
+ JSONL_PARSER_UTIL
117
+ KNOWLEDGE_GIST
118
+ LEARNED
119
+ LEARNED_RULES
120
+ LEDGER_INDEXED
121
+ MEMORY_INDEX
122
+ MEMORY_INDEXED
123
+ MILESTONE_INDEX
124
+ MODEL_TIER_NOTE
125
+ ORPHAN_ESCALATED
126
+ ORPHAN_RECOVERED
127
+ PATTERN
128
+ PHASE
129
+ PHASE_COMPLETE
130
+ PLAN_DAG
131
+ PRIOR_DECISIONS
132
+ PRIOR_GIST
133
+ PROTOCOL_SOURCE
134
+ PUSH_BLOCKED
135
+ PUSH_BLOCKED_EMPTY_BRANCH
136
+ PUSH_FAILED
137
+ REMEDIATION
138
+ REREVIEW_SKIPPED
139
+ REVIEW
140
+ REVIEWER
141
+ REVIEW_ROUTE
142
+ REVIEW_STARTED
143
+ SECURITY_AUDIT
144
+ SIGNAL_RESOLVED
145
+ SIGNAL_UNRESOLVED
146
+ SPAWN_POLICY
147
+ SPEC_DOCTOR_COMPLETE
148
+ SPEC_LOADED
149
+ STALL_DETECTED
150
+ STATE
151
+ SYNTHESIS_BRIEF
152
+ TEST_GATE
153
+ TRAIN_CANDIDATE
154
+ TRAJECTORY
155
+ UNBLOCKED
156
+ USER_FEEDBACK
157
+ "
158
+
159
+ # Build an alternation pattern for registry lookup.
160
+ # Words separated by | for use with grep -E: ^(WORD1|WORD2|...)$
161
+ REGISTRY_NAMES=$(echo "$MARKER_REGISTRY" | tr -s '[:space:]' '\n' | grep -vE '^\s*$' | sort -u | tr '\n' '|' | sed 's/|$//')
162
+ REGISTRY_PATTERN="^(${REGISTRY_NAMES})$"
163
+
164
+ # Allowlist token — a line containing this token is exempt
165
+ ALLOWLIST_TOKEN='allowlist:check-spec-markers'
166
+
167
+ VIOLATIONS=0
168
+
169
+ # ---------------------------------------------------------------------------
170
+ # Pass 1: Validate FORGE: markers in commands/**/*.md
171
+ # ---------------------------------------------------------------------------
172
+
173
+ find_md_files() {
174
+ find "$COMMANDS_DIR" -name '*.md' | sort
175
+ }
176
+
177
+ while IFS= read -r file; do
178
+ [ -f "$file" ] || continue
179
+
180
+ # Extract all lines containing <!-- FORGE:XXXX --> annotations with their line numbers.
181
+ while IFS= read -r markerline; do
182
+ [ -z "$markerline" ] && continue
183
+ lineno="${markerline%%:*}"
184
+ content="${markerline#*:}"
185
+
186
+ # Skip allowlisted lines
187
+ if echo "$content" | grep -qF "$ALLOWLIST_TOKEN"; then
188
+ continue
189
+ fi
190
+
191
+ # Skip markers that are inside code spans (backtick-quoted) — those are docs examples
192
+ # Heuristic: if the FORGE:TYPE appears between backticks on the same line, skip it
193
+ # We check for backtick on either side of the FORGE: marker
194
+ if echo "$content" | grep -qE "\`[^\`]*FORGE:[A-Z_]"; then
195
+ continue
196
+ fi
197
+
198
+ # Extract the full marker string after FORGE: — everything up to a space, -->, or end
199
+ # This captures compound markers like REVIEW-AGENT:material-change
200
+ full_marker=$(echo "$content" | grep -oE 'FORGE:[A-Z][A-Z0-9_:-]*' | head -1 | sed 's/^FORGE://')
201
+
202
+ [ -z "$full_marker" ] && continue
203
+
204
+ # Extract PRIMARY type: part before the first - or : separator
205
+ primary_type=$(echo "$full_marker" | sed 's/[-:].*//')
206
+
207
+ # Validate primary type against registry
208
+ if ! echo "$primary_type" | grep -qE "$REGISTRY_PATTERN"; then
209
+ echo "HIGH | $file:$lineno | unknown FORGE: marker 'FORGE:${full_marker}' (primary type '${primary_type}') — not in registry" >&2
210
+ VIOLATIONS=$((VIOLATIONS + 1))
211
+ fi
212
+ done < <(grep -nE '<!--[[:space:]]*FORGE:[A-Z]' "$file" 2>/dev/null || true)
213
+
214
+ done < <(find_md_files)
215
+
216
+ # ---------------------------------------------------------------------------
217
+ # Pass 2: Detect unsubstituted {PLACEHOLDER} in .github/workflows/*.yml
218
+ #
219
+ # In workflow YAML, {PLACEHOLDER} tokens (not bash ${VAR} expansions) indicate
220
+ # a template variable that should have been resolved before committing.
221
+ #
222
+ # Bash ${VAR} references are excluded — we strip ${...} patterns before scanning.
223
+ # Pattern: {UPPERCASE_3+} — e.g. {GH_REPO}, {NUMBER}, {WORKTREE_PATH}
224
+ # ---------------------------------------------------------------------------
225
+
226
+ WORKFLOWS_DIR="${REPO_ROOT}/.github/workflows"
227
+
228
+ if [ -d "$WORKFLOWS_DIR" ]; then
229
+ # Pattern: { followed by uppercase letter, then 2+ uppercase/digit/underscore, then }
230
+ # Preceded by something other than $ (to exclude bash ${VAR} expansions)
231
+ PLACEHOLDER_PATTERN='\{[A-Z][A-Z0-9_]{2,}\}'
232
+
233
+ while IFS= read -r wf_file; do
234
+ [ -f "$wf_file" ] || continue
235
+
236
+ LINENO=0
237
+
238
+ while IFS= read -r line; do
239
+ LINENO=$((LINENO + 1))
240
+
241
+ # Skip allowlisted lines
242
+ if echo "$line" | grep -qF "$ALLOWLIST_TOKEN"; then
243
+ continue
244
+ fi
245
+
246
+ # Skip comment lines
247
+ if echo "$line" | grep -qE '^[[:space:]]*#'; then
248
+ continue
249
+ fi
250
+
251
+ # Strip bash ${VAR} and $VAR patterns before checking for template placeholders
252
+ # so that ELAPSED in "${ELAPSED}s" doesn't false-positive as {ELAPSED}
253
+ stripped=$(echo "$line" | sed 's/\${\([^}]*\)}/BASH_VAR/g; s/\$[A-Za-z_][A-Za-z0-9_]*/BASH_VAR/g')
254
+
255
+ if echo "$stripped" | grep -qE "$PLACEHOLDER_PATTERN"; then
256
+ placeholder=$(echo "$stripped" | grep -oE "$PLACEHOLDER_PATTERN" | head -1)
257
+ echo "HIGH | $wf_file:$LINENO | unsubstituted placeholder '$placeholder' in workflow" >&2
258
+ VIOLATIONS=$((VIOLATIONS + 1))
259
+ fi
260
+ done < "$wf_file"
261
+ done < <(find "$WORKFLOWS_DIR" -name '*.yml' | sort)
262
+ fi
263
+
264
+ # ---------------------------------------------------------------------------
265
+ # Summary
266
+ # ---------------------------------------------------------------------------
267
+
268
+ if [ "$VIOLATIONS" -gt 0 ]; then
269
+ echo "check-spec-markers: $VIOLATIONS violation(s) found. See stderr for details." >&2
270
+ exit 1
271
+ fi
272
+
273
+ echo "OK: No marker or placeholder violations found"
274
+ exit 0
@@ -1,14 +1,19 @@
1
1
  #!/usr/bin/env bash
2
2
  # classify-lane.sh — Deterministic lane routing from issue milestone
3
3
  #
4
- # Usage: classify-lane.sh <issue_number> [-R <owner/repo>]
4
+ # Usage: classify-lane.sh <issue_number> [-R <owner/repo>] [--json]
5
5
  # issue_number: GitHub issue number (required)
6
6
  # -R <owner/repo>: GitHub repository (optional, defaults to current repo)
7
+ # --json: emit structured JSON object instead of plain lane string (optional)
7
8
  #
8
- # Output: lane string written to stdout
9
+ # Output: lane string written to stdout (default)
9
10
  # staging — issue has no milestone (fast lane)
10
11
  # milestone/{slug} — issue has a milestone (feature lane, slug = lowercased, spaces→hyphens, git-invalid chars stripped)
11
12
  #
13
+ # Output: JSON object when --json flag is passed
14
+ # {"lane":"staging","branch":"staging","source":"fast-lane","milestone":null}
15
+ # {"lane":"milestone/{slug}","branch":"milestone/{slug}","source":"feature-lane","milestone":"{title}","slug":"{slug}"}
16
+ #
12
17
  # Exit codes: 0 = success, 1 = error (invalid issue, gh auth failure, branch missing, etc.)
13
18
  #
14
19
  # Branch existence validation:
@@ -23,11 +28,12 @@ set -euo pipefail
23
28
 
24
29
  ISSUE_NUMBER="${1:-}"
25
30
  GH_REPO_ARGS=()
31
+ JSON_OUTPUT=false
26
32
 
27
- # Parse arguments: issue number + optional -R flag
33
+ # Parse arguments: issue number + optional -R flag + optional --json flag
28
34
  if [ -z "$ISSUE_NUMBER" ]; then
29
35
  echo "ERROR: issue number is required" >&2
30
- echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>]" >&2
36
+ echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>] [--json]" >&2
31
37
  exit 1
32
38
  fi
33
39
 
@@ -37,7 +43,7 @@ while [ $# -gt 0 ]; do
37
43
  -R)
38
44
  if [ $# -lt 2 ]; then
39
45
  echo "ERROR: -R requires a value <owner/repo>" >&2
40
- echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>]" >&2
46
+ echo "Usage: classify-lane.sh <issue_number> [-R <owner/repo>] [--json]" >&2
41
47
  exit 1
42
48
  fi
43
49
  if ! [[ "$2" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then
@@ -47,6 +53,10 @@ while [ $# -gt 0 ]; do
47
53
  GH_REPO_ARGS=(-R "$2")
48
54
  shift 2
49
55
  ;;
56
+ --json)
57
+ JSON_OUTPUT=true
58
+ shift
59
+ ;;
50
60
  *)
51
61
  echo "ERROR: unknown argument: $1" >&2
52
62
  exit 1
@@ -94,7 +104,12 @@ if [ -z "$MILESTONE_TITLE" ]; then
94
104
  echo " Run: git push origin HEAD:$STAGING_BRANCH (from the default branch)" >&2
95
105
  exit 1
96
106
  fi
97
- echo "$STAGING_BRANCH"
107
+ if [ "$JSON_OUTPUT" = "true" ]; then
108
+ printf '{"lane":"%s","branch":"%s","source":"fast-lane","milestone":null}\n' \
109
+ "$STAGING_BRANCH" "$STAGING_BRANCH"
110
+ else
111
+ echo "$STAGING_BRANCH"
112
+ fi
98
113
  else
99
114
  # Slugify: lowercase, spaces → hyphens, strip git-invalid chars, collapse multiple hyphens,
100
115
  # strip leading/trailing hyphens.
@@ -134,5 +149,12 @@ else
134
149
  exit 1
135
150
  fi
136
151
 
137
- echo "$LANE"
152
+ if [ "$JSON_OUTPUT" = "true" ]; then
153
+ # Escape double quotes in milestone title for safe JSON embedding.
154
+ MILESTONE_ESCAPED="${MILESTONE_TITLE//\"/\\\"}"
155
+ printf '{"lane":"%s","branch":"%s","source":"feature-lane","milestone":"%s","slug":"%s"}\n' \
156
+ "$LANE" "$LANE" "$MILESTONE_ESCAPED" "$SLUG"
157
+ else
158
+ echo "$LANE"
159
+ fi
138
160
  fi