forgedock 1.1.11 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/bin/cli-spawn-shared.mjs +25 -0
- package/bin/engine/phases.mjs +178 -28
- package/bin/engine/state.mjs +39 -7
- package/bin/engine-cli.mjs +85 -3
- package/bin/engine.mjs +71 -8
- package/bin/forgedock.mjs +322 -20
- package/bin/hooks/pre-tool-use.mjs +345 -10
- package/bin/init-enrich-api.mjs +12 -4
- package/bin/init-enrich-cli.mjs +328 -0
- package/bin/init-enrich.mjs +137 -0
- package/bin/journey.mjs +116 -15
- package/bin/runner.mjs +653 -10
- package/commands/adopt.md +1 -1
- package/commands/analytics.md +10 -7
- package/commands/audit-agents.md +29 -6
- package/commands/audit.md +9 -20
- package/commands/autopilot.md +52 -78
- package/commands/changelog.md +1 -1
- package/commands/ci-audit.md +19 -10
- package/commands/cleanup.md +1 -1
- package/commands/compat-audit.md +1 -1
- package/commands/deploy-info.md +1 -1
- package/commands/deploy-pr.md +1 -1
- package/commands/design-bench.md +1 -1
- package/commands/design-render-critique-loop.md +1 -1
- package/commands/diagnose.md +1 -1
- package/commands/forge-stats.md +6 -5
- package/commands/forgedock-init.md +1 -1
- package/commands/geo-audit.md +9 -6
- package/commands/incident-response.md +15 -5
- package/commands/issue.md +302 -15
- package/commands/milestone.md +45 -18
- package/commands/optimize.md +1 -1
- package/commands/orchestrate/phase-1-resolve.md +20 -5
- package/commands/orchestrate/phase-4-execution.md +8 -1
- package/commands/orchestrate/phase-5-cleanup.md +43 -2
- package/commands/orchestrate/phase-6-report.md +1 -0
- package/commands/orchestrate.md +1 -1
- package/commands/pipeline-health/phase-4-proposals.md +40 -10
- package/commands/pipeline-health/phase-5-report.md +140 -13
- package/commands/pipeline-health.md +1 -1
- package/commands/pipeline-resume.md +1 -1
- package/commands/pipeline-status.md +1 -1
- package/commands/qa-sweep.md +27 -4
- package/commands/recover-orphans.md +1 -1
- package/commands/replay.md +16 -2
- package/commands/review-pr-agents/protocols.md +4 -0
- package/commands/review-pr-agents/spec-cli.md +179 -0
- package/commands/review-pr-agents.md +7 -4
- package/commands/review-pr-staging.md +44 -15
- package/commands/review-pr.md +122 -28
- package/commands/rollback.md +15 -5
- package/commands/security-audit.md +14 -11
- package/commands/signal-planner.md +32 -12
- package/commands/spec-doctor.md +1 -1
- package/commands/sync-ecosystem.md +54 -11
- package/commands/test-gate.md +49 -11
- package/commands/upgrade-deps.md +518 -0
- package/commands/validate.md +16 -5
- package/commands/work-on/build/architect.md +1 -1
- package/commands/work-on/build/context.md +1 -1
- package/commands/work-on/build/implement.md +2 -2
- package/commands/work-on/build/validate.md +64 -2
- package/commands/work-on/build.md +10 -2
- package/commands/work-on/close.md +1 -1
- package/commands/work-on/decompose.md +35 -25
- package/commands/work-on/investigate.md +2 -2
- package/commands/work-on/remediate.md +1 -1
- package/commands/work-on/review.md +5 -3
- package/commands/work-on-monolithic.md +1 -1
- package/commands/work-on.md +28 -7
- package/docs/AIDER.md +264 -0
- package/docs/AUTOPILOT-CRON.md +130 -0
- package/docs/CI.md +18 -0
- package/docs/CONFIG.md +41 -2
- package/docs/CURSOR.md +135 -0
- package/docs/OPENCODE.md +312 -0
- package/docs/demo.gif +0 -0
- package/docs/eval/README.md +4 -4
- package/docs/launch/show-hn.md +3 -3
- package/docs/site/annotations-explained.md +2 -2
- package/docs/site/architecture.md +180 -0
- package/docs/site/command-reference.md +38 -0
- package/docs/site/configuration.md +243 -0
- package/docs/site/for-companies.md +4 -4
- package/docs/site/forge-annotation-protocol.md +1 -1
- package/docs/site/github-is-the-memory.md +2 -2
- package/docs/site/how-it-works.md +1 -1
- package/docs/site/public/llms-full.txt +380 -0
- package/docs/site/public/llms.txt +35 -0
- package/docs/spec/review-protocol.md +6 -0
- package/docs/spec-graph-schema.md +3 -3
- package/package.json +2 -2
- package/packages/protocol/README.md +75 -0
- package/packages/protocol/src/card.js +114 -0
- package/packages/protocol/src/cli.js +4 -77
- package/packages/protocol/src/emit.js +14 -3
- package/packages/protocol/src/index.js +2 -1
- package/packages/protocol/src/parse.js +17 -8
- package/packages/protocol/src/types.js +1 -1
- package/packages/protocol/src/validate.js +11 -0
- package/scripts/check-command-side-effects.sh +261 -0
- package/scripts/check-spec-markers.sh +273 -0
- package/scripts/classify-lane.sh +29 -7
- package/scripts/forge-run.sh +322 -0
- package/templates/cross-repo-trigger.yml +282 -0
- package/templates/cursor/forge.mdc +148 -0
- package/templates/github-actions/forgedock-autopilot.yml +113 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Periodic security posture audit — runs a scripted 4-phase checklist against repo files (not diffs), creates GitHub issues for confirmed findings
|
|
3
|
-
argument-hint: [--repo <prefix>] [--phase 1|2|3|4|all] [--dry-run]
|
|
3
|
+
argument-hint: "[--repo <prefix>] [--phase 1|2|3|4|all] [--dry-run]"
|
|
4
4
|
install: extras
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
@@ -515,20 +515,18 @@ gh label create "priority:P3" --color "C2E0C6" --description "Low priority — m
|
|
|
515
515
|
|
|
516
516
|
### 5C: Create GitHub issues for CONFIRMED findings
|
|
517
517
|
|
|
518
|
-
For each CONFIRMED finding (not REVIEW-only), create an issue:
|
|
518
|
+
For each CONFIRMED finding (not REVIEW-only), create an issue — routed through the `/issue` create-hook's programmatic invocation contract (see `commands/issue.md` → Programmatic Invocation Contract) so dedup and mandatory-section validation run on every finding:
|
|
519
519
|
|
|
520
520
|
```bash
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
--label "security,audit-finding,{SEVERITY}" \
|
|
524
|
-
--body "$(cat <<'ISSUE_EOF'
|
|
521
|
+
SECURITY_BODY_FILE=$(mktemp)
|
|
522
|
+
cat > "$SECURITY_BODY_FILE" <<'ISSUE_EOF'
|
|
525
523
|
## Problem
|
|
526
524
|
|
|
527
525
|
{2-3 sentences describing the finding, its location, and exploitation path.}
|
|
528
526
|
|
|
529
527
|
## Evidence
|
|
530
528
|
|
|
531
|
-
**File**:
|
|
529
|
+
**File**: `{FILE}`
|
|
532
530
|
**Line**: {LINE}
|
|
533
531
|
**Finding**: {GREP_OUTPUT}
|
|
534
532
|
|
|
@@ -538,23 +536,28 @@ gh issue create -R "{GH_REPO}" \
|
|
|
538
536
|
|
|
539
537
|
## Affected Files
|
|
540
538
|
|
|
541
|
-
1.
|
|
539
|
+
1. `{FILE}` — {what needs to change}
|
|
542
540
|
|
|
543
541
|
## Acceptance Criteria
|
|
544
542
|
|
|
545
543
|
- [ ] {Specific remediation step}
|
|
546
|
-
- [ ] Verified by re-running
|
|
544
|
+
- [ ] Verified by re-running `/security-audit` after fix
|
|
547
545
|
|
|
548
546
|
## Context
|
|
549
547
|
|
|
550
548
|
**Audited at**: {TIMESTAMP}
|
|
551
549
|
**Audit phase**: {PHASE}
|
|
552
550
|
**Severity**: {SEVERITY}
|
|
553
|
-
**Source**:
|
|
551
|
+
**Source**: `/security-audit` run on `{GH_REPO}`
|
|
554
552
|
ISSUE_EOF
|
|
555
|
-
)"
|
|
556
553
|
```
|
|
557
554
|
|
|
555
|
+
```
|
|
556
|
+
Skill(skill="issue", args="--title \"{ISSUE_TITLE}\" --body-file \"$SECURITY_BODY_FILE\" --label \"security\" --label \"audit-finding\" --label \"{SEVERITY}\"")
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
`security`, `audit-finding`, and `{SEVERITY}` are each passed as their own `--label` flag — `/issue`'s programmatic mode does not comma-split a single `--label` value. Note: `/issue` resolves the target repo from `forge.yaml → project.owner/repo` (or the satellite-prefix table), not from a caller-supplied `-R`/`--repo` flag — this is correct for the common case where `{GH_REPO}` matches `forge.yaml`'s project repo.
|
|
560
|
+
|
|
558
561
|
### 5D: Post audit summary comment (if invoked from a GitHub issue context)
|
|
559
562
|
|
|
560
563
|
If this command was invoked from within a `/work-on` context or with an issue reference:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Closed-loop autonomy — convert a production/analytics/incident signal into a dependency-ordered issue DAG, execute via /orchestrate, then verify the originating signal is resolved. Event-driven companion to /autopilot.
|
|
3
|
-
argument-hint: [--signal '<json>' | --metric '<name> <threshold>' | --incident <issue#> | --geo | --dry-run | --max-issues N]
|
|
3
|
+
argument-hint: "[--signal '<json>' | --metric '<name> <threshold>' | --incident <issue#> | --geo | --dry-run | --max-issues N]"
|
|
4
4
|
install: extras
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
@@ -224,10 +224,10 @@ Detect cycles — if a cycle exists, break it by demoting the lower-priority uni
|
|
|
224
224
|
Post the DAG to a tracker issue on GitHub. First, create the tracker issue:
|
|
225
225
|
|
|
226
226
|
```bash
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
TRACKER_TITLE="signal-planner: DAG tracker for {SIGNAL.name}"
|
|
228
|
+
TRACKER_BODY_TMPFILE=$(mktemp)
|
|
229
|
+
trap 'rm -f "$TRACKER_BODY_TMPFILE"' EXIT
|
|
230
|
+
cat > "$TRACKER_BODY_TMPFILE" <<'TRACKER_EOF'
|
|
231
231
|
## Signal
|
|
232
232
|
|
|
233
233
|
**Type**: {SIGNAL.type}
|
|
@@ -247,7 +247,18 @@ This tracker was created by \`/signal-planner\`. The dependency DAG below repres
|
|
|
247
247
|
- [ ] All DAG issues merged
|
|
248
248
|
- [ ] Originating signal verified resolved (FORGE:SIGNAL_RESOLVED posted)
|
|
249
249
|
TRACKER_EOF
|
|
250
|
-
)
|
|
250
|
+
# Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
|
|
251
|
+
# `gh issue create`. This body uses "## Acceptance" rather than the mandatory "## Acceptance
|
|
252
|
+
# Criteria"/"## Affected Files"/"## Problem" sections — /issue's Phase 3F will non-blockingly
|
|
253
|
+
# append placeholder mandatory sections; this is expected for a coordination tracker issue.
|
|
254
|
+
Skill(skill="issue", args="--title \"$TRACKER_TITLE\" --body-file \"$TRACKER_BODY_TMPFILE\" --label signal-planner --label \"priority:{SIGNAL.severity}\"")
|
|
255
|
+
rm -f "$TRACKER_BODY_TMPFILE"
|
|
256
|
+
trap - EXIT
|
|
257
|
+
TRACKER_ISSUE=$(gh issue list -R {GH_REPO} \
|
|
258
|
+
--state open \
|
|
259
|
+
--search "$TRACKER_TITLE" \
|
|
260
|
+
--json number,title \
|
|
261
|
+
--jq --arg t "$TRACKER_TITLE" '.[] | select(.title == $t) | .number' 2>/dev/null | head -1)
|
|
251
262
|
```
|
|
252
263
|
|
|
253
264
|
Then post the `FORGE:PLAN_DAG` comment:
|
|
@@ -312,10 +323,10 @@ If governor would be breached, **hard fail** — do not create any issues, do no
|
|
|
312
323
|
For each work unit with `existing == null`, create a GitHub issue:
|
|
313
324
|
|
|
314
325
|
```bash
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
WORK_UNIT_TITLE="{work_unit.title}"
|
|
327
|
+
WORK_UNIT_BODY_TMPFILE=$(mktemp)
|
|
328
|
+
trap 'rm -f "$WORK_UNIT_BODY_TMPFILE"' EXIT
|
|
329
|
+
cat > "$WORK_UNIT_BODY_TMPFILE" <<'ISSUE_EOF'
|
|
319
330
|
## Problem
|
|
320
331
|
|
|
321
332
|
{work_unit.rationale — why this is needed to resolve the originating signal}
|
|
@@ -341,10 +352,19 @@ Signal context: {relevant subset of SIGNAL.context for this work unit}
|
|
|
341
352
|
---
|
|
342
353
|
*Created by \`/signal-planner\` on $(date -u +%Y-%m-%dT%H:%M:%SZ). Will be investigated before any fix is applied.*
|
|
343
354
|
ISSUE_EOF
|
|
344
|
-
)
|
|
355
|
+
# Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
|
|
356
|
+
# `gh issue create`.
|
|
357
|
+
Skill(skill="issue", args="--title \"$WORK_UNIT_TITLE\" --body-file \"$WORK_UNIT_BODY_TMPFILE\" --label \"priority:{work_unit.priority}\" --label \"{work_unit.type}\"")
|
|
358
|
+
rm -f "$WORK_UNIT_BODY_TMPFILE"
|
|
359
|
+
trap - EXIT
|
|
360
|
+
ISSUE_NUM=$(gh issue list -R {GH_REPO} \
|
|
361
|
+
--state open \
|
|
362
|
+
--search "$WORK_UNIT_TITLE" \
|
|
363
|
+
--json number,title \
|
|
364
|
+
--jq --arg t "$WORK_UNIT_TITLE" '.[] | select(.title == $t) | .number' 2>/dev/null | head -1)
|
|
345
365
|
```
|
|
346
366
|
|
|
347
|
-
Record each created issue number and update the DAG table in the tracker issue.
|
|
367
|
+
Record each created issue number and update the DAG table in the tracker issue. If `/issue` deduped this work unit against an existing open issue, `ISSUE_NUM` will come back empty — fall back to `work_unit.existing` (if already resolved during Phase 2) or treat the work unit as already covered and skip re-creation.
|
|
348
368
|
|
|
349
369
|
### 3C: Compile final DAG issue list
|
|
350
370
|
|
package/commands/spec-doctor.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Evidence-backed spec evolution — reads finding concentration for a spec file, drafts a spec diff, opens a spec-evolution PR with per-change citations
|
|
3
|
-
argument-hint: [spec file path | "auto"]
|
|
3
|
+
argument-hint: "[spec file path | \"auto\"]"
|
|
4
4
|
install: extras
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Detect API changes, sync satellite repos, and publish releases
|
|
3
|
-
argument-hint: [check | auto | status | publish | PR-number]
|
|
3
|
+
argument-hint: "[check | auto | status | publish | PR-number]"
|
|
4
4
|
install: extras
|
|
5
5
|
---
|
|
6
6
|
<!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
|
|
@@ -218,10 +218,16 @@ Create issues in the affected repos:
|
|
|
218
218
|
|
|
219
219
|
```bash
|
|
220
220
|
# For satellite repos (separate GitHub repos):
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
221
|
+
#
|
|
222
|
+
# /issue resolves its target repo in Phase 1A from a `{prefix}:` token at the start of the
|
|
223
|
+
# free-text input, not from an explicit -R override — its multi-repo prefix table is keyed
|
|
224
|
+
# off forge.yaml → repos.satellites (the same registry this file already iterates). Prepend
|
|
225
|
+
# the satellite's configured {COMPONENT} (= satellites[].prefix) to the title so /issue routes
|
|
226
|
+
# to {TARGET_REPO} correctly instead of the default project repo.
|
|
227
|
+
SYNC_TITLE="feat(sync): {description of what API change needs to be reflected}"
|
|
228
|
+
SYNC_BODY_TMPFILE=$(mktemp)
|
|
229
|
+
trap 'rm -f "$SYNC_BODY_TMPFILE"' EXIT
|
|
230
|
+
cat > "$SYNC_BODY_TMPFILE" <<'BODY_EOF'
|
|
225
231
|
## Problem
|
|
226
232
|
|
|
227
233
|
{1-3 sentences: what API or schema change in the upstream repo needs to be reflected in this satellite repo. What will break or be missing if this sync is not done.}
|
|
@@ -252,13 +258,41 @@ Files that need changes:
|
|
|
252
258
|
|
|
253
259
|
{If applicable: describe the new API shape with an example}
|
|
254
260
|
BODY_EOF
|
|
255
|
-
)
|
|
261
|
+
# Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
|
|
262
|
+
# `gh issue create`. Prefixing the title with "{COMPONENT}: " lets /issue's Phase 1A resolve
|
|
263
|
+
# the satellite repo ({TARGET_REPO}) from forge.yaml → repos.satellites, matching how a human
|
|
264
|
+
# would invoke /issue for the same satellite (e.g. "mcp: add list_schemas tool"). The prefix
|
|
265
|
+
# is stripped by /issue's own routing — it does not become part of the created issue title.
|
|
266
|
+
Skill(skill="issue", args="\"{COMPONENT}: $SYNC_TITLE\" --title \"$SYNC_TITLE\" --body-file \"$SYNC_BODY_TMPFILE\" --label feature --label \"priority:P2\"")
|
|
267
|
+
rm -f "$SYNC_BODY_TMPFILE"
|
|
268
|
+
trap - EXIT
|
|
269
|
+
SYNC_ISSUE_NUMBER=$(gh issue list -R {TARGET_REPO} \
|
|
270
|
+
--state open \
|
|
271
|
+
--search "$SYNC_TITLE" \
|
|
272
|
+
--json number,title \
|
|
273
|
+
--jq --arg t "$SYNC_TITLE" '.[] | select(.title == $t) | .number' 2>/dev/null | head -1)
|
|
274
|
+
|
|
275
|
+
# Safety net: the "{COMPONENT}: " prefix trick above is not a documented part of the
|
|
276
|
+
# Programmatic Invocation Contract (issue.md has no --repo/-R flag for programmatic mode) —
|
|
277
|
+
# if /issue's Phase 1A did not honor the prefix and created the issue in the default project
|
|
278
|
+
# repo instead of {TARGET_REPO}, the lookup above returns empty and this would otherwise be
|
|
279
|
+
# silently treated as "deduped by /issue" further down. Distinguish the two cases explicitly.
|
|
280
|
+
if [ -z "$SYNC_ISSUE_NUMBER" ]; then
|
|
281
|
+
MISROUTED_CHECK=$(gh issue list \
|
|
282
|
+
--state open \
|
|
283
|
+
--search "$SYNC_TITLE" \
|
|
284
|
+
--json number,title \
|
|
285
|
+
--jq --arg t "$SYNC_TITLE" '.[] | select(.title == $t) | .number' 2>/dev/null | head -1)
|
|
286
|
+
if [ -n "$MISROUTED_CHECK" ]; then
|
|
287
|
+
echo "WARNING: '/issue' created #$MISROUTED_CHECK in the default project repo instead of {TARGET_REPO} — the '{COMPONENT}:' prefix routing did not take effect. Move/re-file the issue in {TARGET_REPO} manually."
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
256
290
|
|
|
257
291
|
# For monorepo issues (SDKs, packages inside a single repo):
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
292
|
+
MONOREPO_SYNC_TITLE="feat(sync): {description of what API change needs to be reflected}"
|
|
293
|
+
MONOREPO_SYNC_BODY_TMPFILE=$(mktemp)
|
|
294
|
+
trap 'rm -f "$MONOREPO_SYNC_BODY_TMPFILE"' EXIT
|
|
295
|
+
cat > "$MONOREPO_SYNC_BODY_TMPFILE" <<'BODY_EOF'
|
|
262
296
|
## Problem
|
|
263
297
|
|
|
264
298
|
{1-3 sentences: what API or schema change needs to be reflected in this SDK or package. What will be out of sync if not updated.}
|
|
@@ -285,7 +319,16 @@ Files that need changes:
|
|
|
285
319
|
**Change**: {description of upstream API change}
|
|
286
320
|
**Detected by**: `/sync-ecosystem`
|
|
287
321
|
BODY_EOF
|
|
288
|
-
)
|
|
322
|
+
# Route through the /issue create-hook (canonical dedup + body validation) instead of a raw
|
|
323
|
+
# `gh issue create`. No prefix needed — this targets the default/monorepo repo.
|
|
324
|
+
Skill(skill="issue", args="--title \"$MONOREPO_SYNC_TITLE\" --body-file \"$MONOREPO_SYNC_BODY_TMPFILE\" --label feature --label \"priority:P2\"")
|
|
325
|
+
rm -f "$MONOREPO_SYNC_BODY_TMPFILE"
|
|
326
|
+
trap - EXIT
|
|
327
|
+
MONOREPO_SYNC_ISSUE_NUMBER=$(gh issue list \
|
|
328
|
+
--state open \
|
|
329
|
+
--search "$MONOREPO_SYNC_TITLE" \
|
|
330
|
+
--json number,title \
|
|
331
|
+
--jq --arg t "$MONOREPO_SYNC_TITLE" '.[] | select(.title == $t) | .number' 2>/dev/null | head -1)
|
|
289
332
|
```
|
|
290
333
|
|
|
291
334
|
### Add sync issues to Project board
|
package/commands/test-gate.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Deterministic deploy-gate — verify a staging→main bundle's acceptance criteria against running code before deploy
|
|
3
|
-
argument-hint: [--prs "<N1 N2
|
|
3
|
+
argument-hint: "[--prs \"<N1 N2 ...>\"] [--base <branch>]"
|
|
4
4
|
allowed-tools: Task, Bash, Read, Grep, Glob
|
|
5
5
|
install: extras
|
|
6
6
|
---
|
|
@@ -520,10 +520,16 @@ else
|
|
|
520
520
|
SOURCE_ISSUES_LIST="${SOURCE_ISSUES_LIST}\n- #${issue_num}"
|
|
521
521
|
done
|
|
522
522
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
523
|
+
TESTGATE_FAIL_TITLE="fix: test-gate FAIL — cluster '${cluster}' broken by staging→${DEFAULT_BRANCH} bundle"
|
|
524
|
+
# Defense-in-depth: /issue's arg tokenizer (commands/issue.md, forge#2094) uses
|
|
525
|
+
# an xargs-based tokenizer that never expands backtick/$(...) substitution, so
|
|
526
|
+
# this is no longer required for safety — but strip it anyway so the raw title
|
|
527
|
+
# stays readable if it round-trips through any other eval-based consumer.
|
|
528
|
+
# ${cluster} is config-controlled (forge.yaml verification.integration_tests), but sanitize
|
|
529
|
+
# unconditionally for defense-in-depth consistency with the other converted call sites.
|
|
530
|
+
TESTGATE_FAIL_TITLE=$(printf '%s' "$TESTGATE_FAIL_TITLE" | tr '`' "'" | sed 's/\$(/$ (/g')
|
|
531
|
+
TESTGATE_FAIL_BODY_FILE=$(mktemp)
|
|
532
|
+
cat <<ISSUE_EOF > "$TESTGATE_FAIL_BODY_FILE"
|
|
527
533
|
## Problem
|
|
528
534
|
|
|
529
535
|
The \`/test-gate\` command detected a runtime failure in cluster \`${cluster}\` that was introduced by the staging→${DEFAULT_BRANCH} bundle. The failure did not exist on \`${BASE_BRANCH}\` (baseline PASS), confirming it is batch-introduced, not pre-existing.
|
|
@@ -570,7 +576,21 @@ $(echo -e "$SOURCE_ISSUES_LIST")
|
|
|
570
576
|
$(yq ".verification.integration_tests[] | select(.cluster == \"${cluster}\") | .command" "$CONFIG_FILE")
|
|
571
577
|
\`\`\`
|
|
572
578
|
ISSUE_EOF
|
|
573
|
-
|
|
579
|
+
|
|
580
|
+
# Route through the /issue create-hook's programmatic invocation contract (see
|
|
581
|
+
# commands/issue.md § "Programmatic Invocation Contract") instead of the raw issue-creation call.
|
|
582
|
+
Skill(skill="issue", args="--title \"$TESTGATE_FAIL_TITLE\" --body-file \"$TESTGATE_FAIL_BODY_FILE\" --label test-failure --label priority:P1")
|
|
583
|
+
rm -f "$TESTGATE_FAIL_BODY_FILE"
|
|
584
|
+
|
|
585
|
+
# /issue has no machine-readable return contract — resolve the created issue's number by
|
|
586
|
+
# exact-title search immediately after the call. Retry to absorb GitHub Search API indexing lag.
|
|
587
|
+
ISSUE_NUM=""
|
|
588
|
+
for _resolve_attempt in 1 2 3; do
|
|
589
|
+
ISSUE_NUM=$(gh issue list ${GH_FLAG} --search "in:title \"${TESTGATE_FAIL_TITLE}\"" --state open --limit 1 --json number --jq '.[0].number // empty' 2>/dev/null || echo "")
|
|
590
|
+
[ -n "$ISSUE_NUM" ] && break
|
|
591
|
+
sleep 2
|
|
592
|
+
done
|
|
593
|
+
[ -z "$ISSUE_NUM" ] && ISSUE_NUM="FAILED"
|
|
574
594
|
|
|
575
595
|
if [ "$ISSUE_NUM" != "FAILED" ] && [ -n "$ISSUE_NUM" ]; then
|
|
576
596
|
echo "Filed test-failure issue #${ISSUE_NUM} for cluster '${cluster}'"
|
|
@@ -786,10 +806,14 @@ if [ "${UNCOVERED_COUNT}" -gt 0 ]; then
|
|
|
786
806
|
continue
|
|
787
807
|
fi
|
|
788
808
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
809
|
+
TESTGAP_TITLE="fix: test-gap — uncovered criterion in issue #${GAP_ISSUE} (${GAP_TYPE})"
|
|
810
|
+
# Defense-in-depth: /issue's arg tokenizer (commands/issue.md, forge#2094) uses
|
|
811
|
+
# an xargs-based tokenizer that never expands backtick/$(...) substitution, so
|
|
812
|
+
# this is no longer required for safety — but strip it anyway so the raw title
|
|
813
|
+
# stays readable if it round-trips through any other eval-based consumer.
|
|
814
|
+
TESTGAP_TITLE=$(printf '%s' "$TESTGAP_TITLE" | tr '`' "'" | sed 's/\$(/$ (/g')
|
|
815
|
+
TESTGAP_BODY_FILE=$(mktemp)
|
|
816
|
+
cat <<TGAP_EOF > "$TESTGAP_BODY_FILE"
|
|
793
817
|
## Problem
|
|
794
818
|
|
|
795
819
|
The \`/test-gate\` criteria-adequacy check (Phase 6) found a runtime-testable acceptance criterion in issue #${GAP_ISSUE} that is not covered by any configured test cluster. Coverage gaps cannot produce a clean PASS verdict — this criterion must be covered before the staging→${DEFAULT_BRANCH} bundle can pass the deploy gate.
|
|
@@ -822,7 +846,21 @@ Files to be identified during investigation. Start with the test suite for the \
|
|
|
822
846
|
${GAP_CRITERION}
|
|
823
847
|
\`\`\`
|
|
824
848
|
TGAP_EOF
|
|
825
|
-
|
|
849
|
+
|
|
850
|
+
# Route through the /issue create-hook's programmatic invocation contract (see
|
|
851
|
+
# commands/issue.md § "Programmatic Invocation Contract") instead of the raw issue-creation call.
|
|
852
|
+
Skill(skill="issue", args="--title \"$TESTGAP_TITLE\" --body-file \"$TESTGAP_BODY_FILE\" --label test-gap")
|
|
853
|
+
rm -f "$TESTGAP_BODY_FILE"
|
|
854
|
+
|
|
855
|
+
# /issue has no machine-readable return contract — resolve the created issue's number by
|
|
856
|
+
# exact-title search immediately after the call. Retry to absorb GitHub Search API indexing lag.
|
|
857
|
+
GAP_ISSUE_NUM=""
|
|
858
|
+
for _resolve_attempt in 1 2 3; do
|
|
859
|
+
GAP_ISSUE_NUM=$(gh issue list ${GH_FLAG} --search "in:title \"${TESTGAP_TITLE}\"" --state open --limit 1 --json number --jq '.[0].number // empty' 2>/dev/null || echo "")
|
|
860
|
+
[ -n "$GAP_ISSUE_NUM" ] && break
|
|
861
|
+
sleep 2
|
|
862
|
+
done
|
|
863
|
+
[ -z "$GAP_ISSUE_NUM" ] && GAP_ISSUE_NUM="FAILED"
|
|
826
864
|
|
|
827
865
|
if [ "$GAP_ISSUE_NUM" != "FAILED" ] && [ -n "$GAP_ISSUE_NUM" ]; then
|
|
828
866
|
echo "Filed test-gap issue #${GAP_ISSUE_NUM} for uncovered criterion in #${GAP_ISSUE} (${GAP_TYPE})"
|