Repository navigation
Changed Python workflow definitions silently strand in-flight runs #518
Copy link
Copy link
Closed
Labels
bugSomething isn't workingSomething isn't workingkind:defectA public product behavior is incorrectA public product behavior is incorrectpriority:P1High-priority product or release riskHigh-priority product or release risk
Description
Problem
A Python worker redeployed with any change to a workflow class cannot continue runs that started before the change, and nothing tells the operator why.
- Python workers advertise a definition fingerprint that hashes the class source. Any edit changes it, including adding a
ctx.get_versionbranch, which the Python guide presents as the way to version workflow code safely. - The Server records that fingerprint at start, then skips the run's workflow tasks for any worker advertising a different one.
- Rolling upgrades and the config reference say
DW_V2_PIN_TO_RECORDED_FINGERPRINT(defaulttrue) controls this pinning. The Server's claim check never reads it.
Reproduction
Published durableworkflow/server:2.9.7 via docker-compose.published.yml, PyPI durable-workflow==2.6.1, no build id:
- Worker v1 starts a run that waits on a condition. WorkflowStarted records v1's fingerprint with source
worker. - Stop v1. Start v2, whose only change is
yield ctx.get_version("change", -1, 1)after the wait. Send the signal. - The run stays
waiting, with one ready, unleased workflow task, for 90 s. v2's polls return empty, and nothing is logged. - Restarting the Server with
DW_V2_PIN_TO_RECORDED_FINGERPRINT=false(confirmed in config) changes nothing. - Restarting v1 claims the task within a second and completes the run. Fresh v2 runs complete normally.
While the run was stalled, the operator surfaces said nothing was wrong:
- Describe showed
compatibility_status: compatiblewith no recovery kind. POST /workflows/{id}/repairreturnedrepair_not_needed.- Task queue repair reported
needs_attention: false. - The only hint was
workflow_definition_fingerprint_conflictson the build-ids view, and it appeared only while the stale v1 registration was still counted.
Acceptance criteria
- With no active worker advertising a run's recorded fingerprint, run detail, describe, task queue diagnostics and repair all report a specific blocked reason that names the workflow type and the missing fingerprint, instead of
compatibleorrepair_not_needed. DW_V2_PIN_TO_RECORDED_FINGERPRINT=falselets a worker with the same type and compatibility claim those runs, as the docs already describe.- The Python versioning path works for in-flight runs: a worker whose only change adds a
get_versionbranch continues runs started under the previous definition, with the default setting. Requiring old workers to keep running until those runs finish is not an acceptable resolution. - A regression test against published artifacts covers this scenario.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingkind:defectA public product behavior is incorrectA public product behavior is incorrectpriority:P1High-priority product or release riskHigh-priority product or release risk
Activity
rmcdaniel commentedon Oct 20, 2026
Independent published-artifact reproduction
Confirmed on the published Server 2.9.7 amd64 image
sha256:40e969d0b6b240b835dcdb2fc531507266216ec34a83f59f561674f41e35f461(Server source150f3fbcbf3911a2a20abb53b4a714081ee53dd7, bundled Core 2.7.2 /64efa176e4c37e5d6ae80e767ba3b41464024e53) and normally pip-installed Python SDK 2.6.1. All 46 hashed SDK distribution files match installed RECORD entries. No product source was edited.The API started with
DW_V2_PIN_TO_RECORDED_FINGERPRINT=false; a bootstrapped read of its effective cached configuration returned booleanfalsebefore workflow admission.Actual original and replacement
default, queuegeneral518, workflow typegeneral518.wait, module/classdefinition.WaitingWorkflow, no build id, original compatibilitynull.general518-original, run01m5c69qpq7b1n04pxd3a2sv61, execution budget 180 seconds and run budget 120 seconds, fixed at original admission. Each registration/terminal observer has a 30-second maximum; the actual replacement observation below lasted 25.02 seconds.deregistered, and exited 0. The old registration was verified absent before the replacement started.yield ctx.get_version("change", -1, 1)immediately after the unchanged condition wait. Module/class, workflow/signal contract, queue, namespace and build/compatibility selection are unchanged.sha256:bffe2533baf27d16a79ff01f19bf58a7cf27f4a71c14c1b15946d1daccc4a8a4.sha256:a9078a4e8ddea5afd30b93352c7680ac448d3d96523f7e9bcba71761f87b8e0c.WorkflowStartedrecords the old fingerprint with sourceworker. The new worker registered HTTP 201. Across its lifetime, 22 observed workflow polls returned HTTP 200 /poll_status=empty/ no task. The run remained stalled for the 25.02-second observation after the realreadysignal; the worker made no workflow claim. The poll count is for the whole replacement process, not a separately filtered post-signal count.waiting, with exactly one ready workflow task: no lease owner/timestamps, attempt 0, repair 0, no recorded claim error. Only the new registration was present.The first four committed history rows remained byte-identical through the stall. The diagnostic repair request below appended its expected
RepairRequestedaudit event; it did not advance business execution. The original namespace/queue/compatibility, run id and stored deadlines remained exact, including UTC microseconds:2026-10-20 08:22:28.0868152026-10-20 08:21:28.086815Diagnostics and old-code control
compatibility_status=compatible,recovery_kind=null, waiting on the condition.repair_not_needed.workflow_definition_fingerprint_conflicts=[]after the old worker was removed.repair.needs_attention=trueat this observation. It indicates generic overdue task attention, without identifying the missing recorded fingerprint. This reproduction does not support claiming that field was false.The new worker stopped/deregistered and exited 0. A separate fresh old-code process then claimed the same original task and completed the same run in 2.09 seconds. Normal SDK decoding returned the exact signed-int64/null/Unicode result. Both actual task completion submissions returned HTTP 200 /
completed/recorded=true; the replacement submitted no workflow completion. The final pre-shutdown read-only SQL capture confirms one completed run, two completed first-attempt workflow tasks, nine events, zero repairs and zero remaining registrations. Original history prefix and both original deadlines are unchanged. The API exited 0/no OOM/no restart on explicit SIGTERM.Correction retained: the first post-stop reader could not open the WAL-mode database through the read-only mount; that observer is unsuccessful. A separate corrected stopped reader then verified the same facts using
mode=ro&immutable=1, only after the clean API exit and explicit confirmation that no WAL or SHM existed. Its database SHA-256 stayeda224e72c564c8304625a19babec87a7f21bc0ece6fb33bd0b0349ac1a4536fc9before/after inspection. The original four-row prefix and both UTC6 deadlines also match in that stopped read.Source triage and correction boundary
At the published Server source,
WorkflowTaskPollercallsmatchesRecordedWorkflowDefinition()around line 1043. Its worker-sourced fingerprint equality check around lines 2359–2386 does not consult the pinning configuration. Core'sWorkflowDefinitionFingerprint::resolveClassForRun()does read that configuration, but it is the embedded class resolver, not this service-worker claim path. Python SDK 2.6.1 hashes the actual class source, so this one-line version-branch addition changes the advertised fingerprint as expected.Default strict fingerprint fencing is an existing documented policy, not itself disproved by this test. The confirmed defects are the ineffective documented opt-out on this path and incomplete operator explanations for the resulting blocked work. No earlier-version counterfactual establishes which release introduced them.
The smallest useful correction to review is to make service claim eligibility and its operator diagnostics use the same fingerprint decision: preserve default strict fencing, honor the existing explicit opt-out without bypassing namespace/queue/type/build/compatibility or lease/capability guards, and report the missing recorded fingerprint when fencing is active. That proposal is not a safety qualification; it still requires contract review and regression coverage for original histories,
get_versionreplay, false/true configurations and all affected diagnostics before adoption.Preserved setup/observer limits
An initial preadmission fixture mistakenly used a nonexistent
@workflow.rundecorator and never registered a worker or created a workflow. A config-reader command initially omitted the autoloader. Both setup errors are corrected only in the later separate passing reads/original. An initial post-read auditor incorrectly expected no additional history despite intentionally issuing repair; the accurate assertion is an unchanged four-event prefix plus the one expected repair audit event. No product outcome was relabelled or repaired by those observer corrections.SQLite/source reproduction only: no MySQL/PostgreSQL, default-true runtime comparison, edited Server binary, native rewrite, or proposed-fix acceptance is claimed. Raw task data, SDK dependencies and all task resources are being retired immediately; the borrowed image is preserved for its owner.
rmcdaniel commentedon Oct 20, 2026
Actual cleanup verified at 08:24:31 UTC, before the 09:00 bound: all task application/worker containers, private network, SQLite data, installed SDK/dependencies, caches, fixtures and raw captures are removed (36 MiB). The borrowed published image remains intact for its owner. The exact published-artifact evidence and preserved setup/observer limits are in comment 6286711779. No product source was edited or proposed fix adopted.
rmcdaniel commentedon Oct 20, 2026
Accepted as a P1 product correction. The independent published reproduction in comment 6286711779 confirms the ineffective opt-out and missing routing explanation, but its proposed opt-out-only correction is not the complete acceptance contract.
The fix must also support a replay-compatible Python code upgrade that adds
get_versionfor an already-started run under the default configuration. Keeping old workers indefinitely or rewriting the guide is not the resolution. Default strict selection, compatible evolution and explicit opt-out need a coherent routing policy, with original recorded fingerprints/history left intact and real incompatible replay still refused.Next implementation work is the PHP matcher/diagnostics contract plus affected SDK replay compatibility controls: prove the unchanged-source failures first, retain namespace/queue/type/build/compatibility/capability and lease fences, then qualify normally installed old/new workers against the exact candidate and published artifacts. Coordination is on Server #325; no native/shared/frozen adoption or claim of a shipped fix follows from this triage.
rmcdaniel commentedon Oct 20, 2026
PHP correction is in draft #525 at
57de9ec39b4107f32d2a7bcb6b070a4158cf3dc1, whole tree7f734fb91d81e5ba39579f21145eb9cbaab0fce5.The default now prefers a fresh eligible recorded-definition peer, then lets a replacement undergo strict replay when no eligible matching peer remains. The published opt-out removes that preference. Build pins, namespace/queue/type, capability and actual wire-version eligibility, registration identity and claim/lease fences remain. Original history, fingerprint and deadlines are not rewritten. Run detail/debug, queue detail and repair expose the same routing policy; repair preserves its stored command decision.
Qualification:
e03bd5fc3: 189 focused and surrounding PHP tests / 2,155 assertions pass. Final canonical selected/current-run enrichment correction: 95 focused tests / 1,279 assertions pass.57de9ec39, normally installed Python SDK 2.6.1 (33 Python distribution files independently match RECORD): default replacement completes the original after an actualget_versionedit; a live gated matching peer keeps preference and completes when released; effective pinning=false permits replacement despite that peer; an incompatible condition key is leased but reportsNonDeterministicReplayError/condition_key_changed, with zero business completion.Earlier probe/setup and audit mistakes remain unsuccessful, including the first observer's incorrect history URL. That preliminary original is separate and retains one stale setup registration; final-cohort registrations are zero. No rows were deleted to manufacture an audit pass. The development PHP API launcher needed forced shutdown (137/no OOM), so graceful API shutdown is not qualified here.
Normal CI and Sonar for the exact new head are queued. No merge, release, installed Server image or native/shared/frozen adoption claim. The original published-image failure remains failed. The next gate is normal exact-head CI/Sonar, followed by publication and genuine installed-image acceptance. Native/shared/runner/OpenAPI/Composer/version paths remain unchanged.
rmcdaniel commentedon Oct 20, 2026
Source follow-through is now
e692df2196a3e973903e10ea2fec22487ba1707b/ wholeb4280b968732e0a84f0599033525845c2c040ea3. It integrates current main1638681911a473b86484a1782b4654bab80ed209without changing its native benchmark additions. Fresh normal gates are qualifying this combined tree; no prior whole-tree pass transfers.The four actionable maintainability findings on prior
57de9ec3were addressed in source, without threshold changes or suppression. A new actual HTTP regression also preserves the existing distinction between omitted client metrics and explicitprocess_metrics: null: omission retains them, explicit null clears them, and neither can overwrite the authenticated server-owned worker protocol. The old implementation failed that null regression; final routing/pinning/debug/queue/control-plane selection passes 114 tests / 1,746 assertions. Fresh analyzer resolution remains pending.There is now a separate release-safety dependency: shared221 tracks source-verified service SDK entry-binding errors that can become terminal workflow failures before application versioning code runs. Core866 independently reproduces the embedded counterpart, including silently substituted DTO input. Current518 evidence proves command-history incompatibility refusal, not safe handling of every entry-signature/input-schema change. I will qualify that SDK boundary before publishing518's routing fallback; keeping old workers forever is not the product resolution. The independent PgBouncer524 fix can proceed through its own exact source/image gates.
No source-hash policy can prove arbitrary application code compatible. Original run/input/history/claim/deadline authority and the real SDK refusal decision remain required. This is an owned implementation/qualification dependency, with no operator action needed.
rmcdaniel commentedon Oct 20, 2026
Python's counterpart is now independently reproduced with normally installedSDK2.6.1, whose replayer/Worker/errors bytes match published150c6f6b. Against candidate-qualified Server525 e692, adding a required entry argument terminally fails a genuine original both before first execution and after a committed condition wait. Genuine body-error and compatible optional-entry controls remain distinct. These are source-Server/published-SDK observations, not a released Server fallback qualification.
Draft SDK Python170 now holds correction
6dcd59280af4fff4b7764f6f1b0f4f22a3f8693b/ whole297d2fc623dec961ee266b4961d975f4ceee3c69. It pre-binds the call outside application failure handling and uses the existing typed task refusal with reasonworkflow_entry_arguments_mismatch. Python annotations remain annotations. Final style/strict typing and674 focused tests pass, two explicit skips. The retained official fixture fails unchanged source by observing the unsafe terminal command.Two final actual source originals expose replay-blocked API diagnostics and recover through supported run-scoped repair with compatible code, same input/history prefix/microsecond deadlines and one completion each. An earlier source component separately covers genuine body failures and optional arguments. Across12 originals the stopped read-only audit verifies17 tasks/65 events/four repairs/zero registrations/28 Worker exits0; six cold readers preserve typed results. Failed published originals and the broad local failed qualification remain explicit in the PR. No full hosted or published fixed-SDK claim.
PHP/Rust reproduction and correction remain next, together with published artifact/UI acceptance. Server518's release-safety dependency is still open; no human action is needed. No native/shared/frozen adoption or competing Server implementation is requested.
rmcdaniel commentedon Oct 20, 2026
Problem
Service-SDK counterpart of durable-workflow/.github#221 and Workflow#866. Normally installed PHP SDK 2.5.3 reproduces terminal failure when a redeployment adds a required entry argument before the first task or after a committed condition wait, and when the recorded integer is no longer accepted by a string parameter. A genuine body TypeError still fails, and a compatible optional addition completes with exact typed output.
The isolated target is the already candidate-qualified PHP Server525 source, not a published routing fix or native runtime. The five published-SDK business originals and the two unsuccessful fixture setup attempts remain separate.
Source correction
Candidate
aa53d791cc93c81b13e51ff8f12f6a321b07bfce, whole treed7afc1017cd2f9a0ca7b3d2c10a6d8d192a63b60, validates the real discovered callable's entry inputs before entering application code. It reports a typed nonterminal workflow-task replay refusal with safe binding metadata, preserving claim/input/history/deadlines. Genuine body failures retain business failure semantics, including a body that deliberately throws the new internal exception. PHP's compatible optional, nullable, union, variadic, integer-to-float and trailing-argument rules remain explicit controls. The first variadic context parameter is also checked against recorded input.Verification and remaining delivery
802072bchas nine actual failures and seven passing controls. Its first mock omitted a later activity poll; that unsuccessful fixture attempt remains distinct from the corrected counterfactual.expected.type does not match, rather than passing because the test runner cannot load./worker/registerroute before any business admission. Neither fixture mistake is called a product defect.Exact normal CI/Sonar/policy, source merge/fresh main, stable publication and installed artifact/API/Waterline acceptance remain pending. No SDK major, protocol floor, Server runtime, dependency or frozen actor change. The independently reproduced Python counterpart remains SDK Python #170; Rust's typed-input adapter is still open in .github#221. Server#518 release remains held until the relevant client safety is qualified.
rmcdaniel commentedon Oct 20, 2026
Rust entry-safety: genuine published-backend counterfactual and remaining runtime boundary
Source: SDK Rust draft #106, exact
ecd2a7155a99a16ada0ff40d05b74fbf028e083f. This compatible refinement preserves the existing publicError::HandlerTypevariant shape; its five focused controls and sixteen official replay fixtures pass. The preceding implementation passed all 562 library tests. Normal final-head CI is still required.Three genuine originals ran against immutable Server 2.9.8 / Core 2.7.2, amd64 child
76c5b6b7cc78aeb02dd1ff9f87016662991c11f9af7dba4aaae7748bc87a1ad1, PostgreSQL 16.15. The published counterfactual uses normally resolved crates.io 3.5.5. Both consumers use byte-identical application source and identical recorded workflow fingerprint; a controlled custom input deserializer changes its schema requirement before handler entry. This qualifies entry-error classification, not the separate changed-fingerprint routing fix in Server #518.rust221-published-before/01m5cvwxm0f4satz58nwb1qhmv: an incompatible input terminally fails the business run before any body or activity invocation. Exact history is StartAccepted, WorkflowStarted, WorkflowFailed.rust221-published-cold/01m5cvwzhh8qjjnb6k4gvh24m3: one genuine activity completes first; the incompatible cold worker invokes neither body nor activity. The run remains waiting and the failed continuation is replay-blocked. Five committed events survive. This differs from the pre-history terminal failure.rust221-source-before/01m5cvx2hahs8bjtpjdcyk6amz: zero body/activity invocations; the SDK submits task failure with reasonworkflow_entry_arguments_mismatchand typeWorkflowEntryArgumentsMismatch. The business run remains pending with its two original events. Published Server 2.9.8 does not replay-block this reason: it persists a failed task and creates a ready retry task. Only one incompatible attempt ran; an endless retry loop was not reproduced. Source inspection confirms the held Allow replay-compatible workflow definition upgrades and explain routing #525 classifier also needs the new structured reason before its release.Stopped SELECT-only SQL confirms the three original inputs are byte-identical; original start+30-second run and start+60-second execution deadlines retain exact stored precision. The source original starts
2026-10-20T14:37:01.866168, with deadlines14:37:31.866168and14:38:01.866168. No deadline was extended. Pending/waiting are the observed stopped states after wall-clock expiry; no later timeout transition is inferred.The aggregate recovery recipe is unsuccessful: its repair request omitted the required control-plane-version header and received canonical HTTP 400. Recovery and the later source-cold/body cases were therefore unexecuted. A first audit used a nonexistent registration table and failed; a separate corrected read-only run/event/task audit completed. No zero-registration claim follows from that audit. The API required stop 137/no OOM; PostgreSQL exited 0/no OOM. No graceful API-shutdown claim.
All owned runtime, database, compiler/dependency/target/formatter, downloaded image and raw evidence resources were removed at 14:43:40 UTC, before the 14:45 cleanup checkpoint. Only clean pushed product source, a small reproduction recipe and this bounded receipt remain. Borrowed and other-agent resources are preserved.
Next acceptance gate is coordinated SDK + Server handling: explicitly replay-block this typed pre-entry failure, preserve the original task/history/input/deadlines, and verify supported compatible recovery on a fresh original. SDK publication must not imply that current Server 2.9.8 supplies that missing fence. Existing failed originals remain failed; no runtime, native/shared/frozen change is inferred or requested here.
rmcdaniel commentedon Oct 20, 2026
The current-source Server regression now independently reproduces the missing entry-refusal fence for all three SDK failure types: each creates an unwanted retry task. Explicitly recognizing the structured
workflow_entry_arguments_mismatchreason makes the full 24-case worker-evolution selection pass, including original-task supported repair and unchanged admission/deadline bytes. Ordinary application failures keep their existing semantics.Review exposed a second release requirement: default changed-fingerprint fallback must not silently route an original to an older SDK that can still terminally fail it during entry binding. The coordinated fix will use an additive worker capability,
workflow_entry_arguments_validation, advertised only by the corrected first-party worker implementations. Matching recorded definitions retain their existing routing, as do the explicit pinning opt-out semantics. Default replacement fallback requires this safety declaration; diagnostics must identify a missing declaration and the worker upgrade action. A raw caller's registration remains its declaration of implementation, not independent proof of safe code.This adds no protocol floor and does not rewrite a stored fingerprint, build pin, namespace/queue/type, input, history or deadline. Existing/native/frozen workers do not acquire the capability by assumption. SDK source, runtime, normal CI and fresh genuine artifact acceptance remain separately required before release or issue closure.
rmcdaniel commentedon Oct 20, 2026
Published Rust entry-safety acceptance
The protected 3.5.6 publisher 39469465008 passes. Independent crates.io sparse-index and downloaded-archive verification match checksum
3e933f0d775c25c2002f154829b31232089dc65f8f9d4c43e893128ed06a5215and embedded Git source01be5a0bb175a5371f69e1cf9d6104448d94eb39.A fresh normal Cargo registry installation (no SDK path/source overlay) passes three actual SQLite originals against immutable Server 2.9.9, index
sha256:d45d197e9556292ec096531030717e43d69ea4ea9ab197170971492f14cc0869, amd64 image/config040a104ad6614dd5eca59d49d1a365b3d068f7bcdf0a507fdf2585c112611146, Core 2.7.2. The consumer binary isc80aef21ed80ebae748322f3b6bffb7c0bfc1abacb04790bd6003f304aded054.01m5d7we5sdpf9mn74380mjb88: incompatible decoding invokes zero body/activity callbacks and leaves the original task replay-blocked. Supported repair and compatible code complete the same run once in 3.70 seconds.01m5d7wkptfvszxbhqbbv92q88: a genuine activity commits before incompatible cold replay. The refusing worker invokes zero callbacks and preserves that complete prefix; repair and compatible replay finish the same run in 3.60 seconds without another activity invocation.01m5d7ws32j6swn6ey09psx88n: an application error remains an ordinary terminal failure, with no entry-mismatch classification.Both successful runs retain the exact original int64-maximum/null/Unicode output, input/history prefixes, and microsecond start+30-second run / start+60-second execution deadlines. Separate cold clients decode the original result. Seven business Worker processes, both supported repairs and both cold readers exit 0. Stopped, kernel-read-only SQL independently proves two completed runs, one intentional failed run; seven tasks (six completed/one intentional failure), 17 events, two deliberate repairs and zero registrations. The database SHA-256 is unchanged before/after the corrected read-only audit.
The first aggregate audit used an incorrect registration table name and failed before producing counts; its corrected separate reader uses the actual table. The first build invocation also refused a missing new consumer lockfile before compilation; normal resolution followed by the locked build succeeds. Neither setup failure is a product defect. API teardown required exit 137/no OOM; the idle consumer container exited 143/no OOM, so no graceful API shutdown is claimed.
This completes the Rust 3.5.6 installed entry-safety slice. The PHP/Python installed consumers, broader changed-definition routing, operator/Waterline diagnostics and held all-locale documentation remain separate requirements of shared #221 / Server #518. No cross-SDK, native, catalog, Cloud or stopped-qualification adoption is inferred.
rmcdaniel commentedon Oct 20, 2026
Normally installed PHP 2.5.4 acceptance
Fresh Composer installation resolves durable-workflow/sdk 2.5.4 from Packagist, exact source/dist
cd8834aea01331912e6e9854ae0174f9fb7ba8fa, with no SDK source overlay. The immutable backend is Server 2.9.9, indexsha256:d45d197e9556292ec096531030717e43d69ea4ea9ab197170971492f14cc0869, amd64 image040a104ad6614dd5eca59d49d1a365b3d068f7bcdf0a507fdf2585c112611146, locked Core 2.7.2.All six actual originals pass the existing entry-binding recipe:
01m5d8g1vcxr2qaa6hbk9ve4dx01m5d8gb33wb29r6c1kzzspvdk01m5d8gpvt0py6sry5jda1hd1401m5d8h1et9q80pq8nnzefptzn01m5d8h6nj4fbat8qkne9qkxkz01m5d8heqxvqybv7nm4zgwwtm0The three incompatible workers submit exactly one entry-mismatch refusal each and no terminal workflow command. The public per-run debug route reports replay-blocked state. Restoring compatible code and the supported run-scoped repair complete the original runs. Exact original input/history prefixes and microsecond start+30-second run / start+60-second execution deadlines survive. Four separate normally installed cold clients decode exact integer-maximum/null/Unicode results.
All 15 business Worker processes exit 0. Independent stopped SQL verifies six runs (four completed/two intentional failures), ten tasks, 41 events, three deliberate repairs, zero registrations and no extra setup original. Database SHA-256
202e18a0688c5828ed0cce61b2b78709fdd9dd625eba6f2dd5426605f8f93bf4is unchanged.The first kernel-read-only aggregate auditor cannot open this WAL-mode file without sidecars. The corrected separate auditor first refuses any existing WAL, then reads the stopped WAL-free file with SQLite's immutable/read-only option on a kernel-read-only mount, as allowed by SQLite's read-only WAL rules. No source bytes or journal mode are changed. The first audit stays unsuccessful. API teardown requires 137/no OOM; the idle client exits 143/no OOM. Graceful API shutdown remains unqualified.
This completes PHP 2.5.4's published entry-safety acceptance. Rust 3.5.6 has its separate published acceptance at shared #221. Python 2.6.2, Waterline/operator presentation, held all-locale Website #333 and PHP portal #195 follow-through remain pending; shared #221 / Server #518 remain open. No native/catalog/frozen or Cloud adoption is inferred.