forgedock 1.26.0 → 1.28.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 +4 -1
- package/bin/bootstrap.mjs +55 -0
- package/bin/engine/outcome-codes.mjs +13 -0
- package/bin/engine/phases.mjs +38 -2
- package/bin/engine/telemetry-seam.mjs +41 -6
- package/bin/engine/telemetry-seam.test.mjs +35 -0
- package/bin/engine-cli.mjs +616 -54
- package/bin/engine.mjs +13 -5
- package/bin/forgedock.mjs +169 -39
- package/bin/gc.mjs +580 -0
- package/bin/init-detect.mjs +68 -3
- package/bin/init-enrich.mjs +3 -0
- package/bin/init-repo.mjs +213 -0
- package/bin/journey.mjs +278 -43
- package/bin/lean/admission.mjs +33 -3
- package/bin/lean/batch.mjs +24 -5
- package/bin/lean/batch.test.mjs +54 -4
- package/bin/lean/checks.mjs +199 -0
- package/bin/lean/checks.test.mjs +183 -0
- package/bin/lean/conformance.mjs +235 -37
- package/bin/lean/conformance.test.mjs +143 -5
- package/bin/lean/credentials.mjs +29 -0
- package/bin/lean/depcache.mjs +312 -0
- package/bin/lean/depcache.test.mjs +298 -0
- package/bin/lean/drift.mjs +198 -0
- package/bin/lean/drift.test.mjs +223 -0
- package/bin/lean/effects.mjs +178 -16
- package/bin/lean/execution.e2e.test.mjs +81 -0
- package/bin/lean/execution.mjs +431 -0
- package/bin/lean/execution.test.mjs +583 -0
- package/bin/lean/heavy.mjs +289 -0
- package/bin/lean/heavy.test.mjs +207 -0
- package/bin/lean/lean.mjs +877 -151
- package/bin/lean/lean.test.mjs +1798 -28
- package/bin/lean/ledger.mjs +92 -0
- package/bin/lean/ledger.test.mjs +100 -0
- package/bin/lean/limits.mjs +58 -0
- package/bin/lean/limits.test.mjs +150 -0
- package/bin/lean/orchestrate.mjs +321 -19
- package/bin/lean/prompts.mjs +125 -17
- package/bin/lean/refactor.mjs +276 -0
- package/bin/lean/refactor.test.mjs +359 -0
- package/bin/lean/render.mjs +25 -6
- package/bin/lean/review-trigger.mjs +134 -0
- package/bin/lean/review-trigger.test.mjs +104 -0
- package/bin/lean/sandbox-provider.contract.test.mjs +257 -0
- package/bin/lean/sandbox-remote.mjs +142 -0
- package/bin/lean/sched.mjs +221 -0
- package/bin/lean/sched.test.mjs +310 -0
- package/bin/lean/schedd.mjs +247 -0
- package/bin/lean/schedd.test.mjs +180 -0
- package/bin/lean/toolchain.mjs +228 -0
- package/bin/lean/toolchain.test.mjs +154 -0
- package/bin/lean/verify.mjs +124 -0
- package/bin/lean/verify.test.mjs +118 -0
- package/bin/lean/worktree.mjs +69 -0
- package/bin/lean/worktree.test.mjs +141 -0
- package/bin/runner.mjs +53 -10
- package/bin/stats/github-source.mjs +1 -1
- package/bin/stats/index.mjs +14 -2
- package/bin/stats/query.mjs +72 -0
- package/bin/stats/stats.test.mjs +54 -0
- package/commands/adopt.md +3 -0
- package/commands/audit.md +5 -8
- package/commands/autopilot.md +2 -0
- package/commands/bootstrap.md +35 -0
- package/commands/cleanup.md +7 -22
- package/commands/forge-stats.md +2 -1
- package/commands/forgedock-init.md +125 -6
- package/commands/issue.md +89 -830
- package/commands/milestone.md +55 -6
- package/commands/orchestrate.md +10 -2
- package/commands/review-pr.md +2 -2
- package/commands/upgrade-deps.md +3 -3
- package/commands/work-on/build/validate.md +4 -0
- package/commands/work-on/close.md +2 -0
- package/commands/work-on/decompose.md +24 -6
- package/docs/CI.md +3 -3
- package/docs/CONFIG.md +227 -3
- package/docs/OUTCOME-CODES.md +11 -0
- package/docs/TELEMETRY.md +1 -1
- package/docs/site/command-reference.md +22 -0
- package/docs/site/getting-started.md +2 -0
- package/docs/site/start-a-new-project.md +327 -0
- package/package.json +1 -1
- package/scripts/affected-files-check.sh +91 -0
- package/scripts/affected-files-check.test.sh +80 -0
- package/scripts/bootstrap-create.mjs +393 -0
- package/scripts/bootstrap-validate.mjs +321 -0
- package/scripts/calibration.mjs +116 -0
- package/scripts/check-devdocs-freshness.sh +125 -0
- package/scripts/check-devdocs-freshness.test.sh +130 -0
- package/scripts/check-spec-markers.sh +3 -0
- package/scripts/check-spec-size.sh +29 -11
- package/scripts/check-spec-size.test.sh +27 -4
- package/scripts/ci-local/lib.mjs +8 -1
- package/scripts/ci-local/parallelism.test.mjs +27 -0
- package/scripts/ci-local/run.mjs +16 -5
- package/scripts/ci-local/scoping.mjs +82 -0
- package/scripts/ci-local.test.sh +14 -2
- package/scripts/fixtures/symbol-index/other/sample.rb +3 -0
- package/scripts/fixtures/symbol-index/py/pkg/__init__.py +1 -0
- package/scripts/fixtures/symbol-index/py/pkg/a.py +5 -0
- package/scripts/fixtures/symbol-index/py/pkg/b.py +14 -0
- package/scripts/fixtures/symbol-index/py/pkg/c.py +1 -0
- package/scripts/fixtures/symbol-index/py/sample.py +41 -0
- package/scripts/fixtures/symbol-index/ts/sample.ts +13 -0
- package/scripts/fixtures/symbol-move/gen_big.py +201 -0
- package/scripts/fixtures/symbol-move-ts/gen_big.mjs +56 -0
- package/scripts/forge-root.test.sh +1 -1
- package/scripts/hotspots.sh +100 -0
- package/scripts/hotspots.test.sh +101 -0
- package/scripts/issue-create.sh +197 -0
- package/scripts/issue-create.test.sh +166 -0
- package/scripts/maintenance-scan.sh +185 -0
- package/scripts/maintenance-scan.test.sh +89 -0
- package/scripts/milestone-drift.sh +206 -0
- package/scripts/milestone-drift.test.sh +120 -0
- package/scripts/northstar-docs.test.sh +68 -0
- package/scripts/refactor-verify.sh +368 -0
- package/scripts/refactor-verify.test.sh +202 -0
- package/scripts/review-prepare.sh +11 -9
- package/scripts/review-prepare.test.sh +14 -0
- package/scripts/structure-delta.sh +155 -0
- package/scripts/structure-delta.test.sh +136 -0
- package/scripts/symbol-index.mjs +253 -0
- package/scripts/symbol-index.py +354 -0
- package/scripts/symbol-index.test.sh +119 -0
- package/scripts/symbol-move-ts.test.sh +161 -0
- package/scripts/symbol-move.mjs +379 -0
- package/scripts/symbol-move.py +383 -0
- package/scripts/symbol-move.test.sh +168 -0
- package/templates/bootstrap/interview.md +34 -0
- package/templates/bootstrap/issue-body.md +42 -0
- package/templates/bootstrap/needs-human-issue.md +45 -0
- package/templates/bootstrap/stack-profiles.md +34 -0
- package/templates/bootstrap/technical-spike.md +75 -0
package/docs/CONFIG.md
CHANGED
|
@@ -49,12 +49,15 @@ If you have both a local `claude` CLI and `ANTHROPIC_API_KEY` set and want to pi
|
|
|
49
49
|
| [`repos`](#repos-optional) | No | Multi-repository routing |
|
|
50
50
|
| [`project_board`](#project_board-optional) | No | GitHub Projects v2 integration |
|
|
51
51
|
| [`orchestration`](#orchestration-optional) | No | `/orchestrate` concurrency and cascade admission policy |
|
|
52
|
+
| [`hygiene`](#hygiene-optional) | No | Automatic reclaim of ForgeDock-owned leftovers: TTLs, branch deletion, near-ceiling GC, `auto_gc` |
|
|
52
53
|
| [`pipeline`](#pipeline-optional) | No | `/orchestrate` batch-engine tuning (stall detection, token budget, narration) |
|
|
53
54
|
| [`build`](#build-optional) | No | `/work-on` build-phase scope check (`build.scope.*`) and review depth (`review.depth.*`) |
|
|
54
55
|
| [`services`](#services-optional) | No | External service URLs and IDs |
|
|
55
56
|
| [`review`](#review-optional) | No | Context injected into review agents |
|
|
56
57
|
| [`devdocs`](#devdocs-optional) | No | Devdocs knowledge tree path |
|
|
57
58
|
| [`verification`](#verification-optional) | No | Health-check patterns |
|
|
59
|
+
| [`execution`](#execution-optional) | No | `forgedock orchestrate`: run lanes in sandboxes instead of host processes (`mode: local\|remote\|hybrid`, `max_remote`) |
|
|
60
|
+
| [`verify`](#verify-optional) | No | Core work-on: run lane verification on CI runners instead of the host (`mode: local\|ci\|hybrid`) |
|
|
58
61
|
| [`billing`](#billing-optional) | No | Enable financial integrity audit phase |
|
|
59
62
|
| [`adaptive_scripts`](#adaptive_scripts-optional) | No | Per-repo script override configuration |
|
|
60
63
|
|
|
@@ -227,6 +230,12 @@ repos:
|
|
|
227
230
|
|
|
228
231
|
GitHub Projects v2 integration. When configured, ForgeDock automatically adds issues to the project board and updates Status, Lane, Component, Priority, and Workflow fields as issues progress through the pipeline.
|
|
229
232
|
|
|
233
|
+
`/forgedock-init` fills this section for you: it links an existing board or creates a new one, links it to the repo, adds any missing Lane/Priority/Workflow/Component fields (Status is built in), and writes the IDs below. Projects v2 needs the `read:project`/`project` token scope, which `gh auth login` does not grant by default. If the scope is missing, `/forgedock-init` prints the fix:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
gh auth refresh -h github.com -s read:project,project
|
|
237
|
+
```
|
|
238
|
+
|
|
230
239
|
```yaml
|
|
231
240
|
project_board:
|
|
232
241
|
owner: "acme-org"
|
|
@@ -399,7 +408,8 @@ orchestration:
|
|
|
399
408
|
|-------|------|----------|-------------|
|
|
400
409
|
| `max_concurrent` | integer | No | Max concurrent top-level `/work-on` dispatches. It does not bound cascade growth; under `policy: all` it raises drain and spawn throughput together. Default: 12. |
|
|
401
410
|
| `breaker.errors` / `breaker.window_min` | integer | No | `forgedock orchestrate` circuit breaker (#4121): after `errors` engine-errors within `window_min` minutes the batch stops admitting lanes, lets running lanes finish, and keeps the rest pending for `--resume`. `errors: 0` turns it off. Default: 3 in 30 |
|
|
402
|
-
| `max_deferrals` | integer | No | `forgedock orchestrate`: requeues of one issue after a non-host `deferred` (usage limit, merge/push transient, CLI idle, lease held) before its row is final. `host.busy` deferrals are not capped. Default: 3 |
|
|
411
|
+
| `max_deferrals` | integer | No | `forgedock orchestrate`: requeues of one issue after a non-host `deferred` (usage limit, merge/push transient, CLI idle, lease held, gh unavailable) before its row is final. `host.busy` deferrals are not capped. Default: 3 |
|
|
412
|
+
| `global_parallel` | integer | No | `forgedock orchestrate` (#4191): host-wide cap on running lanes across every batch and repo on the host, enforced through the host lane registry (`~/.forge/sched/`, or `FORGEDOCK_SCHED_DIR`). A freed slot goes to the highest-priority queued issue (`priority:P<n>` / `P<n>` labels, unlabelled P2); within a priority, slots are shared across repos by weight (#4193: `forgedock sched weight <owner/repo> N`, `--clear` resets; default equal, FIFO inside a repo), and priority always dominates weights; running lanes are never preempted and `--parallel` K still bounds each batch. `forgedock sched cap N` overrides it live for the whole host (`--clear` reverts); `forgedock sched show` lists the weights, slots and the queue. Default: unset (no global cap) |
|
|
403
413
|
| `cascade.policy` | string | No | `all` \| `balanced` \| `conservative`. Default: `balanced` |
|
|
404
414
|
| `cascade.max_generation` | integer or `"unlimited"` | No | Max cascade generation admitted at Phase 1 resolve time. Default: 1 |
|
|
405
415
|
| `cascade.batch_max_generation` | integer | No | Finite maximum member generation that an automated P3 batch may aggregate. Default: 2 |
|
|
@@ -422,6 +432,37 @@ orchestration:
|
|
|
422
432
|
|
|
423
433
|
---
|
|
424
434
|
|
|
435
|
+
## `hygiene` (OPTIONAL)
|
|
436
|
+
|
|
437
|
+
Automatic reclaim of what ForgeDock leaves behind (#4183), using the `forgedock gc` reaper. Only resources ForgeDock provably owns are reclaimed: entries of a stale lane ledger, Docker containers and volumes labelled `forgedock.owner=1`, `lean-*`/`agent-*` dirs under `.claude/worktrees`, `feat/issue-<n>-lean` branches of closed issues, and ForgeDock scratch under `~/.forge/tmp`. Everything else is reported, never touched, and so are unpushed or uncommitted work, live lanes, protected branches and branches with an open PR. Block form only.
|
|
438
|
+
|
|
439
|
+
```yaml
|
|
440
|
+
hygiene:
|
|
441
|
+
container_ttl_hours: 6
|
|
442
|
+
worktree_ttl_hours: 24
|
|
443
|
+
tmp_ttl_hours: 6
|
|
444
|
+
delete_merged_branches: true
|
|
445
|
+
max_host_load_ratio: 0.8
|
|
446
|
+
auto_gc: true
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
| Field | Type | Default | Description |
|
|
450
|
+
|-------|------|---------|-------------|
|
|
451
|
+
| `container_ttl_hours` | number | `6` | Age after which owned Docker containers and volumes are reclaimed (an expired `forgedock.expires` label reclaims earlier). |
|
|
452
|
+
| `worktree_ttl_hours` | number | `24` | Age after which owned worktrees (clean and pushed) and abandoned lane branches are reclaimed. |
|
|
453
|
+
| `tmp_ttl_hours` | number | `6` | Age after which `~/.forge/tmp` run dirs (owner dead) and runner scratch (no live process) are reclaimed. |
|
|
454
|
+
| `delete_merged_branches` | boolean | `true` | `false`: lane branches of closed issues are reported, never deleted. |
|
|
455
|
+
| `max_host_load_ratio` | number in (0,1] | `0.8` | Near-ceiling trigger relative to the `engine` admission thresholds: load/core at or above ratio × `max_load_per_core`, or free memory/tmp at or below the minimum ÷ ratio. |
|
|
456
|
+
| `auto_gc` | boolean | `true` | `false` disables GC in `forgedock orchestrate`; `forgedock gc` and `forgedock doctor --fix` still work. |
|
|
457
|
+
|
|
458
|
+
Invalid values fall back to the default. `forgedock gc --older-than <d>` overrides every TTL for that run. Resources of a live lane (its ledger owner is alive) are always skipped.
|
|
459
|
+
|
|
460
|
+
**Where it runs**: `forgedock orchestrate` applies GC once before admitting lanes, and again before an admission when the host is near the ceiling (at most every 5 minutes). The batch report ends with `reclaimed: N item(s), SIZE (preflight A, admissions B)`, or `reclaimed: off (hygiene.auto_gc: false)`. `forgedock doctor` shows a **Resource hygiene** check (owned counts and sizes, reclaimable count, unowned stale items as warnings), and `doctor --fix` runs the reaper. `/cleanup branches` calls `forgedock gc`.
|
|
461
|
+
|
|
462
|
+
**Commands that use this section**: `orchestrate`, `doctor`, `gc`, `cleanup`
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
425
466
|
## `pipeline` (OPTIONAL)
|
|
426
467
|
|
|
427
468
|
Tuning knobs for the CLI backend and `/orchestrate`'s batch engine — invocation and stall timeouts, narration verbosity, and (deprecated alias only, see `orchestration.cascade` above) the review-finding cascade token budget. All keys optional; sane defaults apply when omitted.
|
|
@@ -464,6 +505,27 @@ pipeline:
|
|
|
464
505
|
|
|
465
506
|
---
|
|
466
507
|
|
|
508
|
+
## `execution` (OPTIONAL)
|
|
509
|
+
|
|
510
|
+
Owner opt-in for the paid API agent backend (#4257). The API backend is **never the default**: under `--backend auto` (the default) ForgeDock uses the `claude` CLI, and when no CLI is available it fails with `CLI_BACKEND_UNAVAILABLE` instead of billing `ANTHROPIC_API_KEY`.
|
|
511
|
+
|
|
512
|
+
```yaml
|
|
513
|
+
execution:
|
|
514
|
+
# Use the API backend when `auto` finds no usable claude CLI. Takes effect only
|
|
515
|
+
# together with the env var FORGEDOCK_ALLOW_API_BACKEND=1 on the host.
|
|
516
|
+
agent_backend: api
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
| Field | Type | Required | Description |
|
|
520
|
+
|-------|------|----------|-------------|
|
|
521
|
+
| `agent_backend` | string | No | `api` approves the API backend as the `auto` fallback, only when `FORGEDOCK_ALLOW_API_BACKEND=1` is also set. Any other value, or no value, means `auto` never selects `api`. |
|
|
522
|
+
|
|
523
|
+
An explicit `--backend api` / `FORGEDOCK_BACKEND=api` still selects the API backend without this section; that flag is itself the explicit choice.
|
|
524
|
+
|
|
525
|
+
**Commands that use this section**: `forgedock run`, `forgedock run-issue`, `forgedock backend-check`
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
467
529
|
## `engine` (OPTIONAL)
|
|
468
530
|
|
|
469
531
|
Per-command `--model` and `--max-turns` for the CLI backend (`forgedock run-issue` phases and `forgedock run <command>`). Opt-in: with no keys set, the `claude` invocation is unchanged and uses the CLI's own default model with no turn bound. `forge.yaml` is parsed one level deep, so settings are **flat** keys under `engine:`, named `<key>_model` and `<key>_max_turns`, where `<key>` is the command name with every non-alphanumeric character replaced by `_`.
|
|
@@ -484,12 +546,12 @@ engine:
|
|
|
484
546
|
| `<key>_max_turns` | integer | No | Positive integer passed as `--max-turns`. Invalid values are ignored. |
|
|
485
547
|
|
|
486
548
|
| `path` | string | No | `core` (default), `full`, `lean` or `auto`. `core` is the lean work-on (`bin/lean/`): a planning call that writes a frozen contract, an implement call in the same session, one read-only conformance reviewer, GitHub as the register (see `conformance`). `lean` and `auto` are aliases of `core`; `full` runs the legacy multi-phase pipeline and is required for `--retry` and `--wait-lease`. The `--path` flag overrides. `lean_reviewer_model` sets the reviewer model (default `claude-sonnet-5-5`); the builder uses `--model` or the CLI default. `forgedock orchestrate <issues...> [--parallel K] [--lane L]` runs the core per issue (K default 3, lane default `staging`) and honours `**Depends on**` ordering among the given issues. |
|
|
487
|
-
| `conformance` | string | No | Core work-on conformance gate. The builder first writes a contract (requirements quoted verbatim from the issue and maintainers' comments, deliverables, preserve constraints, validate commands), which the engine freezes and posts as `FORGE:CONTRACT`; changes go only through typed amendments (`FORGE:AMENDMENT`). A deterministic precheck (scope, validation, deferrals) and a row-based conformance review follow, and the engine computes the verdict. `shadow` (default): the verdict is posted in `FORGE:REVIEW` and logged (`CONFORMANCE_SHADOW` event) but only what the previous reviewer blocked on still blocks (a requirement found not implemented, a regression); deferrals are
|
|
549
|
+
| `conformance` | string | No | Core work-on conformance gate. The builder first writes a contract (requirements quoted verbatim from the issue and maintainers' comments, deliverables, preserve constraints, validate commands), which the engine freezes and posts as `FORGE:CONTRACT`; changes go only through typed amendments (`FORGE:AMENDMENT`). A deterministic precheck (scope, validation, deferrals) and a row-based conformance review follow, and the engine computes the verdict. `shadow` (default): the verdict is posted in `FORGE:REVIEW` and logged (`CONFORMANCE_SHADOW` event) but only what the previous reviewer blocked on still blocks (a requirement found not implemented, a regression), plus the precheck's equivalent: any `conformance.unimplemented` precheck or amendment failure (a validate command that failed or has no recorded result, a deliver requirement the builder reported as not implemented, an invalid amendment; #4160). These block before any reviewer run. Scope creep and omissions are recorded only. A review still incomplete or inconsistent after one targeted re-ask (only the missing ids) ends `needs-human`, or with `review_incomplete: degraded` merges with a `review-degraded` PR label and a tracked follow-up. Accepted deferrals are filed and linked, as in enforce. `scripts/calibration.mjs --shadow` reports the per-repo shadow verdict mix and when to switch to enforce. `enforce`: every failure blocks (one same-session builder resume, then `needs-human` with a `conformance.*`/`review.*` code, see docs/OUTCOME-CODES.md); deferrals the reviewer accepts are filed in one batched follow-up issue (`scripts/file-deferred-followups.sh`) and linked in the PR body and a `FORGE:TRACKED` comment before merge. |
|
|
488
550
|
| `cli_idle_minutes` | number | No | Core builder idle timeout (#4112): the builder CLI runs with `--output-format stream-json` and is killed (code `cli.idle`) when it shows no stream activity for this many minutes. The run log records the last tool call and the live child processes. Default 15. |
|
|
489
551
|
| `cli_max_minutes` | number | No | Absolute ceiling for a core builder session (code `cli.timeout`), replacing the fixed 60-minute kill. A session that keeps making progress is never idle-killed. Default 180. On either kill, uncommitted work is committed as `wip(forge): checkpoint after <reason>` and the lane branch is pushed (with the push retry), the builder retries once from the checkpoint, and a second kill ends `deferred`; a rerun resumes from the branch. |
|
|
490
552
|
| `lane_limits` | map | No | Cap each core lane's whole process tree (#4115), under `pipeline:` or `engine:`: `memory_max` (for example `3G`) and `cpu_quota` (for example `150%`). When set and `systemd-run --user --scope` works (probed once), the builder CLI is spawned as `systemd-run --user --scope -q -p MemoryMax=... -p CPUQuota=... -- claude ...`; elsewhere the caps are ignored. Write it nested or flat (`lane_limits_memory_max`, `lane_limits_cpu_quota`). While a builder runs the engine polls host load and memory every 15 s against the `admission` thresholds: over them it SIGSTOPs the lane's process group, under them SIGCONT, and never pauses longer than 10 minutes. |
|
|
491
553
|
| `admission` | map | No | Host-resource admission for the core work-on (#4105), checked before the lease, worktree or any GitHub call. When a check fails the run ends `deferred` (code `host.busy`, exit 75) with a last line such as `issue #N → deferred (host busy: mem 1.2G < 2G; retry in 60s)`; no lease is taken and nothing is posted. Keys: `enabled` (default `true`; `false` disables admission), `min_free_mem_mb` (default 2048; `MemAvailable` from `/proc/meminfo`, else `os.freemem()`), `min_free_tmp_mb` (default 1024; free space of the run TMPDIR filesystem, `~/.forge/tmp`, and of `/tmp`, via `statfs`), `max_load_per_core` (default 4; 1-minute load average divided by cores). Write it nested (`engine:` then `admission:` with the keys indented) or flat as `admission_enabled`, `admission_min_free_mem_mb`, `admission_min_free_tmp_mb`, `admission_max_load_per_core`; the parser reads one nested level into `engine`. `/orchestrate` requeues a host-busy lane and lowers K by 1 for 5 minutes. |
|
|
492
|
-
| `merge_wait` | string | No | Core work-on merge gate. `required` (default): wait only for the status checks the base branch requires
|
|
554
|
+
| `merge_wait` | string | No | Core work-on merge gate. `required` (default): wait only for the status checks the base branch requires: the union of classic branch protection (`gh api repos/{o}/{r}/branches/{base}/protection/required_status_checks`, contexts and checks) and repository rulesets (`gh api repos/{o}/{r}/rules/branches/{base}`, `required_status_checks` rules); with neither (404 or empty) merge right after review PASS. A merge rejected by base branch policy (PR `MERGEABLE` but `BLOCKED`) is waited on and retried within the CI budget, then left `ci-pending`; it is never base-synced and never `base.conflict`. A failing required check is `needs-human`; a pending one is polled with backoff and never `needs-human`. `all`: wait for every check (`scripts/wait-ci-green.sh`). `none`: merge right after review. Always `--match-head-commit` at the reviewed head. The policy used is recorded in the `FORGE:WORK-ON` comment. |
|
|
493
555
|
| `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. |
|
|
494
556
|
|
|
495
557
|
**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.
|
|
@@ -508,6 +570,162 @@ engine:
|
|
|
508
570
|
|
|
509
571
|
---
|
|
510
572
|
|
|
573
|
+
## `lean` (OPTIONAL)
|
|
574
|
+
|
|
575
|
+
Settings of the core work-on (`bin/lean/`) that change its contract.
|
|
576
|
+
|
|
577
|
+
```yaml
|
|
578
|
+
lean:
|
|
579
|
+
repro_test: prefer # off | prefer (default)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
| Field | Type | Required | Description |
|
|
583
|
+
|-------|------|----------|-------------|
|
|
584
|
+
| `repro_test` | string | No | `prefer` (default): on an issue labelled `bug` or `type:bug`, the plan step must freeze a `repro_test` in the contract (`{"path","id","command"}`) when the bug is testable. Before review, the engine runs `scripts/acceptance-red-green.sh` for that command on the host in every verify mode: one targeted test, red on the base (head's changed test files overlaid) and green on head. Any other result, including a test that could not run, fails the precheck as `conformance.unimplemented` (it blocks in shadow too) and goes back to the builder for one in-session fix round. A bug that is not testable (specs, docs, workflows, environment) records `"repro_test":"none"` with a one-line `repro_test_reason` and the lane continues as before. `off` gives exactly the previous contract and precheck. Each proof emits a `REPRO_RED_GREEN` run-log event (`base`, `head`) and adds a trajectory decision. |
|
|
585
|
+
|
|
586
|
+
## `verify` (OPTIONAL)
|
|
587
|
+
|
|
588
|
+
Verify-by-CI for the core work-on path (forge#4167). It moves a lane's heavy verification (type-checks, test suites, docker test containers) off the host and onto CI runners, and it catches CI-class failures (lock files, suite wiring, hangs, formatting) before the lane merges into staging. Write it as a block-style section: `parseForgeYaml` does not read inline `{ ... }` maps.
|
|
589
|
+
|
|
590
|
+
```yaml
|
|
591
|
+
verify:
|
|
592
|
+
mode: ci # local (default) | ci | hybrid
|
|
593
|
+
workflow: forgedock-verify.yml # optional; needs an `on: workflow_dispatch` trigger
|
|
594
|
+
paths_input: changed_paths # optional workflow_dispatch input that receives the changed paths (newline-separated)
|
|
595
|
+
skip_paths: "*.md,docs/**" # optional, comma-separated; default *.md,*.mdx,*.rst,*.txt,docs/**,devdocs/**
|
|
596
|
+
max_rounds: 2 # optional CI fix rounds before needs-human (default 2)
|
|
597
|
+
precheck_timeout_seconds: 300 # optional per-command timeout of the declared pre-PR checks (default 300)
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
| Key | Type | Required | Description |
|
|
601
|
+
|-----|------|----------|-------------|
|
|
602
|
+
| `mode` | string | No | `local` (default): today's behaviour; the builder runs every validate command on the host and no CI is started before review. `ci`: the builder runs only formatters and linters on the files it changed (no type-checks, test suites or test containers) and records validate ids it left to CI as `{"id","command","ci":true}`; the engine verifies on CI. `hybrid`: the builder runs the targeted validate commands too, and CI also verifies. |
|
|
603
|
+
| `workflow` | string | No | Workflow file name or id dispatched on the lane branch (`gh workflow run <workflow> --ref <branch>`). The engine waits for the `workflow_dispatch` run of the lane's head commit. Without it, the engine waits on the head commit's push-triggered check runs. |
|
|
604
|
+
| `paths_input` | string | No | Name of a `workflow_dispatch` input that receives the changed paths, so the workflow can scope its jobs to the change. |
|
|
605
|
+
| `skip_paths` | string | No | Comma-separated globs. A glob without `/` matches the basename anywhere; `**` spans directories. When every changed file matches, the lane skips CI verification (a docs-only lane runs no CI matrix). |
|
|
606
|
+
| `max_rounds` | integer | No | Builder fix rounds for red CI (0–10, default 2). Also bounds the merge-time fix rounds and the declared-check fix rounds below, in every mode. |
|
|
607
|
+
| `precheck_timeout_seconds` | integer | No | Timeout of each declared check command (1–3600, default 300); a timed-out command counts as failed (exit 124). |
|
|
608
|
+
|
|
609
|
+
**Flow** (`ci`/`hybrid`, `bin/lean/verify.mjs` `runCiVerify`):
|
|
610
|
+
|
|
611
|
+
1. After the build and before every conformance review, the engine pushes the lane branch (a plain push, never forced) and runs CI on the head. A base-sync that moves the head is verified again.
|
|
612
|
+
2. Green (or skipped by `skip_paths`): the review runs. The precheck accepts validate ids left to CI for that head, but a recorded non-zero exit still fails.
|
|
613
|
+
3. Red: the failed jobs' logs (`gh run view --log-failed`, tails capped at about 12 KB) go back to the builder session. The builder fixes and commits, and the engine pushes and verifies again. Still red after `max_rounds`, or a fix round with no new commit, ends as `needs-human` (`ci.red`) and nothing merges.
|
|
614
|
+
4. Slow CI: once the core CI wait budget (360 min, the same budget the merge wait uses) is used up, the lane ends `ci-pending` and can be resumed. Slow CI is never `needs-human`.
|
|
615
|
+
5. CI unavailable (dispatch refused, or no run or check for the head within 5 minutes): one local fallback. The builder runs every validate command on the host and records real exit codes.
|
|
616
|
+
|
|
617
|
+
Each verification emits a `CI_VERIFY` run-log/telemetry event (`mode`, `round`, `result`, `skipped`, `run_url`) and adds a **Verify** row to the trajectory.
|
|
618
|
+
|
|
619
|
+
**Every mode, including the default `local` (forge#4240):**
|
|
620
|
+
|
|
621
|
+
- **Declared check commands before the PR.** Each `learned.test_commands` entry (a block list under `learned:`) runs with `bash -c` in the lane worktree after the build commit, before review and before the PR, with the per-command timeout above. These run on the host and use no Actions minutes. If they all pass, there is no extra builder turn. A failure resumes the builder in its session with the command, its exit code and the output tail, then the commands run again, up to `max_rounds`. A round with no new commit, or exhausted rounds, is recorded as an anomaly and the lane continues, because CI stays the gate. The engine has no built-in command list: to run `scripts/ci-local.sh`, list it here. Each round emits a `PRECHECK_COMMANDS` run-log event (`round`, `results: [{command, rc}]`).
|
|
622
|
+
- **Fix rounds at merge.** When a required check fails on the PR (`engine.merge_wait: required`), the failed jobs' logs (`gh run view --log-failed`, capped) go back to the builder session. The fixed head is re-reviewed and pushed, then the wait starts again. Still red after `max_rounds` (one budget shared with the `ci`/`hybrid` rounds above), or a round with no new commit, ends `needs-human` (`ci.red`). `max_rounds: 0` ends `ci.red` on the first failure. Each round emits a `CI_FIX_ROUND` run-log event (`trigger`, `round`, `jobs`). `merge_wait: all` is unchanged.
|
|
623
|
+
|
|
624
|
+
**Cost:** `ci`/`hybrid` spend GitHub Actions minutes before review, so they are opt-in per repo. On the green path they add no LLM calls; each red CI round adds one builder resume. The merge gate (`engine.merge_wait`) is unchanged.
|
|
625
|
+
|
|
626
|
+
**Commands that use this section**: core work-on (`forgedock run-issue`, `forgedock orchestrate`; `bin/engine-cli.mjs` `readVerify`)
|
|
627
|
+
|
|
628
|
+
## `execution` (OPTIONAL)
|
|
629
|
+
|
|
630
|
+
Where `forgedock orchestrate` runs its lanes (forge#4253). Block style only.
|
|
631
|
+
|
|
632
|
+
```yaml
|
|
633
|
+
execution:
|
|
634
|
+
mode: remote # local (default) | remote | hybrid
|
|
635
|
+
max_remote: 4 # remote lanes running at once (default 0 = remote off)
|
|
636
|
+
provider: container # default container when mode is not local
|
|
637
|
+
image: my/lane-image:1 # container provider: an image with git and forgedock; never pulled
|
|
638
|
+
secrets: GH_TOKEN,ANTHROPIC_API_KEY # env var NAMES forwarded to the sandbox; nothing else is
|
|
639
|
+
# provider: remote (forge#4254), only when named explicitly:
|
|
640
|
+
# endpoint: https://runner-pool.example/v1 # sandbox HTTP API (protocol below)
|
|
641
|
+
# token_env: FORGEDOCK_SANDBOX_TOKEN # credential NAME: read from the env, else the OS keychain
|
|
642
|
+
# cache_store: s3://lane-cache # shared cache store the sandbox mounts
|
|
643
|
+
# allow_actions: false # true only to accept a GitHub Actions endpoint
|
|
644
|
+
# execution budget (forge#4255): flat keys, forge.yaml is parsed one level deep
|
|
645
|
+
budget_minutes: 600 # hard cap on sandbox-minutes per batch
|
|
646
|
+
budget_usd: 25 # hard cap on estimated sandbox USD per batch
|
|
647
|
+
day_budget_minutes: 2000 # hard cap on sandbox-minutes per host UTC day (all batches, schedd or not)
|
|
648
|
+
day_budget_usd: 80 # hard cap on estimated sandbox USD per host UTC day
|
|
649
|
+
on_budget: stop # stop (default) | local: what the queue does once a cap is reached
|
|
650
|
+
cost_per_minute: 0.04 # USD per sandbox-minute when the provider reports no cost (default 0)
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
| Key | Type | Required | Description |
|
|
654
|
+
|-----|------|----------|-------------|
|
|
655
|
+
| `mode` | string | No | `local` (default): lanes are detached host processes, as before. `remote`: lanes run in sandboxes of `provider`. `hybrid`: each lane is placed from facts (forge#4256, below). |
|
|
656
|
+
| `max_remote` | integer | No | Remote lanes of the batch that may run at once. They take no slot of the host lane cap (`orchestration.global_parallel`, `forgedock sched cap`); the batch `--parallel` still applies. `0` (default) keeps lanes local. |
|
|
657
|
+
| `provider` | string | No | `container` (reference: an ephemeral local container), `remote` (a runner pool or remote session over HTTP, forge#4254) or `local`. The default is `container` off `local` mode; `remote` is used only when named. Unknown names fail the batch start. |
|
|
658
|
+
| `image` | string | No | Container provider image; it must provide `git` and `forgedock`. Started with `--pull never`. |
|
|
659
|
+
| `secrets` | string | No | Comma-separated env var names copied from the dispatcher's env into the sandbox. The sandbox also gets `GH_REPO` (the target repo) and `FORGEDOCK_BATCH_ID`, and nothing else from the host. |
|
|
660
|
+
| `endpoint` | string | `remote` | Base URL of the sandbox API. A GitHub Actions API (`api.github.com`, or any `/actions/` path) is refused unless `allow_actions: true`, so pre-review Actions minutes are never spent implicitly. Missing: the batch start fails (`execution.provider.misconfigured`). |
|
|
661
|
+
| `token_env` | string | No | Name of the API credential, sent as `Authorization: Bearer`. Read from the env, else the OS keychain (service `forgedock`, account = the name: `secret-tool store --label forgedock service forgedock account NAME` on Linux, `security add-generic-password -s forgedock -a NAME -w` on macOS). Never written to the ledger, the batch state, logs or GitHub. |
|
|
662
|
+
| `cache_store` | string | No | Shared cache store the remote sandbox mounts (sent as `cache.store` with `cache.key` = the repo). |
|
|
663
|
+
| `allow_actions` | boolean | No | `true` accepts a GitHub Actions endpoint. Default `false`. |
|
|
664
|
+
| `budget_minutes`, `budget_usd` | number | No | The `execution.budget` per batch: once the batch's sandbox minutes / estimated USD (settled lanes plus the running ones' elapsed time) reach the cap, no new remote lane is admitted. Missing, non-numeric or negative means no cap. |
|
|
665
|
+
| `day_budget_minutes`, `day_budget_usd` | number | No | The same cap per host UTC day, summed over every batch on the host from the host-day store (`execution-budget.json` in `~/.forge/sched/`, or `FORGEDOCK_EXECUTION_BUDGET_FILE`). A corrupt or unreadable store refuses remote lanes while a day cap is set; remove the file to reset it. |
|
|
666
|
+
| `on_budget` | string | No | `stop` (default): running lanes finish and the rest end `held` (`execution budget: ...`) for a later `--resume`. `local`: the rest run as detached host lanes. |
|
|
667
|
+
| `cost_per_minute` | number | No | USD per sandbox-minute, used when the provider reports no cost of its own (the `container` provider never does). Default `0`. |
|
|
668
|
+
|
|
669
|
+
**Lifecycle:** each sandbox is listed in the lane ledger (`~/.forge/ledger/<batch>/sandbox-<n>.json`, kind `sandbox`) before it starts and removed from it only after teardown succeeded: on success, failure, a lane that exits without a result, and adopt after `--resume`. A sandbox left by a killed dispatcher is reaped by `forgedock gc --apply` (scope `--sandboxes`). The container provider mounts the checkout's git dir read-only at `/src` (cloned to the container's own `/work`), `~/.forge/verify-cache` read-only at `/cache`, and the batch `lanes/` dir at `/result`.
|
|
670
|
+
|
|
671
|
+
**Remote provider** (`bin/lean/sandbox-remote.mjs`, forge#4254). The sandbox is addressed by the id ForgeDock picks (`fdrsbx-<batch>-<issue>`), JSON over HTTP:
|
|
672
|
+
|
|
673
|
+
| Request | Response |
|
|
674
|
+
|---------|----------|
|
|
675
|
+
| `POST /sandboxes` | Launch spec `{id, issue, batch, image, repo: {slug, url, ref}, cache: {store, key} \| null, env, command, result_file}`. The sandbox clones `repo.url` at `repo.ref`, mounts the cache, runs `command` (`forgedock run-issue ...`) with exactly `env` (the `secrets` names, resolved from the env or keychain, plus `GH_REPO` and `FORGEDOCK_BATCH_ID`). `429`/`503` = no capacity (`Retry-After` honoured). |
|
|
676
|
+
| `GET /sandboxes/:id` | `{state}`: `pending`/`starting`/`running`, `exited`, or `404` = gone |
|
|
677
|
+
| `GET /sandboxes/:id/logs?cursor=N` | `{text, cursor}`: lane log since `cursor`; appended to the batch lane log with secret values redacted |
|
|
678
|
+
| `GET /sandboxes/:id/result` | The lane's result file (`404` until written) |
|
|
679
|
+
| `GET /sandboxes/:id/usage` | `{minutes}`: billable sandbox minutes, set on the handle and the lane result (`sandbox.minutes`); without it, wall-clock minutes |
|
|
680
|
+
| `DELETE /sandboxes/:id` | Teardown; `404` counts as torn down |
|
|
681
|
+
|
|
682
|
+
**Start failures** (every provider): a sandbox that cannot start ends the lane `needs-human` with code `sandbox.start_failed`, not relaunched and not counted by the engine-error breaker; a capacity error ends it `deferred` (`sandbox.capacity`, backoff, bounded by `orchestration.max_deferrals`). See docs/OUTCOME-CODES.md. `forgedock gc --apply --sandboxes` tears a stale remote sandbox down through the same API (the checkout's forge.yaml `execution`).
|
|
683
|
+
|
|
684
|
+
**Hybrid placement** (`mode: hybrid`, forge#4256, `bin/lean/execution.mjs` `placeLane`, no model call). Each lane is placed when it is admitted. The first matching rule wins:
|
|
685
|
+
|
|
686
|
+
1. `max_remote: 0` → local.
|
|
687
|
+
2. `verify.mode: ci` → local. Heavy validation already runs, and is billed, on CI, so the lane is not billed twice.
|
|
688
|
+
3. A docs-only lane (issue label `docs`, `documentation` or `type:docs`) → local.
|
|
689
|
+
4. Heavy expected validation → remote. This means a declared check command (`learned.test_commands`) is a type check, linter, test runner or build, or the repo has a heavy toolchain marker: `tsconfig.json`, an eslint config, a package.json `test`/`lint`/`typecheck`/`build` script, `Cargo.toml`, `go.mod`, `pyproject.toml`/`setup.cfg`/`pytest.ini`, `pom.xml` or `build.gradle`.
|
|
690
|
+
5. A host without headroom → remote. "Without headroom" means MemAvailable is below `engine.min_free_mem_mb` + `engine.heavy_step_mem_mb`, or the load per core is above `engine.max_load_per_core` when that is set.
|
|
691
|
+
6. Otherwise → local.
|
|
692
|
+
|
|
693
|
+
A lane placed remote runs on the host instead when `max_remote` remote lanes are already running, or when the execution budget refuses it. In hybrid mode this happens whatever `on_budget` says. The decision `{where, reason}` (for example `fallback to local: max_remote 4 reached (heavy validation: tsc)`) is recorded:
|
|
694
|
+
|
|
695
|
+
- in the batch row (`placement`);
|
|
696
|
+
- in the trajectory (`Placement: ...`);
|
|
697
|
+
- in the lane's FORGE:STATE as `placement: {where, reason, mode}`. The lane gets it as `run-issue --placement <local|remote> --placement-reason <text>`.
|
|
698
|
+
|
|
699
|
+
**Cost and budget:** every remote lane's sandbox minutes and estimated cost (the provider's own usage report, e.g. the remote provider's `/usage` minutes, else wall-clock minutes × `cost_per_minute`) land in its row (`sandbox: {minutes, cost_usd, attempts}`, summed over relaunches), in `report.json`, in the printed table (`sbx-min`, `sbx-$` next to the token `cost`, shown only when a lane used a sandbox), in a `sandbox:` summary line with the budget state, and in FORGE:TELEMETRY as one `sandbox_usage` event per lane. The caps are plain arithmetic, no model call. The batch total is kept in the batch `state.json` and the day total in the host-day store, so both survive `--resume` and a schedd restart.
|
|
700
|
+
|
|
701
|
+
**Commands that use this section**: `forgedock orchestrate` (`bin/engine-cli.mjs` `readExecution`), `forgedock gc`
|
|
702
|
+
|
|
703
|
+
## `env` (OPTIONAL)
|
|
704
|
+
|
|
705
|
+
Lane environment for the core work-on (#4163). With no keys set, lanes still pin the target repo's own toolchain:
|
|
706
|
+
|
|
707
|
+
- **Package manager**: `package.json` `packageManager` (`pnpm|yarn|npm@x.y.z`, a `+sha…` suffix is ignored), else a `pnpm`/`yarn` line in `.tool-versions`. After the worktree is added the engine writes shims for it (`pnpm`+`pnpx`, `yarn`, or `npm`+`npx`) to `<run tmp>/toolchain/bin`. Each shim runs `corepack <pm>@<version>` (npm always via `npx -y`), or `npx -y` when corepack is not on PATH (yarn ≥ 2 via `@yarnpkg/cli-dist`). The shim dir goes first on the PATH of every runner call (builder and reviewer), so the builder's shell, its validate commands and git hooks (lefthook, husky) resolve the pinned version. `COREPACK_ENABLE_DOWNLOAD_PROMPT=0` and `COREPACK_ENABLE_STRICT=0` are set. The builder prompt says the toolchain is pinned and not to install global shims.
|
|
708
|
+
- **Node**: `.nvmrc`, else `.node-version`, else `.tool-versions` `nodejs`, else `package.json` `engines.node`. When the host `node` does not satisfy it, the run's trajectory shows a warning under **Anomalies**. Node is not switched.
|
|
709
|
+
|
|
710
|
+
A repo with no pins and no `env` keys runs exactly as before: no shim dir, no env change, no prompt change.
|
|
711
|
+
|
|
712
|
+
```yaml
|
|
713
|
+
env:
|
|
714
|
+
toolchain: "pnpm@10.4.1" # optional: overrides the detected package manager; "off" disables detection
|
|
715
|
+
setup: "bash scripts/reconcile-dev-db.sh" # optional: run once per fresh lane worktree, before the builder
|
|
716
|
+
sparse_max_kb: 1024 # optional: tracked files this size or larger stay out of lane worktrees; 0 = full checkout
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
| Field | Type | Required | Description |
|
|
720
|
+
|-------|------|----------|-------------|
|
|
721
|
+
| `toolchain` | string | No | `<pnpm\|yarn\|npm>@<x.y.z>` replaces the detected package-manager pin; `off` disables all toolchain detection. An invalid value is ignored and recorded as an anomaly. |
|
|
722
|
+
| `setup` | string | No | Shell command (`bash -c`) run once in each fresh lane worktree before the builder starts, with the lane's PATH and `TMPDIR`. Use it for environment drift the repo's migrations don't cover, for example a dev-database reconcile script. Timeout 15 min. A non-zero exit ends the lane `needs-human` with code `env.setup_failed`: an environment error, not a build failure. The builder never runs. |
|
|
723
|
+
| `sparse_max_kb` | integer | No | Default `1024`. Lane worktrees are sparse checkouts: tracked files of this many KB or more (generated JSON, screenshots, PDFs, media) are not written to disk, which cuts per-lane disk in repos that track large assets (#4221). Lockfiles, manifests (`package.json`, `pyproject.toml`, `requirements*.txt`, ...), toolchain pins, `tsconfig*.json`, `forge.yaml`, `.gitattributes` and `.gitignore` are always checked out. A repo with no such file gets a plain checkout. The builder prompt names the absent files; `git sparse-checkout add /<path>` brings one back. `0` disables it. Git sets `extensions.worktreeConfig=true` in the repo's shared config; the sparse setting itself is per lane worktree, so the main checkout is unchanged. If the sparse setup fails, the lane uses a full checkout. |
|
|
724
|
+
|
|
725
|
+
**Commands that use this section**: `forgedock run-issue`, `forgedock orchestrate` (core path; `bin/engine-cli.mjs` `readEnvConfig`)
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
511
729
|
## `build` (OPTIONAL)
|
|
512
730
|
|
|
513
731
|
Tuning for the `/work-on` build phase. Today it holds the **scope check** (forge#3862, replacing the diff-size gate): after the implement phase stages its changes and before validation commits them, the build compares the staged diff with the architect plan (the `PLANNED_FILES:` list and `ESTIMATED_DIFF_LINES` in the plan's `### Scope Baseline`), measures a deterministic `review_weight` with `scripts/diff-size.sh`, and records the outcome in `FORGE:DIFF_SIZE`. **Size never blocks, a finished build is never discarded, and a completed build is never routed to decompose.**
|
|
@@ -977,8 +1195,13 @@ npx forgedock labels setup
|
|
|
977
1195
|
|
|
978
1196
|
# Or target a specific repo explicitly:
|
|
979
1197
|
npx forgedock labels setup --repo owner/repo
|
|
1198
|
+
|
|
1199
|
+
# Also create per-repo domain labels (comma-separated):
|
|
1200
|
+
npx forgedock labels setup --domain frontend,backend,infra,design,legal-gated
|
|
980
1201
|
```
|
|
981
1202
|
|
|
1203
|
+
Domain labels are optional and per-repo. They are not part of `bin/labels.json`, so `forgedock doctor` never requires them. Each one gets the description `Domain: <name>. Managed by ForgeDock.`. `frontend`, `backend`, `infra`, `design` and `legal-gated` have fixed colors; any other name is grey (`#EDEDED`). A name that is already in the manifest is not created twice. `/forgedock-init` derives the list from your spec or takes it from `--domain-labels`.
|
|
1204
|
+
|
|
982
1205
|
Running this command is idempotent — it creates labels that don't exist and updates the color/description of labels that do. Safe to re-run at any time.
|
|
983
1206
|
|
|
984
1207
|
**When to run it**: Once after `npx forgedock install`, or whenever a pipeline command fails with "label not found". The command bootstraps every label the pipeline relies on.
|
|
@@ -1092,6 +1315,7 @@ These variables are read from the operator's runner environment, not from `forge
|
|
|
1092
1315
|
| `FORGE_TRAIL_OVERRIDE_ASSOCIATIONS` | the resolved `FORGE_TRAIL_TRUSTED_ASSOCIATIONS` value | Comma-separated `author_association` values prefiltering break-glass override candidates (the permission API stays authoritative). Never affects which FORGE markers count toward the gate. An approver with concealed org membership can show `CONTRIBUTOR`/`NONE`, so widen this variable (not the trail list) to accept them. An explicitly empty value means no override candidates. |
|
|
1093
1316
|
| `FORGE_TRAIL_PIPELINE_LOGINS` | empty | Comma-separated GitHub logins (case-insensitive) that are the pipeline's own identity and can never approve a phase-trail break-glass override. The authors of trusted FORGE marker comments on the issue are added automatically. |
|
|
1094
1317
|
| `FORGE_TRAIL_QG_SINCE` | `2026-10-07T03:40:12Z` | ISO-8601 UTC cutoff for the legacy `FORGE:QUALITY_GATE` grace. An issue whose trusted `FORGE:BUILDER:COMPLETE` comment was last updated before this time has `QUALITY_GATE` waived. Set to an empty string to disable the grace. A malformed or future value (including a runner clock earlier than this default) fails closed: the verifier exits 2, an infrastructure block rather than `MISSING`. |
|
|
1318
|
+
| `FORGEDOCK_ALLOW_API_BACKEND` | unset | Set to `1` (host owner approval) together with forge.yaml `execution.agent_backend: api` to let `--backend auto` use the paid API backend when no `claude` CLI is available. Without both, `auto` never selects `api` (#4257). |
|
|
1095
1319
|
| `FORGE_TELEMETRY` | `on` | Set to `off` to disable gate telemetry. Gate scripts append one structured record per decision to a trusted `FORGE:TELEMETRY v1` comment; emission is best-effort and never changes a gate's output or exit code. See [TELEMETRY.md](TELEMETRY.md). |
|
|
1096
1320
|
|
|
1097
1321
|
### Install root resolution
|
package/docs/OUTCOME-CODES.md
CHANGED
|
@@ -37,13 +37,18 @@ An absent or unknown code is recorded as `unclassified`, with the raw reason kep
|
|
|
37
37
|
| `merge.transient` | `gh pr merge` kept failing with a transient error (`Base branch was modified`, GraphQL or 5xx) after the retry budget; the run is `deferred` and a rerun resumes at the merge | lean | `bin/lean/lean.mjs` |
|
|
38
38
|
| `push.transient` | `git push` kept failing with a transient error (`remote unpack failed`, `index-pack failed`, `RPC failed`, 5xx, reset, timeout) after 4 attempts; `deferred`, rerun resumes | lean | `bin/lean/lean.mjs` |
|
|
39
39
|
| `cli.idle` | The builder CLI showed no stream activity for `engine.cli_idle_minutes` and was killed; uncommitted work was checkpointed to the lane branch; `deferred` after one automatic retry | lean | `bin/runner.mjs` |
|
|
40
|
+
| `run.interrupted` | A core run got SIGTERM/SIGINT; the lane tore down from its resource ledger (unpushed work checkpointed to the lane branch, worktree removed, lease released) and ended `deferred`, so a batch requeues it | lean | `bin/engine-cli.mjs` |
|
|
40
41
|
| `host.busy` | Core run deferred at admission: free memory, free tmp space or load per core outside `engine.admission` limits; no lease taken, nothing posted | lean | `bin/lean/admission.mjs` |
|
|
42
|
+
| `gh.unavailable` | `run-issue --repo` could not verify the cwd repo because `gh repo view` kept failing after 3 attempts with backoff (typically a timeout under host load); the core or full run is `deferred` (exit 75, retry in 60s) before any lease, nothing posted | engine CLI | `bin/engine-cli.mjs` |
|
|
41
43
|
| `rate_limited` | Session or usage limit outlasted the pause budget | engine | `err.usageLimit` / `err.resetAtEpochMs` |
|
|
42
44
|
| `engine.error` | Engine or tool failure (missing key/SDK, CLI crash, max turns, unresolved args, runner threw every attempt) | engine | `err.code` |
|
|
43
45
|
| `infra.disk_quota` | Disk full or quota exhausted (EDQUOT/ENOSPC, errno -28/-122); retryable infra failure, never `needs-human` | engine | Runner error text |
|
|
44
46
|
| `infra.cli_spawn_transient` | CLI spawn failed transiently before the child ran (ETIMEDOUT, EAGAIN, ENOMEM, EMFILE); retried with backoff on a separate bounded counter that does not consume a phase attempt | engine | `err.code` `CLI_SPAWN_TRANSIENT` |
|
|
45
47
|
| `cli.timeout` | CLI child ran and was killed at the wall-clock limit; a normal attempt (consumes one of `maxAttempts`), usage recorded when the CLI emitted any | engine | `err.code` `CLI_TIMEOUT` |
|
|
46
48
|
| `cli.spawn_failed` | The CLI could not be started for a reason that repeats on every rerun (`E2BIG` argv too large, `ENOENT`/`EACCES`/`ENOEXEC` binary missing or not executable, `ENAMETOOLONG`). Core work-on ends `needs-human` with this code instead of `engine-error`, so `forgedock orchestrate` does not spend its relaunch on it | lean, engine | `err.code` `CLI_SPAWN_FAILED` (E2BIG) or `err.spawnDeterministic` (other errnos keep their `err.code`); `err.spawnErrno` holds the errno |
|
|
49
|
+
| `env.setup_failed` | forge.yaml `env.setup` (run once per fresh lane worktree, before the builder) exited non-zero, timed out or could not start. An environment error, not a build failure: the builder never ran. Core work-on ends `needs-human` with this code instead of `engine-error`, so `forgedock orchestrate` does not relaunch it; fix the setup command or the environment and rerun | lean | `io.runSetup` exit code / spawn error |
|
|
50
|
+
| `sandbox.start_failed` | A `forgedock orchestrate` lane sandbox (forge.yaml `execution`, provider `container` or `remote`) could not start: bad image, missing endpoint or credential, refused launch. The lane never ran. Final `needs-human` row: not relaunched and not counted by the engine-error circuit breaker | orchestrate | `bin/lean/execution.mjs` `providerLanes`: `provider.start` threw without `retryable` |
|
|
51
|
+
| `sandbox.capacity` | The sandbox provider had no capacity for the lane (HTTP 429/503 from the remote transport). `deferred` with backoff (`Retry-After` when sent), bounded by `orchestration.max_deferrals` | orchestrate | `provider.start` threw with `err.retryable: true` |
|
|
47
52
|
| `lease.held` | Another live agent holds the issue lease | engine | Foreign live lease on acquire |
|
|
48
53
|
| `dependency.open` | A dependency is still open: a trusted `**Depends on**: #N` line refused the run before any state write, or the core builder reported `block.kind: "dependency"` naming an issue that is open. Carries `dependencies`; never `needs-human`. `forgedock orchestrate` adds the edge and requeues the issue behind it, without skipping dependents | engine, lean | Trusted `**Depends on**` line, or `result.json` `block.issues`, + the dependency issue's live open state |
|
|
49
54
|
| `dependency.unreadable` | The issue body or a declared dependency could not be read, so the run was refused (fail closed) | engine, lean | Failed or unparsable `gh api` / `gh issue view` read |
|
|
@@ -70,8 +75,14 @@ An absent or unknown code is recorded as `unclassified`, with the raw reason kep
|
|
|
70
75
|
| `conformance.unjustified_deferral` | Core work-on: the reviewer judged a deferred or post-deploy requirement unjustified (it belongs in this change), after one builder resume; nothing is filed or merged | lean | Review requirement row `deferral_ok: false` |
|
|
71
76
|
| `conformance.preserve_violated` | Core work-on: a preserve constraint is violated or unverified, after one builder resume | lean | Review `preserve` row not `intact` |
|
|
72
77
|
| `conformance.regression` | Core work-on: the reviewer named a concrete regression, after one builder resume | lean | Review `regressions` row |
|
|
78
|
+
| `docs.stale` | Core work-on: the target repo's own `scripts/check-devdocs-freshness.sh` (docs must move with `bin/lean/` / `scripts/` behaviour) still fails after one builder resume; caught before the PR instead of by its required check | lean | the repo's `scripts/check-devdocs-freshness.sh <base>` exit 1 |
|
|
79
|
+
| `structure.undeclared_edge` | Core work-on: the change adds a package.json dependency between workspace packages that the frozen contract does not name, still present after one builder resume | lean | `unnamedEdges` over `scripts/structure-delta.sh` `package_edges_added` |
|
|
80
|
+
| `maintenance.hotspot_growth` | Core work-on: the change grows a file already over `maintenance.hotspot.file_lines` by more than `growth_lines` without a `hotspot-exempt: <reason>` in the contract, still present after one builder resume; blocks in shadow and enforce | lean | `hotspotGrowth` precheck over `scripts/hotspots.sh --base` `growth` |
|
|
73
81
|
| `followup.unavailable` | Core work-on: filing or verifying the tracking issue for a deferral failed; the run is deferred with nothing merged, never needs-human | lean | `file-deferred-followups.sh` exit 3/4 or an unreadable tracking issue |
|
|
74
82
|
| `merge.head_mismatch` | Core work-on: CI passed for a head other than the reviewed head; merge refused | lean | Gate head differs from the reviewed head |
|
|
83
|
+
| `split.unsupported` | Core work-on `refactor:split` lane (forge#4211): no codemod or index tool for the target's language (Python and TypeScript have movers), or libcst / ts-morph is not available; refused before any edit | lean | target extension, `scripts/symbol-index.py`/`symbol-index.mjs` `unsupported`, `scripts/symbol-move.py` exit 3 naming libcst, `scripts/symbol-move.mjs` exit 3 `no codemod tool for ts` (forge#4212), `scripts/refactor-verify.sh` exit 2 |
|
|
84
|
+
| `split.plan_invalid` | Core work-on `refactor:split` lane: no target or test command in the issue, an unparseable grouping reply, a failed baseline, or a plan `scripts/symbol-move.py` / `scripts/symbol-move.mjs` refuses; nothing written | lean | `bin/lean/refactor.mjs` parsers, `scripts/symbol-move.py` / `scripts/symbol-move.mjs` exit 3 |
|
|
85
|
+
| `split.verify_failed` | Core work-on `refactor:split` lane: the move did not prove location-only (logic diff, missing symbol, changed pass/skip counts or a new import cycle); the branch is reset and nothing is pushed | lean | `scripts/refactor-verify.sh verify` exit 1 `failures[].kind` |
|
|
75
86
|
| `completed` | Non-failure terminal (merged, phase-complete, investigation) | engine | Terminal reason |
|
|
76
87
|
| `awaiting_merge` | Clean result held for a merge decision | remediate | Terminal reason `awaiting-merge` |
|
|
77
88
|
| `unclassified` | Fallback for an absent or unknown code | engine | The raw reason is kept alongside |
|
package/docs/TELEMETRY.md
CHANGED
|
@@ -40,7 +40,7 @@ is dropped. Payload keys are `[A-Za-z0-9_]+` and cannot shadow the shared keys;
|
|
|
40
40
|
| `ci_local` | `scripts/ci-local.sh run` (validate V4.8), one per run | `result` = `PASS` \| `FAIL` \| `DEGRADED`, `jobs_run`, `jobs_deferred`, `jobs_failed`, `duration_ms` (execution), `wait_ms` (time queued for a machine slot), `fix_iterations` (0 first run, 1 and 2 after fixes), `checks`, `cache_hits`, `narrowed` (checks run on a subset or skipped for no impact), `widened` (checks widened to the full command), `selected` / `total` (summed scope) |
|
|
41
41
|
| `ci_local_check` | `scripts/ci-local.sh run`, one per scoped, widened or executed check (at most 20 per run) | `job`, `selector`, `selected`, `total`, `cache` = `hit` \| `miss` \| `dedup` \| `na`, `duration_ms`, `wait_ms` (job slot wait), `widen` (reason or `none`). A remote-CI failure at merge on a check recorded here as narrowed is a selector bug |
|
|
42
42
|
| `liveness` | `scripts/remediate-liveness.sh` | `decision` = `PROCEED` \| `DEFER` \| `EXHAUSTED`, `why` |
|
|
43
|
-
| `phase_attempt` | the engine (`bin/engine/telemetry-seam.mjs`, one per phase attempt; `phase` = the phase id) | `attempt`, `model`, `turns`, `duration_ms`, `cost_usd`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_creation_tokens` (any value the runner did not report is omitted) |
|
|
43
|
+
| `phase_attempt` | the engine (`bin/engine/telemetry-seam.mjs`, one per phase attempt; `phase` = the phase id) | `attempt`, `model`, `backend` (`cli`/`api`), `turns`, `duration_ms`, `cost_usd`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_creation_tokens` (any value the runner did not report is omitted) |
|
|
44
44
|
| `run_terminal` | the engine (`terminate()`, one per run segment) | `reason` (terminal reason), `code` (see `docs/OUTCOME-CODES.md`), `wall_ms` (this engine process, start to terminal) |
|
|
45
45
|
|
|
46
46
|
The engine records carry `run` = `r_<issue>_<start epoch seconds>` (one per `forgedock run-issue` process) so `forgedock stats`
|
|
@@ -553,6 +553,28 @@ Onboards an existing repo into the ForgeDock pipeline — scans open issues, cla
|
|
|
553
553
|
|
|
554
554
|
---
|
|
555
555
|
|
|
556
|
+
### `/bootstrap`
|
|
557
|
+
|
|
558
|
+
**Idea-to-backlog setup for a new project.**
|
|
559
|
+
|
|
560
|
+
Guided setup for a brand-new project — interviews you about the idea, proposes defaults, surfaces doubts, writes a technical spike doc, and files a validated, orchestrate-ready issue backlog. The plan is validated before any GitHub write.
|
|
561
|
+
|
|
562
|
+
```bash
|
|
563
|
+
/bootstrap "a habit tracker for teams" # Start from a free-text idea
|
|
564
|
+
/bootstrap docs/idea.md # Start from existing docs
|
|
565
|
+
/bootstrap "<idea>" --dry-run # Preview the backlog without GitHub writes
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
Headless, from a terminal or CI:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
npx forgedock bootstrap "<idea>" [--dry-run] # Run /bootstrap without an interactive session
|
|
572
|
+
npx forgedock bootstrap --plan plan.json --dry-run # Preview milestones, titles, DAG and estimated agent cost; no GitHub access
|
|
573
|
+
npx forgedock bootstrap --plan plan.json # Validate and create; re-run the same command to resume from GitHub
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
556
578
|
#### `/sync-ecosystem`
|
|
557
579
|
|
|
558
580
|
**Cross-repo sync.**
|
|
@@ -10,6 +10,8 @@ ForgeDock turns GitHub issues into shipped code — automatically. You open an i
|
|
|
10
10
|
|
|
11
11
|
This tutorial gets you from zero to your first autonomous pipeline run in under 5 minutes.
|
|
12
12
|
|
|
13
|
+
No repo or issues yet? [Start a New Project with ForgeDock](./start-a-new-project.md) turns an idea into a ready-to-build backlog with `/bootstrap`.
|
|
14
|
+
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
## Prerequisites
|