@taifoon/jev 0.2.14 → 0.2.16
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 +7 -6
- package/dist/verify.d.ts +14 -0
- package/dist/verify.js +46 -5
- package/docs/ON_CHAIN.md +2 -2
- package/docs/PIPELINE.md +1 -1
- package/docs/SELLERS.md +374 -0
- package/docs/WORKFLOWS.md +4 -3
- package/package.json +1 -1
- package/workflows/0.2.15/README.md +109 -0
- package/workflows/0.2.15/batch-judge.json +341 -0
- package/workflows/0.2.15/hire-judge-settle.json +920 -0
- package/workflows/0.2.15/jev-grader.json +433 -0
- package/workflows/0.2.15/manifest.json +152 -0
- package/workflows/0.2.15/schemas/answers.json +230 -0
- package/workflows/0.2.15/schemas/common.json +397 -0
- package/workflows/0.2.15/schemas/delivery.json +217 -0
- package/workflows/0.2.15/schemas/facts.json +209 -0
- package/workflows/0.2.15/schemas/hire.json +314 -0
- package/workflows/0.2.15/schemas/job.json +88 -0
- package/workflows/0.2.15/schemas/settlement.json +243 -0
- package/workflows/0.2.15/schemas/verdict.json +296 -0
- package/workflows/0.2.15/stamp-grade.json +175 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
4
|
+
"title": "VERDICT — the decision recorded once, and the receipt it composes to",
|
|
5
|
+
"description": "decision.v1/v2 as the ledger writes it (the digest recomputes from kind, subject, model, answers and — v2 — input_digest), plus the RUBRIC_v1 composed verdict when the source states it. A REF verdict never produces a cheat; needs_review holds the job for the evaluator leg.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"schema": {
|
|
9
|
+
"const": "taifoon.verdict.v1",
|
|
10
|
+
"x-stage": "L",
|
|
11
|
+
"description": "which canonical entity this record is; fixed per schema"
|
|
12
|
+
},
|
|
13
|
+
"decision_id": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"pattern": "^decision-[0-9]+-[0-9a-f]{10}$",
|
|
16
|
+
"x-stage": "S5"
|
|
17
|
+
},
|
|
18
|
+
"version": {
|
|
19
|
+
"enum": [
|
|
20
|
+
"decision.v1",
|
|
21
|
+
"decision.v2"
|
|
22
|
+
],
|
|
23
|
+
"x-stage": "S5"
|
|
24
|
+
},
|
|
25
|
+
"kind": {
|
|
26
|
+
"enum": [
|
|
27
|
+
"pad",
|
|
28
|
+
"suggest",
|
|
29
|
+
"assemble",
|
|
30
|
+
"ref",
|
|
31
|
+
"grade",
|
|
32
|
+
"study"
|
|
33
|
+
],
|
|
34
|
+
"x-stage": "S5"
|
|
35
|
+
},
|
|
36
|
+
"subject": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"properties": {
|
|
39
|
+
"chain_id": {
|
|
40
|
+
"$ref": "common.json#/$defs/chain_id"
|
|
41
|
+
},
|
|
42
|
+
"at": {
|
|
43
|
+
"anyOf": [
|
|
44
|
+
{
|
|
45
|
+
"$ref": "common.json#/$defs/address"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"type": "null"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"ref": {
|
|
53
|
+
"type": "string"
|
|
54
|
+
},
|
|
55
|
+
"label": {
|
|
56
|
+
"type": [
|
|
57
|
+
"string",
|
|
58
|
+
"null"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"required": [
|
|
63
|
+
"chain_id",
|
|
64
|
+
"at",
|
|
65
|
+
"ref",
|
|
66
|
+
"label"
|
|
67
|
+
],
|
|
68
|
+
"additionalProperties": false,
|
|
69
|
+
"x-stage": "S5"
|
|
70
|
+
},
|
|
71
|
+
"subject_id": {
|
|
72
|
+
"$ref": "common.json#/$defs/bytes32",
|
|
73
|
+
"x-stage": "S5"
|
|
74
|
+
},
|
|
75
|
+
"subject_job_key": {
|
|
76
|
+
"anyOf": [
|
|
77
|
+
{
|
|
78
|
+
"$ref": "common.json#/$defs/job_key"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"type": "null"
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
"x-stage": "L"
|
|
85
|
+
},
|
|
86
|
+
"digest": {
|
|
87
|
+
"$ref": "common.json#/$defs/bytes32",
|
|
88
|
+
"x-stage": "S5"
|
|
89
|
+
},
|
|
90
|
+
"input_digest": {
|
|
91
|
+
"anyOf": [
|
|
92
|
+
{
|
|
93
|
+
"$ref": "common.json#/$defs/bytes32"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"type": "null"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"x-stage": "S5"
|
|
100
|
+
},
|
|
101
|
+
"answers_digest": {
|
|
102
|
+
"anyOf": [
|
|
103
|
+
{
|
|
104
|
+
"$ref": "common.json#/$defs/bytes32"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"type": "null"
|
|
108
|
+
}
|
|
109
|
+
],
|
|
110
|
+
"x-stage": "S5"
|
|
111
|
+
},
|
|
112
|
+
"answers": {
|
|
113
|
+
"type": "array",
|
|
114
|
+
"items": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"properties": {
|
|
117
|
+
"id": {
|
|
118
|
+
"type": "string"
|
|
119
|
+
},
|
|
120
|
+
"question": {
|
|
121
|
+
"type": [
|
|
122
|
+
"string",
|
|
123
|
+
"null"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"options": {
|
|
127
|
+
"anyOf": [
|
|
128
|
+
{
|
|
129
|
+
"type": "array",
|
|
130
|
+
"items": {
|
|
131
|
+
"type": "string"
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"type": "object",
|
|
136
|
+
"additionalProperties": {
|
|
137
|
+
"type": "string"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"type": "null"
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
"value": {
|
|
146
|
+
"type": "string"
|
|
147
|
+
},
|
|
148
|
+
"probabilities": {
|
|
149
|
+
"type": "object",
|
|
150
|
+
"additionalProperties": {
|
|
151
|
+
"type": "number"
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
"confidence": {
|
|
155
|
+
"type": "number"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"required": [
|
|
159
|
+
"id",
|
|
160
|
+
"question",
|
|
161
|
+
"options",
|
|
162
|
+
"value",
|
|
163
|
+
"probabilities",
|
|
164
|
+
"confidence"
|
|
165
|
+
],
|
|
166
|
+
"additionalProperties": false
|
|
167
|
+
},
|
|
168
|
+
"x-stage": "S5"
|
|
169
|
+
},
|
|
170
|
+
"confidence_bps": {
|
|
171
|
+
"type": "integer",
|
|
172
|
+
"minimum": 0,
|
|
173
|
+
"maximum": 10000,
|
|
174
|
+
"x-stage": "S5"
|
|
175
|
+
},
|
|
176
|
+
"model": {
|
|
177
|
+
"type": [
|
|
178
|
+
"string",
|
|
179
|
+
"null"
|
|
180
|
+
],
|
|
181
|
+
"x-stage": "S5"
|
|
182
|
+
},
|
|
183
|
+
"answers_source": {
|
|
184
|
+
"enum": [
|
|
185
|
+
"layer",
|
|
186
|
+
"caller-credential"
|
|
187
|
+
],
|
|
188
|
+
"x-stage": "S5"
|
|
189
|
+
},
|
|
190
|
+
"rubric": {
|
|
191
|
+
"type": [
|
|
192
|
+
"object",
|
|
193
|
+
"null"
|
|
194
|
+
],
|
|
195
|
+
"properties": {
|
|
196
|
+
"version": {
|
|
197
|
+
"type": "string"
|
|
198
|
+
},
|
|
199
|
+
"hash": {
|
|
200
|
+
"$ref": "common.json#/$defs/bytes32"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"required": [
|
|
204
|
+
"version",
|
|
205
|
+
"hash"
|
|
206
|
+
],
|
|
207
|
+
"additionalProperties": false,
|
|
208
|
+
"x-stage": "S5"
|
|
209
|
+
},
|
|
210
|
+
"composed": {
|
|
211
|
+
"type": [
|
|
212
|
+
"object",
|
|
213
|
+
"null"
|
|
214
|
+
],
|
|
215
|
+
"properties": {
|
|
216
|
+
"verdict": {
|
|
217
|
+
"enum": [
|
|
218
|
+
"complete",
|
|
219
|
+
"reject",
|
|
220
|
+
"needs_review"
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"auto": {
|
|
224
|
+
"type": "boolean"
|
|
225
|
+
},
|
|
226
|
+
"forced": {
|
|
227
|
+
"anyOf": [
|
|
228
|
+
{
|
|
229
|
+
"const": "hard_fail"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"type": "null"
|
|
233
|
+
}
|
|
234
|
+
]
|
|
235
|
+
},
|
|
236
|
+
"reasons": {
|
|
237
|
+
"type": "array",
|
|
238
|
+
"items": {
|
|
239
|
+
"type": "string"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"receipt_hash": {
|
|
243
|
+
"anyOf": [
|
|
244
|
+
{
|
|
245
|
+
"$ref": "common.json#/$defs/bytes32"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"type": "null"
|
|
249
|
+
}
|
|
250
|
+
]
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"required": [
|
|
254
|
+
"verdict",
|
|
255
|
+
"auto",
|
|
256
|
+
"forced",
|
|
257
|
+
"reasons",
|
|
258
|
+
"receipt_hash"
|
|
259
|
+
],
|
|
260
|
+
"additionalProperties": false,
|
|
261
|
+
"x-stage": "S5"
|
|
262
|
+
},
|
|
263
|
+
"anchors": {
|
|
264
|
+
"type": "array",
|
|
265
|
+
"items": {
|
|
266
|
+
"$ref": "common.json#/$defs/anchor"
|
|
267
|
+
},
|
|
268
|
+
"x-stage": "S6"
|
|
269
|
+
},
|
|
270
|
+
"source": {
|
|
271
|
+
"$ref": "common.json#/$defs/source",
|
|
272
|
+
"x-stage": "L"
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"required": [
|
|
276
|
+
"schema",
|
|
277
|
+
"decision_id",
|
|
278
|
+
"version",
|
|
279
|
+
"kind",
|
|
280
|
+
"subject",
|
|
281
|
+
"subject_id",
|
|
282
|
+
"subject_job_key",
|
|
283
|
+
"digest",
|
|
284
|
+
"input_digest",
|
|
285
|
+
"answers_digest",
|
|
286
|
+
"answers",
|
|
287
|
+
"confidence_bps",
|
|
288
|
+
"model",
|
|
289
|
+
"answers_source",
|
|
290
|
+
"rubric",
|
|
291
|
+
"composed",
|
|
292
|
+
"anchors",
|
|
293
|
+
"source"
|
|
294
|
+
],
|
|
295
|
+
"additionalProperties": false
|
|
296
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Taifoon — Stamp Grade (a recorded verdict → GradeStampRegistry on the devnet)",
|
|
3
|
+
"nodes": [
|
|
4
|
+
{
|
|
5
|
+
"parameters": {},
|
|
6
|
+
"id": "a1",
|
|
7
|
+
"name": "Start",
|
|
8
|
+
"type": "n8n-nodes-base.manualTrigger",
|
|
9
|
+
"typeVersion": 1,
|
|
10
|
+
"position": [
|
|
11
|
+
0,
|
|
12
|
+
0
|
|
13
|
+
],
|
|
14
|
+
"notes": "in: — → out: — · start (pass { decision_id } to stamp another verdict)",
|
|
15
|
+
"notesInFlow": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"parameters": {
|
|
19
|
+
"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 verdict to stamp: a recorded decision (pass { decision_id } on the trigger; the default is job 81067's REF decision)\nconst t = $input.first().json || {};\nconst id = t.decision_id || 'decision-1790239759511-7fc88c5799';\nconst d = (await this.helpers.httpRequest({ method: 'GET', url: SITE + '/v1/judge/decisions/' + id, json: true })).decision;\nconst v = verdictFromDecision(d, null);\nreturn [{ json: { ...v, stamp: { code: stampCodeOf(v), mode: 2 } } }];"
|
|
20
|
+
},
|
|
21
|
+
"id": "e00116ba-ac40-4cbe-9c4c-bddf65b3fc6d",
|
|
22
|
+
"name": "Verdict",
|
|
23
|
+
"type": "n8n-nodes-base.code",
|
|
24
|
+
"typeVersion": 2,
|
|
25
|
+
"position": [
|
|
26
|
+
420,
|
|
27
|
+
300
|
|
28
|
+
],
|
|
29
|
+
"notes": "in: — → out: verdict.json · GET /v1/judge/decisions/:id → the canonical VERDICT (fromDecision) and its stamp code",
|
|
30
|
+
"notesInFlow": true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"parameters": {
|
|
34
|
+
"resource": "assurance",
|
|
35
|
+
"operation": "stampGrade",
|
|
36
|
+
"chainId": 36927,
|
|
37
|
+
"stampSubject": "={{ $json.subject_id }}",
|
|
38
|
+
"stampDigest": "={{ $json.digest }}",
|
|
39
|
+
"stampVerdict": "={{ String($json.stamp.code) }}",
|
|
40
|
+
"stampMode": "={{ String($json.stamp.mode) }}",
|
|
41
|
+
"stampModel": "={{ $json.model || 'jev' }}",
|
|
42
|
+
"stampUri": "={{ $json.source.url }}"
|
|
43
|
+
},
|
|
44
|
+
"id": "b2",
|
|
45
|
+
"name": "Stamp Grade",
|
|
46
|
+
"type": "CUSTOM.taifoon",
|
|
47
|
+
"typeVersion": 1,
|
|
48
|
+
"position": [
|
|
49
|
+
640,
|
|
50
|
+
300
|
|
51
|
+
],
|
|
52
|
+
"notes": "in: verdict.json → out: verdict.json · POST /v1/assurance/call stampGrade: the unsigned GradeStampRegistry.stamp(subject_id, digest, verdict code, mode)",
|
|
53
|
+
"notesInFlow": true
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"parameters": {
|
|
57
|
+
"callsField": "calls",
|
|
58
|
+
"sendRequires": false,
|
|
59
|
+
"wait": true
|
|
60
|
+
},
|
|
61
|
+
"id": "2900d540-a3cd-488b-bc44-175b18575dcf",
|
|
62
|
+
"name": "Taifoon Devnet Signer",
|
|
63
|
+
"type": "CUSTOM.taifoonDevnetSigner",
|
|
64
|
+
"typeVersion": 1,
|
|
65
|
+
"position": [
|
|
66
|
+
860,
|
|
67
|
+
300
|
|
68
|
+
],
|
|
69
|
+
"credentials": {
|
|
70
|
+
"taifoonDevnetKeyApi": {
|
|
71
|
+
"id": "",
|
|
72
|
+
"name": "REPLACE: taifoonDevnetKeyApi"
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"notes": "in: verdict.json → out: verdict.json · sign and send the stamp on devnet 36927 (anvil #0)",
|
|
76
|
+
"notesInFlow": true
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"parameters": {
|
|
80
|
+
"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').first().json };\ndelete v.stamp;\nreturn [{ json: { ...v, anchors: [...v.anchors, ...stampAnchors($json.sent, 36927)] } }];"
|
|
81
|
+
},
|
|
82
|
+
"id": "49dcb63d-9ae5-46e8-b7c3-abdc60598643",
|
|
83
|
+
"name": "Verdict + stamp",
|
|
84
|
+
"type": "n8n-nodes-base.code",
|
|
85
|
+
"typeVersion": 2,
|
|
86
|
+
"position": [
|
|
87
|
+
1080,
|
|
88
|
+
300
|
|
89
|
+
],
|
|
90
|
+
"notes": "in: verdict.json → out: verdict.json · the same VERDICT with the stamp transaction appended to anchors",
|
|
91
|
+
"notesInFlow": true
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"connections": {
|
|
95
|
+
"Start": {
|
|
96
|
+
"main": [
|
|
97
|
+
[
|
|
98
|
+
{
|
|
99
|
+
"node": "Verdict",
|
|
100
|
+
"type": "main",
|
|
101
|
+
"index": 0
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"Verdict": {
|
|
107
|
+
"main": [
|
|
108
|
+
[
|
|
109
|
+
{
|
|
110
|
+
"node": "Stamp Grade",
|
|
111
|
+
"type": "main",
|
|
112
|
+
"index": 0
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
"Stamp Grade": {
|
|
118
|
+
"main": [
|
|
119
|
+
[
|
|
120
|
+
{
|
|
121
|
+
"node": "Taifoon Devnet Signer",
|
|
122
|
+
"type": "main",
|
|
123
|
+
"index": 0
|
|
124
|
+
}
|
|
125
|
+
]
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
"Taifoon Devnet Signer": {
|
|
129
|
+
"main": [
|
|
130
|
+
[
|
|
131
|
+
{
|
|
132
|
+
"node": "Verdict + stamp",
|
|
133
|
+
"type": "main",
|
|
134
|
+
"index": 0
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"settings": {},
|
|
141
|
+
"active": false,
|
|
142
|
+
"meta": {
|
|
143
|
+
"taifoon": {
|
|
144
|
+
"contract": "https://www.taifoon.io/schemas/coordination/v1/",
|
|
145
|
+
"steps": {
|
|
146
|
+
"Start": {
|
|
147
|
+
"in": null,
|
|
148
|
+
"out": null,
|
|
149
|
+
"does": "start (pass { decision_id } to stamp another verdict)"
|
|
150
|
+
},
|
|
151
|
+
"Verdict": {
|
|
152
|
+
"in": null,
|
|
153
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
154
|
+
"does": "GET /v1/judge/decisions/:id → the canonical VERDICT (fromDecision) and its stamp code"
|
|
155
|
+
},
|
|
156
|
+
"Stamp Grade": {
|
|
157
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
158
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
159
|
+
"does": "POST /v1/assurance/call stampGrade: the unsigned GradeStampRegistry.stamp(subject_id, digest, verdict code, mode)"
|
|
160
|
+
},
|
|
161
|
+
"Taifoon Devnet Signer": {
|
|
162
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
163
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
164
|
+
"does": "sign and send the stamp on devnet 36927 (anvil #0)"
|
|
165
|
+
},
|
|
166
|
+
"Verdict + stamp": {
|
|
167
|
+
"in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
168
|
+
"out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
|
|
169
|
+
"does": "the same VERDICT with the stamp transaction appended to anchors"
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"tags": []
|
|
175
|
+
}
|