[M1-SAMPLE-01] hello.laige (headless template) - #40
Merged
Merged
Conversation
M1-SAMPLE-01 (roadmap/M1-heartbeat.md, NFR-13.5, PRD §9.4/§13, TEST-004,
ADR 0002, ARCH-010, CORE-008): the first complete Laige game — the
headless template that every game project (human or AI agent) starts
from: one component, one system, one entity, deterministic by default,
records and replays its own log.
- samples/hello/: hello.cpp (99 code lines < the PRD §9.4 budget of
100; the NFR-13.5 "heavily commented" requirement lives in the
~76 comment lines and does not count), config.json (canonical
60 Hz / 8-entity / 256-churn / 0x1F055EED / fpx16_16 config),
hello.laige (provisional manifest — M3-ASSET-01 owns the real
format), LICENSE, README.md (the full contract: files, build/run,
CLI + stream + exit codes, the game walkthrough, replay, tests,
line budget).
- The game: PlayerPos (M::Vec2, LAIGE_COMPONENT + LAIGE_DETERMINISM_SAFE
at namespace scope — the marks specialize laige::detail traits and
cannot live in an anonymous namespace), MovePlayer (LAIGE_SYSTEM;
constant +1/tick per axis wrapped in the [-16, 16] box; the each
Status is cast away per the FR-1.3/query-guard contract — asserts in
debug, warns in release), one entity at the box center. The config
is embedded in the binary (config.json is the declarative record
consumed by `laige-run --headless` — the hello_config_valid CTest
keeps the two in step); the registration order is part of the replay
identity (ADR 0002).
- stdout contract (docs/api/detcheck.md): the tick-0 line plus one
World::stateHash line per completed tick — 301 lines at 300 ticks —
the detcheck scenario stream; stderr: the engine logging facade +
the `hello {headless|replay} ticks=N status=ok` summary; exit codes
0 ok / 1 run failure (runSystems/writeFrame/finish) / 2 usage/IO/
identity (strict arg parse; paired options; --log/--replay exclusive).
- Replay: --replay LOG records its own run (debug builds only —
parity with the engine's startReplayRecording policy; exit 2 under
NDEBUG), --log LOG loads the log, checks the replay identity
against its own (world, config) via replayIdentityDiff (mismatch =
rejected replay, never a silent divergence), and re-runs the log's
frame count through the same tick loop; record → replay is
byte-identical (hello_replay CTest). laige-replay cannot replay a
game log (it registers only the built-ins — different identity);
the scenario binary carries the mode (docs/api/replay.md).
- Build: samples/hello/CMakeLists.txt (laige_apply_engine_policy +
laige_apply_simmath_policy, links laige-sim, RUNTIME_OUTPUT_DIRECTORY
= samples/hello/bin so the CI detcheck step invokes it from the
repository root with no arguments — the shared-path caveat is
documented in the README); wired into the root CMakeLists (the
sample is a shipped project) and .gitignore (samples/*/bin/).
- CI: tests/sample/ — 9 CTest entries with TSan env blocks and
generated scripts (the tests/replay pattern): hello_scenario
(no-arg run, 301-line structural check, stderr summary), hello_replay
(record → replay byte-identical; Debug-gated — recording is an
NDEBUG-gated feature), hello_replay_missing / _malformed (exit 2 +
`hello: replay:` naming), hello_mode_exclusive, hello_usage_unknown /
_missing_value (exit 2), hello_config_valid (laige-run --headless
consumes config.json, status=ok), hello_line_budget (CMake-core
counter — code lines = non-blank, non-// lines; the script protects
semicolons before the CMake list split and spells the whitespace
class explicitly, since CMake regexes have no POSIX classes).
- CI wiring: the detcheck job's real-scenario step (ci.yml +
ci-pull.yml) self-activates now that samples/hello/bin/hello is
built in the job — two no-arg runs must both exit 0 (M1-DET-04
swaps this for the build-asan vs build two-configuration
comparison); the ci.yml step comment was stale and now matches.
- Docs: detcheck.md (the real scenario now exists — the CI status and
hash-scope paragraphs updated), replay.md (scope: the scenario
binary's own loadReplay + replayIdentityDiff + tick loop),
building.md (the two hello command rows + the target note),
docs/README.md (M1 progress + the Getting-started link).
Local verification: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug +
build warning-clean (-Wall -Werror); ctest 76/76 on the Debug tree
(incl. the 9 hello entries) and 9/9 on a fresh ASan+UBSan tree
(build-asan, -DLAIGE_ASAN=ON); Release tree spot check — no-arg run
exits 0, --replay exits 2 (the NDEBUG gate); the CI forms from the
repository root: `./samples/hello/bin/hello` (301 lines, exit 0) and
`./build/bin/laige-detcheck --run-a=samples/hello/bin/hello
--run-b=samples/hello/bin/hello` (both exit 0, streams byte-identical);
laige-determinism-lint OK (20 files, 0 violations),
laige-include-lint OK (37 files, 1/10 vendored); api-manifest: the
sample adds no public header (no drift — the api tests pass in the
full suite). No float/double tokens in the sample source. Untested
locally: the MSVC/AppleClang paths and the CI-only TSan lane
(single-threaded binary; the ASan+UBSan run covers the memory side).
Compat: additive only — no existing symbol, behavior, or CI gate
changed.
- roadmap/M1-heartbeat.md: M1-SAMPLE-01 box checked (the step's Verify holds: CI builds hello, runs it, replay is bit-exact (hello_replay CTest + the detcheck job's real-scenario step), and the game-code line count is < 100 checked in CI (hello_line_budget — 99 code lines). - roadmap/README.md: Progress Board M1 15 -> 17 done (also picks up the M1-DET-03 box that was checked without a board update), status in progress (M1-SAMPLE-01); Change Log line for 75d1df6.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements M1-SAMPLE-01 ·
hello.laige(headless template) perroadmap/M1-heartbeat.md.The first complete Laige game — the headless template every game project (human or AI agent) starts from: one component, one system, one entity, deterministic by default, records and replays its own log.
What lands
samples/hello/—hello.cpp(99 code lines < the PRD §9.4 budget of 100; the NFR-13.5 "heavily commented" requirement lives in the ~76 comment lines, which don't count),config.json(canonical 60 Hz / 8-entity / 0x1F055EED /fpx16_16config),hello.laige(provisional manifest — M3-ASSET-01 owns the real format),LICENSE,README.md(the full contract).PlayerPos(M::Vec2, component + determinism marks at namespace scope),MovePlayer(LAIGE_SYSTEM; constant +1/tick per axis, wrapped in the[-16, 16]box), one entity at the box center. Config embedded in the binary;config.jsonis its declarative record (thehello_config_validCTest runslaige-run --headlessagainst it).World::stateHashline per completed tick (301 lines at 300 ticks); stderr = engine logging +hello {headless|replay} ticks=N status=ok; exit 0/1/2.--replay LOGrecords its own run (debug builds only — parity with the engine'sstartReplayRecordingpolicy; exit 2 under NDEBUG);--log LOGloads the log, checks the replay identity viareplayIdentityDiff, and re-runs the log's frame count through the same tick loop. Record → replay is byte-identical. (laige-replaycan't replay a game log — it registers only the built-ins — so the scenario binary carries the mode; noted indocs/api/replay.md.)tests/sample/: 9 CTest entries (hello_scenario,hello_replay(Debug-gated),hello_replay_missing/_malformed,hello_mode_exclusive,hello_usage_unknown/_missing_value,hello_config_valid,hello_line_budget— a CMake-core code-line counter checked in CI per the step's Verify). The detcheck job's real-scenario step self-activates: both no-arg runs must exit 0 (M1-DET-04 swaps in the two-configuration comparison).detcheck.md(the real scenario now exists),replay.md(scope),building.md(command rows + target note),docs/README.md(M1 progress + link),ci.ymlstep comment.Verification
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug+ build warning-clean (-Wall -Werror); ctest 76/76 on the Debug tree (incl. the 9 hello entries) and 9/9 on a fresh ASan+UBSan tree (-DLAIGE_ASAN=ON).--replayexits 2 (the NDEBUG gate)../samples/hello/bin/hello(301 lines, exit 0) and./build/bin/laige-detcheck --run-a=samples/hello/bin/hello --run-b=samples/hello/bin/hello(both exit 0, streams byte-identical).laige-determinism-lintOK (20 files, 0 violations),laige-include-lintOK (37 files, 1/10 vendored), api-manifest no drift (the sample adds no public header). Nofloat/doubletokens in the sample source.Compatibility
Additive only — no existing symbol, behavior, or CI gate changed.