@deftai/directive-core 0.103.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 (255) hide show
  1. package/dist/authz/classify.js +682 -35
  2. package/dist/check/cached-orchestrator.d.ts +1 -1
  3. package/dist/check/cached-orchestrator.js +2 -2
  4. package/dist/check/gate-lists.d.ts +3 -2
  5. package/dist/check/gate-lists.js +5 -2
  6. package/dist/check/index.d.ts +1 -0
  7. package/dist/check/index.js +1 -0
  8. package/dist/check/named-cause.d.ts +1 -1
  9. package/dist/check/named-cause.js +33 -3
  10. package/dist/check/session-completed-ac.d.ts +39 -0
  11. package/dist/check/session-completed-ac.js +198 -0
  12. package/dist/codebase/default-extractor.d.ts +5 -0
  13. package/dist/codebase/default-extractor.js +7 -15
  14. package/dist/deposit/copy-tree.js +60 -0
  15. package/dist/deposit/python-free.js +26 -18
  16. package/dist/doctor/checks.d.ts +8 -0
  17. package/dist/doctor/checks.js +37 -25
  18. package/dist/doctor/doctor-state.d.ts +4 -0
  19. package/dist/doctor/doctor-state.js +7 -1
  20. package/dist/doctor/main.js +9 -14
  21. package/dist/doctor/types.d.ts +9 -0
  22. package/dist/fs/contained-write.d.ts +115 -1
  23. package/dist/fs/contained-write.js +242 -5
  24. package/dist/fs/mutation-ledger.d.ts +54 -0
  25. package/dist/fs/mutation-ledger.js +133 -0
  26. package/dist/fs/non-product-dirs.d.ts +79 -0
  27. package/dist/fs/non-product-dirs.js +96 -0
  28. package/dist/hooks/dispatcher.d.ts +3 -1
  29. package/dist/hooks/dispatcher.js +14 -0
  30. package/dist/index.d.ts +2 -0
  31. package/dist/index.js +3 -0
  32. package/dist/init-deposit/agent-hooks.js +17 -15
  33. package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
  34. package/dist/init-deposit/core-guard-pin-content.js +393 -0
  35. package/dist/init-deposit/gitignore.js +9 -0
  36. package/dist/init-deposit/hygiene.d.ts +47 -11
  37. package/dist/init-deposit/hygiene.js +172 -37
  38. package/dist/init-deposit/legacy-detect.js +6 -2
  39. package/dist/init-deposit/refresh.d.ts +21 -1
  40. package/dist/init-deposit/refresh.js +106 -9
  41. package/dist/init-deposit/scaffold.js +28 -279
  42. package/dist/init-deposit/slash-deposit.js +13 -7
  43. package/dist/init-deposit/xbrief-projections.d.ts +8 -1
  44. package/dist/init-deposit/xbrief-projections.js +24 -11
  45. package/dist/intake/clause-derivation.d.ts +74 -0
  46. package/dist/intake/clause-derivation.js +409 -0
  47. package/dist/intake/index.d.ts +1 -0
  48. package/dist/intake/index.js +1 -0
  49. package/dist/intake/issue-ingest.d.ts +3 -1
  50. package/dist/intake/issue-ingest.js +36 -43
  51. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
  52. package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
  53. package/dist/literal-acceptance/capture.d.ts +10 -0
  54. package/dist/literal-acceptance/capture.js +224 -75
  55. package/dist/literal-acceptance/evaluate.d.ts +29 -4
  56. package/dist/literal-acceptance/evaluate.js +70 -2
  57. package/dist/literal-acceptance/index.d.ts +3 -3
  58. package/dist/literal-acceptance/index.js +3 -3
  59. package/dist/literal-acceptance/safety.d.ts +41 -0
  60. package/dist/literal-acceptance/safety.js +126 -2
  61. package/dist/literal-acceptance/types.d.ts +8 -0
  62. package/dist/orphan-active/evaluate.d.ts +7 -1
  63. package/dist/orphan-active/evaluate.js +102 -23
  64. package/dist/platform/platform-capabilities.js +2 -3
  65. package/dist/policy/base-branch.d.ts +37 -0
  66. package/dist/policy/base-branch.js +114 -0
  67. package/dist/policy/branch-sync.d.ts +80 -0
  68. package/dist/policy/branch-sync.js +211 -0
  69. package/dist/policy/ceremony-dial-escalation.js +1 -0
  70. package/dist/policy/ceremony-dial.js +4 -14
  71. package/dist/policy/delivery-branch.d.ts +5 -3
  72. package/dist/policy/delivery-branch.js +7 -3
  73. package/dist/policy/file-size-thresholds.d.ts +24 -0
  74. package/dist/policy/file-size-thresholds.js +23 -0
  75. package/dist/policy/forge-outage-retry.d.ts +47 -0
  76. package/dist/policy/forge-outage-retry.js +157 -0
  77. package/dist/policy/index.d.ts +7 -0
  78. package/dist/policy/index.js +66 -7
  79. package/dist/policy/project-invariants.d.ts +50 -0
  80. package/dist/policy/project-invariants.js +205 -0
  81. package/dist/policy/sync-default.d.ts +129 -0
  82. package/dist/policy/sync-default.js +452 -0
  83. package/dist/policy/sync-max-files.d.ts +93 -0
  84. package/dist/policy/sync-max-files.js +192 -0
  85. package/dist/pr-monitor/absent-required.d.ts +11 -0
  86. package/dist/pr-monitor/absent-required.js +61 -0
  87. package/dist/pr-monitor/constants.d.ts +7 -0
  88. package/dist/pr-monitor/constants.js +7 -0
  89. package/dist/pr-monitor/index.d.ts +2 -1
  90. package/dist/pr-monitor/index.js +2 -1
  91. package/dist/pr-monitor/main.js +4 -0
  92. package/dist/pr-monitor/monitor.js +24 -1
  93. package/dist/pr-wait-mergeable/classify.js +6 -0
  94. package/dist/preflight/evaluate.d.ts +9 -0
  95. package/dist/preflight/evaluate.js +49 -1
  96. package/dist/preflight/index.d.ts +5 -1
  97. package/dist/preflight/index.js +2 -0
  98. package/dist/preflight/intended-placement.d.ts +62 -0
  99. package/dist/preflight/intended-placement.js +275 -0
  100. package/dist/preflight/project-invariants-gate.d.ts +29 -0
  101. package/dist/preflight/project-invariants-gate.js +82 -0
  102. package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
  103. package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
  104. package/dist/product-first-done-gate/acceptance.d.ts +7 -4
  105. package/dist/product-first-done-gate/acceptance.js +79 -8
  106. package/dist/product-first-done-gate/empty-resolution.d.ts +5 -2
  107. package/dist/product-first-done-gate/empty-resolution.js +13 -2
  108. package/dist/product-first-done-gate/evaluate.d.ts +29 -0
  109. package/dist/product-first-done-gate/evaluate.js +307 -33
  110. package/dist/product-first-done-gate/index.d.ts +3 -1
  111. package/dist/product-first-done-gate/index.js +2 -1
  112. package/dist/release/build-dist.d.ts +101 -0
  113. package/dist/release/build-dist.js +428 -46
  114. package/dist/run-summary/emit.d.ts +50 -4
  115. package/dist/run-summary/emit.js +262 -26
  116. package/dist/run-summary/index.d.ts +3 -2
  117. package/dist/run-summary/index.js +2 -1
  118. package/dist/run-summary/session-id.d.ts +21 -0
  119. package/dist/run-summary/session-id.js +92 -0
  120. package/dist/run-summary/share.d.ts +1 -1
  121. package/dist/run-summary/share.js +2 -2
  122. package/dist/run-summary/types.d.ts +38 -6
  123. package/dist/run-summary/types.js +1 -0
  124. package/dist/scope/acceptance-activate-gate.d.ts +1 -1
  125. package/dist/scope/acceptance-activate-gate.js +4 -4
  126. package/dist/scope/acceptance-evidence.d.ts +27 -0
  127. package/dist/scope/acceptance-evidence.js +66 -0
  128. package/dist/scope/capacity-stamp.d.ts +2 -0
  129. package/dist/scope/capacity-stamp.js +4 -0
  130. package/dist/scope/delivery-evidence.d.ts +9 -3
  131. package/dist/scope/delivery-evidence.js +39 -27
  132. package/dist/scope/index.d.ts +1 -0
  133. package/dist/scope/index.js +1 -0
  134. package/dist/scope/parent-lineage.js +7 -6
  135. package/dist/scope/project-invariant-coverage.d.ts +54 -0
  136. package/dist/scope/project-invariant-coverage.js +322 -0
  137. package/dist/scope/transition.js +66 -4
  138. package/dist/scope-provenance/compare-intent.d.ts +27 -0
  139. package/dist/scope-provenance/compare-intent.js +233 -0
  140. package/dist/scope-provenance/digest.d.ts +18 -1
  141. package/dist/scope-provenance/digest.js +16 -11
  142. package/dist/scope-provenance/evaluate.d.ts +5 -1
  143. package/dist/scope-provenance/evaluate.js +64 -4
  144. package/dist/scope-provenance/extract-intent.d.ts +43 -0
  145. package/dist/scope-provenance/extract-intent.js +262 -0
  146. package/dist/scope-provenance/index.d.ts +7 -1
  147. package/dist/scope-provenance/index.js +7 -1
  148. package/dist/scope-provenance/intent-digest.d.ts +12 -0
  149. package/dist/scope-provenance/intent-digest.js +33 -0
  150. package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
  151. package/dist/scope-provenance/intent-evaluate.js +149 -0
  152. package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
  153. package/dist/scope-provenance/json-tokenizer.js +225 -0
  154. package/dist/scope-provenance/known-machine.d.ts +38 -0
  155. package/dist/scope-provenance/known-machine.js +157 -0
  156. package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
  157. package/dist/scope-provenance/mint-artifacts.js +425 -0
  158. package/dist/session/ac-pass-banking.d.ts +4 -36
  159. package/dist/session/ac-pass-banking.js +30 -77
  160. package/dist/session/ac-pass-reuse.d.ts +53 -0
  161. package/dist/session/ac-pass-reuse.js +95 -0
  162. package/dist/session/ceremony-dial-evidence.d.ts +42 -0
  163. package/dist/session/ceremony-dial-evidence.js +194 -0
  164. package/dist/session/git.js +19 -4
  165. package/dist/session/index.d.ts +5 -0
  166. package/dist/session/index.js +5 -0
  167. package/dist/session/occupancy-stress-worker.d.ts +7 -0
  168. package/dist/session/occupancy-stress-worker.js +36 -0
  169. package/dist/session/occupancy.d.ts +97 -0
  170. package/dist/session/occupancy.js +422 -0
  171. package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
  172. package/dist/session/product-state-hash.d.ts +26 -0
  173. package/dist/session/product-state-hash.js +683 -0
  174. package/dist/session/session-ready.d.ts +2 -0
  175. package/dist/session/session-ready.js +52 -30
  176. package/dist/session/session-start.d.ts +7 -0
  177. package/dist/session/session-start.js +88 -5
  178. package/dist/session/verify-ac-session-cache.d.ts +42 -0
  179. package/dist/session/verify-ac-session-cache.js +126 -0
  180. package/dist/session/verify-session-ritual.d.ts +5 -0
  181. package/dist/session/verify-session-ritual.js +9 -0
  182. package/dist/slash/openclaw-deposit.js +13 -9
  183. package/dist/slice/constants.d.ts +2 -2
  184. package/dist/slice/constants.js +2 -2
  185. package/dist/slice/existing.js +21 -4
  186. package/dist/slice/lock.d.ts +16 -1
  187. package/dist/slice/lock.js +137 -11
  188. package/dist/slice/record.d.ts +8 -5
  189. package/dist/slice/record.js +21 -13
  190. package/dist/story-ready/evaluate.d.ts +2 -0
  191. package/dist/story-ready/evaluate.js +13 -0
  192. package/dist/swarm/complete-cohort.d.ts +3 -0
  193. package/dist/swarm/complete-cohort.js +59 -11
  194. package/dist/swarm/finalize-cohort.js +33 -2
  195. package/dist/swarm/launch.d.ts +22 -0
  196. package/dist/swarm/launch.js +155 -0
  197. package/dist/swarm/routing-set-cli.d.ts +11 -1
  198. package/dist/swarm/routing-set-cli.js +13 -3
  199. package/dist/swarm/routing-verify.d.ts +12 -0
  200. package/dist/swarm/routing-verify.js +59 -20
  201. package/dist/swarm/routing.d.ts +14 -0
  202. package/dist/swarm/routing.js +53 -3
  203. package/dist/telemetry-coverage/evaluate.d.ts +42 -0
  204. package/dist/telemetry-coverage/evaluate.js +148 -0
  205. package/dist/telemetry-coverage/fake-trial.d.ts +45 -0
  206. package/dist/telemetry-coverage/fake-trial.js +204 -0
  207. package/dist/telemetry-coverage/index.d.ts +5 -0
  208. package/dist/telemetry-coverage/index.js +5 -0
  209. package/dist/telemetry-coverage/kinds.d.ts +19 -0
  210. package/dist/telemetry-coverage/kinds.js +54 -0
  211. package/dist/telemetry-coverage/scan-callers.d.ts +25 -0
  212. package/dist/telemetry-coverage/scan-callers.js +168 -0
  213. package/dist/triage/actions/index.js +4 -1
  214. package/dist/ts-check-lane/index.d.ts +3 -0
  215. package/dist/ts-check-lane/index.js +2 -0
  216. package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
  217. package/dist/ts-check-lane/progress-reporter.js +38 -0
  218. package/dist/ts-check-lane/progress.d.ts +32 -0
  219. package/dist/ts-check-lane/progress.js +57 -0
  220. package/dist/ts-check-lane/run-lane.d.ts +3 -1
  221. package/dist/ts-check-lane/run-lane.js +8 -6
  222. package/dist/validate-content/validate-links.d.ts +8 -0
  223. package/dist/validate-content/validate-links.js +9 -15
  224. package/dist/value/feedback-file.js +12 -2
  225. package/dist/vbrief-build/index.d.ts +1 -0
  226. package/dist/vbrief-build/index.js +1 -0
  227. package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
  228. package/dist/vbrief-build/project-invariants-io.js +41 -0
  229. package/dist/vbrief-reconcile/graph.js +18 -1
  230. package/dist/vbrief-reconcile/index.d.ts +3 -1
  231. package/dist/vbrief-reconcile/index.js +2 -1
  232. package/dist/vbrief-reconcile/origin-freshness.d.ts +44 -0
  233. package/dist/vbrief-reconcile/origin-freshness.js +221 -0
  234. package/dist/vbrief-reconcile/types.d.ts +13 -0
  235. package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
  236. package/dist/vbrief-reconcile/umbrellas.js +261 -26
  237. package/dist/vbrief-validate/index.d.ts +1 -1
  238. package/dist/vbrief-validate/index.js +1 -1
  239. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  240. package/dist/vbrief-validate/plan-hooks.js +68 -0
  241. package/dist/verify-ac/evaluate.d.ts +7 -1
  242. package/dist/verify-ac/evaluate.js +34 -26
  243. package/dist/verify-ac/flag.d.ts +4 -0
  244. package/dist/verify-ac/flag.js +16 -0
  245. package/dist/verify-ac/index.d.ts +2 -2
  246. package/dist/verify-ac/index.js +2 -2
  247. package/dist/verify-source/contained-writes.js +2 -0
  248. package/dist/verify-source/index.d.ts +1 -0
  249. package/dist/verify-source/index.js +1 -0
  250. package/dist/verify-source/verify-stubs.d.ts +15 -1
  251. package/dist/verify-source/verify-stubs.js +20 -16
  252. package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
  253. package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
  254. package/dist/xbrief-migrate/migrate-project.js +2 -5
  255. package/package.json +3 -3
@@ -9,19 +9,127 @@
9
9
  */
10
10
  import { existsSync, readFileSync } from "node:fs";
11
11
  import { basename, isAbsolute, relative, resolve } from "node:path";
12
- import { evaluateLiteralAcceptanceFromPlan, runLiteralAcceptanceCommands, } from "../literal-acceptance/index.js";
12
+ import { emitAcceptanceStampFromPlan } from "../intake/clause-derivation.js";
13
+ import { appendLiteralAcceptanceAdvisory, evaluateLiteralAcceptanceFromPlan, isNoopRefusalReason, runLiteralAcceptanceCommands, stripLiteralAcceptanceAdvisory, } from "../literal-acceptance/index.js";
13
14
  import { ENV_RUN_SUMMARY_PATH, RunSummaryEmitter, } from "../run-summary/index.js";
14
15
  import { maybeBankOnAcPass } from "../session/ac-pass-banking.js";
16
+ import { resolveAcReuse, resolveScopeIdForAcReuse, snapshotFromReuseFields, } from "../session/ac-pass-reuse.js";
17
+ import { hashProductState } from "../session/product-state-hash.js";
18
+ import { resolveVerifyAcSessionId, writeVerifyAcSessionCache, } from "../session/verify-ac-session-cache.js";
15
19
  import { formatClauseWalkMessage, walkAcceptanceClauses, } from "../verify-ac/clauses.js";
16
20
  import { emitVerifyAcAttempts, evaluateProductOracleIntegrity, mergeOracleVerdict, } from "../verify-ac/evaluate.js";
17
21
  import { readPlanAcceptance, validatePlanAcceptance } from "./acceptance.js";
18
- import { formatSoftEmptyMessage, isEmptyAcResolution, projectHasSuiteFloor, } from "./empty-resolution.js";
22
+ import { clauseWalkBlocks, formatAcceptanceVerdict, resolveAcceptanceVerdict, } from "./acceptance-resolver.js";
23
+ import { formatSoftEmptyMessage, formatTranscriptEmptyMessage, isEmptyAcResolution, projectHasSuiteFloor, } from "./empty-resolution.js";
19
24
  function asRecord(value) {
20
25
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
21
26
  return value;
22
27
  }
23
28
  return null;
24
29
  }
30
+ function tryReuseVerifyAc(plan, acceptance, options, projectRoot) {
31
+ const mode = options.reuseMode ?? "auto";
32
+ if (mode === "never")
33
+ return null;
34
+ const reuse = resolveAcReuse({
35
+ projectRoot,
36
+ plan,
37
+ scopeId: options.bankScopeId,
38
+ sessionId: options.sessionId,
39
+ env: options.env,
40
+ productPaths: options.productPaths,
41
+ allowCache: mode === "auto",
42
+ allowBank: true,
43
+ });
44
+ if (reuse.kind === "miss")
45
+ return null;
46
+ if (reuse.kind === "cache") {
47
+ const snap = reuse.cache.snapshot;
48
+ const cachedAcceptance = readPlanAcceptance({ acceptance: snap.acceptance });
49
+ return {
50
+ ok: snap.ok,
51
+ code: snap.code,
52
+ message: options.quiet
53
+ ? ""
54
+ : snap.message.includes("served_from=")
55
+ ? snap.message
56
+ : `${snap.message} served_from=cache`,
57
+ commands: snap.commands,
58
+ runs: snap.runs,
59
+ rejected: snap.rejected,
60
+ sourceRung: acceptance.source_rung,
61
+ noneStated: acceptance.none_stated,
62
+ acceptance: cachedAcceptance.commands.length > 0 ? cachedAcceptance : acceptance,
63
+ resolution: "verified-pass",
64
+ resolvedCommandCount: snap.resolvedCommandCount,
65
+ servedFrom: "cache",
66
+ };
67
+ }
68
+ const commandCount = acceptance.commands.length;
69
+ if (commandCount === 0)
70
+ return null;
71
+ return {
72
+ ok: true,
73
+ code: 0,
74
+ message: options.quiet
75
+ ? ""
76
+ : `verify:ac passed (#3284) served_from=bank [rung=${acceptance.source_rung}]`,
77
+ commands: acceptance.commands.map((c) => ({
78
+ command: c.command,
79
+ cwd: c.cwd ?? null,
80
+ expectedStdout: c.expectedStdout ?? null,
81
+ expectedExitCode: c.expectedExitCode ?? 0,
82
+ source: "explicit",
83
+ sourceSpan: "plan.acceptance.commands",
84
+ })),
85
+ runs: [],
86
+ sourceRung: acceptance.source_rung,
87
+ noneStated: acceptance.none_stated,
88
+ acceptance,
89
+ resolution: "verified-pass",
90
+ resolvedCommandCount: commandCount,
91
+ servedFrom: "bank",
92
+ };
93
+ }
94
+ function persistVerifyAcSessionCache(result, options, projectRoot, plan) {
95
+ if (!result.ok || result.resolution !== "verified-pass")
96
+ return;
97
+ const sessionId = resolveVerifyAcSessionId(options.env, options.sessionId);
98
+ const resolvedScope = resolveScopeIdForAcReuse(plan, options.bankScopeId);
99
+ if (sessionId === null || resolvedScope === null)
100
+ return;
101
+ const hashed = hashProductState({
102
+ projectRoot,
103
+ plan,
104
+ productPaths: options.productPaths,
105
+ });
106
+ if (!hashed.complete)
107
+ return;
108
+ try {
109
+ writeVerifyAcSessionCache({
110
+ projectRoot,
111
+ sessionId,
112
+ scopeId: resolvedScope,
113
+ productStateHash: hashed.digest,
114
+ snapshot: snapshotFromReuseFields({
115
+ ok: result.ok,
116
+ code: result.code === 2 ? 2 : result.code === 1 ? 1 : 0,
117
+ message: result.message,
118
+ commands: result.commands,
119
+ runs: result.runs,
120
+ rejected: result.rejected,
121
+ sourceRung: result.sourceRung,
122
+ noneStated: result.noneStated,
123
+ acceptance: result.acceptance,
124
+ resolution: result.resolution,
125
+ resolvedCommandCount: result.resolvedCommandCount,
126
+ }),
127
+ });
128
+ }
129
+ catch {
130
+ // fail-open: missing cache must not fail a green run
131
+ }
132
+ }
25
133
  /**
26
134
  * Evaluate product AC from an in-memory plan.
27
135
  */
@@ -30,25 +138,34 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
30
138
  const optionsWithScope = {
31
139
  ...options,
32
140
  oracleScopeKey: options.oracleScopeKey?.trim() || planId || null,
141
+ bankScopeId: options.bankScopeId?.trim() || planId || options.bankScopeId,
142
+ observedAcceptance: options.observedAcceptance !== undefined ? options.observedAcceptance : plan.acceptance,
33
143
  };
34
144
  const acceptance = readPlanAcceptance(plan);
35
145
  const schemaErrors = validatePlanAcceptance(plan.acceptance ?? acceptance);
36
146
  // Only hard-fail schema when an explicit plan.acceptance object exists.
37
147
  if (plan.acceptance !== undefined && schemaErrors.length > 0) {
148
+ const noop = schemaErrors.some((error) => isNoopRefusalReason(error));
38
149
  return applyOracle({
39
150
  ok: false,
40
- code: 2,
41
- message: `verify:ac config error (#3284): ${schemaErrors.join("; ")}`,
151
+ code: noop ? 1 : 2,
152
+ message: noop
153
+ ? `verify:ac rejected-noop (#3396): ${schemaErrors.join("; ")}`
154
+ : `verify:ac config error (#3284): ${schemaErrors.join("; ")}`,
42
155
  commands: [],
43
156
  runs: [],
44
157
  sourceRung: acceptance.source_rung,
45
158
  noneStated: acceptance.none_stated,
46
159
  acceptance,
47
- resolution: "config",
160
+ resolution: noop ? "rejected-noop" : "config",
48
161
  resolvedCommandCount: 0,
49
- }, optionsWithScope);
162
+ }, optionsWithScope, plan);
50
163
  }
51
164
  const projectRoot = resolve(optionsWithScope.projectRoot ?? process.cwd());
165
+ const reused = tryReuseVerifyAc(plan, acceptance, optionsWithScope, projectRoot);
166
+ if (reused !== null) {
167
+ return applyOracle(reused, optionsWithScope, plan);
168
+ }
52
169
  // Prefer shared literal-acceptance path so safety / promotion rules stay one place.
53
170
  // Empty plan.acceptance.commands still consults the #3267 rejected ledger
54
171
  // (Greptile P1: rejected stated AC must never soft-pass).
@@ -64,12 +181,12 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
64
181
  (optionsWithScope.checkIntegrated === true ? false : undefined),
65
182
  quiet: optionsWithScope.quiet,
66
183
  });
67
- // When literal path had nothing executable but plan.acceptance has derived commands,
68
- // run them directly with source=explicit semantics.
69
- if (base.ok &&
70
- base.runs.length === 0 &&
71
- acceptance.commands.length > 0 &&
72
- (acceptance.source_rung === "derived" || acceptance.source_rung === "project_floor")) {
184
+ // When the literal ledger produced no runs, execute non-empty plan.acceptance.commands
185
+ // as source=explicit. The documented key is plan.acceptance.commands (#3284 / #3449);
186
+ // the #3267 ledger is a parallel store and can be empty while stated commands exist.
187
+ // Stated was previously excluded, so rung=stated + empty ledger printed "nothing to run".
188
+ // Do not override a blocking rejected ledger or a config error.
189
+ if (shouldRunPlanAcceptanceDirectly(base, acceptance)) {
73
190
  const runner = optionsWithScope.runner;
74
191
  const direct = runLiteralAcceptanceCommands(acceptance.commands.map((c) => ({
75
192
  command: c.command,
@@ -83,7 +200,17 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
83
200
  runner,
84
201
  allowTaskStatement: optionsWithScope.allowTaskStatement,
85
202
  });
86
- return applyOracle(annotate(direct, acceptance, optionsWithScope.quiet), optionsWithScope);
203
+ // Carry the #3484 demotion forward: the direct path replaces `base`, and the
204
+ // advisory ledger must stay visible (reported, never blocking) (#3497).
205
+ const advisory = base.advisoryRejected ?? [];
206
+ const directWithAdvisory = {
207
+ ...direct,
208
+ advisoryRejected: advisory,
209
+ message: optionsWithScope.quiet === true
210
+ ? direct.message
211
+ : appendLiteralAcceptanceAdvisory(direct.message, advisory),
212
+ };
213
+ return applyOracle(annotate(directWithAdvisory, acceptance, optionsWithScope.quiet), optionsWithScope, plan);
87
214
  }
88
215
  // Check composition: mid-story unpromoted capture-only may soft-pass so the
89
216
  // framework graph is not deadlocked before agents promote peers.
@@ -92,7 +219,7 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
92
219
  if (optionsWithScope.checkIntegrated === true && !base.ok && base.runs.length === 0) {
93
220
  const hasRejected = (base.rejected?.length ?? 0) > 0;
94
221
  if (hasRejected) {
95
- return applyOracle(annotate(base, acceptance, optionsWithScope.quiet), optionsWithScope);
222
+ return applyOracle(annotate(base, acceptance, optionsWithScope.quiet), optionsWithScope, plan);
96
223
  }
97
224
  const unpromoted = /capture-only|task_statement|no matching agent-promoted/i.test(base.message) ||
98
225
  (base.commands.length > 0 && base.commands.every((c) => c.source === "task_statement"));
@@ -120,10 +247,21 @@ export function evaluateVerifyAcFromPlan(plan, options = {}) {
120
247
  rejectedCount: base.rejected?.length ?? 0,
121
248
  }),
122
249
  resolvedCommandCount: base.commands.length,
123
- }, optionsWithScope);
250
+ }, optionsWithScope, plan);
124
251
  }
125
252
  }
126
- return applyOracle(annotate(base, acceptance, optionsWithScope.quiet), optionsWithScope);
253
+ return applyOracle(annotate(base, acceptance, optionsWithScope.quiet), optionsWithScope, plan);
254
+ }
255
+ function shouldRunPlanAcceptanceDirectly(base, acceptance) {
256
+ if (acceptance.commands.length === 0)
257
+ return false;
258
+ if (base.runs.length > 0)
259
+ return false;
260
+ if (base.code === 2)
261
+ return false;
262
+ if ((base.rejected?.length ?? 0) > 0)
263
+ return false;
264
+ return true;
127
265
  }
128
266
  function classifyResolution(input) {
129
267
  if (input.resolution !== undefined) {
@@ -156,6 +294,17 @@ function applyEmptyFloorPolicy(result, options) {
156
294
  }
157
295
  const projectRoot = resolve(options.projectRoot ?? process.cwd());
158
296
  const suiteFloor = options.hasSuiteFloor ?? projectHasSuiteFloor(projectRoot);
297
+ const skippedPrompts = result.transcriptPromptSkipped ?? 0;
298
+ if (suiteFloor && skippedPrompts > 0) {
299
+ return {
300
+ ...result,
301
+ ok: false,
302
+ code: 1,
303
+ message: options.quiet === true ? "" : formatTranscriptEmptyMessage(result.acceptance),
304
+ resolution: "fail",
305
+ resolvedCommandCount: 0,
306
+ };
307
+ }
159
308
  if (suiteFloor) {
160
309
  return {
161
310
  ...result,
@@ -181,9 +330,13 @@ function acceptanceOutcomeOf(result) {
181
330
  return "soft_empty";
182
331
  if (result.resolution === "fail")
183
332
  return "fail";
184
- return null;
333
+ if (result.resolution === "config")
334
+ return "config-error";
335
+ if (result.resolution === "rejected-noop")
336
+ return "rejected-noop";
337
+ return "soft-missing";
185
338
  }
186
- function emitAcceptanceOutcome(result, options, projectRoot) {
339
+ function emitAcceptanceObservedStamp(options, projectRoot) {
187
340
  if (options.env === undefined) {
188
341
  return;
189
342
  }
@@ -191,20 +344,26 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
191
344
  if (dest === undefined || dest.trim().length === 0) {
192
345
  return;
193
346
  }
194
- const outcome = acceptanceOutcomeOf(result);
195
- if (outcome === null) {
347
+ emitAcceptanceStampFromPlan(projectRoot, { acceptance: options.observedAcceptance }, options.env);
348
+ }
349
+ function emitAcceptanceOutcome(result, options, projectRoot) {
350
+ if (options.env === undefined) {
351
+ return;
352
+ }
353
+ const dest = options.env[ENV_RUN_SUMMARY_PATH];
354
+ if (dest === undefined || dest.trim().length === 0) {
196
355
  return;
197
356
  }
198
357
  try {
199
358
  const emitter = new RunSummaryEmitter({
200
359
  projectRoot,
201
- sessionId: (typeof options.env.DEFT_SESSION_ID === "string" && options.env.DEFT_SESSION_ID.trim()) ||
202
- "verify-ac",
360
+ sessionId: options.sessionId,
203
361
  env: options.env,
362
+ component: "verify-ac",
204
363
  });
205
364
  emitter.emitAcceptance({
206
365
  resolved_command_count: result.resolvedCommandCount,
207
- outcome,
366
+ outcome: acceptanceOutcomeOf(result),
208
367
  source_rung: result.sourceRung,
209
368
  none_stated: result.noneStated,
210
369
  clause_count: result.clauseOutcomes?.length,
@@ -212,12 +371,58 @@ function emitAcceptanceOutcome(result, options, projectRoot) {
212
371
  id: row.id,
213
372
  outcome: row.outcome,
214
373
  })),
374
+ served_from: result.servedFrom ?? "executed",
215
375
  });
216
376
  }
217
377
  catch {
218
378
  // fail-open
219
379
  }
220
380
  }
381
+ /** Outcome on every terminal path; stamp from observed plan.acceptance (#3355). */
382
+ function emitAcceptanceTelemetry(result, options, projectRoot) {
383
+ emitAcceptanceObservedStamp(options, projectRoot);
384
+ emitAcceptanceOutcome(result, options, projectRoot);
385
+ }
386
+ /**
387
+ * CLI-only early returns that never reach evaluate still emit an acceptance
388
+ * outcome so field streams see config-error / soft-missing (#3355).
389
+ */
390
+ export function emitVerifyAcTerminalOutcome(input) {
391
+ emitAcceptanceOutcome({
392
+ ok: input.outcome !== "config-error" &&
393
+ input.outcome !== "fail" &&
394
+ input.outcome !== "rejected-noop",
395
+ code: input.outcome === "config-error"
396
+ ? 2
397
+ : input.outcome === "fail" || input.outcome === "rejected-noop"
398
+ ? 1
399
+ : 0,
400
+ message: "",
401
+ commands: [],
402
+ runs: [],
403
+ sourceRung: input.sourceRung ?? "project_floor",
404
+ noneStated: input.noneStated ?? true,
405
+ acceptance: {
406
+ commands: [],
407
+ none_stated: input.noneStated ?? true,
408
+ source_rung: input.sourceRung ?? "project_floor",
409
+ },
410
+ resolution: input.outcome === "config-error"
411
+ ? "config"
412
+ : input.outcome === "soft-missing"
413
+ ? "skipped"
414
+ : input.outcome === "fail"
415
+ ? "fail"
416
+ : input.outcome === "rejected-noop"
417
+ ? "rejected-noop"
418
+ : input.outcome === "soft_empty"
419
+ ? "soft_empty"
420
+ : input.outcome === "verified-pass"
421
+ ? "verified-pass"
422
+ : "empty-pass",
423
+ resolvedCommandCount: 0,
424
+ }, { projectRoot: input.projectRoot, env: input.env }, resolve(input.projectRoot));
425
+ }
221
426
  function applyClauseWalk(result, options) {
222
427
  const clauses = result.acceptance.clauses ?? [];
223
428
  if (clauses.length === 0 || result.resolution === "config" || result.resolution === "skipped") {
@@ -226,7 +431,16 @@ function applyClauseWalk(result, options) {
226
431
  const projectRoot = resolve(options.projectRoot ?? process.cwd());
227
432
  const report = walkAcceptanceClauses(clauses, projectRoot);
228
433
  const message = options.quiet ? result.message : formatClauseWalkMessage(report, result.message);
229
- const ok = result.ok && report.ok;
434
+ // #3497: a clause the static walk cannot decide is evidence of nothing. It blocks
435
+ // only when nothing else verified the product; a green executable acceptance run
436
+ // already is the product-first oracle. Failed clauses still block unconditionally.
437
+ const blocked = clauseWalkBlocks({
438
+ failed: report.failed.length,
439
+ verified: report.verified.length,
440
+ walked: report.clauses.length,
441
+ hasGreenExecutableRun: result.ok && result.runs.length > 0 && result.runs.every((run) => run.ok),
442
+ });
443
+ const ok = result.ok && !blocked;
230
444
  return {
231
445
  ...result,
232
446
  ok,
@@ -241,9 +455,49 @@ function applyClauseWalk(result, options) {
241
455
  clauseWalked: true,
242
456
  };
243
457
  }
244
- function applyOracle(result, options) {
458
+ function applyRejectedNoop(result) {
459
+ if (result.resolution === "rejected-noop") {
460
+ return result;
461
+ }
462
+ const rejected = result.rejected ?? [];
463
+ const fromLedger = rejected.some((row) => isNoopRefusalReason(row.reason));
464
+ const fromRuns = result.runs.some((row) => !row.ok && isNoopRefusalReason(row.detail.replace(/^refused:\s*/i, "")));
465
+ // #3484 / #3497: the advisory block quotes refusal reasons that were deliberately
466
+ // demoted because the plan states structured acceptance commands. Sniffing the
467
+ // rendered message resurrected them as a blocking no-op verdict — verify:ac
468
+ // refused while its own output said "do NOT block". Read the blocking ledgers,
469
+ // and inspect only the non-advisory part of the message.
470
+ const fromMessage = isNoopRefusalReason(stripLiteralAcceptanceAdvisory(result.message));
471
+ if (!fromLedger && !fromRuns && !fromMessage) {
472
+ return result;
473
+ }
474
+ return {
475
+ ...result,
476
+ ok: false,
477
+ code: result.code === 2 ? 2 : 1,
478
+ resolution: "rejected-noop",
479
+ };
480
+ }
481
+ /**
482
+ * Make the rendered message agree with the verdict (#3497).
483
+ *
484
+ * `annotate` stamps "verify:ac passed" from the sub-gate result, but later stages
485
+ * (clause walk, no-op ledger, oracle integrity) can still flip `ok`. The old output
486
+ * left the stale "passed" lead in place, so scope:complete printed four passing lines
487
+ * and then refused. Re-label the lead and name the deciding predicate.
488
+ */
489
+ function labelVerdict(result) {
490
+ const verdict = resolveAcceptanceVerdict(result);
491
+ if (verdict.ok) {
492
+ return result.message;
493
+ }
494
+ const relabelled = result.message.replace(/verify:ac passed \(#3284\)/g, "verify:ac FAILED (#3284)");
495
+ const line = formatAcceptanceVerdict(verdict);
496
+ return relabelled.length > 0 ? `${relabelled}\n${line}` : line;
497
+ }
498
+ function applyOracle(result, options, plan = {}) {
245
499
  const projectRoot = resolve(options.projectRoot ?? process.cwd());
246
- const walked = applyClauseWalk(result, options);
500
+ const walked = applyClauseWalk(applyRejectedNoop(result), options);
247
501
  const gated = walked.clauseWalked === true ? walked : applyEmptyFloorPolicy(walked, options);
248
502
  // Emit/read disk only when the caller supplied env (CLI passes process.env).
249
503
  // Tests stay isolated unless they opt in with env or runSummaryText.
@@ -263,14 +517,23 @@ function applyOracle(result, options) {
263
517
  env: options.env,
264
518
  });
265
519
  next = mergeOracleVerdict(gated, verdict);
266
- if (!verdict.ok && next.resolution !== "soft_empty" && next.resolution !== "config") {
520
+ if (!verdict.ok &&
521
+ next.resolution !== "soft_empty" &&
522
+ next.resolution !== "config" &&
523
+ next.resolution !== "rejected-noop") {
267
524
  next = { ...next, resolution: "fail" };
268
525
  }
269
526
  }
527
+ const stamped = {
528
+ ...next,
529
+ message: options.quiet === true ? next.message : labelVerdict(next),
530
+ servedFrom: next.servedFrom ?? "executed",
531
+ };
532
+ persistVerifyAcSessionCache(stamped, options, projectRoot, plan);
270
533
  if (options.skipAcceptanceEmit !== true) {
271
- emitAcceptanceOutcome(next, options, projectRoot);
534
+ emitAcceptanceTelemetry(stamped, options, projectRoot);
272
535
  }
273
- return next;
536
+ return stamped;
274
537
  }
275
538
  function annotate(result, acceptance, quiet) {
276
539
  const prefix = result.ok
@@ -306,6 +569,7 @@ function annotate(result, acceptance, quiet) {
306
569
  rejectedCount: result.rejected?.length ?? 0,
307
570
  }),
308
571
  resolvedCommandCount: result.runs.length > 0 ? result.runs.length : commandCount,
572
+ servedFrom: "executed",
309
573
  };
310
574
  }
311
575
  /**
@@ -338,13 +602,17 @@ export function evaluateVerifyAcFromPath(xbriefPath, options = {}) {
338
602
  const projectRoot = resolve(options.projectRoot ?? process.cwd());
339
603
  // Path-relative keys stay unique across xbrief/ vs vbrief/ and duplicate plan.id (#3337 Greptile).
340
604
  const oracleScopeKey = options.oracleScopeKey?.trim() || resolveOracleScopeKey(plan, abs, projectRoot);
341
- const result = evaluateVerifyAcFromPlan(plan, {
605
+ const emitOptions = {
342
606
  ...options,
343
- skipAcceptanceEmit: true,
344
607
  oracleScopeKey,
608
+ observedAcceptance: plan.acceptance,
609
+ };
610
+ const result = evaluateVerifyAcFromPlan(plan, {
611
+ ...emitOptions,
612
+ skipAcceptanceEmit: true,
345
613
  });
346
- const banked = maybeAttachAcPassBank(result, plan, abs, options);
347
- emitAcceptanceOutcome(banked, options, projectRoot);
614
+ const banked = maybeAttachAcPassBank(result, plan, abs, emitOptions);
615
+ emitAcceptanceTelemetry(banked, emitOptions, projectRoot);
348
616
  return banked;
349
617
  }
350
618
  /**
@@ -384,11 +652,17 @@ function maybeAttachAcPassBank(result, plan, xbriefPath, options) {
384
652
  .replace(/\.xbrief\.json$/i, "")
385
653
  .replace(/\.vbrief\.json$/i, "");
386
654
  try {
655
+ const hashed = hashProductState({
656
+ projectRoot,
657
+ plan,
658
+ productPaths: options.productPaths,
659
+ });
387
660
  const banked = maybeBankOnAcPass({
388
661
  projectRoot,
389
662
  scopeId,
390
663
  executableRuns: result.runs.length,
391
664
  quiet: options.quiet,
665
+ productStateHash: hashed.complete ? hashed.digest : null,
392
666
  });
393
667
  if (options.quiet || banked.notes.length === 0) {
394
668
  return result;
@@ -4,9 +4,11 @@
4
4
  * acceptance.commands schema, verify:ac evaluation, check-mode composition
5
5
  * (AC-first, hygiene advisory under pressure, rapid=AC-only).
6
6
  */
7
+ export type { AcServedFrom } from "../session/verify-ac-session-cache.js";
7
8
  export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
9
+ export { type AcceptanceGateProfileOptions, type AcceptancePredicate, type AcceptanceReaderProfile, type AcceptanceReading, type AcceptanceVerdict, clauseWalkBlocks, formatAcceptanceVerdict, resolveAcceptanceGateProfile, resolveAcceptanceVerdict, resolvedAcceptanceCommandCount, } from "./acceptance-resolver.js";
8
10
  export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, type ProductFirstCheckModeResolution, type ResolveProductFirstCheckModeInput, resolveProductFirstCheckMode, } from "./check-mode.js";
9
11
  export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, type VerifyAcResolution, } from "./empty-resolution.js";
10
- export { type EvaluateVerifyAcOptions, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, type VerifyAcResult, } from "./evaluate.js";
12
+ export { type EvaluateVerifyAcOptions, emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, type VerifyAcResult, } from "./evaluate.js";
11
13
  export { type AcceptanceCommand, type AcSourceRung, ENV_CHECK_AC_ONLY, ENV_CHECK_MODE, ENV_HYGIENE_ADVISORY, HYGIENE_GATE_ID_PREFIXES, PLAN_ACCEPTANCE_KEY, type PlanAcceptance, PRODUCT_AC_GATE_ID, type ProductFirstCheckMode, } from "./types.js";
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -5,8 +5,9 @@
5
5
  * (AC-first, hygiene advisory under pressure, rapid=AC-only).
6
6
  */
7
7
  export { attachPlanAcceptance, buildAcceptanceFromIntakeCapture, readPlanAcceptance, stampAcceptanceFromLiteralCapture, validatePlanAcceptance, } from "./acceptance.js";
8
+ export { clauseWalkBlocks, formatAcceptanceVerdict, resolveAcceptanceGateProfile, resolveAcceptanceVerdict, resolvedAcceptanceCommandCount, } from "./acceptance-resolver.js";
8
9
  export { applyProductFirstGateMode, isHygieneGate, isProductAcGate, resolveProductFirstCheckMode, } from "./check-mode.js";
9
10
  export { EMPTY_AC_CAUSE, EMPTY_AC_OUTCOME, EMPTY_AC_REMEDY, formatSoftEmptyMessage, isEmptyAcResolution, isSoftEmptyAcText, projectHasSuiteFloor, } from "./empty-resolution.js";
10
- export { evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, } from "./evaluate.js";
11
+ export { emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, evaluateVerifyAcFromPlan, isVerifyAcRequiredAtCeremonyDepth, resolveOracleScopeKey, } from "./evaluate.js";
11
12
  export { ENV_CHECK_AC_ONLY, ENV_CHECK_MODE, ENV_HYGIENE_ADVISORY, HYGIENE_GATE_ID_PREFIXES, PLAN_ACCEPTANCE_KEY, PRODUCT_AC_GATE_ID, } from "./types.js";
12
13
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,104 @@
1
+ import { lstatSync } from "node:fs";
2
+ /**
3
+ * Shared "not product source" core (#3487) plus the coverage artifacts only the
4
+ * release archive names. Scratch and agent worktrees are never shippable
5
+ * framework content (#2953). The archive set is git-tracked files (#3490); this
6
+ * denylist is defence in depth, including for a tracked path under a core name.
7
+ *
8
+ * Callers may still widen this per run via `extraExcludes` / `--exclude-extra`.
9
+ */
1
10
  export declare const DEFAULT_EXCLUDES: Set<string>;
2
11
  export declare const DEFAULT_EXCLUDED_PATH_PREFIXES: readonly ["history/archive", "vbrief/completed", "vbrief/cancelled"];
12
+ /**
13
+ * Generated outputs that may be packed even when untracked. Empty by default:
14
+ * the release archive is a source distribution, so extra outputs must be named
15
+ * explicitly (#3490).
16
+ */
17
+ export declare const DEFAULT_GENERATED_ALLOWLIST: readonly string[];
3
18
  export declare const ARCHIVE_ROOT = "deft";
4
19
  export declare const CONTENT_PREFIX = "content/";
20
+ /** Raised when the resolved archive set contains an untracked surprise (#3490). */
21
+ export declare class UntrackedArchiveEntryError extends Error {
22
+ readonly paths: readonly string[];
23
+ constructor(paths: readonly string[]);
24
+ }
25
+ export type ArchiveSourceEntry = {
26
+ absPath: string | Buffer;
27
+ archiveRel: string;
28
+ archiveRelBytes: Buffer;
29
+ };
30
+ /** Private-use mark so archiver's string `name` can carry raw member bytes. */
31
+ export declare const ARCHIVE_BINARY_NAME_MARK = "\uF000";
32
+ export declare function flattenContentPrefixBytes(relBytes: Buffer): Buffer;
33
+ export declare function isValidUtf8Buffer(buf: Buffer): boolean;
34
+ export declare function archiveMemberBytes(archiveRelBytes: Buffer): Buffer;
35
+ /** Archiver `file()`/`append` name: UTF-8 text, or marked latin1 of raw bytes. */
36
+ export declare function archiverEntryName(memberBytes: Buffer): string;
37
+ /** Original `ls-files -z` segment bytes plus the display/skip string. */
38
+ export type GitLsFilesPath = {
39
+ readonly relPosix: string;
40
+ readonly bytes: Buffer;
41
+ };
42
+ export type ArchiveFsLookup = {
43
+ realpathSync: (path: string | Buffer, options?: {
44
+ encoding?: BufferEncoding | "buffer" | null;
45
+ }) => string | Buffer;
46
+ lstatSync: (path: string | Buffer) => ReturnType<typeof lstatSync>;
47
+ };
48
+ /** `-z` stdout is a NUL-separated byte stream; never UTF-8-decode it whole. */
49
+ export declare const GIT_LS_FILES_Z_ENCODING: null;
50
+ export type GitLsFilesZSpawn = (command: string, args: readonly string[], options: {
51
+ encoding: null;
52
+ timeout?: number;
53
+ maxBuffer?: number;
54
+ stdio?: readonly ["ignore", "pipe", "pipe"];
55
+ }) => {
56
+ status: number | null;
57
+ stdout?: Buffer | string | null;
58
+ stderr?: Buffer | string | null;
59
+ error?: Error | null;
60
+ signal?: NodeJS.Signals | null;
61
+ };
62
+ /** Decode one `ls-files -z` segment; keep non-UTF-8 bytes as latin1. */
63
+ export declare function decodeGitLsFilesPath(segment: Buffer): string;
64
+ /** Split a raw `git ls-files -z` Buffer on 0x00, keeping original path bytes. */
65
+ export declare function splitGitLsFilesZRecords(stdout: Buffer): GitLsFilesPath[];
66
+ /** Split a raw `git ls-files -z` Buffer on 0x00, then decode each path. */
67
+ export declare function splitGitLsFilesZ(stdout: Buffer): string[];
68
+ /**
69
+ * Return `git ls-files -z` paths with original bytes. Fail closed when git
70
+ * cannot enumerate tracked files -- a walk would pack host-local untracked
71
+ * state (#3490).
72
+ */
73
+ export declare function listGitTrackedPathRecords(root: string, spawn?: GitLsFilesZSpawn): GitLsFilesPath[];
74
+ /** Return `git ls-files -z` paths in POSIX form. */
75
+ export declare function listGitTrackedFiles(root: string, spawn?: GitLsFilesZSpawn): string[];
76
+ /**
77
+ * Fail closed when any resolved archive path is untracked and not on the
78
+ * generated-output allowlist (#3490).
79
+ */
80
+ export declare function assertArchiveEntriesTrackedOrGenerated(root: string, rels: readonly string[], generatedAllowlist?: readonly string[], spawn?: GitLsFilesZSpawn): void;
81
+ export type ResolveArchiveEntriesOptions = {
82
+ readonly extraExcludes?: readonly string[];
83
+ readonly excludedPrefixes?: readonly string[];
84
+ readonly generatedAllowlist?: readonly string[];
85
+ readonly spawn?: GitLsFilesZSpawn;
86
+ readonly fs?: ArchiveFsLookup;
87
+ };
88
+ /** Join canonical root bytes with original git path bytes; convert POSIX `/` to OS sep. */
89
+ export declare function joinRootAndRelBytes(rootBytes: Buffer, relBytes: Buffer): Buffer;
90
+ /**
91
+ * String path when the `-z` segment is valid UTF-8; otherwise a Buffer that
92
+ * keeps the original filename bytes for fs lookup (#3490 residual).
93
+ */
94
+ export declare function fsLookupPath(root: string, relPosix: string, bytes: Buffer, rootBytes?: Buffer): string | Buffer | null;
95
+ /** Resolve a POSIX rel path under root, or null if it escapes (#3490 review). */
96
+ export declare function containedAbsPath(root: string, relPosix: string): string | null;
97
+ /**
98
+ * Derive the archive file set from git-tracked paths plus an explicit
99
+ * generated-output allowlist. The basename denylist remains defence in depth.
100
+ */
101
+ export declare function resolveArchiveEntries(root: string, options?: ResolveArchiveEntriesOptions): ArchiveSourceEntry[];
5
102
  export declare function iterSourceFiles(root: string, excludes?: ReadonlySet<string>, excludedPrefixes?: readonly string[]): Array<{
6
103
  absPath: string;
7
104
  archiveRel: string;
@@ -16,8 +113,12 @@ export type BuildProgress = {
16
113
  };
17
114
  export type BuildArchiveOptions = {
18
115
  readonly extraExcludes?: readonly string[];
116
+ /** Untracked generated outputs that may be packed (#3490). */
117
+ readonly generatedAllowlist?: readonly string[];
19
118
  /** Progress sink; default silent so unit tests stay quiet (#2953). */
20
119
  readonly onProgress?: (p: BuildProgress) => void;
120
+ readonly spawn?: GitLsFilesZSpawn;
121
+ readonly fs?: ArchiveFsLookup;
21
122
  };
22
123
  /**
23
124
  * Emit human-readable stage progress for long release packaging runs (#2953).