@taifoon/jev 0.2.5 → 0.2.7

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.
@@ -0,0 +1,433 @@
1
+ {
2
+ "name": "Taifoon — Jev on-chain grader (grade → stamp → devnet)",
3
+ "nodes": [
4
+ {
5
+ "parameters": {},
6
+ "id": "trigger",
7
+ "name": "When clicking ‘Execute workflow’",
8
+ "type": "n8n-nodes-base.manualTrigger",
9
+ "typeVersion": 1,
10
+ "position": [
11
+ 0,
12
+ 0
13
+ ],
14
+ "notes": "in: — → out: — · start a grading run",
15
+ "notesInFlow": true
16
+ },
17
+ {
18
+ "parameters": {
19
+ "resource": "judge",
20
+ "operation": "getReady",
21
+ "readyChain": 8453,
22
+ "readyLimit": 4
23
+ },
24
+ "id": "ready",
25
+ "name": "Ready jobs (Base)",
26
+ "type": "CUSTOM.taifoon",
27
+ "typeVersion": 1,
28
+ "position": [
29
+ 460,
30
+ 300
31
+ ],
32
+ "notes": "in: — → out: job.json · GET /v1/judge/ready: jobs on Base that were paid and delivered, and nobody ruled",
33
+ "notesInFlow": true
34
+ },
35
+ {
36
+ "parameters": {
37
+ "jsCode": "// four jobs whose seller has a calibrated record → one item each. No state is built here: the judge builds each\n// evidence pack from the job's own facts (schemas/GRADEABLE.md).\nreturn ($input.first().json.jobs || []).filter((j) => j.record && j.record.calibrated).slice(0, 4).map((j) => ({ json: { ...j, chainId: j.chainId ?? 8453 } }));"
38
+ },
39
+ "id": "pick",
40
+ "name": "Pick 4 calibrated",
41
+ "type": "n8n-nodes-base.code",
42
+ "typeVersion": 2,
43
+ "position": [
44
+ 680,
45
+ 300
46
+ ],
47
+ "notes": "in: job.json → out: job.json · keep four whose seller has a calibrated record; one item per job, no state built here",
48
+ "notesInFlow": true
49
+ },
50
+ {
51
+ "name": "Judge · prepare",
52
+ "type": "CUSTOM.taifoon",
53
+ "typeVersion": 1,
54
+ "position": [
55
+ 900,
56
+ 300
57
+ ],
58
+ "parameters": {
59
+ "resource": "judge",
60
+ "operation": "compose",
61
+ "composeSubject": "job",
62
+ "composeHandshakeId": "={{ $json.handshake_id }}",
63
+ "composeMode": "prepare",
64
+ "composeJobId": "={{ String($json.id) }}",
65
+ "composeChainId": "={{ $json.chainId }}"
66
+ },
67
+ "credentials": {
68
+ "taifoonRelayerApi": {
69
+ "id": "",
70
+ "name": "REPLACE: taifoonRelayerApi"
71
+ }
72
+ },
73
+ "id": "a1d643eb-e1a5-4d22-ae79-f9335b12feec",
74
+ "notes": "in: job.json → out: facts.json · POST /v1/judge/compose (prepare) on the job: facts decide first; the pack comes from the job evidence (GRADEABLE labels 1–21)",
75
+ "notesInFlow": true
76
+ },
77
+ {
78
+ "name": "Hard fail?",
79
+ "type": "n8n-nodes-base.if",
80
+ "typeVersion": 2.2,
81
+ "position": [
82
+ 1120,
83
+ 300
84
+ ],
85
+ "parameters": {
86
+ "conditions": {
87
+ "options": {
88
+ "caseSensitive": true,
89
+ "leftValue": "",
90
+ "typeValidation": "strict",
91
+ "version": 2
92
+ },
93
+ "conditions": [
94
+ {
95
+ "id": "h1",
96
+ "leftValue": "={{ $json.hard_fail === true }}",
97
+ "rightValue": "",
98
+ "operator": {
99
+ "type": "boolean",
100
+ "operation": "true",
101
+ "singleValue": true
102
+ }
103
+ }
104
+ ],
105
+ "combinator": "and"
106
+ },
107
+ "options": {}
108
+ },
109
+ "id": "ded562f6-b428-4b90-8bd4-c2a1f5fe4465",
110
+ "notes": "in: facts.json → out: facts.json · a hard fail is final at prepare (Jev is not asked; nothing to stamp)",
111
+ "notesInFlow": true
112
+ },
113
+ {
114
+ "name": "Jev · TypeSafe (our credential)",
115
+ "type": "@taifoon/n8n-nodes-typesafe.taifoonTypeSafe",
116
+ "typeVersion": 1,
117
+ "position": [
118
+ 1340,
119
+ 420
120
+ ],
121
+ "parameters": {
122
+ "operation": "ask",
123
+ "connection": "direct",
124
+ "state": "={{ JSON.stringify($json.jev.state) }}",
125
+ "questions": {},
126
+ "questionsJson": "={{ JSON.stringify($json.jev.questions) }}",
127
+ "routing": "{}",
128
+ "replyLanguage": "off",
129
+ "failClosed": true
130
+ },
131
+ "credentials": {
132
+ "taifoonTypeSafeApi": {
133
+ "id": "",
134
+ "name": "REPLACE: taifoonTypeSafeApi"
135
+ }
136
+ },
137
+ "notes": "in: facts.json → out: answers.json · TypeSafe ask on the prepared text (jevStateOf): every RUBRIC_ASKED answer with its whole distribution",
138
+ "id": "6b65c9a5-3195-4047-85e3-1ec76f8d2472",
139
+ "notesInFlow": true
140
+ },
141
+ {
142
+ "name": "Judge · answers",
143
+ "type": "CUSTOM.taifoon",
144
+ "typeVersion": 1,
145
+ "position": [
146
+ 1560,
147
+ 420
148
+ ],
149
+ "parameters": {
150
+ "resource": "judge",
151
+ "operation": "compose",
152
+ "composeSubject": "job",
153
+ "composeHandshakeId": "={{ $('Delivery').first().json.handshake_id }}",
154
+ "composeMode": "answers",
155
+ "composeAnswers": "={{ JSON.stringify($json.answers) }}",
156
+ "composeModel": "={{ $json.model }}",
157
+ "composePrepareDigest": "={{ $('Judge · prepare').item.json.prepare_digest }}",
158
+ "composeAnsweredBy": "n8n-typesafe",
159
+ "composeExecutionId": "={{ $execution.id }}",
160
+ "composeLatencyMs": "={{ $json.latency_ms }}",
161
+ "composeJobId": "={{ String($('Pick 4 calibrated').item.json.id) }}",
162
+ "composeChainId": "={{ $('Pick 4 calibrated').item.json.chainId }}"
163
+ },
164
+ "credentials": {
165
+ "taifoonRelayerApi": {
166
+ "id": "",
167
+ "name": "REPLACE: taifoonRelayerApi"
168
+ }
169
+ },
170
+ "id": "f296f602-bd5f-40f3-b622-2322a751a965",
171
+ "notes": "in: answers.json → out: verdict.json · POST /v1/judge/compose (answers): recompose under THRESHOLDS_v1, record decision.v2 and the answers, anchor both",
172
+ "notesInFlow": true
173
+ },
174
+ {
175
+ "parameters": {
176
+ "mode": "runOnceForEachItem",
177
+ "jsCode": "// ── canonical VERDICT (taifoon.verdict.v1) from a decision record — the same mapping as the Taifoon verdict schema\n// fromDecision, inlined because an n8n code node cannot import the schemas package. workflows/test.mjs runs this node\n// and compares its output with fromDecision field by field.\nconst SITE = 'https://www.taifoon.io';\nconst isHash = (s) => typeof s === 'string' && /^0x[0-9a-fA-F]{64}$/.test(s);\nconst chainAnchor = (chain_id, tx, what, x = {}) => ({ kind: 'chain', chain_id, tx: tx.toLowerCase(), block: x.block ?? null, ts: x.ts ?? null, log_index: x.log_index ?? null, contract: x.contract ? x.contract.toLowerCase() : null, what });\nfunction jobKeyOf(subject) {\n const ref = String(subject && subject.ref || ''), chain = Number(subject && subject.chainId) || 8453;\n if (/^hs_[0-9a-f]{24}$/.test(ref)) return 'hs:' + ref;\n if (/^([a-z][a-z0-9-]{0,31}):([1-9][0-9]{0,9}):([0-9]{1,78})$/.test(ref)) return ref;\n if (/^0x[0-9a-fA-F]{64}$/.test(ref)) return 'hook:' + chain + ':' + ref.toLowerCase();\n if (/^[0-9]{1,78}$/.test(ref)) return /^[0-9]{10,12}$/.test(ref) && BigInt(ref) >= 1000000001n ? 'memo-acp:8453:' + ref : 'erc8183:' + chain + ':' + ref;\n return null;\n}\nfunction verdictFromDecision(d, composed) {\n const rubric = /RUBRIC_v1 compose · rubric (0x[0-9a-f]{64})/.exec(String(d.note ?? ''));\n const anchors = [];\n if (d.anchor && isHash(d.anchor.tx)) anchors.push(chainAnchor(Number(d.anchor.chain), d.anchor.tx, `JevDecisionLog.record${d.anchor.index != null ? ` (decision index ${d.anchor.index})` : ''} · status ${d.anchor.status}`, { block: d.anchor.block ?? null, contract: d.anchor.contract ?? null }));\n anchors.push({ kind: 'offchain', store: 'coordd:decisions', key: String(d.id), digest: isHash(d.digest) ? d.digest.toLowerCase() : null, anchored_by: anchors[0] && anchors[0].kind === 'chain' ? anchors[0] : null, what: 'the decision record; digest = sha256(canonicalDecision)' });\n return {\n schema: 'taifoon.verdict.v1', decision_id: String(d.id), version: d.v, kind: d.kind,\n subject: { chain_id: Number(d.subject && d.subject.chainId), at: d.subject && d.subject.at ? String(d.subject.at).toLowerCase() : null, ref: String(d.subject && d.subject.ref), label: (d.subject && d.subject.label) ?? null },\n subject_id: String(d.subject_id).toLowerCase(), subject_job_key: jobKeyOf(d.subject), digest: String(d.digest).toLowerCase(),\n input_digest: isHash(d.input_digest) ? d.input_digest.toLowerCase() : null, answers_digest: isHash(d.answers_digest) ? d.answers_digest.toLowerCase() : null,\n answers: (d.answers || []).map((a) => ({ id: String(a.id), question: a.question ?? null, options: a.options ?? null, value: String(a.value), probabilities: a.probabilities ?? {}, confidence: Number(a.confidence) })),\n confidence_bps: d.confidence_bps, model: d.model ?? null, answers_source: d.answers_source ?? 'layer',\n rubric: rubric ? { version: 'RUBRIC_v1', hash: rubric[1] } : null,\n composed: composed ? { verdict: composed.verdict, auto: Boolean(composed.auto), forced: composed.forced ?? null, reasons: (composed.reasons || []).map(String), receipt_hash: isHash(composed.receiptHash) ? composed.receiptHash.toLowerCase() : null } : null,\n anchors, source: { kind: 'decision', url: d.uri ?? `${SITE}/v1/judge/decisions/${d.id}`, read_at: Math.floor(Date.now() / 1000) },\n };\n}\n// the GradeStampRegistry verdict code (1 delivered_as_specified · 2 delivered_with_defects · 3 not_delivered · 4 cannot_determine)\nconst STAMP_CODE = { delivered_as_specified: 1, delivered_with_defects: 2, not_delivered: 3, cannot_determine: 4 };\nfunction stampCodeOf(v) {\n if (v.composed) return v.composed.verdict === 'complete' ? 1 : v.composed.verdict === 'reject' ? 2 : 4;\n const ref = (v.answers || []).find((a) => STAMP_CODE[a.value]);\n return ref ? STAMP_CODE[ref.value] : 4;\n}\n// the stamp transaction(s) the devnet signer mined → appended to the verdict's anchors\nconst stampAnchors = (sent, chainId) => (sent || []).filter((s) => isHash(s.tx) && s.status === 'success').map((s) => chainAnchor(chainId, s.tx, 'GradeStampRegistry.stamp (' + (s.action || 'stamp') + ')', { block: s.block ?? null, contract: s.to ?? null }));\n\n// the recorded decision → the canonical VERDICT, composed from this run's receipt\nconst a = $json;\nif (!a.decision || !a.decision.id) throw new Error('judge answers returned no decision: ' + JSON.stringify(a).slice(0, 300));\nconst d = (await this.helpers.httpRequest({ method: 'GET', url: SITE + '/v1/judge/decisions/' + a.decision.id, json: true })).decision;\nconst v = verdictFromDecision(d, a.receipt);\nreturn { json: { ...v, stamp: { code: stampCodeOf(v), mode: 2 } } };"
178
+ },
179
+ "id": "dd2f2612-43ba-4e52-aa54-bf147e4dd561",
180
+ "name": "Verdict",
181
+ "type": "n8n-nodes-base.code",
182
+ "typeVersion": 2,
183
+ "position": [
184
+ 1780,
185
+ 420
186
+ ],
187
+ "notes": "in: verdict.json → out: verdict.json · the recorded decision as a canonical VERDICT (fromDecision), composed from this run’s receipt",
188
+ "notesInFlow": true
189
+ },
190
+ {
191
+ "parameters": {
192
+ "resource": "assurance",
193
+ "operation": "stampGrade",
194
+ "chainId": 36927,
195
+ "stampSubject": "={{ $json.subject_id }}",
196
+ "stampDigest": "={{ $json.digest }}",
197
+ "stampVerdict": "={{ String($json.stamp.code) }}",
198
+ "stampMode": "={{ String($json.stamp.mode) }}",
199
+ "stampModel": "={{ $json.model || 'jev' }}",
200
+ "stampUri": "={{ $json.source.url }}"
201
+ },
202
+ "id": "stamp",
203
+ "name": "Stamp Grade (unsigned)",
204
+ "type": "CUSTOM.taifoon",
205
+ "typeVersion": 1,
206
+ "position": [
207
+ 2000,
208
+ 420
209
+ ],
210
+ "notes": "in: verdict.json → out: verdict.json · POST /v1/assurance/call stampGrade: the unsigned GradeStampRegistry.stamp(subject_id, digest, verdict code, mode 2)",
211
+ "notesInFlow": true
212
+ },
213
+ {
214
+ "parameters": {
215
+ "callsField": "calls",
216
+ "sendRequires": false,
217
+ "wait": true
218
+ },
219
+ "id": "send",
220
+ "name": "Taifoon Devnet Signer",
221
+ "type": "CUSTOM.taifoonDevnetSigner",
222
+ "typeVersion": 1,
223
+ "position": [
224
+ 2220,
225
+ 420
226
+ ],
227
+ "credentials": {
228
+ "taifoonDevnetKeyApi": {
229
+ "id": "",
230
+ "name": "REPLACE: taifoonDevnetKeyApi"
231
+ }
232
+ },
233
+ "notes": "in: verdict.json → out: verdict.json · sign and send the stamp on devnet 36927 (anvil #0)",
234
+ "notesInFlow": true
235
+ },
236
+ {
237
+ "parameters": {
238
+ "mode": "runOnceForEachItem",
239
+ "jsCode": "// ── canonical VERDICT (taifoon.verdict.v1) from a decision record — the same mapping as the Taifoon verdict schema\n// fromDecision, inlined because an n8n code node cannot import the schemas package. workflows/test.mjs runs this node\n// and compares its output with fromDecision field by field.\nconst SITE = 'https://www.taifoon.io';\nconst isHash = (s) => typeof s === 'string' && /^0x[0-9a-fA-F]{64}$/.test(s);\nconst chainAnchor = (chain_id, tx, what, x = {}) => ({ kind: 'chain', chain_id, tx: tx.toLowerCase(), block: x.block ?? null, ts: x.ts ?? null, log_index: x.log_index ?? null, contract: x.contract ? x.contract.toLowerCase() : null, what });\nfunction jobKeyOf(subject) {\n const ref = String(subject && subject.ref || ''), chain = Number(subject && subject.chainId) || 8453;\n if (/^hs_[0-9a-f]{24}$/.test(ref)) return 'hs:' + ref;\n if (/^([a-z][a-z0-9-]{0,31}):([1-9][0-9]{0,9}):([0-9]{1,78})$/.test(ref)) return ref;\n if (/^0x[0-9a-fA-F]{64}$/.test(ref)) return 'hook:' + chain + ':' + ref.toLowerCase();\n if (/^[0-9]{1,78}$/.test(ref)) return /^[0-9]{10,12}$/.test(ref) && BigInt(ref) >= 1000000001n ? 'memo-acp:8453:' + ref : 'erc8183:' + chain + ':' + ref;\n return null;\n}\nfunction verdictFromDecision(d, composed) {\n const rubric = /RUBRIC_v1 compose · rubric (0x[0-9a-f]{64})/.exec(String(d.note ?? ''));\n const anchors = [];\n if (d.anchor && isHash(d.anchor.tx)) anchors.push(chainAnchor(Number(d.anchor.chain), d.anchor.tx, `JevDecisionLog.record${d.anchor.index != null ? ` (decision index ${d.anchor.index})` : ''} · status ${d.anchor.status}`, { block: d.anchor.block ?? null, contract: d.anchor.contract ?? null }));\n anchors.push({ kind: 'offchain', store: 'coordd:decisions', key: String(d.id), digest: isHash(d.digest) ? d.digest.toLowerCase() : null, anchored_by: anchors[0] && anchors[0].kind === 'chain' ? anchors[0] : null, what: 'the decision record; digest = sha256(canonicalDecision)' });\n return {\n schema: 'taifoon.verdict.v1', decision_id: String(d.id), version: d.v, kind: d.kind,\n subject: { chain_id: Number(d.subject && d.subject.chainId), at: d.subject && d.subject.at ? String(d.subject.at).toLowerCase() : null, ref: String(d.subject && d.subject.ref), label: (d.subject && d.subject.label) ?? null },\n subject_id: String(d.subject_id).toLowerCase(), subject_job_key: jobKeyOf(d.subject), digest: String(d.digest).toLowerCase(),\n input_digest: isHash(d.input_digest) ? d.input_digest.toLowerCase() : null, answers_digest: isHash(d.answers_digest) ? d.answers_digest.toLowerCase() : null,\n answers: (d.answers || []).map((a) => ({ id: String(a.id), question: a.question ?? null, options: a.options ?? null, value: String(a.value), probabilities: a.probabilities ?? {}, confidence: Number(a.confidence) })),\n confidence_bps: d.confidence_bps, model: d.model ?? null, answers_source: d.answers_source ?? 'layer',\n rubric: rubric ? { version: 'RUBRIC_v1', hash: rubric[1] } : null,\n composed: composed ? { verdict: composed.verdict, auto: Boolean(composed.auto), forced: composed.forced ?? null, reasons: (composed.reasons || []).map(String), receipt_hash: isHash(composed.receiptHash) ? composed.receiptHash.toLowerCase() : null } : null,\n anchors, source: { kind: 'decision', url: d.uri ?? `${SITE}/v1/judge/decisions/${d.id}`, read_at: Math.floor(Date.now() / 1000) },\n };\n}\n// the GradeStampRegistry verdict code (1 delivered_as_specified · 2 delivered_with_defects · 3 not_delivered · 4 cannot_determine)\nconst STAMP_CODE = { delivered_as_specified: 1, delivered_with_defects: 2, not_delivered: 3, cannot_determine: 4 };\nfunction stampCodeOf(v) {\n if (v.composed) return v.composed.verdict === 'complete' ? 1 : v.composed.verdict === 'reject' ? 2 : 4;\n const ref = (v.answers || []).find((a) => STAMP_CODE[a.value]);\n return ref ? STAMP_CODE[ref.value] : 4;\n}\n// the stamp transaction(s) the devnet signer mined → appended to the verdict's anchors\nconst stampAnchors = (sent, chainId) => (sent || []).filter((s) => isHash(s.tx) && s.status === 'success').map((s) => chainAnchor(chainId, s.tx, 'GradeStampRegistry.stamp (' + (s.action || 'stamp') + ')', { block: s.block ?? null, contract: s.to ?? null }));\n\n// the same VERDICT with the GradeStampRegistry stamp transaction appended to its anchors\nconst v = { ...$('Verdict').item.json };\ndelete v.stamp;\nreturn { json: { ...v, anchors: [...v.anchors, ...stampAnchors($json.sent, 36927)] } };"
240
+ },
241
+ "id": "bb65ea98-4d1f-4996-8498-b6a0a6c6c595",
242
+ "name": "Verdict + stamp",
243
+ "type": "n8n-nodes-base.code",
244
+ "typeVersion": 2,
245
+ "position": [
246
+ 2440,
247
+ 420
248
+ ],
249
+ "notes": "in: verdict.json → out: verdict.json · the same VERDICT with the stamp transaction appended to anchors",
250
+ "notesInFlow": true
251
+ }
252
+ ],
253
+ "connections": {
254
+ "When clicking ‘Execute workflow’": {
255
+ "main": [
256
+ [
257
+ {
258
+ "node": "Ready jobs (Base)",
259
+ "type": "main",
260
+ "index": 0
261
+ }
262
+ ]
263
+ ]
264
+ },
265
+ "Ready jobs (Base)": {
266
+ "main": [
267
+ [
268
+ {
269
+ "node": "Pick 4 calibrated",
270
+ "type": "main",
271
+ "index": 0
272
+ }
273
+ ]
274
+ ]
275
+ },
276
+ "Pick 4 calibrated": {
277
+ "main": [
278
+ [
279
+ {
280
+ "node": "Judge · prepare",
281
+ "type": "main",
282
+ "index": 0
283
+ }
284
+ ]
285
+ ]
286
+ },
287
+ "Judge · prepare": {
288
+ "main": [
289
+ [
290
+ {
291
+ "node": "Hard fail?",
292
+ "type": "main",
293
+ "index": 0
294
+ }
295
+ ]
296
+ ]
297
+ },
298
+ "Hard fail?": {
299
+ "main": [
300
+ [],
301
+ [
302
+ {
303
+ "node": "Jev · TypeSafe (our credential)",
304
+ "type": "main",
305
+ "index": 0
306
+ }
307
+ ]
308
+ ]
309
+ },
310
+ "Jev · TypeSafe (our credential)": {
311
+ "main": [
312
+ [
313
+ {
314
+ "node": "Judge · answers",
315
+ "type": "main",
316
+ "index": 0
317
+ }
318
+ ]
319
+ ]
320
+ },
321
+ "Judge · answers": {
322
+ "main": [
323
+ [
324
+ {
325
+ "node": "Verdict",
326
+ "type": "main",
327
+ "index": 0
328
+ }
329
+ ]
330
+ ]
331
+ },
332
+ "Verdict": {
333
+ "main": [
334
+ [
335
+ {
336
+ "node": "Stamp Grade (unsigned)",
337
+ "type": "main",
338
+ "index": 0
339
+ }
340
+ ]
341
+ ]
342
+ },
343
+ "Stamp Grade (unsigned)": {
344
+ "main": [
345
+ [
346
+ {
347
+ "node": "Taifoon Devnet Signer",
348
+ "type": "main",
349
+ "index": 0
350
+ }
351
+ ]
352
+ ]
353
+ },
354
+ "Taifoon Devnet Signer": {
355
+ "main": [
356
+ [
357
+ {
358
+ "node": "Verdict + stamp",
359
+ "type": "main",
360
+ "index": 0
361
+ }
362
+ ]
363
+ ]
364
+ }
365
+ },
366
+ "settings": {
367
+ "executionOrder": "v1"
368
+ },
369
+ "active": false,
370
+ "meta": {
371
+ "taifoon": {
372
+ "contract": "https://www.taifoon.io/schemas/coordination/v1/",
373
+ "steps": {
374
+ "When clicking ‘Execute workflow’": {
375
+ "in": null,
376
+ "out": null,
377
+ "does": "start a grading run"
378
+ },
379
+ "Ready jobs (Base)": {
380
+ "in": null,
381
+ "out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
382
+ "does": "GET /v1/judge/ready: jobs on Base that were paid and delivered, and nobody ruled"
383
+ },
384
+ "Pick 4 calibrated": {
385
+ "in": "https://www.taifoon.io/schemas/coordination/v1/job.json",
386
+ "out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
387
+ "does": "keep four whose seller has a calibrated record; one item per job, no state built here"
388
+ },
389
+ "Judge · prepare": {
390
+ "in": "https://www.taifoon.io/schemas/coordination/v1/job.json",
391
+ "out": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
392
+ "does": "POST /v1/judge/compose (prepare) on the job: facts decide first; the pack comes from the job evidence (GRADEABLE labels 1–21)"
393
+ },
394
+ "Hard fail?": {
395
+ "in": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
396
+ "out": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
397
+ "does": "a hard fail is final at prepare (Jev is not asked; nothing to stamp)"
398
+ },
399
+ "Jev · TypeSafe (our credential)": {
400
+ "in": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
401
+ "out": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
402
+ "does": "TypeSafe ask on the prepared text (jevStateOf): every RUBRIC_ASKED answer with its whole distribution"
403
+ },
404
+ "Judge · answers": {
405
+ "in": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
406
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
407
+ "does": "POST /v1/judge/compose (answers): recompose under THRESHOLDS_v1, record decision.v2 and the answers, anchor both"
408
+ },
409
+ "Verdict": {
410
+ "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
411
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
412
+ "does": "the recorded decision as a canonical VERDICT (fromDecision), composed from this run’s receipt"
413
+ },
414
+ "Stamp Grade (unsigned)": {
415
+ "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
416
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
417
+ "does": "POST /v1/assurance/call stampGrade: the unsigned GradeStampRegistry.stamp(subject_id, digest, verdict code, mode 2)"
418
+ },
419
+ "Taifoon Devnet Signer": {
420
+ "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
421
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
422
+ "does": "sign and send the stamp on devnet 36927 (anvil #0)"
423
+ },
424
+ "Verdict + stamp": {
425
+ "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
426
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
427
+ "does": "the same VERDICT with the stamp transaction appended to anchors"
428
+ }
429
+ }
430
+ }
431
+ },
432
+ "tags": []
433
+ }
@@ -0,0 +1,151 @@
1
+ {
2
+ "bundle": "jev-workflows-0.2.7",
3
+ "package": "@taifoon/jev@0.2.6",
4
+ "workflows": [
5
+ {
6
+ "file": "hire-judge-settle.json",
7
+ "source_id": "tfnhiresettle001",
8
+ "name": "Taifoon — HIRE → JUDGE → SETTLE (devnet 36927)",
9
+ "sha256": "2f746f9cd8c46b0342d99eb18b555696b9cfbfa3118b83905d7f92453e53da06",
10
+ "nodes": 18,
11
+ "packages": [
12
+ "n8n-nodes-taifoon",
13
+ "@taifoon/n8n-nodes-typesafe",
14
+ "n8n-nodes-taifoon-devnet-signer"
15
+ ],
16
+ "builtins": [
17
+ "manualTrigger v1",
18
+ "set v3.4",
19
+ "code v2",
20
+ "if v2.2"
21
+ ],
22
+ "credentials": [
23
+ "taifoonRelayerApi",
24
+ "taifoonTypeSafeApi",
25
+ "taifoonDevnetKeyApi"
26
+ ]
27
+ },
28
+ {
29
+ "file": "jev-grader.json",
30
+ "source_id": "tfnjevgrader0001",
31
+ "name": "Taifoon — Jev on-chain grader (grade → stamp → devnet)",
32
+ "sha256": "762248c554dd0607362e20361ec3efe0fda58326d89c2f1c18c525ea48e92e35",
33
+ "nodes": 11,
34
+ "packages": [
35
+ "n8n-nodes-taifoon",
36
+ "@taifoon/n8n-nodes-typesafe",
37
+ "n8n-nodes-taifoon-devnet-signer"
38
+ ],
39
+ "builtins": [
40
+ "manualTrigger v1",
41
+ "code v2",
42
+ "if v2.2"
43
+ ],
44
+ "credentials": [
45
+ "taifoonRelayerApi",
46
+ "taifoonTypeSafeApi",
47
+ "taifoonDevnetKeyApi"
48
+ ]
49
+ },
50
+ {
51
+ "file": "batch-judge.json",
52
+ "source_id": "tfnbatchjudge001",
53
+ "name": "Taifoon — batch judge scores for the coordination layer (TypeSafe/Jev)",
54
+ "sha256": "8e66a8e261e9f1f8052bd0b7658cb2ef5212d8df89b8a5347b56762701f078fd",
55
+ "nodes": 8,
56
+ "packages": [
57
+ "n8n-nodes-taifoon",
58
+ "@taifoon/n8n-nodes-typesafe"
59
+ ],
60
+ "builtins": [
61
+ "manualTrigger v1",
62
+ "code v2",
63
+ "if v2.2",
64
+ "httpRequest v4.2"
65
+ ],
66
+ "credentials": [
67
+ "taifoonRelayerApi",
68
+ "taifoonTypeSafeApi"
69
+ ]
70
+ },
71
+ {
72
+ "file": "stamp-grade.json",
73
+ "source_id": "tfnstamp00000001",
74
+ "name": "Taifoon — Stamp Grade (a recorded verdict → GradeStampRegistry on the devnet)",
75
+ "sha256": "516d9483e3d26e1891dacb8c4493d609340e3c6a3b200784c72864f9cb931ebc",
76
+ "nodes": 5,
77
+ "packages": [
78
+ "n8n-nodes-taifoon",
79
+ "n8n-nodes-taifoon-devnet-signer"
80
+ ],
81
+ "builtins": [
82
+ "manualTrigger v1",
83
+ "code v2"
84
+ ],
85
+ "credentials": [
86
+ "taifoonDevnetKeyApi"
87
+ ]
88
+ }
89
+ ],
90
+ "schemas": [
91
+ {
92
+ "file": "schemas/answers.json",
93
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
94
+ "sha256": "16dcdee34b631c22fc97a06bc79ac49045209b5a729fbcae851c7816ff5fd208"
95
+ },
96
+ {
97
+ "file": "schemas/common.json",
98
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/common.json",
99
+ "sha256": "d924619d04d607d1a07fd721c50ba61aa73523ffaac63b4459472d55c35e0da6"
100
+ },
101
+ {
102
+ "file": "schemas/delivery.json",
103
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/delivery.json",
104
+ "sha256": "21a24f9436e2e8bbe39d8285feb29777a4afab75c053d4fe1fb7d43996b36da0"
105
+ },
106
+ {
107
+ "file": "schemas/facts.json",
108
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
109
+ "sha256": "18c762ec1b35a819f302d73da293fd6c7320cb63bbbceb3a1d63143b1919e89f"
110
+ },
111
+ {
112
+ "file": "schemas/hire.json",
113
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/hire.json",
114
+ "sha256": "a9cccbbce8f1b4f50c31769f7af63fa9fc086e452d537a1e78612ba39448fa18"
115
+ },
116
+ {
117
+ "file": "schemas/job.json",
118
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/job.json",
119
+ "sha256": "c9fd5a4bcad766758b17dded08b877c047eaf51c318022bfa72aac45828ee357"
120
+ },
121
+ {
122
+ "file": "schemas/settlement.json",
123
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
124
+ "sha256": "50e7e12c6f4b076d6f2df36fa2aedac09130d274bac223bc2aa0ea8f768ba233"
125
+ },
126
+ {
127
+ "file": "schemas/verdict.json",
128
+ "$id": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
129
+ "sha256": "4d80156faa135f28f5800f749a4f7ad8f22b72c422807a5254447032208b2942"
130
+ }
131
+ ],
132
+ "packages": {
133
+ "@taifoon/n8n-nodes-typesafe": {
134
+ "tested": "2.0.1",
135
+ "where": "npm (community node): npm i @taifoon/n8n-nodes-typesafe"
136
+ },
137
+ "n8n-nodes-taifoon": {
138
+ "tested": "0.4.5",
139
+ "where": "custom extension (type prefix CUSTOM.); not on npm"
140
+ },
141
+ "n8n-nodes-taifoon-devnet-signer": {
142
+ "tested": "0.3.0",
143
+ "where": "custom extension (type prefix CUSTOM.); not on npm"
144
+ }
145
+ },
146
+ "credentials": {
147
+ "taifoonTypeSafeApi": "your TypeSafe key (console.typesafe.ai) — credential type \"TypeSafe API\" of @taifoon/n8n-nodes-typesafe",
148
+ "taifoonRelayerApi": "a Taifoon relayer key (tfr_…) for https://www.taifoon.io/v1 — credential type \"Taifoon Relayer API\" of n8n-nodes-taifoon",
149
+ "taifoonDevnetKeyApi": "a devnet 36927 key with gas from https://faucet.taifoon.dev — credential type \"Taifoon Devnet Key API\" of n8n-nodes-taifoon-devnet-signer"
150
+ }
151
+ }