forgedock 1.1.5 → 1.1.6

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.
@@ -387,12 +387,47 @@ while true; do
387
387
  break
388
388
  fi
389
389
 
390
- # Step 1: Orchestrate all open fast-lane issues in parallel via /orchestrate
391
- echo "=== Fast Lane Iteration $FAST_LANE_ITERATIONS: Orchestrating $OPEN_UNMILESTONED issues ==="
390
+ # Step 1: Drive all open fast-lane issues through the durable engine.
391
+ # Each issue is dispatched individually via `forgedock run-issue` so the engine's
392
+ # phase table (fail-closed review gate, deterministic resume, lease-based concurrency)
393
+ # enforces every transition — not the LLM interpreting work-on.md.
394
+ # Fallback: if forgedock CLI is unavailable, delegate to /orchestrate (markdown-spec path).
395
+ echo "=== Fast Lane Iteration $FAST_LANE_ITERATIONS: Dispatching $OPEN_UNMILESTONED issues via engine ==="
396
+
397
+ FORGEDOCK_AVAILABLE=$(command -v forgedock >/dev/null 2>&1 && echo "true" || echo "false")
398
+
392
399
  if [ "$DRY_RUN" = "false" ]; then
393
- Skill("orchestrate", args="fast-lane")
400
+ if [ "$FORGEDOCK_AVAILABLE" = "true" ]; then
401
+ # Engine-first dispatch: enumerate issues and run each through the durable engine.
402
+ # Issues are dispatched concurrently — the engine holds per-issue leases.
403
+ FAST_LANE_ISSUE_NUMS=$(gh issue list $GH_FLAG \
404
+ --state open \
405
+ --limit 200 \
406
+ --json number,milestone,labels \
407
+ --jq '[.[] | select(
408
+ .milestone == null and
409
+ (.labels | map(.name) | any(. == "workflow:merged" or . == "workflow:invalid" or . == "workflow:decomposed" or . == "needs-human") | not)
410
+ )] | .[].number' \
411
+ 2>/dev/null || echo '')
412
+
413
+ for ISSUE_NUM in $FAST_LANE_ISSUE_NUMS; do
414
+ echo "Dispatching #$ISSUE_NUM via forgedock run-issue --lane staging"
415
+ forgedock run-issue "$ISSUE_NUM" --lane staging &
416
+ done
417
+
418
+ # Wait for all engine workers to complete before proceeding to deploy step.
419
+ wait
420
+ echo "Engine dispatch complete for fast-lane iteration $FAST_LANE_ITERATIONS"
421
+ else
422
+ echo "WARNING: forgedock CLI not in PATH — falling back to Skill(orchestrate, fast-lane)"
423
+ Skill("orchestrate", args="fast-lane")
424
+ fi
394
425
  else
395
- echo "[DRY-RUN] Would invoke: Skill(orchestrate, fast-lane)"
426
+ if [ "$FORGEDOCK_AVAILABLE" = "true" ]; then
427
+ echo "[DRY-RUN] Would dispatch each fast-lane issue via: forgedock run-issue <issue> --lane staging"
428
+ else
429
+ echo "[DRY-RUN] Would invoke: Skill(orchestrate, fast-lane)"
430
+ fi
396
431
  fi
397
432
 
398
433
  # Step 2: Deploy staging→main if staging has new commits
@@ -481,12 +516,43 @@ echo "$MILESTONES" | jq -c '.[]' | while IFS= read -r milestone; do
481
516
 
482
517
  echo "=== Processing milestone: '$MS_TITLE' ($MS_PCT% complete, $MS_OPEN open) ==="
483
518
 
484
- # Step 1: Orchestrate open issues in this milestone
519
+ # Step 1: Drive open issues in this milestone through the durable engine.
520
+ # Same engine-first dispatch as Phase 2 — fail-closed gates apply to milestone issues too.
521
+ # Fallback: if forgedock CLI is unavailable, delegate to /orchestrate.
485
522
  if [ "$MS_OPEN" -gt 0 ]; then
523
+ FORGEDOCK_AVAILABLE=$(command -v forgedock >/dev/null 2>&1 && echo "true" || echo "false")
524
+
486
525
  if [ "$DRY_RUN" = "false" ]; then
487
- Skill("orchestrate", args="milestone $MS_SLUG")
526
+ if [ "$FORGEDOCK_AVAILABLE" = "true" ]; then
527
+ MS_ISSUE_NUMS=$(gh issue list $GH_FLAG \
528
+ --state open \
529
+ --limit 200 \
530
+ --json number,milestone,labels \
531
+ --jq --arg slug "$MS_SLUG" \
532
+ '[.[] | select(
533
+ .milestone != null and
534
+ (.milestone.title | ascii_downcase | gsub("[^a-z0-9]+"; "-") | ltrimstr("-") | rtrimstr("-")) == $slug and
535
+ (.labels | map(.name) | any(. == "workflow:merged" or . == "workflow:invalid" or . == "workflow:decomposed" or . == "needs-human") | not)
536
+ )] | .[].number' \
537
+ 2>/dev/null || echo '')
538
+
539
+ for ISSUE_NUM in $MS_ISSUE_NUMS; do
540
+ echo "Dispatching #$ISSUE_NUM via forgedock run-issue --lane milestone/$MS_SLUG"
541
+ forgedock run-issue "$ISSUE_NUM" --lane "milestone/$MS_SLUG" &
542
+ done
543
+
544
+ wait
545
+ echo "Engine dispatch complete for milestone '$MS_TITLE'"
546
+ else
547
+ echo "WARNING: forgedock CLI not in PATH — falling back to Skill(orchestrate, milestone $MS_SLUG)"
548
+ Skill("orchestrate", args="milestone $MS_SLUG")
549
+ fi
488
550
  else
489
- echo "[DRY-RUN] Would invoke: Skill(orchestrate, milestone $MS_SLUG)"
551
+ if [ "$FORGEDOCK_AVAILABLE" = "true" ]; then
552
+ echo "[DRY-RUN] Would dispatch each milestone issue via: forgedock run-issue <issue> --lane milestone/$MS_SLUG"
553
+ else
554
+ echo "[DRY-RUN] Would invoke: Skill(orchestrate, milestone $MS_SLUG)"
555
+ fi
490
556
  fi
491
557
  else
492
558
  echo "Milestone '$MS_TITLE' has no open issues — checking if branch needs deploy"
@@ -392,8 +392,11 @@ while [ "$REVIEW_ITER" -lt "$MAX_REVIEW_ITER" ] && [ "$REVIEW_GATE_PASSED" = "fa
392
392
  # This loop's next iteration will re-read the latest PR state.
393
393
  fi
394
394
  else
395
- echo "WARNING: Could not determine review verdict from FORGE:REVIEW comment. Assuming APPROVED."
396
- REVIEW_GATE_PASSED=true
395
+ # Fail-closed: unknown verdict must block merge, not grant it.
396
+ # If FORGE:REVIEW comment is missing, malformed, or unparseable for any reason
397
+ # (race condition, context compaction, skill error, comment format mismatch),
398
+ # treat it as a failed review — REVIEW_GATE_PASSED remains false.
399
+ echo "ERROR: Could not determine review verdict from FORGE:REVIEW comment. Blocking merge — review verdict is required. (#1714)"
397
400
  fi
398
401
  done
399
402
 
@@ -443,15 +443,19 @@ Proceed? (yes / adjust / pick specific issues)
443
443
 
444
444
  ---
445
445
 
446
- ## Engine mode (headless)
446
+ ## Engine mode (default)
447
447
 
448
- When running headless/CI, dispatch each issue via the durable execution engine instead of spawning prose agents:
448
+ Dispatch each issue via the durable execution engine. This is the **default execution mode** for both interactive (`/autopilot`, `/orchestrate`) and headless/CI paths:
449
449
 
450
450
  ```bash
451
451
  forgedock run-issue <issue> --lane <staging|milestone/slug>
452
452
  ```
453
453
 
454
- The engine drives every phase transition deterministically, mirrors state to the `FORGE:STATE` block on the issue, and holds a lease. To recover stalls, scan in-flight issues' `FORGE:STATE`; any issue with an expired lease and a non-terminal state is re-dispatched with the same `forgedock run-issue <issue>` command — it resumes from the last committed phase (idempotent). This replaces the label-heuristic "already in progress" check and the resume-with-nagging loop.
454
+ The engine drives every phase transition deterministically, mirrors state to the `FORGE:STATE` block on the issue, and holds a lease. Its **fail-closed review gate** (`phases.mjs → detectOutcome`) means the PR must be confirmed merged before the phase is committed — missing or unparseable review comments are treated as failures, not approvals. To recover stalls, scan in-flight issues' `FORGE:STATE`; any issue with an expired lease and a non-terminal state is re-dispatched with the same `forgedock run-issue <issue>` command — it resumes from the last committed phase (idempotent). This replaces the label-heuristic "already in progress" check and the resume-with-nagging loop.
455
+
456
+ **Why engine-first**: The engine's phase table enforces gate semantics in code — not via LLM interpretation of markdown specs. This eliminates the class of bug where the LLM assumes a review approved when the FORGE:REVIEW comment is absent or unreadable (issue #1714). Interactive sessions that run `/work-on` via Skill invocations additionally bridge to the engine run-log via the SubagentStop hook (`bin/hooks/interactive-engine.mjs`) — state is durable across compaction and context resets.
457
+
458
+ **Fallback (no forgedock CLI)**: If `forgedock` is not in PATH, fall back to spawning Agent sub-agents that run `Skill("work-on", ...)` per issue. The SubagentStop hook still bridges these runs to the engine run-log for state persistence.
455
459
 
456
460
  ### Concurrency model: in-process worker pool + worktree-per-issue
457
461
 
@@ -171,7 +171,31 @@ Use `${ISSUE_LANE[$NUM]}` and `${ISSUE_PR_BASE[$NUM]}` to populate `{LANE}` and
171
171
 
172
172
  ### Step 4A: Dispatch ready issues
173
173
 
174
- **REMINDER: You MUST use the template below verbatim. Only fill in `{VARIABLES}`. Do NOT rewrite the agent prompt. Do NOT write custom implementation instructions. The agent MUST invoke `/work-on` via the Skill tool — this is the HARD RULE from the top of this file.**
174
+ **Engine-first dispatch (default)**: When `forgedock` is in PATH, dispatch each ready issue via the durable engine rather than spawning prose Agent sub-agents. The engine's phase table enforces gate semantics in code — its fail-closed review gate and deterministic phase ordering are not subject to LLM interpretation.
175
+
176
+ ```bash
177
+ # Engine-first dispatch: check CLI availability, then dispatch ready issues in parallel
178
+ FORGEDOCK_AVAILABLE=$(command -v forgedock >/dev/null 2>&1 && echo "true" || echo "false")
179
+
180
+ if [ "$FORGEDOCK_AVAILABLE" = "true" ]; then
181
+ for NUM in {ready_issue_numbers}; do
182
+ LANE="${ISSUE_LANE[$NUM]}"
183
+ PR_BASE="${ISSUE_PR_BASE[$NUM]}"
184
+ echo "Dispatching #$NUM via forgedock run-issue --lane $PR_BASE"
185
+ forgedock run-issue "$NUM" --lane "$PR_BASE" &
186
+ done
187
+ wait
188
+ echo "Engine dispatch complete — advancing to Step 4B (completion sweep)"
189
+ else
190
+ echo "WARNING: forgedock CLI not in PATH — falling back to Agent-spawn path (markdown-spec execution)"
191
+ # Fall through to Agent-spawn template below. The SubagentStop hook (bin/hooks/interactive-engine.mjs)
192
+ # bridges these runs to the engine run-log for state persistence even on the fallback path.
193
+ fi
194
+ ```
195
+
196
+ **Agent-spawn path (fallback when forgedock CLI unavailable)**: When `FORGEDOCK_AVAILABLE=false`, spawn Agent sub-agents per issue using the template below. This preserves engine state via the SubagentStop hook even without the CLI.
197
+
198
+ **REMINDER: You MUST use the template below verbatim when on the Agent-spawn fallback path. Only fill in `{VARIABLES}`. Do NOT rewrite the agent prompt. Do NOT write custom implementation instructions. The agent MUST invoke `/work-on` via the Skill tool — this is the HARD RULE from the top of this file.**
175
199
 
176
200
  For each **ready** issue (all predecessors resolved or no predecessors), spawn an Agent sub-agent that runs the full `/work-on` pipeline. On the initial dispatch, this is every issue with an empty predecessor set. On subsequent dispatches (triggered by agent completions in Step 4B), this is every newly-unblocked issue.
177
201
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forgedock",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "Autonomous dev pipeline for Claude Code. GitHub as institutional memory — every issue, PR, and annotation survives context resets and makes the next agent smarter.",
5
5
  "bin": {
6
6
  "forgedock": "bin/forgedock.mjs"