@slicedearth/whoisleuth-cli 2.3.0 → 2.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 (264) hide show
  1. package/NOTICE +6 -0
  2. package/README.md +19 -15
  3. package/bin/whoisleuth.mjs +8 -1
  4. package/cli/archive-content-digest.mjs +14 -0
  5. package/cli/archive-inspect.mjs +20 -53
  6. package/cli/arguments.mjs +68 -6
  7. package/cli/artifact-validation/case-response.mjs +47 -41
  8. package/cli/artifact-validation/investigation-capsule.mjs +52 -31
  9. package/cli/artifact-validation/lookup-evidence.mjs +12 -6
  10. package/cli/artifact-validation/signed-review.mjs +83 -61
  11. package/cli/artifact-validation/structure-primitives.mjs +1 -105
  12. package/cli/artifact-verify.mjs +90 -24
  13. package/cli/assurance-command-runner.mjs +39 -4
  14. package/cli/bagit-folder.mjs +57 -0
  15. package/cli/bagit-review.mjs +23 -0
  16. package/cli/bounded-json.mjs +1 -1
  17. package/cli/bulk-command-runner.mjs +2 -0
  18. package/cli/bulk-output.mjs +29 -3
  19. package/cli/case-command.mjs +195 -0
  20. package/cli/case-pack.mjs +39 -31
  21. package/cli/command-argument-grammar.mjs +4 -0
  22. package/cli/command-reference.mjs +145 -75
  23. package/cli/completion.mjs +298 -79
  24. package/cli/config-profile.mjs +7 -5
  25. package/cli/ct-event-intake.mjs +12 -12
  26. package/cli/discover.mjs +4 -3
  27. package/cli/discovery-observation-snapshot.mjs +44 -16
  28. package/cli/discovery-scan-command-runner.mjs +2 -0
  29. package/cli/discovery-scan.mjs +7 -4
  30. package/cli/discovery-workflow.mjs +1 -1
  31. package/cli/domain-control-monitor.mjs +50 -20
  32. package/cli/domain-control-observations.mjs +149 -102
  33. package/cli/errors.mjs +33 -1
  34. package/cli/evidence-command-runner.mjs +16 -5
  35. package/cli/evidence-signing.mjs +2 -2
  36. package/cli/external-observation-mapping.mjs +7 -8
  37. package/cli/formatters/terminal-lookup.mjs +18 -46
  38. package/cli/formatters/terminal-shared.mjs +5 -3
  39. package/cli/formatters/terminal-technology.mjs +114 -0
  40. package/cli/inline-command-families.mjs +1 -0
  41. package/cli/interactive-launcher.mjs +2 -160
  42. package/cli/interchange-report.mjs +4 -8
  43. package/cli/investigation-artifacts.mjs +176 -0
  44. package/cli/investigation-checkpoint.mjs +10 -0
  45. package/cli/investigation-folder.mjs +114 -0
  46. package/cli/investigation-manifest.mjs +1 -112
  47. package/cli/investigation-package-review.mjs +103 -0
  48. package/cli/investigation-plan.mjs +32 -19
  49. package/cli/investigation-run.mjs +188 -33
  50. package/cli/local-document-checkpoint.mjs +170 -0
  51. package/cli/lookup-browser.mjs +5 -12
  52. package/cli/lookup-command-runner.mjs +17 -6
  53. package/cli/lookup-diff.mjs +37 -3
  54. package/cli/lookup-plan.mjs +11 -5
  55. package/cli/lookup-reconcile.mjs +10 -8
  56. package/cli/mail-review.mjs +66 -14
  57. package/cli/manual.mjs +1 -0
  58. package/cli/network-command-runner.mjs +2 -0
  59. package/cli/output-file.mjs +31 -5
  60. package/cli/registry-doctor.mjs +3 -1
  61. package/cli/registry-support.mjs +1 -1
  62. package/cli/retained-artifact-diff.mjs +2 -2
  63. package/cli/review-command-runner.mjs +45 -0
  64. package/cli/runner.mjs +55 -6
  65. package/cli/sharing-review.mjs +21 -34
  66. package/cli/signer-trust.mjs +104 -0
  67. package/cli/source-reliability.mjs +74 -83
  68. package/cli/terminal-input.mjs +168 -0
  69. package/cli/workflow-command-runner.mjs +68 -13
  70. package/docs/cli-reference.md +76 -30
  71. package/docs/cli.md +289 -25
  72. package/lib/abort.mjs +4 -1
  73. package/lib/availability.mjs +161 -112
  74. package/lib/bounded-file.mjs +3 -1
  75. package/lib/bounded-html-document.mjs +125 -0
  76. package/lib/bounded-json.mjs +43 -3
  77. package/lib/browser-library-profile.mjs +42 -51
  78. package/lib/browser-library-worker.mjs +112 -0
  79. package/lib/client-behavior-profile.mjs +3 -3
  80. package/lib/ct-search.mjs +11 -4
  81. package/lib/dns-delegation-health.mjs +24 -14
  82. package/lib/dns-inheritance-review.mjs +217 -0
  83. package/lib/dns-intelligence.mjs +36 -23
  84. package/lib/dns-mx.mjs +1 -1
  85. package/lib/dnssec-evidence-validation.mjs +11 -3
  86. package/lib/domain-change-packet.mjs +7 -2
  87. package/lib/domain-change-review.mjs +14 -8
  88. package/lib/domain-control-flight-recorder.mjs +219 -118
  89. package/lib/domain-control-manifest.mjs +117 -57
  90. package/lib/domain-posture-analysis.mjs +20 -7
  91. package/lib/domain-posture-parsers.mjs +88 -6
  92. package/lib/domain-posture.mjs +113 -26
  93. package/lib/evidence-export-privacy.mjs +561 -0
  94. package/lib/evidence-export.mjs +11 -592
  95. package/lib/evidence-report.mjs +3 -1
  96. package/lib/favicon.mjs +117 -41
  97. package/lib/generated/cisa-kev-catalog.mjs +12 -4
  98. package/lib/generated/registrar-standing-catalogue.mjs +7 -7
  99. package/lib/generated/retire-browser-catalog.mjs +2335 -940
  100. package/lib/hostname.mjs +2 -38
  101. package/lib/html-signals.mjs +82 -85
  102. package/lib/http-evidence-bounds.mjs +2 -1
  103. package/lib/http-intelligence.mjs +39 -26
  104. package/lib/lookup-cache.mjs +34 -11
  105. package/lib/lookup-child-profile-contract.mjs +24 -15
  106. package/lib/lookup-diagnostics.mjs +24 -1
  107. package/lib/lookup-progress.mjs +30 -13
  108. package/lib/lookup-response-contract.mjs +1048 -0
  109. package/lib/lookup-response.mjs +7 -10
  110. package/lib/lookup-source-progress.mjs +17 -14
  111. package/lib/lookup.mjs +48 -27
  112. package/lib/observed-network-context.mjs +43 -27
  113. package/lib/outbound-request-bounds.mjs +4 -1
  114. package/lib/page-fingerprints.mjs +69 -133
  115. package/lib/page-role-profile.mjs +12 -8
  116. package/lib/perceptual-hash.mjs +43 -14
  117. package/lib/rdap-bootstrap.mjs +72 -22
  118. package/lib/rdap-client.mjs +15 -7
  119. package/lib/rdap-normalization.mjs +33 -18
  120. package/lib/rdap-search-workbench.mjs +1 -1
  121. package/lib/rdap-transport.mjs +47 -20
  122. package/lib/rdap-types.mjs +2 -1
  123. package/lib/rdap.mjs +18 -3
  124. package/lib/registrable-domain.mjs +21 -1
  125. package/lib/registrar-standing-contract.mjs +1 -0
  126. package/lib/registry-capabilities.mjs +5 -1
  127. package/lib/registry-capability-catalogue.mjs +2 -1
  128. package/lib/registry-dates.mjs +2 -190
  129. package/lib/registry-insights.mjs +56 -18
  130. package/lib/response-policy.mjs +86 -48
  131. package/lib/risk-calibration-summary.mjs +17 -3
  132. package/lib/rpki-evidence.mjs +8 -5
  133. package/lib/safe-fetch.mjs +54 -47
  134. package/lib/security-txt.mjs +10 -7
  135. package/lib/service-binding-dns.mjs +8 -0
  136. package/lib/smtp-transport-review.mjs +27 -15
  137. package/lib/sslbl-certificates.generated.mjs +136 -135
  138. package/lib/static-html-analysis.mjs +117 -146
  139. package/lib/structured-data-identity.mjs +1 -0
  140. package/lib/technology-markup.mjs +100 -0
  141. package/lib/threat-intelligence-manifests.mjs +11 -139
  142. package/lib/threat-intelligence-runtime.mjs +24 -52
  143. package/lib/threat-intelligence-targets.mjs +6 -34
  144. package/lib/threat-intelligence-types.mjs +39 -0
  145. package/lib/typosquat-generator.mjs +19 -15
  146. package/lib/urlhaus-intelligence.mjs +8 -3
  147. package/lib/urlscan-intelligence.mjs +1 -1
  148. package/lib/website-security-posture.mjs +30 -6
  149. package/lib/website-technology.mjs +111 -71
  150. package/lib/whois-authority.mjs +14 -2
  151. package/lib/whois-chain.mjs +9 -2
  152. package/lib/whois-common-formats.mjs +5 -5
  153. package/lib/whois-contracts.mjs +2 -1
  154. package/lib/whois-dialects.mjs +2 -2
  155. package/lib/whois-parser-support.mjs +8 -1
  156. package/lib/whois-transport.mjs +23 -7
  157. package/package.json +2 -1
  158. package/packages/cases/case-attachment-model.mjs +83 -0
  159. package/packages/cases/case-evidence-model.mjs +156 -62
  160. package/packages/cases/case-export-input.mjs +53 -0
  161. package/packages/cases/case-migration-model.mjs +59 -25
  162. package/packages/cases/case-model.mjs +1 -0
  163. package/packages/cases/case-recheck-model.mjs +91 -0
  164. package/packages/cases/case-record-core.mjs +3 -2
  165. package/packages/cases/case-record-operations.mjs +89 -27
  166. package/packages/cases/case-record-projection.mjs +23 -20
  167. package/packages/cases/case-report.mjs +63 -52
  168. package/packages/cases/case-response-actions.mjs +654 -0
  169. package/packages/cases/case-response-model.mjs +41 -1129
  170. package/packages/cases/case-response-outcomes.mjs +317 -0
  171. package/packages/cases/case-response-packet-render.mjs +153 -0
  172. package/packages/cases/case-response-packet-types.mjs +1 -0
  173. package/packages/cases/case-response-packet-vocabulary.mjs +48 -0
  174. package/packages/cases/case-response-packet.mjs +38 -587
  175. package/packages/cases/case-response-records.mjs +94 -0
  176. package/packages/cases/case-response-review-inputs.mjs +371 -0
  177. package/packages/cases/case-response-values.mjs +85 -0
  178. package/packages/cases/case-review-package.mjs +45 -0
  179. package/packages/cases/case-selection.mjs +46 -0
  180. package/packages/cases/case-storage-model.mjs +25 -0
  181. package/packages/cases/response-route-freshness.mjs +16 -0
  182. package/packages/comparison/capture-context.mjs +61 -0
  183. package/packages/comparison/comparison-ledger-bulk.mjs +4 -1
  184. package/packages/comparison/page-similarity.mjs +2 -1
  185. package/packages/comparison/relationship-evidence.mjs +85 -8
  186. package/packages/comparison/relationship-provenance.mjs +93 -0
  187. package/packages/contracts/analyst-interchange.mjs +216 -0
  188. package/packages/contracts/analyst-review-state-contract.mjs +11 -0
  189. package/packages/contracts/analyst-review-state.mjs +5 -4
  190. package/packages/contracts/case-portability.mjs +279 -116
  191. package/packages/contracts/case-views-contract.mjs +12 -0
  192. package/packages/contracts/case-views.mjs +26 -0
  193. package/packages/contracts/cli-command-semantics.mjs +1 -0
  194. package/packages/contracts/cli-lookup.mjs +0 -1
  195. package/packages/contracts/domain-control-flight-recorder.mjs +80 -42
  196. package/packages/contracts/domain-control-manifest.mjs +127 -73
  197. package/packages/contracts/domain-control-monitor.mjs +48 -45
  198. package/packages/contracts/domain-control-review.mjs +179 -121
  199. package/packages/contracts/domain-name.mjs +21 -0
  200. package/packages/contracts/external-observation-interchange.mjs +2 -1
  201. package/packages/contracts/extracted-domain-lifecycle.mjs +11 -32
  202. package/packages/contracts/http-url.mjs +2 -0
  203. package/packages/contracts/investigation-package-limits.mjs +24 -0
  204. package/packages/contracts/investigation-portability.mjs +56 -27
  205. package/packages/contracts/investigation-run.mjs +16 -0
  206. package/packages/contracts/ip-address.mjs +34 -0
  207. package/packages/contracts/lookup-evidence.mjs +35 -0
  208. package/packages/contracts/offline-comparison.mjs +1 -2
  209. package/packages/contracts/page-fingerprints.mjs +15 -0
  210. package/packages/contracts/risk-calibration.mjs +0 -1
  211. package/packages/contracts/schema-compatibility.mjs +1 -1
  212. package/packages/contracts/schema-lifecycle.mjs +83 -153
  213. package/packages/contracts/selected-file-limits.mjs +6 -0
  214. package/packages/contracts/vulnerability-identifiers.mjs +5 -0
  215. package/packages/contracts/web-capture.mjs +10 -0
  216. package/packages/contracts/workspace-portability.mjs +160 -225
  217. package/packages/evidence/artifact-integrity.mjs +8 -2
  218. package/packages/evidence/artifact-structure.mjs +105 -0
  219. package/packages/evidence/decision-fact.mjs +4 -1
  220. package/packages/evidence/domain-control-runtime.mjs +155 -27
  221. package/packages/evidence/domain-posture-context.mjs +79 -0
  222. package/packages/evidence/image-regions.mjs +56 -0
  223. package/packages/evidence/latest-observations.mjs +28 -0
  224. package/packages/evidence/lookup-target.mjs +104 -0
  225. package/packages/evidence/observation.mjs +16 -1
  226. package/packages/evidence/passphrase-encryption.mjs +44 -0
  227. package/packages/evidence/record-identity.mjs +72 -0
  228. package/packages/evidence/registry-dates.mjs +190 -0
  229. package/packages/evidence/retained-file.mjs +69 -0
  230. package/packages/evidence/review-age.mjs +14 -0
  231. package/packages/interchange/bagit.mjs +342 -0
  232. package/packages/interchange/bounded-zip-extraction.mjs +278 -0
  233. package/packages/interchange/crc32.mjs +17 -0
  234. package/packages/interchange/external-findings-import.mjs +89 -49
  235. package/packages/interchange/web-capture-import.mjs +382 -0
  236. package/packages/investigation/bulk-mail-exposure.mjs +2 -1
  237. package/packages/investigation/investigation-bagit.mjs +14 -0
  238. package/packages/investigation/investigation-capsule.mjs +33 -5
  239. package/packages/investigation/investigation-manifest.mjs +200 -0
  240. package/packages/investigation/investigation-package-crypto.mjs +98 -0
  241. package/packages/investigation/investigation-package.mjs +200 -0
  242. package/packages/investigation/lookup-asset-graph-inputs.mjs +38 -0
  243. package/packages/investigation/lookup-asset-graph.mjs +161 -141
  244. package/packages/investigation/lookup-claim-readiness.mjs +18 -0
  245. package/packages/investigation/lookup-investigation-brief.mjs +3 -3
  246. package/packages/investigation/lookup-task-guidance.mjs +58 -0
  247. package/packages/monitoring/analyst-review-state.mjs +38 -90
  248. package/packages/workspace/brand-profile-model.mjs +158 -20
  249. package/packages/workspace/bulk-columns.mjs +17 -0
  250. package/packages/workspace/bulk-review-model.mjs +11 -5
  251. package/packages/workspace/bulk-session-model.mjs +95 -20
  252. package/packages/workspace/bulk-sort.mjs +1 -0
  253. package/packages/workspace/case-views.mjs +102 -0
  254. package/packages/workspace/hostile-input.mjs +29 -8
  255. package/packages/workspace/investigation-template-model.mjs +19 -4
  256. package/packages/workspace/page-baseline.mjs +18 -15
  257. package/packages/workspace/relationship-observation-model.mjs +41 -26
  258. package/packages/workspace/watchlist-history.mjs +2 -2
  259. package/packages/workspace/watchlist-store.mjs +8 -2
  260. package/packages/workspace/website-snapshot-model.mjs +70 -30
  261. package/packages/workspace/workspace-archive-crypto.mjs +41 -61
  262. package/packages/workspace/workspace-archive.mjs +85 -21
  263. package/third-party-notices.txt +29 -2
  264. package/packages/investigation/bulk-review-export.mjs +0 -63
@@ -2,7 +2,15 @@ import { MAX_DOMAIN_NAME_LENGTH } from "./domain-name.mjs";
2
2
  import { defineSchemaCompatibility } from "./schema-compatibility.mjs";
3
3
  import { defineSchemaLifecycleFamily } from "./schema-lifecycle.mjs";
4
4
  export const CASE_CONTRACT_OWNER = 'packages/contracts/case-portability.mts';
5
- export const LATEST_PUBLIC_APPLICATION_VERSION = '2.2.0';
5
+ export const MAX_CASE_OBJECTIVE_LENGTH = 320;
6
+ export const LATEST_PUBLIC_APPLICATION_VERSION = '2.3.0';
7
+ // Published writer identities are independent of the next writer. Historical
8
+ // readers below use fixed release epochs, never a moving "latest" identity.
9
+ export const LATEST_PUBLIC_CASE_SCHEMA_VERSION = 15;
10
+ export const LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION = 11;
11
+ export const LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION = 9;
12
+ export const LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 3;
13
+ export const LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION = 8;
6
14
  export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
7
15
  'LATEST_PUBLIC_APPLICATION_VERSION',
8
16
  'LATEST_PUBLIC_CASE_SCHEMA_VERSION',
@@ -10,9 +18,19 @@ export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
10
18
  'LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION',
11
19
  'LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION',
12
20
  'LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION',
21
+ 'PUBLISHED_V2_2_CASE_SCHEMA_VERSION',
22
+ 'PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION',
23
+ 'PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION',
24
+ 'PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION',
25
+ 'PUBLISHED_V2_2_WORKSPACE_ARCHIVE_VERSION',
26
+ 'PUBLISHED_V2_3_CASE_SCHEMA_VERSION',
27
+ 'PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION',
28
+ 'PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION',
29
+ 'PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION',
13
30
  'CASE_BROWSER_STORE_LIFECYCLE_SCHEMA',
14
31
  'CASE_EXPORT_LIFECYCLE_SCHEMA',
15
32
  'CASE_SCHEMA_VERSION',
33
+ 'INCIDENT_CASE_SCHEMA_VERSION',
16
34
  'CASE_BROWSER_SUPPORTED_VERSIONS',
17
35
  'CASE_IMPORT_VERSIONS',
18
36
  'CASE_REPORT_SCHEMA',
@@ -27,6 +45,8 @@ export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
27
45
  'SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS',
28
46
  'CLI_CASE_PACK_SCHEMA',
29
47
  'CLI_CASE_PACK_VERSION',
48
+ 'CLI_CASE_PACK_WRITER_FIXTURE_ID',
49
+ 'CLI_CASE_PACK_INPUT_CASE_VERSIONS',
30
50
  'SUPPORTED_CLI_CASE_PACK_VERSIONS',
31
51
  'CLI_CASE_PACK_CASE_REPORT_EPOCHS',
32
52
  'WORKSPACE_ARCHIVE_SCHEMA',
@@ -42,12 +62,15 @@ export const CASE_PORTABILITY_IDENTITY_CONSTANTS = Object.freeze([
42
62
  'ENCRYPTED_WORKSPACE_ARCHIVE_VERSION',
43
63
  ]);
44
64
  export const CASE_PORTABILITY_BOUND_CONSTANTS = Object.freeze([
65
+ 'MAX_CASE_OBJECTIVE_LENGTH',
45
66
  'MAX_CASES',
46
67
  'MAX_NOTES_PER_CASE',
47
68
  'MAX_NOTE_LENGTH',
48
69
  'MAX_TAGS_PER_CASE',
49
70
  'MAX_CASE_IMPORT_BYTES',
50
71
  'MAX_CASE_STORE_BYTES',
72
+ 'MAX_EDITABLE_CASE_INPUT_BYTES',
73
+ 'MAX_EDITABLE_CASE_OUTPUT_BYTES',
51
74
  'MAX_EVIDENCE_SNAPSHOTS_PER_CASE',
52
75
  'MAX_EVIDENCE_FACTORS',
53
76
  'MAX_EVIDENCE_NAMESERVERS',
@@ -132,48 +155,59 @@ export const CASE_BROWSER_STORE_LIFECYCLE_SCHEMA = 'whoisleuth.browser.case-stor
132
155
  export const CASE_EXPORT_LIFECYCLE_SCHEMA = 'whoisleuth.case-export';
133
156
  export const PUBLIC_CASE_SCHEMA_VERSION = 12;
134
157
  export const PUBLISHED_V2_CASE_SCHEMA_VERSION = 13;
135
- export const LATEST_PUBLIC_CASE_SCHEMA_VERSION = 14;
136
- export const CASE_SCHEMA_VERSION = 15;
158
+ export const PUBLISHED_V2_2_CASE_SCHEMA_VERSION = 14;
159
+ export const PUBLISHED_V2_3_CASE_SCHEMA_VERSION = 15;
160
+ export const INCIDENT_CASE_SCHEMA_VERSION = 16;
161
+ export const CASE_SCHEMA_VERSION = INCIDENT_CASE_SCHEMA_VERSION;
137
162
  export const CASE_BROWSER_SUPPORTED_VERSIONS = Object.freeze([
138
163
  PUBLIC_CASE_SCHEMA_VERSION,
139
164
  PUBLISHED_V2_CASE_SCHEMA_VERSION,
140
- LATEST_PUBLIC_CASE_SCHEMA_VERSION,
165
+ PUBLISHED_V2_2_CASE_SCHEMA_VERSION,
166
+ PUBLISHED_V2_3_CASE_SCHEMA_VERSION,
141
167
  CASE_SCHEMA_VERSION,
142
168
  ]);
143
169
  export const CASE_IMPORT_VERSIONS = CASE_BROWSER_SUPPORTED_VERSIONS;
144
170
  export const CASE_REPORT_SCHEMA = 'whoisleuth.case-report';
145
171
  export const PUBLIC_CASE_REPORT_SCHEMA_VERSION = 8;
146
172
  export const PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION = 9;
147
- export const LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION = 10;
148
- export const CASE_REPORT_SCHEMA_VERSION = 11;
173
+ export const PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION = 10;
174
+ export const PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION = 11;
175
+ export const CASE_REPORT_SCHEMA_VERSION = 12;
149
176
  export const CASE_REPORT_OUTPUT_VERSIONS = Object.freeze([
150
177
  PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION,
151
- LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION,
178
+ PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION,
179
+ PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION,
152
180
  CASE_REPORT_SCHEMA_VERSION,
153
181
  ]);
154
182
  export const CASE_RESPONSE_PACKET_SCHEMA = 'whoisleuth.case-response-packet';
155
183
  export const PUBLIC_CASE_RESPONSE_PACKET_VERSION = 6;
156
184
  export const PUBLISHED_V2_CASE_RESPONSE_PACKET_VERSION = 7;
157
- export const LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION = 8;
158
- export const CASE_RESPONSE_PACKET_VERSION = 9;
185
+ export const PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION = 8;
186
+ export const PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION = 9;
187
+ export const CASE_RESPONSE_PACKET_VERSION = 10;
159
188
  export const CASE_RESPONSE_PACKET_OUTPUT_VERSIONS = Object.freeze([
160
189
  PUBLIC_CASE_RESPONSE_PACKET_VERSION,
161
190
  PUBLISHED_V2_CASE_RESPONSE_PACKET_VERSION,
162
- LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION,
191
+ PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION,
192
+ PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION,
163
193
  CASE_RESPONSE_PACKET_VERSION,
164
194
  ]);
165
195
  export const SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS = CASE_RESPONSE_PACKET_OUTPUT_VERSIONS;
166
196
  export const CASE_RESPONSE_REVIEW_INPUTS_SCHEMA = 'whoisleuth.case-response-review-inputs';
167
197
  export const PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 1;
168
- export const LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 2;
169
- export const CASE_RESPONSE_REVIEW_INPUTS_VERSION = 3;
198
+ export const PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 2;
199
+ export const PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION = 3;
200
+ export const CASE_RESPONSE_REVIEW_INPUTS_VERSION = 4;
170
201
  export const SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS = Object.freeze([
171
202
  PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
172
- LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
203
+ PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
204
+ PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
173
205
  CASE_RESPONSE_REVIEW_INPUTS_VERSION,
174
206
  ]);
175
207
  export const CLI_CASE_PACK_SCHEMA = 'whoisleuth.cli.case-pack';
176
208
  export const CLI_CASE_PACK_VERSION = 2;
209
+ export const CLI_CASE_PACK_WRITER_FIXTURE_ID = 'cli-case-pack-v2-case-v16-current';
210
+ export const CLI_CASE_PACK_INPUT_CASE_VERSIONS = Object.freeze(CASE_IMPORT_VERSIONS.filter((version) => version >= PUBLISHED_V2_3_CASE_SCHEMA_VERSION));
177
211
  export const SUPPORTED_CLI_CASE_PACK_VERSIONS = Object.freeze([CLI_CASE_PACK_VERSION]);
178
212
  export const CLI_CASE_PACK_ROOT_KEYS = Object.freeze(['version', 'exportedAt', 'cases', 'packet', 'integrity']);
179
213
  export const CLI_CASE_PACK_PACKET_KEYS = Object.freeze(['schema', 'version', 'audience', 'reviewed', 'reports', 'redactionManifest', 'limitations']);
@@ -189,7 +223,8 @@ export const CLI_CASE_PACK_LIMITATIONS = Object.freeze([
189
223
  export const CLI_CASE_PACK_CASE_REPORT_EPOCHS = Object.freeze([
190
224
  Object.freeze({ caseVersions: Object.freeze([PUBLIC_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLIC_CASE_REPORT_SCHEMA_VERSION]) }),
191
225
  Object.freeze({ caseVersions: Object.freeze([PUBLISHED_V2_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION]) }),
192
- Object.freeze({ caseVersions: Object.freeze([LATEST_PUBLIC_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION]) }),
226
+ Object.freeze({ caseVersions: Object.freeze([PUBLISHED_V2_2_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION]) }),
227
+ Object.freeze({ caseVersions: Object.freeze([PUBLISHED_V2_3_CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION]) }),
193
228
  Object.freeze({ caseVersions: Object.freeze([CASE_SCHEMA_VERSION]), reportVersions: Object.freeze([CASE_REPORT_SCHEMA_VERSION]) }),
194
229
  ]);
195
230
  export function caseReportVersionMatchesCase(caseVersion, reportVersion) {
@@ -201,11 +236,12 @@ export function caseReportVersionMatchesCase(caseVersion, reportVersion) {
201
236
  export const WORKSPACE_ARCHIVE_SCHEMA = 'whoisleuth.workspace-archive';
202
237
  export const PUBLIC_WORKSPACE_ARCHIVE_VERSION = 5;
203
238
  export const PUBLISHED_V2_WORKSPACE_ARCHIVE_VERSION = 6;
204
- export const LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION = 7;
205
- export const WORKSPACE_ARCHIVE_VERSION = 8;
239
+ export const PUBLISHED_V2_2_WORKSPACE_ARCHIVE_VERSION = 7;
240
+ export const WORKSPACE_ARCHIVE_VERSION = 9;
206
241
  export const SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS = Object.freeze([
207
242
  PUBLIC_WORKSPACE_ARCHIVE_VERSION,
208
243
  PUBLISHED_V2_WORKSPACE_ARCHIVE_VERSION,
244
+ PUBLISHED_V2_2_WORKSPACE_ARCHIVE_VERSION,
209
245
  LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION,
210
246
  WORKSPACE_ARCHIVE_VERSION,
211
247
  ]);
@@ -217,9 +253,9 @@ export function isSupportedWorkspaceArchiveVersion(value) {
217
253
  export const WORKSPACE_ARCHIVE_SECTION_IDS = Object.freeze([
218
254
  'cases', 'campaigns', 'brandProfiles', 'watchlists', 'shortlist', 'detectionRules',
219
255
  'relationshipObservations', 'bulkSessions', 'websiteSnapshots', 'investigationTemplates',
220
- 'bulkReview', 'analystReviewState', 'settings',
256
+ 'bulkReview', 'analystReviewState', 'caseViews', 'settings',
221
257
  ]);
222
- export const PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS = Object.freeze(WORKSPACE_ARCHIVE_SECTION_IDS.filter((id) => id !== 'analystReviewState'));
258
+ export const PUBLIC_WORKSPACE_ARCHIVE_SECTION_IDS = Object.freeze(WORKSPACE_ARCHIVE_SECTION_IDS.filter((id) => id !== 'analystReviewState' && id !== 'caseViews'));
223
259
  export const WORKSPACE_ARCHIVE_CASE_SECTION = Object.freeze({
224
260
  id: 'cases',
225
261
  schema: null,
@@ -258,8 +294,12 @@ export const MAX_NOTE_LENGTH = 2000;
258
294
  export const MAX_TAGS_PER_CASE = 20;
259
295
  export const MAX_TAG_LENGTH = 40;
260
296
  export const MAX_DOMAIN_LENGTH = MAX_DOMAIN_NAME_LENGTH;
261
- export const MAX_CASE_IMPORT_BYTES = 2 * 1024 * 1024;
262
297
  export const MAX_CASE_STORE_BYTES = 4 * 1024 * 1024;
298
+ // Formatting and JSON escaping may enlarge an admitted canonical Case store.
299
+ // Editable files retain the store budget; the envelope adds bounded metadata.
300
+ export const MAX_CASE_IMPORT_BYTES = MAX_CASE_STORE_BYTES * 4;
301
+ export const MAX_EDITABLE_CASE_INPUT_BYTES = MAX_CASE_IMPORT_BYTES;
302
+ export const MAX_EDITABLE_CASE_OUTPUT_BYTES = MAX_CASE_STORE_BYTES + 256;
263
303
  export const MAX_EVIDENCE_SNAPSHOTS_PER_CASE = 25;
264
304
  export const MAX_EVIDENCE_FACTORS = 20;
265
305
  export const MAX_EVIDENCE_NAMESERVERS = 12;
@@ -308,9 +348,9 @@ export const RESPONSE_ROUTE_STALE_AFTER_DAYS = 30;
308
348
  export const MAX_RESPONSE_AUTHORISATION_CLOCK_SKEW_MS = 300_000;
309
349
  export const MAX_CASE_PACK_INPUT_BYTES = 4 * 1024 * 1024;
310
350
  export const MAX_CASE_PACK_CASES = 25;
311
- export const MAX_WORKSPACE_ARCHIVE_BYTES = 10 * 1024 * 1024;
351
+ export const MAX_WORKSPACE_ARCHIVE_BYTES = 32 * 1024 * 1024;
312
352
  export const MAX_WORKSPACE_ARCHIVE_SECTION_BYTES = 5 * 1024 * 1024;
313
- export const MAX_WORKSPACE_ARCHIVE_SECTIONS = 13;
353
+ export const MAX_WORKSPACE_ARCHIVE_SECTIONS = WORKSPACE_ARCHIVE_SECTION_IDS.length;
314
354
  export const WORKSPACE_ARCHIVE_PBKDF2_ITERATIONS = 600_000;
315
355
  export const MIN_WORKSPACE_ARCHIVE_PASSPHRASE_CHARACTERS = 12;
316
356
  export const MAX_WORKSPACE_ARCHIVE_PASSPHRASE_BYTES = 1024;
@@ -320,49 +360,49 @@ export const CASE_BROWSER_COMPATIBILITY = defineSchemaCompatibility({
320
360
  supportedVersions: CASE_BROWSER_SUPPORTED_VERSIONS, acceptsUnversionedLegacy: false,
321
361
  futureVersionBehavior: 'preserve_without_write', migration: 'normalize_to_current',
322
362
  writeSemantics: 'normalized_rewrite', byteBudget: MAX_CASE_STORE_BYTES, owner: CASE_CONTRACT_OWNER,
323
- note: 'Exact Case versions 12 and 13 migrate directly to current version 14; other retired stores are rejected and future stores remain untouched.',
363
+ note: 'Declared supported Case stores migrate to the current writer; retired stores are rejected and future stores remain untouched.',
324
364
  });
325
365
  export const CASE_EXPORT_COMPATIBILITY = defineSchemaCompatibility({
326
366
  id: 'export.cases', kind: 'export', schema: null, currentVersion: CASE_SCHEMA_VERSION,
327
367
  supportedVersions: CASE_IMPORT_VERSIONS, acceptsUnversionedLegacy: false,
328
368
  futureVersionBehavior: 'reject', migration: 'normalize_to_current',
329
369
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_CASE_IMPORT_BYTES, owner: CASE_CONTRACT_OWNER,
330
- note: 'Exact Case versions 12, 13 and 14 merge non-destructively through one bounded reader; other versions fail without mutation.',
370
+ note: 'Declared supported Case exports merge non-destructively through one bounded reader; other versions fail without mutation.',
331
371
  });
332
372
  export const CASE_REPORT_COMPATIBILITY = defineSchemaCompatibility({
333
373
  id: 'export.case-report', kind: 'export', schema: CASE_REPORT_SCHEMA, currentVersion: CASE_REPORT_SCHEMA_VERSION,
334
374
  supportedVersions: CASE_REPORT_OUTPUT_VERSIONS, acceptsUnversionedLegacy: false,
335
375
  futureVersionBehavior: 'reject', migration: 'read_only', writeSemantics: 'read_only',
336
376
  byteBudget: null, owner: CASE_CONTRACT_OWNER,
337
- note: 'Published Case report version 9 and current version 10 are exact readable output contracts; retired and future report versions are unsupported.',
377
+ note: 'Supported Case reports are exact readable output contracts; retired and future report versions are unsupported.',
338
378
  });
339
379
  export const CASE_RESPONSE_PACKET_COMPATIBILITY = defineSchemaCompatibility({
340
380
  id: 'export.case-response-packet', kind: 'export', schema: CASE_RESPONSE_PACKET_SCHEMA,
341
381
  currentVersion: CASE_RESPONSE_PACKET_VERSION, supportedVersions: CASE_RESPONSE_PACKET_OUTPUT_VERSIONS,
342
382
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'read_only',
343
383
  writeSemantics: 'read_only', byteBudget: null, owner: CASE_CONTRACT_OWNER,
344
- note: 'Exact packet versions 6, 7, 8 and 9 are verified independently; other versions fail closed.',
384
+ note: 'Declared supported packet versions are verified independently; other versions fail closed.',
345
385
  });
346
386
  export const CASE_RESPONSE_REVIEW_INPUTS_COMPATIBILITY = defineSchemaCompatibility({
347
387
  id: 'derived.case-response-review-inputs', kind: 'derived', schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
348
388
  currentVersion: CASE_RESPONSE_REVIEW_INPUTS_VERSION, supportedVersions: SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS,
349
389
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'read_only',
350
390
  writeSemantics: 'read_only', byteBudget: null, owner: CASE_CONTRACT_OWNER,
351
- note: 'Exact review-input versions 1, 2 and 3 remain independently readable for response authorisation; extensions and future versions fail closed.',
391
+ note: 'Declared supported review inputs remain independently readable for response authorisation; extensions and future versions fail closed.',
352
392
  });
353
393
  export const CLI_CASE_PACK_COMPATIBILITY = defineSchemaCompatibility({
354
394
  id: 'export.cli-case-pack', kind: 'export', schema: CLI_CASE_PACK_SCHEMA, currentVersion: CLI_CASE_PACK_VERSION,
355
395
  supportedVersions: SUPPORTED_CLI_CASE_PACK_VERSIONS, acceptsUnversionedLegacy: false,
356
396
  futureVersionBehavior: 'reject', migration: 'read_only', writeSemantics: 'read_only',
357
397
  byteBudget: MAX_CASE_IMPORT_BYTES, owner: CASE_CONTRACT_OWNER,
358
- note: 'Case-pack version 2 verifies exact Case/report epochs 12/8, 13/9, 14/10 and 15/11 with deterministic sorted-json-v2 integrity.',
398
+ note: 'Supported Case packs verify the declared exact Case/report epoch pairs with deterministic sorted-json-v2 integrity.',
359
399
  });
360
400
  export const WORKSPACE_ARCHIVE_COMPATIBILITY = defineSchemaCompatibility({
361
401
  id: 'export.workspace-archive', kind: 'export', schema: WORKSPACE_ARCHIVE_SCHEMA,
362
402
  currentVersion: WORKSPACE_ARCHIVE_VERSION, supportedVersions: SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS,
363
403
  acceptsUnversionedLegacy: false, futureVersionBehavior: 'reject', migration: 'normalize_to_current',
364
404
  writeSemantics: 'non_destructive_merge', byteBudget: MAX_WORKSPACE_ARCHIVE_BYTES, owner: CASE_CONTRACT_OWNER,
365
- note: 'Workspace version 8 carries the current Case contract. Exact versions 5, 6 and 7 remain readable; version 5 gains an empty Review Item section without inventing decisions, and future envelopes fail without mutation.',
405
+ note: 'The current workspace carries the current Case contract. Declared historical versions remain readable; version 5 gains an empty Review Item section without inventing decisions, and future envelopes fail without mutation.',
366
406
  });
367
407
  export const WORKSPACE_SETTINGS_COMPATIBILITY = defineSchemaCompatibility({
368
408
  id: 'export.workspace-settings-section', kind: 'export', schema: WORKSPACE_SETTINGS_SCHEMA,
@@ -408,8 +448,8 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
408
448
  version: PUBLIC_CASE_SCHEMA_VERSION,
409
449
  role: 'historical',
410
450
  expectation: 'normalises_to_current_output',
411
- expectedOutputFixtureId: 'browser-case-v15',
412
- shapeId: 'case.browser-store.public',
451
+ expectedOutputFixtureId: 'browser-case-v16',
452
+ shapeId: `case.browser-store.v${PUBLIC_CASE_SCHEMA_VERSION}`,
413
453
  scope: 'repository',
414
454
  }),
415
455
  Object.freeze({
@@ -422,8 +462,8 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
422
462
  version: PUBLISHED_V2_CASE_SCHEMA_VERSION,
423
463
  role: 'historical',
424
464
  expectation: 'normalises_to_current_output',
425
- expectedOutputFixtureId: 'browser-case-v15',
426
- shapeId: 'case.browser-store.v13-v14',
465
+ expectedOutputFixtureId: 'browser-case-v16',
466
+ shapeId: `case.browser-store.v${PUBLISHED_V2_CASE_SCHEMA_VERSION}`,
427
467
  scope: 'repository',
428
468
  }),
429
469
  Object.freeze({
@@ -433,11 +473,11 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
433
473
  sha256: 'ece46f5d27a52a20196678af8236674836f3979c0d45f0ad5751c00677ec8bae',
434
474
  contentDigestSha256: null,
435
475
  schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
436
- version: LATEST_PUBLIC_CASE_SCHEMA_VERSION,
476
+ version: PUBLISHED_V2_2_CASE_SCHEMA_VERSION,
437
477
  role: 'historical',
438
478
  expectation: 'normalises_to_current_output',
439
- expectedOutputFixtureId: 'browser-case-v15',
440
- shapeId: 'case.browser-store.v13-v14',
479
+ expectedOutputFixtureId: 'browser-case-v16',
480
+ shapeId: `case.browser-store.v${PUBLISHED_V2_2_CASE_SCHEMA_VERSION}`,
441
481
  scope: 'repository',
442
482
  }),
443
483
  Object.freeze({
@@ -447,11 +487,25 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
447
487
  sha256: 'a061116c9052a7f73ca6b5a88870bc58cbb083cf5fe82e942539b3847c12ad88',
448
488
  contentDigestSha256: null,
449
489
  schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
490
+ version: PUBLISHED_V2_3_CASE_SCHEMA_VERSION,
491
+ role: 'historical',
492
+ expectation: 'normalises_to_current_output',
493
+ expectedOutputFixtureId: 'browser-case-v16',
494
+ shapeId: `case.browser-store.v${PUBLISHED_V2_3_CASE_SCHEMA_VERSION}`,
495
+ scope: 'repository',
496
+ }),
497
+ Object.freeze({
498
+ id: 'browser-case-v16',
499
+ path: 'test/fixtures/case-lifecycle/browser-case-v16.json',
500
+ bytes: 3186,
501
+ sha256: 'e0d9dd7d1da29ca31e1b6ccd0c514c44267d84fe9db2eff0ffce23c51fb04ce4',
502
+ contentDigestSha256: null,
503
+ schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
450
504
  version: CASE_SCHEMA_VERSION,
451
505
  role: 'current',
452
506
  expectation: 'accepted_exact',
453
507
  expectedOutputFixtureId: null,
454
- shapeId: 'case.browser-store.current',
508
+ shapeId: `case.browser-store.v${CASE_SCHEMA_VERSION}`,
455
509
  scope: 'repository',
456
510
  }),
457
511
  Object.freeze({
@@ -464,8 +518,8 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
464
518
  version: PUBLIC_CASE_SCHEMA_VERSION,
465
519
  role: 'historical',
466
520
  expectation: 'normalises_to_current_output',
467
- expectedOutputFixtureId: 'case-export-v15',
468
- shapeId: 'case.export.public',
521
+ expectedOutputFixtureId: 'case-export-v16',
522
+ shapeId: `case.export.v${PUBLIC_CASE_SCHEMA_VERSION}`,
469
523
  scope: 'repository',
470
524
  }),
471
525
  Object.freeze({
@@ -478,8 +532,8 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
478
532
  version: PUBLISHED_V2_CASE_SCHEMA_VERSION,
479
533
  role: 'historical',
480
534
  expectation: 'normalises_to_current_output',
481
- expectedOutputFixtureId: 'case-export-v15',
482
- shapeId: 'case.export.v13-v14',
535
+ expectedOutputFixtureId: 'case-export-v16',
536
+ shapeId: `case.export.v${PUBLISHED_V2_CASE_SCHEMA_VERSION}`,
483
537
  scope: 'repository',
484
538
  }),
485
539
  Object.freeze({
@@ -489,11 +543,11 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
489
543
  sha256: '50cf1df53c3558852b712844d588d9eb955af858e160969d9a6af613c257feaf',
490
544
  contentDigestSha256: null,
491
545
  schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
492
- version: LATEST_PUBLIC_CASE_SCHEMA_VERSION,
546
+ version: PUBLISHED_V2_2_CASE_SCHEMA_VERSION,
493
547
  role: 'historical',
494
548
  expectation: 'normalises_to_current_output',
495
- expectedOutputFixtureId: 'case-export-v15',
496
- shapeId: 'case.export.v13-v14',
549
+ expectedOutputFixtureId: 'case-export-v16',
550
+ shapeId: `case.export.v${PUBLISHED_V2_2_CASE_SCHEMA_VERSION}`,
497
551
  scope: 'repository',
498
552
  }),
499
553
  Object.freeze({
@@ -503,11 +557,25 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
503
557
  sha256: '26cd852ee202b7a50bd2a64e5d412ab52a38aa7195f81a86b26b222398b406f9',
504
558
  contentDigestSha256: null,
505
559
  schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
560
+ version: PUBLISHED_V2_3_CASE_SCHEMA_VERSION,
561
+ role: 'historical',
562
+ expectation: 'normalises_to_current_output',
563
+ expectedOutputFixtureId: 'case-export-v16',
564
+ shapeId: `case.export.v${PUBLISHED_V2_3_CASE_SCHEMA_VERSION}`,
565
+ scope: 'repository',
566
+ }),
567
+ Object.freeze({
568
+ id: 'case-export-v16',
569
+ path: 'test/fixtures/case-lifecycle/case-export-v16.json',
570
+ bytes: 3230,
571
+ sha256: 'b0c14fd4d391b1067f101d61c1549c28570999c9cebb58aed21e164d7af56ad8',
572
+ contentDigestSha256: null,
573
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
506
574
  version: CASE_SCHEMA_VERSION,
507
575
  role: 'current',
508
576
  expectation: 'accepted_exact',
509
577
  expectedOutputFixtureId: null,
510
- shapeId: 'case.export.current',
578
+ shapeId: `case.export.v${CASE_SCHEMA_VERSION}`,
511
579
  scope: 'repository',
512
580
  }),
513
581
  Object.freeze({
@@ -535,7 +603,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
535
603
  role: 'historical',
536
604
  expectation: 'accepted_exact',
537
605
  expectedOutputFixtureId: null,
538
- shapeId: 'case.report.v9',
606
+ shapeId: `case.report.v${PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION}`,
539
607
  scope: 'repository',
540
608
  }),
541
609
  Object.freeze({
@@ -545,11 +613,11 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
545
613
  sha256: '36c8b89bb1b335d923bd5035020ff47f89c88775e14d08af2d2eb2ee2f5cc03a',
546
614
  contentDigestSha256: null,
547
615
  schema: CASE_REPORT_SCHEMA,
548
- version: LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION,
616
+ version: PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION,
549
617
  role: 'historical',
550
618
  expectation: 'accepted_exact',
551
619
  expectedOutputFixtureId: null,
552
- shapeId: 'case.report.v10',
620
+ shapeId: `case.report.v${PUBLISHED_V2_2_CASE_REPORT_SCHEMA_VERSION}`,
553
621
  scope: 'repository',
554
622
  }),
555
623
  Object.freeze({
@@ -559,11 +627,25 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
559
627
  sha256: 'bbe57dac61074b7badd094b7ffaabd46d3779c10152563e7a101a2126daf09b6',
560
628
  contentDigestSha256: null,
561
629
  schema: CASE_REPORT_SCHEMA,
630
+ version: PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION,
631
+ role: 'historical',
632
+ expectation: 'accepted_exact',
633
+ expectedOutputFixtureId: null,
634
+ shapeId: `case.report.v${PUBLISHED_V2_3_CASE_REPORT_SCHEMA_VERSION}`,
635
+ scope: 'repository',
636
+ }),
637
+ Object.freeze({
638
+ id: 'case-report-v12',
639
+ path: 'test/fixtures/case-lifecycle/case-report-v12.json',
640
+ bytes: 4778,
641
+ sha256: '175b9ab720d95e497eb89a73f437b00c5db9595833b02b033a09bb526a926f36',
642
+ contentDigestSha256: null,
643
+ schema: CASE_REPORT_SCHEMA,
562
644
  version: CASE_REPORT_SCHEMA_VERSION,
563
645
  role: 'current',
564
646
  expectation: 'accepted_exact',
565
647
  expectedOutputFixtureId: null,
566
- shapeId: 'case.report.v11',
648
+ shapeId: `case.report.v${CASE_REPORT_SCHEMA_VERSION}`,
567
649
  scope: 'repository',
568
650
  }),
569
651
  Object.freeze({
@@ -587,11 +669,11 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
587
669
  sha256: '7bd2237f208714b92c3669c0581dbf0bc2dc22369e53b3ef9da088e8af0f4a83',
588
670
  contentDigestSha256: 'sha256:86d4d88d1d9fc969e6efa432061c64bf0725767d81c50b8655abd2d231dcc5b6',
589
671
  schema: CASE_RESPONSE_PACKET_SCHEMA,
590
- version: LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION,
672
+ version: PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION,
591
673
  role: 'historical',
592
674
  expectation: 'accepted_exact',
593
675
  expectedOutputFixtureId: null,
594
- shapeId: 'case.response-packet.v8',
676
+ shapeId: `case.response-packet.v${PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION}`,
595
677
  scope: 'repository',
596
678
  }),
597
679
  Object.freeze({
@@ -601,11 +683,25 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
601
683
  sha256: '6dbb9b9d2c199ebb0a1f43c3a08aaac98554787b40fed0456eff60ab2f36906c',
602
684
  contentDigestSha256: 'sha256:3500158f08920e43ad16d588b8bfe3b8b92c58370d80468db4330baabeeca497',
603
685
  schema: CASE_RESPONSE_PACKET_SCHEMA,
686
+ version: PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION,
687
+ role: 'historical',
688
+ expectation: 'accepted_exact',
689
+ expectedOutputFixtureId: null,
690
+ shapeId: `case.response-packet.v${PUBLISHED_V2_3_CASE_RESPONSE_PACKET_VERSION}`,
691
+ scope: 'repository',
692
+ }),
693
+ Object.freeze({
694
+ id: 'case-response-packet-v10',
695
+ path: 'test/fixtures/case-lifecycle/case-response-packet-v10.json',
696
+ bytes: 12276,
697
+ sha256: '991475b878f65d9673e60c776162daf921ec5915e16715080ea0534516cff9c4',
698
+ contentDigestSha256: 'sha256:97cfe929ace59edb46ee0b84e6135614aba782e170e730c3de3fb24b82413162',
699
+ schema: CASE_RESPONSE_PACKET_SCHEMA,
604
700
  version: CASE_RESPONSE_PACKET_VERSION,
605
701
  role: 'current',
606
702
  expectation: 'accepted_exact',
607
703
  expectedOutputFixtureId: null,
608
- shapeId: 'case.response-packet.v9',
704
+ shapeId: `case.response-packet.v${CASE_RESPONSE_PACKET_VERSION}`,
609
705
  scope: 'repository',
610
706
  }),
611
707
  Object.freeze({
@@ -629,11 +725,11 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
629
725
  sha256: 'dcc9aaa7cdbd79a33559095f4c212e4eea5ad635c8c36290456d8b1946845aa6',
630
726
  contentDigestSha256: null,
631
727
  schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
632
- version: LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
728
+ version: PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
633
729
  role: 'historical',
634
730
  expectation: 'accepted_exact',
635
731
  expectedOutputFixtureId: null,
636
- shapeId: 'case.response-review-inputs.v2',
732
+ shapeId: `case.response-review-inputs.v${PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION}`,
637
733
  scope: 'repository',
638
734
  }),
639
735
  Object.freeze({
@@ -643,11 +739,25 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
643
739
  sha256: '13e0711d0a75635825af8cff4a1552efe1be153cc1892c886585f28c25ff03ae',
644
740
  contentDigestSha256: null,
645
741
  schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
742
+ version: PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION,
743
+ role: 'historical',
744
+ expectation: 'accepted_exact',
745
+ expectedOutputFixtureId: null,
746
+ shapeId: `case.response-review-inputs.v${PUBLISHED_V2_3_CASE_RESPONSE_REVIEW_INPUTS_VERSION}`,
747
+ scope: 'repository',
748
+ }),
749
+ Object.freeze({
750
+ id: 'case-response-review-inputs-v4',
751
+ path: 'test/fixtures/case-lifecycle/case-response-review-inputs-v4.json',
752
+ bytes: 7604,
753
+ sha256: 'b054fc8405663992055ec6c07998fd19ab2f12dcc581b87b42f773b1ab4acec7',
754
+ contentDigestSha256: null,
755
+ schema: CASE_RESPONSE_REVIEW_INPUTS_SCHEMA,
646
756
  version: CASE_RESPONSE_REVIEW_INPUTS_VERSION,
647
757
  role: 'current',
648
758
  expectation: 'accepted_exact',
649
759
  expectedOutputFixtureId: null,
650
- shapeId: 'case.response-review-inputs.v3',
760
+ shapeId: `case.response-review-inputs.v${CASE_RESPONSE_REVIEW_INPUTS_VERSION}`,
651
761
  scope: 'repository',
652
762
  }),
653
763
  Object.freeze({
@@ -706,6 +816,34 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
706
816
  shapeId: 'case.cli-pack.current',
707
817
  scope: 'repository',
708
818
  }),
819
+ Object.freeze({
820
+ id: 'cli-case-pack-v2-case-v15-current',
821
+ path: 'test/fixtures/case-lifecycle/cli-case-pack-v2-case-v15-current.json',
822
+ bytes: 9439,
823
+ sha256: '9ce7303956589ebfb832157823c74e3ec4d3852507e991d0e1b0c542a06dc377',
824
+ contentDigestSha256: 'sha256:5a88d202efd12c522a6001fa579d2fff8196a0d2ab68571c27894040edbdc3d0',
825
+ schema: CLI_CASE_PACK_SCHEMA,
826
+ version: CLI_CASE_PACK_VERSION,
827
+ role: 'current',
828
+ expectation: 'accepted_exact',
829
+ expectedOutputFixtureId: null,
830
+ shapeId: 'case.cli-pack.current',
831
+ scope: 'repository',
832
+ }),
833
+ Object.freeze({
834
+ id: CLI_CASE_PACK_WRITER_FIXTURE_ID,
835
+ path: `test/fixtures/case-lifecycle/${CLI_CASE_PACK_WRITER_FIXTURE_ID}.json`,
836
+ bytes: 9602,
837
+ sha256: '2ac29a7b64b3d8930e92f6ab6b826566061224f281799f9b4b15c0acc98550f0',
838
+ contentDigestSha256: 'sha256:c6e3cfbe171a85e9318a0a6780d54dc4ad3e662113a75f483fd527c2e36e0143',
839
+ schema: CLI_CASE_PACK_SCHEMA,
840
+ version: CLI_CASE_PACK_VERSION,
841
+ role: 'current',
842
+ expectation: 'accepted_exact',
843
+ expectedOutputFixtureId: null,
844
+ shapeId: 'case.cli-pack.current',
845
+ scope: 'repository',
846
+ }),
709
847
  Object.freeze({
710
848
  id: 'workspace-archive-v5-public',
711
849
  path: 'test/fixtures/case-lifecycle/workspace-archive-v5-public.json',
@@ -716,7 +854,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
716
854
  version: PUBLIC_WORKSPACE_ARCHIVE_VERSION,
717
855
  role: 'historical',
718
856
  expectation: 'normalises_to_current_output',
719
- expectedOutputFixtureId: 'workspace-archive-v8-empty-current',
857
+ expectedOutputFixtureId: `workspace-archive-v${WORKSPACE_ARCHIVE_VERSION}-empty-current`,
720
858
  shapeId: 'case.workspace-archive.v5',
721
859
  scope: 'repository',
722
860
  }),
@@ -730,7 +868,7 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
730
868
  version: PUBLISHED_V2_WORKSPACE_ARCHIVE_VERSION,
731
869
  role: 'historical',
732
870
  expectation: 'normalises_to_current_output',
733
- expectedOutputFixtureId: 'workspace-archive-v8-empty-current',
871
+ expectedOutputFixtureId: `workspace-archive-v${WORKSPACE_ARCHIVE_VERSION}-empty-current`,
734
872
  shapeId: 'case.workspace-archive.v6',
735
873
  scope: 'repository',
736
874
  }),
@@ -741,10 +879,10 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
741
879
  sha256: '892b0d321f42bfb34501019388da682071024af452a8263f138cc08221947f0a',
742
880
  contentDigestSha256: null,
743
881
  schema: WORKSPACE_ARCHIVE_SCHEMA,
744
- version: LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION,
882
+ version: PUBLISHED_V2_2_WORKSPACE_ARCHIVE_VERSION,
745
883
  role: 'historical',
746
884
  expectation: 'normalises_to_current_output',
747
- expectedOutputFixtureId: 'workspace-archive-v8-empty-current',
885
+ expectedOutputFixtureId: `workspace-archive-v${WORKSPACE_ARCHIVE_VERSION}-empty-current`,
748
886
  shapeId: 'case.workspace-archive.v7',
749
887
  scope: 'repository',
750
888
  }),
@@ -755,11 +893,25 @@ const CASE_LIFECYCLE_FIXTURES = Object.freeze([
755
893
  sha256: '747062e2dbbcf96724b19718d36bf0dc50b08e96d8adb670115d2315032b6c05',
756
894
  contentDigestSha256: null,
757
895
  schema: WORKSPACE_ARCHIVE_SCHEMA,
896
+ version: LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION,
897
+ role: 'historical',
898
+ expectation: 'normalises_to_current_output',
899
+ expectedOutputFixtureId: `workspace-archive-v${WORKSPACE_ARCHIVE_VERSION}-empty-current`,
900
+ shapeId: 'case.workspace-archive.v8',
901
+ scope: 'repository',
902
+ }),
903
+ Object.freeze({
904
+ id: 'workspace-archive-v9-empty-current',
905
+ path: 'test/fixtures/case-lifecycle/workspace-archive-v9-empty-current.json',
906
+ bytes: 7613,
907
+ sha256: '6786a9a892bdc21814b0f1165cd9286fb82258862a4c15140daf9caae71daff0',
908
+ contentDigestSha256: null,
909
+ schema: WORKSPACE_ARCHIVE_SCHEMA,
758
910
  version: WORKSPACE_ARCHIVE_VERSION,
759
911
  role: 'current',
760
912
  expectation: 'accepted_exact',
761
913
  expectedOutputFixtureId: null,
762
- shapeId: 'case.workspace-archive.v8',
914
+ shapeId: `case.workspace-archive.v${WORKSPACE_ARCHIVE_VERSION}`,
763
915
  scope: 'repository',
764
916
  }),
765
917
  Object.freeze({
@@ -859,20 +1011,14 @@ function shape(id, schema, versions, requiredKeys, normalisation, target = null)
859
1011
  };
860
1012
  }
861
1013
  const CASE_LIFECYCLE_SHAPES = Object.freeze([
862
- shape('case.browser-store.public', CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, [PUBLIC_CASE_SCHEMA_VERSION], ['version', 'cases'], 'project_known_fields'),
863
- shape('case.browser-store.v13-v14', CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, [PUBLISHED_V2_CASE_SCHEMA_VERSION, LATEST_PUBLIC_CASE_SCHEMA_VERSION], ['version', 'cases'], 'project_known_fields'),
864
- shape('case.browser-store.current', CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], ['version', 'cases'], 'project_known_fields'),
865
- shape('case.export.v13-v14', CASE_EXPORT_LIFECYCLE_SCHEMA, [PUBLISHED_V2_CASE_SCHEMA_VERSION, LATEST_PUBLIC_CASE_SCHEMA_VERSION], ['version', 'cases'], 'preserve_document'),
866
- shape('case.export.public', CASE_EXPORT_LIFECYCLE_SCHEMA, [PUBLIC_CASE_SCHEMA_VERSION], ['version', 'cases'], 'preserve_document'),
1014
+ ...CASE_BROWSER_SUPPORTED_VERSIONS.map((version) => shape(`case.browser-store.v${version}`, CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, [version], ['version', 'cases'], 'project_known_fields')),
1015
+ ...CASE_IMPORT_VERSIONS.map((version) => shape(`case.export.v${version}`, CASE_EXPORT_LIFECYCLE_SCHEMA, [version], ['version', 'cases'], 'preserve_document')),
867
1016
  shape('case.response-packet.v6', CASE_RESPONSE_PACKET_SCHEMA, [PUBLIC_CASE_RESPONSE_PACKET_VERSION], [
868
1017
  'schema', 'schemaVersion', 'generatedAt', 'reviewRequired', 'submissionPerformed',
869
1018
  'profile', 'case', 'incident', 'contacts', 'preflight', 'escalationHistory',
870
1019
  'provenance', 'integrity',
871
1020
  ], 'preserve_signed_document'),
872
- shape('case.export.current', CASE_EXPORT_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], ['version', 'cases'], 'preserve_document'),
873
- shape('case.report.v9', CASE_REPORT_SCHEMA, [PUBLISHED_V2_CASE_REPORT_SCHEMA_VERSION], ['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'responseLifecycle', 'limitations'], 'preserve_document'),
874
- shape('case.report.v10', CASE_REPORT_SCHEMA, [LATEST_PUBLIC_CASE_REPORT_SCHEMA_VERSION], ['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'responseLifecycle', 'limitations'], 'preserve_document'),
875
- shape('case.report.v11', CASE_REPORT_SCHEMA, [CASE_REPORT_SCHEMA_VERSION], ['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'responseLifecycle', 'limitations'], 'preserve_document'),
1021
+ ...CASE_REPORT_OUTPUT_VERSIONS.map((version) => shape(`case.report.v${version}`, CASE_REPORT_SCHEMA, [version], ['schema', 'schemaVersion', 'generatedAt', 'application', 'case', 'currentAssessment', 'evidenceTimeline', 'analystResponse', 'responseLifecycle', 'limitations'], 'preserve_document')),
876
1022
  shape('case.response-packet.v7', CASE_RESPONSE_PACKET_SCHEMA, [PUBLISHED_V2_CASE_RESPONSE_PACKET_VERSION], [
877
1023
  'schema', 'schemaVersion', 'generatedAt', 'reviewRequired', 'submissionPerformed',
878
1024
  'profile', 'case', 'incident', 'contacts', 'provenance', 'selectedEvidence',
@@ -880,42 +1026,26 @@ const CASE_LIFECYCLE_SHAPES = Object.freeze([
880
1026
  'escalationHistoryOmitted', 'escalationHistoryLimitations', 'responseLifecycle',
881
1027
  'authorisation', 'integrity',
882
1028
  ], 'preserve_signed_document'),
883
- shape('case.response-packet.v8', CASE_RESPONSE_PACKET_SCHEMA, [LATEST_PUBLIC_CASE_RESPONSE_PACKET_VERSION], [
1029
+ ...CASE_RESPONSE_PACKET_OUTPUT_VERSIONS.filter((version) => version >= PUBLISHED_V2_2_CASE_RESPONSE_PACKET_VERSION).map((version) => shape(`case.response-packet.v${version}`, CASE_RESPONSE_PACKET_SCHEMA, [version], [
884
1030
  'schema', 'schemaVersion', 'generatedAt', 'reviewRequired', 'submissionPerformed',
885
1031
  'profile', 'case', 'incident', 'contacts', 'recipientRoute', 'actionBinding',
886
1032
  'provenance', 'selectedEvidence', 'contradictions', 'readiness', 'artefactReferences',
887
1033
  'preflight', 'escalationHistory', 'escalationHistoryOmitted',
888
1034
  'escalationHistoryLimitations', 'responseLifecycle', 'authorisation', 'integrity',
889
- ], 'preserve_signed_document'),
890
- shape('case.response-packet.v9', CASE_RESPONSE_PACKET_SCHEMA, [CASE_RESPONSE_PACKET_VERSION], [
891
- 'schema', 'schemaVersion', 'generatedAt', 'reviewRequired', 'submissionPerformed',
892
- 'profile', 'case', 'incident', 'contacts', 'recipientRoute', 'actionBinding',
893
- 'provenance', 'selectedEvidence', 'contradictions', 'readiness', 'artefactReferences',
894
- 'preflight', 'escalationHistory', 'escalationHistoryOmitted',
895
- 'escalationHistoryLimitations', 'responseLifecycle', 'authorisation', 'integrity',
896
- ], 'preserve_signed_document'),
1035
+ ], 'preserve_signed_document')),
897
1036
  shape('case.response-review-inputs.v1', CASE_RESPONSE_REVIEW_INPUTS_SCHEMA, [PUBLISHED_V2_CASE_RESPONSE_REVIEW_INPUTS_VERSION], [
898
1037
  'contract', 'version', 'profile', 'case', 'incident', 'contacts', 'selectedEvidence',
899
1038
  'contradictions', 'readiness', 'artefactReferences', 'escalationHistory',
900
1039
  'escalationHistoryOmitted', 'escalationHistoryLimitations', 'responseLifecycle',
901
1040
  ], 'preserve_signed_document'),
902
- shape('case.response-review-inputs.v2', CASE_RESPONSE_REVIEW_INPUTS_SCHEMA, [LATEST_PUBLIC_CASE_RESPONSE_REVIEW_INPUTS_VERSION], [
1041
+ ...SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS.filter((version) => version >= PUBLISHED_V2_2_CASE_RESPONSE_REVIEW_INPUTS_VERSION).map((version) => shape(`case.response-review-inputs.v${version}`, CASE_RESPONSE_REVIEW_INPUTS_SCHEMA, [version], [
903
1042
  'contract', 'version', 'profile', 'case', 'incident', 'contacts',
904
1043
  'recipientRoute', 'actionBinding', 'selectedEvidence', 'contradictions',
905
1044
  'readiness', 'artefactReferences', 'escalationHistory',
906
1045
  'escalationHistoryOmitted', 'escalationHistoryLimitations', 'responseLifecycle',
907
- ], 'preserve_signed_document'),
908
- shape('case.response-review-inputs.v3', CASE_RESPONSE_REVIEW_INPUTS_SCHEMA, [CASE_RESPONSE_REVIEW_INPUTS_VERSION], [
909
- 'contract', 'version', 'profile', 'case', 'incident', 'contacts',
910
- 'recipientRoute', 'actionBinding', 'selectedEvidence', 'contradictions',
911
- 'readiness', 'artefactReferences', 'escalationHistory',
912
- 'escalationHistoryOmitted', 'escalationHistoryLimitations', 'responseLifecycle',
913
- ], 'preserve_signed_document'),
1046
+ ], 'preserve_signed_document')),
914
1047
  shape('case.cli-pack.current', CLI_CASE_PACK_SCHEMA, [CLI_CASE_PACK_VERSION], CLI_CASE_PACK_ROOT_KEYS, 'preserve_signed_document'),
915
- shape('case.workspace-archive.v5', WORKSPACE_ARCHIVE_SCHEMA, [5], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document'),
916
- shape('case.workspace-archive.v6', WORKSPACE_ARCHIVE_SCHEMA, [PUBLISHED_V2_WORKSPACE_ARCHIVE_VERSION], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document'),
917
- shape('case.workspace-archive.v7', WORKSPACE_ARCHIVE_SCHEMA, [LATEST_PUBLIC_WORKSPACE_ARCHIVE_VERSION], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document'),
918
- shape('case.workspace-archive.v8', WORKSPACE_ARCHIVE_SCHEMA, [WORKSPACE_ARCHIVE_VERSION], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document'),
1048
+ ...SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS.map(version => shape(`case.workspace-archive.v${version}`, WORKSPACE_ARCHIVE_SCHEMA, [version], ['schema', 'version', 'generatedAt', 'manifest', 'sections', 'limitations'], 'preserve_signed_document')),
919
1049
  shape('case.workspace-settings.v1', WORKSPACE_SETTINGS_SCHEMA, [WORKSPACE_SETTINGS_VERSION], ['schema', 'version', 'activeProfileId', 'theme'], 'preserve_document'),
920
1050
  shape('case.encrypted-workspace-archive.v1', ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA, [ENCRYPTED_WORKSPACE_ARCHIVE_VERSION], ['schema', 'version', 'createdAt', 'content', 'kdf', 'cipher', 'ciphertext'], 'preserve_signed_document'),
921
1051
  ]);
@@ -997,6 +1127,13 @@ const CASE_LIFECYCLE_HOOKS = Object.freeze([
997
1127
  module: 'packages/cases/case-response-packet.mts',
998
1128
  exportName: 'validateCaseResponseReviewInputs',
999
1129
  },
1130
+ {
1131
+ id: 'case.editable.read',
1132
+ role: 'structure_validator',
1133
+ runtime: 'shared',
1134
+ module: 'packages/cases/case-export-input.mts',
1135
+ exportName: 'readEditableCaseExport',
1136
+ },
1000
1137
  {
1001
1138
  id: 'case.cli-pack.build',
1002
1139
  role: 'builder',
@@ -1062,6 +1199,14 @@ const CASE_LIFECYCLE_BOUNDS = Object.freeze([
1062
1199
  { id: 'serialised-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_CASE_IMPORT_BYTES, handling: 'reject' },
1063
1200
  ],
1064
1201
  },
1202
+ {
1203
+ id: 'case.editable.bounds',
1204
+ bounds: [
1205
+ { id: 'raw-bytes', path: '$', phase: 'raw_intake', unit: 'bytes', minimum: 1, maximum: MAX_EDITABLE_CASE_INPUT_BYTES, handling: 'reject' },
1206
+ { id: 'canonical-store-bytes', path: '$', phase: 'normalised', unit: 'bytes', minimum: 1, maximum: MAX_CASE_STORE_BYTES, handling: 'reject' },
1207
+ { id: 'output-bytes', path: '$', phase: 'serialised', unit: 'bytes', minimum: 1, maximum: MAX_EDITABLE_CASE_OUTPUT_BYTES, handling: 'reject' },
1208
+ ],
1209
+ },
1065
1210
  {
1066
1211
  id: 'case.domain.bounds',
1067
1212
  bounds: [
@@ -1155,14 +1300,14 @@ const CASE_LIFECYCLE_EXPIRY = Object.freeze([{
1155
1300
  }]);
1156
1301
  const CASE_LIFECYCLE_SERIALISATION = Object.freeze([
1157
1302
  {
1158
- id: 'case.browser-store.json.v15',
1303
+ id: 'case.browser-store.json',
1159
1304
  schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA,
1160
1305
  versions: [CASE_SCHEMA_VERSION],
1161
1306
  mediaType: 'application/json',
1162
1307
  encoding: 'utf-8',
1163
1308
  bom: false,
1164
1309
  indentSpaces: 0,
1165
- terminalLf: true,
1310
+ terminalLf: false,
1166
1311
  propertyOrder: 'normalised_fixed',
1167
1312
  canonicalisation: null,
1168
1313
  integrity: 'none',
@@ -1170,10 +1315,10 @@ const CASE_LIFECYCLE_SERIALISATION = Object.freeze([
1170
1315
  verifierHookIds: [],
1171
1316
  },
1172
1317
  ...[
1173
- ['case.export.json.v15', CASE_EXPORT_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], 'none', []],
1174
- ['case.report.json.v11', CASE_REPORT_SCHEMA, [...CASE_REPORT_OUTPUT_VERSIONS], 'none', []],
1175
- ['case.packet.json.v9', CASE_RESPONSE_PACKET_SCHEMA, [...SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS], 'structural_only_requires_separate_verification', ['case.packet.verify']],
1176
- ['case.workspace.json.v8', WORKSPACE_ARCHIVE_SCHEMA, [WORKSPACE_ARCHIVE_VERSION], 'structural_only_requires_separate_verification', ['case.workspace.verify']],
1318
+ ['case.export.json', CASE_EXPORT_LIFECYCLE_SCHEMA, [CASE_SCHEMA_VERSION], 'none', []],
1319
+ ['case.report.json', CASE_REPORT_SCHEMA, [...CASE_REPORT_OUTPUT_VERSIONS], 'none', []],
1320
+ ['case.packet.json', CASE_RESPONSE_PACKET_SCHEMA, [...SUPPORTED_CASE_RESPONSE_PACKET_VERSIONS], 'structural_only_requires_separate_verification', ['case.packet.verify']],
1321
+ [`case.workspace.json.v${WORKSPACE_ARCHIVE_VERSION}`, WORKSPACE_ARCHIVE_SCHEMA, [WORKSPACE_ARCHIVE_VERSION], 'structural_only_requires_separate_verification', ['case.workspace.verify']],
1177
1322
  ['case.encrypted-workspace.json.v1', ENCRYPTED_WORKSPACE_ARCHIVE_SCHEMA, [ENCRYPTED_WORKSPACE_ARCHIVE_VERSION], 'structural_only_requires_separate_verification', ['case.encrypted-workspace.verify']],
1178
1323
  ].map(([id, schema, versions, integrity, verifierHookIds]) => ({
1179
1324
  id,
@@ -1224,10 +1369,10 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1224
1369
  discriminator: null,
1225
1370
  }],
1226
1371
  emittedContract: { schema: CASE_BROWSER_STORE_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION, discriminator: null },
1227
- shapeIds: ['case.browser-store.public', 'case.browser-store.v13-v14', 'case.browser-store.current'],
1372
+ shapeIds: CASE_BROWSER_SUPPORTED_VERSIONS.map((version) => `case.browser-store.v${version}`),
1228
1373
  boundProfileIds: ['case.browser-store.bounds', 'case.domain.bounds'],
1229
1374
  hookIds: ['case.browser.normalise', 'case.browser.serialise'],
1230
- serialisationProfileId: 'case.browser-store.json.v15',
1375
+ serialisationProfileId: 'case.browser-store.json',
1231
1376
  privacyProfileId: 'case.privacy.browser-state',
1232
1377
  retentionEffect: 'browser_indexeddb',
1233
1378
  ...sharedEdge,
@@ -1243,7 +1388,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1243
1388
  discriminator: null,
1244
1389
  }],
1245
1390
  emittedContract: null,
1246
- shapeIds: ['case.export.public', 'case.export.v13-v14', 'case.export.current'],
1391
+ shapeIds: CASE_IMPORT_VERSIONS.map((version) => `case.export.v${version}`),
1247
1392
  boundProfileIds: ['case.portable.bounds', 'case.domain.bounds'],
1248
1393
  hookIds: ['case.export.merge'],
1249
1394
  serialisationProfileId: null,
@@ -1257,10 +1402,10 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1257
1402
  operation: 'build-portable-export',
1258
1403
  acceptedContracts: [],
1259
1404
  emittedContract: { schema: CASE_EXPORT_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION, discriminator: null },
1260
- shapeIds: ['case.export.current'],
1405
+ shapeIds: [`case.export.v${CASE_SCHEMA_VERSION}`],
1261
1406
  boundProfileIds: ['case.portable.bounds', 'case.domain.bounds'],
1262
1407
  hookIds: ['case.export.build', 'case.portable.serialise'],
1263
- serialisationProfileId: 'case.export.json.v15',
1408
+ serialisationProfileId: 'case.export.json',
1264
1409
  privacyProfileId: 'case.privacy.portable-output',
1265
1410
  retentionEffect: 'operator_controlled_output',
1266
1411
  ...sharedEdge,
@@ -1276,7 +1421,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1276
1421
  discriminator: null,
1277
1422
  }],
1278
1423
  emittedContract: null,
1279
- shapeIds: ['case.report.v9', 'case.report.v10', 'case.report.v11'],
1424
+ shapeIds: CASE_REPORT_OUTPUT_VERSIONS.map((version) => `case.report.v${version}`),
1280
1425
  boundProfileIds: ['case.domain.bounds'],
1281
1426
  hookIds: ['case.cli-pack.verify'],
1282
1427
  serialisationProfileId: null,
@@ -1290,10 +1435,10 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1290
1435
  operation: 'build-report',
1291
1436
  acceptedContracts: [],
1292
1437
  emittedContract: { schema: CASE_REPORT_SCHEMA, version: CASE_REPORT_SCHEMA_VERSION, discriminator: null },
1293
- shapeIds: ['case.report.v11'],
1438
+ shapeIds: [`case.report.v${CASE_REPORT_SCHEMA_VERSION}`],
1294
1439
  boundProfileIds: ['case.domain.bounds'],
1295
1440
  hookIds: ['case.report.build', 'case.portable.serialise'],
1296
- serialisationProfileId: 'case.report.json.v11',
1441
+ serialisationProfileId: 'case.report.json',
1297
1442
  privacyProfileId: 'case.privacy.portable-output',
1298
1443
  retentionEffect: 'operator_controlled_output',
1299
1444
  ...sharedEdge,
@@ -1309,7 +1454,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1309
1454
  discriminator: null,
1310
1455
  }],
1311
1456
  emittedContract: null,
1312
- shapeIds: ['case.response-packet.v6', 'case.response-packet.v7', 'case.response-packet.v8', 'case.response-packet.v9'],
1457
+ shapeIds: CASE_RESPONSE_PACKET_OUTPUT_VERSIONS.map((version) => `case.response-packet.v${version}`),
1313
1458
  boundProfileIds: ['case.packet.bounds'],
1314
1459
  hookIds: ['case.packet.verify'],
1315
1460
  serialisationProfileId: null,
@@ -1328,7 +1473,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1328
1473
  discriminator: null,
1329
1474
  }],
1330
1475
  emittedContract: null,
1331
- shapeIds: ['case.response-packet.v6', 'case.response-packet.v7', 'case.response-packet.v8', 'case.response-packet.v9'],
1476
+ shapeIds: CASE_RESPONSE_PACKET_OUTPUT_VERSIONS.map((version) => `case.response-packet.v${version}`),
1332
1477
  boundProfileIds: ['case.packet.bounds'],
1333
1478
  hookIds: ['case.packet.structure', 'case.packet.verify'],
1334
1479
  serialisationProfileId: null,
@@ -1342,10 +1487,10 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1342
1487
  operation: 'build-packet',
1343
1488
  acceptedContracts: [],
1344
1489
  emittedContract: { schema: CASE_RESPONSE_PACKET_SCHEMA, version: CASE_RESPONSE_PACKET_VERSION, discriminator: null },
1345
- shapeIds: ['case.response-packet.v9'],
1490
+ shapeIds: [`case.response-packet.v${CASE_RESPONSE_PACKET_VERSION}`],
1346
1491
  boundProfileIds: ['case.packet.bounds'],
1347
1492
  hookIds: ['case.packet.build', 'case.portable.serialise'],
1348
- serialisationProfileId: 'case.packet.json.v9',
1493
+ serialisationProfileId: 'case.packet.json',
1349
1494
  privacyProfileId: 'case.privacy.portable-output',
1350
1495
  retentionEffect: 'operator_controlled_output',
1351
1496
  ...sharedEdge,
@@ -1361,7 +1506,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1361
1506
  discriminator: null,
1362
1507
  }],
1363
1508
  emittedContract: null,
1364
- shapeIds: ['case.response-review-inputs.v1', 'case.response-review-inputs.v2', 'case.response-review-inputs.v3'],
1509
+ shapeIds: SUPPORTED_CASE_RESPONSE_REVIEW_INPUTS_VERSIONS.map((version) => `case.response-review-inputs.v${version}`),
1365
1510
  boundProfileIds: ['case.packet.bounds'],
1366
1511
  hookIds: ['case.review-inputs.validate'],
1367
1512
  serialisationProfileId: null,
@@ -1379,7 +1524,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1379
1524
  version: CASE_RESPONSE_REVIEW_INPUTS_VERSION,
1380
1525
  discriminator: null,
1381
1526
  },
1382
- shapeIds: ['case.response-review-inputs.v3'],
1527
+ shapeIds: [`case.response-review-inputs.v${CASE_RESPONSE_REVIEW_INPUTS_VERSION}`],
1383
1528
  boundProfileIds: ['case.packet.bounds'],
1384
1529
  hookIds: ['case.review-inputs.build'],
1385
1530
  serialisationProfileId: null,
@@ -1406,18 +1551,37 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1406
1551
  retentionEffect: 'deliberate_local_file',
1407
1552
  ...sharedEdge,
1408
1553
  },
1554
+ {
1555
+ id: 'case.consumer.cli-edit',
1556
+ plane: 'cli',
1557
+ operation: 'review-and-edit-local-file',
1558
+ acceptedContracts: [{
1559
+ schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
1560
+ versions: [...CLI_CASE_PACK_INPUT_CASE_VERSIONS],
1561
+ mode: 'direct',
1562
+ discriminator: null,
1563
+ }],
1564
+ emittedContract: { schema: CASE_EXPORT_LIFECYCLE_SCHEMA, version: CASE_SCHEMA_VERSION, discriminator: null },
1565
+ shapeIds: CLI_CASE_PACK_INPUT_CASE_VERSIONS.map(version => `case.export.v${version}`),
1566
+ boundProfileIds: ['case.editable.bounds', 'case.portable.bounds'],
1567
+ hookIds: ['case.editable.read', 'case.export.build', 'case.browser.serialise'],
1568
+ serialisationProfileId: null,
1569
+ privacyProfileId: 'case.privacy.portable-output',
1570
+ retentionEffect: 'operator_controlled_output',
1571
+ ...sharedEdge,
1572
+ },
1409
1573
  {
1410
1574
  id: 'case.consumer.cli-pack-build',
1411
1575
  plane: 'cli',
1412
1576
  operation: 'build-case-pack',
1413
1577
  acceptedContracts: [{
1414
1578
  schema: CASE_EXPORT_LIFECYCLE_SCHEMA,
1415
- versions: [CASE_SCHEMA_VERSION],
1579
+ versions: [...CLI_CASE_PACK_INPUT_CASE_VERSIONS],
1416
1580
  mode: 'direct',
1417
1581
  discriminator: null,
1418
1582
  }],
1419
1583
  emittedContract: { schema: CLI_CASE_PACK_SCHEMA, version: CLI_CASE_PACK_VERSION, discriminator: null },
1420
- shapeIds: ['case.export.current', 'case.cli-pack.current'],
1584
+ shapeIds: [...CLI_CASE_PACK_INPUT_CASE_VERSIONS.map((version) => `case.export.v${version}`), 'case.cli-pack.current'],
1421
1585
  boundProfileIds: ['case.portable.bounds', 'case.domain.bounds', 'case.cli-pack.bounds'],
1422
1586
  hookIds: ['case.cli-pack.build', 'case.cli-pack.serialise'],
1423
1587
  serialisationProfileId: 'case.cli-pack.json.v2',
@@ -1441,7 +1605,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1441
1605
  discriminator: null,
1442
1606
  }],
1443
1607
  emittedContract: null,
1444
- shapeIds: ['case.workspace-archive.v5', 'case.workspace-archive.v6', 'case.workspace-archive.v7', 'case.workspace-archive.v8', 'case.workspace-settings.v1'],
1608
+ shapeIds: [...SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS.map(version => `case.workspace-archive.v${version}`), 'case.workspace-settings.v1'],
1445
1609
  boundProfileIds: ['case.workspace.bounds'],
1446
1610
  hookIds: ['case.workspace.verify'],
1447
1611
  serialisationProfileId: null,
@@ -1455,10 +1619,10 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1455
1619
  operation: 'build-workspace',
1456
1620
  acceptedContracts: [],
1457
1621
  emittedContract: { schema: WORKSPACE_ARCHIVE_SCHEMA, version: WORKSPACE_ARCHIVE_VERSION, discriminator: null },
1458
- shapeIds: ['case.workspace-archive.v8'],
1622
+ shapeIds: [`case.workspace-archive.v${WORKSPACE_ARCHIVE_VERSION}`],
1459
1623
  boundProfileIds: ['case.workspace.bounds'],
1460
1624
  hookIds: ['case.workspace.build', 'case.portable.serialise'],
1461
- serialisationProfileId: 'case.workspace.json.v8',
1625
+ serialisationProfileId: `case.workspace.json.v${WORKSPACE_ARCHIVE_VERSION}`,
1462
1626
  privacyProfileId: 'case.privacy.portable-output',
1463
1627
  retentionEffect: 'operator_controlled_output',
1464
1628
  ...sharedEdge,
@@ -1511,7 +1675,7 @@ const CASE_LIFECYCLE_CONSUMERS = Object.freeze([
1511
1675
  version: ENCRYPTED_WORKSPACE_ARCHIVE_VERSION,
1512
1676
  discriminator: null,
1513
1677
  },
1514
- shapeIds: ['case.workspace-archive.v5', 'case.workspace-archive.v6', 'case.workspace-archive.v7', 'case.workspace-archive.v8', 'case.encrypted-workspace-archive.v1'],
1678
+ shapeIds: [...SUPPORTED_WORKSPACE_ARCHIVE_VERSIONS.map(version => `case.workspace-archive.v${version}`), 'case.encrypted-workspace-archive.v1'],
1515
1679
  boundProfileIds: ['case.workspace.bounds', 'case.encrypted-workspace.bounds'],
1516
1680
  hookIds: ['case.encrypted-workspace.build', 'case.portable.serialise'],
1517
1681
  serialisationProfileId: 'case.encrypted-workspace.json.v1',
@@ -1590,7 +1754,6 @@ export const CASE_PORTABILITY_LIFECYCLE_FAMILY = defineSchemaLifecycleFamily({
1590
1754
  ],
1591
1755
  fixtures: CASE_LIFECYCLE_FIXTURES,
1592
1756
  metadata: {
1593
- metadataVersion: 4,
1594
1757
  enforcement: 'declarative_only',
1595
1758
  shapes: CASE_LIFECYCLE_SHAPES,
1596
1759
  boundProfiles: CASE_LIFECYCLE_BOUNDS,