A self-contained, deterministic work-session completion gate.
pip install assay-forge • import assay
assay takes a work session (a ticket plus the agent's recorded acceptance
checks, implementation-step completions, file changes, and test results), runs
four independent coverage checks, and reports a completion gate — with no
LLM, fully deterministic. Same input, same result, every time.
It is a faithful Python port of the SpecForge @specforge/assayforge engine,
extracted as a standalone library, and is the work-session gate of
SpecSmither (sitting beside the
planning gate, crucible). The output
is byte-equivalent to the reference TypeScript engine, verified by differential
tests across single-check and full-lifecycle flow snapshots.
Why
- 🎯 Deterministic — pure logic, no model calls. Reproducible in CI.
- 📦 Self-contained — pure Python; only
pydanticandpyyamlat runtime. - 🔬 Faithful — output matches the reference TS engine (differential-tested).
- 🎚️ Configurable — every check and severity knob lives in config.
- 🏷️ Typed — ships
py.typed; passesmypy --strict.
Install
pip install assay-forge # → import assay
# or
uv add assay-forge
Requires Python ≥ 3.11.
Quick start
from assay import assay, AssayContext, AssayOptions, deterministic_id_generator
context = AssayContext.model_validate(work_session_dict)
result = assay(context, AssayOptions(mode="action", action="check-imp-step"))
print(result.passed) # overall gate: no finding has severity 'error'
print(result.next_step) # in action mode: what to do next (+ guidance)
# Canonical camelCase JSON (matches the reference engine):
result.to_json_dict()
What it computes
assay(context, options) → AssayResult: four independent checks over the
normalized work-session event tables, then a single gate
passed = no finding has severity 'error'.
| Check | Reads |
|---|---|
acceptance-coverage |
acceptanceChecks[] vs the ticket's acceptance criteria |
implementation-coverage |
implStepCompletions[] vs the ticket's implementation steps |
file-action-coverage |
fileChanges[] vs the ticket's planned files (+ justification states) |
test-result-presence |
testResults[] vs the ticket's test specification (+ skip/failure justification) |
Two modes:
action— computesnext_step(the next thing the agent should do) with per-finding verbosity dispatch. Requiresoptions.action.check— computes asummary(pending_by_action,total_pending,blocking_issues).
Determinism is anchored by an injectable id generator (uuid_id_generator
default, deterministic_id_generator for tests) and a meta block
(engine_version, generated_at, config_hash).
Configuration
from assay import load_defaults, merge_config, config_hash
config = merge_config(load_defaults(), {"checks": {"test-result-presence": {"enabled": False}}})
context = AssayContext.model_validate({**work_session_dict, "config": config})
Severity of every check (and the per-justification severity knobs) is config-driven.
load_defaults() returns the packaged AssayforgeConfig (data asset at
assay/config/data/defaults.yml).
Public API
from assay import (
assay, ENGINE_VERSION, # entry point
AssayContext, AssayOptions, AssayResult, # core models
uuid_id_generator, deterministic_id_generator, # id generators
load_defaults, load_config, merge_config, config_hash, # config helpers
AssayforgeConfigSchema, validate_config, HARDCODED_DEFAULTS,
CHECK_REGISTRY, get_file_action_severity, # checks
resolve_next_step, applicable_actions, action_applies, # next-step
compose_finding_guidance, compose_next_step_guidance, # guidance
compose_check_summary, interpolate,
OPERATIONS, OPERATION_NAMES, is_operation_name, # operations vocab
types, # full type namespace
)
Development
uv sync --all-extras --dev
uv run ruff check src tests
uv run mypy
uv run pytest # incl. differential vs the TS engine
Fidelity is verified against committed golden output captured from the reference
engine (fixtures/golden/), so CI needs no Node tooling.
Releasing
assay-forge publishes to PyPI
automatically via GitHub Actions
(publish.yml) using Trusted Publishing
(OIDC — no API tokens stored in the repo).
One-time PyPI setup (by a maintainer): add a pending publisher for the
assay-forge project — owner blacksmithers, repository assay, workflow
publish.yml, environment pypi.
Cut a release:
- Bump the version in
pyproject.tomlandsrc/assay/__init__.py, and add aCHANGELOG.mdentry. - Tag and push — the tag triggers the workflow (build →
twine check→ publish):git tag v0.1.0 git push origin main --tags
The workflow can also be run manually from the Actions tab
(workflow_dispatch). Build locally any time with uv build (artifacts in
dist/).
Status
0.1.0 — a complete port, verified byte-for-byte against the reference TS
assayforge engine across the single-check and full-lifecycle flow surfaces,
both action and check modes.
License
Apache 2.0 © Gabriel Augusto Gonçalves / blacksmithers
Metadata
Release files for assay-forge 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| assay_forge-0.1.0.tar.gz | 40.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| assay_forge-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 109.2 kB
Release files / assay_forge-0.1.0.tar.gz
| Download URL | assay_forge-0.1.0.tar.gz |
|---|---|
| Size | 40.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
02b63fb20ace8d032e02e3e6b8e286b958b2e81b9dbbabc000388e908a88bdea
|
|
BLAKE2b-256 checksum How to use checksums |
3586466f087deff8f1506b3f6e9dcdc8bc6a9bd8a52f109fe7352eda6cf61699
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 26, 2026.
Transparency logRelease files / assay_forge-0.1.0-py3-none-any.whl
| Download URL | assay_forge-0.1.0-py3-none-any.whl |
|---|---|
| Size | 68.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0657b40822c61c269a4148a55017c4b24d6e1ca936bea9348c6edcb30b254dd6
|
|
BLAKE2b-256 checksum How to use checksums |
0c70ef225540b3ff58d34f16f445fa97c8d7e188bfd721e56d5e76fe81edb36f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 26, 2026.
Transparency log