Skip to content

feat(lifecycle): restart-safe lifecycle, persistence, and cross-context coordination - #117

Open
christabel888 wants to merge 2 commits into
Gryd-lock:mainfrom
christabel888:feat/lifecycle-restart-safety-112
Open

feat(lifecycle): restart-safe lifecycle, persistence, and cross-context coordination#117
christabel888 wants to merge 2 commits into
Gryd-lock:mainfrom
christabel888:feat/lifecycle-restart-safety-112

Conversation

@christabel888

Copy link
Copy Markdown

Summary

Closes #112.

  • Disposable/DisposableGroup + OracleLifecycleManager: idempotent init/ready/health/disposal ownership for a composed stack, plus createProductionOracleStack, a validated factory composing withCache -> withRateLimit -> CircuitBreakerOracle -> CoalescingOracle under 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 absolute maxEvidenceAgeMs cap 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, bounded contextId/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 stable contextId.
  • LIFECYCLE_ADR.md (design rationale + explicit out-of-scope items) and RESTART_RUNBOOK.md (operational guidance).
  • Bundle-size budget raised (45 KB -> 58 KB, following this repo's existing convention for that budget) and the smoke-test export list extended for the new public surface.

Explicitly out of scope (see LIFECYCLE_ADR.md)

Ledger-lag-based evidence age (vs. the wall-clock maxEvidenceAgeMs implemented 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 lint
  • npm 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 passing
  • npm run docs:check (typedoc, treatWarningsAsErrors)
  • npm run size
  • npm run build + npm run test:smoke
  • New coverage: tests/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

…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
christabel888 force-pushed the feat/lifecycle-restart-safety-112 branch from aab624b to 76dff3e Compare August 30, 2026 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Epic — Build a restart-safe lifecycle and invalidation protocol for cache, rate limiting, and shared in-flight work

2 participants