@deftai/directive-core 0.104.0 → 0.105.0

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 (225) hide show
  1. package/dist/authz/classify.js +526 -42
  2. package/dist/check/cached-orchestrator.d.ts +1 -1
  3. package/dist/check/cached-orchestrator.js +2 -2
  4. package/dist/check/named-cause.d.ts +1 -1
  5. package/dist/check/named-cause.js +33 -3
  6. package/dist/codebase/default-extractor.d.ts +5 -0
  7. package/dist/codebase/default-extractor.js +7 -15
  8. package/dist/deposit/copy-tree.js +60 -0
  9. package/dist/deposit/python-free.js +26 -18
  10. package/dist/doctor/checks.d.ts +8 -0
  11. package/dist/doctor/checks.js +37 -25
  12. package/dist/doctor/doctor-state.d.ts +4 -0
  13. package/dist/doctor/doctor-state.js +7 -1
  14. package/dist/doctor/main.js +9 -19
  15. package/dist/doctor/types.d.ts +9 -0
  16. package/dist/fs/contained-write.d.ts +115 -1
  17. package/dist/fs/contained-write.js +241 -5
  18. package/dist/fs/mutation-ledger.d.ts +54 -0
  19. package/dist/fs/mutation-ledger.js +133 -0
  20. package/dist/fs/non-product-dirs.d.ts +79 -0
  21. package/dist/fs/non-product-dirs.js +96 -0
  22. package/dist/hooks/dispatcher.d.ts +3 -1
  23. package/dist/hooks/dispatcher.js +14 -0
  24. package/dist/index.d.ts +1 -0
  25. package/dist/index.js +2 -0
  26. package/dist/init-deposit/agent-hooks.js +17 -15
  27. package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
  28. package/dist/init-deposit/core-guard-pin-content.js +393 -0
  29. package/dist/init-deposit/gitignore.js +9 -0
  30. package/dist/init-deposit/hygiene.d.ts +47 -11
  31. package/dist/init-deposit/hygiene.js +172 -37
  32. package/dist/init-deposit/legacy-detect.js +6 -2
  33. package/dist/init-deposit/refresh.d.ts +21 -1
  34. package/dist/init-deposit/refresh.js +106 -9
  35. package/dist/init-deposit/scaffold.js +28 -279
  36. package/dist/init-deposit/slash-deposit.js +13 -7
  37. package/dist/init-deposit/xbrief-projections.d.ts +8 -1
  38. package/dist/init-deposit/xbrief-projections.js +24 -11
  39. package/dist/intake/clause-derivation.d.ts +38 -1
  40. package/dist/intake/clause-derivation.js +211 -9
  41. package/dist/intake/issue-ingest.d.ts +3 -1
  42. package/dist/intake/issue-ingest.js +35 -6
  43. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
  44. package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
  45. package/dist/literal-acceptance/capture.d.ts +10 -0
  46. package/dist/literal-acceptance/capture.js +224 -75
  47. package/dist/literal-acceptance/evaluate.d.ts +29 -4
  48. package/dist/literal-acceptance/evaluate.js +70 -2
  49. package/dist/literal-acceptance/index.d.ts +3 -3
  50. package/dist/literal-acceptance/index.js +3 -3
  51. package/dist/literal-acceptance/safety.d.ts +41 -0
  52. package/dist/literal-acceptance/safety.js +126 -2
  53. package/dist/literal-acceptance/types.d.ts +8 -0
  54. package/dist/orphan-active/evaluate.d.ts +7 -1
  55. package/dist/orphan-active/evaluate.js +102 -23
  56. package/dist/platform/platform-capabilities.js +2 -3
  57. package/dist/policy/base-branch.d.ts +37 -0
  58. package/dist/policy/base-branch.js +114 -0
  59. package/dist/policy/branch-sync.d.ts +80 -0
  60. package/dist/policy/branch-sync.js +211 -0
  61. package/dist/policy/ceremony-dial-escalation.js +1 -0
  62. package/dist/policy/ceremony-dial.js +4 -14
  63. package/dist/policy/delivery-branch.d.ts +5 -3
  64. package/dist/policy/delivery-branch.js +7 -3
  65. package/dist/policy/file-size-thresholds.d.ts +24 -0
  66. package/dist/policy/file-size-thresholds.js +23 -0
  67. package/dist/policy/forge-outage-retry.d.ts +47 -0
  68. package/dist/policy/forge-outage-retry.js +157 -0
  69. package/dist/policy/index.d.ts +7 -0
  70. package/dist/policy/index.js +66 -7
  71. package/dist/policy/project-invariants.d.ts +50 -0
  72. package/dist/policy/project-invariants.js +205 -0
  73. package/dist/policy/sync-default.d.ts +129 -0
  74. package/dist/policy/sync-default.js +452 -0
  75. package/dist/policy/sync-max-files.d.ts +93 -0
  76. package/dist/policy/sync-max-files.js +192 -0
  77. package/dist/pr-monitor/absent-required.d.ts +11 -0
  78. package/dist/pr-monitor/absent-required.js +61 -0
  79. package/dist/pr-monitor/constants.d.ts +7 -0
  80. package/dist/pr-monitor/constants.js +7 -0
  81. package/dist/pr-monitor/index.d.ts +2 -1
  82. package/dist/pr-monitor/index.js +2 -1
  83. package/dist/pr-monitor/main.js +4 -0
  84. package/dist/pr-monitor/monitor.js +24 -1
  85. package/dist/pr-wait-mergeable/classify.js +6 -0
  86. package/dist/preflight/evaluate.d.ts +4 -0
  87. package/dist/preflight/evaluate.js +35 -1
  88. package/dist/preflight/index.d.ts +4 -0
  89. package/dist/preflight/index.js +2 -0
  90. package/dist/preflight/intended-placement.d.ts +62 -0
  91. package/dist/preflight/intended-placement.js +275 -0
  92. package/dist/preflight/project-invariants-gate.d.ts +29 -0
  93. package/dist/preflight/project-invariants-gate.js +82 -0
  94. package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
  95. package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
  96. package/dist/product-first-done-gate/acceptance.d.ts +7 -4
  97. package/dist/product-first-done-gate/acceptance.js +79 -8
  98. package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
  99. package/dist/product-first-done-gate/empty-resolution.js +12 -1
  100. package/dist/product-first-done-gate/evaluate.d.ts +12 -0
  101. package/dist/product-first-done-gate/evaluate.js +254 -31
  102. package/dist/product-first-done-gate/index.d.ts +2 -0
  103. package/dist/product-first-done-gate/index.js +1 -0
  104. package/dist/release/build-dist.d.ts +101 -0
  105. package/dist/release/build-dist.js +428 -46
  106. package/dist/run-summary/emit.d.ts +16 -10
  107. package/dist/run-summary/emit.js +26 -15
  108. package/dist/run-summary/index.d.ts +3 -2
  109. package/dist/run-summary/index.js +2 -1
  110. package/dist/run-summary/session-id.d.ts +21 -0
  111. package/dist/run-summary/session-id.js +92 -0
  112. package/dist/run-summary/types.d.ts +29 -4
  113. package/dist/run-summary/types.js +1 -0
  114. package/dist/scope/acceptance-evidence.d.ts +19 -7
  115. package/dist/scope/acceptance-evidence.js +38 -11
  116. package/dist/scope/delivery-evidence.d.ts +3 -3
  117. package/dist/scope/delivery-evidence.js +22 -27
  118. package/dist/scope/index.d.ts +1 -0
  119. package/dist/scope/index.js +1 -0
  120. package/dist/scope/parent-lineage.js +7 -6
  121. package/dist/scope/project-invariant-coverage.d.ts +54 -0
  122. package/dist/scope/project-invariant-coverage.js +322 -0
  123. package/dist/scope/transition.js +1 -1
  124. package/dist/scope-provenance/compare-intent.d.ts +27 -0
  125. package/dist/scope-provenance/compare-intent.js +233 -0
  126. package/dist/scope-provenance/digest.d.ts +18 -1
  127. package/dist/scope-provenance/digest.js +16 -11
  128. package/dist/scope-provenance/evaluate.d.ts +5 -1
  129. package/dist/scope-provenance/evaluate.js +64 -4
  130. package/dist/scope-provenance/extract-intent.d.ts +43 -0
  131. package/dist/scope-provenance/extract-intent.js +262 -0
  132. package/dist/scope-provenance/index.d.ts +7 -1
  133. package/dist/scope-provenance/index.js +7 -1
  134. package/dist/scope-provenance/intent-digest.d.ts +12 -0
  135. package/dist/scope-provenance/intent-digest.js +33 -0
  136. package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
  137. package/dist/scope-provenance/intent-evaluate.js +149 -0
  138. package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
  139. package/dist/scope-provenance/json-tokenizer.js +225 -0
  140. package/dist/scope-provenance/known-machine.d.ts +38 -0
  141. package/dist/scope-provenance/known-machine.js +157 -0
  142. package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
  143. package/dist/scope-provenance/mint-artifacts.js +425 -0
  144. package/dist/session/ac-pass-banking.d.ts +4 -36
  145. package/dist/session/ac-pass-banking.js +30 -77
  146. package/dist/session/ac-pass-reuse.d.ts +53 -0
  147. package/dist/session/ac-pass-reuse.js +95 -0
  148. package/dist/session/git.js +19 -4
  149. package/dist/session/index.d.ts +4 -0
  150. package/dist/session/index.js +4 -0
  151. package/dist/session/occupancy-stress-worker.d.ts +7 -0
  152. package/dist/session/occupancy-stress-worker.js +36 -0
  153. package/dist/session/occupancy.d.ts +97 -0
  154. package/dist/session/occupancy.js +422 -0
  155. package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
  156. package/dist/session/product-state-hash.d.ts +26 -0
  157. package/dist/session/product-state-hash.js +683 -0
  158. package/dist/session/session-ready.d.ts +2 -0
  159. package/dist/session/session-ready.js +52 -30
  160. package/dist/session/session-start.d.ts +7 -0
  161. package/dist/session/session-start.js +71 -7
  162. package/dist/session/verify-ac-session-cache.d.ts +42 -0
  163. package/dist/session/verify-ac-session-cache.js +126 -0
  164. package/dist/session/verify-session-ritual.d.ts +5 -0
  165. package/dist/session/verify-session-ritual.js +9 -0
  166. package/dist/slash/openclaw-deposit.js +13 -9
  167. package/dist/slice/constants.d.ts +2 -2
  168. package/dist/slice/constants.js +2 -2
  169. package/dist/slice/existing.js +9 -3
  170. package/dist/slice/lock.d.ts +16 -1
  171. package/dist/slice/lock.js +137 -11
  172. package/dist/story-ready/evaluate.d.ts +2 -0
  173. package/dist/story-ready/evaluate.js +13 -0
  174. package/dist/swarm/complete-cohort.d.ts +3 -0
  175. package/dist/swarm/complete-cohort.js +59 -11
  176. package/dist/swarm/finalize-cohort.js +33 -2
  177. package/dist/swarm/launch.d.ts +22 -0
  178. package/dist/swarm/launch.js +155 -0
  179. package/dist/swarm/routing-set-cli.d.ts +11 -1
  180. package/dist/swarm/routing-set-cli.js +13 -3
  181. package/dist/swarm/routing-verify.d.ts +12 -0
  182. package/dist/swarm/routing-verify.js +59 -20
  183. package/dist/swarm/routing.d.ts +14 -0
  184. package/dist/swarm/routing.js +53 -3
  185. package/dist/telemetry-coverage/fake-trial.js +10 -0
  186. package/dist/telemetry-coverage/kinds.js +2 -0
  187. package/dist/triage/actions/index.js +4 -1
  188. package/dist/ts-check-lane/index.d.ts +3 -0
  189. package/dist/ts-check-lane/index.js +2 -0
  190. package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
  191. package/dist/ts-check-lane/progress-reporter.js +38 -0
  192. package/dist/ts-check-lane/progress.d.ts +32 -0
  193. package/dist/ts-check-lane/progress.js +57 -0
  194. package/dist/ts-check-lane/run-lane.d.ts +3 -1
  195. package/dist/ts-check-lane/run-lane.js +8 -6
  196. package/dist/validate-content/validate-links.d.ts +8 -0
  197. package/dist/validate-content/validate-links.js +9 -15
  198. package/dist/value/feedback-file.js +12 -2
  199. package/dist/vbrief-build/index.d.ts +1 -0
  200. package/dist/vbrief-build/index.js +1 -0
  201. package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
  202. package/dist/vbrief-build/project-invariants-io.js +41 -0
  203. package/dist/vbrief-reconcile/graph.js +18 -1
  204. package/dist/vbrief-reconcile/index.d.ts +1 -1
  205. package/dist/vbrief-reconcile/index.js +1 -1
  206. package/dist/vbrief-reconcile/types.d.ts +13 -0
  207. package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
  208. package/dist/vbrief-reconcile/umbrellas.js +261 -26
  209. package/dist/vbrief-validate/index.d.ts +1 -1
  210. package/dist/vbrief-validate/index.js +1 -1
  211. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  212. package/dist/vbrief-validate/plan-hooks.js +68 -0
  213. package/dist/verify-ac/evaluate.d.ts +7 -1
  214. package/dist/verify-ac/evaluate.js +34 -26
  215. package/dist/verify-ac/flag.d.ts +4 -0
  216. package/dist/verify-ac/flag.js +16 -0
  217. package/dist/verify-ac/index.d.ts +2 -2
  218. package/dist/verify-ac/index.js +2 -2
  219. package/dist/verify-source/contained-writes.js +2 -0
  220. package/dist/verify-source/verify-stubs.d.ts +15 -1
  221. package/dist/verify-source/verify-stubs.js +20 -16
  222. package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
  223. package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
  224. package/dist/xbrief-migrate/migrate-project.js +2 -5
  225. package/package.json +3 -3
@@ -12,6 +12,7 @@ import { readCorePackageVersion } from "../engine-version.js";
12
12
  import { ContainedWriteError, ContainedWriteErrorCode, containedWrite, } from "../fs/contained-write.js";
13
13
  import { assertWriteTargetSafe } from "../fs/projection-containment.js";
14
14
  import { resolveRunSummaryDestination } from "./path.js";
15
+ import { resolveRunSummarySessionId } from "./session-id.js";
15
16
  import { ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
16
17
  function sortKeysDeep(value) {
17
18
  if (Array.isArray(value)) {
@@ -250,6 +251,7 @@ export class RunSummaryEmitter {
250
251
  warned = false;
251
252
  projectRoot;
252
253
  sessionId;
254
+ component;
253
255
  frameworkVersion;
254
256
  now;
255
257
  destination;
@@ -258,7 +260,12 @@ export class RunSummaryEmitter {
258
260
  writeStderr;
259
261
  constructor(options) {
260
262
  this.projectRoot = options.projectRoot;
261
- this.sessionId = options.sessionId;
263
+ this.sessionId = resolveRunSummarySessionId({
264
+ projectRoot: options.projectRoot,
265
+ explicit: options.sessionId,
266
+ env: options.env,
267
+ });
268
+ this.component = options.component?.trim() || undefined;
262
269
  this.frameworkVersion = options.frameworkVersion ?? readCorePackageVersion();
263
270
  this.now = options.now ?? (() => new Date());
264
271
  this.env = options.env ?? process.env;
@@ -285,6 +292,7 @@ export class RunSummaryEmitter {
285
292
  ts: this.now().toISOString(),
286
293
  event,
287
294
  payload,
295
+ ...(this.component !== undefined ? { component: this.component } : {}),
288
296
  ...(denominator !== undefined ? { total_tool_turns: denominator } : {}),
289
297
  };
290
298
  }
@@ -352,6 +360,9 @@ export class RunSummaryEmitter {
352
360
  emitAcceptanceStamp(payload) {
353
361
  return this.emit("acceptance_stamp", payload);
354
362
  }
363
+ emitAcPassBank(payload) {
364
+ return this.emit("ac_pass_bank", payload);
365
+ }
355
366
  /** Emit the harness-supplied denominator when DEFT_TOTAL_TOOL_TURNS is set. */
356
367
  emitKnownToolTurnDenominator() {
357
368
  const n = readEnvToolTurnDenominator(this.env);
@@ -361,14 +372,16 @@ export class RunSummaryEmitter {
361
372
  return this.emitToolTurnDenominator({ total_tool_turns: n });
362
373
  }
363
374
  /**
364
- * Always emit a session denominator when the destination is live (#3356).
375
+ * Emit a session denominator only when a host/harness value is known (#3399).
365
376
  * `emitKnownToolTurnDenominator` stays silent unless DEFT_TOTAL_TOOL_TURNS is
366
- * set; this caller records host planned turns or the session:start CLI floor.
377
+ * set; this caller also records DEFT_MAX_TURNS / host maxTurns.
367
378
  */
368
379
  emitSessionToolTurnDenominator(hostMaxTurns) {
369
- return this.emitToolTurnDenominator({
370
- total_tool_turns: resolveSessionToolTurnDenominator(this.env, hostMaxTurns),
371
- });
380
+ const resolved = resolveSessionToolTurnDenominator(this.env, hostMaxTurns);
381
+ if (resolved === undefined) {
382
+ return { emitted: false, destination: this.destination, line: null, warning: false };
383
+ }
384
+ return this.emitToolTurnDenominator(resolved);
372
385
  }
373
386
  }
374
387
  function readPayloadToolTurnDenominator(payload) {
@@ -384,8 +397,6 @@ export function readEnvToolTurnDenominator(env) {
384
397
  }
385
398
  /** Canonical host planned-turn budget recorded at session:start (#3356). */
386
399
  export const ENV_MAX_TURNS_DENOMINATOR = "DEFT_MAX_TURNS";
387
- /** session:start is one CLI invocation when no host/harness count is known (#3356). */
388
- export const SESSION_START_CLI_INVOCATION_DENOMINATOR = 1;
389
400
  function isPositiveIntegerDenominator(value) {
390
401
  return (typeof value === "number" && Number.isInteger(value) && Number.isFinite(value) && value > 0);
391
402
  }
@@ -408,26 +419,26 @@ function readPositiveIntegerEnv(env, key) {
408
419
  return isPositiveIntegerDenominator(n) ? n : undefined;
409
420
  }
410
421
  /**
411
- * Resolve the session tool/turn denominator (#3356).
422
+ * Resolve the session tool/turn denominator (#3399).
412
423
  *
413
424
  * Prefer harness actuals (`DEFT_TOTAL_TOOL_TURNS`), then a host planned-turn
414
- * budget (`DEFT_MAX_TURNS` or `hostMaxTurns` from the session:start descriptor),
415
- * else this CLI invocation (1). An emitted proxy beats a perfect unused kind.
425
+ * budget (`DEFT_MAX_TURNS` or `hostMaxTurns`). Unset undefined (no event).
426
+ * Share is unevaluable without a host/harness value.
416
427
  */
417
428
  export function resolveSessionToolTurnDenominator(env, hostMaxTurns) {
418
429
  const known = readEnvToolTurnDenominator(env);
419
430
  if (known !== undefined) {
420
- return known;
431
+ return { total_tool_turns: known, denominator_source: "harness_actual" };
421
432
  }
422
433
  const planned = readPositiveIntegerEnv(env, ENV_MAX_TURNS_DENOMINATOR);
423
434
  if (planned !== undefined) {
424
- return planned;
435
+ return { total_tool_turns: planned, denominator_source: "host_planned" };
425
436
  }
426
437
  const host = coercePositiveHostDenominator(hostMaxTurns);
427
438
  if (host !== undefined) {
428
- return host;
439
+ return { total_tool_turns: host, denominator_source: "host_planned" };
429
440
  }
430
- return SESSION_START_CLI_INVOCATION_DENOMINATOR;
441
+ return undefined;
431
442
  }
432
443
  /**
433
444
  * One-shot helper for call sites that do not hold an emitter (e.g. dial escalate).
@@ -1,5 +1,6 @@
1
- export { type EmitRunSummaryResult, ENV_MAX_TURNS_DENOMINATOR, emitRunSummaryEvent, RunSummaryEmitter, type RunSummaryEmitterOptions, resolveSessionToolTurnDenominator, SESSION_START_CLI_INVOCATION_DENOMINATOR, } from "./emit.js";
1
+ export { type EmitRunSummaryResult, ENV_MAX_TURNS_DENOMINATOR, emitRunSummaryEvent, type ResolvedSessionToolTurnDenominator, RunSummaryEmitter, type RunSummaryEmitterOptions, resolveSessionToolTurnDenominator, } from "./emit.js";
2
2
  export { gitignoreCoversRunSummary, type ResolveRunSummaryDestinationOptions, resolveRunSummaryDestination, } from "./path.js";
3
+ export { type ResolveRunSummarySessionIdInput, resolveRunSummarySessionId, } from "./session-id.js";
3
4
  export { computeRitualGateShare, parseRunSummaryJsonl, type RitualGateShare, readToolTurnDenominator, } from "./share.js";
4
- export { type AcceptanceClauseOutcomeRow, type AcceptanceRunSummaryOutcome, type AcceptanceRunSummaryPayload, type AcceptanceStampRunSummaryPayload, type CheckGateOutcome, type CheckInvocationRunSummaryPayload, DEFAULT_RUN_SUMMARY_BASENAME, type DialEscalationEvaluationOutcome, type DialEscalationEvaluationRunSummaryPayload, type DialTransitionRunSummaryPayload, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, type RunSummaryBaseFields, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload, type ToolTurnDenominatorRunSummaryPayload, type VerificationOutcome, type VerificationRunSummaryPayload, } from "./types.js";
5
+ export { type AcceptanceClauseOutcomeRow, type AcceptanceRunSummaryOutcome, type AcceptanceRunSummaryPayload, type AcceptanceStampRunSummaryPayload, type AcPassBankRunSummaryPayload, type CheckGateOutcome, type CheckInvocationRunSummaryPayload, DEFAULT_RUN_SUMMARY_BASENAME, type DialEscalationEvaluationOutcome, type DialEscalationEvaluationRunSummaryPayload, type DialTransitionRunSummaryPayload, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, type RunSummaryBaseFields, type RunSummaryDestination, type RunSummaryEventKind, type RunSummaryLine, type RunSummaryPayload, type SessionStartRunSummaryPayload, type ToolTurnDenominatorRunSummaryPayload, type ToolTurnDenominatorSource, type VerificationOutcome, type VerificationRunSummaryPayload, } from "./types.js";
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,6 @@
1
- export { ENV_MAX_TURNS_DENOMINATOR, emitRunSummaryEvent, RunSummaryEmitter, resolveSessionToolTurnDenominator, SESSION_START_CLI_INVOCATION_DENOMINATOR, } from "./emit.js";
1
+ export { ENV_MAX_TURNS_DENOMINATOR, emitRunSummaryEvent, RunSummaryEmitter, resolveSessionToolTurnDenominator, } from "./emit.js";
2
2
  export { gitignoreCoversRunSummary, resolveRunSummaryDestination, } from "./path.js";
3
+ export { resolveRunSummarySessionId, } from "./session-id.js";
3
4
  export { computeRitualGateShare, parseRunSummaryJsonl, readToolTurnDenominator, } from "./share.js";
4
5
  export { DEFAULT_RUN_SUMMARY_BASENAME, ENV_RUN_SUMMARY_PATH, ENV_TOTAL_TOOL_TURNS, RUN_SUMMARY_EVENT_KINDS, RUN_SUMMARY_SCHEMA_VERSION, RUN_SUMMARY_STDOUT_PREFIX, RUN_SUMMARY_WRITE_WARNING, } from "./types.js";
5
6
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared workspace session identity for run-summary emitters (#3399).
3
+ *
4
+ * Ritual-state holds the workspace session id written by session:start.
5
+ * #3350 persisted seq by counting JSONL lines; it is not the identity store.
6
+ */
7
+ export interface ResolveRunSummarySessionIdInput {
8
+ readonly projectRoot: string;
9
+ readonly explicit?: string | null;
10
+ readonly env?: NodeJS.ProcessEnv;
11
+ readonly mint?: () => string;
12
+ }
13
+ /**
14
+ * Resolve one workspace session id for a run-summary line.
15
+ *
16
+ * Order: explicit option → DEFT_SESSION_ID → ritual-state session_id →
17
+ * last session_id already in the destination JSONL → mint only when no
18
+ * workspace session exists yet.
19
+ */
20
+ export declare function resolveRunSummarySessionId(input: ResolveRunSummarySessionIdInput): string;
21
+ //# sourceMappingURL=session-id.d.ts.map
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Shared workspace session identity for run-summary emitters (#3399).
3
+ *
4
+ * Ritual-state holds the workspace session id written by session:start.
5
+ * #3350 persisted seq by counting JSONL lines; it is not the identity store.
6
+ */
7
+ import { randomUUID } from "node:crypto";
8
+ import { existsSync, readFileSync } from "node:fs";
9
+ import { resolve } from "node:path";
10
+ import { readRitualState } from "../session/ritual-sentinel.js";
11
+ import { resolveRunSummaryDestination } from "./path.js";
12
+ import { parseRunSummaryJsonl } from "./share.js";
13
+ const mintedByProjectRoot = new Map();
14
+ function trimNonEmpty(value) {
15
+ if (typeof value !== "string") {
16
+ return undefined;
17
+ }
18
+ const trimmed = value.trim();
19
+ return trimmed.length > 0 ? trimmed : undefined;
20
+ }
21
+ function lastSessionIdFromJsonl(path) {
22
+ try {
23
+ if (!existsSync(path)) {
24
+ return undefined;
25
+ }
26
+ const lines = parseRunSummaryJsonl(readFileSync(path, "utf8"));
27
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
28
+ const id = trimNonEmpty(lines[i]?.session_id);
29
+ if (id !== undefined) {
30
+ return id;
31
+ }
32
+ }
33
+ return undefined;
34
+ }
35
+ catch {
36
+ return undefined;
37
+ }
38
+ }
39
+ function ritualSessionId(projectRoot) {
40
+ try {
41
+ const [state] = readRitualState(projectRoot);
42
+ return trimNonEmpty(state?.sessionId);
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ }
48
+ /**
49
+ * Resolve one workspace session id for a run-summary line.
50
+ *
51
+ * Order: explicit option → DEFT_SESSION_ID → ritual-state session_id →
52
+ * last session_id already in the destination JSONL → mint only when no
53
+ * workspace session exists yet.
54
+ */
55
+ export function resolveRunSummarySessionId(input) {
56
+ const explicit = trimNonEmpty(input.explicit);
57
+ if (explicit !== undefined) {
58
+ return explicit;
59
+ }
60
+ const env = input.env ?? process.env;
61
+ const fromEnv = trimNonEmpty(env.DEFT_SESSION_ID);
62
+ if (fromEnv !== undefined) {
63
+ return fromEnv;
64
+ }
65
+ const fromRitual = ritualSessionId(input.projectRoot);
66
+ if (fromRitual !== undefined) {
67
+ return fromRitual;
68
+ }
69
+ const dest = resolveRunSummaryDestination(input.projectRoot, { env });
70
+ if (dest.kind === "file") {
71
+ const fromJsonl = lastSessionIdFromJsonl(dest.path);
72
+ if (fromJsonl !== undefined) {
73
+ return fromJsonl;
74
+ }
75
+ }
76
+ const rootKey = resolve(input.projectRoot);
77
+ if (input.mint !== undefined) {
78
+ const minted = trimNonEmpty(input.mint());
79
+ if (minted !== undefined) {
80
+ mintedByProjectRoot.set(rootKey, minted);
81
+ return minted;
82
+ }
83
+ }
84
+ const cached = mintedByProjectRoot.get(rootKey);
85
+ if (cached !== undefined) {
86
+ return cached;
87
+ }
88
+ const minted = randomUUID();
89
+ mintedByProjectRoot.set(rootKey, minted);
90
+ return minted;
91
+ }
92
+ //# sourceMappingURL=session-id.js.map
@@ -15,7 +15,7 @@ export declare const DEFAULT_RUN_SUMMARY_BASENAME = ".deft-run-summary.json";
15
15
  export declare const RUN_SUMMARY_STDOUT_PREFIX = "DEFT-TLM:";
16
16
  /** One warning when an explicitly requested write fails. */
17
17
  export declare const RUN_SUMMARY_WRITE_WARNING = "[deft run-summary] failed to write DEFT_RUN_SUMMARY_PATH telemetry (fail-open; exit codes unchanged)";
18
- export declare const RUN_SUMMARY_EVENT_KINDS: readonly ["session_start", "dial_transition", "dial_escalation_evaluation", "check_invocation", "tool_turn_denominator", "verification", "acceptance", "acceptance_stamp"];
18
+ export declare const RUN_SUMMARY_EVENT_KINDS: readonly ["session_start", "dial_transition", "dial_escalation_evaluation", "check_invocation", "tool_turn_denominator", "verification", "acceptance", "acceptance_stamp", "ac_pass_bank"];
19
19
  export type RunSummaryEventKind = (typeof RUN_SUMMARY_EVENT_KINDS)[number];
20
20
  export interface RunSummaryBaseFields {
21
21
  readonly schema_version: typeof RUN_SUMMARY_SCHEMA_VERSION;
@@ -32,6 +32,11 @@ export interface RunSummaryBaseFields {
32
32
  readonly seq: number;
33
33
  readonly ts: string;
34
34
  readonly event: RunSummaryEventKind;
35
+ /**
36
+ * Optional emitter label (verify-ac, clause-derivation, …). Not a closed
37
+ * union — new callers add a string without a schema bump (#3399).
38
+ */
39
+ readonly component?: string;
35
40
  /**
36
41
  * Session total tool/turn count (#3320). Present so ritual+gate share is
37
42
  * computable from the summary alone. Absence means the #3286 trigger is unevaluable.
@@ -88,14 +93,32 @@ export interface CheckInvocationRunSummaryPayload {
88
93
  readonly none_stated?: boolean;
89
94
  readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
90
95
  }
91
- /** Total tool/turn count for the session (#3320). */
96
+ /** How a tool_turn_denominator value was obtained (#3399). */
97
+ export type ToolTurnDenominatorSource = "harness_actual" | "host_planned";
98
+ /** Total tool/turn count for the session (#3320 / #3399). */
92
99
  export interface ToolTurnDenominatorRunSummaryPayload {
93
100
  readonly total_tool_turns: number;
101
+ readonly denominator_source?: ToolTurnDenominatorSource;
102
+ }
103
+ /** Bank checkpoint JSONL payload. Disk ledger stays camelCase (#3387). */
104
+ export interface AcPassBankRunSummaryPayload {
105
+ readonly scope_id: string;
106
+ readonly banked_at: string;
107
+ readonly next_action?: string;
108
+ readonly had_surplus?: boolean;
109
+ readonly surplus_threshold?: number;
110
+ readonly remaining_fraction?: number | null;
111
+ readonly remaining_turns?: number | null;
112
+ readonly remaining_budget?: number | null;
113
+ readonly max_turns?: number | null;
114
+ readonly max_budget?: number | null;
115
+ readonly head_sha?: string | null;
116
+ readonly path?: string;
94
117
  }
95
118
  /** Product-oracle attempt (#3322). Same emitter as #3319 / #3320. */
96
119
  export type VerificationOutcome = "pass" | "fail";
97
120
  /** verify:ac resolution telemetry (#3334 / #3355). Distinct from verification pass/fail. */
98
- export type AcceptanceRunSummaryOutcome = "verified-pass" | "empty-pass" | "soft_empty" | "fail" | "config-error" | "soft-missing";
121
+ export type AcceptanceRunSummaryOutcome = "verified-pass" | "empty-pass" | "soft_empty" | "fail" | "config-error" | "soft-missing" | "rejected-noop";
99
122
  export interface AcceptanceRunSummaryPayload {
100
123
  readonly resolved_command_count: number;
101
124
  readonly outcome: AcceptanceRunSummaryOutcome;
@@ -103,6 +126,8 @@ export interface AcceptanceRunSummaryPayload {
103
126
  readonly none_stated?: boolean;
104
127
  readonly clause_count?: number;
105
128
  readonly clause_outcomes?: readonly AcceptanceClauseOutcomeRow[];
129
+ /** bank | cache | executed — how verify:ac obtained this result (#3387). */
130
+ readonly served_from?: "bank" | "cache" | "executed";
106
131
  }
107
132
  /** Intake-time stamp: which rung locked, whether commands were stated, counts (#3323). */
108
133
  export interface AcceptanceStampRunSummaryPayload {
@@ -122,7 +147,7 @@ export interface VerificationRunSummaryPayload {
122
147
  */
123
148
  readonly independent_rederivation?: boolean;
124
149
  }
125
- export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | DialEscalationEvaluationRunSummaryPayload | CheckInvocationRunSummaryPayload | ToolTurnDenominatorRunSummaryPayload | VerificationRunSummaryPayload | AcceptanceRunSummaryPayload | AcceptanceStampRunSummaryPayload;
150
+ export type RunSummaryPayload = SessionStartRunSummaryPayload | DialTransitionRunSummaryPayload | DialEscalationEvaluationRunSummaryPayload | CheckInvocationRunSummaryPayload | ToolTurnDenominatorRunSummaryPayload | VerificationRunSummaryPayload | AcceptanceRunSummaryPayload | AcceptanceStampRunSummaryPayload | AcPassBankRunSummaryPayload;
126
151
  export type RunSummaryLine = RunSummaryBaseFields & {
127
152
  readonly payload: RunSummaryPayload;
128
153
  };
@@ -24,5 +24,6 @@ export const RUN_SUMMARY_EVENT_KINDS = [
24
24
  "verification",
25
25
  "acceptance",
26
26
  "acceptance_stamp",
27
+ "ac_pass_bank",
27
28
  ];
28
29
  //# sourceMappingURL=types.js.map
@@ -13,6 +13,7 @@
13
13
  * ITEM_CORE is not expanded with bare keys; verify:vbrief-conformance rejects them.
14
14
  */
15
15
  import type { GrantOrigin } from "../authz/types.js";
16
+ import { type AcceptancePredicate } from "../product-first-done-gate/acceptance-resolver.js";
16
17
  import { type EvaluateVerifyAcOptions } from "../product-first-done-gate/evaluate.js";
17
18
  /** Canonical namespaced key for typed acceptance evidence (#3305 / #1620). */
18
19
  export declare const ACCEPTANCE_EVIDENCE_KEY: "x-directive/evidence";
@@ -56,6 +57,10 @@ export interface AcceptanceEvidenceGateResult {
56
57
  readonly ok: boolean;
57
58
  readonly message: string;
58
59
  readonly reports: readonly CriterionAcceptanceReport[];
60
+ /** How the #3357 walk obtained AC (#3387). */
61
+ readonly servedFrom?: "bank" | "cache" | "executed";
62
+ /** Which acceptance check decided the walk (#3497). Undefined when no walk ran. */
63
+ readonly predicate?: AcceptancePredicate;
59
64
  }
60
65
  export declare function isAcceptanceEvidenceKind(value: unknown): value is AcceptanceEvidenceKind;
61
66
  export declare function isAcceptanceDispositionKind(value: unknown): value is AcceptanceDispositionKind;
@@ -100,17 +105,24 @@ export declare function stampNamespacedEvidence(item: Record<string, unknown>, r
100
105
  */
101
106
  export declare function stampNamespacedDisposition(item: Record<string, unknown>, record: AcceptanceDispositionRecord): void;
102
107
  /**
103
- * One remediation when scope:complete cannot accept empty or failing product AC (#3357).
104
- * Item-level disposition is not a substitute for the acceptance walk.
108
+ * Standing contract for scope:complete's acceptance precondition (#3357 / #3497).
109
+ *
110
+ * It no longer asserts a predicate ("empty or failing") — the actual predicate is
111
+ * named per refusal by the shared resolver. It also no longer tells operators to
112
+ * stamp by running verify:ac: verify:ac is the verifier and never writes the brief
113
+ * (a verifier that authors the acceptance it then checks is not a gate). Stamping
114
+ * happens at intake / promote via clause derivation (#3323).
105
115
  */
106
- export declare const SCOPE_COMPLETE_ACCEPTANCE_REMEDIATION = "Run task verify:ac -- <completing-xbrief> and stamp executable plan.acceptance; scope:complete refuses empty or failing acceptance (disposition is not a substitute) (#3357)";
116
+ export declare const SCOPE_COMPLETE_ACCEPTANCE_REMEDIATION: string;
107
117
  /**
108
118
  * Hard precondition for scope:complete (#3357 / #3267 / #3284).
109
119
  *
110
- * Runs the same verify:ac walk check uses. Disposition on plan items does not
111
- * skip it. Briefs with no stamped plan.acceptance and no executable commands
112
- * keep the legacy #3267 "nothing to run" pass so existing evidence-only
113
- * fixtures stay valid.
120
+ * Runs the same verify:ac walk check uses, through the one shared option profile and
121
+ * the one shared verdict resolver (#3497) so scope:complete can no longer report a
122
+ * different story than verify:ac about the same artifact. Disposition on plan items
123
+ * does not skip it. Briefs with no stamped plan.acceptance and no executable commands
124
+ * keep the legacy #3267 "nothing to run" pass so existing evidence-only fixtures stay
125
+ * valid.
114
126
  */
115
127
  export declare function evaluateScopeCompleteAcceptanceWalk(plan: Record<string, unknown>, options?: EvaluateVerifyAcOptions): AcceptanceEvidenceGateResult;
116
128
  /**
@@ -13,6 +13,7 @@
13
13
  * ITEM_CORE is not expanded with bare keys; verify:vbrief-conformance rejects them.
14
14
  */
15
15
  import { isHumanOrigin } from "../authz/origin.js";
16
+ import { formatAcceptanceVerdict, resolveAcceptanceGateProfile, resolveAcceptanceVerdict, } from "../product-first-done-gate/acceptance-resolver.js";
16
17
  import { evaluateVerifyAcFromPlan, } from "../product-first-done-gate/evaluate.js";
17
18
  /** Canonical namespaced key for typed acceptance evidence (#3305 / #1620). */
18
19
  export const ACCEPTANCE_EVIDENCE_KEY = "x-directive/evidence";
@@ -371,41 +372,67 @@ function walkItems(items, pathPrefix, reports) {
371
372
  });
372
373
  }
373
374
  /**
374
- * One remediation when scope:complete cannot accept empty or failing product AC (#3357).
375
- * Item-level disposition is not a substitute for the acceptance walk.
375
+ * Standing contract for scope:complete's acceptance precondition (#3357 / #3497).
376
+ *
377
+ * It no longer asserts a predicate ("empty or failing") — the actual predicate is
378
+ * named per refusal by the shared resolver. It also no longer tells operators to
379
+ * stamp by running verify:ac: verify:ac is the verifier and never writes the brief
380
+ * (a verifier that authors the acceptance it then checks is not a gate). Stamping
381
+ * happens at intake / promote via clause derivation (#3323).
376
382
  */
377
- export const SCOPE_COMPLETE_ACCEPTANCE_REMEDIATION = "Run task verify:ac -- <completing-xbrief> and stamp executable plan.acceptance; scope:complete refuses empty or failing acceptance (disposition is not a substitute) (#3357)";
383
+ export const SCOPE_COMPLETE_ACCEPTANCE_REMEDIATION = "scope:complete requires executable plan.acceptance that runs green; stamp commands on " +
384
+ "plan.acceptance.commands (or plan.metadata.swarm.verify_commands) — task verify:ac verifies, " +
385
+ "it does not stamp — then re-run task verify:ac -- <completing-xbrief> " +
386
+ "(disposition is not a substitute) (#3357/#3497)";
378
387
  /**
379
388
  * Hard precondition for scope:complete (#3357 / #3267 / #3284).
380
389
  *
381
- * Runs the same verify:ac walk check uses. Disposition on plan items does not
382
- * skip it. Briefs with no stamped plan.acceptance and no executable commands
383
- * keep the legacy #3267 "nothing to run" pass so existing evidence-only
384
- * fixtures stay valid.
390
+ * Runs the same verify:ac walk check uses, through the one shared option profile and
391
+ * the one shared verdict resolver (#3497) so scope:complete can no longer report a
392
+ * different story than verify:ac about the same artifact. Disposition on plan items
393
+ * does not skip it. Briefs with no stamped plan.acceptance and no executable commands
394
+ * keep the legacy #3267 "nothing to run" pass so existing evidence-only fixtures stay
395
+ * valid.
385
396
  */
386
397
  export function evaluateScopeCompleteAcceptanceWalk(plan, options = {}) {
387
398
  const stamped = plan.acceptance !== undefined;
399
+ const profile = resolveAcceptanceGateProfile("complete");
388
400
  const walk = evaluateVerifyAcFromPlan(plan, {
389
401
  ...options,
390
- checkIntegrated: false,
391
- captureFromNarratives: options.captureFromNarratives ?? true,
402
+ checkIntegrated: profile.checkIntegrated,
403
+ captureFromNarratives: options.captureFromNarratives ?? profile.captureFromNarratives,
404
+ reuseMode: options.reuseMode ?? profile.reuseMode,
392
405
  });
393
406
  const rejectedCount = walk.rejected?.length ?? 0;
394
407
  const hadWork = walk.runs.length > 0 || walk.commands.length > 0 || rejectedCount > 0;
408
+ const servedFrom = walk.servedFrom ?? "executed";
395
409
  if (!stamped && !hadWork) {
396
410
  return {
397
411
  ok: true,
398
412
  message: "Acceptance walk not required (no stamped plan.acceptance) (#3357)",
399
413
  reports: [],
414
+ servedFrom: "executed",
400
415
  };
401
416
  }
417
+ const verdict = resolveAcceptanceVerdict(walk);
402
418
  if (walk.ok) {
403
- return { ok: true, message: walk.message, reports: [] };
419
+ return {
420
+ ok: true,
421
+ message: walk.message,
422
+ reports: [],
423
+ servedFrom,
424
+ predicate: verdict.predicate,
425
+ };
404
426
  }
405
427
  return {
406
428
  ok: false,
407
- message: `${SCOPE_COMPLETE_ACCEPTANCE_REMEDIATION}\n${walk.message}`,
429
+ // Name the check that refused and the value it read BEFORE the standing
430
+ // contract, so the first line the operator sees is the actual cause (#3497).
431
+ message: `scope:complete refused acceptance — ${formatAcceptanceVerdict(verdict)}\n` +
432
+ `${SCOPE_COMPLETE_ACCEPTANCE_REMEDIATION}\n${walk.message}`,
408
433
  reports: [],
434
+ servedFrom,
435
+ predicate: verdict.predicate,
409
436
  };
410
437
  }
411
438
  /**
@@ -99,13 +99,13 @@ export declare function verifyDeliveryAncestry(projectRoot: string, mergeCommit:
99
99
  remoteTip: string | null;
100
100
  };
101
101
  /**
102
- * Gate delivered completion for a code-bearing scope (#3041).
102
+ * Gate delivered completion for a code-bearing scope (#3041 / #3380).
103
103
  *
104
104
  * Returns ok=true with provenance when:
105
105
  * - scope is not code-bearing (provenance may be null or non-code note), OR
106
106
  * - explicit non-delivery disposition is provided, OR
107
- * - durable delivery evidence validates (PR base == deliveryBranch, merge commit
108
- * ancestor of refreshed remote delivery ref).
107
+ * - merge commit is an ancestor of the refreshed remote delivery ref (prBase is
108
+ * provenance only; it need not equal deliveryBranch).
109
109
  */
110
110
  export declare function evaluateDeliveryGate(options: DeliveryGateOptions): DeliveryGateResult;
111
111
  /** Stamp completion provenance onto plan.metadata (mutates plan). */
@@ -175,7 +175,7 @@ export function verifyDeliveryAncestry(projectRoot, mergeCommit, deliveryBranch,
175
175
  return {
176
176
  ok: false,
177
177
  error: `merge commit ${mergeCommit} is not an ancestor of refreshed remote delivery ref ` +
178
- `${refresh.remoteRef} (${remoteTip}). Intermediate/integration-only merges are not delivery evidence (#3041).`,
178
+ `${refresh.remoteRef} (${remoteTip}).`,
179
179
  remoteTip,
180
180
  };
181
181
  }
@@ -199,14 +199,21 @@ function buildProvenance(input) {
199
199
  uatVerified: e.uatVerified ?? null,
200
200
  };
201
201
  }
202
+ function deliveryEvidenceRemediation(deliveryBranch) {
203
+ return (`Pass scope:complete -- --merge-commit <sha> (and --pr <n> if you have one). ` +
204
+ `When develop is the real delivery target, type plan.policy.deliveryBranch ` +
205
+ `(task policy:show --field=deliveryBranch). ` +
206
+ `When ${deliveryBranch} is delivery, wait until the merge commit is an ancestor of ` +
207
+ `origin/${deliveryBranch}, then complete. Do not use --non-delivery for work that shipped.`);
208
+ }
202
209
  /**
203
- * Gate delivered completion for a code-bearing scope (#3041).
210
+ * Gate delivered completion for a code-bearing scope (#3041 / #3380).
204
211
  *
205
212
  * Returns ok=true with provenance when:
206
213
  * - scope is not code-bearing (provenance may be null or non-code note), OR
207
214
  * - explicit non-delivery disposition is provided, OR
208
- * - durable delivery evidence validates (PR base == deliveryBranch, merge commit
209
- * ancestor of refreshed remote delivery ref).
215
+ * - merge commit is an ancestor of the refreshed remote delivery ref (prBase is
216
+ * provenance only; it need not equal deliveryBranch).
210
217
  */
211
218
  export function evaluateDeliveryGate(options) {
212
219
  const runGit = options.runGit ?? defaultGitRunner;
@@ -267,10 +274,8 @@ export function evaluateDeliveryGate(options) {
267
274
  return {
268
275
  ok: false,
269
276
  message: `Delivery evidence required for code-bearing scope completion (#3041). ` +
270
- `Provide merge/PR delivery proof (merge commit ancestor of remote ` +
271
- `'${deliveryBranch}') or an explicit --non-delivery disposition ` +
272
- `(${NON_DELIVERY_DISPOSITIONS.join("|")}). ` +
273
- `Merged-to-integration alone is not delivery.`,
277
+ `A merge commit that is an ancestor of refreshed origin/${deliveryBranch} is delivery; ` +
278
+ `PR base is provenance only. ${deliveryEvidenceRemediation(deliveryBranch)}`,
274
279
  provenance: null,
275
280
  codeBearing: true,
276
281
  };
@@ -294,27 +299,11 @@ export function evaluateDeliveryGate(options) {
294
299
  };
295
300
  }
296
301
  }
297
- if (prBase !== null && prBase !== deliveryBranch) {
298
- const provenance = buildProvenance({
299
- evidence,
300
- deliveryBranch,
301
- disposition: "merged_to_integration",
302
- handoffState: "merged_to_integration",
303
- nowIso: options.nowIso,
304
- verifier,
305
- });
306
- return {
307
- ok: false,
308
- message: `PR base '${prBase}' is not the configured delivery branch '${deliveryBranch}'. ` +
309
- `A merge into an intermediate/feature/integration branch is not delivery evidence (#3041).`,
310
- provenance,
311
- codeBearing: true,
312
- };
313
- }
314
302
  if (mergeCommit === null) {
315
303
  return {
316
304
  ok: false,
317
- message: "Delivery evidence missing merge_commit_sha; cannot prove ancestry on delivery branch (#3041).",
305
+ message: "Delivery evidence missing merge_commit_sha; cannot prove ancestry on delivery branch (#3041). " +
306
+ deliveryEvidenceRemediation(deliveryBranch),
318
307
  provenance: null,
319
308
  codeBearing: true,
320
309
  };
@@ -339,9 +328,15 @@ export function evaluateDeliveryGate(options) {
339
328
  const ancestry = verifyDeliveryAncestry(options.projectRoot, mergeCommit, deliveryBranch, runGit);
340
329
  if (!ancestry.ok) {
341
330
  const integrationOnly = prBase !== null && prBase !== deliveryBranch;
331
+ const ancestryMsg = ancestry.error ?? "delivery ancestry check failed";
332
+ const rem = deliveryEvidenceRemediation(deliveryBranch);
333
+ const message = integrationOnly
334
+ ? `${ancestryMsg} PR base '${prBase}' is provenance only; ` +
335
+ `the merge is not yet on origin/${deliveryBranch}. ${rem}`
336
+ : `${ancestryMsg} ${rem}`;
342
337
  return {
343
338
  ok: false,
344
- message: ancestry.error ?? "delivery ancestry check failed",
339
+ message,
345
340
  provenance: buildProvenance({
346
341
  evidence,
347
342
  deliveryBranch,
@@ -12,6 +12,7 @@ export * from "./main.js";
12
12
  export * from "./open-umbrella-warning.js";
13
13
  export * from "./parent-lineage.js";
14
14
  export * from "./project-context.js";
15
+ export * from "./project-invariant-coverage.js";
15
16
  export * from "./promote-from-issue.js";
16
17
  export * from "./promote-path.js";
17
18
  export * from "./transition.js";
@@ -12,6 +12,7 @@ export * from "./main.js";
12
12
  export * from "./open-umbrella-warning.js";
13
13
  export * from "./parent-lineage.js";
14
14
  export * from "./project-context.js";
15
+ export * from "./project-invariant-coverage.js";
15
16
  export * from "./promote-from-issue.js";
16
17
  export * from "./promote-path.js";
17
18
  export * from "./transition.js";
@@ -98,20 +98,21 @@ export function extractChildCoverageDraft(child) {
98
98
  candidates.push({ source: "plan", obj: plan });
99
99
  candidates.push({ source: "root", obj: root });
100
100
  for (const c of candidates) {
101
+ const nestedCoverage = asRecord(c.obj.coverage);
101
102
  const hasMap = "coverage_map" in c.obj ||
102
103
  "coverageMap" in c.obj ||
103
- asRecord(c.obj.coverage)?.coverage_map !== undefined;
104
+ nestedCoverage?.coverage_map !== undefined ||
105
+ nestedCoverage?.coverageMap !== undefined;
104
106
  if (!hasMap)
105
107
  continue;
106
- const coverageNested = asRecord(c.obj.coverage);
107
108
  const coverage_map = c.obj.coverage_map ??
108
109
  c.obj.coverageMap ??
109
- coverageNested?.coverage_map ??
110
- coverageNested?.coverageMap;
110
+ nestedCoverage?.coverage_map ??
111
+ nestedCoverage?.coverageMap;
111
112
  const behavioral_deltas = c.obj.behavioral_deltas ??
112
113
  c.obj.behavioralDeltas ??
113
- coverageNested?.behavioral_deltas ??
114
- coverageNested?.behavioralDeltas;
114
+ nestedCoverage?.behavioral_deltas ??
115
+ nestedCoverage?.behavioralDeltas;
115
116
  return {
116
117
  draft: {
117
118
  coverage_map,