You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
refactor(platform): enforce one host selector and structured native boundaries with Dylint #152
Adopt the structured host-platform boundary used by Soldr and zccache instead of scattering #[cfg(target_os = "linux")], cfg!(windows), and native API selection throughout ordinary kernal-api modules. This is an architecture and enforcement change, not a cosmetic substitution of cfg syntax.
Precedents inspected:
Soldr #2493: one exhaustive Rust 1.95 cfg_select! selector, neutral capability facade, private concrete OS trees, and generic caller tests.
This repo already has the core architecture: src/lib.rs contains one exhaustive Windows/Linux/macOS cfg_select!, selecting platform_imp from platform_win, platform_linux, or platform_macos. src/platform.rs and src/platform/** provide semantic capabilities. Rust 1.95.0 is already pinned. Do not add another HAL or a new platform crate.
The enforcement gap is explicit: dylints/kernal_api_platform_boundary/src/lib.rs::check_item returns immediately for the kernal-api package via current_package_is_facade_owner(), and in_scope() also excludes /kernal-api/src/. The client rule therefore does not enforce the owner's internal source boundary. The current detector masks strings/comments and searches text; simply removing those exemptions is not enough to ensure complete structural coverage, particularly for cfg-elided out-of-line modules.
Examples of current migration touch points include host selection in src/tauri.rs, src/wasm/worker_protocol.rs, src/wasm/worker/test_support.rs, tests/wasm_worker_containment.rs, and the ongoing Linux capture adapter under src/tauri/capture_linux.rs on the #17/#19 development branch. Inventory the actual merge-base/source state before generating a baseline; branch-only examples are not assertions that all these changes are on main.
Proposal
One selector; capability-oriented private implementations
Retain and enforce this existing shape:
src/lib.rs one exact cfg_select! host selector
src/platform.rs host-neutral capability index
src/platform/** host-neutral capability facade
src/platform_win.rs private Windows implementation index
src/platform_win/** Windows mechanics and native tests
src/platform_linux.rs private Linux implementation index
src/platform_linux/** Linux mechanics and native tests
src/platform_macos.rs private macOS implementation index
src/platform_macos/** macOS mechanics and native tests
The selector has explicit Windows/Linux/macOS arms, no _ fallback, no broad Unix selection, and no permanent platform_unix tree. Unsupported host OSes fail compilation.
Keep host-neutral, cfg-free helpers shared where useful. Place native webview capture under the selected concrete capability trees (WebView2/CapturePreview, WKWebView/takeSnapshot, WebKitGTK snapshot), not independent selectors in tauri.rs or parallel subsystem-specific OS trees.
Ordinary modules call neutral crate::platform::* capabilities. Permit only the narrowly specified crate-root selection/re-export bridge to name platform_imp; neutral facade leaves can delegate through that bridge, not import concrete trees. Native implementation roots may name their own implementation details.
Preserve the existing process/fs/ipc/executable/host and additional kernel capability vocabulary; do not add placeholder APIs, duplicate native implementations, runtime platform objects, or fallback backends.
Architecture-specific implementation selection belongs inside the already selected concrete OS tree. Keep all six supported combinations: Linux/macOS/Windows × x86-64/ARM64.
Host selection is distinct from guest ABI, parsed target-triple policy, and runtime capability availability. In particular, a wasm32 guest build is not an unsupported native host, and a Linux-hosted Windows cross-build must not select Windows host mechanics.
Feature gating remains valid and must preserve default = []. Cargo target dependency tables remain valid; this rule is not a ban on target-qualified manifest dependencies.
Extend the existing Dylint, including owner code
Make kernal_api_platform_boundary enforce both sides: downstream callers use kernal-api, and kernal-api's ordinary code uses its own neutral platform boundary. Retain the complementary backend/public-signature rule; do not treat this source scan as a replacement for resolved type checking.
Required source coverage:
Parse pre-expansion Rust syntax and nested macro token trees rather than relying on substring/regex matches. Detect #[cfg], inner cfg attributes, cfg_attr, cfg!, and extra/malformed host-selecting cfg_select! invocations.
Recognize windows, unix, target_os, target_family, target_arch, target_env, target_abi, target_vendor, target_endian, and target_pointer_width, including nested all/any/not and mixed feature/host conditions.
Cover public/private items, imports, expressions, inline modules, and cfg-elided out-of-line modules. Discover source roots from Cargo metadata and source/module traversal; a Linux lint pass must inspect Windows/macOS source rather than waiting for a compiled check_item callback from that file.
Reject concrete-tree/platform_imp access and raw native API paths outside approved implementation locations, including renamed imports, absolute paths, and macro-wrapped uses. Include std::os, libc, Windows SDK, and native GUI handles as appropriate; do not accidentally ban ordinary host-neutral third-party implementation use permitted by the existing facade policy.
Validate the root selector structurally, not by giving all of src/lib.rs a blanket exemption. Path classification must not depend on checkout directory spelling or trust a package-name bypass.
Permit feature/test/docs/debug/lint-only cfgs that do not contain host predicates. Comments/string literals must not create false positives.
Cover hand-written unit/integration tests, examples, benches, and worker binaries. Move genuinely native tests beside concrete implementations; generic caller tests must exercise the same semantic facade on every host. Test cfg alone is not a blanket exemption.
Explicitly classify generated Wasm bindings/guest artifacts, vendored code, compiler-target fixtures, build tooling, and lint UI fixtures. These must not become an unbounded escape hatch for handwritten native application code. Document the exact exclusions and test their path handling.
Migration and gate wiring
Record RED tests for the current owner-package bypass, private/expression cfg, inactive out-of-line source, direct native imports, and concrete references.
Inventory all violations and install a temporary exact-occurrence baseline. Each record identifies source path, syntax kind, normalized construct, and stable occurrence identity. No file/directory/package wildcard waivers.
Reject new, duplicate, and stale baseline entries immediately, including a second violation added to an already-baselined file. The count may only decrease. Moving/renaming a file must not hide an occurrence.
Migrate by capability, retaining runtime behavior, output atomicity, resource lifetime, and cancellation semantics. Keep index files small and split concrete implementations rather than dumping them into OS roots.
Wire the lint, its UI tests, and repository acceptance tests into the existing .github/workflows/ci.yml Dylint lane and documented local Soldr commands. Keep the separately pinned Dylint nightly separate from the Rust 1.95 product MSRV. Do not add a second runtime checker or duplicate lint stack.
Completion requires zero remaining boundary debt and removal of the migration baseline and owner-wide exemptions.
Agent and architecture documentation is part of delivery
Update AGENTS.md with the one-selector rule, allowed concrete paths, neutral caller pattern, host-vs-guest/build-target distinction, feature/test cfg exceptions, and mandatory Dylint verification. Link to one canonical detailed boundary document rather than copying the whole design into multiple files.
No root CLAUDE.md or agent.md was found in the inspected checkout. Add a concise root CLAUDE.md that points to AGENTS.md and the canonical boundary guide; update any additional agent-guidance files discovered during implementation. Do not create a competing lowercase agent.md policy beside AGENTS.md.
Update ARCHITECTURE.md, DYLINT.md, and platform/capture documentation so their claims match actual owner/client coverage and the final layout. Include allowed/forbidden examples, exact local lint/UI commands, migration-baseline rules, and instructions for adding a capability without introducing another selector. Correct the present mismatch between broad DYLINT.md coverage wording and the owner exemption.
Acceptance criteria
RED → GREEN evidence demonstrates the owner bypass and each forbidden syntax/location family, including inactive out-of-line source, before implementation and after enforcement.
Exactly one structurally validated host selector remains; no unsupported/Unix fallback or second HAL exists.
Ordinary production and handwritten test code is host-neutral. Native mechanics and native tests live only in approved concrete trees; no decorative cfg-alias workaround merely hides scattered host selection.
Allowed feature/test/docs cfg and genuine guest/target data fixtures pass without weakening native-host rules.
The exact baseline rejects growth, duplicates, and stale entries; final baseline count is zero and the baseline is deleted.
Required CI runs the lint, UI tests, and source-discovery/ratchet tests; a violation added to an inactive Windows module fails a Linux-hosted enforcement run.
Linux/macOS/Windows × x86-64/ARM64 compile checks pass, with representative native behavioral tests on each OS family and a host-vs-requested-target regression.
Default-feature isolation, facade-owned public types, generated ABI compatibility, and existing worker/capture lifecycle guarantees are preserved.
AGENTS.md, CLAUDE.md, ARCHITECTURE.md, DYLINT.md, and canonical platform documentation consistently teach and link the enforced structure.
Migration PRs include focused Soldr validation and the final issue closes only after the complete end state is merged.
Decisions
P2 architecture/enforcement work: the user explicitly requested a tracked repo-specific version; this is not a claim of a new runtime outage.
Reuse this crate's existing platform HAL: unlike the upstream bootstrap issues, kernal-api already owns the selector and must not acquire another platform crate without measured justification.
Use the stronger Soldr test-boundary end state: handwritten tests should not perpetuate scattered host selection; native setup/assertions belong with concrete implementations.
Extend the current platform Dylint: preserve downstream enforcement while replacing owner-wide immunity with structural path rules.
Stage with a shrinking baseline, finish at zero: ongoing Wasm/Tauri work can continue through reviewable migrations without making exemptions permanent.
Documentation changes are required implementation deliverables: this filing does not itself claim those policies have already been implemented or updated.
Related issues
kernal-api #147: overlapping inactive-host enforcement concern for public signatures; coordinate, but do not close it merely because cfg placement is enforced.
kernal-api #77 (closed): canonical HAL ownership; this issue strengthens that existing architecture instead of recreating zccache-platform.
Soldr #2493/#2498 and zccache #1365/#1366 above are the direct architectural precedents.
Documentation slice merged in #244 at commit 79e0380. It adds the requested AGENTS.md and CLAUDE.md guidance, a canonical platform-boundary guide, and records the current Dylint owner/CI limitation. The AST/pre-expansion enforcement and temporary occurrence baseline remain open work in this issue.
Reconnaissance for whoever picks this up, because the first question is how big it is and that is measurable now.
The baseline
Host-selecting cfg invocations in this crate's own src/ — matching target_os, target_arch, target_family or target_env:
343 total
By file, largest first:
file
sites
in a test cfg
src/tauri.rs
34
0
src/snapshot/unwind.rs
31
4
src/crash/mod.rs
22
0
src/snapshot/mod.rs
21
0
src/snapshot/modules.rs
20
2
src/symbolize/split.rs
19
2
src/symbolize/resolve.rs
19
0
src/lib.rs
16
0
src/platform_win/**
20
0
remainder
~140
—
The column that matters is the third: these are production selections, not test scaffolding. The biggest offenders carry no test predicate at all, so this is not a case of tightening a few test gates — it is migrating host selection out of the profiler, snapshot, crash and symbolizer cores and into the concrete trees.
The counts are a grep, so they are an upper bound: they include the legitimate ones this issue allows — the single cfg_select! selector in src/lib.rs, selection inside src/platform_win/** and its siblings, and genuine test-only gates. They also include the arch dispatch in src/snapshot/unwind.rs, which is the case this issue explicitly rules on: "Architecture-specific implementation selection belongs inside the already selected concrete OS tree."
What that implies for sequencing
This is the largest item on the tracker and it is not a single change. Two things follow:
The detector has to land before the migration, not after. This issue asks for a structural, pre-expansion detector that also covers cfg-elided out-of-line modules, replacing the current text scan. That is what produces an accurate baseline; a grep cannot, because it cannot tell the one sanctioned selector from a violation, and it counts comments and strings.
The migration is per-module, ratcheted. Once the detector exists with an exact baseline, src/tauri.rs (34, no test cfgs) is the natural first ratchet step and src/platform_win/** should be excluded by location rather than by count.
What I did not do
I have not started either half, and I am recording this rather than beginning a refactor I cannot finish in one pass — a half-migrated src/tauri.rs or src/crash/mod.rs is worse than an unmigrated one, since the exemption currently makes the inconsistency invisible.
Context
Adopt the structured host-platform boundary used by Soldr and zccache instead of scattering
#[cfg(target_os = "linux")],cfg!(windows), and native API selection throughout ordinary kernal-api modules. This is an architecture and enforcement change, not a cosmetic substitution of cfg syntax.Precedents inspected:
cfg_select!selector, neutral capability facade, private concrete OS trees, and generic caller tests.This repo already has the core architecture:
src/lib.rscontains one exhaustive Windows/Linux/macOScfg_select!, selectingplatform_impfromplatform_win,platform_linux, orplatform_macos.src/platform.rsandsrc/platform/**provide semantic capabilities. Rust 1.95.0 is already pinned. Do not add another HAL or a new platform crate.The enforcement gap is explicit:
dylints/kernal_api_platform_boundary/src/lib.rs::check_itemreturns immediately for thekernal-apipackage viacurrent_package_is_facade_owner(), andin_scope()also excludes/kernal-api/src/. The client rule therefore does not enforce the owner's internal source boundary. The current detector masks strings/comments and searches text; simply removing those exemptions is not enough to ensure complete structural coverage, particularly for cfg-elided out-of-line modules.Examples of current migration touch points include host selection in
src/tauri.rs,src/wasm/worker_protocol.rs,src/wasm/worker/test_support.rs,tests/wasm_worker_containment.rs, and the ongoing Linux capture adapter undersrc/tauri/capture_linux.rson the #17/#19 development branch. Inventory the actual merge-base/source state before generating a baseline; branch-only examples are not assertions that all these changes are on main.Proposal
One selector; capability-oriented private implementations
Retain and enforce this existing shape:
_fallback, no broad Unix selection, and no permanentplatform_unixtree. Unsupported host OSes fail compilation.tauri.rsor parallel subsystem-specific OS trees.crate::platform::*capabilities. Permit only the narrowly specified crate-root selection/re-export bridge to nameplatform_imp; neutral facade leaves can delegate through that bridge, not import concrete trees. Native implementation roots may name their own implementation details.default = []. Cargo target dependency tables remain valid; this rule is not a ban on target-qualified manifest dependencies.Extend the existing Dylint, including owner code
Make
kernal_api_platform_boundaryenforce both sides: downstream callers use kernal-api, and kernal-api's ordinary code uses its own neutral platform boundary. Retain the complementary backend/public-signature rule; do not treat this source scan as a replacement for resolved type checking.Required source coverage:
#[cfg], inner cfg attributes,cfg_attr,cfg!, and extra/malformed host-selectingcfg_select!invocations.windows,unix,target_os,target_family,target_arch,target_env,target_abi,target_vendor,target_endian, andtarget_pointer_width, including nestedall/any/notand mixed feature/host conditions.check_itemcallback from that file.platform_impaccess and raw native API paths outside approved implementation locations, including renamed imports, absolute paths, and macro-wrapped uses. Includestd::os, libc, Windows SDK, and native GUI handles as appropriate; do not accidentally ban ordinary host-neutral third-party implementation use permitted by the existing facade policy.src/lib.rsa blanket exemption. Path classification must not depend on checkout directory spelling or trust a package-name bypass.Migration and gate wiring
.github/workflows/ci.ymlDylint lane and documented local Soldr commands. Keep the separately pinned Dylint nightly separate from the Rust 1.95 product MSRV. Do not add a second runtime checker or duplicate lint stack.Agent and architecture documentation is part of delivery
Update
AGENTS.mdwith the one-selector rule, allowed concrete paths, neutral caller pattern, host-vs-guest/build-target distinction, feature/test cfg exceptions, and mandatory Dylint verification. Link to one canonical detailed boundary document rather than copying the whole design into multiple files.No root
CLAUDE.mdoragent.mdwas found in the inspected checkout. Add a concise rootCLAUDE.mdthat points toAGENTS.mdand the canonical boundary guide; update any additional agent-guidance files discovered during implementation. Do not create a competing lowercaseagent.mdpolicy besideAGENTS.md.Update
ARCHITECTURE.md,DYLINT.md, and platform/capture documentation so their claims match actual owner/client coverage and the final layout. Include allowed/forbidden examples, exact local lint/UI commands, migration-baseline rules, and instructions for adding a capability without introducing another selector. Correct the present mismatch between broad DYLINT.md coverage wording and the owner exemption.Acceptance criteria
AGENTS.md,CLAUDE.md,ARCHITECTURE.md,DYLINT.md, and canonical platform documentation consistently teach and link the enforced structure.Decisions
Related issues