Thanks for your interest. pf-core is a dependency-light Python foundation with opt-in extras; contributions that keep it lean, well-tested, and documented are welcome.
pf-core is infrastructure only. It must never contain business logic, domain
models, or project-specific configuration — anything that only makes sense
inside one application belongs in that application, not here. It does ship
generic entry points (the mountable admin routers, the pf-* console scripts);
those are fine because everything they touch is framework-owned.
A feature earns its place in pf-core when two or more independent projects
need it. A pattern that lives in a single project should stay there until a
second consumer wants it. See .ai/rules/scope.md.
Python 3.12+ is required.
git clone https://github.com/phierceweb/pf-core
cd pf-core
python -m venv .venv && source .venv/bin/activate
# Everything, so the full test suite runs:
pip install -e ".[full,otel,articles,anthropic,image-phash,dev]"
pre-commit installFor lighter work, install only the extras you're touching — the base
pip install -e ".[dev]" is enough for foundation-only changes. See
docs/INSTALLATION.md for the extras matrix.
These checks run in CI and as pre-commit hooks — run them locally first:
bin/test # full suite, must be green
bin/run ruff check src tests # lint
bin/run ruff format --check src tests # formatting (apply with `ruff format src tests`)
bin/run python -m mypy # type check (config in [tool.mypy])
bin/run python -m pf_core.guards # structural gate (reads .pf-guards.toml)bin/test and bin/run dispatch through .venv/ — the same wrappers
bin/new-consumer scaffolds into consumer projects. With the venv activated the
bare pytest / ruff commands work too.
And hold the change to these standards:
- Tests travel with code. New behavior needs tests; a bug fix needs a regression test that fails before your change and passes after.
- Docs travel with code. A change to a module's public API is incomplete
without the matching
docs/*.mdupdate — see.ai/rules/docs-sync.md. - File-size gate. Python files over the hard limit fail the build; over the
soft target they warn (canonical values in
pf_core/guards/config.py). Split by concern instead of growing a monolith — see.ai/rules/code-style.md. - Layering. Respect the layered architecture (repository → service →
orchestrator → entry point); no layer imports from a layer above it. See
.ai/rules/layering.md.
The full set lives in .ai/rules/. The essentials:
- Modern Python 3.12+ syntax —
X | None, lowercasedict/list/tuple. - Type hints on every public signature; Google-style docstrings on public APIs.
- Structured logging via
pf_core.log.get_logger(__name__)— never bareprintoutside CLI entry points. - Raise from the
pf_core.exceptionshierarchy — never a bareException.
Stability lives in tags. main may contain unreleased work between version tags — pin to a tagged release (or a published version) for production use.
- Pre-1.0 (now): a minor bump (
0.X.0) may include breaking changes, always called out inCHANGELOG.md; a patch bump (0.0.X) is fixes only. - From 1.0: semantic versioning — patch = fixes, minor = additive and backward-compatible, major = breaking. Anything documented under
docs/is the stable surface that contract covers.
A deprecated API keeps working and emits a DeprecationWarning naming its replacement, for at least one minor release (pre-1.0) or until the next major (post-1.0) before removal. Current deprecations:
pf_core.clients.routing.get_routed_client(use_claude_code)→ usepf_core.llm.router.resolve_agentorget_client_for_backend.pf_core.llm.tracking.track_run()without an explicitprovider=→ pass the backend (e.g.resolve_agent(...).backend) orprovider=None.
Contributions are accepted under the Apache License 2.0. Opening a pull request licenses your work to the project under those terms.
Open an issue for bugs and feature requests. For anything security-sensitive,
follow SECURITY.md instead of filing a public issue.