Skip to content

fix(registry): fault-isolate per-tenant restore so one bad CSDL doesn't fail boot (ARN-190)#348

Open
rita-aga wants to merge 1 commit into
mainfrom
claude/arn-190-registry-boot-isolation
Open

fix(registry): fault-isolate per-tenant restore so one bad CSDL doesn't fail boot (ARN-190)#348
rita-aga wants to merge 1 commit into
mainfrom
claude/arn-190-registry-boot-isolation

Conversation

@rita-aga

@rita-aga rita-aga commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Closes ARN-190 (High) under epic ARN-165. Draft for review — do not merge.

Bug

populate_registry (live boot path) parsed CSDL + registered each tenant with ? inside the per-tenant loop, so one tenant's corrupt CSDL aborted the whole registry restore → boot failed for all tenants. Only the DST-only restore_registry_from_platform_store was fault-isolating, and its doc falsely claimed the CLI bootstrap used it — so DST tested a different path than prod shipped.

Fix

Extracted one shared fault-isolating core restore_grouped_specs (log + quarantine the bad tenant + continue); all three restore functions funnel through it, so DST exercises the real production isolation. Genuine store I/O errors still propagate. Corrected the false doc. ADR-0160.

Verification

Red→green corrupt-tenant boot test; new DST dst_boot_isolates_corrupt_tenant_csdl drives the production path (5 seeds); dst_platform_boot 10/10; registry_bootstrap 6/6; fmt/clippy clean; code review PASS + DST-READY (genuine).

🤖 Generated with Claude Code

The live Postgres/Turso boot path (`populate_registry`) parsed CSDL and
registered each tenant with `?` inside the per-tenant loop, so one tenant's
corrupt or unparsable CSDL returned Err, propagated through `build_registry`,
and failed boot for every tenant — a multi-tenant outage from one bad row.
Only the DST-only `restore_registry_from_platform_store` was fault-isolating,
and its doc falsely claimed the CLI bootstrap used it, so the DST harness
exercised a different path than production shipped.

Extract one shared fault-isolating core `restore_grouped_specs` that every
restore path funnels through: a bad tenant is logged, quarantined, and skipped;
the rest still restore. `populate_registry` becomes a thin wrapper that layers
verification-status restoration on top; the platform_store path delegates to the
core and reconciles orphaned specs. The live paths keep the offending rows for
inspection (no auto-delete of persisted specs); the platform_store path deletes
them to preserve P1. Corrected the false doc claim.

Red-green: a unit test drives the production Postgres/Turso wrapper with the
corrupt tenant ordered first (fails pre-fix: whole restore aborts); a DST test
drives the shared core via SimPlatformHarness::restart() and asserts the healthy
tenant boots, the corrupt tenant is quarantined and reconciled, and boot
invariants hold across seeds.

ADR-0155.

Fixes ARN-190.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@rita-aga rita-aga marked this pull request as ready for review July 9, 2026 17:48
@rita-aga

rita-aga commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile review

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rita-aga has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rita-aga has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@rita-aga

Copy link
Copy Markdown
Collaborator Author

ARN-165 principle audit — request changes

Extracting a shared restore core is a good step. The PR still does not prove or implement the same production behavior across adapters.

Blocking findings

  1. The DST does not exercise the production adapters. It calls restore_registry_from_platform_store, which deletes corrupt records. Live Postgres/Turso restore preserves them. The inner loop is shared; the adapter/reconciliation behavior that caused the original drift remains untested.
  2. Postgres restores uncommitted specs. The new query does not filter committed = true, unlike the canonical Postgres store loader. A partially installed spec can therefore become active after restart.
  3. “Quarantine” is only log-and-skip. There is no durable quarantine state, reason/source span, metric, repair API, or acknowledgment. RestoreOutcome loses the diagnostic details needed to recover.

Required direction

Run contract tests through the real Postgres and Turso adapters; restore only committed records; persist a tenant/type/version quarantine record with reason and repair workflow; emit bounded metrics; prove one corrupt tenant cannot block healthy tenants and cannot silently disappear.

The branch is behind main. Rebase and rerun after these semantic changes.

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.

1 participant