Contribution Workflow and Quality Gates
PHIDS is not an unconstrained Python project. It is a deterministic simulation system with strong architectural rules, benchmark-sensitive hot paths, and a dual interface surface that must remain internally consistent. This chapter documents how a contributor should approach changes in the current repository.
Development as Controlled Change
A useful way to think about PHIDS development is that every change touches one or more of four boundaries:
- validated ingress — schemas and scenario language,
- runtime execution —
SimulationLoop, engine systems, ECS, and biotope layers, - operator surfaces — REST, WebSocket, and HTMX/Jinja UI,
- analytical outputs — telemetry, replay, exports, and documentation.
Good contributions preserve the invariants at those boundaries instead of only making a local test pass.
Canonical Engineering Non-Negotiables
The strongest repository-level rules are repeated across AGENTS.md,
.github/copilot-instructions.md, and the current engine implementation.
1. Data-oriented runtime model
Entities belong in ECSWorld. They should not be refactored into deep ad-hoc object graphs.
2. NumPy-backed field state
Environmental and matrix-like state belongs in NumPy arrays, not nested Python lists.
3. Buffered environmental semantics
GridEnvironment owns read/write buffer pairs for key environmental layers. Changes that bypass
these mechanics risk breaking determinism and visibility ordering.
4. Rule of 16 discipline
Species and substance spaces are bounded by the shared constants and corresponding schema limits. Hot-path code must not rely on dynamic matrix growth inside the simulation loop.
5. Spatial locality over pairwise search
Local ecological interactions must use the spatial hash (register_position, move_entity,
entities_at) rather than O(N²) scans.
6. Benchmark-sensitive hot paths
Flow-field and spatial-hash behavior are not merely functionally correct surfaces; they are also performance contracts.
Primary Edit Map
A contributor should first identify which subsystem actually owns the behavior they want to change.
Schemas and configuration boundary
src/phids/api/schemas.pysrc/phids/io/scenario.py
Use these when changing:
- scenario structure,
- validation rules,
- trigger schema semantics,
- configuration bounds.
UI draft and builder behavior
src/phids/api/ui_state.pysrc/phids/api/templates/src/phids/api/main.py
Use these when changing:
- the builder workflow,
- draft-state mutations,
- partial rendering,
- load/import/export behavior.
Engine runtime behavior
src/phids/engine/loop.pysrc/phids/engine/core/src/phids/engine/systems/
Use these when changing:
- tick ordering,
- flow-field generation,
- buffering semantics,
- lifecycle, interaction, and signaling behavior.
Telemetry and persistence
src/phids/telemetry/analytics.pysrc/phids/telemetry/conditions.pysrc/phids/telemetry/export.pysrc/phids/io/replay.py
Use these when changing:
- exported metrics,
- replay framing,
- termination semantics,
- analytics artifacts.
Draft vs Live State
One of the most important contribution pitfalls is confusing the UI draft with the live runtime.
Draft state
DraftState is the editable scenario accumulator used by the server-rendered UI.
Live state
SimulationLoop is the active runtime created only after scenario load or load-draft.
Practical consequence:
- if a change affects how the UI edits a scenario, it usually belongs in
ui_state.py, templates, and builder routes, - if a change affects what happens once the simulation is running, it usually belongs in engine code and possibly the schema layer.
Suggested Workflow for a Change
A good current-state workflow for a non-trivial contribution is:
- identify the owning subsystem,
- verify the relevant invariants in code and docs,
- make the smallest coherent change,
- run focused tests for the touched surface,
- run broader gates if the change hits shared/runtime-critical code,
- rebuild the docs if prose, docstrings, or public structure changed.
Tooling Baseline
The repository uses uv as its environment and execution entry point.
Current setup commands documented in the repo are:
uv sync --all-extras --dev
uv run uvicorn phids.api.main:app --reload --app-dir src
The project metadata currently declares:
- Python
>=3.12, - Ruff targeting
py312, - strict mypy over
src/phidsandtests, - pytest with coverage and benchmark addopts,
- Google-style docstring conventions recorded in
pyproject.tomland the documentation guidance.
Quality Gates in Current Configuration
Ruff
Configured in pyproject.toml and mirrored in CI.
mypy
Runs in strict mode and uses the Pydantic mypy plugin.
pytest with coverage and benchmarks
The project-level pytest configuration includes:
- coverage over
src/phids, - fail-under threshold
80, - benchmark sorting and GC control.
MkDocs strict build
Documentation is part of the quality gate and must pass:
uv run mkdocs build --strict
CI Parity
The current GitHub Actions workflow expresses CI as focused jobs rather than one long serial lane.
The active jobs are:
quality—uv run ruff check .anduv run ruff format --check .tests-py312—uv run pytestdocs—uv run mkdocs build --strict
The workflow itself is intentionally scoped to pushes on main, pull requests targeting main,
and manual dispatch, so branch pushes—including develop—do not automatically trigger the
expensive CI lane.
A contributor should treat this as the authoritative parity target for merge-ready work.
pre-commit and mypy still exist as useful local cleanup tools, but they are not currently part
of the merge-blocking CI path because the repository still carries pre-existing type debt and the
maintainers intentionally keep the hosted workflow narrower than the full local contributor gate.
For the reasoning behind that split and how to rehearse it locally with act, see:
Pre-commit Responsibilities
The repository now uses a staged hook topology. This separation is deliberate. A commit should fail quickly on mechanical defects that can be repaired immediately, whereas a push should fail on deeper semantic regressions before they leave the developer workstation.
Commit-stage hooks (pre-commit)
The pre-commit stage performs fast repository hygiene and source normalization:
- trailing-whitespace and end-of-file normalization,
- LF line-ending enforcement,
- YAML, TOML, and JSON structural validation,
- merge-conflict and case-conflict detection,
- large-file and private-key guards,
- Python debug-statement detection,
- Ruff lint autofix and Ruff formatting,
- codespell with a repository-scoped ignore list for domain-specific false positives.
Push-stage hooks (pre-push)
The pre-push stage runs slower whole-project checks whose failure would materially compromise the
simulation as a scientific software artifact:
uv run pytest,uv run mkdocs build --strict.
The strict type gate currently targets runtime source modules (src/phids).
Install both hook types once per clone:
uv run pre-commit install --hook-type pre-commit --hook-type pre-push
Rehearse both blocking stages explicitly when preparing a merge-worthy branch:
uv run pre-commit run --all-files
uv run pre-commit run --all-files --hook-stage pre-push
Focused Test Selection
Not every change requires the entire test suite first. The repo provides strong focused nets.
The canonical testing guidance now lives in:
UI and route changes
Use:
uv run pytest tests/test_ui_routes.py -q
Engine system changes
Use:
uv run pytest tests/test_systems_behavior.py tests/test_termination_and_loop.py -q
Benchmark-sensitive changes
If a change touches diffusion, flow field, or spatial hashing, also run:
uv run pytest tests/test_flow_field_benchmark.py tests/test_spatial_hash_benchmark.py -q
This is especially important because benchmark-sensitive regressions may not show up as functional failures.
Documentation Standards for Contributors
PHIDS documentation is now organized as a scientific MkDocs corpus. Contributors should ensure that new prose:
- describes current behavior rather than aspirational behavior,
- links claims to concrete modules and symbols,
- cites tests when describing validated behavior,
- preserves provenance when migrating legacy content,
- remains navigable and buildable under strict mode.
Docstrings and mkdocstrings
The project’s API reference is generated through mkdocstrings, so docstrings are part of the public documentation surface.
Current rules include:
- Google-style docstrings,
- scholarly prose rules captured in
docs/docstring_guidelines.md, - clear Args/Returns/Notes structure when useful,
- alignment with actual runtime behavior.
Contributors should not treat docstrings as optional commentary; they are part of the documentation contract.
Common Pitfalls
Confusing draft and live state
A very common mistake is implementing a runtime behavior change in the draft layer only, or vice versa.
Forgetting dependent compaction
Deleting flora, predator, or substance definitions usually requires cleanup of:
- IDs,
- matrices,
- trigger rules,
- placements,
- condition-tree references.
Missing benchmark runs
Changes to flow-field logic, spatial hashing, or diffusion should trigger benchmark tests, not just unit tests.
Overstating idealized double-buffering
Current PHIDS most concretely implements buffered environmental state through GridEnvironment.
Contributors should document and reason about the current implementation precisely.
A Useful Mental Model
A practical mental model for contributing to PHIDS is:
- schemas define what may exist,
- draft state defines what may be edited,
- simulation loop defines what runs,
- telemetry and replay define what can be studied afterward.
Good contributions preserve this chain instead of short-circuiting it.
Verified Current-State Evidence
AGENTS.md.github/copilot-instructions.mdpyproject.toml.pre-commit-config.yaml.github/workflows/ci.ymlREADME.mdsrc/phids/api/ui_state.pysrc/phids/api/main.pysrc/phids/engine/loop.pysrc/phids/engine/core/biotope.pysrc/phids/engine/core/ecs.pysrc/phids/engine/core/flow_field.py
Where to Read Next
- For the engine ownership model:
../engine/index.md - For draft-to-live semantics:
../ui/draft-state-and-load-workflow.md - For the architecture-level state boundary overview:
../architecture/index.md - For the current docs handoff and deferred work:
documentation-status-and-open-work.md