feat(lifecycle): restart-safe lifecycle, persistence, and cross-context coordination - #117
Open
christabel888 wants to merge 2 commits into
Open
Conversation
…xt coordination Addresses Gryd-lock#112: withCache, withRateLimit, CircuitBreakerOracle, and CoalescingOracle previously kept process-local state only, with no disposal contract, no durable/namespaced cache schema, and gossip coordination that degraded to N * budget with no cross-context exclusion for background revalidation. - Add a Disposable/DisposableGroup contract and an OracleLifecycleManager that owns init/ready/health/idempotent-disposal for a composed stack, plus createProductionOracleStack, a validated factory composing withCache -> withRateLimit -> CircuitBreakerOracle -> CoalescingOracle under one owner. - Add CacheNamespace (network/contract/evidenceSchemaVersion/policyVersion) and a DurableStore-backed persistence path for withCache: versioned, namespace-checked hydration that quarantines corrupt or foreign-namespace records instead of reviving them, plus an absolute maxEvidenceAgeMs cap that overrides stale-while-revalidate. - Add RefreshLeaseCoordinator: bounded cross-context mutual exclusion for cache revalidation via the Web Locks API where available (exact exclusion, automatic release on context death), falling back to a bounded gossip ticket scheme (Lamport-bakery-style) with timeout-based recovery otherwise. - Harden withRateLimit's gossip protocol (versioned messages, bounded contextId/bucket-count/clock-skew validation, bounded tracked-context memory), add disposal with correct channel-ownership semantics, expose degraded-mode reporting via getCoordinationStatus(), and add optional own-bucket persistence for restart continuity with a stable contextId. - Document the design in LIFECYCLE_ADR.md and operational guidance in RESTART_RUNBOOK.md; raise the full-barrel bundle-size budget and extend the smoke-test export list for the new public surface. All existing suites (fuzzers, concurrency harnesses, adversarial-gossip, CRDT-property tests) pass unmodified. New coverage in tests/lifecycle/, tests/withCache.lifecycle.test.ts, tests/withRateLimit.lifecycle.test.ts, and tests/createProductionOracleStack.test.ts. Server-side authoritative rate-limit quota, ledger-lag-based evidence age, and general distributed cache infrastructure remain out of scope, per the issue's own scoping and its stated dependency on Epic 1/Epic 2 landing first.
christabel888
force-pushed
the
feat/lifecycle-restart-safety-112
branch
from
August 30, 2026 14:14
aab624b to
76dff3e
Compare
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.
Summary
Closes #112.
Disposable/DisposableGroup+OracleLifecycleManager: idempotent init/ready/health/disposal ownership for a composed stack, pluscreateProductionOracleStack, a validated factory composingwithCache->withRateLimit->CircuitBreakerOracle->CoalescingOracleunder one owner.withCache:CacheNamespace(network/contract/evidenceSchemaVersion/policyVersion),DurableStore-backed persistence with versioned/namespace-checked hydration (corrupt or foreign-namespace records are quarantined, never revived), and an absolutemaxEvidenceAgeMscap that overrides stale-while-revalidate.RefreshLeaseCoordinator: bounded cross-context mutual exclusion for cache revalidation — Web Locks API where available (exact exclusion, automatic release on context death), falling back to a bounded gossip ticket scheme (Lamport-bakery-style) with timeout-based recovery otherwise.withRateLimit: versioned gossip protocol, boundedcontextId/bucket-count/clock-skew validation, bounded tracked-context memory,dispose()with correct channel-ownership semantics,getCoordinationStatus()degraded-mode reporting, and optional own-bucket persistence for restart continuity with a stablecontextId.LIFECYCLE_ADR.md(design rationale + explicit out-of-scope items) andRESTART_RUNBOOK.md(operational guidance).Explicitly out of scope (see
LIFECYCLE_ADR.md)Ledger-lag-based evidence age (vs. the wall-clock
maxEvidenceAgeMsimplemented here) depends on Epic 1 (network/contract identity) landing first, per the issue's own stated dependency. A fully authoritative server-side rate-limit quota and general distributed cache infrastructure are out of scope per the issue's own scoping.Test plan
npm run typecheck(incl. type-tests)npm run lintnpm run format:check(clean on every file touched by this PR)npx vitest run— 421/421 passing, all pre-existing suites (fuzzers, concurrency harnesses, adversarial-gossip, CRDT-property tests) unmodified and passingnpm run docs:check(typedoc,treatWarningsAsErrors)npm run sizenpm run build+npm run test:smoketests/lifecycle/*,tests/withCache.lifecycle.test.ts,tests/withRateLimit.lifecycle.test.ts,tests/createProductionOracleStack.test.ts— including the "50 cold requests across 5 contexts create at most one active refresh lease" and "killing the lease owner permits recovery after a bounded timeout" acceptance criteria from the issue