forgedock 1.10.3 → 1.11.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 (46) hide show
  1. package/commands/ci-audit.md +3 -3
  2. package/commands/compat-audit.md +1 -1
  3. package/commands/deploy-pr.md +6 -6
  4. package/commands/explain.md +2 -2
  5. package/commands/fix-ci.md +2 -2
  6. package/commands/forge-stats.md +1 -1
  7. package/commands/forgedock-init.md +3 -3
  8. package/commands/optimize.md +5 -5
  9. package/commands/orchestrate/phase-1-resolve.md +2 -2
  10. package/commands/orchestrate/phase-4-execution.md +182 -41
  11. package/commands/quality-gate.md +1 -1
  12. package/commands/recover-orphans.md +6 -6
  13. package/commands/review-pr-agents/protocols.md +1 -0
  14. package/commands/review-pr-staging.md +28 -6
  15. package/commands/review-pr.md +434 -120
  16. package/commands/security-audit.md +1 -1
  17. package/commands/spec-doctor.md +2 -2
  18. package/commands/test-gate.md +7 -7
  19. package/commands/work-on/build/architect.md +21 -0
  20. package/commands/work-on/build/context.md +1 -1
  21. package/commands/work-on/build/implement.md +17 -0
  22. package/commands/work-on/build/validate.md +2 -2
  23. package/commands/work-on/build.md +98 -9
  24. package/commands/work-on/close.md +33 -11
  25. package/commands/work-on/decompose.md +19 -5
  26. package/commands/work-on/investigate.md +34 -4
  27. package/commands/work-on/remediate.md +30 -6
  28. package/commands/work-on/review.md +10 -8
  29. package/commands/work-on.md +20 -11
  30. package/docs/CONFIG.md +30 -0
  31. package/docs/RELEASE-NOTES.md +9 -0
  32. package/docs/WORK-ON-RUNTIME.md +22 -2
  33. package/docs/site/architecture.md +1 -1
  34. package/docs/spec/forge-protocol-v1.md +10 -0
  35. package/docs/spec/review-protocol.md +1 -0
  36. package/package.json +1 -1
  37. package/packages/protocol/src/types.js +17 -0
  38. package/scripts/check-spec-bash.sh +45 -2
  39. package/scripts/check-spec-bash.test.sh +24 -1
  40. package/scripts/check-spec-markers.sh +53 -0
  41. package/scripts/diff-size.sh +84 -0
  42. package/scripts/diff-size.test.sh +117 -0
  43. package/scripts/forge-root.test.sh +15 -0
  44. package/scripts/remediation-marker-match.test.sh +51 -0
  45. package/scripts/review-pr-arguments.test.sh +180 -0
  46. package/scripts/trusted-comments.test.sh +23 -0
@@ -330,7 +330,7 @@ SWEEP_ID="sweep-$(date -u +%Y%m%dT%H%M%SZ)-$$"
330
330
  # Both reads run under pipefail in a subshell: the pipeline status would otherwise be jq's, and a gh failure
331
331
  # (rate limit, 403) would yield "[]" with exit 0, indistinguishable from "no live holder".
332
332
  claim_orphan() {
333
- local num="$1" now cutoff comments live resp
333
+ local num="${1}" now cutoff comments live resp
334
334
  [ "$DRY_RUN" = "true" ] && return 0 # dry-run: no claim comment is posted (callers already skip dry-run resumes)
335
335
  now=$(date -u +%s); cutoff=$(( now - RECOVERY_CLAIM_TTL_MIN * 60 ))
336
336
  comments=$(set -o pipefail; gh api --paginate "repos/${GH_REPO}/issues/${num}/comments" 2>/dev/null \
@@ -380,7 +380,7 @@ Holder: /recover-orphans — resuming /work-on #${num}. Other sweeps and dispatc
380
380
  # every time (an identical-body edit may not bump updated_at). Never carries FORGE:HEARTBEAT or the RELEASED marker.
381
381
  refresh_orphan() {
382
382
  [ "$DRY_RUN" = "true" ] && return 0
383
- [ -n "${CLAIM_COMMENT_ID:-}" ] && [ "${CLAIM_HELD_NUM:-}" = "$1" ] || return 0
383
+ [ -n "${CLAIM_COMMENT_ID:-}" ] && [ "${CLAIM_HELD_NUM:-}" = "${1}" ] || return 0
384
384
  gh api -X PATCH "repos/${GH_REPO}/issues/comments/${CLAIM_COMMENT_ID}" \
385
385
  -f body="${CLAIM_BODY}
386
386
  Refreshed: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >/dev/null 2>&1 || true
@@ -390,7 +390,7 @@ Refreshed: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >/dev/null 2>&1 || true
390
390
  start_claim_keepalive() {
391
391
  [ "$DRY_RUN" = "true" ] && return 0
392
392
  stop_claim_keepalive
393
- local num="$1" interval=$(( RECOVERY_CLAIM_TTL_MIN * 60 / 3 ))
393
+ local num="${1}" interval=$(( RECOVERY_CLAIM_TTL_MIN * 60 / 3 ))
394
394
  [ "$interval" -ge 1 ] || interval=1
395
395
  ( while sleep "$interval"; do refresh_orphan "$num"; done ) >/dev/null 2>&1 &
396
396
  KEEPALIVE_PID=$!
@@ -408,9 +408,9 @@ stop_claim_keepalive() {
408
408
  release_orphan() {
409
409
  [ "$DRY_RUN" = "true" ] && return 0
410
410
  stop_claim_keepalive
411
- [ "${CLAIM_HELD_NUM:-}" = "$1" ] || return 0
411
+ [ "${CLAIM_HELD_NUM:-}" = "${1}" ] || return 0
412
412
  unset CLAIM_HELD_NUM CLAIM_COMMENT_ID
413
- gh issue comment "$1" ${GH_FLAG} --body "<!-- FORGE:RECOVERY_CLAIM_RELEASED -->
413
+ gh issue comment "${1}" ${GH_FLAG} --body "<!-- FORGE:RECOVERY_CLAIM_RELEASED -->
414
414
  **Sweep: ${SWEEP_ID}**
415
415
  Released by /recover-orphans." >/dev/null 2>&1 || true
416
416
  }
@@ -418,7 +418,7 @@ Released by /recover-orphans." >/dev/null 2>&1 || true
418
418
  # resume_orphan_inline <issue> — run after claim_orphan succeeded. The subshell EXIT trap releases the claim always:
419
419
  # on success and when the Skill call fails. The keepalive runs alongside the Skill call and is stopped by the release.
420
420
  resume_orphan_inline() {
421
- local num="$1"
421
+ local num="${1}"
422
422
  (
423
423
  trap 'release_orphan "$num"' EXIT
424
424
  start_claim_keepalive "$num"
@@ -177,6 +177,7 @@ Append this block at the very end of your comment (after the `---` footer line,
177
177
  7. **Empty block**: If no findings at all, include just the START/END markers
178
178
  8. **HTML comments**: The block is invisible in rendered markdown but parseable by the review system
179
179
  9. **Agent marker**: Include exactly one `<!-- FORGE:REVIEW-AGENT:{domain} -->` marker in the persisted body, where `{domain}` is the lowercase dispatched domain.
180
+ 10. **Reviewed SHA**: Include exactly one line `Reviewed-SHA: [REVIEW_SHA]` (the full 40-hex head you were pinned to) in the persisted body, outside the marker. `/review-pr` scopes its completion wait and idempotent re-entry to this line, so a comment without the current head does not count as a completed review. Do NOT put the SHA inside the `<!-- FORGE:REVIEW-AGENT:{domain} -->` marker.
180
181
 
181
182
  ### Domain Prefixes
182
183
 
@@ -150,10 +150,31 @@ Agents receive `$IS_REENTRY`, `$PRIOR_OPEN_FINDINGS`, and `$PRIOR_RESOLVED_FINDI
150
150
  Resolve the staging→main PR number and post a routing marker immediately. This creates an audit trail — if a staging→main PR has no `FORGE:REVIEW_ROUTE` comment after this command was invoked, the review was bypassed or never started.
151
151
 
152
152
  ```bash
153
- # Resolve PR_NUMBER from $ARGUMENTS
154
- # $ARGUMENTS may be: a PR number, "staging", "feature", or "staging:feature"
155
- if echo "$ARGUMENTS" | grep -qE '^[0-9]+$'; then
156
- PR_NUMBER="$ARGUMENTS"
153
+ # Resolve PR_NUMBER from the argument string (never name the loader placeholder in this block: every
154
+ # occurrence is substituted textually, comments included) <!-- Added: forge#3466 -->
155
+ # The argument string may be: a PR number, "staging", "feature", or "staging:feature"
156
+ # Only the first token is the PR ref; later tokens are flags (--auto-merge, --issue, ...) <!-- allowlist:check-command-side-effects -->
157
+ # The loader substitutes the text before bash parses it, so read it from a quoted heredoc (never a quoted
158
+ # assignment) and reject any string with a quote, backtick, dollar sign, backslash, newline or tab. <!-- Added: forge#3466 -->
159
+ # NONCE RULE: before running, replace NONCE in BOTH delimiter lines with a fresh random hex string (e.g. `openssl rand -hex 16`) not present in the argument text. <!-- Added: forge#3466 -->
160
+ IFS= read -r -d '' STAGING_ARGS_RAW <<'FORGE_ARGS_EOF_NONCE'
161
+ $ARGUMENTS
162
+ FORGE_ARGS_EOF_NONCE
163
+ STAGING_ARGS_RAW="${STAGING_ARGS_RAW%$'\n'}"
164
+ STAGING_ARGS_REJECTED=false
165
+ case "$STAGING_ARGS_RAW" in
166
+ *'"'*|*'`'*|*'$'*|*'\'*|*$'\n'*|*$'\t'*)
167
+ echo "review-pr-staging: rejected argument string (contains a quote, backtick, dollar sign, backslash, newline or tab) - nothing parsed" >&2
168
+ STAGING_ARGS_REJECTED=true; STAGING_ARGS_RAW="" ;;
169
+ esac
170
+ PR_ARG="${STAGING_ARGS_RAW%% *}"
171
+ PR_ARG_NUM=$(printf '%s' "$PR_ARG" | sed -nE 's#^(https?://[^ ]*/pull/)?([0-9]+)$#\2#p')
172
+ if [ -n "$PR_ARG_NUM" ]; then
173
+ PR_NUMBER="$PR_ARG_NUM"
174
+ elif [ "$STAGING_ARGS_REJECTED" = true ]; then
175
+ # Rejected string: stop. Never fall through to the open staging PR lookup or run later phases with no PR. <!-- Added: forge#3466 -->
176
+ echo "review-pr-staging: rejected argument string - stopping (nothing reviewed, nothing posted)" >&2
177
+ exit 1
157
178
  else
158
179
  # Find the open staging→main PR
159
180
  PR_NUMBER=$(gh pr list ${GH_FLAG} \
@@ -164,7 +185,8 @@ else
164
185
  --jq '.[0].number' 2>/dev/null || echo "")
165
186
  fi
166
187
 
167
- REVIEW_SHA_STAGING=$(gh pr view "$PR_NUMBER" ${GH_FLAG} --json headRefOid --jq '.headRefOid' 2>/dev/null | cut -c1-7 || echo "n/a")
188
+ REVIEW_SHA_STAGING="n/a" # gh pr view "" falls back to the current branch PR, so only look up a resolved number <!-- Added: forge#3466 -->
189
+ [ -n "$PR_NUMBER" ] && REVIEW_SHA_STAGING=$(gh pr view "$PR_NUMBER" ${GH_FLAG} --json headRefOid --jq '.headRefOid' 2>/dev/null | cut -c1-7 || echo "n/a")
168
190
 
169
191
  if [ -n "$PR_NUMBER" ]; then
170
192
  gh pr comment "$PR_NUMBER" ${GH_FLAG} --body "<!-- FORGE:REVIEW_ROUTE mode=staging-deploy spec=review-pr-staging.md sha=${REVIEW_SHA_STAGING} -->"
@@ -330,7 +352,7 @@ If the gate exits with `RESULT: PASS` → a `<!-- FORGE:GATE_PASS -->` structure
330
352
  ```bash
331
353
  git fetch origin $DEFAULT_BRANCH $STAGING_BRANCH
332
354
  git diff origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH --stat | tail -20
333
- git diff origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH --numstat | awk '{add+=$1; del+=$2} END {print "Added:", add, "Deleted:", del, "Total:", add+del}'
355
+ git diff origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH --numstat | awk '{add+=$(1); del+=$(2)} END {print "Added:", add, "Deleted:", del, "Total:", add+del}'
334
356
  git diff origin/$DEFAULT_BRANCH...origin/$STAGING_BRANCH --name-only | sort | uniq
335
357
  ```
336
358