Skip to content

feat(core): persist key-epoch records and enforce the cold-start key-epoch check (#445) - #945

Merged
qnbs merged 3 commits into
mainfrom
feat/445-gate3c-key-epochs
Oct 2, 2026
Merged

qnbs merged 3 commits into
mainfrom
feat/445-gate3c-key-epochs

Conversation

@qnbs

@qnbs qnbs commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

User description

Part of #445 / #921 (Gate 3, slice 3C, part 3c-1). No production authority switch: PRODUCTION_AUTHORITY_SWITCH_ALLOWED = NO.

This resolves the deferred #944 review item, tracked on #921: bind active_key_epoch to the root's key route through the authenticated key-epoch set, never through list_epochs.

What lands (root_store)

  • write_key_epoch
    • Persists a key-epoch record generation as <root_dir>/key-epoch/<epoch>/generation-<n>.wsr1 through 3A's staging.
    • The file is immutable, n must be exactly the next generation of that epoch, and it is sealed under the root key route.
    • Returns the record's key_epoch_set_digest entry.
  • load_key_epoch_set
    • Reads the newest generation of each epoch's gap-free chain.
    • Any unexpected name, any gap, or any record that does not open is RECOVERY_REQUIRED.
  • §5.3.1 step 5, enforced by commit_root (before step C, so nothing is prepared) and by load_committed_root (cold start):
    • the set must hash to the root's key_epoch_set_digest;
    • active_key_epoch must be exactly one KEY_EPOCH_ACTIVE record whose route digest is the root's root_key_ref_digest.
  • DurableFs::create_dir_all, for the per-epoch directories.

Proof

tests/gate3c_root_commit_test.rs now has 18 tests. The fixture persists a real ACTIVE epoch-1 record, and every root names the actual set digest. New cases:

  • a root naming another set (refused before step C);
  • an active epoch that is only PREPARED;
  • an active epoch bound to another route;
  • a tampered key-epoch record or a chain gap at cold start;
  • out-of-order generations.

Locally, clippy is clean, the full crate suite passes, and pnpm docs:check passes.

Docs

  • Contract §20 has a new slice 3C part 3c-1 entry, and the status sentences are updated.
  • R15_GATE3=SLICE_3C_KEY_EPOCHS is set in the block and in ledger row 10.

Next: 3c-2, the write-protocol integration (catalog plus root per marker transition, dropping a rolled-back first write), list_records and retention. Then Gate 3 closure with #357 and the asset-pair boundary.

Summary by Sourcery

Persist and authenticate key-epoch records so root commits and cold starts fail closed unless the active epoch is bound to the root’s key route.

New Features:

  • Persist immutable, generation-ordered key-epoch records in the root store and expose key-epoch set loading and writing APIs.

Bug Fixes:

  • Reject root commits and cold starts when key-epoch records are tampered with, incomplete, mismatched, unopenable, or bound to an unauthorized key route.

Enhancements:

  • Enforce the authenticated key-epoch set and active-epoch route binding before root preparation and during trusted startup.
  • Extend durable filesystem support for creating and synchronizing key-epoch directories.

Documentation:

  • Update the secure-storage contract, migration ledger, and changelog to record Gate 3 slice 3C part 3c-1 and its key-epoch verification scope.

Tests:

  • Expand Gate 3 root-commit coverage for mismatched sets, invalid active epochs, route mismatches, tampering, gaps, duplicate active epochs, and generation ordering.

Chores:

  • Keep the production authority switch disabled while advancing the headless secure-storage implementation.

Summary by cubic

Persists key-epoch records in the root store and enforces the §5.3.1 step 5 key-epoch check at both commit and cold start, so active_key_epoch is bound to the root's key route through the authenticated set instead of list_epochs. No production authority switch; PRODUCTION_AUTHORITY_SWITCH_ALLOWED = NO.

Changes

  • write_key_epoch stores an immutable generation-addressed record at key-epoch/<epoch>/generation-<n>.wsr1, refuses any n that is not the epoch's exact next generation, and leaves nothing behind on refusal.
  • load_key_epoch_set reads each epoch's newest generation from a gap-free chain, fails closed on unexpected names, gaps, or unopenable records, and ignores crash leftovers, relocated bytes, and empty epoch directories.
  • commit_root (before step C) and load_committed_root require the set to hash to the root's key_epoch_set_digest, with exactly one ACTIVE record at active_key_epoch binding the root's key route; otherwise RECOVERY_REQUIRED.
  • DurableFs gains create_dir_all, and new epoch directories are synced with their parents before promotion; the gate suite grows to 18 cases covering wrong sets, PREPARED-only or duplicate active epochs, route mismatches, tampered records, chain gaps, crash leftovers, and out-of-order generations.

Written for commit 9902802. Summary will update on new commits.

Review in cubic


CodeAnt-AI Description

Persist key-epoch records and reject roots with untrusted epoch bindings

What Changed

  • Key-epoch records are stored as immutable, generation-numbered files and must be written in sequence.
  • Root commits and cold starts now require the stored key-epoch set to match the root’s recorded digest.
  • The active key epoch must be exactly one active record tied to the root’s key route; tampered, incomplete, invalid, or mismatched records fail closed and require recovery.
  • Added coverage for tampering, chain gaps, out-of-order generations, prepared-only epochs, mismatched sets, and alternate key routes.

Impact

✅ Rejects roots with tampered or incomplete key-epoch data
✅ Prevents active epochs from using an unauthorized key route
✅ Fails closed during unsafe cold starts

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • New Features
    • Protected storage now preserves key-epoch records alongside root-of-trust data.
    • Root commits and startup verify that the stored epoch records match the root’s declared set and active key.
  • Bug Fixes
    • Storage now rejects inconsistent, incomplete, or incorrectly ordered key-epoch records rather than accepting an invalid state.

…epoch check (#445)

write_key_epoch persists generation-addressed key-epoch records under the root key route;
load_key_epoch_set reads each epoch's newest generation from a gap-free chain. commit_root (before
step C) and load_committed_root enforce contract §5.3.1 step 5: the set hashes to the root's
key_epoch_set_digest and active_key_epoch is exactly one KEY_EPOCH_ACTIVE record binding the root's
key route. DurableFs gains create_dir_all.
@vercel

vercel Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
worldscript-studio Ready Ready Preview Oct 2, 2026 3:28am UTC

@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 38 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available. Your 82 included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: aa464725-adeb-4b51-bb78-2e5d2fb7840e

📥 Commits

Reviewing files that changed from the base of the PR and between 529f0b2 and 9902802.

📒 Files selected for processing (4)
  • crates/worldscript-secure-storage/src/commit.rs
  • crates/worldscript-secure-storage/src/root_store.rs
  • crates/worldscript-secure-storage/tests/gate3c_root_commit_test.rs
  • docs/native/CORE-MIGRATION-LEDGER.md
📝 Walkthrough

Walkthrough

The secure-storage crate now writes generation-addressed key-epoch records and validates the persisted set against root data during commits and cold starts. Tests cover ordering, set and route mismatches, tampering, and gaps. Changelog and native storage records document the implemented slice and remaining work.

Changes

Key-epoch persistence and root validation

Layer / File(s) Summary
Storage primitives and record preparation
crates/worldscript-secure-storage/src/durable.rs, crates/worldscript-secure-storage/src/commit.rs, crates/worldscript-secure-storage/src/root_record.rs, crates/worldscript-secure-storage/src/root_store.rs
The filesystem interface adds directory creation. Key-epoch record preparation, generation parsing helpers, and root-store paths and recovery reasons support epoch storage and validation.
Epoch writing and root verification
crates/worldscript-secure-storage/src/root_store.rs, crates/worldscript-secure-storage/src/lib.rs, crates/worldscript-secure-storage/tests/*, crates/worldscript-secure-storage/tests/support/*, CHANGELOG.md, docs/native/CORE-MIGRATION-LEDGER.md, docs/native/R15-SECURE-STORAGE-CONTRACT.md
The root store writes sequential epoch records, loads gap-free epoch chains, and checks the epoch-set digest and active record against the root during commits and cold starts. Tests cover invalid sets, routes, tampering, gaps, and write order. The changelog and native records describe the slice and outstanding work.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to 529f0

The new key-epoch records can lose their directory entry after a power loss, which would leave a later startup requiring recovery. Production authority is not switched by this change, so this is a bounded follow-up. Add the parent directory sync before relying on this path.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

@sourcery-ai

sourcery-ai Bot commented Oct 2, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR persists authenticated key-epoch record generations and makes the root's key-epoch set—and its active epoch's binding to the committed key route—a required fail-closed invariant during both root commits and cold starts, with focused tests and Gate 3C documentation updates.

Sequence diagram for key-epoch persistence and root commit validation

sequenceDiagram
    participant Caller
    participant RootStore
    participant DurableFs
    participant KeyProvider
    participant Staging

    Caller->>RootStore: write_key_epoch()
    RootStore->>DurableFs: create_dir_all()
    RootStore->>DurableFs: list_dir()
    RootStore->>KeyProvider: resolve_ref()
    RootStore->>Staging: stage_and_promote()
    Staging-->>RootStore: KeyEpochEntry
    RootStore-->>Caller: KeyEpochEntry

    Caller->>RootStore: commit_root()
    RootStore->>RootStore: load_key_epoch_set()
    RootStore->>KeyProvider: resolve_ref()
    RootStore->>DurableFs: list_dir() and read()
    RootStore->>RootStore: verify_key_epochs()
    RootStore->>RootStore: prepare_root_anchor()
    RootStore-->>Caller: committed root or recovery error
Loading

Entity relationship diagram for persisted key-epoch generations

erDiagram
    KEY_EPOCH_SET {
        digest key_epoch_set_digest
    }
    KEY_EPOCH_RECORD {
        uint64 epoch
        uint64 registry_generation
        digest content_digest
        status status
        digest root_key_ref_digest
    }
    KEY_EPOCH_SET ||--o{ KEY_EPOCH_RECORD : contains
    KEY_EPOCH_RECORD ||--o{ KEY_EPOCH_RECORD : forms_gap_free_generation_chain
Loading

State diagram for trusted root loading with key-epoch checks

stateDiagram-v2
    [*] --> ReadCommittedRoot
    ReadCommittedRoot --> LoadKeyEpochSet
    LoadKeyEpochSet --> RecoveryRequired: unexpected name, gap, or record fails to open
    LoadKeyEpochSet --> VerifyKeyEpochs
    VerifyKeyEpochs --> RecoveryRequired: set digest mismatch
    VerifyKeyEpochs --> RecoveryRequired: active epoch not bound
    VerifyKeyEpochs --> TrustedRoot: exactly one KEY_EPOCH_ACTIVE matches root route
    TrustedRoot --> [*]
    RecoveryRequired --> [*]
Loading

File-Level Changes

Change Details Files
Added durable persistence and authenticated loading for key-epoch record chains.
  • Persist immutable, consecutively numbered records under per-epoch directories using the existing staging and promotion path.
  • Load the newest record from every gap-free epoch chain while rejecting malformed names, gaps, and authentication failures.
  • Expose key-epoch writing/loading APIs and add durable directory creation support.
crates/worldscript-secure-storage/src/root_store.rs
crates/worldscript-secure-storage/src/root_record.rs
crates/worldscript-secure-storage/src/durable.rs
crates/worldscript-secure-storage/src/commit.rs
crates/worldscript-secure-storage/src/lib.rs
crates/worldscript-secure-storage/tests/gate3_durable_test.rs
crates/worldscript-secure-storage/tests/support/mod.rs
Enforced the root-to-key-epoch binding during both commits and cold-start loading.
  • Verify the persisted set digest matches the root's key_epoch_set_digest before any root commit preparation or durable write.
  • Require exactly one active record for active_key_epoch and require its route digest to match root_key_ref_digest.
  • Return fail-closed recovery reasons for set mismatches and unbound or invalid active epochs.
crates/worldscript-secure-storage/src/root_store.rs
crates/worldscript-secure-storage/tests/gate3c_root_commit_test.rs
Expanded regression coverage for key-epoch integrity and updated Gate 3C contract status.
  • Add tests for mismatched set digests, prepared-only active records, route mismatches, tampering, chain gaps, and non-next generations.
  • Document slice 3c-1 and update R15 gate/status ledger entries while retaining the no-production-authority-switch constraint.
crates/worldscript-secure-storage/tests/gate3c_root_commit_test.rs
docs/native/R15-SECURE-STORAGE-CONTRACT.md
docs/native/CORE-MIGRATION-LEDGER.md

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@deepsource-io

deepsource-io Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 065b221...9902802 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Docker Oct 2, 2026 3:28a.m. Review ↗
Python Oct 2, 2026 3:28a.m. Review ↗
Rust Oct 2, 2026 3:28a.m. Review ↗
Shell Oct 2, 2026 3:28a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@codeant-ai

codeant-ai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: 9902802b
Scan Time: 2026-10-02 03:48:52 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

codescene-access[bot]

This comment was marked as outdated.

@qnbs
qnbs marked this pull request as ready for review October 2, 2026 03:04
@qnbs

qnbs commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@codeant-ai

codeant-ai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 619ae4a Oct 02, 2026 · 03:04 03:06

@sourcery-ai sourcery-ai 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.

Sorry @qnbs, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 5 days and 22 hours by commenting @sourcery-ai review. Upgrade to get a review now.

@codeant-ai

codeant-ai Bot commented Oct 2, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 2, 2026
codescene-access[bot]

This comment was marked as outdated.

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

[check-pr-size] PR size is over the target tier (normal profile): 11 files, 497 meaningful lines, 3 commits — limit ≤8 files / ≤400 lines / ≤6 commits. Consider splitting into smaller, independently reviewable PRs.

Comment thread crates/worldscript-secure-storage/src/root_store.rs Outdated

@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: 1

🧹 Nitpick comments (1)
crates/worldscript-secure-storage/tests/gate3c_root_commit_test.rs (1)

544-640: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for an uncommitted key-epoch generation.

After root generation 1 is committed, write_key_epoch can persist generation 2 without publishing a new root. Add a cold-start test that leaves the committed root digest unchanged and asserts RootStoreError::RecoveryRequired(RootRecoveryReason::KeyEpochSetMismatch). If this check regresses, cold start may accept a key-epoch generation that the committed root does not name.

Suggested test
 #[test]
 fn cold_start_fails_closed_on_a_tampered_or_gapped_key_epoch_chain() {
     let mut fixture = Fixture::new();
     fixture.commit(&mut StdFs, 1).unwrap();
     let record = fixture
         .root_dir
         .join("key-epoch")
         .join("1")
         .join("generation-1.wsr1");
     let original = fs::read(&record).unwrap();
     flip_last_byte(&record);
     let tampered = fixture.loaded_generation();
     fs::write(&record, &original).unwrap();
     // A later generation without the first one is a gap, never a shorter chain.
     fs::rename(&record, record.with_file_name("generation-2.wsr1")).unwrap();
     let gapped = fixture.loaded_generation();
     let mismatch = Err(RootStoreError::RecoveryRequired(
         RootRecoveryReason::KeyEpochSetMismatch,
     ));
     assert_eq!((tampered, gapped), (mismatch.clone(), mismatch));
 }
 
+#[test]
+fn cold_start_rejects_a_key_epoch_written_without_root_commit() {
+    let mut fixture = Fixture::new();
+    fixture.commit(&mut StdFs, 1).unwrap();
+    fixture
+        .write_epoch(1, KeyEpochStatus::Active, 2)
+        .unwrap();
+    assert_eq!(
+        fixture.loaded_generation(),
+        Err(RootStoreError::RecoveryRequired(
+            RootRecoveryReason::KeyEpochSetMismatch,
+        ))
+    );
+}
+
 #[test]
 fn key_epoch_generations_are_written_strictly_in_order() {

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: qnbs/WorldScript-Studio/.coderabbit.yaml

Review profile: CHILL

Plan: Essentials

Run ID: d2241777-36eb-47ce-9675-164d9be353fb

📥 Commits

Reviewing files that changed from the base of the PR and between 065b221 and 529f0b2.

📒 Files selected for processing (11)
  • CHANGELOG.md
  • crates/worldscript-secure-storage/src/commit.rs
  • crates/worldscript-secure-storage/src/durable.rs
  • crates/worldscript-secure-storage/src/lib.rs
  • crates/worldscript-secure-storage/src/root_record.rs
  • crates/worldscript-secure-storage/src/root_store.rs
  • crates/worldscript-secure-storage/tests/gate3_durable_test.rs
  • crates/worldscript-secure-storage/tests/gate3c_root_commit_test.rs
  • crates/worldscript-secure-storage/tests/support/mod.rs
  • docs/native/CORE-MIGRATION-LEDGER.md
  • docs/native/R15-SECURE-STORAGE-CONTRACT.md

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.

Comment thread crates/worldscript-secure-storage/src/root_store.rs Outdated
@codecov

codecov Bot commented Oct 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/worldscript-secure-storage/src/root_store.rs
Comment thread crates/worldscript-secure-storage/src/root_store.rs Outdated
Comment thread docs/native/CORE-MIGRATION-LEDGER.md Outdated
Comment thread crates/worldscript-secure-storage/src/root_store.rs Outdated
Comment thread crates/worldscript-secure-storage/src/root_store.rs Outdated

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 11 files

Requires human review: Auto-approval blocked because this review re-detected 3 unresolved issues already reported by Cubic.

Re-trigger cubic

…overs; require exactly one active epoch (#445)

Review wave on PR #945: write_key_epoch validates before creating anything and syncs the new epoch
directory and its parents before promoting; load_key_epoch_set skips an empty epoch directory and
ignores staging leftovers and relocated bytes (the root's set digest binds what counts); step 5
requires exactly one KEY_EPOCH_ACTIVE record overall, at active_key_epoch, binding the route.

@codescene-access codescene-access 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.

Gates Passed
3 Quality Gates Passed

See analysis details in CodeScene

Quality Gate Profile: The Bare Minimum
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@qnbs
qnbs enabled auto-merge (squash) October 2, 2026 03:48
@qnbs
qnbs merged commit f43c91a into main Oct 2, 2026
51 checks passed
@qnbs
qnbs deleted the feat/445-gate3c-key-epochs branch October 2, 2026 03:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant