forgedock 1.0.22 → 1.1.2

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.
Files changed (184) hide show
  1. package/README.md +255 -211
  2. package/bin/batch-runner.mjs +339 -0
  3. package/bin/cinema.mjs +320 -0
  4. package/bin/demo.mjs +92 -7
  5. package/bin/engine/phases.mjs +168 -0
  6. package/bin/engine/projector.mjs +32 -0
  7. package/bin/engine/reconcile.mjs +11 -0
  8. package/bin/engine/runlog.mjs +79 -0
  9. package/bin/engine/state.mjs +32 -0
  10. package/bin/engine-cli.mjs +222 -0
  11. package/bin/engine.mjs +138 -0
  12. package/bin/forgedock.mjs +896 -1540
  13. package/bin/hooks/interactive-engine.mjs +509 -0
  14. package/bin/hooks/pre-tool-use.mjs +498 -0
  15. package/bin/hooks/subagent-stop-enforce.mjs +197 -0
  16. package/bin/journey.mjs +841 -0
  17. package/bin/registry.mjs +139 -0
  18. package/bin/report.mjs +447 -0
  19. package/bin/runner.mjs +136 -20
  20. package/bin/settings-hook.mjs +346 -0
  21. package/bin/tui.mjs +6 -3
  22. package/commands/adopt.md +4 -1
  23. package/commands/analytics.md +4 -1
  24. package/commands/audit-agents.md +110 -27
  25. package/commands/audit.md +20 -1
  26. package/commands/autopilot.md +84 -12
  27. package/commands/changelog.md +2 -1
  28. package/commands/ci-audit.md +2 -1
  29. package/commands/cleanup.md +161 -1
  30. package/commands/compat-audit.md +2 -1
  31. package/commands/deploy-info.md +63 -4
  32. package/commands/design-bench.md +6 -1
  33. package/commands/design-render-critique-loop.md +2 -1
  34. package/commands/design.md +1 -0
  35. package/commands/diagnose.md +2 -1
  36. package/commands/explain.md +2 -1
  37. package/commands/forge-stats.md +122 -2
  38. package/commands/forgedock-init.md +1 -1
  39. package/commands/geo-audit.md +3 -0
  40. package/commands/incident-response.md +2 -1
  41. package/commands/issue.md +24 -3
  42. package/commands/milestone.md +77 -12
  43. package/commands/optimize.md +2 -1
  44. package/commands/orchestrate/config.md +67 -0
  45. package/commands/orchestrate/phase-1-resolve.md +159 -0
  46. package/commands/orchestrate/phase-2-triage.md +126 -0
  47. package/commands/orchestrate/phase-2.5-synthesis.md +156 -0
  48. package/commands/orchestrate/phase-3-dependency.md +591 -0
  49. package/commands/orchestrate/phase-4-execution.md +962 -0
  50. package/commands/orchestrate/phase-5-cleanup.md +47 -0
  51. package/commands/orchestrate/phase-6-report.md +198 -0
  52. package/commands/orchestrate/safety.md +62 -0
  53. package/commands/orchestrate.md +25 -2027
  54. package/commands/pipeline-health/config.md +34 -0
  55. package/commands/pipeline-health/phase-1-context.md +180 -0
  56. package/commands/pipeline-health/phase-2-metrics.md +1729 -0
  57. package/commands/pipeline-health/phase-3-analyze.md +204 -0
  58. package/commands/pipeline-health/phase-4-proposals.md +117 -0
  59. package/commands/pipeline-health/phase-5-report.md +284 -0
  60. package/commands/pipeline-health/phase-6-summary.md +52 -0
  61. package/commands/pipeline-health.md +25 -2401
  62. package/commands/pipeline-resume.md +24 -1
  63. package/commands/pipeline-status.md +65 -0
  64. package/commands/qa-sweep.md +4 -1
  65. package/commands/quality-gate.md +326 -42
  66. package/commands/replay.md +2 -1
  67. package/commands/review-pr-agents/api.md +135 -0
  68. package/commands/review-pr-agents/auth.md +137 -0
  69. package/commands/review-pr-agents/billing.md +101 -0
  70. package/commands/review-pr-agents/concurrency.md +145 -0
  71. package/commands/review-pr-agents/database.md +103 -0
  72. package/commands/review-pr-agents/frontend.md +89 -0
  73. package/commands/review-pr-agents/infra.md +385 -0
  74. package/commands/review-pr-agents/protocols.md +185 -0
  75. package/commands/review-pr-agents/scraper.md +185 -0
  76. package/commands/review-pr-agents/security.md +406 -0
  77. package/commands/review-pr-agents.md +45 -1722
  78. package/commands/review-pr-staging.md +196 -40
  79. package/commands/review-pr.md +612 -56
  80. package/commands/rollback.md +2 -1
  81. package/commands/scope.md +2 -1
  82. package/commands/security-audit.md +2 -1
  83. package/commands/signal-planner.md +623 -0
  84. package/commands/sync-ecosystem.md +1 -0
  85. package/commands/test-gate.md +17 -1
  86. package/commands/validate.md +2 -1
  87. package/commands/work-on/build/architect.md +28 -0
  88. package/commands/work-on/build/context.md +2 -0
  89. package/commands/work-on/build/implement.md +19 -5
  90. package/commands/work-on/build/validate.md +62 -6
  91. package/commands/work-on/build.md +187 -30
  92. package/commands/work-on/close.md +132 -5
  93. package/commands/work-on/decompose.md +19 -1
  94. package/commands/work-on/investigate.md +117 -2
  95. package/commands/work-on/review.md +48 -4
  96. package/commands/work-on-monolithic.md +8 -2
  97. package/commands/work-on.md +247 -7
  98. package/docs/BRAND.md +16 -5
  99. package/docs/CI.md +2 -2
  100. package/docs/CODEX.md +36 -1
  101. package/docs/CONFIG.md +44 -42
  102. package/docs/FORGE-PROTOCOL.md +15 -1064
  103. package/docs/anthropic-devrel-outreach.md +2 -2
  104. package/docs/articles/command-decomposition-token-savings.md +214 -0
  105. package/docs/articles/model-release-playbook.md +131 -0
  106. package/docs/articles/per-repo-adaptive-scripts-token-savings.md +27 -25
  107. package/docs/claude-breakpoints.json +75 -5
  108. package/docs/design/abc-benchmark.md +1 -1
  109. package/docs/design/design-spec-schema.md +1 -1
  110. package/docs/design/divergent-generation.md +1 -1
  111. package/docs/design/eval-gate-runbook.md +178 -0
  112. package/docs/design/show-hn-draft.md +26 -1
  113. package/docs/design/token-cost-baseline-1298.md +59 -0
  114. package/docs/eval/README.md +117 -0
  115. package/docs/eval/corpus/seed-v1.json +6 -0
  116. package/docs/eval/published/claude-sonnet-4-6-2026-07-04.json +33 -0
  117. package/docs/site/annotations-explained.md +24 -0
  118. package/docs/site/command-learning-path.md +51 -14
  119. package/docs/site/command-reference.md +339 -113
  120. package/docs/site/for-companies.md +95 -0
  121. package/docs/site/forge-annotation-protocol.md +12 -256
  122. package/docs/site/getting-started.md +58 -12
  123. package/docs/site/github-is-the-memory.md +113 -0
  124. package/docs/site/how-it-works.md +3 -13
  125. package/docs/site/index.md +10 -2
  126. package/docs/spec/eval-run-result.md +147 -0
  127. package/docs/spec/forge-protocol-v1.md +441 -18
  128. package/docs/spec/label-state-machine.md +55 -0
  129. package/docs/spec/review-protocol.md +158 -0
  130. package/docs/spec-graph-schema.md +28 -0
  131. package/docs/superpowers/plans/2026-07-03-cinematic-onboarding-tui.md +2339 -0
  132. package/docs/superpowers/plans/2026-07-03-durable-execution-engine.md +1223 -0
  133. package/docs/superpowers/specs/2026-07-03-cinematic-onboarding-tui-design.md +157 -0
  134. package/docs/superpowers/specs/2026-07-03-durable-execution-engine-design.md +194 -0
  135. package/docs/superpowers/specs/2026-07-04-economic-self-governance-design.md +181 -0
  136. package/docs/superpowers/specs/2026-07-04-provenance-trail-design.md +189 -0
  137. package/examples/forgedock-demo/README.md +41 -22
  138. package/examples/forgedock-demo/issues/06-bug-tags-type-validation.md +43 -0
  139. package/examples/forgedock-demo/issues/07-bug-negative-offset-pagination.md +42 -0
  140. package/examples/forgedock-demo/issues/08-bug-patch-mass-assignment.md +49 -0
  141. package/examples/forgedock-demo/issues/09-bug-count-injection.md +47 -0
  142. package/examples/forgedock-demo/issues/10-feature-sort-param.md +39 -0
  143. package/examples/forgedock-demo/issues/11-feature-patch-tags.md +37 -0
  144. package/examples/forgedock-demo/issues/12-feature-bulk-create.md +42 -0
  145. package/examples/forgedock-demo/issues/13-feature-archive-notes.md +46 -0
  146. package/examples/forgedock-demo/issues/14-refactor-extract-title-validation.md +41 -0
  147. package/examples/forgedock-demo/issues/15-refactor-protected-route-flag.md +43 -0
  148. package/examples/forgedock-demo/issues/16-refactor-extract-store.md +45 -0
  149. package/examples/forgedock-demo/issues/17-perf-tags-dedup-set.md +35 -0
  150. package/examples/forgedock-demo/issues/18-perf-remove-redundant-sort.md +37 -0
  151. package/examples/forgedock-demo/issues/19-docs-api-reference-full.md +53 -0
  152. package/examples/forgedock-demo/issues/20-docs-intentional-flaw-convention.md +46 -0
  153. package/examples/forgedock-demo/scripts/smoke.js +18 -1
  154. package/examples/forgedock-demo/src/db.js +36 -11
  155. package/examples/forgedock-demo/src/routes/notes.js +106 -9
  156. package/examples/forgedock-demo/src/server.js +4 -1
  157. package/package.json +12 -4
  158. package/packages/protocol/LICENSE +21 -0
  159. package/packages/protocol/src/cli.js +135 -0
  160. package/packages/protocol/src/emit.js +146 -0
  161. package/packages/protocol/src/index.js +43 -0
  162. package/packages/protocol/src/parse.js +229 -0
  163. package/packages/protocol/src/types.js +171 -0
  164. package/packages/protocol/src/validate.js +122 -0
  165. package/scripts/bench-topology-cost.mjs +372 -0
  166. package/scripts/check-command-docs-drift.sh +198 -0
  167. package/scripts/check-protocol-restatements.sh +176 -0
  168. package/scripts/check-registry/README.md +72 -0
  169. package/scripts/check-registry/manifest.json +6 -0
  170. package/scripts/code-index.sh +454 -0
  171. package/scripts/conformance-check.mjs +168 -0
  172. package/scripts/doctor-pipeline-state.sh +484 -0
  173. package/scripts/eval-gate-baseline.json +17 -0
  174. package/scripts/eval-gate-scorecard.mjs +242 -0
  175. package/scripts/eval-scorecard-aggregator.mjs +252 -0
  176. package/scripts/flaky-quarantine.sh +241 -0
  177. package/scripts/forge-annotation.sh +567 -0
  178. package/scripts/issue-dedup.sh +236 -0
  179. package/scripts/validate-annotation-node.mjs +233 -0
  180. package/scripts/verify-env-vars.sh +18 -2
  181. package/scripts/verify-host-headers.sh +4 -4
  182. package/scripts/verify-route-registration.sh +2 -2
  183. package/scripts/worktree-lifecycle.sh +202 -0
  184. package/templates/devdocs/agent/using-forgedock.md +14 -0
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/flaky-quarantine.sh
3
+ # Classify a failing test as flaky, pre-broken, or real (PR-caused).
4
+ #
5
+ # Usage:
6
+ # bash scripts/flaky-quarantine.sh \
7
+ # --test "<test-id-or-command>" \
8
+ # --base <base-branch> \
9
+ # [--worktree <path>] \
10
+ # [--repo <owner/repo>] \
11
+ # [--issue <pr-issue-number>] \
12
+ # [--retries <N>] \
13
+ # [--manifest <path>]
14
+ #
15
+ # Classification:
16
+ # PRE_BROKEN — test fails on base branch (broken before this PR)
17
+ # FLAKY — test fails on PR branch then passes on at least one retry
18
+ # REAL — test fails deterministically on PR branch, passes on base branch
19
+ #
20
+ # Output (to stdout, one line):
21
+ # CLASSIFICATION: PRE_BROKEN|FLAKY|REAL
22
+ # QUARANTINE_ENTRY: <json-line appended to manifest when PRE_BROKEN or FLAKY>
23
+ #
24
+ # Exit codes:
25
+ # 0 — classification complete (any outcome)
26
+ # 1 — usage error or test command missing
27
+ #
28
+ # Manifest format (JSONL, one entry per line):
29
+ # {"test":"<id>","classification":"<PRE_BROKEN|FLAKY>","base":"<branch>",
30
+ # "pr_branch":"<branch>","issue":"<num>","repo":"<owner/repo>",
31
+ # "first_seen":"<ISO-8601>","runs_pr":<n>,"failures_pr":<n>,"runs_base":<n>,"failures_base":<n>}
32
+ #
33
+ # SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios
34
+ # SPDX-License-Identifier: AGPL-3.0-or-later
35
+
36
+ set -euo pipefail
37
+
38
+ # ── Defaults ──────────────────────────────────────────────────────────────────
39
+ RETRIES=3
40
+ MANIFEST="${FORGEDOCK_QUARANTINE_MANIFEST:-${WORKTREE_PATH:-.}/.forgedock/quarantine.jsonl}"
41
+ BASE_BRANCH=""
42
+ TEST_CMD=""
43
+ WORKTREE="${WORKTREE_PATH:-.}"
44
+ REPO=""
45
+ ISSUE_NUM=""
46
+
47
+ # ── Argument parsing ──────────────────────────────────────────────────────────
48
+ while [[ $# -gt 0 ]]; do
49
+ case "$1" in
50
+ --test) TEST_CMD="$2"; shift 2 ;;
51
+ --base) BASE_BRANCH="$2"; shift 2 ;;
52
+ --worktree) WORKTREE="$2"; shift 2 ;;
53
+ --repo) REPO="$2"; shift 2 ;;
54
+ --issue) ISSUE_NUM="$2"; shift 2 ;;
55
+ --retries) RETRIES="$2"; shift 2 ;;
56
+ --manifest) MANIFEST="$2"; shift 2 ;;
57
+ *) echo "Unknown argument: $1" >&2; exit 1 ;;
58
+ esac
59
+ done
60
+
61
+ if [ -z "$TEST_CMD" ]; then
62
+ echo "Usage: $0 --test <test-id-or-command> --base <base-branch> [options]" >&2
63
+ exit 1
64
+ fi
65
+ if [ -z "$BASE_BRANCH" ]; then
66
+ echo "Error: --base <base-branch> is required" >&2
67
+ exit 1
68
+ fi
69
+
70
+ # ── Helpers ───────────────────────────────────────────────────────────────────
71
+ run_test() {
72
+ # Run the test command in WORKTREE; return exit code only.
73
+ local exit_code=0
74
+ (cd "$WORKTREE" && eval "$TEST_CMD" >/dev/null 2>&1) || exit_code=$?
75
+ echo "$exit_code"
76
+ }
77
+
78
+ iso_now() {
79
+ date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ"
80
+ }
81
+
82
+ # ── Step 1: Run test N times on the PR branch ─────────────────────────────────
83
+ PR_BRANCH=$(git -C "$WORKTREE" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
84
+ echo "Classifying test on PR branch '${PR_BRANCH}' (${RETRIES} run(s))..."
85
+
86
+ RUNS_PR=0
87
+ FAILURES_PR=0
88
+ for i in $(seq 1 "$RETRIES"); do
89
+ RUNS_PR=$((RUNS_PR + 1))
90
+ CODE=$(run_test)
91
+ if [ "$CODE" -ne 0 ]; then
92
+ FAILURES_PR=$((FAILURES_PR + 1))
93
+ echo " PR run $i: FAIL (exit $CODE)"
94
+ else
95
+ echo " PR run $i: PASS"
96
+ fi
97
+ done
98
+
99
+ # ── Step 2: Run test on base branch ──────────────────────────────────────────
100
+ echo "Running test on base branch '${BASE_BRANCH}'..."
101
+ BASE_REF="origin/${BASE_BRANCH}"
102
+
103
+ RUNS_BASE=0
104
+ FAILURES_BASE=0
105
+
106
+ # Create a temporary worktree for the base branch so we do not disturb HEAD.
107
+ TMP_WORKTREE=""
108
+ cleanup() {
109
+ if [ -n "$TMP_WORKTREE" ] && [ -d "$TMP_WORKTREE" ]; then
110
+ git -C "$WORKTREE" worktree remove --force "$TMP_WORKTREE" 2>/dev/null || true
111
+ fi
112
+ }
113
+ trap cleanup EXIT INT TERM
114
+
115
+ if git -C "$WORKTREE" ls-remote --exit-code origin "$BASE_BRANCH" >/dev/null 2>&1; then
116
+ TMP_WORKTREE=$(mktemp -d)
117
+ # Remove the empty dir first — git worktree add requires a non-existent or empty dir.
118
+ rmdir "$TMP_WORKTREE"
119
+ if git -C "$WORKTREE" worktree add --detach "$TMP_WORKTREE" "$BASE_REF" >/dev/null 2>&1; then
120
+ RUNS_BASE=1
121
+ BASE_EXIT=0
122
+ (cd "$TMP_WORKTREE" && eval "$TEST_CMD" >/dev/null 2>&1) || BASE_EXIT=$?
123
+ if [ "$BASE_EXIT" -ne 0 ]; then
124
+ FAILURES_BASE=1
125
+ echo " Base run 1: FAIL (exit $BASE_EXIT)"
126
+ else
127
+ echo " Base run 1: PASS"
128
+ fi
129
+ else
130
+ echo "WARNING: could not create worktree for base branch — skipping base run" >&2
131
+ fi
132
+ else
133
+ echo "WARNING: base branch '${BASE_BRANCH}' not found on remote — skipping base run" >&2
134
+ fi
135
+
136
+ # ── Step 3: Classify ──────────────────────────────────────────────────────────
137
+ CLASSIFICATION=""
138
+
139
+ if [ "$RUNS_BASE" -gt 0 ] && [ "$FAILURES_BASE" -gt 0 ]; then
140
+ # Fails on base → pre-broken regardless of PR behaviour
141
+ CLASSIFICATION="PRE_BROKEN"
142
+ elif [ "$FAILURES_PR" -gt 0 ] && [ "$FAILURES_PR" -lt "$RUNS_PR" ]; then
143
+ # Intermittent on PR → flaky
144
+ CLASSIFICATION="FLAKY"
145
+ elif [ "$FAILURES_PR" -eq "$RUNS_PR" ] && [ "$RUNS_PR" -gt 0 ]; then
146
+ if [ "$RUNS_BASE" -gt 0 ] && [ "$FAILURES_BASE" -eq 0 ]; then
147
+ # Deterministic on PR, passes on base → real regression
148
+ CLASSIFICATION="REAL"
149
+ elif [ "$RUNS_BASE" -eq 0 ]; then
150
+ # No base data — treat as potentially real but flag uncertainty
151
+ CLASSIFICATION="REAL"
152
+ echo "NOTE: base branch not available — REAL classification is unconfirmed" >&2
153
+ else
154
+ CLASSIFICATION="REAL"
155
+ fi
156
+ else
157
+ # Passes on all PR runs — test is not currently failing
158
+ CLASSIFICATION="PASS"
159
+ fi
160
+
161
+ echo ""
162
+ echo "CLASSIFICATION: ${CLASSIFICATION}"
163
+
164
+ # ── Step 4: Write quarantine manifest entry ───────────────────────────────────
165
+ if [ "$CLASSIFICATION" = "PRE_BROKEN" ] || [ "$CLASSIFICATION" = "FLAKY" ]; then
166
+ MANIFEST_DIR=$(dirname "$MANIFEST")
167
+ mkdir -p "$MANIFEST_DIR"
168
+
169
+ TIMESTAMP=$(iso_now)
170
+ # Escape TEST_CMD for JSON safety. A hand-rolled sed replacement only covers
171
+ # backslash and double-quote — a test command containing a literal newline
172
+ # or other control character would produce invalid JSON/JSONL. `jq -Rs '.'`
173
+ # slurps the raw input as one string and emits a fully JSON-escaped literal
174
+ # (including \n, \t, etc.); strip the surrounding quotes since TEST_JSON is
175
+ # embedded inside an already-quoted field below.
176
+ TEST_JSON_QUOTED=$(printf '%s' "$TEST_CMD" | jq -Rs '.')
177
+ TEST_JSON="${TEST_JSON_QUOTED#\"}"
178
+ TEST_JSON="${TEST_JSON%\"}"
179
+
180
+ ENTRY="{\"test\":\"${TEST_JSON}\",\"classification\":\"${CLASSIFICATION}\",\"base\":\"${BASE_BRANCH}\",\"pr_branch\":\"${PR_BRANCH}\",\"issue\":\"${ISSUE_NUM}\",\"repo\":\"${REPO}\",\"first_seen\":\"${TIMESTAMP}\",\"runs_pr\":${RUNS_PR},\"failures_pr\":${FAILURES_PR},\"runs_base\":${RUNS_BASE},\"failures_base\":${FAILURES_BASE}}"
181
+
182
+ # Append only if this exact test+classification is not already in the manifest.
183
+ ALREADY_PRESENT=false
184
+ if [ -f "$MANIFEST" ]; then
185
+ grep -qF "\"${TEST_JSON}\"" "$MANIFEST" && ALREADY_PRESENT=true
186
+ fi
187
+
188
+ if [ "$ALREADY_PRESENT" = "false" ]; then
189
+ echo "$ENTRY" >> "$MANIFEST"
190
+ echo "QUARANTINE_ENTRY: ${ENTRY}"
191
+ echo "Wrote quarantine entry to ${MANIFEST}"
192
+ else
193
+ echo "QUARANTINE_ENTRY: already recorded in ${MANIFEST} — skipping duplicate"
194
+ fi
195
+
196
+ # ── Step 5: File a GitHub issue for newly quarantined tests ──────────────────
197
+ if [ -n "$REPO" ] && command -v gh >/dev/null 2>&1 && [ "$ALREADY_PRESENT" = "false" ]; then
198
+ LABEL_ARGS=""
199
+ if [ "$CLASSIFICATION" = "PRE_BROKEN" ]; then
200
+ LABEL_ARGS="--label bug --label priority:P2"
201
+ else
202
+ LABEL_ARGS="--label bug --label priority:P3"
203
+ fi
204
+
205
+ ISSUE_BODY="## Quarantined test: \`${TEST_JSON}\`
206
+
207
+ **Classification**: ${CLASSIFICATION}
208
+ **PR branch**: \`${PR_BRANCH}\`
209
+ **Base branch**: \`${BASE_BRANCH}\`
210
+ **PR / Issue**: ${ISSUE_NUM:-n/a}
211
+ **Detected**: ${TIMESTAMP}
212
+
213
+ ### Run summary
214
+
215
+ | Branch | Runs | Failures |
216
+ |--------|------|----------|
217
+ | PR (\`${PR_BRANCH}\`) | ${RUNS_PR} | ${FAILURES_PR} |
218
+ | Base (\`${BASE_BRANCH}\`) | ${RUNS_BASE} | ${FAILURES_BASE} |
219
+
220
+ ### Next steps
221
+
222
+ - Investigate root cause of test instability
223
+ - Fix or remove the test once confirmed pre-broken or consistently flaky
224
+ - Update quarantine manifest once resolved: \`${MANIFEST}\`
225
+
226
+ <!-- FORGE:QUARANTINE test=\"${TEST_JSON}\" classification=\"${CLASSIFICATION}\" -->"
227
+
228
+ TITLE_PREFIX=""
229
+ case "$CLASSIFICATION" in
230
+ PRE_BROKEN) TITLE_PREFIX="pre-broken" ;;
231
+ FLAKY) TITLE_PREFIX="flaky" ;;
232
+ esac
233
+
234
+ gh issue create \
235
+ --repo "$REPO" \
236
+ --title "test(quarantine): ${TITLE_PREFIX} — ${TEST_JSON}" \
237
+ --body "$ISSUE_BODY" \
238
+ $LABEL_ARGS \
239
+ 2>/dev/null || echo "WARNING: could not create quarantine issue (gh error — manual filing required)" >&2
240
+ fi
241
+ fi