forgedock 1.0.3 → 1.0.4
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.
- package/README.md +2 -0
- package/bin/forgedock.mjs +1166 -363
- package/commands/cleanup.md +7 -6
- package/commands/forge-stats.md +2 -1
- package/commands/forgedock-init.md +1 -1
- package/commands/issue.md +6 -5
- package/commands/milestone.md +10 -9
- package/commands/orchestrate.md +5 -4
- package/commands/pipeline-health.md +3 -2
- package/commands/quality-gate.md +2 -2
- package/commands/review-pr.md +55 -11
- package/commands/sync-ecosystem.md +6 -5
- package/commands/work-on/close.md +8 -7
- package/commands/work-on/review.md +3 -1
- package/commands/work-on.md +4 -2
- package/package.json +1 -1
package/commands/cleanup.md
CHANGED
|
@@ -19,13 +19,14 @@ Read `forge.yaml` at the project root to resolve all project-specific variables
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
# Parse forge.yaml for project context
|
|
22
|
-
|
|
22
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
23
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
23
24
|
GH_FLAG="-R $GH_REPO"
|
|
24
|
-
REPO_PATH=$(yq '.paths.root'
|
|
25
|
-
STAGING_BRANCH=$(yq '.branches.staging'
|
|
26
|
-
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner'
|
|
27
|
-
PROJECT_NUMBER=$(yq '.project_board.project_number // "1"'
|
|
28
|
-
PROJECT_ID=$(yq '.project_board.project_id'
|
|
25
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
26
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
27
|
+
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner' "$CONFIG_FILE")
|
|
28
|
+
PROJECT_NUMBER=$(yq '.project_board.project_number // "1"' "$CONFIG_FILE")
|
|
29
|
+
PROJECT_ID=$(yq '.project_board.project_id' "$CONFIG_FILE")
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{STAGING_BRANCH}`, `{PROJECT_BOARD_OWNER}`, `{PROJECT_NUMBER}`, and `{PROJECT_ID}` references below are populated from `forge.yaml`.
|
package/commands/forge-stats.md
CHANGED
|
@@ -14,7 +14,8 @@ Tracks the size of every Forge command file over time. Detects bloat by comparin
|
|
|
14
14
|
Read project home from `forge.yaml` before running any mode:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
|
|
17
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
18
|
+
FORGE_HOME=$(yq e '.paths.root' "$CONFIG_FILE")
|
|
18
19
|
echo "Forge home: $FORGE_HOME"
|
|
19
20
|
```
|
|
20
21
|
|
|
@@ -55,7 +55,7 @@ fi
|
|
|
55
55
|
### 1A: Locate and read
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
FORGE_YAML="$(
|
|
58
|
+
FORGE_YAML="${FORGE_CONFIG:-$(git rev-parse --show-toplevel 2>/dev/null)/forge.yaml}"
|
|
59
59
|
if [ ! -f "$FORGE_YAML" ]; then
|
|
60
60
|
echo "forge.yaml not found in current directory."
|
|
61
61
|
echo ""
|
package/commands/issue.md
CHANGED
|
@@ -38,10 +38,11 @@ The user's input (`$ARGUMENTS`) can be:
|
|
|
38
38
|
Read `forge.yaml` to build the routing table dynamically:
|
|
39
39
|
|
|
40
40
|
```bash
|
|
41
|
-
|
|
41
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
42
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
42
43
|
GH_FLAG="-R $GH_REPO"
|
|
43
|
-
REPO_PATH=$(yq '.paths.root'
|
|
44
|
-
STAGING_BRANCH=$(yq '.branches.staging'
|
|
44
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
45
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
45
46
|
# Satellite repos from forge.yaml → repos.satellites (each has: prefix, repo, staging_branch)
|
|
46
47
|
```
|
|
47
48
|
|
|
@@ -424,8 +425,8 @@ fi
|
|
|
424
425
|
|
|
425
426
|
```bash
|
|
426
427
|
# Add to project board if forge.yaml → project_board is configured
|
|
427
|
-
PROJECT_NUMBER=$(yq '.project_board.project_number // ""'
|
|
428
|
-
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner'
|
|
428
|
+
PROJECT_NUMBER=$(yq '.project_board.project_number // ""' "$CONFIG_FILE")
|
|
429
|
+
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner' "$CONFIG_FILE")
|
|
429
430
|
if [ -n "$PROJECT_NUMBER" ] && [ "$PROJECT_NUMBER" != "null" ]; then
|
|
430
431
|
gh project item-add "$PROJECT_NUMBER" --owner "$PROJECT_BOARD_OWNER" --url "${ISSUE_URL}" 2>/dev/null || true
|
|
431
432
|
fi
|
package/commands/milestone.md
CHANGED
|
@@ -19,14 +19,15 @@ Read `forge.yaml` at the project root to resolve all project-specific variables
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
# Parse forge.yaml for project context
|
|
22
|
-
|
|
22
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
23
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
23
24
|
GH_FLAG="-R $GH_REPO"
|
|
24
|
-
REPO_PATH=$(yq '.paths.root'
|
|
25
|
-
PROJECT_NAME=$(yq '.project.name'
|
|
26
|
-
STAGING_BRANCH=$(yq '.branches.staging'
|
|
27
|
-
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner'
|
|
28
|
-
PROJECT_NUMBER=$(yq '.project_board.project_number // "1"'
|
|
29
|
-
PROJECT_ID=$(yq '.project_board.project_id'
|
|
25
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
26
|
+
PROJECT_NAME=$(yq '.project.name' "$CONFIG_FILE")
|
|
27
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
28
|
+
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner' "$CONFIG_FILE")
|
|
29
|
+
PROJECT_NUMBER=$(yq '.project_board.project_number // "1"' "$CONFIG_FILE")
|
|
30
|
+
PROJECT_ID=$(yq '.project_board.project_id' "$CONFIG_FILE")
|
|
30
31
|
# Build satellite repo map from repos.satellites list
|
|
31
32
|
# Each satellite: { prefix, repo, staging_branch }
|
|
32
33
|
```
|
|
@@ -231,7 +232,7 @@ for ISSUE_NUM in {created_issue_numbers}; do
|
|
|
231
232
|
if [ -n "$ITEM_ID" ]; then
|
|
232
233
|
gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" --field-id {PROJECT_BOARD_STATUS_FIELD_ID} --single-select-option-id {PROJECT_BOARD_STATUS_TODO_ID} 2>/dev/null || true # Status=Todo
|
|
233
234
|
gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" --field-id {PROJECT_BOARD_LANE_FIELD_ID} --single-select-option-id {PROJECT_BOARD_LANE_FEATURE_ID} 2>/dev/null || true # Lane=Feature
|
|
234
|
-
gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" --field-id {PROJECT_BOARD_COMPONENT_FIELD_ID} --single-select-option-id {COMPONENT_OPTION_ID} 2>/dev/null || true # Component (from forge.yaml → project_board.
|
|
235
|
+
gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" --field-id {PROJECT_BOARD_COMPONENT_FIELD_ID} --single-select-option-id {COMPONENT_OPTION_ID} 2>/dev/null || true # Component (from forge.yaml → project_board.field_ids)
|
|
235
236
|
gh project item-edit --project-id {PROJECT_ID} --id "$ITEM_ID" --field-id {PROJECT_BOARD_PRIORITY_FIELD_ID} --single-select-option-id {PRIORITY_OPTION_ID} 2>/dev/null || true # Priority (from label)
|
|
236
237
|
fi
|
|
237
238
|
done
|
|
@@ -568,7 +569,7 @@ Syncs the milestone branch with the latest default branch to pick up fast-lane f
|
|
|
568
569
|
|
|
569
570
|
```bash
|
|
570
571
|
cd {REPO_PATH}
|
|
571
|
-
DEFAULT_BRANCH=$(yq '.branches.default'
|
|
572
|
+
DEFAULT_BRANCH=$(yq '.branches.default' "$CONFIG_FILE")
|
|
572
573
|
git fetch origin $DEFAULT_BRANCH milestone/{slug}
|
|
573
574
|
git checkout milestone/{slug}
|
|
574
575
|
git merge origin/$DEFAULT_BRANCH --no-edit
|
package/commands/orchestrate.md
CHANGED
|
@@ -31,11 +31,12 @@ Read `forge.yaml` at the project root to resolve all project-specific variables
|
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
# Parse forge.yaml for project context
|
|
34
|
-
|
|
34
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
35
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
35
36
|
GH_FLAG="-R $GH_REPO"
|
|
36
|
-
REPO_PATH=$(yq '.paths.root'
|
|
37
|
-
PROJECT_NAME=$(yq '.project.name'
|
|
38
|
-
STAGING_BRANCH=$(yq '.branches.staging'
|
|
37
|
+
REPO_PATH=$(yq '.paths.root' "$CONFIG_FILE")
|
|
38
|
+
PROJECT_NAME=$(yq '.project.name' "$CONFIG_FILE")
|
|
39
|
+
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
39
40
|
# Build satellite repo map from repos.satellites list
|
|
40
41
|
# Each satellite: { prefix, repo, staging_branch }
|
|
41
42
|
```
|
|
@@ -14,8 +14,9 @@ You are the Forge pipeline's self-awareness layer. Your job is to measure how we
|
|
|
14
14
|
Read project identity from `forge.yaml` before running any phase:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
18
|
+
FORGE_REPO=$(yq e '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
19
|
+
FORGE_HOME=$(yq e '.paths.root' "$CONFIG_FILE")
|
|
19
20
|
echo "Forge repo: $FORGE_REPO"
|
|
20
21
|
echo "Forge home: $FORGE_HOME"
|
|
21
22
|
```
|
package/commands/quality-gate.md
CHANGED
|
@@ -783,7 +783,7 @@ If 0 findings:
|
|
|
783
783
|
```
|
|
784
784
|
QUALITY GATE: PASS — no issues detected.
|
|
785
785
|
|
|
786
|
-
|
|
786
|
+
**YOU MUST NOW continue to sub-phase 3H (Format and verify) in work-on.md — this PASS is intermediate, NOT terminal. Do NOT stop.**
|
|
787
787
|
```
|
|
788
788
|
|
|
789
789
|
If findings exist:
|
|
@@ -791,5 +791,5 @@ If findings exist:
|
|
|
791
791
|
QUALITY GATE FINDINGS: N issues detected.
|
|
792
792
|
{findings list}
|
|
793
793
|
|
|
794
|
-
|
|
794
|
+
**YOU MUST NOW fix each HIGH and MEDIUM finding, then re-run the quality gate — this result is intermediate, NOT terminal. Do NOT stop.**
|
|
795
795
|
```
|
package/commands/review-pr.md
CHANGED
|
@@ -108,6 +108,22 @@ REVIEW_SHA=$(gh pr view $ARGUMENTS --json headRefOid --jq '.headRefOid')
|
|
|
108
108
|
REVIEW_SHA_SHORT=$(echo "$REVIEW_SHA" | cut -c1-7)
|
|
109
109
|
gh pr diff $ARGUMENTS --name-only
|
|
110
110
|
gh pr diff $ARGUMENTS
|
|
111
|
+
|
|
112
|
+
# Mergeability check — GitHub computes this asynchronously; retry up to 3× on UNKNOWN
|
|
113
|
+
# <!-- Added: forge#194 -->
|
|
114
|
+
MERGE_HEALTH_RESULT=$(gh pr view $ARGUMENTS --json mergeable,mergeStateStatus --jq '"\\(.mergeable)|\\(.mergeStateStatus)"')
|
|
115
|
+
MERGE_HEALTH=${MERGE_HEALTH_RESULT%%|*}
|
|
116
|
+
MERGE_HEALTH_STATE=${MERGE_HEALTH_RESULT##*|}
|
|
117
|
+
MERGE_RETRY=0
|
|
118
|
+
while [ "$MERGE_HEALTH" = "UNKNOWN" ] && [ "$MERGE_RETRY" -lt 3 ]; do
|
|
119
|
+
MERGE_RETRY=$((MERGE_RETRY + 1))
|
|
120
|
+
sleep 5
|
|
121
|
+
MERGE_HEALTH_RESULT=$(gh pr view $ARGUMENTS --json mergeable,mergeStateStatus --jq '"\\(.mergeable)|\\(.mergeStateStatus)"')
|
|
122
|
+
MERGE_HEALTH=${MERGE_HEALTH_RESULT%%|*}
|
|
123
|
+
MERGE_HEALTH_STATE=${MERGE_HEALTH_RESULT##*|}
|
|
124
|
+
done
|
|
125
|
+
# MERGE_HEALTH: MERGEABLE | CONFLICTING | UNKNOWN (still async after retries)
|
|
126
|
+
# MERGE_HEALTH_STATE: CLEAN | DIRTY | BLOCKED | UNSTABLE | UNKNOWN
|
|
111
127
|
```
|
|
112
128
|
|
|
113
129
|
### 1B: Classify
|
|
@@ -508,7 +524,7 @@ Read the file: $FORGE_HOME/commands/review-pr-agents.md
|
|
|
508
524
|
**>>> LOAD CONFIG: Read forge.yaml for project context:**
|
|
509
525
|
```bash
|
|
510
526
|
# Read review config from forge.yaml (if present in project root)
|
|
511
|
-
FORGE_YAML="${
|
|
527
|
+
FORGE_YAML="${FORGE_CONFIG:-$(git rev-parse --show-toplevel 2>/dev/null)/forge.yaml}"
|
|
512
528
|
PROJECT_NAME=$(yq '.project.name' "$FORGE_YAML" 2>/dev/null || echo "this project")
|
|
513
529
|
PROJECT_CONTEXT=$(yq '.review.context' "$FORGE_YAML" 2>/dev/null || echo "")
|
|
514
530
|
TECH_STACK=$(yq '.review.tech_stack' "$FORGE_YAML" 2>/dev/null || echo "")
|
|
@@ -752,7 +768,7 @@ for FINDING_NUM in {numbers}; do
|
|
|
752
768
|
# PROJECT_ID = forge.yaml → project_board.project_id
|
|
753
769
|
# STATUS_FIELD_ID = forge.yaml → project_board.field_ids.status
|
|
754
770
|
# LANE_FIELD_ID = forge.yaml → project_board.field_ids.lane
|
|
755
|
-
# REVIEW_FINDING_OPTION_ID = forge.yaml → project_board.option_ids.
|
|
771
|
+
# REVIEW_FINDING_OPTION_ID = forge.yaml → project_board.option_ids.workflow.in_review
|
|
756
772
|
ITEM_ID=$(gh project item-add ${PROJECT_NUMBER} --owner ${OWNER} --url "https://github.com/${GH_REPO}/issues/${FINDING_NUM}" --format json --jq '.id' 2>/dev/null)
|
|
757
773
|
[ -n "$ITEM_ID" ] && {
|
|
758
774
|
[ -n "$STATUS_FIELD_ID" ] && gh project item-edit --project-id ${PROJECT_ID} --id "$ITEM_ID" --field-id ${STATUS_FIELD_ID} --single-select-option-id ${REVIEW_FINDING_OPTION_ID} 2>/dev/null || true
|
|
@@ -836,16 +852,31 @@ Verdict determined by standard blocking criteria.
|
|
|
836
852
|
1. Phase 2 automated checks failed (build error, type error, test failure)
|
|
837
853
|
2. Agent finding is CONFIRMED at HIGH or CRITICAL severity
|
|
838
854
|
3. **[Milestone PRs only]** Phase 7A Purpose Regression Gate flagged `HAS_PURPOSE_REGRESSION=true` for this finding — regardless of whether it causes a runtime error
|
|
855
|
+
4. `MERGE_HEALTH == "CONFLICTING"` OR `MERGE_HEALTH_STATE` in {`DIRTY`, `BLOCKED`} — PR cannot be merged cleanly into its base branch <!-- Added: forge#194 -->
|
|
856
|
+
- Verdict: CHANGES REQUESTED. Message: "Merge conflict with `{base}`. Rebase `{head}` onto `origin/{base}`, resolve the conflicting files, then re-run /review-pr."
|
|
857
|
+
- If `MERGE_HEALTH == "UNKNOWN"` after retries: emit a WARNING in the verdict body (do NOT treat as a block — GitHub may still be computing it).
|
|
839
858
|
|
|
840
859
|
```bash
|
|
860
|
+
# Determine if mergeability is a blocker (MERGE_HEALTH/MERGE_HEALTH_STATE set in Phase 1A; BASE/HEAD set in Phase 0 Mode 3)
|
|
861
|
+
HAS_MERGE_CONFLICT=false
|
|
862
|
+
MERGE_CONFLICT_MSG=""
|
|
863
|
+
if [ "$MERGE_HEALTH" = "CONFLICTING" ] || [ "$MERGE_HEALTH_STATE" = "DIRTY" ] || [ "$MERGE_HEALTH_STATE" = "BLOCKED" ]; then
|
|
864
|
+
HAS_MERGE_CONFLICT=true
|
|
865
|
+
MERGE_CONFLICT_MSG="Merge conflict with \`${BASE}\`. Rebase \`${HEAD}\` onto \`origin/${BASE}\`, resolve the conflicting files, then re-run /review-pr."
|
|
866
|
+
fi
|
|
867
|
+
|
|
841
868
|
# Stale review:
|
|
842
869
|
gh pr review $ARGUMENTS --comment --body "Review of commit $REVIEW_SHA_SHORT is stale — PR HEAD changed. Re-run /review-pr."
|
|
843
870
|
|
|
844
871
|
# Clean (no blocking issues):
|
|
845
|
-
gh pr review $ARGUMENTS --comment --body "APPROVED: commit $REVIEW_SHA_SHORT after context-aware review ([N] agents: [names]). [M] findings created as issues. Safe to merge.
|
|
872
|
+
gh pr review $ARGUMENTS --comment --body "APPROVED: commit $REVIEW_SHA_SHORT after context-aware review ([N] agents: [names]). [M] findings created as issues. Safe to merge.
|
|
873
|
+
$([ "$MERGE_HEALTH" = "UNKNOWN" ] && echo "
|
|
874
|
+
⚠ Mergeability: GitHub is still computing merge state (UNKNOWN after retries). Verify manually before merging.")"
|
|
846
875
|
|
|
847
|
-
# Blocking issues (including purpose regressions):
|
|
876
|
+
# Blocking issues (including merge conflicts and purpose regressions):
|
|
848
877
|
gh pr review $ARGUMENTS --comment --body "CHANGES REQUESTED: commit $REVIEW_SHA_SHORT — [N] blocking issues found. See GitHub issues.
|
|
878
|
+
$([ "$HAS_MERGE_CONFLICT" = "true" ] && echo "
|
|
879
|
+
🔴 Merge Conflict: ${MERGE_CONFLICT_MSG}")
|
|
849
880
|
$([ "$HAS_PURPOSE_REGRESSION" = "true" ] && echo "
|
|
850
881
|
⚠ Purpose Regression: [N] finding(s) contradict the milestone's stated goal and are automatically blocking regardless of runtime impact. See: ${PURPOSE_REGRESSION_FINDINGS[@]}")"
|
|
851
882
|
```
|
|
@@ -857,15 +888,28 @@ $([ "$HAS_PURPOSE_REGRESSION" = "true" ] && echo "
|
|
|
857
888
|
**Skip if** `AUTO_MERGE=false`.
|
|
858
889
|
|
|
859
890
|
```bash
|
|
860
|
-
#
|
|
861
|
-
|
|
891
|
+
# Pre-merge mergeability guard — re-fetch fresh state before attempting merge <!-- Added: forge#194 -->
|
|
892
|
+
# A PR that was MERGEABLE at Phase 1A may have become CONFLICTING if the base branch
|
|
893
|
+
# received commits while the review was running. Re-check before every auto-merge.
|
|
894
|
+
PRE_MERGE_RESULT=$(gh pr view {PR_NUMBER} {MERGE_GH_FLAG} --json mergeable,mergeStateStatus --jq '"\\(.mergeable)|\\(.mergeStateStatus)"')
|
|
895
|
+
PRE_MERGE_HEALTH=${PRE_MERGE_RESULT%%|*}
|
|
896
|
+
PRE_MERGE_HEALTH_STATE=${PRE_MERGE_RESULT##*|}
|
|
862
897
|
|
|
863
|
-
|
|
864
|
-
gh
|
|
898
|
+
if [ "$PRE_MERGE_HEALTH" = "CONFLICTING" ] || [ "$PRE_MERGE_HEALTH_STATE" = "DIRTY" ] || [ "$PRE_MERGE_HEALTH_STATE" = "BLOCKED" ]; then
|
|
899
|
+
gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "⛔ Auto-merge aborted for PR #{PR_NUMBER}: PR is not mergeable (\`mergeable=${PRE_MERGE_HEALTH}\`, \`mergeStateStatus=${PRE_MERGE_HEALTH_STATE}\`). Rebase the branch onto \`{MERGE_BASE}\` and resolve conflicts, then re-run /review-pr."
|
|
900
|
+
gh issue edit {MERGE_ISSUE} {MERGE_GH_FLAG} --add-label "needs-human" 2>/dev/null || true
|
|
901
|
+
# STOP — do not attempt gh pr merge on a CONFLICTING/DIRTY PR
|
|
902
|
+
else
|
|
903
|
+
# Checkpoint comment on issue
|
|
904
|
+
gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "Review complete for PR #{PR_NUMBER}. Verdict: {VERDICT}. Proceeding to merge."
|
|
905
|
+
|
|
906
|
+
# Merge
|
|
907
|
+
gh pr merge {PR_NUMBER} {MERGE_GH_FLAG} --merge
|
|
865
908
|
|
|
866
|
-
# Verify
|
|
867
|
-
MERGE_STATE=$(gh pr view {PR_NUMBER} {MERGE_GH_FLAG} --json state --jq '.state')
|
|
868
|
-
[ "$MERGE_STATE" != "MERGED" ] && gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "PR #{PR_NUMBER} merge failed. State: $MERGE_STATE."
|
|
909
|
+
# Verify
|
|
910
|
+
MERGE_STATE=$(gh pr view {PR_NUMBER} {MERGE_GH_FLAG} --json state --jq '.state')
|
|
911
|
+
[ "$MERGE_STATE" != "MERGED" ] && gh issue comment {MERGE_ISSUE} {MERGE_GH_FLAG} --body "PR #{PR_NUMBER} merge failed. State: $MERGE_STATE."
|
|
912
|
+
fi
|
|
869
913
|
```
|
|
870
914
|
|
|
871
915
|
**Important**: Phase 8 ONLY merges the PR. It does NOT close the issue, update labels, or clean up worktrees. When invoked via `/work-on`, those responsibilities belong to `work-on/close.md` — which runs after the router detects state 4 (MERGE_COMPLETE: PR merged + issue open). Doing them here would cause the router to hit TERMINAL_MERGED (state 1) and skip the close phase entirely.
|
|
@@ -19,9 +19,10 @@ Before any phase runs, build the ecosystem registry from `forge.yaml → repos.s
|
|
|
19
19
|
# Read satellite repos from forge.yaml
|
|
20
20
|
# Each entry provides: prefix, repo, staging_branch, local_path
|
|
21
21
|
# (Optional: package, publish_workflow — read from each satellite's package.json / workflow files)
|
|
22
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
22
23
|
|
|
23
24
|
# Gate: if repos section is absent or has no satellites, stop here
|
|
24
|
-
if [ -z "$(yq '.repos.satellites // empty'
|
|
25
|
+
if [ -z "$(yq '.repos.satellites // empty' "$CONFIG_FILE" 2>/dev/null)" ]; then
|
|
25
26
|
echo "No satellite repos configured in forge.yaml → repos.satellites."
|
|
26
27
|
echo "Add entries to forge.yaml to use /sync-ecosystem:"
|
|
27
28
|
echo ""
|
|
@@ -52,10 +53,10 @@ fi
|
|
|
52
53
|
# node_sdk: "sdk/node"
|
|
53
54
|
# Then {REPO_PATH}/sdk/python and {REPO_PATH}/sdk/node are the per-SDK roots.
|
|
54
55
|
|
|
55
|
-
PROJECT_NAME=$(yq '.project.name'
|
|
56
|
-
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner'
|
|
57
|
-
PROJECT_BOARD_NUMBER=$(yq '.project_board.project_number // empty'
|
|
58
|
-
PROJECT_BOARD_ID=$(yq '.project_board.project_id // empty'
|
|
56
|
+
PROJECT_NAME=$(yq '.project.name' "$CONFIG_FILE")
|
|
57
|
+
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // .project.owner' "$CONFIG_FILE")
|
|
58
|
+
PROJECT_BOARD_NUMBER=$(yq '.project_board.project_number // empty' "$CONFIG_FILE")
|
|
59
|
+
PROJECT_BOARD_ID=$(yq '.project_board.project_id // empty' "$CONFIG_FILE")
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
---
|
|
@@ -110,13 +110,14 @@ Update the project board to reflect the merged state. This replaces the old Phas
|
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
112
|
# Read project board config from forge.yaml
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
114
|
+
PROJECT_BOARD_OWNER=$(yq '.project_board.owner // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
115
|
+
PROJECT_ID=$(yq '.project_board.project_id // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
116
|
+
PROJECT_NUMBER=$(yq '.project_board.project_number // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
117
|
+
STATUS_FIELD_ID=$(yq '.project_board.field_ids.status // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
118
|
+
WORKFLOW_FIELD_ID=$(yq '.project_board.field_ids.workflow // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
119
|
+
STATUS_DONE_OPTION_ID=$(yq '.project_board.option_ids.status.done // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
120
|
+
WORKFLOW_MERGED_OPTION_ID=$(yq '.project_board.option_ids.workflow.merged // ""' "$CONFIG_FILE" 2>/dev/null || echo "")
|
|
120
121
|
|
|
121
122
|
if [ -z "$PROJECT_BOARD_OWNER" ] || [ -z "$PROJECT_ID" ] || [ -z "$STATUS_FIELD_ID" ]; then
|
|
122
123
|
echo "INFO: project_board not configured in forge.yaml — skipping board update"
|
|
@@ -219,7 +219,9 @@ gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state'
|
|
|
219
219
|
|
|
220
220
|
## Output
|
|
221
221
|
|
|
222
|
-
|
|
222
|
+
**After posting this result, immediately proceed to the close subcommand — do NOT stop here. `REVIEW_RESULT: status: COMPLETE` is an intermediate result, NOT a terminal state. The pipeline is not done. You MUST invoke `Skill("work-on:close", ...)` now to close the issue, update labels to `workflow:merged`, post the trajectory log, and clean up the worktree.**
|
|
223
|
+
|
|
224
|
+
Output this structured block:
|
|
223
225
|
|
|
224
226
|
```
|
|
225
227
|
REVIEW_RESULT:
|
package/commands/work-on.md
CHANGED
|
@@ -741,9 +741,9 @@ if iteration == max_iterations AND not PASS:
|
|
|
741
741
|
Add needs-human label → STOP
|
|
742
742
|
```
|
|
743
743
|
|
|
744
|
-
|
|
744
|
+
# MUST CONTINUE to sub-phase 3H (Format and verify) — quality gate PASS is intermediate, NOT terminal. <!-- Added: forge#220 -->
|
|
745
745
|
|
|
746
|
-
**After quality gate completes (PASS or fixes applied): proceed to
|
|
746
|
+
**After quality gate completes (PASS or fixes applied): proceed immediately to sub-phase 3H below. Quality gate is an intermediate check — "PASS" means the code is clean, NOT that the build is done. Do NOT stop.**
|
|
747
747
|
|
|
748
748
|
### 3H: Format and verify
|
|
749
749
|
|
|
@@ -974,6 +974,8 @@ gh issue view {NUMBER} {GH_FLAG} --json state --jq '.state'
|
|
|
974
974
|
|
|
975
975
|
<!-- FORGE:PHASE_COMPLETE — Review done, PR merged. See Universal Phase Dispatcher: next phase is Phase 6 (Close & Cleanup). Not terminal — continue immediately. -->
|
|
976
976
|
|
|
977
|
+
**After /review-pr returns and the PR is confirmed merged: immediately proceed to Phase 6 (Close & Cleanup). Do NOT stop here. `REVIEW_RESULT: status: COMPLETE` is an intermediate result — the pipeline is NOT done. Invoke Phase 6 now to close the issue, update labels, post the trajectory log, and clean up the worktree.**
|
|
978
|
+
|
|
977
979
|
---
|
|
978
980
|
|
|
979
981
|
## Phase 6: Close & Cleanup
|