perttool 0.3.0 → 0.5.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 (286) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/README.md +134 -24
  3. package/dist/actuals/lifecycle.d.ts +101 -0
  4. package/dist/actuals/lifecycle.d.ts.map +1 -0
  5. package/dist/actuals/lifecycle.js +329 -0
  6. package/dist/actuals/lifecycle.js.map +1 -0
  7. package/dist/actuals/source.d.ts +36 -0
  8. package/dist/actuals/source.d.ts.map +1 -0
  9. package/dist/actuals/source.js +119 -0
  10. package/dist/actuals/source.js.map +1 -0
  11. package/dist/analysis/temporal-deadline.d.ts +2 -2
  12. package/dist/analysis/temporal-deadline.d.ts.map +1 -1
  13. package/dist/analysis/temporal-deadline.js.map +1 -1
  14. package/dist/analysis/temporal-precedence.d.ts +2 -2
  15. package/dist/analysis/temporal-precedence.d.ts.map +1 -1
  16. package/dist/analysis/temporal-precedence.js +2 -0
  17. package/dist/analysis/temporal-precedence.js.map +1 -1
  18. package/dist/analysis/temporal-resource.d.ts +2 -2
  19. package/dist/analysis/temporal-resource.d.ts.map +1 -1
  20. package/dist/analysis/temporal-resource.js +2 -0
  21. package/dist/analysis/temporal-resource.js.map +1 -1
  22. package/dist/application/analyze.d.ts +1 -0
  23. package/dist/application/analyze.d.ts.map +1 -1
  24. package/dist/application/analyze.js +38 -29
  25. package/dist/application/analyze.js.map +1 -1
  26. package/dist/application/check.d.ts +3 -0
  27. package/dist/application/check.d.ts.map +1 -1
  28. package/dist/application/check.js +17 -1
  29. package/dist/application/check.js.map +1 -1
  30. package/dist/application/contract4.js +3 -3
  31. package/dist/application/contract5-governance.d.ts +8 -0
  32. package/dist/application/contract5-governance.d.ts.map +1 -0
  33. package/dist/application/contract5-governance.js +12 -0
  34. package/dist/application/contract5-governance.js.map +1 -0
  35. package/dist/application/contract6-actuals.d.ts +24 -0
  36. package/dist/application/contract6-actuals.d.ts.map +1 -0
  37. package/dist/application/contract6-actuals.js +51 -0
  38. package/dist/application/contract6-actuals.js.map +1 -0
  39. package/dist/application/contract6-mutation.d.ts +18 -0
  40. package/dist/application/contract6-mutation.d.ts.map +1 -0
  41. package/dist/application/contract6-mutation.js +173 -0
  42. package/dist/application/contract6-mutation.js.map +1 -0
  43. package/dist/application/contract6-projection.d.ts +9 -0
  44. package/dist/application/contract6-projection.d.ts.map +1 -0
  45. package/dist/application/contract6-projection.js +105 -0
  46. package/dist/application/contract6-projection.js.map +1 -0
  47. package/dist/application/init.d.ts +3 -12
  48. package/dist/application/init.d.ts.map +1 -1
  49. package/dist/application/init.js +16 -138
  50. package/dist/application/init.js.map +1 -1
  51. package/dist/application/project.d.ts +4 -24
  52. package/dist/application/project.d.ts.map +1 -1
  53. package/dist/application/project.js +3 -52
  54. package/dist/application/project.js.map +1 -1
  55. package/dist/application/target-actuals-advance.d.ts +12 -0
  56. package/dist/application/target-actuals-advance.d.ts.map +1 -0
  57. package/dist/application/target-actuals-advance.js +113 -0
  58. package/dist/application/target-actuals-advance.js.map +1 -0
  59. package/dist/application/target-actuals-analysis.d.ts +76 -0
  60. package/dist/application/target-actuals-analysis.d.ts.map +1 -0
  61. package/dist/application/target-actuals-analysis.js +250 -0
  62. package/dist/application/target-actuals-analysis.js.map +1 -0
  63. package/dist/application/target-actuals-mutation.d.ts +28 -0
  64. package/dist/application/target-actuals-mutation.d.ts.map +1 -0
  65. package/dist/application/target-actuals-mutation.js +404 -0
  66. package/dist/application/target-actuals-mutation.js.map +1 -0
  67. package/dist/application/target-actuals-write.d.ts +7 -0
  68. package/dist/application/target-actuals-write.d.ts.map +1 -0
  69. package/dist/application/target-actuals-write.js +37 -0
  70. package/dist/application/target-actuals-write.js.map +1 -0
  71. package/dist/application/target-governance-init.d.ts +36 -0
  72. package/dist/application/target-governance-init.d.ts.map +1 -0
  73. package/dist/application/target-governance-init.js +195 -0
  74. package/dist/application/target-governance-init.js.map +1 -0
  75. package/dist/application/target-governance-mutation.d.ts +20 -0
  76. package/dist/application/target-governance-mutation.d.ts.map +1 -0
  77. package/dist/application/target-governance-mutation.js +175 -0
  78. package/dist/application/target-governance-mutation.js.map +1 -0
  79. package/dist/application/target-governance-project.d.ts +34 -0
  80. package/dist/application/target-governance-project.d.ts.map +1 -0
  81. package/dist/application/target-governance-project.js +82 -0
  82. package/dist/application/target-governance-project.js.map +1 -0
  83. package/dist/application/target-governance-projection.d.ts +18 -0
  84. package/dist/application/target-governance-projection.d.ts.map +1 -0
  85. package/dist/application/target-governance-projection.js +248 -0
  86. package/dist/application/target-governance-projection.js.map +1 -0
  87. package/dist/application/target-governance-write.d.ts +25 -0
  88. package/dist/application/target-governance-write.d.ts.map +1 -0
  89. package/dist/application/target-governance-write.js +55 -0
  90. package/dist/application/target-governance-write.js.map +1 -0
  91. package/dist/application/target-mutate.d.ts +6 -2
  92. package/dist/application/target-mutate.d.ts.map +1 -1
  93. package/dist/application/target-mutate.js +59 -2
  94. package/dist/application/target-mutate.js.map +1 -1
  95. package/dist/application/target-project-history.d.ts +18 -0
  96. package/dist/application/target-project-history.d.ts.map +1 -0
  97. package/dist/application/target-project-history.js +279 -0
  98. package/dist/application/target-project-history.js.map +1 -0
  99. package/dist/application/target-temporal-analysis.d.ts +13 -5
  100. package/dist/application/target-temporal-analysis.d.ts.map +1 -1
  101. package/dist/application/target-temporal-analysis.js +18 -11
  102. package/dist/application/target-temporal-analysis.js.map +1 -1
  103. package/dist/application/target-temporal-input.d.ts +8 -6
  104. package/dist/application/target-temporal-input.d.ts.map +1 -1
  105. package/dist/application/target-temporal-input.js +14 -3
  106. package/dist/application/target-temporal-input.js.map +1 -1
  107. package/dist/application/target-unit-migration-candidate.d.ts +2 -2
  108. package/dist/application/target-unit-migration-candidate.d.ts.map +1 -1
  109. package/dist/application/target-unit-migration-candidate.js +21 -6
  110. package/dist/application/target-unit-migration-candidate.js.map +1 -1
  111. package/dist/application/target-unit-migration-request.d.ts +4 -4
  112. package/dist/application/target-unit-migration-request.d.ts.map +1 -1
  113. package/dist/application/target-unit-migration-request.js +6 -2
  114. package/dist/application/target-unit-migration-request.js.map +1 -1
  115. package/dist/application/target-unit-migration-result.d.ts +5 -5
  116. package/dist/application/target-unit-migration-result.d.ts.map +1 -1
  117. package/dist/application/target-unit-migration-result.js +1 -1
  118. package/dist/application/target-unit-migration-result.js.map +1 -1
  119. package/dist/application/target-velocity-observation.d.ts +11 -0
  120. package/dist/application/target-velocity-observation.d.ts.map +1 -0
  121. package/dist/application/target-velocity-observation.js +201 -0
  122. package/dist/application/target-velocity-observation.js.map +1 -0
  123. package/dist/application/unit-migration.d.ts +4 -1
  124. package/dist/application/unit-migration.d.ts.map +1 -1
  125. package/dist/application/unit-migration.js +5 -3
  126. package/dist/application/unit-migration.js.map +1 -1
  127. package/dist/cli.js +390 -137
  128. package/dist/cli.js.map +1 -1
  129. package/dist/command/actuals-discovery.d.ts +26 -0
  130. package/dist/command/actuals-discovery.d.ts.map +1 -0
  131. package/dist/command/actuals-discovery.js +271 -0
  132. package/dist/command/actuals-discovery.js.map +1 -0
  133. package/dist/command/actuals-usage.d.ts +22 -0
  134. package/dist/command/actuals-usage.d.ts.map +1 -0
  135. package/dist/command/actuals-usage.js +122 -0
  136. package/dist/command/actuals-usage.js.map +1 -0
  137. package/dist/command/registry.d.ts +7 -1
  138. package/dist/command/registry.d.ts.map +1 -1
  139. package/dist/command/registry.js.map +1 -1
  140. package/dist/command/target-governance-discovery.d.ts +31 -0
  141. package/dist/command/target-governance-discovery.d.ts.map +1 -0
  142. package/dist/command/target-governance-discovery.js +390 -0
  143. package/dist/command/target-governance-discovery.js.map +1 -0
  144. package/dist/command/target-governance-usage.d.ts +22 -0
  145. package/dist/command/target-governance-usage.d.ts.map +1 -0
  146. package/dist/command/target-governance-usage.js +116 -0
  147. package/dist/command/target-governance-usage.js.map +1 -0
  148. package/dist/command/usage.d.ts +2 -0
  149. package/dist/command/usage.d.ts.map +1 -1
  150. package/dist/command/usage.js +43 -25
  151. package/dist/command/usage.js.map +1 -1
  152. package/dist/conversion/mermaid-import.d.ts.map +1 -1
  153. package/dist/conversion/mermaid-import.js +3 -2
  154. package/dist/conversion/mermaid-import.js.map +1 -1
  155. package/dist/conversion/mermaid.d.ts.map +1 -1
  156. package/dist/conversion/mermaid.js +27 -0
  157. package/dist/conversion/mermaid.js.map +1 -1
  158. package/dist/formatter/source-formatter.d.ts +3 -3
  159. package/dist/formatter/source-formatter.d.ts.map +1 -1
  160. package/dist/formatter/source-formatter.js +64 -5
  161. package/dist/formatter/source-formatter.js.map +1 -1
  162. package/dist/formatter/target-source-formatter.d.ts +3 -1
  163. package/dist/formatter/target-source-formatter.d.ts.map +1 -1
  164. package/dist/formatter/target-source-formatter.js +34 -2
  165. package/dist/formatter/target-source-formatter.js.map +1 -1
  166. package/dist/governance/authority.d.ts +25 -0
  167. package/dist/governance/authority.d.ts.map +1 -0
  168. package/dist/governance/authority.js +206 -0
  169. package/dist/governance/authority.js.map +1 -0
  170. package/dist/governance/guidance.d.ts +2 -0
  171. package/dist/governance/guidance.d.ts.map +1 -0
  172. package/dist/governance/guidance.js +2 -0
  173. package/dist/governance/guidance.js.map +1 -0
  174. package/dist/governance/source.d.ts +37 -0
  175. package/dist/governance/source.d.ts.map +1 -0
  176. package/dist/governance/source.js +103 -0
  177. package/dist/governance/source.js.map +1 -0
  178. package/dist/governance/types.d.ts +41 -0
  179. package/dist/governance/types.d.ts.map +1 -0
  180. package/dist/governance/types.js +2 -0
  181. package/dist/governance/types.js.map +1 -0
  182. package/dist/help/actuals-guide.d.ts +10 -0
  183. package/dist/help/actuals-guide.d.ts.map +1 -0
  184. package/dist/help/actuals-guide.js +23 -0
  185. package/dist/help/actuals-guide.js.map +1 -0
  186. package/dist/help/guide.d.ts +8 -5
  187. package/dist/help/guide.d.ts.map +1 -1
  188. package/dist/help/guide.js.map +1 -1
  189. package/dist/help/registry.d.ts.map +1 -1
  190. package/dist/help/registry.js +98 -8
  191. package/dist/help/registry.js.map +1 -1
  192. package/dist/help/target-governance-guide.d.ts +10 -0
  193. package/dist/help/target-governance-guide.d.ts.map +1 -0
  194. package/dist/help/target-governance-guide.js +57 -0
  195. package/dist/help/target-governance-guide.js.map +1 -0
  196. package/dist/history/git-probe.d.ts +52 -0
  197. package/dist/history/git-probe.d.ts.map +1 -0
  198. package/dist/history/git-probe.js +565 -0
  199. package/dist/history/git-probe.js.map +1 -0
  200. package/dist/history/project-history.d.ts +112 -0
  201. package/dist/history/project-history.d.ts.map +1 -0
  202. package/dist/history/project-history.js +786 -0
  203. package/dist/history/project-history.js.map +1 -0
  204. package/dist/history/velocity-observation.d.ts +69 -0
  205. package/dist/history/velocity-observation.d.ts.map +1 -0
  206. package/dist/history/velocity-observation.js +604 -0
  207. package/dist/history/velocity-observation.js.map +1 -0
  208. package/dist/index.d.ts +28 -14
  209. package/dist/index.d.ts.map +1 -1
  210. package/dist/index.js +11 -7
  211. package/dist/index.js.map +1 -1
  212. package/dist/io/safe-write.d.ts +4 -0
  213. package/dist/io/safe-write.d.ts.map +1 -1
  214. package/dist/io/safe-write.js +22 -1
  215. package/dist/io/safe-write.js.map +1 -1
  216. package/dist/io/target-safe-write.d.ts +8 -2
  217. package/dist/io/target-safe-write.d.ts.map +1 -1
  218. package/dist/io/target-safe-write.js +38 -2
  219. package/dist/io/target-safe-write.js.map +1 -1
  220. package/dist/migration/conversion.d.ts +1 -1
  221. package/dist/migration/conversion.d.ts.map +1 -1
  222. package/dist/migration/grammar-boundary.d.ts +1 -1
  223. package/dist/migration/grammar-boundary.d.ts.map +1 -1
  224. package/dist/migration/grammar-boundary.js +15 -4
  225. package/dist/migration/grammar-boundary.js.map +1 -1
  226. package/dist/migration/request.d.ts +5 -5
  227. package/dist/migration/request.d.ts.map +1 -1
  228. package/dist/migration/request.js +15 -2
  229. package/dist/migration/request.js.map +1 -1
  230. package/dist/model/calendar.d.ts +2 -0
  231. package/dist/model/calendar.d.ts.map +1 -1
  232. package/dist/model/calendar.js +19 -0
  233. package/dist/model/calendar.js.map +1 -1
  234. package/dist/model/declaration-fields.d.ts +15 -0
  235. package/dist/model/declaration-fields.d.ts.map +1 -1
  236. package/dist/model/declaration-fields.js +31 -0
  237. package/dist/model/declaration-fields.js.map +1 -1
  238. package/dist/model/exact-person-hours-source.d.ts +2 -0
  239. package/dist/model/exact-person-hours-source.d.ts.map +1 -0
  240. package/dist/model/exact-person-hours-source.js +10 -0
  241. package/dist/model/exact-person-hours-source.js.map +1 -0
  242. package/dist/model/syntax.d.ts +23 -8
  243. package/dist/model/syntax.d.ts.map +1 -1
  244. package/dist/model/syntax.js.map +1 -1
  245. package/dist/mutation/actuals.d.ts +17 -0
  246. package/dist/mutation/actuals.d.ts.map +1 -0
  247. package/dist/mutation/actuals.js +90 -0
  248. package/dist/mutation/actuals.js.map +1 -0
  249. package/dist/mutation/advance.d.ts +17 -0
  250. package/dist/mutation/advance.d.ts.map +1 -1
  251. package/dist/mutation/advance.js +56 -14
  252. package/dist/mutation/advance.js.map +1 -1
  253. package/dist/mutation/entity-editor.d.ts +2 -2
  254. package/dist/mutation/entity-editor.d.ts.map +1 -1
  255. package/dist/mutation/entity-editor.js.map +1 -1
  256. package/dist/mutation/project.d.ts +7 -1
  257. package/dist/mutation/project.d.ts.map +1 -1
  258. package/dist/mutation/project.js +108 -6
  259. package/dist/mutation/project.js.map +1 -1
  260. package/dist/mutation/source.d.ts +3 -3
  261. package/dist/mutation/source.d.ts.map +1 -1
  262. package/dist/mutation/source.js.map +1 -1
  263. package/dist/mutation/target-types.d.ts +18 -1
  264. package/dist/mutation/target-types.d.ts.map +1 -1
  265. package/dist/mutation/types.d.ts +8 -1
  266. package/dist/mutation/types.d.ts.map +1 -1
  267. package/dist/parser/document-parser.d.ts +15 -1
  268. package/dist/parser/document-parser.d.ts.map +1 -1
  269. package/dist/parser/document-parser.js +205 -22
  270. package/dist/parser/document-parser.js.map +1 -1
  271. package/dist/recommendation/override-types.d.ts +2 -2
  272. package/dist/recommendation/override.d.ts +2 -2
  273. package/dist/recommendation/override.d.ts.map +1 -1
  274. package/dist/recommendation/override.js +2 -2
  275. package/dist/recommendation/override.js.map +1 -1
  276. package/dist/semantic/target-validator.d.ts +23 -2
  277. package/dist/semantic/target-validator.d.ts.map +1 -1
  278. package/dist/semantic/target-validator.js +89 -2
  279. package/dist/semantic/target-validator.js.map +1 -1
  280. package/dist/semantic/validator.d.ts +5 -1
  281. package/dist/semantic/validator.d.ts.map +1 -1
  282. package/dist/semantic/validator.js +180 -6
  283. package/dist/semantic/validator.js.map +1 -1
  284. package/dist/version.d.ts +1 -1
  285. package/dist/version.js +1 -1
  286. package/package.json +3 -2
@@ -0,0 +1,786 @@
1
+ import { createHash } from "node:crypto";
2
+ import { reduceTaskLifecycle, } from "../actuals/lifecycle.js";
3
+ import { projectActualsSourceModel, } from "../actuals/source.js";
4
+ import { instantKey } from "../model/calendar-arithmetic.js";
5
+ import { compareStableStrings, sortDiagnostics, } from "../model/diagnostics.js";
6
+ import { add, divide, formatDecimal, multiply, rational, rationalFromDuration, subtract, } from "../model/rational.js";
7
+ import { fieldNamed } from "../model/syntax.js";
8
+ import { projectDeclaredCalendarValue, } from "../model/target-calendar.js";
9
+ import { validateTargetGrammar5Document, } from "../semantic/target-validator.js";
10
+ export const PROJECT_HISTORY_MODEL_VERSION = 1;
11
+ const historyCauseOrder = [
12
+ "no_repository",
13
+ "no_head",
14
+ "unknown_revision",
15
+ "untracked_target",
16
+ "ambiguous_path",
17
+ "shallow_boundary",
18
+ "unsupported_rename",
19
+ "unsupported_source_version",
20
+ "task_identity_replaced",
21
+ "event_payload_changed",
22
+ "duplicate_event_identity",
23
+ "head_changed",
24
+ "target_changed",
25
+ ];
26
+ const actualsCauseOrder = [
27
+ "missing_start",
28
+ "missing_finish",
29
+ "open_suspension",
30
+ "missing_baseline",
31
+ "active_time_absent",
32
+ "effort_absent",
33
+ "history_incomplete",
34
+ "history_unavailable",
35
+ ];
36
+ const eventKindOrder = Object.freeze({
37
+ start: 0,
38
+ suspend: 1,
39
+ resume: 2,
40
+ finish: 3,
41
+ });
42
+ const identifierPattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
43
+ function quantity(value, unit) {
44
+ return Object.freeze({
45
+ numerator: value.numerator.toString(),
46
+ denominator: value.denominator.toString(),
47
+ unit,
48
+ display: formatDecimal(value, 6),
49
+ });
50
+ }
51
+ function durationUnit(suffix) {
52
+ return suffix === "d" ? "day" : suffix === "h" ? "hour" : "point";
53
+ }
54
+ function exactDuration(declaration, name) {
55
+ const field = fieldNamed(declaration, name);
56
+ if (field === undefined ||
57
+ field.value === null ||
58
+ typeof field.value !== "object" ||
59
+ !("suffix" in field.value)) {
60
+ throw new Error(`validated task ${declaration.id} is missing ${name}`);
61
+ }
62
+ return field.value;
63
+ }
64
+ function exactEstimate(declaration, name) {
65
+ const estimate = fieldNamed(declaration, "estimate");
66
+ const child = estimate?.children?.find(({ name: childName }) => childName === name);
67
+ if (child === undefined ||
68
+ child.value === null ||
69
+ typeof child.value !== "object" ||
70
+ !("suffix" in child.value)) {
71
+ throw new Error(`validated task ${declaration.id} estimate is missing ${name}`);
72
+ }
73
+ return child.value;
74
+ }
75
+ function taskPlannedValue(declaration) {
76
+ const estimate = fieldNamed(declaration, "estimate");
77
+ if (estimate === undefined) {
78
+ const duration = exactDuration(declaration, "duration");
79
+ return quantity(rationalFromDuration(duration), durationUnit(duration.suffix));
80
+ }
81
+ const optimistic = exactEstimate(declaration, "optimistic");
82
+ const mostLikely = exactEstimate(declaration, "most_likely");
83
+ const pessimistic = exactEstimate(declaration, "pessimistic");
84
+ const expected = divide(add(add(rationalFromDuration(optimistic), multiply(rational(4n), rationalFromDuration(mostLikely))), rationalFromDuration(pessimistic)), rational(6n));
85
+ return quantity(expected, durationUnit(optimistic.suffix));
86
+ }
87
+ function taskState(declaration) {
88
+ return (fieldNamed(declaration, "status")?.value ?? "planned");
89
+ }
90
+ function taskIdentity(declaration) {
91
+ return `${declaration.from ?? ""}\u0000${declaration.to ?? ""}`;
92
+ }
93
+ function eventQuantity(value, unit) {
94
+ return value === null ? null : quantity(value.value, unit);
95
+ }
96
+ function eventProjection(event) {
97
+ const occurredAt = projectDeclaredCalendarValue(event.occurredAt);
98
+ if (occurredAt === null) {
99
+ throw new Error("validated work event lost its calendar projection");
100
+ }
101
+ return Object.freeze({
102
+ modelVersion: 1,
103
+ id: event.id,
104
+ taskId: event.taskId,
105
+ kind: event.kind,
106
+ occurredAt,
107
+ plannedValue: event.plannedValue === null
108
+ ? null
109
+ : quantity(event.plannedValue.value, event.plannedValue.unit),
110
+ activeTime: eventQuantity(event.activeTime, "hour"),
111
+ effort: eventQuantity(event.effort, "person_hour"),
112
+ reason: event.reason,
113
+ });
114
+ }
115
+ function payloadDigest(projection) {
116
+ const payload = JSON.stringify({
117
+ model_version: projection.modelVersion,
118
+ id: projection.id,
119
+ task_id: projection.taskId,
120
+ kind: projection.kind,
121
+ occurred_at: projection.occurredAt,
122
+ planned_value: projection.plannedValue,
123
+ active_time: projection.activeTime,
124
+ effort: projection.effort,
125
+ reason: projection.reason,
126
+ });
127
+ return `sha256:${createHash("sha256").update(payload, "utf8").digest("hex")}`;
128
+ }
129
+ function historyCause(cause, fields = {}) {
130
+ return Object.freeze({
131
+ cause,
132
+ commitId: fields.commitId ?? null,
133
+ taskId: fields.taskId ?? null,
134
+ eventId: fields.eventId ?? null,
135
+ });
136
+ }
137
+ function actualsCause(cause, taskId, fields = {}) {
138
+ return Object.freeze({
139
+ cause,
140
+ commitId: fields.commitId ?? null,
141
+ taskId,
142
+ eventId: fields.eventId ?? null,
143
+ });
144
+ }
145
+ function diagnostic(cause) {
146
+ const unavailable = new Set([
147
+ "no_repository",
148
+ "no_head",
149
+ "unknown_revision",
150
+ "untracked_target",
151
+ "ambiguous_path",
152
+ ]);
153
+ const race = cause.cause === "head_changed" ||
154
+ cause.cause === "target_changed";
155
+ const conflict = cause.cause === "event_payload_changed" ||
156
+ cause.cause === "duplicate_event_identity";
157
+ const code = race
158
+ ? "PTHIS-104"
159
+ : conflict
160
+ ? "PTHIS-103"
161
+ : unavailable.has(cause.cause)
162
+ ? "PTHIS-101"
163
+ : "PTHIS-102";
164
+ const severity = code === "PTHIS-102" ? "warning" : "error";
165
+ const message = code === "PTHIS-104"
166
+ ? "repository snapshot changed during history inspection"
167
+ : code === "PTHIS-103"
168
+ ? "conflicting work-event history"
169
+ : code === "PTHIS-101"
170
+ ? "project history is unavailable"
171
+ : "project history is incomplete";
172
+ return Object.freeze({
173
+ code,
174
+ severity,
175
+ message,
176
+ ...(cause.eventId === null && cause.taskId === null
177
+ ? {}
178
+ : { entityId: cause.eventId ?? cause.taskId }),
179
+ helpTopic: "project.history",
180
+ data: Object.freeze({
181
+ cause: cause.cause,
182
+ commit_id: cause.commitId,
183
+ task_id: cause.taskId,
184
+ event_id: cause.eventId,
185
+ }),
186
+ });
187
+ }
188
+ function compareNullable(left, right) {
189
+ return compareStableStrings(left ?? "", right ?? "");
190
+ }
191
+ function sortedHistoryCauses(causes) {
192
+ const order = new Map(historyCauseOrder.map((cause, index) => [cause, index]));
193
+ const seen = new Set();
194
+ return Object.freeze([...causes]
195
+ .sort((left, right) => order.get(left.cause) - order.get(right.cause) ||
196
+ compareNullable(left.commitId, right.commitId) ||
197
+ compareNullable(left.taskId, right.taskId) ||
198
+ compareNullable(left.eventId, right.eventId))
199
+ .filter((cause) => {
200
+ const key = JSON.stringify(cause);
201
+ if (seen.has(key))
202
+ return false;
203
+ seen.add(key);
204
+ return true;
205
+ }));
206
+ }
207
+ function sortedActualsCauses(causes) {
208
+ const order = new Map(actualsCauseOrder.map((cause, index) => [cause, index]));
209
+ const seen = new Set();
210
+ return Object.freeze([...causes]
211
+ .sort((left, right) => order.get(left.cause) - order.get(right.cause) ||
212
+ compareNullable(left.commitId, right.commitId) ||
213
+ compareNullable(left.taskId, right.taskId) ||
214
+ compareNullable(left.eventId, right.eventId))
215
+ .filter((cause) => {
216
+ const key = JSON.stringify(cause);
217
+ if (seen.has(key))
218
+ return false;
219
+ seen.add(key);
220
+ return true;
221
+ }));
222
+ }
223
+ function fromProbeAvailability(value) {
224
+ return historyCause(value.cause, { commitId: value.commitId });
225
+ }
226
+ function unsupportedSnapshot(snapshot) {
227
+ return historyCause("unsupported_source_version", {
228
+ commitId: snapshot.commitId,
229
+ });
230
+ }
231
+ function decodeSnapshot(snapshot) {
232
+ if (snapshot.source === null)
233
+ return null;
234
+ try {
235
+ return new TextDecoder("utf-8", { fatal: true }).decode(snapshot.source);
236
+ }
237
+ catch {
238
+ return null;
239
+ }
240
+ }
241
+ function parseSnapshot(snapshot, capability) {
242
+ if (snapshot.source === null) {
243
+ return {
244
+ parsed: {
245
+ snapshot,
246
+ documentId: null,
247
+ grammarVersion: null,
248
+ tasks: new Map(),
249
+ events: Object.freeze([]),
250
+ },
251
+ causes: Object.freeze([]),
252
+ };
253
+ }
254
+ const text = decodeSnapshot(snapshot);
255
+ if (text === null) {
256
+ return {
257
+ parsed: null,
258
+ causes: Object.freeze([unsupportedSnapshot(snapshot)]),
259
+ };
260
+ }
261
+ const checked = validateTargetGrammar5Document(text, capability);
262
+ const project = checked.document.declarations.find(({ kind }) => kind === "project");
263
+ const declaredVersion = project === undefined
264
+ ? null
265
+ : fieldNamed(project, "version")?.value ?? 1;
266
+ const duplicateIds = checked.document.declarations
267
+ .filter(({ kind }) => kind === "work_event")
268
+ .map(({ id }) => id)
269
+ .filter((id, index, ids) => ids.indexOf(id) !== index);
270
+ if (duplicateIds.length > 0) {
271
+ return {
272
+ parsed: null,
273
+ causes: Object.freeze([...new Set(duplicateIds)]
274
+ .sort(compareStableStrings)
275
+ .map((eventId) => historyCause("duplicate_event_identity", {
276
+ commitId: snapshot.commitId,
277
+ eventId,
278
+ }))),
279
+ };
280
+ }
281
+ if (!checked.ok ||
282
+ checked.validatedDocument === null ||
283
+ (declaredVersion !== 1 &&
284
+ declaredVersion !== 2 &&
285
+ declaredVersion !== 3 &&
286
+ declaredVersion !== 4 &&
287
+ declaredVersion !== 5)) {
288
+ return {
289
+ parsed: null,
290
+ causes: Object.freeze([unsupportedSnapshot(snapshot)]),
291
+ };
292
+ }
293
+ const tasks = new Map();
294
+ for (const declaration of checked.validatedDocument.document.declarations) {
295
+ if (declaration.kind !== "task")
296
+ continue;
297
+ tasks.set(declaration.id, {
298
+ state: taskState(declaration),
299
+ identity: taskIdentity(declaration),
300
+ plannedValue: taskPlannedValue(declaration),
301
+ });
302
+ }
303
+ return {
304
+ parsed: {
305
+ snapshot,
306
+ documentId: checked.documentId,
307
+ grammarVersion: checked.validatedDocument.grammarVersion,
308
+ tasks,
309
+ events: projectActualsSourceModel(checked.validatedDocument).events,
310
+ },
311
+ causes: Object.freeze([]),
312
+ };
313
+ }
314
+ function compareEvents(left, right) {
315
+ const instantOrder = (instantKey(left.occurredAt).numerator *
316
+ instantKey(right.occurredAt).denominator -
317
+ instantKey(right.occurredAt).numerator *
318
+ instantKey(left.occurredAt).denominator);
319
+ if (instantOrder !== 0n)
320
+ return instantOrder < 0n ? -1 : 1;
321
+ return eventKindOrder[left.kind] - eventKindOrder[right.kind] ||
322
+ compareStableStrings(left.id, right.id);
323
+ }
324
+ function hoursBetween(start, finish) {
325
+ return quantity(divide(subtract(instantKey(finish.occurredAt), instantKey(start.occurredAt)), rational(3600n)), "hour");
326
+ }
327
+ function suspensionIntervals(events) {
328
+ const ordered = [...events].sort(compareEvents);
329
+ const result = [];
330
+ let suspended = null;
331
+ for (const event of ordered) {
332
+ if (event.kind === "suspend") {
333
+ suspended = event;
334
+ continue;
335
+ }
336
+ if (suspended !== null &&
337
+ (event.kind === "resume" || event.kind === "finish")) {
338
+ const start = projectDeclaredCalendarValue(suspended.occurredAt);
339
+ const finish = projectDeclaredCalendarValue(event.occurredAt);
340
+ if (start === null || finish === null) {
341
+ throw new Error("validated suspension interval lost calendar values");
342
+ }
343
+ result.push(Object.freeze({
344
+ suspendEventId: suspended.id,
345
+ resumeEventId: event.kind === "resume" ? event.id : null,
346
+ start,
347
+ finish,
348
+ duration: hoursBetween(suspended, event),
349
+ }));
350
+ suspended = null;
351
+ }
352
+ }
353
+ if (suspended !== null) {
354
+ const start = projectDeclaredCalendarValue(suspended.occurredAt);
355
+ if (start === null) {
356
+ throw new Error("validated open suspension lost its calendar value");
357
+ }
358
+ result.push(Object.freeze({
359
+ suspendEventId: suspended.id,
360
+ resumeEventId: null,
361
+ start,
362
+ finish: null,
363
+ duration: null,
364
+ }));
365
+ }
366
+ return Object.freeze(result);
367
+ }
368
+ function summaryForTask(taskId, eventEntries, transitions, plannedByCommit, historyStatus, identityReplaced) {
369
+ if (identityReplaced) {
370
+ return Object.freeze({
371
+ taskId,
372
+ coverage: "unavailable",
373
+ eventIds: Object.freeze([]),
374
+ firstStart: null,
375
+ lastFinish: null,
376
+ suspensionIntervals: Object.freeze([]),
377
+ cycleTime: null,
378
+ derivedActiveTime: null,
379
+ explicitActiveTime: null,
380
+ effort: null,
381
+ plannedValue: null,
382
+ baselineSource: null,
383
+ baselineEventId: null,
384
+ baselineCommitId: null,
385
+ qualifiers: Object.freeze(["history_incomplete"]),
386
+ unavailableCauses: Object.freeze([
387
+ actualsCause("history_incomplete", taskId),
388
+ ]),
389
+ });
390
+ }
391
+ const entries = eventEntries
392
+ .filter(({ event }) => event.taskId === taskId)
393
+ .sort((left, right) => compareEvents(left.event, right.event));
394
+ const events = entries.map(({ event }) => event);
395
+ const reduction = reduceTaskLifecycle(events);
396
+ const firstStartEntry = entries.find(({ event }) => event.kind === "start") ??
397
+ null;
398
+ const finishEntries = entries.filter(({ event }) => event.kind === "finish");
399
+ const lastFinishEntry = finishEntries.at(-1) ?? null;
400
+ const firstStart = firstStartEntry === null
401
+ ? null
402
+ : projectDeclaredCalendarValue(firstStartEntry.event.occurredAt);
403
+ const lastFinish = lastFinishEntry === null
404
+ ? null
405
+ : projectDeclaredCalendarValue(lastFinishEntry.event.occurredAt);
406
+ const complete = reduction.ok && reduction.coverage === "complete";
407
+ const cycleTime = complete && firstStartEntry !== null && lastFinishEntry !== null
408
+ ? hoursBetween(firstStartEntry.event, lastFinishEntry.event)
409
+ : null;
410
+ const finishEvent = lastFinishEntry?.event ?? null;
411
+ const explicitActiveTime = finishEvent === null
412
+ ? null
413
+ : eventQuantity(finishEvent.activeTime, "hour");
414
+ const effort = finishEvent === null
415
+ ? null
416
+ : eventQuantity(finishEvent.effort, "person_hour");
417
+ let plannedValue = null;
418
+ let baselineSource = null;
419
+ let baselineEventId = null;
420
+ let baselineCommitId = null;
421
+ if (firstStartEntry !== null &&
422
+ firstStartEntry.event.plannedValue !== null) {
423
+ plannedValue = quantity(firstStartEntry.event.plannedValue.value, firstStartEntry.event.plannedValue.unit);
424
+ baselineSource = "start_baseline";
425
+ baselineEventId = firstStartEntry.event.id;
426
+ baselineCommitId = firstStartEntry.firstSeenCommitId;
427
+ }
428
+ else if (reduction.ok &&
429
+ reduction.coverage === "finish_only" &&
430
+ lastFinishEntry !== null) {
431
+ plannedValue =
432
+ plannedByCommit.get(lastFinishEntry.lastSeenCommitId)?.get(taskId) ??
433
+ null;
434
+ baselineSource = plannedValue === null ? null : "finish_snapshot";
435
+ baselineEventId = plannedValue === null ? null : lastFinishEntry.event.id;
436
+ baselineCommitId =
437
+ plannedValue === null ? null : lastFinishEntry.lastSeenCommitId;
438
+ }
439
+ else if (reduction.ok &&
440
+ reduction.coverage === "unrecorded") {
441
+ const recordedFinish = transitions
442
+ .filter((transition) => transition.taskId === taskId &&
443
+ transition.toState === "done")
444
+ .at(-1);
445
+ if (recordedFinish !== undefined) {
446
+ plannedValue =
447
+ plannedByCommit.get(recordedFinish.commitId)?.get(taskId) ?? null;
448
+ baselineSource = plannedValue === null ? null : "finish_snapshot";
449
+ baselineCommitId =
450
+ plannedValue === null ? null : recordedFinish.commitId;
451
+ }
452
+ }
453
+ const causes = [];
454
+ const coverage = reduction.ok ? reduction.coverage : "unavailable";
455
+ if (firstStartEntry === null) {
456
+ causes.push(actualsCause("missing_start", taskId));
457
+ }
458
+ if (lastFinishEntry === null) {
459
+ causes.push(actualsCause("missing_finish", taskId));
460
+ }
461
+ if (reduction.ok &&
462
+ reduction.coverage === "open" &&
463
+ reduction.state === "suspended") {
464
+ causes.push(actualsCause("open_suspension", taskId));
465
+ }
466
+ if (plannedValue === null) {
467
+ causes.push(actualsCause("missing_baseline", taskId));
468
+ }
469
+ if (explicitActiveTime === null) {
470
+ causes.push(actualsCause("active_time_absent", taskId));
471
+ }
472
+ if (effort === null) {
473
+ causes.push(actualsCause("effort_absent", taskId));
474
+ }
475
+ if (historyStatus === "incomplete") {
476
+ causes.push(actualsCause("history_incomplete", taskId));
477
+ }
478
+ else if (historyStatus === "unavailable" || !reduction.ok) {
479
+ causes.push(actualsCause("history_unavailable", taskId));
480
+ }
481
+ const qualifiers = [
482
+ ...(baselineSource === "finish_snapshot" ? ["finish_snapshot"] : []),
483
+ ...(historyStatus === "incomplete" ? ["history_incomplete"] : []),
484
+ ...(historyStatus === "unavailable" ? ["history_unavailable"] : []),
485
+ ];
486
+ return Object.freeze({
487
+ taskId,
488
+ coverage,
489
+ eventIds: Object.freeze(entries.map(({ event }) => event.id)),
490
+ firstStart,
491
+ lastFinish,
492
+ suspensionIntervals: suspensionIntervals(events),
493
+ cycleTime,
494
+ derivedActiveTime: reduction.ok && reduction.derivedActiveTime !== null
495
+ ? quantity(reduction.derivedActiveTime, "hour")
496
+ : null,
497
+ explicitActiveTime,
498
+ effort,
499
+ plannedValue,
500
+ baselineSource,
501
+ baselineEventId,
502
+ baselineCommitId,
503
+ qualifiers: Object.freeze(qualifiers),
504
+ unavailableCauses: sortedActualsCauses(causes),
505
+ });
506
+ }
507
+ function statusForCauses(probeStatus, causes) {
508
+ if (probeStatus === "unavailable" ||
509
+ causes.some(({ cause }) => cause === "no_repository" ||
510
+ cause === "no_head" ||
511
+ cause === "unknown_revision" ||
512
+ cause === "untracked_target" ||
513
+ cause === "ambiguous_path" ||
514
+ cause === "event_payload_changed" ||
515
+ cause === "duplicate_event_identity" ||
516
+ cause === "head_changed" ||
517
+ cause === "target_changed")) {
518
+ return "unavailable";
519
+ }
520
+ return probeStatus === "incomplete" || causes.length > 0
521
+ ? "incomplete"
522
+ : "complete";
523
+ }
524
+ function emptyResult(probe, causes) {
525
+ const sortedCauses = sortedHistoryCauses(causes);
526
+ return Object.freeze({
527
+ ok: false,
528
+ modelVersion: PROJECT_HISTORY_MODEL_VERSION,
529
+ documentId: null,
530
+ grammarVersion: null,
531
+ history: Object.freeze({
532
+ id: "perttool.project-history",
533
+ version: 1,
534
+ status: "unavailable",
535
+ traversal: "first_parent",
536
+ repositorySnapshotId: probe.repositorySnapshotId,
537
+ repositoryRelativePath: probe.repositoryRelativePath,
538
+ requestedRevision: probe.requestedRevision,
539
+ resolvedRevision: probe.resolvedRevision,
540
+ sourceDigest: probe.selectedSourceDigest,
541
+ inspectedCommitIds: Object.freeze([...probe.inspectedCommitIds]),
542
+ unavailableCauses: sortedCauses,
543
+ }),
544
+ events: Object.freeze([]),
545
+ gitRecordedTransitions: Object.freeze([]),
546
+ tasks: Object.freeze([]),
547
+ diagnostics: Object.freeze(sortDiagnostics(sortedCauses.map(diagnostic))),
548
+ });
549
+ }
550
+ function invalidRequestResult(probe, cause, taskId) {
551
+ return Object.freeze({
552
+ ok: false,
553
+ modelVersion: PROJECT_HISTORY_MODEL_VERSION,
554
+ documentId: null,
555
+ grammarVersion: null,
556
+ history: Object.freeze({
557
+ id: "perttool.project-history",
558
+ version: 1,
559
+ status: "unavailable",
560
+ traversal: "first_parent",
561
+ repositorySnapshotId: probe.repositorySnapshotId,
562
+ repositoryRelativePath: probe.repositoryRelativePath,
563
+ requestedRevision: probe.requestedRevision,
564
+ resolvedRevision: probe.resolvedRevision,
565
+ sourceDigest: probe.selectedSourceDigest,
566
+ inspectedCommitIds: Object.freeze([...probe.inspectedCommitIds]),
567
+ unavailableCauses: Object.freeze([]),
568
+ }),
569
+ events: Object.freeze([]),
570
+ gitRecordedTransitions: Object.freeze([]),
571
+ tasks: Object.freeze([]),
572
+ diagnostics: Object.freeze([
573
+ Object.freeze({
574
+ code: "PTCLI-001",
575
+ severity: "error",
576
+ message: "invalid project history task selection",
577
+ ...(taskId === null ? {} : { entityId: taskId }),
578
+ helpTopic: "project.history",
579
+ data: Object.freeze({
580
+ usage_kind: "invalid_option_value",
581
+ cause,
582
+ task_id: taskId,
583
+ }),
584
+ }),
585
+ ]),
586
+ });
587
+ }
588
+ export function inspectProjectHistory(probe, request, capability) {
589
+ const adapterCauses = probe.availability.map(fromProbeAvailability);
590
+ if (probe.status === "unavailable") {
591
+ return emptyResult(probe, adapterCauses);
592
+ }
593
+ const selectedTaskIds = request.taskIds === undefined
594
+ ? null
595
+ : [...request.taskIds].sort(compareStableStrings);
596
+ if (selectedTaskIds !== null) {
597
+ const invalidTaskId = selectedTaskIds.find((taskId) => !identifierPattern.test(taskId));
598
+ if (invalidTaskId !== undefined) {
599
+ return invalidRequestResult(probe, "invalid_task_id", invalidTaskId);
600
+ }
601
+ const duplicateTaskId = selectedTaskIds.find((taskId, index) => selectedTaskIds.indexOf(taskId) !== index);
602
+ if (duplicateTaskId !== undefined) {
603
+ return invalidRequestResult(probe, "duplicate_task", duplicateTaskId);
604
+ }
605
+ }
606
+ const causes = [...adapterCauses];
607
+ const transitions = [];
608
+ const eventMap = new Map();
609
+ const conflictedEventIds = new Set();
610
+ const eventfulTaskIds = new Set();
611
+ const taskIdentities = new Map();
612
+ const replacedTaskIds = new Set();
613
+ const allTaskIds = new Set();
614
+ const plannedByCommit = new Map();
615
+ let previousTasks = new Map();
616
+ let previousEventIds = new Set();
617
+ let previousSourceDigest = null;
618
+ let transitionContinuityKnown = !adapterCauses.some(({ cause }) => cause === "shallow_boundary" || cause === "unsupported_rename");
619
+ let documentId = null;
620
+ let grammarVersion = null;
621
+ for (const snapshot of probe.snapshots) {
622
+ const parsedSnapshot = parseSnapshot(snapshot, capability);
623
+ causes.push(...parsedSnapshot.causes);
624
+ if (parsedSnapshot.parsed === null) {
625
+ transitionContinuityKnown = false;
626
+ previousEventIds = new Set();
627
+ continue;
628
+ }
629
+ const parsed = parsedSnapshot.parsed;
630
+ if (parsed.documentId !== null)
631
+ documentId = parsed.documentId;
632
+ if (parsed.grammarVersion !== null) {
633
+ grammarVersion = parsed.grammarVersion;
634
+ }
635
+ plannedByCommit.set(snapshot.commitId, new Map([...parsed.tasks].map(([taskId, task]) => [
636
+ taskId,
637
+ task.plannedValue,
638
+ ])));
639
+ const replacedInSnapshot = new Set();
640
+ for (const [taskId, task] of parsed.tasks) {
641
+ allTaskIds.add(taskId);
642
+ const priorIdentity = taskIdentities.get(taskId);
643
+ if (priorIdentity !== undefined && priorIdentity !== task.identity) {
644
+ causes.push(historyCause("task_identity_replaced", {
645
+ commitId: snapshot.commitId,
646
+ taskId,
647
+ }));
648
+ replacedTaskIds.add(taskId);
649
+ replacedInSnapshot.add(taskId);
650
+ taskIdentities.set(taskId, task.identity);
651
+ }
652
+ else if (priorIdentity === undefined) {
653
+ taskIdentities.set(taskId, task.identity);
654
+ }
655
+ }
656
+ const currentEventsById = new Map(parsed.events.map((event) => [event.id, event]));
657
+ for (const eventId of previousEventIds) {
658
+ if (currentEventsById.has(eventId))
659
+ continue;
660
+ const accumulator = eventMap.get(eventId);
661
+ if (accumulator !== undefined &&
662
+ accumulator.removalCommitId === null) {
663
+ accumulator.removalCommitId = snapshot.commitId;
664
+ }
665
+ }
666
+ for (const event of parsed.events) {
667
+ allTaskIds.add(event.taskId);
668
+ eventfulTaskIds.add(event.taskId);
669
+ const projection = eventProjection(event);
670
+ const digest = payloadDigest(projection);
671
+ const existing = eventMap.get(event.id);
672
+ if (existing === undefined) {
673
+ eventMap.set(event.id, {
674
+ event,
675
+ projection,
676
+ payloadDigest: digest,
677
+ firstSeenCommitId: snapshot.commitId,
678
+ lastSeenCommitId: snapshot.commitId,
679
+ removalCommitId: null,
680
+ });
681
+ }
682
+ else if (existing.payloadDigest !== digest ||
683
+ existing.removalCommitId !== null) {
684
+ conflictedEventIds.add(event.id);
685
+ const cause = existing.removalCommitId === null
686
+ ? "event_payload_changed"
687
+ : "duplicate_event_identity";
688
+ causes.push(historyCause(cause, {
689
+ commitId: existing.firstSeenCommitId,
690
+ taskId: existing.event.taskId,
691
+ eventId: event.id,
692
+ }), historyCause(cause, {
693
+ commitId: snapshot.commitId,
694
+ taskId: event.taskId,
695
+ eventId: event.id,
696
+ }));
697
+ }
698
+ else {
699
+ existing.lastSeenCommitId = snapshot.commitId;
700
+ }
701
+ }
702
+ const transitionTaskIds = new Set([
703
+ ...previousTasks.keys(),
704
+ ...parsed.tasks.keys(),
705
+ ]);
706
+ for (const taskId of [...transitionTaskIds].sort(compareStableStrings)) {
707
+ const fromState = previousTasks.get(taskId)?.state ?? "absent";
708
+ const toState = parsed.tasks.get(taskId)?.state ?? "absent";
709
+ if (!transitionContinuityKnown ||
710
+ fromState === toState ||
711
+ replacedInSnapshot.has(taskId) ||
712
+ eventfulTaskIds.has(taskId)) {
713
+ continue;
714
+ }
715
+ const recordedAt = snapshot.recordedAt === null
716
+ ? null
717
+ : projectDeclaredCalendarValue(snapshot.recordedAt);
718
+ transitions.push(Object.freeze({
719
+ taskId,
720
+ fromState,
721
+ toState,
722
+ commitId: snapshot.commitId,
723
+ recordedAt,
724
+ sourceDigest: snapshot.sourceDigest ?? previousSourceDigest ?? "",
725
+ evidenceClass: "git_recorded_transition",
726
+ }));
727
+ }
728
+ previousTasks = new Map(parsed.tasks);
729
+ previousEventIds = new Set(currentEventsById.keys());
730
+ previousSourceDigest = snapshot.sourceDigest;
731
+ transitionContinuityKnown = true;
732
+ }
733
+ const sortedCauses = sortedHistoryCauses(causes);
734
+ const status = statusForCauses(probe.status, sortedCauses);
735
+ if (status === "unavailable") {
736
+ return emptyResult(probe, sortedCauses);
737
+ }
738
+ const eventEntries = [...eventMap]
739
+ .filter(([eventId]) => !conflictedEventIds.has(eventId))
740
+ .map(([, value]) => value)
741
+ .sort((left, right) => compareStableStrings(left.event.id, right.event.id));
742
+ const historyEvents = Object.freeze(eventEntries.map((entry) => Object.freeze({
743
+ event: entry.projection,
744
+ evidenceClass: "declared_actual",
745
+ firstSeenCommitId: entry.firstSeenCommitId,
746
+ lastSeenCommitId: entry.lastSeenCommitId,
747
+ removalCommitId: entry.removalCommitId,
748
+ payloadDigest: entry.payloadDigest,
749
+ })));
750
+ const summaryTaskIds = selectedTaskIds ??
751
+ [...allTaskIds].sort(compareStableStrings);
752
+ const tasks = Object.freeze(summaryTaskIds.map((taskId) => summaryForTask(taskId, eventEntries, transitions, plannedByCommit, status, replacedTaskIds.has(taskId))));
753
+ const selected = selectedTaskIds === null
754
+ ? () => true
755
+ : (taskId) => selectedTaskIds.includes(taskId);
756
+ const filteredEvents = Object.freeze(historyEvents.filter(({ event }) => selected(event.taskId)));
757
+ const filteredTransitions = Object.freeze(transitions
758
+ .filter(({ taskId }) => selected(taskId))
759
+ .sort((left, right) => probe.inspectedCommitIds.indexOf(left.commitId) -
760
+ probe.inspectedCommitIds.indexOf(right.commitId) ||
761
+ compareStableStrings(left.taskId, right.taskId)));
762
+ return Object.freeze({
763
+ ok: true,
764
+ modelVersion: PROJECT_HISTORY_MODEL_VERSION,
765
+ documentId,
766
+ grammarVersion,
767
+ history: Object.freeze({
768
+ id: "perttool.project-history",
769
+ version: 1,
770
+ status,
771
+ traversal: "first_parent",
772
+ repositorySnapshotId: probe.repositorySnapshotId,
773
+ repositoryRelativePath: probe.repositoryRelativePath,
774
+ requestedRevision: probe.requestedRevision,
775
+ resolvedRevision: probe.resolvedRevision,
776
+ sourceDigest: probe.selectedSourceDigest,
777
+ inspectedCommitIds: Object.freeze([...probe.inspectedCommitIds]),
778
+ unavailableCauses: sortedCauses,
779
+ }),
780
+ events: filteredEvents,
781
+ gitRecordedTransitions: filteredTransitions,
782
+ tasks,
783
+ diagnostics: Object.freeze(sortDiagnostics(sortedCauses.map(diagnostic))),
784
+ });
785
+ }
786
+ //# sourceMappingURL=project-history.js.map