Skip to content

Adopt cross-repository rules generalized from tenferro-rs (first task of strided-rs umbrella #199) #6

Description

@shinaoka

Motivation

The 2026-08-02 audit of strided-rs (tensor4all/strided-rs#199, origin/main at 9da9b9f) measured large gaps against rules that today exist only in tenferro-rs REPOSITORY_RULES.md. Most of those rules are repository-neutral. Vendoring them into strided-rs would duplicate them; tenferro's own meta-rule says cross-repository rules belong here. Generalizing them into this repository makes them reusable by every tensor4all project (tenferro-rs, strided-rs, tensor4all-rs, chainrules-rs, and future crates).

This issue is the first task of the strided-rs umbrella: downstream repos adopt by reference, so the shared text must land before the per-repo rule updates.

Gap numbers from the strided-rs audit, as evidence these rules do not enforce themselves: 0 // INVARIANT: markers; // SAFETY: on 29 of 418 unsafe blocks; 33 of 35 #[allow] without rationale; # Errors on 9 of 172 public Result functions; 5 runnable doctests in all of src/; 53 inline #[cfg(test)] modules; 13 files over 1000 lines; a hidden thread_local! buffer pool with no bounds or clear(); a safe fn returning live uninitialized T via Vec::set_len.

Rules to generalize from tenferro-rs REPOSITORY_RULES.md

Proposed placement in parentheses; final layout up to the implementer.

  • Invariant markers and audit hygiene (common/): one canonical // INVARIANT: <why valid, bounded, or intentional> marker for non-obvious intentional invariants; #[allow(...)] requires an adjacent rationale; audit tooling and audit prompts must not flag a site governed by a marker without checking the stated invariant; rejected findings get a marker or source-contract test so the same non-bug is not rediscovered (false-positive ledger).
  • Unsafe hygiene (rust/): a // SAFETY: comment per unsafe block naming the validation site that proves it; keep unsafe next to its proof; focused tests for new unsafe branches.
  • Uninitialized and scratch acquisition (rust/performance.md): raw uninitialized acquisition must be an unsafe fn or return MaybeUninit-typed storage; expose a separate zeroed/initialized acquisition for read-before-write callers; never fix stale reads by unconditional zero-fill of a shared hot-path acquisition; do not zero-initialize buffers that are provably fully overwritten; regression coverage for both contracts.
  • Cache and pool ownership (common/performance.md or rust/performance.md): no hidden thread-local or global caches; every cache/pool has an owner, a bounded default, a way to configure the bound, clear(), and entry/byte introspection; document owner, lifetime, and accounting when adding one.
  • Public error documentation (rust/): every public function returning Result documents a # Errors section naming concrete failure conditions; intentional panics use # Panics. Reference implementation of the checker script can live here or be copied per repo.
  • Doctest policy (common/docs-and-tests.md, strengthen): doc examples must not use ignore or no_run; every example compiles and runs as a doctest; compile_fail only for intentional compile errors; crate READMEs with code fences need an executable sync mechanism (for example #![doc = include_str!("../README.md")]).
  • Unit test organization (rust/): production source files stay free of inline #[cfg(test)] blocks except tiny leaf modules; module-local suites live in src/<module>/tests/*.rs behind #[cfg(test)] mod tests;; crate-root tests/ is for integration tests; tests follow implementation ownership.
  • API evolution (common/repository.md): when a bug exposes an API design mismatch, fix the canonical contract; no try_* compatibility escapes beside a panicking canonical API; no deprecated panicking shims unless a maintainer explicitly requires a compatibility window.
  • Output-update naming principle (common/repository.md or rust/): the mutation semantics of an operation must be visible in its name; an _into-style overwrite name must not read the previous output value; accumulation and destructive in-place update get their own explicit vocabulary. Concrete suffix choices stay per-repo.
  • Typed errors, strengthened (rust/numerical.md): public validation APIs return crate error types, never String/&str; conversions preserve the source() chain; render to text only at display/logging/FFI boundaries.
  • Debug and enum hygiene (rust/): public types implement Debug (hand-written summaries where derived output would dump buffers or leak internals); deliberate #[non_exhaustive] policy for public enums that will grow.
  • File organization (common/repository.md): ~1000 lines as a soft review trigger, split only along real behavior/ownership boundaries, never part1/part2.
  • Complexity budget (common/performance.md): no accidental O(n^2) in planning/metadata/hot paths; intentional superlinear code carries an // INVARIANT: stating the bound.
  • Threading principles (rust/performance.md): one repository source of truth for parallel thresholds; short-circuit to the serial kernel when the effective thread count is one; library kernels must not reach for an ambient global thread pool outside the repository's declared policy mechanism; provider-owned threading (BLAS/OpenMP) is controlled by provider variables.

Adoption follow-ups (per repo, after this lands)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions