forgedock 1.1.7 → 1.1.9

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 (97) hide show
  1. package/README.md +10 -33
  2. package/bin/cinema.mjs +23 -5
  3. package/bin/engine/invariants.mjs +361 -0
  4. package/bin/engine-cli.mjs +137 -11
  5. package/bin/env-detect.mjs +249 -0
  6. package/bin/forge-utils.mjs +364 -4
  7. package/bin/forgedock.mjs +1064 -135
  8. package/bin/hooks/pre-tool-use.mjs +182 -0
  9. package/bin/hooks/session-start.mjs +68 -3
  10. package/bin/init-detect.mjs +101 -12
  11. package/bin/journey.mjs +917 -16
  12. package/bin/labels.json +10 -0
  13. package/bin/recall.mjs +557 -0
  14. package/bin/registry.mjs +91 -3
  15. package/bin/runner.mjs +38 -2
  16. package/bin/tui.mjs +49 -3
  17. package/commands/adopt.md +1 -1
  18. package/commands/analytics.md +8 -8
  19. package/commands/audit.md +1 -1
  20. package/commands/autopilot.md +958 -77
  21. package/commands/ci-audit.md +1 -1
  22. package/commands/cleanup.md +139 -55
  23. package/commands/compat-audit.md +1 -1
  24. package/commands/deploy-info.md +1 -1
  25. package/commands/deploy-pr.md +1 -1
  26. package/commands/diagnose.md +1 -1
  27. package/commands/explain.md +1 -1
  28. package/commands/fix-ci.md +1 -1
  29. package/commands/forgedock-init.md +1 -1
  30. package/commands/incident-response.md +3 -3
  31. package/commands/issue.md +1 -1
  32. package/commands/optimize.md +586 -1
  33. package/commands/orchestrate/config.md +13 -3
  34. package/commands/orchestrate/phase-1-resolve.md +36 -26
  35. package/commands/orchestrate/phase-2-triage.md +1 -1
  36. package/commands/orchestrate/phase-2.5-synthesis.md +1 -1
  37. package/commands/orchestrate/phase-3-dependency.md +587 -48
  38. package/commands/orchestrate/phase-4-execution.md +1031 -57
  39. package/commands/orchestrate/phase-5-cleanup.md +1 -1
  40. package/commands/orchestrate/phase-6-report.md +248 -11
  41. package/commands/orchestrate/safety.md +2 -2
  42. package/commands/pipeline-health/phase-2-metrics.md +305 -1
  43. package/commands/pipeline-health/phase-4-proposals.md +127 -0
  44. package/commands/qa-sweep.md +1 -1
  45. package/commands/quality-gate.md +418 -1
  46. package/commands/recover-orphans.md +5 -5
  47. package/commands/replay.md +1 -1
  48. package/commands/review-pr-agents/api.md +2 -2
  49. package/commands/review-pr-agents/auth.md +2 -2
  50. package/commands/review-pr-agents/billing.md +2 -2
  51. package/commands/review-pr-agents/concurrency.md +2 -2
  52. package/commands/review-pr-agents/database.md +2 -2
  53. package/commands/review-pr-agents/frontend.md +2 -2
  54. package/commands/review-pr-agents/infra.md +2 -2
  55. package/commands/review-pr-agents/protocols.md +1 -1
  56. package/commands/review-pr-agents/scraper.md +2 -2
  57. package/commands/review-pr-agents/security.md +2 -2
  58. package/commands/review-pr-staging.md +41 -3
  59. package/commands/review-pr.md +462 -19
  60. package/commands/rollback.md +1 -1
  61. package/commands/scope.md +1 -1
  62. package/commands/security-audit.md +1 -1
  63. package/commands/spec-doctor.md +429 -0
  64. package/commands/test-gate.md +1 -1
  65. package/commands/validate.md +53 -1
  66. package/commands/work-on/build/architect.md +78 -1
  67. package/commands/work-on/build/context.md +360 -22
  68. package/commands/work-on/build/implement.md +3 -1
  69. package/commands/work-on/build/validate.md +29 -1
  70. package/commands/work-on/build.md +75 -6
  71. package/commands/work-on/close.md +624 -10
  72. package/commands/work-on/decompose.md +1 -1
  73. package/commands/work-on/investigate.md +235 -5
  74. package/commands/work-on/remediate.md +331 -0
  75. package/commands/work-on/review.md +1 -1
  76. package/commands/work-on-monolithic.md +1 -1
  77. package/commands/work-on.md +205 -66
  78. package/docs/CI.md +2 -2
  79. package/docs/CODEX.md +1 -1
  80. package/docs/CONFIG.md +186 -0
  81. package/docs/FORGE-PROTOCOL.md +101 -0
  82. package/docs/site/command-reference.md +31 -1
  83. package/docs/site/getting-started.md +9 -34
  84. package/docs/site/index.md +1 -1
  85. package/docs/spec/forge-protocol-v1.md +97 -5
  86. package/docs/spec/label-state-machine.md +6 -1
  87. package/package.json +3 -2
  88. package/packages/protocol/src/cli.js +374 -84
  89. package/packages/protocol/src/types.js +75 -1
  90. package/scripts/build-knowledge-index.mjs +1761 -0
  91. package/scripts/calibration.mjs +996 -0
  92. package/scripts/check-command-docs-drift.sh +4 -2
  93. package/scripts/danger-zones.mjs +789 -0
  94. package/scripts/doctor-pipeline-state.sh +88 -2
  95. package/scripts/select-fix-targets.sh +234 -0
  96. package/scripts/transition-label.sh +245 -6
  97. package/scripts/refresh-bot-token.sh +0 -97
@@ -13,7 +13,7 @@ You are a quality gate that runs AFTER implementation but BEFORE commit. Your jo
13
13
 
14
14
  You do NOT post to GitHub. You do NOT create issues. You return findings directly to the builder agent that spawned you.
15
15
 
16
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
16
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
17
17
  **NEVER use plan mode (EnterPlanMode).**
18
18
 
19
19
  <!-- FORGE:SPEC_LOADED — quality-gate.md loaded and active. Agent is bound by this spec. -->
@@ -53,6 +53,7 @@ This quality gate uses **domain detection** to adapt to your project's actual te
53
53
  | FORGE_GRAPH | `commands/*.md` specs or `scripts/*` files change (ForgeDock self-consistency) |
54
54
  | CONFIG_SCHEMA | Config files for external tools (`traefik/`, `infra/nginx/`, `k8s/`, `terraform/`, `*.conf`, `*.toml` in infra paths, or `docker-compose*.yml` with service definition changes) |
55
55
  | BILLING | Files under `billing/`, `credits/`, `subscription`, `ledger`, or `payment` paths |
56
+ | WIRE_THROUGH | Any code file where the diff adds new conditional lines (`if`/`elif`/`else`/`guard`/`feature flag`) — triggers wire-through proof check <!-- Added: forge#1731 --> |
56
57
 
57
58
  **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.
58
59
 
@@ -107,6 +108,7 @@ Before running checks, classify the changed files into domains. This avoids runn
107
108
  | `*.py` in `routers/` where the diff removes a line containing an or-gate condition (lines starting with `-` containing `if.*or`) | ROUTER_BUG |
108
109
  | `commands/*.md` or `scripts/*` files (ForgeDock repo only — dogfoods its own spec graph) | FORGE_GRAPH |
109
110
  | Files under `traefik/`, `infra/nginx/`, `k8s/`, `terraform/`, or files matching `*.conf`, `*.toml` in infra paths, or `docker-compose*.yml` with service definition changes | CONFIG_SCHEMA |
111
+ | Executable code files (`.py`, `.ts`, `.tsx`, `.js`, `.sh`) where the diff adds new `if`/`elif`/`else`/guard/feature-flag conditional lines — NOT `.md` prose spec files | WIRE_THROUGH *(universal — see 2G.8)* <!-- Added: forge#1731 --> |
110
112
 
111
113
  **Apply the classification:**
112
114
 
@@ -212,6 +214,22 @@ for f in "${CHANGED_FILES_ARR[@]}"; do
212
214
  esac
213
215
  done
214
216
 
217
+ # Check for newly added conditional paths in code files — wire-through proof trigger <!-- Added: forge#1731 -->
218
+ # Scoped to NEWLY ADDED lines only (lines starting with '+' in the diff, excluding the '+++' header).
219
+ # Pre-existing conditionals are never flagged — only additions.
220
+ # Applies to executable code files ONLY (.py, .ts, .tsx, .js, .sh) — NOT .md prose spec files,
221
+ # which use 'if'/'else' in natural language and pseudocode that is never executed.
222
+ while IFS= read -r f; do
223
+ [ -z "$f" ] && continue
224
+ NEW_CONDITIONALS=$(git diff HEAD -- "$f" 2>/dev/null \
225
+ | grep -E '^\+' | grep -v '^+++' \
226
+ | grep -E '^\+\s*(if |elif |else:?\s*$|if\(|elif\()|\bguard\b|\bfeature.?flag\b|FEATURE_FLAG|ENABLE_[A-Z_]+\s*=|DISABLE_[A-Z_]+\s*=')
227
+ if [ -n "$NEW_CONDITIONALS" ]; then
228
+ DOMAINS="$DOMAINS WIRE_THROUGH"
229
+ break
230
+ fi
231
+ done < <(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.(py|ts|tsx|js|sh)$')
232
+
215
233
  # SECURITY is always included for any code file
216
234
  DOMAINS="SECURITY $DOMAINS"
217
235
 
@@ -246,7 +264,11 @@ Run ONLY the checks whose domain was identified in Step 1.5. Skip checks for dom
246
264
  - **2P (External tool config schema)**: Run if `CONFIG_SCHEMA` in DOMAINS
247
265
  - **2Q (Billing)**: Run if `BILLING` in DOMAINS
248
266
  - **2R (Registry checks)**: ALWAYS run — executes all promoted checks from `scripts/check-registry/manifest.json` <!-- Added: forge#1331 -->
267
+ - **2R.5 (Adaptive gate.d checks)**: ALWAYS run when `adaptive_scripts.enabled` — executes `.forgedock/scripts/gate.d/*.sh`; each script is repo-specific and was promoted from a recurring finding pattern <!-- Added: forge#1739 -->
268
+ - **2G.8 (Wire-through proof)**: Run if `WIRE_THROUGH` in DOMAINS — demands each newly added conditional path be demonstrably reachable <!-- Added: forge#1731 -->
269
+ - **2G.9 (Danger-zone recurrence)**: Run if `FORGE_GRAPH` in DOMAINS — cross-references injected danger-zone rule cards from FORGE:CONTEXT against the diff; violations tagged `known-pattern-recurrence` (HIGH) <!-- Added: forge#1744 -->
249
270
  - **2S (Test failure classification)**: Run if any Step 2 check invoked a test suite and it failed — classifies the failure as PRE_BROKEN, FLAKY, or REAL using `scripts/flaky-quarantine.sh` <!-- Added: forge#1336 -->
271
+ - **2T (Subscribed pattern card rules)**: ALWAYS run when `pattern_feeds.enabled` is true — injects Tier-B (warning-only) learned rules from subscribed exchange cards that have a `gate.d` check template matching any changed file's stack <!-- Added: forge#1746 -->
250
272
 
251
273
  ### 2A: Security (ALL files)
252
274
 
@@ -469,6 +491,30 @@ done < <(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.(tsx?|jsx?)$' | grep -v
469
491
 
470
492
  ### 2G: Cross-service integration (shell scripts with curl/wget)
471
493
 
494
+ <!-- Added: forge#1739 — Tier-B learned-rules injection -->
495
+
496
+ **Tier-B learned-rules injection (SHELL domain)**: Before running host-header and localhost checks, load any repo-specific prose rules from `devdocs/learned-rules/SHELL.md`. These are Tier-B patterns — non-mechanizable defect descriptions that have recurred ≥3 times and been promoted by `/optimize` for inclusion in this domain scan. Inject the content as additional context for the LLM scan, bounded to ~200 tokens (approximately 30 lines). Skip gracefully if the file is absent.
497
+
498
+ ```bash
499
+ # Tier-B learned-rules injection for SHELL domain
500
+ SHELL_LEARNED_RULES=""
501
+ SHELL_RULES_PATH="{WORKTREE_PATH}/devdocs/learned-rules/SHELL.md"
502
+ if [ -f "$SHELL_RULES_PATH" ]; then
503
+ # Bound to first 30 lines (~200 tokens) — enough for pattern descriptions
504
+ SHELL_LEARNED_RULES=$(head -30 "$SHELL_RULES_PATH" 2>/dev/null || true)
505
+ if [ -n "$SHELL_LEARNED_RULES" ]; then
506
+ echo "2G: Injecting Tier-B learned rules from devdocs/learned-rules/SHELL.md ($(wc -l < "$SHELL_RULES_PATH" 2>/dev/null || echo '?') total lines, first 30 loaded)"
507
+ # The learned rules are used as additional LLM scan context below.
508
+ # Each rule entry is a prose description of a recurrent defect class.
509
+ # When scanning shell scripts below, treat any pattern matching a
510
+ # learned-rule description as a MEDIUM finding (advisory, not blocking)
511
+ # unless the rule explicitly marks it HIGH.
512
+ fi
513
+ fi
514
+ ```
515
+
516
+ **Apply Tier-B learned rules**: For each changed `*.sh` file, if `SHELL_LEARNED_RULES` is non-empty, perform an additional LLM-assisted scan: read the shell file diff and the injected rules, emit any matching findings as `SHELL-LEARNED-{slug} | {SEVERITY} | {file} | {rule description}`. This scan is bounded — read only the diff lines (not the full file) and the injected rule content. Skip if no rule content was loaded.
517
+
472
518
  For shell scripts that make HTTP requests to internal services:
473
519
  1. Read the target service's middleware to verify the Host header will be accepted
474
520
  2. Check that auth tokens are included if the endpoint requires them
@@ -572,6 +618,155 @@ fi
572
618
 
573
619
  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).
574
620
 
621
+ ### 2G.8: Wire-through proof for newly added conditional paths
622
+
623
+ <!-- Added: forge#1731 -->
624
+
625
+ **Triggered when**: `WIRE_THROUGH` in DOMAINS — the diff adds new conditional lines (`if`/`elif`/`else`/guard/feature-flag) in executable code files (`.py`, `.ts`, `.tsx`, `.js`, `.sh`). Markdown spec files are explicitly excluded — prose docs use `if`/`else` as natural language, not executable conditionals.
626
+
627
+ **Why this matters**: A recurring defect class ships dead-on-arrival: guards, feature flags, validators, and error branches that are added to the codebase but never actually execute. Examples: #1230 (orchestrate Layer 5 `LAYER1_FILES` never populated — dead code), #1522 (validator pointed at wrong path — permanent no-op), #1244 (guard added to fix Layer 5 itself never fires), #1580 (hook reads wrong transcript schema). Nothing in the quality gate previously asked the one question that kills this class: *prove this new conditional actually runs.*
628
+
629
+ **Scope boundary — newly added lines ONLY**: This check scans lines beginning with `+` in the diff (excluding `+++` file-header lines). Pre-existing conditionals are **never** flagged. A conditional that existed before this diff is outside scope regardless of whether it has tests.
630
+
631
+ **Acceptance criteria for each new conditional**:
632
+
633
+ Each newly added `if`/`elif`/`else` block or guard construct must satisfy AT LEAST ONE of:
634
+
635
+ 1. **Test present in the diff**: The same diff (or any test file in the diff) contains a call that exercises the guarded path — a test function, an invocation with a parameter that triggers the condition, or an assertion on the conditional branch's output.
636
+ 2. **Execution trace annotation**: The new conditional line is immediately preceded or followed by a comment `# WIRE:PROVEN — <method>` where `<method>` describes how the path was verified to fire (e.g., `# WIRE:PROVEN — manual: ran with --debug flag, confirmed guard triggered`).
637
+ 3. **Trivial re-guard** (auto-exempt): The conditional is a null-check, length-check, or type-check on a value already validated upstream on the same code path AND the new condition's body is a `return`, `continue`, `break`, `pass`, or single-line assignment with no side effects. These are defensive re-guards with no new behavior.
638
+
639
+ **Check logic**:
640
+
641
+ ```bash
642
+ # WIRE_THROUGH check — runs when WIRE_THROUGH in DOMAINS
643
+ WIRE_THROUGH_FINDINGS=""
644
+
645
+ while IFS= read -r f; do
646
+ [ -z "$f" ] && continue
647
+ # Extract newly added conditional lines from the diff (+ lines, not +++ header)
648
+ NEW_CONDS=$(git diff HEAD -- "$f" 2>/dev/null \
649
+ | grep -E '^\+' | grep -v '^+++' \
650
+ | grep -nE '\bif\b|\belif\b|\belse\b|guard|feature.?flag|FEATURE_FLAG|ENABLE_|DISABLE_')
651
+
652
+ [ -z "$NEW_CONDS" ] && continue
653
+
654
+ # For each new conditional line, check for acceptance criteria
655
+ while IFS= read -r cond_line; do
656
+ [ -z "$cond_line" ] && continue
657
+ LINENO=$(echo "$cond_line" | cut -d: -f1 | tr -d '+')
658
+
659
+ # Criterion 3 auto-exempt: trivial re-guard (null/len/type check with no-side-effect body)
660
+ COND_TEXT=$(echo "$cond_line" | sed 's/^+//')
661
+ if echo "$COND_TEXT" | grep -qE '\bis (None|null|undefined)\b|\blen\s*\(|\bisinstance\s*\(|\btypeof\b|\bif not\b|\bif !\b'; then
662
+ # Check the body is trivial (next line is return/continue/break/pass/simple assign)
663
+ # Use git diff context to read adjacent lines — simplified: check annotation only
664
+ NEXT_CONTEXT=$(git diff HEAD -- "$f" 2>/dev/null | grep -A2 "^+${COND_TEXT}" | tail -2)
665
+ if echo "$NEXT_CONTEXT" | grep -qE '^\+\s*(return|continue|break|pass|[a-zA-Z_]+ = [^(]+$)'; then
666
+ continue # Auto-exempt trivial re-guard
667
+ fi
668
+ fi
669
+
670
+ # Criterion 2: WIRE:PROVEN annotation present in diff near this line
671
+ if git diff HEAD -- "$f" 2>/dev/null | grep -qE '^\+.*#\s*WIRE:PROVEN'; then
672
+ continue # Annotated — accepted
673
+ fi
674
+
675
+ # Criterion 1: test exercising this path present anywhere in the diff
676
+ # Look for test patterns added in the diff (test_, def test, it(, describe(, assert)
677
+ DIFF_HAS_TEST=$(git diff HEAD -- 2>/dev/null | grep -E '^\+' | grep -v '^+++' \
678
+ | grep -E 'def test_|test\(|it\(|describe\(|assert |expect\(' | head -1)
679
+ if [ -n "$DIFF_HAS_TEST" ]; then
680
+ continue # Test present in diff — accepted
681
+ fi
682
+
683
+ # None of the criteria met — emit a finding
684
+ WIRE_THROUGH_FINDINGS="${WIRE_THROUGH_FINDINGS}
685
+ WIRE_THROUGH | HIGH | $f:${LINENO:-?} | Newly added conditional '$(echo "$COND_TEXT" | xargs | head -c 80)' has no wire-through proof. Add a test that triggers this path, OR annotate with '# WIRE:PROVEN — <method>', OR verify it qualifies as a trivial re-guard (null/len/type check with no-side-effect body)."
686
+ done <<< "$NEW_CONDS"
687
+ done < <(echo {CHANGED_FILES} | tr ' ' '\n' | grep -E '\.(py|ts|tsx|js|sh)$')
688
+
689
+ # Emit findings
690
+ if [ -n "$WIRE_THROUGH_FINDINGS" ]; then
691
+ echo "$WIRE_THROUGH_FINDINGS"
692
+ fi
693
+ ```
694
+
695
+ **Severity**: **HIGH** — a guard that never fires is functionally equivalent to dead code and defeats the purpose of the defensive check. The review agent has consistently flagged this class (see motivation issues above). Blocking at gate eliminates the 1–2 week detection lag.
696
+
697
+ **Auto-exempt patterns** (builder does not need to prove these):
698
+ - Null/None/undefined checks where body is `return`/`continue`/`break`/`pass`
699
+ - Length checks where body is a single-line assignment or early return
700
+ - Type checks (`isinstance`, `typeof`) where body is a single-line coercion
701
+
702
+ **Escaping the check legitimately**:
703
+ - Add a test in the same diff that exercises the conditional
704
+ - Add `# WIRE:PROVEN — <method>` immediately before or after the new conditional
705
+ - Ensure the conditional qualifies as a trivial re-guard (see criterion 3 above)
706
+
707
+ Do NOT suppress the finding by making the check less strict or adding fake no-op tests. The intent is a real proof that the path is reachable. <!-- Added: forge#1731 -->
708
+
709
+ ### 2G.9: Danger-zone recurrence check (FORGE_GRAPH domain) <!-- Added: forge#1744 -->
710
+
711
+ **Triggered when**: `FORGE_GRAPH` in DOMAINS (changed files include `commands/*.md` specs or `scripts/*` files). Skips silently when the FORGE:CONTEXT comment on the issue contains no `### Danger-Zone Rule Cards` section (i.e., Phase C0.5 injected zero cards — nothing to cross-reference).
712
+
713
+ **Why this matters**: When Phase C0.5 injects danger-zone rule cards into the builder's context, those cards represent the highest-value risk knowledge for the files being changed. A violation of an injected rule card is the most embarrassing category of pipeline failure — the system knew about the recurring pattern and the builder violated it anyway. Detecting this at the quality gate (pre-merge) rather than at review (post-PR) is the highest-leverage intervention point. Each violation tagged `known-pattern-recurrence` feeds directly into pipeline-health metrics for A/B evaluation (cards-on vs cards-off first-review finding rate).
714
+
715
+ ```bash
716
+ # 2G.9: Danger-zone recurrence check
717
+ # Step 1: Read injected danger-zone cards from FORGE:CONTEXT comment on the issue
718
+ CONTEXT_COMMENT=$(gh api repos/{GH_REPO}/issues/{ISSUE_NUMBER}/comments \
719
+ --jq '[.[] | select(.body | contains("<!-- FORGE:CONTEXT -->"))] | last | .body // ""' 2>/dev/null \
720
+ | timeout 10 cat || echo '')
721
+
722
+ # Extract the Danger-Zone Rule Cards section
723
+ DZ_SECTION=$(echo "$CONTEXT_COMMENT" \
724
+ | sed -n '/^### Danger-Zone Rule Cards/,/^### /p' \
725
+ | head -30)
726
+
727
+ if [ -z "$DZ_SECTION" ] || ! echo "$DZ_SECTION" | grep -q '^- '; then
728
+ echo "2G.9: no injected danger-zone cards in FORGE:CONTEXT — skipping recurrence check"
729
+ else
730
+ echo "2G.9: checking diff against injected danger-zone rule cards..."
731
+
732
+ # Extract pattern keywords from each card (text after "recurring: " up to " (" or ";")
733
+ # Card format: {file} — {N} findings/90d — recurring: {pattern} (#{issue}); rule: {prevention}
734
+ CARD_PATTERNS=$(echo "$DZ_SECTION" \
735
+ | grep '^- ' \
736
+ | sed -n 's/.*recurring: \([^;(]*\).*/\1/p' \
737
+ | sed 's/[[:space:]]*$//' \
738
+ | grep -v '^$' \
739
+ | head -10)
740
+
741
+ if [ -z "$CARD_PATTERNS" ]; then
742
+ echo "2G.9: no pattern keywords extracted from cards — skipping"
743
+ else
744
+ # Step 2: Read the diff and check for pattern keyword matches
745
+ DIFF_CONTENT=$(cd {WORKTREE_PATH} && git diff --cached 2>/dev/null || git diff HEAD 2>/dev/null)
746
+
747
+ while IFS= read -r pattern; do
748
+ [ -z "$pattern" ] && continue
749
+ # Escape pattern for grep (strip regex-unsafe chars, use literal substring match)
750
+ SAFE_PATTERN=$(echo "$pattern" | head -c 60 | sed 's/[.*+?^${}()|[\]\\]/\\&/g')
751
+ [ -z "$SAFE_PATTERN" ] && continue
752
+
753
+ # Check if the diff introduces lines matching the pattern (added lines only)
754
+ MATCH=$(echo "$DIFF_CONTENT" | grep -E '^\+' | grep -v '^+++' | grep -i "$SAFE_PATTERN" | head -3)
755
+ if [ -n "$MATCH" ]; then
756
+ MATCH_PREVIEW=$(echo "$MATCH" | head -1 | head -c 120)
757
+ echo "FORGE_GRAPH | HIGH | known-pattern-recurrence | Diff matches injected rule card pattern '${pattern}': ${MATCH_PREVIEW} — this is a known-pattern-recurrence: the pipeline injected this rule card and the implementation violated it anyway. Fix before merging."
758
+ fi
759
+ done <<< "$CARD_PATTERNS"
760
+
761
+ echo "2G.9: recurrence check complete"
762
+ fi
763
+ fi
764
+ ```
765
+
766
+ **Severity**: **HIGH** — `known-pattern-recurrence` is the highest embarrassment class. The pipeline had the risk knowledge in context and the implementation violated it regardless. This represents a failure of the context injection mechanism's effectiveness, and every recurrence degrades the A/B signal that measures whether memory injection changes agent behavior. The builder must fix the violation before the gate can pass.
767
+
768
+ **Note**: This check fires only for FORGE_GRAPH domain changes (ForgeDock spec files). For application code changes, violations of injected rule cards are surfaced by the review agents in Phase 5 — the quality gate cross-reference here is specific to pipeline spec changes where the FORGE:CONTEXT from the issue is the definitive context source. <!-- Added: forge#1744 -->
769
+
575
770
  ### 2H: Asyncio cancellation safety (Python async code)
576
771
 
577
772
  **Triggered when**: changed Python files contain `asyncio.shield`, `asyncio.wait_for`, or `Task.cancel`.
@@ -1165,6 +1360,228 @@ If any registry check produces findings, include them in the Step 3 findings lis
1165
1360
 
1166
1361
  ---
1167
1362
 
1363
+ ## Step 2R.5: Adaptive gate.d checks (when adaptive_scripts.enabled)
1364
+
1365
+ <!-- Added: forge#1739 -->
1366
+
1367
+ **Purpose**: Run all repo-specific learned checks that have been promoted from recurring review findings into `.forgedock/scripts/gate.d/`. Unlike the static `scripts/check-registry/` (Step 2R), gate.d scripts live in the per-repo adaptive layer and are generated by `/optimize` from validated Ledger patterns. Each script represents a defect class that appeared ≥3 times in this specific codebase — it catches in milliseconds what previously required a full review cycle to detect.
1368
+
1369
+ **Triggered when**: `adaptive_scripts.enabled` is `true` in `forge.yaml` (default: true) AND the `gate.d/` subdirectory exists under `adaptive_scripts.directory`.
1370
+
1371
+ ```bash
1372
+ # Read adaptive_scripts config from forge.yaml
1373
+ ADAPTIVE_ENABLED=$(yq '.adaptive_scripts.enabled // "true"' "{WORKTREE_PATH}/forge.yaml" 2>/dev/null || echo 'true')
1374
+ ADAPTIVE_DIR_REL=$(yq '.adaptive_scripts.directory // ".forgedock/scripts"' "{WORKTREE_PATH}/forge.yaml" 2>/dev/null || echo '.forgedock/scripts')
1375
+ ADAPTIVE_DIR="{WORKTREE_PATH}/${ADAPTIVE_DIR_REL}"
1376
+
1377
+ # Bounds check: reject adaptive_scripts.directory values that escape the worktree root
1378
+ WORKTREE_NORM=$(realpath -m "{WORKTREE_PATH}" 2>/dev/null || echo "{WORKTREE_PATH}")
1379
+ ADAPTIVE_DIR_NORM=$(realpath -m "$ADAPTIVE_DIR" 2>/dev/null || echo "$ADAPTIVE_DIR")
1380
+ if [[ "$ADAPTIVE_DIR_NORM" != "${WORKTREE_NORM}/"* ]]; then
1381
+ echo "2R.5: WARN — adaptive_scripts.directory resolves outside worktree root ('$ADAPTIVE_DIR_NORM') — skipping gate.d execution"
1382
+ ADAPTIVE_ENABLED="false"
1383
+ fi
1384
+
1385
+ GATED_FINDINGS=""
1386
+
1387
+ if [ "$ADAPTIVE_ENABLED" != "false" ]; then
1388
+ GATE_D_DIR="${ADAPTIVE_DIR}/gate.d"
1389
+
1390
+ if [ ! -d "$GATE_D_DIR" ]; then
1391
+ echo "2R.5: No gate.d directory found at $GATE_D_DIR — skipping adaptive checks."
1392
+ else
1393
+ # Generate unified diff file for scripts to consume
1394
+ DIFF_PATH=$(mktemp /tmp/forge-gate-diff.XXXXXX 2>/dev/null || echo "/tmp/forge-gate-diff.$$")
1395
+ git -C "{WORKTREE_PATH}" diff HEAD -- {CHANGED_FILES} > "$DIFF_PATH" 2>/dev/null || true
1396
+
1397
+ GATE_D_COUNT=$(find "$GATE_D_DIR" -maxdepth 1 -name "*.sh" -type f 2>/dev/null | wc -l | tr -d ' ')
1398
+
1399
+ if [ "$GATE_D_COUNT" -eq 0 ]; then
1400
+ echo "2R.5: gate.d directory exists but is empty — no adaptive checks to run."
1401
+ else
1402
+ echo "2R.5: Running $GATE_D_COUNT adaptive gate.d check(s) from $GATE_D_DIR ..."
1403
+
1404
+ # Run each gate.d script in lexicographic order
1405
+ while IFS= read -r script; do
1406
+ [ -z "$script" ] && continue
1407
+ SLUG=$(basename "$script" .sh)
1408
+
1409
+ if [ ! -x "$script" ]; then
1410
+ echo "2R.5: WARN — gate.d script $SLUG is not executable — skipping (run: chmod +x $script)"
1411
+ continue
1412
+ fi
1413
+
1414
+ # Run with per-script 30s timeout; pass diff path and worktree root
1415
+ # timeout exit code 124 = timed out; treat as fail-closed (HIGH finding)
1416
+ GATE_OUTPUT=$(timeout 30 bash "$script" "$DIFF_PATH" "{WORKTREE_PATH}" 2>/dev/null)
1417
+ GATE_EXIT=$?
1418
+
1419
+ if [ "$GATE_EXIT" -eq 124 ]; then
1420
+ # Timeout — fail closed: block the gate with HIGH finding
1421
+ TIMEOUT_FINDING="GATD-${SLUG}-timeout | HIGH | (gate.d) | gate.d script '$SLUG' timed out after 30s — failing closed. Investigate and optimize the script or increase timeout via gate.d contract."
1422
+ GATED_FINDINGS="${GATED_FINDINGS}
1423
+ ${TIMEOUT_FINDING}"
1424
+ echo "2R.5: TIMEOUT — $SLUG (30s) — fail-closed HIGH finding added"
1425
+ elif [ "$GATE_EXIT" -eq 1 ]; then
1426
+ # Script fired — findings on stdout
1427
+ while IFS= read -r line; do
1428
+ [ -z "$line" ] && continue
1429
+ GATED_FINDINGS="${GATED_FINDINGS}
1430
+ ${line}"
1431
+ done <<< "$GATE_OUTPUT"
1432
+ echo "2R.5: FAIL — $SLUG matched"
1433
+ elif [ "$GATE_EXIT" -eq 0 ] || [ "$GATE_EXIT" -eq 2 ]; then
1434
+ # 0 = pass, 2 = inapplicable (skip silently)
1435
+ echo "2R.5: PASS — $SLUG"
1436
+ else
1437
+ echo "2R.5: WARN — $SLUG exited with unexpected code $GATE_EXIT (expected 0, 1, or 2)"
1438
+ fi
1439
+ done < <(find "$GATE_D_DIR" -maxdepth 1 -name "*.sh" -type f | sort)
1440
+
1441
+ fi
1442
+
1443
+ # Clean up temp diff file
1444
+ rm -f "$DIFF_PATH" 2>/dev/null || true
1445
+ fi
1446
+ fi
1447
+ ```
1448
+
1449
+ If any gate.d check produces findings, include them in the Step 3 findings list prefixed with `GATD-{slug}` (e.g. `GATD-stderr-contamination | HIGH | scripts/doctor.sh | ...`). HIGH-severity findings are blocking; MEDIUM are advisory; LOW are advisory only.
1450
+
1451
+ **gate.d script contract** (for authors of promoted checks):
1452
+ - `$1` = absolute path to the unified diff file (`git diff HEAD`)
1453
+ - `$2` = absolute path to the worktree root
1454
+ - stdout = findings lines, one per line: `SLUG | HIGH|MEDIUM|LOW | FILE | MESSAGE`
1455
+ - Exit 0 = no findings / passed; Exit 1 = findings emitted; Exit 2 = inapplicable (diff has no relevant content)
1456
+ - 30-second wall-time budget enforced by quality-gate; timeout = fail-closed HIGH finding
1457
+ - Do NOT write to disk, do NOT make network requests, do NOT modify any files
1458
+
1459
+ ---
1460
+
1461
+ ## Step 2T: Subscribed pattern card rules (when pattern_feeds enabled)
1462
+
1463
+ <!-- Added: forge#1746 -->
1464
+
1465
+ **Triggered when**: `pattern_feeds.enabled` is `true` in `forge.yaml` AND the local ledger contains at least one card with `origin: <feed-slug>` AND a `gate.d` check template.
1466
+
1467
+ **Purpose**: Cards subscribed from exchange repos (see `forge.yaml → pattern_feeds`) may include a `gate.d` check template — a bash snippet that catches the pattern described in the card. When the changed files include a stack that matches the card's `stacks:` field, the check template is seeded as a Tier-B (warning-only) learned rule and injected into this gate run. This gives cross-repo validated patterns gate enforcement without requiring a human to manually promote them first.
1468
+
1469
+ **CRITICAL — advisory-only**: Step 2T findings are NEVER blocking (HIGH). They are always MEDIUM (advisory) regardless of the card's stated severity. A finding becomes blocking only after the card is promoted to a local validated finding (via the standard 3-occurrence threshold in `/pipeline-health` Phase 4 + Step 2R). This prevents imported patterns from blocking the builder until the pattern has been validated against the local codebase.
1470
+
1471
+ **Fail-safe on malformed templates**: If a `gate.d` check template is absent, empty, syntactically invalid, or exits with an unexpected code, log a warning and continue. Never fail-open silently — log every skip with a reason.
1472
+
1473
+ ```bash
1474
+ # Read pattern_feeds config
1475
+ FEEDS_ENABLED=$(yq '.pattern_feeds.enabled // "false"' "{WORKTREE_PATH}/forge.yaml" 2>/dev/null || echo 'false')
1476
+ LEDGER_PATH=$(yq '.pattern_feeds.ledger_path // ".forge/ledger"' "{WORKTREE_PATH}/forge.yaml" 2>/dev/null || echo '.forge/ledger')
1477
+ LEDGER_DIR="{WORKTREE_PATH}/${LEDGER_PATH}"
1478
+
1479
+ SUBSCRIBED_FINDINGS=""
1480
+
1481
+ if [ "$FEEDS_ENABLED" != "true" ]; then
1482
+ echo "2T: pattern_feeds.enabled is not true — skipping subscribed card rules."
1483
+ else
1484
+ if [ ! -d "$LEDGER_DIR" ]; then
1485
+ echo "2T: Ledger directory not found at $LEDGER_DIR — skipping. Run scripts/build-knowledge-index.mjs to populate."
1486
+ else
1487
+ # Discover subscribed cards: ledger entries with origin != "" (imported from exchange)
1488
+ SUBSCRIBED_CARDS=$(find "$LEDGER_DIR" -name "*.json" -type f 2>/dev/null | xargs grep -l '"origin"' 2>/dev/null | head -50)
1489
+
1490
+ if [ -z "$SUBSCRIBED_CARDS" ]; then
1491
+ echo "2T: No subscribed cards found in ledger — skipping."
1492
+ else
1493
+ CARD_COUNT=$(echo "$SUBSCRIBED_CARDS" | wc -l | tr -d ' ')
1494
+ echo "2T: Found $CARD_COUNT subscribed card(s) — checking for applicable gate.d templates..."
1495
+
1496
+ while IFS= read -r card_file; do
1497
+ [ -z "$card_file" ] && continue
1498
+
1499
+ # Read card metadata
1500
+ CARD_SLUG=$(jq -r '.slug // ""' "$card_file" 2>/dev/null)
1501
+ CARD_ORIGIN=$(jq -r '.origin // ""' "$card_file" 2>/dev/null)
1502
+ CARD_STACKS=$(jq -r '.stacks // [] | .[]' "$card_file" 2>/dev/null | tr '\n' ' ' | xargs)
1503
+ GATE_TEMPLATE=$(jq -r '.gate_template // ""' "$card_file" 2>/dev/null)
1504
+ PREVENTION_RULE=$(jq -r '.prevention // ""' "$card_file" 2>/dev/null)
1505
+
1506
+ [ -z "$CARD_SLUG" ] && continue
1507
+ [ -z "$GATE_TEMPLATE" ] && {
1508
+ echo "2T: SKIP $CARD_SLUG (origin: $CARD_ORIGIN) — no gate.d template"
1509
+ continue
1510
+ }
1511
+
1512
+ # Stack filter: check if any changed file's extension matches the card's stacks
1513
+ # Stack detection: node/typescript=.ts/.tsx/.js/.mjs, python=.py,
1514
+ # bash/gha=.sh/.yml/.yaml, go=.go, rust=.rs, generic=any
1515
+ STACK_MATCH=false
1516
+ for STACK in $CARD_STACKS; do
1517
+ case "$STACK" in
1518
+ node|typescript)
1519
+ echo "{CHANGED_FILES}" | tr ' ' '\n' | grep -qE '\.(ts|tsx|js|mjs|cjs)$' && STACK_MATCH=true ;;
1520
+ python)
1521
+ echo "{CHANGED_FILES}" | tr ' ' '\n' | grep -qE '\.py$' && STACK_MATCH=true ;;
1522
+ bash)
1523
+ echo "{CHANGED_FILES}" | tr ' ' '\n' | grep -qE '\.sh$' && STACK_MATCH=true ;;
1524
+ gha)
1525
+ echo "{CHANGED_FILES}" | tr ' ' '\n' | grep -qE '\.ya?ml$' && STACK_MATCH=true ;;
1526
+ go)
1527
+ echo "{CHANGED_FILES}" | tr ' ' '\n' | grep -qE '\.go$' && STACK_MATCH=true ;;
1528
+ rust)
1529
+ echo "{CHANGED_FILES}" | tr ' ' '\n' | grep -qE '\.rs$' && STACK_MATCH=true ;;
1530
+ generic)
1531
+ STACK_MATCH=true ;;
1532
+ esac
1533
+ [ "$STACK_MATCH" = "true" ] && break
1534
+ done
1535
+
1536
+ if [ "$STACK_MATCH" = "false" ]; then
1537
+ echo "2T: SKIP $CARD_SLUG — no stack match (card stacks: $CARD_STACKS)"
1538
+ continue
1539
+ fi
1540
+
1541
+ # Validate template syntax (bash -n = syntax-only check, no execution)
1542
+ TEMPLATE_FILE=$(mktemp /tmp/forge-gate-template-XXXXXX.sh 2>/dev/null || echo "/tmp/forge-gate-template-$$.sh")
1543
+ echo "$GATE_TEMPLATE" > "$TEMPLATE_FILE" 2>/dev/null
1544
+ if ! bash -n "$TEMPLATE_FILE" 2>/dev/null; then
1545
+ echo "2T: WARN — $CARD_SLUG (origin: $CARD_ORIGIN) has invalid gate.d template syntax — skipping"
1546
+ rm -f "$TEMPLATE_FILE" 2>/dev/null || true
1547
+ continue
1548
+ fi
1549
+ chmod +x "$TEMPLATE_FILE" 2>/dev/null
1550
+
1551
+ # Run template with 15s timeout; pass changed files list and worktree path
1552
+ TEMPLATE_OUTPUT=$(timeout 15 bash "$TEMPLATE_FILE" "{CHANGED_FILES}" "{WORKTREE_PATH}" 2>/dev/null)
1553
+ TEMPLATE_EXIT=$?
1554
+ rm -f "$TEMPLATE_FILE" 2>/dev/null || true
1555
+
1556
+ if [ "$TEMPLATE_EXIT" -eq 124 ]; then
1557
+ echo "2T: WARN — $CARD_SLUG template timed out (15s) — skipping"
1558
+ elif [ "$TEMPLATE_EXIT" -eq 1 ]; then
1559
+ # Template fired — emit as MEDIUM (advisory) regardless of card severity
1560
+ while IFS= read -r line; do
1561
+ [ -z "$line" ] && continue
1562
+ # Rewrite any HIGH severity from card template to MEDIUM (advisory only)
1563
+ SAFE_LINE=$(echo "$line" | sed 's/| HIGH |/| MEDIUM |/g')
1564
+ SUBSCRIBED_FINDINGS="${SUBSCRIBED_FINDINGS}
1565
+ SUBSCRIBED-${CARD_SLUG} | MEDIUM | (from exchange:${CARD_ORIGIN}) | ${SAFE_LINE}"
1566
+ done <<< "$TEMPLATE_OUTPUT"
1567
+ echo "2T: ADVISORY — $CARD_SLUG matched (origin: $CARD_ORIGIN). Prevention rule: ${PREVENTION_RULE}"
1568
+ echo "2T: NOTE — This is an imported pattern (Tier-B advisory). It becomes blocking only after local promotion via /pipeline-health Phase 4."
1569
+ elif [ "$TEMPLATE_EXIT" -eq 0 ] || [ "$TEMPLATE_EXIT" -eq 2 ]; then
1570
+ echo "2T: PASS — $CARD_SLUG (no match)"
1571
+ else
1572
+ echo "2T: WARN — $CARD_SLUG template exited with unexpected code $TEMPLATE_EXIT — skipping"
1573
+ fi
1574
+
1575
+ done <<< "$SUBSCRIBED_CARDS"
1576
+ fi
1577
+ fi
1578
+ fi
1579
+ ```
1580
+
1581
+ If any Step 2T template produces findings, include them in the Step 3 findings list prefixed `SUBSCRIBED-{slug}`. All Step 2T findings are MEDIUM (advisory) — they never block the commit. Include the prevention rule and origin feed in the finding message so the builder can understand the cross-repo context.
1582
+
1583
+ ---
1584
+
1168
1585
  ## Step 2S: Test failure classification (run when tests fail in Step 2)
1169
1586
 
1170
1587
  <!-- Added: forge#1336 -->
@@ -11,7 +11,7 @@ argument-hint: [--dry-run | --since <hours> | --issue <number>]
11
11
 
12
12
  Scan ALL open issues with intermediate workflow labels for orphaned state — issues where the agent died mid-pipeline (context expired, rate-limited, crashed) and no active agent is continuing. Diagnose each orphan's actual GitHub state and apply the appropriate recovery action.
13
13
 
14
- **Agent model policy**: `model: "sonnet"`. Fallback: `model: "opus"` if rate-limited.
14
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet". Fallback: `model: "opus"` if rate-limited.
15
15
  **NEVER use plan mode (EnterPlanMode).**
16
16
  **NEVER use the Agent tool** — recover-orphans re-enters the pipeline via `Skill(skill="work-on", ...)` and `Skill(skill="review-pr", ...)` only.
17
17
 
@@ -336,7 +336,7 @@ for NUM in $ORPHAN_LIST; do
336
336
  else
337
337
  gh issue edit "$NUM" ${GH_FLAG} \
338
338
  --add-label "workflow:merged" \
339
- --remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:invalid,workflow:decomposed" \
339
+ --remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:invalid,workflow:decomposed" \
340
340
  2>/dev/null || true
341
341
  gh issue close "$NUM" ${GH_FLAG} \
342
342
  --comment "Closed by /recover-orphans: PR #${PR_NUM} was already merged. Labels corrected." \
@@ -362,7 +362,7 @@ for NUM in $ORPHAN_LIST; do
362
362
  2>/dev/null || true
363
363
  gh issue edit "$NUM" ${GH_FLAG} \
364
364
  --add-label "workflow:merged" \
365
- --remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:invalid,workflow:decomposed" \
365
+ --remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge,workflow:invalid,workflow:decomposed" \
366
366
  2>/dev/null || true
367
367
  fi
368
368
  fi
@@ -378,7 +378,7 @@ for NUM in $ORPHAN_LIST; do
378
378
  Skill(skill="review-pr", args="${PR_NUM} --auto-merge --issue ${NUM} --gh-flag ${GH_FLAG}")
379
379
  # After review: update label
380
380
  gh issue edit "$NUM" ${GH_FLAG} --add-label "workflow:in-review" \
381
- --remove-label "workflow:building" 2>/dev/null || true
381
+ --remove-label "workflow:building,workflow:awaiting-merge" 2>/dev/null || true
382
382
  fi
383
383
  RECOVERY_RESULTS="${RECOVERY_RESULTS}| #${NUM} | review-pr | PR #${PR_NUM} submitted for review |\n"
384
384
  ;;
@@ -401,7 +401,7 @@ for NUM in $ORPHAN_LIST; do
401
401
  echo " [DRY-RUN] Would: remove workflow:investigating, workflow:ready-to-build, workflow:building, workflow:in-review"
402
402
  else
403
403
  gh issue edit "$NUM" ${GH_FLAG} \
404
- --remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review" \
404
+ --remove-label "workflow:investigating,workflow:ready-to-build,workflow:building,workflow:in-review,workflow:awaiting-merge" \
405
405
  2>/dev/null || true
406
406
  gh issue comment "$NUM" ${GH_FLAG} \
407
407
  --body "<!-- FORGE:ORPHAN_RECOVERED -->
@@ -10,7 +10,7 @@ install: extras
10
10
 
11
11
  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.
12
12
 
13
- **Agent model policy**: `model: "sonnet"` (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
13
+ **Agent model policy**: `model: "{DEFAULT_MODEL}"` — resolved from forge.yaml `agents.default_model`, else "sonnet" (standard tier). Fallback: `model: "opus"` if rate-limited. Feature gate: pass `effort` in Task/Skill spawns only on Claude Code >= 2.1.154.
14
14
 
15
15
  ---
16
16
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: New or modified routers/routes, OR SDK/OpenAPI files changed (`sdk/`, `openapi*.json`, `openapi-versions/`)
14
- **Type**: `general-purpose` | **Model**: `sonnet`
14
+ **Type**: `general-purpose` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: AUTH domain detected
14
- **Type**: `security-exploit-auditor` | **Model**: `sonnet`
14
+ **Type**: `security-exploit-auditor` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: BILLING domain detected
14
- **Type**: `codebase-explorer` | **Model**: `sonnet`
14
+ **Type**: `codebase-explorer` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: CONCURRENCY domain detected OR BILLING domain detected
14
- **Type**: `general-purpose` | **Model**: `sonnet`
14
+ **Type**: `general-purpose` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: DATABASE domain detected
14
- **Type**: `general-purpose` | **Model**: `sonnet`
14
+ **Type**: `general-purpose` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: WEB service touched
14
- **Type**: `codebase-explorer` | **Model**: `sonnet`
14
+ **Type**: `codebase-explorer` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```
@@ -1,5 +1,5 @@
1
1
  ---
2
- install: internal
2
+ install: core
3
3
  ---
4
4
  <!-- SPDX-FileCopyrightText: Copyright (c) RapierCraft Studios -->
5
5
  <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
@@ -11,7 +11,7 @@ install: internal
11
11
 
12
12
 
13
13
  **Trigger**: INFRA service touched
14
- **Type**: `general-purpose` | **Model**: `sonnet`
14
+ **Type**: `general-purpose` | **Model**: `{SUBAGENT_MODEL}`
15
15
 
16
16
  **Prompt template:**
17
17
  ```