@deftai/directive-core 0.104.0 → 0.106.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 (287) hide show
  1. package/dist/authz/classify.js +1108 -67
  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 +34 -4
  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/forward-coverage/diff-coverage.d.ts +39 -0
  17. package/dist/forward-coverage/diff-coverage.js +112 -0
  18. package/dist/forward-coverage/diff-lines.d.ts +19 -0
  19. package/dist/forward-coverage/diff-lines.js +173 -0
  20. package/dist/forward-coverage/evaluate.d.ts +24 -9
  21. package/dist/forward-coverage/evaluate.js +125 -19
  22. package/dist/fs/contained-write.d.ts +115 -1
  23. package/dist/fs/contained-write.js +241 -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 +1 -0
  31. package/dist/index.js +2 -0
  32. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  33. package/dist/init-deposit/agent-hooks.js +18 -16
  34. package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
  35. package/dist/init-deposit/core-guard-pin-content.js +393 -0
  36. package/dist/init-deposit/gitignore.js +9 -0
  37. package/dist/init-deposit/hygiene.d.ts +47 -11
  38. package/dist/init-deposit/hygiene.js +172 -37
  39. package/dist/init-deposit/legacy-detect.js +6 -2
  40. package/dist/init-deposit/refresh.d.ts +21 -1
  41. package/dist/init-deposit/refresh.js +106 -9
  42. package/dist/init-deposit/scaffold.js +28 -279
  43. package/dist/init-deposit/slash-deposit.js +13 -7
  44. package/dist/init-deposit/xbrief-projections.d.ts +8 -1
  45. package/dist/init-deposit/xbrief-projections.js +24 -11
  46. package/dist/intake/clause-derivation.d.ts +47 -1
  47. package/dist/intake/clause-derivation.js +238 -9
  48. package/dist/intake/issue-ingest.d.ts +3 -1
  49. package/dist/intake/issue-ingest.js +35 -6
  50. package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
  51. package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
  52. package/dist/lifecycle-visible/evaluate.d.ts +98 -0
  53. package/dist/lifecycle-visible/evaluate.js +710 -0
  54. package/dist/lifecycle-visible/index.d.ts +2 -0
  55. package/dist/lifecycle-visible/index.js +2 -0
  56. package/dist/literal-acceptance/capture.d.ts +10 -0
  57. package/dist/literal-acceptance/capture.js +226 -77
  58. package/dist/literal-acceptance/evaluate.d.ts +29 -4
  59. package/dist/literal-acceptance/evaluate.js +70 -2
  60. package/dist/literal-acceptance/index.d.ts +3 -3
  61. package/dist/literal-acceptance/index.js +3 -3
  62. package/dist/literal-acceptance/safety.d.ts +41 -0
  63. package/dist/literal-acceptance/safety.js +126 -2
  64. package/dist/literal-acceptance/types.d.ts +8 -0
  65. package/dist/orchestration/judgment-policy.d.ts +13 -0
  66. package/dist/orchestration/judgment-policy.js +15 -0
  67. package/dist/orchestration/probe-session.js +13 -8
  68. package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
  69. package/dist/orchestration/verify-judgment-gates.js +43 -11
  70. package/dist/orphan-active/evaluate.d.ts +7 -1
  71. package/dist/orphan-active/evaluate.js +102 -23
  72. package/dist/platform/platform-capabilities.js +2 -3
  73. package/dist/policy/base-branch.d.ts +37 -0
  74. package/dist/policy/base-branch.js +114 -0
  75. package/dist/policy/branch-sync.d.ts +80 -0
  76. package/dist/policy/branch-sync.js +211 -0
  77. package/dist/policy/ceremony-dial-escalation.js +1 -0
  78. package/dist/policy/ceremony-dial.js +11 -21
  79. package/dist/policy/delivery-branch.d.ts +5 -3
  80. package/dist/policy/delivery-branch.js +7 -3
  81. package/dist/policy/file-size-thresholds.d.ts +24 -0
  82. package/dist/policy/file-size-thresholds.js +23 -0
  83. package/dist/policy/forge-outage-retry.d.ts +47 -0
  84. package/dist/policy/forge-outage-retry.js +157 -0
  85. package/dist/policy/host-hooks.d.ts +20 -0
  86. package/dist/policy/host-hooks.js +121 -2
  87. package/dist/policy/index.d.ts +7 -0
  88. package/dist/policy/index.js +66 -7
  89. package/dist/policy/org-force-on-migration.js +2 -1
  90. package/dist/policy/plan-extensions.d.ts +1 -1
  91. package/dist/policy/plan-extensions.js +2 -0
  92. package/dist/policy/product-signal.js +6 -7
  93. package/dist/policy/project-invariants.d.ts +50 -0
  94. package/dist/policy/project-invariants.js +205 -0
  95. package/dist/policy/require-human-merge.js +8 -6
  96. package/dist/policy/resolve.d.ts +7 -2
  97. package/dist/policy/resolve.js +25 -8
  98. package/dist/policy/sync-default.d.ts +129 -0
  99. package/dist/policy/sync-default.js +452 -0
  100. package/dist/policy/sync-max-files.d.ts +93 -0
  101. package/dist/policy/sync-max-files.js +192 -0
  102. package/dist/policy/value-feedback.js +7 -8
  103. package/dist/pr-monitor/absent-required.d.ts +11 -0
  104. package/dist/pr-monitor/absent-required.js +61 -0
  105. package/dist/pr-monitor/constants.d.ts +7 -0
  106. package/dist/pr-monitor/constants.js +7 -0
  107. package/dist/pr-monitor/index.d.ts +2 -1
  108. package/dist/pr-monitor/index.js +2 -1
  109. package/dist/pr-monitor/main.js +4 -0
  110. package/dist/pr-monitor/monitor.js +24 -1
  111. package/dist/pr-wait-mergeable/classify.js +6 -0
  112. package/dist/preflight/evaluate.d.ts +4 -0
  113. package/dist/preflight/evaluate.js +35 -1
  114. package/dist/preflight/index.d.ts +4 -0
  115. package/dist/preflight/index.js +2 -0
  116. package/dist/preflight/intended-placement.d.ts +62 -0
  117. package/dist/preflight/intended-placement.js +275 -0
  118. package/dist/preflight/project-invariants-gate.d.ts +29 -0
  119. package/dist/preflight/project-invariants-gate.js +82 -0
  120. package/dist/preflight-cache/evaluate.d.ts +12 -0
  121. package/dist/preflight-cache/evaluate.js +15 -1
  122. package/dist/preflight-cache/index.d.ts +1 -1
  123. package/dist/preflight-cache/index.js +1 -1
  124. package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
  125. package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
  126. package/dist/product-first-done-gate/acceptance.d.ts +7 -4
  127. package/dist/product-first-done-gate/acceptance.js +79 -8
  128. package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
  129. package/dist/product-first-done-gate/empty-resolution.js +12 -1
  130. package/dist/product-first-done-gate/evaluate.d.ts +16 -0
  131. package/dist/product-first-done-gate/evaluate.js +298 -34
  132. package/dist/product-first-done-gate/index.d.ts +2 -0
  133. package/dist/product-first-done-gate/index.js +1 -0
  134. package/dist/release/build-dist.d.ts +101 -0
  135. package/dist/release/build-dist.js +428 -46
  136. package/dist/release/closed-verb-gate.d.ts +12 -0
  137. package/dist/release/closed-verb-gate.js +51 -0
  138. package/dist/release/index.d.ts +1 -0
  139. package/dist/release/index.js +1 -0
  140. package/dist/release/pipeline.js +7 -0
  141. package/dist/release/types.d.ts +12 -0
  142. package/dist/render/constants.d.ts +2 -1
  143. package/dist/render/constants.js +2 -1
  144. package/dist/render/framework-commands.js +4 -0
  145. package/dist/render/spec-validate.js +23 -12
  146. package/dist/run-summary/emit.d.ts +16 -10
  147. package/dist/run-summary/emit.js +26 -15
  148. package/dist/run-summary/index.d.ts +3 -2
  149. package/dist/run-summary/index.js +2 -1
  150. package/dist/run-summary/session-id.d.ts +21 -0
  151. package/dist/run-summary/session-id.js +92 -0
  152. package/dist/run-summary/types.d.ts +33 -4
  153. package/dist/run-summary/types.js +1 -0
  154. package/dist/scope/acceptance-evidence.d.ts +19 -7
  155. package/dist/scope/acceptance-evidence.js +38 -11
  156. package/dist/scope/delivery-evidence.d.ts +3 -3
  157. package/dist/scope/delivery-evidence.js +22 -27
  158. package/dist/scope/index.d.ts +1 -0
  159. package/dist/scope/index.js +1 -0
  160. package/dist/scope/main.js +15 -5
  161. package/dist/scope/parent-lineage.js +7 -6
  162. package/dist/scope/project-invariant-coverage.d.ts +54 -0
  163. package/dist/scope/project-invariant-coverage.js +322 -0
  164. package/dist/scope/promote-from-issue.js +5 -5
  165. package/dist/scope/transition.js +1 -1
  166. package/dist/scope-provenance/compare-intent.d.ts +27 -0
  167. package/dist/scope-provenance/compare-intent.js +233 -0
  168. package/dist/scope-provenance/digest.d.ts +18 -1
  169. package/dist/scope-provenance/digest.js +16 -11
  170. package/dist/scope-provenance/evaluate.d.ts +5 -1
  171. package/dist/scope-provenance/evaluate.js +67 -7
  172. package/dist/scope-provenance/extract-intent.d.ts +43 -0
  173. package/dist/scope-provenance/extract-intent.js +262 -0
  174. package/dist/scope-provenance/index.d.ts +7 -1
  175. package/dist/scope-provenance/index.js +7 -1
  176. package/dist/scope-provenance/intent-digest.d.ts +12 -0
  177. package/dist/scope-provenance/intent-digest.js +33 -0
  178. package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
  179. package/dist/scope-provenance/intent-evaluate.js +149 -0
  180. package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
  181. package/dist/scope-provenance/json-tokenizer.js +225 -0
  182. package/dist/scope-provenance/known-machine.d.ts +38 -0
  183. package/dist/scope-provenance/known-machine.js +157 -0
  184. package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
  185. package/dist/scope-provenance/mint-artifacts.js +425 -0
  186. package/dist/session/ac-pass-banking.d.ts +9 -36
  187. package/dist/session/ac-pass-banking.js +33 -79
  188. package/dist/session/ac-pass-reuse.d.ts +53 -0
  189. package/dist/session/ac-pass-reuse.js +95 -0
  190. package/dist/session/git.js +19 -4
  191. package/dist/session/index.d.ts +4 -0
  192. package/dist/session/index.js +4 -0
  193. package/dist/session/occupancy-stress-worker.d.ts +7 -0
  194. package/dist/session/occupancy-stress-worker.js +36 -0
  195. package/dist/session/occupancy.d.ts +97 -0
  196. package/dist/session/occupancy.js +422 -0
  197. package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
  198. package/dist/session/process-cost.d.ts +32 -10
  199. package/dist/session/process-cost.js +189 -1
  200. package/dist/session/product-state-hash.d.ts +26 -0
  201. package/dist/session/product-state-hash.js +700 -0
  202. package/dist/session/ritual-entrypoint.js +16 -1
  203. package/dist/session/session-ready.d.ts +2 -0
  204. package/dist/session/session-ready.js +52 -30
  205. package/dist/session/session-start.d.ts +13 -0
  206. package/dist/session/session-start.js +103 -9
  207. package/dist/session/verify-ac-session-cache.d.ts +42 -0
  208. package/dist/session/verify-ac-session-cache.js +126 -0
  209. package/dist/session/verify-session-ritual.d.ts +28 -0
  210. package/dist/session/verify-session-ritual.js +97 -5
  211. package/dist/slash/openclaw-deposit.js +13 -9
  212. package/dist/slice/constants.d.ts +2 -2
  213. package/dist/slice/constants.js +2 -2
  214. package/dist/slice/existing.js +9 -3
  215. package/dist/slice/lock.d.ts +16 -1
  216. package/dist/slice/lock.js +137 -11
  217. package/dist/story-ready/evaluate.d.ts +2 -0
  218. package/dist/story-ready/evaluate.js +13 -0
  219. package/dist/swarm/complete-cohort.d.ts +3 -0
  220. package/dist/swarm/complete-cohort.js +59 -11
  221. package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
  222. package/dist/swarm/finalize-cohort-cli.js +165 -35
  223. package/dist/swarm/finalize-cohort.d.ts +1 -0
  224. package/dist/swarm/finalize-cohort.js +47 -4
  225. package/dist/swarm/launch.d.ts +22 -0
  226. package/dist/swarm/launch.js +155 -0
  227. package/dist/swarm/routing-set-cli.d.ts +11 -1
  228. package/dist/swarm/routing-set-cli.js +15 -3
  229. package/dist/swarm/routing-verify.d.ts +13 -1
  230. package/dist/swarm/routing-verify.js +60 -21
  231. package/dist/swarm/routing.d.ts +14 -0
  232. package/dist/swarm/routing.js +53 -3
  233. package/dist/telemetry-coverage/fake-trial.js +10 -0
  234. package/dist/telemetry-coverage/kinds.js +2 -0
  235. package/dist/triage/actions/index.js +4 -1
  236. package/dist/triage/bootstrap/index.js +6 -6
  237. package/dist/triage/help/registry-data.d.ts +1 -1
  238. package/dist/triage/help/registry-data.js +1 -1
  239. package/dist/triage/welcome/writers.d.ts +1 -1
  240. package/dist/triage/welcome/writers.js +10 -5
  241. package/dist/ts-check-lane/index.d.ts +3 -0
  242. package/dist/ts-check-lane/index.js +2 -0
  243. package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
  244. package/dist/ts-check-lane/progress-reporter.js +38 -0
  245. package/dist/ts-check-lane/progress.d.ts +32 -0
  246. package/dist/ts-check-lane/progress.js +57 -0
  247. package/dist/ts-check-lane/run-lane.d.ts +3 -1
  248. package/dist/ts-check-lane/run-lane.js +8 -6
  249. package/dist/validate-content/validate-links.d.ts +8 -0
  250. package/dist/validate-content/validate-links.js +9 -15
  251. package/dist/value/feedback-file.js +12 -2
  252. package/dist/value/readback.d.ts +4 -1
  253. package/dist/value/readback.js +51 -12
  254. package/dist/vbrief-build/index.d.ts +1 -0
  255. package/dist/vbrief-build/index.js +1 -0
  256. package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
  257. package/dist/vbrief-build/project-invariants-io.js +41 -0
  258. package/dist/vbrief-reconcile/graph.js +18 -1
  259. package/dist/vbrief-reconcile/index.d.ts +1 -1
  260. package/dist/vbrief-reconcile/index.js +1 -1
  261. package/dist/vbrief-reconcile/types.d.ts +13 -0
  262. package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
  263. package/dist/vbrief-reconcile/umbrellas.js +261 -26
  264. package/dist/vbrief-validate/index.d.ts +1 -1
  265. package/dist/vbrief-validate/index.js +1 -1
  266. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  267. package/dist/vbrief-validate/plan-hooks.js +68 -0
  268. package/dist/vbrief-validate/schema.d.ts +4 -0
  269. package/dist/vbrief-validate/schema.js +1 -1
  270. package/dist/verify-ac/evaluate.d.ts +7 -1
  271. package/dist/verify-ac/evaluate.js +34 -26
  272. package/dist/verify-ac/flag.d.ts +4 -0
  273. package/dist/verify-ac/flag.js +16 -0
  274. package/dist/verify-ac/index.d.ts +2 -2
  275. package/dist/verify-ac/index.js +2 -2
  276. package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
  277. package/dist/verify-env/agent-hook-readiness.js +12 -8
  278. package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
  279. package/dist/verify-env/agent-hooks-live-probe.js +26 -5
  280. package/dist/verify-env/agent-hooks.js +3 -4
  281. package/dist/verify-source/contained-writes.js +2 -0
  282. package/dist/verify-source/verify-stubs.d.ts +15 -1
  283. package/dist/verify-source/verify-stubs.js +20 -16
  284. package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
  285. package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
  286. package/dist/xbrief-migrate/migrate-project.js +2 -5
  287. package/package.json +7 -3
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Typed plan.policy.projectInvariants (#3425 Story A).
3
+ *
4
+ * Authored project-level must-not-break contracts. Empty or absent list is a
5
+ * no-op. Parse/validate only — preflight fail-closed is Story B.
6
+ */
7
+ export declare const FIELD_PROJECT_INVARIANTS = "plan.policy.projectInvariants";
8
+ export declare const FIELD_PROJECT_INVARIANTS_CLI_ALIAS = "projectInvariants";
9
+ export interface ProjectInvariantContractSurface {
10
+ readonly paths: readonly string[];
11
+ readonly moduleIds: readonly string[];
12
+ }
13
+ export interface ProjectInvariant {
14
+ readonly id: string;
15
+ readonly statement: string;
16
+ readonly contractSurface: ProjectInvariantContractSurface;
17
+ }
18
+ export type ProjectInvariantsSource = "typed" | "default" | "default-on-error";
19
+ export interface ProjectInvariantsParseResult {
20
+ readonly invariants: readonly ProjectInvariant[];
21
+ readonly errors: readonly string[];
22
+ }
23
+ export interface ProjectInvariantsResolved {
24
+ readonly invariants: readonly ProjectInvariant[];
25
+ readonly source: ProjectInvariantsSource;
26
+ readonly error: string | null;
27
+ }
28
+ export interface ProjectInvariantsPolicyField {
29
+ readonly name: typeof FIELD_PROJECT_INVARIANTS;
30
+ readonly current: readonly ProjectInvariant[];
31
+ readonly default: readonly ProjectInvariant[];
32
+ readonly source: string;
33
+ }
34
+ /** Parse a raw projectInvariants value. Absent/null → empty list, no errors. */
35
+ export declare function parseProjectInvariants(raw: unknown): ProjectInvariantsParseResult;
36
+ /**
37
+ * Map codeStructure.modules[] id → pathGlobs for applicability intersection.
38
+ * Pure: pass PROJECT-DEFINITION (or plan) data, no disk IO.
39
+ */
40
+ export declare function extractModulePathGlobs(projectDefinition: unknown): Record<string, string[]>;
41
+ /**
42
+ * Resolve projectInvariants from PROJECT-DEFINITION data already in memory.
43
+ * Absent/null → default empty. Present invalid → default-on-error + message.
44
+ */
45
+ export declare function resolveProjectInvariantsFromData(data: Record<string, unknown> | null): ProjectInvariantsResolved;
46
+ /** Resolve from a project root via the PROJECT-DEFINITION IO path. */
47
+ export declare function resolveProjectInvariants(projectRoot: string): ProjectInvariantsResolved;
48
+ /** Inspector row for `task policy:show --field=projectInvariants`. */
49
+ export declare function inspectProjectInvariants(data: Record<string, unknown> | null, projectRoot?: string): ProjectInvariantsPolicyField;
50
+ //# sourceMappingURL=project-invariants.d.ts.map
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Typed plan.policy.projectInvariants (#3425 Story A).
3
+ *
4
+ * Authored project-level must-not-break contracts. Empty or absent list is a
5
+ * no-op. Parse/validate only — preflight fail-closed is Story B.
6
+ */
7
+ import { readPlanPolicy } from "./plan-extensions.js";
8
+ import { loadProjectDefinition } from "./resolve.js";
9
+ export const FIELD_PROJECT_INVARIANTS = "plan.policy.projectInvariants";
10
+ export const FIELD_PROJECT_INVARIANTS_CLI_ALIAS = "projectInvariants";
11
+ const EMPTY = [];
12
+ function asRecord(value) {
13
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
14
+ return value;
15
+ }
16
+ return null;
17
+ }
18
+ function isNonEmptyString(value) {
19
+ return typeof value === "string" && value.trim().length > 0;
20
+ }
21
+ function asStrList(value) {
22
+ if (value === null || value === undefined)
23
+ return [];
24
+ if (typeof value === "string")
25
+ return value.trim().length > 0 ? [value.trim()] : [];
26
+ if (Array.isArray(value)) {
27
+ return value.map((item) => String(item).trim()).filter((s) => s.length > 0);
28
+ }
29
+ return [];
30
+ }
31
+ function pythonTypeName(value) {
32
+ if (value === null)
33
+ return "None";
34
+ if (Array.isArray(value))
35
+ return "list";
36
+ if (typeof value === "boolean")
37
+ return "bool";
38
+ if (typeof value === "number")
39
+ return Number.isInteger(value) ? "int" : "float";
40
+ if (typeof value === "string")
41
+ return "str";
42
+ if (typeof value === "object")
43
+ return "dict";
44
+ return typeof value;
45
+ }
46
+ function parseContractSurface(body, loc) {
47
+ const nested = asRecord(body.contractSurface) ?? asRecord(body.contract_surface) ?? asRecord(body.contract);
48
+ const pathSource = nested ?? body;
49
+ const moduleSource = nested ?? body;
50
+ const pathsRaw = pathSource.paths ?? pathSource.pathGlobs ?? pathSource.path_globs ?? body.paths;
51
+ const moduleRaw = moduleSource.moduleIds ??
52
+ moduleSource.module_ids ??
53
+ moduleSource.modules ??
54
+ body.moduleIds ??
55
+ body.module_ids;
56
+ // Shorthand: contractSurface / contract_surface as a string list of paths.
57
+ const shorthand = body.contractSurface ?? body.contract_surface ?? body.contract;
58
+ const paths = [...asStrList(pathsRaw), ...(nested === null ? asStrList(shorthand) : [])];
59
+ const moduleIds = asStrList(moduleRaw);
60
+ if (paths.length === 0 && moduleIds.length === 0) {
61
+ return {
62
+ surface: null,
63
+ errors: [
64
+ `${loc}: contract surface requires paths[] and/or moduleIds[] ` +
65
+ `(must-not-break module/contract)`,
66
+ ],
67
+ };
68
+ }
69
+ return {
70
+ surface: { paths, moduleIds },
71
+ errors: [],
72
+ };
73
+ }
74
+ /** Parse a raw projectInvariants value. Absent/null → empty list, no errors. */
75
+ export function parseProjectInvariants(raw) {
76
+ if (raw === null || raw === undefined) {
77
+ return { invariants: EMPTY, errors: [] };
78
+ }
79
+ if (!Array.isArray(raw)) {
80
+ return {
81
+ invariants: EMPTY,
82
+ errors: [`${FIELD_PROJECT_INVARIANTS} must be an array; got ${pythonTypeName(raw)}`],
83
+ };
84
+ }
85
+ const invariants = [];
86
+ const errors = [];
87
+ const seen = new Set();
88
+ for (let i = 0; i < raw.length; i += 1) {
89
+ const loc = `${FIELD_PROJECT_INVARIANTS}[${i}]`;
90
+ const item = raw[i];
91
+ const rec = asRecord(item);
92
+ if (rec === null) {
93
+ errors.push(`${loc}: entry must be an object`);
94
+ continue;
95
+ }
96
+ const id = rec.id;
97
+ const statement = rec.statement ?? rec.Statement;
98
+ if (!isNonEmptyString(id)) {
99
+ errors.push(`${loc}: id is required`);
100
+ continue;
101
+ }
102
+ const trimmedId = id.trim();
103
+ if (seen.has(trimmedId)) {
104
+ errors.push(`${loc}: duplicate id '${trimmedId}'`);
105
+ continue;
106
+ }
107
+ if (!isNonEmptyString(statement)) {
108
+ errors.push(`${loc}: statement is required`);
109
+ continue;
110
+ }
111
+ const { surface, errors: surfaceErrors } = parseContractSurface(rec, loc);
112
+ errors.push(...surfaceErrors);
113
+ if (surface === null)
114
+ continue;
115
+ seen.add(trimmedId);
116
+ invariants.push({
117
+ id: trimmedId,
118
+ statement: String(statement).trim(),
119
+ contractSurface: surface,
120
+ });
121
+ }
122
+ return { invariants, errors };
123
+ }
124
+ /**
125
+ * Map codeStructure.modules[] id → pathGlobs for applicability intersection.
126
+ * Pure: pass PROJECT-DEFINITION (or plan) data, no disk IO.
127
+ */
128
+ export function extractModulePathGlobs(projectDefinition) {
129
+ const root = asRecord(projectDefinition);
130
+ if (root === null)
131
+ return {};
132
+ const plan = asRecord(root.plan) ?? root;
133
+ const architecture = asRecord(plan.architecture);
134
+ if (architecture === null)
135
+ return {};
136
+ const codeStructure = asRecord(architecture.codeStructure ?? architecture.code_structure);
137
+ if (codeStructure === null)
138
+ return {};
139
+ const modules = codeStructure.modules;
140
+ if (!Array.isArray(modules))
141
+ return {};
142
+ const out = {};
143
+ for (const item of modules) {
144
+ const rec = asRecord(item);
145
+ if (rec === null || !isNonEmptyString(rec.id))
146
+ continue;
147
+ const globs = asStrList(rec.pathGlobs ?? rec.path_globs ?? rec.paths);
148
+ if (globs.length === 0)
149
+ continue;
150
+ out[rec.id.trim()] = globs;
151
+ }
152
+ return out;
153
+ }
154
+ function readPolicyBlock(data) {
155
+ if (data === null)
156
+ return null;
157
+ const policy = readPlanPolicy(data.plan);
158
+ return asRecord(policy);
159
+ }
160
+ /**
161
+ * Resolve projectInvariants from PROJECT-DEFINITION data already in memory.
162
+ * Absent/null → default empty. Present invalid → default-on-error + message.
163
+ */
164
+ export function resolveProjectInvariantsFromData(data) {
165
+ const policy = readPolicyBlock(data);
166
+ if (policy === null || !("projectInvariants" in policy)) {
167
+ return { invariants: EMPTY, source: "default", error: null };
168
+ }
169
+ const raw = policy.projectInvariants;
170
+ if (raw === null) {
171
+ return { invariants: EMPTY, source: "default", error: null };
172
+ }
173
+ const parsed = parseProjectInvariants(raw);
174
+ if (parsed.errors.length > 0) {
175
+ return {
176
+ invariants: EMPTY,
177
+ source: "default-on-error",
178
+ error: parsed.errors.join("; "),
179
+ };
180
+ }
181
+ return { invariants: parsed.invariants, source: "typed", error: null };
182
+ }
183
+ /** Resolve from a project root via the PROJECT-DEFINITION IO path. */
184
+ export function resolveProjectInvariants(projectRoot) {
185
+ const [data, err] = loadProjectDefinition(projectRoot);
186
+ if (data === null) {
187
+ return { invariants: EMPTY, source: "default", error: err };
188
+ }
189
+ return resolveProjectInvariantsFromData(data);
190
+ }
191
+ /** Inspector row for `task policy:show --field=projectInvariants`. */
192
+ export function inspectProjectInvariants(data, projectRoot) {
193
+ const resolved = data !== null
194
+ ? resolveProjectInvariantsFromData(data)
195
+ : projectRoot !== undefined && projectRoot.length > 0
196
+ ? resolveProjectInvariants(projectRoot)
197
+ : { invariants: EMPTY, source: "default", error: null };
198
+ return {
199
+ name: FIELD_PROJECT_INVARIANTS,
200
+ current: resolved.invariants,
201
+ default: EMPTY,
202
+ source: resolved.source,
203
+ };
204
+ }
205
+ //# sourceMappingURL=project-invariants.js.map
@@ -18,7 +18,7 @@ import { existsSync, readFileSync } from "node:fs";
18
18
  import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
19
19
  import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
20
20
  import { policyColonInvocation } from "./policy-invocation.js";
21
- import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
21
+ import { appendAuditLog, loadProjectDefinition, projectDefinitionPath, stampChangedToken, } from "./resolve.js";
22
22
  export const FIELD_REQUIRE_HUMAN_MERGE = "plan.policy.requireHumanMerge";
23
23
  export const FIELD_REQUIRE_HUMAN_MERGE_CLI_ALIAS = "requireHumanMerge";
24
24
  export const FIELD_AUTO_DEPLOY_ON_MERGE = "plan.policy.autoDeployOnMerge";
@@ -285,7 +285,7 @@ export function setRequireHumanMerge(projectRoot, options) {
285
285
  }
286
286
  }
287
287
  const plan = data.plan;
288
- migrateLegacyPolicyKey(plan);
288
+ const legacyKeyMigrated = migrateLegacyPolicyKey(plan);
289
289
  const existingPolicy = plan[PLAN_POLICY_KEY];
290
290
  if (typeof existingPolicy !== "object" ||
291
291
  existingPolicy === null ||
@@ -309,8 +309,7 @@ export function setRequireHumanMerge(projectRoot, options) {
309
309
  delete narratives[LEGACY_REQUIRE_HUMAN_MERGE_KEY];
310
310
  legacyDropped = true;
311
311
  }
312
- atomicWriteProjectDefinition(path, data);
313
- const changed = previous !== Boolean(requireHumanMerge) || legacyDropped;
312
+ const changed = previous !== Boolean(requireHumanMerge) || legacyDropped || legacyKeyMigrated;
314
313
  const parts = [
315
314
  `actor=${actor}`,
316
315
  `requireHumanMerge=${requireHumanMerge ? "true" : "false"}`,
@@ -321,8 +320,11 @@ export function setRequireHumanMerge(projectRoot, options) {
321
320
  if (note) {
322
321
  parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
323
322
  }
324
- const auditEntry = parts.join(" ");
325
- appendAuditLog(projectRoot, auditEntry);
323
+ const auditEntry = stampChangedToken(parts.join(" "), changed);
324
+ if (changed) {
325
+ atomicWriteProjectDefinition(path, data);
326
+ }
327
+ appendAuditLog(projectRoot, auditEntry, changed);
326
328
  return { changed, auditEntry };
327
329
  });
328
330
  }
@@ -6,6 +6,10 @@ export declare const ENV_BYPASS = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
6
6
  export declare const LEGACY_NARRATIVE_KEY = "Allow direct commits to master";
7
7
  /** Audit log relative path (#746). */
8
8
  export declare const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
9
+ /** Operator line when a policy writer matches current state and does not append (#3528). */
10
+ export declare const POLICY_AUDIT_NOOP_STDOUT = " no-op: value already matched (ledger unchanged).";
11
+ /** Stamp a machine-readable `changed=true|false` token (#3528 / UPGRADING.md). */
12
+ export declare function stampChangedToken(entry: string, changed: boolean): string;
9
13
  export type PolicySource = "typed" | "legacy-narrative" | "env-bypass" | "default-fail-closed";
10
14
  export interface PolicyResult {
11
15
  readonly allowDirectCommits: boolean;
@@ -28,8 +32,9 @@ export declare function coerceLegacyNarrative(value: unknown): {
28
32
  export declare function loadProjectDefinition(projectRoot: string): [Record<string, unknown> | null, string | null];
29
33
  /** Resolve the effective branch-commit policy (#746 / #747). */
30
34
  export declare function resolvePolicy(projectRoot: string): PolicyResult;
31
- /** Append a one-line audit entry to meta/policy-changes.log (#746). */
32
- export declare function appendAuditLog(projectRoot: string, entry: string): string;
35
+ /** Append a one-line audit entry to meta/policy-changes.log (#746).
36
+ * No-op (`changed=false`) does not create or modify the tracked file (#3528). */
37
+ export declare function appendAuditLog(projectRoot: string, entry: string, changed?: boolean): string;
33
38
  /** Write the typed policy flag back to PROJECT-DEFINITION (#746). */
34
39
  export declare function setPolicy(projectRoot: string, options: {
35
40
  allowDirectCommits: boolean;
@@ -13,6 +13,16 @@ export const ENV_BYPASS = "DEFT_ALLOW_DEFAULT_BRANCH_COMMIT";
13
13
  export const LEGACY_NARRATIVE_KEY = "Allow direct commits to master";
14
14
  /** Audit log relative path (#746). */
15
15
  export const AUDIT_LOG_REL_PATH = "meta/policy-changes.log";
16
+ /** Operator line when a policy writer matches current state and does not append (#3528). */
17
+ export const POLICY_AUDIT_NOOP_STDOUT = " no-op: value already matched (ledger unchanged).";
18
+ const CHANGED_TOKEN_RE = /(?:^|\s)changed=(?:true|false)(?:\s|$)/;
19
+ /** Stamp a machine-readable `changed=true|false` token (#3528 / UPGRADING.md). */
20
+ export function stampChangedToken(entry, changed) {
21
+ if (CHANGED_TOKEN_RE.test(entry)) {
22
+ return entry;
23
+ }
24
+ return `${entry} changed=${changed ? "true" : "false"}`;
25
+ }
16
26
  const TRUTHY = new Set(["1", "true", "yes", "on"]);
17
27
  /**
18
28
  * Resolve absolute path to PROJECT-DEFINITION. Layout-aware (#2109 part 2a):
@@ -176,10 +186,15 @@ export function resolvePolicy(projectRoot) {
176
186
  function nowIso() {
177
187
  return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
178
188
  }
179
- /** Append a one-line audit entry to meta/policy-changes.log (#746). */
180
- export function appendAuditLog(projectRoot, entry) {
189
+ /** Append a one-line audit entry to meta/policy-changes.log (#746).
190
+ * No-op (`changed=false`) does not create or modify the tracked file (#3528). */
191
+ export function appendAuditLog(projectRoot, entry, changed = true) {
181
192
  const root = pathResolve(projectRoot);
182
193
  const logPath = join(root, AUDIT_LOG_REL_PATH);
194
+ const stamped = stampChangedToken(entry, changed);
195
+ if (!changed) {
196
+ return logPath;
197
+ }
183
198
  // #2980 wave D: product write sink routes through containedWrite.
184
199
  if (!existsSync(logPath)) {
185
200
  const header = "# meta/policy-changes.log -- audit trail for " +
@@ -194,7 +209,7 @@ export function appendAuditLog(projectRoot, entry) {
194
209
  containedWrite({
195
210
  root,
196
211
  target: AUDIT_LOG_REL_PATH,
197
- data: `${nowIso()} ${entry}\n`,
212
+ data: `${nowIso()} ${stamped}\n`,
198
213
  mode: "append",
199
214
  });
200
215
  return logPath;
@@ -224,7 +239,7 @@ export function setPolicy(projectRoot, options) {
224
239
  }
225
240
  }
226
241
  const plan = data.plan;
227
- migrateLegacyPolicyKey(plan);
242
+ const legacyKeyMigrated = migrateLegacyPolicyKey(plan);
228
243
  const existingPolicy = plan[PLAN_POLICY_KEY];
229
244
  if (typeof existingPolicy !== "object" ||
230
245
  existingPolicy === null ||
@@ -248,8 +263,7 @@ export function setPolicy(projectRoot, options) {
248
263
  delete narratives[LEGACY_NARRATIVE_KEY];
249
264
  legacyDropped = true;
250
265
  }
251
- atomicWriteProjectDefinition(path, data);
252
- const changed = previous !== Boolean(allowDirectCommits) || legacyDropped;
266
+ const changed = previous !== Boolean(allowDirectCommits) || legacyDropped || legacyKeyMigrated;
253
267
  const parts = [
254
268
  `actor=${actor}`,
255
269
  `allowDirectCommitsToMaster=${allowDirectCommits ? "true" : "false"}`,
@@ -261,8 +275,11 @@ export function setPolicy(projectRoot, options) {
261
275
  if (note) {
262
276
  parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
263
277
  }
264
- const auditEntry = parts.join(" ");
265
- appendAuditLog(projectRoot, auditEntry);
278
+ const auditEntry = stampChangedToken(parts.join(" "), changed);
279
+ if (changed) {
280
+ atomicWriteProjectDefinition(path, data);
281
+ }
282
+ appendAuditLog(projectRoot, auditEntry, changed);
266
283
  return { changed, auditEntry };
267
284
  });
268
285
  }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * scm:sync-default (#3391 / #3377 Wave 3).
3
+ *
4
+ * Consumes the shared #3388 detector and #3390 syncMaxFiles. Opens dest-targeted
5
+ * sync PRs. Over the file limit, cut at merge commits when possible so each
6
+ * dest-based leg is at or under the threshold. Each leg is a new branch and a
7
+ * new PR. Never retarget or reuse an oversized PR.
8
+ */
9
+ import { type GitRunner } from "../session/git.js";
10
+ import { type BranchSyncDetection } from "./branch-sync.js";
11
+ import { type SyncMaxFilesProvenance } from "./sync-max-files.js";
12
+ export declare const SYNC_DEFAULT_VERB = "scm:sync-default";
13
+ export declare const FORBIDDEN_SYNC_PR_RETARGET = "gh pr edit --base";
14
+ export type SyncDefaultAction = "noop" | "single" | "staged";
15
+ export type SyncDefaultNoopReason = "not-sync" | "already-synced" | "zero-files" | "fetch-failed" | "diff-failed";
16
+ export type SyncDefaultCutKind = "merge" | "commit" | "tip";
17
+ export interface SyncDefaultCommit {
18
+ readonly sha: string;
19
+ readonly isMerge: boolean;
20
+ }
21
+ export interface SyncDefaultLeg {
22
+ readonly index: number;
23
+ readonly sha: string;
24
+ readonly fileCount: number;
25
+ readonly cutKind: SyncDefaultCutKind;
26
+ readonly branchName: string;
27
+ }
28
+ export interface SyncDefaultPlan {
29
+ readonly action: SyncDefaultAction;
30
+ readonly noopReason: SyncDefaultNoopReason | null;
31
+ readonly dest: string;
32
+ readonly source: string;
33
+ readonly threshold: number;
34
+ readonly provenance: SyncMaxFilesProvenance;
35
+ readonly totalCount: number | null;
36
+ readonly legs: readonly SyncDefaultLeg[];
37
+ readonly nextLegIndex: number | null;
38
+ readonly message: string;
39
+ readonly detectorReason: BranchSyncDetection["reason"];
40
+ }
41
+ export interface SyncDefaultOpenPull {
42
+ readonly number: number;
43
+ readonly htmlUrl: string;
44
+ readonly headRef: string;
45
+ readonly headSha: string;
46
+ readonly baseRef: string;
47
+ }
48
+ export interface SyncDefaultForge {
49
+ listOpenPulls(repo: string, base: string): readonly SyncDefaultOpenPull[];
50
+ createPull(repo: string, input: {
51
+ readonly title: string;
52
+ readonly head: string;
53
+ readonly base: string;
54
+ readonly body: string;
55
+ }): {
56
+ readonly number: number;
57
+ readonly htmlUrl: string;
58
+ };
59
+ }
60
+ export interface SyncDefaultOpened {
61
+ readonly leg: SyncDefaultLeg;
62
+ readonly prNumber: number;
63
+ readonly prUrl: string;
64
+ readonly branch: string;
65
+ readonly reusedExisting: boolean;
66
+ }
67
+ export interface SyncDefaultApplyResult {
68
+ readonly plan: SyncDefaultPlan;
69
+ readonly opened: readonly SyncDefaultOpened[];
70
+ readonly retargeted: false;
71
+ }
72
+ export declare function syncDefaultBranchName(dest: string, source: string, index: number, sha: string): string;
73
+ export declare function parseGithubOwnerRepo(remoteUrl: string): string | null;
74
+ export declare function countChangedFilesOnRange(options: {
75
+ readonly projectRoot: string;
76
+ readonly left: string;
77
+ readonly right: string;
78
+ readonly runGit?: GitRunner;
79
+ }): {
80
+ readonly count: number | null;
81
+ readonly error: string | null;
82
+ };
83
+ export declare function listCommitsAfter(options: {
84
+ readonly projectRoot: string;
85
+ readonly dest: string;
86
+ readonly source: string;
87
+ readonly runGit?: GitRunner;
88
+ }): {
89
+ readonly commits: readonly SyncDefaultCommit[];
90
+ readonly error: string | null;
91
+ };
92
+ /**
93
+ * Cut dest..source into dest-targeted legs whose dest-based file count is at
94
+ * or under the threshold when a merge (or other) cut exists.
95
+ */
96
+ export declare function cutSyncLegs(options: {
97
+ readonly dest: string;
98
+ readonly source: string;
99
+ readonly destRef: string;
100
+ readonly sourceSha: string;
101
+ readonly commits: readonly SyncDefaultCommit[];
102
+ readonly threshold: number;
103
+ readonly countFiles: (left: string, right: string) => number | null;
104
+ /** When set, later cuts must be descendants of the previous cut. */
105
+ readonly isAncestor?: (ancestor: string, descendant: string) => boolean;
106
+ }): {
107
+ readonly legs: readonly SyncDefaultLeg[];
108
+ readonly error: string | null;
109
+ };
110
+ export declare function planSyncDefault(options: {
111
+ readonly projectRoot: string;
112
+ readonly maxFiles?: number | null;
113
+ readonly destTipSha?: string;
114
+ readonly runGit?: GitRunner;
115
+ }): SyncDefaultPlan;
116
+ export declare function nextOpenableLeg(legs: readonly SyncDefaultLeg[], destTipSha: string, projectRoot: string, runGit: GitRunner): SyncDefaultLeg | null;
117
+ export declare function applySyncDefault(options: {
118
+ readonly projectRoot: string;
119
+ readonly repo: string;
120
+ readonly plan?: SyncDefaultPlan;
121
+ readonly maxFiles?: number | null;
122
+ readonly destTipSha?: string;
123
+ readonly dryRun?: boolean;
124
+ readonly runGit?: GitRunner;
125
+ readonly forge?: SyncDefaultForge;
126
+ }): SyncDefaultApplyResult;
127
+ export declare function syncDefaultPrBody(plan: SyncDefaultPlan, leg: SyncDefaultLeg): string;
128
+ export declare function formatSyncDefaultHuman(result: SyncDefaultApplyResult): string;
129
+ //# sourceMappingURL=sync-default.d.ts.map