@ixo/topic-protocol 0.6.0 → 0.8.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 (58) hide show
  1. package/README.md +6 -2
  2. package/dist/schemas/action-receipt.schema.json +105 -11
  3. package/dist/schemas/approval-decision.schema.json +30 -0
  4. package/dist/schemas/claim-record.schema.json +30 -0
  5. package/dist/schemas/decision-to-pay-template.schema.json +346 -0
  6. package/dist/schemas/delivery-decision.schema.json +28 -0
  7. package/dist/schemas/delivery-packet.schema.json +58 -0
  8. package/dist/schemas/evaluation-assertion.schema.json +37 -0
  9. package/dist/schemas/external-work-binding.schema.json +83 -0
  10. package/dist/schemas/external-work-state-map.schema.json +29 -0
  11. package/dist/schemas/payable-obligation.schema.json +312 -0
  12. package/dist/schemas/payment-terms.schema.json +287 -0
  13. package/dist/schemas/settlement-receipt.schema.json +53 -0
  14. package/dist/schemas/topic-action-request.schema.json +30 -0
  15. package/dist/schemas/topic-contract-state.schema.json +29 -3
  16. package/dist/schemas/topic-flow-binding.schema.json +28 -0
  17. package/dist/schemas/topic-kind-profile.schema.json +32 -2
  18. package/dist/schemas/topic-operation.schema.json +2 -2
  19. package/dist/schemas/topic-state.schema.json +14 -0
  20. package/dist/schemas/work-item-snapshot.schema.json +73 -0
  21. package/dist/src/action-runtime/types.d.ts +101 -0
  22. package/dist/src/action-runtime/types.js +12 -0
  23. package/dist/src/action-runtime/types.js.map +1 -0
  24. package/dist/src/contracts/schemas.d.ts +3611 -130
  25. package/dist/src/contracts/schemas.js +28 -0
  26. package/dist/src/contracts/schemas.js.map +1 -1
  27. package/dist/src/contracts/topic-contract.d.ts +8 -1
  28. package/dist/src/contracts/topic-contract.js.map +1 -1
  29. package/dist/src/contracts/topic-kind-profile.d.ts +29 -0
  30. package/dist/src/contracts/topic-kind-profile.js +31 -0
  31. package/dist/src/contracts/topic-kind-profile.js.map +1 -1
  32. package/dist/src/errors.d.ts +1 -1
  33. package/dist/src/errors.js.map +1 -1
  34. package/dist/src/index.d.ts +4 -1
  35. package/dist/src/index.js +4 -1
  36. package/dist/src/index.js.map +1 -1
  37. package/dist/src/projector/payload.d.ts +47 -0
  38. package/dist/src/projector/payload.js +140 -2
  39. package/dist/src/projector/payload.js.map +1 -1
  40. package/dist/src/projector/state.js +150 -0
  41. package/dist/src/projector/state.js.map +1 -1
  42. package/dist/src/projector/types.d.ts +15 -3
  43. package/dist/src/projector/types.js.map +1 -1
  44. package/dist/src/work-bridge/mapping.d.ts +2 -0
  45. package/dist/src/work-bridge/mapping.js +17 -0
  46. package/dist/src/work-bridge/mapping.js.map +1 -0
  47. package/dist/src/work-bridge/project.d.ts +7 -0
  48. package/dist/src/work-bridge/project.js +262 -0
  49. package/dist/src/work-bridge/project.js.map +1 -0
  50. package/dist/src/work-bridge/types.d.ts +232 -0
  51. package/dist/src/work-bridge/types.js +26 -0
  52. package/dist/src/work-bridge/types.js.map +1 -0
  53. package/package.json +4 -2
  54. package/templates/decision-to-pay/README.md +59 -0
  55. package/templates/decision-to-pay/milestone-payment-release.template.json +195 -0
  56. package/templates/decision-to-pay/outcome-payment-claim.template.json +198 -0
  57. package/templates/decision-to-pay/payment-on-delivery.template.json +198 -0
  58. package/templates/decision-to-pay/verified-work-payment.template.json +283 -0
@@ -0,0 +1,232 @@
1
+ export declare const EXTERNAL_WORK_STATES: readonly ["planned", "ready", "in_progress", "blocked", "handoff_ready", "handed_off", "cancelled"];
2
+ export declare const EXTERNAL_WORKFLOW_CATEGORIES: readonly ["triage", "backlog", "unstarted", "started", "completed", "canceled"];
3
+ export declare const EXTERNAL_WORK_HEALTH_STATES: readonly ["up_to_date", "changes_pending", "degraded", "disconnected", "conflicted", "permission_lost"];
4
+ export type ExternalWorkState = (typeof EXTERNAL_WORK_STATES)[number];
5
+ export type ExternalWorkflowCategory = (typeof EXTERNAL_WORKFLOW_CATEGORIES)[number];
6
+ export interface ExternalWorkStateMapping {
7
+ readonly workflowStateId: string;
8
+ readonly category: ExternalWorkflowCategory;
9
+ readonly workState: ExternalWorkState;
10
+ }
11
+ export interface ExternalWorkStateMapV1 {
12
+ readonly version: 1;
13
+ readonly profileId: string;
14
+ readonly provider: string;
15
+ readonly organizationId: string;
16
+ readonly teamId: string;
17
+ readonly mappings: readonly ExternalWorkStateMapping[];
18
+ }
19
+ export interface ExternalWorkflowStateInput {
20
+ readonly workflowStateId: string;
21
+ readonly category: ExternalWorkflowCategory;
22
+ }
23
+ export interface ExternalWorkStateMappingResult {
24
+ readonly workState: ExternalWorkState;
25
+ readonly mappingProfileId: string;
26
+ readonly mappingVersion: 1;
27
+ readonly workflowStateId: string;
28
+ }
29
+ export type Sha256Digest = `sha256:${string}`;
30
+ export type ExternalWorkHealth = (typeof EXTERNAL_WORK_HEALTH_STATES)[number];
31
+ export interface ExternalWorkCheckpoint {
32
+ readonly topicRevision: string;
33
+ readonly effectiveContractDigest?: Sha256Digest;
34
+ readonly externalUpdatedAt?: string;
35
+ readonly externalSnapshotDigest?: Sha256Digest;
36
+ readonly lastEventId?: string;
37
+ readonly syncedAt: string;
38
+ }
39
+ export interface ExternalWorkBindingV1 {
40
+ readonly version: 1;
41
+ readonly bindingId: string;
42
+ readonly topicId: string;
43
+ readonly role: "primary" | "child" | "related" | "verification" | "delivery";
44
+ readonly createdFrom: "topic" | "external";
45
+ readonly external: {
46
+ readonly provider: string;
47
+ readonly resourceType: string;
48
+ readonly organizationId: string;
49
+ readonly teamId?: string;
50
+ readonly resourceId: string;
51
+ readonly identifier: string;
52
+ readonly url: string;
53
+ };
54
+ readonly syncPolicy: {
55
+ readonly mode: "link_only" | "governed_projection";
56
+ readonly direction: "external_to_topic" | "topic_to_external" | "bidirectional";
57
+ readonly comments: "none" | "explicit_promote";
58
+ readonly files: "none" | "reference_only" | "explicit_ingest";
59
+ readonly delete: "tombstone";
60
+ readonly fieldPolicy: {
61
+ readonly id: string;
62
+ readonly version: number;
63
+ readonly digest: Sha256Digest;
64
+ };
65
+ };
66
+ readonly workState: ExternalWorkState;
67
+ readonly checkpoint: ExternalWorkCheckpoint;
68
+ readonly identity: {
69
+ readonly bridgeDid: string;
70
+ readonly externalAppActorId?: string;
71
+ };
72
+ readonly health: ExternalWorkHealth;
73
+ readonly createdBy: string;
74
+ readonly createdAt: string;
75
+ readonly extensions?: Readonly<Record<string, unknown>>;
76
+ }
77
+ export interface ProjectedExternalWorkBinding extends ExternalWorkBindingV1 {
78
+ readonly lastOperationId: string;
79
+ readonly updatedBy?: string;
80
+ readonly updatedAt?: string;
81
+ readonly tombstone?: {
82
+ readonly reason: string;
83
+ readonly unlinkedBy: string;
84
+ readonly unlinkedAt: string;
85
+ readonly operationId: string;
86
+ };
87
+ }
88
+ export interface ExternalWorkSync {
89
+ readonly bindingId: string;
90
+ readonly workState: ExternalWorkState;
91
+ readonly health: ExternalWorkHealth;
92
+ readonly checkpoint: ExternalWorkCheckpoint;
93
+ }
94
+ export interface ExternalWorkTombstone {
95
+ readonly bindingId: string;
96
+ readonly reason: string;
97
+ }
98
+ export interface WorkArtifactReference {
99
+ readonly type: string;
100
+ readonly uri: string;
101
+ readonly digest: Sha256Digest;
102
+ readonly label?: string;
103
+ readonly mediaType?: string;
104
+ }
105
+ export interface WorkEvidenceReference extends Omit<WorkArtifactReference, "mediaType"> {
106
+ readonly provenanceClass: "claimant_provided" | "client_assisted" | "server_verified" | "third_party_attested";
107
+ }
108
+ export interface WorkItemSnapshotV1 extends Readonly<Record<string, unknown>> {
109
+ readonly version: 1;
110
+ readonly snapshotId: string;
111
+ readonly bindingId: string;
112
+ readonly topicId: string;
113
+ readonly digest: Sha256Digest;
114
+ }
115
+ export interface DeliveryPacketV1 {
116
+ readonly version: 1;
117
+ readonly deliveryPacketId: string;
118
+ readonly sequence: number;
119
+ readonly topicId: string;
120
+ readonly bindingId: string;
121
+ readonly supersedes?: string;
122
+ readonly contract: {
123
+ readonly revision: string;
124
+ readonly digest: Sha256Digest;
125
+ readonly status: "accepted";
126
+ };
127
+ readonly workItemSnapshot: {
128
+ readonly snapshotId: string;
129
+ readonly ref: string;
130
+ readonly digest: Sha256Digest;
131
+ };
132
+ readonly deliverables: readonly WorkArtifactReference[];
133
+ readonly evidence: readonly WorkEvidenceReference[];
134
+ readonly traces: readonly WorkArtifactReference[];
135
+ readonly paymentTerms?: {
136
+ readonly ref: string;
137
+ readonly digest: Sha256Digest;
138
+ };
139
+ readonly submittedBy: string;
140
+ readonly submittedAt: string;
141
+ readonly capability: string;
142
+ readonly status: "submitted";
143
+ readonly digest: Sha256Digest;
144
+ readonly extensions?: Readonly<Record<string, unknown>>;
145
+ }
146
+ export interface DeliveryDecisionV1 {
147
+ readonly version: 1;
148
+ readonly decisionId: string;
149
+ readonly topicId: string;
150
+ readonly deliveryPacketId: string;
151
+ readonly deliveryPacketDigest: Sha256Digest;
152
+ readonly decision: "accepted" | "returned" | "rejected";
153
+ readonly reasons?: readonly string[];
154
+ readonly decidedBy: string;
155
+ readonly decidedAt: string;
156
+ readonly capability: string;
157
+ readonly digest: Sha256Digest;
158
+ readonly extensions?: Readonly<Record<string, unknown>>;
159
+ }
160
+ export interface EvaluationAssertionV1 extends Readonly<Record<string, unknown>> {
161
+ readonly version: 1;
162
+ readonly evaluationId: string;
163
+ readonly topicId: string;
164
+ readonly deliveryPacketId: string;
165
+ readonly deliveryPacketDigest: Sha256Digest;
166
+ readonly evidenceDigests: readonly Sha256Digest[];
167
+ readonly traceDigests: readonly Sha256Digest[];
168
+ readonly outcome: "supported" | "refuted" | "insufficient_evidence" | "human_review" | "disputed" | "superseded";
169
+ readonly evaluatorDid: string;
170
+ readonly evaluatedAt: string;
171
+ readonly digest: Sha256Digest;
172
+ }
173
+ export interface ApprovalDecisionV1 extends Readonly<Record<string, unknown>> {
174
+ readonly version: 1;
175
+ readonly approvalId: string;
176
+ readonly topicId: string;
177
+ readonly deliveryPacketId: string;
178
+ readonly deliveryPacketDigest: Sha256Digest;
179
+ readonly evaluationIds: readonly string[];
180
+ readonly decision: "approved" | "rejected" | "changes_requested" | "superseded";
181
+ readonly decidedAt: string;
182
+ readonly digest: Sha256Digest;
183
+ }
184
+ export interface ClaimRecordV1 extends Readonly<Record<string, unknown>> {
185
+ readonly version: 1;
186
+ readonly claimId: string;
187
+ readonly topicId: string;
188
+ readonly deliveryPacketId: string;
189
+ readonly deliveryPacketDigest: Sha256Digest;
190
+ readonly evaluationIds: readonly string[];
191
+ readonly approvalId: string;
192
+ readonly claimantDid: string;
193
+ readonly requested: {
194
+ readonly amount: string;
195
+ readonly asset: string;
196
+ readonly recipient: string;
197
+ };
198
+ readonly status: "pending" | "approved" | "rejected" | "disputed" | "invalidated" | "flagged";
199
+ readonly replayKey: string;
200
+ readonly source: {
201
+ readonly provider: string;
202
+ readonly ref: string;
203
+ };
204
+ readonly recordedAt: string;
205
+ readonly digest: Sha256Digest;
206
+ }
207
+ export interface SettlementReceiptV1 extends Readonly<Record<string, unknown>> {
208
+ readonly version: 1;
209
+ readonly settlementId: string;
210
+ readonly topicId: string;
211
+ readonly claimId: string;
212
+ readonly deliveryPacketId: string;
213
+ readonly deliveryPacketDigest: Sha256Digest;
214
+ readonly evaluationIds: readonly string[];
215
+ readonly approvalId: string;
216
+ readonly idempotencyKey: string;
217
+ readonly amount: string;
218
+ readonly asset: string;
219
+ readonly recipient: string;
220
+ readonly state: "unfunded" | "funded" | "held" | "authorised" | "submitted" | "confirmed" | "failed" | "refunded";
221
+ readonly recordedAt: string;
222
+ readonly digest: Sha256Digest;
223
+ }
224
+ export interface TopicWorkBridgeProjection {
225
+ readonly bindings: readonly ProjectedExternalWorkBinding[];
226
+ readonly deliveryPackets: readonly DeliveryPacketV1[];
227
+ readonly deliveryDecisions: readonly DeliveryDecisionV1[];
228
+ readonly evaluations: readonly EvaluationAssertionV1[];
229
+ readonly approvals: readonly ApprovalDecisionV1[];
230
+ readonly claims: readonly ClaimRecordV1[];
231
+ readonly settlements: readonly SettlementReceiptV1[];
232
+ }
@@ -0,0 +1,26 @@
1
+ export const EXTERNAL_WORK_STATES = [
2
+ "planned",
3
+ "ready",
4
+ "in_progress",
5
+ "blocked",
6
+ "handoff_ready",
7
+ "handed_off",
8
+ "cancelled",
9
+ ];
10
+ export const EXTERNAL_WORKFLOW_CATEGORIES = [
11
+ "triage",
12
+ "backlog",
13
+ "unstarted",
14
+ "started",
15
+ "completed",
16
+ "canceled",
17
+ ];
18
+ export const EXTERNAL_WORK_HEALTH_STATES = [
19
+ "up_to_date",
20
+ "changes_pending",
21
+ "degraded",
22
+ "disconnected",
23
+ "conflicted",
24
+ "permission_lost",
25
+ ];
26
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/work-bridge/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,SAAS;IACT,OAAO;IACP,aAAa;IACb,SAAS;IACT,eAAe;IACf,YAAY;IACZ,WAAW;CACH,CAAC;AAEX,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,QAAQ;IACR,SAAS;IACT,WAAW;IACX,SAAS;IACT,WAAW;IACX,UAAU;CACF,CAAC;AAEX,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,YAAY;IACZ,iBAAiB;IACjB,UAAU;IACV,cAAc;IACd,YAAY;IACZ,iBAAiB;CACT,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ixo/topic-protocol",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "Reference contracts and deterministic projection for IXO Topics",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -18,6 +18,7 @@
18
18
  "files": [
19
19
  "dist",
20
20
  "profiles",
21
+ "templates",
21
22
  "README.md"
22
23
  ],
23
24
  "exports": {
@@ -26,7 +27,8 @@
26
27
  "import": "./dist/src/index.js"
27
28
  },
28
29
  "./schemas/*": "./dist/schemas/*",
29
- "./profiles/*": "./profiles/*"
30
+ "./profiles/*": "./profiles/*",
31
+ "./templates/*": "./templates/*"
30
32
  },
31
33
  "scripts": {
32
34
  "build": "tsc -p tsconfig.build.json",
@@ -0,0 +1,59 @@
1
+ # Decision-to-Pay Topic template pack
2
+
3
+ This directory publishes four draft, machine-readable Topic Templates for
4
+ consequential payment decisions:
5
+
6
+ 1. [Verified Work Payment](verified-work-payment.template.json)
7
+ 2. [Payment on Delivery](payment-on-delivery.template.json)
8
+ 3. [Milestone Payment Release](milestone-payment-release.template.json)
9
+ 4. [Outcome Payment Claim](outcome-payment-claim.template.json)
10
+
11
+ The manifests extend the Topic kinds, recipes, and external-work contracts in
12
+ `@ixo/topic-protocol` 0.7.0. Their compatibility block pins the reviewed
13
+ baseline commit and names every additional contract, capability policy, and
14
+ adapter needed by an implementation.
15
+
16
+ ## Shared contracts
17
+
18
+ - [`decision-to-pay-template.schema.json`](../../schemas/decision-to-pay-template.schema.json)
19
+ validates each template manifest.
20
+ - [`payment-terms.schema.json`](../../schemas/payment-terms.schema.json) defines
21
+ immutable commercial and control terms for one payable unit.
22
+ - [`payable-obligation.schema.json`](../../schemas/payable-obligation.schema.json)
23
+ defines a payment-case envelope with separate delivery, evaluation, approval,
24
+ claim, and settlement projections.
25
+ - Matching synthetic reference vectors live in [`examples/`](../../examples).
26
+
27
+ ## Trust boundary
28
+
29
+ These artifacts are draft contract extensions. They do not grant a capability,
30
+ verify a signature, establish eligibility, approve a claim, or move money.
31
+ Topic Protocol operations and their referenced records remain authoritative for
32
+ delivery, evaluation, approval, claim, and settlement state. A conforming
33
+ runtime must verify DIDs, delegated capabilities, signatures, revocation,
34
+ replay protection, funding, KYC or sanctions requirements, dispute holds,
35
+ trusted time, and payment-rail finality before accepting a settlement receipt.
36
+
37
+ The Payable Obligation is an envelope and index over those separate axes. Its
38
+ `axisProjection` must be derived from referenced protocol records; it is not an
39
+ alternative settlement authority.
40
+
41
+ ## Draft and privacy rules
42
+
43
+ - A Topic may begin as an honest Draft with only a title and Kind.
44
+ - Missing intent, owner, outcome, authority, evidence, amount, or deadline must
45
+ remain visibly missing until supplied or accepted.
46
+ - A template names required capabilities but never grants them.
47
+ - Confidential evidence stays in an appropriately scoped encrypted room or a
48
+ UCAN-protected VFS resource. Topic membership does not grant VFS byte access.
49
+ - The example fixtures set `notRealEvidence: true`; zero digests and example
50
+ identifiers must never be treated as verified evidence.
51
+
52
+ ## Compatibility notes
53
+
54
+ The Job Card profile and external-work contracts support the strongest path for
55
+ Verified Work Payment. Physical delivery still needs a delivery-order resource
56
+ or profile. The milestone template uses one child Topic per payable milestone,
57
+ so corrections do not become new milestones. Outcome Payment Claim assumes a
58
+ Delivery Packet for the underlying intervention; claims without external work
59
+ need a future generalised obligation handoff.
@@ -0,0 +1,195 @@
1
+ {
2
+ "$schema": "../../schemas/decision-to-pay-template.schema.json",
3
+ "version": 1,
4
+ "id": "urn:ixo:decision-to-pay:template:milestone-payment-release",
5
+ "templateVersion": "0.1.0",
6
+ "label": "Milestone Payment Release",
7
+ "description": "Create one governed child Topic for each payable milestone so its terms, handoff, evaluation, claim, settlement, corrections, and disputes remain independently auditable.",
8
+ "maturity": "draft",
9
+ "compatibility": {
10
+ "protocolPackage": "@ixo/topic-protocol",
11
+ "protocolVersion": "0.7.0",
12
+ "sourceCommit": "40d1ce5160757553cd57f670b5b1708a5c0d0ea3",
13
+ "implementationStatus": "draft_contract_extension",
14
+ "requiredExtensions": [
15
+ "org.ixo.payment-terms/v1",
16
+ "org.ixo.payable-obligation/v1",
17
+ "ixo.payment capability policy/v1",
18
+ "payment-rail adapter"
19
+ ],
20
+ "constraints": [
21
+ "One child Topic represents one payable milestone; Delivery Packet sequence numbers represent corrections and not additional milestones.",
22
+ "Parent project completion, child milestone acceptance, and payment settlement remain separate statuses."
23
+ ]
24
+ },
25
+ "topic": {
26
+ "primaryRole": "payable_milestone",
27
+ "kindRef": {"source": "standard", "kind": "task"},
28
+ "recipe": "project",
29
+ "workingMode": "client",
30
+ "temporalMode": "finite",
31
+ "acceptance": {
32
+ "requiredTopicContractPaths": [
33
+ "/intent/text",
34
+ "/outcome/statement/text",
35
+ "/outcome/successCriteria",
36
+ "/ownerId",
37
+ "/completion/definition",
38
+ "/completion/acceptanceAuthorityIds"
39
+ ],
40
+ "allowHonestDraft": true
41
+ },
42
+ "lifecyclePolicy": {
43
+ "separateTopicContractOutcomeAxes": true,
44
+ "waitingRule": "A milestone Topic may wait on dependencies, delivery correction, evaluation, approval, claim, or settlement without changing the parent project outcome.",
45
+ "resolveRule": "Resolve the milestone under its own accepted closure rule; never infer parent project completion from one paid child."
46
+ }
47
+ },
48
+ "topology": {
49
+ "requiresParentTopic": true,
50
+ "parentBaseKind": "task",
51
+ "relation": "branches_from",
52
+ "onePayableUnitPerTopic": true
53
+ },
54
+ "actors": [
55
+ {"role": "payer", "label": "Project client or payer", "required": true, "identity": "did", "capabilities": ["ixo.payment/accept-terms"]},
56
+ {"role": "milestone_owner", "label": "Milestone owner and claimant", "required": true, "identity": "did", "capabilities": ["topic/write", "ixo.payment/submit-delivery", "ixo.payment/record-claim"]},
57
+ {"role": "milestone_acceptor", "label": "Milestone acceptance authority", "required": true, "identity": "did", "capabilities": ["ixo.payment/accept-delivery"]},
58
+ {"role": "evaluator", "label": "Milestone evaluator", "required": true, "identity": "did", "capabilities": ["ixo.payment/evaluate"]},
59
+ {"role": "payment_approver", "label": "Payment approval authority", "required": true, "identity": "did", "capabilities": ["ixo.payment/approve"]},
60
+ {"role": "settlement_controller", "label": "Settlement controller", "required": true, "identity": "did", "capabilities": ["ixo.payment/record-settlement"]}
61
+ ],
62
+ "resources": [
63
+ {"role": "parent_topic", "type": "ixo.topic", "required": true, "binding": "external_reference", "authority": "topic_contract", "availability": "topic_protocol_0.7", "confidentiality": "room_encrypted", "notes": "Linked by an append-only Topic relation; the parent does not own this child's payment state."},
64
+ {"role": "payment_terms", "type": "org.ixo.payment-terms", "required": true, "binding": "typed_context", "authority": "bound_resource", "availability": "contract_pack", "schemaRef": "https://topic-protocol.ixo.world/schemas/payment-terms.schema.json", "confidentiality": "vfs_ucan"},
65
+ {"role": "payable_obligation", "type": "org.ixo.payable-obligation", "required": true, "binding": "typed_context", "authority": "bound_resource", "availability": "contract_pack", "schemaRef": "https://topic-protocol.ixo.world/schemas/payable-obligation.schema.json", "confidentiality": "room_encrypted"},
66
+ {"role": "external_work", "type": "ixo.external-work-binding", "required": true, "binding": "protocol_record", "authority": "external_system", "availability": "topic_protocol_0.7", "schemaRef": "https://topic-protocol.ixo.world/schemas/external-work-binding.schema.json", "confidentiality": "room_encrypted"},
67
+ {"role": "milestone_evidence", "type": "ixo.vfs", "required": true, "binding": "vfs_attachment", "authority": "bound_resource", "availability": "topic_protocol_0.7", "schemaRef": "https://topic-protocol.ixo.world/schemas/topic-file-attachment.schema.json", "confidentiality": "vfs_ucan"}
68
+ ],
69
+ "protocolContracts": [
70
+ {"name": "topic-contract-state", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/topic-contract-state.schema.json", "required": true},
71
+ {"name": "external-work-binding", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/external-work-binding.schema.json", "required": true},
72
+ {"name": "delivery-packet", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/delivery-packet.schema.json", "required": true},
73
+ {"name": "delivery-decision", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/delivery-decision.schema.json", "required": true},
74
+ {"name": "evaluation-assertion", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/evaluation-assertion.schema.json", "required": true},
75
+ {"name": "approval-decision", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/approval-decision.schema.json", "required": true},
76
+ {"name": "claim-record", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/claim-record.schema.json", "required": true},
77
+ {"name": "settlement-receipt", "source": "topic_protocol", "schemaRef": "https://topic-protocol.ixo.world/schemas/settlement-receipt.schema.json", "required": true},
78
+ {"name": "payment-terms", "source": "contract_pack", "schemaRef": "https://topic-protocol.ixo.world/schemas/payment-terms.schema.json", "required": true},
79
+ {"name": "payable-obligation", "source": "contract_pack", "schemaRef": "https://topic-protocol.ixo.world/schemas/payable-obligation.schema.json", "required": true}
80
+ ],
81
+ "operations": [
82
+ "branch",
83
+ "propose-contract",
84
+ "update-contract",
85
+ "accept-contract",
86
+ "supersede-contract",
87
+ "bind-external-work",
88
+ "sync-external-work",
89
+ "submit-delivery",
90
+ "record-delivery-decision",
91
+ "record-evaluation",
92
+ "record-approval",
93
+ "record-claim",
94
+ "record-settlement",
95
+ "change-status",
96
+ "resolve-conflict"
97
+ ],
98
+ "gates": [
99
+ {
100
+ "id": "milestone-terms-accepted",
101
+ "stage": "terms",
102
+ "description": "The child Topic must pin one milestone, one amount rule, dependencies, evidence, acceptance authority, and payment timing.",
103
+ "all": [
104
+ {"contract": "topic-contract-state", "path": "/contracts/effective/body/status", "operator": "equals", "value": "accepted"},
105
+ {"contract": "payment-terms", "path": "/calculation/model", "operator": "equals", "value": "milestone"},
106
+ {"contract": "payment-terms", "path": "/status", "operator": "equals", "value": "accepted"}
107
+ ],
108
+ "onFailure": "block"
109
+ },
110
+ {
111
+ "id": "milestone-delivery-submitted",
112
+ "stage": "delivery",
113
+ "description": "The Delivery Packet must bind only this milestone's accepted contract, work snapshot, deliverables, and evidence.",
114
+ "all": [
115
+ {"contract": "delivery-packet", "path": "/status", "operator": "equals", "value": "submitted"},
116
+ {"contract": "delivery-packet", "path": "/contract/digest", "operator": "digest_matches", "value": "effective_child_contract"},
117
+ {"contract": "delivery-packet", "path": "/paymentTerms/digest", "operator": "digest_matches", "value": "accepted_milestone_terms"}
118
+ ],
119
+ "onFailure": "return_for_changes"
120
+ },
121
+ {
122
+ "id": "milestone-accepted",
123
+ "stage": "delivery",
124
+ "description": "The authorised acceptance decision must accept the current packet under the child's completion criteria.",
125
+ "all": [
126
+ {"contract": "delivery-decision", "path": "/decision", "operator": "equals", "value": "accepted"},
127
+ {"contract": "delivery-decision", "path": "/deliveryPacketDigest", "operator": "digest_matches", "value": "current_delivery_packet"}
128
+ ],
129
+ "onFailure": "return_for_changes"
130
+ },
131
+ {
132
+ "id": "milestone-evaluation-supported",
133
+ "stage": "evaluation",
134
+ "description": "Every required success criterion must be supported by the approved milestone rubric.",
135
+ "all": [
136
+ {"contract": "evaluation-assertion", "path": "/outcome", "operator": "all_equal", "value": "supported"},
137
+ {"contract": "evaluation-assertion", "path": "/rubric/governanceStatus", "operator": "all_equal", "value": "approved"}
138
+ ],
139
+ "onFailure": "human_review"
140
+ },
141
+ {
142
+ "id": "milestone-payment-approved",
143
+ "stage": "approval",
144
+ "description": "Approval must reference the exact child packet and supported evaluation set.",
145
+ "all": [
146
+ {"contract": "approval-decision", "path": "/decision", "operator": "equals", "value": "approved"},
147
+ {"contract": "approval-decision", "path": "/evaluationIds", "operator": "all_equal", "value": "settlement_evaluation_ids"}
148
+ ],
149
+ "onFailure": "block"
150
+ },
151
+ {
152
+ "id": "milestone-claim-approved",
153
+ "stage": "claim",
154
+ "description": "The claim must be approved for this milestone's exact amount, asset, and recipient.",
155
+ "all": [
156
+ {"contract": "claim-record", "path": "/status", "operator": "equals", "value": "approved"},
157
+ {"contract": "claim-record", "path": "/requested", "operator": "digest_matches", "value": "payable_obligation_requested"}
158
+ ],
159
+ "onFailure": "block"
160
+ },
161
+ {
162
+ "id": "milestone-settlement-clear",
163
+ "stage": "settlement",
164
+ "description": "Dependencies, disputes, funding, identity, and replay checks must pass for this child Topic only.",
165
+ "all": [
166
+ {"contract": "operator-verification", "path": "/dependencies", "operator": "equals", "value": "satisfied"},
167
+ {"contract": "operator-verification", "path": "/disputeHold", "operator": "equals", "value": false},
168
+ {"contract": "operator-verification", "path": "/funding", "operator": "equals", "value": "passed"},
169
+ {"contract": "operator-verification", "path": "/idempotencyKey", "operator": "exists"}
170
+ ],
171
+ "onFailure": "block"
172
+ }
173
+ ],
174
+ "settlement": {
175
+ "movesFunds": false,
176
+ "requiresExternalAdapter": true,
177
+ "failClosed": true,
178
+ "requiredAxes": ["delivery", "evaluation", "approval", "claim", "settlement"],
179
+ "exactMatchFields": ["topicId", "deliveryPacketId", "deliveryPacketDigest", "evaluationIds", "approvalId", "claimId", "amount", "asset", "recipient"],
180
+ "terminalState": "confirmed"
181
+ },
182
+ "privacy": {
183
+ "topicIsConfidentialityBoundary": false,
184
+ "topicCapabilityGrantsVfsAccess": false,
185
+ "sensitiveResourceStorage": "encrypted_room_or_ucan_vfs",
186
+ "minimumRoomVisibleFields": ["milestone label", "milestone phase", "target date"]
187
+ },
188
+ "acceptanceCriteria": [
189
+ {"id": "one-payable-unit", "statement": "Each child Topic contains exactly one payable milestone and one Payment Terms resource."},
190
+ {"id": "correction-not-milestone", "statement": "A corrected handoff supersedes the prior Delivery Packet and never creates a second payable milestone."},
191
+ {"id": "parent-child-separation", "statement": "Parent project, child milestone, claim, and settlement states remain independently visible."},
192
+ {"id": "dependency-gate", "statement": "Unmet accepted dependencies block payment without fabricating milestone failure."},
193
+ {"id": "exact-amount", "statement": "The claim and settlement must exactly match the accepted milestone amount, asset, and recipient."}
194
+ ]
195
+ }