@open-agent-toolkit/cli 0.2.64 → 0.2.66

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 (217) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +19 -1
  3. package/assets/docs/cli-utilities/index.md +3 -0
  4. package/assets/docs/cli-utilities/remote-project-management.md +191 -0
  5. package/assets/docs/contributing/skills.md +15 -5
  6. package/assets/docs/reference/cli-reference.md +12 -1
  7. package/assets/docs/reference/file-locations.md +22 -0
  8. package/assets/docs/reference/oat-directory-structure.md +30 -17
  9. package/assets/docs/reference/troubleshooting.md +44 -0
  10. package/assets/docs/workflows/skills/index.md +33 -0
  11. package/assets/public-package-versions.json +4 -4
  12. package/assets/skills/analyze/SKILL.md +2 -1
  13. package/assets/skills/authoring-docs/SKILL.md +2 -1
  14. package/assets/skills/compare/SKILL.md +2 -1
  15. package/assets/skills/create-agnostic-skill/SKILL.md +2 -1
  16. package/assets/skills/deep-research/SKILL.md +2 -1
  17. package/assets/skills/explainer-kit/SKILL.md +2 -1
  18. package/assets/skills/oat-agent-instructions-analyze/SKILL.md +2 -1
  19. package/assets/skills/oat-agent-instructions-apply/SKILL.md +2 -1
  20. package/assets/skills/oat-brainstorm/SKILL.md +2 -1
  21. package/assets/skills/oat-cursor-cloud-projects/SKILL.md +2 -1
  22. package/assets/skills/oat-dispatch-subagents/SKILL.md +2 -1
  23. package/assets/skills/oat-docs/SKILL.md +2 -1
  24. package/assets/skills/oat-docs-analyze/SKILL.md +2 -1
  25. package/assets/skills/oat-docs-apply/SKILL.md +2 -1
  26. package/assets/skills/oat-docs-authoring/SKILL.md +2 -1
  27. package/assets/skills/oat-docs-bootstrap/SKILL.md +2 -1
  28. package/assets/skills/oat-doctor/SKILL.md +5 -4
  29. package/assets/skills/oat-explainer-kit/SKILL.md +2 -1
  30. package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +322 -2
  31. package/assets/skills/oat-idea-ideate/SKILL.md +2 -1
  32. package/assets/skills/oat-idea-new/SKILL.md +2 -1
  33. package/assets/skills/oat-idea-scratchpad/SKILL.md +2 -1
  34. package/assets/skills/oat-idea-summarize/SKILL.md +2 -1
  35. package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +2 -1
  36. package/assets/skills/oat-pjm-decision/SKILL.md +2 -1
  37. package/assets/skills/oat-pjm-remote/SKILL.md +101 -0
  38. package/assets/skills/oat-pjm-remote/references/external-action-protocol.md +43 -0
  39. package/assets/skills/oat-pjm-review-backlog/SKILL.md +2 -1
  40. package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +2 -1
  41. package/assets/skills/oat-project-autonomous/SKILL.md +2 -1
  42. package/assets/skills/oat-project-capture/SKILL.md +2 -1
  43. package/assets/skills/oat-project-clear-active/SKILL.md +2 -1
  44. package/assets/skills/oat-project-complete/SKILL.md +2 -1
  45. package/assets/skills/oat-project-design/SKILL.md +2 -1
  46. package/assets/skills/oat-project-discover/SKILL.md +2 -1
  47. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +2 -1
  48. package/assets/skills/oat-project-document/SKILL.md +2 -1
  49. package/assets/skills/oat-project-implement/SKILL.md +2 -1
  50. package/assets/skills/oat-project-import-plan/SKILL.md +2 -1
  51. package/assets/skills/oat-project-lite/SKILL.md +2 -1
  52. package/assets/skills/oat-project-new/SKILL.md +2 -1
  53. package/assets/skills/oat-project-next/SKILL.md +2 -1
  54. package/assets/skills/oat-project-open/SKILL.md +2 -1
  55. package/assets/skills/oat-project-plan/SKILL.md +2 -1
  56. package/assets/skills/oat-project-plan-writing/SKILL.md +2 -1
  57. package/assets/skills/oat-project-pr-final/SKILL.md +2 -1
  58. package/assets/skills/oat-project-pr-progress/SKILL.md +2 -1
  59. package/assets/skills/oat-project-progress/SKILL.md +2 -1
  60. package/assets/skills/oat-project-promote-spec-driven/SKILL.md +2 -1
  61. package/assets/skills/oat-project-quick-start/SKILL.md +2 -1
  62. package/assets/skills/oat-project-reconcile/SKILL.md +2 -1
  63. package/assets/skills/oat-project-retro/SKILL.md +2 -1
  64. package/assets/skills/oat-project-retro-file/SKILL.md +2 -1
  65. package/assets/skills/oat-project-review-provide/SKILL.md +2 -1
  66. package/assets/skills/oat-project-review-provide-remote/SKILL.md +2 -1
  67. package/assets/skills/oat-project-review-receive/SKILL.md +2 -1
  68. package/assets/skills/oat-project-review-receive-remote/SKILL.md +2 -1
  69. package/assets/skills/oat-project-revise/SKILL.md +2 -1
  70. package/assets/skills/oat-project-spec/SKILL.md +2 -1
  71. package/assets/skills/oat-project-split/SKILL.md +2 -1
  72. package/assets/skills/oat-project-summary/SKILL.md +2 -1
  73. package/assets/skills/oat-repo-improve/SKILL.md +1 -1
  74. package/assets/skills/oat-repo-knowledge-index/SKILL.md +2 -1
  75. package/assets/skills/oat-repo-maintainability-review/SKILL.md +2 -1
  76. package/assets/skills/oat-review-provide/SKILL.md +2 -1
  77. package/assets/skills/oat-review-provide-remote/SKILL.md +2 -1
  78. package/assets/skills/oat-review-receive/SKILL.md +2 -1
  79. package/assets/skills/oat-review-receive-remote/SKILL.md +2 -1
  80. package/assets/skills/oat-wave-execute/SKILL.md +2 -1
  81. package/assets/skills/oat-wave-program/SKILL.md +2 -1
  82. package/assets/skills/oat-worktree-bootstrap/SKILL.md +2 -1
  83. package/assets/skills/oat-worktree-bootstrap-auto/SKILL.md +2 -1
  84. package/assets/skills/oat-wrap-up/SKILL.md +2 -1
  85. package/assets/skills/recon/SKILL.md +2 -1
  86. package/assets/skills/skeptic/SKILL.md +2 -1
  87. package/assets/skills/subagent-orchestration/SKILL.md +2 -1
  88. package/assets/skills/synthesize/SKILL.md +2 -1
  89. package/dist/commands/backlog/new.d.ts +1 -0
  90. package/dist/commands/backlog/new.d.ts.map +1 -1
  91. package/dist/commands/backlog/new.js +3 -1
  92. package/dist/commands/config/index.d.ts +1 -0
  93. package/dist/commands/config/index.d.ts.map +1 -1
  94. package/dist/commands/config/index.js +232 -1
  95. package/dist/commands/pjm/doctor.d.ts +2 -0
  96. package/dist/commands/pjm/doctor.d.ts.map +1 -1
  97. package/dist/commands/pjm/doctor.js +71 -1
  98. package/dist/commands/pjm/index.d.ts +2 -0
  99. package/dist/commands/pjm/index.d.ts.map +1 -1
  100. package/dist/commands/pjm/index.js +3 -0
  101. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts +60 -0
  102. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.d.ts.map +1 -0
  103. package/dist/commands/pjm/remote/__integration__/lifecycle-harness.js +151 -0
  104. package/dist/commands/pjm/remote/association.d.ts +27 -0
  105. package/dist/commands/pjm/remote/association.d.ts.map +1 -0
  106. package/dist/commands/pjm/remote/association.js +68 -0
  107. package/dist/commands/pjm/remote/authority.d.ts +210 -0
  108. package/dist/commands/pjm/remote/authority.d.ts.map +1 -0
  109. package/dist/commands/pjm/remote/authority.js +341 -0
  110. package/dist/commands/pjm/remote/batch.d.ts +15 -0
  111. package/dist/commands/pjm/remote/batch.d.ts.map +1 -0
  112. package/dist/commands/pjm/remote/batch.js +126 -0
  113. package/dist/commands/pjm/remote/closeout.d.ts +69 -0
  114. package/dist/commands/pjm/remote/closeout.d.ts.map +1 -0
  115. package/dist/commands/pjm/remote/closeout.js +208 -0
  116. package/dist/commands/pjm/remote/create-binding.d.ts +62 -0
  117. package/dist/commands/pjm/remote/create-binding.d.ts.map +1 -0
  118. package/dist/commands/pjm/remote/create-binding.js +126 -0
  119. package/dist/commands/pjm/remote/credential-safety.d.ts +7 -0
  120. package/dist/commands/pjm/remote/credential-safety.d.ts.map +1 -0
  121. package/dist/commands/pjm/remote/credential-safety.js +21 -0
  122. package/dist/commands/pjm/remote/discussion.d.ts +49 -0
  123. package/dist/commands/pjm/remote/discussion.d.ts.map +1 -0
  124. package/dist/commands/pjm/remote/discussion.js +124 -0
  125. package/dist/commands/pjm/remote/doctor.d.ts +22 -0
  126. package/dist/commands/pjm/remote/doctor.d.ts.map +1 -0
  127. package/dist/commands/pjm/remote/doctor.js +380 -0
  128. package/dist/commands/pjm/remote/external-action.d.ts +208 -0
  129. package/dist/commands/pjm/remote/external-action.d.ts.map +1 -0
  130. package/dist/commands/pjm/remote/external-action.js +367 -0
  131. package/dist/commands/pjm/remote/host-execution.d.ts +28 -0
  132. package/dist/commands/pjm/remote/host-execution.d.ts.map +1 -0
  133. package/dist/commands/pjm/remote/host-execution.js +63 -0
  134. package/dist/commands/pjm/remote/index.d.ts +41 -0
  135. package/dist/commands/pjm/remote/index.d.ts.map +1 -0
  136. package/dist/commands/pjm/remote/index.js +280 -0
  137. package/dist/commands/pjm/remote/jira-adf.d.ts +35 -0
  138. package/dist/commands/pjm/remote/jira-adf.d.ts.map +1 -0
  139. package/dist/commands/pjm/remote/jira-adf.js +169 -0
  140. package/dist/commands/pjm/remote/lifecycle.d.ts +163 -0
  141. package/dist/commands/pjm/remote/lifecycle.d.ts.map +1 -0
  142. package/dist/commands/pjm/remote/lifecycle.js +224 -0
  143. package/dist/commands/pjm/remote/local-projection.d.ts +30 -0
  144. package/dist/commands/pjm/remote/local-projection.d.ts.map +1 -0
  145. package/dist/commands/pjm/remote/local-projection.js +95 -0
  146. package/dist/commands/pjm/remote/managed-markdown.d.ts +30 -0
  147. package/dist/commands/pjm/remote/managed-markdown.d.ts.map +1 -0
  148. package/dist/commands/pjm/remote/managed-markdown.js +128 -0
  149. package/dist/commands/pjm/remote/migrate.d.ts +14 -0
  150. package/dist/commands/pjm/remote/migrate.d.ts.map +1 -0
  151. package/dist/commands/pjm/remote/migrate.js +32 -0
  152. package/dist/commands/pjm/remote/operation-state.d.ts +15 -0
  153. package/dist/commands/pjm/remote/operation-state.d.ts.map +1 -0
  154. package/dist/commands/pjm/remote/operation-state.js +143 -0
  155. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts +25 -0
  156. package/dist/commands/pjm/remote/outbound-projection-safety.d.ts.map +1 -0
  157. package/dist/commands/pjm/remote/outbound-projection-safety.js +99 -0
  158. package/dist/commands/pjm/remote/output.d.ts +60 -0
  159. package/dist/commands/pjm/remote/output.d.ts.map +1 -0
  160. package/dist/commands/pjm/remote/output.js +48 -0
  161. package/dist/commands/pjm/remote/preview.d.ts +88 -0
  162. package/dist/commands/pjm/remote/preview.d.ts.map +1 -0
  163. package/dist/commands/pjm/remote/preview.js +154 -0
  164. package/dist/commands/pjm/remote/provider-conformance.d.ts +13 -0
  165. package/dist/commands/pjm/remote/provider-conformance.d.ts.map +1 -0
  166. package/dist/commands/pjm/remote/provider-conformance.js +37 -0
  167. package/dist/commands/pjm/remote/provider.d.ts +57 -0
  168. package/dist/commands/pjm/remote/provider.d.ts.map +1 -0
  169. package/dist/commands/pjm/remote/provider.js +28 -0
  170. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts +46 -0
  171. package/dist/commands/pjm/remote/providers/github-publication-safety.d.ts.map +1 -0
  172. package/dist/commands/pjm/remote/providers/github-publication-safety.js +126 -0
  173. package/dist/commands/pjm/remote/providers/github.d.ts +207 -0
  174. package/dist/commands/pjm/remote/providers/github.d.ts.map +1 -0
  175. package/dist/commands/pjm/remote/providers/github.js +1456 -0
  176. package/dist/commands/pjm/remote/providers/jira.d.ts +245 -0
  177. package/dist/commands/pjm/remote/providers/jira.d.ts.map +1 -0
  178. package/dist/commands/pjm/remote/providers/jira.js +1650 -0
  179. package/dist/commands/pjm/remote/providers/linear.d.ts +182 -0
  180. package/dist/commands/pjm/remote/providers/linear.d.ts.map +1 -0
  181. package/dist/commands/pjm/remote/providers/linear.js +1289 -0
  182. package/dist/commands/pjm/remote/purpose-policy.d.ts +29 -0
  183. package/dist/commands/pjm/remote/purpose-policy.d.ts.map +1 -0
  184. package/dist/commands/pjm/remote/purpose-policy.js +118 -0
  185. package/dist/commands/pjm/remote/reconcile.d.ts +42 -0
  186. package/dist/commands/pjm/remote/reconcile.d.ts.map +1 -0
  187. package/dist/commands/pjm/remote/reconcile.js +101 -0
  188. package/dist/commands/pjm/remote/resolution.d.ts +122 -0
  189. package/dist/commands/pjm/remote/resolution.d.ts.map +1 -0
  190. package/dist/commands/pjm/remote/resolution.js +243 -0
  191. package/dist/commands/pjm/remote/schema.d.ts +9309 -0
  192. package/dist/commands/pjm/remote/schema.d.ts.map +1 -0
  193. package/dist/commands/pjm/remote/schema.js +1503 -0
  194. package/dist/commands/pjm/remote/service.d.ts +20 -0
  195. package/dist/commands/pjm/remote/service.d.ts.map +1 -0
  196. package/dist/commands/pjm/remote/service.js +5121 -0
  197. package/dist/commands/pjm/remote/shared-storage.d.ts +36 -0
  198. package/dist/commands/pjm/remote/shared-storage.d.ts.map +1 -0
  199. package/dist/commands/pjm/remote/shared-storage.js +110 -0
  200. package/dist/commands/pjm/remote/snapshot.d.ts +32 -0
  201. package/dist/commands/pjm/remote/snapshot.d.ts.map +1 -0
  202. package/dist/commands/pjm/remote/snapshot.js +140 -0
  203. package/dist/commands/pjm/remote/storage-locator.d.ts +29 -0
  204. package/dist/commands/pjm/remote/storage-locator.d.ts.map +1 -0
  205. package/dist/commands/pjm/remote/storage-locator.js +62 -0
  206. package/dist/commands/pjm/remote/store.d.ts +85 -0
  207. package/dist/commands/pjm/remote/store.d.ts.map +1 -0
  208. package/dist/commands/pjm/remote/store.js +553 -0
  209. package/dist/commands/pjm/remote/verification.d.ts +40 -0
  210. package/dist/commands/pjm/remote/verification.d.ts.map +1 -0
  211. package/dist/commands/pjm/remote/verification.js +100 -0
  212. package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -1
  213. package/dist/commands/tools/shared/pack-manifest.js +1 -0
  214. package/dist/config/oat-config.d.ts +26 -0
  215. package/dist/config/oat-config.d.ts.map +1 -1
  216. package/dist/config/oat-config.js +186 -2
  217. package/package.json +2 -2
@@ -0,0 +1,29 @@
1
+ import type { OatPjmRemoteOperationClass } from '../../../config/oat-config.js';
2
+ import type { RemoteBindingMetadata } from './schema.js';
3
+ export type BindingPurpose = RemoteBindingMetadata['purposes'][number];
4
+ export type SharedRemoteField = 'title' | 'description' | 'priority';
5
+ export type FieldDirection = 'inbound' | 'outbound';
6
+ export type CloseoutAnnotationPolicy = 'propose' | 'none';
7
+ export type CloseoutTransitionPolicy = 'propose' | 'provider-automation' | 'none';
8
+ export interface PurposePolicy {
9
+ fields: Record<SharedRemoteField, readonly FieldDirection[]>;
10
+ lifecycle: readonly OatPjmRemoteOperationClass[];
11
+ closeout: {
12
+ annotation: CloseoutAnnotationPolicy;
13
+ transition: CloseoutTransitionPolicy;
14
+ };
15
+ }
16
+ export interface ComposedPurposePolicy {
17
+ purposes: BindingPurpose[];
18
+ fields: Record<SharedRemoteField, FieldDirection[]>;
19
+ lifecycle: OatPjmRemoteOperationClass[];
20
+ closeout: {
21
+ annotation: CloseoutAnnotationPolicy;
22
+ transition: CloseoutTransitionPolicy | 'choice-required';
23
+ };
24
+ choiceRequired: boolean;
25
+ noOp: boolean;
26
+ }
27
+ export declare const PURPOSE_POLICIES: Readonly<Record<BindingPurpose, PurposePolicy>>;
28
+ export declare function composePurposePolicies(purposes: readonly BindingPurpose[]): ComposedPurposePolicy;
29
+ //# sourceMappingURL=purpose-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purpose-policy.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/purpose-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAErE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,aAAa,GAAG,UAAU,CAAC;AACrE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,CAAC;AACpD,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,MAAM,CAAC;AAC1D,MAAM,MAAM,wBAAwB,GAChC,SAAS,GACT,qBAAqB,GACrB,MAAM,CAAC;AAEX,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC,iBAAiB,EAAE,SAAS,cAAc,EAAE,CAAC,CAAC;IAC7D,SAAS,EAAE,SAAS,0BAA0B,EAAE,CAAC;IACjD,QAAQ,EAAE;QACR,UAAU,EAAE,wBAAwB,CAAC;QACrC,UAAU,EAAE,wBAAwB,CAAC;KACtC,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,iBAAiB,EAAE,cAAc,EAAE,CAAC,CAAC;IACpD,SAAS,EAAE,0BAA0B,EAAE,CAAC;IACxC,QAAQ,EAAE;QACR,UAAU,EAAE,wBAAwB,CAAC;QACrC,UAAU,EAAE,wBAAwB,GAAG,iBAAiB,CAAC;KAC1D,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;IACxB,IAAI,EAAE,OAAO,CAAC;CACf;AAWD,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,cAAc,EAAE,aAAa,CAAC,CAkDzE,CAAC;AAkBL,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAClC,qBAAqB,CA2DvB"}
@@ -0,0 +1,118 @@
1
+ const INBOUND = Object.freeze(['inbound']);
2
+ const BIDIRECTIONAL = Object.freeze(['inbound', 'outbound']);
3
+ const NO_DIRECTIONS = Object.freeze([]);
4
+ const CLOSEOUT_LIFECYCLE = Object.freeze([
5
+ 'annotate',
6
+ 'transition',
7
+ ]);
8
+ const NO_LIFECYCLE = Object.freeze([]);
9
+ export const PURPOSE_POLICIES = Object.freeze({
10
+ source: Object.freeze({
11
+ fields: Object.freeze({
12
+ title: INBOUND,
13
+ description: INBOUND,
14
+ priority: INBOUND,
15
+ }),
16
+ lifecycle: CLOSEOUT_LIFECYCLE,
17
+ closeout: Object.freeze({
18
+ annotation: 'propose',
19
+ transition: 'propose',
20
+ }),
21
+ }),
22
+ planning: Object.freeze({
23
+ fields: Object.freeze({
24
+ title: BIDIRECTIONAL,
25
+ description: BIDIRECTIONAL,
26
+ priority: BIDIRECTIONAL,
27
+ }),
28
+ lifecycle: CLOSEOUT_LIFECYCLE,
29
+ closeout: Object.freeze({
30
+ annotation: 'propose',
31
+ transition: 'propose',
32
+ }),
33
+ }),
34
+ delivery: Object.freeze({
35
+ fields: Object.freeze({
36
+ title: NO_DIRECTIONS,
37
+ description: NO_DIRECTIONS,
38
+ priority: NO_DIRECTIONS,
39
+ }),
40
+ lifecycle: NO_LIFECYCLE,
41
+ closeout: Object.freeze({
42
+ annotation: 'none',
43
+ transition: 'provider-automation',
44
+ }),
45
+ }),
46
+ reference: Object.freeze({
47
+ fields: Object.freeze({
48
+ title: NO_DIRECTIONS,
49
+ description: NO_DIRECTIONS,
50
+ priority: NO_DIRECTIONS,
51
+ }),
52
+ lifecycle: NO_LIFECYCLE,
53
+ closeout: Object.freeze({
54
+ annotation: 'none',
55
+ transition: 'none',
56
+ }),
57
+ }),
58
+ });
59
+ const SHARED_FIELDS = [
60
+ 'title',
61
+ 'description',
62
+ 'priority',
63
+ ];
64
+ const OPERATION_ORDER = [
65
+ 'create',
66
+ 'update-fields',
67
+ 'annotate',
68
+ 'transition',
69
+ 'delete',
70
+ 'relink',
71
+ 'detach',
72
+ 'recreate',
73
+ ];
74
+ export function composePurposePolicies(purposes) {
75
+ if (purposes.length === 0) {
76
+ throw new Error('A binding must declare at least one purpose.');
77
+ }
78
+ const uniquePurposes = new Set();
79
+ for (const purpose of purposes) {
80
+ if (!(purpose in PURPOSE_POLICIES)) {
81
+ throw new Error(`Unknown purpose '${String(purpose)}'.`);
82
+ }
83
+ if (uniquePurposes.has(purpose)) {
84
+ throw new Error('Binding purposes must be unique.');
85
+ }
86
+ uniquePurposes.add(purpose);
87
+ }
88
+ const policies = purposes.map((purpose) => PURPOSE_POLICIES[purpose]);
89
+ const fields = Object.fromEntries(SHARED_FIELDS.map((field) => [
90
+ field,
91
+ intersectOrdered(policies.map((policy) => policy.fields[field]), ['inbound', 'outbound']),
92
+ ]));
93
+ const lifecycle = intersectOrdered(policies.map((policy) => policy.lifecycle), OPERATION_ORDER);
94
+ const annotation = policies.every((policy) => policy.closeout.annotation === 'propose')
95
+ ? 'propose'
96
+ : 'none';
97
+ const transitionValues = new Set(policies.map((policy) => policy.closeout.transition));
98
+ const nonNoneTransitions = new Set([...transitionValues].filter((value) => value !== 'none'));
99
+ const transition = transitionValues.has('none')
100
+ ? 'none'
101
+ : nonNoneTransitions.size > 1
102
+ ? 'choice-required'
103
+ : ([...nonNoneTransitions][0] ?? 'none');
104
+ const choiceRequired = transition === 'choice-required';
105
+ const noOp = SHARED_FIELDS.every((field) => fields[field].length === 0) &&
106
+ lifecycle.length === 0;
107
+ return {
108
+ purposes: [...purposes],
109
+ fields,
110
+ lifecycle,
111
+ closeout: { annotation, transition },
112
+ choiceRequired,
113
+ noOp,
114
+ };
115
+ }
116
+ function intersectOrdered(values, order) {
117
+ return order.filter((candidate) => values.every((value) => value.includes(candidate)));
118
+ }
@@ -0,0 +1,42 @@
1
+ import type { OatPjmRemoteDescriptionMode } from '../../../config/oat-config.js';
2
+ import type { FieldDirection, SharedRemoteField } from './purpose-policy.js';
3
+ export type ReconciliationClass = 'no-change' | 'local-only' | 'remote-only' | 'converged' | 'disjoint' | 'conflict' | 'remote-anomaly' | 'uncertain-operation';
4
+ export type ReconciliationDirection = 'inbound' | 'outbound' | 'none' | 'choice-required';
5
+ export type RemoteLifecycleCondition = 'active' | 'archived' | 'moved' | 'missing-or-invisible' | 'deleted-confirmed' | 'temporarily-unavailable';
6
+ export interface SharedFieldValues {
7
+ title: string | null;
8
+ description: string | null;
9
+ priority: string | null;
10
+ }
11
+ export interface ReconciliationInput {
12
+ base: SharedFieldValues;
13
+ local: SharedFieldValues;
14
+ remote: SharedFieldValues;
15
+ fieldDirections: Record<SharedRemoteField, readonly FieldDirection[]>;
16
+ descriptionMode: OatPjmRemoteDescriptionMode;
17
+ priorityMapping: boolean;
18
+ remoteLifecycle: RemoteLifecycleCondition;
19
+ uncertainOperation: boolean;
20
+ }
21
+ export interface FieldReconciliation {
22
+ field: SharedRemoteField;
23
+ base: string | null;
24
+ local: string | null;
25
+ remote: string | null;
26
+ classification: Exclude<ReconciliationClass, 'disjoint' | 'remote-anomaly' | 'uncertain-operation'>;
27
+ proposedDirection: ReconciliationDirection;
28
+ }
29
+ export interface BindingReconciliationPreview {
30
+ classification: ReconciliationClass;
31
+ fields: {
32
+ title: FieldReconciliation;
33
+ description: FieldReconciliation & {
34
+ scope: 'none' | 'managed-section' | 'full';
35
+ };
36
+ priority?: FieldReconciliation;
37
+ };
38
+ choiceRequired: boolean;
39
+ blockedBy: string[];
40
+ }
41
+ export declare function reconcileBinding(input: ReconciliationInput): BindingReconciliationPreview;
42
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1E,MAAM,MAAM,mBAAmB,GAC3B,WAAW,GACX,YAAY,GACZ,aAAa,GACb,WAAW,GACX,UAAU,GACV,UAAU,GACV,gBAAgB,GAChB,qBAAqB,CAAC;AAE1B,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,UAAU,GACV,MAAM,GACN,iBAAiB,CAAC;AAEtB,MAAM,MAAM,wBAAwB,GAChC,QAAQ,GACR,UAAU,GACV,OAAO,GACP,sBAAsB,GACtB,mBAAmB,GACnB,yBAAyB,CAAC;AAE9B,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,iBAAiB,CAAC;IACzB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC,iBAAiB,EAAE,SAAS,cAAc,EAAE,CAAC,CAAC;IACtE,eAAe,EAAE,2BAA2B,CAAC;IAC7C,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,wBAAwB,CAAC;IAC1C,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,cAAc,EAAE,OAAO,CACrB,mBAAmB,EACnB,UAAU,GAAG,gBAAgB,GAAG,qBAAqB,CACtD,CAAC;IACF,iBAAiB,EAAE,uBAAuB,CAAC;CAC5C;AAED,MAAM,WAAW,4BAA4B;IAC3C,cAAc,EAAE,mBAAmB,CAAC;IACpC,MAAM,EAAE;QACN,KAAK,EAAE,mBAAmB,CAAC;QAC3B,WAAW,EAAE,mBAAmB,GAAG;YACjC,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,MAAM,CAAC;SAC5C,CAAC;QACF,QAAQ,CAAC,EAAE,mBAAmB,CAAC;KAChC,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,mBAAmB,GACzB,4BAA4B,CA6C9B"}
@@ -0,0 +1,101 @@
1
+ export function reconcileBinding(input) {
2
+ const blockedBy = [];
3
+ if (!input.priorityMapping) {
4
+ blockedBy.push('priority-mapping-unavailable');
5
+ }
6
+ const title = reconcileField('title', input);
7
+ const description = {
8
+ ...reconcileField('description', input),
9
+ scope: descriptionScope(input.descriptionMode),
10
+ };
11
+ if (input.descriptionMode === 'none' &&
12
+ description.proposedDirection === 'outbound') {
13
+ description.proposedDirection = 'none';
14
+ }
15
+ const priority = input.priorityMapping
16
+ ? reconcileField('priority', input)
17
+ : undefined;
18
+ const fieldList = [title, description, ...(priority ? [priority] : [])];
19
+ let classification = classifyBinding(fieldList);
20
+ if (input.uncertainOperation) {
21
+ classification = 'uncertain-operation';
22
+ blockedBy.unshift('uncertain-operation');
23
+ blockDirections(fieldList);
24
+ }
25
+ else if (input.remoteLifecycle !== 'active') {
26
+ classification = 'remote-anomaly';
27
+ blockedBy.unshift(`remote-lifecycle:${input.remoteLifecycle}`);
28
+ blockDirections(fieldList);
29
+ }
30
+ return {
31
+ classification,
32
+ fields: {
33
+ title,
34
+ description,
35
+ ...(priority ? { priority } : {}),
36
+ },
37
+ choiceRequired: fieldList.some((field) => field.proposedDirection === 'choice-required'),
38
+ blockedBy,
39
+ };
40
+ }
41
+ function reconcileField(field, input) {
42
+ const base = input.base[field];
43
+ const local = input.local[field];
44
+ const remote = input.remote[field];
45
+ const classification = classifyField(base, local, remote);
46
+ return {
47
+ field,
48
+ base,
49
+ local,
50
+ remote,
51
+ classification,
52
+ proposedDirection: proposedDirection(classification, input.fieldDirections[field]),
53
+ };
54
+ }
55
+ function classifyField(base, local, remote) {
56
+ if (local === remote)
57
+ return local === base ? 'no-change' : 'converged';
58
+ const localChanged = local !== base;
59
+ const remoteChanged = remote !== base;
60
+ if (localChanged && remoteChanged)
61
+ return 'conflict';
62
+ if (localChanged)
63
+ return 'local-only';
64
+ if (remoteChanged)
65
+ return 'remote-only';
66
+ return 'no-change';
67
+ }
68
+ function proposedDirection(classification, allowed) {
69
+ if (classification === 'conflict')
70
+ return 'choice-required';
71
+ if (classification === 'local-only' && allowed.includes('outbound')) {
72
+ return 'outbound';
73
+ }
74
+ if (classification === 'remote-only' && allowed.includes('inbound')) {
75
+ return 'inbound';
76
+ }
77
+ return 'none';
78
+ }
79
+ function classifyBinding(fields) {
80
+ const classes = new Set(fields.map((field) => field.classification));
81
+ if (classes.has('conflict'))
82
+ return 'conflict';
83
+ if (classes.has('local-only') && classes.has('remote-only'))
84
+ return 'disjoint';
85
+ if (classes.has('local-only'))
86
+ return 'local-only';
87
+ if (classes.has('remote-only'))
88
+ return 'remote-only';
89
+ if (classes.has('converged'))
90
+ return 'converged';
91
+ return 'no-change';
92
+ }
93
+ function descriptionScope(mode) {
94
+ if (mode === 'replace')
95
+ return 'full';
96
+ return mode;
97
+ }
98
+ function blockDirections(fields) {
99
+ for (const field of fields)
100
+ field.proposedDirection = 'none';
101
+ }
@@ -0,0 +1,122 @@
1
+ import { type RemoteProvider } from './provider.js';
2
+ export interface ResolutionIdentity {
3
+ stableId: string;
4
+ context: Record<string, string | undefined>;
5
+ aliases: Array<{
6
+ kind: 'url' | 'display' | 'key';
7
+ value: string;
8
+ }>;
9
+ }
10
+ export interface ResolutionBinding {
11
+ bindingId: string;
12
+ provider: RemoteProvider;
13
+ targetRef: string;
14
+ remoteIdentity: ResolutionIdentity;
15
+ identityHistory: Array<{
16
+ provider: RemoteProvider;
17
+ identity: ResolutionIdentity;
18
+ replacedAt: string;
19
+ replacedByOperationId: string;
20
+ }>;
21
+ lifecycle: 'active' | 'blocked' | 'tombstoned';
22
+ snapshotDigest: string | null;
23
+ }
24
+ export interface ResolutionApproval {
25
+ previewDigest: string;
26
+ approvedAt: string;
27
+ source: string;
28
+ }
29
+ export interface ResolutionJournal {
30
+ schemaVersion: 1;
31
+ operationId: string;
32
+ bindingId: string;
33
+ kind: 'relink' | 'detach' | 'recreate';
34
+ previewDigest: string;
35
+ state: 'pending' | 'blocked' | 'partial' | 'uncertain' | 'complete';
36
+ bindingTransitionCompleted: boolean;
37
+ associationCompleted: boolean;
38
+ resultingBinding: ResolutionBinding | null;
39
+ searchCompleted?: boolean;
40
+ createIntentPersisted?: boolean;
41
+ createAttempted?: boolean;
42
+ createdAt: string;
43
+ updatedAt: string;
44
+ }
45
+ export interface ResolutionStore {
46
+ findByIdentity(provider: RemoteProvider, identity: ResolutionIdentity): Promise<string | null>;
47
+ writeBinding(binding: ResolutionBinding): Promise<void>;
48
+ writeAssociation(input: {
49
+ targetRef: string;
50
+ bindingId: string | null;
51
+ referenceRef: string | null;
52
+ }): Promise<void>;
53
+ readJournal(operationId: string): Promise<ResolutionJournal | null>;
54
+ writeJournal(journal: ResolutionJournal): Promise<void>;
55
+ }
56
+ interface CommonResolutionInput {
57
+ operationId: string;
58
+ binding: ResolutionBinding;
59
+ now: string;
60
+ previewDigest: string;
61
+ approval?: ResolutionApproval;
62
+ crash?: (point: 'after-binding' | 'after-association') => void;
63
+ }
64
+ export declare function relinkBinding(input: CommonResolutionInput & {
65
+ replacement: {
66
+ identity: ResolutionIdentity;
67
+ verifiedAt: string;
68
+ evidenceDigest: string;
69
+ };
70
+ }, store: ResolutionStore): Promise<{
71
+ binding: ResolutionBinding;
72
+ journal: ResolutionJournal;
73
+ }>;
74
+ export declare function detachBinding(input: CommonResolutionInput & {
75
+ keepReference: boolean;
76
+ }, store: ResolutionStore): Promise<{
77
+ binding: ResolutionBinding;
78
+ journal: ResolutionJournal;
79
+ }>;
80
+ export type DuplicateSearchOutcome = {
81
+ kind: 'found-existing';
82
+ replacement: {
83
+ identity: ResolutionIdentity;
84
+ verifiedAt: string;
85
+ evidenceDigest: string;
86
+ };
87
+ } | {
88
+ kind: 'search-unavailable';
89
+ } | {
90
+ kind: 'ambiguous';
91
+ candidates: string[];
92
+ } | {
93
+ kind: 'no-match';
94
+ };
95
+ export type RecreateAttemptOutcome = {
96
+ kind: 'committed';
97
+ replacement: {
98
+ identity: ResolutionIdentity;
99
+ verifiedAt: string;
100
+ evidenceDigest: string;
101
+ };
102
+ } | {
103
+ kind: 'uncertain';
104
+ } | {
105
+ kind: 'rejected';
106
+ };
107
+ export declare function recreateBinding(input: CommonResolutionInput & {
108
+ searchDuplicates(): Promise<DuplicateSearchOutcome>;
109
+ createReplacement(): Promise<RecreateAttemptOutcome>;
110
+ }, store: ResolutionStore): Promise<{
111
+ status: 'verified' | 'blocked' | 'uncertain';
112
+ binding: ResolutionBinding;
113
+ journal: ResolutionJournal;
114
+ }>;
115
+ export declare function buildResolutionPreviewDigest(input: {
116
+ kind: ResolutionJournal['kind'];
117
+ binding: ResolutionBinding;
118
+ replacement?: ResolutionIdentity;
119
+ keepReference?: boolean;
120
+ }): string;
121
+ export {};
122
+ //# sourceMappingURL=resolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolution.d.ts","sourceRoot":"","sources":["../../../../src/commands/pjm/remote/resolution.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ import { semanticDigest } from './provider.js';
2
+ export async function relinkBinding(input, store) {
3
+ assertFreshApproval(input);
4
+ assertVerifiedReplacement(input.replacement);
5
+ const duplicate = await store.findByIdentity(input.binding.provider, input.replacement.identity);
6
+ if (duplicate && duplicate !== input.binding.bindingId) {
7
+ throw new Error(`Replacement identity is already bound by '${duplicate}'.`);
8
+ }
9
+ let journal = await store.readJournal(input.operationId);
10
+ assertJournalMatches(journal, input, 'relink');
11
+ if (!journal) {
12
+ journal = createJournal(input, 'relink');
13
+ await store.writeJournal(journal);
14
+ }
15
+ if (!journal.bindingTransitionCompleted) {
16
+ const nextBinding = {
17
+ ...input.binding,
18
+ remoteIdentity: structuredClone(input.replacement.identity),
19
+ identityHistory: [
20
+ ...input.binding.identityHistory,
21
+ {
22
+ provider: input.binding.provider,
23
+ identity: structuredClone(input.binding.remoteIdentity),
24
+ replacedAt: input.now,
25
+ replacedByOperationId: input.operationId,
26
+ },
27
+ ],
28
+ lifecycle: 'active',
29
+ };
30
+ await store.writeBinding(nextBinding);
31
+ journal = {
32
+ ...journal,
33
+ state: 'partial',
34
+ bindingTransitionCompleted: true,
35
+ resultingBinding: nextBinding,
36
+ updatedAt: input.now,
37
+ };
38
+ await store.writeJournal(journal);
39
+ input.crash?.('after-binding');
40
+ }
41
+ if (!journal.associationCompleted) {
42
+ await store.writeAssociation({
43
+ targetRef: input.binding.targetRef,
44
+ bindingId: input.binding.bindingId,
45
+ referenceRef: null,
46
+ });
47
+ journal = completeAssociation(journal, input.now);
48
+ await store.writeJournal(journal);
49
+ input.crash?.('after-association');
50
+ }
51
+ return { binding: journal.resultingBinding, journal };
52
+ }
53
+ export async function detachBinding(input, store) {
54
+ assertFreshApproval(input);
55
+ let journal = await store.readJournal(input.operationId);
56
+ assertJournalMatches(journal, input, 'detach');
57
+ if (!journal) {
58
+ journal = createJournal(input, 'detach');
59
+ await store.writeJournal(journal);
60
+ }
61
+ if (!journal.bindingTransitionCompleted) {
62
+ const nextBinding = {
63
+ ...input.binding,
64
+ lifecycle: 'tombstoned',
65
+ };
66
+ await store.writeBinding(nextBinding);
67
+ journal = {
68
+ ...journal,
69
+ state: 'partial',
70
+ bindingTransitionCompleted: true,
71
+ resultingBinding: nextBinding,
72
+ updatedAt: input.now,
73
+ };
74
+ await store.writeJournal(journal);
75
+ input.crash?.('after-binding');
76
+ }
77
+ if (!journal.associationCompleted) {
78
+ await store.writeAssociation({
79
+ targetRef: input.binding.targetRef,
80
+ bindingId: null,
81
+ referenceRef: input.keepReference
82
+ ? `${input.binding.provider}:${input.binding.remoteIdentity.stableId}`
83
+ : null,
84
+ });
85
+ journal = completeAssociation(journal, input.now);
86
+ await store.writeJournal(journal);
87
+ input.crash?.('after-association');
88
+ }
89
+ return { binding: journal.resultingBinding, journal };
90
+ }
91
+ export async function recreateBinding(input, store) {
92
+ assertFreshApproval(input);
93
+ let journal = await store.readJournal(input.operationId);
94
+ assertJournalMatches(journal, input, 'recreate');
95
+ if (journal?.createAttempted && !journal.bindingTransitionCompleted) {
96
+ return {
97
+ status: 'uncertain',
98
+ binding: { ...input.binding, lifecycle: 'blocked' },
99
+ journal,
100
+ };
101
+ }
102
+ if (!journal) {
103
+ journal = createJournal(input, 'recreate');
104
+ await store.writeJournal(journal);
105
+ }
106
+ const search = await input.searchDuplicates();
107
+ journal = {
108
+ ...journal,
109
+ searchCompleted: true,
110
+ updatedAt: input.now,
111
+ };
112
+ await store.writeJournal(journal);
113
+ if (search.kind === 'search-unavailable' || search.kind === 'ambiguous') {
114
+ journal = { ...journal, state: 'blocked', updatedAt: input.now };
115
+ await store.writeJournal(journal);
116
+ return { status: 'blocked', binding: input.binding, journal };
117
+ }
118
+ let replacement = null;
119
+ if (search.kind === 'found-existing') {
120
+ replacement = search.replacement;
121
+ }
122
+ else {
123
+ journal = {
124
+ ...journal,
125
+ createIntentPersisted: true,
126
+ updatedAt: input.now,
127
+ };
128
+ await store.writeJournal(journal);
129
+ journal = { ...journal, createAttempted: true, updatedAt: input.now };
130
+ await store.writeJournal(journal);
131
+ const create = await input.createReplacement();
132
+ if (create.kind !== 'committed') {
133
+ journal = {
134
+ ...journal,
135
+ state: create.kind === 'uncertain' ? 'uncertain' : 'blocked',
136
+ updatedAt: input.now,
137
+ };
138
+ await store.writeJournal(journal);
139
+ return {
140
+ status: create.kind === 'uncertain' ? 'uncertain' : 'blocked',
141
+ binding: { ...input.binding, lifecycle: 'blocked' },
142
+ journal,
143
+ };
144
+ }
145
+ replacement = create.replacement;
146
+ }
147
+ assertVerifiedReplacement(replacement);
148
+ const duplicate = await store.findByIdentity(input.binding.provider, replacement.identity);
149
+ if (duplicate && duplicate !== input.binding.bindingId) {
150
+ throw new Error(`Replacement identity is already bound by '${duplicate}'.`);
151
+ }
152
+ if (!journal.bindingTransitionCompleted) {
153
+ const nextBinding = {
154
+ ...input.binding,
155
+ remoteIdentity: structuredClone(replacement.identity),
156
+ identityHistory: [
157
+ ...input.binding.identityHistory,
158
+ {
159
+ provider: input.binding.provider,
160
+ identity: structuredClone(input.binding.remoteIdentity),
161
+ replacedAt: input.now,
162
+ replacedByOperationId: input.operationId,
163
+ },
164
+ ],
165
+ lifecycle: 'active',
166
+ };
167
+ await store.writeBinding(nextBinding);
168
+ journal = {
169
+ ...journal,
170
+ state: 'partial',
171
+ bindingTransitionCompleted: true,
172
+ resultingBinding: nextBinding,
173
+ updatedAt: input.now,
174
+ };
175
+ await store.writeJournal(journal);
176
+ input.crash?.('after-binding');
177
+ }
178
+ if (!journal.associationCompleted) {
179
+ await store.writeAssociation({
180
+ targetRef: input.binding.targetRef,
181
+ bindingId: input.binding.bindingId,
182
+ referenceRef: null,
183
+ });
184
+ journal = completeAssociation(journal, input.now);
185
+ await store.writeJournal(journal);
186
+ input.crash?.('after-association');
187
+ }
188
+ return { status: 'verified', binding: journal.resultingBinding, journal };
189
+ }
190
+ function assertJournalMatches(journal, input, kind) {
191
+ if (journal &&
192
+ (journal.kind !== kind ||
193
+ journal.bindingId !== input.binding.bindingId ||
194
+ journal.previewDigest !== input.previewDigest)) {
195
+ throw new Error('Persisted resolution journal does not match the requested kind, binding, and preview.');
196
+ }
197
+ }
198
+ function assertFreshApproval(input) {
199
+ const approval = input.approval;
200
+ const age = approval
201
+ ? Date.parse(input.now) - Date.parse(approval.approvedAt)
202
+ : Number.POSITIVE_INFINITY;
203
+ if (!approval ||
204
+ approval.previewDigest !== input.previewDigest ||
205
+ !Number.isFinite(age) ||
206
+ age < 0 ||
207
+ age > 5 * 60 * 1_000) {
208
+ throw new Error('Resolution requires fresh approval of the exact preview.');
209
+ }
210
+ }
211
+ function assertVerifiedReplacement(replacement) {
212
+ if (replacement.identity.stableId.length === 0 ||
213
+ replacement.evidenceDigest.length === 0 ||
214
+ !Number.isFinite(Date.parse(replacement.verifiedAt))) {
215
+ throw new Error('Relink requires a verified stable replacement identity.');
216
+ }
217
+ }
218
+ function createJournal(input, kind) {
219
+ return {
220
+ schemaVersion: 1,
221
+ operationId: input.operationId,
222
+ bindingId: input.binding.bindingId,
223
+ kind,
224
+ previewDigest: input.previewDigest,
225
+ state: 'pending',
226
+ bindingTransitionCompleted: false,
227
+ associationCompleted: false,
228
+ resultingBinding: null,
229
+ createdAt: input.now,
230
+ updatedAt: input.now,
231
+ };
232
+ }
233
+ function completeAssociation(journal, updatedAt) {
234
+ return {
235
+ ...journal,
236
+ state: 'complete',
237
+ associationCompleted: true,
238
+ updatedAt,
239
+ };
240
+ }
241
+ export function buildResolutionPreviewDigest(input) {
242
+ return semanticDigest(input);
243
+ }