Migrate field-convert + field-convert-link onto a lifecycle driver#56
Merged
Conversation
Tightly-coupled family migration: both runner kinds share the convertField
endpoint and the same lifecycle, so add one family-shaped driver
field-convert-lifecycle.ts that owns the repeated protocol:
prepare(seed) -> seedReady -> measured convert+readiness -> build result
(twice: diagnostic catch + success) -> finally keep-or-delete cleanup.
The driver owns the shared keepFixture decision (Class D: convert rewrites the
column in place, so keep only when the execute DB is isolated or a reusable
seed was never converted); the runner declares which table(s) to drop
(field-convert: host table; field-convert-link: host + foreign table).
field-convert.runner.ts / field-convert-link.runner.ts keep only their
case-specific diff logic (seed-cache fixture with its own create/seed
measurements, seed assertion, the measured convert + routing + readiness
bundle, result assembly, cleanup target) and wire into the driver via a spec.
G1 mask additions (scripts/diff-artifacts.mjs): these cases were never
G1-diffed before; baseline A vs B surfaced generated-field-id noise the
comparator did not yet mask. All genuinely volatile on unchanged code, none
semantic:
- details.convert.sourceFieldId (added to GENERATED_ID_KEYS; sourceFieldName
stays visible),
- details.convertedField.id (scoped; convertedField name/type stay visible),
- details.verifiedSamples[].actual.id for text-to-link (scoped; the linked
cell's actual.title stays visible and asserted).
Semantic proofs stay caught: perturbing convertedField.type, convert.targetType,
convert.sourceFieldName, verifiedSamples[].actual.title, fullScan.scannedRecords,
or phases[0].name all still fail the diff.
Verification: pnpm check green; baseline A/B and A↔candidate pass for all 4
cases x v1+v2; full negative battery (6 semantic fails, 3 masked-id passes);
updated comparator still passes for the migrated record-reorder artifacts.
Tracker: field-convert + field-convert-link Not migrated -> Migrated. Counts
15->17 / 35 runner kinds, 17->21 / 55 cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tightly-coupled family migration.
field-convertandfield-convert-linkshare the
convertFieldendpoint and the same lifecycle, so they move onto onenew family-shaped driver.
Scope
field-convert(2 cases) +field-convert-link(2 cases)field-convert/10k-link-to-text, field-convert/10k-text-to-link
Change
field-convert-lifecycle.tsowns the repeated protocol: prepare(seed) →seedReady → measured convert+readiness → build result (twice: diagnostic
catch + success) → finally keep-or-delete cleanup. It owns the shared
keepFixturedecision (Class D — the convert rewrites the column in place, sokeep only when the execute DB is isolated or a reusable seed was never
converted); the runner declares which table(s) to drop (field-convert: host;
field-convert-link: host + foreign).
its own create/seed measurements, seed assertion, the measured convert +
routing + readiness bundle, result assembly, cleanup target) and wire into the
driver via a spec.
G1 mask additions (
scripts/diff-artifacts.mjs)These cases were never G1-diffed before; baseline A vs B surfaced
generated-field-id noise the comparator did not yet mask. All genuinely volatile
on unchanged code, none semantic:
details.convert.sourceFieldId(→ GENERATED_ID_KEYS;sourceFieldNamestays visible)details.convertedField.id(scoped;convertedField.name/typestay visible)details.verifiedSamples[].actual.idfor text-to-link (scoped; the linkedcell's
actual.titlestays visible and asserted)Verification
pnpm check: green.(with the noted mask additions, confirmed sufficient by A↔B).
convertedField.type,convert.targetType,convert.sourceFieldName,verifiedSamples[].actual.title,fullScan.scannedRecords, orphases[0].nameall fail the diff; the 3masked id fields correctly pass.
artifacts (no comparator regression).
field-convert/field-convert-link, fullScan scannedRecords=10000.Protected case-contract surface (cases/**, registry.ts, framework/types.ts
config, framework/artifacts.ts schema, ../teable-ee) untouched. The
scripts/diff-artifacts.mjsmask change is task-sanctioned and masks onlyrun-to-run-volatile generated ids.
🤖 Generated with Claude Code