@cleocode/core 2026.10.1 → 2026.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-provenance.json +2 -2
- package/dist/cloud/http.d.ts +2 -0
- package/dist/cloud/http.d.ts.map +1 -1
- package/dist/cloud/http.js +3 -0
- package/dist/cloud/http.js.map +1 -1
- package/dist/cloud/journal.d.ts +9 -5
- package/dist/cloud/journal.d.ts.map +1 -1
- package/dist/cloud/journal.js +29 -8
- package/dist/cloud/journal.js.map +1 -1
- package/dist/cloud/manifest-check.d.ts +140 -4
- package/dist/cloud/manifest-check.d.ts.map +1 -1
- package/dist/cloud/manifest-check.js +322 -10
- package/dist/cloud/manifest-check.js.map +1 -1
- package/dist/cloud/nexus-attach.d.ts +25 -0
- package/dist/cloud/nexus-attach.d.ts.map +1 -1
- package/dist/cloud/nexus-attach.js +81 -6
- package/dist/cloud/nexus-attach.js.map +1 -1
- package/dist/cloud/nexus-cloud-activity.d.ts +37 -0
- package/dist/cloud/nexus-cloud-activity.d.ts.map +1 -0
- package/dist/cloud/nexus-cloud-activity.js +79 -0
- package/dist/cloud/nexus-cloud-activity.js.map +1 -0
- package/dist/cloud/nexus-cloud-status.d.ts.map +1 -1
- package/dist/cloud/nexus-cloud-status.js +36 -0
- package/dist/cloud/nexus-cloud-status.js.map +1 -1
- package/dist/cloud/nexus-device.d.ts +1 -1
- package/dist/cloud/nexus-enrol.d.ts.map +1 -1
- package/dist/cloud/nexus-enrol.js +5 -0
- package/dist/cloud/nexus-enrol.js.map +1 -1
- package/dist/cloud/nexus-home.d.ts +74 -0
- package/dist/cloud/nexus-home.d.ts.map +1 -0
- package/dist/cloud/nexus-home.js +97 -0
- package/dist/cloud/nexus-home.js.map +1 -0
- package/dist/cloud/nexus-vault-keys.d.ts +130 -0
- package/dist/cloud/nexus-vault-keys.d.ts.map +1 -0
- package/dist/cloud/nexus-vault-keys.js +347 -0
- package/dist/cloud/nexus-vault-keys.js.map +1 -0
- package/dist/cloud/nexus-vault-state.d.ts +121 -0
- package/dist/cloud/nexus-vault-state.d.ts.map +1 -0
- package/dist/cloud/nexus-vault-state.js +274 -0
- package/dist/cloud/nexus-vault-state.js.map +1 -0
- package/dist/cloud/nexus-vault.d.ts +160 -0
- package/dist/cloud/nexus-vault.d.ts.map +1 -0
- package/dist/cloud/nexus-vault.js +1745 -0
- package/dist/cloud/nexus-vault.js.map +1 -0
- package/dist/cloud/signing.d.ts +24 -8
- package/dist/cloud/signing.d.ts.map +1 -1
- package/dist/cloud/signing.js +61 -15
- package/dist/cloud/signing.js.map +1 -1
- package/dist/docs/export-document.js +8137 -3318
- package/dist/docs/export-document.js.map +3 -3
- package/dist/doctor/migrations.d.ts +75 -0
- package/dist/doctor/migrations.d.ts.map +1 -0
- package/dist/doctor/migrations.js +136 -0
- package/dist/doctor/migrations.js.map +1 -0
- package/dist/doctor/sync-sealer.d.ts +48 -0
- package/dist/doctor/sync-sealer.d.ts.map +1 -0
- package/dist/doctor/sync-sealer.js +109 -0
- package/dist/doctor/sync-sealer.js.map +1 -0
- package/dist/doctor/sync-triggers.d.ts +53 -0
- package/dist/doctor/sync-triggers.d.ts.map +1 -0
- package/dist/doctor/sync-triggers.js +157 -0
- package/dist/doctor/sync-triggers.js.map +1 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +5 -1
- package/dist/init.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/llm/plugin-facade.js +8442 -3623
- package/dist/llm/plugin-facade.js.map +3 -3
- package/dist/nexus/projects-clean.d.ts +3 -0
- package/dist/nexus/projects-clean.d.ts.map +1 -1
- package/dist/nexus/projects-clean.js +9 -3
- package/dist/nexus/projects-clean.js.map +1 -1
- package/dist/nexus/registry-roots.d.ts +3 -1
- package/dist/nexus/registry-roots.d.ts.map +1 -1
- package/dist/nexus/registry-roots.js +5 -2
- package/dist/nexus/registry-roots.js.map +1 -1
- package/dist/nexus/registry.d.ts +8 -1
- package/dist/nexus/registry.d.ts.map +1 -1
- package/dist/nexus/registry.js +24 -10
- package/dist/nexus/registry.js.map +1 -1
- package/dist/nexus/workspace.d.ts +4 -1
- package/dist/nexus/workspace.d.ts.map +1 -1
- package/dist/nexus/workspace.js +10 -4
- package/dist/nexus/workspace.js.map +1 -1
- package/dist/orchestrate/spawn-ops.d.ts.map +1 -1
- package/dist/orchestrate/spawn-ops.js +7 -2
- package/dist/orchestrate/spawn-ops.js.map +1 -1
- package/dist/orchestrate/worker-verify.d.ts +121 -12
- package/dist/orchestrate/worker-verify.d.ts.map +1 -1
- package/dist/orchestrate/worker-verify.js +172 -21
- package/dist/orchestrate/worker-verify.js.map +1 -1
- package/dist/orchestration/spawn-prompt.d.ts.map +1 -1
- package/dist/orchestration/spawn-prompt.js +15 -9
- package/dist/orchestration/spawn-prompt.js.map +1 -1
- package/dist/release/ci-evidence.d.ts +9 -0
- package/dist/release/ci-evidence.d.ts.map +1 -1
- package/dist/release/ci-evidence.js +9 -2
- package/dist/release/ci-evidence.js.map +1 -1
- package/dist/resources/backend.d.ts +15 -3
- package/dist/resources/backend.d.ts.map +1 -1
- package/dist/resources/backend.js +7 -3
- package/dist/resources/backend.js.map +1 -1
- package/dist/resources/darwin-backend.d.ts +142 -0
- package/dist/resources/darwin-backend.d.ts.map +1 -0
- package/dist/resources/darwin-backend.js +288 -0
- package/dist/resources/darwin-backend.js.map +1 -0
- package/dist/resources/governor.d.ts +44 -1
- package/dist/resources/governor.d.ts.map +1 -1
- package/dist/resources/governor.js +100 -36
- package/dist/resources/governor.js.map +1 -1
- package/dist/resources/heavy-command.d.ts +132 -0
- package/dist/resources/heavy-command.d.ts.map +1 -0
- package/dist/resources/heavy-command.js +768 -0
- package/dist/resources/heavy-command.js.map +1 -0
- package/dist/resources/monitor.d.ts +50 -1
- package/dist/resources/monitor.d.ts.map +1 -1
- package/dist/resources/monitor.js +83 -1
- package/dist/resources/monitor.js.map +1 -1
- package/dist/resources/run-admission.d.ts +303 -0
- package/dist/resources/run-admission.d.ts.map +1 -0
- package/dist/resources/run-admission.js +648 -0
- package/dist/resources/run-admission.js.map +1 -0
- package/dist/resources/run-class.d.ts +111 -0
- package/dist/resources/run-class.d.ts.map +1 -0
- package/dist/resources/run-class.js +658 -0
- package/dist/resources/run-class.js.map +1 -0
- package/dist/resources/run-governed.d.ts +158 -0
- package/dist/resources/run-governed.d.ts.map +1 -0
- package/dist/resources/run-governed.js +406 -0
- package/dist/resources/run-governed.js.map +1 -0
- package/dist/resources/slot-holder.d.ts +267 -0
- package/dist/resources/slot-holder.d.ts.map +1 -0
- package/dist/resources/slot-holder.js +505 -0
- package/dist/resources/slot-holder.js.map +1 -0
- package/dist/resources/tool-groups.d.ts +74 -0
- package/dist/resources/tool-groups.d.ts.map +1 -0
- package/dist/resources/tool-groups.js +118 -0
- package/dist/resources/tool-groups.js.map +1 -0
- package/dist/routing/build-command-groups.d.ts.map +1 -1
- package/dist/routing/build-command-groups.js +1 -0
- package/dist/routing/build-command-groups.js.map +1 -1
- package/dist/selfimprove/fix-gen.js +8446 -3627
- package/dist/selfimprove/fix-gen.js.map +3 -3
- package/dist/sentient/cross-project-hygiene.d.ts +3 -2
- package/dist/sentient/cross-project-hygiene.d.ts.map +1 -1
- package/dist/sentient/cross-project-hygiene.js +5 -2
- package/dist/sentient/cross-project-hygiene.js.map +1 -1
- package/dist/sentient/state.d.ts +7 -0
- package/dist/sentient/state.d.ts.map +1 -1
- package/dist/sentient/state.js.map +1 -1
- package/dist/sentient/tick.d.ts +14 -2
- package/dist/sentient/tick.d.ts.map +1 -1
- package/dist/sentient/tick.js +63 -4
- package/dist/sentient/tick.js.map +1 -1
- package/dist/store/backup-recover.d.ts.map +1 -1
- package/dist/store/backup-recover.js +5 -0
- package/dist/store/backup-recover.js.map +1 -1
- package/dist/store/data-accessor.d.ts +1 -0
- package/dist/store/data-accessor.d.ts.map +1 -1
- package/dist/store/data-accessor.js +7 -0
- package/dist/store/data-accessor.js.map +1 -1
- package/dist/store/dual-scope-db.d.ts +23 -1
- package/dist/store/dual-scope-db.d.ts.map +1 -1
- package/dist/store/dual-scope-db.js +126 -36
- package/dist/store/dual-scope-db.js.map +1 -1
- package/dist/store/exodus/recovery.d.ts.map +1 -1
- package/dist/store/exodus/recovery.js +15 -0
- package/dist/store/exodus/recovery.js.map +1 -1
- package/dist/store/legacy-tasks-lineage.d.ts.map +1 -1
- package/dist/store/legacy-tasks-lineage.js +5 -0
- package/dist/store/legacy-tasks-lineage.js.map +1 -1
- package/dist/store/lock.d.ts +8 -1
- package/dist/store/lock.d.ts.map +1 -1
- package/dist/store/lock.js +10 -2
- package/dist/store/lock.js.map +1 -1
- package/dist/store/migration-manager.d.ts +5 -0
- package/dist/store/migration-manager.d.ts.map +1 -1
- package/dist/store/migration-manager.js +11 -0
- package/dist/store/migration-manager.js.map +1 -1
- package/dist/store/migration-runner.d.ts +158 -0
- package/dist/store/migration-runner.d.ts.map +1 -0
- package/dist/store/migration-runner.js +493 -0
- package/dist/store/migration-runner.js.map +1 -0
- package/dist/store/portable-bundle-import.d.ts +15 -1
- package/dist/store/portable-bundle-import.d.ts.map +1 -1
- package/dist/store/portable-bundle-import.js +24 -4
- package/dist/store/portable-bundle-import.js.map +1 -1
- package/dist/store/portable-bundle-relocate.d.ts +43 -0
- package/dist/store/portable-bundle-relocate.d.ts.map +1 -1
- package/dist/store/portable-bundle-relocate.js +79 -5
- package/dist/store/portable-bundle-relocate.js.map +1 -1
- package/dist/store/portable-bundle-scan.d.ts +42 -0
- package/dist/store/portable-bundle-scan.d.ts.map +1 -1
- package/dist/store/portable-bundle-scan.js +132 -25
- package/dist/store/portable-bundle-scan.js.map +1 -1
- package/dist/store/portable-bundle.d.ts +40 -0
- package/dist/store/portable-bundle.d.ts.map +1 -1
- package/dist/store/portable-bundle.js +43 -7
- package/dist/store/portable-bundle.js.map +1 -1
- package/dist/store/row-identity-registry.d.ts +137 -7
- package/dist/store/row-identity-registry.d.ts.map +1 -1
- package/dist/store/row-identity-registry.js +364 -9
- package/dist/store/row-identity-registry.js.map +1 -1
- package/dist/store/row-identity.d.ts.map +1 -1
- package/dist/store/row-identity.js +9 -10
- package/dist/store/row-identity.js.map +1 -1
- package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
- package/dist/store/sqlite-data-accessor.js +10 -0
- package/dist/store/sqlite-data-accessor.js.map +1 -1
- package/dist/store/sqlite.d.ts +31 -0
- package/dist/store/sqlite.d.ts.map +1 -1
- package/dist/store/sqlite.js +5 -1
- package/dist/store/sqlite.js.map +1 -1
- package/dist/store/sync/capture.d.ts +203 -0
- package/dist/store/sync/capture.d.ts.map +1 -0
- package/dist/store/sync/capture.js +562 -0
- package/dist/store/sync/capture.js.map +1 -0
- package/dist/store/sync/clock-store.d.ts.map +1 -1
- package/dist/store/sync/clock-store.js +2 -0
- package/dist/store/sync/clock-store.js.map +1 -1
- package/dist/store/sync/fk-orphans.d.ts +133 -0
- package/dist/store/sync/fk-orphans.d.ts.map +1 -0
- package/dist/store/sync/fk-orphans.js +265 -0
- package/dist/store/sync/fk-orphans.js.map +1 -0
- package/dist/store/sync/flags.d.ts +7 -0
- package/dist/store/sync/flags.d.ts.map +1 -1
- package/dist/store/sync/flags.js +16 -0
- package/dist/store/sync/flags.js.map +1 -1
- package/dist/store/sync/hlc.d.ts.map +1 -1
- package/dist/store/sync/hlc.js +4 -0
- package/dist/store/sync/hlc.js.map +1 -1
- package/dist/store/sync/identity-fill.d.ts +45 -0
- package/dist/store/sync/identity-fill.d.ts.map +1 -0
- package/dist/store/sync/identity-fill.js +85 -0
- package/dist/store/sync/identity-fill.js.map +1 -0
- package/dist/store/sync/machinery.d.ts +27 -0
- package/dist/store/sync/machinery.d.ts.map +1 -0
- package/dist/store/sync/machinery.js +44 -0
- package/dist/store/sync/machinery.js.map +1 -0
- package/dist/store/sync/netting.d.ts +120 -0
- package/dist/store/sync/netting.d.ts.map +1 -0
- package/dist/store/sync/netting.js +336 -0
- package/dist/store/sync/netting.js.map +1 -0
- package/dist/store/sync/replica.d.ts +27 -2
- package/dist/store/sync/replica.d.ts.map +1 -1
- package/dist/store/sync/replica.js +47 -3
- package/dist/store/sync/replica.js.map +1 -1
- package/dist/store/sync/schema.d.ts +12 -0
- package/dist/store/sync/schema.d.ts.map +1 -1
- package/dist/store/sync/schema.js +21 -0
- package/dist/store/sync/schema.js.map +1 -1
- package/dist/store/sync/seal-backlog.d.ts +21 -0
- package/dist/store/sync/seal-backlog.d.ts.map +1 -0
- package/dist/store/sync/seal-backlog.js +24 -0
- package/dist/store/sync/seal-backlog.js.map +1 -0
- package/dist/store/sync/sealer-values.d.ts +34 -0
- package/dist/store/sync/sealer-values.d.ts.map +1 -0
- package/dist/store/sync/sealer-values.js +53 -0
- package/dist/store/sync/sealer-values.js.map +1 -0
- package/dist/store/sync/sealer.d.ts +145 -0
- package/dist/store/sync/sealer.d.ts.map +1 -0
- package/dist/store/sync/sealer.js +906 -0
- package/dist/store/sync/sealer.js.map +1 -0
- package/dist/store/sync/structural.d.ts +63 -0
- package/dist/store/sync/structural.d.ts.map +1 -0
- package/dist/store/sync/structural.js +180 -0
- package/dist/store/sync/structural.js.map +1 -0
- package/dist/store/sync/timestamps.d.ts +92 -0
- package/dist/store/sync/timestamps.d.ts.map +1 -0
- package/dist/store/sync/timestamps.js +228 -0
- package/dist/store/sync/timestamps.js.map +1 -0
- package/dist/store/sync/tombstones.d.ts +75 -0
- package/dist/store/sync/tombstones.d.ts.map +1 -0
- package/dist/store/sync/tombstones.js +103 -0
- package/dist/store/sync/tombstones.js.map +1 -0
- package/dist/store/sync/trigger-classes.d.ts +221 -0
- package/dist/store/sync/trigger-classes.d.ts.map +1 -0
- package/dist/store/sync/trigger-classes.js +441 -0
- package/dist/store/sync/trigger-classes.js.map +1 -0
- package/dist/store/sync/trigger-suspend-at-path.d.ts +23 -0
- package/dist/store/sync/trigger-suspend-at-path.d.ts.map +1 -0
- package/dist/store/sync/trigger-suspend-at-path.js +44 -0
- package/dist/store/sync/trigger-suspend-at-path.js.map +1 -0
- package/dist/store/sync/writer-version.d.ts +43 -0
- package/dist/store/sync/writer-version.d.ts.map +1 -0
- package/dist/store/sync/writer-version.js +90 -0
- package/dist/store/sync/writer-version.js.map +1 -0
- package/dist/store/table-classification.d.ts.map +1 -1
- package/dist/store/table-classification.js +102 -0
- package/dist/store/table-classification.js.map +1 -1
- package/dist/store/trigger-suspend-schema.d.ts +44 -0
- package/dist/store/trigger-suspend-schema.d.ts.map +1 -0
- package/dist/store/trigger-suspend-schema.js +25 -0
- package/dist/store/trigger-suspend-schema.js.map +1 -0
- package/dist/store/vault-manifest.d.ts +365 -0
- package/dist/store/vault-manifest.d.ts.map +1 -0
- package/dist/store/vault-manifest.js +812 -0
- package/dist/store/vault-manifest.js.map +1 -0
- package/dist/store/writer-lease.d.ts +31 -0
- package/dist/store/writer-lease.d.ts.map +1 -1
- package/dist/store/writer-lease.js +65 -2
- package/dist/store/writer-lease.js.map +1 -1
- package/dist/system/health.d.ts.map +1 -1
- package/dist/system/health.js +21 -0
- package/dist/system/health.js.map +1 -1
- package/dist/system/project-health.d.ts.map +1 -1
- package/dist/system/project-health.js +3 -1
- package/dist/system/project-health.js.map +1 -1
- package/dist/tasks/affected-packages.d.ts +58 -0
- package/dist/tasks/affected-packages.d.ts.map +1 -1
- package/dist/tasks/affected-packages.js +75 -6
- package/dist/tasks/affected-packages.js.map +1 -1
- package/dist/tasks/affected-scope.d.ts +150 -23
- package/dist/tasks/affected-scope.d.ts.map +1 -1
- package/dist/tasks/affected-scope.js +320 -32
- package/dist/tasks/affected-scope.js.map +1 -1
- package/dist/tasks/change-set.d.ts +96 -0
- package/dist/tasks/change-set.d.ts.map +1 -1
- package/dist/tasks/change-set.js +169 -4
- package/dist/tasks/change-set.js.map +1 -1
- package/dist/tasks/complete-ci.d.ts +115 -0
- package/dist/tasks/complete-ci.d.ts.map +1 -0
- package/dist/tasks/complete-ci.js +171 -0
- package/dist/tasks/complete-ci.js.map +1 -0
- package/dist/tasks/complete.d.ts.map +1 -1
- package/dist/tasks/complete.js +50 -17
- package/dist/tasks/complete.js.map +1 -1
- package/dist/tasks/compute-task-view.d.ts.map +1 -1
- package/dist/tasks/compute-task-view.js +7 -0
- package/dist/tasks/compute-task-view.js.map +1 -1
- package/dist/tasks/done-plan.d.ts.map +1 -1
- package/dist/tasks/done-plan.js +51 -30
- package/dist/tasks/done-plan.js.map +1 -1
- package/dist/tasks/done-record.d.ts +6 -0
- package/dist/tasks/done-record.d.ts.map +1 -1
- package/dist/tasks/done-record.js +9 -3
- package/dist/tasks/done-record.js.map +1 -1
- package/dist/tasks/evidence.d.ts +3 -2
- package/dist/tasks/evidence.d.ts.map +1 -1
- package/dist/tasks/evidence.js +143 -12
- package/dist/tasks/evidence.js.map +1 -1
- package/dist/tasks/gate-notes.d.ts +22 -0
- package/dist/tasks/gate-notes.d.ts.map +1 -0
- package/dist/tasks/gate-notes.js +33 -0
- package/dist/tasks/gate-notes.js.map +1 -0
- package/dist/tasks/index.d.ts +2 -1
- package/dist/tasks/index.d.ts.map +1 -1
- package/dist/tasks/index.js +2 -1
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/test-run-binding.d.ts +117 -0
- package/dist/tasks/test-run-binding.d.ts.map +1 -0
- package/dist/tasks/test-run-binding.js +365 -0
- package/dist/tasks/test-run-binding.js.map +1 -0
- package/dist/tasks/tool-cache-env.d.ts +123 -0
- package/dist/tasks/tool-cache-env.d.ts.map +1 -0
- package/dist/tasks/tool-cache-env.js +287 -0
- package/dist/tasks/tool-cache-env.js.map +1 -0
- package/dist/tasks/tool-cache-failed-first.d.ts +155 -0
- package/dist/tasks/tool-cache-failed-first.d.ts.map +1 -0
- package/dist/tasks/tool-cache-failed-first.js +285 -0
- package/dist/tasks/tool-cache-failed-first.js.map +1 -0
- package/dist/tasks/tool-cache.d.ts +375 -55
- package/dist/tasks/tool-cache.d.ts.map +1 -1
- package/dist/tasks/tool-cache.js +912 -303
- package/dist/tasks/tool-cache.js.map +1 -1
- package/dist/tasks/tool-resolver.d.ts +4 -3
- package/dist/tasks/tool-resolver.d.ts.map +1 -1
- package/dist/tasks/tool-resolver.js +15 -4
- package/dist/tasks/tool-resolver.js.map +1 -1
- package/dist/tasks/tool-semaphore.d.ts +67 -7
- package/dist/tasks/tool-semaphore.d.ts.map +1 -1
- package/dist/tasks/tool-semaphore.js +134 -56
- package/dist/tasks/tool-semaphore.js.map +1 -1
- package/dist/upgrade.d.ts.map +1 -1
- package/dist/upgrade.js +4 -0
- package/dist/upgrade.js.map +1 -1
- package/dist/validation/engine-ops.d.ts +6 -0
- package/dist/validation/engine-ops.d.ts.map +1 -1
- package/dist/validation/engine-ops.js +8 -0
- package/dist/validation/engine-ops.js.map +1 -1
- package/migrations/drizzle-cleo-project/20260930170000_t12819-trigger-suspend-clause/migration.sql +367 -0
- package/migrations/sync-journal/20260930120000_t12343-capture/migration.sql +47 -0
- package/migrations/sync-journal/20261001170000_t12984-sealer/migration.sql +101 -0
- package/package.json +13 -13
- package/templates/CLEO-INJECTION.md +5 -5
- package/templates/CLEO-REFERENCE.md +8 -4
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trigger classes and the suspension clause (journal spec §3.5 Rule 4, C2,
|
|
3
|
+
* D4; T12813, T12819, T12827).
|
|
4
|
+
*
|
|
5
|
+
* Every trigger in a store has exactly one class. The class decides whether
|
|
6
|
+
* the trigger carries the suspension clause, which a rebase frame uses to
|
|
7
|
+
* turn a class off inside its own transaction:
|
|
8
|
+
*
|
|
9
|
+
* | Class | Carries the clause | Example |
|
|
10
|
+
* |---|---|---|
|
|
11
|
+
* | `capture` | yes, scope `capture` | `_sync_cap_*` (S2 capture triggers) |
|
|
12
|
+
* | `guard` | yes, scope `guard` | the parent-cycle and status-pipeline guards |
|
|
13
|
+
* | `side-effect` | yes, scope `side-effect` | claim release, the handoff mirror |
|
|
14
|
+
* | `derived-maintenance` | no, always active | FTS `_ai`/`_ad`/`_au` |
|
|
15
|
+
* | `frozen-guard` | no, never modified | triggers on frozen-legacy bare twins |
|
|
16
|
+
*
|
|
17
|
+
* `guard` and `side-effect` triggers are OWNED: {@link OWNED_TRIGGERS} names
|
|
18
|
+
* each one, and its DDL is the last `CREATE TRIGGER` for that name in the
|
|
19
|
+
* migration lineage from the C2 migration on, or the code-held DDL of
|
|
20
|
+
* {@link CODE_OWNED_TRIGGER_DDL} ({@link ownedTriggerDdl}). The open pass and
|
|
21
|
+
* the doctor compare each owned trigger's LIVE text with that DDL and re-run
|
|
22
|
+
* the DDL when it differs or is missing (C2(b)), but only where every table
|
|
23
|
+
* the trigger needs exists ({@link OWNED_TRIGGER_REQUIRES}).
|
|
24
|
+
*
|
|
25
|
+
* A trigger is `frozen-guard` ONLY because of the table it fires ON: a
|
|
26
|
+
* `frozen-legacy` bare twin. A rebase never touches a frozen twin, and giving
|
|
27
|
+
* such a trigger a clause would make 9.24's replace-if-text-differs
|
|
28
|
+
* (`twin-collapse.ts`) and this build's repair flip its DDL on every open
|
|
29
|
+
* that alternates builds (D4). Its name never decides: a trigger on a live
|
|
30
|
+
* table (even one named `t12535_*`) is owned, or unclassified.
|
|
31
|
+
*
|
|
32
|
+
* @task T12819
|
|
33
|
+
* @task T12827
|
|
34
|
+
* @module store/sync/trigger-classes
|
|
35
|
+
*/
|
|
36
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
37
|
+
import { join } from 'node:path';
|
|
38
|
+
import { resolveCorePackageMigrationsFolder } from '../resolve-migrations-folder.js';
|
|
39
|
+
import { classifyTable } from '../table-classification.js';
|
|
40
|
+
/** The schema-owned flag table the suspension clause reads. Never dropped. */
|
|
41
|
+
export const TRIGGER_SUSPEND_TABLE = 'cleo_trigger_suspend';
|
|
42
|
+
/**
|
|
43
|
+
* DDL of the flag table, byte-identical to the C2 migration's first
|
|
44
|
+
* statement. The open pass runs it (step 0) before migrations.
|
|
45
|
+
*/
|
|
46
|
+
export const TRIGGER_SUSPEND_TABLE_DDL = `CREATE TABLE IF NOT EXISTS \`cleo_trigger_suspend\` (
|
|
47
|
+
\`scope\` TEXT PRIMARY KEY NOT NULL CHECK (\`scope\` IN ('capture', 'guard', 'side-effect', 'all'))
|
|
48
|
+
)`;
|
|
49
|
+
/** The C2 migration folder (drizzle-cleo-project). Owned DDL is read from it on. */
|
|
50
|
+
export const TRIGGER_CLAUSE_MIGRATION = '20260930170000_t12819-trigger-suspend-clause';
|
|
51
|
+
/** The lineage the owned triggers live in. */
|
|
52
|
+
export const OWNED_TRIGGER_LINEAGE = 'drizzle-cleo-project';
|
|
53
|
+
/**
|
|
54
|
+
* The suspension clause a trigger of `cls` carries, exactly as written in
|
|
55
|
+
* migration SQL. Verification compares it whitespace-normalized.
|
|
56
|
+
*
|
|
57
|
+
* The table name is unqualified on purpose. A persistent trigger resolves an
|
|
58
|
+
* unqualified name in its own schema (a TEMP table cannot shadow it), so the
|
|
59
|
+
* trigger keeps working when the store is ATTACHed under another name; a
|
|
60
|
+
* `main.` qualifier makes an attached schema unreadable ("cannot reference
|
|
61
|
+
* objects in database main").
|
|
62
|
+
*/
|
|
63
|
+
export function suspendClause(cls) {
|
|
64
|
+
return `NOT EXISTS (SELECT 1 FROM cleo_trigger_suspend WHERE scope IN ('${cls}', 'all'))`;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The owned guard and side-effect triggers (project store), by name. Each
|
|
68
|
+
* one's DDL comes from the migration lineage or from
|
|
69
|
+
* {@link CODE_OWNED_TRIGGER_DDL} ({@link ownedTriggerDdl}).
|
|
70
|
+
*/
|
|
71
|
+
export const OWNED_TRIGGERS = {
|
|
72
|
+
tasks_tasks_parent_cycle_guard_insert: 'guard',
|
|
73
|
+
tasks_tasks_parent_cycle_guard_update: 'guard',
|
|
74
|
+
tasks_tasks_parent_type_matrix_insert: 'guard',
|
|
75
|
+
tasks_tasks_parent_type_matrix_update: 'guard',
|
|
76
|
+
trg_tasks_tasks_status_pipeline_insert: 'guard',
|
|
77
|
+
trg_tasks_tasks_status_pipeline_update: 'guard',
|
|
78
|
+
tasks_task_relations_non_containment_insert: 'guard',
|
|
79
|
+
tasks_task_relations_non_containment_update: 'guard',
|
|
80
|
+
tasks_task_acceptance_child_target_insert: 'guard',
|
|
81
|
+
tasks_task_acceptance_child_target_update: 'guard',
|
|
82
|
+
trg_tasks_session_handoff_no_update: 'guard',
|
|
83
|
+
tasks_tasks_lease_iso_insert: 'guard',
|
|
84
|
+
tasks_tasks_lease_iso_update: 'guard',
|
|
85
|
+
// T12886 (9.26): first created by its own released migration; the C2 file
|
|
86
|
+
// (dated after it) re-creates both with the suspension clause.
|
|
87
|
+
tasks_task_dependencies_cycle_guard_insert: 'guard',
|
|
88
|
+
tasks_task_dependencies_cycle_guard_update: 'guard',
|
|
89
|
+
trg_tasks_session_handoff_mirror: 'side-effect',
|
|
90
|
+
tasks_sessions_release_claims_on_end: 'side-effect',
|
|
91
|
+
tasks_sessions_release_claims_on_delete: 'side-effect',
|
|
92
|
+
tasks_tasks_release_claim_on_terminal: 'side-effect',
|
|
93
|
+
trg_tasks_ac_uid_graveyard: 'side-effect',
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Owned triggers whose DDL is held in code, not in the C2 migration.
|
|
97
|
+
*
|
|
98
|
+
* The AC uid graveyard trigger writes `tasks_ac_uid_graveyard`. A store whose
|
|
99
|
+
* t12341 migration was probe-stamped after only its ADD COLUMNs ran (the
|
|
100
|
+
* 9.25 live-cleocode shape) has no graveyard table, and the table is created
|
|
101
|
+
* only by the flag-gated identity heal. A migration that created the trigger
|
|
102
|
+
* unconditionally would make every acceptance-criterion delete fail there
|
|
103
|
+
* ("no such table: main.tasks_ac_uid_graveyard"), so the trigger is installed
|
|
104
|
+
* by the open pass, and only where its table exists.
|
|
105
|
+
*/
|
|
106
|
+
export const CODE_OWNED_TRIGGER_DDL = {
|
|
107
|
+
trg_tasks_ac_uid_graveyard: `CREATE TRIGGER \`trg_tasks_ac_uid_graveyard\`
|
|
108
|
+
AFTER DELETE ON \`tasks_task_acceptance_criteria\`
|
|
109
|
+
WHEN (OLD.\`uid\` IS NOT NULL)
|
|
110
|
+
AND ${suspendClause('side-effect')}
|
|
111
|
+
BEGIN
|
|
112
|
+
INSERT INTO \`tasks_ac_uid_graveyard\` (\`ac_id\`, \`uid\`, \`task_id\`, \`ordinal\`, \`text\`, \`birth_fp\`, \`deleted_at\`)
|
|
113
|
+
VALUES (OLD.\`id\`, OLD.\`uid\`, OLD.\`task_id\`, OLD.\`ordinal\`, OLD.\`text\`, OLD.\`birth_fp\`, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'));
|
|
114
|
+
END`,
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Tables an owned trigger needs besides the one it fires ON. Where one is
|
|
118
|
+
* missing the trigger is not verified or installed, and a live copy is a
|
|
119
|
+
* finding (`dangling`) that repair drops: it would fail every write that
|
|
120
|
+
* fires it.
|
|
121
|
+
*/
|
|
122
|
+
export const OWNED_TRIGGER_REQUIRES = {
|
|
123
|
+
trg_tasks_ac_uid_graveyard: ['tasks_ac_uid_graveyard'],
|
|
124
|
+
};
|
|
125
|
+
/** Prefix of every capture trigger (S2, §2.3). */
|
|
126
|
+
export const CAPTURE_TRIGGER_PREFIX = '_sync_cap_';
|
|
127
|
+
/**
|
|
128
|
+
* The tables a trigger body writes: `INSERT INTO t`, `UPDATE t`,
|
|
129
|
+
* `DELETE FROM t`, `REPLACE INTO t`. Lower-cased; quotes stripped.
|
|
130
|
+
*/
|
|
131
|
+
export function triggerWriteTargets(sql) {
|
|
132
|
+
const body = sql.slice(Math.max(0, sql.search(/\bBEGIN\b/i)));
|
|
133
|
+
const out = new Set();
|
|
134
|
+
const re = /\b(?:insert(?:\s+or\s+\w+)?\s+into|replace\s+into|update(?:\s+or\s+\w+)?|delete\s+from)\s+[`"[]?(?:main\.)?[`"[]?(\w+)/gi;
|
|
135
|
+
for (const m of body.matchAll(re))
|
|
136
|
+
out.add(m[1].toLowerCase());
|
|
137
|
+
return [...out];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Classify one trigger. Returns `undefined` for a trigger no rule covers,
|
|
141
|
+
* which the Gate test reports.
|
|
142
|
+
*
|
|
143
|
+
* @param scope - The store the trigger lives in.
|
|
144
|
+
* @param name - `sqlite_master.name`.
|
|
145
|
+
* @param table - `sqlite_master.tbl_name`.
|
|
146
|
+
* @param sql - `sqlite_master.sql`.
|
|
147
|
+
*/
|
|
148
|
+
export function classifyTrigger(scope, name, table, sql) {
|
|
149
|
+
if (name.startsWith(CAPTURE_TRIGGER_PREFIX)) {
|
|
150
|
+
return { name, table, class: 'capture', reason: 'journal capture trigger (§2.3)' };
|
|
151
|
+
}
|
|
152
|
+
const tableClass = classifyTable(scope, table);
|
|
153
|
+
if (tableClass.kind === 'entry' && tableClass.entry.status === 'frozen-legacy') {
|
|
154
|
+
return {
|
|
155
|
+
name,
|
|
156
|
+
table,
|
|
157
|
+
class: 'frozen-guard',
|
|
158
|
+
reason: 'on a frozen-legacy bare twin: no clause, never rewritten (D4)',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const owned = OWNED_TRIGGERS[name];
|
|
162
|
+
if (owned !== undefined) {
|
|
163
|
+
return { name, table, class: owned, reason: 'owned by the C2 migration (T12819)' };
|
|
164
|
+
}
|
|
165
|
+
const targets = triggerWriteTargets(sql);
|
|
166
|
+
if (targets.length > 0 &&
|
|
167
|
+
targets.every((t) => {
|
|
168
|
+
const c = classifyTable(scope, t);
|
|
169
|
+
return (c.kind === 'entry' || c.kind === 'pattern') && c.class === 'derived';
|
|
170
|
+
})) {
|
|
171
|
+
return {
|
|
172
|
+
name,
|
|
173
|
+
table,
|
|
174
|
+
class: 'derived-maintenance',
|
|
175
|
+
reason: `writes only derived tables (${targets.join(', ')}); always active`,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
/** Collapse whitespace so text comparisons ignore layout. */
|
|
181
|
+
export function normalizeSql(sql) {
|
|
182
|
+
return sql
|
|
183
|
+
.replace(/--[^\n]*/g, '')
|
|
184
|
+
.replace(/\s+/g, ' ')
|
|
185
|
+
.replace(/\s*;\s*$/, '')
|
|
186
|
+
.trim();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Split migration text into `CREATE TRIGGER` statements, by name. Later
|
|
190
|
+
* definitions of the same name replace earlier ones. `IF NOT EXISTS` is
|
|
191
|
+
* removed, as SQLite removes it from `sqlite_master.sql`.
|
|
192
|
+
*/
|
|
193
|
+
export function createTriggerStatements(migrationSql) {
|
|
194
|
+
const out = new Map();
|
|
195
|
+
for (const chunk of migrationSql.split('--> statement-breakpoint')) {
|
|
196
|
+
const stmt = chunk.replace(/^(?:\s*--[^\n]*\n|\s*\n)*/, '').trim();
|
|
197
|
+
const m = /^CREATE\s+TRIGGER\s+(?:IF\s+NOT\s+EXISTS\s+)?[`"]?(\w+)[`"]?/i.exec(stmt);
|
|
198
|
+
if (!m)
|
|
199
|
+
continue;
|
|
200
|
+
out.set(m[1], stmt
|
|
201
|
+
.replace(/^CREATE\s+TRIGGER\s+IF\s+NOT\s+EXISTS\s+/i, 'CREATE TRIGGER ')
|
|
202
|
+
.replace(/;$/, ''));
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The owned trigger DDL: {@link CODE_OWNED_TRIGGER_DDL}, then the last
|
|
208
|
+
* `CREATE TRIGGER` for each owned name in the lineage's migration files from
|
|
209
|
+
* {@link TRIGGER_CLAUSE_MIGRATION} on, in folder order (a later migration
|
|
210
|
+
* replaces a code-held text).
|
|
211
|
+
*
|
|
212
|
+
* @param folder - The lineage folder (tests pass one; default: installed).
|
|
213
|
+
* @throws {Error} When an owned trigger has no DDL from C2 on.
|
|
214
|
+
*/
|
|
215
|
+
export function ownedTriggerDdl(folder = resolveCorePackageMigrationsFolder(OWNED_TRIGGER_LINEAGE)) {
|
|
216
|
+
const cached = ownedDdlCache.get(folder);
|
|
217
|
+
if (cached)
|
|
218
|
+
return cached;
|
|
219
|
+
const ddl = new Map(Object.entries(CODE_OWNED_TRIGGER_DDL));
|
|
220
|
+
const names = readdirSync(folder)
|
|
221
|
+
.filter((n) => n >= TRIGGER_CLAUSE_MIGRATION && existsSync(join(folder, n, 'migration.sql')))
|
|
222
|
+
.sort();
|
|
223
|
+
for (const n of names) {
|
|
224
|
+
for (const [name, sql] of createTriggerStatements(readFileSync(join(folder, n, 'migration.sql'), 'utf8'))) {
|
|
225
|
+
if (Object.hasOwn(OWNED_TRIGGERS, name))
|
|
226
|
+
ddl.set(name, sql);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const missing = Object.keys(OWNED_TRIGGERS).filter((n) => !ddl.has(n));
|
|
230
|
+
if (missing.length > 0) {
|
|
231
|
+
// @sync-invariant none:local-only owned-trigger DDL missing from the build; packaging check on local schema
|
|
232
|
+
throw new Error(`owned triggers without DDL from ${TRIGGER_CLAUSE_MIGRATION}: ${missing.join(', ')}`);
|
|
233
|
+
}
|
|
234
|
+
ownedDdlCache.set(folder, ddl);
|
|
235
|
+
return ddl;
|
|
236
|
+
}
|
|
237
|
+
/** Owned DDL per lineage folder: migration files never change at runtime. */
|
|
238
|
+
const ownedDdlCache = new Map();
|
|
239
|
+
/** Whether the flag table exists. Read-only. */
|
|
240
|
+
export function hasTriggerSuspendTable(db) {
|
|
241
|
+
return (db
|
|
242
|
+
.prepare("SELECT 1 FROM main.sqlite_master WHERE type = 'table' AND name = ?")
|
|
243
|
+
.get(TRIGGER_SUSPEND_TABLE) !== undefined);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Compare every owned trigger's live `sqlite_master.sql` with its owned DDL,
|
|
247
|
+
* and optionally repair (drop, then re-run the owned DDL) each one that is
|
|
248
|
+
* missing or differs.
|
|
249
|
+
*
|
|
250
|
+
* Only meaningful on a store whose journal has the C2 migration: before it,
|
|
251
|
+
* the owned DDL references a table the store may not have. Callers check
|
|
252
|
+
* {@link hasTriggerSuspendTable} and that the owned tables exist.
|
|
253
|
+
*
|
|
254
|
+
* @param db - A project store handle.
|
|
255
|
+
* @param options - `repair` rewrites; `ddl` overrides the owned DDL (tests).
|
|
256
|
+
* @returns One finding per trigger that was not identical.
|
|
257
|
+
*/
|
|
258
|
+
export function verifyOwnedTriggers(db, options = {}) {
|
|
259
|
+
const ddl = options.ddl ?? ownedTriggerDdl();
|
|
260
|
+
const live = new Map(db
|
|
261
|
+
.prepare("SELECT name, tbl_name, sql FROM main.sqlite_master WHERE type = 'trigger'")
|
|
262
|
+
.all().map((r) => [r.name, r]));
|
|
263
|
+
const hasTable = (t) => db.prepare("SELECT 1 FROM main.sqlite_master WHERE type = 'table' AND name = ?").get(t) !==
|
|
264
|
+
undefined;
|
|
265
|
+
const findings = [];
|
|
266
|
+
// Repairs are planned first and applied in one unit (T13024 MED-2).
|
|
267
|
+
const work = [];
|
|
268
|
+
for (const [name, cls] of Object.entries(OWNED_TRIGGERS)) {
|
|
269
|
+
const want = ddl.get(name);
|
|
270
|
+
const onTable = /\bON\s+[`"]?(\w+)[`"]?/i.exec(want.slice(0, want.search(/\bBEGIN\b/i)))?.[1];
|
|
271
|
+
// The trigger's table may not exist in this store shape (a store the
|
|
272
|
+
// owning migration never reached); nothing to verify then.
|
|
273
|
+
if (onTable && !hasTable(onTable))
|
|
274
|
+
continue;
|
|
275
|
+
const row = live.get(name);
|
|
276
|
+
if ((OWNED_TRIGGER_REQUIRES[name] ?? []).some((t) => !hasTable(t))) {
|
|
277
|
+
if (!row)
|
|
278
|
+
continue;
|
|
279
|
+
if (options.repair)
|
|
280
|
+
work.push({ name, create: null });
|
|
281
|
+
findings.push({ name, problem: 'dangling', repaired: options.repair === true });
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
let problem;
|
|
285
|
+
if (!row)
|
|
286
|
+
problem = 'missing';
|
|
287
|
+
else if (normalizeSql(row.sql) !== normalizeSql(want)) {
|
|
288
|
+
problem = normalizeSql(row.sql).includes(normalizeSql(suspendClause(cls)))
|
|
289
|
+
? 'text-differs'
|
|
290
|
+
: 'no-clause';
|
|
291
|
+
}
|
|
292
|
+
if (!problem)
|
|
293
|
+
continue;
|
|
294
|
+
if (options.repair)
|
|
295
|
+
work.push({ name, create: want });
|
|
296
|
+
findings.push({ name, problem, repaired: options.repair === true });
|
|
297
|
+
}
|
|
298
|
+
if (work.length > 0) {
|
|
299
|
+
atomicDdl(db, () => {
|
|
300
|
+
for (const w of work) {
|
|
301
|
+
db.exec(`DROP TRIGGER IF EXISTS \`${w.name}\``);
|
|
302
|
+
if (w.create !== null)
|
|
303
|
+
db.exec(w.create);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
return findings;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Run DDL as one unit (T13024 MED-2): `BEGIN IMMEDIATE` … `COMMIT` on an idle
|
|
311
|
+
* connection, a SAVEPOINT inside a caller's transaction. A failure rolls the
|
|
312
|
+
* whole unit back, so a DROP never commits without its CREATE.
|
|
313
|
+
*/
|
|
314
|
+
export function atomicDdl(db, fn) {
|
|
315
|
+
if (db.isTransaction) {
|
|
316
|
+
db.exec('SAVEPOINT cleo_atomic_ddl');
|
|
317
|
+
try {
|
|
318
|
+
const out = fn();
|
|
319
|
+
db.exec('RELEASE cleo_atomic_ddl');
|
|
320
|
+
return out;
|
|
321
|
+
}
|
|
322
|
+
catch (err) {
|
|
323
|
+
// SQLite may already have rolled the caller's transaction back (FULL,
|
|
324
|
+
// IOERR, interrupt): then there is no savepoint, and the original error
|
|
325
|
+
// is the one to report.
|
|
326
|
+
if (db.isTransaction) {
|
|
327
|
+
try {
|
|
328
|
+
db.exec('ROLLBACK TO cleo_atomic_ddl; RELEASE cleo_atomic_ddl');
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
// keep the original error
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
throw err;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
db.exec('BEGIN IMMEDIATE');
|
|
338
|
+
try {
|
|
339
|
+
const out = fn();
|
|
340
|
+
db.exec('COMMIT');
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
if (db.isTransaction)
|
|
345
|
+
db.exec('ROLLBACK');
|
|
346
|
+
throw err;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Classify every trigger of a store. Read-only.
|
|
351
|
+
*
|
|
352
|
+
* @returns The classified triggers, and the names no rule covers.
|
|
353
|
+
*/
|
|
354
|
+
export function classifyStoreTriggers(db, scope) {
|
|
355
|
+
const rows = db
|
|
356
|
+
.prepare("SELECT name, tbl_name, sql FROM main.sqlite_master WHERE type = 'trigger' ORDER BY name")
|
|
357
|
+
.all();
|
|
358
|
+
const classified = [];
|
|
359
|
+
const unclassified = [];
|
|
360
|
+
for (const r of rows) {
|
|
361
|
+
const c = classifyTrigger(scope, r.name, r.tbl_name, r.sql);
|
|
362
|
+
if (c)
|
|
363
|
+
classified.push(c);
|
|
364
|
+
else
|
|
365
|
+
unclassified.push(r.name);
|
|
366
|
+
}
|
|
367
|
+
return { classified, unclassified };
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Open-pass step 0 (C2, round 9): make sure `cleo_trigger_suspend` exists,
|
|
371
|
+
* BEFORE migrations, and that it is empty.
|
|
372
|
+
*
|
|
373
|
+
* Every write to tasks, sessions and acceptance criteria fires an owned
|
|
374
|
+
* trigger that reads the table, and any `ALTER … RENAME` re-validates every
|
|
375
|
+
* trigger, so a store missing it cannot be written. A committed row is
|
|
376
|
+
* impossible through the rebase frame (it deletes and asserts before
|
|
377
|
+
* COMMIT); if one is found anyway it is cleared, because a committed
|
|
378
|
+
* suspension would silently disable guards for everyone.
|
|
379
|
+
*
|
|
380
|
+
* Writes nothing when the table exists and is empty.
|
|
381
|
+
*/
|
|
382
|
+
export function ensureTriggerSuspendTable(db) {
|
|
383
|
+
if (!hasTriggerSuspendTable(db)) {
|
|
384
|
+
db.exec(TRIGGER_SUSPEND_TABLE_DDL);
|
|
385
|
+
return { created: true, cleared: 0 };
|
|
386
|
+
}
|
|
387
|
+
const n = db.prepare('SELECT count(*) AS n FROM main.cleo_trigger_suspend').get().n;
|
|
388
|
+
if (n > 0)
|
|
389
|
+
db.exec('DELETE FROM main.cleo_trigger_suspend');
|
|
390
|
+
return { created: false, cleared: n };
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Throw unless `cleo_trigger_suspend` is empty. A frame calls this right
|
|
394
|
+
* before COMMIT, so a suspension can never be committed (C4).
|
|
395
|
+
*
|
|
396
|
+
* @throws {Error} `E_TRIGGER_SUSPEND_NOT_EMPTY` when a row remains.
|
|
397
|
+
*/
|
|
398
|
+
export function assertTriggerSuspendEmpty(db) {
|
|
399
|
+
const row = db.prepare('SELECT scope FROM main.cleo_trigger_suspend LIMIT 1').get();
|
|
400
|
+
if (row) {
|
|
401
|
+
// @sync-invariant none:local-only owned-trigger repair/suspension guard on this store; local schema, not synced rows
|
|
402
|
+
throw Object.assign(new Error(`E_TRIGGER_SUSPEND_NOT_EMPTY: cleo_trigger_suspend still holds scope '${row.scope}'; refusing to commit a suspension`), { code: 'E_TRIGGER_SUSPEND_NOT_EMPTY' });
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/** Purposes that may suspend the `guard` class (T12344 AC5). */
|
|
406
|
+
const GUARD_SUSPEND_PURPOSES = new Set(['rewind', 'undo']);
|
|
407
|
+
/**
|
|
408
|
+
* Run `fn` with the given trigger classes suspended, inside the caller's
|
|
409
|
+
* transaction: insert the scope rows, run, delete them, and assert the table
|
|
410
|
+
* is empty before returning. Other connections never see the rows (WAL), so
|
|
411
|
+
* their triggers stay active; a crash rolls the rows back.
|
|
412
|
+
*
|
|
413
|
+
* Suspending `guard` (or `all`) is allowed only to rewind or undo (T12344
|
|
414
|
+
* AC5): a forward apply or replay with the guards off would commit a merged
|
|
415
|
+
* state they refuse (a dependency or parent cycle) without a word.
|
|
416
|
+
*
|
|
417
|
+
* @throws {Error} When called outside a transaction, or
|
|
418
|
+
* `E_GUARD_SUSPEND_FORWARD` when a forward purpose suspends `guard`.
|
|
419
|
+
*/
|
|
420
|
+
export function withTriggersSuspended(db, scopes, purpose, fn) {
|
|
421
|
+
if (!db.isTransaction) {
|
|
422
|
+
// @sync-invariant none:local-only programming-error guard on the local suspension bracket
|
|
423
|
+
throw new Error('withTriggersSuspended must run inside the caller transaction');
|
|
424
|
+
}
|
|
425
|
+
if ((scopes.includes('guard') || scopes.includes('all')) &&
|
|
426
|
+
!GUARD_SUSPEND_PURPOSES.has(purpose)) {
|
|
427
|
+
// @sync-invariant none:local-only owned-trigger repair/suspension guard on this store; local schema, not synced rows
|
|
428
|
+
throw Object.assign(new Error(`E_GUARD_SUSPEND_FORWARD: guards may be suspended only to rewind or undo, not for a '${purpose}' write`), { code: 'E_GUARD_SUSPEND_FORWARD' });
|
|
429
|
+
}
|
|
430
|
+
const ins = db.prepare('INSERT OR IGNORE INTO main.cleo_trigger_suspend (scope) VALUES (?)');
|
|
431
|
+
for (const s of scopes)
|
|
432
|
+
ins.run(s);
|
|
433
|
+
try {
|
|
434
|
+
return fn();
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
db.exec('DELETE FROM main.cleo_trigger_suspend');
|
|
438
|
+
assertTriggerSuspendEmpty(db);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=trigger-classes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-classes.js","sourceRoot":"","sources":["../../../src/store/sync/trigger-classes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create `cleo_trigger_suspend` in a store FILE that was just placed outside
|
|
3
|
+
* the open pass: a bundle import or a restore from a snapshot (journal spec
|
|
4
|
+
* §3.5 Rule 4, round 9; T12819).
|
|
5
|
+
*
|
|
6
|
+
* A placed store that syncs also has its whole sync set marked suspect
|
|
7
|
+
* (§2.3a rule 3). A placed store may come from an older build. Until the next chokepoint
|
|
8
|
+
* open, a 9.24 binary would open it as is; with the table present its owned
|
|
9
|
+
* triggers (if any carry the clause) keep working. A file that is not a
|
|
10
|
+
* project store (no `tasks_tasks`) is left alone.
|
|
11
|
+
*
|
|
12
|
+
* @task T12819
|
|
13
|
+
* @module store/sync/trigger-suspend-at-path
|
|
14
|
+
*/
|
|
15
|
+
import { type TriggerSuspendStepZero } from './trigger-classes.js';
|
|
16
|
+
/**
|
|
17
|
+
* Open `dbPath` read-write, create the flag table when it is a project store
|
|
18
|
+
* missing it, and close.
|
|
19
|
+
*
|
|
20
|
+
* @returns What step 0 did, or `null` when the file is not a project store.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ensureTriggerSuspendTableAtPath(dbPath: string): TriggerSuspendStepZero | null;
|
|
23
|
+
//# sourceMappingURL=trigger-suspend-at-path.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-suspend-at-path.d.ts","sourceRoot":"","sources":["../../../src/store/sync/trigger-suspend-at-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9F;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAiB7F"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create `cleo_trigger_suspend` in a store FILE that was just placed outside
|
|
3
|
+
* the open pass: a bundle import or a restore from a snapshot (journal spec
|
|
4
|
+
* §3.5 Rule 4, round 9; T12819).
|
|
5
|
+
*
|
|
6
|
+
* A placed store that syncs also has its whole sync set marked suspect
|
|
7
|
+
* (§2.3a rule 3). A placed store may come from an older build. Until the next chokepoint
|
|
8
|
+
* open, a 9.24 binary would open it as is; with the table present its owned
|
|
9
|
+
* triggers (if any carry the clause) keep working. A file that is not a
|
|
10
|
+
* project store (no `tasks_tasks`) is left alone.
|
|
11
|
+
*
|
|
12
|
+
* @task T12819
|
|
13
|
+
* @module store/sync/trigger-suspend-at-path
|
|
14
|
+
*/
|
|
15
|
+
import { openCleoDbSnapshot } from '../open-cleo-db.js';
|
|
16
|
+
import { syncSetTables } from './capture.js';
|
|
17
|
+
import { markSuspect } from './structural.js';
|
|
18
|
+
import { ensureTriggerSuspendTable } from './trigger-classes.js';
|
|
19
|
+
/**
|
|
20
|
+
* Open `dbPath` read-write, create the flag table when it is a project store
|
|
21
|
+
* missing it, and close.
|
|
22
|
+
*
|
|
23
|
+
* @returns What step 0 did, or `null` when the file is not a project store.
|
|
24
|
+
*/
|
|
25
|
+
export function ensureTriggerSuspendTableAtPath(dbPath) {
|
|
26
|
+
const snap = openCleoDbSnapshot(dbPath, { readOnly: false });
|
|
27
|
+
try {
|
|
28
|
+
const isProject = snap.db
|
|
29
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'tasks_tasks'")
|
|
30
|
+
.get() !== undefined;
|
|
31
|
+
if (!isProject)
|
|
32
|
+
return null;
|
|
33
|
+
const step0 = ensureTriggerSuspendTable(snap.db);
|
|
34
|
+
// §2.3a rule 3 (B): nothing in a placed file was written through this
|
|
35
|
+
// device's capture, so a store that syncs has its whole sync set marked
|
|
36
|
+
// suspect for the repair diff. A store without the sync schema stays so.
|
|
37
|
+
markSuspect(snap.db, 'project', syncSetTables('project'));
|
|
38
|
+
return step0;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
snap.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=trigger-suspend-at-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trigger-suspend-at-path.js","sourceRoot":"","sources":["../../../src/store/sync/trigger-suspend-at-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAAE,yBAAyB,EAA+B,MAAM,sBAAsB,CAAC;AAE9F;;;;;GAKG;AACH,MAAM,UAAU,+BAA+B,CAAC,MAAc;IAC5D,MAAM,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC;QACH,MAAM,SAAS,GACb,IAAI,CAAC,EAAE;aACJ,OAAO,CAAC,2EAA2E,CAAC;aACpF,GAAG,EAAE,KAAK,SAAS,CAAC;QACzB,IAAI,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAC5B,MAAM,KAAK,GAAG,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjD,sEAAsE;QACtE,wEAAwE;QACxE,yEAAyE;QACzE,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;YAAS,CAAC;QACT,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store-level minimum writer version (S2 ruling (c), T12343).
|
|
3
|
+
*
|
|
4
|
+
* A released binary older than S2 writes without frames; its captures are
|
|
5
|
+
* sealed as singleton transactions flagged `unframed` (S3), never claimed
|
|
6
|
+
* atomic. To bound that mixed-version window, turning capture on records
|
|
7
|
+
* `min_writer_version` in `_sync_meta` (this build's version, never lowered).
|
|
8
|
+
* Every build from S2 on checks it at open, before any write, and refuses to
|
|
9
|
+
* write a store that requires a newer writer. Builds released before S2 do
|
|
10
|
+
* not know the marker: that is the window the release notes name.
|
|
11
|
+
*
|
|
12
|
+
* @task T12343
|
|
13
|
+
* @module store/sync/writer-version
|
|
14
|
+
*/
|
|
15
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
16
|
+
/** `_sync_meta` key of the marker. */
|
|
17
|
+
export declare const MIN_WRITER_VERSION_KEY = "min_writer_version";
|
|
18
|
+
/** A writer older than the store's `min_writer_version`. */
|
|
19
|
+
export declare class StoreWriterTooOldError extends Error {
|
|
20
|
+
readonly required: string;
|
|
21
|
+
readonly actual: string;
|
|
22
|
+
readonly code = "E_STORE_WRITER_TOO_OLD";
|
|
23
|
+
constructor(required: string, actual: string);
|
|
24
|
+
}
|
|
25
|
+
/** Compare two versions by their numeric parts; null when either is unparseable. */
|
|
26
|
+
export declare function compareWriterVersions(a: string, b: string): number | null;
|
|
27
|
+
/** The store's marker, or undefined. Read-only. */
|
|
28
|
+
export declare function readMinWriterVersion(db: DatabaseSync): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Record `version` as the store's minimum writer version, unless the marker
|
|
31
|
+
* already requires as much or more. Call inside the capture-on transaction.
|
|
32
|
+
*
|
|
33
|
+
* @returns Whether the marker changed.
|
|
34
|
+
*/
|
|
35
|
+
export declare function raiseMinWriterVersion(db: DatabaseSync, version?: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Refuse to write a store whose marker requires a newer writer. Call at open,
|
|
38
|
+
* before any write. An unparseable version on either side never refuses.
|
|
39
|
+
*
|
|
40
|
+
* @throws {StoreWriterTooOldError}
|
|
41
|
+
*/
|
|
42
|
+
export declare function assertWriterVersion(db: DatabaseSync, version?: string): void;
|
|
43
|
+
//# sourceMappingURL=writer-version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer-version.d.ts","sourceRoot":"","sources":["../../../src/store/sync/writer-version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,sCAAsC;AACtC,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,4DAA4D;AAC5D,qBAAa,sBAAuB,SAAQ,KAAK;IAI7C,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,QAAQ,CAAC,IAAI,4BAA4B;gBAG9B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM;CAO1B;AAQD,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASzE;AAED,mDAAmD;AACnD,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAMzE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,YAAY,EAChB,OAAO,GAAE,MAAyB,GACjC,OAAO,CAUT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,GAAE,MAAyB,GAAG,IAAI,CAM9F"}
|