Skip to content

v0.22.0

Choose a tag to compare

@github-actions github-actions released this 11 Nov 21:40
· 464 commits to main since this release

Added

  • gates.merge_gate_links — repository-relative git-ignored paths (for example local.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 an api-fit: line; --allow-api-mismatch opens anyway and prints
    each line as a note (B338)
  • code-forge cloud-coder --run <r> --block <id> (Go) — runs a cloud coder from config coder.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 report has a pace section: per-block and run pace in minutes; text, --json and --export carry 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 (or land.push: true) pushes main after the merge; a rejected push queues a heal item
    of kind land (argv block land <ids> --run <r> --merge --push; already landed blocks only push) and exits 1; --watch-ci starts a detached code-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 land text no longer says the merge gate runs the full suite: it uses the Go test selection
    (B404); new skill/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 close queues 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 with not_approved or
    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>.lock and 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, logs broke 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 --max deadline (then wait_timeout); a dead daemon (connect refused, connection closed, invalid answer) still stops the join at once with the same message (B417)
  • block close and block ready (Go) check the code-forge-runs-access transcript rule themselves instead of handing any transcript with .code-forge text 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 quoted echo text a command only writes to a file does not; .code-forge.yml does not). Go also decides the git/gh/rm and code-forge launcher rules, sh -c wrappers, interpreter scripts and non-ASCII text, so a coder-forge path or forge/r-… branch name no longer hands over; only Codex logs, .code-forge/reviews and .code-forge/queue text, 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 --changed and the block land --merge and 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 under test/ 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 and repeat_new_tests skip such files the same way (B394)
  • gates run --changed and the land gates select the CLI tests for a change to any verb module: Go's import graph counts bin/code-forge.mjs as importing every src/cli/*.mjs its router loads by readdir, and bin/code-forge as importing the entry (a change to src/cli/version.mjs selected 0 tests before) (B373c)
  • gates run (Go, every mode: --changed, full, block land and batch merge gates) runs lint, types and format before the test gate. When one of them is red the test command never starts: its entry stays in the result (same order) with skipped: true, ok: false, reason: pre_gate_failed and pre_gates_failed naming the red gates, gate.done records 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> --merge skips 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_reused in land.timing and the --json landed event; a block on an older base still runs it. gate.done rows from a block worktree record the tree they ran on (B393)
  • Batch block land <id> <id>... (Go) runs git merge-tree --write-tree for every block against the running result before any rebase, gate or suite. A block that would conflict is parked with reason block_rebase_conflict and its conflicting files (--json event: parked, land.batch parked); 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, gates go-only scope, review-file --wait/--wait-block, review wait-block, status direct) run fixture git in the fixture repo without inherited GIT_* variables and start their code-forge children without them, so a GIT_DIR, GIT_INDEX_FILE or GIT_WORK_TREE from 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/blockopen and native/gatecheck run fixture git with the fixture repo as the directory and gitx.ChildEnv() (no inherited GIT_*), with a guard test that reruns them under GIT_DIR/GIT_INDEX_FILE/GIT_WORK_TREE pointing nowhere; cloud-coder takes its deadline from a replaceable cloudDeadline, 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 change test/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 or src/config/ change (B409). A changed test the runner cannot take is dropped, never widened; an empty selection skips the test gate. The reason is go_only or import_graph. The block land --merge merge 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.mjs runs only for a native change. gates run --run R --block B --full is the only local full suite (scope: full, reason: explicit) (B404)
  • In a code-forge worktree, block rebase rebuilds the worktree's native binary (node scripts/build-native.mjs --host) when the rebase brings a change to native/** or scripts/build-native.mjs, and gates run --run --block rebuilds it before any gate when it is missing or older than native/go.mod or a non-test native/**/*.go file (the gate JSON shows native_rebuilt: true). A failed build exits 1 naming native-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/runs path that a coder only writes as text: the body of a cat > file <<'EOF' or tee file heredoc (not piped on, no shell substitution) and quoted echo/printf arguments written into plans/ give no code-forge-runs-access hit. 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 the block rebase --accept-landed fix), open review findings, rule_break and, with --report, coder_report_failed; exit 0 clean, 1 with the list. gates run --run --block runs the same preflight by default and stops before any gate command, one stderr line per blocker; --no-preflight skips it (B392)
  • gates.extra.invariants is now refused instead of silently ignored (no engine ever ran its script): config validation rejects it as an unknown key under /gates/extra, and Go gates run (scoped, unscoped and inside block land) exits 2 before any gate runs with one line naming the key and gates.custom as 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 signed gate.deferred row (scope deferred, reason merge-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/**/*.mjs except scripts/build-native.mjs, package.json, package-lock.json) runs no full suite at all; land.batch records merge_gate_reason go_only or node_product (B398)
  • gates run --run R --block B --changed no longer falls back to the full Node suite for a Go-only block: every owned path is under native/, docs/, plans/ or .github/, a runnable test under test/, a *.md file or scripts/build-native.mjs (no src/, bin/, other scripts/*.mjs, package.json or package-lock.json). Go runs the test gate once with the block's tests, test/compat/go.test.mjs and the compat tests named after the block's Go files (status_direct.go → parity-status-direct); the result and gate.done carry reason: go_only and gate.done lists test_files. Blocks with a Node product file keep the old scope; block land --batch keeps its own plan (a full fallback defers to the one merge gate, B398) (B399b)
  • block ready and block close (Go) find review tickets by themselves: under --report, a changed file with no reviewed <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 with coder report FAILED: no review-file ticket id for <file>; report ticket lines stay optional and still win (B405)
  • A tracked .gitattributes sets CHANGELOG.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-coder and docs now have thin Node shims: with CODE_FORGE_NATIVE=0 (or no Go binary) --help/-h prints 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. help and the command reference list them from src/cli like 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}, gets a.b.FooTest,a.b.BarTest with gates run --run --block, and is dropped (full suite) when no test matches; paths outside every rule are ignored. New Go verb code-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 TAP not ok lines, and saves the whole suite output to ~/.code-forge/runs/<run>/land-logs/<block>-<time>.log with one line full log: <path>, so no 20-minute rerun is needed to find the failures (B374)
  • review-file enqueue, --wait and --wait-block run 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-block joins 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 land and block close --auto-claim now refuse to auto-claim .code-forge.yml while hooks.on_block_end is set, with the same reason, order and text as Node (B385)
  • gates run --run R --block B checks 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 fix code-forge block rebase <id> --run <R> --accept-landed. --auto-rebase runs that rebase and then gates; --allow-stale-base gates 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 sync fetches the published llms-full.txt and keeps it in ~/.code-forge/docs/ only when its version: line equals the installed version; otherwise, offline or on an HTTP error it keeps the packed copy and still exits 0. docs path prints the best copy, or exits 1 when there is none. upgrade runs docs sync best 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 run takes a full or changed slot before its test command, changed runs share max(1, min(cores/4, free GB/2)) slots, and a killed holder frees its slot within 2 seconds. code-forge queue shows holders and waiters, code-forge queue run -- <cmd> queues any other test command, status shows queued #k, and CODE_FORGE_TEST_QUEUE=off turns 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 same worker.reattach ledger row as run start --reattach and one stderr line worker: re-pinned run <id> from dead pid <old> to <new>, so a block close after a worker restart no longer stops on worker.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-2 in Node and Go; claude-fable-5-2 is a known model id, and claude-fable-5-1 stays known, so configs that name it still validate (B353)
  • Every context pack (Node and Go, byte-equal) has a ## forbidden section: 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 close no longer refuses unreviewed for an owned file the block did not change (equal to its base) when every review of its current content is a no_change result, as after block rebase --accept-landed onto 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 refuses no_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) and test/compat/go.test.mjs gives Go 10 minutes and prints every output line of a failed Go test; native-install runs npm pack on a private package copy, so a file another test adds to and removes from native/bin no 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_HELD set by gates run and code-forge queue run), and keeps max(1, cores/2)
    outside a slot; an explicit --test-concurrency or CODE_FORGE_TEST_CONCURRENCY still wins (B376)
  • block close and block ready judge review caps, reopened and unwaived findings, agent and autopilot waivers, review.agent_waivers.require_ack (agent-waivers-not-acked until 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 the agent_waivers, agent_reopens and accepted_hits JSON keys only when they are not empty. block close --help|-h and block human-pass --help|-h print the usage in Go and exit 0. Rows or dates Go cannot read exactly like Node still hand over as close-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 run keeps 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 tree field, 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 rebase replays first and refuses only a real conflict (Go) — owned files main changed no longer refuse
    up front: a clean replay moves the base, names them upstream_owned and queues them for review again; a real conflict
    restores HEAD and refuses rebase-conflict naming the files; CHANGELOG.md lines both sides only added are kept (main
    first), also under merge=union, in --rebase-check, --accept-landed and batch land (B431)
  • npm run release: when local npm test fails, 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.mjs and src/gates/invariants.mjs. src/config/schema-paths.mjs stays, 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