|
84 | 84 | | ~~`#[contract]` on CLI commands~~ | 172 annotations workspace-wide (70 in apr-cli). PMAT-543 closed. | |
85 | 85 | | ~~Phase 2g: QA playbook~~ | 5 crates ported, 2,792 tests, 256 playbooks. PMAT-532 closed. | |
86 | 86 | | ~~Model type taxonomy~~ | `is_llm()` + 3 new Architecture variants + import guards. PMAT-526 closed. | |
87 | | -| ~~24 unauthorized binaries~~ | 20 crates, 21 binaries. 2 migrated to `[[example]]` (serve, train). 8 legacy remain. PMAT-545. | |
| 87 | +| ~~24 unauthorized binaries~~ | 20 crates, 21 binaries: 1 user-facing (`apr`), 2 standalone (`pv`, `ttop`), 8 legacy. PMAT-545. | |
88 | 88 | | ~~ratatui migration~~ | 0 deps remain. PMAT-539 closed. | |
89 | 89 |
|
90 | 90 | **Open gaps (2 of 9):** |
@@ -262,12 +262,19 @@ Every `aprender-*` crate is a **library**. They expose `pub fn` APIs consumed |
262 | 262 | by `apr-cli` or by external Rust code via `use aprender_compute::*;`. |
263 | 263 | They do NOT produce binaries, CLIs, or executables. |
264 | 264 |
|
265 | | -Exception: `aprender-contracts-cli` may produce a `pv` binary for standalone |
266 | | -contract validation (build tooling, not user-facing ML tooling). |
| 265 | +**Exceptions** (2 standalone binaries that MUST remain separate): |
267 | 266 |
|
268 | | -**Status (2026-04-10)**: AUDITED — 22 crates have `[[bin]]` targets (24 total). |
| 267 | +1. **`pv`** (`aprender-contracts-cli`) — standalone contract validation tool. |
| 268 | + Used in build.rs, pre-commit hooks, and CI pipelines independently of `apr`. |
| 269 | + Cannot be an `apr` subcommand because it's a build dependency, not a runtime tool. |
| 270 | + |
| 271 | +2. **`ttop`** (`aprender-viz-ttop`) — standalone terminal system monitor. |
| 272 | + Used independently for GPU/CPU monitoring outside the ML workflow. |
| 273 | + Separate binary provides zero-dep install for sysadmin use cases. |
| 274 | + |
| 275 | +**Status (2026-04-12)**: AUDITED — 20 crates have `[[bin]]` targets (21 total). |
269 | 276 | Classified in `apr-mono-binary-rule-v1.yaml` v2.0: 1 user-facing (`apr`), |
270 | | -1 build-tool (`pv`), 9 internal-helpers, 2 QA-tools, 11 legacy-to-migrate. |
| 277 | +2 standalone tools (`pv`, `ttop`), 9 internal-helpers, 2 QA-tools, 8 legacy-to-migrate. |
271 | 278 | Legacy binaries have `apr` subcommand migration paths documented. PMAT-545 closed. |
272 | 279 |
|
273 | 280 | ### Rule 3: CLI Contract Coverage |
@@ -384,6 +391,69 @@ The CUDA executor tries to initialize; on failure, falls through to CPU. |
384 | 391 |
|
385 | 392 | **Contract**: `contracts/apr-cli-command-safety-v1.yaml` — `long_running_graceful` equation. |
386 | 393 |
|
| 394 | +### Rule 8: CI Gate Completeness |
| 395 | + |
| 396 | +**The `ci / gate` check MUST verify ALL quality dimensions. No silent skips.** |
| 397 | + |
| 398 | +This rule was added after PR #726 exposed a spec-implementation gap: the spec |
| 399 | +said "CI must pass before merge" but `ci / gate` only checked 3 of 6 CI jobs, |
| 400 | +allowing security failures to merge silently. |
| 401 | + |
| 402 | +#### Five-Whys (2026-04-12) |
| 403 | + |
| 404 | +| # | Question | Answer | |
| 405 | +|---|----------|--------| |
| 406 | +| 1 | Why is main broken after PR merge? | `ci / security` fails on main | |
| 407 | +| 2 | Why did security-failing PR merge? | `ci / security` is not a required check | |
| 408 | +| 3 | Why isn't security required? | `ci / gate` doesn't check `needs.security.result` | |
| 409 | +| 4 | Why doesn't gate check security? | Security was `continue-on-error: true` ("informational") | |
| 410 | +| 5 | Why was this accepted? | Spec says "CI must pass" but doesn't define which checks | |
| 411 | + |
| 412 | +**Root cause**: Spec-implementation gap. The spec promises "CI must pass before |
| 413 | +merge" but doesn't enumerate which jobs constitute "CI". Branch protection |
| 414 | +enforces `ci / gate` only, and the gate silently skips security. |
| 415 | + |
| 416 | +#### Required CI jobs (MUST block merge) |
| 417 | + |
| 418 | +| Job | What it validates | Runner | Required? | |
| 419 | +|-----|-------------------|--------|-----------| |
| 420 | +| `ci / test` | `cargo test --workspace --lib` | `[self-hosted, clean-room]` | **YES** | |
| 421 | +| `ci / lint` | `cargo clippy` + `cargo fmt` | `[self-hosted, clean-room]` | **YES** | |
| 422 | +| `ci / coverage` | Coverage report generation | `[self-hosted, clean-room]` | **YES** | |
| 423 | +| `ci / security` | `cargo audit` (exemptions via `.cargo/audit.toml`) | `[self-hosted, clean-room]` | **YES** — was ubuntu-latest + informational, now self-hosted + mandatory | |
| 424 | +| `ci / gate` | Aggregates test + lint + coverage + security | `[self-hosted, clean-room]` | **YES** — branch protection required check | |
| 425 | +| `workspace-test` | Full workspace test + integration tests | `self-hosted` + container | **YES** — branch protection required check | |
| 426 | +| `ci / provenance` | SLSA attestation | `ubuntu-latest` | NO — informational (no code execution) | |
| 427 | +| `ci / bench` | Performance benchmarks | `[self-hosted, clean-room]` | NO — main-only | |
| 428 | + |
| 429 | +**Sovereign runner policy**: ALL jobs that execute repo code MUST run on |
| 430 | +`[self-hosted, clean-room]` or `self-hosted` with the sovereign container. |
| 431 | +Only metadata jobs (provenance attestation) may use `ubuntu-latest`. |
| 432 | +This ensures consistent hardware (Intel + GPU), local container registry |
| 433 | +(`localhost:5000/sovereign-ci:stable`), and NVMe RAID storage. |
| 434 | + |
| 435 | +#### Implementation |
| 436 | + |
| 437 | +The `ci / gate` job in `paiml/.github/sovereign-ci.yml` must check: |
| 438 | + |
| 439 | +```yaml |
| 440 | +# BEFORE (broken — silently skips security): |
| 441 | +if needs.test.result == "failure" → fail |
| 442 | +if needs.lint.result == "failure" → fail |
| 443 | +if needs.coverage.result == "failure" → fail |
| 444 | + |
| 445 | +# AFTER (complete — all quality dimensions): |
| 446 | +if needs.test.result == "failure" → fail |
| 447 | +if needs.lint.result == "failure" → fail |
| 448 | +if needs.coverage.result == "failure" → fail |
| 449 | +if needs.security.result == "failure" → fail # ADDED |
| 450 | +``` |
| 451 | + |
| 452 | +Branch protection must also add `workspace-test` as a required check |
| 453 | +(it's a local job in `ci.yml`, not part of the reusable workflow). |
| 454 | + |
| 455 | +**Contract**: `contracts/ci-security-audit-v1.yaml` (FALSIFY-AUDIT-001, FALSIFY-AUDIT-002). |
| 456 | + |
387 | 457 | --- |
388 | 458 |
|
389 | 459 | ### Citations |
|
0 commit comments