Skip to content

Migrate formula-table, record-update-attachment, duplicate-table onto lifecycle drivers#63

Merged
HynLcc merged 4 commits into
mainfrom
migrate-batch-formula-attachment-dup
Jun 20, 2026
Merged

Migrate formula-table, record-update-attachment, duplicate-table onto lifecycle drivers#63
HynLcc merged 4 commits into
mainfrom
migrate-batch-formula-attachment-dup

Conversation

@HynLcc

@HynLcc HynLcc commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

What

Migrate three legacy runner kinds onto lifecycle drivers, taking the tracker
from 23/35 → 26/35 runner kinds (31/55 → 36/55 cases). Each migrated runner
delegates the repeated protocol to a driver and reuses its UNCHANGED
result-assembly, proven byte-equivalent by the G1 artifact diff over every
case × engine.

Runner Driver decision Why
formula-table Reuse field-add-lifecycle.ts (4th member) Same family as field-create: seed a table → add field(s) → wait computed backfill → verify → restore-by-delete. The sourceReady-vs-seedReady phase relabel and trailing formulaReady phase append are expressed in the spec; driver unchanged.
record-update-attachment Reuse record-mutation-lifecycle.ts (useRecordWindow:false) Same family as record-update: bulk update over seeded rows. The execute-only token upload + warmup + p95 sampling live in the measured op (NOT prepareFixture — tokens must never be cached: seed→execute carries no storage volume); driver unchanged.
duplicate-table New duplicate-lifecycle.ts (1st member) No existing driver fits "duplicate an entire seeded entity": field-add's cleanup is restore-by-delete, but duplicate must DROP the created copy. New minimal, family-shaped driver (duplicate-base is the deferred 2nd member).

No shared driver was modifiedfield-add-lifecycle.ts and
record-mutation-lifecycle.ts stay byte-stable, so their existing members
(conditional-lookup, field-duplicate, field-create / record-update,
record-create, record-reorder, selection-clear, record-update-link) need no
re-validation.

G1 evidence (baseline A on unmodified main ↔ candidate, local v1+v2)

All 10 case × engine artifacts diff CLEAN. result=pass, error=null on every one.

Case Engine result route (v1=false/v2=true) primary metric trace refs (cand==base)
formula/10k-calc v1 pass n/a (no routing assert) formulaFullReadyMs 1238ms 12==12
formula/10k-calc v2 pass n/a formulaFullReadyMs 1128ms 12==12
formula/10k-5-concurrent v1 pass n/a formulasFullReadyMs 3422ms 16==16
formula/10k-5-concurrent v2 pass n/a formulasFullReadyMs 1919ms 16==16
record-update/attachment-insert-100 v1 pass routeMatched, v2=false p95 95ms 21==21
record-update/attachment-insert-100 v2 pass routeMatched, v2=true p95 240ms 21==21
duplicate-table/10k-20f v1 pass routeMatched, v2=false duplicateTableRequestMs 335ms 12==12
duplicate-table/10k-20f v2 pass routeMatched, v2=true duplicateTableRequestMs 7870ms 12==12
duplicate-table/10k-25f-5formula v1 pass routeMatched, v2=false duplicateTableRequestMs 599ms 12==12
duplicate-table/10k-25f-5formula v2 pass routeMatched, v2=true duplicateTableRequestMs 9828ms 12==12

Baseline A↔B noise check is clean with the existing + added noise masks; the
seedHash-family difference appears ONLY in G1 (never in A↔B), as expected.

Mask deltas (scripts/diff-artifacts.mjs)

  • formula-table — seedHash family: extended the details.seed rule to mask
    seedTableName (its suffix IS the hash; already masked seedHash/seedHashShort).
    Noise: details.{formula,formulas[],formulaResults[]}.compiledExpression
    (embeds generated A/B/C field ids).
  • record-update-attachment — no new seedHash mask (its hash nests under the
    already-masked details.seed.cache). Noise: details.request.attachmentFieldId,
    details.update.expectedTokens.
  • duplicate-table — no new seedHash mask (hash nests under the already-masked
    details.prepare.cache). Noise: details.sourceFields[].id,
    details.sourceFormulas[].id, details.duplicate.duplicatedFormulaFields[].id.

Every noise mask is justified by the baseline A↔B diff (volatile on UNCHANGED
code); field NAMES, expected values, counts, routing, and verification evidence
stay visible.

Negative tests (diff has teeth) — all 9 behaved correctly

  • (a) semantic perturbation → FAIL: formula verifiedSamples[].expected,
    duplicate rowCount, attachment rowCount each made the diff FAIL.
  • (b) masked-field change → PASS: formula seed.seedTableName, duplicate
    sourceFields[].id, attachment update.expectedTokens each kept the diff PASS.
  • (c) over-mask guard (unmasked sibling) → FAIL: formula seed.seedNamePrefix
    (sibling of masked seedHash/seedTableName), duplicate sourceFields[].name
    (sibling of masked id), attachment update.updatedRecords (sibling of masked
    expectedTokens) each still made the diff FAIL.

Verification

  • pnpm check passes.
  • Trace refs preserved (candidate traceRefCount == baseline on every case;
    savedTraceCount=0 locally — no Jaeger — CI is where saved==ref holds).

🤖 Generated with Claude Code

HynLcc and others added 4 commits June 20, 2026 13:51
formula-table now delegates to field-add-lifecycle.ts as the family's fourth
member: prepareFixture seeds the numeric source table (parking its
createTable/seedRecords measurements on the fixture), assertSeedReady waits for
source-row readability, runPrimary creates the formula field(s) and waits for
the computed backfill + full scan, and cleanup restores the reusable seed by
deleting the added formula fields (or drops the table). The shared driver is
unchanged: the source-ready phase relabel (sourceReady on execute vs seedReady
on seed) and the trailing formulaReady phase append are expressed in the spec,
reusing the unchanged buildFormulaCaseResult so artifacts stay byte-equivalent.

G1: formula/10k-calc and formula/10k-5-concurrent x v1,v2 diff clean vs baseline.
Masks: details.seed.seedTableName (the hash-suffixed seed name, a seedHash-family
content address that only appears in G1, never in baseline A<->B) and
details.{formula,formulas[],formulaResults[]}.compiledExpression (generated
A/B/C field ids embedded in the compiled form; uncompiled expression stays
visible).

Co-Authored-By: Claude <noreply@anthropic.com>
…river

record-update-attachment now delegates to record-mutation-lifecycle.ts (the
record-update family, useRecordWindow:false): prepareFixture seeds the Title +
empty-Attachment table, assertSeedReady asserts the cells are empty, and the
measured operation uploads the attachment tokens (execute-only — the
seed->execute cache carries no storage volume, so tokens must never be cached),
warms up, p95-samples the idempotent bulk attachment-cell update, asserts
routing, and verifies. uploadSetupMs is parked on the fixture so buildResult
still reports it on the diagnostic path. The shared driver is unchanged and the
unchanged result-assembly keeps artifacts byte-equivalent.

G1: record-update/attachment-insert-100 x v1,v2 diff clean vs baseline with no
new seedHash mask (its seed hash already nests under details.seed.cache). Masks:
details.request.attachmentFieldId and details.update.expectedTokens (generated
field id + random upload tokens, noise confirmed by baseline A<->B).

Co-Authored-By: Claude <noreply@anthropic.com>
No existing driver fits "duplicate an entire seeded entity", so this adds a new,
minimal framework/runners/duplicate-lifecycle.ts as the duplicate family's FIRST
member (duplicate-base is the deferred second member). Its skeleton mirrors
field-add-lifecycle (prepare/seedReady -> measured primary -> diagnostic wrap ->
cleanup in finally) but its cleanup DROPS the created copy (read from the mutable
fixture) and the source unless it is a reusable cached seed — drop-the-copy, not
restore-by-delete. Per runner-framework.md it is intentionally family-shaped, not
universal.

duplicate-table delegates to it: prepareFixture seeds/restores the source table
(parking its "prepare" measurement), assertSeedReady full-scans the source, and
runPrimary runs the trace-wrapped duplicateTableTotalReady measurement (request +
copy-readiness scan), parking the created copy id for cleanup. The unchanged
buildDuplicateTableCaseResult keeps artifacts byte-equivalent.

G1: duplicate-table/10k-20f and duplicate-table/10k-25f-5formula x v1,v2 diff
clean vs baseline with no new seedHash mask (its seed hash already nests under
details.prepare.cache). Masks: details.sourceFields[].id,
details.sourceFormulas[].id, details.duplicate.duplicatedFormulaFields[].id
(generated field ids, noise confirmed by baseline A<->B; names stay visible).

Co-Authored-By: Claude <noreply@anthropic.com>
Move formula-table, record-update-attachment, and duplicate-table to Migrated
(field-add-lifecycle, record-mutation-lifecycle, and the new duplicate-lifecycle
respectively) and drop them from Not migrated.

Co-Authored-By: Claude <noreply@anthropic.com>
@HynLcc HynLcc merged commit 89e331f into main Jun 20, 2026
6 checks passed
@HynLcc HynLcc deleted the migrate-batch-formula-attachment-dup branch June 20, 2026 06:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant