-
Notifications
You must be signed in to change notification settings - Fork 47
Add .github/copilot-instructions.md #744
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,161 @@ | ||||||||||||||||
| # Copilot Instructions for embedded-services | ||||||||||||||||
|
|
||||||||||||||||
| ## Overview | ||||||||||||||||
|
|
||||||||||||||||
| This is an embedded controller (EC) services workspace — a collection of `no_std` Rust crates implementing hardware-agnostic business logic for embedded controllers. Services glue together MCU HALs (via `embedded-hal` traits), peripheral drivers, and EC subsystem abstractions (sensors, batteries, fans, USB-PD, etc.) using the Embassy async runtime. | ||||||||||||||||
|
|
||||||||||||||||
| ## Build, Test, and Lint | ||||||||||||||||
|
|
||||||||||||||||
| Toolchain: Rust 1.88 (`rust-toolchain.toml`), edition 2024. Targets: `x86_64-unknown-linux-gnu` (std/testing) and `thumbv8m.main-none-eabihf` (ARM Cortex-M33). | ||||||||||||||||
|
|
||||||||||||||||
| ```shell | ||||||||||||||||
| # Format | ||||||||||||||||
| cargo fmt --check | ||||||||||||||||
|
|
||||||||||||||||
| # Lint (all feature combos, both targets) | ||||||||||||||||
| cargo hack --feature-powerset --mutually-exclusive-features=log,defmt,defmt-timestamp-uptime clippy --locked --target x86_64-unknown-linux-gnu | ||||||||||||||||
| cargo hack --feature-powerset --mutually-exclusive-features=log,defmt,defmt-timestamp-uptime clippy --locked --target thumbv8m.main-none-eabihf | ||||||||||||||||
|
|
||||||||||||||||
| # Test (workspace, host target only) | ||||||||||||||||
| cargo test --locked | ||||||||||||||||
|
|
||||||||||||||||
| # Test a single crate | ||||||||||||||||
| cargo test --locked -p partition-manager | ||||||||||||||||
|
|
||||||||||||||||
| # Test a single test function | ||||||||||||||||
| cargo test --locked -p partition-manager test_name | ||||||||||||||||
|
|
||||||||||||||||
| # Lint test code | ||||||||||||||||
| cargo clippy --locked --tests | ||||||||||||||||
|
|
||||||||||||||||
| # Check docs | ||||||||||||||||
| cargo doc --no-deps -F log --locked | ||||||||||||||||
| cargo doc --no-deps -F defmt --locked | ||||||||||||||||
|
|
||||||||||||||||
| # Unused dependency check | ||||||||||||||||
| cargo machete | ||||||||||||||||
|
|
||||||||||||||||
| # Dependency license/advisory/audit checks | ||||||||||||||||
| cargo deny check --all-features --locked | ||||||||||||||||
| cargo vet --locked | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| The `examples/` directory contains separate workspaces (excluded from the root workspace). Build/lint them independently: | ||||||||||||||||
|
|
||||||||||||||||
| ```shell | ||||||||||||||||
| # ARM board examples | ||||||||||||||||
| cd examples/rt685s-evk && cargo clippy --target thumbv8m.main-none-eabihf --locked | ||||||||||||||||
| # Std examples | ||||||||||||||||
| cd examples/std && cargo clippy --locked | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| ## Architecture | ||||||||||||||||
|
|
||||||||||||||||
| ### Service Pattern | ||||||||||||||||
kurtjd marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
|
|
||||||||||||||||
| Each service crate follows a consistent structure: | ||||||||||||||||
|
|
||||||||||||||||
| 1. **Service struct** with a `comms::Endpoint` and domain-specific context/state | ||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we're in the process of making 1-3 here not true; we've already migrated a bunch of services away from the comms system and out of OnceLock statics. May want to just remove those pieces |
||||||||||||||||
| 2. **`MailboxDelegate` impl** — the `receive()` method handles incoming messages using type-safe downcasting via `message.data.get::<T>()` | ||||||||||||||||
| 3. **Global singleton** — services are stored in `OnceLock<Service>` statics | ||||||||||||||||
| 4. **Async task function** — registers the endpoint, then loops calling a `process()` or `process_next()` method | ||||||||||||||||
| 5. **Spawned via Embassy** — `#[embassy_executor::task]` functions are spawned from main | ||||||||||||||||
|
Comment on lines
+56
to
+62
|
||||||||||||||||
|
|
||||||||||||||||
| ```rust | ||||||||||||||||
| // Typical service skeleton | ||||||||||||||||
| pub struct MyService { | ||||||||||||||||
| endpoint: comms::Endpoint, | ||||||||||||||||
| // ... domain state | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| impl comms::MailboxDelegate for MyService { | ||||||||||||||||
| fn receive(&self, message: &comms::Message) -> Result<(), comms::MailboxDelegateError> { | ||||||||||||||||
| if let Some(event) = message.data.get::<MyEvent>() { | ||||||||||||||||
| // handle event | ||||||||||||||||
| } | ||||||||||||||||
| Ok(()) | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| static SERVICE: OnceLock<MyService> = OnceLock::new(); | ||||||||||||||||
|
|
||||||||||||||||
| pub async fn task() { | ||||||||||||||||
| let service = SERVICE.get_or_init(MyService::new); | ||||||||||||||||
| comms::register_endpoint(service, &service.endpoint).await.unwrap(); | ||||||||||||||||
|
||||||||||||||||
| comms::register_endpoint(service, &service.endpoint).await.unwrap(); | |
| if let Err(err) = comms::register_endpoint(service, &service.endpoint).await { | |
| // TODO: handle registration error (e.g., log and return or trigger a safe reset) | |
| return; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here - comms system is going away
Copilot
AI
Mar 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GlobalRawMutex is described as ThreadModeRawMutex on ARM bare-metal and CriticalSectionRawMutex in tests/std, but the actual cfg also selects CriticalSectionRawMutex on riscv32. Consider mentioning that exception to avoid confusing cross-target contributors.
| - **`GlobalRawMutex`**: `ThreadModeRawMutex` on ARM bare-metal, `CriticalSectionRawMutex` in tests/std | |
| - **`GlobalRawMutex`**: `ThreadModeRawMutex` on ARM bare-metal, `CriticalSectionRawMutex` on RISC-V bare-metal and in tests/std |
kurtjd marked this conversation as resolved.
Show resolved
Hide resolved
Copilot
AI
Mar 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error-handling conventions are stated too strongly: not all error types are enums and not all derive Clone, Copy, PartialEq, Eq (e.g., some error enums only derive Debug/Copy, and some services use a struct error). Consider changing this to “prefer” these derives when practical, and note that some modules use lightweight struct errors.
| - Custom `enum` error types per module — no `thiserror` (it requires std) | |
| - All error enums derive `Debug, Clone, Copy, PartialEq, Eq` | |
| - Prefer custom `enum` error types per module — no `thiserror` (it requires std); some modules instead use lightweight struct error types when that is a better fit | |
| - Prefer deriving `Debug, Clone, Copy, PartialEq, Eq` on error enums when practical (some errors may only derive a subset, e.g., `Debug`/`Copy`) |
Copilot
AI
Mar 9, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Testing guidance says async tests use embassy_futures::block_on(async { ... }), but there are many async unit tests written with #[tokio::test] in this repo. It would be more accurate to mention both patterns and when each is used.
| - Async tests use `embassy_futures::block_on(async { ... })` | |
| - Dev-dependencies enable `std` features: `embassy-sync/std`, `embassy-time/std`, `critical-section/std` | |
| - `tokio` with `rt`, `macros`, `time` features for integration tests | |
| - Async unit tests in `no_std`/Embassy-focused crates use `embassy_futures::block_on(async { ... })` to stay runtime-agnostic | |
| - Host-only async tests and integration tests may use `#[tokio::test]` in crates that depend on `tokio` | |
| - Dev-dependencies enable `std` features: `embassy-sync/std`, `embassy-time/std`, `critical-section/std` | |
| - `tokio` with `rt`, `macros`, `time` features is used to support `#[tokio::test]`-based host/integration tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The examples build/lint section omits the
examples/rt633workspace, which is present in the repo and is also checked in CI. Add an example command forexamples/rt633(or generalize the instructions to cover allexamples/*workspaces).