forgedock 1.0.16 → 1.0.18
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 +54 -3
- package/bin/cross-publish.mjs +344 -0
- package/bin/forgedock.mjs +2205 -541
- package/bin/github-app-manifest.json.license +2 -0
- package/bin/hooks/session-start.mjs +4 -5
- package/bin/registry.mjs +20 -0
- package/bin/tui.mjs +172 -96
- package/commands/adopt.md +433 -0
- package/commands/analytics.md +2 -0
- package/commands/audit-agents.md +2 -0
- package/commands/audit.md +2 -0
- package/commands/autopilot.md +77 -0
- package/commands/changelog.md +354 -0
- package/commands/cleanup.md +2 -0
- package/commands/compat-audit.md +330 -0
- package/commands/deploy-info.md +6 -5
- package/commands/design-bench.md +176 -0
- package/commands/design-render-critique-loop.md +103 -0
- package/commands/design.md +259 -0
- package/commands/diagnose.md +483 -0
- package/commands/explain.md +349 -0
- package/commands/forge-stats.md +2 -0
- package/commands/forgedock-init.md +2 -0
- package/commands/geo-audit.md +2 -0
- package/commands/incident-response.md +2 -0
- package/commands/issue.md +9 -3
- package/commands/milestone.md +2 -0
- package/commands/optimize.md +411 -0
- package/commands/orchestrate.md +239 -0
- package/commands/pipeline-health.md +60 -1
- package/commands/qa-sweep.md +39 -2
- package/commands/quality-gate.md +67 -0
- package/commands/replay.md +263 -0
- package/commands/resume.md +264 -0
- package/commands/review-pr-agents.md +5 -1
- package/commands/review-pr-staging.md +160 -2
- package/commands/review-pr.md +85 -3
- package/commands/rollback.md +2 -0
- package/commands/scope.md +280 -0
- package/commands/security-audit.md +2 -0
- package/commands/status.md +262 -0
- package/commands/sync-ecosystem.md +2 -0
- package/commands/test-gate.md +929 -0
- package/commands/validate.md +2 -0
- package/commands/work-on/build/architect.md +128 -18
- package/commands/work-on/build/context.md +130 -17
- package/commands/work-on/build/implement.md +7 -3
- package/commands/work-on/build/validate.md +2 -0
- package/commands/work-on/build.md +32 -7
- package/commands/work-on/close.md +85 -0
- package/commands/work-on/decompose.md +2 -0
- package/commands/work-on/investigate.md +30 -3
- package/commands/work-on/review.md +13 -1
- package/commands/work-on-monolithic.md +2 -0
- package/commands/work-on.md +676 -38
- package/docs/BRAND.md +51 -0
- package/docs/CI.md +177 -0
- package/docs/CONFIG.md +94 -0
- package/docs/FORGE-PROTOCOL.md +324 -0
- package/docs/articles/b-vs-c-baseline.md +210 -0
- package/docs/articles/full-abc-benchmark.md +205 -0
- package/docs/articles/per-repo-adaptive-scripts-token-savings.md +174 -0
- package/docs/articles/selective-spec-loading-token-savings.md +140 -0
- package/docs/claude-breakpoints.json +47 -0
- package/docs/design/abc-benchmark.md +195 -0
- package/docs/design/design-architect-rationale.md +123 -0
- package/docs/design/design-memory.md +61 -0
- package/docs/design/design-spec-schema.md +248 -0
- package/docs/design/design-system-lint.md +150 -0
- package/docs/design/divergent-generation.md +106 -0
- package/docs/design/effects-appropriateness.md +133 -0
- package/docs/design/fixtures/briefs/README.md +47 -0
- package/docs/design/fixtures/briefs/cadence.md +39 -0
- package/docs/design/fixtures/briefs/plume.md +39 -0
- package/docs/design/fixtures/briefs/slipstream.md +39 -0
- package/docs/design/fixtures/briefs/tender.md +39 -0
- package/docs/design/fixtures/briefs/voltage.md +39 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/runs.json +175 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/scorecard.json +138 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/b-vs-c-baseline/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-1.html +562 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-2.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-a/run-3.html +618 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-1.html +364 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-2.html +420 -0
- package/docs/design/fixtures/runs/full-abc/cadence/arm-b/run-3.html +506 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-1.html +527 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-2.html +445 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-a/run-3.html +483 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-1.html +611 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-2.html +594 -0
- package/docs/design/fixtures/runs/full-abc/plume/arm-b/run-3.html +817 -0
- package/docs/design/fixtures/runs/full-abc/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-1.html +496 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-2.html +447 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-a/run-3.html +564 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-1.html +373 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-2.html +398 -0
- package/docs/design/fixtures/runs/full-abc/slipstream/arm-b/run-3.html +595 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-1.html +622 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-2.html +475 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-a/run-3.html +660 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-1.html +531 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-2.html +565 -0
- package/docs/design/fixtures/runs/full-abc/tender/arm-b/run-3.html +674 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-1.html +525 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-a/run-3.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-1.html +388 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-2.html +463 -0
- package/docs/design/fixtures/runs/full-abc/voltage/arm-b/run-3.html +568 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-1.html +1068 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-2.html +1423 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-a/run-3.html +1245 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-1.html +305 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-2.html +327 -0
- package/docs/design/fixtures/runs/full-abc-opus/cadence/arm-b/run-3.html +428 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-1.html +1270 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-2.html +1172 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-a/run-3.html +1532 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-1.html +315 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-2.html +342 -0
- package/docs/design/fixtures/runs/full-abc-opus/plume/arm-b/run-3.html +353 -0
- package/docs/design/fixtures/runs/full-abc-opus/runs.json +190 -0
- package/docs/design/fixtures/runs/full-abc-opus/scorecard.json +144 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-1.html +345 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-a/run-3.html +502 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-1.html +408 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-2.html +446 -0
- package/docs/design/fixtures/runs/full-abc-opus/slipstream/arm-b/run-3.html +496 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-1.html +813 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-2.html +711 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-a/run-3.html +721 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-1.html +280 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-2.html +291 -0
- package/docs/design/fixtures/runs/full-abc-opus/tender/arm-b/run-3.html +366 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-1.html +1010 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-2.html +836 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-a/run-3.html +930 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-1.html +407 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-2.html +396 -0
- package/docs/design/fixtures/runs/full-abc-opus/voltage/arm-b/run-3.html +529 -0
- package/docs/design/foundations/brutalist.css +294 -0
- package/docs/design/foundations/editorial.css +300 -0
- package/docs/design/foundations/minimal-luxury.css +295 -0
- package/docs/design/foundations/technical.css +293 -0
- package/docs/design/foundations/warm-photo.css +299 -0
- package/docs/design/reference-corpus.md +1726 -0
- package/docs/design/render-critique-loop.md +57 -0
- package/docs/site/getting-started.md +21 -0
- package/docs/spec-graph-schema.md +397 -0
- package/package.json +3 -3
- package/scripts/bench-scorecard.mjs +239 -0
- package/scripts/build-spec-graph.mjs +509 -0
- package/scripts/classify-lane.sh +138 -0
- package/scripts/design-system-lint.mjs +993 -0
- package/scripts/graph-query.sh +486 -0
- package/scripts/transition-label.sh +169 -0
- package/scripts/validate-pr-target.sh +30 -0
- package/scripts/validate-spec-graph.sh +423 -0
- package/templates/devdocs/README.md +72 -0
- package/templates/devdocs/agent/using-forgedock.md +3 -0
- package/templates/devdocs/agent/using-github.md +3 -0
- package/templates/devdocs/index.yaml +162 -0
- package/templates/devdocs/project/architecture.md +3 -0
- package/templates/devdocs/project/conventions.md +3 -0
- package/templates/devdocs/project/custom-instructions.md +3 -0
- package/templates/devdocs/project/glossary.md +3 -0
- package/templates/devdocs/project/stack.md +3 -0
- package/templates/devdocs/templates/commit-convention.md +3 -0
- package/templates/devdocs/templates/issue.md +3 -0
- package/templates/devdocs/templates/pull-request.md +3 -0
- package/templates/devdocs/templates/review-finding.md +3 -0
- package/templates/workflows/forgedock-review.yml +96 -0
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Self-analysis — measures pipeline performance, correlates with prompt changes, proposes improvements
|
|
3
3
|
argument-hint: [project repo slug or "all"]
|
|
4
4
|
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
5
7
|
|
|
6
8
|
# /pipeline-health — Forge Self-Analysis
|
|
7
9
|
|
|
@@ -541,6 +543,57 @@ fi
|
|
|
541
543
|
|
|
542
544
|
**Why this matters**: Outcome metrics (findings per PR, fix-up rate) measure what the pipeline produces. Orchestration efficiency measures how much wasted motion occurs getting there. An agent that takes 40 minutes to do 8 minutes of actual work has 80% idle time — all of which is pipeline overhead. Reducing stall time directly improves throughput without changing code quality. This is the only metric that reveals whether the routing loop in `work-on.md` is functioning or causing agents to stop and wait.
|
|
543
545
|
|
|
546
|
+
**Step 4 — Batch stall rate (from FORGE:STALL_DETECTED annotations)**:
|
|
547
|
+
|
|
548
|
+
Query issues that received a `<!-- FORGE:STALL_DETECTED -->` comment within the analysis window. This measures how often the orchestrator's time-based stall detector fired — distinct from the resume-cycle count (which comes from completion-event stalls).
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
# Denominator: issues that entered the pipeline in the window (have any workflow:* label)
|
|
552
|
+
# Using pipeline-active issues avoids inflating the denominator with all 500 issues in window
|
|
553
|
+
BATCH_TOTAL=$(gh issue list -R $GH_REPO \
|
|
554
|
+
--state all --limit 500 \
|
|
555
|
+
--json number,createdAt,labels \
|
|
556
|
+
--jq "[.[] | select(.createdAt > \"$SINCE\") | select(.labels | map(.name) | any(startswith(\"workflow:\")))] | length" \
|
|
557
|
+
2>/dev/null || echo 0)
|
|
558
|
+
|
|
559
|
+
# Enumerate pipeline-active issue numbers for per-issue comment queries
|
|
560
|
+
PIPELINE_ISSUE_NUMS=$(gh issue list -R $GH_REPO \
|
|
561
|
+
--state all --limit 500 \
|
|
562
|
+
--json number,createdAt,labels \
|
|
563
|
+
--jq "[.[] | select(.createdAt > \"$SINCE\") | select(.labels | map(.name) | any(startswith(\"workflow:\"))) | .number]" \
|
|
564
|
+
2>/dev/null || echo "[]")
|
|
565
|
+
|
|
566
|
+
# Count issues with at least one FORGE:STALL_DETECTED comment
|
|
567
|
+
# Use wc -l pattern (not variable accumulation) to avoid bash subshell scope loss
|
|
568
|
+
STALL_COUNT=$(echo "$PIPELINE_ISSUE_NUMS" | jq -r '.[]' | while read -r NUM; do
|
|
569
|
+
STALL_COMMENTS=$(gh api repos/$GH_REPO/issues/${NUM}/comments \
|
|
570
|
+
--jq '[.[] | select(.body | contains("FORGE:STALL_DETECTED"))] | length' 2>/dev/null || echo 0)
|
|
571
|
+
[ "$STALL_COMMENTS" -gt 0 ] && echo "$NUM"
|
|
572
|
+
done | wc -l)
|
|
573
|
+
|
|
574
|
+
# Count issues that escalated (2+ stall comments = auto-resume exhausted)
|
|
575
|
+
ESCALATED_COUNT=$(echo "$PIPELINE_ISSUE_NUMS" | jq -r '.[]' | while read -r NUM; do
|
|
576
|
+
STALL_COMMENTS=$(gh api repos/$GH_REPO/issues/${NUM}/comments \
|
|
577
|
+
--jq '[.[] | select(.body | contains("FORGE:STALL_DETECTED"))] | length' 2>/dev/null || echo 0)
|
|
578
|
+
[ "$STALL_COMMENTS" -ge 2 ] && echo "$NUM"
|
|
579
|
+
done | wc -l)
|
|
580
|
+
|
|
581
|
+
# Compute batch stall rate
|
|
582
|
+
if [ "$BATCH_TOTAL" -gt 0 ]; then
|
|
583
|
+
BATCH_STALL_RATE=$(echo "scale=1; $STALL_COUNT * 100 / $BATCH_TOTAL" | bc 2>/dev/null || echo "N/A")
|
|
584
|
+
echo "Batch stall rate: ${BATCH_STALL_RATE}% (${STALL_COUNT}/${BATCH_TOTAL} pipeline issues stalled) — target: < 10%"
|
|
585
|
+
echo "Escalated to needs-human: ${ESCALATED_COUNT} issues"
|
|
586
|
+
else
|
|
587
|
+
BATCH_STALL_RATE="N/A"
|
|
588
|
+
echo "Batch stall rate: N/A (no pipeline issues in window or FORGE:STALL_DETECTED not yet deployed)"
|
|
589
|
+
fi
|
|
590
|
+
export BATCH_STALL_RATE STALL_COUNT BATCH_TOTAL ESCALATED_COUNT
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Target**: `BATCH_STALL_RATE` < 10% (stalled issues / total issues in window). A rate above 10% indicates systemic pipeline unreliability — agents stopping without reaching terminal state frequently enough to require orchestrator intervention.
|
|
594
|
+
|
|
595
|
+
**Note**: `BATCH_STALL_RATE` requires `FORGE:STALL_DETECTED` comments, which are written by the stall detector in `/orchestrate` Step 4B.5. If the stall detector is not deployed, this metric returns N/A and falls back to the resume-cycle proxy from Step 3.
|
|
596
|
+
|
|
544
597
|
### 2L: Post-deploy failure metrics (pipeline escape signal)
|
|
545
598
|
|
|
546
599
|
`/audit` creates `audit-finding` labeled issues in the Forge repo whenever a defect escapes the pipeline and reaches staging or production. Each finding is tagged with a `**Failure point**:` field indicating which pipeline phase failed (INVESTIGATION, IMPLEMENTATION, REVIEW, REVIEW_FALSE_NEG, DEPLOY_GATE, etc.). Aggregating these reveals the post-deploy defect rate and review escape rate — signals invisible to the pre-merge metrics above.
|
|
@@ -2100,6 +2153,8 @@ gh issue create -R $FORGE_REPO \
|
|
|
2100
2153
|
| — Avg agent idle% | ?% | < 30% | ✅/⚠️/❌ |
|
|
2101
2154
|
| — Avg resume cycles per agent | ? | < 1 | ✅/⚠️/❌ |
|
|
2102
2155
|
| — Clean agent rate | ?% | > 60% | ✅/⚠️/❌ |
|
|
2156
|
+
| — Batch stall rate | ?% (?/? issues) | < 10% | ✅/⚠️/❌ |
|
|
2157
|
+
| — Escalated to needs-human | ? issues | 0 | ✅/⚠️/❌ |
|
|
2103
2158
|
| Original work ratio | ?% | > 70% | ✅/⚠️/❌ |
|
|
2104
2159
|
| Milestone merge success | ?% | 80%+ | ✅/⚠️/❌ |
|
|
2105
2160
|
| Issue close velocity | ? days | < 2 days | ✅/⚠️/❌ |
|
|
@@ -2123,13 +2178,15 @@ _If no trajectory data available in window: "No FORGE:TRAJECTORY comments found
|
|
|
2123
2178
|
|
|
2124
2179
|
## Orchestration Efficiency
|
|
2125
2180
|
|
|
2126
|
-
_From Phase 2K — persisted FORGE:AUDIT-AGENTS summaries ([N] sessions, [M] agents total)
|
|
2181
|
+
_From Phase 2K — persisted FORGE:AUDIT-AGENTS summaries ([N] sessions, [M] agents total) + FORGE:STALL_DETECTED annotations_
|
|
2127
2182
|
|
|
2128
2183
|
| Metric | Value | Target | Status |
|
|
2129
2184
|
|--------|-------|--------|--------|
|
|
2130
2185
|
| Avg agent idle% | ?% | < 30% | ✅/⚠️/❌ |
|
|
2131
2186
|
| Avg resume cycles per agent | ? | < 1 | ✅/⚠️/❌ |
|
|
2132
2187
|
| Clean agent rate | ?% | > 60% | ✅/⚠️/❌ |
|
|
2188
|
+
| Batch stall rate | ?% (?/? issues) | < 10% | ✅/⚠️/❌ |
|
|
2189
|
+
| Escalated to needs-human | ? issues | 0 | ✅/⚠️/❌ |
|
|
2133
2190
|
|
|
2134
2191
|
**Top stall boundaries** (phase transitions causing the most stalls):
|
|
2135
2192
|
| Boundary | Occurrences |
|
|
@@ -2140,6 +2197,8 @@ _From Phase 2K — persisted FORGE:AUDIT-AGENTS summaries ([N] sessions, [M] age
|
|
|
2140
2197
|
|
|
2141
2198
|
_If no FORGE:AUDIT-AGENTS data available: "No persisted audit-agents summaries found in window. Run \`audit-agents --persist\` after orchestration runs to enable this metric. Proxy: [N] issues had resume/stall anomalies in trajectory data."_
|
|
2142
2199
|
|
|
2200
|
+
_Batch stall rate: sourced from \`FORGE:STALL_DETECTED\` comments (requires \`/orchestrate\` Step 4B.5 to be active). N/A if stall detector is not deployed._
|
|
2201
|
+
|
|
2143
2202
|
## Transcript Flow Metrics
|
|
2144
2203
|
|
|
2145
2204
|
_From Phase 2N — JSONL transcript analytics ([N] pipeline sessions, [M] subagent sessions in window)_
|
package/commands/qa-sweep.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Full platform QA sweep — auto-discovers every page, tests every UI element via browser automation, creates GitHub issues for all findings
|
|
3
3
|
argument-hint: [all | dashboard | marketing | page <route> | journey | visual | functional | a11y]
|
|
4
4
|
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
5
7
|
|
|
6
8
|
# /qa-sweep — Platform-Wide QA Testing
|
|
7
9
|
|
|
@@ -14,9 +16,26 @@ You are the QA orchestrator. Auto-discover every page across the platform (dashb
|
|
|
14
16
|
|
|
15
17
|
---
|
|
16
18
|
|
|
19
|
+
## Playwright MCP Prerequisite (MANDATORY — check before proceeding)
|
|
20
|
+
|
|
21
|
+
Playwright MCP is a **guaranteed ForgeDock dependency** for all browser automation commands. Before running any qa-sweep phase, verify it is available:
|
|
22
|
+
|
|
23
|
+
**Check**: Confirm `mcp__playwright__*` tools are available in your tool context (e.g., `mcp__playwright__browser_navigate`, `mcp__playwright__browser_snapshot`).
|
|
24
|
+
|
|
25
|
+
**If Playwright MCP tools are NOT available**: STOP immediately. Do not attempt to run qa-sweep — all browser automation steps will fail silently.
|
|
26
|
+
|
|
27
|
+
**Fix**:
|
|
28
|
+
1. Register Playwright MCP: `claude mcp add playwright npx @playwright/mcp@latest`
|
|
29
|
+
2. Restart Claude Code to reload MCP servers
|
|
30
|
+
3. Verify with: `npx forgedock doctor` (Check 9 confirms registration)
|
|
31
|
+
|
|
32
|
+
See README.md Requirements section for full setup instructions.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
17
36
|
## Browser Tool Reference
|
|
18
37
|
|
|
19
|
-
All browser automation uses Playwright MCP tools (`mcp__playwright__*`).
|
|
38
|
+
All browser automation uses Playwright MCP tools (`mcp__playwright__*`). Playwright MCP is a guaranteed ForgeDock dependency — see the Playwright MCP Prerequisite section above.
|
|
20
39
|
|
|
21
40
|
| Action | Tool | Key param |
|
|
22
41
|
|--------|------|-----------|
|
|
@@ -95,7 +114,25 @@ Depth-first, workflow-driven:
|
|
|
95
114
|
|
|
96
115
|
## Phase 0: Setup & Auth
|
|
97
116
|
|
|
98
|
-
**
|
|
117
|
+
**Playwright MCP prerequisite guard** — run BEFORE loading forge.yaml config:
|
|
118
|
+
|
|
119
|
+
Confirm `mcp__playwright__browser_navigate` (or any `mcp__playwright__*` tool) is available in your tool context. If Playwright MCP tools are absent:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
STOP. Playwright MCP is not registered in this Claude Code session.
|
|
123
|
+
|
|
124
|
+
Register it with: claude mcp add playwright npx @playwright/mcp@latest
|
|
125
|
+
Then restart Claude Code and run /qa-sweep again.
|
|
126
|
+
|
|
127
|
+
Verify installation with: npx forgedock doctor (Check 9 confirms Playwright MCP status)
|
|
128
|
+
See: README.md Requirements section for full setup instructions.
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Do not proceed past this guard if Playwright MCP tools are unavailable — all browser automation steps will fail silently without clear error messages.
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
**Load config from forge.yaml** after confirming Playwright MCP is available:
|
|
99
136
|
|
|
100
137
|
```bash
|
|
101
138
|
QA_CONFIG=$(python3 - <<'PYEOF'
|
package/commands/quality-gate.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
description: Pre-commit quality check — catches defects the review would flag, so the builder can fix them before committing
|
|
3
3
|
argument-hint: (invoked by /work-on, not directly)
|
|
4
4
|
---
|
|
5
|
+
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
6
|
+
<!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
|
|
5
7
|
|
|
6
8
|
# /quality-gate — Pre-Commit Quality Check
|
|
7
9
|
|
|
@@ -15,6 +17,42 @@ You do NOT post to GitHub. You do NOT create issues. You return findings directl
|
|
|
15
17
|
|
|
16
18
|
---
|
|
17
19
|
|
|
20
|
+
## Adaptive Design
|
|
21
|
+
|
|
22
|
+
This quality gate uses **domain detection** to adapt to your project's actual tech stack. Checks are not one-size-fits-all — they are selectively triggered based on the file types and patterns present in each change.
|
|
23
|
+
|
|
24
|
+
**How it works**:
|
|
25
|
+
1. Changed files are classified into domains (AUTH, DEPLOY, DATABASE, FRONTEND, etc.) by pattern-matching against file paths, extensions, and diff content
|
|
26
|
+
2. Only checks for detected domains are executed — irrelevant checks are skipped entirely
|
|
27
|
+
3. A project that only changes markdown files runs zero domain checks; a project that changes only TypeScript components runs only SECURITY, FRONTEND, and PROXY checks
|
|
28
|
+
|
|
29
|
+
**Universal check** (always runs for any code file):
|
|
30
|
+
- **SECURITY** — SQL injection, SSRF, path traversal, hardcoded secrets, XSS, command injection
|
|
31
|
+
|
|
32
|
+
**Conditional checks** (triggered only when relevant patterns are detected):
|
|
33
|
+
|
|
34
|
+
| Domain | Triggered when |
|
|
35
|
+
|--------|----------------|
|
|
36
|
+
| AUTH | Files matching `*router*` or `route.ts` handlers |
|
|
37
|
+
| DEPLOY | Files referencing `os.getenv` or `process.env` |
|
|
38
|
+
| DATABASE | `.sql` files or files under `migrations/` |
|
|
39
|
+
| FRONTEND | `.tsx`/`.ts` files in client component paths |
|
|
40
|
+
| PROXY | Client components using `fetch`/`useSWR`/`apiFetch` |
|
|
41
|
+
| SHELL | `.sh` files or scripts using `curl`/`wget` |
|
|
42
|
+
| STRING_SETS | Python files in anti-detection or browser automation paths |
|
|
43
|
+
| CONCURRENCY | Python files using `asyncio.shield`, `asyncio.wait_for`, or `Task.cancel` |
|
|
44
|
+
| STATE | Python files with new module-level mutable variable assignments |
|
|
45
|
+
| CAPACITY | Worker/infra Python files with new size/limit/threshold constants |
|
|
46
|
+
| WORKFLOW | `.yml` files in `.github/workflows/` |
|
|
47
|
+
| IMPORT_RESOLUTION | Python files with new `from app.*` imports added outside `try:` blocks |
|
|
48
|
+
| INFRA | Dockerfiles or entrypoints with runtime UID changes |
|
|
49
|
+
| ROUTER_BUG | Router Python files where gate conditions are narrowed |
|
|
50
|
+
| FORGE_GRAPH | `commands/*.md` specs or `scripts/*` files change (ForgeDock self-consistency) |
|
|
51
|
+
|
|
52
|
+
**Stack-agnostic coverage**: The example domains above reflect common patterns caught in production; they are not requirements. A Go or Ruby project benefits from SECURITY checks. A Node.js project benefits from FRONTEND, PROXY, and DEPLOY checks. Any project benefits from DATABASE and WORKFLOW checks when those file types are present. The stack-specific examples (Python routers, SOPS secrets chain, FastAPI layouts, appleboy SSH deploys) are illustrative — the domain detection system applies the applicable subset to any codebase.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
18
56
|
## Input Format
|
|
19
57
|
|
|
20
58
|
The builder passes:
|
|
@@ -61,6 +99,7 @@ Before running checks, classify the changed files into domains. This avoids runn
|
|
|
61
99
|
| `*.py` with new `from app.` import statements added outside `try:` blocks | IMPORT_RESOLUTION |
|
|
62
100
|
| `Dockerfile*` or `entrypoint*.sh` with added/changed `USER`, `su-exec`, `gosu`, or `setuid` | INFRA |
|
|
63
101
|
| `*.py` in `routers/` where the diff removes a line containing an or-gate condition (lines starting with `-` containing `if.*or`) | ROUTER_BUG |
|
|
102
|
+
| `commands/*.md` or `scripts/*` files (ForgeDock repo only — dogfoods its own spec graph) | FORGE_GRAPH |
|
|
64
103
|
|
|
65
104
|
**Apply the classification:**
|
|
66
105
|
|
|
@@ -88,6 +127,9 @@ for f in {CHANGED_FILES}; do
|
|
|
88
127
|
echo "$f" | grep -qE '\.py$' && DOMAINS="$DOMAINS STRING_SETS"
|
|
89
128
|
;;
|
|
90
129
|
esac
|
|
130
|
+
case "$f" in
|
|
131
|
+
commands/*.md|scripts/*) DOMAINS="$DOMAINS FORGE_GRAPH" ;;
|
|
132
|
+
esac
|
|
91
133
|
case "$f" in
|
|
92
134
|
.github/workflows/*.yml) DOMAINS="$DOMAINS WORKFLOW" ;;
|
|
93
135
|
esac
|
|
@@ -375,6 +417,31 @@ done
|
|
|
375
417
|
|
|
376
418
|
Report any hit as **HIGH** — the DB connection will fail in any environment where PostgreSQL is not on `127.0.0.1:5432`.
|
|
377
419
|
|
|
420
|
+
### 2G.6: Spec graph self-consistency (FORGE_GRAPH domain)
|
|
421
|
+
|
|
422
|
+
**Triggered when**: changed files include `commands/*.md` specs or `scripts/*` files (ForgeDock repo only — it dogfoods its own Spec Knowledge Graph). Skips silently in any project without `scripts/validate-spec-graph.sh`.
|
|
423
|
+
|
|
424
|
+
**Why this matters**: ForgeDock's pipeline behavior is encoded in a ~1.2 MB prose spec corpus where drift is invisible — a `FORGE:*` annotation written but never read, a `Skill(skill="X")` referencing a sub-phase that no longer exists, or a `workflow:*` label set by no command. The Spec Knowledge Graph (`build-spec-graph.mjs`) makes this queryable; `validate-spec-graph.sh` consumes it and partitions findings into HARD (dangling cross-refs, broken label transitions) and SOFT (orphan annotations).
|
|
425
|
+
|
|
426
|
+
```bash
|
|
427
|
+
# Run only when commands/*.md or scripts/* changed and the validator ships in this repo.
|
|
428
|
+
VALIDATOR="$FORGEDOCK_SCRIPTS/validate-spec-graph.sh"
|
|
429
|
+
[ -f "$VALIDATOR" ] || VALIDATOR="{WORKTREE_PATH}/scripts/validate-spec-graph.sh"
|
|
430
|
+
if [ -f "$VALIDATOR" ]; then
|
|
431
|
+
# The graph is auto-built from the worktree (gitignored JSON not required).
|
|
432
|
+
# --soft keeps the gate non-blocking on the documented baseline orphans;
|
|
433
|
+
# HARD findings are still reported below and surfaced as gate findings.
|
|
434
|
+
GRAPH_REPORT=$(bash "$VALIDATOR" --root "{WORKTREE_PATH}" --soft 2>&1 || true)
|
|
435
|
+
echo "$GRAPH_REPORT"
|
|
436
|
+
# Map any HARD finding to a quality-gate finding (dangling ref / broken transition).
|
|
437
|
+
echo "$GRAPH_REPORT" | grep -E '^\s*\[HARD\]' | while IFS= read -r hit; do
|
|
438
|
+
echo "FORGE_GRAPH | HIGH | spec-graph | ${hit#*] }"
|
|
439
|
+
done
|
|
440
|
+
fi
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Report each `[HARD]` line as **HIGH** — a dangling `Skill()` target or a label set by no command is a real pipeline break (an agent will invoke a phase that does not exist, or a state will never be entered/exited). Orphan-annotation `[SOFT]` lines are advisory: surface them in the report for periodic triage but do NOT block the commit. Scaffolding `[INFO]` lines are expected and need no action. To gate on HARD findings (fail the build), drop `--soft`; the default keeps existing baseline orphans from blocking until they are triaged. <!-- Added: forge#869 -->
|
|
444
|
+
|
|
378
445
|
### 2H: Asyncio cancellation safety (Python async code)
|
|
379
446
|
|
|
380
447
|
**Triggered when**: changed Python files contain `asyncio.shield`, `asyncio.wait_for`, or `Task.cancel`.
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Read-only playback of a completed pipeline run — shows FORGE annotations phase by phase with timestamps
|
|
3
|
+
argument-hint: <issue-number>
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /replay — Pipeline Run Playback
|
|
7
|
+
|
|
8
|
+
**Input**: $ARGUMENTS (issue number)
|
|
9
|
+
|
|
10
|
+
Replay a completed pipeline run. Fetches all FORGE annotations posted during the pipeline and displays them chronologically — phase headings, timestamps, and key structured fields. Read-only: nothing is written, no labels changed.
|
|
11
|
+
|
|
12
|
+
**Agent model policy**: Default `model: "sonnet"`. If Sonnet is rate-limited, fall back to `model: "opus"`.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Config Preamble
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
CONFIG_FILE="${FORGE_CONFIG:-forge.yaml}"
|
|
20
|
+
if [ -f "$CONFIG_FILE" ]; then
|
|
21
|
+
GH_OWNER=$(yq '.project.owner' "$CONFIG_FILE")
|
|
22
|
+
GH_REPO_NAME=$(yq '.project.repo' "$CONFIG_FILE")
|
|
23
|
+
GH_REPO="${GH_OWNER}/${GH_REPO_NAME}"
|
|
24
|
+
GH_FLAG="-R $GH_REPO"
|
|
25
|
+
else
|
|
26
|
+
echo "ERROR: forge.yaml not found."
|
|
27
|
+
echo "Run: npx forgedock init"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Step 1: Parse Input
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
NUMBER=$(echo "$ARGUMENTS" | grep -oP '^\s*#?\K\d+' | head -1)
|
|
38
|
+
if [ -z "$NUMBER" ]; then
|
|
39
|
+
echo "Usage: /replay <issue-number>"
|
|
40
|
+
echo "Example: /replay 42"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
echo "Replaying pipeline run for issue #${NUMBER}..."
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 2: Fetch Issue Metadata
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ISSUE_JSON=$(gh issue view "$NUMBER" "$GH_FLAG" \
|
|
52
|
+
--json number,title,state,labels,createdAt,closedAt 2>/dev/null)
|
|
53
|
+
|
|
54
|
+
if [ -z "$ISSUE_JSON" ] || echo "$ISSUE_JSON" | grep -q '"message"'; then
|
|
55
|
+
echo "ERROR: Issue #${NUMBER} not found in ${GH_REPO}."
|
|
56
|
+
exit 1
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
ISSUE_TITLE=$(echo "$ISSUE_JSON" | jq -r '.title')
|
|
60
|
+
ISSUE_STATE=$(echo "$ISSUE_JSON" | jq -r '.state')
|
|
61
|
+
ISSUE_CREATED=$(echo "$ISSUE_JSON" | jq -r '.createdAt')
|
|
62
|
+
ISSUE_CLOSED=$(echo "$ISSUE_JSON" | jq -r '.closedAt // "open"')
|
|
63
|
+
WORKFLOW_LABELS=$(echo "$ISSUE_JSON" | jq -r '[.labels[].name | select(startswith("workflow:"))] | join(", ")')
|
|
64
|
+
|
|
65
|
+
echo ""
|
|
66
|
+
echo "══════════════════════════════════════════════════════════════"
|
|
67
|
+
echo " Pipeline Replay — #${NUMBER}: ${ISSUE_TITLE}"
|
|
68
|
+
echo "══════════════════════════════════════════════════════════════"
|
|
69
|
+
echo " State : ${ISSUE_STATE}"
|
|
70
|
+
echo " Opened : ${ISSUE_CREATED}"
|
|
71
|
+
[ "$ISSUE_CLOSED" != "open" ] && echo " Closed : ${ISSUE_CLOSED}"
|
|
72
|
+
[ -n "$WORKFLOW_LABELS" ] && echo " Labels : ${WORKFLOW_LABELS}"
|
|
73
|
+
echo "══════════════════════════════════════════════════════════════"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Step 3: Fetch All Comments
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# One API call — returns all comments in chronological order
|
|
82
|
+
ALL_COMMENTS=$(gh api "repos/${GH_REPO}/issues/${NUMBER}/comments" 2>/dev/null)
|
|
83
|
+
|
|
84
|
+
if [ -z "$ALL_COMMENTS" ] || [ "$ALL_COMMENTS" = "[]" ]; then
|
|
85
|
+
echo ""
|
|
86
|
+
echo " No comments on this issue. No pipeline run to replay."
|
|
87
|
+
exit 0
|
|
88
|
+
fi
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Step 4: Identify FORGE Annotations
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Filter to comments that contain a FORGE pipeline annotation
|
|
97
|
+
FORGE_COMMENTS=$(echo "$ALL_COMMENTS" | jq -c '[.[] | select(.body | test("<!-- FORGE:(INVESTIGATOR|CONTRACT|CONTEXT|ARCHITECT|BUILDER|DECOMPOSED|TRAJECTORY)"))]')
|
|
98
|
+
FORGE_COUNT=$(echo "$FORGE_COMMENTS" | jq 'length')
|
|
99
|
+
|
|
100
|
+
if [ "$FORGE_COUNT" -eq 0 ]; then
|
|
101
|
+
echo ""
|
|
102
|
+
echo " No FORGE pipeline annotations found on issue #${NUMBER}."
|
|
103
|
+
echo " This issue was not processed by the /work-on pipeline,"
|
|
104
|
+
echo " or comments were posted before pipeline annotation was introduced."
|
|
105
|
+
exit 0
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
echo ""
|
|
109
|
+
echo " Found ${FORGE_COUNT} pipeline annotation(s). Playing back..."
|
|
110
|
+
echo ""
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Step 5: Display Each Annotation
|
|
116
|
+
|
|
117
|
+
Phase mapping (inline): `INVESTIGATOR`→Phase 1, `CONTRACT`→Phase 3C, `CONTEXT`→Phase 3C.5, `ARCHITECT`→Phase 3C.6, `BUILDER`→Phase 3M, `DECOMPOSED`→Phase 2, `TRAJECTORY`→Phase 7.
|
|
118
|
+
|
|
119
|
+
For each FORGE comment in chronological order, print a formatted block. Use Python for robust multiline extraction from the comment body.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
echo "$FORGE_COMMENTS" | jq -c '.[]' | while IFS= read -r COMMENT; do
|
|
123
|
+
BODY=$(echo "$COMMENT" | jq -r '.body')
|
|
124
|
+
CREATED=$(echo "$COMMENT" | jq -r '.created_at')
|
|
125
|
+
AUTHOR=$(echo "$COMMENT" | jq -r '.user.login')
|
|
126
|
+
|
|
127
|
+
# Identify annotation type
|
|
128
|
+
ANNOTATION=$(echo "$BODY" | grep -oP '(?<=<!-- )FORGE:[A-Z:]+(?= -->| -->)' | head -1)
|
|
129
|
+
|
|
130
|
+
# Map to phase label
|
|
131
|
+
case "$ANNOTATION" in
|
|
132
|
+
"FORGE:INVESTIGATOR") PHASE_LABEL="Phase 1 — Investigation" ;;
|
|
133
|
+
"FORGE:CONTRACT") PHASE_LABEL="Phase 3C — Builder Contract" ;;
|
|
134
|
+
"FORGE:CONTEXT") PHASE_LABEL="Phase 3C.5 — Context Gathering" ;;
|
|
135
|
+
"FORGE:ARCHITECT") PHASE_LABEL="Phase 3C.6 — Architecture Plan" ;;
|
|
136
|
+
"FORGE:BUILDER") PHASE_LABEL="Phase 3M — Implementation" ;;
|
|
137
|
+
"FORGE:DECOMPOSED") PHASE_LABEL="Phase 2 — Decomposition" ;;
|
|
138
|
+
"FORGE:TRAJECTORY") PHASE_LABEL="Phase 7 — Pipeline Trajectory" ;;
|
|
139
|
+
*) PHASE_LABEL="$ANNOTATION" ;;
|
|
140
|
+
esac
|
|
141
|
+
|
|
142
|
+
# Format timestamp: 2025-01-15T14:32:00Z → 2025-01-15 14:32 UTC
|
|
143
|
+
TIMESTAMP=$(echo "$CREATED" | sed 's/T/ /; s/:[0-9][0-9]Z/ UTC/')
|
|
144
|
+
|
|
145
|
+
echo "┌─────────────────────────────────────────────────────────────"
|
|
146
|
+
echo "│ ${PHASE_LABEL}"
|
|
147
|
+
echo "│ ${TIMESTAMP} · @${AUTHOR}"
|
|
148
|
+
echo "├─────────────────────────────────────────────────────────────"
|
|
149
|
+
|
|
150
|
+
# Extract key fields per annotation type
|
|
151
|
+
ANNOTATION="$ANNOTATION" BODY_CONTENT="$BODY" python3 - <<'PYEOF'
|
|
152
|
+
import re, sys, os
|
|
153
|
+
|
|
154
|
+
body = os.environ.get('BODY_CONTENT', '')
|
|
155
|
+
|
|
156
|
+
def extract(pattern, default="—"):
|
|
157
|
+
m = re.search(pattern, body, re.MULTILINE | re.DOTALL)
|
|
158
|
+
return m.group(1).strip() if m else default
|
|
159
|
+
|
|
160
|
+
def count_lines(header):
|
|
161
|
+
m = re.search(r'### ' + header + r'\n(.*?)(?=\n###|\Z)', body, re.DOTALL)
|
|
162
|
+
if not m: return 0
|
|
163
|
+
return len([l for l in m.group(1).strip().splitlines() if l.strip()])
|
|
164
|
+
|
|
165
|
+
annotation = os.environ.get('ANNOTATION', '')
|
|
166
|
+
|
|
167
|
+
if annotation == "FORGE:INVESTIGATOR":
|
|
168
|
+
verdict = extract(r'\*\*Verdict\*\*:\s*(\S+)')
|
|
169
|
+
confidence = extract(r'\*\*Confidence\*\*:\s*(\S+)')
|
|
170
|
+
severity = extract(r'\*\*Severity\*\*:\s*(\S+)')
|
|
171
|
+
task_type = extract(r'\*\*Task Type\*\*:\s*([^\n]+)')
|
|
172
|
+
root_cause = extract(r'### Root Cause\n([^\n]+)')
|
|
173
|
+
decompose = extract(r'\*\*(YES|NO)\*\* —', '—')
|
|
174
|
+
print(f"│ Verdict : {verdict} ({confidence} confidence)")
|
|
175
|
+
print(f"│ Severity : {severity} | Task type: {task_type}")
|
|
176
|
+
print(f"│ Root cause : {root_cause[:80]}")
|
|
177
|
+
print(f"│ Decompose : {decompose}")
|
|
178
|
+
|
|
179
|
+
elif annotation == "FORGE:CONTRACT":
|
|
180
|
+
task_type = extract(r'\*\*Task type\*\*:\s*([^\n]+)')
|
|
181
|
+
approach = extract(r'### Proposed Approach\n([^\n]+)')
|
|
182
|
+
n_files = len(re.findall(r'^\|[^|]+\|[^|]+\|[^|]+\|', body, re.MULTILINE)) - 2 # subtract header+sep
|
|
183
|
+
n_criteria = len(re.findall(r'^- \[', body, re.MULTILINE))
|
|
184
|
+
print(f"│ Task type : {task_type}")
|
|
185
|
+
print(f"│ Approach : {approach[:80]}")
|
|
186
|
+
print(f"│ Deliverables: {max(0,n_files)} file(s) | Criteria: {n_criteria}")
|
|
187
|
+
|
|
188
|
+
elif annotation == "FORGE:CONTEXT":
|
|
189
|
+
pitfalls = extract(r'### Known Pitfalls[^\n]*\n([^\n]+)')
|
|
190
|
+
print(f"│ Pitfalls : {pitfalls[:80]}")
|
|
191
|
+
print(f"│ (Context gathered — see full comment for details)")
|
|
192
|
+
|
|
193
|
+
elif annotation == "FORGE:ARCHITECT":
|
|
194
|
+
n_paths = len(re.findall(r'^\| \d+', body, re.MULTILINE))
|
|
195
|
+
order_m = re.search(r'### Implementation Order\n(.*?)(?=\n###|\Z)', body, re.DOTALL)
|
|
196
|
+
n_steps = len([l for l in (order_m.group(1).strip().splitlines() if order_m else []) if re.match(r'^\d+\.', l.strip())])
|
|
197
|
+
print(f"│ Paths : {n_paths} affected path(s)")
|
|
198
|
+
print(f"│ Order : {n_steps} implementation step(s)")
|
|
199
|
+
print(f"│ (Full plan in comment)")
|
|
200
|
+
|
|
201
|
+
elif annotation == "FORGE:BUILDER":
|
|
202
|
+
branch = extract(r'\*\*Branch\*\*:\s*`([^`]+)`')
|
|
203
|
+
commits = extract(r'\*\*Commits\*\*:\s*([^\n]+)')
|
|
204
|
+
n_files = extract(r'\*\*Files changed\*\*:\s*(\d+)')
|
|
205
|
+
approach = extract(r'### Approach\n([^\n]+)')
|
|
206
|
+
print(f"│ Branch : {branch}")
|
|
207
|
+
print(f"│ Commits : {commits}")
|
|
208
|
+
print(f"│ Files : {n_files}")
|
|
209
|
+
print(f"│ Approach : {approach[:80]}")
|
|
210
|
+
|
|
211
|
+
elif annotation == "FORGE:DECOMPOSED":
|
|
212
|
+
subs_m = re.search(r'### Sub-Issues Created\n(.*?)(?=\n###|\Z)', body, re.DOTALL)
|
|
213
|
+
subs = [l.strip() for l in (subs_m.group(1).strip().splitlines() if subs_m else []) if l.strip().startswith('-')]
|
|
214
|
+
print(f"│ Sub-issues : {len(subs)}")
|
|
215
|
+
for s in subs[:5]:
|
|
216
|
+
print(f"│ {s}")
|
|
217
|
+
|
|
218
|
+
elif annotation == "FORGE:TRAJECTORY":
|
|
219
|
+
# Print the phase table lines verbatim (they're already formatted)
|
|
220
|
+
in_table = False
|
|
221
|
+
count = 0
|
|
222
|
+
for line in body.splitlines():
|
|
223
|
+
if '| Phase' in line or (in_table and line.startswith('|')):
|
|
224
|
+
in_table = True
|
|
225
|
+
print(f"│ {line}")
|
|
226
|
+
count += 1
|
|
227
|
+
elif in_table and not line.startswith('|'):
|
|
228
|
+
break
|
|
229
|
+
if count == 0:
|
|
230
|
+
print("│ (Trajectory table not found in comment)")
|
|
231
|
+
decisions = extract(r'\*\*Decisions\*\*:\s*([^\n]+)')
|
|
232
|
+
anomalies = extract(r'\*\*Anomalies\*\*:\s*([^\n]+)')
|
|
233
|
+
print(f"│ Decisions : {decisions[:80]}")
|
|
234
|
+
print(f"│ Anomalies : {anomalies[:80]}")
|
|
235
|
+
|
|
236
|
+
else:
|
|
237
|
+
# Unknown annotation — print first non-empty, non-marker line
|
|
238
|
+
for line in body.splitlines():
|
|
239
|
+
stripped = line.strip()
|
|
240
|
+
if stripped and not stripped.startswith('<!--') and not stripped.startswith('#'):
|
|
241
|
+
print(f"│ {stripped[:100]}")
|
|
242
|
+
break
|
|
243
|
+
PYEOF
|
|
244
|
+
|
|
245
|
+
echo "└─────────────────────────────────────────────────────────────"
|
|
246
|
+
echo ""
|
|
247
|
+
done
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Step 7: Summary Footer
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
echo "══════════════════════════════════════════════════════════════"
|
|
256
|
+
echo " Replay complete — ${FORGE_COUNT} pipeline phase(s) shown"
|
|
257
|
+
echo " Issue: https://github.com/${GH_REPO}/issues/${NUMBER}"
|
|
258
|
+
echo "══════════════════════════════════════════════════════════════"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
**Constraints**: Read-only — never writes to GitHub, never posts comments, never edits labels. Partial pipeline runs are shown as-is (missing phases are absent, not errors).
|