FrameForge v2 (2.6.0) keeps its documents, grammar, schema, prose, and
Python code in sync — the Pydantic models are the source of truth and everything
else is generated from or checked against them.
FrameForge aims to be an agent-native visual-authoring substrate — one
structured, programmable foundation (SDK + MCP) for producing professional visual
assets across documents, decks, diagrams, books, and letters today, extending
toward UIs, vector graphics, logos, and design systems. See PURPOSE.md
for the full why and scope.
Status: FrameForge v2 is a proposed, not-yet-conformantly-implemented system. The prose and grammar are design targets to verify. The parts you can actually run — the models, the generated schema, the validator, and the codemod — are the parts to trust.
src/frameforge/ ← the Python package (strictly downstream of the models — ADR-0002):
rendering/ ← renderer (DDD split): domain + application (the Renderer) + infrastructure.
sdk/ ← authoring SDK: builders/geometry/paint/widgets that lower into the models.
mcp/ ← MCP server: author→render loop + the coordinate/measurement tool layer.
vision/ ← raster→vector lane: measure/compare/vectorize/propose (optional deps).
coach/ ← Vector Construction Coach: style-grammar, layer order, silhouette gate.
live/ ← local web UI for live MCP feedback sessions (`make live`).
src/frameforge/model.py ← SOURCE OF TRUTH (Pydantic v2). Core conformance profile + all patches.
docs/schema/
frameforge-v2.schema.json ← GENERATED from the models (88 $defs). Do not hand-edit.
build_schema.py ← regenerates the schema; `--check` fails if it drifts.
docs/grammar/
frameforge-v2.ebnf ← the consolidated CORE grammar (base + P1–P4); styling deferred to the module.
frameforge-v2-style.ebnf ← the AUTHORITATIVE CSS style module (adopted verbatim at 2.2.0).
docs/spec/frameforge-v2-spec.md ← the normative prose (folds P1–P4 + the style module + cascade + corrections).
static/examples/ ← runnable SDK clients — indexed in docs/examples.md (GENERATED).
frameforge_logo.py ← the BRAND LOGO source of truth → generates the (out-of-tree) logo masters.
frameforge_seed_deck.py ← the canonical seed pitch deck (imports the mark + wordmark from above).
tooling/
validate.py ← structural (models) + static/geometric rules the schema can't express.
codemod.py ← migrates a document to HEAD (stroke split, size→sizing, gradient, aliases).
render_fixtures.py ← SVG render CLI driver (re-exports the Renderer; `--check-overflow` text-fit gate).
render_chromium.py ← optional Headless-Chromium SVG→PNG raster renderer (CSS-fidelity path).
render_fg_doc.py ← the matplotlib PROXY renderer, patched to HEAD (sanity check only).
pdf_to_frameforge_yml.py ← optional PyMuPDF PDF → fixed-layout FrameForge YAML extractor.
(HTML export moved into the package → `ff-render --to html`; the DocumentRenderer
port at src/frameforge/rendering/infrastructure/backends/html.py — contract-tested.)
gen_status.py ← GENERATES docs/FIXTURE-STATUS.md from the validator (`--check` gates drift).
gen_docs.py ← GENERATES the docs-site pages (reference/gallery/spec/grammar plus SDK docs).
gen_capability_manifest.py ← GENERATES docs/capability-manifest.json (core/SDK/MCP status per capability).
gen_examples_index.py ← GENERATES docs/examples.md (the examples cookbook index).
check_grammar_sync.py ← GATES grammar ⇄ models drift (core profile); `--strict` for full parity.
check_accessibility.py ← GATES page reading_order integrity; warns on missing image alt (a11y).
render_golden.py ← GATES b1/ oracle SVG output against a pinned hash lock (golden).
tests/fixtures/ ← the fixture corpus (declared versions span 2.0.0–2.4.x; top-level YAML gated by `make validate`, the b1/ oracle by test_head.py).
b1/ ← the 8 AUTHORITATIVE fixtures (the oracle the tests assert against).
conftest.py ← shared pytest bootstrap (sys.path + the frameforge shadow-module rule).
tests/
test_head.py ← assertions: authoritative fixtures validate, schema in sync, style surface, P3.
test_docs_in_sync.py ← doc drift gate: numbers, Layout paths, generated-doc policy, fixture status.
test_doc_examples.py ← validates every complete FrameForge example shown in the prose.
docs/ + mkdocs.yml ← the MkDocs site: `index.md` is hand-written, `sdk*.md` are committed generated snapshots, transient generated pages are ignored, and non-site sources (schema/spec/grammar, plus seed/ and decisions/) are `exclude_docs`.
docs/capability-manifest.json ← GENERATED machine-readable capability status {core, sdk, mcp} (ADR-0002).
docs/error-codes.md ← every validator finding code + SDK rule_id: meaning and fix (sync-tested).
docs/output-space.md ← what FrameForge can generate: the verified-today backends + the conceptual output space (anchor drift-gated by tests/test_output_space_doc.py).
docs/BRAND.md ← the brand guideline (proposal); §3 governs the generated logo.
docs/FIXTURE-STATUS.md ← GENERATED validator status for the delivered fixtures (gen_status.py).
docs/codebase-standards.md ← the elevated engineering bar, status-tagged (Enforced / Adopted / Target).
AGENTS.md ← programmatic CLI/tooling reference (make targets, tooling flags, workflows).
Dockerfile + docker/ ← the font-rich SDK/MCP runtime image (`make docker-build`).
CHANGELOG.md ← version, the breaking change + migration, conformance classes, rec. resolution.
pyproject.toml + uv.lock ← the real hatchling package since 2.5.0 (`[tool.uv] package = true`): `uv sync` installs it editable with the `ff-render`/`fg-font` console scripts; dep groups dev/render/browser/pdf/pdfout/metrics/mcp/vision.
Makefile + .github/workflows/ ← `make check` = the local gate; CI mirrors it (+ a docs build/deploy job).
- Schema ⇄ models.
docs/schema/frameforge-v2.schema.jsonis produced byDocument.model_json_schema().build_schema.py --checkreturns non-zero if the committed file differs from a fresh build — so they cannot silently drift. - Validator ⇄ models.
validate.pyvalidates against the sameDocumentmodel, then layers the §3.3/§3.6/§9.6 rules. - Codemod ⇄ validator. The codemod's migrations are exactly the breaking/renamed forms the validator rejects; running it makes a legacy document pass.
- Grammar ⇄ models. The EBNF is a view of the models (the source). This is now
enforced, not trusted:
tooling/check_grammar_sync.py(thegrammar-checkgate) introspects the models and diffs the EBNF, failing CI on core-profile drift — a mismatched object/flowtypediscriminator or a divergent enum. Out-of-profile grammar (charts, the UML zoo, connectors) is reported as a non-blocking warning;--strictdemands full parity.
The project is managed with uv. uv sync once
creates .venv with the runtime deps and the dev group (see
pyproject.toml), and installs the frameforge package itself — putting the
ff-render and fg-font console scripts on PATH; prefix commands with uv run.
uv sync # create/populate .venv
# schema is generated and in sync
uv run python docs/schema/build_schema.py --check
# validate the delivered tracked fixtures — 39/39 zero errors in docs/FIXTURE-STATUS.md
make validate
# migrate a legacy v2 document to HEAD
uv run python tooling/codemod.py path/to/legacy.fg.json --in-place --bump
# the HEAD assertions (13/13 green)
uv run python tests/test_head.py
# or the full pytest suite (also run by `make check`)
uv run pytest
# SVG proxy renderer (dependency-free core) -> out/render/index.html
uv run python tooling/render_fixtures.py --all
# optional browser-fidelity raster renderer (install Playwright + Chromium first)
uv sync --group browser
uv run playwright install chromium
uv run python tooling/render_chromium.py tests/fixtures/effects.fg.yaml --out out/chromium
# optional PDF text/layout extractor (install PyMuPDF first)
uv sync --group pdf
uv run python tooling/pdf_to_frameforge_yml.py input.pdf output.frameforge.yml
# optional MCP server for AI feedback loops:
# Python SDK code -> generated FrameForge YAML -> validation + rendered SVG/PNG,
# plus the coordinate-aware measurement layer (see "Subsystems" below)
uv sync --group mcp
uv run --group mcp python -m frameforge.mcp
# or: make mcp
# optional local web UI over the same MCP feedback functions
make live # http://127.0.0.1:8789
# choose a port when needed: make live LIVE_PORT=8790
# the whole local gate — every gate on the Makefile `check` target
make check
# build & browse the generated documentation site (Material theme, live reload)
make docs-serveThe matplotlib proxy renderer (tooling/render_fg_doc.py) needs the extra
render dependency group:
uv sync --group render # adds matplotlib + pillow- MCP measurement layer (
frameforge/mcp/+frameforge/vision/). Besides the author→render loop, the MCP server exposes a coordinate-aware raster→vector reconstruction toolset:measure_image(grids/rulers/regions/ landmarks/zoom crops),mark_points,overlay_images, a statefulworkspacepin board,construct_vectors,vectorize_image(region/outline/potrace/ layers tracing),score_reconstruction(numeric edge-match convergence), andmap_coordinates(homography/warp). The live tool registry is enumerated in docs/capability-manifest.json; it needs thevisiondependency group. Every runtime knob (FRAMEFORGE_*env vars — session/publish roots, transport budgets, render ceilings, Chromium flags, the VLM lane) is documented with its real default in the gated table at src/frameforge/mcp/README.md; container-only knobs live in docker/README.md. - Vector Construction Coach (
src/frameforge/coach/). A staged construction loop over the SDK — style-grammar checks, layer-order rules, a silhouette gate, SVG ingest/cleaning, and figure-proportion helpers. It coaches construction discipline; it is not a curve-drawing engine. Demos:static/examples/coach_demo.pyand the othercoach_*examples. - Docker runtime (
Dockerfile+docker/). The font-rich canonical SDK/MCP runtime (thousands of font families baked in) for font-faithful raster verification:make docker-build/docker-mcp/docker-shell/docker-fonts; client wiring indocker/mcp.docker.json. - Live UI (
frameforge/live/). A local web view over the same MCP feedback functions for humans watching/driving a session:make live(http://127.0.0.1:8789, port viaLIVE_PORT). - Font determinism toolchain (
src/frameforge/fontpack.py+tooling/render_chromium.py; ADR-0004).fg-font—--listresolvable families,--check DOC(non-zero exit if a content font would substitute),--pack DOC --out P.fp(portable pack of the exact TTFs + sha256 manifest;--fetchprovisions missing families from the open Google Fonts corpus so packs build on thin hosts),--install P.fp(scoped fontconfig) — andrender_chromium.py --font-pack P.fp, which scopes fontconfig to the pack before Chromium launches so measure == render on any host. Installed as a console script since 2.5.0:uv run fg-font …(or themake font-*targets;tooling/fg_font.pyremains as a direct-run shim). Silent font substitution is banned: layout emits afont_substitutionwarning to diagnostics and stderr whenever a requested face is missing.
Programmatic entry points for all of the above — every make target and tooling CLI with flags — are catalogued in AGENTS.md; the examples cookbook is generated at docs/examples.md.
At 2.2.0 the authoritative CSS style module is adopted verbatim (Style is the
~80-property bag; TextStyle/StrokeStyle are projections of it; fill/stroke are
Paint; gradient stops use position; class + css escape). 2.1.0 folded Patches
1–4, made the stroke single-form breaking, renamed size → sizing, generated the
schema from Pydantic, and added the validator + codemod. Full detail in CHANGELOG.md.
These predecessor documents are not included in this HEAD bundle — their content is folded into the artifacts above; they are listed only for historical context:
FrameForge-2.0.0-Specification.md— the spec reverse-engineered from the renderer, superseded bydocs/spec/frameforge-v2-spec.md.FrameForge-2.0.0-Specification-Complement.md— the reconciliation that produced the recommendations this release implements; its §8 actions are resolved inCHANGELOG.md.- The four standalone patch documents (P1–P4) are folded into the grammar, the spec, and the models here; they remain useful as rationale.
- This is a proposed format. No renderer is conformant; the proxy renderer uses DejaVu stand-in fonts and is a sanity check, not a fidelity guarantee.
- The grammar is consolidated and paren-balanced and is a view of the models — now
kept honest by the
grammar-checkgate (check_grammar_sync.py) for the core profile; the models remain the authority if the two ever disagree. - Font pinning enables deterministic layout only up to a stated rounding tolerance (a defined shaping model is also required) — not pixel-exact identity (§9.6).
- The current delivered top-level fixture status is generated in
FIXTURE-STATUS.md; at this snapshot 39/39 have zero errors. Advisory warnings, when present, are recorded there instead of summarized by hand here. - What FrameForge can — and deliberately will not — generate is mapped in docs/output-space.md: the backends wired today (whose entry points are drift-gated) plus the conceptual output space the IR admits.
- How FrameForge presents itself — name, voice, colour, type, and the logo — is the
docs/BRAND.md guideline (a proposal). The logo is generated: the
mark/wordmark source of truth is
static/examples/frameforge_logo.py, which writes
the masters out of tree (
_tmp/brand/) — brand assets are non-core and are not tracked in this repository.