Skip to content

Reviewed automation and template-release comparison

Evaluation for #393, 2026-10-24, using the common baseline and actual independent patch results from #392. All four approaches can hand the same reviewed stable patch to Forge’s existing eligibility selector. None replaces its identity, commit, file-scope or unreleased-source policy. Candidate metadata files introduce an explicit integration decision; they are rejected by the current production allowlist rather than implicitly trusted.

Public-boundary rehearsal

The fixture invokes the unchanged scripts/select-automatic-release.ts CLI against fresh local Git repositories. Each has a previous published-baseline commit/tag, a reviewed patch commit and a local-only origin. Actual git diff, manifest reads, baseline fetch and since-release source comparison execute. The gh facade allows only the expected API reads and returns controlled associated PR, merged identity and tag responses; the npm facade permits only the expected previous-version metadata read. Every other operation, including publication, is rejected. No real API mutations or npm publication are performed.

Input is the preparation fixture’s observed model 1.2.0 -> 1.2.1 result for Forge, Changesets 3.0.3, Nx Release 24.0.1 and release-please 17.11.2. Each value is asserted against the literal expected patch. This is a metadata handoff, not a claim that each candidate natively evaluates GitHub App identity. The peer lane feeds that candidate patch into the same reviewed metadata shape. The template lane invokes Forge’s unchanged public prepare-template-sync.ts CLI with controlled verified-publication and registry metadata, obtaining generator 2.3.0 -> 2.3.1, model pin 1.2.1 and compatible CMS pin 0.251.0. It retains that domain preparer for every candidate; native candidate generation of the generator patch is not claimed. The synthetic template lock refresh is marked as simulated and does not prove installation, compatibility tests or a real registry attestation.

Reproduce after the normal locked Forge installation and the #392 preparation run:

RELEASE_TOOLS=/tmp/forge-370/tools/node_modules FORGE_SOURCE="$PWD" \
  node docs/reviews/fixtures/release-preparation/run.cjs
# Use the scratch location printed by that command:
PREPARATION_RESULTS=/tmp/forge-release-preparation-EXAMPLE/results.json \
  FORGE_SOURCE="$PWD" node docs/reviews/fixtures/release-automation/run.cjs

Results and full command logs are preserved in the printed temporary directory. Commits and tags are created only there; the fixture configures no network remote. No source workflow, eligible-file policy, publisher, template delivery or approval setting is modified.

Equivalent scenario results

Each baseline/finalist runs peer and template lanes against the same scenarios. A positive result must exit zero and print the exact selected package. Ordinary rejections must exit zero and visibly print eligible=false; invalid SHA and unavailable required registry state must fail with their expected reason. This prevents a broken facade or arbitrary process failure from counting as rejection.

Case, applied equally to every approachObserved outcome
Configured App, reviewed merged exact stable next patchEligible model or generator; unrelated package unchanged
Unmerged PR / wrong merged commitIneligible
Untrusted author / foreign head or base repositoryIneligible
Wrong target branch / source branchIneligible
Source implementation added to patch / another package mixed inIneligible
Prerelease / skipped patch versionIneligible
Missing changed manifest / missing changed changelogIneligible
Non-full release SHAExplicit CLI failure
No associated App PR / ambiguous App PR associationIneligible
Missing previous package tagIneligible, manual baseline notice
Previous package version unavailable from registryExplicit CLI failure
Unreleased implementation since previous published baselineIneligible, manual baseline notice
Template pins include prerelease modelIneligible; template lane only
Extra candidate metadata fileIneligible; each non-baseline candidate, both lanes

The artifact/publishing controls of #394 remain a separate seam: selecting a package cannot establish inspected-byte publication, registry completion, provenance or failure recovery.

Approval and metadata integration cost

The eligible CLI output is the same eligible=true / package=<slug> contract that the existing workflow uses to select npm-release-auto. Live environment inspection in #391 found that environment allows main and has no reviewer rule. Thus the retained workflow path adds no second environment approval after the reviewed merge. This local fixture proves selection and handoff, not actual human review or a hosted environment deployment. Ordinary/ineligible release intent still needs the existing manual tagged path and npm-release approval.

ApproachRetained policy and operating stepAdditional adaptation demonstrated or still needed
Forge baselineExisting reviewed peer/template PR, merged selector and automatic environmentNo new metadata convention
ChangesetsSame selector and template preparer, candidate patch handed offChangeset intent/state outside current allowlist rejects; integrate reviewed metadata generation and consumption before the eligible merge, or narrowly validate extra intent files in a later separately reviewed policy change
Nx ReleaseSame selector and template preparer, candidate patch handed offVersion-plan state outside current allowlist rejects; account for project configuration, generated metadata and plan consumption without granting source changes broad eligibility
release-pleaseSame selector and template preparer, candidate patch handed offManifest state outside current allowlist rejects. Default separate release PR is another operating/review step; no-second-approval requires intentionally preparing metadata in the existing reviewed automation PR or retaining the Forge exception path rather than blindly adding another release PR

The extra-file observations use representative .changeset/reviewed.md, .nx/version-plans/reviewed.md and .release-please-manifest.json paths; they prove the current selector rejects those paths, not the complete set of changed files for every possible tool configuration. A candidate can operate without introducing those files into the eligible commit only if its proposed adapter establishes reviewed intent elsewhere. No broad allowlist relaxation was implemented. An already functioning metadata calculator alone therefore does not eliminate this custom logic or establish a maintenance win.

Template policy remains unchanged

Generator releases remain justified by copied template content and exact pinned model/CMS/lock metadata, not a runtime dependency from create-forge to content-model. The current template-sync patch stays in place for every finalist. Downstream compatibility validation does not require arbitrary package publication.

If fresh resolution is adopted in a future approved change, an upstream release inside unchanged compatible bundled ranges need not trigger a generator release. Template source, ranges, pins and compatibility-rule corrections still require release assessment. #369 proposed retaining the bundled lock, so this evaluation neither activates that conditional policy nor deletes template-sync automation. Task-runner selection remains independent.

Verification and limits

The public selector rehearsal completed 170 cases across four approaches and two lanes: 8 eligible handoffs and 162 visible rejection/failure cases. The current selector cannot make a candidate’s metadata files eligible unchanged; that is a demonstrated integration limit, not a reason to weaken its safety rule. Focused fixture formatting/lint, Markdown/spelling/documentation checks and git diff --check pass. The existing 33 release regression tests also pass.

Not demonstrated: live review enforcement, candidate-created hosted automation PRs, end-to-end no-second-approval publication, actual template lock resolution, real trusted publisher configuration or native tool-specific automation policy. All finalists retain the same Forge policy adapter in this comparison; none gets native-capability credit for that shared custom layer.