Skip to content

Commit a0a1456

Browse files
committed
wip: federated updated_at disposition; confirmation roster entry
Claude-Session: https://claude.ai/code/session_01Utp6Cb2FL7H75wB1wA6PpZ Co-authored-by: Claude <noreply@anthropic.com>
1 parent c6b6cfe commit a0a1456

3 files changed

Lines changed: 15 additions & 2 deletions

File tree

‎packages/cli/test/migrate-confirmation-exit.pin.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ const ROSTER: Record<string, Member> = {
290290
writes: BOOT,
291291
},
292292
'security-catalog-overlays': { argv: ['--apply'], confirm: '--yes', writes: BOOT },
293+
'stored-expressions': { argv: ['--apply'], confirm: '--yes', writes: BOOT },
293294
'summary-nulls': { argv: ['--apply'], confirm: '--yes', writes: BOOT },
294295
'value-shapes': { argv: ['--apply'], confirm: '--yes', writes: BOOT },
295296
};

‎packages/objectql/src/federated-injected-column-readers.test.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,14 @@ const READERS: Record<string, Row> = {
377377
disposition: 'skips',
378378
why: 'the recompute reads the stamp its apply keeps, and leaves the column out of its read (and its write) on a federated object',
379379
},
380+
'stored-expression-backfill.ts#backfillStoredExpressions :: isFederatedUnprovisionedInjectedColumn()': {
381+
disposition: 'skips',
382+
why: 'the backfill never sends an injected column a federated object does not provision',
383+
},
384+
'stored-expression-backfill.ts#backfillStoredExpressions :: updated_at': {
385+
disposition: 'skips',
386+
why: 'the backfill keeps the stamp of the row it writes, and sends none on a federated object',
387+
},
380388
'engine-migration-state.ts#recordObservedDeviation :: updated_at': { disposition: 'not-a-read', why: PLATFORM_ROW },
381389
'engine-migration-state.ts#retractCreationAttestation :: updated_at': { disposition: 'not-a-read', why: PLATFORM_ROW },
382390
'plugin.ts#reconcileFederatedBindings :: isFederatedObject()': { disposition: 'not-a-read', why: SYNC_ROUTE },
@@ -703,7 +711,7 @@ describe('[#21918] every engine reader of an injected column has a disposition t
703711
}
704712
}, SCAN_BUDGET_MS);
705713

706-
it('the sites that skip are exactly the cascade scan, its plan, the organization-delete count, the lifecycle tenant partition, and the home-amount recompute', () => {
714+
it('the sites that skip are exactly the cascade scan, its plan, the organization-delete count, the lifecycle tenant partition, the home-amount recompute and the stored-expression backfill', () => {
707715
const skipping = new Set(
708716
Object.entries(READERS)
709717
.filter(([, row]) => row.disposition === 'skips')
@@ -715,6 +723,7 @@ describe('[#21918] every engine reader of an injected column has a disposition t
715723
'engine-cascade-planning.ts#planCascadeAtomicity',
716724
'home-amount-recompute.ts#recomputeHomeAmounts',
717725
'lifecycle/lifecycle-service.ts#tenantWindowsFor',
726+
'stored-expression-backfill.ts#backfillStoredExpressions',
718727
]);
719728
});
720729

‎packages/objectql/src/stored-expression-backfill.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import { keysetWalk, type KeysetPageQuery } from '@objectstack/types';
7070
import { objectNotFoundError, withTransientRetry, type RetryOptions } from '@objectstack/core';
7171

7272
import { planStoredExpressions } from './engine-top-level.js';
73+
import { isFederatedUnprovisionedInjectedColumn } from './federated-object.js';
7374

7475
/** The engine surface the run needs: the declarations, a read, the stored-expression computation and the update. */
7576
export interface StoredExpressionBackfillEngine {
@@ -347,7 +348,9 @@ export async function backfillStoredExpressions(
347348
outcomes.set(field, outcome);
348349
report.fields.push(outcome);
349350
}
350-
const hasUpdatedAt = 'updated_at' in (engine.getObject(object)?.fields ?? {});
351+
// The stamp the apply keeps; a federated object's injected columns are not provisioned, so its write sends none.
352+
const schema = engine.getObject(object);
353+
const hasUpdatedAt = 'updated_at' in (schema?.fields ?? {}) && !isFederatedUnprovisionedInjectedColumn(schema, 'updated_at');
351354

352355
// Seek by `id`: this run's claim is about EVERY stored row, which an offset walk cannot promise.
353356
const walk = keysetWalk<Record<string, unknown>>(

0 commit comments

Comments
 (0)