Repository navigation
Conversation
The disposition sweep decided every open item whose deadline had passed, so the first sweep after OpenClear came back from being down across a cut-off applied each customer's default to everything nobody could look at (CONTINUITY_PLAN.md BC1). continuity_service.recover_from_gap runs at the start of the sweep: when the sweep's own last run is older than its interval plus five minutes, every open item whose deadline fell in the gap gets the bank's minimum review time again, never past a check's midnight deadline (deadline_moved_reason = "outage", migration d4e8f2a6c1b7); the gap is in the bank's action log and a continuity.gap event (high); the bank's administrators and each customer's deciders are emailed; the Controls page shows it for a week (check continuity_gaps, control OC-CP-02). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
services/readiness.py checks the database, file storage (a probe byte
written and deleted), Redis when limits are shared through it, and
whether the cut-off sweep has run within its interval; /ready answers
ready, degraded (Redis or the sweep: the instance still serves) or
unavailable with a 503 (the database or storage), and the metrics gain
openclear_component_up{component=…}. /health stays the load balancer's
one-question check. The runbook says what to alert on (CONTINUITY_PLAN.md BC-D6).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The host's backup job leaves a manifest (backup_manifests/latest.json; the runbook gives its shape) and the Controls page's "Backups fresh" (control OC-CP-03) reads it, warning when there's none or the last backup is older than OPENCLEAR_BACKUP_MAX_AGE_HOURS. A SQLite installation has no backup job, so housekeeping makes a consistent copy nightly (VACUUM INTO), keeps OPENCLEAR_SQLITE_BACKUP_KEEP_DAYS of them, and writes the manifest itself (CONTINUITY_PLAN.md BC-D2, BC-D8). storage.read_key reads a file OpenClear didn't write. The outage notice is excluded from the notification preferences page, which enumerates every type (BC1's suite found the gap). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
scripts/restore_drill.py restores a backup into a scratch database (a copy of a SQLite file, or pg_restore), migrates it to head, verifies every bank's action-log chain, reconciles the restored rows against file storage, checks the dump's hash against the manifest, and records the signed result as a restore_test row (migration e5f9a3b7d2c8); the newest passed drill is the last restore test on every bank's Controls page, in place of a date typed in. services/storage_reconcile.py walks every row that names a file and every storage area (storage gains exists, ref and list_keys on each backend): nightly as "Files and rows agree" (control OC-CP-04, warning only) and from Platform → Continuity, which also shows the backup manifest and the drills (CONTINUITY_PLAN.md BC-D2, BC-D3). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n plan OPENCLEAR_MAINTENANCE_MODE=1: people sign in and look, an administrator can take a data export, every other change answers 503 with the reason, every page carries the notice, and the cut-off sweep pauses, marking when it did so the pause counts as an outage across a cut-off when the mode ends (continuity_service.note_paused / gap_before). The Controls page shows the mode (check maintenance_mode). services/continuity_snapshot.py writes each bank's open and pending items hourly as a signed CSV in its storage, keeps a dated copy at the bank's end of day, and emails it to the incident contacts when the bank asks (tenant.continuity_snapshot_emailed, migration f7a1b5c9d3e2); Settings → Continuity downloads them. It's what CUEC B-CP-2-01's plan works from when OpenClear can't be reached (CONTINUITY_PLAN.md BC-D4, BC-D5). Tests: the export, decision-file and audit-archive areas now live under each test's temp directory like the others, so the nightly consistency check no longer counts the repository's own data folders. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Nine steps in the order each needs the one before: the banks told; the keys from the key store before the database; maintenance mode on; the restore and alembic upgrade head; file storage to the same point; /ready; the action-log chains and the files-and-rows reconciliation; maintenance mode off and what the first sweep then does with the gap; closing the incident and recording the drill (CONTINUITY_PLAN.md BC-D7). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…at go through the leader lock Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…found it; the SQLite drill test skips there Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
chaffed
enabled auto-merge
October 31, 2026 23:12
…browser's string CodeQL (#177–#179) traced `?day=` from the download route into a storage path. The route parses the day as a date and builds the file name from that; anything else is a 404. The local branch of `storage.read_key` now goes through `LocalStorage._path`, so its resolved path is checked against the area like every other local read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
chaffed
pushed a commit
that referenced
this pull request
Oct 31, 2026
Version, migration head f7a1b5c9d3e2, the CHANGELOG section, the MASTER_PLAN note and the security metrics for the continuity work (CONTINUITY_PLAN.md BC1–BC6, #71). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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
plans/CONTINUITY_PLAN.mdBC1–BC6: what OpenClear does when something is down, and how a recovery is proven.continuity.gap(high), emails the administrators and each customer's deciders, and the Controls page shows it for a week (OC-CP-02).GET /readynames the failing component (database, storage, Redis, the sweep's last run) andopenclear_component_up{component=…}joins the metrics;/healthstays the load balancer's check.backup_manifests/latest.json(shape in the runbook); Backups fresh (OC-CP-03) warns when it's missing or old. A SQLite installation copies itself nightly (VACUUM INTO) and writes the manifest.scripts/restore_drill.pyrestores a backup into a scratch database, migrates, verifies every action-log chain, reconciles rows against files and signs the result (restore_test); the newest passed drill is the last restore test on the Controls page. Nightly Files and rows agree (OC-CP-04); Platform → Continuity.OPENCLEAR_MAINTENANCE_MODE=1: sign-in, reading and the data export work; other writes 503; the sweep pauses and the pause counts as a gap) and the bank's open items as a signed file, hourly with a dated daily copy, downloadable from Settings → Continuity and emailed to the incident contacts on request — what CUEC B-CP-2-01's plan works from.Migrations:
d4e8f2a6c1b7(exception_item.deadline_moved_reason),e5f9a3b7d2c8(restore_test),f7a1b5c9d3e2(tenant.continuity_snapshot_emailed). New scheduled jobcontinuity_snapshot_job(hourly). Tests now isolate every storage area under the test's temp directory.Security checklist
/readyis public like/health; the open-items download refuses customer-scoped sessions)restore_testhas notenant_id(installation-wide)continuity.gap); notification types excluded from preferences where always-email🤖 Generated with Claude Code