19 KiB
C++ Template Design Source of Truth
This document is the implementation-facing source-of-truth index for the C++ project template redesign. It does not replace the detailed drafts; it identifies which parts are normative and records the constraints implementation agents must preserve.
Normative Inputs
.sisyphus/plans/cpp-template-redesign.mdis the read-only executable implementation plan owned by Atlas/Orchestrator..sisyphus/drafts/cpp-template-redesign.mdis normative for consolidated dependency, tooling, style, preset, hook, install/export, FuzzTest, and QA decisions..sisyphus/drafts/cmake-api-design.mdis normative for the CMake helper API, dependency loading policy, install/export behavior, and FuzzTest patch/dependency policy.
Older research notes, review findings, and open-question discussions in the drafts are historical unless their decisions appear in the latest consolidated or final-resolution sections.
Fixed Project Constraints
- Primary standard: C++14.
- C++11: best-effort/smoke compatibility only, not the default blocking path.
- C++17: isolated opt-in path, primarily for the
fuzzpreset and FuzzTest. - Toolchains: Clang/GCC only; no MSVC support.
- Dependency loading: local CPM archives under
3rd/archives/withURL_HASH SHA256=...; no hidden remote fallback. - Offline strategy: local archive/source paths and hash verification, not a global offline mode switch.
- FuzzTest: required by the redesign only in the isolated
fuzzpreset/tree, with pristine upstream archive, patch files, script-driven apply/verify workflow, and no protobuf-backed FuzzTest path. - HTTP baseline: cpp-httplib.
libhvis excluded from v1 unless explicitly re-approved. - Removed/not allowed by default:
magic_enum, protobuf FuzzTest integration, global offline mode, CMakeformat/check_format/run_clang_tidytargets.
Normative Template C++ Style
These template-specific style rules intentionally supersede generic agent C++ defaults wherever they conflict:
- clang-format 17+ is required.
- Indentation: 4 spaces, no tabs.
- Column target: 120.
- Functions, classes, structs, enums, and namespaces use Allman braces.
- Control statements and lambdas use attached braces.
- Include order: corresponding/main header first, then project/generated quoted headers, then C headers, C++ standard headers, known third-party headers, OS/system headers, and other angle headers.
- Private/protected members use
m_+snake_case. - Static non-const members and function-local statics use
s_+snake_case. - Constants, including local constants, use
kCamelCase. - Public struct fields and local variables use
snake_case. - Types use
PascalCase; functions and methods usecamelCase; namespaces uselower_case. - clang-tidy is strict from the start with warnings-as-errors for enabled checks.
.clang-formatis the formatter source-of-truth for enforceable whitespace, brace, column, pointer/reference, return-type, and include-order rules.scripts/format.py --checkverifies selected C/C++ files andscripts/format.py --fixrewrites them; naming rules such asm_,s_, andkCamelCaseremain documented conventions because clang-format cannot enforce identifier names.
Implementation Handoff Rule
Implementation agents must treat the latest consolidated/final-resolution draft sections as authoritative. If an older section conflicts with the constraints above, the older section is historical/superseded and must not drive implementation.
v1 Dependency Inventory (Frozen)
Source of truth: cmake/deps/versions.cmake.
| Name | Version | C++ Gate | CMake Targets | Presets | Archive | SHA256 |
|---|---|---|---|---|---|---|
| spdlog | 1.15.3 | C++11 | spdlog::spdlog |
debug,release,fuzz | spdlog-1.15.3.tar.gz | PENDING_T12 |
| fmt | 11.0.2 | C++11 | fmt::fmt |
debug,release,fuzz | fmt-11.0.2.tar.gz | PENDING_T12 |
| nlohmann/json | 3.11.3 | C++11 | nlohmann_json::nlohmann_json |
debug,release,fuzz | nlohmann-json-3.11.3.tar.gz | PENDING_T12 |
| toml11 | 4.4.0 | C++11 | toml11::toml11 |
debug,release,fuzz | toml11-4.4.0.tar.gz | PENDING_T12 |
| CLI11 | 2.4.2 | C++11 | CLI11::CLI11 |
debug,release,fuzz | CLI11-2.4.2.tar.gz | PENDING_T12 |
| asio (standalone) | 1.30.2 | C++11 | asio::asio |
debug,release,fuzz | asio-1.30.2.tar.gz | PENDING_T12 |
| concurrentqueue | 1.0.4 | C++11 | concurrentqueue::concurrentqueue |
debug,release,fuzz | concurrentqueue-1.0.4.tar.gz | PENDING_T12 |
| readerwriterqueue | 1.0.6 | C++11 | readerwriterqueue::readerwriterqueue |
debug,release,fuzz | readerwriterqueue-1.0.6.tar.gz | PENDING_T12 |
| ghc::filesystem | 1.5.14 | C++11 | ghcFilesystem::ghc_filesystem |
debug,release,fuzz | ghc-filesystem-1.5.14.tar.gz | PENDING_T12 |
| expected-lite | 0.8.0 | C++11 | nonstd::expected-lite |
debug,release,fuzz | expected-lite-0.8.0.tar.gz | PENDING_T12 |
| cpp-httplib | 0.18.3 | C++11 | httplib::httplib |
debug,release,fuzz | cpp-httplib-0.18.3.tar.gz | PENDING_T12 |
| GTest/GMock (normal) | 1.16.0 | C++14 | GTest::gtest, GTest::gtest_main, GTest::gmock, GTest::gmock_main |
debug,release | googletest-1.16.0.tar.gz | PENDING_T12 |
| Benchmark | 1.7.1 | C++11 | benchmark::benchmark, benchmark::benchmark_main |
debug,release | benchmark-1.7.1.tar.gz | PENDING_T12 |
| GTest/GMock (fuzz) | 1.17.0 | C++17 | GTest::gtest, GTest::gmock |
fuzz | googletest-1.17.0.tar.gz | PENDING_T12 |
| Abseil | 20260107.1 | C++17 | absl::* |
fuzz | abseil-cpp-20260107.1.tar.gz | PENDING_T12 |
| RE2 | 2025-11-05 | C++17 | re2::re2 |
fuzz | re2-2025-11-05.tar.gz | PENDING_T12 |
| ANTLR4 runtime | 4.13.2 | C++17 | antlr4_static |
fuzz | antlr4-4.13.2.tar.gz | PENDING_T12 |
| FuzzTest | 2026-02-19 | C++17 | fuzztest::fuzztest |
fuzz | fuzztest-2026-02-19.tar.gz | 1c6e04...04ef (pinned T5) |
Excluded from v1
libhv— excluded; cpp-httplib is the v1 HTTP baseline.magic_enum— removed per user request.protobuf— FuzzTest protobuf-backed paths forbidden.oatpp,tracy,microprofile,breakpad— removed from default template.crow— C++17-only; cpp-httplib covers C++14 HTTP.BLAKE3— CMake propagatescxx_std_20PUBLIC.bitsery— repo/path verification unresolved.outcome— replaced by expected-lite.OpenSSL— rejected as default dependency; too heavy.
GTest Version Isolation
Normal C++14 builds use GTest/GMock 1.16.0. Fuzz/C++17 builds use GTest/GMock 1.17.x. These must never coexist in the same build tree. The dependency modules enforce this separation.
Archive & Hash Notes
- SHA256 values marked
PENDING_T12require concrete archive acquisition (T12/T14). - FuzzTest SHA256 is pinned from T5:
1c6e04065eb988e2c99613369db8294aa58429d392bf479740b237f1255204ef. - CPM
URL_HASH SHA256=...enforcement in per-dependency modules (T13) consumesversions.cmakevariables. - Abseil/RE2/ANTLR4 versions must match the pinned FuzzTest release's
BuildDependencies.cmake; re-verify when rebasing.
CMake Preset Contract (T6)
This section defines the visible CMake preset contract that later implementation tasks must preserve. T6 is a contract-only step: the repository currently has no root CMakePresets.json, and T11 owns the full preset implementation and configure/build/test validation. If T11 adds hidden/internal presets, each hidden preset must be minimal, justified in this document, and must not expand the visible user-facing set.
Normative compatibility constraints:
- CMake preset file compatibility is separate from the project CMake code minimum. Project CMake code remains minimum 3.19, but a single root
CMakePresets.jsoncontaining configure, build, and test presets should use CMakePresets schema version 2, which requires CMake 3.20+ for preset users. - If strict CMake 3.19 preset-file compatibility is required, schema version 1 supports configure presets only; build/test behavior must then be invoked manually or documented outside presets. Do not add a
$schemafield while the project supports older preset file versions. - Every visible configure preset must use the Ninja generator.
- Every visible configure preset must set
CMAKE_EXPORT_COMPILE_COMMANDS=ON. - Every visible configure preset must use
binaryDirequivalent to${sourceDir}/build/${presetName}; the documented shorthand isbuild/${presetName}. - The visible configure preset names are exactly
debug,release,asan,tsan,fuzz, andcxx11-smoke. No MSVC presets are part of v1. - Build presets should mirror each visible configure preset name and build its matching configure preset.
- Test presets should exist only where tests are part of the lane contract:
debug,release,asan,tsan, andfuzzfor deterministic fuzz smoke.cxx11-smokeis configure/build only unless a compatible C++11 test stack is explicitly proven later.
| Preset | Purpose | C++ standard | Build type | Tests, benchmark, fuzz behavior | Sanitizer flags intent | Build directory | Compile commands | Dependency lane |
|---|---|---|---|---|---|---|---|---|
debug |
Default developer lane and fast local checks. | C++14 | Debug |
Tests enabled; fast developer checks enabled; benchmarks may be built when the normal benchmark option is enabled; fuzz disabled. | None; sanitizer behavior belongs only to asan/tsan. |
build/debug |
ON |
Core deps plus normal GTest/GMock 1.16.0 and Benchmark 1.7.1. |
release |
Optimized normal build lane. | C++14 | Release |
Tests enabled for release validation; benchmarks may be enabled for release performance checks; fuzz disabled. | None. | build/release |
ON |
Core deps plus normal GTest/GMock 1.16.0 and Benchmark 1.7.1. |
asan |
AddressSanitizer validation lane. | C++14 | Debug |
Tests enabled; benchmark/fuzz disabled by default so sanitizer runs stay focused on correctness tests. | Enable AddressSanitizer compile and link flags in the implementation; do not hide ASan in debug. |
build/asan |
ON |
Same normal dependency lane as debug; no FuzzTest stack. |
tsan |
ThreadSanitizer validation lane. | C++14 | Debug |
Tests enabled; benchmark/fuzz disabled by default so sanitizer runs stay focused on correctness tests. | Enable ThreadSanitizer compile and link flags in the implementation; do not hide TSan in debug. |
build/tsan |
ON |
Same normal dependency lane as debug; no FuzzTest stack. |
fuzz |
Isolated deterministic fuzz smoke and FuzzTest development lane. | C++17 | Debug |
Normal tests are not the focus; deterministic fuzz smoke is registered in CTest with fixed seed/run count; long fuzzing stays direct executable invocation outside default CTest. Benchmarks disabled. | No default sanitizer requirement in the contract; sanitizer fuzzing, if added later, must be explicit and not affect normal lanes. | build/fuzz |
ON |
Core deps plus isolated FuzzTest lane: GTest/GMock 1.17.0, Abseil 20260107.1, RE2 2025-11-05, ANTLR4 runtime 4.13.2, FuzzTest 2026-02-19. Requires Clang in implementation. |
cxx11-smoke |
Best-effort compatibility smoke for the C++11-compatible subset. | C++11 | Debug |
Configure/build compatible subset only; tests, benchmarks, and fuzz are disabled until a compatible stack is explicitly proven. | None. | build/cxx11-smoke |
ON |
Core dependencies whose inventory gate is C++11 only; excludes normal GTest/GMock 1.16.0, Benchmark, and all FuzzTest-lane dependencies. |
Implementation notes for T11 and later tasks:
- Preset options must make lane behavior explicit through cache variables rather than implicit hidden magic. Suggested option intent: enable tests for normal and sanitizer lanes, enable deterministic fuzz smoke only in
fuzz, and disable tests/benchmark/fuzz incxx11-smoke. debugremains the fast default path for developer checks; it must not silently enable sanitizer flags or FuzzTest.fuzzis the only v1 lane allowed to require C++17 and Clang for FuzzTest. It must not load normal GoogleTest/GMock 1.16.0 into the same build tree as FuzzTest's GoogleTest/GMock 1.17.0 lane.- Hidden presets are not required by this contract. If T11 uses a hidden base to remove duplication, keep it internal, do not expose extra user presets, and document the reason here and in evidence.
T11 Implementation Status
- A hidden
_basepreset providesgenerator: "Ninja"andCMAKE_EXPORT_COMPILE_COMMANDS=ONto avoid repetition across six visible presets. It does not appear incmake --list-presets. Rationale: the only shared state is the generator and compile-commands export; build type, C++ standard, and sanitizer flags are per-lane. - Each visible preset sets its own explicit
binaryDirrather than relying on${presetName}expansion from the hidden base, because${presetName}behavior with inherited presets can be ambiguous in older CMake 3.20.x patch levels. - Sanitizer flags are set directly in preset
cacheVariables(CMAKE_CXX_FLAGS,CMAKE_EXE_LINKER_FLAGS). Later tasks (T16+) may add CMake-level option gating (e.g.,CC_ENABLE_SANITIZERS) that consumes or overrides these; the preset contract remains the user-facing surface. - Three lane option cache variables are set in all presets:
CPP_TEMPLATE_ENABLE_TESTS(ON in debug/release/asan/tsan, OFF in fuzz/cxx11-smoke),CPP_TEMPLATE_ENABLE_BENCHMARKS(ON in debug/release, OFF in asan/tsan/fuzz/cxx11-smoke),CPP_TEMPLATE_FUZZ_LANE(ON only in fuzz, OFF everywhere else). fuzzpreset enforces Clang at configure time:CMakeLists.txtcontainsif(CPP_TEMPLATE_FUZZ_LANE)guard that callsmessage(FATAL_ERROR ...)on non-Clang compilers. Verified: GCC 13.3.0 fails with clear message; Clang 18.1.3 configures successfully.- A deterministic fuzz smoke CTest placeholder
fuzz_lane.config_checkis registered viacmake -E echoinside theCPP_TEMPLATE_FUZZ_LANEguard. It has labelfuzz_smokeand is gated to the fuzz preset only. Normalcalc_testis not registered in the fuzz lane. cxx11-smokedisables tests, benchmarks, and fuzz via cache variables. Verified: configure produces 0 CTest registrations.debug,release,asan, andtsanconfigure successfully with tests enabled. Verified:calc_testregistered in each; sanitizer presets keep benchmarks disabled.cxx11-smokehas no test preset; configure/build only per contract.
Fuzz Smoke Testing
The fuzz preset runs FuzzTest targets in unit-test mode (FUZZTEST_FUZZING_MODE=OFF), which executes fuzz tests as deterministic GTest-compatible tests rather than continuous fuzzing campaigns.
Deterministic CTest Smoke
All fuzz targets registered via cc_fuzz() appear in CTest with the fuzz_smoke label:
# Run all fuzz smoke tests
ctest --test-dir build/fuzz --output-on-failure -L fuzz_smoke
Determinism is ensured through two mechanisms:
.WithSeeds()in the fuzz test source: provides concrete fixed inputs that always run. In unit-test mode, FuzzTest runs each seed once as a GTest test case.FUZZTEST_PRNG_SEEDenvironment variable: set viaSMOKE_ENVincc_fuzz()registration. Makes any additional PRNG-driven input generation reproducible across runs.
The cc_fuzz() helper supports three smoke-test customization keywords:
| Keyword | Purpose | Example |
|---|---|---|
SMOKE_ARGS |
Arguments for the CTest smoke command | (empty by default) |
SMOKE_ENV |
Environment variables for CTest smoke | FUZZTEST_PRNG_SEED=42 |
SMOKE_TIMEOUT |
CTest timeout in seconds | 30 (default) |
Long-Running Fuzzing (Outside CTest)
Continuous fuzzing campaigns use the fuzz executable directly with --fuzz flags. These are NOT registered in CTest because they run indefinitely.
Important: --fuzz, --fuzz_for, and --time_limit_per_input require a fuzzing-mode build (FUZZTEST_FUZZING_MODE=ON). The default fuzz preset CTest smoke runs in unit-test mode (FUZZTEST_FUZZING_MODE=OFF) using .WithSeeds() and FUZZTEST_PRNG_SEED. To use continuous fuzzing flags, rebuild with FUZZTEST_FUZZING_MODE=ON or invoke a fuzzing-mode binary directly.
# List available fuzz tests
./build/fuzz/calc_fuzz --gtest_list_tests
# Run a specific fuzz test in fuzzing mode with safety limits
# (requires FUZZTEST_FUZZING_MODE=ON build)
./build/fuzz/calc_fuzz \
--fuzz=CalcFuzz.AddNeverCrashes \
--time_limit_per_input=30s \
--fuzz_for=60s
Safety flags for long fuzzing (fuzzing mode only):
--fuzz_for=<duration>: total fuzzing time (e.g.,60s,5m).--time_limit_per_input=<duration>: max time per input before timeout.- These flags have no effect in unit-test mode (
FUZZTEST_FUZZING_MODE=OFF).
T9 clang-tidy Policy
scripts/clang_tidy.pyis the script-first strict clang-tidy entry point; CMake must not definetidy,run_clang_tidy,clang-tidy, orcheck_tidytargets.- The runner requires clang-tidy 17+ and a
compile_commands.jsonin--build-dirbefore real execution. The default build directory isbuild/debug. - Debug fast checks configure with
-DCMAKE_EXPORT_COMPILE_COMMANDS=ONsoscripts/dev_check.py --fastcan feed clang-tidy without relying on CMake-integrated tidy targets. - The runner uses the shared project path filter and checks only project C/C++ files, skipping build outputs, vendored trees, generated/task-state directories,
.sisyphus,.opencode,.git,3rd/, andthird_party/. - Warnings are errors through both
.clang-tidy(WarningsAsErrors: '*') and the runner CLI (--warnings-as-errors=*). Each file is invoked separately so failures report exactfailed_file:paths.
T10 Local Git Hook Workflow
- Git hooks are local and opt-in. The template does not automatically install hooks during configure, build, test, formatting, clang-tidy, or other developer checks.
.githooks/pre-commitis the repository hook entry point. It resolves the repository root withgit rev-parse --show-toplevel, changes to that root, and delegates topython3 scripts/pre_commit.py.scripts/pre_commit.pyprints and runs the exact fast-check delegation:python3 scripts/dev_check.py --fast. Its--dry-runmode prints the same command plan without executing checks.scripts/setup_hooks.pyis the only setup entry point for hook installation. Non-dry-run setup verifies.githooks/pre-commit, ensures it is executable, and runs onlygit config --local core.hooksPath .githooks.scripts/setup_hooks.py --dry-runprints the intended local-only git config command without mutating git config.scripts/setup_hooks.py --statusreports hook file existence, executable state, and the localcore.hooksPathvalue when available.- Global or user git configuration must never be modified by the template hook workflow.