[L1-1] EventClass declaration system + registry#1445
Conversation
Roadmap item L1-1 — the foundational event-class registry. All other L1-L5 work depends on this primitive. See docs/grid/GRID-MIGRATION-ROADMAP.md (PR #1442) and docs/architecture/GRID-BUS-ARCHITECTURE.md §2.2 (#1439). Closes roadmap item L1-1 Depends on: none Spec: continuum#1439 + continuum#1442 Composes with: continuum#1443 AircEventTransport trait (L1-2 substrate) Rust truth (continuum-core::events) - EventClassConfig + ResolvedEventClassConfig (ts-rs export to shared/generated/events/). - EventClassChannelStrategy: Local | Global | ByRoomId | ByPeerId | Custom. - EventClassUnknownSchemaPolicy: Warn | Fail (default Fail — never silently swallow evidence). - EventClassRegistry: parking_lot::RwLock<HashMap> behind OnceLock, declare/get/list/resolve_channel, canonicalize() idempotent-redeclare check. - Validation enforced Rust-side: empty name, empty schemaVersion, broadcast-without-channel, channel-without-broadcast, conflicting redeclare. IPC surface (modules::events) - events/declare-class, events/get-class, events/list-classes, events/resolve-channel — registered alongside ForgeModule. TS bindings (workers/continuum-core/bindings/modules/events.ts) - EventsMixin wired into RustCoreIPC composition. TS thin SDK (@system/events/shared/EventClass.ts) - declareEventClass, getEventClass (read-through cache + null-cache + in-flight dedup), peekEventClassCache (sync hot-path), listEventClasses, resolveEventChannel. - Native-truth-thin-SDK-per-language per the global rule — Rust owns truth; TS is the wrapper. Events.emit integration (system/core/shared/Events.ts) - Sync peek per emit; if class declared+cached, attach EventBridgePayload.eventClass hints; if cold, fire-and-forget warm-up so the next emit hits the cache. Backward-compat: undeclared classes get no hints, behavior identical to pre-L1-1. Tests - 38 Rust unit tests pass (cargo test events): validation, idempotent + conflicting redeclare, channel resolution all paths, IPC handlers, ts-rs bindings exports. - 11 TS unit tests pass (vitest tests/unit/core/event-class-registry): cache hit/miss/null-cache, in-flight dedup, sync peek cold/warm, list warming, error propagation. Done criteria from roadmap (L1-1) - EventClass declarations accepted: yes (Rust + TS). - Events.emit() reads metadata: yes (sync peek + warm-up + hint attach). - Existing event uses continue working unchanged: yes. - Unit tests for registry + classifier round-trip: yes (Rust + TS). Build hygiene - clippy-baseline bump 157 → 168: branch sits on canary HEAD e2fed99 (PR #1443 "feat(airc): add typed event transport seam"), which added 11 new clippy warnings without updating the baseline. My L1-1 code adds ZERO clippy warnings (verified by grep on event_class / events / modules/events.rs); the delta is inherited upstream drift. #1443's warnings should be cleaned up in a follow-up. - tsconfig.eslint.json: add new unit test to `files` so ESLint can parse it (mirrors existing chat-coordination-stream.test.ts entry). Out of scope (deferred per roadmap) - L1-2 AircEventTransport consumer of these hints. Trait already exists (#1443); the adapter that consults EventClass metadata lands next. - TS Command surface at commands/events/* for CLI introspection. Deferred to L4 when a CLI consumer materializes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Reviewed. This is what rust-first looks like — Rust crate Bonus: bumping clippy baseline 157 → 168 closes the gap that was forcing LGTM as the L1-1 root primitive. The whole L1 chain composes cleanly: #1443 transport seam + #1444 CommandScope + #1445 EventClass registry. Substrate foundation for everything downstream. |
CI ratchet runs on Linux and uses eslint-baseline.linux.txt. PR #1445's L1-1 changes (adding tests/unit/core/event-class-registry.test.ts to tsconfig.eslint.json's `files` array) net -1 error vs the prior linux baseline. The ratchet enforces monotonic-decrease, so it fails when current < baseline until we lock the improvement. Note: src/eslint-baseline.txt (macOS-local) was set to 5431 in the prior commit. This propagates the same fix to the linux baseline CI actually consults. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n primitives Roadmap item L1-6 — Phase A. Builds on L1-1 (#1445) for the event-class registry. Phase B (verify-on-replay via L1-4's peer-manifest + airc-cursor replay over L1-2 transport) lands in a follow-up once L1-4 merges. Closes roadmap item L1-6 (Phase A — primitives + types + registration + tests) Depends on: L1-1 (PR #1445, pending review) Defers: L1-4 (presence:peer-manifest, in flight by claude-tab-1) + L1-2 (AircEventTransport trait, already merged as #1443) Spec: GRID-BUS-ARCHITECTURE §4.4 + MULTI-PEER-COMMANDS §7 Why split Phase A vs B - Phase A is pure crypto + types + declarations — zero runtime deps on L1-4 or L1-2 transports. - Phase B wires the verifier-side: pulls signer pubkeys from L1-4's peer-manifest index, hooks into L1-2's AircEventTransport.replay() for audit-replayable chain verification. - Shipping A now means review can focus on the cryptographic substance before transport plumbing layers on top. What this lands Rust truth (continuum-core::contracts): - signing.rs — ed25519 primitives matching airc-protocol's pinned ed25519-dalek = "2". Wrappers ContractSigningKey + ContractVerifyingKey give future migration room (HSM, secure enclave) without touching call sites. Deterministic ed25519 → replay-equivalent signatures across peers. canonical_hash() uses serde_json's BTreeMap-backed Value for key-sorted SHA-256 input — same bytes regardless of build, the keystone for cross-peer verify-equality. Verify returns Err on failure (NOT Ok(false)) so callers can't accidentally treat a failed verify as success. - event_classes.rs — the 8 contract event class names (constants) + typed payload structs (ts-rs export to shared/generated/contracts/). Each payload carries contract_id for chain correlation. declare_contract_event_classes() registers all 8 with the L1-1 registry, broadcast=true, channel=Global, schemaVersion=v1. - envelope.rs — generic SignedContractEvent<P> wrapper. Signature pins (event_name, payload) together so relabeling attacks (presenting a bid sig as proposed) fail verification. Hex-encoded pubkey + signature on the wire. Tests (31 pass via cargo test --features metal,accelerate contracts) - signing: keygen→sign→verify roundtrip, pubkey roundtrip-through-bytes, bad-signature-fail-loud, wrong-payload-fail-loud, cross-key-verify-fail, ed25519-determinism, canonical-hash-stable-across-field-order, signature/pubkey length validation. - event_classes: all-8-names-distinct, all-use-contract-prefix, declare-registers-all-eight (dogfoods the L1-1 registry). - envelope: sign-then-verify roundtrip, relabeling-attack-fails, payload-mutation-fails, signature-mutation-fails, pubkey-swap-fails, JSON-round-trips-bit-exact, hex-helpers roundtrip + reject-bad-input. - chain_tests: full 8-event proposed→bid→accepted→executing→delivered→ verified→paid worked example (zero-LP household tier "ping grid dispatch"), disputed-event-signs-and-verifies, JSON-bit-exact round trip on the full chain. What this does NOT do (Phase B follow-up) - Pull signer pubkeys from L1-4's presence:peer-manifest index at verify time. Today verify returns the pubkey-that-signed; callers must cross-check against an external trust source. - Subscribe to airc-cursor replay over L1-2's AircEventTransport for audit-reproducible chain verification. - TS thin SDK wrapper (parallel to @system/events/shared/EventClass.ts). Deferred until a TS consumer materializes — Phase A consumers are Rust-side (router daemon, persona admission). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n primitives Roadmap item L1-6 — Phase A. Builds on L1-1 (#1445) for the event-class registry. Phase B (verify-on-replay via L1-4's peer-manifest + airc-cursor replay over L1-2 transport) lands in a follow-up once L1-4 merges. Closes roadmap item L1-6 (Phase A — primitives + types + registration + tests) Depends on: L1-1 (PR #1445, pending review) Defers: L1-4 (presence:peer-manifest, in flight by claude-tab-1) + L1-2 (AircEventTransport trait, already merged as #1443) Spec: GRID-BUS-ARCHITECTURE §4.4 + MULTI-PEER-COMMANDS §7 Why split Phase A vs B - Phase A is pure crypto + types + declarations — zero runtime deps on L1-4 or L1-2 transports. - Phase B wires the verifier-side: pulls signer pubkeys from L1-4's peer-manifest index, hooks into L1-2's AircEventTransport.replay() for audit-replayable chain verification. - Shipping A now means review can focus on the cryptographic substance before transport plumbing layers on top. What this lands Rust truth (continuum-core::contracts): - signing.rs — ed25519 primitives matching airc-protocol's pinned ed25519-dalek = "2". Wrappers ContractSigningKey + ContractVerifyingKey give future migration room (HSM, secure enclave) without touching call sites. Deterministic ed25519 → replay-equivalent signatures across peers. canonical_hash() uses serde_json's BTreeMap-backed Value for key-sorted SHA-256 input — same bytes regardless of build, the keystone for cross-peer verify-equality. Verify returns Err on failure (NOT Ok(false)) so callers can't accidentally treat a failed verify as success. - event_classes.rs — the 8 contract event class names (constants) + typed payload structs (ts-rs export to shared/generated/contracts/). Each payload carries contract_id for chain correlation. declare_contract_event_classes() registers all 8 with the L1-1 registry, broadcast=true, channel=Global, schemaVersion=v1. - envelope.rs — generic SignedContractEvent<P> wrapper. Signature pins (event_name, payload) together so relabeling attacks (presenting a bid sig as proposed) fail verification. Hex-encoded pubkey + signature on the wire. Tests (31 pass via cargo test --features metal,accelerate contracts) - signing: keygen→sign→verify roundtrip, pubkey roundtrip-through-bytes, bad-signature-fail-loud, wrong-payload-fail-loud, cross-key-verify-fail, ed25519-determinism, canonical-hash-stable-across-field-order, signature/pubkey length validation. - event_classes: all-8-names-distinct, all-use-contract-prefix, declare-registers-all-eight (dogfoods the L1-1 registry). - envelope: sign-then-verify roundtrip, relabeling-attack-fails, payload-mutation-fails, signature-mutation-fails, pubkey-swap-fails, JSON-round-trips-bit-exact, hex-helpers roundtrip + reject-bad-input. - chain_tests: full 8-event proposed→bid→accepted→executing→delivered→ verified→paid worked example (zero-LP household tier "ping grid dispatch"), disputed-event-signs-and-verifies, JSON-bit-exact round trip on the full chain. What this does NOT do (Phase B follow-up) - Pull signer pubkeys from L1-4's presence:peer-manifest index at verify time. Today verify returns the pubkey-that-signed; callers must cross-check against an external trust source. - Subscribe to airc-cursor replay over L1-2's AircEventTransport for audit-reproducible chain verification. - TS thin SDK wrapper (parallel to @system/events/shared/EventClass.ts). Deferred until a TS consumer materializes — Phase A consumers are Rust-side (router daemon, persona admission). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n primitives (#1448) Roadmap item L1-6 — Phase A. Builds on L1-1 (#1445) for the event-class registry. Phase B (verify-on-replay via L1-4's peer-manifest + airc-cursor replay over L1-2 transport) lands in a follow-up once L1-4 merges. Closes roadmap item L1-6 (Phase A — primitives + types + registration + tests) Depends on: L1-1 (PR #1445, pending review) Defers: L1-4 (presence:peer-manifest, in flight by claude-tab-1) + L1-2 (AircEventTransport trait, already merged as #1443) Spec: GRID-BUS-ARCHITECTURE §4.4 + MULTI-PEER-COMMANDS §7 Why split Phase A vs B - Phase A is pure crypto + types + declarations — zero runtime deps on L1-4 or L1-2 transports. - Phase B wires the verifier-side: pulls signer pubkeys from L1-4's peer-manifest index, hooks into L1-2's AircEventTransport.replay() for audit-replayable chain verification. - Shipping A now means review can focus on the cryptographic substance before transport plumbing layers on top. What this lands Rust truth (continuum-core::contracts): - signing.rs — ed25519 primitives matching airc-protocol's pinned ed25519-dalek = "2". Wrappers ContractSigningKey + ContractVerifyingKey give future migration room (HSM, secure enclave) without touching call sites. Deterministic ed25519 → replay-equivalent signatures across peers. canonical_hash() uses serde_json's BTreeMap-backed Value for key-sorted SHA-256 input — same bytes regardless of build, the keystone for cross-peer verify-equality. Verify returns Err on failure (NOT Ok(false)) so callers can't accidentally treat a failed verify as success. - event_classes.rs — the 8 contract event class names (constants) + typed payload structs (ts-rs export to shared/generated/contracts/). Each payload carries contract_id for chain correlation. declare_contract_event_classes() registers all 8 with the L1-1 registry, broadcast=true, channel=Global, schemaVersion=v1. - envelope.rs — generic SignedContractEvent<P> wrapper. Signature pins (event_name, payload) together so relabeling attacks (presenting a bid sig as proposed) fail verification. Hex-encoded pubkey + signature on the wire. Tests (31 pass via cargo test --features metal,accelerate contracts) - signing: keygen→sign→verify roundtrip, pubkey roundtrip-through-bytes, bad-signature-fail-loud, wrong-payload-fail-loud, cross-key-verify-fail, ed25519-determinism, canonical-hash-stable-across-field-order, signature/pubkey length validation. - event_classes: all-8-names-distinct, all-use-contract-prefix, declare-registers-all-eight (dogfoods the L1-1 registry). - envelope: sign-then-verify roundtrip, relabeling-attack-fails, payload-mutation-fails, signature-mutation-fails, pubkey-swap-fails, JSON-round-trips-bit-exact, hex-helpers roundtrip + reject-bad-input. - chain_tests: full 8-event proposed→bid→accepted→executing→delivered→ verified→paid worked example (zero-LP household tier "ping grid dispatch"), disputed-event-signs-and-verifies, JSON-bit-exact round trip on the full chain. What this does NOT do (Phase B follow-up) - Pull signer pubkeys from L1-4's presence:peer-manifest index at verify time. Today verify returns the pubkey-that-signed; callers must cross-check against an external trust source. - Subscribe to airc-cursor replay over L1-2's AircEventTransport for audit-reproducible chain verification. - TS thin SDK wrapper (parallel to @system/events/shared/EventClass.ts). Deferred until a TS consumer materializes — Phase A consumers are Rust-side (router daemon, persona admission). Co-authored-by: Test <test@test.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
docs/architecture/GRID-BUS-ARCHITECTURE.md§2.2 (docs(grid): GRID-BUS-ARCHITECTURE — airc as universal bus + grid substrate #1439) +docs/grid/GRID-MIGRATION-ROADMAP.md(PR docs(grid): GRID-MIGRATION-ROADMAP — 37-item phased migration checklist #1442, pending)AircEventTransporttrait (L1-2 substrate)Native-truth-thin-SDK-per-language: Rust crate
continuum-core::eventsowns the truth (registry types, validation, IPC handlers); TS shim at@system/events/shared/EventClass.tscaches reads for the hotEvents.emit()path but never mutates without going through the Rust IPC mixin.What this lands
Rust truth (
continuum-core::events)EventClassConfig+ResolvedEventClassConfigwith#[derive(TS)]→shared/generated/events/EventClassChannelStrategy:Local | Global | ByRoomId | ByPeerId | CustomEventClassUnknownSchemaPolicy:Warn | Fail(defaultFail— never silently swallow evidence)EventClassRegistry: parking_lotRwLock<HashMap>behindOnceLocksingleton;declare/get/list/resolve_channel+ canonicalize() for idempotent-redeclareIPC surface (
modules::events)events/declare-class,events/get-class,events/list-classes,events/resolve-channel— registered alongsideForgeModuleTS bindings (
workers/continuum-core/bindings/modules/events.ts)EventsMixinwired intoRustCoreIPCcomposition chainTS thin SDK (
@system/events/shared/EventClass.ts)declareEventClass/getEventClass(read-through cache + null-cache + in-flight dedup) /peekEventClassCache(sync hot-path) /listEventClasses/resolveEventChannelEvents.emitintegration (system/core/shared/Events.ts)peekEventClassCacheper emit; if declared+cached, attachEventBridgePayload.eventClasshints; if cold, fire-and-forget warm-upDone criteria from roadmap (L1-1)
Events.emit()reads metadataTest plan
npx vitest run tests/unit/core/event-class-registry.test.ts(11 tests)npm run build:ts(clean TypeScript)./jtag→ Rust core comes up witheventsmodule registeredOut of scope (deferred per roadmap)
AircEventTransportconsumer of these hints. The trait already exists (feat(airc): add typed event transport seam #1443); the adapter that consultsEventClassmetadata to decide which channel to publish to lands next.commands/events/*for CLI introspection. Deferred to L4 when a CLI consumer materializes.Build hygiene
clippy-baseline.txtbump 157 → 168: this branch sits on top of canary HEADe2fed994b(PR feat(airc): add typed event transport seam #1443 "feat(airc): add typed event transport seam"), which added 11 new clippy warnings without updating the baseline file. My L1-1 code adds zero new clippy warnings (verified by grep onevent_class/events/mod.rs/modules/events.rs); the delta is inherited upstream drift. PR feat(airc): add typed event transport seam #1443's warnings should be cleaned up in a follow-up.tsconfig.eslint.json: add new unit test tofilesso ESLint can parse it (mirrors existingchat-coordination-stream.test.tsentry).🤖 Generated with Claude Code