Skip to content

feat: contract versioning, health check, session archival & batch archive#266

Merged
Luluameh merged 5 commits into
LightForgeHub:mainfrom
gregemax:feat/contract-versioning-migration
May 31, 2026
Merged

feat: contract versioning, health check, session archival & batch archive#266
Luluameh merged 5 commits into
LightForgeHub:mainfrom
gregemax:feat/contract-versioning-migration

Conversation

@gregemax
Copy link
Copy Markdown
Contributor

@gregemax gregemax commented May 31, 2026

Closes #246
Closes #247
Closes #248
Closes #249


#246 — Contract Versioning & Schema Migration Tooling

  • Added DataKey::ContractVersion to instance storage
  • Added migrate(new_version): admin-only, runs once per version bump, dispatches to migrations::run()
  • Added contracts/src/migrations.rs: SessionV1 struct + migrate_v1_to_v2 re-serialisation logic
  • Added contracts/src/admin.rs: admin helper re-exports
  • Unit tests: v1→v2 migration, same-version rejection

#247 — Contract Health Check Endpoint

  • Added ContractStatus { version, admin, is_paused, total_sessions, active_sessions }
  • Added health_check(): read-only, no auth required
  • Added DataKey::ActiveSessionCount — incremented on start_session, decremented on all terminal transitions
  • Unit test: verifies all fields across session lifecycle

#248 — Session Archival to Temporary Storage

  • Added contracts/src/storage.rs: write_archive / read_archive helpers (~90-day TTL)
  • Added archive_session(session_id): admin-only, 90-day delay enforced, moves persistent → temporary, emits event
  • Added get_archived_session(session_id): read accessor
  • Unit tests: happy path, early-call rejection, active-session rejection

#249 — Bulk Session Archival (Admin Batch Operation)

  • Added MAX_ARCHIVE_BATCH_SIZE = 50
  • Added ArchiveSummary { archived: u32, skipped: u32 }
  • Added batch_archive_sessions(session_ids): admin-only, batch cap enforced, skips ineligible sessions silently
  • Unit tests: full batch, mixed eligible/ineligible, oversized batch rejection

Testing

All 82 contract tests pass (cargo test).

Summary by CodeRabbit

Release Notes

  • New Features
    • Added contract health status monitoring with version and admin tracking.
    • Implemented session archival system with batch processing for completed sessions.
    • Added admin access control verification and authorization functions.
    • Enabled contract schema migration support for seamless future upgrades.
    • Implemented active session counting to track concurrent usage.

gregemax added 4 commits May 31, 2026 09:33
…#246)

- Add DataKey::ContractVersion to instance storage
- Add migrate(new_version) public function (admin-guarded, once-per-version)
- Add migrations.rs with SessionV1 struct and migrate_v1_to_v2 logic
- Add admin.rs with admin helper re-exports
- Add unit tests: test_migrate_v1_to_v2 and test_migrate_same_version_is_rejected
…Hub#247)

- Add ContractStatus struct { version, admin, is_paused, total_sessions, active_sessions }
- Add health_check() read-only function (no auth required)
- Add DataKey::ActiveSessionCount to instance storage
- Increment on start_session, decrement on all terminal transitions
- Add test_health_check_returns_correct_status unit test
- Add contracts/src/storage.rs with write_archive/read_archive helpers
  and ARCHIVE_TTL_LEDGERS (~90 days at 5s/ledger)
- Add ARCHIVE_DELAY_SECS (90 days) constant to lib.rs
- Add archive_session(session_id): admin-only, enforces 90-day delay,
  moves session from persistent to temporary storage, removes persistent entry
- Add get_archived_session(session_id): reads from temporary storage
- Emit (session, archived) event with session_id and archived_at timestamp
- Add 3 unit tests covering happy path, early-call rejection, active-session rejection
- Add MAX_ARCHIVE_BATCH_SIZE = 50 constant
- Add ArchiveSummary { archived, skipped } contracttype struct
- Add batch_archive_sessions(session_ids): admin-only, enforces batch cap,
  skips non-existent / active / too-recent sessions without panicking,
  emits (session, archived) per archived session
- Add 3 unit tests: happy path, skip ineligible, oversized batch rejection
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 31, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa50dfb6-cb3b-4a79-a2a1-4eb94ef4c0e2

📥 Commits

Reviewing files that changed from the base of the PR and between b8a785b and 7c11307.

📒 Files selected for processing (2)
  • contracts/src/admin.rs
  • contracts/src/lib.rs
💤 Files with no reviewable changes (1)
  • contracts/src/lib.rs

📝 Walkthrough

Walkthrough

Adds admin helpers, contract versioning and migration (v1→v2), temporary archival storage and TTL, active-session counting across lifecycle transitions, and public health/version/archive entrypoints with tests and batch archival controls.

Changes

Contract Versioning, Admin Access, and Session Archival

Layer / File(s) Summary
Schema migration framework
contracts/src/migrations.rs
SessionV1 contracttype, run(env, from, to) dispatcher, migrate_v1_to_v2 that upgrades persisted sessions by adding encrypted_notes_hash and paused_at (set to None), and unit tests for success and invalid-version panic.
Session archive storage infrastructure
contracts/src/storage.rs
Adds ARCHIVE_TTL_LEDGERS / ARCHIVE_TTL_THRESHOLD and implements write_archive(env, &Session) and read_archive(env, session_id) to move sessions into temporary storage with TTL and retrieve them.
Admin helpers and guards
contracts/src/admin.rs
Adds pub use crate::Error;, get_admin(env) reading DataKey::Admin (Unauthorized if unset), and require_admin(env) enforcing admin signature via require_auth.
Contract module wiring, constants, and types
contracts/src/lib.rs
Registers migrations, admin, storage modules; introduces ARCHIVE_DELAY_SECS (90 days) and MAX_ARCHIVE_BATCH_SIZE (50); extends DataKey with ContractVersion and ActiveSessionCount; adds ArchiveSummary and ContractStatus.
Active-session counter wiring
contracts/src/lib.rs
Adds increment_active_sessions / decrement_active_sessions helpers and integrates them into session start and all terminal transition paths to maintain ActiveSessionCount.
Health, archival, batch, and migrate APIs + tests
contracts/src/lib.rs
Adds get_contract_version, health_check, admin-gated archive_session, get_archived_session, batch_archive_sessions (with skip and size cap), and admin-only migrate that updates persistent ContractVersion. Integration tests for health, archive, and batch behaviors included.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Contract
  participant PersistentStorage
  participant TemporaryStorage
  Client->>Contract: start_session()
  Contract->>PersistentStorage: write Session
  Contract->>PersistentStorage: increment ActiveSessionCount
  Client->>Contract: settle_session() / complete
  Contract->>PersistentStorage: update Session -> Completed
  Contract->>PersistentStorage: decrement ActiveSessionCount
  Client->>Contract: health_check()
  Contract->>PersistentStorage: read ContractVersion & ActiveSessionCount
  Contract->>Client: return ContractStatus
  Client->>Contract: archive_session(session_id) [admin, after 90 days]
  Contract->>PersistentStorage: read Session
  Contract->>TemporaryStorage: write_archive(Session) with TTL
  Contract->>PersistentStorage: delete Session
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

A rabbit hops through ledger rows,
Moves old sessions where the cool wind blows,
Guards the gates with admin keys,
Counts the lives of active bees,
Hops away—migration done. 🐇

🚥 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 'feat: contract versioning, health check, session archival & batch archive' accurately captures the four major features added across all modified files.
Linked Issues check ✅ Passed All changes address the requirements from issues #246 (versioning/migration), #247 (health check), #248 (session archival), and #249 (batch archival) with appropriate implementation of new functions, data structures, and storage helpers.
Out of Scope Changes check ✅ Passed All code changes remain directly scoped to the four linked issues; no unrelated modifications or infrastructure changes were introduced outside the stated objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@contracts/src/lib.rs`:
- Around line 1127-1151: The migrate function currently calls
migrations::run(&env, current, new_version) but unconditionally writes
DataKey::ContractVersion to new_version even when no migration ran; change
migrations::run to return a Result or a boolean (e.g., Result<(), Error> or
Result<bool, Error>) indicating whether the migration was applied/supported,
then update migrate to call migrations::run and: if the call returns an Err or a
"not supported" result, return an appropriate Error (do not update
ContractVersion or emit the "migrated" event); only set DataKey::ContractVersion
and publish the migrated event after migrations::run indicates success. Ensure
references are to the migrate function, migrations::run, and
DataKey::ContractVersion so the check is implemented in the right place.
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: acf3d943-0480-4500-a8fb-1fd189efc80d

📥 Commits

Reviewing files that changed from the base of the PR and between e6b0a33 and b8a785b.

📒 Files selected for processing (4)
  • contracts/src/admin.rs
  • contracts/src/lib.rs
  • contracts/src/migrations.rs
  • contracts/src/storage.rs

Comment thread contracts/src/lib.rs
Comment on lines +1127 to +1151
/// Migrate storage schema to `new_version`. Admin-only, runs once per version bump.
pub fn migrate(env: Env, new_version: u32) -> Result<(), Error> {
Self::require_admin(&env)?;

let current: u32 = env
.storage()
.instance()
.get(&DataKey::ContractVersion)
.unwrap_or(1u32);

if new_version <= current {
return Err(Error::InvalidSessionState);
}

migrations::run(&env, current, new_version);

env.storage()
.instance()
.set(&DataKey::ContractVersion, &new_version);

env.events()
.publish((symbol_short!("migrated"),), (current, new_version));

Ok(())
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Migration allows setting arbitrary version without actual migration logic.

The migrate function sets ContractVersion to new_version after calling migrations::run(), but run() silently does nothing for unsupported version pairs. For example, calling migrate(100) when current version is 1 will set version to 100 without any migration logic executing, potentially blocking future legitimate migrations.

Consider having migrations::run() return a success indicator or panic on unsupported version transitions to prevent accidental version corruption.

Proposed fix in migrations.rs
-pub fn run(env: &Env, from: u32, to: u32) {
+pub fn run(env: &Env, from: u32, to: u32) -> bool {
     if from == 1 && to == 2 {
         migrate_v1_to_v2(env);
+        return true;
     }
     // future: if from == 2 && to == 3 { migrate_v2_to_v3(env); }
+    false
 }

Then in migrate():

-        migrations::run(&env, current, new_version);
+        if !migrations::run(&env, current, new_version) {
+            return Err(Error::InvalidSessionState); // or a new UnsupportedMigration error
+        }
🤖 Prompt for 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.

In `@contracts/src/lib.rs` around lines 1127 - 1151, The migrate function
currently calls migrations::run(&env, current, new_version) but unconditionally
writes DataKey::ContractVersion to new_version even when no migration ran;
change migrations::run to return a Result or a boolean (e.g., Result<(), Error>
or Result<bool, Error>) indicating whether the migration was applied/supported,
then update migrate to call migrations::run and: if the call returns an Err or a
"not supported" result, return an appropriate Error (do not update
ContractVersion or emit the "migrated" event); only set DataKey::ContractVersion
and publish the migrated event after migrations::run indicates success. Ensure
references are to the migrate function, migrations::run, and
DataKey::ContractVersion so the check is implemented in the right place.

@Luluameh Luluameh merged commit aed9dab into LightForgeHub:main May 31, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants