forgedock 1.0.17 → 1.0.19
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 +34 -1
- package/bin/cross-publish.mjs +344 -0
- package/bin/forgedock.mjs +229 -17
- package/commands/autopilot.md +77 -0
- package/commands/design-bench.md +176 -0
- package/commands/design-render-critique-loop.md +103 -0
- package/commands/design.md +259 -0
- package/commands/issue.md +7 -3
- package/commands/orchestrate.md +121 -0
- package/commands/{resume.md → pipeline-resume.md} +4 -4
- package/commands/{status.md → pipeline-status.md} +1 -1
- package/commands/qa-sweep.md +37 -2
- package/commands/quality-gate.md +57 -0
- package/commands/review-pr-staging.md +158 -2
- package/commands/test-gate.md +929 -0
- package/commands/work-on/build/implement.md +5 -3
- package/commands/work-on.md +53 -3
- package/docs/CI.md +177 -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/selective-spec-loading-token-savings.md +140 -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/spec-graph-schema.md +397 -0
- package/package.json +1 -1
- package/scripts/bench-scorecard.mjs +239 -0
- package/scripts/build-spec-graph.mjs +509 -0
- package/scripts/check-native-conflicts.sh +200 -0
- package/scripts/classify-lane.sh +0 -7
- package/scripts/design-system-lint.mjs +993 -0
- package/scripts/graph-query.sh +486 -0
- package/scripts/transition-label.sh +10 -2
- package/scripts/validate-spec-graph.sh +423 -0
- package/templates/workflows/forgedock-review.yml +96 -0
package/commands/quality-gate.md
CHANGED
|
@@ -47,6 +47,7 @@ This quality gate uses **domain detection** to adapt to your project's actual te
|
|
|
47
47
|
| IMPORT_RESOLUTION | Python files with new `from app.*` imports added outside `try:` blocks |
|
|
48
48
|
| INFRA | Dockerfiles or entrypoints with runtime UID changes |
|
|
49
49
|
| ROUTER_BUG | Router Python files where gate conditions are narrowed |
|
|
50
|
+
| FORGE_GRAPH | `commands/*.md` specs or `scripts/*` files change (ForgeDock self-consistency) |
|
|
50
51
|
|
|
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.
|
|
52
53
|
|
|
@@ -98,6 +99,7 @@ Before running checks, classify the changed files into domains. This avoids runn
|
|
|
98
99
|
| `*.py` with new `from app.` import statements added outside `try:` blocks | IMPORT_RESOLUTION |
|
|
99
100
|
| `Dockerfile*` or `entrypoint*.sh` with added/changed `USER`, `su-exec`, `gosu`, or `setuid` | INFRA |
|
|
100
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 |
|
|
101
103
|
|
|
102
104
|
**Apply the classification:**
|
|
103
105
|
|
|
@@ -125,6 +127,9 @@ for f in {CHANGED_FILES}; do
|
|
|
125
127
|
echo "$f" | grep -qE '\.py$' && DOMAINS="$DOMAINS STRING_SETS"
|
|
126
128
|
;;
|
|
127
129
|
esac
|
|
130
|
+
case "$f" in
|
|
131
|
+
commands/*.md|scripts/*) DOMAINS="$DOMAINS FORGE_GRAPH" ;;
|
|
132
|
+
esac
|
|
128
133
|
case "$f" in
|
|
129
134
|
.github/workflows/*.yml) DOMAINS="$DOMAINS WORKFLOW" ;;
|
|
130
135
|
esac
|
|
@@ -412,6 +417,58 @@ done
|
|
|
412
417
|
|
|
413
418
|
Report any hit as **HIGH** — the DB connection will fail in any environment where PostgreSQL is not on `127.0.0.1:5432`.
|
|
414
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
|
+
|
|
445
|
+
### 2G.7: Native command conflict check (FORGE_GRAPH domain)
|
|
446
|
+
|
|
447
|
+
**Triggered when**: changed files include `commands/*.md` files (top-level, not sub-phase subdirectories). Skips silently in any project without `scripts/check-native-conflicts.sh`.
|
|
448
|
+
|
|
449
|
+
**Why this matters**: ForgeDock commands are installed into `~/.claude/commands/`, the same namespace as native Claude Code built-in slash commands. A top-level command file whose basename matches a native command name (e.g. `resume.md` → `/resume`, `status.md` → `/status`) completely shadows the native command — users lose access to core Claude Code functionality with no error message. This check prevents the bug class from ever reaching `staging` or `npm publish`. <!-- Added: forge#1074 -->
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
# Run only when top-level commands/*.md files changed and the script ships in this repo.
|
|
453
|
+
# Sub-phase files in subdirectories (commands/work-on/*.md) are installed with their
|
|
454
|
+
# relative path preserved and resolve to namespaced commands — they cannot shadow
|
|
455
|
+
# root-level native commands and are not scanned.
|
|
456
|
+
CONFLICT_CHECKER="$FORGEDOCK_SCRIPTS/check-native-conflicts.sh"
|
|
457
|
+
[ -f "$CONFLICT_CHECKER" ] || CONFLICT_CHECKER="{WORKTREE_PATH}/scripts/check-native-conflicts.sh"
|
|
458
|
+
if [ -f "$CONFLICT_CHECKER" ]; then
|
|
459
|
+
CONFLICT_REPORT=$(bash "$CONFLICT_CHECKER" "{WORKTREE_PATH}/commands" 2>&1) || CONFLICT_EXIT=$?
|
|
460
|
+
echo "$CONFLICT_REPORT"
|
|
461
|
+
if [ "${CONFLICT_EXIT:-0}" -ne 0 ]; then
|
|
462
|
+
# Extract each conflicting file from the report and emit a gate finding.
|
|
463
|
+
echo "$CONFLICT_REPORT" | grep -E '^\s+commands/' | while IFS= read -r hit; do
|
|
464
|
+
echo "FORGE_GRAPH | HIGH | native-conflict | ${hit#* }"
|
|
465
|
+
done
|
|
466
|
+
fi
|
|
467
|
+
fi
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Report each conflict as **HIGH** — a shadowed native command is a silent user-facing regression (the native command becomes unreachable without warning). The builder must rename the conflicting file before the commit. Blocklist updates are in `scripts/check-native-conflicts.sh` (NATIVE_COMMANDS array).
|
|
471
|
+
|
|
415
472
|
### 2H: Asyncio cancellation safety (Python async code)
|
|
416
473
|
|
|
417
474
|
**Triggered when**: changed Python files contain `asyncio.shield`, `asyncio.wait_for`, or `Task.cancel`.
|
|
@@ -26,9 +26,13 @@ GH_REPO=$(yq '.project.owner + "/" + .project.repo' "$CONFIG_FILE")
|
|
|
26
26
|
GH_FLAG="-R $GH_REPO"
|
|
27
27
|
DEFAULT_BRANCH=$(yq '.branches.default' "$CONFIG_FILE")
|
|
28
28
|
STAGING_BRANCH=$(yq '.branches.staging' "$CONFIG_FILE")
|
|
29
|
+
|
|
30
|
+
# Test-gate config (Phase 6.5) — read here so vars are available before first use
|
|
31
|
+
GATE_POSTURE=$(yq '.verification.test_gate.posture // "blocking"' "$CONFIG_FILE" 2>/dev/null || echo "blocking")
|
|
32
|
+
OVERRIDE_PHRASE=$(yq '.verification.test_gate.override_phrase // "OVERRIDE: shipping with test failures —"' "$CONFIG_FILE" 2>/dev/null || echo "OVERRIDE: shipping with test failures —")
|
|
29
33
|
```
|
|
30
34
|
|
|
31
|
-
All `$DEFAULT_BRANCH
|
|
35
|
+
All `$DEFAULT_BRANCH`, `$STAGING_BRANCH`, `$GATE_POSTURE`, and `$OVERRIDE_PHRASE` references below are populated from `forge.yaml`.
|
|
32
36
|
|
|
33
37
|
---
|
|
34
38
|
|
|
@@ -342,6 +346,132 @@ Agent maps dependencies, assesses integration points (service boundaries, env va
|
|
|
342
346
|
|
|
343
347
|
---
|
|
344
348
|
|
|
349
|
+
## Phase 6.5: Runtime Test Gate (BLOCKING — runs after static analysis, before finding triage)
|
|
350
|
+
|
|
351
|
+
**Purpose**: Verify the integrated bundle's acceptance criteria against running code before the deploy verdict. Catches runtime defects (cross-PR interactions, container/startup failures, regression in tested behaviour) that static review cannot surface.
|
|
352
|
+
|
|
353
|
+
**Why here**: Phase 6 completes all static analysis (regression risk, security, quality). Phase 6.5 adds the runtime dimension before Phase 7 triages findings — so any test-gate failures can be filed as `test-failure` issues by `/test-gate` itself, then surface in Phase 7's triage pass.
|
|
354
|
+
|
|
355
|
+
**Posture**: Controlled by `verification.test_gate.posture` in `forge.yaml` (resolved in Config Resolution as `$GATE_POSTURE`). Default: `blocking`. Set to `advisory` to surface failures without preventing deploy. <!-- Added: forge#906 -->
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
echo "=== Phase 6.5: Runtime Test Gate ==="
|
|
359
|
+
echo "Bundle PRs: $(echo $ALL_PR_NUMBERS | tr '\n' ' ')"
|
|
360
|
+
echo "Posture: ${GATE_POSTURE}"
|
|
361
|
+
|
|
362
|
+
# Initialize test-gate verdict (default SKIP — safe if Phase 6.5 is bypassed)
|
|
363
|
+
TEST_GATE_VERDICT="SKIP"
|
|
364
|
+
TEST_GATE_REASON="Phase 6.5 not yet run"
|
|
365
|
+
|
|
366
|
+
# Invoke /test-gate with the bundle PRs already computed in Phase 0A
|
|
367
|
+
# ALL_PR_NUMBERS is the de-duplicated union of both scan methods (commit log + merge subjects)
|
|
368
|
+
GATE_OUTPUT=$(Skill("test-gate", "--prs \"$(echo $ALL_PR_NUMBERS | tr '\n' ' ' | xargs)\" --base $DEFAULT_BRANCH"))
|
|
369
|
+
|
|
370
|
+
# Extract machine-readable verdict from Skill output
|
|
371
|
+
TEST_GATE_VERDICT=$(echo "$GATE_OUTPUT" | grep -oP '(?<=FORGE:TEST_GATE:RESULT=)(BLOCK|PASS|SKIP)' | tail -1 || echo "SKIP")
|
|
372
|
+
|
|
373
|
+
echo "Test-gate verdict: ${TEST_GATE_VERDICT}"
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Verdict handling**:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
case "$TEST_GATE_VERDICT" in
|
|
380
|
+
|
|
381
|
+
SKIP)
|
|
382
|
+
echo "ℹ️ Test gate: SKIP — no executable changes or tests not configured."
|
|
383
|
+
echo " This gap will appear in the Phase 8 summary. No deploy impact."
|
|
384
|
+
TEST_GATE_REASON="SKIP — no runtime tests ran (docs-only bundle, no integration tests configured, or manual-only criteria)"
|
|
385
|
+
;;
|
|
386
|
+
|
|
387
|
+
PASS)
|
|
388
|
+
echo "✅ Test gate: PASS — all test clusters passed. Deploy may proceed."
|
|
389
|
+
TEST_GATE_REASON="PASS — all automated test clusters passed"
|
|
390
|
+
;;
|
|
391
|
+
|
|
392
|
+
BLOCK)
|
|
393
|
+
# Check for override comment on the staging→main PR (mirrors Phase 0A pattern)
|
|
394
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
395
|
+
TG_OVERRIDE=$(gh pr view "$PR_NUMBER" ${GH_FLAG} \
|
|
396
|
+
--json comments \
|
|
397
|
+
--jq "[.comments[].body | select(startswith(\"${OVERRIDE_PHRASE}\"))] | length" 2>/dev/null || echo 0)
|
|
398
|
+
else
|
|
399
|
+
TG_OVERRIDE=0
|
|
400
|
+
fi
|
|
401
|
+
|
|
402
|
+
if [ "${TG_OVERRIDE:-0}" -gt 0 ]; then
|
|
403
|
+
OVERRIDE_REASON=$(gh pr view "$PR_NUMBER" ${GH_FLAG} \
|
|
404
|
+
--json comments \
|
|
405
|
+
--jq "[.comments[].body | select(startswith(\"${OVERRIDE_PHRASE}\"))] | last" 2>/dev/null || echo "(reason not captured)")
|
|
406
|
+
echo "⚠️ Test gate: BLOCK — but override comment detected on PR #${PR_NUMBER}."
|
|
407
|
+
echo " Override: ${OVERRIDE_REASON}"
|
|
408
|
+
echo " Proceeding with deploy. Override is logged in Phase 8 summary."
|
|
409
|
+
TEST_GATE_VERDICT="PASS"
|
|
410
|
+
TEST_GATE_REASON="BLOCK downgraded to PASS by override: ${OVERRIDE_REASON}"
|
|
411
|
+
|
|
412
|
+
elif [ "$GATE_POSTURE" = "advisory" ]; then
|
|
413
|
+
echo "⚠️ Test gate: BLOCK (advisory posture) — runtime failures detected but deploy is NOT prevented."
|
|
414
|
+
echo " Switch verification.test_gate.posture to 'blocking' in forge.yaml to enforce this gate."
|
|
415
|
+
TEST_GATE_REASON="BLOCK (advisory) — runtime failures detected; deploy allowed by advisory posture"
|
|
416
|
+
|
|
417
|
+
else
|
|
418
|
+
# blocking posture (default) — STOP
|
|
419
|
+
echo "⛔ DEPLOY BLOCKED — /test-gate returned BLOCK verdict."
|
|
420
|
+
echo ""
|
|
421
|
+
echo "Runtime failures were detected in the staging→${DEFAULT_BRANCH} bundle."
|
|
422
|
+
echo "The failures were batch-introduced (not pre-existing on ${DEFAULT_BRANCH} baseline)."
|
|
423
|
+
echo ""
|
|
424
|
+
echo "Options:"
|
|
425
|
+
echo " 1. Fix the failing tests and merge fixes to staging before retrying the deploy."
|
|
426
|
+
echo " 2. Post a comment on this PR starting with \"${OVERRIDE_PHRASE} <reason>\" to bypass this gate."
|
|
427
|
+
echo " 3. Set verification.test_gate.posture: advisory in forge.yaml to downgrade to a warning."
|
|
428
|
+
echo ""
|
|
429
|
+
echo "RESULT: BLOCK DEPLOY"
|
|
430
|
+
|
|
431
|
+
# Post structured FORGE:GATE_FAILURE comment for pipeline-health tracking
|
|
432
|
+
GATE_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
433
|
+
if [ -n "$PR_NUMBER" ]; then
|
|
434
|
+
gh pr comment "$PR_NUMBER" ${GH_FLAG} --body "<!-- FORGE:GATE_FAILURE -->
|
|
435
|
+
## Deploy Gate: BLOCKED
|
|
436
|
+
|
|
437
|
+
**Gate**: test-gate
|
|
438
|
+
**Timestamp**: ${GATE_TIMESTAMP}
|
|
439
|
+
**Bundle PRs**: $(echo $ALL_PR_NUMBERS | tr '\n' ' ')
|
|
440
|
+
**Posture**: ${GATE_POSTURE}
|
|
441
|
+
|
|
442
|
+
### What Happened
|
|
443
|
+
|
|
444
|
+
\`/test-gate\` detected runtime failures in the staging→\`${DEFAULT_BRANCH}\` bundle that were NOT present on the \`${DEFAULT_BRANCH}\` baseline. These are batch-introduced regressions.
|
|
445
|
+
|
|
446
|
+
\`/test-gate\` has filed \`test-failure\` issues for each failing cluster — check the issue tracker for details.
|
|
447
|
+
|
|
448
|
+
### Resolution
|
|
449
|
+
|
|
450
|
+
1. Fix the failing tests and merge fixes to staging.
|
|
451
|
+
2. Retry the staging→\`${DEFAULT_BRANCH}\` deploy.
|
|
452
|
+
|
|
453
|
+
To override (ship known failures with documented reason), post a comment containing:
|
|
454
|
+
\`${OVERRIDE_PHRASE} <reason>\`
|
|
455
|
+
|
|
456
|
+
<!-- FORGE:GATE_FAILURE:TYPE=test-gate|BUNDLE=$(echo $ALL_PR_NUMBERS | tr '\n' ' ' | xargs) -->" 2>/dev/null || true
|
|
457
|
+
fi
|
|
458
|
+
exit 1
|
|
459
|
+
fi
|
|
460
|
+
;;
|
|
461
|
+
|
|
462
|
+
*)
|
|
463
|
+
echo "⚠️ Test gate: unrecognised verdict '${TEST_GATE_VERDICT}' — treating as SKIP."
|
|
464
|
+
TEST_GATE_VERDICT="SKIP"
|
|
465
|
+
TEST_GATE_REASON="SKIP — unrecognised verdict from /test-gate (treated as SKIP)"
|
|
466
|
+
;;
|
|
467
|
+
|
|
468
|
+
esac
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
If the gate exits with `RESULT: BLOCK DEPLOY` → **STOP**. A `<!-- FORGE:GATE_FAILURE -->` structured comment is automatically posted on the staging→main PR (if `$PR_NUMBER` is set) for pipeline-health tracking. `/test-gate` will have filed `test-failure` issues for each failing cluster before returning BLOCK.
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
345
475
|
## Phase 7: Finding Triage & Issue Creation
|
|
346
476
|
|
|
347
477
|
### 7A: Extract Findings
|
|
@@ -424,10 +554,36 @@ Labels: `review-finding` + `needs-validation` + `staging-review` + priority (`pr
|
|
|
424
554
|
Post summary with verdict:
|
|
425
555
|
1. CI failed + autofix failed → BLOCK DEPLOY
|
|
426
556
|
2. CI failed + autofix succeeded → continue
|
|
557
|
+
2.5. Test gate returned BLOCK (blocking posture, no override) → BLOCK DEPLOY *(handled in Phase 6.5 — if Phase 8 is reached, BLOCK was either overridden or posture is advisory)*
|
|
427
558
|
3. CONFIRMED CRITICAL (non-CI) → BLOCK DEPLOY
|
|
428
559
|
4. CONFIRMED HIGH blocking (crashes, data loss) → NEEDS FIXES FIRST
|
|
429
560
|
5. All else → APPROVE FOR DEPLOY
|
|
430
561
|
|
|
431
|
-
Include: Material Changes Summary, Risk Matrix (CI, Build, Bugs, Security, Billing, Quality, Regression), Finding Triage Results, Blocking Issues, Deployment Checklist (pre-deploy, deploy, post-deploy verification, rollback triggers), Stats.
|
|
562
|
+
Include: Material Changes Summary, Risk Matrix (CI, Build, Bugs, Security, Billing, Quality, Regression, **Test Gate**), Finding Triage Results, Blocking Issues, Deployment Checklist (pre-deploy, deploy, post-deploy verification, rollback triggers), Stats.
|
|
563
|
+
|
|
564
|
+
**Risk Matrix must include a Test Gate row** (use `$TEST_GATE_VERDICT` and `$TEST_GATE_REASON` set in Phase 6.5):
|
|
565
|
+
|
|
566
|
+
| Domain | Result | Notes |
|
|
567
|
+
|--------|--------|-------|
|
|
568
|
+
| CI | ... | ... |
|
|
569
|
+
| Build | ... | ... |
|
|
570
|
+
| Bugs | ... | ... |
|
|
571
|
+
| Security | ... | ... |
|
|
572
|
+
| Billing | ... | ... |
|
|
573
|
+
| Quality | ... | ... |
|
|
574
|
+
| Regression | ... | ... |
|
|
575
|
+
| **Test Gate** | `${TEST_GATE_VERDICT:-SKIP}` | `${TEST_GATE_REASON:-Phase 6.5 not run}` |
|
|
576
|
+
|
|
577
|
+
If `TEST_GATE_VERDICT` is `SKIP`, surface the gap explicitly in the summary:
|
|
578
|
+
|
|
579
|
+
> **Test Gate: SKIP** — No runtime tests ran for this bundle. This means acceptance criteria were NOT verified against running code before deploy. Cause: `${TEST_GATE_REASON}`. To enable runtime testing, configure `verification.integration_tests` in `forge.yaml`.
|
|
580
|
+
|
|
581
|
+
If `TEST_GATE_VERDICT` is `PASS`, note it as a positive signal:
|
|
582
|
+
|
|
583
|
+
> **Test Gate: PASS** — Acceptance criteria verified against running code. No batch-introduced runtime failures detected.
|
|
584
|
+
|
|
585
|
+
If `TEST_GATE_VERDICT` is `BLOCK` and Phase 8 was reached (advisory posture or override active), note it as a risk:
|
|
586
|
+
|
|
587
|
+
> **Test Gate: BLOCK (override/advisory)** — Runtime failures were detected but deploy is proceeding. Reason: `${TEST_GATE_REASON}`. Filed `test-failure` issues track the failures.
|
|
432
588
|
|
|
433
589
|
**CRITICAL**: This review NEVER merges staging → main. User makes deploy decision via GitHub web UI.
|