From 8fb39399e759c9dd7c7666004b64749a5cca211c Mon Sep 17 00:00:00 2001 From: Jens Holdgaard Pedersen Date: Thu, 11 Jun 2026 00:04:05 +0200 Subject: [PATCH 1/4] =?UTF-8?q?ci:=20deny=20rustdoc=20breakage=20=E2=80=94?= =?UTF-8?q?=20workspace=20lints=20+=20a=20cargo=20doc=20job=20(#124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit broken_intra_doc_links / private_intra_doc_links / invalid_rust_codeblocks deny + bare_urls warn at the workspace level; fix the 16 existing offenders (private or unresolved link targets downgraded to plain code spans); add a docs CI job so the class of mechanical rustdoc comments never reaches review. Co-Authored-By: Claude Fable 5 --- .claude/settings.json | 23 +++++++++++++++++++ .github/workflows/ci.yml | 14 +++++++++++ Cargo.toml | 11 +++++++++ crates/ourios-bench/src/store.rs | 4 ++-- crates/ourios-core/src/audit.rs | 8 +++---- crates/ourios-miner/src/cluster.rs | 8 +++---- crates/ourios-miner/src/overflow.rs | 2 +- .../ourios-parquet/src/audit_record_batch.rs | 2 +- crates/ourios-parquet/src/audit_writer.rs | 2 +- crates/ourios-parquet/src/writer.rs | 6 ++--- 10 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..81b5a74a --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,23 @@ +{ + "permissions": { + "allow": [ + "Bash(cargo fmt --all --check)", + "Bash(kubectl get *)", + "Bash(kubectl logs *)", + "mcp__github__get_file_contents", + "mcp__github__search_code", + "mcp__github__pull_request_read", + "mcp__github__search_repositories", + "mcp__kubernetes__pods_log", + "mcp__kubernetes__resources_get", + "mcp__kubernetes__resources_list", + "mcp__kubernetes__pods_list_in_namespace", + "mcp__kubernetes__events_list", + "mcp__azure-devops__repo_get_file_content", + "mcp__azure-devops__repo_list_directory" + ] + }, + "enabledPlugins": { + "rust-analyzer-lsp@claude-plugins-official": true + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62f8a2f4..5d6e0e91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,20 @@ jobs: - uses: Swatinem/rust-cache@v2 - run: cargo clippy --all-targets --all-features -- -D warnings + docs: + name: cargo doc + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable + - uses: Swatinem/rust-cache@v2 + # The [workspace.lints.rustdoc] denies (broken intra-doc links + # etc., #124) fail this build, so doc rot is caught in CI, not + # in review comments. + - run: cargo doc --workspace --no-deps --all-features + env: + RUSTDOCFLAGS: -D warnings + test: name: cargo test runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index 06f132cc..dde7064c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,16 @@ repository = "https://github.com/jensholdgaard/ourios" [workspace.lints.rust] unsafe_code = "deny" +# Rustdoc breakage (broken intra-doc links etc.) kept reaching review +# as Copilot comments the compiler could have raised first (#124). +# All member crates declare `[lints] workspace = true`, so this +# propagates without per-crate edits; `cargo doc --workspace +# --no-deps --all-features` is the local check. +[workspace.lints.rustdoc] +broken_intra_doc_links = "deny" +private_intra_doc_links = "deny" +invalid_rust_codeblocks = "deny" +bare_urls = "warn" + [workspace.lints.clippy] pedantic = { level = "warn", priority = -1 } diff --git a/crates/ourios-bench/src/store.rs b/crates/ourios-bench/src/store.rs index c073f6be..8d7bd637 100644 --- a/crates/ourios-bench/src/store.rs +++ b/crates/ourios-bench/src/store.rs @@ -32,7 +32,7 @@ const ERROR_BAND: std::ops::RangeInclusive = 17..=20; #[derive(Debug, Clone, Copy)] pub struct BuiltStore { /// Tenant every record was written under (the corpus loader is - /// single-tenant — [`crate::corpus`]'s `BENCH_TENANT`). A query must + /// single-tenant — `crate::corpus`'s `BENCH_TENANT`). A query must /// use this tenant or it scans nothing (RFC0007.5 isolation). pub tenant: &'static str, /// Total rows written across all partitions. @@ -55,7 +55,7 @@ pub struct BuiltStore { /// Load the corpus at `corpus_dir`, mine it, and write the emitted /// records as a partitioned RFC 0005 Parquet store under -/// `bucket_root` (which a [`ourios_querier::Querier`] can then be +/// `bucket_root` (which a `ourios_querier::Querier` can then be /// rooted at). Returns a [`BuiltStore`] summary. /// /// # Errors diff --git a/crates/ourios-core/src/audit.rs b/crates/ourios-core/src/audit.rs index 009b8bbe..53a7b601 100644 --- a/crates/ourios-core/src/audit.rs +++ b/crates/ourios-core/src/audit.rs @@ -368,7 +368,7 @@ pub struct SlotExpansion { /// The byte layout is private; the public surface is value-based /// (`singleton`, `insert`, `contains`, `iter`, `is_empty`, `union`) /// so the layout can change without breaking callers. A new -/// `ParamType` variant forces an update to [`SlotTypes::bit`] — +/// `ParamType` variant forces an update to `SlotTypes::bit` — /// the compiler enforces totality via the exhaustive match. Eight /// variants fit a `u8`; if the alphabet grows past eight, widen the /// backing integer here (the same exhaustive match will fail to @@ -478,7 +478,7 @@ pub struct AuditEvent { pub payload: AuditPayload, } -/// Truncated blake3 of `bytes` for [`AuditEvent::triggering_line_hash`]. +/// Truncated blake3 of `bytes` for `AuditEvent::triggering_line_hash`. /// /// Returns the first 16 bytes of the blake3 digest. Centralised here /// so every producer (and the future WAL-side joiner) uses the same @@ -495,7 +495,7 @@ pub fn hash_triggering_line(bytes: &[u8]) -> [u8; 16] { /// First 256 *bytes* of `raw`, truncated at the nearest preceding /// UTF-8 char boundary so the result is always valid `String`. /// -/// Helper for populating [`AuditEvent::triggering_line_sample`] per +/// Helper for populating `AuditEvent::triggering_line_sample` per /// the RFC §6.4 "first 256 B of `L_raw`" rule. Bytes — not chars — /// so the bound on the audit-stream's per-event size is predictable /// regardless of the input's multibyte content. @@ -599,7 +599,7 @@ impl AuditSink for InMemoryAuditSink { /// still has a handle for inspection. /// /// `Clone` yields another handle to the *same* buffer — that's the -/// whole point: hand one clone to [`MinerCluster::with_audit_sink`] +/// whole point: hand one clone to `MinerCluster::with_audit_sink` /// and keep another to drain after the act. `InMemoryAuditSink` /// alone would require `&mut self` to drain, which the trait-object /// indirection on the producer side rules out. diff --git a/crates/ourios-miner/src/cluster.rs b/crates/ourios-miner/src/cluster.rs index 7e8b4903..3e1a230b 100644 --- a/crates/ourios-miner/src/cluster.rs +++ b/crates/ourios-miner/src/cluster.rs @@ -1,6 +1,6 @@ //! Per-tenant template cluster. //! -//! Holds one [`TenantState`] per [`TenantId`] (`[CLAUDE.md §3.7]`): +//! Holds one `TenantState` per [`TenantId`] (`[CLAUDE.md §3.7]`): //! every ingested record is keyed on its tenant, and per-tenant //! template *stores* are isolated — no template ever crosses //! tenants. The `template_id` allocator, by contrast, is @@ -91,7 +91,7 @@ pub const NO_TEMPLATE: u64 = 0; /// A multi-tenant in-memory miner. /// -/// Holds one [`TenantState`] per [`TenantId`]; per-tenant state +/// Holds one `TenantState` per [`TenantId`]; per-tenant state /// is allocated lazily on the first `ingest` call for that /// tenant. Tenant deprovisioning (`TenantPaused`, /// `TenantDeleted`) is RFC 0001 §9 territory and not in this @@ -306,7 +306,7 @@ impl MinerCluster { } /// Register a per-tenant [`MinerConfig`] override per RFC 0004 - /// §3.4. The override is captured by [`TenantState`] at lazy + /// §3.4. The override is captured by `TenantState` at lazy /// allocation (i.e. on the first ingest for `tenant_id`); set /// it before the tenant is first observed. /// @@ -1750,7 +1750,7 @@ impl MinerCluster { /// (tree leaves + structured-template entries). Returns 0 for /// a tenant the cluster has never seen. /// - /// O(1): served from the [`TenantState::template_count`] + /// O(1): served from the `TenantState::template_count` /// cache rather than walking the tree. #[must_use] pub fn template_count(&self, tenant_id: &TenantId) -> usize { diff --git a/crates/ourios-miner/src/overflow.rs b/crates/ourios-miner/src/overflow.rs index 97d0ed28..dcd032ae 100644 --- a/crates/ourios-miner/src/overflow.rs +++ b/crates/ourios-miner/src/overflow.rs @@ -14,7 +14,7 @@ //! cardinality destroys Parquet's dictionary encoding and bloats //! files. The byte-limit + spill-to-body contract is the answer //! to the canonical "stack trace in a `params` slot" hazard -//! ([`docs/hazards.md`] H2). +//! (`docs/hazards.md` H2). use sha2::{Digest, Sha256}; diff --git a/crates/ourios-parquet/src/audit_record_batch.rs b/crates/ourios-parquet/src/audit_record_batch.rs index 3475212f..1a00aa6e 100644 --- a/crates/ourios-parquet/src/audit_record_batch.rs +++ b/crates/ourios-parquet/src/audit_record_batch.rs @@ -497,7 +497,7 @@ fn append_slots(builder: &mut GenericListBuilder, slots: &[S /// /// # Panics /// -/// Structurally impossible. The inner [`ReasonPayload`] is two +/// Structurally impossible. The inner `ReasonPayload` is two /// owned-or-borrowed scalars; `serde_json::to_string` only fails /// when the `Serialize` impl produces an error (e.g. an /// invalid-UTF-8 map key) which neither field can ever do. diff --git a/crates/ourios-parquet/src/audit_writer.rs b/crates/ourios-parquet/src/audit_writer.rs index 9b57865e..9b942505 100644 --- a/crates/ourios-parquet/src/audit_writer.rs +++ b/crates/ourios-parquet/src/audit_writer.rs @@ -169,7 +169,7 @@ impl AuditWriter { /// /// `append_events` is **not all-or-nothing** across the /// sub-batches it issues internally. The slice is chunked - /// into [`SUB_BATCH_ROWS`]-sized pieces; if chunk *N* writes + /// into `SUB_BATCH_ROWS`-sized pieces; if chunk *N* writes /// successfully and chunk *N+1*'s `audit_events_to_batch` /// then errors with `Batch`, the events from chunks `0..N` /// have already landed in the in-progress row group. Callers diff --git a/crates/ourios-parquet/src/writer.rs b/crates/ourios-parquet/src/writer.rs index 46fdde2f..ffa3c8fd 100644 --- a/crates/ourios-parquet/src/writer.rs +++ b/crates/ourios-parquet/src/writer.rs @@ -208,7 +208,7 @@ impl Writer { /// a `RecordBatch`, and forwards to `ArrowWriter::write`. /// /// **Row-group sizing.** Internally chunks `records` into - /// sub-batches of [`SUB_BATCH_ROWS`] (1024) rows and runs a + /// sub-batches of `SUB_BATCH_ROWS` (1024) rows and runs a /// flush-when-over-threshold check before each sub-batch /// write. RFC 0005 §3.5 pins the row-group target at 128 MiB /// – 1 GiB uncompressed; chunking + per-sub-batch flush @@ -230,7 +230,7 @@ impl Writer { /// /// `append_records` is **not all-or-nothing** across the /// sub-batches it issues internally. The slice is chunked - /// into [`SUB_BATCH_ROWS`]-sized pieces; if chunk *N* writes + /// into `SUB_BATCH_ROWS`-sized pieces; if chunk *N* writes /// successfully and chunk *N+1*'s `mined_records_to_batch` /// then errors with `Batch`, the rows from chunks `0..N` have /// already landed in the in-progress row group. Callers that @@ -467,7 +467,7 @@ pub enum WriterError { op: &'static str, /// The primary path the operation was acting on. For /// `rename`, this is the *destination*; the source - /// path lives in [`Self::source_path`] (when set). + /// path lives in `Self::source_path` (when set). path: PathBuf, /// Secondary path for two-path operations (only /// populated for `rename`, where it carries the source From 1e97dd4bc9b1c441f0317e85eda6e83d8f04101f Mon Sep 17 00:00:00 2001 From: Jens Holdgaard Pedersen Date: Thu, 11 Jun 2026 00:06:19 +0200 Subject: [PATCH 2/4] ci: gate merges on the docs job via ci-success needs Without it the new cargo doc job would run but never block. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d6e0e91..a510b7a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -169,7 +169,7 @@ jobs: ci-success: name: CI Success runs-on: ubuntu-latest - needs: [fmt, clippy, test, book, semconv] + needs: [fmt, clippy, docs, test, book, semconv] if: always() steps: - name: Verify required checks From 380ed4755b5faa78d53a27fad70f36727d5bb83d Mon Sep 17 00:00:00 2001 From: Jens Holdgaard Pedersen Date: Thu, 11 Jun 2026 06:43:15 +0200 Subject: [PATCH 3/4] ci: docs job hardening + drop the settings.json that git add -A swept in Remove .claude/settings.json from the branch (local permission allowlist, not PR content) + gitignore it alongside the lock file. Docs job: read-only token, no persisted credentials, and no RUSTDOCFLAGS override so bare_urls stays advisory per the workspace lint table. Co-Authored-By: Claude Fable 5 --- .claude/settings.json | 23 ----------------------- .github/workflows/ci.yml | 13 ++++++++----- .gitignore | 4 ++++ 3 files changed, 12 insertions(+), 28 deletions(-) delete mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 81b5a74a..00000000 --- a/.claude/settings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(cargo fmt --all --check)", - "Bash(kubectl get *)", - "Bash(kubectl logs *)", - "mcp__github__get_file_contents", - "mcp__github__search_code", - "mcp__github__pull_request_read", - "mcp__github__search_repositories", - "mcp__kubernetes__pods_log", - "mcp__kubernetes__resources_get", - "mcp__kubernetes__resources_list", - "mcp__kubernetes__pods_list_in_namespace", - "mcp__kubernetes__events_list", - "mcp__azure-devops__repo_get_file_content", - "mcp__azure-devops__repo_list_directory" - ] - }, - "enabledPlugins": { - "rust-analyzer-lsp@claude-plugins-official": true - } -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a510b7a1..ce284bea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,16 +39,19 @@ jobs: docs: name: cargo doc runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - # The [workspace.lints.rustdoc] denies (broken intra-doc links - # etc., #124) fail this build, so doc rot is caught in CI, not - # in review comments. + # The [workspace.lints.rustdoc] levels are the single source of + # truth: the deny-level lints (broken intra-doc links etc., + # #124) fail this build on their own; no RUSTDOCFLAGS override, + # so `bare_urls = "warn"` stays advisory in CI too. - run: cargo doc --workspace --no-deps --all-features - env: - RUSTDOCFLAGS: -D warnings test: name: cargo test diff --git a/.gitignore b/.gitignore index 6db3449a..f96fbf0f 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,10 @@ # scheduled-task lock file is per-machine ephemera that leaked into # PR #62 once and shouldn't again. /.claude/scheduled_tasks.lock +# Per-machine permission allowlist — same leak class (swept into a +# PR by `git add -A` once); the tracked `.claude/` content above is +# unaffected. +/.claude/settings.json # Cargo.lock is NOT ignored — Ourios is a binary (CLAUDE.md §1) and its # lockfile is part of the reproducible-build contract. From fcfea8093203603cbb6188da575097fcafe0608d Mon Sep 17 00:00:00 2001 From: Jens Holdgaard Pedersen Date: Thu, 11 Jun 2026 17:06:03 +0200 Subject: [PATCH 4/4] docs(bench): downgrade the dev-dep querier link in the module docs too Dead in rendered docs (ourios-querier is a dev-dependency); rustdoc happens not to flag this one, but consistency with the 16 sibling fixes says no links to unresolvable targets. Co-Authored-By: Claude Fable 5 --- crates/ourios-bench/src/store.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ourios-bench/src/store.rs b/crates/ourios-bench/src/store.rs index 8d7bd637..bf3909a6 100644 --- a/crates/ourios-bench/src/store.rs +++ b/crates/ourios-bench/src/store.rs @@ -3,7 +3,7 @@ //! The A1 path measures the bytes a corpus compresses to; the B1/B2 //! latency benches need to *query* the same corpus, so they need the //! mined records laid down as a real partitioned Parquet store they -//! can point a [`ourios_querier::Querier`] at. Both public builders +//! can point a `ourios_querier::Querier` at. Both public builders //! reuse the same corpus loader and miner harness the gates run on //! (so the store matches what A1 measured), then write every emitted //! record via per-partition [`Writer`]s (the same streaming write A1