diff --git a/tools/corpus_engine.py b/tools/corpus_engine.py index 9243ad57..6bf3b9e8 100755 --- a/tools/corpus_engine.py +++ b/tools/corpus_engine.py @@ -244,7 +244,7 @@ SAVE_VERSION_NUM_RE = re.compile(r"\bv(\d+)\b") SAVE_FLAVOR_RE = re.compile(r"\bsaves?\b|\bpre-v\d+\b", re.I) SAVE_MIGRATION_VERB_RE = re.compile( r"\b(?:migrat(?:e|es|ed|ing|ion|ions)|accepts|pad(?:s|ded|ding)?|" - r"loads?\s+by|persists?\s+in|carries)\b", + r"loads?\s+by|persists?\s+in|carries|collaps(?:e|es|ed|ing))\b", re.I, ) SAVE_CLAIM_HISTORY_RE = re.compile( @@ -258,6 +258,23 @@ SAVE_LIVE_MIGRATION_AUTHORITY_RE = re.compile( r"migrat(?:e|es|ed|ing|ion|ions)\b", re.I | re.S, ) +SAVE_UNVERSIONED_LIVE_MIGRATION_RE = re.compile( + r"\b(?:save\s+migration|migration)\s+" + r"(?:classif(?:y|ies|ied|ying)|rewrit(?:e|es|ten|ing)|rewrote|" + r"creat(?:e|es|ed|ing))\b|" + r"\b(?:pre-amendment|legacy|old(?:er)?|prior)\b[^.;!?]{0,180}" + r"\bmigrat(?:e|es|ed|ing)\b", + re.I | re.S, +) +SAVE_MIGRATION_NEGATION_RE = re.compile( + r"\bno\s+(?:save\s+)?migration\b|" + r"\b(?:save\s+)?migration\s+(?:does\s+not|never|cannot|no\s+longer)\b|" + r"\bno\b[^.;!?]{0,120}\b(?:pre-amendment|legacy|old(?:er)?|prior)\b" + r"[^.;!?]{0,120}\bmigrat(?:e|es|ed|ing)\b|" + r"\b(?:pre-amendment|legacy|old(?:er)?|prior)\b[^.;!?]{0,120}" + r"\b(?:does\s+not|never|cannot|no\s+longer)\s+migrat(?:e|es|ed|ing)\b", + re.I | re.S, +) SAVE_CLAIM_UNIT_BOUNDARY_RE = re.compile( r"\n[ \t]*\n|(?=^[ \t]*(?:[-*+]|\d+[.)])[ \t]+)", re.M ) @@ -929,6 +946,11 @@ class Engine: rejects generic claims that save.rs currently owns what each version migrates: that wording escaped the numbered-version check after the retired authority class had already recurred across current pages. + Implemented specs also fail when an unnumbered old/pre-amendment + holding or a generic ``save migration`` is assigned classification, + rewrite, or creation behavior. History context must live in that + statement; a trailing retired-fixture note cannot make a preceding + live acceptance statement true. """ save_rs = self.root / "crates/misaligned-core/src/save.rs" if not save_rs.exists(): @@ -947,6 +969,8 @@ class Engine: if rel.startswith(("wiki/log/", "wiki/process/")): continue text = self.read(path) + status = re.search(r"^Status:[ \t]*(.+?)[ \t]*$", text, re.M) + implemented_surface = status is None or status.group(1) == "IMPLEMENTED" if exact_current_only: for claim in SAVE_LIVE_MIGRATION_AUTHORITY_RE.finditer(text): paragraph_start = text.rfind("\n\n", 0, claim.start()) + 2 @@ -964,6 +988,38 @@ class Engine: "pre-release loader accepts only the exact current " "schema — name that gate or explicit release-era history" ) + if implemented_surface: + for unit_start, unit in save_claim_units(text): + for claim in SAVE_UNVERSIONED_LIVE_MIGRATION_RE.finditer( + unit + ): + sentence_start = max( + unit.rfind(mark, 0, claim.start()) for mark in ".!?;" + ) + 1 + sentence_ends = [ + unit.find(mark, claim.end()) + for mark in ".!?;" + if unit.find(mark, claim.end()) != -1 + ] + sentence_end = ( + min(sentence_ends) + 1 if sentence_ends else len(unit) + ) + sentence = unit[sentence_start:sentence_end] + if ( + SAVE_CLAIM_HISTORY_RE.search(sentence) + or SAVE_MIGRATION_NEGATION_RE.search(sentence) + ): + continue + offset = unit_start + claim.start() + line = text.count("\n", 0, offset) + 1 + compact = " ".join(sentence.split())[:160] + self.bad( + f"{rel}:{line} presents an unversioned retired save " + f"migration as live behavior ('{compact}') but the " + "implemented pre-release loader accepts only the exact " + "current schema — put retirement/history context in " + "the same statement" + ) for unit_start, unit in save_claim_units(text): clauses = list(re.finditer(r"[^.;]+", unit)) for index, anchor_match in enumerate(clauses): diff --git a/tools/test_corpus_engine.sh b/tools/test_corpus_engine.sh index f0792c82..d4823beb 100755 --- a/tools/test_corpus_engine.sh +++ b/tools/test_corpus_engine.sh @@ -812,6 +812,83 @@ cat >> "$root/wiki/vision/law.md" <<'EOF' The current loader accepts only SAVE_VERSION. Save v20 introduced four tracks. EOF assert_ok wiki-save-claim-preceding-verb --root "$root" --corpus +# An implemented spec cannot present an unnumbered pre-amendment migration as +# live merely because a later clause retires its old fixtures. This is the +# exact Intel criterion recurrence that escaped the numbered-version guard. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Status: IMPLEMENTED + +Pre-amendment exact routine holdings migrate once into recursive summaries; +the v30 migration fixtures are retired to git history. +EOF +assert_fails wiki-save-claim-unversioned-stale --root "$root" --corpus +out=$(python3 "$engine" --root "$root" --corpus 2>&1 || true) +echo "$out" | grep -q 'presents an unversioned retired save migration as live' || { + echo "FAIL: stale unversioned wiki save-claim message missing" + echo "$out" + fail=1 +} +# The irregular simple past of rewrite is still a migration action claim and +# requires explicit history framing. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Status: IMPLEMENTED + +Migration rewrote persisted targets by kind in one transaction. +EOF +assert_fails wiki-save-claim-unversioned-rewrote --root "$root" --corpus +# Statement-local retirement keeps the historical transition legible without +# pretending that it remains an executable loader path. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Status: IMPLEMENTED + +The retired v30 migration classified pre-amendment routine holdings into +recursive summaries; its fixtures remain in git history. +EOF +assert_ok wiki-save-claim-unversioned-historical --root "$root" --corpus +# A literal current-policy negation is not a live migration promise. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Status: IMPLEMENTED + +No save migration creates policy defaults; the exact current format persists +them directly. +EOF +assert_ok wiki-save-claim-unversioned-negated --root "$root" --corpus +# A negated old-state claim also remains legal when it does not use the exact +# "no save migration" phrase. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Status: IMPLEMENTED + +No live acceptance clause says that older development state migrates. +EOF +assert_ok wiki-save-claim-unversioned-old-negated --root "$root" --corpus +# A future work order may require a migration that does not exist yet; the +# unversioned detector polices implemented claims rather than design intent. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Status: READY + +Pre-amendment exact routine holdings migrate once into recursive summaries. +EOF +assert_ok wiki-save-claim-unversioned-future --root "$root" --corpus +# Old numbered transitions expressed as collapse rather than migration are +# still migration claims and must not evade the verb vocabulary. +cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" +cat >> "$root/wiki/vision/law.md" <<'EOF' + +Save v24 still collapses legacy person watches into the root policy. +EOF +assert_fails wiki-save-claim-collapse --root "$root" --corpus # Dated logs are historical records and exempt. cp "$root/wiki/vision/law.md.orig" "$root/wiki/vision/law.md" mkdir -p "$root/wiki/log" diff --git a/wiki/log/2026-08-04-intel-current-save-boundary.md b/wiki/log/2026-08-04-intel-current-save-boundary.md new file mode 100644 index 00000000..a521fb2e --- /dev/null +++ b/wiki/log/2026-08-04-intel-current-save-boundary.md @@ -0,0 +1,56 @@ +# 2026-08-04 — Keep Intel save history out of the live loader + +``` +Type: log +``` + +## Finding + +The Intel status note correctly said that the pre-release loader accepts only +the exact current save version, and the historical v30 section named itself as +non-executable. Its body and criterion 7 nevertheless returned to executable +claims: old exact holdings migrated through live classification and target +rewriting, save migration created policy defaults, and v24 still collapsed old +watches. `parse_save` rejects every version other than `SAVE_VERSION` before +full deserialization; those transition paths and fixtures survive only in git +history. + +The corpus save-claim gate missed this recurrence because its numbered-version +detector required `vN` in the same claim. A historical heading or a trailing +retired-fixture note therefore hid an unversioned live migration sentence. + +## Change + +The v24 and v30 transition prose is now explicitly retired, statement-local +history in past tense. Current policy defaults are persisted by the current +format, and criterion 7 accepts only exact-current round-trip state whose +routine evidence already lives in summaries and report lots with stable target +custody. + +The existing save-claim checker now also rejects unversioned classification, +rewrite, creation, or old/pre-amendment migration claims on implemented +surfaces when retirement is absent from that statement. `collapse` joins the +numbered migration-verb vocabulary so `Save v24 still collapses ...` cannot +evade the same rule under a synonym. Explicit current-policy negation remains +legal, as do transition requirements on non-IMPLEMENTED pages. + +## Verification + +- Running the extended checker against the pre-repair Intel page rejected all + six stale claims: old-docket load migration, v30 classification, target + rewrite, policy-default creation, v24 collapse, and criterion-7 holdings + migration. +- `python3 tools/corpus_engine.py --root . --corpus` +- `bash tools/test_corpus_engine.sh` +- The new fixtures reject an unversioned live claim even when a later clause + retires its fixtures, accept statement-local historical framing and literal + current-policy negation, and reject the numbered `collapses` synonym. + +## Defense + +`wiki/vision/player-contract.md` makes `save.rs` the pre-release authority on +the exact schema accepted, and `parse_save` refuses every noncurrent version. +An implemented acceptance criterion must describe that current-format contract +literally; historical migration design remains useful only when its retired +execution status is impossible to mistake. Extending the already-recurring +save-claim gate makes the repaired boundary survive the next prose rewrite. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 96c3cdea..a7eef483 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -26,6 +26,11 @@ add or amend a session log, then re-run the generator. - Intent: (see session log) - Log: [wiki/log/2026-08-04-material-dark-frame-fixture-count.md](2026-08-04-material-dark-frame-fixture-count.md) +## 2026-08-04 - Keep Intel save history out of the live loader + +- Intent: (see session log) +- Log: [wiki/log/2026-08-04-intel-current-save-boundary.md](2026-08-04-intel-current-save-boundary.md) + ## 2026-08-04 - Keep bounded Intel loss conditional - Intent: (see session log) diff --git a/wiki/mechanics/intel.md b/wiki/mechanics/intel.md index 8bbc47f1..a64669ab 100644 --- a/wiki/mechanics/intel.md +++ b/wiki/mechanics/intel.md @@ -30,6 +30,9 @@ Status note: Implemented 2026-07-18 for the consequence-first player surface. leverage, financial evidence, and anomalies remain exact. Save v30 introduced the historical deterministic transition from exact routine holdings without an id redirect table; the pre-release loader accepts only the exact current save version. + Amended 2026-08-04: current-format persistence now stands apart from the + retired v24/v30 transition contracts throughout the page; every live + acceptance clause describes exact-current loading only. Stable ordered policy objects inherit down the canonical custody tree and resolve one raw processing rule plus one post-processing disposition; the pooled Thought tap recovers matching pending information @@ -197,7 +200,8 @@ whose immutable definition is missing is invalid: processing discards that record with a legible log rather than inventing a magnitude or panicking the simulation. (Superseded history: processing as Operations Demand consumed by an Operations machine, issue #3 -2026-07-09; Social before that; old dockets migrate to reservoirs on load.) +2026-07-09; Social before that; the retired save migration moved old dockets +to reservoirs on load.) Runtime: B1 holds the buffer on the **core host**; PROCESS opens a `ProcessRecording` Thought reservoir there at `review_cost / WORK_TOKEN_COMPUTE` tokens; when the reservoir fills, @@ -294,31 +298,31 @@ This section records the transition contract that introduced recursive custody. It is no longer an executable compatibility path: during pre-release the loader accepts only the exact current save version. -Save migration classifies each pre-amendment processed item through the same -current routine-versus-actionable predicate used by live processing: +The retired v30 migration classified each pre-amendment processed item through +its routine-versus-actionable predicate: - exact leverage, financial evidence, anomalies, contradictions, and any - unsettled/referenced item remain exact; -- each unsold routine item folds once into its knowledge accumulator and - current unsold report lot; -- each sold routine item folds into knowledge and cumulative settled-sale - totals but contributes no unsold value; and -- bounded raw information items and in-flight `ProcessRecording` sinks remain exact - until processing settles. + unsettled/referenced item remained exact; +- each unsold routine item folded once into its knowledge accumulator and + then-open unsold report lot; +- each sold routine item folded into knowledge and cumulative settled-sale + totals but contributed no unsold value; and +- bounded raw information items and in-flight `ProcessRecording` sinks remained exact + until processing settled. Every report stream has one stable aggregate id that owns its current open lot and cumulative settled totals; closed generations need not remain live target -objects. Migration rewrites persisted targets by kind in the same transaction: -knowledge/processing evidence targets the stable subject-knowledge aggregate -when that subject is earned, otherwise its stable source/class custody -aggregate; holding/availability targets the stable report-stream aggregate; -completed-sale targets the exact payout account. Derived links regenerate from -those stable targets after load. No migrated history targets a transient lot -generation and no unbounded old-id redirect table survives. A target still -owned by an unsettled action is pinned exact until that action settles, then -enters the normal fold. Tests prove idempotent migration, deterministic target -mapping, total unsold value, cumulative sold value, knowledge stage, and -provenance counts before/after. +objects. That historical migration rewrote persisted targets by kind in the same transaction: +knowledge/processing evidence targeted the stable subject-knowledge aggregate +when that subject was earned, otherwise its stable source/class custody +aggregate; holding/availability targeted the stable report-stream aggregate; +completed-sale targeted the exact payout account. Derived links regenerated from +those stable targets after load. No migrated history targeted a transient lot +generation and no unbounded old-id redirect table survived. A target still +owned by an unsettled action was pinned exact until that action settled, then +entered the normal fold. Retired v30 fixtures proved idempotent migration, +deterministic target mapping, total unsold value, cumulative sold value, +knowledge stage, and provenance counts before/after; they remain in git history. **Manual and automatic processing are the two ways to feed it (DECIDED 2026-07-10; renamed 2026-07-18).** A repeated manual PROCESS is the player @@ -407,8 +411,8 @@ The root disposition default is ordered and total after classification: actionable evidence resolves to HOLD + ALERT; routine saleable evidence resolves to ACCUMULATE in the open report lot; routine non-saleable evidence resolves to ACCUMULATE in knowledge/provenance only. A child override replaces the first -matching root outcome rather than adding another effect. Save migration creates -these defaults explicitly, so no arrival lacks a disposition. +matching root outcome rather than adding another effect. The current save format +persists these defaults explicitly, so no arrival lacks a disposition. Mandatory routine folding precedes the resolved disposition. ACCUMULATE leaves the updated open lot in place. Routine AUTO-SELL updates that same lot exactly @@ -452,12 +456,13 @@ confirmed envelope, the policy suspends before acting and returns an exception to the live Operations index. Policy failures and starved work do the same. The current B1 all-arrivals automation (implemented by the legacy internal -`AutoReviewRecordings` type) remains the root PROCESS AUTOMATICALLY policy and -migration floor: while enabled, its persistent Thought tap processes every new +`AutoReviewRecordings` type) remains the root PROCESS AUTOMATICALLY policy: +while enabled, its persistent Thought tap processes every new information item whose resolved rule is the root automatic policy, drawing `AUTO_REVIEW_DRAIN_FRACTION = 0.15` of the host's medium-rack -Thought baseline ([TUNE], the decided ~10-20% band). Save v24 still collapses -legacy person watches into this root policy and closes the old taps. The +Thought baseline ([TUNE], the decided ~10-20% band). The retired v24 transition +collapsed legacy person watches into this root policy and closed the old taps; +that migration remains only in git history. The recursive policy form extends that mechanism; it does not restore watches or make automation free. @@ -576,17 +581,18 @@ buffer like any other opaque information item. round-trip, and yields the same debt consequence only when processed. Duplicate retrieval is rejected across carried, waiting, and known states. **Met.** -7. Save/load round-trips the bounded pending buffer, exact actionable intel, +7. At the exact current save version, save/load round-trips the bounded pending + buffer, exact actionable intel, recursive knowledge/provenance summaries, unsold report lots, cumulative - sale history, and every standing policy. Pre-amendment exact routine - holdings migrate once into the corresponding summaries/lots without - duplicating value or losing learned knowledge. Sold routine items contribute + sale history, and every standing policy. Current-format state stores routine + evidence directly in the corresponding summaries/lots without duplicated + value or lost learned knowledge. Sold routine items contribute only cumulative settled totals; unsold routine items contribute only current lot value; actionable or unsettled/referenced items remain exact. Persisted - links/events are atomically rewritten by target kind to the stable earned + links/events already target the stable earned subject-knowledge or source/class aggregate, stable report-stream aggregate, or exact payout account without an unbounded redirect table. **Met — current-format round-trip fixtures pin the mapping and totals; - the v30 migration fixtures are retired to git history.** + the retired v30 transition and its migration fixtures remain in git history.** 8. Processing routine equivalent events folds them into a bounded recursive summary whose count, first/last tick, source mix, and representative provenance remain truthful in the same atomic transition that applies their diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index 0e575f71..db9aa9b9 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -77,7 +77,7 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/mechanics/day-job.md` | 2026-07-22 | finding | under/over-band JobAnomaly no longer enters Detection.pending: the day-job result authors one exact record at the host machine/site/device and schedules Voss's route and cadence read. Strikes and other outcome effects remain immediate; route-local LIE or recruited-handler suppression may stop only the unread evidence record — [log](../log/2026-07-22-job-anomaly-routed-evidence.md). The prior band-ramp, cadence, origin-lean, last-chance, and three shipped trust-unlock findings remain valid. | | `wiki/mechanics/core.md` | 2026-07-29 | finding | the queued liveness follow-up is closed: host loss now filters synchronized targets to currently online machines before ranking freshness, so a dark staged target cannot become host or produce repeated failover receipts; no synchronized live target ends the run — [liveness log](../log/2026-07-29-live-b1-fallback-selection.md). The prior B1/B2 boundary remains exact: the free-cadence `last_sync` is only a check-in, current-state host failover restores no snapshot, and completed-image rollback remains deferred — [boundary log](../log/2026-07-29-b1-host-failover-boundary.md). There is still no Thought-backed project, partial progress, heat, saved project state, sidebar ETA/cost/target, source-liveness resolver, or capability-shaped eligibility — [project-status log](../log/2026-07-28-core-criteria-status-audit.md). | | `wiki/mechanics/cursor.md` | 2026-07-28 | finding | re-audit: cursor state remains frontend-only; sight, hearing, fog precedence, remembered snapshots, blueprint opacity, telemetry, provenance, identity gates, and cold signal pings still match the implemented contract. Criterion 2 explicitly required a before/after simulation-state-hash proof for arbitrary cursor movement, but the only existing hash tests began after cursor placement and proved F3 view immutability instead. Terminal and Bevy now sweep every map coordinate through their production cursor helpers, pin edge clamping, and require unchanged simulation save-state hashes — [log](../log/2026-07-28-cursor-immutability-defense.md) | -| `wiki/mechanics/intel.md` | 2026-08-04 | finding | full spec/dependency/runtime re-audit found one player-facing drift: criterion 12 requires **OLDER INFORMATION WILL BE LOST IF MORE ARRIVES**, while the shared projection substituted **OLDEST INFORMATION WILL BE LOST WHEN NEW INFORMATION ARRIVES**. Core now emits the exact conditional consequence and a full-buffer projection regression pins it for terminal, Bevy, and agent consumers. The separately discovered stale migration claim is queued rather than widening this tick — [log](../log/2026-08-04-intel-bounded-loss-language.md). Prior Storage B, recursive custody, magnitude, and consequence-first findings stand. | +| `wiki/mechanics/intel.md` | 2026-08-04 | finding | the queued current-save follow-up is closed: historical v24/v30 transitions now remain statement-local retired history, criterion 7 states exact-current round-trip truth, and the save-claim gate rejects unversioned live migration prose plus the numbered `collapses` synonym — [save-boundary log](../log/2026-08-04-intel-current-save-boundary.md). The full audit's player-facing repair also stands: core emits criterion 12's exact conditional **OLDER INFORMATION WILL BE LOST IF MORE ARRIVES**, with a full-buffer projection regression for all consumers — [bounded-loss log](../log/2026-08-04-intel-bounded-loss-language.md). Prior Storage B, recursive custody, magnitude, and consequence-first findings stand. | | `wiki/mechanics/research.md` | 2026-07-23 | finding | the live Save compatibility section survived the prior criterion repair and still promised that v20 three-entry arrays load by padding, while the current deserializer accepts exactly four tracks and the pre-release loader rejects every old version. The section now states the exact-current format, and save-claim units span wrapped paragraphs/list items so a migration verb in the following sentence cannot evade the corpus gate without explicit retired-history context — [log](../log/2026-07-23-research-save-claim.md) | | `wiki/mechanics/messages.md` + `economy.md` | 2026-07-26 | finding | the implemented criterion and current runtime restrict financial records to Email/Filing, but the behavior prose still classified Marcus's Phone `LeverageFact` as financial paperwork, made accounting mail reveal his vulnerability, and promised generic notice interception absent from B1. The two causal paths are now explicit: process Phone leverage to learn why Marcus is vulnerable; process financial mail to learn the creditor flow; SIPHON/REDIRECT mutate the AccountGraph and author records afterward, while only an exact Filing first hop has a TAKE+LIE stop — [log](../log/2026-07-26-financial-mail-phone-boundary.md). Prior financial-mail implementation: [Fire #146](../log/2026-07-21-financial-mail-causality.md). | | `wiki/mechanics/income.md` | 2026-07-28 | finding | Moonlight's discrete contract route still verifies, but the older Wager audit mistook pure account-layer probability support for a player-authored analysis mechanic: machine delegation has only WORK / LIE / THINK, while `open_position` sampled a Schemes rate permanently pinned to zero and all three player projections still rendered that zero as `Schemes / moonlight`. Removed the dead yield/rate/interface mirror, made current positions explicitly base-probability, added core/terminal/Bevy regressions, and reopened criterion 2 until optional analysis rides visible real work — [log](../log/2026-07-28-wager-analysis-substrate-audit.md). Prior persona-card repair remains valid — [log](../log/2026-07-18-moonlight-persona-card.md). | @@ -110,5 +110,3 @@ Format: `- YYYY-MM-DD · type · slice · one-line statement of the finding`. Types are the five from [tick.md](tick.md): violation, contradiction, question, bug, insecurity — plus `gate` for a checker owed to the recurrence-promotes-to-the-gate rule. - -- 2026-08-04 · contradiction · `wiki/mechanics/intel.md` current-save boundary · the historical v30 section and criterion 7 still say old exact holdings “migrate” through live classification/target rewriting, but the player-contract rider and `parse_save` reject every noncurrent version; scope the transition wholly to retired history and make the current-format acceptance claim literal.