Releases: prabhuakshay/forge
Release list
v0.11.1
Changed
/forge:releasecommit message now follows the conventional-commit formchore(release): release vX.Y.Zinstead ofRelease vX.Y.Z, and the command now has an explicit commit step.- Changelog guidance for
/forge:releasenow records only the net user-visible change in a release, not intra-release churn: something added and then removed within the same release is listed nowhere, and something added and then fixed within the same release is listed only as the final working feature.
Fixed
- README layout table brought back in sync with the tree: the
agents/,lib/, andreferences/listings now include thesecurity-auditoragent, thesecurity/versionsmodules, and thefastapi/library/pytestreferences added in 0.11.0.
v0.11.0
Added
- Version-agreement check in
/forge:audit. The mechanical audit now refuses
to pass when a project declares its version in more than one place that
disagree —pyproject.toml [project], a package__version__, and every
.claude-plugin/*.jsonmanifest (top-level and nested plugin entries). This
caught a real drift:pyproject.tomlwas0.10.0while the plugin/marketplace
manifests were still0.9.1. (lib/versions.py) - Security scanning in
/forge:audit. Optionalpip-audit(dependency CVEs,
blocking) andbandit(risky code patterns, advisory — or blocking under
FORGE_SECURITY_STRICT=1) scans. Both degrade to a hint when the tool isn't
installed, so forge never forces the dependency. (lib/security.py) python-security-auditoragent for logic-level security review of changed
code in/forge:review, complementing the mechanical scanners.- Override hygiene.
/forge:override listprints the consumed-bypass trail and
/forge:override prune [keep]compacts it;/forge:statusnow warns when the
trail piles up (a signal a gate may be mis-calibrated). FORGE_COVERAGE_FLOORenv var to override the default coverage floor (80),
mirroringFORGE_GATE_TIMEOUT. A project's ownfail_understill wins.- Three new style references:
fastapi,pytest, andlibrary(packaging /
library-authoring conventions), installable via/forge:reference add.
v0.10.0
Added
- Dual Docker setup for all projects:
Dockerfile(production, multi-stage optimized) andDockerfile.dev(development with watchfiles hot reload). Includes.dockerignorefor efficient builds. - Docker Compose files:
docker-compose.yml(production with health checks and restart policy) anddocker-compose.dev.yml(development with volume mounts for live code changes). - Django-specific Docker:
Dockerfile.djangoandDockerfile.django.devwith proper Django setup (gunicorn, migrations, collectstatic). Includesdocker-compose.django.ymlanddocker-compose.django.dev.yml. - Django health check integration: Management command template (
healthcheck.py) automatically created for Django projects. Docker health checks integrated by default viapython manage.py healthcheck(checks database connectivity).
v0.9.1
A correctness-and-clarity patch.
Fixed
- Concurrent
/forge:decideruns can no longer collide on an ADR number. Number allocation was the one state mutation that ran without the.forgelock, so two racing decisions could both claim0004— duplicate ADRs and an ambiguous directive back-reference. Allocation and the ADR/directive/index writes now run together understate.lockedvia a newdecisions.record_decision, and the ADR is written atomically (temp +os.replace) and first, so a crash mid-write can at worst orphan an ADR — never leave a directive pointing at a number that was never written. - The coverage-floor check no longer trips on a comment. Detection of whether a project sets its own floor is now section-scoped:
fail_underonly counts in[tool.coverage.report],--cov-fail-underonly in[tool.pytest.ini_options], comments ignored. - The commit-time gate now measures coverage like the rest. forge's prek
pytesthook ran without--cov; it now runspytest -q --cov, matching the dev command and the in-session gate.
Changed
- Clearer command guidance.
/forge:buildstates the two--no-docsskip conditions as independent;/forge:initlists concrete type-detection markers instead of "from any existing files"; and the scaffoldedCLAUDE.mdnow documents thescripts/check_env_sync.pyguard that was already wired into pre-commit.
Full changelog: v0.9.0...v0.9.1
v0.9.0
forge v0.9.0 hardens the core that the whole enforcement model rests on — the
workflow state and the green-gate fingerprint — and pins down the supported
platform.
Fixed
- The green-gate fingerprint no longer misses a same-second edit. A new
stat-keyed digest cache makescode_fingerprintskip files it can prove are
unchanged, but a naive(mtime, size)cache would wrongly trust a same-size
edit landing in the same clock-granularity tick as the last hash — the classic
"racy clean" problem, and not theoretical (fast/tmpfs writes hit it). The cache
now applies git's rule: an entry is trusted only when the file's mtime is in a
strictly earlier whole second than the recorded cache build time. A
same-second change is always re-hashed; the content-addressed guarantee holds.
Changed
- Workflow state is now concurrency-safe. Claude Code can run tools (and
hooks) at once; two updaters racing on a read-modify-write ofstate.json
could let oneos.replacesilently drop the other's change — a lostdirty_py
entry, or worse a droppedoverridesaudit record. Every mutation now runs
under an exclusive POSIX advisory lock (.forge/.state.lock). code_fingerprintonly re-reads what changed, via a git-ignored
.forge/fpcache.jsoncache, so the per-commit hash stays proportional to the
edit rather than to repo size. The logic moved into a new dependency-free
lib/fingerprint.py;statere-exports it, so callers are unchanged.
Documentation
- forge now states its requirements plainly: Python projects only, and
Linux/macOS only — the hooks invokepython3and the state relies on POSIX
file locking (fcntl), so Windows is unsupported. The manifests carry the same
constraint. docs/state-schema.mddocuments the two new local-only sidecar files.
Internal
- A meta-test asserts the prompt layer stays wired up: every
bin/*.pya command
invokes, every agent it names, and everyhooks.jsonscript must resolve to a
real file — so a rename or typo there can't ship undetected.
Full changelog: v0.8.0...v0.9.0
v0.8.0
forge v0.8.0 tightens the env-drift gate, sharpens the docs, and removes dead code — a correctness-and-honesty release.
Changed
env_scandetects the env reads it was silently missing. The drift scan now recognisesos.environ.pop(...)andos.environ.setdefault(...)(both read the variable, just like.get()), and captures variable names in any case rather thanUPPER_CASEonly. Environment variables are case-sensitive on POSIX, so a read likeos.getenv("debug_mode")is real config that onboarding must document — matching only[A-Z…]quietly dropped every lower/mixed-case read on the floor. The drift check compares names verbatim, the way the OS resolves them (pydantic-settings stays case-folded, since it resolves env vars case-insensitively). This makes the/forge:auditenv check and the Stop gate's drift check catch undocumented configuration they previously let through.
Removed
lib/state.clear_dirty— dead code with no caller (record_passclears the dirty set inline). No command, hook, or documented behaviour referenced it.
Documentation
- README now states plainly that only two agents are evidence-bound —
doc-sync-auditorandreference-auditormust cite afile:linefor every finding — whilepython-quality-auditor,doc-gap-scanner, andpython-test-authorare advisory/generative. - README's Stop gate description now says when it fires (turn-end), what it runs (format/lint whole-tree, mypy scoped to changed files and skipped when nothing is dirty, env drift — never the test suite), and that failures are fed back rather than walling off.
docs/state-schema.mddocuments thatdirty_pyclears only on a greencheck, so it intentionally accumulates pastaudit/reviewpasses./forge:docsreport header no longer looks like a subcommand;/forge:releasegives the concrete command for listing commits since the last tag; thelib/layout list now includesstatus(caught by the doc-sync auditor during this release).
Internal
- Closed branch-coverage gaps in
lib/cmdscan(91%→98%) andlib/state(89%→96%); suite total 94%→96%, 272 tests.
Full changelog: v0.7.0...v0.8.0
v0.7.0
This release hardens the enforcement layer and closes a consistency gap in how the review gate was documented. It's backward-compatible — no command contract or .forge/ layout changed.
Robustness
- State is now persisted atomically.
lib/state.savewrites a temp file andos.replaces it into place. A torn write was previously read back as corrupt JSON and silently reset to the empty skeleton — dropping recorded passes and the overrides audit trail — and two concurrent tool calls could lose each other's writes. The audit trail is the whole point of the override mechanism, so it must never vanish silently. - Gates check freshness before consuming an override.
require_check,require_review,require_audit, andrequire_plannow confirm the gate is actually unsatisfied before taking a one-shot override — so a green/satisfied action never burns (or falsely logs) a bypass that wasn't needed. require_planonly treats a plan with unchecked items as active. A completed plan (every item ticked) or an empty file no longer holds the gate open, so finishing a plan correctly re-demands a new one before more source is written.auto_formatis scoped to forge-enabled projects, like every other hook — no more silentruffruns on.pyedits in unrelated repos.require_reviewdecodes git's C-quoted paths (octal-escaped non-ASCII / special filenames) before matching them against reference globs, so the reference half of the gate no longer misses a governed file with such a name.
Added
bin/plan.py active <path>(backed bylib/state.set_active_plan) —/forge:planrecords the active plan through a helper instead of hand-editingstate.json, where a malformed write would corrupt workflow state. This was the last state mutation without a dedicated entrypoint.
Documentation
- Propagated the review gate into every place it had drifted from:
/forge:status, the scaffoldedCLAUDE.mdtemplate,/forge:init's loop string, and the README loop diagram — and qualified the README's auto-format claim to match its new forge-scoped behaviour. doc-gap-scannerno longer assumes the plugin's own layout;src/**is the universal case and plugin-only kinds are optional.
Quality
Coverage of lib/ rises to 94% (gate runner 90% → 99%): added unit tests for the subprocess timeout and full/fast gate aggregation, atomic-write failure cleanup, the override-not-consumed-when-satisfied path across all four gates, git C-path unquoting, and the completed-plan case. 258 tests, green across Python 3.10–3.13.
Full changelog: v0.6.0...v0.7.0
v0.6.0
Added
- Review gate (
require_review) —git commitis now blocked until
/forge:reviewis green for the current tree, closing the gap where binding
directives and blocking style references were enforced only by an optional
command. It binds only where there's something to enforce (option B): a
recorded directive, or an installed reference governing a file in the change
set — projects with neither are unaffected. Like the other gates it works on a
fingerprinted pass (last_review, recorded by/forge:reviewvia
bin/mark.py review), is invalidated by any.pyedit, and honours a one-shot
logged override (/forge:override review "<why>")./forge:statussurfaces the
review gate when it applies. lib/decisions.binding_directive_count/has_binding_directives— distinguish
a project that has actually recorded a directive from one that only carries the
scaffolded template prose (the signal the review gate keys on)./forge:status
now uses this for its directive count.
Changed
references.for_filebreaks an equal-specificity tie deterministically:
blockingreferences outrankadvisoryones (then name), and the
reference-auditordocuments the rule./forge:initpins the default Python version (3.13) instead of "a current
stable, e.g. 3.12";/forge:builddocuments that it is resumable (works only
unchecked items).
v0.5.0
forge v0.5.0 adds two utility commands and makes the type-check step adapt to projects that don't use mypy.
Added
/forge:status— a one-screen snapshot of where a project stands: phase and active plan, each gate (check/audit) as green-for-the-current-tree / stale / never-run, the dirty set, installed style references, the binding-directive count, plus any armed one-shot override (about to fire) and recent override history. Pure read; the report builder lives inlib/status.py./forge:override <gate> "<reason>"— an ergonomic, logged front door to the one-shot bypass the hooks already honour (gates:check,audit,stop,plan,uv). It arms.forge/override-<gate>with the reason baked in; the next matching gated action consumes it and records the bypass. Writing the sentinel by hand still works. The hook deny messages anddocs/state-schema.mdnow point at the command.
Changed
- The type-check step is now opt-in by configuration. mypy runs only when the project configures it (
[tool.mypy],mypy.ini/.mypy.ini, or a[mypy]section insetup.cfg); otherwise the step is skipped, not failed. Previouslyuv run mypyon a project that never installed mypy would fail the gate for a tool it deliberately doesn't use. forge-scaffolded projects ship[tool.mypy], so they keep type-checking unchanged.
Full changelog: https://github.com/prabhuakshay/forge/blob/main/CHANGELOG.md
v0.4.0
Added
- uv-only dependency enforcement. A new
require_uvPreToolUse hook blocks
non-uv dependency commands (pip install,uv pip install,poetry/pipenv,
conda,pip-compile,easy_install, …) in forge-enabled projects, steering
all dependency changes throughuv add/uv removesopyproject.tomland
uv.lockstay the single source of truth. Read-only commands (pip list,
uv pip freeze) are untouched, and.forge/override-uvis the logged one-shot
escape hatch. The policy is also stated as a binding rule in thepython-base
reference and the scaffoldedCLAUDE.md. cmdscan.dep_install_command()— parses a Bash line and names the first
non-uv dependency invocation it finds (reusing the tokenizer behind the
commit/push guards).