Skip to content

ci: one job for the source gates, and caching that is not a net loss - #1857

Merged
tato123 merged 3 commits into
mainfrom
ci/consolidate-gates-and-fix-caching
Aug 12, 2026
Merged

ci: one job for the source gates, and caching that is not a net loss#1857
tato123 merged 3 commits into
mainfrom
ci/consolidate-gates-and-fix-caching

Conversation

@tato123

@tato123 tato123 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Owner-directed in session rather than ticket-derived — the ask was "CI takes forever, can we unify it, prune it, and cache properly". Plumbing only: no change to what is verified, only to how many runners verify it and how they cache.

What was actually slow

Measured, not guessed. A representative check-boundaries run:

Step Time
Setup + checkout + rustup 4s
Cache Cargo (restore) 7s
cargo xtask check-boundaries 26s
cargo test -p xtask check_boundaries 13s
actual file walking <1s

Seven workflows did exactly that, and all seven shared one cache key, so they raced and six of every seven saves were discarded.

There were never too many tests. streamlib-engine holds 1396 #[test]; CI runs two module filters. Of 65 integration test targets tree-wide, CI runs one. Pruning tests would have been the wrong lever — what was bloated was jobs.

Changes

Job consolidation — 13 checks to 6. check-all-source-gates runs all eight gates in one process, reporting every failure before exiting, so a single job still surfaces every breakage at once. One cargo test -p xtask replaces eight filtered subsets of the same 178-test binary. License headers and the ship-change gate tests (bash + git only) share a second runner.

Caching was a net loss. Repo usage was 11.76 GB against GitHub's 10 GB cap, so every save evicted a live entry; restoring the 3.72 GB cargo-test entry took 66s to serve 53s of tests. Two causes — caching bare target/, and ref-scoped saves (three near-identical 1.97 GB wheel entries coexisted: main, PR #1853, PR #1854, none readable by the others). Now Swatinem/rust-cache with save-if on main. The seven orphaned entries are purged.

Nothing cancelled superseded runs. Every PR-triggered workflow now has a concurrency group. The two expensive jobs skip prose-only changes; the source gates deliberately do not, since they scan docs/ and Markdown.

cargo ci mirrors CI, plus cargo gates for the fast no-build subset. This required the license check to leave inline workflow YAML — inline bash is by construction something nobody can run before pushing — so it is now scripts/check-license-headers.sh, shared by both. It discovers files with git ls-files rather than find: a find matches CI's clean checkout but produced 15,722 false positives locally, where venvs and uv caches sit in the tree.

Second commit corrects the first

The first commit disabled incremental compilation after finding 419 GB in target/debug/incremental. Measuring the thing that actually matters says that was wrong:

One-line edit to streamlib-engine, then cargo check -p streamlib-engine
incremental off 8s
incremental on 3.0s (steady state, ~255 MB)

The 419 GB was months of accumulation across crates, profiles and feature permutations, not a per-build cost — rm -rf target/debug/incremental reclaims it in seconds. Trading a 2.7x slower edit loop for that is a bad deal, so incremental stays on locally. CI keeps CARGO_INCREMENTAL=0, where it is unambiguously right.

The same commit runs the gates in release locally: they syn-parse ~7k files, so the profile dominates — 11s release against 24s debug. CI deliberately does not mirror it, since its job already compiles xtask in debug for the tests and a second profile would cost more than the 13s it saves.

Verification

  • cargo test -p xtask — 178 passed, in one run
  • cargo gates — all 8 gates pass, 11.5s
  • cargo ci — full local mirror, exit 0
  • License gate negative-tested both directions: header-less .rs and .py probes both caught, clean tree green
  • shellcheck clean; all 8 workflow files parse
  • First CI run on this branch: all 6 checks green

Caveat on the wall-clock claim: that run was fully cold — caches had just been purged, and PRs deliberately save nothing. Test came in at 161s vs a 150s baseline and Python Wheel at 209s vs 165s. That should invert once this merges and main warms the new caches, but the warm case is projected, not measured.

Notes, not blockers

  • The disabled engine suite is the real coverage gap and is untouched here by agreement — 1396 tests behind a documented parallel-run flake, plus the DRM-probe hang and fd-capture flakes. cargo-nextest (no config exists yet) is the natural vehicle, since per-test process isolation addresses that flake class directly. Worth its own ticket.
  • sdk/streamlib-python-wheel/.venv-pyright looked unignored while fixing the license walk, but uv venv writes a .gitignore containing * inside each venv it creates, so it self-ignores. Non-issue.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added consolidated commands for running source checks and the complete local CI gate suite.
    • Added automated license-header validation for Rust and Python files.
    • Added unified CI workflows for source gates and repository-level checks.
  • Improvements

    • CI now cancels outdated pull-request runs, uses shared caching, and skips documentation-only changes.
    • Test and build workflows now use consistent timeouts and faster, more predictable compilation settings.
  • Maintenance

    • Consolidated and removed redundant standalone CI workflows.

Thirteen jobs fired on every push. Seven of them were source-walking xtask
gates, and each one spent ~26s compiling the xtask binary and ~13s compiling
its test binary in order to do a few seconds of file walking. They also shared
a single cache key, so they raced and six of every seven saves were discarded.

`cargo xtask check-all-source-gates` runs all eight gates in one process and
reports every failure before exiting, so one job still surfaces every breakage
at once. The per-gate subcommands stay for narrowing a failure down locally.
One `cargo test -p xtask` replaces eight filtered subsets of the same 178-test
binary. License headers and the ship-change gate tests — bash and git only —
share a second runner. Thirteen checks become six.

The cache was a net loss, not a win. Repo usage stood at 11.76 GB against
GitHub's 10 GB cap, so every save evicted a live entry, and restoring the
3.72 GB `cargo-test` entry took 66s to serve 53s of tests. Two causes: caching
bare `target/`, which keeps workspace artifacts that are invalid on the next
commit, and ref-scoped saves — three near-identical 1.97 GB wheel entries
existed at once, one for main and one for each open PR, none readable by the
others. Swatinem/rust-cache keeps dependency artifacts only, and `save-if` on
main means PRs restore and never mint their own.

No workflow cancelled a superseded run, so three pushes to a PR ran three full
suites and nobody read the first two. Every PR-triggered workflow now has a
concurrency group. The two expensive jobs skip prose-only changes; the source
gates deliberately do not, since they scan `docs/` and Markdown.

`cargo ci` runs what CI runs, so a green local run predicts a green PR. That
needed the license check to leave inline workflow YAML — inline bash is by
construction something nobody can run before pushing — so it is now
`scripts/check-license-headers.sh`, shared by both. It discovers files with
`git ls-files` rather than `find`: a `find` matches CI's clean checkout but
reports 15k false positives on a developer machine, where venvs, uv caches and
build output sit in the tree.

Incremental compilation is off. Its artifacts had reached 419 GB of an 892 GB
`target/` — cargo keeps a separate incremental tree per crate per profile and
never collects across them, so on a workspace this wide the disk churn costs
more than the rebuild it saves. Delete `incremental = false` to get it back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 674dd90a-1a61-4334-a66e-a2aac27b0b2c

📥 Commits

Reviewing files that changed from the base of the PR and between 25a73f8 and 9eefef1.

📒 Files selected for processing (6)
  • .cargo/config.toml
  • .github/workflows/python-wheel.yml
  • .github/workflows/source-gates.yml
  • .github/workflows/test.yml
  • scripts/check-license-headers.sh
  • xtask/src/check_no_inventory_submit.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • .cargo/config.toml
  • .github/workflows/python-wheel.yml
  • scripts/check-license-headers.sh
  • .github/workflows/source-gates.yml
  • .github/workflows/test.yml

📝 Walkthrough

Walkthrough

The PR consolidates source and local CI gates through xtask, adds Cargo aliases, replaces standalone gate workflows with unified workflows, and updates workflow filtering, concurrency, incremental compilation, and caching.

Changes

CI gate consolidation

Layer / File(s) Summary
Source gate aggregation
xtask/src/main.rs
Registers eight source gates and reports all failures after execution.
Local CI orchestration
xtask/src/main.rs, .cargo/config.toml
Adds check-all-source-gates and run-local-ci-gates, with Cargo aliases and aggregate failure handling.
Unified gate workflows
.github/workflows/source-gates.yml, .github/workflows/repo-gates.yml, scripts/check-license-headers.sh
Adds unified source and repository gate workflows and license-header validation.
Workflow runtime policies
.github/workflows/*.yml
Adds path filters, concurrency cancellation, disabled CI incremental compilation, revised Rust caching, credential-free checkout, and locked tests. Also removes standalone gate workflows.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Developer
  participant Cargo
  participant xtask
  participant SourceGates
  participant LicenseCheck
  Developer->>Cargo: cargo run-local-ci-gates
  Cargo->>xtask: run-local-ci-gates
  xtask->>SourceGates: run all source gates
  SourceGates-->>xtask: aggregated gate results
  xtask->>LicenseCheck: run license-header check
  LicenseCheck-->>xtask: validation result
  xtask-->>Developer: aggregate CI result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: consolidating source gates into one job and improving CI caching.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/consolidate-gates-and-fix-caching

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/source-gates.yml:
- Line 54: Update the actions/checkout@v4 step in the source-gates workflow to
set persist-credentials to false, matching the configuration used in
repo-gates.yml while preserving the existing checkout behavior.

In `@scripts/check-license-headers.sh`:
- Around line 66-72: Update the Rust invocation of report_files_missing_header
to scan the workspace-wide `**/*.rs` pathspec, including `xtask/**/*.rs`, while
retaining exactly the three existing `vendor/tatolab-vulkanalia*` exclusions and
the current Rust failure handling.
- Around line 39-51: Update the header validation loop in the license-check
script to validate both required header lines, including the exact
SPDX-License-Identifier: BUSL-1.1 line, rather than checking only
expected_header. Ensure Rust files without either required line are added to
missing_files while preserving the existing shebang/header positioning behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2c6f4b5c-7d46-4eaa-9819-20ee423fdbba

📥 Commits

Reviewing files that changed from the base of the PR and between 5d28763 and 878deb6.

📒 Files selected for processing (18)
  • .cargo/config.toml
  • .github/workflows/check-boundaries.yml
  • .github/workflows/check-device-wait-idle.yml
  • .github/workflows/check-no-escalate-in-lifecycle.yml
  • .github/workflows/check-no-in-process-placement.yml
  • .github/workflows/check-no-inventory-submit.yml
  • .github/workflows/check-no-unbounded-cstr-from-ptr.yml
  • .github/workflows/check-ship-change-removed-gate.yml
  • .github/workflows/license-check.yml
  • .github/workflows/lint-logging.yml
  • .github/workflows/pr-title.yml
  • .github/workflows/python-wheel.yml
  • .github/workflows/repo-gates.yml
  • .github/workflows/schemas.yml
  • .github/workflows/source-gates.yml
  • .github/workflows/test.yml
  • scripts/check-license-headers.sh
  • xtask/src/main.rs
💤 Files with no reviewable changes (9)
  • .github/workflows/license-check.yml
  • .github/workflows/check-no-unbounded-cstr-from-ptr.yml
  • .github/workflows/check-no-escalate-in-lifecycle.yml
  • .github/workflows/check-ship-change-removed-gate.yml
  • .github/workflows/check-device-wait-idle.yml
  • .github/workflows/check-no-in-process-placement.yml
  • .github/workflows/check-boundaries.yml
  • .github/workflows/lint-logging.yml
  • .github/workflows/check-no-inventory-submit.yml

Comment thread .github/workflows/source-gates.yml
Comment thread scripts/check-license-headers.sh Outdated
Comment on lines +66 to +72
report_files_missing_header \
"// Copyright (c) 2025 Jonathan Fontanez" Rust \
'runtime/*.rs' 'sdk/*.rs' 'adapters/*.rs' 'vendor/*.rs' 'examples/*.rs' \
':(exclude)vendor/tatolab-vulkanalia/*' \
':(exclude)vendor/tatolab-vulkanalia-sys/*' \
':(exclude)vendor/tatolab-vulkanalia-vma/*' ||
failed_language_checks+=("Rust")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Scan all Rust source paths.

The Rust pathspec list excludes xtask/**/*.rs. A new Rust file in xtask/ can merge without the required header. Use a workspace-wide Rust pathspec, then retain the three exact vendored-tree exclusions.

Proposed fix
 report_files_missing_header \
   "// Copyright (c) 2025 Jonathan Fontanez" Rust \
-  'runtime/*.rs' 'sdk/*.rs' 'adapters/*.rs' 'vendor/*.rs' 'examples/*.rs' \
+  ':(glob)**/*.rs' \
   ':(exclude)vendor/tatolab-vulkanalia/*' \

As per coding guidelines: **/*.rs requires the BUSL copyright and SPDX header for every new Rust file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check-license-headers.sh` around lines 66 - 72, Update the Rust
invocation of report_files_missing_header to scan the workspace-wide `**/*.rs`
pathspec, including `xtask/**/*.rs`, while retaining exactly the three existing
`vendor/tatolab-vulkanalia*` exclusions and the current Rust failure handling.

Source: Coding guidelines

…ease

Two corrections after measuring what the previous commit only asserted.

Disabling incremental compilation was wrong. The 419 GB in
`target/debug/incremental` was real, but it was months of accumulation across
crates, profiles and feature permutations — not a per-build cost. Measured on
a one-line edit to streamlib-engine followed by `cargo check -p
streamlib-engine`: 3.0s incremental, 8s without, with one crate's incremental
state settling at ~255 MB. Trading a 2.7x slower edit loop for disk that
`rm -rf target/debug/incremental` reclaims in seconds is a bad deal. CI keeps
CARGO_INCREMENTAL=0 in the workflows, where it is unambiguously right: a runner
never rebuilds after an edit.

The gates now run in release locally. They syn-parse ~7k files, so the profile
dominates — 11s release against 24s debug. That is the difference between a
pre-commit check that gets run and one that gets skipped. CI deliberately does
not mirror it: its job already compiles xtask in debug for `cargo test -p
xtask`, and a second profile there would cost more compile time than the 13s it
saves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.cargo/config.toml:
- Around line 8-11: Update the ci and gates aliases in .cargo/config.toml to
pass --locked to their cargo commands, matching the locked dependency behavior
used by the CI workflow while preserving the existing xtask subcommands.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 19b1f7ed-7803-4919-a70a-0ca0cec4cf77

📥 Commits

Reviewing files that changed from the base of the PR and between 878deb6 and 25a73f8.

📒 Files selected for processing (1)
  • .cargo/config.toml

Comment thread .cargo/config.toml Outdated
Four review findings, all verified against the tree before applying.

The licence gate only ever checked the copyright line, so a file carrying it
without `SPDX-License-Identifier: BUSL-1.1` passed — and SPDX is the half a
licence scanner actually reads. Both lines are now checked, as whole lines. No
existing file fails either check, so this is pure hardening.

Its Rust pathspec named runtime/ sdk/ adapters/ vendor/ examples/, which
silently exempted `xtask/` and `tools/`. Widening it to every tracked `.rs`,
minus the three vendored vulkanalia dirs, immediately found
`xtask/src/check_no_inventory_submit.rs` carrying a `2026` copyright line —
CLAUDE.md and .claude/rules/licensing.md both mandate `2025` verbatim, with no
year-varying provision, so the file is corrected to match the rule.

The PR-triggered workflows compile and run PR-authored code — build scripts,
proc macros, test code — while `actions/checkout` leaves the job token in
`.git/config` where any of it can read. All five checkout steps across
source-gates, test and python-wheel now set `persist-credentials: false`;
repo-gates already did. No workspace member has a git dependency, so nothing
here needed credentials (the only git deps in the repo are public ones in
`packages/clap`, which these jobs never build).

`cargo ci` and `cargo gates` omitted `--locked` while the workflows use it, so
they could resolve a different dependency graph or quietly rewrite Cargo.lock
and go green on a build the PR would not reproduce — defeating the one promise
those aliases exist to make.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tato123
tato123 merged commit 9ac207c into main Aug 12, 2026
9 checks passed
@tato123
tato123 deleted the ci/consolidate-gates-and-fix-caching branch August 12, 2026 21:13
tato123 added a commit that referenced this pull request Aug 13, 2026
* refactor(engine)!: unique names are minted, never read off a wall clock

Sixteen sites reached for `SystemTime::now()` to mint a unique name — iceoryx2
service names in `/dev/shm`, pipeline-cache temp files, test socket paths, a
runtime-name seed. None of them wanted a timestamp; they wanted a string no
concurrent process and no earlier run could collide with. Reading a wall clock
for that is both wrong in kind and weaker in practice: two mints in the same
nanosecond produced the same name.

`mint_machine_global_unique_name_suffix()` is the one primitive for the job —
pid (concurrent processes) + `MediaClock::now()` (runs on one boot, so a
recycled pid cannot resurrect a stale `/dev/shm` entry) + a sequence (two mints
in one nanosecond). It carries no separator a path or a file name would reject,
so callers compose it into their own naming convention rather than the mint
guessing at one.

Not the mint:
- `streamlib-surface-client`'s test sockets take a `TempDir` each. The kernel
  answers uniqueness and the socket is unlinked on drop rather than littering
  /tmp. That crate reaches no engine code, and a second local mint would be a
  parallel abstraction.
- The api-server's runtime-name seed takes the OS CSPRNG it already links for
  bearer tokens, falling back to the pid with a warning rather than failing a
  runtime over a display name. Wall clock ^ pid was the weaker entropy source.
- `apple/time.rs::system_time_to_ns` is deleted. Nothing imports `apple::time`,
  and a wall-clock-to-nanoseconds converter is the exact shape the
  one-monotonic-clock ADR retires.

Behavior is unchanged everywhere; only the source of the unique part moves.

Refs #1728

* feat(ci): check-clock-usage enforces the wall-clock allowlist

The plan permits wall clock on exactly four observability surfaces and requires
monotonic everywhere else. Prose alone does not hold that line: `SystemTime::now()`
is the reflexive spelling for "what time is it", and a wall-clock value that
reaches the data plane is indistinguishable from a media timestamp until someone
subtracts one from the other.

`cargo xtask check-clock-usage` scans `runtime/ sdk/ adapters/ xtask/` for
wall-clock reads in Rust and Python and fails on any outside the permitted files.
It is an entry in `ALL_SOURCE_WALKING_GATES`, so the existing `source-gates` job
runs it — #1857 consolidated the per-gate workflows, and this ticket's "plus its
workflow" predates that.

What keeps the list from growing by accident:
- No per-line pragma and no opt-out attribute. The file allowlist is the only way
  past, and every entry names one of four `ObservabilitySurface` variants — so a
  fifth surface means adding a variant, which is not something anyone does while
  reaching for a convenient timestamp.
- Every entry must still read a wall clock. A surface that moves leaves a licence
  behind on a path some later change would inherit; the gate fails instead.
- Every scan root and every language arm must read files. A gate whose tree moved
  out from under it reports clean, which is the failure mode that matters most.

Discovery is `git ls-files`, not a filesystem walk: `sdk/streamlib-python-wheel`
carries `.venv-pyright` with ~6000 third-party sources that are not ours to gate.
Prose is blanked before scanning, so the wheel's own `clock.py` — whose module
docstring names `time.time` / `datetime.now` / `time.time_ns` precisely to warn
readers off them — is not a violation.

Also corrects `RuntimeLogEvent::host_ts`, whose doc read "Host monotonic receipt
timestamp, nanoseconds since UNIX epoch" — self-contradictory, and exactly the
confusion this gate exists to prevent.

Closes #1728

* docs(plan): record what the check-clock-usage guard actually landed as

Two claims in the one-monotonic-clock change file were open or falsified, both
derivable from the tree now: the guard has no workflow of its own (#1857
consolidated them) and no `Date.now` arm (no JS/TS source exists), and the
unique-name uses the recon left "final at implementation" all converted rather
than any joining the allowlist. Also records where the epoch-parity test landed —
as the host + wheel pair the bullet itself anticipated, not one cross-process test.

Records, not decisions: no DECIDED or OPEN entry moves.

Refs #1728

* fix(ci): the clock gate refuses prose it cannot delimit

Review found the one hole that mattered: `blank_out_python_prose` tracked
triple-quoted spans across lines and threw the state away at EOF, so a span that
never closed blanked every line after it and the file scanned clean. `files_scanned`
still incremented, so neither read-source check noticed. That is this gate's only
failure mode — it ships with no per-line pragma, so silent blindness is the whole
risk surface — and the sibling `check-no-in-process-placement` already guards the
identical hazard for `~~` spans.

`blank_out_prose` now returns `Result`, and the Python arm refuses an unterminated
span naming the file. Two tests cover it: the blanker's own error, and a scan that
fails rather than reporting a file with a live `time.time_ns()` as clean.

Also from review:
- `SCAN_EXEMPT_FILES` replaces the single hardcoded gate-source path, so the next
  file that legitimately spells a banned pattern in source text is a reviewed list
  entry rather than a redesign.
- The allowlist-liveness check no longer builds a violation list and a String per
  hit to answer a boolean.
- Tests take the workspace root from `CARGO_MANIFEST_DIR` like the three sibling
  gates, instead of spawning `cargo locate-project` twice.
- The api-server name fallback drew both indices from a u64 whose high half is
  zero when seeded from a pid, pinning every degraded runtime to the same noun.
- Dropped three comments that narrated the change rather than explaining the code,
  per `.claude/rules/comments.md`.

The adapter-cuda test takes a `TempDir` rather than reaching into
`streamlib_engine::*` for the mint — `check-boundaries` rejects a consumer-tree
test importing engine internals directly, and a socket path wants the kernel's
uniqueness answer anyway.

Refs #1728

* fix(engine): the mint rides the uuid the engine already links

Review falsified the search that justified this module: `streamlib-engine` has
carried `uuid` with `v4` since before this branch, and `pubsub/integration_tests.rs`
already minted an iceoryx2 service name with `Uuid::new_v4()`. Adding a second
answer to "give me a unique service name" is the parallel abstraction the engine
doctrine exists to prevent, and the search that should have caught it was mine.

`mint_machine_global_unique_name_suffix()` is now `<pid>-<uuid>`. A v4 UUID's 122
random bits make every mint distinct across processes, runs and reboots on their
own, so the monotonic read and the sequence counter are both gone. That also
retires a hazard the composite carried: the monotonic component resets on reboot
while pids recycle, so a crash-left `/tmp` temp file plus a post-reboot pid
collision at a similar offset was a real, if unlikely, collision the old doc
claimed was impossible. The pid prefix stays, now honestly documented as
diagnostic only — it is what names the process that left a stale entry.

The one pre-existing service-name mint in `pubsub/integration_tests.rs` moves onto
the helper, so the tree has one idiom for this concern rather than two. The other
`Uuid::new_v4()` sites mint runtime ids and handle ids — a different concern, left
alone.

Review also showed the old `successive_mints_never_repeat` locked nothing: it
passed with the counter replaced by a constant and with the clock replaced by a
constant, because any one component makes 10k in-process mints distinct. Its
message asserted a premise the evidence disproved. The new
`the_uuid_component_is_what_differs_between_two_mints` strips the fixed pid prefix
and compares what is left, so it fails if the random component is ever dropped.

Refs #1728

* fix(ci): the clock gate sees the syscall a session would actually copy

Review planted four wall-clock spellings the gate walked past. The load-bearing
one: `libc::clock_gettime(libc::CLOCK_REALTIME, ..)`. The engine's own canonical
clock read is `libc::clock_gettime(libc::CLOCK_MONOTONIC, ..)` in
`core/media_clock.rs` — the file this gate's failure message points readers at —
so the likeliest way to introduce a wall-clock read here is to copy that line and
flip one token, which the gate could not see. `CLOCK_REALTIME` has zero hits in
the tree, so banning it in both language arms cannot false-positive. Python has
the mirror hole via `time.clock_gettime_ns(time.CLOCK_REALTIME)`, and the existing
`accepts_the_monotonic_spellings` test enshrined that call without ever reading
its clock-id argument. `UNIX_EPOCH.elapsed` joins them.

The other two evasions — a call split at the `::`, and a wall clock renamed at the
import — are the accepted floor of a substring scan, now stated in the module doc
next to the trailing-comment limit rather than left for the next reader to
discover.

`packages/test-fixtures` joins the scan roots. It is engine-side test
infrastructure compiled into the engine's own runs, not a consumer, and its 9
processors stamp data. It is clean today so the addition is free. The scan-root
doc now names why `packages/escalate` and `packages/core` stay out (schemas only,
no source to read) instead of implying the whole of `packages/` is downstream.

Also: the api-server's degraded naming path took both indices from disjoint halves
of a seed that is a bare pid on that path, and a pid is under 2^16 on any host with
the kernel-default `pid_max` — so `>> 32`, and then `>> 16`, both pinned every
degraded runtime to one noun. The seed is diffused through a hasher before
indexing, as it was before this branch touched it.

Corrects two facts in the change-file record: 16 sites span 15 files, not 13, and
the cuda adapter's round-trip test took a `TempDir` too.

Refs #1728

* fix(engine): test sockets take a TempDir, not a long unique name

CodeRabbit caught a regression this branch introduced. `sun_path` is 108 bytes,
and moving these paths onto `<pid>-<uuid>` cost 18 of them: headroom under a
default `/tmp` fell from 31 bytes to 14, so a `TMPDIR` longer than 15 bytes now
fails a bind that used to fit in 46. CI is unaffected — Actions leaves `TMPDIR`
unset — but the failure it would produce is an opaque bind error, not a message
naming the cause.

Both remaining socket sites take a `TempDir`, which is what the surface-client
and cuda-adapter tests already did: the path is ~35 bytes with 72 to spare, the
socket is unlinked on drop, and uniqueness is the kernel's answer rather than a
name we compose. That leaves one idiom for a test socket path across all four
sites, and the mint keeps the two namespaces that have no length limit —
iceoryx2 service names and pipeline-cache file names.

Also from that review:
- The mint's docs claimed a collision was impossible. 122 random bits make one
  vanishingly improbable, which is a different and true statement.
- The version test asserted only that the suffix parses, so a timestamp-based
  UUID would have satisfied it — and a timestamp-based UUID is a wall-clock read,
  the one thing this ticket exists to keep out of the tree. It now asserts v4.
  The `uuid` dependency enabling only `v4` is the guard that fires first, via the
  compiler; this is the one that survives another feature being switched on.
- `--help` for `check-clock-usage` listed four scan roots, not five.

Refs #1728
tato123 added a commit that referenced this pull request Aug 17, 2026
* ci(ops): fmt and clippy are enforced again, and docs/logging.md stops claiming they already were

PR #1857 consolidated 13 workflow jobs into 6 and retired
`lint-logging.yml`. Since then **no workflow has run `cargo fmt` or
`cargo clippy`** — while `docs/logging.md` went on stating that
"`cargo clippy --workspace` fails on any violation" and that a PR is
merge-blocked by a workflow that no longer exists. Two `eprintln!` calls sat
behind that claim, in the OpenGL and Skia adapter test helpers.

**Placement follows cost.** `cargo fmt --all --check` compiles nothing, so it
rides `source-gates.yml`, the job that already has a toolchain and no engine
build dependencies. `cargo clippy` has to compile the workspace, so it rides
`test.yml`'s Linux job, which already carries those dependencies and a warm
cache. No new workflow — the consolidation stands.

**Default targets only, deliberately.** `--all-targets` would deny `println!`
in tests, which `xtask lint-logging` exempts on purpose by skipping `tests`
directories. Scoping clippy the same way keeps the two layers saying the same
thing.

**The two helpers get an allow, not a rewrite.** Both are `[[bin]]` fixtures a
harness spawns and reads stderr from, with no tracing subscriber installed —
`tracing::error!` there would go nowhere, so stderr is the mechanism rather
than a lapse. That is also why the layers disagreed: a `[[bin]]` whose `path`
points into `tests/` is a default target to clippy and an exempt path to the
AST walk. The doc now says so.

Both gates were checked to actually fail: a stray edit trips rustfmt, and
removing an allow trips clippy. The rustfmt gate caught its own author's
formatting on the first run.

`cargo xtask run-local-ci-gates` covers all three, so this stays runnable
before pushing rather than only in CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci(ops): the skia allow narrows to the two functions that write, and the doc stops overclaiming

Both from CodeRabbit on #1888, both fair.

The skia helper had a file-level `#![allow]` where the OpenGL one had a
function-scoped `#[allow]` — inconsistent, and the blanket would have let a
`println!` added anywhere else in that file through unchecked. Now scoped to
`die` and `crash_mid_skia_write`. Verified it still bites: a stray `println!`
in `run()` fails clippy with the narrowed allow in place.

And `run_local_ci_gates` cannot run layer 3 — the runtime fd2 capture is a
property of the running host, not a gate — so "runs all three" was wrong in the
same doc I was correcting for overclaiming. Fixed rather than left ironic.

Not taken: "afterwards" → "afterward". No locale is configured (there is no
`.coderabbit.yaml`), and the `-wards` forms are already used across
`ARCHITECTURE.md`, `docs/logging.md` and the change archive, so the change
would introduce the inconsistency it claims to remove.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci(ops): clippy excludes the skia adapter, which no CI job has ever been able to build

The new clippy step went red on its first CI run, and not on a lint:
`skia-bindings` 404s on its prebuilt download and then needs FreeType headers
to build from source.

The cause is that this step is the first `--workspace` anything in the repo —
every other cargo invocation in every workflow is `-p`-scoped, so no runner has
ever built that crate. Adding FreeType would make CI build Skia from source for
the first time, which is minutes of build and a new failure surface, to lint
one adapter. That is its own decision, not a side effect of turning clippy on.

The local aggregator takes the same exclusion. A local gate that lints more
than CI is a gate whose result nobody can act on.

Consequence, stated rather than buried: `streamlib-adapter-skia` is linted by
nothing. That was already true before this PR — it is now visible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant