This release is a pre-release and may not be stable for production use.
ForgeWire Anvil
A local-first planning ledger for human and agentic work.
Projects, issues, boards, sprints, workflows, and typed relations — provider neutral, auditable, dependency-free, and fully usable with no network and no external issue-tracker account.
pip install forgewire-anvil
anvil project create PF "Platform"
anvil issue create PF "Ship the thing" --priority high --label release
anvil transition PF-1 todo
anvil relation add PF-1 fabric_task task-42 --label "Build"
anvil validate
from forgewire_anvil import Anvil, IssueRelation
anvil = Anvil() # user-scoped ledger; honors ANVIL_DATA_DIR
await anvil.provider.create_project(key="PF", name="Platform")
issue = await anvil.provider.create_issue(project_key="PF", summary="Ship it")
await anvil.provider.add_relation(issue.key, IssueRelation(
source_key=issue.key, target_type="fabric_task", target_id="task-42",
))
Status: developer preview (
0.1.0a0). The API and on-disk schema may change between releases — pin an exact version. The JSON store is single-writer; read docs/limitations.md before pointing more than one process at a ledger.
Why
Planning tools assume a server, an account, and a human. Anvil assumes none of them. It is a ledger you own, on disk, in plain JSON you can read — designed so agents and people can plan against the same records without a hosted service in the middle.
Being local-first is the point, not a limitation:
- Yours. Plain JSON in a directory you choose. No account, no telemetry, no network calls. Ever.
- Durable. Atomic writes, schema versioning, and lossless export/import. Fields a newer build wrote survive an older build reading and re-saving them.
- Provider-neutral.
IssueTrackerProviderandIssueStoreare protocols; the built-in provider and JSON store are just the default implementations. - Dependency-free. No runtime dependencies. The CLI is stdlib
argparse.
What Anvil owns — and what it doesn't
Anvil owns projects, issues, planning lifecycle, workflows and transitions, boards and sprints, comments and activity history, issue-to-issue links, typed relations to external work objects, local storage, schema migration, and import/export.
Anvil does not own execution. It links to and summarises execution records; it is not the execution authority, and its activity log is a convenience history rather than an audit trail. Signed remote execution, host execution, repository governance, and approval workflows belong to the systems that own them.
That boundary is the design: cross-system references are typed relations and summarised events, never duplicate authoritative records.
Where data lives
Resolved in this order:
- an explicit path (
Anvil("/path"), oranvil --data-dir /path); - the
ANVIL_DATA_DIRenvironment variable; - a per-user platform directory:
- Windows —
%LOCALAPPDATA%\ForgeWire\Anvil - macOS —
~/Library/Application Support/ForgeWire/Anvil - Linux/BSD —
$XDG_DATA_HOME/forgewire/anvil(else~/.local/share/forgewire/anvil)
- Windows —
There is deliberately no working-directory-relative default: an operational ledger that follows your shell around scatters itself across checkouts.
Each project is a directory of human-readable JSON:
<data-dir>/
├── .anvil-store.json # schema version marker
└── PF/
├── project.json # metadata + next-issue counter
├── issues.json # every issue, with comments, links, relations, history
└── boards.json # boards, with embedded sprints
Already have a ledger somewhere else? anvil migrate store <source> adopts it
losslessly and only ever reads the source.
Errors
Anvil raises; it does not wrap. Every failure is an IssueTrackerError subclass
carrying a stable error_code (ISSUE_NOT_FOUND, INVALID_TRANSITION,
VALIDATION_ERROR, …), so an embedding application maps errors once at its own
boundary instead of unwrapping a second envelope.
The CLI surfaces the same contract: exit 0 on success, 1 on a
planning-domain error (with its code on stderr), 2 on a usage error. Add
--json to any command for machine-readable output.
Relations vs. typed_links
Issue.relations is the forward canonical field for provider-neutral links to
non-issue work objects. Issue.typed_links is an earlier name for the same
records, kept as a compatibility read window and still written as a mirror,
so downlevel readers keep working. Legacy typed_links-only data upgrades via:
anvil migrate relations
The migration is lossless, idempotent, and preserves unknown fields. No
deprecation of typed_links is scheduled; it will not be removed without a
migration path and a stated window.
Development
pytest # no install needed: pyproject sets pythonpath
python -m build
Requires Python 3.11+. Tested against CPython 3.11, 3.12, and 3.13.
Contributing
Anvil is developed in ForgeWire's private monorepo and published here as a mirror, so this repository does not accept pull requests — a sync would overwrite them. Issues and discussion are welcome and read.
Licence
Metadata
Release files for forgewire-anvil 0.1.0a0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| forgewire_anvil-0.1.0a0.tar.gz | 54.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| forgewire_anvil-0.1.0a0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 96.4 kB
Release files / forgewire_anvil-0.1.0a0.tar.gz
| Download URL | forgewire_anvil-0.1.0a0.tar.gz |
|---|---|
| Size | 54.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3c8f374ad73cc75ddbddaf036c50d99d79a5975129928c5bbc1f8bafa563a7d1
|
|
BLAKE2b-256 checksum How to use checksums |
f43d77043067ed0d23e4372febd44708708fc5f3222ca60606cbb7d819bc06e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.13
|
Release files / forgewire_anvil-0.1.0a0-py3-none-any.whl
| Download URL | forgewire_anvil-0.1.0a0-py3-none-any.whl |
|---|---|
| Size | 41.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
41f327a8422e03dff7e40d5a515bd6f2429a508c212bfe1eff13c40c36449c92
|
|
BLAKE2b-256 checksum How to use checksums |
7a19d7e3e0c1d4b29ca9318a8bfc2eb3eff81fce9c66ee8bf7c7b790f84c5503
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.13
|
Security for forgewire-anvil 0.1.0a0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.