Skip to content

refactor: expose schema recovery through engine facade - #45

Merged
0xgleb merged 1 commit into
masterfrom
feat/0.4-schema-engine
Aug 12, 2026
Merged

0xgleb merged 1 commit into
masterfrom
feat/0.4-schema-engine

Conversation

@0xgleb

@0xgleb 0xgleb commented Jul 16, 2026 •

Copy link
Copy Markdown
Member

Closes #80.


This is part 26 of 32 in a stack made with GitButler:

Summary by CodeRabbit

  • New Features
    • Added schema version tracking and reconciliation for persisted event data.
    • Automatically clears eligible snapshots when schemas change.
    • Protects compacted snapshots from being cleared during schema updates.
    • Added support for recording schema versions and compaction policies.

@coderabbitai

coderabbitai Bot commented Jul 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@0xgleb, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1e66ff48-a7e7-4be1-bbfb-20074c9a32b6

📥 Commits

Reviewing files that changed from the base of the PR and between 3092de4 and e806919.

📒 Files selected for processing (1)
  • crates/event-sorcery/src/engine.rs

Walkthrough

The schema registry now reconciles target metadata, clears eligible stale snapshots, records schema versions, and loads events with typed SQLx queries. Engine exposes reconciliation and recording methods with error mapping and integration tests.

Changes

Schema reconciliation

Layer / File(s) Summary
Schema target and registry reconciliation
crates/event-sorcery/src/schema_registry.rs, .sqlx/query-*.json
SchemaTarget centralizes aggregate metadata, compaction policy, schema reconciliation, version recording, ordered event loading, and permitted snapshot deletion.
Engine schema facade and validation
crates/event-sorcery/src/engine.rs, crates/event-sorcery/src/lib.rs
Engine exposes asynchronous reconciliation and recording methods. Schema errors map to PersistenceError::UnknownError. Tests cover unchanged schemas, snapshot clearing, and compacted snapshot protection. SchemaTarget is publicly re-exported.

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 states that schema recovery is exposed through the engine facade, which matches the primary change.
Linked Issues check ✅ Passed The PR exposes schema reconciliation and schema recording through Engine, satisfying issue #80's shared engine boundary requirement.
Out of Scope Changes check ✅ Passed The SQLx metadata, registry refactor, engine integration, and tests directly support the schema recovery facade objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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 feat/0.4-schema-engine

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@0xgleb
0xgleb force-pushed the bench/0.4-haskell branch from 02bb9ec to 0ce52ec Compare July 16, 2026 06:26
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from 139b2e9 to 220c993 Compare July 16, 2026 06:26
@0xgleb 0xgleb self-assigned this Jul 16, 2026
@0xgleb
0xgleb marked this pull request as ready for review July 16, 2026 08:12
@0xgleb
0xgleb force-pushed the bench/0.4-haskell branch from d605689 to f9eacfd Compare July 16, 2026 08:12
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from 7a92e1a to 7d04195 Compare July 16, 2026 08:15
@0xgleb
0xgleb force-pushed the bench/0.4-haskell branch from f9eacfd to bdee9ec Compare July 16, 2026 08:20
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from 7d04195 to 2190814 Compare July 16, 2026 08:20
@0xgleb
0xgleb force-pushed the bench/0.4-haskell branch from bdee9ec to 4ad080f Compare July 16, 2026 09:44
@0xgleb 0xgleb added this to the 0.4.0 milestone Jul 16, 2026
@0xgleb 0xgleb moved this from Todo to In Progress in event-sorcery Jul 16, 2026
@0xgleb
0xgleb force-pushed the bench/0.4-haskell branch from 4ad080f to d9c2a0c Compare July 16, 2026 13:57
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from 2190814 to b90c801 Compare July 16, 2026 14:00
@0xgleb
0xgleb force-pushed the bench/0.4-haskell branch 2 times, most recently from 1c123cc to 2eef46d Compare August 12, 2026 03:58
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from b90c801 to d64fa30 Compare August 12, 2026 04:00
Base automatically changed from bench/0.4-haskell to master August 12, 2026 04:56
@0xgleb
0xgleb enabled auto-merge August 12, 2026 04:58
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from d64fa30 to 3092de4 Compare August 12, 2026 05:00
coderabbitai[bot]
coderabbitai Bot previously requested changes Aug 12, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@crates/event-sorcery/src/engine.rs`:
- Around line 1607-1609: Replace the raw SqlitePool setup before each
Engine::new call in crates/event-sorcery/src/engine.rs at lines 1607-1609,
1631-1633, and 1652-1654 with sqlite_es::testing::create_test_pool(), preserving
the existing migration and test setup.
- Around line 318-320: Construct and store a single Reconciler in Engine during
Engine::new, then reuse that instance for both reconcile_target and
record_target instead of creating one per facade call. Update
crates/event-sorcery/src/engine.rs lines 318-320 and 325-327 to invoke the
stored reconciler; both sites require this shared-instance change.
- Around line 1662-1677: Update the reconciliation test around
persist_test_snapshot and reconcile_schema to capture the exact snapshot value
before reconciliation, then load it afterward and use assert_eq! to compare both
values. Replace the is_some existence check while preserving the existing error
assertion.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a6c977fb-ab08-4eec-a067-57fe2eb11fbc

📥 Commits

Reviewing files that changed from the base of the PR and between c7b1267 and 3092de4.

📒 Files selected for processing (5)
  • .sqlx/query-5f367f145e8aed4391af7a1b2b3de8f12ab61d44f3a664f89752b34338ff0b09.json
  • .sqlx/query-978a25f67d0b5d2b5a21c9f298d1b7ade60c52869bd382d8494520c7ba97cd42.json
  • crates/event-sorcery/src/engine.rs
  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/schema_registry.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (6)
  • GitHub Check: clippy
  • GitHub Check: check
  • GitHub Check: fmt
  • GitHub Check: haskell
  • GitHub Check: test
  • GitHub Check: hooks
🧰 Additional context used
📓 Path-based instructions (2)
**/*

📄 CodeRabbit inference engine (AGENTS.md)

**/*: Before work, read SPEC.md and docs/domain.md; read relevant supplemental documentation before implementation.
New features must be documented in SPEC.md before implementation and must follow the hierarchy SPEC.md -> issue -> plan -> tests -> implementation.
Fix all known problems immediately, complete all tasks, and do not allow warnings or errors to pass through.
Keep a granular task list and clear completed tasks from the active list.
All new or modified logic must have corresponding test coverage.
Understand relevant documentation and source code before implementation, keep diffs small, and review the approach critically.
When changing direction or making an important undocumented architectural decision, obtain confirmation; record significant decisions as ADRs under adrs/.
Before handover, review the diff, revert unjustified changes, and check for scope creep.
Each aggregate in a consuming application must use exactly one SqliteCqrs instance constructed at startup; per-request construction is forbidden.
Never read secret or credential files such as .env*, credentials.json, *.key, *.pem, *.p12, *.pfx, or sensitive database files without explicit permission.
Never bypass, disable, suppress, or obscure quality-control mechanisms without explicit permission; fix lint and test issues at their root.
Use cargo check, cargo nextest, and cargo clippy for verification; never use cargo build unless build artifacts are required.

Files:

  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/engine.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Organize code by business feature rather than technical layer; avoid catch-all modules such as types.rs, error.rs, models.rs, utils.rs, helpers.rs, and services.rs.
Never write directly to the events table; emit events through CqrsFramework::execute() or execute_with_metadata().
Use cqrs-es Services for side effects in handle() and follow the {Action}er -> {Domain}Service -> {Domain}Manager naming pattern.
Place command-execution logging in aggregate handle() methods rather than callers.
Model invalid states with enums, ADTs, newtypes, and typestate rather than relying on runtime validation.
Use domain newtypes at APIs and convert to SDK primitives inside the callee, except at cross-crate boundaries where conversion at the call site is necessary.
Keep visibility as restrictive as possible: private over pub(crate) over pub.
Use a three-group import order: external crates, workspace crates, then crate-internal imports; do not use function-level imports except enum variants.
Do not use unwrap() or expect() in production Rust code; they are permitted in #[cfg(test)] code.
Never create error variants containing opaque String values; prefer #[from], ?, #[source], and preserve error chains.
Log a warning or error before silent early returns such as let-else failures.
Never silently mask numeric failures with caps, fallback defaults, precision truncation, unwrap_or(), or unwrap_or_default(); use explicit checked conversions and errors.
Prefer functional patterns, pattern matching, combinators, type-driven design, and iterators over imperative loops unless complexity increases.
Use ASCII in identifiers, comments, log messages, and configuration keys; Unicode is preferred only in user-facing rendered output.
Do not use single-letter variables, arguments, closure parameters, or generic type parameters except an unambiguous lone type parameter or short unambiguous closure.
Every module must have a //! docstring and should order public API, private implementati...

Files:

  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/schema_registry.rs
  • crates/event-sorcery/src/engine.rs
🧠 Learnings (4)
📚 Learning: 2026-07-16T15:10:47.551Z
Learnt from: 0xgleb
Repo: dataclique/event-sorcery PR: 24
File: crates/event-sorcery/src/lib.rs:88-88
Timestamp: 2026-07-16T15:10:47.551Z
Learning: In `crates/event-sorcery/src/lib.rs`, the crate-root `engine` module should remain private (`mod engine;`). Rust permits descendant modules, including `crates/event-sorcery/src/job_sqlite.rs` and `crates/event-sorcery/src/sqlite_event_repository.rs`, to access it through `crate::engine`; it does not need `pub(crate)` visibility for that internal access.

Applied to files:

  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/engine.rs
📚 Learning: 2026-07-16T09:05:57.710Z
Learnt from: CR
Repo: dataclique/event-sorcery PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-16T09:05:57.710Z
Learning: Applies to crates/**/*.rs : Use in-memory SQLite pools via sqlite_es::testing::create_test_pool() for database test isolation.

Applied to files:

  • crates/event-sorcery/src/lib.rs
  • crates/event-sorcery/src/engine.rs
📚 Learning: 2026-07-16T09:05:57.710Z
Learnt from: CR
Repo: dataclique/event-sorcery PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-07-16T09:05:57.710Z
Learning: Applies to crates/**/*.rs : Never write directly to the events table; emit events through CqrsFramework::execute() or execute_with_metadata().

Applied to files:

  • crates/event-sorcery/src/schema_registry.rs
📚 Learning: 2026-07-16T21:10:49.512Z
Learnt from: 0xgleb
Repo: dataclique/event-sorcery PR: 0
File: :0-0
Timestamp: 2026-07-16T21:10:49.512Z
Learning: In `crates/event-sorcery/src/job_backend.rs`, `JobClaimHandle` is deliberately non-serializable. It contains private fencing identity, so foreign-language bindings must retain it in trusted process memory and expose only a binding-owned opaque token; accepting caller-provided serialized handles could permit forged claim state.

Applied to files:

  • crates/event-sorcery/src/engine.rs
🔇 Additional comments (5)
crates/event-sorcery/src/schema_registry.rs (1)

40-61: LGTM!

Also applies to: 179-250, 252-261, 298-303, 313-318

.sqlx/query-5f367f145e8aed4391af7a1b2b3de8f12ab61d44f3a664f89752b34338ff0b09.json (1)

1-26: LGTM!

.sqlx/query-978a25f67d0b5d2b5a21c9f298d1b7ade60c52869bd382d8494520c7ba97cd42.json (1)

1-12: LGTM!

crates/event-sorcery/src/engine.rs (1)

17-17: LGTM!

Also applies to: 221-222, 1262-1262, 1475-1475, 2226-2242

crates/event-sorcery/src/lib.rs (1)

149-151: LGTM!

Comment thread crates/event-sorcery/src/engine.rs Outdated
Comment thread crates/event-sorcery/src/engine.rs Outdated
Comment thread crates/event-sorcery/src/engine.rs Outdated
@0xgleb
0xgleb force-pushed the feat/0.4-schema-engine branch from 3092de4 to e806919 Compare August 12, 2026 05:09
@0xgleb
0xgleb dismissed coderabbitai[bot]’s stale review August 12, 2026 05:09

All three findings addressed: Reconciler now constructed once in Engine::new (Arc-shared), schema tests use create_test_pool(), snapshot preservation asserted by exact value

@0xgleb
0xgleb added this pull request to the merge queue Aug 12, 2026
Merged via the queue into master with commit d14f5f4 Aug 12, 2026
8 checks passed
@0xgleb
0xgleb deleted the feat/0.4-schema-engine branch August 12, 2026 05:22
@github-project-automation github-project-automation Bot moved this from In Progress to Done in event-sorcery Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Schema recovery bypasses the shared engine facade

1 participant