diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5ef7b1..15eb62a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,28 @@ name: build-and-test # Tests are framework-free and fail via a non-zero exit in every build type # (R4), so a single Release run is authoritative -- no Debug workaround needed. # The warnings-as-errors policy (R3) is enforced with -DADSC_WERROR=ON. +# +# CI-hardening mini-WP (adsc-specification-v5.md:296-300, +# _tasks_local/01-pre-wp13-fixpack.md): four jobs total. +# 1. build-test -- Ubuntu/GCC. THE single authority: build, ctest, +# regenerate_all.sh, README/docs fillers, then the +# reproducibility gate (R6). Unchanged by this +# mini-WP. +# 2. build-matrix -- Ubuntu/GCC+Clang, Release, -Werror: build+ctest +# only, no regeneration/gate/artifacts. +# 3. sanitizers -- Debug + ASan/UBSan: ctest only (C++ unit tests), +# no regeneration/gate/artifacts. +# 4. python-cross-platform -- Windows + macOS: runs the Python-only +# evidence/compliance/viz/forensics/prioritization/ +# readme_numbers/docs_numbers suites plus both +# fillers in --check mode directly against the +# committed artifacts (no C++ build). Turns the +# "byte-identical across platforms" claim into +# CI-enforced evidence. +# Jobs 2-4 hardening layers are non-gating by design: only job 1's +# reproducibility gate is authoritative (spec:287-291, +# adsc-specification-v5.md "CI-hardening mini-WP" section). None of jobs 2-4 +# upload artifacts or run the `git diff --exit-code` gate. on: push: @@ -15,7 +37,7 @@ on: jobs: build-test: - name: cmake build + ctest (Ubuntu / GCC / Eigen3) + name: cmake build + ctest (Ubuntu / GCC / Eigen3) -- reproducibility gate (single authority) runs-on: ubuntu-latest steps: - name: Checkout @@ -80,3 +102,156 @@ jobs: path: | generated/ evidence/ + + build-matrix: + name: cmake build + ctest (Ubuntu / ${{ matrix.compiler }}, Release, -Werror) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - compiler: gcc + cc: gcc + cxx: g++ + - compiler: clang + cc: clang + cxx: clang++ + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Install Eigen3 + compilers + run: | + sudo apt-get update + sudo apt-get install -y libeigen3-dev g++ clang + + - name: Configure (Release, warnings-as-errors, ${{ matrix.compiler }}) + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DADSC_WERROR=ON \ + -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} + + - name: Build + run: cmake --build build --parallel + + - name: Test (CTest) + run: ctest --test-dir build --output-on-failure + + # This job intentionally stops at ctest. It does not run + # tools/regenerate_all.sh, the README/docs fillers, or the + # reproducibility gate, and it uploads no artifacts -- the Ubuntu/GCC + # `build-test` job above remains the SINGLE authority for + # reproducibility (spec:287-291, "keep the reproducibility gate the + # single authority"). This job exists only to catch compiler-specific + # warnings/UB that a single-compiler CI would miss. + + sanitizers: + name: ASan+UBSan (Debug, ctest only) + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Install Eigen3 + run: | + sudo apt-get update + sudo apt-get install -y libeigen3-dev + + # Zero-CMakeLists-change route: rather than adding a bespoke + # ADSC_SANITIZE option/toggle to CMakeLists.txt, the sanitizer flags are + # injected straight into this job's own CMAKE_CXX_FLAGS / + # CMAKE_EXE_LINKER_FLAGS cache variables at configure time. No + # CMakeLists.txt change is required for this job to exist (checked: + # CMakeLists.txt has no ADSC_SANITIZE option today). + - name: Configure (Debug, -fsanitize=address,undefined) + run: | + cmake -S . -B build-san -DCMAKE_BUILD_TYPE=Debug \ + -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer" \ + -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address,undefined" + + - name: Build + run: cmake --build build-san --parallel + + # Per adsc-specification-v5.md:287-291/297-300 -- quoted verbatim: + # "sanitizers on TEST builds only (artifact generation stays plain + # Release -- sanitizer-built binaries must never write generated/ or + # the SHA gate dies)". Accordingly this job: + # - runs ctest ONLY -- no ./build-san/adsc_sim, no + # tools/regenerate_all.sh, no README/docs fillers, no + # reproducibility gate, no artifact upload; + # - excludes the Python-driven ctest tests (viz, compliance, + # evidence, forensics, prioritization, readme_numbers, + # docs_numbers). Those tests regenerate-in-place and diff against + # the committed generated/evidence/README/docs files as their own + # pass condition (by design -- see each test's own docstring); they + # do not exercise any sanitizer-instrumented binary (they shell out + # to plain `python3`), so running them here would touch + # generated/evidence/README.md/docs/ for zero sanitizer coverage, + # which is exactly what the spec rule above forbids. The C++ unit + # tests below give full ASan/UBSan coverage without touching + # generated/ at all. + - name: Test (CTest, ASan+UBSan, C++ unit tests only) + env: + ASAN_OPTIONS: detect_leaks=1:halt_on_error=1 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1 + run: | + ctest --test-dir build-san --output-on-failure \ + -E '^(viz|compliance|evidence|forensics|prioritization|readme_numbers|docs_numbers)$' + + python-cross-platform: + name: Python suites (${{ matrix.os }}) -- byte-identical-across-platforms evidence + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [windows-latest, macos-latest] + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + # These are Python 3 stdlib-only (spec v4.2 R9) and read the CSV/MD + # artifacts already committed under generated/ and evidence/ (produced + # by the Ubuntu/GCC build-test job's C++ regeneration) -- they need no + # C++ compiler or Eigen on this runner. Running them here on Windows and + # macOS turns "the regenerated bytes are identical across platforms" + # from an author assertion into CI-enforced evidence. C++ regeneration + # itself (tools/regenerate_all.sh, which invokes compiled binaries) + # stays Ubuntu-only in build-test: standing up Eigen3 + a C++ toolchain + # on Windows/macOS runners is disproportionate cost for a claim this + # job already covers via the Python path, and no committed artifact + # depends on a non-Linux C++ build existing. + - name: evidence + run: python tools/evidence/test_evidence.py . + + - name: compliance + run: python tools/compliance/test_compliance.py . + + - name: viz + run: python tools/viz/test_viz.py . + + - name: forensics + run: python tools/forensics/test_forensics.py . + + - name: prioritization + run: python tools/prioritization/test_prioritization.py . + + - name: readme_numbers + run: python tools/readme/test_readme_numbers.py . + + - name: docs_numbers + run: python tools/docs/test_docs_numbers.py . + + # --check mode only: verifies the committed README.md/docs/*.md marker + # blocks already match what the fillers would regenerate. It does NOT + # rewrite files, so this job makes no working-tree changes and stays + # non-gating for reproducibility (build-test's gate remains the single + # authority). + - name: README marker blocks (--check) + run: python tools/readme/fill_readme_numbers.py . --check + + - name: docs/ marker blocks (--check) + run: python tools/docs/fill_docs_numbers.py . --check diff --git a/CMakeLists.txt b/CMakeLists.txt index a7d623b..11a6bcc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,10 @@ cmake_minimum_required(VERSION 3.16) -project(ADSC VERSION 4.0 LANGUAGES CXX) +# VERSION tracks the git-tag scheme in docs/release_engineering.md +# (v0.-, e.g. v0.15-proposal-package): the +# CMake-internal version now bumps in lockstep with the phase tags instead of +# carrying its own separate number. Reconciled 2026-07-12 (was a stale 4.0 +# flagged as a known inconsistency in release_engineering.md). +project(ADSC VERSION 0.15.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/docs/release_engineering.md b/docs/release_engineering.md index c5a50f8..53ac5e8 100644 --- a/docs/release_engineering.md +++ b/docs/release_engineering.md @@ -23,26 +23,31 @@ v0.- - `` is a short human-readable label for what that phase delivered (`phase0-baseline`, `fidelity`), not a restatement of the WP number. -- Tags are lightweight/annotated git tags on the merge commit; `CMakeLists.txt`'s - own `project(ADSC VERSION 4.0 ...)` field is a separate, legacy - CMake-internal version number and is not currently kept in lockstep with - the git tag scheme (noted here as a known inconsistency, not fixed by - this PR — flag to the owner if it should be reconciled). +- Tags are lightweight/annotated git tags on the merge commit. **Resolved + 2026-07-12:** `CMakeLists.txt`'s `project(ADSC VERSION ...)` field used to + be a separate, legacy CMake-internal number (`4.0`) that drifted from the + git tag scheme; this is no longer the case. CMake `VERSION` now tracks the + tag scheme directly (currently `0.15.1`, tied to the upcoming + `v0.15-proposal-package` tag) and is expected to bump alongside future + phase tags rather than carry its own independent number. -## Precedents (both already in the repository) +## Precedents (all already in the repository) | tag | commit | what it marks | |---|---|---| | `v0.10-phase0-baseline` | `1f8c0aa` | End of Phase 0 (WP10: spec adoption, citation-fill, keep-out-violation forensics — no behavior-changing code). Cut **before** WP11 landed specifically so the pre-WP11 keep-out-violation rate (0.014 [0.007, 0.029]) had an archived, citable reference commit for the R15 BEFORE/AFTER table once WP11 closed it to 0/500 (spec §1). | | `v0.12-fidelity` | `a0deb9d` (merge of PR #26, `fixpack-pre-wp13`) | End of the WP12 fidelity-ladder phase plus the pre-WP13 fix-pack, cut immediately before WP13 (kit-class trade + EDT physics) began — a clean boundary between "safety/fidelity" work and "kit/cost" work. | +| `v0.13-kit-trade-edt` | `4b02ac0` | WP13 (kit-class trade + EDT-v1 physics core) merge commit — backfilled 2026-07-12, see below. | +| `v0.14-cost-fom` | `f41cb85` | WP14 (parametric cost/FoM + prioritization table) merge commit — backfilled 2026-07-12, see below. | -**Known gap, stated plainly:** WP13 (`4b02ac0`) and WP14 (`f41cb85`) merged -without their own phase tags — the tag-at-phase-boundary practice was not -applied mechanically to every WP in this chain. This is not fixed -retroactively by this PR (no git tags are created here); it is flagged so -the owner can decide whether to backfill `v0.13-kit-trade` / -`v0.14-cost-fom` tags on their existing merge commits, or accept the gap -and simply resume the practice from WP15 forward. +**Known gap — resolved 2026-07-12:** WP13 (`4b02ac0`) and WP14 (`f41cb85`) +originally merged without their own phase tags — the tag-at-phase-boundary +practice was not applied mechanically to every WP in this chain. The two +tags have since been backfilled directly on those existing merge commits as +`v0.13-kit-trade-edt` and `v0.14-cost-fom` respectively, so the chain from +`v0.12-fidelity` through the upcoming `v0.15-proposal-package` now has a +continuous set of phase-boundary anchors. The practice resumes normally +from WP15 forward. ## Practice going forward diff --git a/docs/technical_architecture.md b/docs/technical_architecture.md index c196ec9..2a9fa43 100644 --- a/docs/technical_architecture.md +++ b/docs/technical_architecture.md @@ -157,8 +157,11 @@ docs/ concept, technical architecture (this file), gnc, safety, roadmap, pitch_agencies (spec:274), plus the one-pager, 5-page summary, deck source and release-engineering notes (WP15 tasks 4-5) -.github/ CI (Ubuntu + Eigen: cmake build + ctest, warnings-as-errors, - reproducibility gate) +.github/ CI: 4 jobs -- (1) Ubuntu/GCC build+ctest+regenerate+ + reproducibility gate (single authority), (2) Ubuntu GCC+Clang + build+ctest matrix, (3) Debug ASan/UBSan ctest-only, (4) + Windows+macOS Python-suite cross-platform check (see "CI + gates" below) adsc-specification-v5.md active spec (work packages, hard rules, locked decisions) adsc-specification-v4.md retired v4.2 spec (superseded by v5; kept because v5 references its D/R/T items) @@ -166,32 +169,68 @@ adsc-specification-v4.md retired v4.2 spec (superseded by v5; kept because v5 ## CI gates -`.github/workflows/ci.yml` runs, on every push and pull request: install -Eigen3 (apt), configure with `-DADSC_WERROR=ON` (R3, warnings-as-errors), -build, run `ctest`, run `./build/adsc_sim`, then run -`bash tools/regenerate_all.sh build` (the exact script above — "single source -of truth for reproduction" per its own header) and -`python3 tools/readme/fill_readme_numbers.py .` (fills README's two number -marker-blocks from the committed WP5/WP6 CSVs — a separate step from -`regenerate_all.sh` by design, documented in that script's own header, since -README.md lives at the repo root and is not under `generated/` or -`evidence/`). It then runs the **reproducibility gate**: - -``` -git diff --exit-code -- generated/ evidence/ README.md -``` - -— any drift between committed bytes and freshly regenerated bytes fails the -build (R6). Generated artifacts are uploaded even on gate failure, so an R15 -re-baselining PR can take CI's regenerated bytes as its new committed -reference instead of requiring a local toolchain. +`.github/workflows/ci.yml` runs four jobs on every push and pull request. +Only the first is authoritative for reproducibility; the other three are +non-gating hardening layers added by the CI-hardening mini-WP +(adsc-specification-v5.md:296-300) and upload no artifacts. + +1. **`build-test` (Ubuntu / GCC) -- the single authority.** Installs Eigen3 + (apt), configures with `-DADSC_WERROR=ON` (R3, warnings-as-errors), builds, + runs `ctest`, runs `./build/adsc_sim`, then runs + `bash tools/regenerate_all.sh build` (the exact script above — "single + source of truth for reproduction" per its own header), + `python3 tools/readme/fill_readme_numbers.py .` (fills README's two number + marker-blocks from the committed WP5/WP6 CSVs) and + `python3 tools/docs/fill_docs_numbers.py .` (fills every `docs/*.md` + DOCS-*-START/END marker block from the CSVs it cites — the docs/ sibling + of the README step, same "separate step before the gate" convention, + since neither README.md nor docs/ lives under `generated/` or + `evidence/`). It then runs the **reproducibility gate**: + + ``` + git diff --exit-code -- generated/ evidence/ README.md docs/ + ``` + + — any drift between committed bytes and freshly regenerated bytes fails + the build (R6). This is the one job that gates reproducibility; it is + the only job that uploads artifacts (`adsc-generated`, even on gate + failure, so an R15 re-baselining PR can take CI's regenerated bytes as + its new committed reference instead of requiring a local toolchain). + +2. **`build-matrix` (Ubuntu / GCC + Clang).** Configures Release with + `-DADSC_WERROR=ON` under both compilers and runs `ctest` — catches + compiler-specific warnings/UB a single-compiler job would miss. Stops at + `ctest`: no regeneration, no fillers, no gate, no artifact upload. + +3. **`sanitizers` (Debug, ASan+UBSan, ctest only).** Configures a Debug build + with `-fsanitize=address,undefined` injected via `-DCMAKE_CXX_FLAGS`/ + `-DCMAKE_EXE_LINKER_FLAGS` at the CI-job level (no `ADSC_SANITIZE` option + was added to `CMakeLists.txt` — this is a zero-CMakeLists-change route), + then runs `ctest` restricted to the C++ unit tests. Per + adsc-specification-v5.md:287-291/297-300 ("sanitizers on TEST builds + only... sanitizer-built binaries must never write `generated/` or the SHA + gate dies"), this job never runs `adsc_sim`, `tools/regenerate_all.sh`, + the fillers, or the gate, and excludes the Python-driven ctest tests + (`viz`, `compliance`, `evidence`, `forensics`, `prioritization`, + `readme_numbers`, `docs_numbers`) since those regenerate-in-place against + `generated/`/`evidence/`/`README.md`/`docs/` by design and exercise no + sanitizer-instrumented binary. + +4. **`python-cross-platform` (Windows + macOS).** Runs the Python 3 + stdlib-only `evidence`/`compliance`/`viz`/`forensics`/`prioritization`/ + `readme_numbers`/`docs_numbers` suites plus both fillers in `--check` mode + directly against the artifacts already committed under `generated/` and + `evidence/` — no C++ build, no Eigen setup. This turns the + "byte-identical across platforms" claim from author assertion into + CI-enforced evidence. C++ regeneration itself stays Ubuntu-only (job 1): + standing up Eigen3 + a C++ toolchain on Windows/macOS runners is + disproportionate cost that this job's Python-only check already makes + unnecessary for the reproducibility claim. **docs/ and the reproducibility gate.** R16 requires every number under `docs/` to come from a generated include or an explicit reference, with the -same drift protection `generated/`/`evidence/`/`README.md` already have. -Extending the gate's diff path above to include `docs/`, and building the +same drift protection `generated/`/`evidence/`/`README.md` already have. As +of WP15 task 3, the gate's diff path above includes `docs/` and the `docs/`-scoped claims-audit (banned-word, numeric-consistency, L-tag -patterns) that makes drift or an unaudited numeric literal fail CI, is a -required companion change tracked alongside this docs/ tree (WP15 task 3) — -call out its status explicitly in the PR body rather than assuming it is -already wired in. +patterns, run as part of the `evidence` ctest target/`test_evidence.py`) is +wired in — this is no longer an open item.