Skip to content

PR: feat: wasm32 baseline — pure FIH state machine with trait-injected clock #{80}#82

Merged
metasmile merged 15 commits into
mainfrom
80-nex-cf-model-integration
Jun 4, 2026
Merged

PR: feat: wasm32 baseline — pure FIH state machine with trait-injected clock #{80}#82
metasmile merged 15 commits into
mainfrom
80-nex-cf-model-integration

Conversation

@metasmile

@metasmile metasmile commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

model + nex scope review summary

Positive

  • Blackboard: &self migration: Correct decision. Interior mutability is the implementation's responsibility; the trait contract must not force mutability on the caller.
  • StorageSend conditional marker: Applying Send only to aggregate traits (HotStorage, ColdStorage) rather than fine-grained traits is the correct abstraction boundary.
  • Now trait with now_secs(): Eliminates duplicate SystemTime::now() calls in heartbeat/eviction and unifies all time semantics through a single injection point.
  • SharedGraph + read_graph/graph accessors: Platform differences are fully encapsulated. Callers never know whether locks exist.
  • GraphRead/GraphWrite for Ref/RefMut: The Cypher query engine works identically across platforms.

Minor items to verify

  1. model/src/storage/aggregate.rs send_marker module: Two conditional modules is slightly heavy. A direct #[cfg] + pub trait combination at file scope is more idiomatic. Current code is functionally correct.
  2. nex/src/blackboard/mod.rs flush(&mut self): DefaultBlackboard::flush() remains &mut self while Blackboard is now &self. This is not a Blackboard trait 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 #81 NativeFihStorage flush can use AtomicU64 cursor for &self).
  3. gateway/nex-cf/src/lib.rs SyncOnce::unsafe impl Sync: OnceLock::call_once already provides internal synchronization. On wasm32, Sync is a no-op since the runtime is single-threaded. Safe.

Not found

  • Circular dependencies
  • Deadlock potential (wasm32 is single-threaded; native RwLock is correct)
  • Trait hierarchy violations

metasmile added 14 commits June 3, 2026 19:37
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
metasmile

This comment was marked as duplicate.

metasmile

This comment was marked as duplicate.

@metasmile

Copy link
Copy Markdown
Contributor Author

model + nex scope review summary

Positive

  • Blackboard: &self migration: Correct. Interior mutability is the implementation's responsibility; the trait contract must not force mutability on the caller.
  • StorageSend conditional marker: Applying Send only to aggregate traits (HotStorage, ColdStorage), not fine-grained capability traits, is the correct abstraction boundary.
  • Now trait with now_secs(): Eliminates duplicate SystemTime::now() in heartbeat/eviction. Single injection point for all time semantics.
  • SharedGraph + read_graph/write_graph accessors: Platform differences fully encapsulated. Callers never see locks.
  • GraphRead/GraphWrite for Ref/RefMut: Cypher query engine works identically across platforms.

Minor items (non-blocking)

  1. model/src/storage/aggregate.rs send_marker module: Two conditional modules is slightly heavy. A direct #[cfg] + pub trait at file scope reads cleaner. Current code is functionally correct.
  2. nex/src/blackboard/mod.rs flush(&mut self): Not a Blackboard trait method so no contract violation, but consistency could improve. (Native FIH storage: trait-bound blob store with unified encryption, composite tiering, and sparse retrieval interface #81 NativeFihStorage can make flush &self via AtomicU64 cursor.)
  3. gateway/nex-cf/src/lib.rs SyncOnce::unsafe impl Sync: OnceLock::call_once provides internal synchronization. On wasm32, Sync is a no-op (single-threaded). Safe.

Not found

  • Circular dependencies
  • Deadlock potential
  • Trait hierarchy violations

@metasmile metasmile merged commit ff2d9e2 into main Jun 4, 2026
5 checks passed
@metasmile metasmile deleted the 80-nex-cf-model-integration branch June 4, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gateway/nex-cf: Adopt nexus-model types and Blackboard trait

1 participant