diff --git a/docs/SCALABILITY_PROFILES.md b/docs/SCALABILITY_PROFILES.md index a5973bb..2bad360 100644 --- a/docs/SCALABILITY_PROFILES.md +++ b/docs/SCALABILITY_PROFILES.md @@ -195,6 +195,13 @@ make failures worse. PostgreSQL is the production database. SQLite remains a local-development and tiny disposable profile only. +Files avoids SQLite's second-writer deadlock by recording blob recovery intent +inside the caller transaction. Handled rollback reconstructs durable recovery +evidence, but a hard process loss before commit can leave an object without an +Ops ledger row. Run a complete Files integrity scan after such a loss and +reconcile reported orphans before resuming writes. PostgreSQL retains the +independent pre-effect recovery-intent guarantee required for production. + Production migrations should run explicitly before startup or package activation. Module install/uninstall workflows must use database backup and restore-check hooks for PostgreSQL before migrations or destructive retirement. diff --git a/src/govoplan_ops/backend/manifest.py b/src/govoplan_ops/backend/manifest.py index f0ddcfd..66bfb13 100644 --- a/src/govoplan_ops/backend/manifest.py +++ b/src/govoplan_ops/backend/manifest.py @@ -154,7 +154,7 @@ manifest = ModuleManifest( id="ops.runtime-coordination-and-recovery", title="Drain runtime nodes and inspect recovery evidence", summary="Ops projects shared runtime heartbeats, replica gaps, drain controls, and recovery states that require operator attention.", - body="Use the runtime table to identify stale or composition-skewed API and worker replicas. Drain before replacement so API readiness closes and workers stop taking new queue work; cancellation is available while the node is still draining. The recovery table reports durable Core recovery operations. A rejected operation is a verified provider rejection and needs no recovery; outcome-unknown and recovery-required operations still require reconciliation through the owning module. Core module-lifecycle entries block every later install or live graph change: use the installer run id to verify package, backup, migration, and health evidence before rollback or forward repair. Mail SMTP and IMAP APPEND entries use stable attempt identifiers and digest-only evidence: reconcile the Mail command from provider evidence, never by replaying the original effect from Ops. Dataflow database-only runs are atomic, while published-output runs use forward recovery: reconcile the recorded output digest and sink idempotency key before allowing another publication. Backup status separately projects only the sanitized deployment verification receipt: a verified status identifies a coordinated recovery point and isolated restore drill, while absent, expired, or invalid evidence blocks a release-changing migration.", + body="Use the runtime table to identify stale or composition-skewed API and worker replicas. Drain before replacement so API readiness closes and workers stop taking new queue work; cancellation is available while the node is still draining. The recovery table reports durable Core recovery operations. A rejected operation is a verified provider rejection and needs no recovery; outcome-unknown and recovery-required operations still require reconciliation through the owning module. Core module-lifecycle entries block every later install or live graph change: use the installer run id to verify package, backup, migration, and health evidence before rollback or forward repair. Mail SMTP and IMAP APPEND entries use stable attempt identifiers and digest-only evidence: reconcile the Mail command from provider evidence, never by replaying the original effect from Ops. Files object effects use independently committed recovery intent on PostgreSQL. Development SQLite can show only handled-rollback reconstruction because its Files intent shares the caller transaction; after a hard SQLite process loss, run the owning Files integrity scan because an orphan may have no Ops ledger row. Dataflow database-only runs are atomic, while published-output runs use forward recovery: reconcile the recorded output digest and sink idempotency key before allowing another publication. Backup status separately projects only the sanitized deployment verification receipt: a verified status identifies a coordinated recovery point and isolated restore drill, while absent, expired, or invalid evidence blocks a release-changing migration.", documentation_types=("admin", "user"), audience=("operator", "system_admin"), conditions=( @@ -163,7 +163,7 @@ manifest = ModuleManifest( any_scopes=OPS_READ_SCOPES, ), ), - related_modules=("audit", "notifications"), + related_modules=("audit", "files", "notifications"), metadata={ "kind": "workflow", "route": "/ops", @@ -178,6 +178,7 @@ manifest = ModuleManifest( "limitations": [ "Drain is observed on the runtime heartbeat interval and does not forcibly terminate active work.", "Ops does not create or restore backups and never receives private artifact or key-custody references.", + "SQLite is a development-only recovery profile; Files objects created before an unhandled process loss may require integrity-scan discovery because the caller-transaction intent was not committed.", "A verified receipt proves the recorded drill; it does not make an unsafe post-migration code rollback reversible.", ], "help_contexts": [