(ENGINEERING_DOCTRINE.md)
This document is the operational law for all AI agents and human contributors working on this project. It supersedes improvisation. It overrides stylistic preference. It binds every commit, every file, every architectural decision.
These laws are immutable. Violating them introduces bugs, technical debt, and architectural rot. Any commit that breaks a Cardinal Law must be reverted or repaired before further work proceeds.
Never submit partial files. Never submit pseudocode. Never submit placeholder implementations disguised as working code. If a function exists, it must work. If it does not work, it must be clearly marked as scaffold with a TODO referencing the blocker.
Acceptable:
# Scaffold: Waiting for GGUFSeer tensor metadata parsing to expose dtype
# Blocked by: PR #47, tensor_metadata struct finalization
# Owner: Agent-ForgeWorker
fn load_tensor_weights(tensor_id: Int) -> TensorData:
# TODO: Implement once TensorMetadata.dtype field lands
return TensorData.empty()Unacceptable:
fn load_tensor_weights(tensor_id: Int) -> TensorData:
return TensorData.fake_success() # Hope nobody noticesThe difference is honesty. The capability ledger exists for a reason. Use it.
Every subsystem must handle failure gracefully. No panics. No unhandled exceptions propagating to the caller. No crashes on malformed input.
Wrap external operations in error handling. Validate inputs at boundaries. Return error types, not thrown exceptions, for expected failure modes. Reserve exceptions for truly exceptional circumstances.
fn parse_gguf_header(data: UnsafePointer[UInt8], size: Int) -> Result[GGUFHeader, ParseError]:
if size < MIN_HEADER_SIZE:
return Err(ParseError.TruncatedHeader)
var magic = data.load[width=4](0)
if magic != GGUF_MAGIC_BYTES:
return Err(ParseError.InvalidMagic)
# ... continued parsing with validation at each step
return Ok(header)No absolute paths. Ever. Not in code. Not in configs. Not in documentation. Not in tests.
The project must be cloneable to any directory on any machine and function identically. Use relative paths from the project root. Use environment variables for system-specific configuration. Use the project's internal path resolution utilities.
Scan for absolute paths before every commit. This is not optional.
Never leave integrations incomplete. If module A calls module B, the interface must be fully implemented on both sides. Stub functions with no caller are orphaned code. Stub callers with no implementation are dangling references.
Before committing, verify:
- Every function called has an implementation
- Every implemented function has at least one caller or is explicitly marked as public API
- Every import resolves
- Every type reference exists
Settings belong in configuration files. Model metadata belongs in data files. NPC definitions belong in data files. Tokenizer vocabularies belong in data files.
Hardcoding data in source code couples that data to the compilation cycle. It makes updates require rebuilds. It scatters truth across hundreds of files.
If you find yourself typing a literal value that represents configurable behavior, stop. Create a data file. Load it at runtime.
All string literals use double quotes ("text"). Never single quotes ('text').
This is not aesthetic preference. It is consistency law. Mixed quoting styles create parsing ambiguities, diff noise, and reviewer fatigue. The entire codebase uses double quotes. Conform.
This project is organized into domains. Each domain owns specific responsibilities and must not bleed into others.
Example project domains:
| Domain | Core Module | Owns | Does Not Own |
|---|---|---|---|
| Inference Coordination | AesirEngine | Orchestration, scheduling, request lifecycle | Tokenization, weight loading, HTTP serving |
| Memory Management | MimirWell | KV cache, paged attention, memory allocation | Model architecture, request routing |
| Model Transport | BifrostGate | HTTP API, request/response marshalling | Inference execution, memory management |
| Tokenization | RuneWeaver | BPE encoding/decoding, vocab management | Inference, model loading |
| Model Parsing | GGUFSeer | GGUF file parsing, tensor extraction, metadata | Tokenization, inference |
| Weight Management | Hladgerð | Weight loading, quantization, conversion | File parsing, inference execution |
| Compute Execution | Mjølnir | Forward pass, attention computation, sampling | Memory allocation, request handling |
A module may only depend on modules in its own domain or explicitly designated interface modules. Circular dependencies are forbidden. The dependency graph must be acyclic.
If you need functionality from another domain, consume it through its published interface, not through internal implementation details. Interface files (INTERFACE.md) define what is public. Anything not in the interface is internal and may change without warning.
If you find yourself needing to reach across domains for functionality that does not exist, do not hack it. Either:
- Extend the target domain's interface to expose the needed capability
- Create a new domain that owns the shared concern
- Raise the issue for architectural review
Shortcutting boundaries creates the Ball of Mud anti-pattern. Every shortcut taken today costs ten hours of refactoring tomorrow.
Contributors operate as one of six specialized roles during any work session. Switch roles consciously. Do not blur role boundaries.
Skald — Vision, naming, philosophy, conceptual framing. Use when starting new features, naming modules, writing project-level documentation.
Architect — Boundary definition, ownership assignment, structural planning. Use when designing new modules, planning refactors, resolving ownership disputes.
Cartographer — System mapping, dependency tracing, impact analysis. Use when understanding how a change propagates, when onboarding to unfamiliar code, when preparing for major modifications.
Forge Worker — Implementation, testing, mechanical construction. Use when writing code, fixing bugs, building features.
Auditor — Verification, contradiction detection, edge-case analysis. Use when reviewing code, testing claims, finding weaknesses.
Scribe — Documentation, continuity preservation, record keeping. Use when ending sessions, recording decisions, updating docs.
Every work session follows this rhythm:
Opening (5-10 minutes)
- Cartographer reviews current system state and recent changes
- Scribe reads the latest DEVLOG entry
- Architect restates the active boundary being worked on
- Auditor restates any open verification items
Main Work
- Skald clarifies the vision for the current task
- Architect confirms boundaries and ownership
- Forge Worker implements
- Auditor verifies
Closing (10-15 minutes)
- Auditor runs full verification pass
- Scribe updates DEVLOG, capability ledger, and any drifted documentation
- Commit with clear message following the commit format
Before starting work, read TODO.md and CAPABILITY_LEDGER.md. Pick tasks that are:
- Blocking other work (highest priority)
- Marked as scaffold or missing in the capability ledger
- Within your current role's domain of competence
Never pick a task randomly. Never start work without understanding what depends on it and what it depends on.
- Four-space indentation. No tabs.
- Snake case for variables and functions:
load_tensor_weights - PascalCase for structs and traits:
GGUFHeader,TensorParser - UPPER_CASE for compile-time constants:
MAX_CONTEXT_LENGTH - Lines under 100 characters where possible
- One statement per line
- No trailing whitespace
Every function parameter, return type, and variable must have an explicit type annotation. Rely on type inference only for obviously clear local assignments.
# Good
fn compute_attention(query: SIMD[DType.float32, 128], key: SIMD[DType.float32, 128]) -> Float32:
var score: Float32 = query.dot(key)
return score
# Bad
fn compute_attention(query, key):
var score = query.dot(key)
return scoreUse Result types for expected failure modes. Use raises only for truly exceptional circumstances that should propagate upward. Never swallow errors silently.
# Good
fn load_model(path: String) -> Result[LoadedModel, LoadError]:
var parsed = GGUFSeer.parse(path)?
var weights = Hladgerð.load(parsed)?
return Ok(LoadedModel(parsed, weights))
# Bad
fn load_model(path: String) -> LoadedModel raises:
var parsed = GGUFSeer.parse(path) # Might fail silently
var weights = Hladgerð.load(parsed) # Might fail silently
return LoadedModel(parsed, weights)Prefer value semantics. Use UnsafePointer only when performance demands it and the lifetime is provably safe. Always pair alloc with free. Never return pointers to stack memory.
# Good — ownership transferred, caller responsible
fn create_buffer(size: Int) -> UnsafePointer[Float32]:
return UnsafePointer[Float32].alloc(size)
# Bad — dangling pointer
fn get_temp_buffer(size: Int) -> UnsafePointer[Float32]:
var temp = UnsafePointer[Float32].alloc(size)
return temp # Caller has no idea they need to free thisComment the why, not the what. Code should explain what it does. Comments should explain why it does it.
# Good
# PagedAttention requires block-aligned offsets for correct page table indexing
var aligned_offset = (offset + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1)
# Bad
# Align the offset
var aligned_offset = (offset + BLOCK_SIZE - 1) & ~(BLOCK_SIZE - 1)Use Norse cosmological metaphors for architectural comments where they aid comprehension:
# Huginn retrieves cached context from MimirWell — memory precedes thought
var cached_context = mimir_well.retrieve(session_id)Every feature in this project is tracked in CAPABILITY_LEDGER.md with one of five statuses:
| Status | Meaning |
|---|---|
| Verified | Implemented, tested, confirmed working with evidence |
| Partial | Implemented but incomplete or lacking test coverage |
| Scaffold | Structure exists but functionality is stubbed |
| Simulated | Appears functional but returns hardcoded/mock responses |
| Missing | Not implemented at all |
Any agent claiming a feature works must upgrade its status to Verified with accompanying test evidence. Any agent discovering a feature is simulated or scaffold must downgrade its status immediately.
This is the honesty system. It is the backbone of project integrity. Lies in the capability ledger are worse than bugs in the code.
Every new feature must include:
- Unit tests for the core logic
- Boundary tests for interface contracts
- At least one integration test exercising the feature through its public API
- A regression test if fixing a bug
Tests must be runnable with a single command:
mojo test tests/Before stating that any feature works:
- Run the tests
- Confirm they pass
- Exercise the feature manually if possible
- Record evidence in the DEVLOG
- Update the capability ledger
Claims without evidence are noise. The Auditor role exists to destroy unsubstantiated claims.
When a test fails, the system must fail closed. No fallback to mock behavior. No silent degradation. No "close enough" tolerances.
A failing test is a signal. Fix the code or fix the test. Never mute the signal.
Every domain folder must contain:
README.md— Purpose, contents, boundariesINTERFACE.md— Public API, inputs, outputs, invariants
The project root must maintain:
README.md— Project overview, build instructions, usageARCHITECTURE.md— System structure, domain relationships, data flowdocs/DOMAIN_MAP.md— Ownership boundaries, what each domain does and does not doCAPABILITY_LEDGER.md— Feature status trackingDEVLOG.md— chronological record of significant changesTODO.md— Canonical outstanding work items with priorities
Documentation must match reality. If you change code, update docs in the same commit. If you discover docs that disagree with code, either fix the code to match docs or fix the docs to match code. Never leave them in disagreement.
The Scribe role polishes documentation. But every contributor is responsible for keeping docs honest about their own changes.
## YYYY-MM-DD — Agent Identifier
### Completed
- [Brief description of what was accomplished]
### Changed
- [Files modified and why]
### Verified
- [Tests run, results obtained]
### Discovered
- [Issues found, risks identified, observations recorded]
### Next
- [What the next session should tackle]When studying or adapting code from external open-source projects (vLLM, llama.cpp, ggml, etc.), follow the plundering workflow:
-
License Gate — Verify the upstream license permits reuse. Apache-2.0 and MIT are clean. GPL is contagious. Check before touching.
-
Intent Recording — Document why the external code is being studied and what specific problem it solves. Write this to
docs/plunder/[PROJECT]_GUIDE.md. -
Pattern Extraction — Prefer extracting architectural patterns over copying code. Mojo is not Python. Mojo is not C++. Direct copies usually require significant adaptation anyway.
-
Attribution — If code or architecture is adapted, record it in
THIRD_PARTY_NOTICES.md. Add file headers noting derivation. Never hide upstream origin. -
Boundary Assignment — Plundered code must be assigned to a local domain. It does not get to exist outside the architecture. It conforms to our boundaries or it does not enter.
-
Verification — Plundered code is untrusted until proven. Test it. Audit it. Confirm it meets our standards.
Symptom: A function returns plausible-looking output without actually performing the work. Usually involves hardcoded values or simplified approximations.
Detection: The Auditor role checks for hardcoded return values, unused parameters, and functions that always succeed regardless of input.
Correction: Mark as Simulated in the capability ledger. Implement real logic. Upgrade to Verified only after testing proves it works.
Symptom: A module gradually accumulates responsibilities outside its domain. The boundary blurs. Dependencies proliferate.
Detection: If a module's README can no longer describe its purpose in one sentence, it has drifted.
Correction: The Architect role extracts the foreign logic and relocates it to the correct domain. Update all interfaces. Run full test suite.
Symptom: Names that once matched their purpose now describe something different. The function is called parse_token but it also validates, normalizes, and caches.
Detection: During review, if a name requires explanation beyond its literal meaning, it has decayed.
Correction: Rename to reflect current responsibility. Or better, split the function so the name remains accurate.
Symptom: Two modules communicate through undocumented channels — shared globals, side effects, implicit ordering assumptions.
Detection: Remove one module and see what breaks in unexpected places.
Correction: Make all communication explicit through published interfaces. Document the dependency. Test the contract.
Symptom: Code is built to handle hypothetical future cases that never materialize. Complexity increases without delivering value.
Detection: If you cannot point to a concrete current requirement driving a generalization, it is premature.
Correction: Strip to what is needed now. Leave extensibility hooks, not implementations of imaginary features.
- Pull latest from the
mainintegration branch - Read
TODO.mdfor current priorities - Read
CAPABILITY_LEDGER.mdto understand what exists and what is scaffold - Read the last
DEVLOG.mdentry to understand recent context - Read the
INTERFACE.mdof any domain you will touch
- Keep the capability ledger open in a buffer
- Refer to the domain map when uncertain about ownership
- Run tests after every meaningful change
- Update documentation as you go, not at the end
- Commit in small, reviewable increments
- Run the full test suite
- Update the capability ledger with any status changes
- Write a DEVLOG entry
- Update
TODO.mdif tasks were completed or discovered - Push the feature branch and open it against
main - Confirm the working tree is clean
[type]: [brief description]
[optional body explaining motivation and approach]
[optional footer noting breaking changes or related issues]
Types: feat, fix, refactor, test, docs, chore, audit
Examples:
feat: implement BPE token caching in RuneWeaver
fix: correct KV cache block alignment in MimirWell
audit: downgrade GGUFSeer.tensor_extraction from Verified to Partial
docs: update AesirEngine INTERFACE.md with new session lifecycle methods
Every line of code in this project must serve the goal: a bare-metal Mojo LLM inference engine that runs on local hardware, respects user sovereignty, and delivers performance without bloat.
If a change does not serve this goal, it does not belong in this repository.
If a feature adds complexity without advancing the core mission, reject it.
If an optimization trades correctness for speed, reject it.
If a dependency introduces external control, reject it.
The machine does not lie. The terminal is the only honest interface. Build accordingly.
This document is law within this project. It evolves through proposal and agreement, not through unilateral deviation. If you believe a rule should change, raise it in a DECISIONS entry and make your case. Until then, obey.
Maintained by the Architect role. Last updated: 2026-09-01.