@taifoon/jev 0.2.13 → 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/docs/WORKFLOWS.md +5 -4
- package/package.json +3 -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,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,152 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bundle": "jev-workflows-0.2.14",
|
|
3
|
+
"package": "@taifoon/jev@0.2.13",
|
|
4
|
+
"workflows": [
|
|
5
|
+
{
|
|
6
|
+
"file": "hire-judge-settle.json",
|
|
7
|
+
"source_id": "tfnhiresettle001",
|
|
8
|
+
"name": "Taifoon — HIRE → JUDGE → SETTLE (devnet 36927)",
|
|
9
|
+
"sha256": "3bd58902cb36b3ee4bd8d61c7ad02475f0b3db1869cd3c1f3055a4608ef5b99f",
|
|
10
|
+
"nodes": 20,
|
|
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
|
+
"httpRequest v4.2"
|
|
22
|
+
],
|
|
23
|
+
"credentials": [
|
|
24
|
+
"taifoonRelayerApi",
|
|
25
|
+
"taifoonTypeSafeApi",
|
|
26
|
+
"taifoonDevnetKeyApi"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"file": "jev-grader.json",
|
|
31
|
+
"source_id": "tfnjevgrader0001",
|
|
32
|
+
"name": "Taifoon — Jev on-chain grader (grade → stamp → devnet)",
|
|
33
|
+
"sha256": "762248c554dd0607362e20361ec3efe0fda58326d89c2f1c18c525ea48e92e35",
|
|
34
|
+
"nodes": 11,
|
|
35
|
+
"packages": [
|
|
36
|
+
"n8n-nodes-taifoon",
|
|
37
|
+
"@taifoon/n8n-nodes-typesafe",
|
|
38
|
+
"n8n-nodes-taifoon-devnet-signer"
|
|
39
|
+
],
|
|
40
|
+
"builtins": [
|
|
41
|
+
"manualTrigger v1",
|
|
42
|
+
"code v2",
|
|
43
|
+
"if v2.2"
|
|
44
|
+
],
|
|
45
|
+
"credentials": [
|
|
46
|
+
"taifoonRelayerApi",
|
|
47
|
+
"taifoonTypeSafeApi",
|
|
48
|
+
"taifoonDevnetKeyApi"
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"file": "batch-judge.json",
|
|
53
|
+
"source_id": "tfnbatchjudge001",
|
|
54
|
+
"name": "Taifoon — batch judge scores for the coordination layer (TypeSafe/Jev)",
|
|
55
|
+
"sha256": "8e66a8e261e9f1f8052bd0b7658cb2ef5212d8df89b8a5347b56762701f078fd",
|
|
56
|
+
"nodes": 8,
|
|
57
|
+
"packages": [
|
|
58
|
+
"n8n-nodes-taifoon",
|
|
59
|
+
"@taifoon/n8n-nodes-typesafe"
|
|
60
|
+
],
|
|
61
|
+
"builtins": [
|
|
62
|
+
"manualTrigger v1",
|
|
63
|
+
"code v2",
|
|
64
|
+
"if v2.2",
|
|
65
|
+
"httpRequest v4.2"
|
|
66
|
+
],
|
|
67
|
+
"credentials": [
|
|
68
|
+
"taifoonRelayerApi",
|
|
69
|
+
"taifoonTypeSafeApi"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"file": "stamp-grade.json",
|
|
74
|
+
"source_id": "tfnstamp00000001",
|
|
75
|
+
"name": "Taifoon — Stamp Grade (a recorded verdict → GradeStampRegistry on the devnet)",
|
|
76
|
+
"sha256": "516d9483e3d26e1891dacb8c4493d609340e3c6a3b200784c72864f9cb931ebc",
|
|
77
|
+
"nodes": 5,
|
|
78
|
+
"packages": [
|
|
79
|
+
"n8n-nodes-taifoon",
|
|
80
|
+
"n8n-nodes-taifoon-devnet-signer"
|
|
81
|
+
],
|
|
82
|
+
"builtins": [
|
|
83
|
+
"manualTrigger v1",
|
|
84
|
+
"code v2"
|
|
85
|
+
],
|
|
86
|
+
"credentials": [
|
|
87
|
+
"taifoonDevnetKeyApi"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"schemas": [
|
|
92
|
+
{
|
|
93
|
+
"file": "schemas/answers.json",
|
|
94
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
|
|
95
|
+
"sha256": "16dcdee34b631c22fc97a06bc79ac49045209b5a729fbcae851c7816ff5fd208"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"file": "schemas/common.json",
|
|
99
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/common.json",
|
|
100
|
+
"sha256": "c98f0b7e8610edf4c407c3e94fb8dd9bda389dbf008ca0df7f56a963cab06bcb"
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"file": "schemas/delivery.json",
|
|
104
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/delivery.json",
|
|
105
|
+
"sha256": "21a24f9436e2e8bbe39d8285feb29777a4afab75c053d4fe1fb7d43996b36da0"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"file": "schemas/facts.json",
|
|
109
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
|
|
110
|
+
"sha256": "18c762ec1b35a819f302d73da293fd6c7320cb63bbbceb3a1d63143b1919e89f"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"file": "schemas/hire.json",
|
|
114
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/hire.json",
|
|
115
|
+
"sha256": "a9cccbbce8f1b4f50c31769f7af63fa9fc086e452d537a1e78612ba39448fa18"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"file": "schemas/job.json",
|
|
119
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/job.json",
|
|
120
|
+
"sha256": "c9fd5a4bcad766758b17dded08b877c047eaf51c318022bfa72aac45828ee357"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"file": "schemas/settlement.json",
|
|
124
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
|
|
125
|
+
"sha256": "25b0049dddd93eeba19dcc80eac9a93f9097d06927577e92cb5a6b206817cda2"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"file": "schemas/verdict.json",
|
|
129
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
130
|
+
"sha256": "4d80156faa135f28f5800f749a4f7ad8f22b72c422807a5254447032208b2942"
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"packages": {
|
|
134
|
+
"@taifoon/n8n-nodes-typesafe": {
|
|
135
|
+
"tested": "2.0.1",
|
|
136
|
+
"where": "npm (community node): npm i @taifoon/n8n-nodes-typesafe"
|
|
137
|
+
},
|
|
138
|
+
"n8n-nodes-taifoon": {
|
|
139
|
+
"tested": "0.4.5",
|
|
140
|
+
"where": "custom extension (type prefix CUSTOM.); not on npm"
|
|
141
|
+
},
|
|
142
|
+
"n8n-nodes-taifoon-devnet-signer": {
|
|
143
|
+
"tested": "0.3.0",
|
|
144
|
+
"where": "custom extension (type prefix CUSTOM.); not on npm"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"credentials": {
|
|
148
|
+
"taifoonTypeSafeApi": "your TypeSafe key (console.typesafe.ai) — credential type \"TypeSafe API\" of @taifoon/n8n-nodes-typesafe",
|
|
149
|
+
"taifoonRelayerApi": "a Taifoon relayer key (tfr_…) for https://www.taifoon.io/v1 — credential type \"Taifoon Relayer API\" of n8n-nodes-taifoon",
|
|
150
|
+
"taifoonDevnetKeyApi": "a devnet 36927 key with gas from https://faucet.taifoon.dev — credential type \"Taifoon Devnet Key API\" of n8n-nodes-taifoon-devnet-signer"
|
|
151
|
+
}
|
|
152
|
+
}
|