perttool 0.2.0 → 0.4.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 (259) hide show
  1. package/CHANGELOG.md +81 -1
  2. package/README.md +121 -28
  3. package/dist/analysis/temporal-deadline.d.ts +59 -0
  4. package/dist/analysis/temporal-deadline.d.ts.map +1 -0
  5. package/dist/analysis/temporal-deadline.js +387 -0
  6. package/dist/analysis/temporal-deadline.js.map +1 -0
  7. package/dist/analysis/temporal-precedence.d.ts +45 -0
  8. package/dist/analysis/temporal-precedence.d.ts.map +1 -0
  9. package/dist/analysis/temporal-precedence.js +123 -0
  10. package/dist/analysis/temporal-precedence.js.map +1 -0
  11. package/dist/analysis/temporal-resource.d.ts +66 -0
  12. package/dist/analysis/temporal-resource.d.ts.map +1 -0
  13. package/dist/analysis/temporal-resource.js +292 -0
  14. package/dist/analysis/temporal-resource.js.map +1 -0
  15. package/dist/application/check.d.ts +16 -0
  16. package/dist/application/check.d.ts.map +1 -1
  17. package/dist/application/check.js +51 -0
  18. package/dist/application/check.js.map +1 -1
  19. package/dist/application/contract4.d.ts +17 -0
  20. package/dist/application/contract4.d.ts.map +1 -0
  21. package/dist/application/contract4.js +27 -0
  22. package/dist/application/contract4.js.map +1 -0
  23. package/dist/application/contract5-governance.d.ts +8 -0
  24. package/dist/application/contract5-governance.d.ts.map +1 -0
  25. package/dist/application/contract5-governance.js +12 -0
  26. package/dist/application/contract5-governance.js.map +1 -0
  27. package/dist/application/init.d.ts +3 -11
  28. package/dist/application/init.d.ts.map +1 -1
  29. package/dist/application/init.js +16 -129
  30. package/dist/application/init.js.map +1 -1
  31. package/dist/application/mutate.d.ts +19 -0
  32. package/dist/application/mutate.d.ts.map +1 -1
  33. package/dist/application/mutate.js +34 -16
  34. package/dist/application/mutate.js.map +1 -1
  35. package/dist/application/next.d.ts +2 -0
  36. package/dist/application/next.d.ts.map +1 -1
  37. package/dist/application/next.js +7 -1
  38. package/dist/application/next.js.map +1 -1
  39. package/dist/application/project.d.ts +4 -22
  40. package/dist/application/project.d.ts.map +1 -1
  41. package/dist/application/project.js +3 -51
  42. package/dist/application/project.js.map +1 -1
  43. package/dist/application/target-check.d.ts +34 -0
  44. package/dist/application/target-check.d.ts.map +1 -0
  45. package/dist/application/target-check.js +90 -0
  46. package/dist/application/target-check.js.map +1 -0
  47. package/dist/application/target-governance-init.d.ts +36 -0
  48. package/dist/application/target-governance-init.d.ts.map +1 -0
  49. package/dist/application/target-governance-init.js +185 -0
  50. package/dist/application/target-governance-init.js.map +1 -0
  51. package/dist/application/target-governance-mutation.d.ts +20 -0
  52. package/dist/application/target-governance-mutation.d.ts.map +1 -0
  53. package/dist/application/target-governance-mutation.js +175 -0
  54. package/dist/application/target-governance-mutation.js.map +1 -0
  55. package/dist/application/target-governance-project.d.ts +34 -0
  56. package/dist/application/target-governance-project.d.ts.map +1 -0
  57. package/dist/application/target-governance-project.js +80 -0
  58. package/dist/application/target-governance-project.js.map +1 -0
  59. package/dist/application/target-governance-projection.d.ts +18 -0
  60. package/dist/application/target-governance-projection.d.ts.map +1 -0
  61. package/dist/application/target-governance-projection.js +248 -0
  62. package/dist/application/target-governance-projection.js.map +1 -0
  63. package/dist/application/target-governance-write.d.ts +17 -0
  64. package/dist/application/target-governance-write.d.ts.map +1 -0
  65. package/dist/application/target-governance-write.js +55 -0
  66. package/dist/application/target-governance-write.js.map +1 -0
  67. package/dist/application/target-grammar-boundary.d.ts +22 -0
  68. package/dist/application/target-grammar-boundary.d.ts.map +1 -0
  69. package/dist/application/target-grammar-boundary.js +91 -0
  70. package/dist/application/target-grammar-boundary.js.map +1 -0
  71. package/dist/application/target-mutate.d.ts +10 -0
  72. package/dist/application/target-mutate.d.ts.map +1 -0
  73. package/dist/application/target-mutate.js +81 -0
  74. package/dist/application/target-mutate.js.map +1 -0
  75. package/dist/application/target-project.d.ts +28 -0
  76. package/dist/application/target-project.d.ts.map +1 -0
  77. package/dist/application/target-project.js +60 -0
  78. package/dist/application/target-project.js.map +1 -0
  79. package/dist/application/target-temporal-analysis.d.ts +140 -0
  80. package/dist/application/target-temporal-analysis.d.ts.map +1 -0
  81. package/dist/application/target-temporal-analysis.js +414 -0
  82. package/dist/application/target-temporal-analysis.js.map +1 -0
  83. package/dist/application/target-temporal-input.d.ts +86 -0
  84. package/dist/application/target-temporal-input.d.ts.map +1 -0
  85. package/dist/application/target-temporal-input.js +240 -0
  86. package/dist/application/target-temporal-input.js.map +1 -0
  87. package/dist/application/target-unit-migration-candidate.d.ts +60 -0
  88. package/dist/application/target-unit-migration-candidate.d.ts.map +1 -0
  89. package/dist/application/target-unit-migration-candidate.js +294 -0
  90. package/dist/application/target-unit-migration-candidate.js.map +1 -0
  91. package/dist/application/target-unit-migration-request.d.ts +35 -0
  92. package/dist/application/target-unit-migration-request.d.ts.map +1 -0
  93. package/dist/application/target-unit-migration-request.js +70 -0
  94. package/dist/application/target-unit-migration-request.js.map +1 -0
  95. package/dist/application/target-unit-migration-result.d.ts +66 -0
  96. package/dist/application/target-unit-migration-result.d.ts.map +1 -0
  97. package/dist/application/target-unit-migration-result.js +86 -0
  98. package/dist/application/target-unit-migration-result.js.map +1 -0
  99. package/dist/application/unit-migration.d.ts +16 -0
  100. package/dist/application/unit-migration.d.ts.map +1 -0
  101. package/dist/application/unit-migration.js +11 -0
  102. package/dist/application/unit-migration.js.map +1 -0
  103. package/dist/cli.js +383 -132
  104. package/dist/cli.js.map +1 -1
  105. package/dist/command/discovery.d.ts +3 -3
  106. package/dist/command/discovery.d.ts.map +1 -1
  107. package/dist/command/discovery.js +149 -22
  108. package/dist/command/discovery.js.map +1 -1
  109. package/dist/command/registry.d.ts +8 -2
  110. package/dist/command/registry.d.ts.map +1 -1
  111. package/dist/command/registry.js +14 -1
  112. package/dist/command/registry.js.map +1 -1
  113. package/dist/command/target-governance-discovery.d.ts +31 -0
  114. package/dist/command/target-governance-discovery.d.ts.map +1 -0
  115. package/dist/command/target-governance-discovery.js +390 -0
  116. package/dist/command/target-governance-discovery.js.map +1 -0
  117. package/dist/command/target-governance-usage.d.ts +22 -0
  118. package/dist/command/target-governance-usage.d.ts.map +1 -0
  119. package/dist/command/target-governance-usage.js +116 -0
  120. package/dist/command/target-governance-usage.js.map +1 -0
  121. package/dist/command/usage.d.ts +2 -0
  122. package/dist/command/usage.d.ts.map +1 -1
  123. package/dist/command/usage.js +44 -26
  124. package/dist/command/usage.js.map +1 -1
  125. package/dist/conversion/mermaid-import.d.ts.map +1 -1
  126. package/dist/conversion/mermaid-import.js +3 -2
  127. package/dist/conversion/mermaid-import.js.map +1 -1
  128. package/dist/conversion/mermaid.d.ts.map +1 -1
  129. package/dist/conversion/mermaid.js +27 -0
  130. package/dist/conversion/mermaid.js.map +1 -1
  131. package/dist/formatter/source-formatter.d.ts +12 -0
  132. package/dist/formatter/source-formatter.d.ts.map +1 -1
  133. package/dist/formatter/source-formatter.js +59 -6
  134. package/dist/formatter/source-formatter.js.map +1 -1
  135. package/dist/formatter/target-source-formatter.d.ts +9 -0
  136. package/dist/formatter/target-source-formatter.d.ts.map +1 -0
  137. package/dist/formatter/target-source-formatter.js +52 -0
  138. package/dist/formatter/target-source-formatter.js.map +1 -0
  139. package/dist/governance/authority.d.ts +21 -0
  140. package/dist/governance/authority.d.ts.map +1 -0
  141. package/dist/governance/authority.js +206 -0
  142. package/dist/governance/authority.js.map +1 -0
  143. package/dist/governance/guidance.d.ts +2 -0
  144. package/dist/governance/guidance.d.ts.map +1 -0
  145. package/dist/governance/guidance.js +2 -0
  146. package/dist/governance/guidance.js.map +1 -0
  147. package/dist/governance/source.d.ts +37 -0
  148. package/dist/governance/source.d.ts.map +1 -0
  149. package/dist/governance/source.js +103 -0
  150. package/dist/governance/source.js.map +1 -0
  151. package/dist/governance/types.d.ts +41 -0
  152. package/dist/governance/types.d.ts.map +1 -0
  153. package/dist/governance/types.js +2 -0
  154. package/dist/governance/types.js.map +1 -0
  155. package/dist/help/guide.d.ts +8 -5
  156. package/dist/help/guide.d.ts.map +1 -1
  157. package/dist/help/guide.js +1 -1
  158. package/dist/help/guide.js.map +1 -1
  159. package/dist/help/registry.d.ts.map +1 -1
  160. package/dist/help/registry.js +124 -13
  161. package/dist/help/registry.js.map +1 -1
  162. package/dist/help/target-governance-guide.d.ts +10 -0
  163. package/dist/help/target-governance-guide.d.ts.map +1 -0
  164. package/dist/help/target-governance-guide.js +57 -0
  165. package/dist/help/target-governance-guide.js.map +1 -0
  166. package/dist/index.d.ts +25 -17
  167. package/dist/index.d.ts.map +1 -1
  168. package/dist/index.js +8 -8
  169. package/dist/index.js.map +1 -1
  170. package/dist/io/safe-write.d.ts +11 -0
  171. package/dist/io/safe-write.d.ts.map +1 -1
  172. package/dist/io/safe-write.js +40 -10
  173. package/dist/io/safe-write.js.map +1 -1
  174. package/dist/io/target-safe-write.d.ts +10 -0
  175. package/dist/io/target-safe-write.d.ts.map +1 -0
  176. package/dist/io/target-safe-write.js +51 -0
  177. package/dist/io/target-safe-write.js.map +1 -0
  178. package/dist/migration/conversion.d.ts +40 -0
  179. package/dist/migration/conversion.d.ts.map +1 -0
  180. package/dist/migration/conversion.js +106 -0
  181. package/dist/migration/conversion.js.map +1 -0
  182. package/dist/migration/grammar-boundary.d.ts +20 -0
  183. package/dist/migration/grammar-boundary.d.ts.map +1 -0
  184. package/dist/migration/grammar-boundary.js +95 -0
  185. package/dist/migration/grammar-boundary.js.map +1 -0
  186. package/dist/migration/request.d.ts +79 -0
  187. package/dist/migration/request.d.ts.map +1 -0
  188. package/dist/migration/request.js +308 -0
  189. package/dist/migration/request.js.map +1 -0
  190. package/dist/model/calendar-arithmetic.d.ts +46 -0
  191. package/dist/model/calendar-arithmetic.d.ts.map +1 -0
  192. package/dist/model/calendar-arithmetic.js +236 -0
  193. package/dist/model/calendar-arithmetic.js.map +1 -0
  194. package/dist/model/calendar.d.ts +22 -0
  195. package/dist/model/calendar.d.ts.map +1 -0
  196. package/dist/model/calendar.js +83 -0
  197. package/dist/model/calendar.js.map +1 -0
  198. package/dist/model/declaration-fields.d.ts +22 -0
  199. package/dist/model/declaration-fields.d.ts.map +1 -0
  200. package/dist/model/declaration-fields.js +72 -0
  201. package/dist/model/declaration-fields.js.map +1 -0
  202. package/dist/model/exact-duration-source.d.ts +10 -0
  203. package/dist/model/exact-duration-source.d.ts.map +1 -0
  204. package/dist/model/exact-duration-source.js +69 -0
  205. package/dist/model/exact-duration-source.js.map +1 -0
  206. package/dist/model/rational.d.ts +2 -2
  207. package/dist/model/rational.d.ts.map +1 -1
  208. package/dist/model/rational.js +3 -1
  209. package/dist/model/rational.js.map +1 -1
  210. package/dist/model/syntax.d.ts +9 -2
  211. package/dist/model/syntax.d.ts.map +1 -1
  212. package/dist/model/syntax.js +20 -4
  213. package/dist/model/syntax.js.map +1 -1
  214. package/dist/model/target-calendar.d.ts +25 -0
  215. package/dist/model/target-calendar.d.ts.map +1 -0
  216. package/dist/model/target-calendar.js +56 -0
  217. package/dist/model/target-calendar.js.map +1 -0
  218. package/dist/mutation/advance.d.ts +11 -0
  219. package/dist/mutation/advance.d.ts.map +1 -1
  220. package/dist/mutation/advance.js +37 -7
  221. package/dist/mutation/advance.js.map +1 -1
  222. package/dist/mutation/milestone.d.ts +10 -1
  223. package/dist/mutation/milestone.d.ts.map +1 -1
  224. package/dist/mutation/milestone.js +77 -21
  225. package/dist/mutation/milestone.js.map +1 -1
  226. package/dist/mutation/project.d.ts +11 -1
  227. package/dist/mutation/project.d.ts.map +1 -1
  228. package/dist/mutation/project.js +117 -11
  229. package/dist/mutation/project.js.map +1 -1
  230. package/dist/mutation/target-types.d.ts +59 -0
  231. package/dist/mutation/target-types.d.ts.map +1 -0
  232. package/dist/mutation/target-types.js +2 -0
  233. package/dist/mutation/target-types.js.map +1 -0
  234. package/dist/mutation/task.d.ts +12 -1
  235. package/dist/mutation/task.d.ts.map +1 -1
  236. package/dist/mutation/task.js +108 -49
  237. package/dist/mutation/task.js.map +1 -1
  238. package/dist/mutation/types.d.ts +16 -3
  239. package/dist/mutation/types.d.ts.map +1 -1
  240. package/dist/parser/document-parser.d.ts +21 -0
  241. package/dist/parser/document-parser.d.ts.map +1 -1
  242. package/dist/parser/document-parser.js +197 -38
  243. package/dist/parser/document-parser.js.map +1 -1
  244. package/dist/recommendation/override-types.d.ts +2 -2
  245. package/dist/recommendation/override.d.ts +2 -2
  246. package/dist/recommendation/override.d.ts.map +1 -1
  247. package/dist/recommendation/override.js +49 -12
  248. package/dist/recommendation/override.js.map +1 -1
  249. package/dist/semantic/target-validator.d.ts +46 -0
  250. package/dist/semantic/target-validator.d.ts.map +1 -0
  251. package/dist/semantic/target-validator.js +130 -0
  252. package/dist/semantic/target-validator.js.map +1 -0
  253. package/dist/semantic/validator.d.ts +3 -0
  254. package/dist/semantic/validator.d.ts.map +1 -1
  255. package/dist/semantic/validator.js +78 -5
  256. package/dist/semantic/validator.js.map +1 -1
  257. package/dist/version.d.ts +1 -1
  258. package/dist/version.js +1 -1
  259. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2,20 +2,23 @@
2
2
  import { readFile } from "node:fs/promises";
3
3
  import process from "node:process";
4
4
  import { TextDecoder } from "node:util";
5
- import { analyzeDocument } from "./application/analyze.js";
5
+ import { analyzeDocument, selectNextTasks, } from "./application/contract4.js";
6
6
  import { checkDocument } from "./application/check.js";
7
7
  import { planFormat } from "./application/format.js";
8
8
  import { planProjectInit, projectInitResultToJson, renderProjectInitResult, withProjectInitOutput, } from "./application/init.js";
9
- import { planBatchMutation, planMutation } from "./application/mutate.js";
10
- import { selectNextTasks } from "./application/next.js";
9
+ import { planUnitMigration, withUnitMigrationWrite, } from "./application/unit-migration.js";
11
10
  import { getProjectMetadata, } from "./application/project.js";
11
+ import { planTargetGovernanceAdvance, planTargetGovernanceBatchMutation, planTargetGovernanceMutation, } from "./application/target-governance-mutation.js";
12
+ import { renderTargetGovernanceDecision, renderTargetGovernanceProjectText, targetGovernanceMutationResultToJson, targetGovernanceProjectResultToJson, } from "./application/target-governance-projection.js";
13
+ import { persistTargetGovernanceResult, } from "./application/target-governance-write.js";
12
14
  import { getAgentHelp } from "./application/agent-help.js";
13
15
  import { exportMermaid, } from "./conversion/mermaid.js";
14
16
  import { importMermaid } from "./conversion/mermaid-import.js";
15
- import { getGuide, renderGuideResult, serializeGuideResult, } from "./help/guide.js";
17
+ import { getTargetGovernanceGuide, renderTargetGovernanceGuideResult, serializeTargetGovernanceGuideResult, } from "./help/target-governance-guide.js";
16
18
  import { commandOptionSets, } from "./command/registry.js";
17
- import { CONTRACT3_COMMAND_REGISTRY, getCommandDiscovery, renderCommandHelpResult, serializeCommandHelpResult, } from "./command/discovery.js";
18
- import { handlerCommandUsageError, renderCommandUsageError, serializeCommandUsageError, validateCommandInvocation, } from "./command/usage.js";
19
+ import { TARGET_GOVERNANCE_COMMAND_REGISTRY, getTargetGovernanceCommandDiscovery, renderTargetGovernanceCommandHelpResult, serializeTargetGovernanceCommandHelpResult, } from "./command/target-governance-discovery.js";
20
+ import { handlerCommandUsageError, } from "./command/usage.js";
21
+ import { renderTargetGovernanceCommandUsageError, serializeTargetGovernanceCommandUsageError, validateTargetGovernanceCommandInvocation, } from "./command/target-governance-usage.js";
19
22
  import { agentGuidanceResultToJson } from "./guidance/projection.js";
20
23
  import { agentGuidanceExitCode, renderAgentGuidanceText, } from "./guidance/text.js";
21
24
  import { documentContentFromBytes, readDocumentFile, } from "./io/document-file.js";
@@ -24,7 +27,7 @@ import { formatDecimal } from "./model/rational.js";
24
27
  import { convertWithVelocity, durationSuffix } from "./model/units.js";
25
28
  import { recommendationInvariantExitCode } from "./recommendation/failure.js";
26
29
  import { recommendationAnalysisToJson } from "./recommendation/json.js";
27
- import { planAdvance, } from "./mutation/advance.js";
30
+ import { TARGET_GRAMMAR_4_CAPABILITY, } from "./parser/document-parser.js";
28
31
  import { TOOL_VERSION } from "./version.js";
29
32
  class UsageError extends Error {
30
33
  code = "PTCLI-001";
@@ -79,7 +82,7 @@ function parseOptions(args, valueOptions, flagOptions, repeatableOptions = new S
79
82
  return { positionals, values, repeatedValues, flags };
80
83
  }
81
84
  function parseCommandOptions(operation, args) {
82
- const descriptor = CONTRACT3_COMMAND_REGISTRY.find((candidate) => candidate.operation === operation);
85
+ const descriptor = TARGET_GOVERNANCE_COMMAND_REGISTRY.find((candidate) => candidate.operation === operation);
83
86
  if (descriptor === undefined) {
84
87
  throw new Error(`command descriptor is missing for ${operation}`);
85
88
  }
@@ -182,6 +185,18 @@ async function readStdin() {
182
185
  function writeJson(value) {
183
186
  process.stdout.write(`${JSON.stringify(value)}\n`);
184
187
  }
188
+ function snakeJson(value) {
189
+ if (typeof value === "bigint")
190
+ return value.toString();
191
+ if (Array.isArray(value))
192
+ return value.map(snakeJson);
193
+ if (value === null || typeof value !== "object")
194
+ return value;
195
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
196
+ key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase(),
197
+ snakeJson(item),
198
+ ]));
199
+ }
185
200
  function cliError(error, exitCode, operation, json) {
186
201
  const code = error instanceof UsageError
187
202
  ? error.code
@@ -205,7 +220,7 @@ function cliError(error, exitCode, operation, json) {
205
220
  if (json) {
206
221
  writeJson({
207
222
  schema_version: "Perttool.CliError.v1",
208
- cli_contract_version: 3,
223
+ cli_contract_version: 5,
209
224
  tool_version: TOOL_VERSION,
210
225
  operation,
211
226
  ok: false,
@@ -240,8 +255,8 @@ async function runCheck(args) {
240
255
  const ok = result.ok && !warningFailure;
241
256
  if (format === "json") {
242
257
  writeJson({
243
- schema_version: "Perttool.CheckResult.v1",
244
- cli_contract_version: 3,
258
+ schema_version: "Perttool.CheckResult.v2",
259
+ cli_contract_version: 5,
245
260
  tool_version: TOOL_VERSION,
246
261
  operation: "document.check",
247
262
  ok,
@@ -252,11 +267,12 @@ async function runCheck(args) {
252
267
  diagnostics_truncated: result.diagnosticsTruncated,
253
268
  grammar_version: result.grammarVersion,
254
269
  summary: result.summary,
270
+ temporal_inputs: snakeJson(result.temporalInputs),
255
271
  });
256
272
  }
257
273
  else {
258
274
  if (ok) {
259
- process.stdout.write(`OK ${source} project=${result.documentId ?? "-"} milestones=${result.summary.milestones} tasks=${result.summary.tasks} gates=${result.summary.gates} resources=${result.summary.resources}\n`);
275
+ process.stdout.write(`OK ${source} project=${result.documentId ?? "-"} milestones=${result.summary.milestones} tasks=${result.summary.tasks} gates=${result.summary.gates} resources=${result.summary.resources} temporal=milestone_deadlines:${result.temporalInputs?.milestoneDeadlines.length ?? 0},task_not_before:${result.temporalInputs?.taskConstraints.filter(({ notBefore }) => notBefore !== null).length ?? 0},task_deadlines:${result.temporalInputs?.taskConstraints.filter(({ deadline }) => deadline !== null).length ?? 0}\n`);
260
276
  }
261
277
  for (const diagnostic of result.diagnostics) {
262
278
  process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
@@ -267,36 +283,6 @@ async function runCheck(args) {
267
283
  }
268
284
  return ok ? 0 : 1;
269
285
  }
270
- function projectJson(project) {
271
- return {
272
- id: project.id,
273
- version: project.version,
274
- title: project.title,
275
- description: project.description,
276
- as_of: project.asOf,
277
- duration_unit: project.durationUnit,
278
- velocity: project.velocity,
279
- finish: project.finish,
280
- critical_epsilon: project.criticalEpsilon,
281
- target_duration: project.targetDuration,
282
- };
283
- }
284
- function renderProjectText(project) {
285
- const optional = (value) => value ?? "-";
286
- return [
287
- `PROJECT ${project.id}`,
288
- `VERSION ${project.version}`,
289
- `TITLE ${JSON.stringify(project.title)}`,
290
- `DESCRIPTION ${project.description === null ? "-" : JSON.stringify(project.description)}`,
291
- `AS_OF ${optional(project.asOf)}`,
292
- `DURATION_UNIT ${project.durationUnit}`,
293
- `VELOCITY ${optional(project.velocity)}`,
294
- `FINISH ${project.finish}`,
295
- `CRITICAL_EPSILON ${optional(project.criticalEpsilon)}`,
296
- `TARGET_DURATION ${optional(project.targetDuration)}`,
297
- "",
298
- ].join("\n");
299
- }
300
286
  async function runProjectShow(args) {
301
287
  const parsed = parseCommandOptions("project.show", args);
302
288
  if (parsed.positionals.length !== 1) {
@@ -320,24 +306,11 @@ async function runProjectShow(args) {
320
306
  result.diagnostics.some((diagnostic) => diagnostic.severity === "warning"));
321
307
  const ok = result.ok && !warningFailure;
322
308
  if (format === "json") {
323
- writeJson({
324
- schema_version: "Perttool.ProjectResult.v1",
325
- cli_contract_version: 3,
326
- tool_version: TOOL_VERSION,
327
- operation: "project.show",
328
- ok,
329
- document_id: result.documentId,
330
- source,
331
- source_digest: input.digest,
332
- diagnostics: result.diagnostics.map(jsonDiagnostic),
333
- diagnostics_truncated: result.diagnosticsTruncated,
334
- grammar_version: result.grammarVersion,
335
- project: result.project === null ? null : projectJson(result.project),
336
- });
309
+ writeJson(targetGovernanceProjectResultToJson(result, source, input.digest, ok));
337
310
  }
338
311
  else {
339
312
  if (ok && result.project !== null) {
340
- process.stdout.write(renderProjectText(result.project));
313
+ process.stdout.write(renderTargetGovernanceProjectText(result.project));
341
314
  }
342
315
  for (const diagnostic of result.diagnostics) {
343
316
  process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
@@ -368,6 +341,27 @@ async function runProjectInit(args) {
368
341
  ...(parsed.values.get("velocity") === undefined
369
342
  ? {}
370
343
  : { velocity: parsed.values.get("velocity") }),
344
+ ...(parsed.values.get("initial-milestone-deadline") === undefined
345
+ ? {}
346
+ : {
347
+ initialMilestoneDeadline: parsed.values.get("initial-milestone-deadline"),
348
+ }),
349
+ ...(parsed.values.get("goal-owner") === undefined
350
+ ? {}
351
+ : { goalOwner: parsed.values.get("goal-owner") }),
352
+ ...(parsed.values.get("goal-delegates") === undefined
353
+ ? {}
354
+ : {
355
+ goalDelegates: principalListOption(parsed.values.get("goal-delegates")),
356
+ }),
357
+ ...(parsed.values.get("dag-owner") === undefined
358
+ ? {}
359
+ : { dagOwner: parsed.values.get("dag-owner") }),
360
+ ...(parsed.values.get("dag-delegates") === undefined
361
+ ? {}
362
+ : {
363
+ dagDelegates: principalListOption(parsed.values.get("dag-delegates")),
364
+ }),
371
365
  });
372
366
  let writeResult = null;
373
367
  const output = parsed.values.get("out");
@@ -471,6 +465,39 @@ function editingWriteRequest(parsed, sourceOperand, check = false) {
471
465
  return { mode: "out", target: out };
472
466
  return { mode: "preview", target: null };
473
467
  }
468
+ function governanceRequest(parsed, writeRequest) {
469
+ return Object.freeze({
470
+ intent: writeRequest.mode === "preview"
471
+ ? "preview"
472
+ : "persist",
473
+ actor: parsed.values.get("actor") ?? null,
474
+ acceptedByOwner: Object.freeze([
475
+ ...(parsed.repeatedValues.get("accepted-by-owner") ?? []),
476
+ ]),
477
+ });
478
+ }
479
+ async function persistGovernedResult(result, request, sourceOperand) {
480
+ if (request.mode === "preview") {
481
+ return Object.freeze({
482
+ mode: "preview",
483
+ target: null,
484
+ written: false,
485
+ });
486
+ }
487
+ return persistTargetGovernanceResult(result, TARGET_GRAMMAR_4_CAPABILITY, request.mode === "in_place"
488
+ ? {
489
+ mode: "in_place",
490
+ target: request.target,
491
+ ...(request.expectedDigest === undefined
492
+ ? {}
493
+ : { expectedDigest: request.expectedDigest }),
494
+ }
495
+ : {
496
+ mode: "out",
497
+ source: sourceOperand,
498
+ target: request.target,
499
+ });
500
+ }
474
501
  function assertExpectedDigest(request, initialDigest) {
475
502
  if (request.mode === "in_place" &&
476
503
  request.expectedDigest !== undefined &&
@@ -503,6 +530,9 @@ function writeFailureExit(error, operation, json) {
503
530
  function renderWriteSummary(operation, result) {
504
531
  return `WRITE ${operation} mode=${result.mode} target=${result.target} digest=${result.digest} written=${result.written}\n`;
505
532
  }
533
+ function renderGovernanceWriteSummary(operation, result, digest) {
534
+ return `WRITE ${operation} mode=${result.mode} target=${result.target ?? "-"} digest=${digest ?? "-"} written=${result.written}\n`;
535
+ }
506
536
  async function runFormat(args) {
507
537
  const parsed = parseCommandOptions("document.format", args);
508
538
  if (parsed.positionals.length !== 1) {
@@ -546,7 +576,7 @@ async function runFormat(args) {
546
576
  if (format === "json") {
547
577
  writeJson({
548
578
  schema_version: "Perttool.FormatResult.v1",
549
- cli_contract_version: 3,
579
+ cli_contract_version: 5,
550
580
  tool_version: TOOL_VERSION,
551
581
  operation: "document.format",
552
582
  ok,
@@ -614,6 +644,12 @@ function uniqueRepeated(parsed, option) {
614
644
  }
615
645
  return values;
616
646
  }
647
+ function principalListOption(value) {
648
+ const interior = value.slice(1, -1).trim();
649
+ return interior === ""
650
+ ? Object.freeze([])
651
+ : Object.freeze(interior.split(",").map((item) => item.trim()));
652
+ }
617
653
  function enumRepeated(parsed, option, allowed) {
618
654
  return uniqueRepeated(parsed, option).map((value) => {
619
655
  if (!allowed.has(value)) {
@@ -700,6 +736,12 @@ function taskMutationFromOptions(action, parsed) {
700
736
  ...(parsed.values.get("description") === undefined
701
737
  ? {}
702
738
  : { description: parsed.values.get("description") }),
739
+ ...(parsed.values.get("not-before") === undefined
740
+ ? {}
741
+ : { notBefore: parsed.values.get("not-before") }),
742
+ ...(parsed.values.get("deadline") === undefined
743
+ ? {}
744
+ : { deadline: parsed.values.get("deadline") }),
703
745
  ...(status === undefined ? {} : { status }),
704
746
  ...(priority === undefined ? {} : { priority }),
705
747
  ...(requirements.length === 0 ? {} : { requirements }),
@@ -720,7 +762,7 @@ function taskMutationFromOptions(action, parsed) {
720
762
  }
721
763
  const clear = enumRepeated(parsed, "clear", new Set([
722
764
  "description", "status", "priority", "owner", "blocked_reason", "source",
723
- "tags", "requires",
765
+ "tags", "requires", "not_before", "deadline",
724
766
  ]));
725
767
  const addTags = uniqueRepeated(parsed, "add-tag");
726
768
  const removeTags = uniqueRepeated(parsed, "remove-tag");
@@ -735,6 +777,12 @@ function taskMutationFromOptions(action, parsed) {
735
777
  ? {}
736
778
  : { description: parsed.values.get("description") }),
737
779
  ...timing,
780
+ ...(parsed.values.get("not-before") === undefined
781
+ ? {}
782
+ : { notBefore: parsed.values.get("not-before") }),
783
+ ...(parsed.values.get("deadline") === undefined
784
+ ? {}
785
+ : { deadline: parsed.values.get("deadline") }),
738
786
  ...(status === undefined ? {} : { status }),
739
787
  ...(priority === undefined ? {} : { priority }),
740
788
  ...(parsed.values.get("owner") === undefined
@@ -749,6 +797,8 @@ function taskMutationFromOptions(action, parsed) {
749
797
  };
750
798
  const clearConflicts = new Map([
751
799
  ["description", parsed.values.has("description")],
800
+ ["not_before", parsed.values.has("not-before")],
801
+ ["deadline", parsed.values.has("deadline")],
752
802
  ["status", status !== undefined],
753
803
  ["priority", priority !== undefined],
754
804
  ["owner", parsed.values.has("owner")],
@@ -786,6 +836,10 @@ function projectMutationFromOptions(parsed) {
786
836
  "velocity",
787
837
  "critical_epsilon",
788
838
  "target_duration",
839
+ "goal_owner",
840
+ "goal_delegates",
841
+ "dag_owner",
842
+ "dag_delegates",
789
843
  ]));
790
844
  const conflicts = new Map([
791
845
  ["description", parsed.values.has("description")],
@@ -793,6 +847,10 @@ function projectMutationFromOptions(parsed) {
793
847
  ["velocity", parsed.values.has("velocity")],
794
848
  ["critical_epsilon", parsed.values.has("critical-epsilon")],
795
849
  ["target_duration", parsed.values.has("target-duration")],
850
+ ["goal_owner", parsed.values.has("goal-owner")],
851
+ ["goal_delegates", parsed.values.has("goal-delegates")],
852
+ ["dag_owner", parsed.values.has("dag-owner")],
853
+ ["dag_delegates", parsed.values.has("dag-delegates")],
796
854
  ]);
797
855
  const conflict = clear.find((field) => conflicts.get(field) === true);
798
856
  if (conflict !== undefined) {
@@ -823,6 +881,22 @@ function projectMutationFromOptions(parsed) {
823
881
  ...(parsed.values.get("target-duration") === undefined
824
882
  ? {}
825
883
  : { targetDuration: parsed.values.get("target-duration") }),
884
+ ...(parsed.values.get("goal-owner") === undefined
885
+ ? {}
886
+ : { goalOwner: parsed.values.get("goal-owner") }),
887
+ ...(parsed.values.get("goal-delegates") === undefined
888
+ ? {}
889
+ : {
890
+ goalDelegates: principalListOption(parsed.values.get("goal-delegates")),
891
+ }),
892
+ ...(parsed.values.get("dag-owner") === undefined
893
+ ? {}
894
+ : { dagOwner: parsed.values.get("dag-owner") }),
895
+ ...(parsed.values.get("dag-delegates") === undefined
896
+ ? {}
897
+ : {
898
+ dagDelegates: principalListOption(parsed.values.get("dag-delegates")),
899
+ }),
826
900
  };
827
901
  return {
828
902
  kind: "project.set",
@@ -848,18 +922,22 @@ function milestoneMutationFromOptions(action, parsed) {
848
922
  ? {}
849
923
  : { description: parsed.values.get("description") }),
850
924
  ...(state === undefined ? {} : { state }),
925
+ ...(parsed.values.get("deadline") === undefined
926
+ ? {}
927
+ : { deadline: parsed.values.get("deadline") }),
851
928
  ...((parsed.repeatedValues.get("tag") ?? []).length === 0
852
929
  ? {}
853
930
  : { tags: parsed.repeatedValues.get("tag") }),
854
931
  },
855
932
  };
856
933
  }
857
- const clear = enumRepeated(parsed, "clear", new Set(["description", "state", "tags"]));
934
+ const clear = enumRepeated(parsed, "clear", new Set(["description", "state", "deadline", "tags"]));
858
935
  const addTags = uniqueRepeated(parsed, "add-tag");
859
936
  const removeTags = uniqueRepeated(parsed, "remove-tag");
860
937
  ensureDisjoint(addTags, "add-tag", removeTags, "remove-tag");
861
938
  if (clear.some((field) => (field === "description" && parsed.values.has("description")) ||
862
939
  (field === "state" && state !== undefined) ||
940
+ (field === "deadline" && parsed.values.has("deadline")) ||
863
941
  (field === "tags" && (addTags.length > 0 || removeTags.length > 0)))) {
864
942
  throw new UsageError("--clear conflicts with another milestone field option");
865
943
  }
@@ -871,6 +949,9 @@ function milestoneMutationFromOptions(action, parsed) {
871
949
  ? {}
872
950
  : { description: parsed.values.get("description") }),
873
951
  ...(state === undefined ? {} : { state }),
952
+ ...(parsed.values.get("deadline") === undefined
953
+ ? {}
954
+ : { deadline: parsed.values.get("deadline") }),
874
955
  };
875
956
  return {
876
957
  kind: "milestone.set",
@@ -1040,20 +1121,24 @@ async function runMutation(resource, action, args) {
1040
1121
  maxDiagnostics,
1041
1122
  originalLabel: source,
1042
1123
  updatedLabel: "candidate",
1124
+ governance: governanceRequest(parsed, writeRequest),
1043
1125
  };
1044
1126
  const result = resource === "batch"
1045
- ? planBatchMutation(input.text, mutation, mutationOptions)
1046
- : planMutation(input.text, mutation, mutationOptions);
1127
+ ? planTargetGovernanceBatchMutation(input.text, mutation, TARGET_GRAMMAR_4_CAPABILITY, mutationOptions)
1128
+ : planTargetGovernanceMutation(input.text, mutation, TARGET_GRAMMAR_4_CAPABILITY, mutationOptions);
1047
1129
  const warningFailure = parsed.flags.has("warnings-as-errors") &&
1048
1130
  (result.diagnosticsTruncated ||
1049
1131
  result.diagnostics.some((diagnostic) => diagnostic.severity === "warning"));
1050
1132
  const ok = result.ok && !warningFailure;
1051
- let writeResult = null;
1133
+ let writeResult = Object.freeze({
1134
+ mode: writeRequest.mode,
1135
+ target: writeRequest.target,
1136
+ written: false,
1137
+ });
1052
1138
  if (result.ok) {
1053
1139
  try {
1054
- assertExpectedDigest(writeRequest, input.digest);
1055
1140
  if (ok && writeRequest.mode !== "preview") {
1056
- writeResult = await commitCandidate(writeRequest, result.updatedText, input.digest);
1141
+ writeResult = await persistGovernedResult(result, writeRequest, sourceOperand);
1057
1142
  }
1058
1143
  }
1059
1144
  catch (error) {
@@ -1061,24 +1146,12 @@ async function runMutation(resource, action, args) {
1061
1146
  }
1062
1147
  }
1063
1148
  if (format === "json") {
1064
- writeJson({
1065
- schema_version: "Perttool.MutationResult.v1",
1066
- cli_contract_version: 3,
1067
- tool_version: TOOL_VERSION,
1068
- operation,
1069
- ok,
1070
- document_id: result.documentId,
1071
- source,
1072
- source_digest: input.digest,
1073
- diagnostics: result.diagnostics.map(jsonDiagnostic),
1074
- diagnostics_truncated: result.diagnosticsTruncated,
1075
- ...previewResultJson(result, result.ok, writeRequest, writeResult),
1076
- });
1149
+ writeJson(targetGovernanceMutationResultToJson(ok ? result : Object.freeze({ ...result, ok: false }), operation, source, writeResult));
1077
1150
  }
1078
1151
  else {
1079
1152
  if (ok) {
1080
- if (writeResult !== null) {
1081
- process.stderr.write(renderWriteSummary(operation, writeResult));
1153
+ if (writeRequest.mode !== "preview") {
1154
+ process.stderr.write(renderGovernanceWriteSummary(operation, writeResult, result.updatedDigest));
1082
1155
  }
1083
1156
  else {
1084
1157
  process.stdout.write(parsed.flags.has("diff") ? (result.diff ?? "") : (result.updatedText ?? ""));
@@ -1087,6 +1160,9 @@ async function runMutation(resource, action, args) {
1087
1160
  }
1088
1161
  }
1089
1162
  }
1163
+ if (result.governance !== null) {
1164
+ process.stderr.write(renderTargetGovernanceDecision(result.governance));
1165
+ }
1090
1166
  for (const diagnostic of result.diagnostics) {
1091
1167
  process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
1092
1168
  }
@@ -1096,18 +1172,141 @@ async function runMutation(resource, action, args) {
1096
1172
  }
1097
1173
  return ok ? 0 : 1;
1098
1174
  }
1099
- function advanceResultJson(details) {
1100
- return details === null
1101
- ? null
1102
- : {
1103
- removed_task_ids: details.removedTaskIds,
1104
- removed_gate_ids: details.removedGateIds,
1105
- removed_milestone_ids: details.removedMilestoneIds,
1106
- frontier_before: details.frontierBefore,
1107
- frontier_after: details.frontierAfter,
1108
- ready_before: details.readyBefore,
1109
- ready_after: details.readyAfter,
1110
- };
1175
+ function unitMigrationJson(result, source, sourceDigest, ok) {
1176
+ const exact = (value) => ({
1177
+ numerator: value.numerator.toString(),
1178
+ denominator: value.denominator.toString(),
1179
+ unit: value.unit,
1180
+ });
1181
+ return {
1182
+ schema_version: result.schemaVersion,
1183
+ cli_contract_version: 5,
1184
+ tool_version: TOOL_VERSION,
1185
+ operation: "project.migrate-unit",
1186
+ ok,
1187
+ document_id: result.documentId,
1188
+ source,
1189
+ source_digest: sourceDigest,
1190
+ diagnostics: result.diagnostics.map(jsonDiagnostic),
1191
+ diagnostics_truncated: result.diagnosticsTruncated,
1192
+ unit_migration: result.unitMigration,
1193
+ source_grammar_version: result.sourceGrammarVersion,
1194
+ target_grammar_version: result.targetGrammarVersion,
1195
+ grammar_disposition: result.grammarDisposition,
1196
+ source_unit: result.sourceUnit,
1197
+ target_unit: result.targetUnit,
1198
+ effective_velocity: result.effectiveVelocity === null
1199
+ ? null
1200
+ : {
1201
+ points: exact(result.effectiveVelocity.points),
1202
+ period: exact(result.effectiveVelocity.period),
1203
+ },
1204
+ velocity_disposition: result.velocityDisposition,
1205
+ changed: result.changed,
1206
+ converted_fields: result.convertedFields.map((field) => ({
1207
+ entity_kind: field.entityKind,
1208
+ entity_id: field.entityId,
1209
+ field_path: field.fieldPath,
1210
+ original: exact(field.original),
1211
+ converted: exact(field.converted),
1212
+ canonical_token: field.canonicalToken,
1213
+ })),
1214
+ reversibility: result.reversibility,
1215
+ qualifications: result.qualifications,
1216
+ unavailable_causes: result.unavailableCauses.map((cause) => ({
1217
+ cause: cause.cause,
1218
+ diagnostic_code: cause.diagnosticCode,
1219
+ field_paths: cause.fieldPaths,
1220
+ })),
1221
+ original_digest: result.originalDigest,
1222
+ updated_digest: result.updatedDigest,
1223
+ updated_text: result.updatedText,
1224
+ diff: result.diff,
1225
+ edits: result.edits.map((edit) => ({
1226
+ start_offset: edit.startOffset,
1227
+ end_offset: edit.endOffset,
1228
+ replacement: edit.replacement,
1229
+ })),
1230
+ write: {
1231
+ mode: result.write.mode,
1232
+ target: result.write.target,
1233
+ written: result.write.written,
1234
+ },
1235
+ };
1236
+ }
1237
+ async function runUnitMigration(args) {
1238
+ const parsed = parseCommandOptions("project.migrate-unit", args);
1239
+ if (parsed.positionals.length !== 1) {
1240
+ throw new UsageError("project migrate-unit requires exactly one <file>");
1241
+ }
1242
+ const format = outputFormat(parsed.values.get("format"));
1243
+ const color = colorMode(parsed.values.get("color"), format);
1244
+ const targetUnit = enumOption(requiredOption(parsed, "to-unit"), "to-unit", new Set(["day", "hour", "point"]));
1245
+ const maxDiagnostics = boundedInteger(parsed.values.get("max-diagnostics"), "max-diagnostics", 100, 1, 1000);
1246
+ const sourceOperand = parsed.positionals[0];
1247
+ const writeRequest = editingWriteRequest(parsed, sourceOperand);
1248
+ const source = sourceOperand === "-" ? "<stdin>" : sourceOperand;
1249
+ let input;
1250
+ try {
1251
+ input = await readDocument(sourceOperand);
1252
+ }
1253
+ catch (error) {
1254
+ return cliError(error instanceof Error ? error : new Error(String(error)), 3, "project.migrate-unit", format === "json");
1255
+ }
1256
+ let result = planUnitMigration(input.text, {
1257
+ targetUnit,
1258
+ ...(parsed.values.get("replacement-velocity") === undefined
1259
+ ? {}
1260
+ : {
1261
+ replacementVelocity: parsed.values.get("replacement-velocity"),
1262
+ }),
1263
+ }, {
1264
+ maxDiagnostics,
1265
+ originalLabel: source,
1266
+ updatedLabel: "candidate",
1267
+ });
1268
+ const warningFailure = parsed.flags.has("warnings-as-errors") &&
1269
+ (result.diagnosticsTruncated ||
1270
+ result.diagnostics.some(({ severity }) => severity === "warning"));
1271
+ const ok = result.ok && !warningFailure;
1272
+ let writeResult = null;
1273
+ if (result.ok) {
1274
+ try {
1275
+ assertExpectedDigest(writeRequest, input.digest);
1276
+ if (ok && writeRequest.mode !== "preview") {
1277
+ writeResult = await commitCandidate(writeRequest, result.updatedText, input.digest);
1278
+ result = withUnitMigrationWrite(result, writeResult);
1279
+ }
1280
+ }
1281
+ catch (error) {
1282
+ return writeFailureExit(error, "project.migrate-unit", format === "json");
1283
+ }
1284
+ }
1285
+ if (format === "json") {
1286
+ writeJson(unitMigrationJson(result, source, input.digest, ok));
1287
+ }
1288
+ else {
1289
+ if (ok) {
1290
+ if (writeResult !== null) {
1291
+ process.stderr.write(renderWriteSummary("project.migrate-unit", writeResult));
1292
+ }
1293
+ else {
1294
+ process.stdout.write(parsed.flags.has("diff")
1295
+ ? (result.diff ?? "")
1296
+ : (result.updatedText ?? ""));
1297
+ if (!parsed.flags.has("diff")) {
1298
+ process.stderr.write(`PREVIEW project.migrate-unit changed=${result.changed} source_unit=${result.sourceUnit ?? "-"} target_unit=${result.targetUnit} original_digest=${result.originalDigest} updated_digest=${result.updatedDigest ?? "-"}\n`);
1299
+ }
1300
+ }
1301
+ }
1302
+ for (const diagnostic of result.diagnostics) {
1303
+ process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
1304
+ }
1305
+ if (result.diagnosticsTruncated) {
1306
+ process.stderr.write(`DIAGNOSTICS_TRUNCATED true limit=${maxDiagnostics}\n`);
1307
+ }
1308
+ }
1309
+ return ok ? 0 : 1;
1111
1310
  }
1112
1311
  function renderAdvanceSummary(details) {
1113
1312
  const list = (ids) => ids.join(",") || "-";
@@ -1135,21 +1334,25 @@ async function runAdvance(args) {
1135
1334
  catch (error) {
1136
1335
  return cliError(error instanceof Error ? error : new Error(String(error)), 3, "dag.advance", format === "json");
1137
1336
  }
1138
- const result = planAdvance(input.text, {
1337
+ const result = planTargetGovernanceAdvance(input.text, TARGET_GRAMMAR_4_CAPABILITY, {
1139
1338
  maxDiagnostics,
1140
1339
  originalLabel: source,
1141
1340
  updatedLabel: "candidate",
1341
+ governance: governanceRequest(parsed, writeRequest),
1142
1342
  });
1143
1343
  const warningFailure = parsed.flags.has("warnings-as-errors") &&
1144
1344
  (result.diagnosticsTruncated ||
1145
1345
  result.diagnostics.some((diagnostic) => diagnostic.severity === "warning"));
1146
1346
  const ok = result.ok && !warningFailure;
1147
- let writeResult = null;
1347
+ let writeResult = Object.freeze({
1348
+ mode: writeRequest.mode,
1349
+ target: writeRequest.target,
1350
+ written: false,
1351
+ });
1148
1352
  if (result.ok) {
1149
1353
  try {
1150
- assertExpectedDigest(writeRequest, input.digest);
1151
1354
  if (ok && writeRequest.mode !== "preview") {
1152
- writeResult = await commitCandidate(writeRequest, result.updatedText, input.digest);
1355
+ writeResult = await persistGovernedResult(result, writeRequest, sourceOperand);
1153
1356
  }
1154
1357
  }
1155
1358
  catch (error) {
@@ -1157,25 +1360,12 @@ async function runAdvance(args) {
1157
1360
  }
1158
1361
  }
1159
1362
  if (format === "json") {
1160
- writeJson({
1161
- schema_version: "Perttool.MutationResult.v1",
1162
- cli_contract_version: 3,
1163
- tool_version: TOOL_VERSION,
1164
- operation: "dag.advance",
1165
- ok,
1166
- document_id: result.documentId,
1167
- source,
1168
- source_digest: input.digest,
1169
- diagnostics: result.diagnostics.map(jsonDiagnostic),
1170
- diagnostics_truncated: result.diagnosticsTruncated,
1171
- ...previewResultJson(result, result.ok, writeRequest, writeResult),
1172
- advance: advanceResultJson(result.advance),
1173
- });
1363
+ writeJson(targetGovernanceMutationResultToJson(ok ? result : Object.freeze({ ...result, ok: false }), "dag.advance", source, writeResult));
1174
1364
  }
1175
1365
  else {
1176
1366
  if (ok && result.advance !== null) {
1177
- if (writeResult !== null) {
1178
- process.stderr.write(renderWriteSummary("dag.advance", writeResult));
1367
+ if (writeRequest.mode !== "preview") {
1368
+ process.stderr.write(renderGovernanceWriteSummary("dag.advance", writeResult, result.updatedDigest));
1179
1369
  }
1180
1370
  else {
1181
1371
  process.stdout.write(parsed.flags.has("diff") ? (result.diff ?? "") : (result.updatedText ?? ""));
@@ -1185,6 +1375,9 @@ async function runAdvance(args) {
1185
1375
  }
1186
1376
  process.stderr.write(renderAdvanceSummary(result.advance));
1187
1377
  }
1378
+ if (result.governance !== null) {
1379
+ process.stderr.write(renderTargetGovernanceDecision(result.governance));
1380
+ }
1188
1381
  for (const diagnostic of result.diagnostics) {
1189
1382
  process.stderr.write(`${renderDiagnostic(diagnostic, source, color)}\n`);
1190
1383
  }
@@ -1437,6 +1630,41 @@ function renderAnalysisText(result) {
1437
1630
  if (resource.resources.length === 0)
1438
1631
  lines.push("-");
1439
1632
  }
1633
+ if (result.temporal !== null) {
1634
+ const temporalPoint = (point) => {
1635
+ const causes = point.unavailableCauses
1636
+ .map(({ cause }) => cause)
1637
+ .join(",") || "-";
1638
+ return [
1639
+ `state=${point.state}`,
1640
+ `relative=${point.relative === null ? "-" : `${point.relative.numerator}/${point.relative.denominator}${durationSuffix(point.relative.unit)}`}`,
1641
+ `calendar=${point.calendar?.sourceText ?? "-"}`,
1642
+ `causes=${causes}`,
1643
+ ].join(" ");
1644
+ };
1645
+ for (const [title, projection] of [
1646
+ ["TEMPORAL PRECEDENCE", result.temporal.precedence],
1647
+ ["TEMPORAL RESOURCE", result.temporal.resource],
1648
+ ]) {
1649
+ lines.push("", title, `STATE ${projection.state}`, `ALGORITHM ${projection.algorithm === null ? "-" : `${projection.algorithm.id}@${projection.algorithm.version} optimal=${projection.algorithm.optimal}`}`, `CONDITIONAL_ON_BLOCKS_RESOLVED ${projection.conditionalOnBlocksResolved}`, `BLOCKED_TASKS ${projection.blockedTaskIds.join(",") || "-"}`);
1650
+ for (const task of projection.tasks) {
1651
+ lines.push(`${task.taskId} START ${temporalPoint(task.start)} FINISH ${temporalPoint(task.finish)}`);
1652
+ }
1653
+ if (projection.tasks.length === 0)
1654
+ lines.push("-");
1655
+ }
1656
+ lines.push("", "DEADLINES");
1657
+ for (const evaluation of result.temporal.deadlineEvaluations) {
1658
+ const causes = [
1659
+ ...evaluation.current.unavailableCauses,
1660
+ ...evaluation.precedence.unavailableCauses,
1661
+ ...evaluation.resource.unavailableCauses,
1662
+ ].map(({ cause }) => cause).join(",") || "-";
1663
+ lines.push(`${evaluation.subject.kind}:${evaluation.subject.id} assessment=${evaluation.combinedAssessment} precedence=${evaluation.precedence.assessment ?? evaluation.precedence.state} resource=${evaluation.resource.assessment ?? evaluation.resource.state} conditional=${evaluation.conditionalOnBlocksResolved} causes=${causes}`);
1664
+ }
1665
+ if (result.temporal.deadlineEvaluations.length === 0)
1666
+ lines.push("-");
1667
+ }
1440
1668
  return `${lines.join("\n")}\n`;
1441
1669
  }
1442
1670
  async function runAnalyze(args) {
@@ -1473,8 +1701,8 @@ async function runAnalyze(args) {
1473
1701
  const ok = result.ok && !warningFailure;
1474
1702
  if (format === "json") {
1475
1703
  writeJson({
1476
- schema_version: "Perttool.AnalysisResult.v2",
1477
- cli_contract_version: 3,
1704
+ schema_version: "Perttool.AnalysisResult.v3",
1705
+ cli_contract_version: 5,
1478
1706
  tool_version: TOOL_VERSION,
1479
1707
  operation: "dag.analyze",
1480
1708
  ok,
@@ -1483,6 +1711,7 @@ async function runAnalyze(args) {
1483
1711
  source_digest: input.digest,
1484
1712
  diagnostics: result.diagnostics.map(jsonDiagnostic),
1485
1713
  diagnostics_truncated: result.diagnosticsTruncated,
1714
+ grammar_version: result.grammarVersion,
1486
1715
  mode: result.mode,
1487
1716
  precision: result.precision,
1488
1717
  ...(result.durationUnit === null
@@ -1499,6 +1728,7 @@ async function runAnalyze(args) {
1499
1728
  resource: result.resource === null || result.durationUnit === null
1500
1729
  ? null
1501
1730
  : resourceJson(result.resource, result.durationUnit, result.precision),
1731
+ temporal: snakeJson(result.temporal),
1502
1732
  });
1503
1733
  }
1504
1734
  else {
@@ -1586,7 +1816,7 @@ async function runRender(args) {
1586
1816
  if (format === "json") {
1587
1817
  writeJson({
1588
1818
  schema_version: "Perttool.ExportResult.v1",
1589
- cli_contract_version: 3,
1819
+ cli_contract_version: 5,
1590
1820
  tool_version: TOOL_VERSION,
1591
1821
  operation: "dag.render",
1592
1822
  ok,
@@ -1678,7 +1908,7 @@ async function runImport(args) {
1678
1908
  if (format === "json") {
1679
1909
  writeJson({
1680
1910
  schema_version: "Perttool.ImportResult.v1",
1681
- cli_contract_version: 3,
1911
+ cli_contract_version: 5,
1682
1912
  tool_version: TOOL_VERSION,
1683
1913
  operation: "dag.import",
1684
1914
  ok,
@@ -1895,7 +2125,15 @@ function renderExplanation(node, indent) {
1895
2125
  function renderNextText(result) {
1896
2126
  const unit = result.durationUnit;
1897
2127
  const taskById = new Map(result.tasks.map((task) => [task.id, task]));
2128
+ const authority = result.temporal.authority;
1898
2129
  const lines = [
2130
+ "START AUTHORITY",
2131
+ `POLICY ${authority.policy}`,
2132
+ `STARTABLE RECOMMENDED ${authority.startableRecommendedTaskIds.join(",") || "-"}`,
2133
+ `DELAYED RECOMMENDED ${authority.delayedRecommendedTaskIds.join(",") || "-"}`,
2134
+ `UNAVAILABLE RECOMMENDED ${authority.unavailableRecommendedTaskIds.join(",") || "-"}`,
2135
+ "DEADLINE FACTS INFORMATIONAL FOR RANKING v1",
2136
+ "",
1899
2137
  `PERTTOOL NEXT ${result.documentId ?? "-"}`,
1900
2138
  `VELOCITY ${result.velocity === null ? "-" : velocityText(result.velocity, result.precision)}`,
1901
2139
  `VELOCITY_FORECAST_UNIT ${result.velocityForecast?.targetUnit ?? "-"}`,
@@ -1934,6 +2172,15 @@ function renderNextText(result) {
1934
2172
  section("READY / WAITING RESOURCE", result.groups.ready.filter((id) => !result.groups.runnableNow.includes(id)), "rejection");
1935
2173
  section("BLOCKED NOW", result.groups.blockedNow, "blocked");
1936
2174
  section("UPCOMING", result.groups.upcoming, "explanation");
2175
+ lines.push("", "TEMPORAL CONTEXT");
2176
+ for (const temporal of result.temporal.tasks) {
2177
+ const causes = temporal.timeEligibility.unavailableCauses
2178
+ .map(({ cause }) => cause)
2179
+ .join(",") || "-";
2180
+ lines.push(`${temporal.taskId} eligibility=${temporal.timeEligibility.state} release=${temporal.timeEligibility.releaseBound === null ? "-" : `${temporal.timeEligibility.releaseBound.numerator}/${temporal.timeEligibility.releaseBound.denominator}${durationSuffix(temporal.timeEligibility.releaseBound.unit)}`} task_deadline=${temporal.taskDeadline?.sourceText ?? "-"} destination=${temporal.destinationMilestoneId} destination_deadline=${temporal.destinationDeadline?.sourceText ?? "-"} causes=${causes}`);
2181
+ }
2182
+ if (result.temporal.tasks.length === 0)
2183
+ lines.push("-");
1937
2184
  return `${lines.join("\n")}\n`;
1938
2185
  }
1939
2186
  async function runNext(args) {
@@ -1968,7 +2215,7 @@ async function runNext(args) {
1968
2215
  if (format === "json") {
1969
2216
  writeJson({
1970
2217
  schema_version: "Perttool.CliError.v1",
1971
- cli_contract_version: 3,
2218
+ cli_contract_version: 5,
1972
2219
  tool_version: TOOL_VERSION,
1973
2220
  operation: "dag.next",
1974
2221
  ok: false,
@@ -1988,8 +2235,8 @@ async function runNext(args) {
1988
2235
  const ok = result.ok && !warningFailure;
1989
2236
  if (format === "json") {
1990
2237
  writeJson({
1991
- schema_version: "Perttool.NextResult.v3",
1992
- cli_contract_version: 3,
2238
+ schema_version: "Perttool.NextResult.v4",
2239
+ cli_contract_version: 5,
1993
2240
  recommendation_interface_version: 1,
1994
2241
  tool_version: TOOL_VERSION,
1995
2242
  operation: "dag.next",
@@ -1999,7 +2246,9 @@ async function runNext(args) {
1999
2246
  source_digest: input.digest,
2000
2247
  diagnostics: result.diagnostics.map(jsonDiagnostic),
2001
2248
  diagnostics_truncated: result.diagnosticsTruncated,
2249
+ grammar_version: result.grammarVersion,
2002
2250
  ...(result.durationUnit === null ? {} : nextJson(result)),
2251
+ temporal: snakeJson(result.temporal),
2003
2252
  });
2004
2253
  }
2005
2254
  else {
@@ -2023,12 +2272,12 @@ function runGuide(args) {
2023
2272
  colorMode(parsed.values.get("color"), format);
2024
2273
  const topicId = parsed.positionals.length === 0 ? null : parsed.positionals.join(".");
2025
2274
  const level = helpLevel(parsed.values.get("level"), topicId !== null);
2026
- const result = getGuide(topicId, level);
2275
+ const result = getTargetGovernanceGuide(topicId, level);
2027
2276
  if (format === "json") {
2028
- process.stdout.write(serializeGuideResult(result));
2277
+ process.stdout.write(serializeTargetGovernanceGuideResult(result));
2029
2278
  }
2030
2279
  else {
2031
- const rendered = renderGuideResult(result);
2280
+ const rendered = renderTargetGovernanceGuideResult(result);
2032
2281
  if (result.ok) {
2033
2282
  process.stdout.write(rendered);
2034
2283
  }
@@ -2044,15 +2293,15 @@ function runCommandHelp(args) {
2044
2293
  throw new UsageError("help accepts at most <resource> <action>");
2045
2294
  }
2046
2295
  const format = outputFormat(parsed.values.get("format"));
2047
- const result = getCommandDiscovery({
2296
+ const result = getTargetGovernanceCommandDiscovery({
2048
2297
  resource: parsed.positionals[0] ?? null,
2049
2298
  action: parsed.positionals[1] ?? null,
2050
2299
  });
2051
2300
  if (format === "json") {
2052
- process.stdout.write(serializeCommandHelpResult(result));
2301
+ process.stdout.write(serializeTargetGovernanceCommandHelpResult(result));
2053
2302
  }
2054
2303
  else {
2055
- const rendered = renderCommandHelpResult(result);
2304
+ const rendered = renderTargetGovernanceCommandHelpResult(result);
2056
2305
  if (result.ok) {
2057
2306
  process.stdout.write(rendered);
2058
2307
  }
@@ -2082,7 +2331,7 @@ function runAgentHelp(args) {
2082
2331
  const { schema_version: schemaVersion, ...payload } = projected;
2083
2332
  writeJson({
2084
2333
  schema_version: schemaVersion,
2085
- cli_contract_version: 3,
2334
+ cli_contract_version: 5,
2086
2335
  ...payload,
2087
2336
  });
2088
2337
  }
@@ -2120,6 +2369,8 @@ async function dispatchCommand(descriptor, args) {
2120
2369
  return runProjectInit(args);
2121
2370
  case "project.show":
2122
2371
  return runProjectShow(args);
2372
+ case "project.migrate-unit":
2373
+ return runUnitMigration(args);
2123
2374
  case "dag.analyze":
2124
2375
  return runAnalyze(args);
2125
2376
  case "dag.next":
@@ -2138,14 +2389,14 @@ async function dispatchCommand(descriptor, args) {
2138
2389
  const [resource, action] = descriptor.path;
2139
2390
  return runMutation(resource, action, args);
2140
2391
  }
2141
- throw new Error(`no Contract 3 handler for ${descriptor.operation}`);
2392
+ throw new Error(`no Contract 5 handler for ${descriptor.operation}`);
2142
2393
  }
2143
2394
  function emitCommandUsage(error, json) {
2144
2395
  if (json) {
2145
- process.stdout.write(serializeCommandUsageError(error));
2396
+ process.stdout.write(serializeTargetGovernanceCommandUsageError(error));
2146
2397
  }
2147
2398
  else {
2148
- process.stderr.write(renderCommandUsageError(error));
2399
+ process.stderr.write(renderTargetGovernanceCommandUsageError(error));
2149
2400
  }
2150
2401
  return 2;
2151
2402
  }
@@ -2157,13 +2408,13 @@ async function main(argv) {
2157
2408
  if (argv.length === 1 && argv[0] === "--help") {
2158
2409
  return runCommandHelp([]);
2159
2410
  }
2160
- const validation = validateCommandInvocation(argv);
2411
+ const validation = validateTargetGovernanceCommandInvocation(argv);
2161
2412
  if (!validation.ok) {
2162
2413
  if (jsonRequested(argv)) {
2163
- process.stdout.write(serializeCommandUsageError(validation.error));
2414
+ process.stdout.write(serializeTargetGovernanceCommandUsageError(validation.error));
2164
2415
  }
2165
2416
  else {
2166
- process.stderr.write(renderCommandUsageError(validation.error));
2417
+ process.stderr.write(renderTargetGovernanceCommandUsageError(validation.error));
2167
2418
  }
2168
2419
  return 2;
2169
2420
  }