@@ -43,6 +43,7 @@ export type ResolvedSecretIncompletenessReason =
4343 | 'durable-provenance-malformed'
4444 | 'tool-input-not-enumerable'
4545 | 'tool-params-transform-failed'
46+ | 'mcp-tool-execution-timeout'
4647 | 'structural-input-projection-incomplete'
4748 | 'structural-input-root-unprojected'
4849 | 'mothership-provenance-invalid'
@@ -60,17 +61,20 @@ export type ResolvedSecretIncompletenessReason =
6061 | 'knowledge-row-missing'
6162 | 'knowledge-row-content-mismatch'
6263 | 'memory-crossing-capacity-exceeded'
63- | 'workspace-scope-missing'
6464 | 'table-result-provenance-unavailable'
6565 | 'mounted-file-provenance-unavailable'
66+ | 'workspace-file-provenance-unknown'
67+ | 'file-source-unidentified'
6668 | 'table-snapshot-unsafe-for-mount'
6769 | 'restored-provenance-untrusted'
6870 | 'backfill-checkpoint-absent'
6971 | 'backfill-checkpoint-unusable'
7072 | 'log-creation-skipped'
7173 /**
72- * Only for a caller that has not been given a reason yet. A refusal reporting this names no
73- * guard, which is the state that made a production latch untraceable — prefer adding a literal.
74+ * No production caller uses this, and none should: a refusal reporting it names no guard, which
75+ * is the state that made a production latch untraceable. It survives for tests that need a
76+ * latched registry and have no guard to name, where a borrowed real reason would read as a claim
77+ * about which one tripped. A new caller wanting it wants a new literal instead.
7478 */
7579 | 'unspecified'
7680
@@ -122,6 +126,23 @@ const BY_DESIGN_INCOMPLETENESS_REASONS = new Set<ResolvedSecretIncompletenessRea
122126 'log-creation-skipped' ,
123127] )
124128
129+ /**
130+ * Sole owner of the report level, shared by every latch that reports one.
131+ *
132+ * The registry, its input paths, and the accumulator each latch for their own reasons but classify
133+ * them identically, and a copy of the split per latch is a copy that can be updated alone — which
134+ * would let the same reason be a fault in one place and routine in another.
135+ */
136+ function reportIncompleteness (
137+ message : string ,
138+ reason : ResolvedSecretIncompletenessReason ,
139+ details : Record < string , unknown >
140+ ) : void {
141+ if ( BY_DESIGN_INCOMPLETENESS_REASONS . has ( reason ) ) return
142+ if ( ORIGINATING_FAULT_REASONS . has ( reason ) ) logger . error ( message , { reason, ...details } )
143+ else logger . warn ( message , { reason, ...details } )
144+ }
145+
125146/**
126147 * Origins are caller-supplied strings rather than a closed union, so they carry an explicit bound;
127148 * one run reaching this many distinct importers already tells the whole story.
@@ -624,6 +645,7 @@ export function isResolvedSecretTraceProvenanceV1(
624645export class ResolvedSecretTraceProvenanceAccumulator {
625646 private readonly scope ?: ResolvedSecretTraceScopeV1
626647 private provenance : ResolvedSecretTraceProvenanceV1
648+ private reportedGuard = false
627649
628650 constructor ( scope ?: ResolvedSecretTraceScopeV1 ) {
629651 this . scope = scope ? cloneProvenanceScope ( scope ) : undefined
@@ -677,9 +699,26 @@ export class ResolvedSecretTraceProvenanceAccumulator {
677699 return true
678700 }
679701
680- /** Marks the invocation incomplete and discards entries that can no longer be trusted. */
681- markIncomplete ( ) : void {
702+ /**
703+ * Marks the invocation incomplete and discards entries that can no longer be trusted.
704+ *
705+ * `reason` is required for the same purpose it is on {@link ResolvedSecretTraceRegistry}, and
706+ * matters more here: the wire format carries only `complete`, so the consumer that imports this
707+ * bundle can only latch with `source-provenance-incomplete` and can never name the guard. This
708+ * line is the sole record of which one tripped.
709+ *
710+ * Only the first guard reports. Later ones restate an invocation that already cannot vouch, and
711+ * a caller walking a list of sources would otherwise emit a line per remaining source. A latch
712+ * from {@link record} does not report at all: it reflects a bundle whose own registry already
713+ * reported, so this would only restate it with less context.
714+ */
715+ markIncomplete ( reason : ResolvedSecretIncompletenessReason ) : void {
682716 this . provenance = this . emptyProvenance ( false )
717+ if ( this . reportedGuard ) return
718+ this . reportedGuard = true
719+ reportIncompleteness ( 'Resolved secret provenance accumulator marked incomplete' , reason , {
720+ scopeWorkspaceId : this . scope ?. workspaceId ,
721+ } )
683722 }
684723
685724 exportProvenance ( ) : ResolvedSecretTraceProvenanceV1 {
@@ -1583,17 +1622,13 @@ export class ResolvedSecretTraceRegistry {
15831622 if ( ! this . complete ) return
15841623 this . complete = false
15851624 this . modelEgressRevision += 1
1586- if ( this . staged || BY_DESIGN_INCOMPLETENESS_REASONS . has ( reason ) ) return
1587- const details = {
1588- reason,
1625+ if ( this . staged ) return
1626+ reportIncompleteness ( 'Resolved secret registry marked incomplete' , reason , {
15891627 ...( context . origin ? { origin : context . origin } : { } ) ,
15901628 scopeWorkspaceId : this . scope ?. workspaceId ,
15911629 activeEntryCount : this . activeEntries . size ,
15921630 incompleteInputPathCount : this . incompleteInputPaths . size ,
1593- }
1594- const message = 'Resolved secret registry marked incomplete'
1595- if ( ORIGINATING_FAULT_REASONS . has ( reason ) ) logger . error ( message , details )
1596- else logger . warn ( message , details )
1631+ } )
15971632 }
15981633
15991634 /**
@@ -2038,17 +2073,13 @@ export class ResolvedSecretTraceRegistry {
20382073 if ( this . incompleteInputPaths . has ( key ) ) return
20392074 this . incompleteInputPaths . set ( key , [ ...path ] )
20402075 this . modelEgressRevision += 1
2041- if ( this . staged || BY_DESIGN_INCOMPLETENESS_REASONS . has ( reason ) ) return
2042- const details = {
2043- reason,
2076+ if ( this . staged ) return
2077+ reportIncompleteness ( 'Resolved secret input path marked incomplete' , reason , {
20442078 ...( origin ? { origin } : { } ) ,
20452079 inputPath : path . join ( '.' ) ,
20462080 scopeWorkspaceId : this . scope ?. workspaceId ,
20472081 activeEntryCount : this . activeEntries . size ,
2048- }
2049- const message = 'Resolved secret input path marked incomplete'
2050- if ( ORIGINATING_FAULT_REASONS . has ( reason ) ) logger . error ( message , details )
2051- else logger . warn ( message , details )
2082+ } )
20522083 }
20532084
20542085 private copyIncompleteInputPathsTo (
0 commit comments