Skip to content

Conversation

@ryzen-xp
Copy link
Contributor

@ryzen-xp ryzen-xp commented Jan 22, 2026

🧠 SkillSphere Pull Request 🌐

Mark with an x all the checkboxes that apply (like [x])

  • Closes Implement "Ban Expert" Logic #2
  • Added tests (if necessary)
  • Ran cargo test (All tests passed)
  • Evidence attached (Screenshots, Logs, or Transaction Hashes)
  • Commented the code

📌 Type of Change

  • 📚 Documentation (updates to README, docs, or comments)
  • 🐛 Bug fix (non-breaking change which fixes an issue)
  • ✨ Enhancement (non-breaking change which adds functionality)
  • 💥 Breaking change (fix or feature that would cause existing functionality to change)
  • 🏗️ Refactor (code improvement/cleanup without logical changes)

📝 Changes Description

Implemented Ban Expert security feature to enable admins to revoke verification status from bad actors.

Changes:

  • Added ban_expert() function in src/contract.rs with admin auth
  • Exposed ban_expert() and get_status() in src/lib.rs
  • Added AlreadyBanned error variant
  • Created comprehensive test suite (8 tests covering main flow, edge cases, and authorization)

Storage Impact: Single storage write + event emission (similar gas cost to verify_expert)


📸 Evidence

Screenshot From 2026-01-22 19-21-00

Tests include:

  • ✅ Main ban functionality
  • ✅ Admin-only enforcement
  • ✅ Duplicate ban prevention
  • ✅ Edge cases (unverified experts, lifecycle)

Summary by CodeRabbit

  • New Features

    • Admins can initialize and be set as the contract admin (protected against reinitialization).
    • Admins can verify experts individually or in batches.
    • Admins can ban experts.
    • Query an expert’s current status.
    • Status-change events now record admin-driven verifications and bans.
  • Tests

    • Expanded coverage for banning, verification (including batch), status transitions, unauthorized attempts, uninitialized cases, and full expert lifecycle.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

📝 Walkthrough

Walkthrough

Adds admin-managed registry initialization, admin-authorized expert verification and banning, batch expert addition, a public status getter, admin storage helpers (with TTL touches), status-change events, and expanded tests covering verification and banning lifecycles.

Changes

Cohort / File(s) Summary
Core contract logic
contracts/identity-registry-contract/src/contract.rs
Added initialize_registry, batch_add_experts, ban_expert, get_expert_status; changed verify_expert to require admin auth, update storage, and emit ExpertStatusChanged events.
Public contract interface
contracts/identity-registry-contract/src/lib.rs
Exposed ban_expert(env, expert) and get_status(env, expert) -> ExpertStatus; adjusted imports to bring ExpertStatus into scope; retained existing init/add APIs.
Storage & helpers
contracts/identity-registry-contract/src/storage.rs
Added has_admin, set_admin, get_admin, get_expert_status; get_expert_record/set_expert_record now extend TTL on reads/writes.
Events
contracts/identity-registry-contract/src/events.rs
Added derives to ExpertStatusChangedEvent, reordered imports and reformatted emit helper (no semantic change).
Tests
contracts/identity-registry-contract/src/test.rs
Replaced env.register_contract with env.register(...); added extensive ban-related tests and full lifecycle coverage; updated assertions to use RegistryError and ExpertStatus.
Types & Errors
contracts/identity-registry-contract/src/types.rs, contracts/identity-registry-contract/src/error.rs
Minor import/formatting and newline adjustments; no public API signature changes.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Admin as Admin (caller)
participant Contract as IdentityRegistryContract
participant Storage as Storage
participant Events as EventSink

Admin->>Contract: initialize_registry(admin) / ban_expert(expert) / batch_add_experts(...)
Note right of Contract: require admin auth (when applicable)
Contract->>Storage: get_expert_record(expert)
alt record exists and status != Banned
Contract->>Storage: set_expert_record(status = Banned)
Contract->>Events: emit_status_change(expert, Banned, admin)
Contract-->>Admin: Ok
else already Banned
Contract-->>Admin: Err(AlreadyBanned)
else not initialized
Contract-->>Admin: Err(NotInitialized)
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Bosun-Josh121

Poem

🐇
I nibble ledger carrots, hop the gate,
Admin stamps, I celebrate,
Verified then banned — a tidy feat,
Events sing out with every beat,
Hooray — the registry's neat!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ❓ Inconclusive Changes include initialize_registry(), batch_add_experts(), and administrative helper functions not explicitly mentioned in issue #2, which are scope-adjacent but not core to the ban feature. Clarify whether initialize_registry(), batch_add_experts(), admin helpers in storage.rs, and changes to verify_expert() flow are intentional features supporting the ban logic or separate enhancements that should be in different issues.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: implementing ban expert logic, which is the primary focus of this pull request.
Description check ✅ Passed The PR description follows the template with all required sections completed: checkboxes, type of change, changes description, and evidence provided.
Linked Issues check ✅ Passed All requirements from issue #2 are met: ban_expert() implemented with admin auth [#2], AlreadyBanned error added [#2], function exposed in lib.rs [#2], and comprehensive test suite created [#2].
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 docstrings

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

@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

🤖 Fix all issues with AI agents
In `@contracts/identity-registry-contract/src/contract.rs`:
- Around line 41-62: In verify_expert, add a guard to prevent re-verifying a
banned expert: fetch current_status via storage::get_expert_status(env, expert)
(same as ban_expert uses) and if current_status == ExpertStatus::Banned return
Err(RegistryError::AlreadyBanned) before the existing AlreadyVerified check;
this enforces that ban revokes verification (alternatively implement a separate
unban_expert with explicit authorization if unbanning is allowed).
🧹 Nitpick comments (3)
contracts/identity-registry-contract/src/lib.rs (1)

35-37: Consider adding a doc comment for API consistency.

The other public methods (init, add_expert, ban_expert) have doc comments, but get_status does not. Adding one would maintain consistency in the public API documentation.

📝 Suggested doc comment
+    /// Get the current status of an expert
     pub fn get_status(env: Env, expert: Address) -> ExpertStatus {
         contract::get_expert_status(&env, &expert)
     }
contracts/identity-registry-contract/src/test.rs (2)

99-130: Consider adding explicit authorization verification for ban_expert.

The test validates the ban flow and error handling, but unlike test_add_expert (lines 45-58), it doesn't explicitly verify that ban_expert requires admin authorization via AuthorizedInvocation check. Additionally, per PR objectives, ban_expert should emit an ExpertStatusChanged event, but this isn't verified.

♻️ Suggested additions to enhance test coverage
     // Ban the expert (should succeed)
     client.ban_expert(&expert);
 
+    // Verify admin authorization was required
+    assert_eq!(
+        env.auths().last().unwrap(),
+        (
+            admin.clone(),
+            AuthorizedInvocation {
+                function: AuthorizedFunction::Contract((
+                    contract_id.clone(),
+                    Symbol::new(&env, "ban_expert"),
+                    (expert.clone(),).into_val(&env)
+                )),
+                sub_invocations: std::vec![]
+            }
+        )
+    );
+
+    // Verify ExpertStatusChanged event was emitted
+    let events = env.events().all();
+    let event = events.last().unwrap();
+    assert_eq!(event.0, contract_id);
+    let topic: Symbol = event.1.get(0).unwrap().try_into_val(&env).unwrap();
+    assert_eq!(topic, Symbol::new(&env, "status_change"));
+
     // Check that status is now Banned
     let status = client.get_status(&expert);

144-152: Simplify the authorization mocking setup.

The sequence of mock_all_auths_allowing_non_root_auth() followed by mock_auths(&[]) is confusing. A simpler pattern, consistent with test_add_expert_unauthorized, would be to avoid any auth mocking before calling ban_expert:

♻️ Simplified version
     client.init(&admin);
 
     env.mock_all_auths();
     client.add_expert(&expert);
 
-    env.mock_all_auths_allowing_non_root_auth();
-
-    env.mock_auths(&[]);
+    // Clear auth mocking - subsequent calls will require real authorization
+    env.mock_auths(&[]);
 
     client.ban_expert(&expert);
 }

Alternatively, for maximum clarity and consistency with test_add_expert_unauthorized, you could use a scoped approach where auth mocking only applies to the add_expert call.

Comment on lines +41 to +62
pub fn ban_expert(env: &Env, expert: &Address) -> Result<(), RegistryError> {
let admin = storage::get_admin(env).ok_or(RegistryError::NotInitialized)?;
admin.require_auth();

let current_status = storage::get_expert_status(env, expert);

if current_status == ExpertStatus::Banned {
return Err(RegistryError::AlreadyBanned);
}

storage::set_expert_record(env, expert, ExpertStatus::Banned);

events::emit_status_change(
env,
expert.clone(),
current_status,
ExpertStatus::Banned,
admin,
);

Ok(())
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the verify_expert function implementation
cat -n contracts/identity-registry-contract/src/contract.rs | sed -n '1,80p'

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 2208


🏁 Script executed:

# Search for test files related to ban and verify operations
fd -e rs -path "*/test*" | head -20

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 247


🏁 Script executed:

# Look for any tests that mention ban_expert or the interaction between ban and verify
rg "ban_expert|Banned.*Verified|verify.*ban" -B 3 -A 3 --type rust

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 7289


🏁 Script executed:

# Check if there's a verify_expert function and see its implementation
ast-grep --pattern 'fn verify_expert($_) { $$$ }'

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 61


🏁 Script executed:

# Look for any comments, TODOs, or documentation about expert status handling
rg "TODO|FIXME|NOTE|preemptive|unbann" --type rust -i

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 61


🏁 Script executed:

# Search more thoroughly for tests that might cover re-verification of banned experts
cat -n contracts/identity-registry-contract/src/test.rs | head -300

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 9485


🏁 Script executed:

# Look for the complete test_ban_expert_workflow test
rg "test_ban_expert_workflow" -A 50 -B 5

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 4640


🏁 Script executed:

# Check ExpertStatus enum to understand all possible states
rg "enum ExpertStatus" -A 10

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 847


🏁 Script executed:

# Look for any documentation or comments about the intended behavior of ban/verify
rg "Ban|Verified|expert" contracts/identity-registry-contract/src/lib.rs -B 2 -A 2

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 600


🏁 Script executed:

# Check if there's any test for re-verifying a banned expert (verify after ban)
rg "ban_expert.*verify_expert|verify_expert.*ban_expert" -B 5 -A 5

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 61


🏁 Script executed:

# Search more comprehensively for any mention of re-verification after ban
rg "Verified.*Banned|Banned.*Verified" -B 3 -A 3

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 61


🏁 Script executed:

# Check if there's documentation or requirements about the design
find . -name "README*" -o -name "REQUIREMENTS*" -o -name "*.md" | head -10

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 125


🏁 Script executed:

# Look for any TODOs or FIXMEs related to ban/verify logic
rg "TODO|FIXME" contracts/identity-registry-contract/ -B 2 -A 2

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 61


🏁 Script executed:

# Check the README for design documentation or requirements
cat README.md

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 8315


🏁 Script executed:

# Look specifically for the PR template to understand requirements
cat ./.github/pull_request_template.md

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 1480


🏁 Script executed:

# Check if there's any test attempting to re-verify a banned expert
rg "ban_expert" -A 20 contracts/identity-registry-contract/src/test.rs | rg -A 15 "verify_expert|add_expert"

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 1605


🏁 Script executed:

# Look at complete_expert_lifecycle test to confirm it doesn't test re-verification after ban
cat -n contracts/identity-registry-contract/src/test.rs | sed -n '238,261p'

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 971


Add a check in verify_expert to prevent re-verifying banned experts.

The implementation currently allows re-verifying a Banned expert back to Verified status. The verify_expert function only guards against AlreadyVerified but does not check Banned status. This conflicts with the documented semantics that banning "revokes" verification. Add: if current_status == ExpertStatus::Banned { return Err(RegistryError::AlreadyBanned); } before the existing check, or introduce an explicit unban_expert function if unbanning should require separate authorization.

🤖 Prompt for AI Agents
In `@contracts/identity-registry-contract/src/contract.rs` around lines 41 - 62,
In verify_expert, add a guard to prevent re-verifying a banned expert: fetch
current_status via storage::get_expert_status(env, expert) (same as ban_expert
uses) and if current_status == ExpertStatus::Banned return
Err(RegistryError::AlreadyBanned) before the existing AlreadyVerified check;
this enforces that ban revokes verification (alternatively implement a separate
unban_expert with explicit authorization if unbanning is allowed).

Copy link

@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

🤖 Fix all issues with AI agents
In `@contracts/identity-registry-contract/src/storage.rs`:
- Around line 7-9: The admin record stored under DataKey::Admin currently never
extends its instance TTL, so modify has_admin, set_admin, and get_admin to call
the same extend_ttl() used for expert records: after successfully setting the
admin in set_admin call extend_ttl() to bump TTL; in get_admin and has_admin, if
the admin is found return the value and call extend_ttl() to refresh TTL (mirror
the expert get/set pattern). Locate DataKey::Admin and the functions has_admin,
set_admin, and get_admin and add the extend_ttl() invocation in the same spots
and order as the expert record code to ensure consistent TTL behavior.
♻️ Duplicate comments (1)
contracts/identity-registry-contract/src/contract.rs (1)

18-29: Block re‑verification of banned experts.

verify_expert only guards Verified, so a Banned expert can be re‑verified, undermining the ban semantics. Add an explicit Banned check before the existing guard.

🐛 Suggested guard in verify_expert
     let current_status = storage::get_expert_status(env, expert);

+    if current_status == ExpertStatus::Banned {
+        return Err(RegistryError::AlreadyBanned);
+    }
     if current_status == ExpertStatus::Verified {
         return Err(RegistryError::AlreadyVerified);
     }

Comment on lines 7 to +9
pub enum DataKey {
Admin,
Expert(Address),
Admin,
Expert(Address),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n contracts/identity-registry-contract/src/storage.rs | head -50

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 214


🏁 Script executed:

cat contracts/identity-registry-contract/src/storage.rs

Repository: LightForgeHub/SkillSphere-Contracts

Length of output: 2568


Extend admin instance TTL to prevent silent expiration.

Admin is stored in instance storage without TTL extension. Unlike expert records which correctly call extend_ttl() on both set and get operations, admin functions (has_admin, set_admin, get_admin) never bump the TTL. Once the admin expires, has_admin() silently returns false and the registry becomes unmanaged or can be re-initialized. Apply the same TTL extension pattern used for expert records:

TTL extension for admin storage
 pub fn has_admin(env: &Env) -> bool {
-    env.storage().instance().has(&DataKey::Admin)
+    let has = env.storage().instance().has(&DataKey::Admin);
+    if has {
+        env.storage().instance().extend_ttl(LEDGERS_THRESHOLD, LEDGERS_EXTEND_TO);
+    }
+    has
 }

 pub fn set_admin(env: &Env, admin: &Address) {
     env.storage().instance().set(&DataKey::Admin, admin);
+    env.storage().instance().extend_ttl(LEDGERS_THRESHOLD, LEDGERS_EXTEND_TO);
 }

 pub fn get_admin(env: &Env) -> Option<Address> {
-    env.storage().instance().get(&DataKey::Admin)
+    let admin = env.storage().instance().get(&DataKey::Admin);
+    if admin.is_some() {
+        env.storage().instance().extend_ttl(LEDGERS_THRESHOLD, LEDGERS_EXTEND_TO);
+    }
+    admin
 }
🤖 Prompt for AI Agents
In `@contracts/identity-registry-contract/src/storage.rs` around lines 7 - 9, The
admin record stored under DataKey::Admin currently never extends its instance
TTL, so modify has_admin, set_admin, and get_admin to call the same extend_ttl()
used for expert records: after successfully setting the admin in set_admin call
extend_ttl() to bump TTL; in get_admin and has_admin, if the admin is found
return the value and call extend_ttl() to refresh TTL (mirror the expert get/set
pattern). Locate DataKey::Admin and the functions has_admin, set_admin, and
get_admin and add the extend_ttl() invocation in the same spots and order as the
expert record code to ensure consistent TTL behavior.

@ryzen-xp
Copy link
Contributor Author

Hello !!! can you please give me compliments !!

@Bosun-Josh121
Copy link
Collaborator

kindly fix the conflicts then ready for merge! @ryzen-xp

@ryzen-xp
Copy link
Contributor Author

Hello @Bosun-Josh121 check it

Copy link

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

🤖 Fix all issues with AI agents
In `@contracts/identity-registry-contract/src/contract.rs`:
- Around line 1-5: The file imports ExpertStatus twice causing a duplicate
import error (E0252); remove the redundant use of ExpertStatus so only a single
import remains (keep the canonical import from crate::types::ExpertStatus and
delete the duplicate in the grouped crate import), ensuring functions/types that
reference ExpertStatus still compile and no other use statements are
accidentally removed.

In `@contracts/identity-registry-contract/src/test.rs`:
- Around line 5-8: The test import block contains a duplicate import of
ExpertStatus which causes E0252; remove the redundant import so ExpertStatus is
imported only once (for example keep crate::types::ExpertStatus and delete the
duplicate in the crate::{storage, types::ExpertStatus} or vice versa) and ensure
the remaining imports still include IdentityRegistryContract,
IdentityRegistryContractClient and storage so types and clients resolve
correctly.
♻️ Duplicate comments (1)
contracts/identity-registry-contract/src/contract.rs (1)

40-59: Prevent re-verifying banned experts (ban should be terminal unless explicitly unbanned).

verify_expert still allows a Banned expert to be set back to Verified, which undermines the “ban revokes verification” intent. Consider blocking Banned here (and/or introduce a dedicated unban flow).

🔧 Proposed fix
     let current_status = storage::get_expert_status(env, expert);

+    if current_status == ExpertStatus::Banned {
+        return Err(RegistryError::AlreadyBanned);
+    }
     if current_status == ExpertStatus::Verified {
         return Err(RegistryError::AlreadyVerified);
     }

@Bosun-Josh121
Copy link
Collaborator

Hello @ryzen-xp, please makes sure there's no build errors

@ryzen-xp
Copy link
Contributor Author

Hello @Bosun-Josh121 can you recheck

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
contracts/identity-registry-contract/src/contract.rs (2)

17-37: Block re-verification of banned experts in batch flow.

batch_add_experts will currently re-verify Banned experts. If ban is meant to be revocation, this should return AlreadyBanned before setting status to Verified.

✅ Proposed fix
     for expert in experts {
         let status = storage::get_expert_status(&env, &expert);
+        if status == ExpertStatus::Banned {
+            return Err(RegistryError::AlreadyBanned);
+        }
         if status == ExpertStatus::Verified {
             return Err(RegistryError::AlreadyVerified);
         }
         storage::set_expert_record(&env, &expert, ExpertStatus::Verified);
         events::emit_status_change(&env, expert, status, ExpertStatus::Verified, admin.clone());
     }

6-15: Add admin.require_auth() to prevent admin squatting during initialization.

Any caller can invoke initialize_registry and designate themselves as the admin before the intended admin does. This is a critical vulnerability since the admin role controls all expert verification and banning. All other admin functions (batch_add_experts, verify_expert, ban_expert) properly call admin.require_auth(); initialization must do the same. Note: the same vulnerability exists in payment-vault-contract/src/contract.rs in the initialize_vault function.

🔒 Proposed fix
 pub fn initialize_registry(env: &Env, admin: &Address) -> Result<(), RegistryError> {
     if storage::has_admin(env) {
         return Err(RegistryError::AlreadyInitialized);
     }
 
+    admin.require_auth();
     storage::set_admin(env, admin);
 
     Ok(())
 }
🤖 Fix all issues with AI agents
In `@contracts/identity-registry-contract/src/contract.rs`:
- Around line 17-19: Update the function doc comment to accurately describe
batch_add_experts: replace the singular "Verify an expert" line with a
description indicating this function batch-verifies multiple experts
(admin-only), e.g., note that batch_add_experts takes Env and Vec<Address> and
sets multiple experts' status to Verified, returning Result<(), RegistryError>;
keep mention of admin-only and Batch Verification.
♻️ Duplicate comments (1)
contracts/identity-registry-contract/src/contract.rs (1)

39-61: Prevent re-verifying banned experts (already flagged).

Same concern as previous review: add a Banned guard before the AlreadyVerified check.

Comment on lines +17 to 19
/// Verify an expert by setting their status to Verified (Admin only)
/// Batch Verification
pub fn batch_add_experts(env:Env, experts: Vec<Address>) -> Result<(), RegistryError> {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the batch function doc comment.

The doc comment says “Verify an expert” but this function batch-verifies. Consider updating for accuracy.

🤖 Prompt for AI Agents
In `@contracts/identity-registry-contract/src/contract.rs` around lines 17 - 19,
Update the function doc comment to accurately describe batch_add_experts:
replace the singular "Verify an expert" line with a description indicating this
function batch-verifies multiple experts (admin-only), e.g., note that
batch_add_experts takes Env and Vec<Address> and sets multiple experts' status
to Verified, returning Result<(), RegistryError>; keep mention of admin-only and
Batch Verification.

@Bosun-Josh121
Copy link
Collaborator

Check tests as well @ryzen-xp

@ryzen-xp
Copy link
Contributor Author

hi @Bosun-Josh121 , plz give me some compliments on drip .

@Bosun-Josh121 Bosun-Josh121 merged commit 106db3c into LightForgeHub:main Jan 23, 2026
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

Development

Successfully merging this pull request may close these issues.

Implement "Ban Expert" Logic

2 participants