Skip to content

Commit 0392152

Browse files
fix: aletheia has not compiled since June — repair it, then gate it (#123)
## Summary A sitrep on this repo turned up something bigger than the CI gap it started as: **`aletheia` has not compiled since 2026-06-17.** This branch fixes that, then adds the gate that would have caught it, and clears two unrelated reds. | # | Finding | State | |---|---|---| | 1 | `aletheia` fails to build — unclosed delimiter in `src/config.rs` | **fixed & verified** | | 2 | `aletheia` has no CI anywhere; its 16 workflows are inert | **root gate added** | | 3 | Governance red — `pages.yml` missing SPDX header | **fixed** | | 4 | `absolute-zero` pinned to a nonexistent commit; Dependabot dead | **re-pinned — needs your confirmation** | ## 1. The compile break `Config::load_config` opened three blocks on one collapsed line and closed two. Introduced by `b5322c2` *"security: remediate Track C and Track E findings"* — which correctly added a 1 MiB read cap against memory exhaustion, then dropped a brace collapsing the block. **`main` has been unbuildable for over a month.** The security intent is preserved exactly; the cap is retained and now commented. ## 2. Why nothing noticed `aletheia/` is vendored as **plain tracked files (mode 100644), not a submodule**. Actions only reads `.github/workflows/` at the *repository root*, so all 16 files under `aletheia/.github/workflows/` — `rust-ci`, `codeql`, `cflite_*`, SLSA3, GHCR — have **never executed**. There is no standalone `hyperpolymath/aletheia` running them either; it was removed from GitHub in early 2026. No root workflow mentioned cargo, and root `codeql.yml`'s language matrix excludes Rust. ~962 lines were completely ungated. Adds root `.github/workflows/rust-ci.yml`: debug + release build, 26 unit tests, `cargo fmt --check`, and a zero-dependency check enforcing the RSR Bronze constraint from `aletheia/CLAUDE.md`. It uses the runner's preinstalled Rust — **no new third-party action**, no added supply-chain surface. Also adds `aletheia/.github/workflows/README.md` so nobody edits an inert workflow again. ### What I deliberately did *not* gate Two things are genuinely red, and a hollow green job is worse than no job. Neither is masked with `continue-on-error`; both are documented in the workflow header: - **Integration tests: 27 of 29 fail.** They exercise a CLI surface — `--help`, `--version`, `--format=`, `--badge`, `--html`, `--init-hook` — that `src/main.rs` does not implement. It parses only `--json` and `--sarif`. - **`clippy -D warnings`: 25 findings**, mostly dead code. These share one root cause: the crate was split into 5 modules (962 lines) but `main.rs` is only 117 lines and never wires most of them up. `glob_match`, `check_path_security`, `parse_toml`, `TomlValue`, `PathCheckResult` are all unreachable. **aletheia is mid-refactor and incomplete** — its 823-line test suite is closer to a specification of the intended tool than a description of the current one. That is a product decision, not a CI fix, so I have left it to you. ## 4. Submodule re-pin — please confirm `ad085baa` **does not exist** upstream (API 422), nor on the GitLab or Codeberg mirrors; orphaned by a force-push during the Ddraig rollout. Dependabot has failed since 2026-07-20 with `upload-pack: not our ref`. Re-pinned to current upstream main `87902bb7`. **This is a semantic bump, not a restore** — the old pin's content is unrecoverable, so I cannot verify `87902bb7` is the intended CNO state. It may also not be durable: this repo has already oscillated three times (#89 → `bef4c92` → #117). Worth settling submodule-vs-vendor, or barring force-push on `absolute-zero`. ## Verification All run locally from a clean target dir before pushing: ``` build: PASS release: PASS tests: 26 passed; 0 failed fmt: PASS zero-dep: PASS git submodule update --init absolute-zero -> checks out 87902bb7 cleanly ``` ## Notes - `PROOF-NEEDS.md` is **stale**: it claims one `Admitted` in `y_not_cno`. There are zero — it is a documented *KEPT AXIOM*. A declared trust assumption, not an unproven hole. Worth correcting separately. - `rustfmt.toml` sets 18 nightly-only options that stable rustfmt silently ignores. - `aletheia/Cargo.toml` still points `repository` at the deleted GitHub repo. - Untouched: the uncommitted SonarCloud edits in the working copy (unpinned `@master`), and the 4 unpushed local commits. Both are yours to decide. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ba29a18 commit 0392152

11 files changed

Lines changed: 239 additions & 63 deletions

File tree

.github/workflows/pages.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
#
4+
# pages.yml — GitHub Pages deployment via Ddraig SSG.
15
name: GitHub Pages (Ddraig SSG)
26
on:
37
push:

.github/workflows/rust-ci.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
#
4+
# rust-ci.yml — Build and test the Aletheia microkernel (Rust).
5+
#
6+
# WHY THIS LIVES AT THE REPOSITORY ROOT
7+
# ------------------------------------
8+
# `aletheia/` is vendored as plain tracked files, not a submodule. GitHub Actions
9+
# only reads workflows from `.github/workflows/` at the repository root, so the 16
10+
# workflow files under `aletheia/.github/workflows/` have never executed — see
11+
# `aletheia/.github/workflows/README.md`. Until this file landed, ~962 lines of Rust
12+
# had no build, test, or format gate of any kind, and `main` sat uncompilable for
13+
# over a month (broken 2026-06-17 by b5322c2, fixed in this change).
14+
#
15+
# SCOPE OF THIS GATE — read before trusting a green tick
16+
# -----------------------------------------------------
17+
# Gated here (all genuinely passing, all blocking):
18+
# * debug + release build
19+
# * the 26 unit tests
20+
# * `cargo fmt --check`
21+
#
22+
# NOT gated here, because they are genuinely red today and a passing-but-hollow
23+
# job is worse than no job:
24+
# * `tests/integration_tests.rs` — 27 of 29 fail; they exercise a CLI surface
25+
# (--help, --version, --format=, --badge, --html, --init-hook) that `src/main.rs`
26+
# does not implement.
27+
# * `cargo clippy -- -D warnings` — 25 findings, mostly dead code from modules
28+
# that `main.rs` never wires up.
29+
# Both are tracked as issues. Add them here as blocking jobs once they pass; do not
30+
# add them with `continue-on-error`.
31+
name: Rust CI
32+
33+
on:
34+
pull_request:
35+
branches: ['**']
36+
push:
37+
branches: [main, master]
38+
workflow_dispatch:
39+
40+
permissions:
41+
contents: read
42+
43+
concurrency:
44+
group: rust-ci-${{ github.ref }}
45+
cancel-in-progress: true
46+
47+
defaults:
48+
run:
49+
working-directory: aletheia
50+
51+
jobs:
52+
build:
53+
name: Build (debug + release)
54+
runs-on: ubuntu-latest
55+
timeout-minutes: 15
56+
steps:
57+
- name: Checkout repository
58+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
59+
with:
60+
submodules: false
61+
62+
- name: Show toolchain
63+
run: cargo --version && rustc --version
64+
65+
- name: Verify zero dependencies (RSR Bronze constraint)
66+
run: |
67+
if cargo tree --depth 1 | tail -n +2 | grep -q '[a-z]'; then
68+
echo "::error::Aletheia must have zero dependencies (see aletheia/CLAUDE.md)"
69+
cargo tree --depth 1
70+
exit 1
71+
fi
72+
echo "Zero dependencies confirmed"
73+
74+
- name: Build (debug)
75+
run: cargo build --locked --verbose
76+
77+
- name: Build (release)
78+
run: cargo build --locked --release --verbose
79+
80+
test:
81+
name: Unit tests
82+
runs-on: ubuntu-latest
83+
timeout-minutes: 15
84+
steps:
85+
- name: Checkout repository
86+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
87+
with:
88+
submodules: false
89+
90+
- name: Run unit tests
91+
run: cargo test --locked --bins --verbose
92+
93+
format:
94+
name: Formatting
95+
runs-on: ubuntu-latest
96+
timeout-minutes: 10
97+
steps:
98+
- name: Checkout repository
99+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
100+
with:
101+
submodules: false
102+
103+
- name: cargo fmt --check
104+
run: cargo fmt --all --check

absolute-zero

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!--
2+
SPDX-License-Identifier: CC-BY-SA-4.0
3+
SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
-->
5+
6+
# These 16 workflows do not run. They never have.
7+
8+
GitHub Actions only reads workflow files from `.github/workflows/` **at the root of a
9+
repository**. This directory is nested inside `maa-framework`, so every YAML file here
10+
is inert — including `rust-ci.yml`, `codeql.yml`, `cflite_pr.yml`, `cflite_batch.yml`,
11+
`generator-generic-ossf-slsa3-publish.yml`, `scorecard.yml`, and `ghcr-publish.yml`.
12+
13+
## Why they are here
14+
15+
`aletheia` began as the standalone repository `hyperpolymath/aletheia` (created
16+
2025-12-11), where these workflows *did* run. That repository was removed from GitHub —
17+
its mirrors on GitLab, Codeberg and Bitbucket all stop by early January 2026. On
18+
2026-02-21, commit `639f389` left behind a dangling gitlink with no `.gitmodules`
19+
entry; on 2026-03-02, commit `25cf219` ("Fix stale submodule pointers after repo
20+
cleanup") replaced that pointer by vendoring 361 files — these workflows among them —
21+
into `maa-framework` as ordinary tracked files.
22+
23+
Nothing has executed them since.
24+
25+
## What that cost
26+
27+
Between 2026-06-17 and 2026-07-21, `aletheia` **did not compile**. Commit `b5322c2`
28+
("security: remediate Track C and Track E findings") correctly added a 1 MiB read cap
29+
to `Config::load_config`, but collapsed the block onto one line and dropped a closing
30+
brace. A single `cargo build` would have caught it. Nothing ran one, so `main` stayed
31+
broken for over a month.
32+
33+
## Where the real gate lives now
34+
35+
`/.github/workflows/rust-ci.yml`, at the repository root. It builds debug and release,
36+
runs the 26 unit tests, checks formatting, and enforces the zero-dependency constraint
37+
from `aletheia/CLAUDE.md`. Read its header comment for what it deliberately does *not*
38+
gate yet.
39+
40+
## If you are changing CI for aletheia
41+
42+
Edit the root workflow. Editing anything in this directory has no effect. These files
43+
are retained only as the source material for porting the capabilities that were lost —
44+
ClusterFuzzLite fuzzing, SLSA3 provenance, GHCR publishing — back to root workflows.
45+
Once a capability is ported, delete its file here so this directory shrinks toward
46+
empty.

aletheia/benches/verification_benchmark.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,10 @@ fn main() {
238238
} else if avg_ms < 10.0 {
239239
println!("\n Status: ✅ TARGET MET ({:.2}ms < 10ms)", avg_ms);
240240
} else {
241-
println!("\n Status: ⚠️ NEEDS IMPROVEMENT ({:.2}ms >= 10ms)", avg_ms);
241+
println!(
242+
"\n Status: ⚠️ NEEDS IMPROVEMENT ({:.2}ms >= 10ms)",
243+
avg_ms
244+
);
242245
}
243246

244247
// Memory info (if available on Linux)

aletheia/src/checks.rs

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
//! RSR Compliance Verification Kernel.
44
//!
5-
//! This module implements the deterministic checks used by Aletheia to
6-
//! audit repository state. It performs physical filesystem analysis to
5+
//! This module implements the deterministic checks used by Aletheia to
6+
//! audit repository state. It performs physical filesystem analysis to
77
//! validate documentation, build system files, and security configurations.
88
99
use std::fs;
@@ -20,7 +20,7 @@ pub fn glob_match(pattern: &str, text: &str) -> bool {
2020
}
2121

2222
/// SECURITY: Validates that a path does not contain malicious symlinks.
23-
/// Specifically checks if a symlink "escapes" the repository root, which
23+
/// Specifically checks if a symlink "escapes" the repository root, which
2424
/// is a critical safety invariant for air-gapped or verified builds.
2525
pub fn check_path_security(path: &Path, repo_root: &Path) -> PathCheckResult {
2626
let metadata = match fs::symlink_metadata(path) {
@@ -29,19 +29,36 @@ pub fn check_path_security(path: &Path, repo_root: &Path) -> PathCheckResult {
2929
};
3030

3131
if !metadata.file_type().is_symlink() {
32-
return PathCheckResult { exists: true, ..Default::default() };
32+
return PathCheckResult {
33+
exists: true,
34+
..Default::default()
35+
};
3336
}
3437

3538
// RESOLUTION: Determine the absolute target of the symlink.
3639
let target = match fs::read_link(path) {
3740
Ok(t) => t,
38-
Err(_) => return PathCheckResult { exists: true, is_symlink: true, ..Default::default() },
41+
Err(_) => {
42+
return PathCheckResult {
43+
exists: true,
44+
is_symlink: true,
45+
..Default::default()
46+
}
47+
},
3948
};
4049

4150
// ESCAPE DETECTION: Canonicalize and verify prefix.
42-
let canonical_root = repo_root.canonicalize().unwrap_or_else(|_| repo_root.to_path_buf());
43-
let resolved_target = if target.is_absolute() { target } else { path.parent().expect("TODO: handle error").join(target) };
44-
let canonical_target = resolved_target.canonicalize().unwrap_or_else(|_| resolved_target);
51+
let canonical_root = repo_root
52+
.canonicalize()
53+
.unwrap_or_else(|_| repo_root.to_path_buf());
54+
let resolved_target = if target.is_absolute() {
55+
target
56+
} else {
57+
path.parent().expect("TODO: handle error").join(target)
58+
};
59+
let canonical_target = resolved_target
60+
.canonicalize()
61+
.unwrap_or_else(|_| resolved_target);
4562

4663
PathCheckResult {
4764
exists: true,
@@ -85,11 +102,8 @@ pub fn check_spdx_headers(report: &mut ComplianceReport, repo_path: &Path) {
85102
if path.extension().map_or(false, |ext| ext == "rs") {
86103
checked += 1;
87104
if let Ok(content) = fs::read_to_string(&path) {
88-
let first_10_lines: String = content
89-
.lines()
90-
.take(10)
91-
.collect::<Vec<_>>()
92-
.join("\n");
105+
let first_10_lines: String =
106+
content.lines().take(10).collect::<Vec<_>>().join("\n");
93107
if first_10_lines.contains("SPDX-License-Identifier") {
94108
valid += 1;
95109
}
@@ -121,7 +135,10 @@ pub fn check_workflow_pins(report: &mut ComplianceReport, repo_path: &Path) {
121135
for entry in entries {
122136
if let Ok(entry) = entry {
123137
let path = entry.path();
124-
if path.extension().map_or(false, |ext| ext == "yml" || ext == "yaml") {
138+
if path
139+
.extension()
140+
.map_or(false, |ext| ext == "yml" || ext == "yaml")
141+
{
125142
checked_files += 1;
126143
if let Ok(content) = fs::read_to_string(&path) {
127144
// Check if all 'uses:' lines have SHA pinning (40 hex chars)

aletheia/src/config.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,13 @@ impl Config {
7373
let config_path = repo_path.join(".aletheia.toml");
7474

7575
if config_path.is_file() {
76-
if let Ok(mut file) = fs::File::open(&config_path) { use std::io::Read; let mut content = String::new(); if file.take(1024 * 1024).read_to_string(&mut content).is_ok() {
77-
return Self::parse_from_string(&content);
76+
if let Ok(file) = fs::File::open(&config_path) {
77+
use std::io::Read;
78+
let mut content = String::new();
79+
// Cap the read at 1 MiB so a hostile config cannot exhaust memory.
80+
if file.take(1024 * 1024).read_to_string(&mut content).is_ok() {
81+
return Self::parse_from_string(&content);
82+
}
7883
}
7984
}
8085

aletheia/src/main.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
//! Aletheia — Authoritative RSR Compliance Verification.
44
//!
5-
//! Named after the Greek concept of "unconcealment," Aletheia is the
6-
//! gatekeeper for the Rhodium Standard Repository (RSR) ecosystem.
7-
//! It provides automated, deterministic audits of repository state to
5+
//! Named after the Greek concept of "unconcealment," Aletheia is the
6+
//! gatekeeper for the Rhodium Standard Repository (RSR) ecosystem.
7+
//! It provides automated, deterministic audits of repository state to
88
//! ensure adherence to safety, security, and documentation standards.
99
//!
1010
//! COMPLIANCE DIMENSIONS:
@@ -70,7 +70,7 @@ fn main() {
7070
Err(e) => {
7171
eprintln!("Error: {}", e);
7272
process::exit(1);
73-
}
73+
},
7474
};
7575

7676
// 2. ENVIRONMENT VALIDATION
@@ -93,7 +93,11 @@ fn main() {
9393
}
9494

9595
// 5. EXIT POLICY
96-
let exit_code = if report.checks.iter().any(|c| !c.passed && c.required_for == ComplianceLevel::Bronze) {
96+
let exit_code = if report
97+
.checks
98+
.iter()
99+
.any(|c| !c.passed && c.required_for == ComplianceLevel::Bronze)
100+
{
97101
exit_codes::COMPLIANCE_FAILED
98102
} else if report.warnings.iter().any(|w| w.level == "critical") {
99103
exit_codes::SECURITY_WARNING

aletheia/src/output.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,25 @@
22
// Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
33
//! Aletheia Output and Reporting Engine.
44
//!
5-
//! This module implements the presentation layer for compliance audits.
6-
//! It provides multiple serialization formats (JSON, SARIF, HTML) and
5+
//! This module implements the presentation layer for compliance audits.
6+
//! It provides multiple serialization formats (JSON, SARIF, HTML) and
77
//! a high-fidelity human-readable CLI report.
88
//!
9-
//! ZERO-DEPENDENCY DESIGN: To maintain RSR Bronze compliance, this module
10-
//! implements its own timestamp formatting and string escaping rather than
9+
//! ZERO-DEPENDENCY DESIGN: To maintain RSR Bronze compliance, this module
10+
//! implements its own timestamp formatting and string escaping rather than
1111
//! pulling in external crates like `chrono` or `serde_json`.
1212
13-
use std::time::SystemTime;
1413
use crate::types::*;
14+
use std::time::SystemTime;
1515

1616
/// The current version of the Aletheia tool.
1717
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
1818

1919
/// EXIT STRATEGY: Standardized exit codes for CI/CD integration.
2020
pub mod exit_codes {
21-
pub const SUCCESS: i32 = 0; // Bronze compliance achieved.
21+
pub const SUCCESS: i32 = 0; // Bronze compliance achieved.
2222
pub const COMPLIANCE_FAILED: i32 = 1; // Mandatory checks failed.
23-
pub const SECURITY_WARNING: i32 = 2; // Critical security issues (e.g. symlink escape).
23+
pub const SECURITY_WARNING: i32 = 2; // Critical security issues (e.g. symlink escape).
2424
}
2525

2626
/// ALGORITHM: Manual timestamp formatter.
@@ -40,7 +40,7 @@ pub fn format_timestamp(time: SystemTime) -> String {
4040
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
4141
year, month, day, hour, minute, second
4242
)
43-
}
43+
},
4444
Err(_) => "2026-02-21T00:00:00Z".to_string(),
4545
}
4646
}
@@ -137,16 +137,25 @@ pub fn print_report(report: &ComplianceReport) {
137137
pub fn print_json_report(report: &ComplianceReport) {
138138
println!("{{");
139139
println!(" \"version\": \"{}\",", VERSION);
140-
println!(" \"repository\": \"{}\",", report.repository_path.display());
141-
println!(" \"timestamp\": \"{}\",", format_timestamp(report.verified_at));
140+
println!(
141+
" \"repository\": \"{}\",",
142+
report.repository_path.display()
143+
);
144+
println!(
145+
" \"timestamp\": \"{}\",",
146+
format_timestamp(report.verified_at)
147+
);
142148
println!(" \"checks\": [");
143149

144150
for (i, check) in report.checks.iter().enumerate() {
145151
println!(" {{");
146152
println!(" \"category\": \"{}\",", check.category);
147153
println!(" \"item\": \"{}\",", check.item);
148154
println!(" \"passed\": {}", check.passed);
149-
println!(" }}{}", if i < report.checks.len() - 1 { "," } else { "" });
155+
println!(
156+
" }}{}",
157+
if i < report.checks.len() - 1 { "," } else { "" }
158+
);
150159
}
151160

152161
println!(" ],");

0 commit comments

Comments
 (0)