FastPDLC
Product-as-code as a validated graph — for any project.
Your product intent — the glossary, the constraints, the business rules, the features, the decisions — usually lives as hopeful markdown that quietly rots. FastPDLC turns it into code: you declare your typed artifacts in one config file, and it loads them, enforces the schema and the cross-references, compiles a JSON bundle your app or docs can render, and fails CI when the committed bundle drifts.
It started as the product-as-code engine inside a payments platform and was extracted so any team can use it.
pip install fastpdlc
Quickstart
-
Describe your artifact types in
product.config.yaml:product_dir: product output: build/product.generated.json types: - name: terms # a glossary dir: terms id_prefix: "TERM-" # ids must be TERM-<slug> and match the filename required: [id, term, definition] fields: [term, definition, see_also] references: - field: see_also # every see_also must resolve to another term to: terms - name: rules # the durable invariants dir: rules id_prefix: "BR-" required: [id, title, statement] fields: [title, statement]
-
Author artifacts as markdown-with-frontmatter under
product/:--- id: TERM-payment term: Payment definition: An instruction to move money between two parties. see_also: [TERM-ledger] --- The canonical unit of work in the system.
-
Build the bundle and gate it in CI:
fastpdlc build # -> build/product.generated.json (commit it) fastpdlc validate # schema + graph + staleness; non-zero exit on errors
What validate enforces
Every finding carries a stable PAC-NNN code (an API — CI and dashboards match on the
code, never the prose):
| code | meaning |
|---|---|
PAC-001 |
an artifact is missing a required field |
PAC-010 |
an id doesn't start with its type's id_prefix |
PAC-011 |
an id doesn't match its filename |
PAC-012 |
a duplicate id within a type |
PAC-020 |
a reference field doesn't resolve to a known artifact |
PAC-030 |
a field value isn't in the type's allowed set (enums) |
PAC-060 |
the committed generated bundle is missing or stale |
Config reference
Each entry under types:
name— the collection name (and the key in the bundle).dir— the subdirectory underproduct_dir.id_prefix(optional) — required id prefix; withid_matches_filename(defaulttrue), the id must equal the filename stem.required— frontmatter fields that must be present and non-empty (idalways is).fields— the fields captured into the bundle.enums—field -> [allowed values].references—[{ field, to }]: values offieldmust be the id of atoartifact.
Extending it — plugins
Real projects need more than schema: cross-file checks ("does this links.code path
exist?"), derived bundle fields (reverse edges, rollups), extra generated outputs (a
runtime catalogue), and their own diagnostic codes. A plugin registers those without
forking the engine — which is how a large project migrates onto FastPDLC with no loss
of functionality:
# product_hooks.py
from fastpdlc import register
def register(reg):
register("PAC-900", "links.code path does not exist on disk")
@reg.validator
def code_paths_exist(bundle, config, root, report):
for f in bundle["features"]:
for path in f.get("code") or []:
if not (root / path).exists():
report.add("PAC-900", f"missing {path}", f["_file"])
@reg.bundle_transformer
def reverse_edges(bundle, config, root):
... # enrich the bundle in place
reg.extra_output("build/catalogue.json", render_catalogue) # staleness-gated too
fastpdlc -p product_hooks.py validate
Start a new repo in one command
Scaffold a ready-to-go product-as-code repo (config, example artifacts, and the CI gate) with the copier template:
pipx run copier copy --trust gh:tarvitave/fastpdlc my-product-repo
--trust lets the template run fastpdlc build once so the new repo is valid on its
first commit.
CI
Use the reusable Action — it installs FastPDLC and runs the gate:
# .github/workflows/product.yml
name: product-as-code
on: [pull_request, push]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: tarvitave/fastpdlc@v0.1.0
with:
config: product.config.yaml # optional (default)
plugin: product_hooks.py # optional project checks
Prefer plain pip?
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install fastpdlc
- run: fastpdlc validate
Used in production
FastPDLC is the product-as-code engine of the pharthing / KibiPay payments
platform (39 features, a concept catalogue, and a ~283 KB render bundle). pharthing's
CI runs fastpdlc validate as its sole gate via a plugin that adds domain checks — a
byte-identical parity test proves nothing was lost in the extraction. That's the
plugin system above, doing real work.
Releasing
Publishing to PyPI is automated via GitHub Releases + Trusted Publishing — see RELEASING.md.
License
LGPL-3.0-or-later (copyleft, but you can import it as a library without your project inheriting the licence). See LICENSE.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastpdlc-0.1.0.tar.gz.
File metadata
- Download URL: fastpdlc-0.1.0.tar.gz
- Upload date:
- Size: 17.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
faf2b62aa47edca008033dcf2fe1aeb04c205e1728946c53e36cfd2b036a5b33
|
|
| MD5 |
d58506ca20fd647bf176fc6ca4a8da3d
|
|
| BLAKE2b-256 |
66c5a2f3426ea908c5aa03c2ff20a80d26f87feff84f59db0646b5bd12089b97
|
Provenance
The following attestation bundles were made for fastpdlc-0.1.0.tar.gz:
Publisher:
publish.yml on tarvitave/fastpdlc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastpdlc-0.1.0.tar.gz -
Subject digest:
faf2b62aa47edca008033dcf2fe1aeb04c205e1728946c53e36cfd2b036a5b33 - Sigstore transparency entry: 2568843920
- Sigstore integration time:
-
Permalink:
tarvitave/fastpdlc@d89dd5c3cd24510c229362f71abc4c4b964f4fb3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/tarvitave
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d89dd5c3cd24510c229362f71abc4c4b964f4fb3 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file fastpdlc-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fastpdlc-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a8d8b92ac280a7903ae23f23a261fc9512e7e746907cb3e731eac24ccde3333a
|
|
| MD5 |
84efb2911b6c518cdd9fbbeab2dbc0d8
|
|
| BLAKE2b-256 |
321d2be25eaabbf88bb3b155ce2f4589efd9dbdc4475cee846161d8da4e6c80a
|
Provenance
The following attestation bundles were made for fastpdlc-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on tarvitave/fastpdlc
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastpdlc-0.1.0-py3-none-any.whl -
Subject digest:
a8d8b92ac280a7903ae23f23a261fc9512e7e746907cb3e731eac24ccde3333a - Sigstore transparency entry: 2568843922
- Sigstore integration time:
-
Permalink:
tarvitave/fastpdlc@d89dd5c3cd24510c229362f71abc4c4b964f4fb3 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/tarvitave
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d89dd5c3cd24510c229362f71abc4c4b964f4fb3 -
Trigger Event:
workflow_dispatch
-
Statement type: