@taifoon/jev 0.2.12 → 0.2.14
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.
- package/README.md +3 -3
- package/dist/wilson.d.ts +2 -49
- package/dist/wilson.js +3 -66
- package/docs/WORKFLOWS.md +5 -4
- package/package.json +5 -2
- package/workflows/0.2.14/README.md +109 -0
- package/workflows/0.2.14/batch-judge.json +341 -0
- package/workflows/0.2.14/hire-judge-settle.json +920 -0
- package/workflows/0.2.14/jev-grader.json +433 -0
- package/workflows/0.2.14/manifest.json +152 -0
- package/workflows/0.2.14/schemas/answers.json +230 -0
- package/workflows/0.2.14/schemas/common.json +397 -0
- package/workflows/0.2.14/schemas/delivery.json +217 -0
- package/workflows/0.2.14/schemas/facts.json +209 -0
- package/workflows/0.2.14/schemas/hire.json +314 -0
- package/workflows/0.2.14/schemas/job.json +88 -0
- package/workflows/0.2.14/schemas/settlement.json +243 -0
- package/workflows/0.2.14/schemas/verdict.json +296 -0
- package/workflows/0.2.14/stamp-grade.json +175 -0
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Taifoon — HIRE → JUDGE → SETTLE (devnet 36927)",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {},
|
|
6
|
+
"id": "n0",
|
|
7
|
+
"name": "Start",
|
|
8
|
+
"type": "n8n-nodes-base.manualTrigger",
|
|
9
|
+
"typeVersion": 1,
|
|
10
|
+
"position": [
|
|
11
|
+
0,
|
|
12
|
+
300
|
|
13
|
+
],
|
|
14
|
+
"notes": "in: — → out: — · start a run (swap for a Webhook or Schedule trigger)",
|
|
15
|
+
"notesInFlow": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"parameters": {
|
|
19
|
+
"assignments": {
|
|
20
|
+
"assignments": [
|
|
21
|
+
{
|
|
22
|
+
"id": "i1",
|
|
23
|
+
"name": "task",
|
|
24
|
+
"value": "Verify Base block 51828000 under the SuperRoot and return the portable V5 proof (class proof.verify.v5).",
|
|
25
|
+
"type": "string"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "i2",
|
|
29
|
+
"name": "candidate",
|
|
30
|
+
"value": "0x3574999dd4c96eb73bd6e11d4177010c83e14f5b",
|
|
31
|
+
"type": "string"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "i3",
|
|
35
|
+
"name": "endpoint",
|
|
36
|
+
"value": "https://www.taifoon.io/api/mcp",
|
|
37
|
+
"type": "string"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "i4",
|
|
41
|
+
"name": "tool",
|
|
42
|
+
"value": "taifoon_proof",
|
|
43
|
+
"type": "string"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": "i5",
|
|
47
|
+
"name": "block_number",
|
|
48
|
+
"value": 51828000,
|
|
49
|
+
"type": "number"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"id": "i6",
|
|
53
|
+
"name": "buyer",
|
|
54
|
+
"value": "0xf6806EFcb34dDa2eE78967384D09367ECd364483",
|
|
55
|
+
"type": "string"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "i7",
|
|
59
|
+
"name": "seller",
|
|
60
|
+
"value": "0xe6209aD65a66d5eF11E00A7B33Cd21ce7084715c",
|
|
61
|
+
"type": "string"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"id": "i8",
|
|
65
|
+
"name": "price",
|
|
66
|
+
"value": "1000000",
|
|
67
|
+
"type": "string"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "i9",
|
|
71
|
+
"name": "chainId",
|
|
72
|
+
"value": 36927,
|
|
73
|
+
"type": "number"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "i10",
|
|
77
|
+
"name": "lane",
|
|
78
|
+
"value": "a",
|
|
79
|
+
"type": "string"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "i11",
|
|
83
|
+
"name": "tenant",
|
|
84
|
+
"value": "",
|
|
85
|
+
"type": "string"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "i12",
|
|
89
|
+
"name": "deadline_sec",
|
|
90
|
+
"value": 300,
|
|
91
|
+
"type": "number"
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"options": {}
|
|
96
|
+
},
|
|
97
|
+
"id": "n1",
|
|
98
|
+
"name": "Inputs",
|
|
99
|
+
"type": "n8n-nodes-base.set",
|
|
100
|
+
"typeVersion": 3.4,
|
|
101
|
+
"position": [
|
|
102
|
+
220,
|
|
103
|
+
300
|
|
104
|
+
],
|
|
105
|
+
"notes": "in: — → out: hire.json · the hire intent: task, candidate seller, endpoint/tool, buyer, price, chain (a HIRE in state Open)",
|
|
106
|
+
"notesInFlow": true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"parameters": {
|
|
110
|
+
"resource": "handshake",
|
|
111
|
+
"operation": "open",
|
|
112
|
+
"candidateAddress": "={{ $json.candidate }}",
|
|
113
|
+
"candidateKind": "mcp",
|
|
114
|
+
"task": "={{ $json.task }}",
|
|
115
|
+
"openOptions": {
|
|
116
|
+
"args": "={{ JSON.stringify({ chain_id: 8453, block_number: $json.block_number }) }}",
|
|
117
|
+
"chain": "={{ String($json.chainId) }}",
|
|
118
|
+
"dispatch": true,
|
|
119
|
+
"endpoint": "={{ $json.endpoint }}",
|
|
120
|
+
"hirer": "={{ $json.buyer }}",
|
|
121
|
+
"required_skills": "proof.verify.v5",
|
|
122
|
+
"tool": "={{ $json.tool }}"
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"id": "n2",
|
|
126
|
+
"name": "Offer + deliver",
|
|
127
|
+
"type": "CUSTOM.taifoon",
|
|
128
|
+
"typeVersion": 1,
|
|
129
|
+
"position": [
|
|
130
|
+
440,
|
|
131
|
+
300
|
|
132
|
+
],
|
|
133
|
+
"notes": "in: hire.json → out: job.json · POST /v1/handshake {dispatch:true}: the offer goes to the seller’s own endpoint; the reply is held with its keccak digest",
|
|
134
|
+
"notesInFlow": true,
|
|
135
|
+
"credentials": {
|
|
136
|
+
"taifoonRelayerApi": {
|
|
137
|
+
"id": "",
|
|
138
|
+
"name": "REPLACE: taifoonRelayerApi"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"parameters": {
|
|
144
|
+
"jsCode": "// Delivery: what came back over the wire. No digest = nothing to settle; say why and stop.\nconst r = $input.first().json;\nconst d = r.delivery || {};\nconst digest = d.reply_digest || d.digest || null;\nif (!r.handshake_id) throw new Error('no handshake id in ' + JSON.stringify(r).slice(0, 300));\nif (!digest) throw new Error('no delivery digest: dispatch status ' + (d.status || 'none') + (d.reason ? ' — ' + d.reason : ''));\nreturn [{ json: { handshake_id: r.handshake_id, delivery_status: d.status || null, protocol: d.protocol || null, latency_ms: d.latency_ms ?? null, reply_head: d.reply_head || null, evidence: digest, target: r.target || null } }];"
|
|
145
|
+
},
|
|
146
|
+
"id": "n3",
|
|
147
|
+
"name": "Delivery",
|
|
148
|
+
"type": "n8n-nodes-base.code",
|
|
149
|
+
"typeVersion": 2,
|
|
150
|
+
"position": [
|
|
151
|
+
660,
|
|
152
|
+
300
|
|
153
|
+
],
|
|
154
|
+
"notes": "in: job.json → out: delivery.json · stop unless a reply digest is held (no digest = nothing to settle)",
|
|
155
|
+
"notesInFlow": true
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "Judge · prepare",
|
|
159
|
+
"type": "CUSTOM.taifoon",
|
|
160
|
+
"typeVersion": 1,
|
|
161
|
+
"position": [
|
|
162
|
+
880,
|
|
163
|
+
300
|
|
164
|
+
],
|
|
165
|
+
"parameters": {
|
|
166
|
+
"resource": "judge",
|
|
167
|
+
"operation": "compose",
|
|
168
|
+
"composeSubject": "handshake",
|
|
169
|
+
"composeHandshakeId": "={{ $json.handshake_id }}",
|
|
170
|
+
"composePriceUsdc": 1,
|
|
171
|
+
"composeMode": "prepare"
|
|
172
|
+
},
|
|
173
|
+
"credentials": {
|
|
174
|
+
"taifoonRelayerApi": {
|
|
175
|
+
"id": "",
|
|
176
|
+
"name": "REPLACE: taifoonRelayerApi"
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
"id": "c2af3b0e-7e53-4102-88b8-4d9cea1be0d8",
|
|
180
|
+
"notes": "in: delivery.json → out: facts.json · POST /v1/judge/compose (prepare): the facts decide first; hard_fail ends here; else the exact text Jev reads + prepare_digest",
|
|
181
|
+
"notesInFlow": true
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "Hard fail?",
|
|
185
|
+
"type": "n8n-nodes-base.if",
|
|
186
|
+
"typeVersion": 2.2,
|
|
187
|
+
"position": [
|
|
188
|
+
1100,
|
|
189
|
+
300
|
|
190
|
+
],
|
|
191
|
+
"parameters": {
|
|
192
|
+
"conditions": {
|
|
193
|
+
"options": {
|
|
194
|
+
"caseSensitive": true,
|
|
195
|
+
"leftValue": "",
|
|
196
|
+
"typeValidation": "strict",
|
|
197
|
+
"version": 2
|
|
198
|
+
},
|
|
199
|
+
"conditions": [
|
|
200
|
+
{
|
|
201
|
+
"id": "h1",
|
|
202
|
+
"leftValue": "={{ $json.hard_fail === true }}",
|
|
203
|
+
"rightValue": "",
|
|
204
|
+
"operator": {
|
|
205
|
+
"type": "boolean",
|
|
206
|
+
"operation": "true",
|
|
207
|
+
"singleValue": true
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
],
|
|
211
|
+
"combinator": "and"
|
|
212
|
+
},
|
|
213
|
+
"options": {}
|
|
214
|
+
},
|
|
215
|
+
"id": "039a7992-9c11-4902-a64e-47518a3451c4",
|
|
216
|
+
"notes": "in: facts.json → out: facts.json · branch: a hard fail goes straight to the verdict without asking Jev",
|
|
217
|
+
"notesInFlow": true
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"name": "Jev · TypeSafe (our credential)",
|
|
221
|
+
"type": "@taifoon/n8n-nodes-typesafe.taifoonTypeSafe",
|
|
222
|
+
"typeVersion": 1,
|
|
223
|
+
"position": [
|
|
224
|
+
1320,
|
|
225
|
+
420
|
|
226
|
+
],
|
|
227
|
+
"parameters": {
|
|
228
|
+
"operation": "ask",
|
|
229
|
+
"connection": "direct",
|
|
230
|
+
"state": "={{ JSON.stringify($json.jev.state) }}",
|
|
231
|
+
"questions": {},
|
|
232
|
+
"questionsJson": "={{ JSON.stringify($json.jev.questions) }}",
|
|
233
|
+
"routing": "{}",
|
|
234
|
+
"replyLanguage": "off",
|
|
235
|
+
"failClosed": true
|
|
236
|
+
},
|
|
237
|
+
"credentials": {
|
|
238
|
+
"taifoonTypeSafeApi": {
|
|
239
|
+
"id": "",
|
|
240
|
+
"name": "REPLACE: taifoonTypeSafeApi"
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
"notes": "in: facts.json → out: answers.json · TypeSafe ask on the prepared text: every RUBRIC_ASKED answer with its whole distribution",
|
|
244
|
+
"id": "ba778a32-16e0-4076-b815-ffd923ee73f0",
|
|
245
|
+
"notesInFlow": true
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "Judge · answers",
|
|
249
|
+
"type": "CUSTOM.taifoon",
|
|
250
|
+
"typeVersion": 1,
|
|
251
|
+
"position": [
|
|
252
|
+
1540,
|
|
253
|
+
420
|
|
254
|
+
],
|
|
255
|
+
"parameters": {
|
|
256
|
+
"resource": "judge",
|
|
257
|
+
"operation": "compose",
|
|
258
|
+
"composeSubject": "handshake",
|
|
259
|
+
"composeHandshakeId": "={{ $('Delivery').first().json.handshake_id }}",
|
|
260
|
+
"composePriceUsdc": 1,
|
|
261
|
+
"composeMode": "answers",
|
|
262
|
+
"composeAnswers": "={{ JSON.stringify($json.answers) }}",
|
|
263
|
+
"composeModel": "={{ $json.model }}",
|
|
264
|
+
"composePrepareDigest": "={{ $('Judge · prepare').first().json.prepare_digest }}",
|
|
265
|
+
"composeAnsweredBy": "n8n-typesafe",
|
|
266
|
+
"composeExecutionId": "={{ $execution.id }}",
|
|
267
|
+
"composeLatencyMs": "={{ $json.latency_ms }}",
|
|
268
|
+
"composeRecord": "devnet"
|
|
269
|
+
},
|
|
270
|
+
"credentials": {
|
|
271
|
+
"taifoonRelayerApi": {
|
|
272
|
+
"id": "",
|
|
273
|
+
"name": "REPLACE: taifoonRelayerApi"
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"id": "7b18e907-2293-4bb6-8199-c2a090bb7839",
|
|
277
|
+
"notes": "in: answers.json → out: verdict.json · POST /v1/judge/compose (answers): recompose under THRESHOLDS_v1, record decision.v2, anchor on JevDecisionLog",
|
|
278
|
+
"notesInFlow": true
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
"parameters": {
|
|
282
|
+
"jsCode": "// The verdict, with what the settlement needs carried forward. Two roads reach here: a hard fail straight from\n// Judge · prepare (the facts decided; Jev was not asked), or Judge · answers (our TypeSafe credential answered, the layer composed).\n// The receipt is the composed verdict: verdict (complete | reject | needs_review), auto, reasons[], receiptHash.\nconst j = $input.first().json;\nconst del = $('Delivery').first().json;\nconst inp = $('Inputs').first().json;\nif (j.ok === false) throw new Error('judge refused: ' + (j.code ? j.code + ': ' : '') + (j.error || JSON.stringify(j).slice(0, 300)));\nconst decision = j.decision || {};\nconst rc = j.receipt || {};\nreturn [{ json: { ...inp, handshake_id: del.handshake_id, evidence: del.evidence, reply_head: del.reply_head, decision_id: decision.id || null, decision_digest: decision.digest || null, decision_anchor: decision.anchor || null, verdict: rc.verdict || null, verdict_auto: rc.auto ?? null, forced: rc.forced || null, reasons: rc.reasons || [], receipt_hash: rc.receiptHash || null, next: j.next || null, judge_called: j.judge_called ?? null, model: j.model || null, judge_latency_ms: j.latency_ms ?? null, answers_source: j.answers_source || null, execution: j.execution || null, prepare_digest: j.prepare_digest || null, quota: j.quota || null } }];"
|
|
283
|
+
},
|
|
284
|
+
"id": "n5",
|
|
285
|
+
"name": "Verdict",
|
|
286
|
+
"type": "n8n-nodes-base.code",
|
|
287
|
+
"typeVersion": 2,
|
|
288
|
+
"position": [
|
|
289
|
+
1760,
|
|
290
|
+
300
|
|
291
|
+
],
|
|
292
|
+
"notes": "in: verdict.json | facts.json → out: verdict.json · the composed verdict (complete | reject | needs_review) with what settlement needs carried forward",
|
|
293
|
+
"notesInFlow": true
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
"parameters": {
|
|
297
|
+
"resource": "settlement",
|
|
298
|
+
"operation": "settle",
|
|
299
|
+
"kind": "agent",
|
|
300
|
+
"chainId": 36927,
|
|
301
|
+
"buyer": "={{ $json.buyer }}",
|
|
302
|
+
"seller": "={{ $json.seller }}",
|
|
303
|
+
"price": "={{ $json.price }}",
|
|
304
|
+
"evidence": "={{ $json.evidence }}",
|
|
305
|
+
"refId": "={{ $json.handshake_id }}",
|
|
306
|
+
"settleOptions": {
|
|
307
|
+
"endpoint": "={{ $json.endpoint }}",
|
|
308
|
+
"note": "={{ 'agent ' + $json.candidate + ' · decision ' + ($json.decision_id || 'none') + ' · verdict ' + ($json.verdict || 'n/a') }}",
|
|
309
|
+
"tenant": "={{ $json.tenant }}",
|
|
310
|
+
"deadline": "={{ Math.floor(Date.now() / 1000) + Number($json.deadline_sec || 3600) }}"
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
"credentials": {
|
|
314
|
+
"taifoonRelayerApi": {
|
|
315
|
+
"id": "",
|
|
316
|
+
"name": "REPLACE: taifoonRelayerApi"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"id": "n6",
|
|
320
|
+
"name": "Settle",
|
|
321
|
+
"type": "CUSTOM.taifoon",
|
|
322
|
+
"typeVersion": 1,
|
|
323
|
+
"position": [
|
|
324
|
+
1980,
|
|
325
|
+
300
|
|
326
|
+
],
|
|
327
|
+
"notes": "in: verdict.json → out: settlement.json · POST /v1/settle: the plan (jobId, price, premium, deposit, calls[] in order, who signs each)",
|
|
328
|
+
"notesInFlow": true
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"parameters": {
|
|
332
|
+
"callsField": "calls",
|
|
333
|
+
"onlySigner": "seller",
|
|
334
|
+
"onlyActions": "approve-deposit",
|
|
335
|
+
"sendRequires": false,
|
|
336
|
+
"wait": true
|
|
337
|
+
},
|
|
338
|
+
"credentials": {
|
|
339
|
+
"taifoonDevnetKeyApi": {
|
|
340
|
+
"id": "",
|
|
341
|
+
"name": "REPLACE: taifoonDevnetKeyApi"
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"id": "n7",
|
|
345
|
+
"name": "Seller · approve deposit",
|
|
346
|
+
"type": "CUSTOM.taifoonDevnetSigner",
|
|
347
|
+
"typeVersion": 1,
|
|
348
|
+
"position": [
|
|
349
|
+
2200,
|
|
350
|
+
300
|
|
351
|
+
],
|
|
352
|
+
"notes": "in: settlement.json → out: settlement.json · the seller approves D (> P); the tx joins settlement.txs",
|
|
353
|
+
"notesInFlow": true
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"parameters": {
|
|
357
|
+
"callsField": "calls",
|
|
358
|
+
"onlySigner": "buyer",
|
|
359
|
+
"onlyActions": "fund-job",
|
|
360
|
+
"sendRequires": false,
|
|
361
|
+
"wait": true
|
|
362
|
+
},
|
|
363
|
+
"credentials": {
|
|
364
|
+
"taifoonDevnetKeyApi": {
|
|
365
|
+
"id": "",
|
|
366
|
+
"name": "REPLACE: taifoonDevnetKeyApi"
|
|
367
|
+
}
|
|
368
|
+
},
|
|
369
|
+
"id": "n8",
|
|
370
|
+
"name": "Buyer · approve + fund",
|
|
371
|
+
"type": "CUSTOM.taifoonDevnetSigner",
|
|
372
|
+
"typeVersion": 1,
|
|
373
|
+
"position": [
|
|
374
|
+
2420,
|
|
375
|
+
300
|
|
376
|
+
],
|
|
377
|
+
"notes": "in: settlement.json → out: settlement.json · the buyer approves P + π and calls fundJob: machine Delivered → Funded",
|
|
378
|
+
"notesInFlow": true
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
"parameters": {
|
|
382
|
+
"callsField": "calls",
|
|
383
|
+
"onlySigner": "seller",
|
|
384
|
+
"onlyActions": "submit",
|
|
385
|
+
"sendRequires": false,
|
|
386
|
+
"wait": true
|
|
387
|
+
},
|
|
388
|
+
"credentials": {
|
|
389
|
+
"taifoonDevnetKeyApi": {
|
|
390
|
+
"id": "",
|
|
391
|
+
"name": "REPLACE: taifoonDevnetKeyApi"
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
"id": "n9",
|
|
395
|
+
"name": "Seller · submit",
|
|
396
|
+
"type": "CUSTOM.taifoonDevnetSigner",
|
|
397
|
+
"typeVersion": 1,
|
|
398
|
+
"position": [
|
|
399
|
+
2640,
|
|
400
|
+
300
|
|
401
|
+
],
|
|
402
|
+
"notes": "in: settlement.json → out: settlement.json · submit(jobId, evidenceDigest = delivery.digest): machine Funded → Submitted",
|
|
403
|
+
"notesInFlow": true
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"parameters": {
|
|
407
|
+
"callsField": "calls",
|
|
408
|
+
"onlySigner": "",
|
|
409
|
+
"onlyActions": "complete,capture",
|
|
410
|
+
"sendRequires": false,
|
|
411
|
+
"wait": true
|
|
412
|
+
},
|
|
413
|
+
"credentials": {
|
|
414
|
+
"taifoonDevnetKeyApi": {
|
|
415
|
+
"id": "",
|
|
416
|
+
"name": "REPLACE: taifoonDevnetKeyApi"
|
|
417
|
+
}
|
|
418
|
+
},
|
|
419
|
+
"id": "n10",
|
|
420
|
+
"name": "Buyer · complete + capture",
|
|
421
|
+
"type": "CUSTOM.taifoonDevnetSigner",
|
|
422
|
+
"typeVersion": 1,
|
|
423
|
+
"position": [
|
|
424
|
+
3080,
|
|
425
|
+
160
|
|
426
|
+
],
|
|
427
|
+
"notes": "in: settlement.json → out: settlement.json · complete(jobId) (+ capture): machine → Settled",
|
|
428
|
+
"notesInFlow": true
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
"parameters": {
|
|
432
|
+
"method": "POST",
|
|
433
|
+
"url": "https://www.taifoon.io/v1/settle/decided",
|
|
434
|
+
"authentication": "predefinedCredentialType",
|
|
435
|
+
"nodeCredentialType": "taifoonRelayerApi",
|
|
436
|
+
"sendBody": true,
|
|
437
|
+
"specifyBody": "json",
|
|
438
|
+
"jsonBody": "={{ JSON.stringify({ chainId: 36927, jobId: $('Settle').first().json.jobId, decision_id: $('Verdict').first().json.decision_id, end_tx: (($('Buyer · complete + capture').first().json.sent || []).find((s) => s.action === 'complete') || {}).tx || null }) }}",
|
|
439
|
+
"options": {
|
|
440
|
+
"timeout": 30000,
|
|
441
|
+
"response": {
|
|
442
|
+
"response": {
|
|
443
|
+
"neverError": true
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
"name": "Settle row · decided",
|
|
449
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
450
|
+
"typeVersion": 4.2,
|
|
451
|
+
"position": [
|
|
452
|
+
3190,
|
|
453
|
+
20
|
|
454
|
+
],
|
|
455
|
+
"id": "n10dec",
|
|
456
|
+
"credentials": {
|
|
457
|
+
"taifoonRelayerApi": {
|
|
458
|
+
"id": "",
|
|
459
|
+
"name": "REPLACE: taifoonRelayerApi"
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
"notes": "in: settlement.json → out: settlement.json · the settle row names the decision, not a document: POST /v1/settle/decided { jobId, decision_id, end_tx = the complete transaction } on the lane’s relayer key. The layer writes decided_by \"jev\" only when the chain proves it (an automatic complete, the decision’s anchor on the JevDecisionLog, the job bound to the handshake and its sealed reply, Completed for this job); it never errors the run"
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"parameters": {
|
|
466
|
+
"jsCode": "// The record: every transaction with its proof and explorer link, the decision, the job, and whether money moved.\nconst legs = ['Seller · approve deposit', 'Buyer · approve + fund', 'Seller · submit', 'Buyer · complete + capture'];\nconst txs = [];\nfor (const n of legs) { let sent = []; try { sent = $(n).first().json.sent || []; } catch (e) { /* a leg the verdict skipped */ }\n for (const s of sent) txs.push({ leg: n, action: s.action, tx: s.tx, status: s.status, block: s.block, proof: 'https://www.taifoon.io/v1/proof/tx/36927/' + s.tx, scan: 'https://www.taifoon.io/scan/36927/tx/' + s.tx }); }\nlet held = null; try { held = $('Held for review').first().json; } catch (e) { held = null; }\n// _SETTLE_DECIDED_v1_: what the layer wrote in the settle row after the complete (decided_by jev, or why not)\nlet decided = null; try { decided = $('Settle row · decided').first().json; } catch (e) { decided = null; }\nconst s = $('Settle').first().json;\nconst v = $('Verdict').first().json;\nconst complete = txs.find((t) => t.action === 'complete') || null;\nreturn [{ json: { handshake_id: v.handshake_id, decision_id: v.decision_id, decision_digest: v.decision_digest, decision_anchor_tx: v.decision_anchor ? v.decision_anchor.tx : null, verdict: v.verdict, verdict_auto: v.verdict_auto, reasons: v.reasons, receipt_hash: v.receipt_hash, judge: { called: v.judge_called, model: v.model, latency_ms: v.judge_latency_ms, answers_source: v.answers_source, execution: v.execution, prepare_digest: v.prepare_digest, quota: v.quota }, ending: complete ? 'completed: seller paid, deposit returned, payment captured' : held ? 'held: ' + held.next : 'incomplete', lane: v.lane || null, tenant: s.tenant || null, deadline: s.deadline ?? null, jobId: s.jobId, correlator: s.correlator, chainId: 36927, hook: s.hook, token: s.token, price: s.price, premium: s.premium, deposit: s.deposit, evidence: v.evidence, settlement_tx: complete ? complete.tx : null, settlement_proof: complete ? complete.proof : null, settle_row: decided ? { decided_by: decided.ok ? decided.decided_by || null : null, decision_id: decided.ok ? decided.decision_id || null : null, refused: decided.ok ? null : (decided.code || decided.error || 'no answer'), row: 'https://www.taifoon.io/v1/settle/' + s.jobId } : null, transactions: txs, all_mined: txs.every((t) => t.status === 'success') && txs.length === (complete ? 6 : 4) } }];"
|
|
467
|
+
},
|
|
468
|
+
"id": "n11",
|
|
469
|
+
"name": "Receipt",
|
|
470
|
+
"type": "n8n-nodes-base.code",
|
|
471
|
+
"typeVersion": 2,
|
|
472
|
+
"position": [
|
|
473
|
+
3300,
|
|
474
|
+
300
|
|
475
|
+
],
|
|
476
|
+
"notes": "in: settlement.json → out: job.json · the whole JOB: hire, delivery, facts, verdict, settlement — every transaction with its proof link",
|
|
477
|
+
"notesInFlow": true
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
"parameters": {
|
|
481
|
+
"conditions": {
|
|
482
|
+
"options": {
|
|
483
|
+
"caseSensitive": true,
|
|
484
|
+
"leftValue": "",
|
|
485
|
+
"typeValidation": "strict",
|
|
486
|
+
"version": 2
|
|
487
|
+
},
|
|
488
|
+
"conditions": [
|
|
489
|
+
{
|
|
490
|
+
"id": "g1",
|
|
491
|
+
"leftValue": "={{ $('Verdict').first().json.verdict }}",
|
|
492
|
+
"rightValue": "complete",
|
|
493
|
+
"operator": {
|
|
494
|
+
"type": "string",
|
|
495
|
+
"operation": "equals"
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
],
|
|
499
|
+
"combinator": "and"
|
|
500
|
+
},
|
|
501
|
+
"options": {}
|
|
502
|
+
},
|
|
503
|
+
"id": "n12",
|
|
504
|
+
"name": "Verdict says complete?",
|
|
505
|
+
"type": "n8n-nodes-base.if",
|
|
506
|
+
"typeVersion": 2.2,
|
|
507
|
+
"position": [
|
|
508
|
+
2860,
|
|
509
|
+
300
|
|
510
|
+
],
|
|
511
|
+
"notes": "in: settlement.json → out: settlement.json · branch on the composed verdict: complete → complete; otherwise hold (Submitted → Held)",
|
|
512
|
+
"notesInFlow": true
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
"parameters": {
|
|
516
|
+
"method": "POST",
|
|
517
|
+
"url": "https://www.taifoon.io/v1/settle/evaluate",
|
|
518
|
+
"authentication": "predefinedCredentialType",
|
|
519
|
+
"nodeCredentialType": "taifoonRelayerApi",
|
|
520
|
+
"sendBody": true,
|
|
521
|
+
"specifyBody": "json",
|
|
522
|
+
"jsonBody": "={{ JSON.stringify({ open: true, chainId: 36927, tenant: $('Settle').first().json.tenant || undefined, jobId: $('Settle').first().json.jobId, decision_id: $('Verdict').first().json.decision_id }) }}",
|
|
523
|
+
"options": {
|
|
524
|
+
"timeout": 30000,
|
|
525
|
+
"response": {
|
|
526
|
+
"response": {
|
|
527
|
+
"neverError": true
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
},
|
|
532
|
+
"name": "Operator step · open",
|
|
533
|
+
"type": "n8n-nodes-base.httpRequest",
|
|
534
|
+
"typeVersion": 4.2,
|
|
535
|
+
"position": [
|
|
536
|
+
3080,
|
|
537
|
+
640
|
|
538
|
+
],
|
|
539
|
+
"id": "n13op",
|
|
540
|
+
"credentials": {
|
|
541
|
+
"taifoonRelayerApi": {
|
|
542
|
+
"id": "",
|
|
543
|
+
"name": "REPLACE: taifoonRelayerApi"
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
"notes": "in: settlement.json → out: settlement.json · the evaluator leg, not a document: POST /v1/settle/evaluate {open:true} opens the operator step of a job held on needs_review. The workflow does not decide: the step waits for an operator record (decided_by loop-agent on the devnet, or operator with the operator key), anchored on chain before the evaluator posts. A composed reject is not an operator step (the layer answers not_needs_review)",
|
|
547
|
+
"notesInFlow": true
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
"parameters": {
|
|
551
|
+
"jsCode": "// Held: the job is funded and the evidence is sealed; nothing is paid. The workflow does not decide: on needs_review the\n// operator step was opened through the evaluator leg (the node before), and it waits for an operator record.\nconst v = $('Verdict').first().json;\nconst s = $('Settle').first().json;\nlet op = null; try { op = $('Operator step · open').first().json; } catch (e) { op = null; }\nconst step = op && op.ok && op.operator_step ? op.operator_step : null;\nconst next = step\n ? 'needs_review: operator step ' + step.status + ' (GET /v1/settle/operator/' + s.jobId + '); it waits for ' + step.waits_for\n : v.next || (v.verdict === 'reject' ? 'reject: the buyer may reject on chain; the pool pays nothing on a rejection' : 'needs_review: the operator step was not opened (' + ((op && (op.code || op.error)) || 'no answer') + '); after the deadline anyone may expire the job');\nreturn [{ json: { held: true, verdict: v.verdict, reasons: v.reasons, next, operator_step: step ? { status: step.status, jev: step.jev, waits_for: step.waits_for } : null, jobId: s.jobId, hook: s.hook, chainId: s.chainId, deadline: s.deadline, what_is_on_chain: 'fundJob (price + premium in escrow, seller deposit locked) and submit (evidence digest sealed); complete/reject not sent' } }];"
|
|
552
|
+
},
|
|
553
|
+
"id": "n13",
|
|
554
|
+
"name": "Held for review",
|
|
555
|
+
"type": "n8n-nodes-base.code",
|
|
556
|
+
"typeVersion": 2,
|
|
557
|
+
"position": [
|
|
558
|
+
3080,
|
|
559
|
+
480
|
|
560
|
+
],
|
|
561
|
+
"notes": "in: settlement.json → out: settlement.json · needs_review / reject: funded and sealed, nothing paid; the operator step (a record, then the evaluator’s postVerdict) or the deadline decides",
|
|
562
|
+
"notesInFlow": true
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
"parameters": {
|
|
566
|
+
"jsCode": "// Every transaction of the run is proven under the V5 superroot before the run ends (/v1/proof/tx/36927/<tx>).\n// The producer runs ~6-8 devnet blocks (~15 s) behind the head, and its blob route can answer \"pending\" under load:\n// a pending answer is waited out with backoff (5 s doubling to 60 s), never reported as a gap. The wait is capped at\n// 240 s (n8n's task-runner limit); what is still pending is listed, and the runner (workflows/e2e.sh) keeps waiting.\nconst r = $input.first().json;\nconst SITE = 'https://www.taifoon.io';\nconst txs = [...(r.transactions || []).map((t) => ({ leg: t.leg, action: t.action, tx: t.tx })), ...(r.decision_anchor_tx ? [{ leg: 'decision anchor', action: 'JevDecisionLog.record', tx: r.decision_anchor_tx }] : [])];\nconst until = Date.now() + 240000;\nconst sleep = (ms) => new Promise((res) => setTimeout(res, ms));\nconst final = (c) => Boolean(c && c.proof_served && c.block_hash_matches && c.within_verifiable_range);\nconst proofs = [];\nfor (const t of txs) {\n let wait = 5000, attempts = 0, last = null;\n for (;;) {\n attempts++;\n try { last = await this.helpers.httpRequest({ method: 'GET', url: SITE + '/v1/proof/tx/36927/' + t.tx + (attempts > 3 ? '?wait=1' : ''), json: true, timeout: 60000, ignoreHttpStatusErrors: true }); }\n catch (e) { last = { ok: false, error: String((e && e.message) || e).slice(0, 160) }; }\n if (final(last && last.checks)) break;\n if (last && last.code === 'PROVABLE_GAP' && last.gap) break; // the producer skipped this block: no wait fills it\n if (Date.now() + wait > until) break;\n await sleep(wait); wait = Math.min(wait * 2, 60000);\n }\n const c = (last && last.checks) || {};\n proofs.push({ ...t, finalized: final(c), gap: Boolean(last && last.gap), attempts, checks: c, proof: SITE + '/v1/proof/tx/36927/' + t.tx });\n}\nreturn [{ json: { ...r, proofs, all_proven: proofs.length > 0 && proofs.every((p) => p.finalized), proofs_pending: proofs.filter((p) => !p.finalized).map((p) => p.tx) } }];"
|
|
567
|
+
},
|
|
568
|
+
"id": "n17",
|
|
569
|
+
"name": "Proofs · wait",
|
|
570
|
+
"type": "n8n-nodes-base.code",
|
|
571
|
+
"typeVersion": 2,
|
|
572
|
+
"position": [
|
|
573
|
+
3520,
|
|
574
|
+
300
|
|
575
|
+
],
|
|
576
|
+
"notes": "in: job.json → out: job.json · GET /v1/proof/tx/36927/<tx> for every transaction of the run; pending is retried with backoff (5 s -> 60 s, <= 240 s), never reported as a gap",
|
|
577
|
+
"notesInFlow": true
|
|
578
|
+
}
|
|
579
|
+
],
|
|
580
|
+
"connections": {
|
|
581
|
+
"Start": {
|
|
582
|
+
"main": [
|
|
583
|
+
[
|
|
584
|
+
{
|
|
585
|
+
"node": "Inputs",
|
|
586
|
+
"type": "main",
|
|
587
|
+
"index": 0
|
|
588
|
+
}
|
|
589
|
+
]
|
|
590
|
+
]
|
|
591
|
+
},
|
|
592
|
+
"Inputs": {
|
|
593
|
+
"main": [
|
|
594
|
+
[
|
|
595
|
+
{
|
|
596
|
+
"node": "Offer + deliver",
|
|
597
|
+
"type": "main",
|
|
598
|
+
"index": 0
|
|
599
|
+
}
|
|
600
|
+
]
|
|
601
|
+
]
|
|
602
|
+
},
|
|
603
|
+
"Offer + deliver": {
|
|
604
|
+
"main": [
|
|
605
|
+
[
|
|
606
|
+
{
|
|
607
|
+
"node": "Delivery",
|
|
608
|
+
"type": "main",
|
|
609
|
+
"index": 0
|
|
610
|
+
}
|
|
611
|
+
]
|
|
612
|
+
]
|
|
613
|
+
},
|
|
614
|
+
"Delivery": {
|
|
615
|
+
"main": [
|
|
616
|
+
[
|
|
617
|
+
{
|
|
618
|
+
"node": "Judge · prepare",
|
|
619
|
+
"type": "main",
|
|
620
|
+
"index": 0
|
|
621
|
+
}
|
|
622
|
+
]
|
|
623
|
+
]
|
|
624
|
+
},
|
|
625
|
+
"Verdict": {
|
|
626
|
+
"main": [
|
|
627
|
+
[
|
|
628
|
+
{
|
|
629
|
+
"node": "Settle",
|
|
630
|
+
"type": "main",
|
|
631
|
+
"index": 0
|
|
632
|
+
}
|
|
633
|
+
]
|
|
634
|
+
]
|
|
635
|
+
},
|
|
636
|
+
"Settle": {
|
|
637
|
+
"main": [
|
|
638
|
+
[
|
|
639
|
+
{
|
|
640
|
+
"node": "Seller · approve deposit",
|
|
641
|
+
"type": "main",
|
|
642
|
+
"index": 0
|
|
643
|
+
}
|
|
644
|
+
]
|
|
645
|
+
]
|
|
646
|
+
},
|
|
647
|
+
"Seller · approve deposit": {
|
|
648
|
+
"main": [
|
|
649
|
+
[
|
|
650
|
+
{
|
|
651
|
+
"node": "Buyer · approve + fund",
|
|
652
|
+
"type": "main",
|
|
653
|
+
"index": 0
|
|
654
|
+
}
|
|
655
|
+
]
|
|
656
|
+
]
|
|
657
|
+
},
|
|
658
|
+
"Buyer · approve + fund": {
|
|
659
|
+
"main": [
|
|
660
|
+
[
|
|
661
|
+
{
|
|
662
|
+
"node": "Seller · submit",
|
|
663
|
+
"type": "main",
|
|
664
|
+
"index": 0
|
|
665
|
+
}
|
|
666
|
+
]
|
|
667
|
+
]
|
|
668
|
+
},
|
|
669
|
+
"Seller · submit": {
|
|
670
|
+
"main": [
|
|
671
|
+
[
|
|
672
|
+
{
|
|
673
|
+
"node": "Verdict says complete?",
|
|
674
|
+
"type": "main",
|
|
675
|
+
"index": 0
|
|
676
|
+
}
|
|
677
|
+
]
|
|
678
|
+
]
|
|
679
|
+
},
|
|
680
|
+
"Buyer · complete + capture": {
|
|
681
|
+
"main": [
|
|
682
|
+
[
|
|
683
|
+
{
|
|
684
|
+
"node": "Settle row · decided",
|
|
685
|
+
"type": "main",
|
|
686
|
+
"index": 0
|
|
687
|
+
}
|
|
688
|
+
]
|
|
689
|
+
]
|
|
690
|
+
},
|
|
691
|
+
"Verdict says complete?": {
|
|
692
|
+
"main": [
|
|
693
|
+
[
|
|
694
|
+
{
|
|
695
|
+
"node": "Buyer · complete + capture",
|
|
696
|
+
"type": "main",
|
|
697
|
+
"index": 0
|
|
698
|
+
}
|
|
699
|
+
],
|
|
700
|
+
[
|
|
701
|
+
{
|
|
702
|
+
"node": "Operator step · open",
|
|
703
|
+
"type": "main",
|
|
704
|
+
"index": 0
|
|
705
|
+
}
|
|
706
|
+
]
|
|
707
|
+
]
|
|
708
|
+
},
|
|
709
|
+
"Held for review": {
|
|
710
|
+
"main": [
|
|
711
|
+
[
|
|
712
|
+
{
|
|
713
|
+
"node": "Receipt",
|
|
714
|
+
"type": "main",
|
|
715
|
+
"index": 0
|
|
716
|
+
}
|
|
717
|
+
]
|
|
718
|
+
]
|
|
719
|
+
},
|
|
720
|
+
"Judge · prepare": {
|
|
721
|
+
"main": [
|
|
722
|
+
[
|
|
723
|
+
{
|
|
724
|
+
"node": "Hard fail?",
|
|
725
|
+
"type": "main",
|
|
726
|
+
"index": 0
|
|
727
|
+
}
|
|
728
|
+
]
|
|
729
|
+
]
|
|
730
|
+
},
|
|
731
|
+
"Hard fail?": {
|
|
732
|
+
"main": [
|
|
733
|
+
[
|
|
734
|
+
{
|
|
735
|
+
"node": "Verdict",
|
|
736
|
+
"type": "main",
|
|
737
|
+
"index": 0
|
|
738
|
+
}
|
|
739
|
+
],
|
|
740
|
+
[
|
|
741
|
+
{
|
|
742
|
+
"node": "Jev · TypeSafe (our credential)",
|
|
743
|
+
"type": "main",
|
|
744
|
+
"index": 0
|
|
745
|
+
}
|
|
746
|
+
]
|
|
747
|
+
]
|
|
748
|
+
},
|
|
749
|
+
"Jev · TypeSafe (our credential)": {
|
|
750
|
+
"main": [
|
|
751
|
+
[
|
|
752
|
+
{
|
|
753
|
+
"node": "Judge · answers",
|
|
754
|
+
"type": "main",
|
|
755
|
+
"index": 0
|
|
756
|
+
}
|
|
757
|
+
]
|
|
758
|
+
]
|
|
759
|
+
},
|
|
760
|
+
"Judge · answers": {
|
|
761
|
+
"main": [
|
|
762
|
+
[
|
|
763
|
+
{
|
|
764
|
+
"node": "Verdict",
|
|
765
|
+
"type": "main",
|
|
766
|
+
"index": 0
|
|
767
|
+
}
|
|
768
|
+
]
|
|
769
|
+
]
|
|
770
|
+
},
|
|
771
|
+
"Receipt": {
|
|
772
|
+
"main": [
|
|
773
|
+
[
|
|
774
|
+
{
|
|
775
|
+
"node": "Proofs · wait",
|
|
776
|
+
"type": "main",
|
|
777
|
+
"index": 0
|
|
778
|
+
}
|
|
779
|
+
]
|
|
780
|
+
]
|
|
781
|
+
},
|
|
782
|
+
"Operator step · open": {
|
|
783
|
+
"main": [
|
|
784
|
+
[
|
|
785
|
+
{
|
|
786
|
+
"node": "Held for review",
|
|
787
|
+
"type": "main",
|
|
788
|
+
"index": 0
|
|
789
|
+
}
|
|
790
|
+
]
|
|
791
|
+
]
|
|
792
|
+
},
|
|
793
|
+
"Settle row · decided": {
|
|
794
|
+
"main": [
|
|
795
|
+
[
|
|
796
|
+
{
|
|
797
|
+
"node": "Receipt",
|
|
798
|
+
"type": "main",
|
|
799
|
+
"index": 0
|
|
800
|
+
}
|
|
801
|
+
]
|
|
802
|
+
]
|
|
803
|
+
}
|
|
804
|
+
},
|
|
805
|
+
"settings": {
|
|
806
|
+
"executionOrder": "v1"
|
|
807
|
+
},
|
|
808
|
+
"active": false,
|
|
809
|
+
"meta": {
|
|
810
|
+
"taifoon": {
|
|
811
|
+
"contract": "https://www.taifoon.io/schemas/coordination/v1/",
|
|
812
|
+
"steps": {
|
|
813
|
+
"Start": {
|
|
814
|
+
"in": null,
|
|
815
|
+
"out": null,
|
|
816
|
+
"does": "start a run (swap for a Webhook or Schedule trigger)"
|
|
817
|
+
},
|
|
818
|
+
"Inputs": {
|
|
819
|
+
"in": null,
|
|
820
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/hire.json",
|
|
821
|
+
"does": "the hire intent: task, candidate seller, endpoint/tool, buyer, price, chain (a HIRE in state Open)"
|
|
822
|
+
},
|
|
823
|
+
"Offer + deliver": {
|
|
824
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/hire.json",
|
|
825
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
|
|
826
|
+
"does": "POST /v1/handshake {dispatch:true}: the offer goes to the seller’s own endpoint; the reply is held with its keccak digest"
|
|
827
|
+
},
|
|
828
|
+
"Delivery": {
|
|
829
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/job.json",
|
|
830
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/delivery.json",
|
|
831
|
+
"does": "stop unless a reply digest is held (no digest = nothing to settle)"
|
|
832
|
+
},
|
|
833
|
+
"Judge · prepare": {
|
|
834
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/delivery.json",
|
|
835
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
|
|
836
|
+
"does": "POST /v1/judge/compose (prepare): the facts decide first; hard_fail ends here; else the exact text Jev reads + prepare_digest"
|
|
837
|
+
},
|
|
838
|
+
"Hard fail?": {
|
|
839
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
|
|
840
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
|
|
841
|
+
"does": "branch: a hard fail goes straight to the verdict without asking Jev"
|
|
842
|
+
},
|
|
843
|
+
"Jev · TypeSafe (our credential)": {
|
|
844
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
|
|
845
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
|
|
846
|
+
"does": "TypeSafe ask on the prepared text: every RUBRIC_ASKED answer with its whole distribution"
|
|
847
|
+
},
|
|
848
|
+
"Judge · answers": {
|
|
849
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
|
|
850
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
851
|
+
"does": "POST /v1/judge/compose (answers): recompose under THRESHOLDS_v1, record decision.v2, anchor on JevDecisionLog"
|
|
852
|
+
},
|
|
853
|
+
"Verdict": {
|
|
854
|
+
"in": [
|
|
855
|
+
"https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
856
|
+
"https://www.taifoon.io/schemas/coordination/v1/facts.json"
|
|
857
|
+
],
|
|
858
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
859
|
+
"does": "the composed verdict (complete | reject | needs_review) with what settlement needs carried forward"
|
|
860
|
+
},
|
|
861
|
+
"Settle": {
|
|
862
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
863
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
864
|
+
"does": "POST /v1/settle: the plan (jobId, price, premium, deposit, calls[] in order, who signs each)"
|
|
865
|
+
},
|
|
866
|
+
"Seller · approve deposit": {
|
|
867
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
868
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
869
|
+
"does": "the seller approves D (> P); the tx joins settlement.txs"
|
|
870
|
+
},
|
|
871
|
+
"Buyer · approve + fund": {
|
|
872
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
873
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
874
|
+
"does": "the buyer approves P + π and calls fundJob: machine Delivered → Funded"
|
|
875
|
+
},
|
|
876
|
+
"Seller · submit": {
|
|
877
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
878
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
879
|
+
"does": "submit(jobId, evidenceDigest = delivery.digest): machine Funded → Submitted"
|
|
880
|
+
},
|
|
881
|
+
"Verdict says complete?": {
|
|
882
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
883
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
884
|
+
"does": "branch on the composed verdict: complete → complete; otherwise hold (Submitted → Held)"
|
|
885
|
+
},
|
|
886
|
+
"Buyer · complete + capture": {
|
|
887
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
888
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
889
|
+
"does": "complete(jobId) (+ capture): machine → Settled"
|
|
890
|
+
},
|
|
891
|
+
"Settle row · decided": {
|
|
892
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
893
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
894
|
+
"does": "the settle row names the decision, not a document: POST /v1/settle/decided { jobId, decision_id, end_tx = the complete transaction } on the lane’s relayer key. The layer writes decided_by \"jev\" only when the chain proves it (an automatic complete, the decision’s anchor on the JevDecisionLog, the job bound to the handshake and its sealed reply, Completed for this job); it never errors the run"
|
|
895
|
+
},
|
|
896
|
+
"Held for review": {
|
|
897
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
898
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
899
|
+
"does": "needs_review / reject: funded and sealed, nothing paid; the operator step (a record, then the evaluator’s postVerdict) or the deadline decides"
|
|
900
|
+
},
|
|
901
|
+
"Receipt": {
|
|
902
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
903
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
|
|
904
|
+
"does": "the whole JOB: hire, delivery, facts, verdict, settlement — every transaction with its proof link"
|
|
905
|
+
},
|
|
906
|
+
"Proofs · wait": {
|
|
907
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/job.json",
|
|
908
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
|
|
909
|
+
"does": "GET /v1/proof/tx/36927/<tx> for every transaction of the run; pending is retried with backoff (5 s -> 60 s, <= 240 s), never reported as a gap"
|
|
910
|
+
},
|
|
911
|
+
"Operator step · open": {
|
|
912
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
913
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
914
|
+
"does": "the evaluator leg, not a document: POST /v1/settle/evaluate {open:true} opens the operator step of a job held on needs_review. The workflow does not decide: the step waits for an operator record (decided_by loop-agent on the devnet, or operator with the operator key), anchored on chain before the evaluator posts. A composed reject is not an operator step (the layer answers not_needs_review)"
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
"tags": []
|
|
920
|
+
}
|