Skip to content

chore: release fp-library v0.16.0 / fp-macros v0.7.0#25

Closed
nothingnesses wants to merge 210 commits into
mainfrom
2026-04-03
Closed

chore: release fp-library v0.16.0 / fp-macros v0.7.0#25
nothingnesses wants to merge 210 commits into
mainfrom
2026-04-03

Conversation

@nothingnesses

Copy link
Copy Markdown
Owner

Summary

  • Release fp-library v0.16.0 and fp-macros v0.7.0.
  • Ref trait hierarchy (RefFunctor, RefSemimonad, etc.) with by-reference dispatch.
  • Brand inference system (InferableBrand auto-generated by trait_kind!/impl_kind!).
  • Val/Ref dispatch: 19 dispatch modules unifying by-value and by-reference operations.
  • 38 inference wrapper functions with automatic HM signature generation.
  • ContravariantDispatch for contramap.
  • Manual override for #[document_signature].
  • Inferred mode and ref qualifier for m_do!/a_do!.
  • Snapshot regression tests (19 per-file + 14 edge case).
  • Documentation updates for new module structure.

Test plan

  • just verify passes (fmt, check, clippy, doc, test).
  • All feature flag combinations compile (no-default-features, rayon, serde, stacker).
  • 291 fp-macros lib tests, all passing.
  • All 38 HM signatures verified correct in rendered docs.

Add lychee to the Nix dev shell and integrate offline markdown link
checking into the `just doc` recipe. Broken relative links in README
and docs now fail the doc check.

Update release.yml validate job to use `just verify` instead of raw
cargo commands, matching ci.yml and ensuring unicode, link, and doc
checks run consistently.

Fix README doc links to point to fp-library/docs/ after the docs
move.
The library contains no unsafe blocks. Forbidding unsafe at the crate
level prevents accidental introduction and documents this guarantee.
Design plan for extending the type class hierarchy with by-ref trait
variants (RefPointed, RefLift, RefSemiapplicative, RefSemimonad) and
marker-type dispatch to unify map/bind/apply free functions across
owned and borrowed containers. Enables Lazy to implement the full
Functor -> Applicative -> Monad chain.

Key decisions: independent traits (no sub/supertrait relationship),
Fn over FnOnce for by-ref closures, Clone bound only on RefPointed,
ref qualifier for m_do!, skip RefFoldable/RefTraversable initially.
Add marker-type dispatch infrastructure in functor_dispatch.rs that
routes a single `map` free function to either Functor::map or
RefFunctor::ref_map based on the closure's argument type. Val/Ref
marker types and a FunctorDispatch trait enable the compiler to infer
the correct dispatch target at compile time with no runtime cost.

Remove the separate `map` free function from functor.rs and
`ref_map` free function from ref_functor.rs. Update all call sites
from `map::<Brand, _, _>` to `map::<Brand, _, _, _>` (adding the
inferred Marker parameter). Update the a_do! macro codegen to emit
4 generic arguments. Fix benchmark imports to use functor_dispatch::map.

Update compile-fail test stderr for arc_coyoneda after associated
type bounds changes. Add #![forbid(unsafe_code)] to the crate.
Add benchmark compile check to just verify. Fix test cache find
paths to include fp-library/tests and fp-library/benches.

Add plan for by-reference trait hierarchy with unified dispatch,
documenting design decisions, POC results, and rejected alternatives.
Design plan for a DefaultBrand trait that maps concrete types back
to their brand, enabling turbofish-free calls like map_infer(f, xs)
for types with a single unambiguous brand (Option, Vec, Identity,
CatList, Lazy, Coyoneda variants, etc.). Purely additive; existing
turbofish code is unaffected. Types with multiple brands (Result,
Tuple2, Pair, ControlFlow) remain explicit.
Make inference-based functions the primary API (map, bind, apply)
with explicit-brand versions renamed to _brand suffix. Add POC step
to verify GAT equality bounds before implementation. Switch
impl_kind! DefaultBrand generation to opt-out (#[no_default_brand])
instead of opt-in. Drop pure_infer (return-type inference is
unreliable). Remove absolute filesystem paths from references. Add
section on interaction with ref-hierarchy dispatch extensions.
Add POC tests in functor_dispatch.rs confirming that DefaultBrand
trait enables turbofish-free map calls. Inference works for Option,
Vec (Val dispatch) and RcLazy (Ref dispatch). ArcLazy blocked on
SendRefFunctor dispatch from the ref-hierarchy plan.

Expand plan with DefaultBrand trait family using content-hash naming
to mirror Kind traits across all arities. This enables bimap
inference for bifunctor types (Result, Tuple2, Pair, ControlFlow)
that are ambiguous at arity 1 but unambiguous at arity 2. Add
trait_default_brand! and DefaultBrand! macros to the implementation
order. Add diagnostic::on_unimplemented for error messages. Use
_explicit suffix. Document incremental scope and parallel function
exclusion.
…o Fn

Change ref_map and send_ref_map trait method signatures from
FnOnce(&A) -> B to Fn(&A) -> B. This enables types like Vec to
implement RefFunctor (calling the closure per element) while Lazy
continues to work (calls it once). Update FunctorDispatch Ref impl,
Lazy and TryLazy trait impls to match.

Breaking change: closures that move out of captures (FnOnce but not
Fn) no longer compile with ref_map. These are rare and can be
restructured. This change is a prerequisite for the by-reference
trait hierarchy (RefApplicative, RefMonad, etc.).
Add three new by-reference type class traits enabling memoized types
to participate in the applicative/monadic hierarchy:

- RefPointed: construct a context from &A (requires A: Clone)
- RefLift: lift a binary Fn(&A, &B) -> C over two contexts
- RefSemimonad: sequence with Fn(&A) -> Of<B>

Implement all three for LazyBrand<RcLazyConfig>. ArcLazyConfig impls
require SendRef variants (SendRefPointed, SendRefLift, SendRefSemimonad)
due to Send + Sync bounds on ArcLazy::new, to be added in a follow-up.

Register new modules in classes.rs. Free functions (ref_pure,
ref_lift2, ref_bind) are auto-re-exported via generate_function_re_exports.
Mark Steps 3-4 as done (FnOnce->Fn change, RefPointed/RefLift/
RefSemimonad with RcLazy impls). Add open questions section covering
RefSemiapplicative integration with CloneableFn and whether RefMonad
can bypass RefApplicative. Recommend deferring RefSemiapplicative
and defining RefMonad as RefPointed + RefSemimonad directly.
Replace deferred RefSemiapplicative approach with CloneableFn<Mode>
design: add a default Mode parameter to CloneableFn/SendCloneableFn
using the Val/Ref markers from FunctorDispatch. This enables
RefSemiapplicative without new function wrapper traits, unblocking
the standard RefApplicative and RefMonad hierarchy.

Update implementation order to include CloneableFn Mode parameter
step and re-sequence remaining steps. Mark all open questions as
resolved.
Replace the naive CloneableFn<Mode> design with the ClosureMode
trait approach. Document the two obstacles (Deref bound on associated
type, Function: Category + Strong supertrait) and explain how
ClosureMode resolves them by parameterizing the Deref target.

Include fallback approaches (separate RefCloneableFn trait on
backup/ref-cloneable-fn branch, remove Deref bound entirely) in
case the ClosureMode GAT interaction doesn't compile. Add open
question about unsized GAT + Deref interaction verification.
Document the Callable/CloneableCallable/Arrow naming scheme for the
function wrapper trait hierarchy. Renames are deferred to Step 10
(after structural changes are verified) since they are mechanical
and independent of the ClosureMode design.

Callable<Mode> extracts the shared Deref bound. CloneableFn becomes
CloneableCallable. Function becomes Arrow (aligning with Haskell's
Arrow type class).
Use LiftFn/SendLiftFn as pre-rename names for the construction
trait (split from CloneableFn::new). Finalize post-rename names:
Function<Mode> (base callable), Arrow (composable), CloneFn
(cloneable), LiftFn (construction). The rename step reclaims
Function for the general concept and introduces Arrow for the
Category + Strong specialization.
Add ClosureMode trait with Val/Ref impls that parameterize the
Deref target of CloneableFn wrappers. CloneableFn<Val> wraps
Fn(A) -> B (default, unchanged for existing code). CloneableFn<Ref>
wraps Fn(&A) -> B (new, for RefSemiapplicative).

Split CloneableFn::new into a separate LiftFn: CloneableFn<Val>
trait because the closure parameter type depends on the mode and
cannot be expressed in a single trait method signature. Rename the
free function from cloneable_fn_new to lift_fn_new.

Remove Function supertrait from CloneableFn. CloneableFn's purpose
is cloneable+callable wrappers, not composability. Code needing
Category/Strong adds + Function explicitly.

Add coerce_ref_fn to UnsizedCoercible for constructing by-ref
wrappers. Implement for RcBrand and ArcBrand.

Convert many explicit class imports to wildcards to prevent future
import breakage when new traits are added to modules.
Mark Step 5 (ClosureMode + LiftFn split) as done. Add new Step 6
for CloneableFn<Ref> impl and SendCloneableFn parameterization.
Renumber remaining steps. Update completed changes section with
all ClosureMode-related work.
ClosureMode approach succeeded. Remove fallback approaches B and C.
Mark unsized GAT open question as verified.
…Ref>

Add SendTarget GAT to ClosureMode for Send+Sync closure targets.
Parameterize SendCloneableFn with Mode, defaulting to Val. Split
SendCloneableFn::send_cloneable_fn_new into SendLiftFn::new trait.

Add CloneableFn<Ref> and SendCloneableFn<Ref> impls for FnBrand<P>,
using P::CloneableOf and P::SendOf with Fn(&A) -> B targets. These
enable RefSemiapplicative to use CloneableFn<Ref> for by-ref
function wrappers without creating new traits.

Rename free function from send_cloneable_fn_new to send_lift_fn_new.
Update all call sites and regenerate compile-fail test stderr files.
Add RefSemiapplicative trait for applying wrapped by-ref functions
(CloneableFn<Ref>) within contexts. The wrapped functions receive
&A instead of owned A, so no Clone bound on elements is needed.

Implement for LazyBrand<RcLazyConfig>: evaluates both the function
and value lazily, then applies the by-ref function to produce the
result.
RefApplicative = RefPointed + RefSemiapplicative.
RefMonad = RefApplicative + RefSemimonad.

LazyBrand<RcLazyConfig> automatically implements both via the
blanket impls, completing the by-ref Functor -> Applicative -> Monad
chain for RcLazy. Document applicative and monad laws with
RcLazy examples showing left identity, right identity, and
associativity.

Add plan entry for ref parity traits (RefApplyFirst, RefApplySecond,
ref_monad_if, etc.) and their SendRef equivalents.
Add four thread-safe by-ref traits: SendRefPointed, SendRefLift,
SendRefSemiapplicative, SendRefSemimonad. Each mirrors its Ref
counterpart but adds Send + Sync bounds on element types and
Send on closures.

Implement all four for LazyBrand<ArcLazyConfig>, completing the
thread-safe by-ref trait hierarchy for ArcLazy.

Fix doc links in RefApplicative and RefMonad module headers.
SendRefApplicative = SendRefPointed + SendRefSemiapplicative.
SendRefMonad = SendRefApplicative + SendRefSemimonad.

LazyBrand<ArcLazyConfig> automatically implements both, completing
the thread-safe by-ref Functor -> Applicative -> Monad chain for
ArcLazy. Document monad laws with ArcLazy examples.
Add RefApplyFirst and RefApplySecond with blanket impls for any
RefLift implementor. Add SendRefApplyFirst and SendRefApplySecond
with blanket impls for any SendRefLift implementor.

Update RefApplicative and SendRefApplicative to include
ApplyFirst/ApplySecond supertraits, matching the non-ref Applicative.

Add ref_if_m and ref_unless_m free functions to ref_monad.rs for
conditional monadic actions via by-ref bind.
Mark steps 6-11 as done. Reorder remaining steps: rename (12),
dispatch unification (13, moved after rename), documentation (14),
m_do! integration (15). Fold remaining free function parity items
into the dispatch unification step since each gets a dispatched
version directly. Update completed changes section.
Skip the Callable<Mode> base trait extraction (GAT associated type
bounds unsupported on stable Rust, re-declaring Of creates
non-interoperable types). Update rename table: CloneableFn -> CloneFn,
SendCloneableFn -> SendCloneFn (independent, SendOf -> Of),
Function -> Arrow (new -> arrow). Document rationale for each
decision.
Rename CloneableFn to CloneFn, SendCloneableFn to SendCloneFn,
and Function to Arrow across the entire codebase.

Arrow aligns with Haskell's Arrow type class (arr + Category +
first/second). Arrow::new renamed to Arrow::arrow. Free function
fn_new removed (profunctor::arrow already provides this).

Make SendCloneFn independent (remove CloneFn supertrait). Rename
SendOf to Of on SendCloneFn since there is no longer a name
conflict with the parent trait.

Rename files: function.rs -> arrow.rs, cloneable_fn.rs ->
clone_fn.rs, send_cloneable_fn.rs -> send_clone_fn.rs.

Fix document_parameters for single-parameter methods. Regenerate
compile-fail test stderr files. Update doc links in
limitations-and-workarounds.md, std-coverage-checklist.md,
and optics-analysis.md.
Break dispatch into three categories: closure-dispatched (bind,
lift2, etc. infer mode from closure args), container-dispatched
(apply infers from wrapped function type), and not dispatched
(pure has no argument to infer from). Single mode parameter for
lift2 (both args must match, mixed modes rejected). Document
apply dispatch fallback if compiler can't differentiate container
types.
Add dispatch traits for bind and lift2 that route to either the
by-value (Semimonad::bind, Lift::lift2) or by-ref
(RefSemimonad::ref_bind, RefLift::ref_lift2) trait method based on
the closure's argument type. Follows the same pattern as
FunctorDispatch.

Currently pub(crate) to avoid name conflicts with existing free
functions. Will be promoted to pub when the old free functions are
removed and call sites are updated.
Replace the separate bind (Semimonad) and ref_bind (RefSemimonad)
free functions with a unified bind that dispatches based on the
closure's argument type: Fn(A) -> Of<B> routes to Semimonad::bind,
Fn(&A) -> Of<B> routes to RefSemimonad::ref_bind.

Update all call sites from bind::<Brand, _, _> to
bind::<Brand, _, _, _> (adding inferred Marker parameter). Update
m_do! macro codegen to emit 4 generic arguments. Fix ref_if_m and
ref_unless_m to call Brand::ref_bind instead of Brand::bind.
The closureless dispatch fallback (for container params with
InferableBrand bounds not in container_map) now handles Nested and
Tuple return structures in addition to Applied. Fixes separate showing
FA instead of Brand E. Traverse still shows Brand B (wrong element
type) due to extract_last_of_type_args_clean picking up the output
element type instead of the input element type from the Apply! macro.
…m parser

Replace all string-based macro text parsing in dispatch analysis with
calls to get_apply_macro_parameters(), the existing token-stream-based
Apply! parser. This eliminates fragile rfind("Of"), string counting,
and comma splitting on rendered macro text.

Changes:
- Add extract_apply_type_args() helper that parses Type::Macro via
  get_apply_macro_parameters and returns type arg names.
- Rewrite extract_return_structure to use classify_return_type() which
  dispatches on syn::Type variants (Tuple, Macro, other) and uses the
  proper parser for Apply! macros.
- Rewrite classify_arrow_output to parse Apply! brands and args
  structurally instead of string matching.
- Rewrite extract_container_params to use extract_apply_type_args
  instead of string-based extract_last_of_type_args_clean.
- Remove deprecated string-based functions: extract_last_of_type_args_clean,
  extract_non_brand_param, extract_apply_brand, extract_apply_brand_name.

This fixes wither (now produces correct signature) and improves
robustness for all functions. Remaining edge cases (traverse container
element, compose_kleisli arrow output, apply_first/second projection)
are separate issues not caused by string parsing.
Replace output table with current state: 30 functions produce correct
signatures after replacing string-based parsing with proper Apply!
parser. Document 5 remaining issues with root causes and approaches.

Add step 7-8g: regression tests for all 37 function signatures to
catch future regressions. Tests will assert exact HM output for every
inference wrapper function.
The container_map was using trait param names (e.g., "FTA" for traverse)
but the function uses different names (e.g., "FA"). This caused the
container_map lookup to fail, falling back to the InferableBrand
heuristic which used the wrong element type.

Fix: build_container_map() scans the function's impl *Dispatch<...>
parameter (or where-clause dispatch bounds for closureless dispatch)
to find the function's actual type arg names, then maps them to the
element types from dispatch_info.container_params.

Fixes traverse showing Brand B instead of Brand A.

Also removes all debug eprintln! statements added during investigation.
Update output status to 32/37 correct after container map fix.
Document resolved traverse issue (build_container_map from function's
dispatch trait type args). Update remaining issues table to 5 functions
with refined root causes and approaches. Add resolved-since-last-update
note for traverse.
Fix the last 5 inference wrapper functions that produced incorrect
Hindley-Milner type signatures:

- compose_kleisli, compose_kleisli_flipped, bi_traverse: Sub-arrow
  outputs showed () instead of Brand B / Brand C. Root cause was a
  lossy string round-trip in tuple closure extraction. Added SubArrow
  variant to DispatchArrowParam to preserve structured arrow data.

- wilt: Fell through to standalone macro entirely. Added NestedTuple
  variant to ReturnStructure for nested applications containing tuples
  of brand applications (M (Brand E, Brand O)). Changed
  build_applied_type to parse elements as syn::Type instead of
  syn::Ident, enabling compound types like Result<O, E>.

- apply_first, apply_second: Second parameter showed raw FA instead of
  Brand B. Added associated type extraction from Val impl blocks to
  resolve <FA as Dispatch>::FB projections.

- separate: Input container showed Brand E instead of Brand (Result O E).
  Added self-type element extraction from Val impl's Apply! self type
  for closureless dispatch where the trait is implemented on the
  container itself.

Additional improvements:
- Element type params collected from all sources, deduplicated, and
  sorted alphabetically for consistent forall ordering.
- Tuple param detection for where-clause dispatch (compose_kleisli_flipped).
- Wither now correctly renders closure parameter (A -> M (Option B)).
…7-8g)

Plan for insta-based snapshot tests that run document_module_worker on
real dispatch module source files and assert on the generated HM
signature strings. Covers all 37 inference wrapper functions across
18 test functions.
…d Apply!

Two changes to obtain information from direct sources instead of
indirect derivation:

1. Type param ordering: replace alphabetical sort with the dispatch
   trait definition's generic param list (type_param_order field).
   This preserves the trait author's intended ordering. Affects wither
   (Brand M A B instead of Brand A B M) and wilt (Brand M A E O
   instead of Brand A E O M).

2. Self-type element resolution: inner Apply! macros in
   self_type_elements are now resolved to qualified paths via
   apply_worker during extraction. Fixes compact showing Brand A
   instead of Brand (Option A), and join showing Brand A instead of
   Brand (Brand A). Removes the Apply!/Kind! string filter heuristic
   that was working around the unresolved macros.
Revised to process the full dispatch.rs module instead of per-file
isolation, matching production behavior. Added heuristic replacement
tasks (build_container_map positional alignment, brand_param from
trait definition, InferableBrand fallback from type_param_order) and
edge case test specifications for robustness.
…nfrastructure

Heuristic replacements:

- build_container_map: replaced "next unmatched multi-letter ident"
  scanning with direct positional lookup using ContainerParam.position.
  Introduced ContainerParam struct with name, position, and
  element_types fields.

- find_brand_param: added find_brand_param_from_trait_def that finds
  the type param with a Kind_* bound in the trait definition (direct
  source). Used as primary, with Val impl scan as fallback.

- InferableBrand fallback: added type_param_order-based fallback
  between self_type_elements and return structure. Extracts
  single-letter element types from the trait definition's params.

Snapshot test infrastructure:

- Added insta dev-dependency to fp-macros.
- Created signature_snapshot_tests module with 18 per-file tests.
- Tests extract mod inner body, run through document_module_worker,
  and snapshot the generated HM signatures.
- Closure-based dispatch tests produce correct output. Closureless
  dispatch tests (alt, compact, join, separate, apply_first/second)
  produce FA instead of Brand A in per-file isolation; this is a
  test-context issue (production docs are correct). To be investigated.
Heuristic replacements (1a, 1b, 1c) are done. Snapshot infrastructure
is implemented with 18 per-file tests. Closure-based dispatch (31
functions) produces correct output. Closureless dispatch (6 functions)
shows FA instead of branded types in per-file test isolation;
production docs are correct. Documented the module structure finding
(each dispatch file has its own #[document_module], not a single
parent module) and the remaining investigation needed.
18 per-file snapshot tests covering all 37 inference wrapper function
signatures. Each test reads the dispatch submodule source, extracts
the mod inner body, runs it through document_module_worker, and
asserts the generated HM signatures against insta snapshots.

The closureless dispatch issue (FA instead of Brand A) was caused by
the explicit:: submodule's functions overwriting inference wrapper
signatures in the BTreeMap (both named "alt", "compact", etc.). Fixed
by skipping mod explicit during signature collection.
Dispatch analysis edge cases (9 tests):
- Brand param in middle of generic param list (not first after lifetime)
- Brand param with unusual name (F instead of Brand)
- No semantic constraint in Val impl where clause
- No Val impl found (orphan dispatch trait)
- Extra type params between Brand and Marker
- Associated types extracted from Val impl
- Container param position indices correct
- Type param order preserves trait definition order

Signature generation edge cases (5 tests):
- Missing Kind hash falls back (no dispatch signature generated)
- No #[document_signature] attribute (function skipped)
- #[document_signature] without dispatch trait reference (left for standalone)
- Simple dispatch produces correct full-pipeline output
- Bifunctor two-element container with tuple closure
All three tasks complete: heuristic replacement (1a, 1b, 1c),
snapshot regression tests (18 per-file, all 37 signatures), and
edge case tests (9 dispatch analysis + 5 signature generation).
Documented the mod explicit name collision fix.
Created dispatch/contravariant.rs with ContravariantDispatch trait,
Val impl, inference wrapper, and explicit function. This follows the
same pattern as all other dispatch modules, enabling automatic HM
signature generation for contramap.

Only a Val dispatch exists (no Ref variant) because contramap's
closure produces elements (Fn(B) -> A), not consumes them, so the
&A convention does not apply.

Generated signature: forall Brand A B. Contravariant Brand => (B -> A, Brand A) -> Brand B

Deleted functions/contravariant.rs (replaced by dispatch module).
Updated functions.rs re-exports, explicit re-exports, and call sites
in doc examples and property tests.
#[document_signature("forall A B. (A -> B) -> A -> B")] now emits
the provided string directly as the HM signature, bypassing the
generation pipeline. Works in both the standalone macro context and
the document_module dispatch-aware context (where it takes precedence
over auto-generation).

Tests added for both paths:
- Standalone: string emitted, auto-generation still works, empty
  string rejected, non-string argument rejected (4 tests).
- document_module: override emits string, override takes precedence
  over dispatch-aware generation (2 tests).

Also: removed unused parse_empty_attributes utility.
Updated four documentation files to reflect that inference wrapper
functions now live in dispatch modules (not functions/), and that
each dispatch module contains the dispatch trait, Val/Ref impls,
inference wrapper, and explicit submodule.

- dispatch.md: Updated module structure diagram.
- architecture.md: Revised "Free Functions" section from three layers
  to two layers + facade. Documented colocation rationale.
- project-structure.md: Updated dispatch and functions module
  descriptions.
- CLAUDE.md: Updated dependency graph (added dispatch layer), key
  locations (added dispatch/ and analysis/dispatch.rs).
All 5 doc examples in dispatch/contravariant.rs now demonstrate
actual contramap usage with ProfunctorSecondAppliedBrand<RcFnBrand, _>
and assert on expected output, instead of assert!(true) placeholders.
- Replaced 5 assert!(true) placeholders in dispatch/contravariant.rs
  with real contramap examples using ProfunctorSecondAppliedBrand.
- Added explanation in inference wrapper doc for why the explicit
  variant is used (profunctor types have ambiguous brands).
- Updated #[document_signature] proc macro docs in lib.rs to document
  the manual override string argument (with usage example and expanded
  output) and dispatch-aware generation.
- Update todo.md
Shebang recipes don't receive just's *args via bash $@. Added
set -- {{args}} to populate bash positional params before the
test guard and cargo invocation.
Changed markdown links like [hkt.md](./hkt.md) to use the
document's actual title, e.g., [Higher-Kinded Types](./hkt.md).
Updated links in features.md, dispatch.md,
limitations-and-workarounds.md, and README.md.
- update-nixpkgs: weekly (Monday) flake.lock update via nix flake update.
- update-cargo-deps: weekly (Thursday) Cargo.lock update via cargo update.

Both create PRs with auto-merge enabled, gated on CI status checks.
Uses GitHub App token to ensure CI triggers on automated PRs.

Also enabled repo settings: allow_auto_merge, delete_branch_on_merge,
and branch protection on main requiring Formatting, Clippy,
Documentation, and Tests status checks.
The stacker dependency pulls in ar_archive_writer which uses this
license. The LLVM exception is strictly more permissive than plain
Apache-2.0 (relaxes restrictions on derivative works).
Added cargo-deny check as a just recipe and inserted it into the
verify pipeline between clippy and doc. This closes the gap where
CI ran cargo-deny but local verification did not.

Updated CLAUDE.md and CONTRIBUTING.md to reflect the six-step
verify pipeline.
@nothingnesses nothingnesses deleted the 2026-04-03 branch April 14, 2026 22:41
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