Next-generation platform engineering handbook¶
Engineering contracts and risk gates. See delivery status for runnable entry points, individual implementations and actual evidence. This handbook defines team responsibilities and acceptance boundaries; illustrative schemas/APIs do not replace source. OCI preparation, formal offline builds, software candidates and physical qualification are accepted separately.
See next-generation-platform.md for the platform design and integration-contracts.md for external product, Board and component contracts. This handbook turns those boundaries into rules that a team of about ten can execute, review and maintain. Those design documents define the final interfaces and directories.
1. 工程目标与完成的含义¶
The engineering system must answer four questions: what was selected, whether implementation meets its contract, whether actual resources and timing meet product requirements, and whether the delivered artifact is the one verified. Tool counts, test counts, page counts and green pipeline counts are not goals.
The first version follows these principles:
One default configuration input, one resolved result and one software dependency graph. Different tools must not interpret the same selection independently.
Standard C, CMake and testing tools each do their own job. Custom tools add only configuration semantics, hardware resource validation and evidence binding.
The platform provides shared mechanisms and truthful capabilities. Products own business workers, protocols, partitions, updates, health and manufacturing policy.
Compilation, host models, actual kernel execution, ARM linking, physical measurements and release qualification are recorded separately; none substitutes for another.
Make failure paths, resource boundaries and concurrency contracts verifiable before optimizing performance. Never trade ownership or settlement conditions for apparent speed.
Each stage has executable exit conditions. Plan completion, implementation completion, software acceptance, physical acceptance and release qualification are different states.
The user has deferred physical device operations. The engineering model can first deliver software and HIL tooling preparation; the physical stage remains not executed until a real station and actual results are available.
2. 目录、语言与依赖边界¶
Recommended production-code organization follows these responsibilities. This table is a directory convention, not another automatic loading or startup framework.
Directory |
Engineering responsibility |
|---|---|
|
Small shared types, statuses and foundational contracts, with bounded infrastructure startup; no product main loop. |
|
CPU-local interrupt state, barriers, necessary atomic primitives and optional timing foundations. |
|
Exact silicon/variant, clocks, controllers, IRQ/DMA and private vendor SDK integration. |
|
Reviewed shared IP register algorithms, selected privately by the SoC target |
|
Typed I/O contracts, explicit implementation integration surfaces and necessary portable adapters. |
|
Bounded OS/backend integration; application tasks and scheduling policy belong to products. |
|
Shared components that can be selected, tested and linked independently. |
|
Configuration parsing, build entry points, artifact checks, evidence and packaging tools. |
|
Host contracts, fault models, link fixtures, external consumers and HIL tooling tests. |
Products and Boards may be external packages. Public examples demonstrate product assembly without becoming mandatory platform business modules. Maintained SoCs and Boards use explicit targets; directory scanning, name matching and implicit default platforms never select implementations.
2.1 Isolating C11 production code from C++ tests¶
First-version production modules use C11; necessary startup/CPU primitives use controlled assembly. Enabling tests must not add a C++ runtime, exceptions, RTTI or STL dependencies to production targets. Public C headers must be self-contained and provide required C++ linkage declarations so external C++ products can consume the C API.
New behavior tests for host C APIs, components and providers use C++17, GoogleTest and GoogleMock. Helpers, fault injection, host fixtures and mock SDKs belong only to test targets; directory-global includes/defines must not leak into production compilation. Python tools use stdlib unittest and do not require a Python runtime in embedded firmware. Historical C models remain regressions; new behavior follows the TDD contract below.
Verify at least three consumption boundaries: a pure C public-header consumer, a C++ public-header consumer and a real external product consumer. Production target interfaces and resource contracts stay consistent with tests enabled or disabled; instrumentation-profile artifacts are identified separately.
2.2 Public headers must establish a real compile boundary¶
Separate public, provider and private headers physically. Ordinary consumers receive only selected public include roots; SoC, Board and implementation adapter targets receive the provider/private roots they need. Placing every header in one PUBLIC root and saying “do not include it” does not establish isolation.
Checks include positive and negative cases: public consumers compile necessary types but cannot directly include vendor, private-controller or implementation-registration headers. Visible SDK source does not mean every target should inherit its include paths.
Manage vendor source and rules separately. Public structures must not contain vendor types or require products to inherit vendor compilation macros manually to use public platform APIs.
3. 标准 CMake 是唯一软件依赖图¶
Components, SoCs, Boards, tests and products express sources, usage requirements and link relationships through ordinary CMake targets. PUBLIC, PRIVATE and INTERFACE distinctions are contractual; do not depend on directory-global includes, links or compile options.
List maintained sources and known generated outputs explicitly. The first version does not discover arbitrary components through
GLOB_RECURSE.CMake targets are authoritative for software dependencies. Configuration JSON and generators do not maintain a parallel “component A depends on B” graph.
Target definitions and necessary semantic checks establish legal component selection. Generators emit selections and configuration facts; CMake propagates dependencies and determines build order.
Do not build a custom linker or reimplement CMake target graphs, test discovery or incremental builds.
Startup, vectors, static bindings and strong IRQ handlers that must survive linking require explicit retention roots and ELF checks. Global
whole-archiveor deleting retention roots must not conceal assembly defects. The default architecture does not generate a generic device registry.Propagate CPU/FPU/ABI, optimization and instrumentation options through named configuration targets, rather than appending conflicting flags in different subdirectories.
Component sources, public/private includes and target dependencies stay in their CMake definitions. Optional capability metadata describes implementation facts; it must not redefine software dependencies or claim validation solely from metadata.
Generator output should be small and inspectable: resolved configuration, configuration headers, selected-target identities, static resource bindings and necessary linker layouts. Humans maintain ordinary typed C initialization and data paths. Do not generate business workers, scheduling algorithms, driver state machines or a general execution DSL to save a few initialization lines.
4. 首版配置:assembly.toml 到 resolved.json¶
4.1 One default input model¶
Assembly uses product-owned TOML schema 2 in assembly.toml: reference an explicit Board, select its exact SoC part, then select the OS/backend, components, instance resources and budgets. Standard Python tomllib parses it; strict field models also provide editor schemas. The CLI exposes init/list-bindings/check/explain/generate.
assembly.toml contains no arbitrary software dependency declarations, shell commands or code expressions. Strings cannot redefine CPU instruction sets, ABIs, controller functions or arbitrary compile flags. Modes and optimization options come from supported named profiles; toolchain rules produce and record actual flags.
The first version introduces no overlay, inherit, recursive merge or additional configuration language. External products express requirements through complete, explicit assemblies. The same field must not have separate override rules in assembly, Board input, CMake cache and environment variables.
SoC inputs declare exact variants, physical memory, controllers and implemented capabilities. Board inputs declare oscillators, wiring and reviewed resource routes. Product assemblies declare application choices and required capacities. Schemas fix field ownership; conflicts fail rather than letting the last input win.
4.2 Parse once; derived outputs have one source¶
Configuration constructs frozen ConfigurationIR, ControllerIR, EndpointIR and MemoryBudgetIR values. Constructors consume IR directly; resolved.json retains the same decisions, input identities and resolver version. The same resolution derives:
Compile-time constants, separate private instances, read-only faces and typed IDs/getters in
nexus_factory.h.Explicit selections for CMake.
Board route and resource-conflict check results.
Linker/region definitions for product layouts.
Configuration digests and resource-budget inputs.
Never edit derived files manually. resolved.json is not user input either: missing or mismatched input identities require regeneration, without fallback to an old generated directory. Failed configuration leaves no partial bundle that can be consumed as success.
The resolver rejects duplicate TOML tables/keys, duplicate JSON fact keys, unknown fields, wrong types, out-of-range values, contradictory choices, missing inputs, unimplemented capabilities, illegal IRQ/DMA/pin routes and overlapping layouts. Declared capabilities cannot override implementation capabilities. String conversion to C/headers/CMake requires explicit encoding and escaping; user data is never executed as a script.
4.3 Separate build roots and caches¶
Each assembly + toolchain + profile combination uses a separate build root. A CMake cache must not silently switch a root to different silicon, Board, backend or ABI. Entry points verify identity and require a new root or bounded reconfiguration on mismatch.
Caches improve speed without establishing acceptance identity. CI may reuse correctly keyed caches, but formal candidates need at least clean-build evidence. Keys include tool environment, configuration and dependency identity rather than only branch names.
4.4 The role of Kconfig¶
The first version has no Kconfig frontend. Resource safety, static assembly and CMake builds do not require Kconfig.
If configuration scale later justifies an interactive choice/dependency UI, implement a separate Kconfig frontend targeting the same typed IR contract. Define one authoritative frontend and preserve semantic checks. Kconfig, TOML and CMake cache must not all become sources of truth, and implicit select/default must not bypass unimplemented-capability checks.
5. 外部 package 接入¶
5.1 Component packages¶
A component provides an ordinary CMake target, explicit public headers, private implementation and validation scope. Applications link it explicitly. add_subdirectory must not start threads, register product main functions, read devices or change the external environment.
Required hardware enters through typed ports and caller injection. Worker, queue, storage and deadline requirements must be contractual and provided or explicitly approved by products, rather than hidden initialization side effects.
5.2 Board package¶
A Board package contains exact board/silicon identity, declared inputs, a resource manifest and bounded C/CMake wiring. It owns no product tasks, private protocols, product partitions or manufacturing credentials. The existence of onboard devices and routes does not establish electrical validation.
Declared inputs cover compiled Board sources and files needed for generation. Before integration, check routes, resource conflicts, variant/density, clocks and necessary safe initial states. Hashes prove input identity without isolating arbitrary CMake execution; formal builds still use trusted source and controlled environments.
5.3 Product packages and SDKs¶
Product repositories provide assemblies, main/workers, business scheduling, protocols, partitions, updates and health policy. They consume platform and components explicitly. Standard platform initialization handles infrastructure without generating applications.
The first version primarily promises a relocatable source SDK. Binary SDKs, stable ABI, support windows and LTS each need separate policy and evidence. Every formal source package must be verified by real C/C++ consumers outside the package in fresh directories; success inside the source tree does not establish relocatability.
Pin external packages to exact versions or source commits/digests. Automatically downloading the newest package by name must not enter formal candidate builds.
6. 工具环境、工具锁与入口¶
6.1 A small set of tools, each with one responsibility¶
Responsibility |
First-version tool choice |
|---|---|
Build and test orchestration |
CMake、Ninja、CTest |
Production compilation |
Locked ARM GCC; Native GCC supplies the matching software-validation environment. |
Host C/C++ tests |
Locked GoogleTest/GoogleMock; no propagation to production targets. |
C/C++ formatting and selected analysis |
clang-format and clang-tidy from the same locked version series. |
Configuration, evidence and packaging tools |
Python; tool tests use stdlib unittest; Ruff provides consistent Python formatting/lint. |
Formal build environment |
Linux OCI image pinned by digest. |
Do not stack multiple formatters, equivalent analyzers or build systems by default. Add a second tool only when its distinct value is demonstrated, with explicit ownership, rules and maintenance cost. Tool checks need actual execution; a configuration file is not evidence of success.
6.2 One formal toolchain lock¶
One toolchain/environment lock pins ARM/Native compilers, binutils/sysroots, CMake, Ninja, Python, formatters/analyzers, Ruff and the build-image digest. Installers, CI and release tools consume it rather than declaring independent versions.
The lock records exact versions, content digests, legal origins and verification methods. Version strings alone do not identify compiler/sysroot bytes. Host platforms may have explicit development entries, but formal candidates initially use one verified Linux environment, without promising unverified host combinations.
A separate dependencies lock pins source revisions, contents, origins and licenses. Source and tool locks have distinct responsibilities; do not combine compiler installation and component dependencies into another software dependency solver.
6.3 One thin entry point calling native tools directly¶
tools/dev/dev.py calls real CMake, CTest, style and TDD gates, propagates exit codes and verifies fresh test reports. Presets choose build directories; additional native arguments pass through directly, while the sole resolver interprets assembly.
python tools/dev/dev.py doctor
python tools/dev/dev.py configure --preset native-debug
python tools/dev/dev.py build --preset native-debug
python tools/dev/dev.py test --preset native-debug
python tools/dev/dev.py check --preset native-debug
Local development and CI use the same entry point and underlying commands. CI YAML handles triggers, permissions, matrices and artifact transport without duplicating configuration parsing or checking algorithms. Records retain commands, working directories, non-sensitive environment summaries and exit codes; credentials never enter logs or artifacts.
The main platform CI runs all applicable complete check domains for every pull_request. Its push trigger follows only main and develop, avoiding two complete push/PR pipelines for the same feature commit. Manual and scheduled entry points remain available. Event deduplication does not remove PR checks, mode matrices or formal candidate acceptance.
6.4 Download caches reuse only locked tool archives¶
Tool preparation may cache archives. Every cache hit must still verify all archive bytes against the SHA-256 in the environment lock before extraction. Cache keys include the locked archive identity; a branch name, version string or old installation directory cannot replace content verification. Downloads use bounded attempts and timeouts. A temporary file is published atomically only after verification; exhaustion returns nonzero and must not change the source, downgrade the tool or skip verification.
A cache contains no acceptance conclusion. Each verification still executes the checks and produces fresh reports for the current source and tool identities. Old JUnit, analysis reports and success receipts cannot stand in for a successful cache hit. Formal offline builds and two independent clean builds remain required.
7. Profile、资源预算与性能门禁¶
7.1 Debugging, benchmarking and production have different identities¶
Development profiles provide symbols, diagnostics and appropriate checks; Native sanitizer profiles provide ASan/UBSan; benchmark profiles control measurement fixtures and minimize instrumentation; production profiles fix actual optimization, ABI, diagnostics and resource choices.
Profiles do not inherit each other’s artifact qualification. Production mirrors may retain debug symbols, whose presence alone does not change device contracts. Changes in flags, instrumentation or runtime code require new artifact identities. Instrumented timing is not production-image timing; actual release images still need the required acceptance.
The first version does not presume O3, Os or LTO is always best. Compare O2/Os/O3 and LTO on representative workloads, then choose one or two formal optimization profiles. Profile changes rerun compilation, ABI/static-binding/IRQ, correctness, external-consumer and resource checks. Caller-supplied flags cannot override optimization rules arbitrarily.
Production profiles retain necessary parameter, request-state, dynamic-borrow/revocation, resource and safety checks. Class/owner/lifecycle facts already established for fixed ports during build and control paths need not be rechecked on every GPIO data call. APIs define calling context and single-owner assumptions; debug identity assertions are not C permission isolation. Pure debug diagnostics may be disabled, but NDEBUG must not remove necessary failure handling. Optimizations that change numerical contracts, such as fast-math, are disabled by default.
7.2 Budgets come from products, not library advertising¶
Product assemblies provide reviewed Flash, static RAM, MSP/PSP, task-stack, heap/pool, buffer and deadline budgets, plus workload assumptions. The platform validates their expression; actual artifacts and field measurements verify satisfaction. Reasonable defaults are not product-approved budgets.
Resource reports distinguish Flash LOAD bytes from layout space, .data/.bss, linker-reserved heap/MSP, task/queue storage, management-domain heaps, runtime high-water marks and minimum margins. ELF file size, section Total including debug, or text+bss alone is not total device cost.
Every enabled instance has an explicit resource cost. Trim unused controllers and legacy storage against the actual BOM before discussing abstraction overhead. Before optimizing hot paths, measure fixed-pool capacity, lookup/critical-section duration, copies and background-worker cost.
7.3 Three classes of performance checks¶
Check |
Execution environment and gate |
|---|---|
Static resources |
PRs inspect actual ELF/maps, block absolute-budget violations and report changes against a baseline of the same profile. |
Controlled benchmarks |
Host or dedicated MCU runners record successful work, raw time/cycles, sample distributions, measurement overhead and environment; small timing differences on shared cloud runners are trends only. |
Product timing |
Dedicated HIL checks deadlines, IRQ response, transfer settlement and resource margins under actual production artifacts, loads and interference. |
Benchmarks validate successful work; FULL, fast error returns or discarded messages must not count as successful throughput. Pregenerate inputs and exclude unrelated key formatting, logging and measurement storage from the measured interval. Record means, percentiles, maximum observations and failures/retries/drops; average throughput must not conceal tail latency.
MCU timing records clocks, Flash wait states, FPU settings, IRQ/load modes, counter wrap and instrumentation overhead. CPU-cycle measurements and wire/IRQ end-to-end measurements have different responsibilities. Maximum observed duration is not a strict WCET proof; products declare measurement conditions, analysis scope and reserved margin.
Do not block ordinary feature PRs with unstable microsecond thresholds on shared CI runners. Relative timing gates require calibrated dedicated runners and repeatable baselines before tolerances and false-positive handling are reviewed.
8. 五层验证流水线¶
8.1 Fast local: find problems early¶
Run input/schema checks, formatting for changed owned files, incremental compilation and relevant contract tests. Public interfaces and cross-layer state changes require the developer to run applicable integration sets. Local results state their scope and do not directly qualify a release.
Before the complete commit gate, run inexpensive preflight checks: formatting and comments, input/schema validation, catalogue/preset/instance-selection consistency, generated code, and compilation and linking of affected targets. Remove these deterministic errors first, then freeze the same functional increment and run the ordinary complete staged-snapshot gate. Preflight does not remove checks from the complete gate or use filters, shards, SKIP or hook bypasses to obtain a pass.
Low-impact documentation and formatting changes use appropriate checks; do not create implementation-mirroring tests for each paragraph. Behavior, state, resource or error-semantic changes need observable success and failure-path tests.
8.2 PR: verify shared contracts remain intact¶
Required checks include owned-production compilation and selected warnings, formatting, selected static rules, key complete Native contracts with ASan/UBSan, public-header isolation, real external consumers and representative ARM linking/resource checks for two SoCs × two backends.
Separate execution by what is being verified. Native Debug, Release, ASan/UBSan and TSan CTest runs retain all compilation-dependent C models and GoogleTest programs. Their compiler options and runtime checks differ, so one mode cannot replace another. Python checks, SDK sub-builds with a fixed profile, and FreeRTOS tool contracts that do not depend on the parent mode run completely once in an explicit CI tool domain. They must not be registered repeatedly in all four Native presets, and their cases must not be deleted. If a sub-build actually consumes the parent mode, record that propagation in the plan and retain the required matrix.
Ordinary commits continue to execute the four complete tool domains, testing, configure, ci and workflows, plus the GoogleTest gate. The longer migration and SDK/HIL boundary contracts run in the explicit CI tool domain and are not added again to the commit gate. Tool reports identify the domain and record actual discovery/execution, skips, errors and counts. GoogleTest reports retain case identities. A domain-migration review separately checks the original module/case set and replacement mapping, and verifies removal from the old location, nonzero execution in the new location and the required CI dependency, so no suite loses its execution owner.
Use the following entry points for the complete mode-independent boundary contracts and the FreeRTOS tool suite. Their reports are separate and do not count as Native sanitizer coverage:
python -B tools/testing/run_tool_tests.py --suite contracts \
--report build/quality/tools-contracts.json
python -B tools/testing/run_tool_tests.py --suite freertos \
--report build/quality/tools-freertos.json
Configuration parsing, public API, ownership, IRQ/DMA, synchronization, linker, toolchain and release-tool changes trigger full cross-layer checks. Selective testing follows explicit dependencies and risk rules, not path-only guesses. Report the selected plan so an empty selection cannot appear passed.
Tests cover failure semantics and invariants: exhausted pools, stale references, busy/retry, partial initialization, timeout/cancel races, cleanup failures and final buffer return. Native fault models prove their model contracts, not real MCU electrical behavior or DMA settlement.
8.3 Nightly: verify breadth and complex paths¶
Run all maintained Boards/profiles, complete source-SDK consumers, fault/property/fuzz checks and longer software workloads; retain resource trends. Evaluate dependency/tool upgrades, optimization exploration, second compilers and additional hosts here before adding formal support.
Turn repeatable minimum checks for critical nightly regressions into PR checks. Nightly success does not replace unexecuted mandatory PR checks or justify an unlimited matrix without maintainers and capability commitments.
8.4 Hardware: verify real device conditions¶
HIL admission first validates exact artifact, profile, board/silicon, fixture, station and required instrument identity, then performs real operations. Check IRQ/DMA, electrical behavior, timeout/cancel settlement, extreme loads, stack/pool high-water marks, power loss/recovery and product deadlines.
Empty stations, wrong images/Boards, missing raw data and host-model-only results cannot produce physical passed status. Record instrument/fixture versions, calibration and load conditions; do not generalize one lightly loaded run on one Board to every density, Board and product.
Tool readiness and device operations are separate. Admission tests alone establish HIL tooling ready; while hardware is deferred, physical qualification remains not executed.
8.5 Release: promote the same candidate artifact¶
Release candidates bind exact source/dependencies/configuration/environment, actual compilation/linking, ELF/BIN/HEX and evidence. Verify source packages, licenses/SBOM, signatures/provenance and required product physical qualification. Release owners review explicit support scope and recovery policy.
After HIL or signing, promote artifacts with the same hashes rather than rebuilding an image with the same name. Code, configuration, dependency or environment changes create new candidates and rerun relevant qualification. Source-SDK acceptance does not automatically establish binary ABI, signatures, product certification or LTS.
9. Provenance、hermetic与reproducible分开验收¶
9.1 Provenance: traceable input/output relationships¶
Every release candidate binds one complete input tuple: platform/product source identities, external package/dependency identities, assembly/SoC/Board/layout inputs, resolved configuration, tool environment, profile and generator versions. Every artifact file has a content hash.
The one-to-one relationship means a candidate’s input context identifies that build’s artifacts, not that a Git SHA can produce only one binary. Different profiles or Boards create separate candidate records. Source SHAs do not replace artifact identity. Identical bytes can appear in different contexts; manifests retain each origin.
Execution reports retain actual commands, exits, planned/actually enumerated tests, compile databases, link inputs/outputs, raw logs and hash references. Unsigned hashes detect changes without proving publisher or approver identity; formal attestations and signatures need their own trust policy.
9.2 Hermeticity: read only declared inputs¶
Formal builds use pinned images and verified dependencies; downloading and preparation are separate from building. Builds have no network and read no arbitrary host directories, developer credentials, undeclared environment settings or system-tool fallbacks. Sources are read-only, outputs separate, and sources must not change during builds.
Record compiler/sysroot bytes and the tool environment they read, not just compiler banners. Pinned tool versions and Git revisions alone do not make a build hermetic; declared Board inputs do not constrain arbitrary CMake access to other directories.
9.3 Reproducibility: identical inputs repeatedly produce identical outputs¶
Perform two independent clean builds with different absolute paths and cache conditions; compare target ELF/BIN and declared deterministic package contents. Fix SOURCE_DATE_EPOCH and path mapping, control archive order, mtimes and generated text, and prevent current time or machine paths from introducing incidental firmware differences.
Store execution times, logs, signatures and deterministic payload separately. Avoid self-referential manifests and embedding run timestamps in binaries. For randomized signatures/wrapping, define comparison scope; ignoring arbitrary differences does not establish bit-exactness.
Claim reproducibility only after independent rebuild comparison. A pinned container plus hashes, one successful build or a deterministic source archive is insufficient.
Ordinary relocatable source-SDK acceptance establishes configuration, building, linking and execution after relocation; it does not guarantee identical ELFs at arbitrary installation paths. Without path mapping, fault location __FILE__ strings can contain absolute SDK paths; path length and link alignment can change Flash bytes. Static-cost differences across SDK prefixes therefore cannot directly establish resource optimization. The formal reproduction tools for current C11 firmware apply -ffile-prefix-map to source, consumer and build directories and compare two actual ELF/BIN outputs. That process must still execute before claiming byte-for-byte reproducibility.
10. 证据模型与失败传播¶
Use one versioned result schema for run identity, planned/executed checks, exit/status, raw-log/artifact references, qualified scope and limitations. Store full compile databases, maps and logs as referenced artifacts, rather than nesting the same large files in every profile.
Missing mandatory checks, zero executions, entirely skipped runs, stale results and tool failures never produce passed.
Distinguish passed, failed, not executed and unsupported; unsupported is not a validated implementation.
Historical results remain useful comparisons after source/configuration/environment changes but do not confer qualification automatically.
Explain results and unresolved conditions by risk; “N tests passed” must not conceal unexecuted critical acceptance.
Generate summaries from results. Workflow definitions, existing targets, role files and checked plan boxes are not execution evidence.
Retain evidence by candidate/run identity with immutable artifact references. Expiring CI artifacts must not be the sole long-term support record.
Diagnostics preserve actual errors and cleanup results rather than repeatedly wrapping them as generic failures. Tool failures retain investigable output and return nonzero; empty JSON or a successful artifact upload does not establish successful checks.
Timing reports distinguish end-to-end wall time, cumulative runtime of parallel jobs, and parent/child command relationships. Do not add nested child durations to their parent duration. Identify the scope of queueing, preparation, build, test, analysis and failed reruns separately. Do not infer percentages for unmeasured editing, analysis or waiting, or describe runner minutes as user waiting time. Speedup claims require actual before/after execution with the same inputs and conditions.
Keep critical source, input manifests, original failure output and final execution receipts in persistent storage. Temporary directories hold regenerable build caches and must not be the only recoverable delivery record. Keep one verifiable original evidence copy and reference it. Routine iterations must not establish parallel status pages, transport copies or new evidence frameworks.
11. 十人的工作与review模型¶
Codex currently maintains all Arch/SoC/Board, I/O/OS, component, build/evidence and platform-qualification domains. The ten-person configuration describes work domains and capacity without requiring named individuals. Role files validate paths, backup domains and automated gates; they do not invent GitHub accounts.
Assigned maintenance responsibilities, remote branch rules, independent human review, credentials and release authorization are recorded separately. Product decisions and private qualification remain in external product repositories.
11.1 Ordinary and high-risk changes¶
Ordinary internal changes undergo domain review and automated gates. Public APIs, dependency direction, IRQ/DMA/ownership, persistent formats, security and release-chain changes also need independent agent or consumer cross-layer review and executed failure-path evidence. Agent review is not independent human approval; where remote rules require human review, record its actual state.
Do not convene an architecture committee for every PR. Small ADRs cover real cross-layer decisions: problem, decision, tradeoff, interface and acceptance. Mechanical renames use ordinary PR descriptions. Review contracts, failure paths, concurrency, resources/timing and maintenance cost; tools handle formatting.
Keep changes reviewable: one explainable behavior with its callers/tests/docs. Do not split interface changes into prolonged broken intermediate states. When products cannot migrate atomically, specify a migration window, compatibility-layer owner and removal conditions; compatibility is not a permanent second object system.
Commit complete functional increments: converge implementation, configuration/instances, support catalogues, relevant regressions, bilingual contracts and one evidence attachment together. During development, run focused RED/GREEN checks and inexpensive preflight. After convergence, run the ordinary complete gate once on frozen inputs; later behavior changes require the relevant validation again. Do not split tightly related fixes, catalogues or logs into ancillary commits that repeatedly trigger the full gate merely to increase commit counts. Do not combine unrelated features in a way that obscures review boundaries.
11.2 Work items and exit states¶
Each item records its problem, owner, dependencies, acceptance and risks. Distinguish design accepted, implemented, software accepted, physically accepted and releasable. List external prerequisites separately rather than hiding hardware or responsibility gaps in implemented status.
Engineering assessment focuses on repeatable failures, escaped defects, resource/timing risks and delivery blockers. Lines of code, PR/test counts, aggregate coverage and document counts are not individual/module performance metrics. Coverage locates unverified paths without proving reliability alone.
Actual product and release owners handle releases. HIL reviewers assess real budgets and qualification scope; security/trust owners assess signing and recovery policy. CI executes mechanically provable conditions without replacing human product decisions.
12. 代码风格、注释与静态检查¶
12.1 Preserve current formatter and naming rules¶
Preserve the root .clang-format and .editorconfig rather than creating a new style. Lock tool versions in formal environments. The current configuration declares compatibility with clang-format 14 and later; chosen versions must be verified against it, without using upgrades to change style.
Keep 80 columns, four spaces, no tabs, attached K&R braces, type-side pointer alignment (char* ptr), indented switch cases, current include ordering and at most one consecutive blank line. Do not compress control statements or ordinary function bodies into single lines. Long typed interfaces wrap with the formatter rather than expanding to 100 columns. EditorConfig specifies UTF-8, LF, final newlines and per-file indentation.
Follow the contributing guide and coding standards: snake_case for files/functions/ordinary variables; existing module prefixes such as nx_ and osal_ for functions/public symbols; _t suffixes for types; UPPER_CASE for macros/enumerators/constants. File-static variables use s_; global variables use g_ and should be avoided. Private functions and file-local data are static. Prefer functions, const tables and typed structures over macros hiding control flow or cleanup.
Split files by actual state, lifecycle and responsibility. Do not fragment one state machine into many .inc files to shorten files, or collect every interface in a giant umbrella header.
12.2 Preserve Doxygen form and improve contract content¶
Follow the existing comment standard. C/C++ uses backslash Doxygen tags: \brief, \param[in], \param[out], \param[in,out], \return, \retval, \details and \note. Align descriptions with current templates; do not introduce @brief style.
Public headers describe parameter directions, returns, necessary errors and shared contracts. Sources do not repeat header \param/\return lists; use \brief, \details and \note for implementation, and simplified \brief for static helpers. Structure/enumeration members use /**< ... */; ordinary/trailing comments use /* ... */, with two spaces before trailing comments. Sections use existing /*---...---*/ templates. C/C++ examples follow the same forms.
Headers retain file headers containing \file/\brief/\author; C sources retain complete \version/\date/\copyright headers, adding \details when needed. Preserve fuller existing headers and licenses. Keep fields accurate without inventing authors, versions or dates. Git/release history supplements rather than replaces file headers; functions do not need new handwritten change logs.
CMake and Shell use # comments; Python uses # and triple-quoted docstrings. C/C++ comment forms do not apply to these languages. Third-party sources retain upstream style and licenses.
Public APIs provide the caller’s contract in those existing forms:
Item |
Required meaning |
|---|---|
context |
Task/ISR context, permitted IRQ priority or masking conditions, and single-core/SMP scope. |
timing |
Blocking behavior, timeout/deadline origin, units, zero/forever meaning and progression responsibility. |
ownership |
Object ownership, borrow start/end, copied-handle behavior and validity after close. |
callback |
Execution context, lock state, reentry permission and borrowing before/after callback return. |
failure |
Return codes, output validity, remaining ownership and retry/recovery behavior. |
resources |
Fixed capacity, possible allocations, copies, workers and budget requirements. |
Put common rules on types or module contracts; functions document necessary differences using existing \details/\note forms. Internal comments explain errata, barriers, races and invariants rather than restating code. Preserve traceable requirements and licenses without inventing requirement IDs. Better contracts retain the current presentation style.
TODOs reference work items with owners and exit conditions. Capability, timing and “thread-safe” claims need specific scope; wording does not replace proof.
12.3 Selected warnings and static rules¶
Owned production uses Wall/Wextra and reviewed prototype, shadow, conversion and similar rules. Formal gates block new diagnostics in selected rules. Establish stable meaning under C11 and supported toolchains before enabling rules; do not enable every analyzer rule and produce mass ignores.
clang-tidy consumes actual compile commands and generated configuration within owned boundaries. Vendor targets are handled separately; global silencing must not hide owned defects. Suppressions need specific reasons and local scope; no unexplained whole-directory exclusions.
Deduplicate static analysis only for identical actual compilation contexts: the same canonical source, working directory, compiler and complete ordered semantic arguments. Strip only explicitly recognized output-file and dependency-file generation arguments. Entries with different macros, include search order, CPU/ABI, language standard, optimization or instrumentation remain separate; never merge by source path alone. Reports record the original entries, deduplicated contexts and their mapping to representative entries. Deduplication changes scheduling, not rules, diagnostics or failure propagation.
Analysis parallelism must have a positive integer limit selected for available memory and runner capacity. The default is serial; CI may explicitly use --parallel 2. Child processes use separate outputs, and aggregation waits for all exits. Any execution failure or diagnostic outside the allowed boundary still fails. Timeouts and cancellation must stop and reap started processes, leave no background analysis, and never treat an empty report as success. Report elapsed parallel time and cumulative task time separately.
When a CodeQL database and its intermediate query cache are stored on tmpfs, their files share one cgroup memory budget with query processes, child processes and kernel memory. Free space on separate tmpfs mounts does not provide independent RAM budgets. Set --ram, --threads and --max-disk-cache explicitly from observed headroom, leaving room for database growth, temporary files created by running queries and other processes. --ram is a memory target for the evaluator; file-backed mmap can cause actual usage to exceed it. --max-disk-cache limits the intermediate-result cache and does not impose a hard quota on running queries, all temporary files or the entire cgroup. A build/ path does not establish a cache role; classify files by their producer role and actual references before cleanup, treat postlink audit JSON, .su, *.compiler.raw and *.objdump.raw as producer evidence, and retain the original failure and recovery facts.
After an OOM or interruption, retain the commands, receipts, logs and result identities from the original failed and partial attempts. Confirm that their processes have fully exited, then check the finalized database’s core identity, source, tools and query scope. When regenerable caches need cleanup, use the pinned version’s official database cleanup --cache-cleanup=clear --max-disk-cache=0 -- <database> and check the core database identity before and after cleanup. During failure recovery for the same immutable source, do not extract the verified database again.
When the target source semantics change, first extract and finalize a database for the new source, then run the complete queries; --rerun on the old database does not update its source. A full --rerun on the same database applies only when the Source/dataset are unchanged but the fixed-query or model identities have changed, or reuse evidence is insufficient. Official --no-rerun may complete missing queries only when the original Source/dataset/CLI/fixed-query/model inputs are all unchanged and completion evidence is complete for every reused query. That evidence must include completion logs, runinfo, fully readable BQRS and a binding to the current inputs; queries lacking it must be recomputed. Then perform a new full-suite interpretation. For this C++ analysis, all 183 queries must map one-to-one, with no missing or timeout warning, and complete interpretation and SARIF admission must pass before reporting a completed cross-attempt analysis. Do not describe it as a single fresh 183-query run; retain earlier timeout/cancel attempts as failed, and require actual results for each finding disposition.
The RAM target of 4 GiB, cache target of 768 MiB and 2 query threads recorded in this 4 CPU / 16 GiB environment correspond to the actual arguments --ram=4096 --max-disk-cache=768 --threads=2. These values describe one resource adjustment in this environment. They establish no default for other environments, optimal configuration, peak-usage guarantee or successful query result.
Cppcheck 2.13 does not read -isystem from compile databases. Tools project it to -I per actual translation unit, preserving ordinary-before-system search order; compiler ABI queries still use original arguments. Like clang-tidy, analysis excludes individually only third-party headers under vendors/ or ext/ that are in that translation unit’s actual SYSTEM directories, recording paths, canonical paths and SHA-256. Owned headers, third-party implementation files, non-SYSTEM directories and aliases to owned files stay checked. No global diagnostic suppression or rebaselining. This owned-code boundary does not prove third-party code defect-free.
These rules form an engineering baseline, not MISRA, functional-safety or other certification. Products requiring conformity need exact standards/versions, scope, deviations, tool qualification and independent review; cert-* or analyzer settings alone do not prove compliance.
12.4 Current implementation and historical migration¶
The previous architecture had inconsistent file headers, comment tags and formatting. This iteration removed old HAL/OSAL, Runtime and business services and unified current Core, I/O, OS, Arch, SoC, components and tests under existing formatting and backslash Doxygen rules. Root .clang-format and .editorconfig are unchanged.
The format directory list covers all maintained C/C++ and .inc files, including measurement workloads. The installer pins pre-commit 4.3.0 and clang-format 14.0.6 and installs real pre-commit/commit-msg hooks. Default commit checks inspect staged snapshots; manual --files/--all-files checks inspect worktrees. CI runs the same whole-file gates; static analysis uses actual compile databases.
The first audit’s 596 files and 337/291 formatting/comment debts are historical traceability only. Both current frozen baseline debt sets are empty. All new source passes whole-file rules, without new or rebased exemptions. Actual gates for the current commit determine acceptance; historical counts cannot be inherited. Archived records and third-party source retain their own identities rather than posing as current owned-code acceptance.
Environment locks, the unified dev entry, source SDK, ELF budgets, offline reproducibility and HIL tooling are implemented; see delivery scope. Formatters handle layout and comment checkers handle mechanical rules; API semantics still need review and behavioral tests. Security diagnostics are reviewed within maintenance scope without global silencing. Software checks do not replace physical timing, product safety or conformity qualification.
13. 错误、所有权与并发API规范¶
Use a small set of actionable status classes: invalid input/state/context, unsupported, not found, busy, full/empty, timeout/cancelled and I/O/integrity errors. Optional vendor raw flags remain details without leaking vendor types or flattening distinct errors into generic failures.
Return statuses do not trigger global logging or global error callbacks automatically; callers choose policy. Concurrent APIs do not use global errno/last-error as authoritative results.
ISR diagnostics use bounded, disableable binary records. Suitable tasks handle string formatting and complex storage.
Status and ownership are orthogonal. Define cancel acceptance, terminal results and settled buffers separately;
TIMEOUT/CANCELLEDalone does not prove buffers returned.Rejected create/open or submit admission creates no valid new handle/ticket and no new borrow. Start/error/timeout after ACCEPTED belong to that request’s result and cannot be relabeled as zero-borrow rejection. Query/receive contracts state whether failure leaves outputs untouched or invalid.
Failed close preserves existing references and responsibilities. Rollback reports separately retain the original failure, cleanup failure and remaining ownership; a second error must not overwrite the reason.
Check all lengths, offsets and resource multiplication before operations. Protocol/persistence encoding does not depend on C structure padding, enum width or host endianness.
volatileis not task/ISR synchronization. Explicit Arch contracts govern atomics, barriers and IRQ masks; single-core rules are not SMP safety.Assertions express internal invariants and explicit fail-stop behavior, not recoverable input/resource handling. Production retains necessary boundary checks.
Finite-resource exhaustion returns contractual failure rather than looping until eventual success. Blocking/retries need budgets; callers or explicit component workers own progression. No undocumented hidden threads or timers.
14. 工程批次与退出条件¶
Batches may accompany vertical platform slices, but actual results close their status. Time and headcount do not replace exit conditions.
Batch |
Deliverables and exit conditions |
|---|---|
E1 Tools and entry points |
A fresh checkout enters the locked environment; one thin entry calls standard build/test tools; local and CI share implementation and one version authority. |
E2 Configuration and integration |
One assembly/SoC/Board resolution creates the resolved bundle; contradictory/unsupported inputs fail; real external products/Boards integrate; positive and negative public/private/vendor compile-boundary checks pass. |
E3 Vertical contracts and code rules |
An end-to-end slice handles success, failure, context, ownership and budgets; C11 production/C++ tests are isolated; formatter and selected rules actually execute. |
E4 Software pipelines and evidence |
PR/nightly risk matrices execute; missing/zero/skipped/stale results are actually rejected; resource overruns are blocked; duplicate old entries are removed; results bind exact inputs and artifacts. |
E5 Hermetic builds and HIL preparation |
Formal network-disabled builds pass; independent clean rebuilds compare; HIL admission rejects wrong artifacts/profiles/empty stations; physical qualification remains unexecuted until real measurements. |
E6 Product release qualification |
Actual evidence and reviewers cover physical, safety, recovery and load budgets; sealed candidates pass signature/provenance verification; identical-hash artifacts are promoted; responsibility and support scope are real. |
E1–E4 deliver a complete software engineering system. Tool readiness, hermeticity and reproducibility in E5 are accepted separately; actual HIL closes independently. Neither this handbook, a plan nor software pass counts replaces E6.
E1–E6 describe acceptance domains, not another schedule or task authority. The sole implementation ledger is B0–B6 in execution checklist: E1/E2 start in B1; E3 runs through B2–B4 slices; E4/E5 converge in B5; B6 seals only software candidates. E6 completes later when external products and actual hardware evidence are ready.
15. 首个落地切片¶
First take one exact Board, one OS/backend and one external product from a fresh checkout to a real consumer build, then expand to another backend and SoC. The slice includes small typed I/O, error/cleanup paths, budgets, public/private include boundaries and raw artifact evidence.
Let this slice justify tools, schemas, CMake modules and review rules. Every new combination adds executed validation and maintenance responsibility. Do not build a vast framework first and claim completion through empty fixtures.
16. TDD 与 GoogleTest/GoogleMock 强制开发合同¶
New production behavior and defect fixes follow RED → GREEN → REFACTOR: write a failing public-contract test first, execute it and verify that required behavior is missing, implement the smallest correct change, then refactor and rerun relevant tests. For new APIs, initial compile failure may establish interface RED; executable behavior tests follow once the interface exists. Retrofitting regressions for existing behavior does not prove historical TDD.
GoogleTest provides assertions, fixtures and boundary cases; GoogleMock replaces only dependency ports such as hardware, time, notification and executors. Tests call actual public Nexus APIs rather than mocking entire providers, factories or owners and proving their own expectations. Prioritize instance isolation, zero-side-effect rejection, resource conflicts, deadlines, post-cancel draining and final publication order. Success and failure together define the contract; do not duplicate implementation details for counts.
The framework source is provided by the Git submodule ext/googletest. dependencies/googletest.lock.json records official version 1.16.0 at upstream base 6910c9d9165801d8827d628cb72eb7ea9dd538c5; the Gitlink and selected lock commit pin organization patch 58ec32c09ed8a099e55cbdab9418597c4fd9b14f on that base, including the host-test RE copy-assignment ownership fix. Dependency inventory records the official upstream base and the selected patch separately. Dependency preparation is explicit. CMake uses only prepared local sources, downloads nothing and never falls back to a system framework; missing sources, a version mismatch or local submodule changes cause failure. tests/google/CMakeLists.txt enables C++17 and keeps the dependency within the Native test tree. ARM builds, builds with testing disabled and source SDK consumers acquire no C++ runtime or test-framework dependency. The license and notices remain with the pinned submodule source.
Unified execution entry:
python scripts/ci/tdd_gate.py --all --preset native-debug
python tools/dev/dev.py test --preset native-debug
The first command runs the four complete tool domains of the commit gate, configures and builds nexus_google_contracts, discovers the complete cases from each binary, then executes the actual tests without filtering. It removes GoogleTest filter/shard settings from the environment, deletes old XML, rejects zero cases, nonexecution, skips, failures, stale reports and mismatches with discovery, and preserves raw output, GoogleTest XML and summaries in build/<preset>/tdd/. The second command runs all compilation-dependent CTest models and GoogleTest regressions for that Native preset. Mode-independent tool contracts run in the explicit CI tool domain instead of repeating in each Native preset. Neither execution replaces the other.
The installed pre-commit hook runs the same GoogleTest gate for changes to production, tests, configuration and build behavior. Prose-only changes do not require a rebuild. Before and after execution, the hook checks unstaged/untracked behavior changes and compares the initial and final git write-tree, requiring an identical staged tree. Editing or restaging source during execution rejects acceptance and produces no new summary, preventing workspace fixes or concurrent edits from falsely certifying the source about to be committed. CI independently verifies the corresponding contracts through the complete Native plan and explicit tool domains, regardless of whether a contributor installed or ran local hooks. --no-verify, SKIP, disabled tests or handwritten XML do not constitute valid acceptance.
Behavior-change reviews name failing tests, actual RED commands/reasons, GREEN commands and scenario coverage. Review checks whether tests first define observable contracts. Automation proves execution, report integrity and validation of current source, but cannot mechanically prove every developer’s historical test-first ordering. One green gate is not complete TDD-process compliance.
16.1 Retaining, merging and deleting tests¶
A test earns its place through an observable contract, an independent failure path or a historical defect, not through file or assertion counts. Retain distinct risks across normal, boundary, rejection, failure and recovery scenarios. Hardware models, ARM linking and actual HIL establish different facts; calling the same API does not make them duplicates.
Decision |
Required conditions |
|---|---|
Retain |
Identify the contract or defect regression, the production path under test, an independent oracle and the specific defect that a failure prevents |
Merge |
Inputs, execution context, fault injection and observable assertions are equivalent. The review maps original case identities to replacements, retains distinct contract scenarios and diagnostics, and prevents one failure from hiding subsequent scenarios |
Delete |
Obsolete behavior is explicitly removed, or a maintained test takes over the complete equivalent scenario. The review records the old case identity, reason and replacement case/contract, executes the replacement suite and confirms complete registration |
An independent oracle comes from a public contract, precise datasheet facts or predetermined errors/outputs. Do not call the parser under test to calculate an expectation and then use that same result to prove the parser correct. Generated code must undergo actual compilation/linking or behavioral verification; matching a generator to a template constructed by itself is insufficient.
Remove assertions that only prove a test calls itself, repeat constants or implementation branches, check calls without a contract result or necessary side effect/order, or mechanically freeze the current test count. If they also verify failure propagation, enumeration completeness or actual builds, first preserve that contract in a suitable test. Report checkers must still reject zero execution, failures, skips and discovery/execution mismatches. Fewer cases alone are not an optimization result. Do not weaken assertions, skip cases or widen mock boundaries to keep a green result.
When reusing a heavy fixture or parameterizing equivalent inputs, enumerate each contract scenario separately, keep failures identifiable and preserve required state isolation. Reuse only immutable inputs and preparation steps proven to leave no residual state. Scenarios involving cold starts, failure contamination, borrows or recovery must not share an instance that has not been reset. Test deletion and execution-domain migration are behavior changes subject to the same TDD, review and complete-gate requirements.