Skip to content

[M1-SYS-01] System registry - #24

Merged
offdev merged 2 commits into
masterfrom
m1-sys-01-system-registry
Sep 14, 2026
Merged

offdev merged 2 commits into
masterfrom
m1-sys-01-system-registry

Conversation

@offdev

@offdev offdev commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Roadmap step M1-SYS-01 (system registry, roadmap/M1-heartbeat.md), scope only.

What

Plain registered system functions (FR-1.3: no class, no inheritance) with declared time budgets and declared component I/O:

  • src/laige-sim/include/laige/sim/system.h (new):
    • SystemId — dense 32-bit ids from 1, registration order, per-world, deterministic (ARCH-010, the ComponentTypeId precedent);
    • SystemDef — name + SystemFn (void (*)(World&, SystemContext&)) + budgetMs in milliseconds as fpx16_16 (exact, ADR 0002 — keeps the future M1-DET-01 source scan float-free);
    • LAIGE_SYSTEM(Name, budget_ms) — namespace-scope macro: the function declaration + the Name##Def def variable (the PRD Appendix B shape);
    • SystemContext — the per-tick world view; each<T1..TN>(fn, Read/Write tags...) delegates to World::each (definition out-of-line in entity.h where World is complete);
    • Io<T, Access> — the per-component declared I/O tag; a component appears at most once per system (a set, not a multiset) — M1-SYS-02's conflict checks consume the disjoint read/write sets;
    • SystemInfo — the snapshot the scheduler/profiler pull (declaresRead/declaresWrite);
    • kMaxSystems = 256 (engine-level bound, CORE-005).
  • World::registerSystem(def, Io<...>...), World::systemCount(), World::system(id) — the World home in entity.h + systems.cpp; the fixed record table is allocated in create() (setup path, like the component registry); the registry travels with the world on move and survives clear().

Validation (normative order — first failure wins)

failure result event
moved-from world InvalidArgument (no warn, the registerComponent precedent) —
null/empty name InvalidArgument + warn system/name_invalid
null run InvalidArgument + warn system/run_invalid
budget <= 0 InvalidArgument + warn (the budget must be explicit) system/budget_invalid
duplicate name InvalidArgument + warn system/duplicate
Io<T>: T unregistered in this world InvalidArgument + warn system/io_unregistered
same component declared twice (any access) InvalidArgument + warn system/io_duplicate
more than kMaxSystems systems BudgetExhausted + warn system/budget_exhausted

A non-component T in Io<T, ...> is a compile error (static_assert). Every warn is rate-limited (LOG-004), subsystem system, with stable fields (name, budget_raw, existing_system_id, component_id, systems).

Tests

New SystemRegistry suite — 25 tests, CTest entry system_registry (also in the TSan property list): registration, dense ids, the def value copy, the I/O sets + zero-I/O pack, context delegation (write + read paths through the plain functions), every validation error, the 257-system budget, system() id validation, id stability across two worlds (ARCH-010), move/clear/moved-from-world lifetime, the zero-alloc registration window (test-only operator-new counter, non-sanitizer trees), and the warn-once + rate_limited sink checks.

Verify (local)

  • ctest -R system_registry green (25/25, incl. the zero-alloc window); full suite 41/41 on build.
  • Full suite 41/41 on build-asan (leak-free), build-tsan, build-clang, build-release, build-shared; zero new warnings under NFR-8.10.
  • laige-api.json regenerated (489 symbols); api-real-tree + the api fixture tests green.
  • tools/laige-include-lint OK (27 source files, 1/10 vendored deps).

Docs (CORE-006)

  • docs/api/system_registry.md (full contract + Performance section), linked from docs/README.md;
  • src/laige-sim/README.md status updated (incl. the M1-ECS-06/07 lines);
  • Progress Board: M1 7 -> 8 (Total 27 -> 28); Change Log row (this PR); roadmap box checked.

Notes

  • SystemDef carries the declared I/O at registration, not in the def: component ids are per-world runtime values and cannot be baked into the compile-time def (documented in the header).
  • The traits are in class form (IsIoTag/IsIoComponent): the api scanner parses class template partial specializations; variable templates are an unsupported scanner construct (avoided rather than extending the scanner in this step).
  • The LAIGE_SYSTEM expansion ends in ;: the call site (per the PRD sketch) carries no trailing semicolon.

Roadmap step M1-SYS-01 (roadmap/M1-heartbeat.md), scope only.

System registry (FR-1.3: plain registered functions with declared
time budgets and declared component I/O):

Public header src/laige-sim/include/laige/sim/system.h:
- SystemId: 32-bit dense id assigned in registration order (0
  reserved, per-world, deterministic - component.h id contract).
- SystemDef: name + run (SystemFn = void(*)(World&, SystemContext&))
  + budgetMs in ms (fpx16_16, exact - ADR 0002; the future sim source
  scan stays float-free).
- LAIGE_SYSTEM(Name, budget_ms): namespace-scope macro declaring the
  plain system function and building the def (Name##Def) - no class,
  no inheritance (FR-1.3).
- SystemContext: the per-tick view of the world; each<T1..TN>(fn,
  Read/Write tags...) delegates to World::each (query.h; the
  definition lives in entity.h where World is complete).
- Io<T, Access>: the per-component declared I/O tag for
  World::registerSystem; the I/O is a set, not a multiset (disjoint
  read/write id sets in the world's record; ascending-id
  enumeration order documented).
- SystemInfo: the registered system's snapshot (def copy +
  declaresRead/declaresWrite), the M1-SYS-02/M1-PROF-01 feed.
- detail::SystemRecord + the IsIoTag/IsIoComponent/IoComponent traits
  (class form - the api scanner parses class partial specializations;
  variable templates are an unsupported scanner construct).

World API (entity.h, the World home): registerSystem(def, Io<...>...)
(header-defined template), systemCount(), system(id) (systems.cpp).
The fixed kMaxSystems (256) record table is allocated in create() like
the component registry; the registry travels with the world on move
and survives clear().

Validation (first failure wins; every failure is one rate-limited
structured warn + Status - FR-12.3, LOG-004): moved-from world ->
InvalidArgument (no warn, like registerComponent); null/empty name ->
system/name_invalid; null run -> system/run_invalid; budget <= 0 ->
system/budget_invalid (the budget must be explicit and positive);
duplicate name -> system/duplicate (the roadmap's named property);
Io T not a component -> compile error (static_assert); Io T not
registered in this world -> system/io_unregistered; same component
declared twice (any access) -> system/io_duplicate; > kMaxSystems ->
BudgetExhausted + system/budget_exhausted. The def is value-copied
into the record table: no allocation at registration (setup path).

Tests: new SystemRegistry suite (ctest -R system_registry; 25 tests) -
registration, ids, the def value copy, the I/O sets + zero-I/O pack,
context delegation (write + read paths through the plain functions),
every validation error, the kMaxSystems budget (257 distinct names),
system() id validation, id stability across two worlds (ARCH-010),
registration-order-determines-ids, move/clear/moved-from-world
lifetime, the zero-alloc registration window (operator-new counter,
non-sanitizer trees), and the warn-once + rate_limited-sink checks
(system/duplicate fields, system/budget_invalid budget_raw field).

Docs (CORE-006/DOC-007): docs/api/system_registry.md (full contract +
Performance section) linked from docs/README.md; src/laige-sim/
README status updated (including the M1-ECS-06/07 lines); entity.h
preamble + World member docs carry the M1-SYS-01 note;
laige-api.json regenerated (489 symbols; api-real-tree green).

Verify (local):
- ctest -R system_registry green on build (25/25, incl. the
  zero-alloc window), full suite 41/41 on build.
- Full suite 41/41 on build-asan (leak-free), build-tsan, build-clang,
  build-release, build-shared; zero new warnings under NFR-8.10 on
  every tree.
- laige-api regenerated + api-real-tree/api fixture tests green;
  tools/laige-include-lint OK (27 source files, 1/10 vendored deps).
@offdev
offdev merged commit 73b0ea5 into master Sep 14, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant