@@ -56,6 +56,14 @@ export interface TableBackfillPayload {
5656 overwrite : boolean
5757 /** User who triggered the schema change, for usage attribution on the row writes. */
5858 actorUserId ?: string | null
59+ /**
60+ * Person whose permission group gates any cell the backfill's writes cascade
61+ * into. Separate from `actorUserId`, which is a billing attribution and names
62+ * the workspace billed account when the schema change carried no human. Null
63+ * when the change had no acting person; absent on payloads enqueued before
64+ * this field existed, which read as null — the pre-existing behavior.
65+ */
66+ capabilityGovernedUserId ?: string | null
5967}
6068
6169/**
@@ -136,8 +144,11 @@ async function processBackfillPage(opts: {
136144 execs : Array < { rowId : string ; executionId : string | null } >
137145 requestId : string
138146 actorUserId ?: string | null
147+ /** See {@link TableBackfillPayload.capabilityGovernedUserId}. */
148+ capabilityGovernedUserId ?: string | null
139149} ) : Promise < number > {
140- const { table, outputs, overwrite, execs, requestId, actorUserId } = opts
150+ const { table, outputs, overwrite, execs, requestId, actorUserId, capabilityGovernedUserId } =
151+ opts
141152
142153 const executionIdsByRow = new Map < string , string > ( )
143154 for ( const e of execs ) {
@@ -225,11 +236,14 @@ async function processBackfillPage(opts: {
225236 workspaceId : table . workspaceId ,
226237 actorUserId,
227238 /**
228- * A backfill replays values already produced by earlier runs; it starts
229- * no enrichment of its own and carries no acting person into this
230- * background pass.
239+ * A backfill replays values already produced by earlier runs, but the
240+ * cells it fills are dependencies: `batchUpdateRows` starts every
241+ * downstream group whose deps just became satisfied. Those cells are
242+ * governed by whoever made the schema change, carried separately from
243+ * `actorUserId` — an attribution that names the workspace billed account
244+ * when the change carried no human, whose denylist is nobody's to run.
231245 */
232- capabilityGovernedUserId : null ,
246+ capabilityGovernedUserId : capabilityGovernedUserId ?? null ,
233247 secretProvenanceByRowId,
234248 } ,
235249 table ,
@@ -248,7 +262,8 @@ async function processBackfillPage(opts: {
248262 * passes skip already-filled cells).
249263 */
250264export async function runTableBackfill ( payload : TableBackfillPayload ) : Promise < void > {
251- const { jobId, tableId, groupId, outputs, overwrite, actorUserId } = payload
265+ const { jobId, tableId, groupId, outputs, overwrite, actorUserId, capabilityGovernedUserId } =
266+ payload
252267 const requestId = generateId ( ) . slice ( 0 , 8 )
253268
254269 try {
@@ -274,6 +289,7 @@ export async function runTableBackfill(payload: TableBackfillPayload): Promise<v
274289 execs,
275290 requestId,
276291 actorUserId,
292+ capabilityGovernedUserId,
277293 } )
278294 processed += execs . length
279295 }
@@ -328,8 +344,11 @@ export async function maybeBackfillGroupOutputs(opts: {
328344 overwrite : boolean
329345 requestId : string
330346 actorUserId ?: string | null
347+ /** See {@link TableBackfillPayload.capabilityGovernedUserId}. */
348+ capabilityGovernedUserId ?: string | null
331349} ) : Promise < void > {
332- const { table, groupId, outputs, overwrite, requestId, actorUserId } = opts
350+ const { table, groupId, outputs, overwrite, requestId, actorUserId, capabilityGovernedUserId } =
351+ opts
333352 if ( outputs . length === 0 ) return
334353
335354 const [ { count : completedCount } ] = await db
@@ -353,7 +372,15 @@ export async function maybeBackfillGroupOutputs(opts: {
353372 const execs = await selectCompletedExecPage ( table . id , groupId , afterRowId , BACKFILL_PAGE_SIZE )
354373 if ( execs . length === 0 ) break
355374 afterRowId = execs [ execs . length - 1 ] . rowId
356- await processBackfillPage ( { table, outputs, overwrite, execs, requestId, actorUserId } )
375+ await processBackfillPage ( {
376+ table,
377+ outputs,
378+ overwrite,
379+ execs,
380+ requestId,
381+ actorUserId,
382+ capabilityGovernedUserId,
383+ } )
357384 }
358385 return
359386 }
@@ -376,6 +403,7 @@ export async function maybeBackfillGroupOutputs(opts: {
376403 outputs,
377404 overwrite,
378405 actorUserId,
406+ capabilityGovernedUserId,
379407 }
380408 if ( isTriggerDevEnabled ) {
381409 try {
0 commit comments