Move high-level docs into docs/, matching doubleo7's convention
All checks were successful
CI / build (pull_request) Successful in 48s
CI / test (pull_request) Successful in 2m1s
CI / audit (pull_request) Successful in 9s
CI / coverage (pull_request) Successful in 3m53s

SPEC.md and ARCHITECTURE.md were sitting at the repo root alongside
Cargo.toml/Makefile.toml/packages.d — moved both into docs/ (doubleo7
already does this for its own supplementary docs, so this matches an
existing convention in the fleet rather than inventing a new one).

Updated every doc-comment cross-reference across src/*.rs and
Makefile.toml (23 references) to the new docs/SPEC.md / docs/
ARCHITECTURE.md paths. The two files' own cross-references to each other
didn't need changing — they're still same-directory relative references.

Also updated the project reference memory pointing at ARCHITECTURE.md's
location, so it doesn't go stale pointing at a path that no longer exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Austin Schaefer 2026-09-17 13:22:40 +02:00
parent f55188e36f
commit 3e87282b21
13 changed files with 23 additions and 23 deletions

View file

@ -37,7 +37,7 @@ args = ["test"]
# orchestration too (hence its own low number) — its one pure decision # orchestration too (hence its own low number) — its one pure decision
# function (decide_tier_action) is unit tested and should stay visible in # function (decide_tier_action) is unit tested and should stay visible in
# this report; excluding the whole file would hide that signal along with # this report; excluding the whole file would hide that signal along with
# the untested parts. See ARCHITECTURE.md > "separate pure decision logic # the untested parts. See docs/ARCHITECTURE.md > "separate pure decision logic
# from I/O." # from I/O."
[tasks.coverage-report] [tasks.coverage-report]
command = "cargo" command = "cargo"

View file

@ -36,7 +36,7 @@ pub struct BuildResult {
/// artifact, then runs `makepkg` in `build_dir`. /// artifact, then runs `makepkg` in `build_dir`.
/// ///
/// Deliberately one fixed "prebuilt binary" shape, not a templating engine /// Deliberately one fixed "prebuilt binary" shape, not a templating engine
/// — see SPEC.md > Scaling > Template reuse. Covers the two shapes the two /// — see docs/SPEC.md > Scaling > Template reuse. Covers the two shapes the two
/// currently-tracked packages actually need: a bare-binary download /// currently-tracked packages actually need: a bare-binary download
/// (scaleway-cli) and a tarball containing a same-named directory (uv). /// (scaleway-cli) and a tarball containing a same-named directory (uv).
/// Extend when a third real shape shows up rather than guessing at /// Extend when a third real shape shows up rather than guessing at
@ -71,7 +71,7 @@ pub fn build(req: &BuildRequest, build_dir: &Path) -> Result<BuildResult> {
} }
/// Builds the PKGBUILD text for `req`, validating every upstream-controlled /// Builds the PKGBUILD text for `req`, validating every upstream-controlled
/// string first (see SPEC.md > Architecture > Builder: "strict validation /// string first (see docs/SPEC.md > Architecture > Builder: "strict validation
/// on any upstream-controlled string ... never unescaped interpolation"). /// on any upstream-controlled string ... never unescaped interpolation").
/// Pure and side-effect-free so it's testable without invoking `makepkg`. /// Pure and side-effect-free so it's testable without invoking `makepkg`.
fn generate_pkgbuild(req: &BuildRequest) -> Result<String> { fn generate_pkgbuild(req: &BuildRequest) -> Result<String> {
@ -155,7 +155,7 @@ fn archive_stem(asset_name: &str) -> Option<&str> {
/// `asset_name` (via `install_source`) and `binary_name` end up embedded /// `asset_name` (via `install_source`) and `binary_name` end up embedded
/// so `${srcdir}`/`${pkgdir}` can expand. A single check covering both /// so `${srcdir}`/`${pkgdir}` can expand. A single check covering both
/// contexts is safer than trying to remember which fields land in which /// contexts is safer than trying to remember which fields land in which
/// quoting style. See SPEC.md > Architecture > Builder ("never unescaped /// quoting style. See docs/SPEC.md > Architecture > Builder ("never unescaped
/// interpolation"). /// interpolation").
fn validate_shell_safe(field: &str, value: &str) -> Result<()> { fn validate_shell_safe(field: &str, value: &str) -> Result<()> {
if value.contains(['\'', '\n', '$', '`', '\\']) { if value.contains(['\'', '\n', '$', '`', '\\']) {

View file

@ -16,7 +16,7 @@ struct PackageFile {
pub struct Package { pub struct Package {
pub repo: String, pub repo: String,
/// Exact GitHub release asset name (still not a glob — see /// Exact GitHub release asset name (still not a glob — see
/// SPEC.md > Architecture > Fetcher), optionally containing a /// docs/SPEC.md > Architecture > Fetcher), optionally containing a
/// `{version}` placeholder for projects whose asset names embed the /// `{version}` placeholder for projects whose asset names embed the
/// version (e.g. `scaleway-cli_{version}_linux_amd64`). Substituted via /// version (e.g. `scaleway-cli_{version}_linux_amd64`). Substituted via
/// `checker::version_from_tag` before matching. /// `checker::version_from_tag` before matching.
@ -29,7 +29,7 @@ pub struct Package {
/// the repo name). Defaults to the package name when omitted. /// the repo name). Defaults to the package name when omitted.
pub binary_name: Option<String>, pub binary_name: Option<String>,
/// Post-build correctness check (not a security control — see /// Post-build correctness check (not a security control — see
/// SPEC.md > Verification trust tiers). Runs `command` against the /// docs/SPEC.md > Verification trust tiers). Runs `command` against the
/// freshly built binary and confirms `version_regex`'s capture group /// freshly built binary and confirms `version_regex`'s capture group
/// matches the version pkgwatch believes it just built. /// matches the version pkgwatch believes it just built.
pub sanity_check: Option<SanityCheck>, pub sanity_check: Option<SanityCheck>,
@ -53,7 +53,7 @@ pub struct SanityCheck {
#[serde(tag = "method", rename_all = "kebab-case")] #[serde(tag = "method", rename_all = "kebab-case")]
pub enum Verification { pub enum Verification {
/// Tier 4: proves transport integrity only, not authorship. See /// Tier 4: proves transport integrity only, not authorship. See
/// SPEC.md > Verification trust tiers. `checksum_asset_pattern` may /// docs/SPEC.md > Verification trust tiers. `checksum_asset_pattern` may
/// also contain a `{version}` placeholder, same as `asset_pattern`. /// also contain a `{version}` placeholder, same as `asset_pattern`.
SameOriginSha256 { checksum_asset_pattern: String }, SameOriginSha256 { checksum_asset_pattern: String },
/// Tier 2: GitHub build-provenance attestation, verified via `gh /// Tier 2: GitHub build-provenance attestation, verified via `gh
@ -71,7 +71,7 @@ impl Verification {
} }
/// Loads every `*.toml` file in `dir` (the `packages.d/` layout from /// Loads every `*.toml` file in `dir` (the `packages.d/` layout from
/// SPEC.md > Scaling to many packages), keyed by package name. /// docs/SPEC.md > Scaling to many packages), keyed by package name.
pub fn load_packages_dir(dir: &Path) -> Result<Vec<(String, Package)>> { pub fn load_packages_dir(dir: &Path) -> Result<Vec<(String, Package)>> {
let mut out = Vec::new(); let mut out = Vec::new();
for entry in std::fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? { for entry in std::fs::read_dir(dir).with_context(|| format!("reading {}", dir.display()))? {

View file

@ -1,5 +1,5 @@
//! Two functions, shared by two real callers (`verifier`, `builder`) — //! Two functions, shared by two real callers (`verifier`, `builder`) —
//! not a general-purpose utils dump. See ARCHITECTURE.md > "organize by //! not a general-purpose utils dump. See docs/ARCHITECTURE.md > "organize by
//! pipeline stage, not by layer" for why that distinction matters. //! pipeline stage, not by layer" for why that distinction matters.
use anyhow::{Context, Result}; use anyhow::{Context, Result};

View file

@ -1,6 +1,6 @@
//! Entry point: parses `argv` and dispatches to `pipeline`. Nothing here //! Entry point: parses `argv` and dispatches to `pipeline`. Nothing here
//! makes a network/subprocess call or contains a decision worth a test — //! makes a network/subprocess call or contains a decision worth a test —
//! see ARCHITECTURE.md > "main is a dispatcher, not the program." //! see docs/ARCHITECTURE.md > "main is a dispatcher, not the program."
mod builder; mod builder;
mod checker; mod checker;
@ -20,8 +20,8 @@ use anyhow::{Result, bail};
/// check -> fetch -> verify -> build -> sanity-check -> publish, for /// check -> fetch -> verify -> build -> sanity-check -> publish, for
/// whatever is in packages.d/. Tier 1-3 passes auto-publish; tier 4-6 /// whatever is in packages.d/. Tier 1-3 passes auto-publish; tier 4-6
/// passes queue for `pkgwatch review`. See SPEC.md > Architecture for what /// passes queue for `pkgwatch review`. See docs/SPEC.md > Architecture for what
/// each stage does, and ARCHITECTURE.md for how the code implementing it /// each stage does, and docs/ARCHITECTURE.md for how the code implementing it
/// is organized. /// is organized.
fn main() -> Result<()> { fn main() -> Result<()> {
let args: Vec<String> = std::env::args().skip(1).collect(); let args: Vec<String> = std::env::args().skip(1).collect();

View file

@ -1,7 +1,7 @@
//! Orchestrates one run of check -> fetch -> verify -> build -> //! Orchestrates one run of check -> fetch -> verify -> build ->
//! sanity-check -> publish across every configured package, plus the //! sanity-check -> publish across every configured package, plus the
//! `review` subcommand for tier 4-6 approvals. The only module that calls //! `review` subcommand for tier 4-6 approvals. The only module that calls
//! more than one other pipeline-stage module — see ARCHITECTURE.md > "main //! more than one other pipeline-stage module — see docs/ARCHITECTURE.md > "main
//! is a dispatcher, not the program" for why this lives here and not in //! is a dispatcher, not the program" for why this lives here and not in
//! `main.rs`. //! `main.rs`.
@ -73,7 +73,7 @@ pub fn run_check() -> Result<()> {
/// What to do about a package after verification, derived purely from the /// What to do about a package after verification, derived purely from the
/// verification outcome and whether this exact version is already queued /// verification outcome and whether this exact version is already queued
/// for review — no I/O. See ARCHITECTURE.md > "separate pure decision /// for review — no I/O. See docs/ARCHITECTURE.md > "separate pure decision
/// logic from I/O." /// logic from I/O."
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TierAction { enum TierAction {

View file

@ -8,7 +8,7 @@ use std::path::{Path, PathBuf};
use std::process::Command; use std::process::Command;
/// Pacman's system-wide config — hardcoded like the rest of this tool's /// Pacman's system-wide config — hardcoded like the rest of this tool's
/// Arch/Manjaro-specific assumptions (see SPEC.md > Scope). /// Arch/Manjaro-specific assumptions (see docs/SPEC.md > Scope).
const PACMAN_CONF: &str = "/etc/pacman.conf"; const PACMAN_CONF: &str = "/etc/pacman.conf";
/// Copies the built package into `repo_dir` and runs `repo-add` against /// Copies the built package into `repo_dir` and runs `repo-add` against

View file

@ -1,6 +1,6 @@
//! Post-build correctness check: runs the freshly built binary and //! Post-build correctness check: runs the freshly built binary and
//! confirms it reports the version pkgwatch believes it just built. Not a //! confirms it reports the version pkgwatch believes it just built. Not a
//! security control — see SPEC.md > Verification trust tiers. //! security control — see docs/SPEC.md > Verification trust tiers.
use crate::config::SanityCheck; use crate::config::SanityCheck;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
@ -14,7 +14,7 @@ use std::process::Command;
/// whatever's already on the system. Confirms `check.version_regex`'s /// whatever's already on the system. Confirms `check.version_regex`'s
/// capture group matches `expected_version`. /// capture group matches `expected_version`.
/// ///
/// Correctness check only, not a security control — see SPEC.md > /// Correctness check only, not a security control — see docs/SPEC.md >
/// Verification trust tiers. Catches checker bugs and mangled/wrong-asset /// Verification trust tiers. Catches checker bugs and mangled/wrong-asset
/// downloads, not malicious releases. /// downloads, not malicious releases.
pub fn run(check: &SanityCheck, pkg_bin_dir: &Path, expected_version: &str) -> Result<()> { pub fn run(check: &SanityCheck, pkg_bin_dir: &Path, expected_version: &str) -> Result<()> {

View file

@ -8,7 +8,7 @@ use std::path::Path;
/// Last-known-published version per package, so re-runs don't re-flag a /// Last-known-published version per package, so re-runs don't re-flag a
/// version already handled. Deliberately just one file per package for /// version already handled. Deliberately just one file per package for
/// now — this is where a real review-queue persistence layer plugs in /// now — this is where a real review-queue persistence layer plugs in
/// later (see SPEC.md > Architecture > Reviewer queue). /// later (see docs/SPEC.md > Architecture > Reviewer queue).
pub fn load_last_version(state_dir: &Path, name: &str) -> Option<String> { pub fn load_last_version(state_dir: &Path, name: &str) -> Option<String> {
std::fs::read_to_string(state_dir.join(format!("{name}.version"))) std::fs::read_to_string(state_dir.join(format!("{name}.version")))
.ok() .ok()
@ -22,7 +22,7 @@ pub fn save_last_version(state_dir: &Path, name: &str, version: &str) -> Result<
} }
/// Tag currently awaiting human review for a tier 4-6 package (see /// Tag currently awaiting human review for a tier 4-6 package (see
/// SPEC.md > Architecture > Reviewer queue), if any. Separate from /// docs/SPEC.md > Architecture > Reviewer queue), if any. Separate from
/// `load_last_version`/`save_last_version`: approving a review doesn't /// `load_last_version`/`save_last_version`: approving a review doesn't
/// mean future versions auto-publish, so the two must be tracked /// mean future versions auto-publish, so the two must be tracked
/// independently. /// independently.

View file

@ -1,6 +1,6 @@
//! Test-only fixture helpers shared across modules' `#[cfg(test)]` code //! Test-only fixture helpers shared across modules' `#[cfg(test)]` code
//! (`publisher`, `sanity`) — not production code, and not built outside //! (`publisher`, `sanity`) — not production code, and not built outside
//! `cargo test`. See ARCHITECTURE.md > "organize by pipeline stage, not //! `cargo test`. See docs/ARCHITECTURE.md > "organize by pipeline stage, not
//! by layer": this exists to remove one specific piece of duplication //! by layer": this exists to remove one specific piece of duplication
//! (two near-identical copies of "write an executable shell script"), not //! (two near-identical copies of "write an executable shell script"), not
//! as a general test-utils dump. //! as a general test-utils dump.

View file

@ -1,7 +1,7 @@
//! Runs the trust-tier-specific check declared for a package against a //! Runs the trust-tier-specific check declared for a package against a
//! downloaded artifact, and reports a pass/fail plus the tier it implies. //! downloaded artifact, and reports a pass/fail plus the tier it implies.
//! The only module that knows what each `Verification::method` actually //! The only module that knows what each `Verification::method` actually
//! proves — see SPEC.md > Verification trust tiers. //! proves — see docs/SPEC.md > Verification trust tiers.
use crate::checker::version_from_tag; use crate::checker::version_from_tag;
use crate::config::Verification; use crate::config::Verification;
@ -19,7 +19,7 @@ pub struct VerificationResult {
} }
/// Runs the verification method declared for a package against a /// Runs the verification method declared for a package against a
/// downloaded artifact. See SPEC.md > Verification trust tiers for what /// downloaded artifact. See docs/SPEC.md > Verification trust tiers for what
/// each tier does and does not prove. /// each tier does and does not prove.
pub fn verify( pub fn verify(
client: &reqwest::blocking::Client, client: &reqwest::blocking::Client,
@ -59,7 +59,7 @@ pub fn verify(
passed, passed,
justification: if passed { justification: if passed {
"same-origin sha256 matched — proves transport integrity only, \ "same-origin sha256 matched — proves transport integrity only, \
not authorship (see tier 4 in SPEC.md)" not authorship (see tier 4 in docs/SPEC.md)"
.into() .into()
} else { } else {
format!("sha256 mismatch: expected {expected}, got {actual}") format!("sha256 mismatch: expected {expected}, got {actual}")