forgedock 1.20.0 → 1.21.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/bin/engine/phases.mjs +4 -2
- package/bin/engine/telemetry-seam.mjs +1 -1
- package/bin/engine-cli.mjs +40 -1
- package/bin/forgedock.mjs +1 -1
- package/bin/lean/effects.mjs +51 -0
- package/bin/lean/lean.mjs +352 -0
- package/bin/lean/lean.test.mjs +525 -0
- package/bin/lean/prompts.mjs +62 -0
- package/bin/lean/risk.mjs +49 -0
- package/bin/runner.mjs +79 -2
- package/commands/work-on/build/validate.md +13 -6
- package/commands/work-on/review.md +1 -1
- package/docs/CONFIG.md +3 -0
- package/docs/RELEASE-NOTES.md +6 -0
- package/package.json +2 -2
package/bin/runner.mjs
CHANGED
|
@@ -671,6 +671,23 @@ export function isClaudeCliAvailable(
|
|
|
671
671
|
// gated on the cached `claude --version` probe, never on help-text scraping.
|
|
672
672
|
const CLI_MIN_VERSION_MODEL_FLAG = [1, 0, 0];
|
|
673
673
|
const CLI_MIN_VERSION_MAX_TURNS_FLAG = [1, 0, 0];
|
|
674
|
+
// forge#4065: minimal phase sessions. Conservative floor: the oldest version
|
|
675
|
+
// known to accept `--strict-mcp-config`, `--mcp-config` and
|
|
676
|
+
// `--disable-slash-commands` together. Unparseable/older => flags omitted.
|
|
677
|
+
const CLI_MIN_VERSION_MINIMAL_SESSION = [2, 1, 296];
|
|
678
|
+
|
|
679
|
+
// Phase commands whose spec never invokes `Skill(...)`; only these run with
|
|
680
|
+
// `--disable-slash-commands` (spec-coverage test enforces the invariant).
|
|
681
|
+
export const SKILL_FREE_PHASE_COMMANDS = Object.freeze(["work-on/investigate", "work-on/close"]);
|
|
682
|
+
|
|
683
|
+
// Engine phase commands (mirrors PHASES in bin/engine/phases.mjs; a test enforces it). Only these
|
|
684
|
+
// get the minimal-session group: other `forgedock run` commands (qa-sweep, analytics, geo-audit,
|
|
685
|
+
// autopilot) may need the operator's MCP servers and skills.
|
|
686
|
+
export const ENGINE_PHASE_COMMANDS = Object.freeze([
|
|
687
|
+
"work-on/investigate", "work-on/decompose", "work-on/build", "work-on/review", "work-on/remediate", "work-on/close",
|
|
688
|
+
]);
|
|
689
|
+
|
|
690
|
+
const MINIMAL_SESSION_MCP_CONFIG = '{"mcpServers":{}}';
|
|
674
691
|
|
|
675
692
|
/**
|
|
676
693
|
* Extract a `major.minor.patch` version from probe output (the text after the
|
|
@@ -702,7 +719,7 @@ function versionAtLeast(version, min) {
|
|
|
702
719
|
*
|
|
703
720
|
* @param {string} [cwd]
|
|
704
721
|
* @param {object} [opts] - Forwarded to `isClaudeCliAvailable()` (test seams).
|
|
705
|
-
* @returns {{model: boolean, maxTurns: boolean}}
|
|
722
|
+
* @returns {{model: boolean, maxTurns: boolean, strictMcp: boolean, disableSlash: boolean}}
|
|
706
723
|
*/
|
|
707
724
|
export function getClaudeCliCapabilities(cwd = process.cwd(), opts = {}) {
|
|
708
725
|
isClaudeCliAvailable(cwd, opts);
|
|
@@ -710,9 +727,53 @@ export function getClaudeCliCapabilities(cwd = process.cwd(), opts = {}) {
|
|
|
710
727
|
return {
|
|
711
728
|
model: versionAtLeast(version, CLI_MIN_VERSION_MODEL_FLAG),
|
|
712
729
|
maxTurns: versionAtLeast(version, CLI_MIN_VERSION_MAX_TURNS_FLAG),
|
|
730
|
+
strictMcp: versionAtLeast(version, CLI_MIN_VERSION_MINIMAL_SESSION),
|
|
731
|
+
disableSlash: versionAtLeast(version, CLI_MIN_VERSION_MINIMAL_SESSION),
|
|
713
732
|
};
|
|
714
733
|
}
|
|
715
734
|
|
|
735
|
+
/**
|
|
736
|
+
* Build the minimal-session argv group (forge#4065): no operator MCP servers
|
|
737
|
+
* and, for Skill-free phases only, no slash-command/skill listing. Operator
|
|
738
|
+
* settings are deliberately NOT skipped (the CLI setting-sources flag is not passed) so
|
|
739
|
+
* ForgeDock's user-scope PreToolUse/SubagentStop hooks keep running.
|
|
740
|
+
* Non-engine commands, `FORGEDOCK_FULL_SESSION=1` (exactly) or an unsupported CLI yield `[]`.
|
|
741
|
+
*
|
|
742
|
+
* @param {string} commandName
|
|
743
|
+
* @param {{strictMcp: boolean, disableSlash: boolean}} caps
|
|
744
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
745
|
+
* @returns {string[]}
|
|
746
|
+
*/
|
|
747
|
+
export function buildMinimalSessionFlags(commandName, caps, env = process.env) {
|
|
748
|
+
if (env?.FORGEDOCK_FULL_SESSION === "1" || !caps) return [];
|
|
749
|
+
const name = String(commandName ?? "").trim().replace(/^\/+/, "").replace(/\.md$/i, "");
|
|
750
|
+
if (!ENGINE_PHASE_COMMANDS.includes(name)) return [];
|
|
751
|
+
const flags = [];
|
|
752
|
+
if (caps.strictMcp) flags.push("--strict-mcp-config", "--mcp-config", MINIMAL_SESSION_MCP_CONFIG);
|
|
753
|
+
if (caps.disableSlash && SKILL_FREE_PHASE_COMMANDS.includes(name)) flags.push("--disable-slash-commands");
|
|
754
|
+
return flags;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Lean-path (#4081) minimal session flags: reuses the #4065 capability probe. Every phase is Skill-free, so slash
|
|
759
|
+
* commands are off too; user-scope settings and hooks still load. Unsupported CLI or FORGEDOCK_FULL_SESSION=1 yields [].
|
|
760
|
+
*/
|
|
761
|
+
export function buildLeanSessionFlags(caps, env = process.env) {
|
|
762
|
+
// Same group as the Skill-free engine phases (no MCP, no slash commands). Operator settings stay loaded so the
|
|
763
|
+
// user-scope safety hooks keep running, exactly like #4077.
|
|
764
|
+
return buildMinimalSessionFlags("work-on/investigate", caps, env);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Read-only reviewer tools (#4081): only Read, Grep and Glob. Uses `--tools` where the capability probe says the
|
|
769
|
+
* CLI is recent enough, else denies the mutating tools with `--disallowedTools`.
|
|
770
|
+
*/
|
|
771
|
+
export function buildReadOnlyToolFlags(caps) {
|
|
772
|
+
return caps?.strictMcp
|
|
773
|
+
? ["--tools", "Read,Grep,Glob"]
|
|
774
|
+
: ["--disallowedTools", "Bash", "Edit", "Write", "MultiEdit", "NotebookEdit"];
|
|
775
|
+
}
|
|
776
|
+
|
|
716
777
|
/**
|
|
717
778
|
* Return the exact absolute `claude` executable path that
|
|
718
779
|
* `isClaudeCliAvailable()` validated for `cwd` (issue #2741), ensuring the
|
|
@@ -1443,6 +1504,9 @@ export function runCliBackend({
|
|
|
1443
1504
|
model = null,
|
|
1444
1505
|
maxTurns = null,
|
|
1445
1506
|
leaseHolder = null,
|
|
1507
|
+
sessionFlags = [],
|
|
1508
|
+
// #4081: extra argv (e.g. ["--resume", id]) appended verbatim, never dropped by FORGEDOCK_FULL_SESSION.
|
|
1509
|
+
extraArgs = [],
|
|
1446
1510
|
}) {
|
|
1447
1511
|
// forge#3589: opt-in flags, validated again here as defence in depth. Callers
|
|
1448
1512
|
// (runCommand) are responsible for the version gate. Unset -> argv unchanged.
|
|
@@ -1451,6 +1515,12 @@ export function runCliBackend({
|
|
|
1451
1515
|
const optionalFlags = [
|
|
1452
1516
|
...(modelFlag ? ["--model", modelFlag] : []),
|
|
1453
1517
|
...(maxTurnsFlag ? ["--max-turns", String(maxTurnsFlag)] : []),
|
|
1518
|
+
// forge#4065: minimal-session group, built by buildMinimalSessionFlags().
|
|
1519
|
+
// Re-checked here so FORGEDOCK_FULL_SESSION=1 always wins.
|
|
1520
|
+
...(process.env.FORGEDOCK_FULL_SESSION === "1" || !Array.isArray(sessionFlags)
|
|
1521
|
+
? []
|
|
1522
|
+
: sessionFlags.map(String)),
|
|
1523
|
+
...(Array.isArray(extraArgs) ? extraArgs.map(String) : []),
|
|
1454
1524
|
];
|
|
1455
1525
|
const rawTimeout = parseInt(process.env.FORGEDOCK_CLI_TIMEOUT_MS, 10);
|
|
1456
1526
|
const configuredTimeout = resolveConfiguredCliTimeout(cwd);
|
|
@@ -1738,6 +1808,7 @@ export function runCliBackend({
|
|
|
1738
1808
|
// bin/batch-runner.mjs's `tokenCost()` (`input + output`).
|
|
1739
1809
|
const stdoutTrimmed = stdout.trim();
|
|
1740
1810
|
let parsedResult = null;
|
|
1811
|
+
let sessionId = null;
|
|
1741
1812
|
let usage = null;
|
|
1742
1813
|
let turns = null;
|
|
1743
1814
|
// forge#3696: envelope cost/duration per attempt (null when absent or non-finite).
|
|
@@ -1750,6 +1821,7 @@ export function runCliBackend({
|
|
|
1750
1821
|
if (Number.isFinite(parsed.total_cost_usd)) costUsd = parsed.total_cost_usd;
|
|
1751
1822
|
if (Number.isFinite(parsed.duration_ms)) durationMs = parsed.duration_ms;
|
|
1752
1823
|
parsedResult = typeof parsed.result === "string" ? parsed.result : null;
|
|
1824
|
+
if (typeof parsed.session_id === "string" && parsed.session_id) sessionId = parsed.session_id;
|
|
1753
1825
|
if (parsed.usage && typeof parsed.usage === "object") {
|
|
1754
1826
|
usage = {
|
|
1755
1827
|
input_tokens: toFiniteUsageNumber(parsed.usage.input_tokens),
|
|
@@ -1835,6 +1907,7 @@ export function runCliBackend({
|
|
|
1835
1907
|
turns,
|
|
1836
1908
|
costUsd,
|
|
1837
1909
|
durationMs,
|
|
1910
|
+
sessionId,
|
|
1838
1911
|
backend: "cli",
|
|
1839
1912
|
// forge#3521: the phase's final reply (the CLI envelope's `.result`), so
|
|
1840
1913
|
// callers can read its structured `*_RESULT` block. Additive; "" when absent.
|
|
@@ -2715,10 +2788,13 @@ export async function runCommand(opts = {}) {
|
|
|
2715
2788
|
: null;
|
|
2716
2789
|
let cliModel = commandSettings.model ?? runLevelModel;
|
|
2717
2790
|
let cliMaxTurns = commandSettings.maxTurns;
|
|
2718
|
-
|
|
2791
|
+
// forge#4065: minimal phase session flags, computed per call (never cached).
|
|
2792
|
+
let sessionFlags = [];
|
|
2793
|
+
if (resolvedBin) {
|
|
2719
2794
|
const caps = getClaudeCliCapabilities(cwd);
|
|
2720
2795
|
if (cliModel && !caps.model) cliModel = null;
|
|
2721
2796
|
if (cliMaxTurns && !caps.maxTurns) cliMaxTurns = null;
|
|
2797
|
+
sessionFlags = buildMinimalSessionFlags(commandName, caps);
|
|
2722
2798
|
}
|
|
2723
2799
|
if (!resolvedBin && backend === "auto") {
|
|
2724
2800
|
resolvedBackend = "api";
|
|
@@ -2741,6 +2817,7 @@ export async function runCommand(opts = {}) {
|
|
|
2741
2817
|
model: cliModel,
|
|
2742
2818
|
maxTurns: cliMaxTurns,
|
|
2743
2819
|
leaseHolder,
|
|
2820
|
+
sessionFlags,
|
|
2744
2821
|
},
|
|
2745
2822
|
});
|
|
2746
2823
|
}
|
|
@@ -842,11 +842,18 @@ DEGRADED_CHECKS="${DEGRADED_CHECKS:-}"
|
|
|
842
842
|
GATE_STATUS=$([ "$GATE_PASSED" != "true" ] && echo FAIL || { [ -n "$DEGRADED_CHECKS" ] && echo DEGRADED || echo PASS; })
|
|
843
843
|
# Bind the PASS to what was actually gated: the staged tree is exactly the tree the V5 commit will have.
|
|
844
844
|
# scripts/verify-phase-trail.sh --head-tree (work-on/review.md R1.5) rejects a PASS recorded for a different tree. <!-- Added: forge#3149 -->
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
#
|
|
848
|
-
|
|
849
|
-
|
|
845
|
+
if [ "${REGATE_MERGED_HEAD:-}" = "1" ]; then
|
|
846
|
+
# Marker-only re-gate (invoked by work-on/review.md after a base sync or a stale re-review): HEAD is already committed, so the
|
|
847
|
+
# gated tree must be HEAD's tree. Never stage here: require a clean worktree so no uncommitted fix sits outside the marked tree.
|
|
848
|
+
if [ -n "$(git -C {WORKTREE_PATH} status --porcelain --untracked-files=no)" ]; then
|
|
849
|
+
echo "QUALITY_GATE: marker-only re-gate needs a clean worktree (uncommitted tracked changes) - commit the fix, re-run the scoped V1 gate on the committed head, then re-run this step; not posting a marker" >&2
|
|
850
|
+
exit 1
|
|
851
|
+
fi
|
|
852
|
+
GATE_TREE=$(git -C {WORKTREE_PATH} rev-parse 'HEAD^{tree}')
|
|
853
|
+
else
|
|
854
|
+
git -C {WORKTREE_PATH} add -u # same staging as the commit below; valid only because V2-V4 are done
|
|
855
|
+
GATE_TREE=$(git -C {WORKTREE_PATH} write-tree)
|
|
856
|
+
fi
|
|
850
857
|
[ -n "$GATE_TREE" ] || { echo "QUALITY_GATE: tree unresolved - not posting a marker" >&2; exit 1; }
|
|
851
858
|
QG_BODY="<!-- FORGE:QUALITY_GATE -->
|
|
852
859
|
## Quality Gate Result
|
|
@@ -860,7 +867,7 @@ QG_BODY="<!-- FORGE:QUALITY_GATE -->
|
|
|
860
867
|
gh issue comment {NUMBER} {GH_FLAG} --body "$QG_BODY" # allowlist:check-command-side-effects
|
|
861
868
|
```
|
|
862
869
|
|
|
863
|
-
**Marker-only re-gate mode** <!-- Added: forge#3983 -->: when `work-on/review.md` re-gates a merged head (Pre-Handoff Base Sync or stale re-review), it runs the scoped V1 gate on the committed head first, then sets `REGATE_MERGED_HEAD=1` and runs only this Step 1 with the real `GATE_RESULT` (FAIL stays FAIL). Steps 2-4 are skipped: there is nothing staged and no commit to make.
|
|
870
|
+
**Marker-only re-gate mode** <!-- Added: forge#3983 -->: when `work-on/review.md` re-gates a merged head (Pre-Handoff Base Sync or stale re-review), it runs the scoped V1 gate on the committed head first, then sets `REGATE_MERGED_HEAD=1` and runs only this Step 1 with the real `GATE_RESULT` (FAIL stays FAIL). Steps 2-4 are skipped: there is nothing staged and no commit to make. **Precondition**: a clean worktree (no uncommitted tracked changes, untracked files ignored); a dirty tracked file or staged change exits 1 with no marker, so a fix made after a FAIL must be committed and the scoped V1 gate re-run on the committed head before this step is re-run, keeping the gated tree equal to the marked `HEAD^{tree}`.
|
|
864
871
|
|
|
865
872
|
### V5 Step 2: Commit
|
|
866
873
|
|
|
@@ -205,7 +205,7 @@ On conflict, resolve every file, keep both sides' intent, `git add`, `git commit
|
|
|
205
205
|
**Re-gate the merged head** <!-- Added: forge#3983 -->: the `FORGE:QUALITY_GATE` PASS is bound to `HEAD^{tree}` (R1.5 `--head-tree`), so a merge that changes the tree invalidates it. After the merge and any conflict resolution, compare `PRE_SYNC_TREE` with the current `HEAD^{tree}` (fail closed if either cannot be resolved):
|
|
206
206
|
|
|
207
207
|
- Unchanged: do nothing; never re-gate.
|
|
208
|
-
- Changed: run the scoped quality gate on the merged head here (`work-on/build/validate` V1 loop over the PR's changed files), then post the marker via validate.md V5 Step 1 "Marker-only re-gate mode" (`**Tree**` = merged `HEAD^{tree}`, `**Commands run**` = what ran). Post the real result: on FAIL, fix and re-run within the validate bounds, else add `needs-human` and return `REVIEW_RESULT: status: BLOCKED`, blocker "post-sync quality gate failed"; never post PASS without a run.
|
|
208
|
+
- Changed: run the scoped quality gate on the merged head here (`work-on/build/validate` V1 loop over the PR's changed files), then post the marker via validate.md V5 Step 1 "Marker-only re-gate mode" (`**Tree**` = merged `HEAD^{tree}`, `**Commands run**` = what ran). Post the real result: on FAIL, fix, commit the fix (marker-only mode requires a clean worktree so the re-gated tree is the committed `HEAD^{tree}`), and re-run within the validate bounds, else add `needs-human` and return `REVIEW_RESULT: status: BLOCKED`, blocker "post-sync quality gate failed"; never post PASS without a run.
|
|
209
209
|
|
|
210
210
|
## Phase R1: Pre-Push Ancestry Guard
|
|
211
211
|
|
package/docs/CONFIG.md
CHANGED
|
@@ -481,6 +481,7 @@ engine:
|
|
|
481
481
|
| `<key>_model` | string | No | Model alias (`sonnet`, `opus`, `haiku`) or full model id passed as `--model`. Invalid values are ignored. |
|
|
482
482
|
| `<key>_max_turns` | integer | No | Positive integer passed as `--max-turns`. Invalid values are ignored. |
|
|
483
483
|
|
|
484
|
+
| `path` | string | No | `full` (default), `lean` or `auto`. The lean path (#4081, `bin/lean/`) runs validate, architect and implement in one warm builder session, deterministic gates, one independent reviewer and a no-model merge; `auto` uses it unless the risk tier is high and the diff is large. The `--path` flag overrides. `lean_reviewer_model` sets the reviewer model (default `claude-sonnet-5-5`); the builder uses `--model` or the CLI default. |
|
|
484
485
|
| `ci_pending_max_minutes` | integer | No | Minutes the engine waits for CI when the review phase ends BLOCKED with "checks still pending", or the remediate phase holds at its M7 re-gate on "CI pending" (the merge gate's wait window ended while CI was still running). Default: 90. Invalid values are ignored. This key is flat like the others but is not part of the `<key>_model` / `<key>_max_turns` pattern. |
|
|
485
486
|
|
|
486
487
|
**CI still pending**: the engine polls `scripts/wait-ci-green.sh` (no model calls) one read per poll on a 60 s, 2 min, 4 min, 5 min (cap) backoff with +/-20% jitter, while the lease keeps renewing, then re-dispatches the phase that was waiting, review or remediate (idempotent at an unchanged head: review re-runs the merge gate and merges; remediate resumes at its M7 re-gate). Before each poll it reads `gh api rate_limit` (not itself rate-limited) and, below 10% of the REST budget, sleeps until the window resets instead of polling; a rate-limited or transient (5xx, network) poll is retried and never re-dispatches the phase. Red CI takes the normal ci-gate path. When the budget is exhausted the run ends `needs-human` with outcome code `review.ci_pending_timeout`; `--retry` resumes. Each wait emits a `ci_pending_wait` telemetry event (`docs/TELEMETRY.md`) with the minutes waited, polls made and rate-limit sleeps.
|
|
@@ -493,6 +494,8 @@ engine:
|
|
|
493
494
|
|
|
494
495
|
**Per-run scratch dir** (`tmp_root`): every engine child runs with `TMPDIR` and `CLAUDE_CODE_TMPDIR` set to `<tmp_root>/<repo-key>/<issue>` so venvs and Claude session data stay off a size-limited `/tmp`. `engine.tmp_root` is an absolute or `~`-prefixed path; default `~/.forge/tmp`. The `<repo-key>` is the checkout's directory name plus 8 hex characters of a hash of its real path, so the same issue number in two repos never shares a dir; the dir is created `0700` and an existing symlink or dir owned by another user is not used (the child keeps the inherited tmp). Old `<tmp_root>/<issue>` dirs from earlier versions are not migrated. When `tmp_root` is unset and the operator already exported `TMPDIR` or `CLAUDE_CODE_TMPDIR`, that choice is respected and never cleaned. The dir is removed on a terminal outcome and kept on `engine-error` so `--retry` resumes. A disk-full/quota failure (EDQUOT, ENOSPC, errno -28/-122) ends a retryable `engine-error` (outcome code `infra.disk_quota`), never `needs-human`.
|
|
495
496
|
|
|
497
|
+
**Minimal phase sessions** (forge#4065): on a supported CLI (`claude --version` >= 2.1.296, probed once and cached like the flags above) every engine phase (`work-on/*`) runs with `--strict-mcp-config --mcp-config '{"mcpServers":{}}'`, so operator MCP servers and connectors are not loaded. Phases whose spec never calls `Skill(...)` (`work-on/investigate`, `work-on/close`) also get `--disable-slash-commands`; skill-using phases keep skills. Operator settings are intentionally not skipped (no `--setting-sources`), because ForgeDock's user-scope PreToolUse/SubagentStop hooks must keep running. Set `FORGEDOCK_FULL_SESSION=1` to restore the previous full session (no flags). Other `forgedock run` commands (qa-sweep, analytics, geo-audit, autopilot) keep the full session because they may need MCP servers. An old or unparseable CLI version also omits the flags.
|
|
498
|
+
|
|
496
499
|
**Commands that use this section**: CLI backend (`runCommand` in `bin/runner.mjs`)
|
|
497
500
|
|
|
498
501
|
---
|
package/docs/RELEASE-NOTES.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
Operator-facing notes for behavior changes that need action or awareness. Newest first.
|
|
4
4
|
|
|
5
|
+
## Lean path engine (#4081)
|
|
6
|
+
|
|
7
|
+
`forgedock run-issue N --lane L --path lean|full|auto` (or `engine.path` in forge.yaml; default `full`) runs validate, architect and implement in one warm, minimal builder session, then deterministic gates (a test-file change unless docs-only, ci-local), one independent Sonnet reviewer (plus a security reviewer for auth, crypto, secret, permission, migration, payment or workflow paths), then PR, remote CI wait, merge and close with no model. Gate failures and blocking findings (CRITICAL/HIGH, or CONFIRMED MEDIUM introduced) resume the same builder session with `--resume`, at most 2 rounds each, then `needs-human`; a blocking round is followed by one delta re-review. Non-blocking findings are listed in `FORGE:REVIEW` and never filed. Each stage posts `FORGE:VALIDATE`, `FORGE:PLAN`, `FORGE:IMPLEMENT`, `FORGE:REVIEW`, `FORGE:CLOSE`; telemetry is one `phase_attempt` per stage plus `run_terminal`. `auto` goes lean unless the risk tier is high and the diff is large, in which case it falls back to `full`. Slow CI is polled with backoff and ends `ci-pending` (resumable), never `needs-human`. See docs/CONFIG.md (`engine.path`).
|
|
8
|
+
|
|
9
|
+
Action: none; the default path is unchanged.
|
|
10
|
+
|
|
5
11
|
## v1.19.1: build-time self-review, ci-local under concurrency, review roster gates
|
|
6
12
|
|
|
7
13
|
- **Build-time self-review (B5.6, #3971).** After implement, the risk-tier review roster's domain agents review the staged diff, and the builder fixes CONFIRMED/LIKELY findings at MEDIUM or above in one repair round before validate. Small or docs-only diffs skip. Each build that runs it spends extra reviewer tokens, which the official review should save back through fewer findings and remediation rounds. Off switch: `FORGE_SELFREVIEW=off`. Wait budget: `SELFREVIEW_WAIT_SECS` (default 600, max 1800). Telemetry: `build_selfreview`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgedock",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "Autonomous development pipeline for coding agents. GitHub as institutional memory, with native Claude Code and OpenCode entrypoints.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"forgedock": "bin/forgedock.mjs"
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"README.md"
|
|
49
49
|
],
|
|
50
50
|
"scripts": {
|
|
51
|
-
"test": "bash scripts/with-run-tmpdir.sh node --test \"bin/tests/**/*.test.mjs\" \"bin/engine/**/*.test.mjs\" \"bin/stats/**/*.test.mjs\" \"packages/protocol/test/**/*.test.mjs\"",
|
|
51
|
+
"test": "bash scripts/with-run-tmpdir.sh node --test \"bin/tests/**/*.test.mjs\" \"bin/engine/**/*.test.mjs\" \"bin/stats/**/*.test.mjs\" \"bin/lean/**/*.test.mjs\" \"packages/protocol/test/**/*.test.mjs\"",
|
|
52
52
|
"test:sh": "bash scripts/run-shell-tests.sh",
|
|
53
53
|
"docs:dev": "vitepress dev docs/site",
|
|
54
54
|
"docs:build": "vitepress build docs/site",
|