PR: feat: wasm32 baseline — pure FIH state machine with trait-injected clock #{80}#82
Merged
Conversation
metadata as JSON in Fact content
Phase 1 of model integration. Replaced custom Fact/Intent/Hint types with nexus_model::fih::* types. DocMeta stored inside Fact.content.data as JSON blob. Key insight: nex (DefaultBlackboard + petgraph + sha2) exceeds CF Workers free tier CPU limit (10ms). nexus-model types + async KV handlers is the practical approach — same types, same data model, same APIs as gateway/api and storage/ve-composite. Changes: - Added nexus-model dep, removed nex dep - Custom Fact/Intent/Hint replaced with nexus_model::fih imports - DocMeta helper for document metadata inside Content.data - All handlers use nexus-model types (Fact, Intent, FihHash, Content) - ID encoding (/, # → _) for KV key safety
IoBufferKv → AsyncStoreKv IoBufferBlob → AsyncStoreBlob IoBufferObject → AsyncStoreObject IoBufferSessionMeta → AsyncStoreSessionMeta IoBufferSession → AsyncStoreSession iobuf.rs → async_store.rs Motivation: The old name was ambiguous (I/O context? file I/O?). AsyncStore clearly communicates the role: sync trait implementations backed by HashMap buffers that bridge between async CF bindings and sync core logic. All store traits (MetaStore, BlobStore, ObjectStore) remain unchanged. All tests pass, clippy clean, zero IoBuffer references remaining.
bindings.rs provides typed CF binding wrappers (WorkerEnv) for future DO/R2 operations. Removal would cause functional regression when DO CAS integration begins.
CfBlackboard wraps AsyncStoreKv (from nex) to implement: - FactCapable, IntentCapable: sync &self via Arc<RwLock<HashMap>> - Blackboard: delegates to Capable traits via &*self coercion - hydrate/drain: async bridge to CF KV Key architecture: hydrate CF KV → AsyncStoreKv (async) Blackboard trait methods (sync, pure logic) drain AsyncStoreKv → CF KV (async) Lightweight: no petgraph, no SHA256. LTO strips unused nex code. bindings.rs preserved (no functional regression).
…#{80}
CfBlackboard now holds:
- facts/intents: AsyncStoreKv (FIH CRUD, sync)
- session: AsyncStoreSession (CompositeColdStorage, cursor/snapshot)
hydrate_session/drain_session bridge cursor between CF KV and
session.meta_buf(). Full 3-tier store lifecycle:
hydrate → Blackboard trait (sync logic) → drain
All tests pass, clippy clean.
Zero dependency on nex/petgraph. Uses nexus_model::fih types (Fact, Intent, FihHash, Content) with CF KV for persistence. Verified on CF Workers Free tier: - POST /facts → stores Fact to KV - GET /state → returns counts - POST /intents → creates Intent - POST /intents/:id/claim → CAS claim (409 on conflict) - POST /intents/:id/conclude → creates result Fact Root cause of earlier 1101 errors: DefaultBlackboard + petgraph exceeds 10ms CPU limit on Free tier. Paid plan required for nex core. This version works on Free tier and uses the same nexus-model types as gateway/api and storage/ve-composite.
Free tier: - thread_local! is destroyed between requests (isolate recycling not guaranteed) - DefaultBlackboard + petgraph + SHA256 exceeds 10ms CPU limit on every cold start - 1101 errors on first request, state lost Paid plan (30s CPU, persistent isolates): - thread_local! survives across requests - DefaultBlackboard state persists correctly - petgraph integration works Switching to DefaultBlackboard with thread_local!. KV-backed fallback available when nex dep is removed. To unlock: upgrade Workers plan to Paid.
…ock #{80}
model:
- Blackboard trait: &mut self → &self (interior mutability belongs to impl)
- Remove Send+Sync from fine-grained storage traits (BlobStore, MetaStore,
ObjectStore, StorageRead, Now)
- Add StorageSend marker trait for aggregate traits (HotStorage, ColdStorage)
— enforces Send on native, no-op on wasm32
- Now trait: add now_secs() for heartbeat/eviction; SystemClock updated
nex:
- SharedGraph: conditional type — Arc<RwLock<>> on native, Rc<RefCell<>> on wasm32
- read_graph()/write_graph() accessor functions hide platform difference
- GraphRead/GraphWrite impls for std::cell::Ref/RefMut (wasm32)
- PetgraphStorage: inject Box<dyn Now> — WasmClock (wasm32 zero-time),
SystemClock (native real-time), with_clock() for injection
- DefaultBlackboard: Mutex<ClaimsTracker> replaces ClaimGuard, &self impl
- ReadGuard<'a> conditional type alias for graph()/snapshot() return
gateway/nex-cf:
- thread_local! + RefCell<DefaultBlackboard> for WASM single-thread
- stores.rs: CfMetaStore/CfBlobStore/CfObjectStore stubs
- workerd/config.capnp for local Workerd testing
- All endpoints GET-only: /, /state, /fact, /intent, /claim, /conclude
23 tasks
9 tasks
9 tasks
Contributor
Author
model + nex scope review summaryPositive
Minor items (non-blocking)
Not found
|
10 tasks
…e and wasm32) #{80}
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.
model + nex scope review summary
Positive
Blackboard: &selfmigration: Correct decision. Interior mutability is the implementation's responsibility; the trait contract must not force mutability on the caller.StorageSendconditional marker: ApplyingSendonly to aggregate traits (HotStorage,ColdStorage) rather than fine-grained traits is the correct abstraction boundary.Nowtrait withnow_secs(): Eliminates duplicateSystemTime::now()calls in heartbeat/eviction and unifies all time semantics through a single injection point.SharedGraph+read_graph/graphaccessors: Platform differences are fully encapsulated. Callers never know whether locks exist.GraphRead/GraphWriteforRef/RefMut: The Cypher query engine works identically across platforms.Minor items to verify
model/src/storage/aggregate.rssend_markermodule: Two conditional modules is slightly heavy. A direct#[cfg]+pub traitcombination at file scope is more idiomatic. Current code is functionally correct.nex/src/blackboard/mod.rsflush(&mut self):DefaultBlackboard::flush()remains&mut selfwhileBlackboardis now&self. This is not aBlackboardtrait method so there's no contract violation, but consistency could be improved in the future. (Native FIH storage: trait-bound blob store with unified encryption, composite tiering, and sparse retrieval interface #81NativeFihStorageflush can useAtomicU64cursor for&self).gateway/nex-cf/src/lib.rsSyncOnce::unsafe impl Sync:OnceLock::call_oncealready provides internal synchronization. On wasm32,Syncis a no-op since the runtime is single-threaded. Safe.Not found
RwLockis correct)