initialize spec macro and standards

Signed-off-by: Onur Özkan <work@onurozkan.dev>
This commit is contained in:
Onur Özkan committed 2026-07-16 22:16:45 +03:00
1 parent bac05a4562
commit ceba320a96
11 files changed
+471 -1

No files matched your search

Generated
+238
View File
@@ -2,6 +2,244 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "kaptan-runtime"
version = "0.1.0"
[[package]]
name = "kaptan-spec"
version = "0.1.0"
dependencies = [
"trybuild",
]
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_spanned"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26"
dependencies = [
"serde_core",
]
[[package]]
name = "syn"
version = "2.0.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "target-triple"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "591ef38edfb78ca4771ee32cf494cb8771944bee237a9b91fc9c1424ac4b777b"
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]]
name = "toml"
version = "1.1.3+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.1.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2"
[[package]]
name = "trybuild"
version = "1.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06649c6f63d86604ba0c8950d5a1829fc9a17afd70fc6629f481d75b6a624c78"
dependencies = [
"glob",
"serde",
"serde_derive",
"serde_json",
"target-triple",
"termcolor",
"toml",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "winnow"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+1
View File
@@ -1,5 +1,6 @@
[workspace]
members = [
"kaptan-spec",
"kaptan-runtime",
]
resolver = "2"
+41 -1
View File
@@ -1,2 +1,42 @@
# kaptan
next-gen flight controller operating system
Kaptan is a modular flight controller platform focused on deterministic bare-metal execution for flight-critical paths and strong verification discipline around the rest of the stack.
The repository is currently in infrastructure setup. Formal project specifications should be linked directly from the code they govern through compile-time attributes.
## Current Direction
- Flight-critical runtime stays bare-metal with no RTOS in the control path.
- The first vehicle scope is multirotor.
- The first board support lane is Kakute F7 Mini V3.
- Non-critical extensibility, companion integration, and user-facing tooling will be added only after core runtime and verification contracts are established.
## Immediate Priorities
- Create specification infrastructure without inventing the first formal spec.
- Establish crate and board boundaries that fit the first supported hardware lane.
- Add deterministic host-side validation before simulation and hardware automation expand.
## Repository Structure
- `kaptan-runtime/`: bare-metal runtime crate scaffold.
- `tests/sitl/`: host-side simulation and deterministic verification scaffolding.
- `tests/hitl/`: hardware-in-the-loop scaffolding.
- `scripts/`: repository validation and developer helper scripts.
## Spec Infrastructure
Formal specs should be colocated with the code or board package they govern.
- Implementation-linked specs should use versioned sidecar filenames such as `control_loop.v0.1.0.spec.md` next to the related module or package.
- Rust items should link to those sidecar specs through `#[spec("...")]`.
- Missing linked spec files must fail compilation.
Example:
```rust
use kaptan_spec::spec;
#[spec("control_loop.v0.1.0.spec.md")]
pub struct ControlLoop;
```
+65
View File
@@ -0,0 +1,65 @@
# Specifications
Formal Kaptan specifications should normally live next to the code item they govern.
Examples:
- `kaptan-runtime/src/control_loop.rs`
- `kaptan-runtime/src/control_loop.v0.1.0.spec.md`
- `kaptan-runtime/src/scheduler.rs`
- `kaptan-runtime/src/scheduler.v0.1.0.spec.md`
## Compile-Time Linking
Code implementations should link to their governing spec through `#[spec("...")]`.
Example:
```rust
use kaptan_spec::spec;
#[spec("control_loop.v0.1.0.spec.md")]
pub struct ControlLoop;
```
The attribute expands to a compile-time file inclusion guard. If the linked file does not exist, compilation fails.
## Spec Format
Formal specs are expected to declare these keys in YAML frontmatter:
- `title`
- `status`
- `owners`
Allowed statuses are:
- `in-progress`
- `implemented`
- `deprecated`
Owner entries must use full name and email address in this format:
```text
- Full Name <name@example.com>
```
The spec path is the identifier for linkage and traceability.
- The code path plus the `#[spec("...")]` reference identifies the governing spec.
- The version is part of the spec filename and therefore part of the spec identity.
## Template
Use this starter when creating a new colocated spec:
```markdown
---
title: Replace with title
status: in-progress
owners:
- Full Name <name@example.com>
---
# TODO
```
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "kaptan-spec"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
[lib]
proc-macro = true
[dev-dependencies]
trybuild = "1.0"
+70
View File
@@ -0,0 +1,70 @@
use proc_macro::{Literal, Punct, Spacing, TokenStream, TokenTree};
#[proc_macro_attribute]
pub fn spec(attr: TokenStream, item: TokenStream) -> TokenStream {
match parse_spec_path(attr) {
Ok(path) => expand_spec_guard(&path, item),
Err(message) => compile_error(&message),
}
}
fn parse_spec_path(attr: TokenStream) -> Result<String, String> {
let tokens: Vec<TokenTree> = attr.into_iter().collect();
match tokens.as_slice() {
[TokenTree::Literal(path)] if is_string_literal(path) => Ok(path.to_string()),
[
TokenTree::Ident(name),
TokenTree::Punct(eq),
TokenTree::Literal(path),
] if name.to_string() == "path" && is_equals(eq) && is_string_literal(path) => {
Ok(path.to_string())
}
[] => Err("missing spec path; use #[spec(\"file.v0.1.0.spec.md\")]".into()),
_ => Err("invalid spec attribute; use #[spec(\"file.v0.1.0.spec.md\")]".into()),
}
}
fn is_string_literal(literal: &Literal) -> bool {
let value = literal.to_string();
value.starts_with('"') || value.starts_with("r\"") || value.starts_with("r#")
}
fn is_equals(punct: &Punct) -> bool {
punct.as_char() == '=' && punct.spacing() == Spacing::Alone
}
fn expand_spec_guard(path: &str, item: TokenStream) -> TokenStream {
let guard = format!("const _: &str = include_str!({path});");
let mut output = TokenStream::new();
output.extend(
guard
.parse::<TokenStream>()
.expect("spec macro must generate valid Rust tokens"),
);
output.extend(item);
output
}
fn compile_error(message: &str) -> TokenStream {
let escaped = escape_string(message);
format!("compile_error!(\"{escaped}\");")
.parse::<TokenStream>()
.expect("spec macro must generate valid compile_error tokens")
}
fn escape_string(value: &str) -> String {
let mut escaped = String::with_capacity(value.len());
for ch in value.chars() {
match ch {
'\\' => escaped.push_str("\\\\"),
'"' => escaped.push_str("\\\""),
_ => escaped.push(ch),
}
}
escaped
}
+11
View File
@@ -0,0 +1,11 @@
#[test]
fn accepts_existing_sidecar_spec() {
let tests = trybuild::TestCases::new();
tests.pass("tests/ui/flight_core_contract.rs");
}
#[test]
fn rejects_missing_sidecar_spec() {
let tests = trybuild::TestCases::new();
tests.compile_fail("tests/ui/fail_missing_sidecar.rs");
}
@@ -0,0 +1,8 @@
use kaptan_spec::spec;
#[spec("fail_missing_sidecar.v0.1.0.spec.md")]
struct MissingSpec;
fn main() {
let _ = core::mem::size_of::<MissingSpec>();
}
@@ -0,0 +1,5 @@
error: couldn't read `$DIR/tests/ui/fail_missing_sidecar.v0.1.0.spec.md`: No such file or directory (os error 2)
--> tests/ui/fail_missing_sidecar.rs:3:1
|
3 | #[spec("fail_missing_sidecar.v0.1.0.spec.md")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -0,0 +1,9 @@
use kaptan_spec::spec;
#[spec("flight_core_contract.v0.1.0.spec.md")]
struct FlightCore;
fn main() {
let size = core::mem::size_of::<FlightCore>();
assert_eq!(size, 0);
}
@@ -0,0 +1,10 @@
---
title: Flight core contract
status: in-progress
owners:
- Test Name <name@test.com>
---
# Summary
Minimal spec for compile-time linkage testing.