Contribution Style Guide¶
This guide defines the technical standards for changes to bvlos-sim. It is intended for contributors working on schemas, estimator logic, adapters, reports, tests, or documentation.
Goals¶
Prefer changes that:
- preserve explicit package boundaries
- keep public contracts stable
- move invariants into typed models and enums
- keep tests behavior-focused
- improve maintainability without adding unnecessary abstraction
Package Boundaries¶
Current responsibilities:
schemas/: Pydantic input models for mission, vehicle, and scenario filesestimator/core/: public enums, options, result models, constants, and typed errorsestimator/execution/: deterministic estimation, static feasibility, scenario execution, and runtime contextestimator/environment/: wind provider abstractionsestimator/math/: pure math and geometry helpersadapters/: CLI, file loading, envelopes, output rendering, and Markdown reportsadapters/sitl/: ArduPilot SITL adapter, artifact recording, evidence building, and comparison report logic
Rules:
- Do not move execution-only behavior into
estimator/core/. - Do not let
schemas/depend on adapters or execution internals. - Do not put domain rules in CLI code.
- Keep package-root
estimatorimports stable. - Prefer a focused module over a mixed-responsibility file.
Public Contracts¶
Treat these as stable:
- package-root
estimatorimports mission.v6vehicle.v4scenario.v1uncertainty.v1estimator-envelope.v7scenario-report.v2uncertainty-report.v1sitl-evidence.v1geofence-geojson.v1landing-zone-geojson.v1population-grid.v1- CLI exit-code semantics
- Markdown report output covered by golden fixtures
If a public contract changes intentionally, update:
docs/VERSIONING_POLICY.md- golden fixtures
- regression tests
- user-facing documentation
Validation Ownership¶
Use schema validation for:
- field shape
- required and optional fields
- numeric bounds
- route-item authoring invariants
- scenario authoring invariants
Use runtime validation for:
- cross-object consistency
- execution-time feasibility
- defensive checks against mutated model instances
- conditions that require resolved runtime context
Avoid duplicating the same rule in multiple layers unless the runtime version is clearly defensive.
Typed Models¶
Prefer:
StrEnumfor closed sets- Pydantic models or dataclasses for structured data
- named aggregate objects over positional tuples
- typed exceptions over message-only
ValueErrorflows
Avoid:
- stringly typed identifiers when the set is closed
- ad hoc dict schemas for structured output
- tuple unpacking where field names matter
Metadata¶
Metadata is allowed when it is deliberate.
Rules:
- Keep machine-facing metadata stable when possible.
- Use stable identifiers instead of internal class names.
- Do not hide contract changes in free-form metadata maps.
- Document compatibility-relevant metadata keys.
Execution Logic¶
For estimator and scenario execution:
- keep functions small and focused
- keep route-item dispatch table-driven
- keep math helpers pure where practical
- make unsupported and infeasible outcomes explicit
- return structured failures rather than raw dependency exceptions
- preserve determinism unless a future mode explicitly introduces randomness
When extending mission actions:
- update
MissionAction - update route-item requirements
- update executor coverage
- add behavior tests
- update golden fixtures if output contracts change
When extending scenario behavior:
- update scenario schema enums and validation
- add runner tests for event and assertion outcomes
- keep unsupported behavior explicit
- update scenario envelope or fixtures only when the public contract changes intentionally
Adapter Style¶
Adapters should translate between external surfaces and domain models.
Rules:
adapters/io.pyloads and classifies input errors.adapters/envelope.pybuilds stable estimator output structures.adapters/scenario_envelope.pybuilds stable scenario output structures.adapters/cli.pywires command surfaces and exit-code policy.- Markdown renderers should format existing results, not reimplement domain rules.
Prefer adapter-local typed errors for I/O and rendering failures.
Testing¶
Prefer tests that verify behavior and contracts.
Write tests for:
- observable estimator behavior
- scenario event and assertion outcomes
- invariant enforcement
- envelope semantics
- CLI exit-code behavior
- deterministic output
- golden fixtures for supported public outputs
Avoid:
- tests that only restate Pydantic internals
- brittle snapshots of implementation details
- hard-coded counts unless the count itself is behavior
Before considering work complete, run:
Documentation¶
Update documentation when changing:
- package boundaries
- public contract semantics
- output formats
- supported fields
- supported actions
- known limitations
- CLI behavior
- ticket status
Do not leave stale comments, docstrings, examples, or roadmap statements after behavior changes.
Refactoring¶
A refactor should do at least one of the following:
- reduce duplication
- remove an unsafe pattern
- replace implicit behavior with typed structure
- simplify an extension point
- improve contract stability
Avoid refactors that mainly rename symbols, add indirection, or broaden scope without reducing real complexity.