Skip to content

Commit edca07c

Browse files
icecrasher321claude
andcommitted
refactor(provenance): make reason the only locator, not one of two
Two fields had grown into competing answers to the same question. `origin` is a free-form label for which importer accepted an already-incomplete bundle; four latches had started passing `markIncomplete('unspecified', { origin })`, using it to stand in for a reason that did not exist yet. That splits one fact across a closed enum and an open string, leaving neither worth alerting on. Give those four the literal they were reaching for — none needed a new one — and split the five reasons that covered genuinely different guards, so the reason alone locates the site rather than needing an origin beside it. `origin` keeps its narrow job, now documented: it disambiguates importers that share one guard, and a latch that wants an origin because no reason fits should add a reason instead. No production call site passes 'unspecified' any more. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent adb60d0 commit edca07c

7 files changed

Lines changed: 46 additions & 15 deletions

File tree

apps/sim/executor/handlers/generic/generic-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ export class GenericBlockHandler implements BlockHandler {
234234
continue
235235
}
236236
if (boundary.requiredProjectionRoots.has(projection.path[0])) {
237-
registry.markIncomplete('structural-input-projection-incomplete')
237+
registry.markIncomplete('structural-input-root-unprojected')
238238
}
239239
continue
240240
}

apps/sim/executor/handlers/mothership/mothership-handler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ async function readMothershipExecuteResponse(
464464
result = (await response.json()) as MothershipExecuteResult
465465
} catch (error) {
466466
if (expectsProvenance) {
467-
registry?.markIncomplete('mothership-provenance-invalid')
467+
registry?.markIncomplete('mothership-response-unreadable')
468468
throw new Error('Mothership response provenance metadata is invalid')
469469
}
470470
throw error
@@ -948,7 +948,7 @@ export class MothershipBlockHandler implements BlockHandler {
948948
try {
949949
payload = (await response.clone().json()) as MothershipExecuteResult
950950
} catch {
951-
resultRegistry?.markIncomplete('mothership-provenance-invalid')
951+
resultRegistry?.markIncomplete('mothership-response-unreadable')
952952
throw new Error('Mothership response provenance metadata is invalid')
953953
}
954954
await consumeMothershipProvenance(payload, response, resultRegistry)

apps/sim/executor/utils/resolved-secret-trace-registry.test.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1449,7 +1449,10 @@ describe('incompleteness diagnostics', () => {
14491449
'structural-input-projection-incomplete',
14501450
'mothership-provenance-invalid',
14511451
'client-tool-seal-failed',
1452+
'knowledge-row-missing',
14521453
'knowledge-row-content-mismatch',
1454+
'mothership-response-unreadable',
1455+
'structural-input-root-unprojected',
14531456
'backfill-scope-mismatch',
14541457
] as const)('reports %s at error, since it cannot trip on a healthy run', (reason) => {
14551458
new ResolvedSecretTraceRegistry([], scope).markIncomplete(reason)
@@ -1463,8 +1466,11 @@ describe('incompleteness diagnostics', () => {
14631466

14641467
it.each([
14651468
'mothership-provenance-missing',
1466-
'client-tool-completion-unavailable',
1469+
'client-tool-completion-missing',
1470+
'client-tool-completion-deferred',
1471+
'client-tool-completion-unidentified',
14671472
'client-tool-execution-untrusted',
1473+
'client-tool-content-unavailable',
14681474
'knowledge-result-provenance-unavailable',
14691475
'knowledge-response-capacity-exceeded',
14701476
'memory-crossing-capacity-exceeded',

apps/sim/executor/utils/resolved-secret-trace-registry.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,19 @@ export type ResolvedSecretIncompletenessReason =
4444
| 'tool-input-not-enumerable'
4545
| 'tool-params-transform-failed'
4646
| 'structural-input-projection-incomplete'
47+
| 'structural-input-root-unprojected'
4748
| 'mothership-provenance-invalid'
49+
| 'mothership-response-unreadable'
4850
| 'mothership-provenance-missing'
4951
| 'client-tool-seal-failed'
50-
| 'client-tool-completion-unavailable'
52+
| 'client-tool-completion-missing'
53+
| 'client-tool-completion-deferred'
54+
| 'client-tool-completion-unidentified'
5155
| 'client-tool-execution-untrusted'
56+
| 'client-tool-content-unavailable'
5257
| 'knowledge-result-provenance-unavailable'
5358
| 'knowledge-response-capacity-exceeded'
59+
| 'knowledge-row-missing'
5460
| 'knowledge-row-content-mismatch'
5561
| 'memory-crossing-capacity-exceeded'
5662
| 'workspace-scope-missing'
@@ -95,7 +101,10 @@ const ORIGINATING_FAULT_REASONS = new Set<ResolvedSecretIncompletenessReason>([
95101
'structural-input-projection-incomplete',
96102
'mothership-provenance-invalid',
97103
'client-tool-seal-failed',
104+
'knowledge-row-missing',
98105
'knowledge-row-content-mismatch',
106+
'mothership-response-unreadable',
107+
'structural-input-root-unprojected',
99108
'backfill-scope-mismatch',
100109
])
101110

@@ -219,6 +228,16 @@ type PreparedProvenanceFilterResult =
219228
/** Extra attribution for a latch: which registry it propagated from, and which importer caused it. */
220229
interface MarkIncompleteContext {
221230
source?: ResolvedSecretTraceRegistry
231+
/**
232+
* Which importer accepted an already-incomplete bundle — only meaningful where several callers
233+
* share one guard, as {@link ImportResolvedSecretTraceProvenanceOptions.origin} describes.
234+
*
235+
* It is not a second way to say what `reason` says. A latch that reaches for an origin because no
236+
* reason fits is the signal to add a reason literal instead: `reason` is a closed set that can be
237+
* alerted on and aggregated, and splitting the same fact across two fields leaves neither
238+
* trustworthy. Passing `'unspecified'` alongside an origin is the shape that produced a
239+
* production latch naming no guard at all.
240+
*/
222241
origin?: string
223242
}
224243

apps/sim/lib/copilot/request/tools/client.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ export async function waitForClientToolCompletion({
116116
}
117117
}
118118
} catch {
119-
toolRegistry?.markIncomplete('unspecified', { origin: 'copilotToolClient.sealedContext' })
119+
toolRegistry?.markIncomplete('client-tool-seal-failed', {
120+
origin: 'copilotToolClient.sealedContext',
121+
})
120122
} finally {
121123
finishPendingActivation?.()
122124
}
@@ -243,18 +245,18 @@ export async function waitForWorkflowToolCompletion({
243245
try {
244246
completion = await waitForToolCompletion(toolCallId, timeoutMs, abortSignal)
245247
if (!completion) {
246-
toolRegistry?.markIncomplete('client-tool-completion-unavailable')
248+
toolRegistry?.markIncomplete('client-tool-completion-missing')
247249
return null
248250
}
249251

250252
const executionId = getWorkflowToolCompletionExecutionId(completion.data)
251253
const deploymentError = getAsyncWorkflowDeploymentError(completion.data)
252254
if (completion.status === ASYNC_TOOL_CONFIRMATION_STATUS.background) {
253-
toolRegistry?.markIncomplete('client-tool-completion-unavailable')
255+
toolRegistry?.markIncomplete('client-tool-completion-deferred')
254256
return structuralWorkflowCompletion(completion.status, workflowId, executionId)
255257
}
256258
if (!workflowId || !executionId) {
257-
toolRegistry?.markIncomplete('client-tool-completion-unavailable')
259+
toolRegistry?.markIncomplete('client-tool-completion-unidentified')
258260
const structuralStatus =
259261
completion.status === MothershipStreamV1ToolOutcome.success
260262
? MothershipStreamV1ToolOutcome.error
@@ -284,7 +286,7 @@ export async function waitForWorkflowToolCompletion({
284286
}
285287

286288
if (!trustedExecution.contentAvailable) {
287-
toolRegistry?.markIncomplete('client-tool-execution-untrusted')
289+
toolRegistry?.markIncomplete('client-tool-content-unavailable')
288290
return structuralWorkflowCompletion(
289291
getWorkflowToolConfirmationStatus(trustedExecution.status),
290292
workflowId,

apps/sim/lib/copilot/tools/handlers/function-execute.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ export async function resolveInputFiles(
539539
})
540540
}
541541
} catch {
542-
resolvedSecretTraceRegistry.markIncomplete('unspecified', {
542+
resolvedSecretTraceRegistry.markIncomplete('source-provenance-incomplete', {
543543
origin: 'copilotFunctionExecute.result',
544544
})
545545
}
@@ -573,9 +573,13 @@ async function importMountedProvenance(
573573
trusted: true,
574574
})
575575
if (!imported)
576-
target.markIncomplete('unspecified', { origin: 'copilotFunctionExecute.crossing' })
576+
target.markIncomplete('value-provenance-import-failed', {
577+
origin: 'copilotFunctionExecute.crossing',
578+
})
577579
} catch {
578-
target.markIncomplete('unspecified', { origin: 'copilotFunctionExecute.crossing' })
580+
target.markIncomplete('value-provenance-import-failed', {
581+
origin: 'copilotFunctionExecute.crossing',
582+
})
579583
}
580584
}
581585

apps/sim/lib/knowledge/secret-provenance.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,14 +480,14 @@ export async function importKnowledgePersistedResponseSecretProvenance(options:
480480
const documentById = new Map(documentRows.map((row) => [row.id, row]))
481481
const chunkById = new Map(chunkRows.map((row) => [row.id, row]))
482482
if (documentById.size !== documentIds.length || chunkById.size !== chunkIds.length) {
483-
options.registry.markIncomplete('knowledge-row-content-mismatch')
483+
options.registry.markIncomplete('knowledge-row-missing')
484484
return false
485485
}
486486

487487
for (const item of documents) {
488488
const row = documentById.get(item.id)
489489
if (!row) {
490-
options.registry.markIncomplete('knowledge-row-content-mismatch')
490+
options.registry.markIncomplete('knowledge-row-missing')
491491
return false
492492
}
493493
const source = createKnowledgeDocumentSourceValue(row)

0 commit comments

Comments
 (0)