From 69ea7dc81bf2a8a97ef950020171140b7b0d57e7 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:15:19 +0000 Subject: [PATCH 1/2] Propose exact original task and run SQL snapshots for #123 --- .../sql-proposal/README.md | 49 +++++++++++++++++ .../sql-proposal/original-run.sql | 15 ++++++ .../sql-proposal/original-task.sql | 29 ++++++++++ .../sql-proposal/sources.json | 53 +++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md create mode 100644 tests/fixtures/worker_startup_reconstruction/sql-proposal/original-run.sql create mode 100644 tests/fixtures/worker_startup_reconstruction/sql-proposal/original-task.sql create mode 100644 tests/fixtures/worker_startup_reconstruction/sql-proposal/sources.json diff --git a/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md b/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md new file mode 100644 index 0000000..e258ab9 --- /dev/null +++ b/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md @@ -0,0 +1,49 @@ +# Original-row SQL source proposal + +These two read-only SELECT texts are an unexecuted slice of the accepted #123 +snapshot contract. They target the historical MySQL 8.0 schema, not current +Server schema. `sources.json` binds the exact historical source evidence and +local SQL bytes. No database, API, historical package or dependency was opened, +imported, installed or acquired to prepare this slice. It is not a driver, +execution manifest, complete comparator or new operating authority. + +The task read requires the exact fixture namespace, original run and original +task. The separate run read also requires the original workflow instance. +Neither query selects a latest run, replacement task, status-filtered successor, +payload, raw error, lease owner, registration token or process metadata. Binary +comparisons prevent case-insensitive matching from substituting a different +identity. A null namespace does not become `default`: the historical Server +namespace scope uses the supplied namespace directly. + +The row cap is two to expose an unexpected duplicate, not permission to use the +first result. Zero rows means missing; two rows or any inconsistent identity, +type or field means invalid. Exact column/identity validation, driver-specific +bound parameters and explicit complete/missing/truncated/invalid records remain +unimplemented. Do not interpolate parameters or execute these texts directly +from this proposal. The field `lease_owner_present` says only that the column +is non-null. Error-present booleans also mean non-null, including an empty +string; they do not establish an error's contents, severity or cause. + +Each query returns its own database UTC sample. The future collector must bracket +each read with observer monotonic times, retain the session timezone used to +decode timestamp columns, and keep before/after-teardown reads distinct. +`UTC_TIMESTAMP(6)` does not make those views atomic or establish a shared clock. +The row cap does not bound database work or wall time, particularly with binary +casts: the future fixture-only owner must enforce the accepted five-second +snapshot budget inside the unchanged case window, cancel/close overdue reads, +and retain the ten-second overall cleanup boundary. No schema/index change, +timeout extension, lock, transaction or worker mutation is proposed here. + +Private poll/claim/lease-owner binding, registration routing/current-incarnation +and freshness evidence, API/history linkage, the snapshot collector and terminal +classification are intentionally still missing. They must take the explicit +incomplete/unknown path; these task/run projections cannot establish a committed +claim, fresh eligible registration, hydration, publication or cleanup causality. +In particular, an incremented attempt is not proof that history was delivered, +and a ready row after teardown does not prove it was never claimed. + +Next owners: Server reviews these exact source fields and MySQL texts; +sdk-python owns corrections and the later bounded collector/private comparator. +Any historical execution still requires its complete source/input/owner packet +and separate operating decision. The original untraced failures, frozen 2.15 +evidence and all #184 policy/operation holds remain separate and unchanged. diff --git a/tests/fixtures/worker_startup_reconstruction/sql-proposal/original-run.sql b/tests/fixtures/worker_startup_reconstruction/sql-proposal/original-run.sql new file mode 100644 index 0000000..14982a6 --- /dev/null +++ b/tests/fixtures/worker_startup_reconstruction/sql-proposal/original-run.sql @@ -0,0 +1,15 @@ +-- Separate from the task read: a missing task/run cannot disappear in a join. +-- This is an unexecuted proposal, not an installed comparator or driver. +SELECT + UTC_TIMESTAMP(6) AS database_time_utc, + r.id AS original_run_id, + r.workflow_instance_id, + r.namespace, + r.status, + r.execution_deadline_at, + r.run_deadline_at +FROM workflow_runs AS r +WHERE CAST(r.namespace AS BINARY) = CAST(:namespace AS BINARY) + AND CAST(r.workflow_instance_id AS BINARY) = CAST(:workflow_instance_id AS BINARY) + AND CAST(r.id AS BINARY) = CAST(:original_run_id AS BINARY) +LIMIT 2; diff --git a/tests/fixtures/worker_startup_reconstruction/sql-proposal/original-task.sql b/tests/fixtures/worker_startup_reconstruction/sql-proposal/original-task.sql new file mode 100644 index 0000000..dcf2e55 --- /dev/null +++ b/tests/fixtures/worker_startup_reconstruction/sql-proposal/original-task.sql @@ -0,0 +1,29 @@ +-- Unexecuted MySQL 8.0 source proposal; fixture namespace and original IDs only. +-- Parameter binding, query deadline and row validation are not implemented here. +SELECT + UTC_TIMESTAMP(6) AS database_time_utc, + t.id AS original_task_id, + t.workflow_run_id AS original_run_id, + t.namespace, + t.task_type, + t.status, + t.queue, + t.compatibility, + t.available_at, + t.leased_at, + t.lease_expires_at, + t.lease_owner IS NOT NULL AS lease_owner_present, + t.attempt_count, + t.repair_count, + t.last_dispatch_attempt_at, + t.last_dispatched_at, + t.last_claim_failed_at, + t.repair_available_at, + t.last_dispatch_error IS NOT NULL AS dispatch_error_present, + t.last_claim_error IS NOT NULL AS claim_error_present, + t.last_error IS NOT NULL AS task_error_present +FROM workflow_tasks AS t +WHERE CAST(t.namespace AS BINARY) = CAST(:namespace AS BINARY) + AND CAST(t.workflow_run_id AS BINARY) = CAST(:original_run_id AS BINARY) + AND CAST(t.id AS BINARY) = CAST(:original_task_id AS BINARY) +LIMIT 2; diff --git a/tests/fixtures/worker_startup_reconstruction/sql-proposal/sources.json b/tests/fixtures/worker_startup_reconstruction/sql-proposal/sources.json new file mode 100644 index 0000000..2e8d35a --- /dev/null +++ b/tests/fixtures/worker_startup_reconstruction/sql-proposal/sources.json @@ -0,0 +1,53 @@ +{ + "accepted_contract": "https://github.com/durable-workflow/sdk-python/blob/78ff7a1857961f92e4b9ac277b83ca60ca53e3b7/tests/fixtures/worker_startup_reconstruction/proposal.json", + "actual_queries_executed": 0, + "collector_source_selected": null, + "database_dialect": "MySQL8.0, proposed only; no SQL engine/parser execution", + "execution_admitted": false, + "historical_schema_sources": [ + { + "blob": "e9000f7476a7e5fea8c09be3107b67adc595e140", + "bytes": 2593, + "commit": "19a7a1cf33604c66f1f838e8b3db22563041f6b2", + "path": "src/migrations/2026_04_05_000103_create_workflow_tasks_table.php", + "repository": "durable-workflow/workflow", + "sha256": "273882a0a89fa55a84fa0a8499ed144d166806e3715805eea319c17c45735b92" + }, + { + "blob": "bddd04060a5671d07652500424202fdd054838f4", + "bytes": 3026, + "commit": "19a7a1cf33604c66f1f838e8b3db22563041f6b2", + "path": "src/migrations/2026_04_05_000101_create_workflow_runs_table.php", + "repository": "durable-workflow/workflow", + "sha256": "a747f47bcf164adfb4853e81347ed08aecbc790e2a696c9bd4142cac968aec96" + }, + { + "blob": "3716ab6ac6bde6e8a2f58eb8ced707e1afcb4f5b", + "bytes": 3754, + "commit": "8709feb28f1b85853de1d9ad15152d961860926f", + "path": "app/Support/NamespaceWorkflowScope.php", + "repository": "durable-workflow/server", + "sha256": "3a093652cc41ed4ecb4d8454c38ef1abb1086159698e77a685a55e89cc0f6b84" + } + ], + "issue": "https://github.com/durable-workflow/sdk-python/issues/123", + "parameter_driver_selected": null, + "private_binding_comparator_selected": null, + "registration_comparator_selected": null, + "server_comparator_review": "https://github.com/durable-workflow/sdk-python/issues/123#issuecomment-6646942397", + "source_owner": "sdk-python", + "source_review_owner": "server", + "sql_files": [ + { + "bytes": 585, + "file": "original-run.sql", + "sha256": "46f291d0cd698f693845aa999139342fb2b0e314b7eaf5aa1cd68231fe40382f" + }, + { + "bytes": 1047, + "file": "original-task.sql", + "sha256": "41284331ea4ad06904cbe4312afc9aec5eff789e560e7910c50fe48397202725" + } + ], + "status": "unexecuted-source-proposal-not-a-complete-comparator" +} From 30f249c79468f2acfd394ec20142ed02f16ff7a8 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 5 Nov 2026 19:21:42 +0000 Subject: [PATCH 2/2] Clarify missing database clock sample for absent original rows --- .../worker_startup_reconstruction/sql-proposal/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md b/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md index e258ab9..f715513 100644 --- a/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md +++ b/tests/fixtures/worker_startup_reconstruction/sql-proposal/README.md @@ -24,8 +24,10 @@ from this proposal. The field `lease_owner_present` says only that the column is non-null. Error-present booleans also mean non-null, including an empty string; they do not establish an error's contents, severity or cause. -Each query returns its own database UTC sample. The future collector must bracket -each read with observer monotonic times, retain the session timezone used to +Each returned row includes that query's database UTC sample. Zero rows also +means no clock sample from that query: mark it missing rather than borrowing the +other query's timestamp. The future collector must bracket each read with +observer monotonic times, retain the session timezone used to decode timestamp columns, and keep before/after-teardown reads distinct. `UTC_TIMESTAMP(6)` does not make those views atomic or establish a shared clock. The row cap does not bound database work or wall time, particularly with binary