Contributing to DAM¶
Thank you for contributing to the Detachable Action Monitor. This guide covers essential information for developers.
Quick Setup¶
git clone https://github.com/ez945y/DAM.git && cd DAM
# Fork, clone your fork, add upstream
git remote add upstream https://github.com/ez945y/DAM.git
# One-time setup
make setup
# Start the dev server (backend + Next.js hot-reload)
make dev
# Create a development branch off `dev`
git checkout -b feat/my-feature # or fix/bug-id, issue/42
Branch & Release Strategy¶
| Branch | Purpose |
|---|---|
main |
Stable releases only (protected). |
dev |
Integration branch. All PRs target here. |
feat/*, fix/*, issue/* |
Development branches. Cut from dev, merge back via PR. |
Workflow: dev → accumulate features → merge to main → release tags
Required: Tests & Green CI¶
Non-Negotiable Rules¶
- Every PR must include tests for changed behavior
- All CI checks must pass before merge (no "fix after merge")
- Safety regressions must be written if guard logic changes:
Test Locally¶
# Pure Python (always works)
pytest tests/ -m "not hardware and not rust and not ros2"
# Full suite (if Rust compiled)
make test
Documentation Changes¶
For documentation-only changes, run the lightweight docs gate:
This builds MkDocs in strict mode and checks for onboarding command patterns that should not return, such as old --stack syntax.
When a documentation change completes a meaningful PM checkpoint, append a short log entry:
python scripts/log_writer.py "Clarified first-run troubleshooting" \
--phase docs-troubleshooting \
--files docs/getting-started/troubleshooting.md \
--metrics "Setup and task-name issues have recovery steps"
Keep documentation user-outcome focused: explain how to install, validate, run, observe, debug, and safely iterate before exposing implementation details.
Commit & PR Format¶
Follow Conventional Commits with DAM scopes:
Types: feat fix test refactor docs ci
Scopes: core guard boundary adapter rust stackfile cli
Examples:
feat(guard): L1 motion guard with joint limit clamping
fix(boundary): handle empty node list in advance()
test(guard): add regression for velocity clamping
PR Description: Include what changed, why, and how you tested it.
Adding Guards¶
Guards enforce safety constraints. See Guards Reference for the full guard layer specification.
Skeleton¶
from dam.guard.base import Guard
from dam.types import Observation, ActionProposal, GuardResult
import dam
@dam.guard(layer="L2") # L0, L1, L2, L3, or L3
class MyGuard(Guard):
"""Guard description."""
def check(self, obs: Observation, action: ActionProposal, my_param: float) -> GuardResult:
if bad_condition(obs, action, my_param):
return GuardResult.reject(reason="why", guard_name=self.get_name())
return GuardResult.pass_(guard_name=self.get_name())
Critical: Raise exceptions in check() — they're caught and converted to REJECT (fail-to-reject principle).
Place in dam/guard/builtin/ for built-in guards or anywhere for custom ones.
Add to Stackfile:
guards:
- L2: execution
phase: 1
boundaries:
my_guard:
layer: L2
type: single
nodes:
- callback: my_guard
fallback: hold_position
params:
my_param: 0.5
Adding Adapters (Hardware/Policy)¶
DAM uses duck typing. No base class required; implement these methods:
Source (Sensors)¶
Policy¶
Sink (Hardware)¶
def apply(self, action: ValidatedAction) -> None:
"""Send validated action to hardware."""
def get_hardware_status(self) -> dict: # optional
"""Return motor health, temperature, etc. for L3 guard."""
Register with runtime:
runtime.register_source("main", MySource())
runtime.register_policy(MyPolicy())
runtime.register_sink(MySink())
Code Quality¶
Python¶
- Type hints: All public interfaces, checked with
mypy --strict - Formatting:
ruff format(black-compatible) - Linting:
ruff check— zero warnings - Immutability: Use
@dataclass(frozen=True)for core types - State: No mutable module-level state (except registries)
Pre-commit (Highly Recommended)¶
This project uses pre-commit to ensure code quality on every commit. It automatically runs ruff, mypy, and cargo fmt.
- Install Hooks:
make setup-precommit(already included inmake setup) - Run Manually:
pre-commit run --all-files - Skip Temporarily:
git commit --no-verify(use only if necessary)
Rust (dam-rust/)¶
- Format:
cargo fmt - Lint:
cargo clippy -- -D warnings unsafeblocks: Only at PyO3 boundaries. Must have// SAFETY:comment.- Tests:
cargo test --release
The Fail-to-Reject Principle¶
This is non-negotiable. Any timeout, exception, or error in guard execution must result in immediate action rejection.
When designing guards: - Let exceptions propagate (framework catches them → REJECT) - Don't hide failures with try-except-pass - Set conservative defaults if uncertain - Guard timeouts → REJECT (watchdog enforces this)
This is how DAM keeps guard failures conservative in the safety model.
License¶
By contributing, you agree your work is licensed under Mozilla Public License 2.0.
Questions?¶
- Check GitHub Discussions
- See Full Documentation