@sema-agent/server 7.146.0 → 7.147.0
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/MIGRATION.md +384 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/USAGE.md +170 -66
- package/deploy/facts/lanes.json +7 -0
- package/deploy/sema-up/README.md +2 -2
- package/deploy/sema-up/chart/templates/_helpers.tpl +22 -0
- package/deploy/sema-up/chart/templates/server-v4pro.yaml +5 -4
- package/deploy/sema-up/chart/templates/server.yaml +5 -4
- package/deploy/sema-up/chart/values.yaml +1 -1
- package/deploy/sema-up/cluster-up.sh +2 -1
- package/deploy/sema-up/lib/flagshape.sh +4 -0
- package/deploy/sema-up/lib/lanes.json +2 -5
- package/deploy/sema-up/sema-up.sh +12 -9
- package/deploy/sema-up/stack/compose.yaml +5 -4
- package/deploy/sema-up/stack/sema-stack-cluster.yaml +4 -3
- package/dist/approval-card.d.ts +4 -4
- package/dist/approval.d.ts +3 -3
- package/dist/audit.js +2 -2
- package/dist/bench/s1/live-deps.js +3 -1
- package/dist/boot/crash-last.js +2 -1
- package/dist/boot/device-lane.js +5 -5
- package/dist/boot/engine-lease.d.ts +12 -11
- package/dist/boot/execution-env.d.ts +2 -1
- package/dist/boot/execution-env.js +45 -33
- package/dist/boot/execution-lane.d.ts +37 -9
- package/dist/boot/execution-lane.js +74 -20
- package/dist/boot/exit-boundary.d.ts +1 -1
- package/dist/boot/exit-last.js +2 -1
- package/dist/boot/governance-seams.d.ts +2 -2
- package/dist/boot/leader.js +1 -1
- package/dist/boot/memory-boundary.d.ts +11 -11
- package/dist/boot/memory-boundary.js +3 -3
- package/dist/boot/memory-consolidation.d.ts +13 -24
- package/dist/boot/memory-consolidation.js +0 -13
- package/dist/boot/org-memory.js +3 -2
- package/dist/boot/parked-revive-gate.d.ts +1 -1
- package/dist/boot/parked-revive-gate.js +1 -1
- package/dist/boot/reapers.js +1 -1
- package/dist/boot/resolve-spec.js +91 -111
- package/dist/boot/retention-lane.d.ts +4 -42
- package/dist/boot/session-faces.d.ts +1 -0
- package/dist/boot/session-faces.js +8 -2
- package/dist/boot/stage-01-config.d.ts +1 -1
- package/dist/boot/stage-01-config.js +6 -2
- package/dist/boot/stage-02-web-admin-pull.d.ts +1 -1
- package/dist/boot/stage-02-web-admin-pull.js +10 -8
- package/dist/boot/stage-03-store-backend.d.ts +1 -1
- package/dist/boot/stage-04-stores.d.ts +1 -1
- package/dist/boot/stage-05-execution-env.d.ts +2 -2
- package/dist/boot/stage-05-execution-env.js +3 -3
- package/dist/boot/stage-06-runners.d.ts +3 -3
- package/dist/boot/stage-06-runners.js +1 -3
- package/dist/boot/stage-07-capability-layer.d.ts +3 -3
- package/dist/boot/stage-07-capability-layer.js +4 -9
- package/dist/boot/stage-08-reapers.d.ts +3 -3
- package/dist/boot/stage-09-leader.d.ts +4 -3
- package/dist/boot/stage-10-http-server.d.ts +4 -3
- package/dist/boot/stage-10-http-server.js +2 -1
- package/dist/boot/stores.d.ts +9 -0
- package/dist/boot/stores.js +18 -7
- package/dist/boot/web-admin.js +3 -3
- package/dist/capabilities/center-plugins.d.ts +2 -0
- package/dist/capabilities/center-plugins.js +6 -2
- package/dist/capabilities/composite-members.d.ts +1 -0
- package/dist/capabilities/composite-members.js +1 -0
- package/dist/capabilities/execution-lane.d.ts +7 -7
- package/dist/capabilities/execution-lane.js +2 -2
- package/dist/capabilities/hands-lane.d.ts +1 -1
- package/dist/capabilities/scenarios.d.ts +32 -18
- package/dist/capabilities/scenarios.js +13 -9
- package/dist/capabilities/settings-contract.d.ts +29 -0
- package/dist/capabilities/settings-contract.js +14 -0
- package/dist/capabilities/skills.d.ts +5 -5
- package/dist/capability-facts.d.ts +38 -0
- package/dist/capability-facts.js +34 -1
- package/dist/center-credential-renewer.js +3 -2
- package/dist/config-catalog.js +6 -5
- package/dist/config-invariants.d.ts +18 -3
- package/dist/config-invariants.js +36 -19
- package/dist/config-lkg.d.ts +1 -1
- package/dist/config-lkg.js +5 -6
- package/dist/config-provider.js +2 -0
- package/dist/config-types.d.ts +190 -186
- package/dist/config.d.ts +68 -5
- package/dist/config.js +80 -54
- package/dist/degenerate-instrument.js +1 -1
- package/dist/deploy-facts.js +3 -2
- package/dist/deployment-shape.d.ts +21 -12
- package/dist/deployment-shape.js +24 -6
- package/dist/env-facts.d.ts +7 -5
- package/dist/env-facts.js +10 -6
- package/dist/execution-config-root.d.ts +5 -5
- package/dist/execution-config-root.js +5 -5
- package/dist/execution-env-purpose.d.ts +14 -0
- package/dist/execution-env-purpose.js +29 -0
- package/dist/execution-lane-caps.d.ts +49 -86
- package/dist/execution-lane-caps.js +52 -62
- package/dist/execution-placement.d.ts +18 -2
- package/dist/execution-placement.js +19 -0
- package/dist/file-history-disclosure.d.ts +2 -2
- package/dist/fleet/drain-entry.d.ts +17 -10
- package/dist/fleet/drain-entry.js +1 -1
- package/dist/fleet/serving-lease.js +3 -4
- package/dist/history-anchor.d.ts +22 -0
- package/dist/history-anchor.js +14 -0
- package/dist/hooks/command-spawn.d.ts +1 -4
- package/dist/hooks/command-spawn.js +0 -11
- package/dist/hooks/hook-runner.d.ts +52 -8
- package/dist/hooks/hook-runner.js +160 -36
- package/dist/http/approval-sweeps.d.ts +2 -5
- package/dist/http/approval-sweeps.js +3 -2
- package/dist/http/checkpoint-ctx.d.ts +17 -0
- package/dist/http/checkpoint-ctx.js +6 -0
- package/dist/http/claim-latency.d.ts +10 -0
- package/dist/http/claim-latency.js +12 -0
- package/dist/http/dispatch.js +1 -1
- package/dist/http/durable-run-leg.js +2 -1
- package/dist/http/execution-context.d.ts +3 -1
- package/dist/http/execution-context.js +4 -0
- package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
- package/dist/http/memory-compliance-failure-reply.js +3 -0
- package/dist/http/memory-store-refusal-reply.d.ts +3 -3
- package/dist/http/memory-store-refusal-reply.js +3 -0
- package/dist/http/owner-access.d.ts +11 -0
- package/dist/http/owner-access.js +7 -4
- package/dist/http/principal-gate.d.ts +3 -14
- package/dist/http/principal-gate.js +0 -13
- package/dist/http/request-guards.d.ts +1 -1
- package/dist/http/resume-legs.d.ts +7 -10
- package/dist/http/resume-legs.js +79 -50
- package/dist/http/rewind-files-reply.d.ts +34 -0
- package/dist/http/rewind-files-reply.js +36 -0
- package/dist/http/route-ctx.d.ts +32 -16
- package/dist/http/route-table.js +2 -0
- package/dist/http/routes/approvals-assistant.js +5 -25
- package/dist/http/routes/capabilities.js +8 -2
- package/dist/http/routes/devices.js +1 -1
- package/dist/http/routes/memory-origin.d.ts +1 -1
- package/dist/http/routes/notify-wake.js +2 -1
- package/dist/http/routes/projects.d.ts +70 -0
- package/dist/http/routes/projects.js +86 -0
- package/dist/http/routes/rules.js +30 -2
- package/dist/http/routes/runs.js +8 -8
- package/dist/http/routes/session-sync.js +27 -10
- package/dist/http/routes/sessions.d.ts +7 -1
- package/dist/http/routes/sessions.js +104 -2
- package/dist/http/routes/tasks.js +9 -3
- package/dist/http/server.d.ts +24 -10
- package/dist/http/server.js +17 -6
- package/dist/http/stored-body-replay.d.ts +5 -3
- package/dist/http/stored-body-replay.js +10 -1
- package/dist/http/wire-types.d.ts +19 -10
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/instruction-source-notice.d.ts +1 -1
- package/dist/memory-embedder-reembed.d.ts +38 -0
- package/dist/memory-embedder-reembed.js +20 -0
- package/dist/memory-incident-sink.d.ts +0 -28
- package/dist/memory-incident-sink.js +5 -0
- package/dist/memory-scope.d.ts +2 -2
- package/dist/memory-scope.js +1 -1
- package/dist/memory-store-refusal.d.ts +17 -4
- package/dist/memory-store-refusal.js +1 -1
- package/dist/memory-sync-client.d.ts +2 -0
- package/dist/memory-sync-client.js +13 -11
- package/dist/memory-sync.js +1 -1
- package/dist/model-compat.d.ts +35 -21
- package/dist/model-compat.js +49 -21
- package/dist/model-entry-refusal.d.ts +11 -1
- package/dist/model-entry-refusal.js +7 -0
- package/dist/namespaced-rule-name.d.ts +18 -1
- package/dist/namespaced-rule-name.js +12 -0
- package/dist/observability/fail-open.d.ts +17 -9
- package/dist/observability/fail-open.js +17 -9
- package/dist/observability/metrics.d.ts +2 -0
- package/dist/observability/metrics.js +2 -0
- package/dist/observability/secret-env-scrub.d.ts +30 -4
- package/dist/observability/secret-env-scrub.js +15 -3
- package/dist/orchestration/workflow-agent-session-index.d.ts +7 -12
- package/dist/orchestration/workflow-agent-session-index.js +1 -6
- package/dist/orchestration/workflow-completion-inbox.d.ts +26 -6
- package/dist/orchestration/workflow-completion-inbox.js +63 -45
- package/dist/org-memory-admission.d.ts +5 -2
- package/dist/org-memory-admission.js +25 -10
- package/dist/parked-decide.d.ts +33 -26
- package/dist/parked-decide.js +36 -44
- package/dist/plugins/approval-ask-store-file.d.ts +10 -2
- package/dist/plugins/approval-ask-store-file.js +37 -4
- package/dist/plugins/approval-ask-store-memory.d.ts +5 -3
- package/dist/plugins/approval-ask-store-memory.js +11 -2
- package/dist/plugins/approval-ask-store-sql.d.ts +3 -0
- package/dist/plugins/approval-ask-store-sql.js +3 -1
- package/dist/plugins/blob-backend.d.ts +38 -195
- package/dist/plugins/blob-backend.js +33 -229
- package/dist/plugins/caching-session-store.d.ts +9 -4
- package/dist/plugins/checkpoint-index-columns.d.ts +1 -1
- package/dist/plugins/checkpoint-index-columns.js +3 -2
- package/dist/plugins/checkpoint-store-face.d.ts +1 -1
- package/dist/plugins/device-store-sql.d.ts +2 -2
- package/dist/plugins/device-store-sql.js +5 -10
- package/dist/plugins/execution-lane-rename-m40.d.ts +20 -0
- package/dist/plugins/execution-lane-rename-m40.js +10 -0
- package/dist/plugins/execution-placement-sql.d.ts +63 -88
- package/dist/plugins/execution-placement-sql.js +94 -54
- package/dist/plugins/file-run-store.d.ts +26 -12
- package/dist/plugins/file-run-store.js +63 -63
- package/dist/plugins/imported-layer-root-retirement.d.ts +17 -8
- package/dist/plugins/imported-layer-root-retirement.js +4 -8
- package/dist/plugins/inbox-payload-column.d.ts +14 -0
- package/dist/plugins/inbox-payload-column.js +13 -0
- package/dist/plugins/local-checkpoint-store.d.ts +5 -0
- package/dist/plugins/local-checkpoint-store.js +11 -7
- package/dist/plugins/local-data-dir-modes-m39.d.ts +48 -0
- package/dist/plugins/local-data-dir-modes-m39.js +182 -0
- package/dist/plugins/local-engine-high-water.d.ts +11 -0
- package/dist/plugins/local-engine-high-water.js +59 -0
- package/dist/plugins/local-session-store.d.ts +91 -25
- package/dist/plugins/local-session-store.js +124 -45
- package/dist/plugins/local-task-attachment-store.d.ts +2 -0
- package/dist/plugins/local-task-attachment-store.js +10 -11
- package/dist/plugins/memory-embedder-fingerprint.d.ts +33 -10
- package/dist/plugins/memory-embedder-fingerprint.js +52 -12
- package/dist/plugins/memory-embedder.d.ts +1 -1
- package/dist/plugins/memory-embedder.js +2 -2
- package/dist/plugins/memory-engine-pg.d.ts +4 -3
- package/dist/plugins/memory-engine-pg.js +4 -4
- package/dist/plugins/memory-engine-sql-apply.d.ts +13 -4
- package/dist/plugins/memory-engine-sql-apply.js +1 -1
- package/dist/plugins/memory-engine-tidb.d.ts +8 -5
- package/dist/plugins/memory-engine-tidb.js +7 -8
- package/dist/plugins/one-time-migrations.d.ts +145 -35
- package/dist/plugins/one-time-migrations.js +272 -120
- package/dist/plugins/owner-only-file-mode.d.ts +29 -0
- package/dist/plugins/owner-only-file-mode.js +141 -0
- package/dist/plugins/permission-rule-store-file.d.ts +8 -8
- package/dist/plugins/permission-rule-store-file.js +14 -2
- package/dist/plugins/pg-pool.d.ts +2 -1
- package/dist/plugins/pg-pool.js +11 -4
- package/dist/plugins/release-version.d.ts +20 -0
- package/dist/plugins/release-version.js +28 -0
- package/dist/plugins/remote-env-adb.d.ts +20 -4
- package/dist/plugins/remote-env-adb.js +39 -11
- package/dist/plugins/remote-env-e2b.d.ts +12 -3
- package/dist/plugins/remote-env-e2b.js +6 -4
- package/dist/plugins/remote-env-host.d.ts +9 -1
- package/dist/plugins/remote-env-host.js +6 -5
- package/dist/plugins/remote-env-k8s.d.ts +5 -2
- package/dist/plugins/remote-env-k8s.js +15 -4
- package/dist/plugins/remote-env-local-docker.d.ts +4 -1
- package/dist/plugins/remote-env-local-docker.js +3 -3
- package/dist/plugins/remote-env-ssh.d.ts +8 -2
- package/dist/plugins/remote-env-ssh.js +26 -24
- package/dist/plugins/remote-shell.d.ts +59 -30
- package/dist/plugins/remote-shell.js +175 -62
- package/dist/plugins/retention-declaration.d.ts +37 -0
- package/dist/plugins/retention-declaration.js +9 -0
- package/dist/plugins/retention-deployment.d.ts +126 -0
- package/dist/plugins/retention-deployment.js +88 -0
- package/dist/plugins/retention-lane-store-sql.d.ts +74 -4
- package/dist/plugins/retention-lane-store-sql.js +103 -1
- package/dist/plugins/retention-sql.d.ts +29 -0
- package/dist/plugins/retention-sql.js +8 -0
- package/dist/plugins/schema-consistency.d.ts +0 -6
- package/dist/plugins/schema-consistency.js +1 -2
- package/dist/plugins/schema-ensure-legs.d.ts +1 -1
- package/dist/plugins/schema-ensure-legs.js +11 -1
- package/dist/plugins/session-ext-table.d.ts +74 -0
- package/dist/plugins/session-ext-table.js +121 -0
- package/dist/plugins/session-fork.d.ts +1 -1
- package/dist/plugins/session-row-sql.d.ts +8 -7
- package/dist/plugins/session-row-sql.js +15 -7
- package/dist/plugins/session-store-sql.d.ts +131 -0
- package/dist/plugins/session-store-sql.js +298 -0
- package/dist/plugins/session-workspace-cell.d.ts +2 -2
- package/dist/plugins/session-workspace-cell.js +2 -2
- package/dist/plugins/shared-memory-store-sql.d.ts +10 -0
- package/dist/plugins/shared-memory-store-sql.js +13 -13
- package/dist/plugins/snapshot-bucket-presence.js +1 -1
- package/dist/plugins/sql-driver.d.ts +23 -7
- package/dist/plugins/sql-driver.js +41 -8
- package/dist/plugins/sql-errors.d.ts +5 -2
- package/dist/plugins/sql-errors.js +4 -0
- package/dist/plugins/store-backend.d.ts +35 -8
- package/dist/plugins/store-backend.js +184 -62
- package/dist/plugins/store-contracts.d.ts +9 -2
- package/dist/plugins/store-contracts.js +1 -0
- package/dist/plugins/task-attachment-store.d.ts +6 -8
- package/dist/plugins/task-attachment-store.js +15 -10
- package/dist/plugins/task-list-store-sql.d.ts +1 -1
- package/dist/plugins/text-column-capacity.d.ts +21 -0
- package/dist/plugins/text-column-capacity.js +5 -0
- package/dist/plugins/tidb-pool.d.ts +3 -2
- package/dist/plugins/tidb-pool.js +19 -31
- package/dist/plugins/user-input-ledger-sql.d.ts +1 -1
- package/dist/read-face-posture.d.ts +6 -6
- package/dist/read-face-posture.js +2 -2
- package/dist/request-value-closure.d.ts +5 -5
- package/dist/request-value-closure.js +1 -1
- package/dist/rules-consent.d.ts +41 -13
- package/dist/rules-consent.js +101 -53
- package/dist/run-local.js +17 -13
- package/dist/runs.d.ts +48 -30
- package/dist/runs.js +26 -16
- package/dist/runtime-caps-resolver.d.ts +15 -8
- package/dist/runtime-caps-resolver.js +14 -10
- package/dist/sealed-key-layout.d.ts +2 -3
- package/dist/sealed-key-layout.js +0 -1
- package/dist/sealed-key.js +7 -5
- package/dist/security.d.ts +5 -3
- package/dist/sema-config-dir.js +2 -2
- package/dist/server-secret-env.d.ts +2 -0
- package/dist/service-version.d.ts +2 -1
- package/dist/service-version.js +2 -2
- package/dist/session-error-wire.d.ts +1 -1
- package/dist/session-incarnation.d.ts +8 -5
- package/dist/session-resident-background.d.ts +85 -32
- package/dist/session-resident-background.js +75 -7
- package/dist/session-row.d.ts +1 -1
- package/dist/session-sync-kernel.d.ts +1 -1
- package/dist/session-workspace.d.ts +11 -2
- package/dist/session-workspace.js +5 -0
- package/dist/sighup-idle.d.ts +1 -1
- package/dist/store-posture.d.ts +2 -1
- package/dist/stored-request-hooks-m20.d.ts +3 -2
- package/dist/stored-request-hooks-m20.js +19 -14
- package/dist/stores-sql/blob-index-sql.d.ts +148 -0
- package/dist/stores-sql/blob-index-sql.js +154 -0
- package/dist/stores-sql/checkpoint-store-sql.d.ts +1 -1
- package/dist/stores-sql/checkpoint-store-sql.js +11 -27
- package/dist/{plugins → stores-sql}/file-history-store-sql.d.ts +23 -11
- package/dist/{plugins → stores-sql}/file-history-store-sql.js +23 -18
- package/dist/{plugins → stores-sql}/pg-session-storage.d.ts +23 -76
- package/dist/{plugins → stores-sql}/pg-session-storage.js +80 -209
- package/dist/{plugins → stores-sql}/retention-store-sql.d.ts +123 -217
- package/dist/{plugins → stores-sql}/retention-store-sql.js +66 -190
- package/dist/{plugins → stores-sql}/session-event-payload.d.ts +1 -1
- package/dist/{plugins → stores-sql}/session-event-payload.js +1 -1
- package/dist/stores-sql/session-policy-store-sql.d.ts +1 -1
- package/dist/stores-sql/session-write-hooks.d.ts +59 -0
- package/dist/stores-sql/session-write-hooks.js +2 -0
- package/dist/{plugins → stores-sql}/tidb-session-storage.d.ts +10 -11
- package/dist/{plugins → stores-sql}/tidb-session-storage.js +10 -21
- package/dist/{plugins → stores-sql}/tidb-session-store.d.ts +32 -122
- package/dist/{plugins → stores-sql}/tidb-session-store.js +71 -171
- package/dist/stores-sql/tool-result-store-sql.d.ts +1 -1
- package/dist/stores-sql/tool-result-store-sql.js +1 -1
- package/dist/stores-sql/windowed-session-storage.d.ts +56 -0
- package/dist/stores-sql/windowed-session-storage.js +61 -0
- package/dist/task-a2a.d.ts +7 -1
- package/dist/task-a2a.js +15 -14
- package/dist/task-cwd.d.ts +11 -3
- package/dist/task-cwd.js +17 -1
- package/dist/task-mcp.d.ts +10 -2
- package/dist/task-mcp.js +13 -16
- package/dist/task-settings.d.ts +9 -4
- package/dist/task-settings.js +17 -6
- package/dist/tool-approval.d.ts +21 -8
- package/dist/tool-approval.js +10 -6
- package/dist/trace/core-keyset-guard.d.ts +9 -3
- package/dist/trace/engine-notice-wire.d.ts +1 -1
- package/dist/trace/engine-notice-wire.js +2 -0
- package/dist/trace/exit-seat.d.ts +23 -1
- package/dist/trace/exit-seat.js +3 -0
- package/dist/trace/project.d.ts +33 -16
- package/dist/trace/project.js +12 -9
- package/dist/trace/redact.d.ts +1 -1
- package/dist/trace/redact.js +61 -73
- package/dist/trace/self-minted-wire.d.ts +263 -10
- package/dist/trace/self-minted-wire.js +90 -2
- package/dist/trace/sema-provenance.d.ts +45 -41
- package/dist/trace/sema-provenance.js +81 -10
- package/dist/trace/task-notification-facets.d.ts +70 -2
- package/dist/trace/task-notification-facets.js +71 -1
- package/dist/trace/wire-projection-faces.d.ts +38 -2
- package/dist/trace/wire-projection-faces.js +26 -1
- package/dist/unread-request-members.d.ts +66 -0
- package/dist/unread-request-members.js +41 -0
- package/dist/user-input-ledger.d.ts +2 -2
- package/dist/web-admin-client/apply-effective.js +22 -26
- package/dist/web-admin-client/candidate-gate.d.ts +35 -3
- package/dist/web-admin-client/candidate-gate.js +53 -5
- package/dist/web-admin-client/center-request.d.ts +4 -1
- package/dist/web-admin-client/center-request.js +2 -1
- package/dist/web-admin-client/effective-keys.js +0 -1
- package/dist/web-admin-client/facade.d.ts +1 -1
- package/dist/web-admin-client/http-client.d.ts +1 -0
- package/dist/web-admin-client/http-client.js +17 -9
- package/dist/web-admin-client/restart-signal.js +5 -2
- package/dist/web-admin-client/skills-mcp.d.ts +20 -6
- package/dist/web-admin-client/skills-mcp.js +26 -21
- package/dist/web-admin-client/types.d.ts +34 -49
- package/docs/DEVICE-EXECUTOR-PROTOCOL.md +2 -2
- package/package.json +13 -12
- package/scripts/drain-check-local.mjs +2 -28
- package/scripts/drain-check.mysql.sql +2 -18
- package/scripts/drain-check.pg.sql +2 -18
- package/wire-changes/server@7.147.0.json +813 -0
|
@@ -4,8 +4,6 @@ import { judgeCheckpointIndexCells } from "./checkpoint-index-columns.js";
|
|
|
4
4
|
import { dialectProtocolJsonEncoder } from "./sql-driver.js";
|
|
5
5
|
import { SCHEMA_STATEMENTS, acquireEnsureSchemaLock, releaseEnsureSchemaLock } from "./tidb-pool.js";
|
|
6
6
|
import { PG_SCHEMA_STATEMENTS, acquirePgEnsureSchemaLock, releasePgEnsureSchemaLock } from "./pg-pool.js";
|
|
7
|
-
import { CENTRAL_SCHEMA_INDEXES } from "./schema-indexes.js";
|
|
8
|
-
import { ensureIndexes } from "./ensure-index.js";
|
|
9
7
|
import { createObjectColumnReader, decodeJsonColumn, decodeNullableJsonColumn, isJsonColumnBodyProblem, jsonColumnText, jsonColumnTextExpr, readOpaqueJsonValueColumn, } from "./sql-json-column.js";
|
|
10
8
|
import { incarnationFromCell, sessionFaceFromRow } from "../session-incarnation.js";
|
|
11
9
|
import { StoreBootRefusalError } from "./store-contracts.js";
|
|
@@ -16,14 +14,15 @@ import { retireStoredCardWords } from "../retired-card-words.js";
|
|
|
16
14
|
import { redactSecrets } from "../trace/redact.js";
|
|
17
15
|
import { CLAIM_OWNERLESS_PLACED_SQL, claimOwnerlessParams, isOrphanSessionOwner, judgeDelegatedOwner } from "../delegated-session-owner.js";
|
|
18
16
|
import { MAILBOX_KEYED_TABLES, mailboxKeyDeleteSql, storedMailboxKeyless } from "../stores-sql/mailbox-store-sql.js";
|
|
19
|
-
import {
|
|
17
|
+
import { backfillWriterStatement, countOrphanBindingsStatement, migrateDeviceBindingsOntoSideTableStatements, migrateDeviceBindingsStatement } from "./execution-placement-sql.js";
|
|
18
|
+
import { EXECUTION_PLACEMENT_COLUMNS, SESSION_EXT_COLUMNS, SESSION_EXT_TABLE } from "./session-ext-table.js";
|
|
20
19
|
import { APPROVAL_ASK_ENDING_CAUSE_COLUMN, APPROVAL_ASK_EXECUTION_LANE_COLUMN, APPROVAL_ASK_PARK_TRIGGER_COLUMN, APPROVAL_ASK_TABLE } from "./approval-ask-store-sql.js";
|
|
20
|
+
import { RENAMED_EXECUTION_LANE_M40, RETIRED_EXECUTION_LANE_WORD_M40, retiredExecutionLaneHintM40 } from "./execution-lane-rename-m40.js";
|
|
21
21
|
import { retireImportedLayerRootAllows } from "./imported-layer-root-retirement.js";
|
|
22
22
|
import { isUnusableArchive } from "../execution-placement.js";
|
|
23
23
|
const RETIRED_SESSION_RULE_BUCKETS = ["commandAllow", "commandDeny"];
|
|
24
24
|
export function buildMigrationReceipts() {
|
|
25
25
|
return {
|
|
26
|
-
workflowParkIndexRowsCarried: 0,
|
|
27
26
|
undurableScopeRuleRows: [],
|
|
28
27
|
undurableScopeRuleBucketsDeferred: 0,
|
|
29
28
|
undurableScopeRuleBucketsUnreadable: [],
|
|
@@ -52,6 +51,9 @@ export function buildMigrationReceipts() {
|
|
|
52
51
|
deviceBindingTablesDropped: 0,
|
|
53
52
|
executionPlacementsBackfilled: 0,
|
|
54
53
|
askExecutionLanesBackfilled: 0,
|
|
54
|
+
executionLaneWordsRenamed: 0,
|
|
55
|
+
sessionExtRowsMoved: 0,
|
|
56
|
+
sessionMetaColumnsDropped: 0,
|
|
55
57
|
executionPlacementSessionsUnreadable: [],
|
|
56
58
|
localArchiveRowFactsBackfilled: 0,
|
|
57
59
|
localArchivesAnonymized: 0,
|
|
@@ -63,19 +65,18 @@ export function buildMigrationReceipts() {
|
|
|
63
65
|
askJournalRecordsUpgraded: 0,
|
|
64
66
|
importedLayerRootAllowsRetired: [],
|
|
65
67
|
importedLayerRootBucketsSkipped: [],
|
|
68
|
+
importedLayerRootBucketsUnnamed: 0,
|
|
66
69
|
};
|
|
67
70
|
}
|
|
68
71
|
function completedReport(r) {
|
|
69
|
-
const {
|
|
70
|
-
if (
|
|
71
|
-
throw new Error(`one-time migrations finished without settling a shape reading (
|
|
72
|
+
const { sessionRuleColumn, workflowJournalScopeColumn } = r;
|
|
73
|
+
if (sessionRuleColumn === undefined || workflowJournalScopeColumn === undefined) {
|
|
74
|
+
throw new Error(`one-time migrations finished without settling a shape reading (M17 ${String(sessionRuleColumn)} / M24 ${String(workflowJournalScopeColumn)}) — a step did not record it`);
|
|
72
75
|
}
|
|
73
|
-
return { ...r,
|
|
76
|
+
return { ...r, sessionRuleColumn, workflowJournalScopeColumn };
|
|
74
77
|
}
|
|
75
78
|
function changedAnything(r) {
|
|
76
|
-
return (r.
|
|
77
|
-
r.workflowParkIndexRowsCarried > 0 ||
|
|
78
|
-
r.undurableScopeRuleRows.length > 0 ||
|
|
79
|
+
return (r.undurableScopeRuleRows.length > 0 ||
|
|
79
80
|
r.undurableScopeRuleBucketsDeferred > 0 ||
|
|
80
81
|
r.undurableScopeRuleBucketsUnreadable.length > 0 ||
|
|
81
82
|
r.sessionRuleColumn === "renamed" ||
|
|
@@ -105,6 +106,9 @@ function changedAnything(r) {
|
|
|
105
106
|
r.deviceBindingTablesDropped > 0 ||
|
|
106
107
|
r.executionPlacementsBackfilled > 0 ||
|
|
107
108
|
r.askExecutionLanesBackfilled > 0 ||
|
|
109
|
+
r.executionLaneWordsRenamed > 0 ||
|
|
110
|
+
r.sessionExtRowsMoved > 0 ||
|
|
111
|
+
r.sessionMetaColumnsDropped > 0 ||
|
|
108
112
|
r.executionPlacementSessionsUnreadable.length > 0 ||
|
|
109
113
|
r.localArchiveRowFactsBackfilled > 0 ||
|
|
110
114
|
r.localArchivesAnonymized > 0 ||
|
|
@@ -115,7 +119,8 @@ function changedAnything(r) {
|
|
|
115
119
|
r.askParkTriggersMoved > 0 ||
|
|
116
120
|
r.askJournalRecordsUpgraded > 0 ||
|
|
117
121
|
r.importedLayerRootAllowsRetired.length > 0 ||
|
|
118
|
-
r.importedLayerRootBucketsSkipped.length > 0
|
|
122
|
+
r.importedLayerRootBucketsSkipped.length > 0 ||
|
|
123
|
+
r.importedLayerRootBucketsUnnamed > 0);
|
|
119
124
|
}
|
|
120
125
|
function readingsSnapshot(r) {
|
|
121
126
|
return {
|
|
@@ -157,24 +162,6 @@ export class OneTimeMigrationInterruptedError extends Error {
|
|
|
157
162
|
}
|
|
158
163
|
}
|
|
159
164
|
export const ONE_TIME_MIGRATIONS = [
|
|
160
|
-
{
|
|
161
|
-
id: "M9",
|
|
162
|
-
introducedIn: "7.118.0",
|
|
163
|
-
releasedOn: "2026-10-07",
|
|
164
|
-
removableAfter: "2026-11-06",
|
|
165
|
-
removal: "删 migrateOn 的 M9 步(`reshapeWorkflowParkIndex` / `execAtomically` / `finishParkIndexShape`:收尾段、主键宽度探针、暂存表复制、换名、删旧表、索引补建、主键约束名)与 " +
|
|
166
|
-
"`primaryKeyWidth` / `WORKFLOW_PARK_INDEX_TABLE` / `WORKFLOW_PARK_INDEX_STAGING` / `WORKFLOW_PARK_INDEX_RETIRED`;报告、日志行与 `NO_SQL_MIGRATIONS` 的 " +
|
|
167
|
-
"`workflowParkIndexKey` / `workflowParkIndexRowsCarried`;**连同读侧半场一起删**(都是「引擎 7.43 及以前写下的 workflow 子代停驻记录」这同一件事,同窗同删,不另起登记行):" +
|
|
168
|
-
"`orchestration/workflow-agent-session-index.ts` 的 `older_engine` 读臂(`routeWorkflowPark` 规则 ③、`WorkflowParkRoute` 与 `parked-decide.ts` `ParkOrigin` 上 `cause` 里的这一词," +
|
|
169
|
-
"`denyWorkflowParksUnreadable` 的对应分支;`unreadable` 成为唯一成因)与随包排空判据 `workflow-park-holder`(`scripts/drain-check.mysql.sql` / `scripts/drain-check.pg.sql` 的那一段" +
|
|
170
|
-
"与文件头判据清单里的那一行、`scripts/drain-check-local.mjs` 的 `DRAIN_CRITERIA` 那一条与 `readParkIndex`,以及 `MIGRATION.md` / `docs/DEPLOY-PREREQS.md` 里点名它的说明)",
|
|
171
|
-
afterRemoval: "没有响亮臂:删码之后仍是旧主键(只有 `session_id`)的库(跳过了带本条的版本直接升)上,同一个子会话只能有一枚指针 —— 续跑链 / 分叉里" +
|
|
172
|
-
"后写的那条记录覆盖先写的,判决只看得到一条记录:分叉判不出、链尾可能被更早的记录盖掉(决定投给那一条)。运维手工照中央 DDL 重建这张表即可" +
|
|
173
|
-
"(它是派生投影,丢了只丢升级那一刻还停驻着的旧卡的指针 —— 那些卡改答诚实 miss)。读侧半场删后:升级那一刻还停驻着、点名它的条目没有持有者位的存量卡" +
|
|
174
|
-
"(跳过了带本条的版本直接升的库)路由判 `none` ⇒ 落 legacy 腿,答 `409 conflict.resume_context_unavailable`(响亮,但成因是子会话没有续跑上下文,不再是 " +
|
|
175
|
-
"`decide.workflow_parks_unreadable`);出路不变(续跑发起它的那条 workflow run);随包排空判据没了,换钉前没有机器读数可数这些卡",
|
|
176
|
-
reportKeys: ["workflowParkIndexKey", "workflowParkIndexRowsCarried"],
|
|
177
|
-
},
|
|
178
165
|
{
|
|
179
166
|
id: "M10",
|
|
180
167
|
introducedIn: "7.119.0",
|
|
@@ -307,6 +294,9 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
307
294
|
"`test/task-a2a.test.ts` 里的克隆钉(标题去掉开头的 `🔴 ` 后以 `克隆钉:` 开头的各格,整格删;按这个精确前缀删,不按标题里的「M19 重读」之类泛词删 —— 那会误删同文件里的通用不变式钉),以及三格不带前缀、删码后要改写的钉:`需要「读」的两个键…` 那格的 `readRequestToolAxes` 调用方名单(`normalizeA2aPeer` 只在 M19 重读臂里调它,删后名单只剩 `mintPeerSnapshot`,标题里「与读器里 M19 重读产物的那一次」半句同删)、`读器与门的差别只有三格…` 改成两格(删 ③ 的注释行与标题里的 ③)、`请求入口快照的形…` 里 `snapshotRequestA2a(…, { reread: false })` 的第二实参(删 `reread` 形参后改成单参);" +
|
|
308
295
|
"`test/one-time-migration-window-gate.test.ts` 里 M19 一节中与克隆有关的三格(声明抽取器正负控 · 删法条与克隆名字两向对账 · 克隆钉标题前缀对账)与它们共用的帮手 `declaredNames`;" +
|
|
309
296
|
"`test/mcp-tool-axes-m19.test.ts`(窗关那一格 —— 存量坏形整条不进、warn 与通告点名 `field: toolAxes` —— 改钉到 `test/task-mcp.test.ts`);" +
|
|
297
|
+
"「请求成员读不出」一问上的重读座走线:`http/stored-body-replay.ts` 里递给 `unreadRequestMembers` 的 `storedToolAxes: storedToolAxesRereadM19(today)` 与它的 import," +
|
|
298
|
+
"`unread-request-members.ts` 的 `StoredToolAxesRereadM19` 型 import、`UnreadMembersLeg` 续跑臂的 `storedToolAxes` 字段与 `unreadRequestMembers` 里取它、递给两只谓词的那几处," +
|
|
299
|
+
"`task-mcp.ts` 的 `requestMcpTransportUnread` 与 `task-a2a.ts` 的 `requestA2aHeadersUnread` 的 `storedToolAxes` 形参(连同它们递给 `normalizeServer` / `normalizeA2aPeer` 的那一实参);" +
|
|
310
300
|
"本文件的 `migrationWindowOpen`(若再无别的读者)",
|
|
311
301
|
afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里引擎判官判不过的 `toolAxes`,续跑重放时这一条 server 整条不进," +
|
|
312
302
|
"运维 warn `mcp_injection_dropped`(`reason: malformed_entry`、`field: toolAxes`)+ 用户面通告 `mcp.injection_dropped`,兄弟照挂;停驻在这台 server 工具审批上的任务" +
|
|
@@ -320,14 +310,16 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
320
310
|
introducedIn: "7.131.0",
|
|
321
311
|
releasedOn: "2026-10-16",
|
|
322
312
|
removableAfter: "2026-11-15",
|
|
323
|
-
removal: "删 `src/stored-request-hooks-m20.ts` 整只文件(`storedHooksRereadM20` / `rereadStoredHooksM20` / `StoredHooksRereadM20` / `
|
|
313
|
+
removal: "删 `src/stored-request-hooks-m20.ts` 整只文件(`storedHooksRereadM20` / `rereadStoredHooksM20` / `StoredHooksRereadM20` / `MATCHER_FIELD_NAMES` / `nodeAt` / `cloneDoc` / `removeKeyAt`);" +
|
|
324
314
|
"`boot/resolve-spec.ts` 里递给 `parseTaskSettings` 的重读座实参(`opts?.leg === \"fresh\" || !taskHooksHonored(config) ? {} : { storedHooksReread: storedHooksRereadM20(…) }`)与它的 import、" +
|
|
325
315
|
"`hooks_read_as_before_upgrade` 那一段 warn;`task-settings.ts` 的 `ParseTaskSettingsSeats` / `ParseTaskSettingsResult.hooksReread` / `parseTaskSettings` 第二形参与 hooks 臂里的重读那一支;" +
|
|
326
316
|
"`hooks/hook-runner.ts` 的 `ParseHooksConfigResult.pieces` / `HooksRefusalPiece`(若再无别的读者)与 `hooksCoordinate` 的 export;" +
|
|
327
|
-
"
|
|
328
|
-
|
|
329
|
-
"
|
|
330
|
-
|
|
317
|
+
"「请求成员读不出」一问上的重读座走线:`unread-request-members.ts` 的 `storedHooksRereadM20` import 与 `unreadRequestMembers` 里 `hooksReread` 那一行(递给 `parseTaskSettings` 的 `storedHooksReread` 座)、" +
|
|
318
|
+
"`UnreadMembersLeg` 续跑臂的 `today` 字段(只为 M20 的窗问它)与 `http/stored-body-replay.ts`、`boot/resolve-spec.ts` 递 `today` 的那两处(`stored-body-replay.ts` 的 `today` 局部若 M19 已删再无读者一并删);" +
|
|
319
|
+
"`test/hooks-stored-reread-m20.test.ts`(窗关那一格 —— 续跑拒 409 `resume_blocked_by_policy` —— 改钉到 `test/hooks-entry-admission.test.ts` 的续跑腿组)",
|
|
320
|
+
afterRemoval: "没有新的臂,接住它的是今天的读法(删码零行为变化:窗关那一刻起重读臂已经不生效):升级前存下的请求体里带这两形的 `settings.hooks`,在会采纳 hooks 的车道上续跑被拒 —— " +
|
|
321
|
+
"「请求成员读不出」判据(`src/unread-request-members.ts`)答 409 `resume_blocked_by_policy`(`blockedBy: request.field_invalid`,`field: settings.hooks`),卡仍待决、行不动(7.147.0 起;此前是整份不采纳照跑)—— " +
|
|
322
|
+
"出路是取消这类停驻任务、删掉那个未知字段 / `__proto__` 键后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行(头注 M20 段)",
|
|
331
323
|
reportKeys: [],
|
|
332
324
|
},
|
|
333
325
|
{
|
|
@@ -505,7 +497,7 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
505
497
|
"`RETIRED_DEVICE_BINDING_TABLE` / `PARKED_DEVICE_BINDING_TABLE` / `FENCE_ROOTS_SHOWN`、`ExecutionPlacementSwitchRefusedError` / `EXECUTION_PLACEMENT_SWITCH_REFUSED_CODE` / `ExecutionPlacementSwitchRefusalReason`)" +
|
|
506
498
|
"与 DDL 前探针(`assertExecutionPlacementSwitchAdmissible` / `assertExecutionPlacementSwitchAdmissibleOnPool`,`store-backend.ts` 的 `withRetiredSchemaProbe` 里调它的那一行);" +
|
|
507
499
|
"`runSqlOneTimeMigrations` 的第三参 `ExecutionPlacementSwitchInput`(若再无别的条目用它)与它在两只 SQL 后端 / 测试夹具上的实参;local 腿(`LocalBackend.runOneTimeMigrations` 里的 `backfillLocalExecutionPlacements` 与 `FileRunStore.sessionIdsWithRuns`);" +
|
|
508
|
-
"`execution-placement-sql.ts` 的 `backfillWriterStatement` / `migrateDeviceBindingsStatement` / `countOrphanBindingsStatement`;报告、日志行与 `NO_SQL_MIGRATIONS` 的 `executionPlacementColumnsAdded` / `deviceBindingsMoved` / " +
|
|
500
|
+
"`execution-placement-sql.ts` 的 `backfillWriterStatement` / `migrateDeviceBindingsStatement` / `migrateDeviceBindingsOntoSideTableStatements` / `countOrphanBindingsStatement`;报告、日志行与 `NO_SQL_MIGRATIONS` 的 `executionPlacementColumnsAdded` / `deviceBindingsMoved` / " +
|
|
509
501
|
"`deviceBindingsOrphaned` / `deviceBindingTablesDropped` / `executionPlacementsBackfilled` / `executionPlacementSessionsUnreadable` 与 `device_bindings_orphaned` / `execution_placement_session_unreadable` 两行 warn;`test/execution-placement-switch-db-integration.test.ts` 整份与 `test/one-time-migrations.test.ts` 的 M32 组。" +
|
|
510
502
|
"**local 腿的回填席与它的点名词一并删**:`LocalSessionStore` 的 `executionPlacementBackfill`(属性与构造里的那一臂)、`execution-placement.ts` 的 `LocalPlacementBackfillSeat` / `UnusableArchive` / `UnusableArchiveWord`(含它的 `storage` 词 —— 读存档文件时抛了别的故障、`errno` 随带,该词只有回填点名用)/ `isUnusableArchive`、" +
|
|
511
503
|
"`ExecutionPlacementSessionUnreadable`;`local-session-store.ts` 里 `placementRead` 并回 `readExecutionPlacement`、`metaRead` 的 unusable 臂与 `forkingOf` 的 `storage` 臂改成就地上抛 `probe.fault`(`archiveFaultWord` / `unusableArchiveOf` 随词删,探针只留「不在」与已知在而用不了的读法);" +
|
|
@@ -570,13 +562,13 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
570
562
|
"删什么:`src/plugins/imported-layer-root-retirement.ts` 整个文件;migrateStepsOn 的 M36 步(规则表与 `grammar` 列的在场探针 + 对 `retireImportedLayerRootAllows` 的调用)与握锁连接上的 `query` 口" +
|
|
571
563
|
"(`LockedMigrationDb` 与 `mysqlDb` / `pgDb` 里的那一员,若届时再无别的步骤用它);`permission-rule-store-sql.ts` 的 `listRuleBucketOwners` / `isSqlBucketRefusal` / `isSqlBucketVanished` / `RuleBucketVanishedError`(取点那一句的具名类,届时若再无别的读者按类判它,收回成裸错)/ `IMPORT_RECORD_KIND`;" +
|
|
572
564
|
"`permission-rule-store-file.ts` 的 `FileRuleApprovalRecordStore.importRecordOwners` / `fileRuleBucketOwners` / `isFileBucketDamaged`;`store-backend.ts` 的 `LocalBackend.runOneTimeMigrations` 里 M36 那一腿(规则目录在场判与那一处调用)与报告两键的那两行;" +
|
|
573
|
-
"本文件的 `createSqlImportedLayerRootSource` 与 `importedLayerRootAllowsRetired` / `importedLayerRootBucketsSkipped`(报告 / 收据 / `changedAnything` / `readingsSnapshot` / `readingFields` / `receiptSentence` / `NO_SQL_MIGRATIONS` 的排除表)、" +
|
|
574
|
-
"`imported_layer_root_allow_retired` / `imported_layer_root_bucket_skipped`
|
|
565
|
+
"本文件的 `createSqlImportedLayerRootSource` 与 `importedLayerRootAllowsRetired` / `importedLayerRootBucketsSkipped` / `importedLayerRootBucketsUnnamed`(报告 / 收据 / `changedAnything` / `readingsSnapshot` / `readingFields` / `receiptSentence` / `NO_SQL_MIGRATIONS` 的排除表)、" +
|
|
566
|
+
"`imported_layer_root_allow_retired` / `imported_layer_root_bucket_skipped` / `imported_layer_root_buckets_unnamed` 三行 warn 与 `importedLayerRootSkipNote`、读数行 note 里的 M36 句与头注 M36 段;" +
|
|
575
567
|
"`test/imported-layer-root-retirement.test.ts`、`test/imported-layer-root-retirement-db-integration.test.ts`(连同 `scripts/db-integration-files.json` 里它那一行)与 `test/helpers/imported-layer-root-fixture.ts` 整文件。" +
|
|
576
568
|
"**不随本条删**:`SqlDurableRulePartitionProvider` 收「方言 + 执行口」的那一形(`BucketSql`;常设,池照旧满足它)",
|
|
577
569
|
afterRemoval: "没有响亮臂(如实):删码之后,跳过了带本条的版本直接升的库(或回滚到引擎 7.61.0 之前、在其上又导入过的库),存量的这种全局 `/x` allow 照旧在每个项目根放行,本服务不再点名 —— " +
|
|
578
570
|
"出路 = 升级时先过一次带本条的版本并起服;或运维列出桶里出处为导入(`adds[].origin` = `imported-cc`)、scope 为全局、从 `/` 写起的 allow(`GET /v1/rules`)并撤掉(`DELETE /v1/rules`,撤的是这条规则的全部批准)",
|
|
579
|
-
reportKeys: ["importedLayerRootAllowsRetired", "importedLayerRootBucketsSkipped"],
|
|
571
|
+
reportKeys: ["importedLayerRootAllowsRetired", "importedLayerRootBucketsSkipped", "importedLayerRootBucketsUnnamed"],
|
|
580
572
|
},
|
|
581
573
|
{
|
|
582
574
|
id: "M37",
|
|
@@ -604,6 +596,46 @@ export const ONE_TIME_MIGRATIONS = [
|
|
|
604
596
|
"出路是取消那条 run,把凭据挪进引擎进程环境后重新提交。会撞上它的是活过窗的升级前停驻行:发布后才升级的部署、调长了 `APPROVAL_TERMINAL_BACKSTOP_MS` 的部署、滚动升级里旧副本在窗内新写的行、窗内续跑过又停驻到窗外的任务(头注 M38 段)",
|
|
605
597
|
reportKeys: [],
|
|
606
598
|
},
|
|
599
|
+
{
|
|
600
|
+
id: "M39",
|
|
601
|
+
introducedIn: "7.147.0",
|
|
602
|
+
releasedOn: "2026-11-07",
|
|
603
|
+
removableAfter: "2026-12-07",
|
|
604
|
+
removal: "删 `src/plugins/local-data-dir-modes-m39.ts` 整只文件;`store-backend.ts` 里 `LocalBackend.runOneTimeMigrations` 开头调它的那一段(两只日志座的接线)、`dataDirLayout` 字段与它的赋值、顶部的 import(含 `memoryEngineRootFor`),以及 `runOneTimeMigrations` 与 `discloseWarn` 注释里点名 M39 的那两句;" +
|
|
605
|
+
"各店为名册导出的目录路径函数(`fileRunStoreDirsFor` / `localCheckpointStoreDirsFor` / `attachmentStoreDirFor` / `scratchpadRootFor` / `memorySyncCursorDirFor`)只剩本店自己读的去掉 `export` 与注释里的 M39 一句(`pluginCacheRootFor` 组合根仍读,留);" +
|
|
606
|
+
"`test/data-dir-modes-upgrade-m39.test.ts` 整只文件;`test/data-dir-law-census.test.ts` 例外名册里 `plugins/local-data-dir-modes-m39.ts` 那一行、模式值例外名册里它那一行、「M39 名册零字面路径」一格与「字面量 = ensureDir 铸出的模式」一格里的迁移半场;本文件头注 M39 段;重生成 `docs/core-consumption/READ-TABLE.json`(迁移读 core 的 `physicalPathOf` 那一行随之去掉)。" +
|
|
607
|
+
"**不随本条删**:数据根建目录只经 `ensureDir` 这条律本身(常设)、`plugins/owner-only-file-mode.ts`(本服务文件的 `0600` 唯一值与原子写的两个出口:异步出口常设,同步出口按它自己的退役条件 —— core 的原子写改成独占新建 tmp —— 走,不随本条)及它们的钉 —— `test/data-dir-law-census.test.ts`(`src/**` 零裸 mkdir、零手写 0o700、文件模式只认一个名字、tmp 只在一只写口里铸)与 `test/data-dir-modes.test.ts`(每只 local 店首建目录 0700、旧 tmp 不被复用)",
|
|
608
|
+
afterRemoval: "没有响亮臂(如实,也无需):删码之后,跳过了带本条的版本直接升的数据根,升级前铸成的 0755 目录原样留着(新建的目录一律 0700,不受影响;里面的文件多半仍是 0600),本服务不再点名 —— " +
|
|
609
|
+
"出路 = 运维一次 `chmod 700` 数据根下 `runs`(及其子目录)· `space-commits` · `checkpoint-ctx` · `.tmp` · `attachments` · `scratchpad`(及其子目录)· `plugin-cache`(及其子目录)· `<记忆引擎根>/sync-cursors`,或升级时先过一次带本条的版本并起服",
|
|
610
|
+
reportKeys: [],
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
id: "M40",
|
|
614
|
+
introducedIn: "7.147.0",
|
|
615
|
+
releasedOn: "2026-11-07",
|
|
616
|
+
removableAfter: "2026-12-07",
|
|
617
|
+
removal: "删 `src/plugins/execution-lane-rename-m40.ts` 整只文件;本文件的 M40 步(`renameRetiredExecutionLaneWordsM40` / `refuseWhileRunsHeartbeat`〔它读的 `readLiveRunHeartbeat` / `LiveRunHeartbeat` 由 M41 共读,M41 也已删码时随之删〕/ `ExecutionLaneRenameRefusedError` / " +
|
|
618
|
+
"`EXECUTION_LANE_RENAME_REFUSED_CODE` / `ExecutionLaneRenameInput`,`migrateStepsOn` 第一步那一行与 `SqlOneTimeMigrationInput` 里的那一半)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `executionLaneWordsRenamed`、头注 M40 段;" +
|
|
619
|
+
"`store-backend.ts` 的 `LocalBackend.ensureSchema` 第一步与 `executionLaneWordsRenamed` 字段、两只 SQL 后端递 `liveRunWindowMs` 的那一半;" +
|
|
620
|
+
"`LocalSessionStore.renameRetiredExecutionLaneWordM40`、`FileApprovalAskStore.renameRetiredExecutionLaneWordM40` 与内核的 `renameExecutionLaneWord`;" +
|
|
621
|
+
"`approval-ask-store-sql.ts` / `execution-placement-sql.ts` 拒句里的 `retiredExecutionLaneHintM40(…)` 补句;`test/execution-lane-rename-m40.test.ts` 整只文件与 `test/execution-placement-switch-db-integration.test.ts` 的 M40 组",
|
|
622
|
+
afterRemoval: "既有读口的拒臂接住(响亮,不再改名):仍带旧词的行 —— 跳过了带本条的版本直接升的库、窗关之后还在写的更早版本副本 —— SQL 问行读口拒、会话执行格读口拒(`ExecutionPlacementRowError`)," +
|
|
623
|
+
"local 会话元数据边档整只严格拒启(`config.schema_mismatch`),local 问账日志由 M32 回填的词表外拒臂拒启(M32 删码之后:那只问行原样装回)。出路 = 升级时先过一次带本条的版本并起服",
|
|
624
|
+
reportKeys: ["executionLaneWordsRenamed"],
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
id: "M41",
|
|
628
|
+
introducedIn: "7.147.0",
|
|
629
|
+
releasedOn: "2026-11-07",
|
|
630
|
+
removableAfter: "2026-12-07",
|
|
631
|
+
removal: "删本文件的 M41 步(`moveSessionCellsToSideTable` / `refuseSessionExtMoveWhileRunsHeartbeat` / `deploymentColumnsOnMeta` / `SessionExtMoveRefusedError` / `SESSION_EXT_MOVE_REFUSED_CODE` / " +
|
|
632
|
+
"`SessionExtMoveRefusalReason` / `sameCell` / `SESSION_EXT_MOVE_PAGE`,`migrateStepsOn` 里那两行)、报告 / 收据 / 日志 / `NO_SQL_MIGRATIONS` 的 `sessionExtRowsMoved` / `sessionMetaColumnsDropped`、头注 M41 段;" +
|
|
633
|
+
"M32 读形里的 `cellsOnSideTable` 判(它读本条的 `deploymentColumnsOnMeta`):M32 还在时改成恒旁表形 —— 删 M32 写 `session_meta` 八列的那些臂(② ④ ④b ⑥ 与 ③ 的 `migrateDeviceBindingsStatement` 臂)," +
|
|
634
|
+
"留 ③ 的旁表臂(`migrateDeviceBindingsOntoSideTableStatements`:围栏拒句的「采旧」在任何库上仍要能落到执行格);M32 先于本条删码时这些随 M32 一并删;`readLiveRunHeartbeat` 在 M40 也已删码时随之删;`test/session-ext-move-db-integration.test.ts` 整只文件",
|
|
635
|
+
afterRemoval: "跳过了带本条的版本直接升上来的库:`session_meta` 上仍带部署格列 ⇒ 起服列册对账拒启(`config.schema_mismatch`,那几列是本树之外的多余列,拒句不给 ALTER —— 出路是带本条的版本);" +
|
|
636
|
+
"部署格从旁表读,旧列上的值不被任何读者读。出路 = 升级时先过一次带本条的版本并起服",
|
|
637
|
+
reportKeys: ["sessionExtRowsMoved", "sessionMetaColumnsDropped"],
|
|
638
|
+
},
|
|
607
639
|
];
|
|
608
640
|
export function migrationWindowOpen(window, today) {
|
|
609
641
|
return window.removableAfter === null || today <= window.removableAfter;
|
|
@@ -617,8 +649,6 @@ export function migrationWindowEnd(id) {
|
|
|
617
649
|
return row.removableAfter;
|
|
618
650
|
}
|
|
619
651
|
export const NO_SQL_MIGRATIONS = {
|
|
620
|
-
workflowParkIndexKey: "file-ledger",
|
|
621
|
-
workflowParkIndexRowsCarried: 0,
|
|
622
652
|
undurableScopeRuleRows: [],
|
|
623
653
|
undurableScopeRuleBucketsDeferred: 0,
|
|
624
654
|
undurableScopeRuleBucketsUnreadable: [],
|
|
@@ -647,6 +677,8 @@ export const NO_SQL_MIGRATIONS = {
|
|
|
647
677
|
deviceBindingsMoved: 0,
|
|
648
678
|
deviceBindingsOrphaned: 0,
|
|
649
679
|
deviceBindingTablesDropped: 0,
|
|
680
|
+
sessionExtRowsMoved: 0,
|
|
681
|
+
sessionMetaColumnsDropped: 0,
|
|
650
682
|
askCauseColumnsChanged: 0,
|
|
651
683
|
askParkTriggersMoved: 0,
|
|
652
684
|
};
|
|
@@ -703,7 +735,7 @@ async function columnExists(db, dialect, table, column) {
|
|
|
703
735
|
return (await db.rows(sql)).length > 0;
|
|
704
736
|
}
|
|
705
737
|
export function createSqlImportedLayerRootSource(exec) {
|
|
706
|
-
return { candidateOwners: () => listRuleBucketOwners(exec), durable: new SqlDurableRulePartitionProvider(exec), isWholeBucketRefusal: isSqlBucketRefusal, isVanishedBucket: isSqlBucketVanished };
|
|
738
|
+
return { candidateOwners: async () => ({ owners: await listRuleBucketOwners(exec), unnamedBuckets: 0 }), durable: new SqlDurableRulePartitionProvider(exec), isWholeBucketRefusal: isSqlBucketRefusal, isVanishedBucket: isSqlBucketVanished };
|
|
707
739
|
}
|
|
708
740
|
export async function runSqlOneTimeMigrations(dialect, pool, input) {
|
|
709
741
|
return dialect === "mysql" ? withMysqlLock(pool, (db) => migrateOn(db, "mysql", input)) : withPgLock(pool, (db) => migrateOn(db, "pg", input));
|
|
@@ -747,9 +779,10 @@ async function migrateOn(db, dialect, input) {
|
|
|
747
779
|
return completedReport(receipts);
|
|
748
780
|
}
|
|
749
781
|
async function migrateStepsOn(db, dialect, receipts, input) {
|
|
782
|
+
await renameRetiredExecutionLaneWordsM40(db, dialect, receipts, input);
|
|
783
|
+
await refuseSessionExtMoveWhileRunsHeartbeat(db, dialect, input);
|
|
750
784
|
await migrateRetiredCardWords(db, dialect, receipts);
|
|
751
785
|
await splitApprovalAskCauseColumns(db, dialect, receipts);
|
|
752
|
-
await reshapeWorkflowParkIndex(db, dialect, receipts);
|
|
753
786
|
await migrateSessionRuleGrammar(db, dialect, receipts);
|
|
754
787
|
await repairParkedRows(db, dialect, receipts);
|
|
755
788
|
await backfillParkedApprovalCards(db, dialect, receipts);
|
|
@@ -765,6 +798,149 @@ async function migrateStepsOn(db, dialect, receipts, input) {
|
|
|
765
798
|
await dropRetiredBreakerTable(db, dialect, receipts);
|
|
766
799
|
await dropRejectedMailboxKeys(db, dialect, receipts);
|
|
767
800
|
await switchExecutionPlacement(db, dialect, receipts, input);
|
|
801
|
+
await moveSessionCellsToSideTable(db, dialect, receipts);
|
|
802
|
+
}
|
|
803
|
+
export const EXECUTION_LANE_RENAME_REFUSED_CODE = "config.execution_lane_rename_refused";
|
|
804
|
+
export class ExecutionLaneRenameRefusedError extends StoreBootRefusalError {
|
|
805
|
+
pendingRows;
|
|
806
|
+
liveRuns;
|
|
807
|
+
restartAfter;
|
|
808
|
+
code = EXECUTION_LANE_RENAME_REFUSED_CODE;
|
|
809
|
+
constructor(pendingRows, liveRuns, restartAfter, detail) {
|
|
810
|
+
super(`${EXECUTION_LANE_RENAME_REFUSED_CODE}: ${detail}`);
|
|
811
|
+
this.pendingRows = pendingRows;
|
|
812
|
+
this.liveRuns = liveRuns;
|
|
813
|
+
this.restartAfter = restartAfter;
|
|
814
|
+
this.name = "ExecutionLaneRenameRefusedError";
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
export class ExecutionLaneRenameWriteRefusedError extends StoreBootRefusalError {
|
|
818
|
+
file;
|
|
819
|
+
code = EXECUTION_LANE_RENAME_REFUSED_CODE;
|
|
820
|
+
errno;
|
|
821
|
+
constructor(file, cause) {
|
|
822
|
+
const errno = typeof cause?.code === "string" ? cause.code : undefined;
|
|
823
|
+
const fault = errno ?? (cause instanceof Error ? cause.message : String(cause));
|
|
824
|
+
super(`${EXECUTION_LANE_RENAME_REFUSED_CODE}: one-time migration M40 could not rewrite ${file} (${fault}) — it still records the retired execution-lane word ` +
|
|
825
|
+
`"${RETIRED_EXECUTION_LANE_WORD_M40}" (now "${RENAMED_EXECUTION_LANE_M40}"), which this build refuses to read, so it refuses to start rather than run without that data. ` +
|
|
826
|
+
`The file is unchanged. Restore write access to the file and its directory (clear an immutable flag, remount read-write, free disk space), then restart: the rename runs again on the next start.`, { cause });
|
|
827
|
+
this.file = file;
|
|
828
|
+
this.name = "ExecutionLaneRenameWriteRefusedError";
|
|
829
|
+
this.errno = errno;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
const EXECUTION_LANE_TABLES = [APPROVAL_ASK_TABLE, "session_meta", SESSION_EXT_TABLE];
|
|
833
|
+
export async function renameRetiredExecutionLaneWordsM40(db, dialect, receipts, input) {
|
|
834
|
+
const tables = [];
|
|
835
|
+
for (const table of EXECUTION_LANE_TABLES)
|
|
836
|
+
if (await columnExists(db, dialect, table, "execution_lane"))
|
|
837
|
+
tables.push(table);
|
|
838
|
+
let pendingRows = 0;
|
|
839
|
+
for (const table of tables) {
|
|
840
|
+
const [row] = await db.rows(`SELECT COUNT(*) AS n FROM ${table} WHERE execution_lane = ${dialect === "mysql" ? "?" : "$1"}`, [RETIRED_EXECUTION_LANE_WORD_M40]);
|
|
841
|
+
pendingRows += Number(row?.["n"] ?? 0);
|
|
842
|
+
}
|
|
843
|
+
if (pendingRows === 0)
|
|
844
|
+
return;
|
|
845
|
+
await refuseWhileRunsHeartbeat(db, dialect, pendingRows, input);
|
|
846
|
+
for (const table of tables) {
|
|
847
|
+
receipts.executionLaneWordsRenamed += await db.exec(dialect === "mysql" ? `UPDATE ${table} SET execution_lane = ? WHERE execution_lane = ?` : `UPDATE ${table} SET execution_lane = $1 WHERE execution_lane = $2`, [RENAMED_EXECUTION_LANE_M40, RETIRED_EXECUTION_LANE_WORD_M40]);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
async function readLiveRunHeartbeat(db, dialect, input) {
|
|
851
|
+
if (!(await tableExists(db, dialect, "task_run")))
|
|
852
|
+
return undefined;
|
|
853
|
+
const since = new Date(Date.now() - input.liveRunWindowMs);
|
|
854
|
+
const [row] = await db.rows(`SELECT COUNT(*) AS n, MAX(updated_at) AS newest FROM task_run WHERE status = 'running' AND updated_at >= ${dialect === "mysql" ? "?" : "$1"}`, [since]);
|
|
855
|
+
const liveRuns = Number(row?.["n"] ?? 0);
|
|
856
|
+
if (liveRuns === 0)
|
|
857
|
+
return undefined;
|
|
858
|
+
const newestCell = row?.["newest"];
|
|
859
|
+
const newest = newestCell instanceof Date ? newestCell : new Date(String(newestCell));
|
|
860
|
+
const now = Date.now();
|
|
861
|
+
const restartAtMs = (Number.isNaN(newest.getTime()) ? now : newest.getTime()) + input.liveRunWindowMs;
|
|
862
|
+
return {
|
|
863
|
+
liveRuns,
|
|
864
|
+
windowSec: Math.round(input.liveRunWindowMs / 1000),
|
|
865
|
+
restartAfter: new Date(restartAtMs).toISOString(),
|
|
866
|
+
after: `${new Date(restartAtMs).toISOString()} (${Math.max(0, Math.ceil((restartAtMs - now) / 1000))}s from now)`,
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
async function refuseWhileRunsHeartbeat(db, dialect, pendingRows, input) {
|
|
870
|
+
const heartbeat = await readLiveRunHeartbeat(db, dialect, input);
|
|
871
|
+
if (heartbeat === undefined)
|
|
872
|
+
return;
|
|
873
|
+
const { liveRuns, windowSec, restartAfter, after } = heartbeat;
|
|
874
|
+
throw new ExecutionLaneRenameRefusedError(pendingRows, liveRuns, restartAfter, `${pendingRows} stored rows (approval asks / sessions) carry the execution lane word "${RETIRED_EXECUTION_LANE_WORD_M40}", the name an earlier version gave the lane with no execution environment, ` +
|
|
875
|
+
`which this version calls "${RENAMED_EXECUTION_LANE_M40}" and does not read; this boot renames them (one-time migration M40), but ${liveRuns} running runs on this database heartbeat within the last ${windowSec}s ` +
|
|
876
|
+
`(REAP_RUN_STALE_SEC) — a replica of an earlier version is still serving, and it would keep writing the old word after the rename. No stored row was changed. ` +
|
|
877
|
+
"Stop every replica of the earlier version, then start this version (stop, migrate, start — this release cannot be rolled across replicas of both versions). " +
|
|
878
|
+
`If every replica is already stopped, those are the runs they left behind: start this version again after ${after}`);
|
|
879
|
+
}
|
|
880
|
+
export const SESSION_EXT_MOVE_REFUSED_CODE = "config.session_ext_move_refused";
|
|
881
|
+
export class SessionExtMoveRefusedError extends StoreBootRefusalError {
|
|
882
|
+
reason;
|
|
883
|
+
code = SESSION_EXT_MOVE_REFUSED_CODE;
|
|
884
|
+
constructor(reason, detail) {
|
|
885
|
+
super(`${SESSION_EXT_MOVE_REFUSED_CODE} (reason: ${reason}): ${detail}`);
|
|
886
|
+
this.reason = reason;
|
|
887
|
+
this.name = "SessionExtMoveRefusedError";
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async function deploymentColumnsOnMeta(db, dialect) {
|
|
891
|
+
if (!(await tableExists(db, dialect, "session_meta")))
|
|
892
|
+
return [];
|
|
893
|
+
const present = [];
|
|
894
|
+
for (const column of SESSION_EXT_COLUMNS)
|
|
895
|
+
if (await columnExists(db, dialect, "session_meta", column.name))
|
|
896
|
+
present.push(column);
|
|
897
|
+
return present;
|
|
898
|
+
}
|
|
899
|
+
const sameCell = (dialect, column) => {
|
|
900
|
+
const side = column.name === "pinned" ? "COALESCE(se.pinned, FALSE)" : `se.${column.name}`;
|
|
901
|
+
return dialect === "mysql" ? `sm.${column.name} <=> ${side}` : `sm.${column.name} IS NOT DISTINCT FROM ${side}`;
|
|
902
|
+
};
|
|
903
|
+
const SESSION_EXT_MOVE_PAGE = 500;
|
|
904
|
+
export async function refuseSessionExtMoveWhileRunsHeartbeat(db, dialect, input) {
|
|
905
|
+
const columns = await deploymentColumnsOnMeta(db, dialect);
|
|
906
|
+
if (columns.length === 0)
|
|
907
|
+
return;
|
|
908
|
+
const heartbeat = await readLiveRunHeartbeat(db, dialect, input);
|
|
909
|
+
if (heartbeat === undefined)
|
|
910
|
+
return;
|
|
911
|
+
throw new SessionExtMoveRefusedError("live_runs", `${columns.length} session cells (${columns.map((c) => c.name).join(", ")}) still live on session_meta; this version keeps them on the side table ${SESSION_EXT_TABLE} and moves them on this boot ` +
|
|
912
|
+
`(one-time migration M41: copy, verify, then drop the columns), but ${heartbeat.liveRuns} running runs on this database heartbeat within the last ${heartbeat.windowSec}s (REAP_RUN_STALE_SEC) — ` +
|
|
913
|
+
"a replica of an earlier version is still serving; a cell it writes between the copy and the drop would be lost, and every cell write it makes after the drop fails. Nothing was changed. " +
|
|
914
|
+
"Stop every replica of the earlier version, then start this version (stop, migrate, start — this release cannot be rolled across replicas of both versions). " +
|
|
915
|
+
`If every replica is already stopped, those are the runs they left behind: start this version again after ${heartbeat.after}`);
|
|
916
|
+
}
|
|
917
|
+
export async function moveSessionCellsToSideTable(db, dialect, receipts) {
|
|
918
|
+
const columns = await deploymentColumnsOnMeta(db, dialect);
|
|
919
|
+
if (columns.length === 0)
|
|
920
|
+
return;
|
|
921
|
+
const names = columns.map((c) => c.name);
|
|
922
|
+
const p = dialect === "mysql" ? () => "?" : (n) => `$${n}`;
|
|
923
|
+
const upsert = `INSERT INTO ${SESSION_EXT_TABLE} (session_id, ${names.join(", ")}) SELECT session_id, ${names.join(", ")} FROM session_meta ` +
|
|
924
|
+
`WHERE session_id > ${p(1)} AND session_id <= ${p(2)} ` +
|
|
925
|
+
(dialect === "mysql" ? `ON DUPLICATE KEY UPDATE ${names.map((n) => `${SESSION_EXT_TABLE}.${n} = VALUES(${n})`).join(", ")}` : `ON CONFLICT (session_id) DO UPDATE SET ${names.map((n) => `${n} = EXCLUDED.${n}`).join(", ")}`);
|
|
926
|
+
let after = "";
|
|
927
|
+
for (;;) {
|
|
928
|
+
const page = await db.rows(`SELECT session_id FROM session_meta WHERE session_id > ${p(1)} ORDER BY session_id LIMIT ${SESSION_EXT_MOVE_PAGE}`, [after]);
|
|
929
|
+
if (page.length === 0)
|
|
930
|
+
break;
|
|
931
|
+
const last = String(page[page.length - 1]["session_id"]);
|
|
932
|
+
await db.exec(upsert, [after, last]);
|
|
933
|
+
receipts.sessionExtRowsMoved += page.length;
|
|
934
|
+
after = last;
|
|
935
|
+
}
|
|
936
|
+
const [mismatch] = await db.rows(`SELECT sm.session_id FROM session_meta sm LEFT JOIN ${SESSION_EXT_TABLE} se ON se.session_id = sm.session_id WHERE NOT (${columns.map((c) => sameCell(dialect, c)).join(" AND ")}) LIMIT 1`);
|
|
937
|
+
if (mismatch !== undefined) {
|
|
938
|
+
throw new SessionExtMoveRefusedError("copy_mismatch", `after copying the session cells (${names.join(", ")}) from session_meta to ${SESSION_EXT_TABLE}, session ${String(mismatch["session_id"])} does not hold the same values on both tables — ` +
|
|
939
|
+
"a writer changed it while this boot copied (an earlier-version replica still serving), or the copy did not land. No column was dropped. " +
|
|
940
|
+
"Stop every replica of the earlier version and start this version again: the copy runs again from the start");
|
|
941
|
+
}
|
|
942
|
+
await execUntil(db, `ALTER TABLE session_meta ${names.map((n) => `DROP COLUMN ${n}`).join(", ")}`, async () => (await deploymentColumnsOnMeta(db, dialect)).length === 0);
|
|
943
|
+
receipts.sessionMetaColumnsDropped += columns.length;
|
|
768
944
|
}
|
|
769
945
|
const RETIRED_ASK_CAUSE_COLUMN = "denied_reason";
|
|
770
946
|
function renameAskCauseColumnSql(dialect) {
|
|
@@ -806,12 +982,13 @@ async function readSwitchShape(db, dialect) {
|
|
|
806
982
|
const sessionMeta = await tableExists(db, dialect, "session_meta");
|
|
807
983
|
const placementColumns = new Set();
|
|
808
984
|
if (sessionMeta)
|
|
809
|
-
for (const c of
|
|
985
|
+
for (const c of EXECUTION_PLACEMENT_COLUMNS)
|
|
810
986
|
if (await columnExists(db, dialect, "session_meta", c.name))
|
|
811
987
|
placementColumns.add(c.name);
|
|
812
988
|
return {
|
|
813
989
|
sessionMeta,
|
|
814
990
|
placementColumns,
|
|
991
|
+
cellsOnSideTable: sessionMeta && (await deploymentColumnsOnMeta(db, dialect)).length === 0,
|
|
815
992
|
bindingTable: await tableExists(db, dialect, RETIRED_DEVICE_BINDING_TABLE),
|
|
816
993
|
parkedTable: await tableExists(db, dialect, PARKED_DEVICE_BINDING_TABLE),
|
|
817
994
|
taskRun: sessionMeta && (await tableExists(db, dialect, "task_run")),
|
|
@@ -823,11 +1000,11 @@ async function askTableShape(db, dialect) {
|
|
|
823
1000
|
return { askTable, askLaneColumn: askTable && (await columnExists(db, dialect, APPROVAL_ASK_TABLE, "execution_lane")) };
|
|
824
1001
|
}
|
|
825
1002
|
function switchStarted(s) {
|
|
826
|
-
return s.placementColumns.has("execution_role") || s.parkedTable;
|
|
1003
|
+
return s.placementColumns.has("execution_role") || s.parkedTable || s.cellsOnSideTable;
|
|
827
1004
|
}
|
|
828
1005
|
const SWITCH_COMPLETION_COLUMN = "fork_key";
|
|
829
1006
|
function switchComplete(s) {
|
|
830
|
-
return s.placementColumns.size ===
|
|
1007
|
+
return (s.placementColumns.size === EXECUTION_PLACEMENT_COLUMNS.length || s.cellsOnSideTable) && (!s.askTable || s.askLaneColumn) && !s.bindingTable && !s.parkedTable;
|
|
831
1008
|
}
|
|
832
1009
|
function askLaneBackfillSql(dialect) {
|
|
833
1010
|
return dialect === "mysql"
|
|
@@ -878,7 +1055,7 @@ const EXECUTION_PLACEMENT_BACKFILL_NEEDS = [
|
|
|
878
1055
|
{ what: "approval ask rows that do not record the execution lane they were raised on (they would record their root session's lane, or the only declared lane)", holds: askRowsAwaitingLane },
|
|
879
1056
|
];
|
|
880
1057
|
async function refuseOnAmbiguousBackfill(read, dialect, shape, lanes) {
|
|
881
|
-
if (!shape.sessionMeta || switchComplete(shape) || lanes.length === 1)
|
|
1058
|
+
if (!shape.sessionMeta || shape.cellsOnSideTable || switchComplete(shape) || lanes.length === 1)
|
|
882
1059
|
return;
|
|
883
1060
|
const pending = [];
|
|
884
1061
|
for (const need of EXECUTION_PLACEMENT_BACKFILL_NEEDS)
|
|
@@ -931,7 +1108,7 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
|
|
|
931
1108
|
id: "① park the retired binding table",
|
|
932
1109
|
run: async ({ db, dialect, shape }) => {
|
|
933
1110
|
const s = await shape();
|
|
934
|
-
if (!s.bindingTable || s.placementColumns.has("execution_role"))
|
|
1111
|
+
if (!s.bindingTable || s.placementColumns.has("execution_role") || s.cellsOnSideTable)
|
|
935
1112
|
return;
|
|
936
1113
|
const sql = dialect === "mysql" ? `RENAME TABLE ${RETIRED_DEVICE_BINDING_TABLE} TO ${PARKED_DEVICE_BINDING_TABLE}` : `ALTER TABLE ${RETIRED_DEVICE_BINDING_TABLE} RENAME TO ${PARKED_DEVICE_BINDING_TABLE}`;
|
|
937
1114
|
await execUntil(db, sql, async () => {
|
|
@@ -955,7 +1132,9 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
|
|
|
955
1132
|
{
|
|
956
1133
|
id: "② add the execution placement columns",
|
|
957
1134
|
run: async ({ db, dialect, receipts, shape }) => {
|
|
958
|
-
|
|
1135
|
+
if ((await shape()).cellsOnSideTable)
|
|
1136
|
+
return;
|
|
1137
|
+
for (const column of EXECUTION_PLACEMENT_COLUMNS) {
|
|
959
1138
|
if (column.name === SWITCH_COMPLETION_COLUMN)
|
|
960
1139
|
continue;
|
|
961
1140
|
if ((await shape()).placementColumns.has(column.name))
|
|
@@ -973,10 +1152,17 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
|
|
|
973
1152
|
{
|
|
974
1153
|
id: "③ move the bindings onto the root session rows",
|
|
975
1154
|
run: async ({ db, dialect, receipts, shape }) => {
|
|
976
|
-
|
|
1155
|
+
const s = await shape();
|
|
1156
|
+
if (!s.parkedTable)
|
|
977
1157
|
return;
|
|
978
1158
|
const orphans = countOrphanBindingsStatement(dialect, PARKED_DEVICE_BINDING_TABLE);
|
|
979
1159
|
receipts.deviceBindingsOrphaned += Number((await db.rows(orphans.text, orphans.values))[0]?.["orphans"] ?? 0);
|
|
1160
|
+
if (s.cellsOnSideTable) {
|
|
1161
|
+
const [ensure, move] = migrateDeviceBindingsOntoSideTableStatements(dialect, PARKED_DEVICE_BINDING_TABLE);
|
|
1162
|
+
await db.exec(ensure.text, ensure.values);
|
|
1163
|
+
receipts.deviceBindingsMoved += await db.exec(move.text, move.values);
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
980
1166
|
const move = migrateDeviceBindingsStatement(dialect, PARKED_DEVICE_BINDING_TABLE);
|
|
981
1167
|
receipts.deviceBindingsMoved += await db.exec(move.text, move.values);
|
|
982
1168
|
},
|
|
@@ -985,6 +1171,8 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
|
|
|
985
1171
|
id: "④ backfill placements of sessions with runs",
|
|
986
1172
|
run: async ({ db, dialect, input, receipts, shape }) => {
|
|
987
1173
|
const s = await shape();
|
|
1174
|
+
if (s.cellsOnSideTable)
|
|
1175
|
+
return;
|
|
988
1176
|
if (input.lanes.length !== 1) {
|
|
989
1177
|
await refuseOnAmbiguousBackfill((sql) => db.rows(sql), dialect, s, input.lanes);
|
|
990
1178
|
return;
|
|
@@ -998,7 +1186,7 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
|
|
|
998
1186
|
id: "④b backfill the execution lane of approval ask rows",
|
|
999
1187
|
run: async ({ db, dialect, input, receipts, shape }) => {
|
|
1000
1188
|
const s = await shape();
|
|
1001
|
-
if (!s.askTable || !s.askLaneColumn)
|
|
1189
|
+
if (!s.askTable || !s.askLaneColumn || s.cellsOnSideTable)
|
|
1002
1190
|
return;
|
|
1003
1191
|
if (input.lanes.length !== 1) {
|
|
1004
1192
|
await refuseOnAmbiguousBackfill((sql) => db.rows(sql), dialect, s, input.lanes);
|
|
@@ -1019,9 +1207,10 @@ const EXECUTION_PLACEMENT_SWITCH_STEPS = [
|
|
|
1019
1207
|
{
|
|
1020
1208
|
id: "⑥ add the completion marker column",
|
|
1021
1209
|
run: async ({ db, dialect, receipts, shape }) => {
|
|
1022
|
-
|
|
1210
|
+
const s = await shape();
|
|
1211
|
+
if (s.placementColumns.has(SWITCH_COMPLETION_COLUMN) || s.cellsOnSideTable)
|
|
1023
1212
|
return;
|
|
1024
|
-
const marker =
|
|
1213
|
+
const marker = EXECUTION_PLACEMENT_COLUMNS.find((c) => c.name === SWITCH_COMPLETION_COLUMN);
|
|
1025
1214
|
await execUntil(db, `ALTER TABLE session_meta ADD COLUMN ${marker[dialect]}`, async () => (await shape()).placementColumns.has(SWITCH_COMPLETION_COLUMN));
|
|
1026
1215
|
receipts.executionPlacementColumnsAdded += 1;
|
|
1027
1216
|
},
|
|
@@ -1063,9 +1252,17 @@ export async function backfillLocalExecutionPlacements(sessionIds, seat, input)
|
|
|
1063
1252
|
}
|
|
1064
1253
|
return { backfilled: placed, unreadable };
|
|
1065
1254
|
}
|
|
1255
|
+
const UNKNOWN_LANE_ASKS_SHOWN = 5;
|
|
1066
1256
|
export function backfillLocalAskExecutionLanes(store, input) {
|
|
1067
|
-
return store.backfillExecutionLanes(input.lanes, (
|
|
1068
|
-
|
|
1257
|
+
return store.backfillExecutionLanes(input.lanes, (refusal) => {
|
|
1258
|
+
if (refusal.kind === "unknown_lane") {
|
|
1259
|
+
const shown = refusal.asks.slice(0, UNKNOWN_LANE_ASKS_SHOWN).map((a) => `${a.askId} = ${JSON.stringify(a.word)}${retiredExecutionLaneHintM40(a.word) === "" ? "" : " (the retired name of none)"}`);
|
|
1260
|
+
const more = refusal.asks.length > shown.length ? ` and ${refusal.asks.length - shown.length} more` : "";
|
|
1261
|
+
throw new ExecutionPlacementSwitchRefusedError("unknown_lane", `the local approval ask journal holds ${refusal.asks.length} asks whose recorded execution lane is not a lane this version knows (${shown.join(", ")}${more}) — ` +
|
|
1262
|
+
"recording the deployment's lane over it would rewrite the lane the ask was raised on, and this server will not guess one. Nothing was changed. " +
|
|
1263
|
+
"The journal was written by a newer version or altered by hand: start the version that wrote it, or restore the journal from a copy");
|
|
1264
|
+
}
|
|
1265
|
+
throw new ExecutionPlacementSwitchRefusedError("backfill_ambiguous", `the local approval ask journal holds asks that do not record the execution lane they were raised on (${refusal.pending}), and the deployment declares ${input.lanes.length === 0 ? "no lane" : `${input.lanes.length} lanes (${input.lanes.join(", ")})`} — ` +
|
|
1069
1266
|
"with more than one lane there is no single right answer, and this server will not guess one. Nothing was changed. " +
|
|
1070
1267
|
"Start this version once with a single declared lane (REMOTE_EXEC set to one lane), then declare the additional lanes");
|
|
1071
1268
|
});
|
|
@@ -1294,55 +1491,6 @@ function renameSessionRulesColumnSql(dialect, from, to) {
|
|
|
1294
1491
|
const definition = SESSION_RULES_COLUMN.mysql.slice(SESSION_RULES_COLUMN.name.length);
|
|
1295
1492
|
return dialect === "mysql" ? `ALTER TABLE ${SESSION_POLICY_TABLE} CHANGE COLUMN ${from} ${to}${definition}` : `ALTER TABLE ${SESSION_POLICY_TABLE} RENAME COLUMN ${from} TO ${to}`;
|
|
1296
1493
|
}
|
|
1297
|
-
const WORKFLOW_PARK_INDEX_TABLE = "workflow_agent_session";
|
|
1298
|
-
async function primaryKeyWidth(db, dialect, table) {
|
|
1299
|
-
const sql = dialect === "mysql"
|
|
1300
|
-
? `SELECT COUNT(*) AS n FROM information_schema.key_column_usage WHERE table_schema = DATABASE() AND table_name = '${table}' AND constraint_name = 'PRIMARY'`
|
|
1301
|
-
: `SELECT COUNT(*) AS n FROM information_schema.table_constraints tc JOIN information_schema.key_column_usage kcu ` +
|
|
1302
|
-
`ON kcu.constraint_name = tc.constraint_name AND kcu.table_schema = tc.table_schema AND kcu.table_name = tc.table_name ` +
|
|
1303
|
-
`WHERE tc.table_schema = current_schema() AND tc.table_name = '${table}' AND tc.constraint_type = 'PRIMARY KEY'`;
|
|
1304
|
-
return Number((await db.rows(sql))[0]?.n ?? 0);
|
|
1305
|
-
}
|
|
1306
|
-
const WORKFLOW_PARK_INDEX_STAGING = `${WORKFLOW_PARK_INDEX_TABLE}_m9`;
|
|
1307
|
-
const WORKFLOW_PARK_INDEX_RETIRED = `${WORKFLOW_PARK_INDEX_TABLE}_m9_old`;
|
|
1308
|
-
async function reshapeWorkflowParkIndex(db, dialect, receipts) {
|
|
1309
|
-
const T = WORKFLOW_PARK_INDEX_TABLE;
|
|
1310
|
-
const S = WORKFLOW_PARK_INDEX_STAGING;
|
|
1311
|
-
const O = WORKFLOW_PARK_INDEX_RETIRED;
|
|
1312
|
-
const exists = (t) => tableExists(db, dialect, t);
|
|
1313
|
-
const ddl = (dialect === "mysql" ? SCHEMA_STATEMENTS : PG_SCHEMA_STATEMENTS).find((stmt) => stmt.includes(`CREATE TABLE IF NOT EXISTS ${T} (`));
|
|
1314
|
-
if (ddl === undefined)
|
|
1315
|
-
throw new Error(`one-time migration M9: the central schema carries no definition of ${T} — refusing to reshape a table it cannot rebuild`);
|
|
1316
|
-
const copyPointers = (to, from) => db.exec(dialect === "mysql"
|
|
1317
|
-
? `INSERT IGNORE INTO ${to} (session_id, run_id, agent_key, scope) SELECT session_id, run_id, agent_key, scope FROM ${from}`
|
|
1318
|
-
: `INSERT INTO ${to} (session_id, run_id, agent_key, scope) SELECT session_id, run_id, agent_key, scope FROM ${from} ON CONFLICT DO NOTHING`);
|
|
1319
|
-
const resumed = await exists(S);
|
|
1320
|
-
if (resumed) {
|
|
1321
|
-
await db.exec(ddl);
|
|
1322
|
-
receipts.workflowParkIndexRowsCarried += await copyPointers(T, S);
|
|
1323
|
-
await execUntil(db, `DROP TABLE ${S}`, async () => !(await exists(S)));
|
|
1324
|
-
}
|
|
1325
|
-
if (await exists(O))
|
|
1326
|
-
await execUntil(db, `DROP TABLE ${O}`, async () => !(await exists(O)));
|
|
1327
|
-
if ((await primaryKeyWidth(db, dialect, T)) !== 1) {
|
|
1328
|
-
if (await exists(T))
|
|
1329
|
-
await finishParkIndexShape(db, dialect);
|
|
1330
|
-
receipts.workflowParkIndexKey = resumed ? "reshaped" : "current";
|
|
1331
|
-
return;
|
|
1332
|
-
}
|
|
1333
|
-
await db.exec(ddl.replace(`CREATE TABLE IF NOT EXISTS ${T} (`, `CREATE TABLE IF NOT EXISTS ${S} (`));
|
|
1334
|
-
receipts.workflowParkIndexRowsCarried += await copyPointers(S, T);
|
|
1335
|
-
const swapped = async () => !(await exists(S)) && (await exists(T)) && (await primaryKeyWidth(db, dialect, T)) !== 1;
|
|
1336
|
-
if (dialect === "mysql") {
|
|
1337
|
-
await execUntil(db, `RENAME TABLE ${T} TO ${O}, ${S} TO ${T}`, swapped);
|
|
1338
|
-
await execUntil(db, `DROP TABLE IF EXISTS ${O}`, async () => !(await exists(O)));
|
|
1339
|
-
}
|
|
1340
|
-
else {
|
|
1341
|
-
await execAtomically(db, [`ALTER TABLE ${T} RENAME TO ${O}`, `ALTER TABLE ${S} RENAME TO ${T}`, `DROP TABLE ${O}`], async () => (await swapped()) && !(await exists(O)));
|
|
1342
|
-
}
|
|
1343
|
-
await finishParkIndexShape(db, dialect);
|
|
1344
|
-
receipts.workflowParkIndexKey = "reshaped";
|
|
1345
|
-
}
|
|
1346
1494
|
async function execAtomically(db, statements, postcondition) {
|
|
1347
1495
|
try {
|
|
1348
1496
|
await db.exec("BEGIN");
|
|
@@ -1362,16 +1510,6 @@ async function execAtomically(db, statements, postcondition) {
|
|
|
1362
1510
|
throw e;
|
|
1363
1511
|
}
|
|
1364
1512
|
}
|
|
1365
|
-
async function finishParkIndexShape(db, dialect) {
|
|
1366
|
-
await ensureIndexes(async (sql, params) => (dialect === "mysql" ? [await db.rows(sql, params)] : { rows: await db.rows(sql, params) }), dialect, CENTRAL_SCHEMA_INDEXES.filter((spec) => spec.table === WORKFLOW_PARK_INDEX_TABLE));
|
|
1367
|
-
if (dialect !== "pg")
|
|
1368
|
-
return;
|
|
1369
|
-
const conventional = `${WORKFLOW_PARK_INDEX_TABLE}_pkey`;
|
|
1370
|
-
const pk = await db.rows(`SELECT constraint_name FROM information_schema.table_constraints WHERE table_schema = current_schema() AND table_name = '${WORKFLOW_PARK_INDEX_TABLE}' AND constraint_type = 'PRIMARY KEY'`);
|
|
1371
|
-
const name = pk[0]?.constraint_name;
|
|
1372
|
-
if (typeof name === "string" && name !== conventional)
|
|
1373
|
-
await db.exec(`ALTER TABLE ${WORKFLOW_PARK_INDEX_TABLE} RENAME CONSTRAINT ${name} TO ${conventional}`);
|
|
1374
|
-
}
|
|
1375
1513
|
const RETIRED_CARD_WORD_PATTERNS = ['%"shadowed"%', '%"governance_unavailable"%'];
|
|
1376
1514
|
const RETIRED_CARD_WORD_BATCH = 500;
|
|
1377
1515
|
function decodeForM18(text, where) {
|
|
@@ -1621,8 +1759,6 @@ export async function inheritPlacedSessionOwners(db, dialect, receipts) {
|
|
|
1621
1759
|
}
|
|
1622
1760
|
function readingFields(r) {
|
|
1623
1761
|
return {
|
|
1624
|
-
...(r.workflowParkIndexKey !== undefined ? { workflowParkIndexKey: r.workflowParkIndexKey } : {}),
|
|
1625
|
-
workflowParkIndexRowsCarried: r.workflowParkIndexRowsCarried,
|
|
1626
1762
|
undurableScopeRuleRowsRemoved: r.undurableScopeRuleRows.length,
|
|
1627
1763
|
undurableScopeRuleBucketsDeferred: r.undurableScopeRuleBucketsDeferred,
|
|
1628
1764
|
undurableScopeRuleBucketsUnreadable: r.undurableScopeRuleBucketsUnreadable.length,
|
|
@@ -1653,6 +1789,9 @@ function readingFields(r) {
|
|
|
1653
1789
|
deviceBindingTablesDropped: r.deviceBindingTablesDropped,
|
|
1654
1790
|
executionPlacementsBackfilled: r.executionPlacementsBackfilled,
|
|
1655
1791
|
askExecutionLanesBackfilled: r.askExecutionLanesBackfilled,
|
|
1792
|
+
executionLaneWordsRenamed: r.executionLaneWordsRenamed,
|
|
1793
|
+
sessionExtRowsMoved: r.sessionExtRowsMoved,
|
|
1794
|
+
sessionMetaColumnsDropped: r.sessionMetaColumnsDropped,
|
|
1656
1795
|
executionPlacementSessionsUnreadable: r.executionPlacementSessionsUnreadable.length,
|
|
1657
1796
|
localArchiveRowFactsBackfilled: r.localArchiveRowFactsBackfilled,
|
|
1658
1797
|
localArchivesAnonymized: r.localArchivesAnonymized,
|
|
@@ -1664,6 +1803,7 @@ function readingFields(r) {
|
|
|
1664
1803
|
askJournalRecordsUpgraded: r.askJournalRecordsUpgraded,
|
|
1665
1804
|
importedLayerRootAllowsRetired: r.importedLayerRootAllowsRetired.length,
|
|
1666
1805
|
importedLayerRootBucketsSkipped: r.importedLayerRootBucketsSkipped.length,
|
|
1806
|
+
importedLayerRootBucketsUnnamed: r.importedLayerRootBucketsUnnamed,
|
|
1667
1807
|
};
|
|
1668
1808
|
}
|
|
1669
1809
|
export function logOneTimeMigrations(log, backend, report) {
|
|
@@ -1671,8 +1811,7 @@ export function logOneTimeMigrations(log, backend, report) {
|
|
|
1671
1811
|
backend,
|
|
1672
1812
|
...readingFields(report),
|
|
1673
1813
|
removableAfter: Object.fromEntries(ONE_TIME_MIGRATIONS.map((w) => [w.id, w.removableAfter])),
|
|
1674
|
-
note: "one-time persisted-data migrations (
|
|
1675
|
-
"its rows carried over; M10 writes nothing at boot — on the local backend, while its window is open, the first task submitted with a principal " +
|
|
1814
|
+
note: "one-time persisted-data migrations (M10 writes nothing at boot — on the local backend, while its window is open, the first task submitted with a principal " +
|
|
1676
1815
|
"on a session written by an earlier version (which recorded no owner) claims that session for the principal, one session_owner_claimed line per claim; " +
|
|
1677
1816
|
"M11: persisted permission rules whose project scope root is not a complete absolute path — accepted by earlier versions, dropped on every read " +
|
|
1678
1817
|
"by the current engine and not removable through DELETE /v1/rules — removed, one permission_rule_row_removed line per row; a bucket the read path " +
|
|
@@ -1721,7 +1860,10 @@ export function logOneTimeMigrations(log, backend, report) {
|
|
|
1721
1860
|
"M36: allow approvals that an import before engine 7.61.0 stored as global rules from a settings file's user-layer path written from `/` — which the user layer reads at its " +
|
|
1722
1861
|
"configuration directory and a global rule reads at every task's project root — retired by tombstones naming exactly those approvals (a rule that also holds an approval a person " +
|
|
1723
1862
|
"gave directly stays in effect; deny and ask rules are never retired), one imported_layer_root_allow_retired line per rule; a bucket that could not be read or written, or that another replica deleted while it was being written, is named by an " +
|
|
1724
|
-
"imported_layer_root_bucket_skipped line on every boot
|
|
1863
|
+
"imported_layer_root_bucket_skipped line on every boot; " +
|
|
1864
|
+
"M40: the stored execution lane word in-process — the name earlier versions gave the lane with no execution environment, which this version calls none and does not read — " +
|
|
1865
|
+
"rewritten as none on approval ask rows and session rows (on the local backend: in the session metadata file and the approval ask journal); a boot that finds such rows while " +
|
|
1866
|
+
"runs on the same database still heartbeat refuses with nothing changed, because a replica of an earlier version is still serving and would keep writing the old word). Each step " +
|
|
1725
1867
|
"runs on every boot until its window closes (the release that introduced it + 30 days; the per-step date is in " +
|
|
1726
1868
|
"removableAfter, null while that release is not published yet) and is deleted in the first minor after that — start a " +
|
|
1727
1869
|
"version that still carries a step at least once before upgrading past its deletion.",
|
|
@@ -1841,6 +1983,16 @@ function logReceiptRows(log, backend, r) {
|
|
|
1841
1983
|
for (const bucket of r.importedLayerRootBucketsSkipped) {
|
|
1842
1984
|
log.warn("imported_layer_root_bucket_skipped", { backend, ownerKey: bucket.ownerKey, reason: bucket.reason, why: bucket.why, note: importedLayerRootSkipNote(bucket.reason) });
|
|
1843
1985
|
}
|
|
1986
|
+
if (r.importedLayerRootBucketsUnnamed > 0) {
|
|
1987
|
+
log.warn("imported_layer_root_buckets_unnamed", {
|
|
1988
|
+
backend,
|
|
1989
|
+
count: r.importedLayerRootBucketsUnnamed,
|
|
1990
|
+
note: "this many permission-rule bucket files are present in the rules directory but neither the directory (the local owner's bucket, the identities its adoption marker names) " +
|
|
1991
|
+
"nor this store's import records name their owner (a bucket file is named by a one-way digest of its owner), so the one-time retirement of imported allow rules written from `/` " +
|
|
1992
|
+
"did not read them: any such rule in them stays in effect. This is a gap in the census, not a statement that they are empty. Each boot reports it again; " +
|
|
1993
|
+
"list the rules of every owner you know (`GET /v1/rules`) and remove what should not stand",
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1844
1996
|
for (const bucket of r.undurableScopeRuleBucketsUnreadable) {
|
|
1845
1997
|
log.warn("permission_rule_bucket_unreadable", {
|
|
1846
1998
|
backend,
|
|
@@ -1901,7 +2053,7 @@ function logOneTimeMigrationsInterrupted(log, backend, error) {
|
|
|
1901
2053
|
...readingFields(error.receipts),
|
|
1902
2054
|
note: "the one-time migrations failed after committing changes: the readings on this line and the lines that follow are what this boot already committed — a retried " +
|
|
1903
2055
|
"boot finds that work done and does not report it again, so these lines are the only record of them, written once by this boot — save them. The failure itself follows " +
|
|
1904
|
-
"as this boot's refusal. A shape reading absent from this line (
|
|
2056
|
+
"as this boot's refusal. A shape reading absent from this line (sessionRuleColumn / workflowJournalScopeColumn) belongs to a step that did not " +
|
|
1905
2057
|
"finish; sessionRuleColumn absent after session_rule_row_old_grammar_entries lines means the column still carries its in-progress name — the next boot rewrites the " +
|
|
1906
2058
|
"remaining rows (rows already rewritten are left alone) and publishes the final name",
|
|
1907
2059
|
});
|