Repository navigation
v0.22.0
Added
gates.merge_gate_links— repository-relative git-ignored paths (for examplelocal.properties,
app/google-services.json) are linked from the main checkout into the temporary merge-gate tree before setup; each
link must stay under the repository root. A retried land merges only the closed block head (B343b)block open --allow-api-mismatch— block open (Go) checks the brief's uses/extends lines against the code
(api-fit). A missing name or parameter exits 2 with anapi-fit:line;--allow-api-mismatchopens anyway and prints
each line as a note (B338)code-forge cloud-coder --run <r> --block <id>(Go) — runs a cloud coder from configcoder.cloud(submit, then
fetch until a patch or timeout), applies the patch to the block's owned files only and queues review; a patch fetched
at the deadline still lands (B342)code-forge reporthas a pace section: per-block and run pace in minutes; text,--jsonand--exportcarry the
same 18 sections (B341, B358)- heal queue: gates run, block rebase and block land queue a failure that passes by itself (a held lock, a transient git
error) in ~/.code-forge/heal/.jsonl and print heal: queued ; code-forge heal list|show|run retries each item
with backoff (15 s, 60 s, 240 s), removes a lock whose owner pid is dead, and marks needs_coder after --max-tries
(B429) - Main first:
block land --push(orland.push: true) pushes main after the merge; a rejected push queues a heal item
of kind land (argvblock land <ids> --run <r> --merge --push; already landed blocks only push) and exits 1;--watch-cistarts a detachedcode-forge ci watch, a new Go verb that follows the
commit's GitHub Actions runs and queues a heal item of kind ci naming the failing jobs, tests and block ids (B430) - Under an active autopilot grant an agent may run block close --decide without a terminal for transcript rule_break
hits and warning/nit findings; criticals stay with the owner; each decision is a signed close.decision row shown by
the new code-forge decisions list (B390)
Changed
- the skill's
block landtext no longer says the merge gate runs the full suite: it uses the Go test selection
(B404); newskill/references/code.md§5.1 tells the orchestrator how to use the changed-test selection, preflight,
close review wait, Go rebase replay, batch land,--push/--watch-ci, the heal queue and the machine test queue,
with pointer lines in SKILL.md (B443) - native
block closequeues a review for each changed owned file that has no review (also after an acceptance
amendment), waits once for all of them (review.close_wait_s, default 600), then stops withnot_approvedor
review_pending; close never approves a file itself (B339) - A Go verb that holds the run lock writes an owner file (pid, start token, verb) in
<run>.lockand removes it on release; a waiting Go verb breaks a lock whose owner process is gone or replaced (or, with no owner file, that is older than 10 minutes) once, logsbroke stale run lock (pid N)and takes it, so a crashed holder no longer blocks every verb until a human removes the dir; a lock with a live owner is never broken (B423) block land(Go) no longer stops with "land stopped at join: reviews did not finish" when a loaded machine misses the run daemon's fixed 2 s connect/ping deadline: the review join asks the daemon again until land's own--maxdeadline (thenwait_timeout); a dead daemon (connect refused, connection closed, invalid answer) still stops the join at once with the same message (B417)block closeandblock ready(Go) check thecode-forge-runs-accesstranscript rule themselves instead of handing any transcript with.code-forgetext to Node: plain-text logs and Claude Code JSONL logs get the same hits as Node after B415 (a Read/Bash/Glob/write-tool path or command word naming the run records, absolute or home-relative, hits; a heredoc body or quotedechotext a command only writes to a file does not;.code-forge.ymldoes not). Go also decides the git/gh/rm and code-forge launcher rules,sh -cwrappers, interpreter scripts and non-ASCII text, so acoder-forgepath orforge/r-…branch name no longer hands over; only Codex logs,.code-forge/reviewsand.code-forge/queuetext, invalid UTF-8, lone surrogates and JavaScript-only line separators still hand over (B416)- Go gate children start at nice 10 (or the engine's higher nice) before their own code runs: the engine starts each gate through a short copy of itself that sets its own nice and then execs the gate program with the same argv, environment and spawn errors. Before, the engine lowered the child after start, and on macOS that change could be lost while the child was still starting, leaving a gate (and the nice test) at nice 0 (B420)
gates run --changedand theblock land --mergeand batch-land merge gates (Go) run only real test files (*.test.*,*.spec.*,*_test.go,*Test.php,test_*.py,*_test.py,*Tests.swift): a changed helper, fixture or script undertest/is no longer run as a test or picked as a stem-paired test, and selects the tests that import it or name it by path instead. The owned-file test fallback, the no-JS changed scope andrepeat_new_testsskip such files the same way (B394)gates run --changedand the land gates select the CLI tests for a change to any verb module: Go's import graph countsbin/code-forge.mjsas importing everysrc/cli/*.mjsits router loads by readdir, andbin/code-forgeas importing the entry (a change tosrc/cli/version.mjsselected 0 tests before) (B373c)gates run(Go, every mode:--changed, full,block landand batch merge gates) runslint,typesandformatbefore the test gate. When one of them is red the test command never starts: its entry stays in the result (same order) withskipped: true,ok: false,reason: pre_gate_failedandpre_gates_failednaming the red gates,gate.donerecords the same reason, and the exit code stays 1, so two type errors no longer cost a 12-minute suite first (B418)block land <id> --mergeskips the merge-gate run when the merged tree equals the clean tree the block's last green gate ran on and that gate covered the merge selection:merge gate: reused green gate on tree <id> (gate_reused),merge_gate_reason: gate_reusedinland.timingand the--jsonlandedevent; a block on an older base still runs it.gate.donerows from a block worktree record thetreethey ran on (B393)- Batch
block land <id> <id>...(Go) runsgit merge-tree --write-treefor every block against the running result before any rebase, gate or suite. A block that would conflict is parked with reasonblock_rebase_conflictand its conflicting files (--jsonevent: parked,land.batchparked); the other blocks still gate and land. When every block conflicts, nothing lands and land exits 1 naming each block (B397) - Go tests that build a fixture repo (
gates run,gatesgo-only scope,review-file --wait/--wait-block,review wait-block,statusdirect) run fixture git in the fixture repo without inheritedGIT_*variables and start their code-forge children without them, so aGIT_DIR,GIT_INDEX_FILEorGIT_WORK_TREEfrom a hook or worktree no longer breaks them; a guard test reruns them with those variables set (B410) - Go tests in
native/gates,native/blockops,native/blockopenandnative/gatecheckrun fixture git with the fixture repo as the directory andgitx.ChildEnv()(no inheritedGIT_*), with a guard test that reruns them underGIT_DIR/GIT_INDEX_FILE/GIT_WORK_TREEpointing nowhere;cloud-codertakes its deadline from a replaceablecloudDeadline, so the fetch-timeout test expires after two polls instead of 2 s of wall time and no longer fails on a loaded machine (B411) gates run --run R --block B --changed(Go) selects its tests in Go and never runs or hands over to the full Node suite: the block's tests, owned and changed tests and the stem-paired tests of changed sources (any language), every test that imports a changed file (static and dynamic imports,require,new URL, path mentions anywhere in the closure, imports of deleted files), for a native changetest/compat/go.test.mjs, the compat tests named after the Go files and every test that builds or runs the native binary (B330), and the config golden test for a fixture, schema orsrc/config/change (B409). A changed test the runner cannot take is dropped, never widened; an empty selection skips the test gate. The reason isgo_onlyorimport_graph. Theblock land --mergemerge gate and the batch-land merge gate use the same selection (merge gate: scoped tests passed (...)for--merge) instead of the full suite; a test command without{test_files}gets the selection appended and never runs unscoped;go.test.mjsruns only for a native change.gates run --run R --block B --fullis the only local full suite (scope: full,reason: explicit) (B404)- In a code-forge worktree,
block rebaserebuilds the worktree's native binary (node scripts/build-native.mjs --host) when the rebase brings a change tonative/**orscripts/build-native.mjs, andgates run --run --blockrebuilds it before any gate when it is missing or older thannative/go.modor a non-testnative/**/*.gofile (the gate JSON showsnative_rebuilt: true). A failed build exits 1 namingnative-rebuild-failed; the gate then runs nothing. Compat tests no longer run a binary built on the old base (B408) - The close transcript check no longer counts a
~/.code-forge/runspath that a coder only writes as text: the body of acat > file <<'EOF'ortee fileheredoc (not piped on, no shell substitution) and quotedecho/printfarguments written intoplans/give nocode-forge-runs-accesshit. Real access still does:cat/ls/cd/<on the path, a Read or other tool on it, a heredoc fed to a shell or piped on,$(…)in an unquoted heredoc, and now also the home-relative spelling.code-forge/runs(B415) - New
block ready <id> --run <r> --preflight(Go) lists in about a second, with no gate result, the close blockers a gate cannot fix:stale_base(with the base branch tip sha and theblock rebase --accept-landedfix), open review findings,rule_breakand, with--report,coder_report_failed; exit 0 clean, 1 with the list.gates run --run --blockruns the same preflight by default and stops before any gate command, one stderr line per blocker;--no-preflightskips it (B392) gates.extra.invariantsis now refused instead of silently ignored (no engine ever ran its script): config validation rejects it as an unknown key under/gates/extra, and Gogates run(scoped, unscoped and insideblock land) exits 2 before any gate runs with one line naming the key andgates.customas the way to run the script (B402)- Batch
block land <id> <id>...no longer runs a full suite per block when a block's changed scope needs the full fallback: it writes a signedgate.deferredrow (scopedeferred, reasonmerge-gate) and the one merge-gate suite with its prefix bisect still names a failing block. A batch where no block owns or changes a Node product file (src/**,bin/**,scripts/**/*.mjsexceptscripts/build-native.mjs,package.json,package-lock.json) runs no full suite at all;land.batchrecordsmerge_gate_reasongo_onlyornode_product(B398) gates run --run R --block B --changedno longer falls back to the full Node suite for a Go-only block: every owned path is undernative/,docs/,plans/or.github/, a runnable test undertest/, a*.mdfile orscripts/build-native.mjs(nosrc/,bin/, otherscripts/*.mjs,package.jsonorpackage-lock.json). Go runs the test gate once with the block's tests,test/compat/go.test.mjsand the compat tests named after the block's Go files (status_direct.go→parity-status-direct); the result andgate.donecarryreason: go_onlyandgate.doneliststest_files. Blocks with a Node product file keep the old scope;block land --batchkeeps its own plan (a full fallback defers to the one merge gate, B398) (B399b)block readyandblock close(Go) find review tickets by themselves: under--report, a changed file with noreviewed <file> ticket <id>line is credited by the latest done review-file ticket of the same run, block and file for its current content hash. A ticket for older content still fails withcoder report FAILED: no review-file ticket id for <file>; report ticket lines stay optional and still win (B405)- A tracked
.gitattributessetsCHANGELOG.md merge=union, so blocks that each add a CHANGELOG line at the top of[Unreleased]merge and rebase without a conflict on every clone and in CI (B396) cloud-coderanddocsnow have thin Node shims: withCODE_FORGE_NATIVE=0(or no Go binary)--help/-hprints the same usage as Go and exits 0, any other call prints one line naming the missing Go binary and exits 1; with the Go binary the call is handed to it.helpand the command reference list them fromsrc/clilike every other verb; a Go test fails when a new Go route is missing from help (B386)- New config
gates.test_class_map(rules{glob, strip_prefix, strip_suffix, separator}) maps a block's own test files to a test-class filter: an argv item holding{test_classes}in a fixed gate, e.g.-Pandroid.testInstrumentationRunnerArguments.class={test_classes}, getsa.b.FooTest,a.b.BarTestwithgates run --run --block, and is dropped (full suite) when no test matches; paths outside every rule are ignored. New Go verbcode-forge gates classes --run <id> --block <id>prints the filter (B335) - A failed land suite (single or batch, changed gates or merge gate) now names up to 10 failing tests from the node spec reporter's
✖ failing tests:list as "name (file:line)" as well as TAPnot oklines, and saves the whole suite output to~/.code-forge/runs/<run>/land-logs/<block>-<time>.logwith one linefull log: <path>, so no 20-minute rerun is needed to find the failures (B374) review-fileenqueue,--waitand--wait-blockrun in Go without Node:--help, mixed flag order,--cwd, usage errors (exit 2), the no-repo refusal (exit 1) and the block-scope refusal match Node byte for byte. With the daemon off, unreachable or giving an invalid answer,--wait-blockjoins in Go, enqueues missing tickets with Node's ticket bytes and prints Node's JSON. A live worker with a stopped review that has findings still hands over to Node (wait-block-stopped), before any queue write (B382)- Go
block landandblock close --auto-claimnow refuse to auto-claim.code-forge.ymlwhilehooks.on_block_endis set, with the same reason, order and text as Node (B385) gates run --run R --block Bchecks the block's base first: when the block's base branch moved past its recorded base, no gate runs, it exits 1 at once with one line naming the fixcode-forge block rebase <id> --run <R> --accept-landed.--auto-rebaseruns that rebase and then gates;--allow-stale-basegates on the old base with a WARN line. Go and Node behave the same (B363)- Facts, not guesses (owner rule): every coder context pack, Node and Go byte-equal, ends its proof rule with a bullet that every reported cause, pass or fail names its proof (error text, log path, or command and exit code); the skill gets hard rule R18 "Facts, not guesses" for the orchestrator, so there are now eighteen hard rules (B362b)
- New Go verbs
code-forge docs sync|path [--json].docs syncfetches the publishedllms-full.txtand keeps it in~/.code-forge/docs/only when itsversion:line equals the installed version; otherwise, offline or on an HTTP error it keeps the packed copy and still exits 0.docs pathprints the best copy, or exits 1 when there is none.upgraderunsdocs syncbest effort after a successful upgrade (B348) - The close transcript check reads Claude Code sub-agent JSONL logs by shape: only tool_use Bash commands, write paths and other tool inputs are scanned, so tool help, tool_result text and the coder prompt no longer give false rule_break hits; a broken or mixed log still falls back to the plain fail-closed grep (B361)
- Test runs wait in one machine-wide FIFO queue:
gates runtakes a full or changed slot before its test command, changed runs sharemax(1, min(cores/4, free GB/2))slots, and a killed holder frees its slot within 2 seconds.code-forge queueshows holders and waiters,code-forge queue run -- <cmd>queues any other test command,statusshowsqueued #k, andCODE_FORGE_TEST_QUEUE=offturns it off (B352) code-forge worker --run <id>re-pins the run to itself when every pinned worker is dead (no process, or another start time), with the sameworker.reattachledger row asrun start --reattachand one stderr lineworker: re-pinned run <id> from dead pid <old> to <new>, so a block close after a worker restart no longer stops onworker.replaced; a pinned worker that is still alive and not serving this queue is never replaced (exit 1) (B355)- The anthropic default L3 model is now
claude-fable-5-2in Node and Go;claude-fable-5-2is a known model id, andclaude-fable-5-1stays known, so configs that name it still validate (B353) - Every context pack (Node and Go, byte-equal) has a
## forbiddensection: one line per rule the close transcript scan enforces for a coder, built from the rule table, then the safe ways for a red run on base code, clean-up and run facts (B364) block closeno longer refusesunreviewedfor an owned file the block did not change (equal to its base) when every review of its current content is ano_changeresult, as afterblock rebase --accept-landedonto a main that changed it. A changed file, or one whose current content has a refusal with findings, still needs an approval. Such a file is not counted as work either, so a block with no other change still refusesno_changes; Node and Go agree (B365)- Three tests no longer fail on a busy machine: the Go torn-tail recovery test is bounded only by
go test -timeout(not 20 s) andtest/compat/go.test.mjsgives Go 10 minutes and prints every output line of a failed Go test;native-installrunsnpm packon a private package copy, so a file another test adds to and removes fromnative/binno longer breaks it (B375) - The package test suite (
npm test,test/helpers/run-suite.sh) runs one test file per core when it holds the machine
test slot (CODE_FORGE_TEST_QUEUE_HELDset bygates runandcode-forge queue run), and keepsmax(1, cores/2)
outside a slot; an explicit--test-concurrencyorCODE_FORGE_TEST_CONCURRENCYstill wins (B376) block closeandblock readyjudge review caps, reopened and unwaived findings, agent and autopilot waivers,review.agent_waivers.require_ack(agent-waivers-not-ackeduntil a signed human ack row), accepted rule hits, old result-file rows and an amended acceptance in Go, with the same verdict and output as Node. Close prints the agent waiver, agent reopen and accepted hit lines and theagent_waivers,agent_reopensandaccepted_hitsJSON keys only when they are not empty.block close --help|-handblock human-pass --help|-hprint the usage in Go and exit 0. Rows or dates Go cannot read exactly like Node still hand over asclose-gates-format(B380)- Batch
block land <id> <id>...(Go) lands closed blocks as one checked batch: blocks that did not land are restored,
landed ones are marked, and a bad--max, a bad timeout or a duplicate id is refused (B340) - Gate child processes run at low priority (nice 10), and tests share one cached Go test binary (B354)
gates runkeeps the full test scope when the block's base moves in an unsafe way or a baseline is rejected;
text-only blocks stay scoped (B318R)- The run lock now guards only a short read-modify-write of the run record (Go): batch land (rebases, gates, merge
gate), block rebase and the rebase check work on a snapshot under the block's own lock and commit only their blocks in
one short write, so verbs on other blocks of the run never wait; a same-block change meanwhile stops the verb with
block-changed, a second long verb on the same block with block-busy (B426) - The changed-test import graph is narrow (Go): comments make no edge, named imports are followed through re-export
barrels and to the exports of a module that reach the change, and the new gates.import_graph.stop list ends the
closure at hub files; this repository stops at 5 measured hubs, taking the B362b+B402 batch from 348 of 380 tests to
65 (B427)
Fixed
- parity tests follow the Go-only behaviour of B393 (gate
treefield, merge-gate reuse), B394, B418 and B339, so CI
is green again; Node parity is still checked for every other field (B437, B439, B440) block rebasereplays first and refuses only a real conflict (Go) — owned files main changed no longer refuse
up front: a clean replay moves the base, names themupstream_ownedand queues them for review again; a real conflict
restores HEAD and refusesrebase-conflictnaming the files;CHANGELOG.mdlines both sides only added are kept (main
first), also undermerge=union, in--rebase-check,--accept-landedand batch land (B431)npm run release: when localnpm testfails, only the failing files rerun alone; the release goes on only when each
passes alone and the CI run for HEAD is green (local flaky (passed alone, CI green): <files>). Worker test deadlines
scale with host load (B345)
Removed
- Three Node modules that nothing ever imported, with their tests:
src/engines/alias-map.mjs,src/engines/isolation.mjsandsrc/gates/invariants.mjs.src/config/schema-paths.mjsstays, because the config schema tests use it (B400)
npm: https://www.npmjs.com/package/@codedology/code-forge/v/0.22.0
Full changelog: https://github.com/ricardov03/code-forge/blob/v0.22.0/CHANGELOG.md