Skip to content

Commit 468a6c0

Browse files
committed
chore: harden release quality
1 parent df7d55b commit 468a6c0

94 files changed

Lines changed: 1150 additions & 568 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# Merge-gate pipeline (specs.md §15). Every job below must pass for a PR to
44
# merge. The pipeline is pinned to the project toolchain (the stable channel in
55
# rust-toolchain.toml) for determinism; post-pivot there is no nightly-only
6-
# proving substrate. Golden-parity (export harness) gates are wired by their own
7-
# issues (backlog M2/M7).
6+
# proving substrate. `bash ci/check-local.sh full` mirrors the non-external
7+
# merge gates for local validation.
88
name: CI
99

1010
on:
@@ -111,17 +111,26 @@ jobs:
111111
# Internal cross-reference check across the docs corpus (file + anchor).
112112
- name: Internal link check (docs/)
113113
run: python3 ci/check-links.py
114+
# Public prose convention from AGENTS.md: no em dashes in README/docs/site.
115+
- name: Prose style (no em dashes)
116+
run: python3 ci/check-prose-style.py
114117
# Python<->Rust canonical-encoding parity (the export-side commitment bridge).
115118
- name: Export canonical parity (Python vs Rust)
116119
run: python3 crates/pwm-export/python/tests/test_canonical_parity.py
117-
# Export pipeline (E-201/202/203/208) on synthetic le-wm-shaped data (NumPy).
120+
# Export pipeline on synthetic le-wm-shaped data (NumPy).
118121
- name: Export pipeline (ingest/quantize/fold/bundle)
119122
run: |
120123
python3 -m pip install --quiet numpy
121124
python3 crates/pwm-export/python/tests/test_export_pipeline.py
122125
# Python lint (config in crates/pwm-export/python/pyproject.toml). Pinned ruff.
123126
- name: Python lint (ruff)
124-
run: pipx run ruff==0.15.16 check crates/pwm-export/python
127+
run: bash ci/check-python-style.sh
128+
# Release helper self-checks, including SemVer ordering and changelog parsing.
129+
- name: Release tooling
130+
run: python3 ci/test-release-tools.py
131+
# Shebang-bearing helper scripts must be directly executable.
132+
- name: Script modes
133+
run: bash ci/check-script-modes.sh
125134

126135
license:
127136
name: License / SPDX
@@ -146,21 +155,20 @@ jobs:
146155
with:
147156
toolchain: "stable"
148157
- uses: Swatinem/rust-cache@v2
149-
# Skeleton scope: the runner evaluates every registered campaign (only the
150-
# empty campaign exists yet) and exits non-zero on a survivor or a score
151-
# below target (INV-TEST-05). Components register real campaigns as they land.
158+
# The runner evaluates every registered verifier mutation campaign and
159+
# exits non-zero on a survivor or a score below target.
152160
- run: cargo run -p pwm-testkit --bin mutation_runner --locked
153161

154162
lean:
155-
name: Lean soundness proofs (D-805)
163+
name: Lean soundness proofs
156164
runs-on: ubuntu-latest
157165
steps:
158166
- uses: actions/checkout@v4
159167
# Builds the argmin uniqueness proof + the Freivalds probability bound.
160168
# Install elan/toolchain manually with retries: GitHub release downloads
161169
# intermittently 504, and `lean-action`'s installer has no retry, so a
162-
# transient blip fails the whole job. Mathlib oleans come from the (reliable)
163-
# `lake exe cache get` Azure cache no Mathlib rebuild.
170+
# transient blip fails the whole job. Mathlib oleans come from the reliable
171+
# `lake exe cache get` Azure cache; no Mathlib rebuild.
164172
- name: Install elan + Lean toolchain (retry transient GitHub 504s)
165173
working-directory: lean
166174
run: |

.github/workflows/release.yml

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# SPDX-License-Identifier: Apache-2.0
22
#
3-
# Release automation (docs/spec/09-release-and-versioning.md, RFC-0016).
3+
# Release automation for the workspace policy in CHANGELOG.md and CONTRIBUTING.md.
44
#
55
# Triggered by pushing a semver tag `vX.Y.Z`. Validates the tag against the
6-
# semver policy and the changelog (INV-REL-10), runs the license/SPDX gate and
7-
# the quality bar, packages the publishable crates, and publishes a GitHub
8-
# Release with the changelog section as notes and the `.crate` files attached.
6+
# semver policy and the changelog, runs the license/SPDX gate and the quality
7+
# bar, packages the publishable crates, and publishes a GitHub Release with the
8+
# changelog section as notes and the `.crate` files attached.
99
#
1010
# Publishing to a registry is intentionally out of scope (deferred); this
1111
# produces the packaged artifacts but does not run `cargo publish`. A manual
@@ -15,8 +15,9 @@ name: Release
1515
on:
1616
push:
1717
tags:
18-
- "v[0-9]+.[0-9]+.[0-9]+"
19-
- "v[0-9]+.[0-9]+.[0-9]+-*"
18+
# GitHub tag filters are globs, not regex. Match broadly here and enforce
19+
# strict SemVer before packaging or publishing.
20+
- "v*.*.*"
2021
workflow_dispatch:
2122
inputs:
2223
version:
@@ -48,7 +49,7 @@ jobs:
4849
- name: Tag matches semver policy
4950
run: |
5051
VERSION="${{ steps.version.outputs.version }}"
51-
if ! echo "$VERSION" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'; then
52+
if ! echo "$VERSION" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-((0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*)(\.(0|[1-9][0-9]*|[0-9]*[A-Za-z-][0-9A-Za-z-]*))*))?(\+([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?$'; then
5253
echo "::error::tag version '$VERSION' is not semver-conformant"
5354
exit 1
5455
fi
@@ -62,30 +63,85 @@ jobs:
6263
exit 1
6364
fi
6465
65-
- name: Changelog completeness (INV-REL-10)
66+
- name: Changelog completeness
6667
run: python3 ci/check-changelog.py "${{ steps.version.outputs.version }}"
68+
- name: Release tooling self-check
69+
run: python3 ci/test-release-tools.py
70+
- name: Script modes
71+
run: bash ci/check-script-modes.sh
6772

6873
- uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # pinned (no semver tags; SHA of master @ stable)
6974
with:
7075
toolchain: "stable"
7176
components: rustfmt, clippy
77+
- name: Install MSRV toolchain
78+
run: rustup toolchain install 1.85 --profile minimal
7279
- uses: Swatinem/rust-cache@v2
7380

7481
# License gate runs in the release path (acceptance criterion).
7582
- name: License / SPDX gate
7683
run: bash ci/check-spdx.sh
77-
- name: cargo-deny (licenses)
84+
- name: cargo-deny (licenses + advisories + bans + sources)
7885
uses: EmbarkStudios/cargo-deny-action@v2
7986
with:
80-
command: check licenses
87+
command: check
8188

82-
# Quality barthe release must pass the same gates as a merge.
89+
# Quality bar: the release must pass the same core gates as a merge.
8390
- name: Format
8491
run: cargo fmt --all -- --check
92+
- name: Prose style
93+
run: python3 ci/check-prose-style.py
8594
- name: Lint
8695
run: cargo clippy --all-targets --all-features --locked -- -D warnings
8796
- name: Tests
8897
run: cargo test --workspace --all-features --locked
98+
- name: MSRV (Rust 1.85)
99+
run: cargo +1.85 build --workspace --locked
100+
- name: Full-dims predictor (ignored, release)
101+
run: cargo test --workspace --all-features --release --locked -- --ignored
102+
- name: no_std verifier build
103+
run: |
104+
cargo build -p pwm-verifier --no-default-features --locked
105+
cargo build -p pwm-core --no-default-features --locked
106+
- name: Rustdoc
107+
run: cargo doc --workspace --no-deps --locked
108+
env:
109+
RUSTDOCFLAGS: "-D warnings"
110+
- name: Internal link check
111+
run: python3 ci/check-links.py
112+
- name: Export canonical parity
113+
run: python3 crates/pwm-export/python/tests/test_canonical_parity.py
114+
- name: Export pipeline
115+
run: |
116+
python3 -m pip install --quiet numpy
117+
python3 crates/pwm-export/python/tests/test_export_pipeline.py
118+
- name: Python style
119+
run: bash ci/check-python-style.sh
120+
- name: Constraint mutation
121+
run: cargo run -p pwm-testkit --bin mutation_runner --locked
122+
- name: Install elan + Lean toolchain (retry transient GitHub 504s)
123+
working-directory: lean
124+
run: |
125+
set -euo pipefail
126+
url=https://github.com/leanprover/elan/releases/latest/download/elan-x86_64-unknown-linux-gnu.tar.gz
127+
for i in $(seq 1 8); do
128+
curl -fL --retry 5 --retry-all-errors --retry-delay 10 "$url" -o elan.tar.gz && break
129+
echo "elan download attempt $i failed (transient 504?); retrying in 20s"; sleep 20
130+
done
131+
tar xzf elan.tar.gz
132+
./elan-init -y --default-toolchain none
133+
echo "$HOME/.elan/bin" >> "$GITHUB_PATH"
134+
export PATH="$HOME/.elan/bin:$PATH"
135+
for i in $(seq 1 6); do
136+
elan toolchain install "$(cat lean-toolchain)" && break
137+
echo "toolchain install attempt $i failed; retrying in 20s"; sleep 20
138+
done
139+
- name: Lean soundness proofs
140+
working-directory: lean
141+
run: |
142+
set -euo pipefail
143+
lake exe cache get
144+
lake build
89145
90146
# Package the publishable crates (pwm-testkit is publish=false). The whole
91147
# workspace is packaged together so inter-crate deps resolve among the set.

AGENTS.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ Without Docker:
7171
cargo run -p pwm-testkit --bin pwm --release -- prove-predictor # synthetic
7272
cargo run -p pwm-testkit --bin pwm --release -- prove-predictor <bundle> # real weights
7373
cargo test --workspace # accept + reject suites
74+
bash ci/check-local.sh quick # fast local quality loop
75+
bash ci/check-local.sh full # local merge-gate mirror
7476
```
7577

7678
The `pwm` CLI prints a five-stage pipeline (LOAD, INFER, COMMIT, VERIFY, TAMPER); it
@@ -84,8 +86,12 @@ honors `NO_COLOR` and `--json`.
8486
doc-comments may keep them per repo style.
8587
- Commits and PRs carry no tool attribution and no `Co-Authored-By` lines.
8688
- One logical change per branch and PR. Branch from `main`, open a PR, keep CI
87-
green (fmt, clippy `-D warnings`, the full test suite, SPDX headers, link check,
88-
the no-em-dash check). Confirm before any destructive git operation.
89+
green (fmt, clippy `-D warnings`, the full test suite, release-mode ignored
90+
predictor test, no_std builds, rustdoc, link check, Python parity/export/style,
91+
script modes, mutation, Lean proofs, cargo-deny, SPDX headers, prose-style check). Confirm
92+
before any destructive git operation.
93+
- Prefer `bash ci/check-local.sh full` before claiming a broad quality or release
94+
pass. Use `quick` only for the inner loop.
8995
- Keep complexity and file size as low as the task allows. Small, focused files.
9096

9197
## Pointers

CHANGELOG.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ entry.
1313

1414
## [Unreleased]
1515

16+
## [0.1.0] - 2026-06-11
17+
1618
### Security
1719

1820
- **Per-buffer range enforcement on the exact-recompute path** (#180): the
19-
verifier now rejects with the typed `VerifyError::BufferRange` /
20-
`VerifyError::InputRange` any prover-supplied value outside the single-M31
21+
verifier now rejects, with the typed `VerifyError::BufferRange` /
22+
`VerifyError::InputRange`, any prover-supplied value outside the single-M31
2123
envelope `[-P_HALF, P_HALF]` before computing on it: every exactly-recomputed
2224
op's claimed output (flat trace and block paths), every seeded block input
2325
buffer, and every proof-carried quantization parameter (`zero_point`,
@@ -26,7 +28,7 @@ entry.
2628
in `i128` with explicit envelope bound checks (mirroring
2729
`fixed_point::finish`), and `ActivationTable::eval` computes its index in
2830
`i128`, so an adversarial buffer or table can no longer wrap `i64` arithmetic
29-
into a fail-closed **panic** (under `overflow-checks`) it is rejected
31+
into a fail-closed **panic** (under `overflow-checks`); it is rejected
3032
cleanly. Narrows verifier acceptance; no honest proof regresses (full suite
3133
plus the real-dims predictor test stay green).
3234

@@ -39,17 +41,27 @@ entry.
3941
`CommitmentMismatch(Model)` unless the proven weights reproduce the export's
4042
commitment bit-for-bit. The artifact alone now certifies which weight set was
4143
proven (bundle ⇄ export bound; checkpoint ⇄ export remains trusted
42-
preprocessing documented in README and specs §13). The Python↔Rust
44+
preprocessing, documented in README and specs §13). The Python↔Rust
4345
canonical-encoding parity gate is now two-way: the multi-leaf `weights_root`
4446
vector and the full predictor weight scheme are pinned on both sides.
4547

4648
### Changed
4749

48-
- Polished the explainer site against the Impeccable design guidelines: removed
49-
the AI-default scaffolding (per-section uppercase eyebrow kickers, side-stripe
50+
- Added reproducible public-prose and Python-style gates: `ci/check-prose-style.py`
51+
is now wired into CI and release validation, current first-party docs pass it,
52+
and `ci/check-python-style.sh` runs the pinned ruff check through `uvx`, `uv`,
53+
`pipx`, or a temporary venv so local contributors can reproduce CI without
54+
relying on one preinstalled runner.
55+
- Hardened tagged releases to run the same core quality bar before packaging:
56+
full cargo-deny, fmt, clippy, workspace tests, release-mode ignored predictor,
57+
no_std builds, rustdoc, link/prose checks, export parity/pipeline, Python
58+
style, the mutation campaign, and Lean soundness proofs.
59+
60+
- Polished the explainer site against the project design guidelines: removed
61+
the generic scaffolding (per-section uppercase eyebrow kickers, side-stripe
5062
accent borders, hero metric boxes, decorative card chips), swapped the body
5163
face from Inter to Archivo, and fixed real defects found while verifying in a
52-
browser grid blowouts that caused horizontal page scroll (`.start`, `.arch`),
64+
browser: grid blowouts that caused horizontal page scroll (`.start`, `.arch`),
5365
a tamper switch that could not be toggled from the keyboard, the closed mobile
5466
menu remaining in the tab order, a focus ring that reshaped pill buttons, and
5567
missing arrow-key navigation on the tier tabs.
@@ -105,8 +117,8 @@ entry.
105117
small-dims predictor accept tests now pin the exact integer output vector
106118
(not just `is_ok` + float tolerance), locking the quantized semantics
107119
end-to-end; and the soundness mutation campaign gained a
108-
`range_check.modp_predictor` mutant the mod-`p` accumulator-aliasing
109-
forgery on a `BatchedLinear` accumulator so the predictor/block path's
120+
`range_check.modp_predictor` mutant: the mod-`p` accumulator-aliasing
121+
forgery on a `BatchedLinear` accumulator, so the predictor/block path's
110122
Freivalds range guard is merge-gated like the flat demo path's.
111123
- The soundness mutation campaign registered no mutant for the `tensor_memory`
112124
(op-to-op wiring) component, so its merge gate passed vacuously; it now exercises
@@ -172,4 +184,5 @@ entry.
172184
`overflow-checks` is enabled in the release profile so any integer wrap on the
173185
recompute path traps instead of silently producing a wrong-but-matching value.
174186

175-
[Unreleased]: https://github.com/AbdelStark/ProvableWorldModel/commits/main
187+
[Unreleased]: https://github.com/AbdelStark/ProvableWorldModel/compare/v0.1.0...main
188+
[0.1.0]: https://github.com/AbdelStark/ProvableWorldModel/releases/tag/v0.1.0

CONTRIBUTING.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ under [docs/legacy-stark/](docs/legacy-stark/) and is no longer normative.
1616
3. Branch from `main`. Reference the issue in the pull request. Keep the diff
1717
focused on the issue's scope.
1818

19+
Run `bash ci/check-local.sh quick` while iterating. Before opening or merging a
20+
pull request, run `bash ci/check-local.sh full`; it mirrors the non-external CI
21+
gates and fails on the first broken contract. Before tagging, run
22+
`bash ci/check-local.sh release <version>` after preparing the changelog.
23+
1924
## Changing the specification
2025

2126
Load-bearing decisions are recorded in [specs.md](specs.md), not in code review. A
@@ -35,9 +40,11 @@ an approximation, a planner rule, or serialization mints a new `relation_id`; se
3540
- The verifier must not depend on the export pipeline or on PyTorch.
3641
- Public items carry doc comments stating preconditions, postconditions, and
3742
errors. Source files carry an `SPDX-License-Identifier: Apache-2.0` header.
38-
- Python (export pipeline): typed, formatted, and linted per the project
39-
configuration; the fixed-point reference must match the Rust reference
40-
bit-for-bit.
43+
- Public prose (README, specs, demo docs, changelog, website copy) uses no em
44+
dashes; run `python3 ci/check-prose-style.py` before opening a pull request.
45+
- Python (export pipeline): keep code type-annotated and pass the pinned Ruff
46+
gate with `bash ci/check-python-style.sh`.
47+
The fixed-point reference must match the Rust reference bit-for-bit.
4148

4249
## Testing
4350

@@ -49,10 +56,17 @@ primitives ship with committed golden vectors and stated domain bounds.
4956
## Continuous integration gates
5057

5158
A pull request must pass: formatting, `clippy -D warnings`, the unit and
52-
integration test suites, golden-vector parity (Python integer reference vs Rust
53-
integer reference), the `no_std` float-free verifier build, the documentation
54-
build, and the SPDX/license check. See [specs.md](specs.md#15-testing-strategy)
55-
and [backlog.md](backlog.md) (milestone M7).
59+
integration test suites, the MSRV build, golden-vector parity (Python integer
60+
reference vs Rust integer reference), the `no_std` float-free verifier build,
61+
the documentation build, the release-mode full-dims predictor test, the mutation
62+
gate, the Lean proofs, the Python style check, the prose-style check, the script
63+
mode check, and the SPDX/license/supply-chain check. See
64+
[specs.md](specs.md#15-testing-strategy) and the hardening ledger in
65+
[backlog.md](backlog.md).
66+
67+
The local entry point is `bash ci/check-local.sh full`. The hosted Real E2E
68+
workflow remains separate because it downloads external Hugging Face assets and
69+
builds the PyTorch export image.
5670

5771
## Security
5872

Cargo.toml

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
# Workspace manifest for ProvableWorldModel.
44
#
55
# Pins the shared package metadata (MSRV, edition, license), the workspace-wide
6-
# lint configuration, and the build profiles. Post-pivot (CommitLLM commit-and-
7-
# audit backend) the system is five first-party crates under `crates/`:
8-
# pwm-core, pwm-export, pwm-prover, pwm-verifier, pwm-testkit. The STARK AIR /
9-
# circuits crates and the vendored Stwo prover were removed; see specs.md.
6+
# lint configuration, and the build profiles. Post-pivot, the CommitLLM
7+
# commit-and-audit backend is five first-party crates under `crates/`: pwm-core,
8+
# pwm-export, pwm-prover, pwm-verifier, pwm-testkit. The STARK AIR / circuits
9+
# crates and the vendored Stwo prover were removed; see specs.md.
1010

1111
[workspace]
1212
resolver = "2"
@@ -21,6 +21,7 @@ edition = "2021"
2121
rust-version = "1.85"
2222
license = "Apache-2.0"
2323
repository = "https://github.com/AbdelStark/ProvableWorldModel"
24+
readme = "crates/README.md"
2425
authors = ["ProvableWorldModel contributors"]
2526

2627
# Centralized dependency versions. First-party crates carry a `version` (not just
@@ -32,15 +33,15 @@ pwm-core = { path = "crates/pwm-core", version = "0.1.0" }
3233
pwm-export = { path = "crates/pwm-export", version = "0.1.0" }
3334
pwm-prover = { path = "crates/pwm-prover", version = "0.1.0" }
3435
pwm-verifier = { path = "crates/pwm-verifier", version = "0.1.0" }
35-
# Blake2s commitment / digest / transcript primitive (specs.md §4, §6). no_std;
36-
# the only crypto dependency in the pwm-core trust root (INV-ARCH-01).
36+
# Blake2s commitment / digest / transcript primitive. no_std; the only crypto
37+
# dependency in the pwm-core trust root.
3738
blake2 = { version = "0.10", default-features = false }
3839
# Test-only third-party dependencies (used by pwm-testkit and pwm-core dev-deps).
3940
serde = { version = "1", features = ["derive"] }
4041
serde_json = "1"
4142

4243
# Shared lints applied to every first-party crate via `[lints] workspace = true`.
43-
# The CI clippy/fmt/header gates (#73) build on this baseline.
44+
# The CI clippy/fmt/header gates build on this baseline.
4445
[workspace.lints.rust]
4546
unsafe_code = "warn"
4647
unreachable_pub = "warn"
@@ -63,8 +64,8 @@ explicit_iter_loop = "warn"
6364
unreadable_literal = "warn"
6465
semicolon_if_nothing_returned = "warn"
6566

66-
# Build profiles. The performance-budget profile is tuned under the perf gate
67-
# (#75); these are conservative defaults appropriate for a prover binary.
67+
# Build profiles. These are conservative defaults appropriate for a prover
68+
# binary.
6869
[profile.release]
6970
opt-level = 3
7071
lto = "thin"

0 commit comments

Comments
 (0)