Skip to content

Project Review and Evaluation #17

Description

@ShapelessCat

Combined Project Review and Evaluation

This file merges the content of project_review_and_evaluation_0.md (from Claude Code opus 4.6 (1M) High) and project_review_and_evaluation_1.md (from Codex GPT-5.4 Extra High).
Each retained entry is explicitly labeled with its source.

Overview

What It Is

Source: project_review_and_evaluation_0.md

A Rust library implementing the Memento design pattern via proc macros. You annotate a struct/enum, and it generates a companion "memento" type that captures only the durable fields, plus trait impls to apply that memento back onto a live instance, leaving runtime-only fields untouched.

The core insight is that Deserialize builds a new value, but many real systems need to update a live object while preserving runtime state such as caches, handles, or closures. This is a genuine gap in the Rust ecosystem. Serde gives you construction, but not selective mutation. The Memento pattern fills that gap cleanly.

Strengths

1. The Problem Is Real and Well-Scoped

Source: project_review_and_evaluation_0.md

The "deserialize into a live object" problem is common in durable execution engines, event-sourced systems, and anything with connection handles or caches. The README's "Why Not Just Deserialize?" section nails the motivation. This isn't a solution looking for a problem.

2. The Trait Hierarchy Is Elegant

Source: project_review_and_evaluation_0.md

Recallable declares the memento type, Recall applies it infallibly, and TryRecall applies it fallibly. The separation is clean, and the no_std compatibility is a nice touch for embedded use cases.

3. The Macro Architecture Is Genuinely Well-Engineered

Source: project_review_and_evaluation_0.md

The three-phase pipeline, parse to IR to codegen, with StructIr and EnumIr as shared intermediate representations is textbook-good proc-macro design. The IR is built once and consumed by four independent codegen passes: memento type, Recallable impl, Recall impl, and From impl. This avoids the common proc-macro antipattern of reparsing the same input multiple times.

4. The Generic Retention System Is Impressive

Source: project_review_and_evaluation_0.md

The fixed-point loop in recallable-macro/src/context/internal/shared/generics.rs that determines which generic params to keep on the memento, including transitive dependencies through where-clause predicates, is the most technically sophisticated part of the codebase. It handles const generics, lifetime params, and synthesizes PhantomData markers for unreferenced-but-retained params. This is hard to get right and it is done correctly.

5. Container-Agnostic Design

Source: project_review_and_evaluation_0.md

The deliberate decision not to prescribe memento semantics for container types such as Option, Vec, and HashMap is the right call. The recallable/tests/container_impls.rs test file demonstrates multiple Option strategies, showing that the abstraction is flexible enough to support domain-specific behavior without framework opinions.

6. Testing Is Thorough and Multi-Layered

Source: project_review_and_evaluation_0.md

The project uses unit tests inside the macro crate, integration tests with serde_json and postcard, trybuild compile-fail UI tests, property-based tests with proptest, fuzz targets, and a four-combo feature matrix. The coverage thresholds enforced in CI are serious, and the coverage-comparison job that checks regressions against the base branch is a strong signal of engineering discipline.

7. Documentation Is Above Average for a Rust Crate at This Stage

Source: project_review_and_evaluation_0.md

README, GUIDE.md, MACRO_INTERNALS.md, CONTRIBUTING.md, CHANGELOG.md, public-item doc comments with examples, and runnable examples together amount to a much stronger documentation set than most crates at this maturity level.

Criticisms and Findings

1. The Naming Is Confusing

Source: project_review_and_evaluation_0.md

"Recallable" and "Recall" are not standard terms for the Memento pattern. In GoF terms, the roles are Originator, Memento, and Caretaker. The verb "recall" in English suggests remembering, while the code is actually applying a saved state. Names such as Restorable and Restore or Revertible and Revert would map more naturally to the behavior.

2. The #[recallable_model] Attribute Ordering Footgun Is a Design Smell

Source: project_review_and_evaluation_0.md

The requirement that #[recallable_model] must appear before #[derive(Serialize)] because attribute macros only see attributes below them is a real usability problem. The project documents it extensively, which is a sign that the API surface has a sharp edge. The duplicate-detecting compile error is better than silent failure, but users will still reach that error by following the natural Rust convention of putting derives first.

3. The Enum Support Split Is Awkward

Source: project_review_and_evaluation_0.md

#[derive(Recallable)] works on all enums, but #[derive(Recall)] and #[recallable_model] only work on "assignment-only" enums. The asymmetry means a user can successfully derive Recallable, then hit a conceptual cliff when adding Recall. The guide explains this, but it is still overhead for users.

4. Serde Attributes Are Not Forwarded to the Generated Memento (High Priority)

Source: project_review_and_evaluation_0.md

This is listed as a current limitation, but it is a significant one. If the source struct uses #[serde(rename = "...")], #[serde(default)], or #[serde(rename_all = "...")], the generated memento will not inherit those semantics. For a crate whose primary workflow is serialize source, deserialize memento, apply, that is a real wire-compatibility footgun.

5. The Memento Type Is Opaque, but Maybe Too Opaque

Source: project_review_and_evaluation_0.md

Mementos derive Deserialize but not Serialize, and the fields are always private. That fits the "apply-side token" model, but it also makes mementos harder to inspect, round-trip, or use as general-purpose DTOs. The impl_from feature helps for creation, but the lack of Serialize and field visibility keeps the type intentionally constrained.

6. The Crate Has No Runtime Cost Story (Medium Priority)

Source: project_review_and_evaluation_0.md

The docs do not discuss performance characteristics. For users in durable execution engines or streaming systems, the lack of benchmark data or even a short performance narrative leaves an obvious gap.

7. The no_std Story Has a Gap (Medium Priority)

Source: project_review_and_evaluation_0.md

The traits crate is #![no_std], but some commonly expected conveniences around containers still imply alloc or std expectations. The docs could be more explicit about where that boundary actually is.

8. Some Areas Feel Over-Documented While Others Feel Under-Documented

Source: project_review_and_evaluation_0.md

There is overlap between CLAUDE.md, GUIDE.md, MACRO_INTERNALS.md, and README.md, especially around the attribute-ordering caveat. Meanwhile, some power-user features, such as #[recallable(skip_memento_default_derives)], do not get the same degree of practical guidance.

9. The recallable-macro Feature Flags Are a Leaky Abstraction (High Priority)

Source: project_review_and_evaluation_0.md

Forwarding features from recallable to recallable-macro is standard for proc-macro crates, but the trybuild interaction shows that the split still leaks into contributor experience and test maintenance.

10. There Is No Migration or Versioning Story

Source: project_review_and_evaluation_0.md

The crate targets persistence and recovery use cases, but it does not provide a clear strategy for schema evolution. The schema-drift tests demonstrate that field additions, removals, and renames matter, but the guide does not offer much beyond warning users that the shape is sensitive.

11. PhantomData Auto-Skip Is Name-Based, Not Type-Based (High Priority)

Source: project_review_and_evaluation_1.md

PhantomData auto-skip is implemented as a path-name heuristic, not true type resolution, so it can silently drop real user state if a non-marker type happens to be named PhantomData. This was confirmed with an offline scratch crate using a user-defined custom::PhantomData<u8> field that was omitted from the generated memento and never recalled. That is silent semantic corruption, not a compile-time failure.

12. The Blanket TryRecall Impl Blocks Custom Fallible TryRecall on Recall Types (Medium Priority)

Source: project_review_and_evaluation_1.md

Because TryRecall has a blanket impl for all T: Recall, a user cannot also provide a custom fallible TryRecall for a type that implements Recall. A scratch crate reproduced the compiler conflict (E0119). This makes the API more constraining than it first appears.

13. impl_from Is Not Additive Under Cargo Feature Unification (Medium Priority)

Source: project_review_and_evaluation_1.md

Enabling impl_from adds extra nested From bounds to derives. That means a previously valid derive can stop compiling if some other dependency enables the feature. The project's own tests already cfg around this in one case, and a scratch crate reproduced the failure.

14. #[recallable_model] Is Overly Magical for a Convenience Macro

Source: project_review_and_evaluation_1.md

The macro mutates source-side serde behavior and is sensitive to attribute order. That makes the "easy path" surprisingly invasive and pushes users toward a macro that carries more hidden behavior than its name suggests.

Minor Issues (High Priority)

1. extend_where_clause Feels Slightly Awkward

Source: project_review_and_evaluation_0.md

The helper creates a where clause even when it may stay empty, then checks whether it ended up empty. A builder-style approach would read more naturally.

2. The CodegenItemIr GAT Shape Is Correct but Ugly

Source: project_review_and_evaluation_0.md

The explicit FlatMap type in the enum impl is a Rust limitation more than a project flaw, but it still adds visual friction.

3. __recallable_restore_from_memento Pollutes the Method Namespace

Source: project_review_and_evaluation_0.md

A helper method on the type works, but a free function in the hidden generated scope would be cleaner.

4. proc-macro-crate Is Standard but Still Another Dependency

Source: project_review_and_evaluation_0.md

This is not unusual for proc-macro crates, but it still adds complexity around crate-path resolution.

Overall Assessment

Verdict

Source: project_review_and_evaluation_0.md

This is a well-built, well-tested library solving a real problem. The macro architecture is better than most proc-macro crates. The trait design is clean, the test coverage is serious, and the documentation effort is substantial.

The main risks identified in that review were the naming, the serde attribute forwarding gap, the enum support asymmetry, and the lack of a versioning or migration story for persistent state.

Additional Overall Assessment

Source: project_review_and_evaluation_1.md

The project is strong overall. The docs are unusually thorough, the proc-macro backend is sensibly split into IR and codegen layers, and the verification discipline is good: make test and cargo clippy --workspace --all-targets --all-features both passed.

The main weakness is not obvious breakage in current CI. It is API sharpness. The crate is promising infrastructure, but some choices are too heuristic (PhantomData), too globally constraining (TryRecall blanket impl), or too feature-sensitive (impl_from) for a library that wants to be dependable in downstream ecosystems.

Verification

Verification Steps and Confirmations

Source: project_review_and_evaluation_1.md

  • make test
  • cargo clippy --workspace --all-targets --all-features
  • Offline scratch crate confirmed TryRecall impl conflict (E0119)
  • Offline scratch crate confirmed user-defined PhantomData-named type is silently skipped
  • Offline scratch crate confirmed impl_from can break downstream derives with extra nested From bounds

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions