@dsh-enhanced/plugin-control-plane 0.1.64 → 0.1.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +81 -5
  2. package/lib/attestation.d.ts.map +1 -1
  3. package/lib/attestation.js +33 -1
  4. package/lib/attestation.js.map +1 -1
  5. package/lib/creation-capability-empty-handoff.d.ts +57 -0
  6. package/lib/creation-capability-empty-handoff.d.ts.map +1 -0
  7. package/lib/creation-capability-empty-handoff.js +147 -0
  8. package/lib/creation-capability-empty-handoff.js.map +1 -0
  9. package/lib/creation-capability-execution.d.ts +9 -0
  10. package/lib/creation-capability-execution.d.ts.map +1 -0
  11. package/lib/creation-capability-execution.js +70 -0
  12. package/lib/creation-capability-execution.js.map +1 -0
  13. package/lib/creation-capability-feedback.d.ts +58 -1
  14. package/lib/creation-capability-feedback.d.ts.map +1 -1
  15. package/lib/creation-capability-feedback.js +532 -50
  16. package/lib/creation-capability-feedback.js.map +1 -1
  17. package/lib/creation-capability-handoff-common.d.ts +60 -0
  18. package/lib/creation-capability-handoff-common.d.ts.map +1 -0
  19. package/lib/creation-capability-handoff-common.js +195 -0
  20. package/lib/creation-capability-handoff-common.js.map +1 -0
  21. package/lib/creation-capability-journal.d.ts +19 -0
  22. package/lib/creation-capability-journal.d.ts.map +1 -1
  23. package/lib/creation-capability-journal.js +62 -21
  24. package/lib/creation-capability-journal.js.map +1 -1
  25. package/lib/creation-capability-runtime.d.ts +18 -0
  26. package/lib/creation-capability-runtime.d.ts.map +1 -1
  27. package/lib/creation-capability-runtime.js +76 -18
  28. package/lib/creation-capability-runtime.js.map +1 -1
  29. package/lib/creation-capability-sealed-audit.d.ts +19 -0
  30. package/lib/creation-capability-sealed-audit.d.ts.map +1 -0
  31. package/lib/creation-capability-sealed-audit.js +114 -0
  32. package/lib/creation-capability-sealed-audit.js.map +1 -0
  33. package/lib/creation-capability-sealed-handoff.d.ts +160 -0
  34. package/lib/creation-capability-sealed-handoff.d.ts.map +1 -0
  35. package/lib/creation-capability-sealed-handoff.js +695 -0
  36. package/lib/creation-capability-sealed-handoff.js.map +1 -0
  37. package/lib/creation-capability-trend-snapshot.d.ts +70 -0
  38. package/lib/creation-capability-trend-snapshot.d.ts.map +1 -0
  39. package/lib/creation-capability-trend-snapshot.js +117 -0
  40. package/lib/creation-capability-trend-snapshot.js.map +1 -0
  41. package/lib/creation-capability-types.d.ts +254 -4
  42. package/lib/creation-capability-types.d.ts.map +1 -1
  43. package/lib/in-process-reload.d.ts +191 -0
  44. package/lib/in-process-reload.d.ts.map +1 -0
  45. package/lib/in-process-reload.js +284 -0
  46. package/lib/in-process-reload.js.map +1 -0
  47. package/lib/index.d.ts +12 -4
  48. package/lib/index.d.ts.map +1 -1
  49. package/lib/index.js +8 -2
  50. package/lib/index.js.map +1 -1
  51. package/lib/linux-source-build.d.ts +16 -0
  52. package/lib/linux-source-build.d.ts.map +1 -0
  53. package/lib/linux-source-build.js +268 -0
  54. package/lib/linux-source-build.js.map +1 -0
  55. package/lib/linux-source-preflight.d.ts +31 -0
  56. package/lib/linux-source-preflight.d.ts.map +1 -0
  57. package/lib/linux-source-preflight.js +101 -0
  58. package/lib/linux-source-preflight.js.map +1 -0
  59. package/lib/live-qualification-runtime.d.ts +6 -2
  60. package/lib/live-qualification-runtime.d.ts.map +1 -1
  61. package/lib/live-qualification-runtime.js +65 -11
  62. package/lib/live-qualification-runtime.js.map +1 -1
  63. package/lib/owner-task-gap-types.d.ts +23 -0
  64. package/lib/owner-task-gap-types.d.ts.map +1 -1
  65. package/lib/owner-task-gaps.d.ts +4 -0
  66. package/lib/owner-task-gaps.d.ts.map +1 -1
  67. package/lib/owner-task-gaps.js +35 -1
  68. package/lib/owner-task-gaps.js.map +1 -1
  69. package/lib/scanner-owner-runtime.d.ts +34 -0
  70. package/lib/scanner-owner-runtime.d.ts.map +1 -0
  71. package/lib/scanner-owner-runtime.js +99 -0
  72. package/lib/scanner-owner-runtime.js.map +1 -0
  73. package/lib/service.d.ts +82 -5
  74. package/lib/service.d.ts.map +1 -1
  75. package/lib/service.js +450 -45
  76. package/lib/service.js.map +1 -1
  77. package/lib/source-approval-authority.d.ts.map +1 -1
  78. package/lib/source-approval-authority.js +25 -7
  79. package/lib/source-approval-authority.js.map +1 -1
  80. package/lib/source-build-diagnostics.d.ts +31 -0
  81. package/lib/source-build-diagnostics.d.ts.map +1 -0
  82. package/lib/source-build-diagnostics.js +110 -0
  83. package/lib/source-build-diagnostics.js.map +1 -0
  84. package/lib/source-build.d.ts +25 -2
  85. package/lib/source-build.d.ts.map +1 -1
  86. package/lib/source-build.js +145 -26
  87. package/lib/source-build.js.map +1 -1
  88. package/lib/source-creation.d.ts +6 -2
  89. package/lib/source-creation.d.ts.map +1 -1
  90. package/lib/source-creation.js +11 -2
  91. package/lib/source-creation.js.map +1 -1
  92. package/lib/source-job-types.d.ts +3 -1
  93. package/lib/source-job-types.d.ts.map +1 -1
  94. package/lib/source-jobs.d.ts +28 -1
  95. package/lib/source-jobs.d.ts.map +1 -1
  96. package/lib/source-jobs.js +117 -26
  97. package/lib/source-jobs.js.map +1 -1
  98. package/lib/sqlite.d.ts +1 -1
  99. package/lib/sqlite.d.ts.map +1 -1
  100. package/lib/sqlite.js +36 -1
  101. package/lib/sqlite.js.map +1 -1
  102. package/lib/store.d.ts +21 -1
  103. package/lib/store.d.ts.map +1 -1
  104. package/lib/store.js +470 -174
  105. package/lib/store.js.map +1 -1
  106. package/lib/systemd-host-authority.d.ts.map +1 -1
  107. package/lib/systemd-host-authority.js +4 -0
  108. package/lib/systemd-host-authority.js.map +1 -1
  109. package/lib/task-observation-runtime.d.ts +6 -2
  110. package/lib/task-observation-runtime.d.ts.map +1 -1
  111. package/lib/task-observation-runtime.js +69 -11
  112. package/lib/task-observation-runtime.js.map +1 -1
  113. package/lib/types.d.ts +47 -1
  114. package/lib/types.d.ts.map +1 -1
  115. package/lib/version.d.ts +1 -1
  116. package/lib/version.js +1 -1
  117. package/lib/work-item-creation-contract.d.ts +17 -0
  118. package/lib/work-item-creation-contract.d.ts.map +1 -0
  119. package/lib/work-item-creation-contract.js +61 -0
  120. package/lib/work-item-creation-contract.js.map +1 -0
  121. package/package.json +10 -10
@@ -1,10 +1,16 @@
1
1
  import { controlPlaneDigest } from './store.js';
2
+ /** Below this many samples a measure is marked insufficient; latency percentiles are withheld, quality counts stay exact. */
3
+ export const DEFAULT_MINIMUM_SAMPLES = 5;
2
4
  const same = (a, b) => controlPlaneDigest(a) === controlPlaneDigest(b);
3
5
  /** Read the current canonical head and the corresponding authenticated Delivery source. */
4
6
  function readTask(input, inboxId) {
5
- const { owner, evaluation, delivery } = input;
7
+ const { owner, evaluation } = input;
6
8
  const scope = evaluation.canonicalHostScope({ workspace: owner.workspace, preset: owner.agentPreset });
7
- const canonical = evaluation.getTrustedForegroundLearningProjection({ scope, inboxId });
9
+ return certifiedTask(input, evaluation.getTrustedForegroundLearningProjection({ scope, inboxId }), inboxId);
10
+ }
11
+ /** The authenticated Delivery source of one current certified foreground head, or undefined. */
12
+ function certifiedTask(input, canonical, inboxId) {
13
+ const { owner, delivery } = input;
8
14
  if (!canonical || canonical.projection.subjectKind !== 'foreground-turn'
9
15
  || canonical.projection.subjectRef !== inboxId || canonical.projection.disposition !== 'upsert')
10
16
  return undefined;
@@ -23,14 +29,20 @@ function readTask(input, inboxId) {
23
29
  return undefined;
24
30
  return source;
25
31
  }
26
- /** No cache: every result is checked against the current canonical writer and owner route. */
27
- export function inspectCreationCapabilityTaskAssociations(input) {
28
- const { record, calls, owner, delivery, evaluation } = input;
29
- const receipt = record.receipt;
30
- if (!receipt || receipt.planId !== record.planId || input.triggerInboxId.length === 0)
31
- return [];
32
- const now = input.now ?? Date.now();
33
- const receiptDigest = controlPlaneDigest(receipt);
32
+ function taskModel(source) {
33
+ const selection = source.modelSelection;
34
+ if (source.modelSelectionState === 'missing')
35
+ return { state: 'missing' };
36
+ if (source.modelSelectionState !== 'frozen' || !selection || typeof selection.provider !== 'string'
37
+ || typeof selection.model !== 'string' || (selection.reasoningEffort !== undefined
38
+ && typeof selection.reasoningEffort !== 'string'))
39
+ return { state: 'inconsistent' };
40
+ return { state: 'frozen', provider: selection.provider, model: selection.model,
41
+ ...(selection.reasoningEffort === undefined ? {} : { reasoningEffort: selection.reasoningEffort }) };
42
+ }
43
+ /** Completed foreground calls of this exact adoption grouped by Inbox; undefined for a mixed-session Inbox. */
44
+ function eligibleCalls(input, receipt, receiptDigest, now) {
45
+ const { record, calls, owner } = input;
34
46
  const grouped = new Map();
35
47
  for (const call of calls) {
36
48
  const proof = call.foreground;
@@ -51,14 +63,54 @@ export function inspectCreationCapabilityTaskAssociations(input) {
51
63
  continue;
52
64
  const prior = grouped.get(proof.task.inboxId);
53
65
  if (prior && prior.sessionId !== proof.task.sessionId)
54
- return [];
66
+ return undefined;
55
67
  if (prior)
56
68
  prior.calls.push(call);
57
69
  else
58
70
  grouped.set(proof.task.inboxId, { sessionId: proof.task.sessionId, calls: [call] });
59
71
  }
60
- if (grouped.size === 0)
61
- return [];
72
+ return grouped;
73
+ }
74
+ /** Owner-fenced original task source whose binding matches the current learning head. */
75
+ function originalTaskSource(input, source, inboxId) {
76
+ const { owner, delivery } = input;
77
+ const originalSource = delivery.inspectOwnerForegroundTaskSource({ authorityId: owner.authorityId,
78
+ principalId: owner.principalId, workspace: owner.workspace, agentPreset: owner.agentPreset,
79
+ expectedOwner: { authorityHash: owner.authorityHash, principalRecordId: owner.principalRecordId,
80
+ principalVersion: owner.principalVersion }, inboxId });
81
+ if (!originalSource || source.source.sessionId !== originalSource.binding.sessionId
82
+ || source.owner.bindingVersion !== originalSource.binding.version
83
+ || source.owner.generation !== originalSource.binding.generation
84
+ || originalSource.authorityId !== owner.authorityId || originalSource.authorityHash !== owner.authorityHash
85
+ || originalSource.principalId !== owner.principalId
86
+ || originalSource.owner.principalRecordId !== owner.principalRecordId
87
+ || originalSource.owner.principalVersion !== owner.principalVersion)
88
+ return undefined;
89
+ return originalSource;
90
+ }
91
+ const sameBinding = (call, binding) => {
92
+ const task = call.foreground.task;
93
+ return task.binding.id === binding.id && task.binding.version === binding.version && task.binding.generation === binding.generation;
94
+ };
95
+ const taskLatencyMs = (execution) => execution?.quiescent && Number.isSafeInteger(execution.completedAt)
96
+ && Number.isSafeInteger(execution.dispatchedAt) && execution.completedAt >= execution.dispatchedAt
97
+ ? execution.completedAt - execution.dispatchedAt : undefined;
98
+ /**
99
+ * One fenced read of grouped candidate Inboxes into current association rows. Undefined means no receipt, a mixed
100
+ * session Inbox, a stale or changed fence, or a throw; `fenceEmpty` still checks the original task with no candidates.
101
+ */
102
+ function readAssociationRows(input, fenceEmpty) {
103
+ const { record, owner, evaluation } = input;
104
+ const receipt = record.receipt;
105
+ if (!receipt || receipt.planId !== record.planId || input.triggerInboxId.length === 0)
106
+ return undefined;
107
+ const now = input.now ?? Date.now();
108
+ const receiptDigest = controlPlaneDigest(receipt);
109
+ const grouped = eligibleCalls(input, receipt, receiptDigest, now);
110
+ if (!grouped)
111
+ return undefined;
112
+ if (grouped.size === 0 && !fenceEmpty)
113
+ return { rows: [], candidates: 0, receiptDigest, now };
62
114
  const scope = evaluation.canonicalHostScope({ workspace: owner.workspace, preset: owner.agentPreset });
63
115
  const read = () => {
64
116
  const original = input.readSourceCurrent();
@@ -67,49 +119,38 @@ export function inspectCreationCapabilityTaskAssociations(input) {
67
119
  throw new Error('created capability original task is no longer current');
68
120
  }
69
121
  const evidence = [original.projection];
70
- const items = [];
122
+ const rows = [];
71
123
  let watermark = original.scopeWatermark;
72
124
  for (const [inboxId, group] of grouped) {
73
125
  const source = readTask(input, inboxId);
74
126
  if (!source)
75
127
  continue;
76
- const originalSource = delivery.inspectOwnerForegroundTaskSource({ authorityId: owner.authorityId,
77
- principalId: owner.principalId, workspace: owner.workspace, agentPreset: owner.agentPreset,
78
- expectedOwner: { authorityHash: owner.authorityHash, principalRecordId: owner.principalRecordId,
79
- principalVersion: owner.principalVersion }, inboxId });
80
- if (!originalSource || originalSource.binding.sessionId !== group.sessionId
81
- || source.source.sessionId !== group.sessionId
82
- || source.owner.bindingVersion !== originalSource.binding.version
83
- || source.owner.generation !== originalSource.binding.generation
84
- || originalSource.authorityId !== owner.authorityId || originalSource.authorityHash !== owner.authorityHash
85
- || originalSource.principalId !== owner.principalId
86
- || originalSource.owner.principalRecordId !== owner.principalRecordId
87
- || originalSource.owner.principalVersion !== owner.principalVersion)
128
+ const originalSource = originalTaskSource(input, source, inboxId);
129
+ if (!originalSource || originalSource.binding.sessionId !== group.sessionId)
88
130
  continue;
89
- const valid = group.calls.filter(call => {
90
- const task = call.foreground.task;
91
- return task.binding.id === originalSource.binding.id && task.binding.version === originalSource.binding.version
92
- && task.binding.generation === originalSource.binding.generation;
93
- });
131
+ const valid = group.calls.filter(call => sameBinding(call, originalSource.binding));
94
132
  if (valid.length === 0)
95
133
  continue;
96
134
  evidence.push(source.canonical.projection);
97
135
  watermark = Math.max(watermark, source.canonical.scopeWatermark);
98
136
  const projection = source.canonical.projection;
99
- items.push({ protocol: 'dsh-created-capability-task-association/v1', planId: record.planId,
100
- inboxId, sessionId: group.sessionId, callKeys: valid.map(call => call.key).sort(),
101
- receiptDigest, artifactSha256: receipt.artifactSha256, schemaDigest: receipt.schemaDigest,
102
- adoptionStatus: record.status,
103
- withinSignedUseWindow: now >= receipt.adoptedAt && now < receipt.expiresAt,
104
- task: { projection: { subjectKind: 'foreground-turn', subjectRef: inboxId, version: projection.version,
105
- digest: projection.digest, disposition: 'upsert' }, scopeWatermark: source.canonical.scopeWatermark,
106
- outcomeId: source.canonical.triggerOutcomeId,
107
- judgement: source.judgement,
108
- status: source.canonical.objective.status,
109
- sourceDigest: controlPlaneDigest({ protocol: source.protocol, source: source.source,
110
- judgement: source.judgement, ownerRevision: source.ownerRevision }) } });
137
+ const latencyMs = taskLatencyMs(originalSource.execution);
138
+ rows.push({ association: { protocol: 'dsh-created-capability-task-association/v1', planId: record.planId,
139
+ inboxId, sessionId: group.sessionId, callKeys: valid.map(call => call.key).sort(),
140
+ receiptDigest, artifactSha256: receipt.artifactSha256, schemaDigest: receipt.schemaDigest,
141
+ adoptionStatus: record.status,
142
+ withinSignedUseWindow: now >= receipt.adoptedAt && now < receipt.expiresAt,
143
+ task: { projection: { subjectKind: 'foreground-turn', subjectRef: inboxId, version: projection.version,
144
+ digest: projection.digest, disposition: 'upsert' }, scopeWatermark: source.canonical.scopeWatermark,
145
+ outcomeId: source.canonical.triggerOutcomeId,
146
+ judgement: source.judgement,
147
+ status: source.canonical.objective.status,
148
+ sourceDigest: controlPlaneDigest({ protocol: source.protocol, source: source.source,
149
+ judgement: source.judgement, ownerRevision: source.ownerRevision }) } },
150
+ model: taskModel(source.source), callLatencyMs: valid.map(call => call.settledAt - call.claimedAt),
151
+ ...(latencyMs === undefined ? {} : { taskLatencyMs: latencyMs }) });
111
152
  }
112
- return { original, items, evidence, watermark };
153
+ return { original, rows, evidence, watermark };
113
154
  };
114
155
  try {
115
156
  const before = read();
@@ -118,15 +159,456 @@ export function inspectCreationCapabilityTaskAssociations(input) {
118
159
  const during = read();
119
160
  if (!same(during, before))
120
161
  throw new Error('created capability task association source changed');
121
- return during.items;
162
+ return during.rows;
163
+ });
164
+ return result.matched ? { rows: result.value, candidates: grouped.size, receiptDigest, now } : undefined;
165
+ }
166
+ catch {
167
+ return undefined;
168
+ }
169
+ }
170
+ /** No cache: every result is checked against the current canonical writer and owner route. */
171
+ export function inspectCreationCapabilityTaskAssociations(input) {
172
+ const rows = readAssociationRows(input, false)?.rows ?? [];
173
+ return rows.length === 0 ? [] : Object.freeze(rows.map(({ association: item }) => Object.freeze({ ...item,
174
+ callKeys: Object.freeze([...item.callKeys]),
175
+ task: Object.freeze({ ...item.task, projection: Object.freeze({ ...item.task.projection }) }),
176
+ })));
177
+ }
178
+ const modelKey = (model) => model.state === 'frozen'
179
+ ? JSON.stringify([model.state, model.provider, model.model, model.reasoningEffort ?? null]) : JSON.stringify([model.state]);
180
+ function latency(values, minimumSamples) {
181
+ if (values.length === 0)
182
+ return { status: 'unmeasured', samples: 0 };
183
+ if (values.length < minimumSamples)
184
+ return { status: 'insufficient', samples: values.length };
185
+ const sorted = [...values].sort((a, b) => a - b);
186
+ return { status: 'measured', samples: sorted.length, p50Ms: sorted[Math.ceil(sorted.length / 2) - 1],
187
+ maxMs: sorted[sorted.length - 1] };
188
+ }
189
+ function measures(rows, minimumSamples) {
190
+ const achieved = rows.filter(row => row.association.task.status === 'achieved').length;
191
+ return { quality: { status: rows.length < minimumSamples ? 'insufficient' : 'measured', samples: rows.length,
192
+ achieved, notAchieved: rows.length - achieved },
193
+ callLatency: latency(rows.flatMap(row => row.callLatencyMs), minimumSamples),
194
+ taskLatency: latency(rows.flatMap(row => row.taskLatencyMs === undefined ? [] : [row.taskLatencyMs]), minimumSamples) };
195
+ }
196
+ function deepFreeze(value) {
197
+ if (typeof value === 'object' && value !== null) {
198
+ for (const item of Object.values(value))
199
+ deepFreeze(item);
200
+ Object.freeze(value);
201
+ }
202
+ return value;
203
+ }
204
+ /**
205
+ * Descriptive, non-causal counts over the same fenced current associations: one latest-revision quality sample per
206
+ * Inbox, strata by frozen task model only. Undefined only for a missing receipt, a failed fence or a throw.
207
+ */
208
+ export function inspectCreationCapabilityOrdinaryUseReport(input) {
209
+ const minimumSamples = input.minimumSamples ?? DEFAULT_MINIMUM_SAMPLES;
210
+ if (!Number.isSafeInteger(minimumSamples) || minimumSamples < 1) {
211
+ throw new TypeError('created capability ordinary-use minimumSamples must be a positive safe integer');
212
+ }
213
+ const read = readAssociationRows(input, true);
214
+ const receipt = input.record.receipt;
215
+ if (!read || !receipt)
216
+ return undefined;
217
+ const rows = [...read.rows].sort((a, b) => a.association.inboxId < b.association.inboxId ? -1
218
+ : a.association.inboxId > b.association.inboxId ? 1 : 0);
219
+ const buckets = new Map();
220
+ for (const row of rows) {
221
+ const key = modelKey(row.model);
222
+ const bucket = buckets.get(key);
223
+ if (bucket)
224
+ bucket.rows.push(row);
225
+ else
226
+ buckets.set(key, { model: row.model, rows: [row] });
227
+ }
228
+ const strata = [...buckets].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
229
+ .map(([, bucket]) => ({ model: bucket.model, ...measures(bucket.rows, minimumSamples) }));
230
+ return deepFreeze({ protocol: 'dsh-created-capability-ordinary-use-report/v1', causal: false,
231
+ planId: input.record.planId, receiptDigest: read.receiptDigest, artifactSha256: receipt.artifactSha256,
232
+ schemaDigest: receipt.schemaDigest, adoptionStatus: input.record.status,
233
+ withinSignedUseWindow: read.now >= receipt.adoptedAt && read.now < receipt.expiresAt, minimumSamples,
234
+ associationsDigest: controlPlaneDigest(rows.map(row => row.association)),
235
+ unknown: { inboxes: read.candidates - rows.length }, overall: measures(rows, minimumSamples), strata,
236
+ regressions: { status: 'unmeasured' }, cost: { status: 'unmeasured' } });
237
+ }
238
+ export const DEFAULT_MAX_SCANNED_HEADS = 5_000;
239
+ /** Every current canonical head of the scope under one unchanged scope watermark; throws when it moves or overflows. */
240
+ function scanScope(evaluation, scope, maxScannedHeads) {
241
+ const heads = [];
242
+ let after;
243
+ let scopeWatermark;
244
+ for (;;) {
245
+ const page = evaluation.listTrustedTaskLearningProjections({ scope, ...(after ? { after } : {}), limit: 100 });
246
+ if (scopeWatermark !== undefined && page.scopeWatermark !== scopeWatermark) {
247
+ throw new Error('created capability attribution scope changed during scan');
248
+ }
249
+ scopeWatermark = page.scopeWatermark;
250
+ heads.push(...page.items.map(item => item.receipt));
251
+ if (heads.length > maxScannedHeads)
252
+ throw new Error('created capability attribution scope is too large');
253
+ if (!page.hasMore)
254
+ return { heads, scopeWatermark: page.scopeWatermark };
255
+ after = page.nextCursor;
256
+ }
257
+ }
258
+ function arm(rows, minimumSamples) {
259
+ const achieved = rows.filter(row => row.status === 'achieved').length;
260
+ return { quality: { status: rows.length < minimumSamples ? 'insufficient' : 'measured', samples: rows.length,
261
+ achieved, notAchieved: rows.length - achieved },
262
+ taskLatency: latency(rows.flatMap(row => row.taskLatencyMs === undefined ? [] : [row.taskLatencyMs]), minimumSamples) };
263
+ }
264
+ /** a1/n1 - a0/n0 = (a1*n0 - a0*n1) / (n0*n1), exact and unreduced. */
265
+ function delta(before, after) {
266
+ if (before.quality.status !== 'measured' || after.quality.status !== 'measured')
267
+ return { status: 'insufficient' };
268
+ const numerator = after.quality.achieved * before.quality.samples - before.quality.achieved * after.quality.samples;
269
+ return { status: 'paired', numerator, denominator: before.quality.samples * after.quality.samples,
270
+ sign: numerator > 0 ? 1 : numerator < 0 ? -1 : 0 };
271
+ }
272
+ /**
273
+ * Intention-to-treat before/after attribution of one adoption: every certified current owner foreground task (one
274
+ * latest-revision head per Inbox; corrections replace, withdrawals retract) in equal windows around adoption, by
275
+ * frozen task model. Read once, then reread under an unchanged Evaluation scope watermark. Undefined for a missing
276
+ * receipt, an oversized scope, a changed source, scope or fence, or a throw. Observational, never causal.
277
+ */
278
+ export function inspectCreationCapabilityAvailabilityAttribution(input) {
279
+ const minimumSamples = input.minimumSamples ?? DEFAULT_MINIMUM_SAMPLES;
280
+ const maxScannedHeads = input.maxScannedHeads ?? DEFAULT_MAX_SCANNED_HEADS;
281
+ for (const [name, value] of [['minimumSamples', minimumSamples], ['maxScannedHeads', maxScannedHeads]]) {
282
+ if (!Number.isSafeInteger(value) || value < 1) {
283
+ throw new TypeError(`created capability attribution ${name} must be a positive safe integer`);
284
+ }
285
+ }
286
+ if (input.windowMs !== undefined && (!Number.isSafeInteger(input.windowMs) || input.windowMs < 1)) {
287
+ throw new TypeError('created capability attribution windowMs must be a positive safe integer');
288
+ }
289
+ const { record, owner, evaluation } = input;
290
+ const receipt = record.receipt;
291
+ if (!receipt || receipt.planId !== record.planId || input.triggerInboxId.length === 0)
292
+ return undefined;
293
+ const now = input.now ?? Date.now();
294
+ const receiptDigest = controlPlaneDigest(receipt);
295
+ const elapsed = Math.max(0, Math.min(now, receipt.expiresAt) - receipt.adoptedAt);
296
+ const windowMs = Math.min(input.windowMs ?? elapsed, elapsed);
297
+ const grouped = eligibleCalls(input, receipt, receiptDigest, now);
298
+ if (!grouped)
299
+ return undefined;
300
+ const scope = evaluation.canonicalHostScope({ workspace: owner.workspace, preset: owner.agentPreset });
301
+ const scan = () => scanScope(evaluation, scope, maxScannedHeads);
302
+ const read = (heads) => {
303
+ const original = input.readSourceCurrent();
304
+ if (original.projection.subjectKind !== 'foreground-turn'
305
+ || original.projection.subjectRef !== input.triggerInboxId || original.projection.disposition !== 'upsert') {
306
+ throw new Error('created capability original task is no longer current');
307
+ }
308
+ const excluded = { nonForeground: 0, trigger: 0, retracted: 0, unverified: 0, outsideWindow: 0 };
309
+ const rows = [];
310
+ for (const head of heads) {
311
+ const inboxId = head.projection.subjectRef;
312
+ if (head.projection.subjectKind !== 'foreground-turn') {
313
+ excluded.nonForeground++;
314
+ continue;
315
+ }
316
+ if (inboxId === input.triggerInboxId) {
317
+ excluded.trigger++;
318
+ continue;
319
+ }
320
+ if (head.projection.disposition !== 'upsert') {
321
+ excluded.retracted++;
322
+ continue;
323
+ }
324
+ const source = certifiedTask(input, head, inboxId);
325
+ const originalSource = source && originalTaskSource(input, source, inboxId);
326
+ const dispatchedAt = originalSource?.execution?.dispatchedAt;
327
+ if (!source || !originalSource || !Number.isSafeInteger(dispatchedAt)) {
328
+ excluded.unverified++;
329
+ continue;
330
+ }
331
+ const at = dispatchedAt;
332
+ const side = windowMs > 0 && at >= receipt.adoptedAt - windowMs && at <= receipt.adoptedAt ? 'before'
333
+ : windowMs > 0 && at > receipt.adoptedAt && at <= receipt.adoptedAt + windowMs ? 'after' : undefined;
334
+ if (!side) {
335
+ excluded.outsideWindow++;
336
+ continue;
337
+ }
338
+ const group = grouped.get(inboxId);
339
+ const latencyMs = taskLatencyMs(originalSource.execution);
340
+ rows.push({ arm: side, inboxId, model: taskModel(source.source),
341
+ status: source.canonical.objective.status,
342
+ projection: { version: head.projection.version, digest: head.projection.digest },
343
+ outcomeId: head.triggerOutcomeId,
344
+ sourceDigest: controlPlaneDigest({ protocol: source.protocol, source: source.source,
345
+ judgement: source.judgement, ownerRevision: source.ownerRevision }),
346
+ exposed: side === 'after' && group !== undefined && group.sessionId === originalSource.binding.sessionId
347
+ && group.calls.some(call => sameBinding(call, originalSource.binding)),
348
+ ...(latencyMs === undefined ? {} : { taskLatencyMs: latencyMs }) });
349
+ }
350
+ rows.sort((a, b) => a.inboxId < b.inboxId ? -1 : a.inboxId > b.inboxId ? 1 : 0);
351
+ return { original, rows, excluded };
352
+ };
353
+ let result;
354
+ try {
355
+ const scanned = scan();
356
+ const before = read(scanned.heads);
357
+ // An unchanged scope watermark means an unchanged feed; Delivery and the original source are reread inside.
358
+ const fenced = evaluation.withTrustedCanonicalScopeWriterFence({ scope, scopeWatermark: scanned.scopeWatermark }, () => {
359
+ const during = read(scanned.heads);
360
+ if (!same(during, before))
361
+ throw new Error('created capability attribution source changed');
362
+ return during;
363
+ });
364
+ if (!fenced.matched)
365
+ return undefined;
366
+ result = { ...scanned, read: fenced.value };
367
+ }
368
+ catch {
369
+ return undefined;
370
+ }
371
+ const { rows, excluded } = result.read;
372
+ const buckets = new Map();
373
+ for (const row of rows) {
374
+ const key = modelKey(row.model);
375
+ const bucket = buckets.get(key);
376
+ if (bucket)
377
+ bucket.rows.push(row);
378
+ else
379
+ buckets.set(key, { model: row.model, rows: [row] });
380
+ }
381
+ const strata = [...buckets].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
382
+ .map(([, bucket]) => {
383
+ const beforeArm = arm(bucket.rows.filter(row => row.arm === 'before'), minimumSamples);
384
+ const afterArm = arm(bucket.rows.filter(row => row.arm === 'after'), minimumSamples);
385
+ return { model: bucket.model, before: beforeArm, after: afterArm, achievedRateDelta: delta(beforeArm, afterArm) };
386
+ });
387
+ const signs = new Set(strata.flatMap(stratum => stratum.achievedRateDelta.status === 'paired'
388
+ ? [stratum.achievedRateDelta.sign] : []));
389
+ const direction = signs.size === 0 ? 'insufficient' : signs.size > 1 ? 'mixed'
390
+ : signs.has(1) ? 'improved' : signs.has(-1) ? 'regressed' : 'unchanged';
391
+ const concurrent = windowMs === 0 ? 0 : (input.otherAdoptedAt ?? []).filter(at => Number.isFinite(at)
392
+ && at >= receipt.adoptedAt - windowMs && at <= receipt.adoptedAt + windowMs).length;
393
+ const afterRows = rows.filter(row => row.arm === 'after');
394
+ return deepFreeze({ protocol: 'dsh-created-capability-availability-attribution/v1', causal: false,
395
+ design: 'adoption-before-after-itt-model-stratified', planId: record.planId, receiptDigest,
396
+ artifactSha256: receipt.artifactSha256, schemaDigest: receipt.schemaDigest, adoptionStatus: record.status,
397
+ withinSignedUseWindow: now >= receipt.adoptedAt && now < receipt.expiresAt, minimumSamples, windowMs,
398
+ scopeWatermark: result.scopeWatermark, rowsDigest: controlPlaneDigest(rows),
399
+ population: { scanned: result.heads.length, counted: rows.length, excluded },
400
+ exposure: { afterTasks: afterRows.length, exposedTasks: afterRows.filter(row => row.exposed).length },
401
+ concurrentAdoptions: { status: concurrent === 0 ? 'none' : 'flagged', count: concurrent },
402
+ strata, direction, regressions: { status: 'unmeasured' }, cost: { status: 'unmeasured' } });
403
+ }
404
+ export const TREND_DEFAULT_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
405
+ export const TREND_WINDOW_MS_BOUNDS = { min: 60 * 60 * 1000, max: 90 * 24 * 60 * 60 * 1000 };
406
+ export const TREND_DEFAULT_PRE_WINDOWS = 4;
407
+ export const TREND_DEFAULT_MAX_POST_WINDOWS = 12;
408
+ export const TREND_WINDOW_COUNT_MAX = 52;
409
+ export const TREND_DEFAULT_PARALLEL_TREND_THRESHOLD = 0.2;
410
+ /** Fixed, machine-readable statement of what the estimate assumes; it is never a causal claim. */
411
+ export const TREND_ASSUMPTIONS = Object.freeze([
412
+ 'parallel-trends: without this adoption the treated and control session cohorts would change achieved rate equally',
413
+ 'session-cohort: a session is treated when a counted after-window task in it has an exact call of this adoption; membership is chosen after adoption, so selection on post-adoption use is not removed',
414
+ 'no-spillover: control sessions are assumed unaffected although the capability is available to them',
415
+ 'independent-tasks: the 95% interval is a four-cell Price-Bonett adjusted Wald interval (each cell (y+0.5)/(n+1)), centered on the adjusted estimate, over independent task outcomes',
416
+ 'session-rotation: after an owner DM /new rotation the earlier session is a time-split control of the same owner, not a parallel one',
417
+ 'availability: after windows end at min(now, expiresAt) only; a non-active adoption record (closed, rejected, unknown) is still treated as available and flagged adoption-not-active',
418
+ ]);
419
+ const quality = (rows, minimumSamples) => {
420
+ const achieved = rows.filter(row => row.status === 'achieved').length;
421
+ return { status: rows.length < minimumSamples ? 'insufficient' : 'measured', samples: rows.length,
422
+ achieved, notAchieved: rows.length - achieved };
423
+ };
424
+ const rate = (cell) => cell.achieved / cell.samples;
425
+ function differenceInDifferences(cells) {
426
+ if (cells.some(cell => cell.status !== 'measured'))
427
+ return { status: 'insufficient' };
428
+ const [t0, t1, c0, c1] = cells;
429
+ const estimate = (rate(t1) - rate(t0)) - (rate(c1) - rate(c0));
430
+ // Price-Bonett: half a success and half a failure per cell, interval centered on the adjusted contrast.
431
+ const adjusted = (cell) => (cell.achieved + 0.5) / (cell.samples + 1);
432
+ const adjustedEstimate = (adjusted(t1) - adjusted(t0)) - (adjusted(c1) - adjusted(c0));
433
+ const standardError = Math.sqrt(cells.reduce((sum, cell) => {
434
+ const p = adjusted(cell);
435
+ return sum + p * (1 - p) / (cell.samples + 1);
436
+ }, 0));
437
+ return { status: 'estimated', estimate, adjustedEstimate, standardError, interval95: {
438
+ low: Math.max(-2, adjustedEstimate - 1.96 * standardError), high: Math.min(2, adjustedEstimate + 1.96 * standardError)
439
+ } };
440
+ }
441
+ function parallelTrend(cells, threshold) {
442
+ const tested = cells.filter(cell => cell.index < 0 && cell.treated.status === 'measured' && cell.control.status === 'measured');
443
+ if (tested.length < 2)
444
+ return { status: 'untestable', testedWindows: tested.length };
445
+ const gap = (cell) => rate(cell.treated) - rate(cell.control);
446
+ const gapChange = gap(tested[tested.length - 1]) - gap(tested[0]);
447
+ return { status: Math.abs(gapChange) > threshold ? 'parallel-trend-suspect' : 'not-flagged',
448
+ testedWindows: tested.length, gapChange };
449
+ }
450
+ function boundedInteger(name, value, min, max) {
451
+ if (!Number.isSafeInteger(value) || value < min || value > max) {
452
+ throw new TypeError(`created capability ordinary-use trend ${name} must be an integer in [${min}, ${max}]`);
453
+ }
454
+ return value;
455
+ }
456
+ /**
457
+ * Longitudinal ordinary-use series of one adoption in fixed windows around adoption, by frozen task model, with a
458
+ * same-scope session-cohort control and a pooled difference-in-differences estimate. Every certified current owner
459
+ * foreground task counts once by its latest revision (corrections replace, withdrawals retract). Read once, then
460
+ * reread under an unchanged Evaluation scope watermark. Undefined for a missing receipt, an oversized scope, a changed
461
+ * source, scope or fence, or a throw. Pure read; observational, never causal.
462
+ */
463
+ export function inspectCreationCapabilityOrdinaryUseTrend(input) {
464
+ const minimumSamples = boundedInteger('minimumSamples', input.minimumSamples ?? DEFAULT_MINIMUM_SAMPLES, 1, Number.MAX_SAFE_INTEGER);
465
+ const maxScannedHeads = boundedInteger('maxScannedHeads', input.maxScannedHeads ?? DEFAULT_MAX_SCANNED_HEADS, 1, Number.MAX_SAFE_INTEGER);
466
+ const windowMs = boundedInteger('windowMs', input.windowMs ?? TREND_DEFAULT_WINDOW_MS, TREND_WINDOW_MS_BOUNDS.min, TREND_WINDOW_MS_BOUNDS.max);
467
+ const preWindows = boundedInteger('preWindows', input.preWindows ?? TREND_DEFAULT_PRE_WINDOWS, 2, TREND_WINDOW_COUNT_MAX);
468
+ const maxPostWindows = boundedInteger('maxPostWindows', input.maxPostWindows ?? TREND_DEFAULT_MAX_POST_WINDOWS, 1, TREND_WINDOW_COUNT_MAX);
469
+ const threshold = input.parallelTrendThreshold ?? TREND_DEFAULT_PARALLEL_TREND_THRESHOLD;
470
+ if (!Number.isFinite(threshold) || threshold <= 0 || threshold > 1) {
471
+ throw new TypeError('created capability ordinary-use trend parallelTrendThreshold must be in (0, 1]');
472
+ }
473
+ const { record, owner, evaluation } = input;
474
+ const receipt = record.receipt;
475
+ if (!receipt || receipt.planId !== record.planId || input.triggerInboxId.length === 0)
476
+ return undefined;
477
+ const now = input.now ?? Date.now();
478
+ const receiptDigest = controlPlaneDigest(receipt);
479
+ const observedUntil = Math.max(receipt.adoptedAt, Math.min(now, receipt.expiresAt));
480
+ const postWindows = Math.min(maxPostWindows, Math.ceil((observedUntil - receipt.adoptedAt) / windowMs));
481
+ const windows = [];
482
+ for (let index = -preWindows; index < postWindows; index++) {
483
+ const fullEnd = receipt.adoptedAt + (index + 1) * windowMs;
484
+ // After windows never extend past min(now, expiresAt); the latest one is clipped there and marked incomplete.
485
+ windows.push({ index, startExclusive: fullEnd - windowMs,
486
+ endInclusive: index < 0 ? fullEnd : Math.min(fullEnd, observedUntil), complete: index < 0 || fullEnd <= observedUntil });
487
+ }
488
+ const first = windows[0].startExclusive;
489
+ const last = Math.min(windows[windows.length - 1].endInclusive, observedUntil);
490
+ // A dispatch exactly at adoption precedes it, matching the strict call admission rule. Dispatches after
491
+ // min(now, expiresAt) are outside every window, so an expired adoption stops accruing after tasks.
492
+ const windowOf = (at) => at <= first || at > last ? undefined
493
+ : Math.ceil((at - receipt.adoptedAt) / windowMs) - 1;
494
+ const grouped = eligibleCalls(input, receipt, receiptDigest, now);
495
+ if (!grouped)
496
+ return undefined;
497
+ const scope = evaluation.canonicalHostScope({ workspace: owner.workspace, preset: owner.agentPreset });
498
+ const read = (heads) => {
499
+ const original = input.readSourceCurrent();
500
+ if (original.projection.subjectKind !== 'foreground-turn'
501
+ || original.projection.subjectRef !== input.triggerInboxId || original.projection.disposition !== 'upsert') {
502
+ throw new Error('created capability original task is no longer current');
503
+ }
504
+ const excluded = { nonForeground: 0, trigger: 0, retracted: 0, unverified: 0, outsideWindow: 0 };
505
+ const rows = [];
506
+ for (const head of heads) {
507
+ const inboxId = head.projection.subjectRef;
508
+ if (head.projection.subjectKind !== 'foreground-turn') {
509
+ excluded.nonForeground++;
510
+ continue;
511
+ }
512
+ if (inboxId === input.triggerInboxId) {
513
+ excluded.trigger++;
514
+ continue;
515
+ }
516
+ if (head.projection.disposition !== 'upsert') {
517
+ excluded.retracted++;
518
+ continue;
519
+ }
520
+ const source = certifiedTask(input, head, inboxId);
521
+ const originalSource = source && originalTaskSource(input, source, inboxId);
522
+ const dispatchedAt = originalSource?.execution?.dispatchedAt;
523
+ if (!source || !originalSource || !Number.isSafeInteger(dispatchedAt)) {
524
+ excluded.unverified++;
525
+ continue;
526
+ }
527
+ const index = windowOf(dispatchedAt);
528
+ if (index === undefined) {
529
+ excluded.outsideWindow++;
530
+ continue;
531
+ }
532
+ const group = grouped.get(inboxId);
533
+ const calls = index >= 0 && group && group.sessionId === originalSource.binding.sessionId
534
+ ? group.calls.filter(call => sameBinding(call, originalSource.binding)).length : 0;
535
+ rows.push({ index, inboxId, sessionId: originalSource.binding.sessionId, model: taskModel(source.source),
536
+ status: source.canonical.objective.status,
537
+ projection: { version: head.projection.version, digest: head.projection.digest },
538
+ outcomeId: head.triggerOutcomeId,
539
+ sourceDigest: controlPlaneDigest({ protocol: source.protocol, source: source.source,
540
+ judgement: source.judgement, ownerRevision: source.ownerRevision }), calls });
541
+ }
542
+ rows.sort((a, b) => a.inboxId < b.inboxId ? -1 : a.inboxId > b.inboxId ? 1 : 0);
543
+ return { original, rows, excluded };
544
+ };
545
+ let result;
546
+ try {
547
+ const scanned = scanScope(evaluation, scope, maxScannedHeads);
548
+ const before = read(scanned.heads);
549
+ const fenced = evaluation.withTrustedCanonicalScopeWriterFence({ scope, scopeWatermark: scanned.scopeWatermark }, () => {
550
+ const during = read(scanned.heads);
551
+ if (!same(during, before))
552
+ throw new Error('created capability ordinary-use trend source changed');
553
+ return during;
122
554
  });
123
- return result.matched ? Object.freeze(result.value.map(item => Object.freeze({ ...item,
124
- callKeys: Object.freeze([...item.callKeys]),
125
- task: Object.freeze({ ...item.task, projection: Object.freeze({ ...item.task.projection }) }),
126
- }))) : [];
555
+ if (!fenced.matched)
556
+ return undefined;
557
+ result = { ...scanned, read: fenced.value };
127
558
  }
128
559
  catch {
129
- return [];
560
+ return undefined;
130
561
  }
562
+ const { rows, excluded } = result.read;
563
+ const treatedSessions = new Set(rows.filter(row => row.calls > 0).map(row => row.sessionId));
564
+ const controlSessions = new Set(rows.filter(row => !treatedSessions.has(row.sessionId)).map(row => row.sessionId));
565
+ const buckets = new Map();
566
+ for (const row of rows) {
567
+ const key = modelKey(row.model);
568
+ const bucket = buckets.get(key);
569
+ if (bucket)
570
+ bucket.rows.push(row);
571
+ else
572
+ buckets.set(key, { model: row.model, rows: [row] });
573
+ }
574
+ const strata = [...buckets].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)
575
+ .map(([, bucket]) => {
576
+ const treated = bucket.rows.filter(row => treatedSessions.has(row.sessionId));
577
+ const control = bucket.rows.filter(row => !treatedSessions.has(row.sessionId));
578
+ const cells = windows.map(({ index }) => {
579
+ const inWindow = bucket.rows.filter(row => row.index === index);
580
+ return { index, treated: quality(treated.filter(row => row.index === index), minimumSamples),
581
+ control: quality(control.filter(row => row.index === index), minimumSamples),
582
+ calls: inWindow.reduce((sum, row) => sum + row.calls, 0),
583
+ exposedTasks: inWindow.filter(row => row.calls > 0).length };
584
+ });
585
+ const pooled = (rows, after) => quality(rows.filter(row => (row.index >= 0) === after), minimumSamples);
586
+ return { model: bucket.model, cells,
587
+ differenceInDifferences: differenceInDifferences([pooled(treated, false), pooled(treated, true),
588
+ pooled(control, false), pooled(control, true)]),
589
+ parallelTrend: parallelTrend(cells, threshold) };
590
+ });
591
+ const concurrent = (input.otherAdoptedAt ?? []).filter(at => Number.isFinite(at) && at > first && at <= last).length;
592
+ const flags = [];
593
+ if (strata.some(stratum => stratum.parallelTrend.status === 'parallel-trend-suspect'))
594
+ flags.push('parallel-trend-suspect');
595
+ if (controlSessions.size === 0)
596
+ flags.push('no-control-cohort');
597
+ if (concurrent > 0)
598
+ flags.push('concurrent-adoptions');
599
+ if (windows.some(window => !window.complete))
600
+ flags.push('partial-window');
601
+ if (record.status !== 'active')
602
+ flags.push('adoption-not-active');
603
+ return deepFreeze({ protocol: 'dsh-created-capability-ordinary-use-trend/v1', causal: false,
604
+ design: 'session-cohort-difference-in-differences-model-stratified', planId: record.planId, receiptDigest,
605
+ artifactSha256: receipt.artifactSha256, schemaDigest: receipt.schemaDigest, adoptionStatus: record.status,
606
+ withinSignedUseWindow: now >= receipt.adoptedAt && now < receipt.expiresAt, minimumSamples, windowMs,
607
+ parallelTrendThreshold: threshold, scopeWatermark: result.scopeWatermark,
608
+ rowsDigest: controlPlaneDigest(rows.map(({ sessionId: _sessionId, ...row }) => row)), windows,
609
+ population: { scanned: result.heads.length, counted: rows.length, excluded },
610
+ cohorts: { treatedSessions: treatedSessions.size, controlSessions: controlSessions.size },
611
+ concurrentAdoptions: { status: concurrent === 0 ? 'none' : 'flagged', count: concurrent },
612
+ strata, flags, assumptions: [...TREND_ASSUMPTIONS], regressions: { status: 'unmeasured' }, cost: { status: 'unmeasured' } });
131
613
  }
132
614
  //# sourceMappingURL=creation-capability-feedback.js.map