Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 176 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,36 @@ 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:
pull_request:

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
Expand Down Expand Up @@ -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
7 changes: 6 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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.<phase-number>-<short-descriptor>, 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)
Expand Down
31 changes: 18 additions & 13 deletions docs/release_engineering.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,31 @@ v0.<phase-number>-<short-descriptor>
- `<short-descriptor>` 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

Expand Down
93 changes: 66 additions & 27 deletions docs/technical_architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,41 +157,80 @@ 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)
```

## 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.
Loading