You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Split out of #41, which is closed in favour of this. The decision behind it was taken during that triage: this project carries no backwards compatibility below 1.0.
What this removes
ADR-0015's penultimate Consequences bullet enumerates a back-compatibility cluster and states that it must retire as one piece — removing any part of it ahead of the others would leave a site mid-upgrade with an unrecoverable build. The members it names:
Sweeper::reclaimable()'s carve-out for the stranded stall
Extraction_Job::is_pre_adaptation_stall()
Dispatcher::resume_failed(), the resume path that reads it, and is_resumable() with it
Sealed_Writer::seed_index() and its call in Artifact_Builder::advance()
Build_Progress::$legacy_names
every from_array() branch whose absent key is exclusively a signal that a record predates 0.6.0 — the schema-8 budget keys, the schema-6 segment_names/table_offset/table_cursor triad, the schema-6 attempts/error pair, and the schema-3/4/5 fields older still
ADR-0015 is equally explicit about what is not in the cluster, and that boundary is load-bearing here. A few other tolerant from_array() branches read an absent key as the ordinary not-yet-populated shape of a brand-new current-schema record as much as a legacy one: progress, progressed_at, the schema-8 additions (host_memory_limit and its three siblings), skipped_files, and attempt_log. Those stay.
Why this is possible now
ADR-0015 states the retirement condition — that no un-resumed pre-adaptation stall remains on any site still running this plugin — and then says plainly that nothing the plugin can observe establishes it, "because it ships to sites the author does not operate."
That premise no longer holds. The plugin is installed on exactly one site, operated by the author, and has been distributed to nobody else. The condition is settled by decision rather than by observation, which is the outcome ADR-0015 anticipated but could not reach.
The same bullet warns that an unimplemented recommendation sitting in a Consequences list is the shape that quietly becomes permanent. This ticket is the alternative to that.
Agent Brief
Category: enhancement Summary: Remove the pre-adaptation back-compatibility cluster in one piece, so a failed extraction record is terminal for every purpose and no record is exempt from the TTL sweep.
Current behavior:
A record written by 0.5.1 or earlier — failed, carrying a diagnosis, and missing the budget keys this release always writes — is treated as a resumable stall. The TTL sweep spares it unconditionally, the Dispatcher may re-drive it into running, the builder reseeds a sealed index from segment names a schema-6 record carried, and record deserialisation tolerates a family of absent keys that only such a record has.
Two consequences follow, and the second is a defect. A failed job is not strictly terminal. And because the sweep's carve-out is wider than the resume it protects, a legacy record stalled where nothing can shrink — a structure-only table, or the sealed index — is spared forever and re-driven by nobody (#41).
Desired behavior:
No record is exempt from the sweep. failed is terminal for every purpose, in both directions: it frees the concurrency slot, and nothing re-enters it into running. The sweep applies its ordinary TTL and absolute-lifetime windows to every failed record alike. Deserialisation understands the current schema and no earlier one.
A record the plugin can no longer parse must degrade quietly — skipped, with the enumeration continuing — never thrown. A stray legacy file left on a live site must not be able to break the listing or the sweep for the jobs around it. This is the one behaviour that must be got right, because it is what makes the removal safe to ship to a site that may still hold such a file.
Key interfaces:
Sweeper::reclaimable() — becomes a question about terminality alone; a failed record is reclaimable like any other
Dispatcher — loses the resume path and the predicates serving it; the tick and watchdog no longer consider a failed job
Extraction_Job::from_array() — parses the current schema; branches that exist only to read a pre-0.6.0 write are gone, and the ones ADR-0015 excludes from the cluster remain
Extraction_Job::$budget_keys_present — with no record lacking the keys, judge whether the property still earns its place, and remove it if not. to_array()'s use of it to omit the keys again goes with it
Build_Progress — loses its legacy-name tolerance and the segment_names branch that feeds it
Sealed_Writer::seed_index() — removed, along with its call site and the branch selecting it
Acceptance criteria:
Nothing in the codebase references is_pre_adaptation_stall(), resume_failed(), seed_index(), or $legacy_names; the symbols are gone rather than merely unused
The sweep reclaims a failed record on the same TTL and lifetime windows as any unfinished job, with no exemption of any kind
A failed job is never re-entered into running by a tick, the watchdog, or any other path
Deserialising a record that predates the current schema does not throw; the record is skipped and enumeration of the records around it completes
The integration suite's assertions covering the pre-adaptation shape, the resume, and the adapt-after-stall path are removed or rewritten to the new behaviour, and the suite passes — the three files touching this are consume-cancel-ttl-test.php, resume-and-adapt-test.php and opaque-failure-test.php
composer gate passes
ADR-0015 carries a dated addendum recording that the exemption was retired by decision rather than by the observation it said it could not make, and that the ADR's own re-drive half no longer describes the code. Keep its existing text verbatim with a bolded pointer, per the convention ADR-0013 established
CHANGELOG.md's [Unreleased] records the change, including that a failed record is now reclaimed on the ordinary windows
CONTEXT.md no longer defines terms that name only the removed path
Out of scope:
Any migration, cleanup routine, or one-shot path that hunts down legacy records. There is none, deliberately. The sweep reclaims what it finds on its ordinary windows, which is the whole disposal story.
The adaptation machinery itself — budget halving, the attempt counter, the floor, the host-limit raise, the attempt log. Only the pre-adaptation legacy path retires. A stall this release diagnoses still shrinks its chunk and retries.
API_VERSION and SCHEMA_VERSION. Nothing here changes the sealed container's byte format or the REST contract; the record shape a current release writes is unchanged.
Split out of #41, which is closed in favour of this. The decision behind it was taken during that triage: this project carries no backwards compatibility below 1.0.
What this removes
ADR-0015's penultimate Consequences bullet enumerates a back-compatibility cluster and states that it must retire as one piece — removing any part of it ahead of the others would leave a site mid-upgrade with an unrecoverable build. The members it names:
Sweeper::reclaimable()'s carve-out for the stranded stallExtraction_Job::is_pre_adaptation_stall()Dispatcher::resume_failed(), the resume path that reads it, andis_resumable()with itSealed_Writer::seed_index()and its call inArtifact_Builder::advance()Build_Progress::$legacy_namesfrom_array()branch whose absent key is exclusively a signal that a record predates 0.6.0 — the schema-8 budget keys, the schema-6segment_names/table_offset/table_cursortriad, the schema-6attempts/errorpair, and the schema-3/4/5 fields older stillADR-0015 is equally explicit about what is not in the cluster, and that boundary is load-bearing here. A few other tolerant
from_array()branches read an absent key as the ordinary not-yet-populated shape of a brand-new current-schema record as much as a legacy one:progress,progressed_at, the schema-8 additions (host_memory_limitand its three siblings),skipped_files, andattempt_log. Those stay.Why this is possible now
ADR-0015 states the retirement condition — that no un-resumed pre-adaptation stall remains on any site still running this plugin — and then says plainly that nothing the plugin can observe establishes it, "because it ships to sites the author does not operate."
That premise no longer holds. The plugin is installed on exactly one site, operated by the author, and has been distributed to nobody else. The condition is settled by decision rather than by observation, which is the outcome ADR-0015 anticipated but could not reach.
The same bullet warns that an unimplemented recommendation sitting in a Consequences list is the shape that quietly becomes permanent. This ticket is the alternative to that.
Agent Brief
Category: enhancement
Summary: Remove the pre-adaptation back-compatibility cluster in one piece, so a failed extraction record is terminal for every purpose and no record is exempt from the TTL sweep.
Current behavior:
A record written by 0.5.1 or earlier — failed, carrying a diagnosis, and missing the budget keys this release always writes — is treated as a resumable stall. The TTL sweep spares it unconditionally, the Dispatcher may re-drive it into
running, the builder reseeds a sealed index from segment names a schema-6 record carried, and record deserialisation tolerates a family of absent keys that only such a record has.Two consequences follow, and the second is a defect. A failed job is not strictly terminal. And because the sweep's carve-out is wider than the resume it protects, a legacy record stalled where nothing can shrink — a structure-only table, or the sealed index — is spared forever and re-driven by nobody (#41).
Desired behavior:
No record is exempt from the sweep.
failedis terminal for every purpose, in both directions: it frees the concurrency slot, and nothing re-enters it intorunning. The sweep applies its ordinary TTL and absolute-lifetime windows to every failed record alike. Deserialisation understands the current schema and no earlier one.A record the plugin can no longer parse must degrade quietly — skipped, with the enumeration continuing — never thrown. A stray legacy file left on a live site must not be able to break the listing or the sweep for the jobs around it. This is the one behaviour that must be got right, because it is what makes the removal safe to ship to a site that may still hold such a file.
Key interfaces:
Sweeper::reclaimable()— becomes a question about terminality alone; a failed record is reclaimable like any otherDispatcher— loses the resume path and the predicates serving it; the tick and watchdog no longer consider a failed jobExtraction_Job::from_array()— parses the current schema; branches that exist only to read a pre-0.6.0 write are gone, and the ones ADR-0015 excludes from the cluster remainExtraction_Job::$budget_keys_present— with no record lacking the keys, judge whether the property still earns its place, and remove it if not.to_array()'s use of it to omit the keys again goes with itBuild_Progress— loses its legacy-name tolerance and thesegment_namesbranch that feeds itSealed_Writer::seed_index()— removed, along with its call site and the branch selecting itAcceptance criteria:
is_pre_adaptation_stall(),resume_failed(),seed_index(), or$legacy_names; the symbols are gone rather than merely unusedrunningby a tick, the watchdog, or any other pathconsume-cancel-ttl-test.php,resume-and-adapt-test.phpandopaque-failure-test.phpcomposer gatepassesCHANGELOG.md's[Unreleased]records the change, including that a failed record is now reclaimed on the ordinary windowsCONTEXT.mdno longer defines terms that name only the removed pathOut of scope:
API_VERSIONandSCHEMA_VERSION. Nothing here changes the sealed container's byte format or the REST contract; the record shape a current release writes is unchanged.erroris null and the poll reports a fallback sentence #25 and ADR-0022.thrownis a current-schema field and stays.