436 chain of command restructure for receptors#440
Merged
Conversation
e3052d0 to
1a2aba2
Compare
Collaborator
Author
|
ready for review |
evomimic
approved these changes
Apr 6, 2026
Owner
evomimic
left a comment
There was a problem hiding this comment.
Review Findings
Approving for merge with explicit defer of known non-blocking issues.
Context: this PR is being merged to unblock a queued PR convoy, and current validation is green:
- happ build passes
- host build passes
- npm tests pass
- app runs in both
HC_DEV_MODE=1andHC_DEV_MODE=0
Known issues deferred (tracked for follow-up integration work):
get_recovery_receptor_from_factorycurrently drops computed result and returnsNone(recovery receptor lookup path).root_spacecommand currently usesunimplemented!()placeholder.- Local setup currently only registers local receptors when
"recovery"feature is enabled. - LocalRecovery wiring is still partial beyond lookup (session/runtime path not fully connected end-to-end).
- Recovery trigger integration (
snapshot_after, undo/redo/lifecycle cleanup): still pending full command-path hookup. - Receptor factory/runtime transition: still in-progress; edge behavior finalization deferred to follow-up integration.
Given current scope/usage, these are being deferred to the follow-up integration sequence rather than blocking this merge. I've created Issue #441 to ensure these issues are tracked going forward.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chain-of-Command Restructure for Receptors
1. Problem Statement (Issue #436)
2. Architectural Changes
2.1 Chain-of-Command (Before → After)
Before:
After:
The Runtime now selects receptors through
ReceptorFactory. Theholons_clientis the owner of receptor lifecycle. Each concrete receptor type handles only its own concerns.2.2
ReceptorBehaviourRemoved →ReceptorEnumThe
ReceptorBehaviourtrait was removed. In its place,holons_client/src/lib.rsexposes aReceptorenum:Methods like
handle_map_request,get_space_info, andtransaction_contextare dispatched through this enum. New receptor variants are added to the enum rather than implementing a shared trait.2.3
ReceptorTypeas the Shared IdentityReceptorType(in the newshared_typescrate) is the canonical identifier for a receptor's role:BaseReceptorcarries areceptor_type, areceptor_id(derived from the name instorage.json), optionalclient_handler, andproperties. This is the configuration contract between setup and the factory.3. New Crates Introduced
3.1
recovery_receptor(host/crates/recovery_receptor/)Purpose: A self-contained receptor for crash-resilient transaction snapshot persistence, backed by SQLite. This is the outcome of the architectural discussion in PR #418 — recovery is now its own receptor, not a flag on the Holochain receptor.
Key types:
LocalRecoveryReceptorlocal_recovery_receptor.rsTransactionRecoveryStorestorage/transaction_store.rsrecovery_session+recovery_checkpointTransactionSnapshotstorage/transaction_snapshot.rsRecoveryStore(trait)storage/recovery_store.rsLocalRecoveryReceptorlifecycle:ReceptorFactorywhen aBaseReceptorof typeLocalRecoveryis registered.init_session(context)— called once during runtime init. Attempts crash recovery by checking for orphaned transactions in the store.persist(description, disable_undo)— called after every successful command to checkpoint the transaction state.undo()/redo()— restore prior/forward checkpoints.SQLite schema (embedded, no migration files):
Commit/rollback deletes the entire transaction via
CASCADE.3.2
shared_types(host/crates/shared_types/)Purpose: Extracts host-side shared type definitions out of
holons_client/src/shared_types/into a standalone crate, reducing coupling.Exports:
base_receptorBaseReceptor,ReceptorTypemap_requestMapRequestmap_responseMapResponseholon_spaceHolonSpace,SpaceInfoThis crate is re-exported as
client_shared_typesin workspace members.4. Crate Renames
holochain_receptordeprecated_holochain_receptorholons_receptorlocal_receptorsholons_client.5. Changes to
holons_clientholons_clientis now the owner of receptor lifecycle.New files:
receptor_factory.rsReceptorFactory— creates receptors fromBaseReceptorconfigs, registers them in the cache. Moved fromholons_receptor.receptor_cache.rsReceptorCache— thread-safeArc<Mutex<HashMap<ReceptorKey, Arc<Receptor>>>>. Look up by type or by ID.client_session.rsClientSession— wraps aHolonSpaceManager+ optionalLocalRecoveryReceptor. Opens (or recovers) a transaction on construction.ReceptorFactoryAPI:ClientSessionconstruction:On creation:
recoveryisSome(LocalRecovery(...))and the store has orphaned transactions from a prior crash → reopens that transaction and restores its snapshot.6. Changes to
conductorasetup/providers/local/setup.rsLocalSetup::setup()now checks whether the"recovery"feature is listed inLocalConfig.features. If so, it creates aTransactionRecoveryStore(async, offloaded viaspawn_blocking) and registers aBaseReceptorof typeLocalRecovery.runtime/init_runtime.rsinit_from_state()now:HolonSpaceManagerviainit_client_runtime(Some(initiator)).LocalRecoveryReceptorfrom theReceptorFactoryin Tauri app state.ClientSessionwith the space manager and recovery receptor.RuntimeSessionandRuntimeinto Tauri app state.If no recovery receptor is registered (e.g., the
"recovery"feature is disabled in config), recovery is silently disabled.config/storage.jsonA new
"local_recovery"provider entry is registered:This is what triggers
LocalSetupto create theLocalRecoveryReceptorat startup.setup/receptor_config_registry.rsUpdated to handle the new receptor types and IDs from the refactored provider setup. Storage provider entries with the same name (which becomes
receptor_id) now cause a startup failure, enforcing uniqueness.7. Crate Dependency Graph (Post-Commit)
recovery_receptorhas no dependency on Holochain. It depends only oncore_types,holons_core, andrusqlite.8. What Is Still Pending
LocalReceptorimplementationlocal_receptorscrate exists but is fully stubbed outClientSessionmethodscommit,undo,redo,add,save,listare noted as TODOsget_root_spaces()NotImplementedClientSessionis created but not yet wired into command dispatchsnapshot_afterpolicy hookcan_redo() == falseafter new command following undo9. Key Principles Established
Recovery persistence is its own receptor. It is not a flag on the Holochain receptor or any other primary storage receptor. It is configured independently in
storage.jsonand provisioned via its own provider path.ReceptorTypedrives lookup, not string matching. The factory resolves receptors byReceptorTypeenum, eliminating the hardcoded"holochain"string-key lookup bug fixed in PR 412 recovery and transactional snapshots #418.storage.jsonprovider names are source of truth forreceptor_id. Duplicate names cause a startup failure. The name becomes the receptor's canonical ID in the factory cache.Blocking I/O stays off the async thread. SQLite open and directory creation are wrapped in
tokio::task::spawn_blocking.Crash recovery is automatic. On startup,
ClientSession::newchecks the recovery store for orphaned transactions. If found, the session reopens the transaction and restores the last snapshot before returning to the caller.