@wowok/agent-mcp 2.6.0 → 2.6.1
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 +5 -3
- package/dist/extensions/capability-manifest.d.ts +125 -0
- package/dist/extensions/capability-manifest.js +594 -0
- package/dist/extensions/constraint-registry.d.ts +24 -0
- package/dist/extensions/constraint-registry.js +196 -0
- package/dist/extensions/index.d.ts +12 -0
- package/dist/extensions/index.js +6 -0
- package/dist/extensions/metric-registry.d.ts +26 -0
- package/dist/extensions/metric-registry.js +257 -0
- package/dist/extensions/mode-evaluator.d.ts +15 -0
- package/dist/extensions/mode-evaluator.js +170 -0
- package/dist/extensions/modes.d.ts +2 -0
- package/dist/extensions/modes.js +407 -0
- package/dist/extensions/registry.d.ts +48 -0
- package/dist/extensions/registry.js +629 -0
- package/dist/extensions/types.d.ts +218 -0
- package/dist/extensions/types.js +1 -0
- package/dist/knowledge/deployment-scanner.d.ts +3 -0
- package/dist/knowledge/deployment-scanner.js +64 -3
- package/dist/knowledge/guard-risk.d.ts +13 -0
- package/dist/knowledge/guard-risk.js +57 -0
- package/dist/knowledge/guard-templates.js +278 -0
- package/dist/knowledge/machine-templates.js +20 -1
- package/dist/knowledge/service-confirm.d.ts +14 -5
- package/dist/knowledge/service-confirm.js +116 -8
- package/dist/knowledge/tool-constraints.js +6 -2
- package/dist/project/deployment-bridge.d.ts +1 -1
- package/dist/project/deployment-bridge.js +27 -4
- package/dist/project/deployment-doc.d.ts +3 -0
- package/dist/project/deployment-doc.js +72 -10
- package/dist/project/evaluation.d.ts +2 -0
- package/dist/project/evaluation.js +569 -84
- package/dist/project/graph-builder.d.ts +4 -1
- package/dist/project/graph-builder.js +126 -61
- package/dist/project/graph.d.ts +1 -0
- package/dist/project/handlers.d.ts +219 -5
- package/dist/project/handlers.js +819 -9
- package/dist/project/stage-gate.d.ts +4 -0
- package/dist/project/stage-gate.js +64 -5
- package/dist/project/task-tracker.d.ts +26 -0
- package/dist/project/task-tracker.js +78 -0
- package/dist/safety/preview.js +16 -0
- package/dist/schema/call/allocation.d.ts +16 -16
- package/dist/schema/call/base.d.ts +21 -13
- package/dist/schema/call/base.js +27 -6
- package/dist/schema/call/bridge.d.ts +5 -5
- package/dist/schema/call/bridge.js +3 -1
- package/dist/schema/call/demand.d.ts +23 -31
- package/dist/schema/call/guard.js +1 -1
- package/dist/schema/call/machine.d.ts +402 -376
- package/dist/schema/call/order.d.ts +149 -228
- package/dist/schema/call/order.js +7 -3
- package/dist/schema/call/payment.d.ts +183 -3
- package/dist/schema/call/payment.js +21 -3
- package/dist/schema/call/personal.d.ts +241 -52
- package/dist/schema/call/progress.d.ts +53 -61
- package/dist/schema/call/progress.js +18 -4
- package/dist/schema/call/repository.d.ts +23 -31
- package/dist/schema/call/semantic.d.ts +1 -1
- package/dist/schema/call/semantic.js +30 -1
- package/dist/schema/call/service.d.ts +95 -119
- package/dist/schema/call/service.js +22 -1
- package/dist/schema/common/index.d.ts +11 -2
- package/dist/schema/common/index.js +43 -14
- package/dist/schema/local/index.d.ts +32 -35
- package/dist/schema/local/index.js +23 -5
- package/dist/schema/messenger/index.d.ts +274 -46
- package/dist/schema/operations.d.ts +680 -539
- package/dist/schema/operations.js +22 -0
- package/dist/schema/project/index.d.ts +1796 -80
- package/dist/schema/project/index.js +304 -14
- package/dist/schema/query/index.d.ts +705 -349
- package/dist/schema/query/index.js +164 -31
- package/dist/schema/schema-query/index.d.ts +15 -3
- package/dist/schema/schema-query/index.js +23 -5
- package/dist/schema/utils/node-parser.js +7 -4
- package/dist/schema-query/index.d.ts +7 -1
- package/dist/schema-query/index.js +204 -4
- package/dist/schemas/account_operation.output.json +7 -1
- package/dist/schemas/account_operation.schema.json +2 -2
- package/dist/schemas/bridge_operation.output.json +6 -0
- package/dist/schemas/bridge_operation.schema.json +1 -1
- package/dist/schemas/guard-templates.json +379 -0
- package/dist/schemas/guard2file.schema.json +1 -1
- package/dist/schemas/index.json +1 -1
- package/dist/schemas/local_info_operation.output.json +6 -0
- package/dist/schemas/local_mark_operation.output.json +7 -1
- package/dist/schemas/local_mark_operation.schema.json +1 -1
- package/dist/schemas/machineNode2file.schema.json +1 -1
- package/dist/schemas/messenger_operation.schema.json +4 -4
- package/dist/schemas/onchain_events.output.json +1 -1
- package/dist/schemas/onchain_operations.schema.json +348 -296
- package/dist/schemas/onchain_operations_allocation.schema.json +34 -25
- package/dist/schemas/onchain_operations_arbitration.schema.json +8 -8
- package/dist/schemas/onchain_operations_contact.schema.json +8 -8
- package/dist/schemas/onchain_operations_demand.schema.json +8 -8
- package/dist/schemas/onchain_operations_gen_passport.schema.json +14 -14
- package/dist/schemas/onchain_operations_gen_proof.schema.json +2 -2
- package/dist/schemas/onchain_operations_guard.schema.json +1 -1
- package/dist/schemas/onchain_operations_machine.schema.json +41 -33
- package/dist/schemas/onchain_operations_order.schema.json +71 -77
- package/dist/schemas/onchain_operations_payment.schema.json +141 -111
- package/dist/schemas/onchain_operations_permission.schema.json +2 -2
- package/dist/schemas/onchain_operations_personal.schema.json +7 -7
- package/dist/schemas/onchain_operations_progress.schema.json +8 -8
- package/dist/schemas/onchain_operations_proof.schema.json +7 -7
- package/dist/schemas/onchain_operations_repository.schema.json +8 -8
- package/dist/schemas/onchain_operations_reward.schema.json +10 -10
- package/dist/schemas/onchain_operations_service.schema.json +46 -35
- package/dist/schemas/onchain_operations_treasury.schema.json +8 -8
- package/dist/schemas/onchain_table_data.output.json +33 -25
- package/dist/schemas/onchain_table_data.schema.json +12 -12
- package/dist/schemas/project_operation.output.json +1175 -23
- package/dist/schemas/project_operation.schema.json +40 -4
- package/dist/schemas/query_toolkit.output.json +104 -76
- package/dist/schemas/query_toolkit.schema.json +9 -9
- package/dist/schemas/schema_query.output.json +7 -3
- package/dist/schemas/schema_query.schema.json +17 -3
- package/dist/tools/handlers/local.js +20 -5
- package/dist/tools/handlers/onchain.js +23 -0
- package/dist/tools/handlers/project.js +52 -3
- package/dist/tools/handlers/query.js +27 -0
- package/dist/tools/handlers/schema-query.js +19 -0
- package/dist/tools/handlers/task-status.d.ts +170 -0
- package/dist/tools/handlers/task-status.js +55 -0
- package/dist/tools/handlers/wip.js +47 -1
- package/dist/tools/index.js +211 -7
- package/dist/tools/retry.d.ts +8 -0
- package/dist/tools/retry.js +85 -0
- package/dist/tools/wip-deploy-assist.d.ts +28 -0
- package/dist/tools/wip-deploy-assist.js +278 -0
- package/package.json +2 -2
- package/dist/schemas/guard-node-examples.md +0 -199
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { IndustryModeRegistry } from "../extensions/registry.js";
|
|
1
2
|
const CRITICAL_BINDINGS = [
|
|
2
3
|
{ sourceType: "Service", targetType: "Machine", fieldName: "machine", critical: true },
|
|
3
4
|
{ sourceType: "Service", targetType: "Permission", fieldName: "permission", critical: true },
|
|
4
5
|
{ sourceType: "Service", targetType: "Guard", fieldName: "buy_guard", critical: false },
|
|
5
|
-
{ sourceType: "Service", targetType: "
|
|
6
|
-
{ sourceType: "Service", targetType: "Allocation", fieldName: "order_allocators", critical: false },
|
|
6
|
+
{ sourceType: "Service", targetType: "Allocation", fieldName: "order_allocators", critical: true },
|
|
7
7
|
{ sourceType: "Service", targetType: "Arbitration", fieldName: "arbitrations", critical: false },
|
|
8
8
|
{ sourceType: "Machine", targetType: "Permission", fieldName: "permission", critical: true },
|
|
9
9
|
];
|
|
@@ -11,21 +11,47 @@ const MERCHANT_REQUIRED_ITEMS = [
|
|
|
11
11
|
{ id: "R1", objectType: "Permission", fieldName: "", required: true, description: "Governance — who can manage the service" },
|
|
12
12
|
{ id: "R2", objectType: "Service", fieldName: "", required: true, description: "Core offering — the service itself" },
|
|
13
13
|
{ id: "R3", objectType: "Machine", fieldName: "machine", required: true, description: "Workflow — order processing pipeline" },
|
|
14
|
-
{ id: "R4", objectType: "Guard", fieldName: "
|
|
15
|
-
{ id: "R5", objectType: "Allocation", fieldName: "order_allocators", required: true, description: "Order allocators —
|
|
16
|
-
{ id: "R6", objectType: "Treasury", fieldName: "
|
|
17
|
-
{ id: "R7", objectType: "Arbitration", fieldName: "arbitrations", required: true, description: "Dispute resolution — arbitration configured" },
|
|
14
|
+
{ id: "R4", objectType: "Guard", fieldName: "", required: true, description: "Purchase validation — Guard objects configured (binding to Service.buy_guard is optional)" },
|
|
15
|
+
{ id: "R5", objectType: "Allocation", fieldName: "order_allocators", required: true, description: "Order allocators template — inline Allocators on Service (independent Allocation objects are created per-order)" },
|
|
16
|
+
{ id: "R6", objectType: "Treasury", fieldName: "", required: true, description: "Compensation fund — independent Treasury object for buyer refund protection (industry-aware: optional for freelance)" },
|
|
17
|
+
{ id: "R7", objectType: "Arbitration", fieldName: "arbitrations", required: true, description: "Dispute resolution — arbitration configured (required when compensation_fund > 0 per service.move:494)" },
|
|
18
18
|
{ id: "R8", objectType: "Contact", fieldName: "", required: true, description: "Customer communication — contact info" },
|
|
19
19
|
{ id: "R9", objectType: "Progress", fieldName: "", required: false, description: "Order tracking — progress nodes" },
|
|
20
20
|
{ id: "R10", objectType: "Reward", fieldName: "rewards", required: false, description: "Customer incentives — reward claims" },
|
|
21
21
|
{ id: "R11", objectType: "Repository", fieldName: "repositories", required: false, description: "Documentation — published data" },
|
|
22
22
|
{ id: "R12", objectType: "Demand", fieldName: "", required: false, description: "Market discovery — demand signals" },
|
|
23
23
|
];
|
|
24
|
+
function resolveRequiredItems(industry, compensationFundBalance = 0) {
|
|
25
|
+
let result = MERCHANT_REQUIRED_ITEMS;
|
|
26
|
+
if (industry && industry !== "general") {
|
|
27
|
+
let mode;
|
|
28
|
+
try {
|
|
29
|
+
mode = IndustryModeRegistry.Instance().getMode(industry);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
}
|
|
33
|
+
if (mode) {
|
|
34
|
+
const requireFund = mode.risk_constraints?.require_compensation_fund ?? true;
|
|
35
|
+
if (!requireFund) {
|
|
36
|
+
result = MERCHANT_REQUIRED_ITEMS.map(item => {
|
|
37
|
+
if (item.id === "R6" || item.id === "R7") {
|
|
38
|
+
return { ...item, required: false };
|
|
39
|
+
}
|
|
40
|
+
return item;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (compensationFundBalance > 0) {
|
|
46
|
+
result = result.map(item => item.id === "R7" ? { ...item, required: true } : item);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
24
50
|
const CONSUMER_DUE_DILIGENCE = [
|
|
25
51
|
{ id: "E1", check: "Service is published (bPublished=true)", severity: "critical", deduction: -30 },
|
|
26
52
|
{ id: "E2", check: "Service is not paused (bPaused=false)", severity: "warning", deduction: -20 },
|
|
27
53
|
{ id: "E3", check: "Machine has workflow nodes", severity: "warning", deduction: -15 },
|
|
28
|
-
{ id: "E4", check: "Service has buy_guard (purchase validation)", severity: "
|
|
54
|
+
{ id: "E4", check: "Service has buy_guard (purchase validation)", severity: "info", deduction: 0 },
|
|
29
55
|
{ id: "E5", check: "Service has compensation_fund (refund protection)", severity: "warning", deduction: -15 },
|
|
30
56
|
{ id: "E6", check: "Arbitration is configured (dispute resolution)", severity: "critical", deduction: -25 },
|
|
31
57
|
{ id: "E7", check: "Permission is present (governance)", severity: "warning", deduction: -10 },
|
|
@@ -34,7 +60,6 @@ const CONSUMER_DUE_DILIGENCE = [
|
|
|
34
60
|
{ id: "E10", check: "Reward is configured (customer incentives)", severity: "info", deduction: -3 },
|
|
35
61
|
{ id: "E11", check: "Service has description (offering clarity)", severity: "info", deduction: -5 },
|
|
36
62
|
{ id: "E12", check: "Registrar reputation is positive", severity: "warning", deduction: -10 },
|
|
37
|
-
{ id: "E13", check: "No critical findings in risk evaluation", severity: "critical", deduction: -20 },
|
|
38
63
|
];
|
|
39
64
|
function clampScore(n) {
|
|
40
65
|
return Math.max(0, Math.min(100, Math.round(n)));
|
|
@@ -44,7 +69,11 @@ function shortTypeName(objectType) {
|
|
|
44
69
|
return "unknown";
|
|
45
70
|
const base = objectType.replace(/<.*>/, "").trim();
|
|
46
71
|
const segments = base.split("::");
|
|
47
|
-
|
|
72
|
+
const shortName = segments[segments.length - 1] || "unknown";
|
|
73
|
+
if (shortName.length > 0 && shortName === shortName.toLowerCase()) {
|
|
74
|
+
return shortName.charAt(0).toUpperCase() + shortName.slice(1);
|
|
75
|
+
}
|
|
76
|
+
return shortName;
|
|
48
77
|
}
|
|
49
78
|
function parseCacheData(cached) {
|
|
50
79
|
if (!cached)
|
|
@@ -65,6 +94,104 @@ function countArrayField(data, field) {
|
|
|
65
94
|
return v.length;
|
|
66
95
|
return 0;
|
|
67
96
|
}
|
|
97
|
+
export function getNumberField(data, field) {
|
|
98
|
+
if (!data)
|
|
99
|
+
return 0;
|
|
100
|
+
const v = data[field];
|
|
101
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
102
|
+
return v;
|
|
103
|
+
if (typeof v === "string") {
|
|
104
|
+
const n = Number(v);
|
|
105
|
+
if (Number.isFinite(n))
|
|
106
|
+
return n;
|
|
107
|
+
}
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
export function getMachineNodeCount(data) {
|
|
111
|
+
if (!data)
|
|
112
|
+
return 0;
|
|
113
|
+
const nc = getNumberField(data, "node_count");
|
|
114
|
+
if (nc > 0)
|
|
115
|
+
return nc;
|
|
116
|
+
const arrLen = countArrayField(data, "nodes");
|
|
117
|
+
if (arrLen > 0)
|
|
118
|
+
return arrLen;
|
|
119
|
+
const nodesObj = data["nodes"];
|
|
120
|
+
if (nodesObj && typeof nodesObj === "object") {
|
|
121
|
+
const fieldsObj = nodesObj["fields"];
|
|
122
|
+
if (fieldsObj && typeof fieldsObj === "object") {
|
|
123
|
+
const sizeVal = getNumberField(fieldsObj, "size");
|
|
124
|
+
if (sizeVal > 0)
|
|
125
|
+
return sizeVal;
|
|
126
|
+
}
|
|
127
|
+
const directSize = getNumberField(nodesObj, "size");
|
|
128
|
+
if (directSize > 0)
|
|
129
|
+
return directSize;
|
|
130
|
+
}
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
function getBalanceValue(data, field) {
|
|
134
|
+
if (!data)
|
|
135
|
+
return 0;
|
|
136
|
+
const v = data[field];
|
|
137
|
+
if (v == null)
|
|
138
|
+
return 0;
|
|
139
|
+
if (typeof v === "number" && Number.isFinite(v))
|
|
140
|
+
return v;
|
|
141
|
+
if (typeof v === "string") {
|
|
142
|
+
const n = Number(v);
|
|
143
|
+
if (Number.isFinite(n))
|
|
144
|
+
return n;
|
|
145
|
+
}
|
|
146
|
+
if (typeof v === "object") {
|
|
147
|
+
const obj = v;
|
|
148
|
+
const inner = obj.value ?? obj.fields;
|
|
149
|
+
if (typeof inner === "number" && Number.isFinite(inner))
|
|
150
|
+
return inner;
|
|
151
|
+
if (typeof inner === "string") {
|
|
152
|
+
const n = Number(inner);
|
|
153
|
+
if (Number.isFinite(n))
|
|
154
|
+
return n;
|
|
155
|
+
}
|
|
156
|
+
if (typeof inner === "object" && inner !== null) {
|
|
157
|
+
const innerObj = inner;
|
|
158
|
+
const val = innerObj.value;
|
|
159
|
+
if (typeof val === "number" && Number.isFinite(val))
|
|
160
|
+
return val;
|
|
161
|
+
if (typeof val === "string") {
|
|
162
|
+
const n = Number(val);
|
|
163
|
+
if (Number.isFinite(n))
|
|
164
|
+
return n;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return 0;
|
|
169
|
+
}
|
|
170
|
+
function getMaxSalePrice(data) {
|
|
171
|
+
if (!data)
|
|
172
|
+
return 0;
|
|
173
|
+
const sales = data.sales;
|
|
174
|
+
if (!Array.isArray(sales) || sales.length === 0)
|
|
175
|
+
return 0;
|
|
176
|
+
let max = 0;
|
|
177
|
+
for (const sale of sales) {
|
|
178
|
+
if (typeof sale !== "object" || sale === null)
|
|
179
|
+
continue;
|
|
180
|
+
const price = sale.price;
|
|
181
|
+
let n = 0;
|
|
182
|
+
if (typeof price === "number" && Number.isFinite(price)) {
|
|
183
|
+
n = price;
|
|
184
|
+
}
|
|
185
|
+
else if (typeof price === "string") {
|
|
186
|
+
const parsed = Number(price);
|
|
187
|
+
if (Number.isFinite(parsed))
|
|
188
|
+
n = parsed;
|
|
189
|
+
}
|
|
190
|
+
if (n > max)
|
|
191
|
+
max = n;
|
|
192
|
+
}
|
|
193
|
+
return max;
|
|
194
|
+
}
|
|
68
195
|
function detectCircularRefs(edges) {
|
|
69
196
|
const adj = new Map();
|
|
70
197
|
for (const e of edges) {
|
|
@@ -78,14 +205,36 @@ function detectCircularRefs(edges) {
|
|
|
78
205
|
const visited = new Set();
|
|
79
206
|
const recursionStack = new Set();
|
|
80
207
|
const path = [];
|
|
81
|
-
const
|
|
82
|
-
visited.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
208
|
+
for (const startNode of adj.keys()) {
|
|
209
|
+
if (visited.has(startNode))
|
|
210
|
+
continue;
|
|
211
|
+
const stack = [{
|
|
212
|
+
node: startNode,
|
|
213
|
+
nextNeighborIdx: 0,
|
|
214
|
+
neighbors: adj.get(startNode) ?? [],
|
|
215
|
+
}];
|
|
216
|
+
visited.add(startNode);
|
|
217
|
+
recursionStack.add(startNode);
|
|
218
|
+
path.push(startNode);
|
|
219
|
+
while (stack.length > 0) {
|
|
220
|
+
const frame = stack[stack.length - 1];
|
|
221
|
+
if (frame.nextNeighborIdx >= frame.neighbors.length) {
|
|
222
|
+
stack.pop();
|
|
223
|
+
path.pop();
|
|
224
|
+
recursionStack.delete(frame.node);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const neighbor = frame.neighbors[frame.nextNeighborIdx];
|
|
228
|
+
frame.nextNeighborIdx++;
|
|
87
229
|
if (!visited.has(neighbor)) {
|
|
88
|
-
|
|
230
|
+
visited.add(neighbor);
|
|
231
|
+
recursionStack.add(neighbor);
|
|
232
|
+
path.push(neighbor);
|
|
233
|
+
stack.push({
|
|
234
|
+
node: neighbor,
|
|
235
|
+
nextNeighborIdx: 0,
|
|
236
|
+
neighbors: adj.get(neighbor) ?? [],
|
|
237
|
+
});
|
|
89
238
|
}
|
|
90
239
|
else if (recursionStack.has(neighbor)) {
|
|
91
240
|
const cycleStart = path.indexOf(neighbor);
|
|
@@ -93,12 +242,6 @@ function detectCircularRefs(edges) {
|
|
|
93
242
|
cycles.push(cycle.join("→"));
|
|
94
243
|
}
|
|
95
244
|
}
|
|
96
|
-
path.pop();
|
|
97
|
-
recursionStack.delete(node);
|
|
98
|
-
};
|
|
99
|
-
for (const node of adj.keys()) {
|
|
100
|
-
if (!visited.has(node))
|
|
101
|
-
dfs(node);
|
|
102
245
|
}
|
|
103
246
|
return cycles;
|
|
104
247
|
}
|
|
@@ -145,8 +288,164 @@ function buildBindingIndex(edges) {
|
|
|
145
288
|
idx.set(key, new Set());
|
|
146
289
|
idx.get(key).add(e.targetObject);
|
|
147
290
|
}
|
|
291
|
+
for (const e of edges) {
|
|
292
|
+
if (e.edgeSource !== "potential")
|
|
293
|
+
continue;
|
|
294
|
+
if (!e.fieldName)
|
|
295
|
+
continue;
|
|
296
|
+
const key = `${e.sourceObject}#${e.fieldName}`;
|
|
297
|
+
if (!idx.has(key) || idx.get(key).size === 0) {
|
|
298
|
+
if (!idx.has(key))
|
|
299
|
+
idx.set(key, new Set());
|
|
300
|
+
idx.get(key).add(e.targetObject);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
148
303
|
return idx;
|
|
149
304
|
}
|
|
305
|
+
function evaluateIndustryPattern(project, objects, objectCache, edges) {
|
|
306
|
+
const result = { strengths: [], findings: [], matched: false };
|
|
307
|
+
const industryName = project.industry;
|
|
308
|
+
if (!industryName || industryName === "general") {
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
let mode;
|
|
312
|
+
try {
|
|
313
|
+
mode = IndustryModeRegistry.Instance().getMode(industryName);
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
if (!mode)
|
|
319
|
+
return result;
|
|
320
|
+
const byType = new Map();
|
|
321
|
+
for (const obj of objects) {
|
|
322
|
+
const t = shortTypeName(obj.objectType);
|
|
323
|
+
if (!byType.has(t))
|
|
324
|
+
byType.set(t, []);
|
|
325
|
+
byType.get(t).push(obj);
|
|
326
|
+
}
|
|
327
|
+
const bindingIndex = buildBindingIndex(edges);
|
|
328
|
+
let matchCount = 0;
|
|
329
|
+
let totalChecks = 0;
|
|
330
|
+
const expectedNodes = mode.machine_shape;
|
|
331
|
+
if (expectedNodes.length > 0) {
|
|
332
|
+
totalChecks++;
|
|
333
|
+
const machines = byType.get("Machine") ?? [];
|
|
334
|
+
if (machines.length > 0) {
|
|
335
|
+
const machineObj = machines[0];
|
|
336
|
+
const cached = parseCacheData(objectCache.get(machineObj.objectAddress));
|
|
337
|
+
const nodeCount = getNumberField(cached, "node_count") || countArrayField(cached ?? {}, "nodes");
|
|
338
|
+
if (nodeCount > 0 && nodeCount >= expectedNodes.length) {
|
|
339
|
+
matchCount++;
|
|
340
|
+
result.strengths.push(`Industry pattern match: Machine has ${nodeCount} nodes, covering "${industryName}" mode's ${expectedNodes.length} expected node(s)`);
|
|
341
|
+
}
|
|
342
|
+
else if (nodeCount > 0) {
|
|
343
|
+
result.findings.push({
|
|
344
|
+
criterion: "industry_pattern",
|
|
345
|
+
severity: "info",
|
|
346
|
+
message: `Industry pattern: Machine has ${nodeCount} nodes, but "${industryName}" mode expects at least ${expectedNodes.length}`,
|
|
347
|
+
deduction: 0,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const expectedGuards = mode.guards;
|
|
353
|
+
if (expectedGuards.length > 0) {
|
|
354
|
+
totalChecks++;
|
|
355
|
+
const guards = byType.get("Guard") ?? [];
|
|
356
|
+
const minGuards = expectedGuards.length;
|
|
357
|
+
const maxGuards = expectedGuards.length * 2;
|
|
358
|
+
if (guards.length >= minGuards && guards.length <= maxGuards) {
|
|
359
|
+
matchCount++;
|
|
360
|
+
result.strengths.push(`Industry pattern match: ${guards.length} Guard(s) present, within "${industryName}" mode's expected range [${minGuards}-${maxGuards}]`);
|
|
361
|
+
}
|
|
362
|
+
else if (guards.length > 0 && guards.length < minGuards) {
|
|
363
|
+
result.findings.push({
|
|
364
|
+
criterion: "industry_pattern",
|
|
365
|
+
severity: "info",
|
|
366
|
+
message: `Industry pattern: ${guards.length} Guard(s) present, but "${industryName}" mode expects at least ${minGuards} (recommended range [${minGuards}-${maxGuards}])`,
|
|
367
|
+
deduction: 0,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
else if (guards.length > maxGuards) {
|
|
371
|
+
matchCount++;
|
|
372
|
+
result.strengths.push(`Industry pattern match: ${guards.length} Guard(s) present, covering "${industryName}" mode's ${minGuards} expected guard(s)`);
|
|
373
|
+
result.findings.push({
|
|
374
|
+
criterion: "industry_pattern",
|
|
375
|
+
severity: "info",
|
|
376
|
+
message: `Industry pattern: ${guards.length} Guard(s) present, exceeds recommended max ${maxGuards}. Consider consolidating guards if some are redundant.`,
|
|
377
|
+
deduction: 0,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const expectedAllocStrategy = mode.allocator;
|
|
382
|
+
if (expectedAllocStrategy) {
|
|
383
|
+
totalChecks++;
|
|
384
|
+
const allocations = byType.get("Allocation") ?? [];
|
|
385
|
+
const services4Alloc = byType.get("Service") ?? [];
|
|
386
|
+
const serviceForAlloc = services4Alloc[0];
|
|
387
|
+
const hasOrderAllocators = serviceForAlloc
|
|
388
|
+
? !!bindingIndex.get(`${serviceForAlloc.objectAddress}#order_allocators`)?.size
|
|
389
|
+
: false;
|
|
390
|
+
const expectsMultiple = mode.traits.multi_tier_allocation;
|
|
391
|
+
if (hasOrderAllocators) {
|
|
392
|
+
matchCount++;
|
|
393
|
+
if (allocations.length > 0) {
|
|
394
|
+
result.strengths.push(`Industry pattern match: Service.order_allocators bound + ${allocations.length} Allocation object(s) present (post-order) — matching "${industryName}" allocation pattern`);
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
result.strengths.push(`Industry pattern match: Service.order_allocators template bound — matching "${industryName}" allocation pattern (Allocation objects created per-order)`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else if (allocations.length > 0) {
|
|
401
|
+
matchCount++;
|
|
402
|
+
result.strengths.push(`Industry pattern match: ${allocations.length} Allocation object(s) present — matching "${industryName}" allocation pattern`);
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
result.findings.push({
|
|
406
|
+
criterion: "industry_pattern",
|
|
407
|
+
severity: "info",
|
|
408
|
+
message: `Industry pattern: Service.order_allocators not bound and no Allocation objects — "${industryName}" mode expects ${expectsMultiple ? "multi-tier" : "single-tier"} allocation (Allocation objects are created per-order, so this is only a template-level gap)`,
|
|
409
|
+
deduction: 0,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const requireCompensationFund = mode.risk_constraints?.require_compensation_fund ?? false;
|
|
414
|
+
if (requireCompensationFund || mode.traits.deposit_required || mode.traits.trust_sensitivity !== "low") {
|
|
415
|
+
totalChecks++;
|
|
416
|
+
const treasuries = byType.get("Treasury") ?? [];
|
|
417
|
+
const services4Treas = byType.get("Service") ?? [];
|
|
418
|
+
const serviceForTreas = services4Treas[0];
|
|
419
|
+
const serviceDataForTreas = serviceForTreas
|
|
420
|
+
? parseCacheData(objectCache.get(serviceForTreas.objectAddress))
|
|
421
|
+
: null;
|
|
422
|
+
const compensationBalance = serviceForTreas
|
|
423
|
+
? getBalanceValue(serviceDataForTreas, "compensation_fund")
|
|
424
|
+
: 0;
|
|
425
|
+
if (treasuries.length > 0 || compensationBalance > 0) {
|
|
426
|
+
matchCount++;
|
|
427
|
+
const parts = [];
|
|
428
|
+
if (treasuries.length > 0)
|
|
429
|
+
parts.push(`${treasuries.length} Treasury object(s)`);
|
|
430
|
+
if (compensationBalance > 0)
|
|
431
|
+
parts.push(`compensation_fund balance ${compensationBalance}`);
|
|
432
|
+
result.strengths.push(`Industry pattern match: ${parts.join(" + ")} — matching "${industryName}" trust/deposit pattern`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
result.matched = totalChecks > 0 && matchCount / totalChecks >= 0.6;
|
|
436
|
+
if (result.matched) {
|
|
437
|
+
result.strengths.push(`Industry pattern: project structure matches "${industryName}" mode (${matchCount}/${totalChecks} checks passed)`);
|
|
438
|
+
}
|
|
439
|
+
else if (totalChecks > 0) {
|
|
440
|
+
result.findings.push({
|
|
441
|
+
criterion: "industry_pattern",
|
|
442
|
+
severity: "info",
|
|
443
|
+
message: `Industry pattern: project partially matches "${industryName}" mode (${matchCount}/${totalChecks} checks passed)`,
|
|
444
|
+
deduction: 0,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
return result;
|
|
448
|
+
}
|
|
150
449
|
function evaluateRisk(input) {
|
|
151
450
|
const findings = [];
|
|
152
451
|
const strengths = [];
|
|
@@ -162,19 +461,24 @@ function evaluateRisk(input) {
|
|
|
162
461
|
const bindingIndex = buildBindingIndex(edges);
|
|
163
462
|
let structuralScore = 100;
|
|
164
463
|
const serviceObj = services[0];
|
|
464
|
+
const svcDataForStruct = serviceObj ? parseCacheData(objectCache.get(serviceObj.objectAddress)) : null;
|
|
465
|
+
const compensationBalanceForStruct = serviceObj ? getBalanceValue(svcDataForStruct, "compensation_fund") : 0;
|
|
165
466
|
if (serviceObj) {
|
|
166
467
|
for (const expected of CRITICAL_BINDINGS) {
|
|
167
468
|
if (expected.sourceType !== "Service")
|
|
168
469
|
continue;
|
|
470
|
+
const effectiveCritical = expected.fieldName === "arbitrations"
|
|
471
|
+
? (compensationBalanceForStruct > 0 ? true : expected.critical)
|
|
472
|
+
: expected.critical;
|
|
169
473
|
const key = `${serviceObj.objectAddress}#${expected.fieldName}`;
|
|
170
474
|
const bindings = bindingIndex.get(key);
|
|
171
475
|
if (!bindings || bindings.size === 0) {
|
|
172
|
-
const deduction =
|
|
476
|
+
const deduction = effectiveCritical ? -25 : -10;
|
|
173
477
|
structuralScore += deduction;
|
|
174
478
|
findings.push({
|
|
175
479
|
criterion: "structural_risk",
|
|
176
|
-
severity:
|
|
177
|
-
message: `Service missing ${expected.fieldName} binding (expected ${expected.targetType})`,
|
|
480
|
+
severity: effectiveCritical ? "critical" : "warning",
|
|
481
|
+
message: `Service missing ${expected.fieldName} binding (expected ${expected.targetType})${expected.fieldName === "arbitrations" ? (compensationBalanceForStruct > 0 ? " — required by contract when compensation_fund > 0 (service.move:494)" : " — optional when compensation_fund is 0") : ""}`,
|
|
178
482
|
deduction,
|
|
179
483
|
});
|
|
180
484
|
}
|
|
@@ -195,27 +499,72 @@ function evaluateRisk(input) {
|
|
|
195
499
|
structuralScore = clampScore(structuralScore);
|
|
196
500
|
let fundScore = 100;
|
|
197
501
|
const treasuries = byType.get("Treasury") ?? [];
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
502
|
+
const serviceData = serviceObj ? parseCacheData(objectCache.get(serviceObj.objectAddress)) : null;
|
|
503
|
+
const compensationBalance = serviceObj ? getBalanceValue(serviceData, "compensation_fund") : 0;
|
|
504
|
+
let industryRequiresFund = true;
|
|
505
|
+
if (project.industry && project.industry !== "general") {
|
|
506
|
+
try {
|
|
507
|
+
const mode = IndustryModeRegistry.Instance().getMode(project.industry);
|
|
508
|
+
if (mode) {
|
|
509
|
+
industryRequiresFund = mode.risk_constraints?.require_compensation_fund ?? true;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
catch { }
|
|
209
513
|
}
|
|
210
|
-
if (
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
fundScore -= 20;
|
|
514
|
+
if (treasuries.length === 0 && compensationBalance === 0) {
|
|
515
|
+
if (industryRequiresFund) {
|
|
516
|
+
fundScore -= 30;
|
|
214
517
|
findings.push({
|
|
215
518
|
criterion: "fund_safety",
|
|
216
519
|
severity: "warning",
|
|
217
|
-
message: "Service
|
|
218
|
-
deduction: -
|
|
520
|
+
message: "No Treasury object and Service.compensation_fund is empty — no refund fund for buyers",
|
|
521
|
+
deduction: -30,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
fundScore -= 10;
|
|
526
|
+
findings.push({
|
|
527
|
+
criterion: "fund_safety",
|
|
528
|
+
severity: "info",
|
|
529
|
+
message: `No Treasury/compensation_fund — industry "${project.industry}" does not require refund fund (optional)`,
|
|
530
|
+
deduction: -10,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
if (treasuries.length > 0) {
|
|
536
|
+
strengths.push(`${treasuries.length} Treasury object(s) present — independent fund holder`);
|
|
537
|
+
}
|
|
538
|
+
if (compensationBalance > 0) {
|
|
539
|
+
strengths.push(`Service.compensation_fund has ${compensationBalance} units — inline refund fund active`);
|
|
540
|
+
const maxSalePrice = getMaxSalePrice(serviceData);
|
|
541
|
+
if (maxSalePrice > 0) {
|
|
542
|
+
const requiredThreshold = 5 * maxSalePrice;
|
|
543
|
+
if (compensationBalance < requiredThreshold) {
|
|
544
|
+
fundScore -= 10;
|
|
545
|
+
findings.push({
|
|
546
|
+
criterion: "fund_safety",
|
|
547
|
+
severity: "warning",
|
|
548
|
+
message: `Service.compensation_fund balance (${compensationBalance}) is less than 5× the max order amount (${maxSalePrice} → threshold ${requiredThreshold}). Refund coverage may be insufficient — top up via service.compensation_fund_add to avoid arbitration rulings exceeding the fund.`,
|
|
549
|
+
deduction: -10,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
findings.push({
|
|
554
|
+
criterion: "fund_safety",
|
|
555
|
+
severity: "info",
|
|
556
|
+
message: `Service.compensation_fund balance (${compensationBalance}) covers 5× the max order amount (${maxSalePrice}). Fund is sufficient for refund protection.`,
|
|
557
|
+
deduction: 0,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
findings.push({
|
|
564
|
+
criterion: "fund_safety",
|
|
565
|
+
severity: "info",
|
|
566
|
+
message: "Treasury present but Service.compensation_fund balance is 0 — consider depositing refund fund",
|
|
567
|
+
deduction: -5,
|
|
219
568
|
});
|
|
220
569
|
}
|
|
221
570
|
}
|
|
@@ -274,18 +623,27 @@ function evaluateRisk(input) {
|
|
|
274
623
|
if (serviceObj) {
|
|
275
624
|
const data = parseCacheData(objectCache.get(serviceObj.objectAddress));
|
|
276
625
|
if (data && !isAddress(data.um)) {
|
|
277
|
-
governanceScore -=
|
|
626
|
+
governanceScore -= 5;
|
|
278
627
|
findings.push({
|
|
279
628
|
criterion: "governance_risk",
|
|
280
|
-
severity: "
|
|
281
|
-
message: "Service has no UM (manager) —
|
|
282
|
-
deduction: -
|
|
629
|
+
severity: "info",
|
|
630
|
+
message: "Service has no UM (manager) — UM is optional; set via um_set if management delegation needed",
|
|
631
|
+
deduction: -5,
|
|
283
632
|
});
|
|
284
633
|
}
|
|
285
634
|
}
|
|
286
635
|
governanceScore = clampScore(governanceScore);
|
|
287
636
|
let coherenceScore = 100;
|
|
288
|
-
const objectAddresses = new Set(
|
|
637
|
+
const objectAddresses = new Set();
|
|
638
|
+
for (const o of objects) {
|
|
639
|
+
if (o.objectAddress)
|
|
640
|
+
objectAddresses.add(o.objectAddress);
|
|
641
|
+
if (o.objectName) {
|
|
642
|
+
objectAddresses.add(o.objectName);
|
|
643
|
+
const shortType = shortTypeName(o.objectType).toLowerCase();
|
|
644
|
+
objectAddresses.add(`${shortType}:${o.objectName}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
289
647
|
let danglingCount = 0;
|
|
290
648
|
for (const e of edges) {
|
|
291
649
|
if (e.edgeSource === "potential")
|
|
@@ -423,15 +781,10 @@ function evaluateTrust(input) {
|
|
|
423
781
|
const bgKey = `${services[0].objectAddress}#buy_guard`;
|
|
424
782
|
if (bindingIndex.get(bgKey)?.size) {
|
|
425
783
|
guardScore = clampScore(guardScore + 15);
|
|
426
|
-
strengths.push("Service has buy_guard — purchase validation active");
|
|
784
|
+
strengths.push("Service has buy_guard bound — purchase validation active");
|
|
427
785
|
}
|
|
428
|
-
else {
|
|
429
|
-
|
|
430
|
-
criterion: "guard_quality",
|
|
431
|
-
severity: "warning",
|
|
432
|
-
message: "Service buy_guard not bound — unvalidated purchases",
|
|
433
|
-
deduction: -15,
|
|
434
|
-
});
|
|
786
|
+
else if (guards.length > 0) {
|
|
787
|
+
strengths.push("Guard objects present (buy_guard binding optional)");
|
|
435
788
|
}
|
|
436
789
|
}
|
|
437
790
|
guardScore = clampScore(guardScore);
|
|
@@ -491,14 +844,16 @@ function evaluateTrust(input) {
|
|
|
491
844
|
case "E3":
|
|
492
845
|
passed = (byType.get("Machine") ?? []).some(m => {
|
|
493
846
|
const d = parseCacheData(objectCache.get(m.objectAddress));
|
|
494
|
-
return d && (countArrayField(d, "nodes") > 0);
|
|
847
|
+
return d && (getNumberField(d, "node_count") > 0 || countArrayField(d, "nodes") > 0);
|
|
495
848
|
});
|
|
496
849
|
break;
|
|
497
850
|
case "E4":
|
|
498
|
-
passed = !!bindingIndex2.get(`${serviceObj2.objectAddress}#buy_guard`)?.size
|
|
851
|
+
passed = !!bindingIndex2.get(`${serviceObj2.objectAddress}#buy_guard`)?.size
|
|
852
|
+
|| (byType.get("Guard") ?? []).length > 0;
|
|
499
853
|
break;
|
|
500
854
|
case "E5":
|
|
501
|
-
passed =
|
|
855
|
+
passed = getBalanceValue(svcData, "compensation_fund") > 0
|
|
856
|
+
|| (byType.get("Treasury") ?? []).length > 0;
|
|
502
857
|
break;
|
|
503
858
|
case "E6":
|
|
504
859
|
passed = (byType.get("Arbitration") ?? []).length > 0;
|
|
@@ -524,20 +879,32 @@ function evaluateTrust(input) {
|
|
|
524
879
|
passed = !!rep && rep.likes >= rep.dislikes;
|
|
525
880
|
}
|
|
526
881
|
break;
|
|
527
|
-
case "E13":
|
|
528
|
-
passed = true;
|
|
529
|
-
break;
|
|
530
882
|
}
|
|
531
883
|
if (passed) {
|
|
532
884
|
diligencePassed.push(check.id);
|
|
533
885
|
}
|
|
534
886
|
else {
|
|
535
|
-
|
|
887
|
+
let effectiveDeduction = check.deduction;
|
|
888
|
+
let effectiveSeverity = check.severity;
|
|
889
|
+
if (check.id === "E5" && project.industry && project.industry !== "general") {
|
|
890
|
+
try {
|
|
891
|
+
const mode = IndustryModeRegistry.Instance().getMode(project.industry);
|
|
892
|
+
if (mode && mode.risk_constraints?.require_compensation_fund === false) {
|
|
893
|
+
effectiveDeduction = -3;
|
|
894
|
+
effectiveSeverity = "info";
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
catch { }
|
|
898
|
+
}
|
|
899
|
+
diligenceScore += effectiveDeduction;
|
|
900
|
+
const detailSuffix = check.id === "E5" && effectiveDeduction !== check.deduction
|
|
901
|
+
? " (industry does not require compensation_fund — reduced deduction)"
|
|
902
|
+
: "";
|
|
536
903
|
findings.push({
|
|
537
904
|
criterion: "consumer_diligence",
|
|
538
|
-
severity:
|
|
539
|
-
message: `${check.id}: ${check.check} —
|
|
540
|
-
deduction:
|
|
905
|
+
severity: effectiveSeverity,
|
|
906
|
+
message: `${check.id}: ${check.check} — FAIL: condition not met${detailSuffix} [severity: ${effectiveSeverity}]`,
|
|
907
|
+
deduction: effectiveDeduction,
|
|
541
908
|
});
|
|
542
909
|
}
|
|
543
910
|
}
|
|
@@ -592,7 +959,12 @@ function evaluateCompleteness(input) {
|
|
|
592
959
|
}
|
|
593
960
|
const services = byType.get("Service") ?? [];
|
|
594
961
|
const bindingIndex = buildBindingIndex(edges);
|
|
595
|
-
const
|
|
962
|
+
const svcObjForRequiredItems = services[0];
|
|
963
|
+
const svcDataForRequiredItems = svcObjForRequiredItems
|
|
964
|
+
? parseCacheData(objectCache.get(svcObjForRequiredItems.objectAddress))
|
|
965
|
+
: null;
|
|
966
|
+
const compensationBalanceForItems = getBalanceValue(svcDataForRequiredItems, "compensation_fund");
|
|
967
|
+
const requiredItems = resolveRequiredItems(project.industry ?? undefined, compensationBalanceForItems);
|
|
596
968
|
const requiredCount = requiredItems.filter(r => r.required).length;
|
|
597
969
|
const optionalItems = requiredItems.filter(r => !r.required);
|
|
598
970
|
let requiredMet = 0;
|
|
@@ -650,23 +1022,28 @@ function evaluateCompleteness(input) {
|
|
|
650
1022
|
let bindingScore = 100;
|
|
651
1023
|
if (services[0]) {
|
|
652
1024
|
const serviceAddr = services[0].objectAddress;
|
|
1025
|
+
const svcDataForBind = parseCacheData(objectCache.get(serviceAddr));
|
|
1026
|
+
const compensationBalanceForBind = getBalanceValue(svcDataForBind, "compensation_fund");
|
|
653
1027
|
let boundCount = 0;
|
|
654
1028
|
let totalCount = 0;
|
|
655
1029
|
for (const expected of CRITICAL_BINDINGS) {
|
|
656
1030
|
if (expected.sourceType !== "Service")
|
|
657
1031
|
continue;
|
|
658
1032
|
totalCount++;
|
|
1033
|
+
const effectiveCritical = expected.fieldName === "arbitrations"
|
|
1034
|
+
? (compensationBalanceForBind > 0 ? true : expected.critical)
|
|
1035
|
+
: expected.critical;
|
|
659
1036
|
const key = `${serviceAddr}#${expected.fieldName}`;
|
|
660
1037
|
if (bindingIndex.get(key)?.size) {
|
|
661
1038
|
boundCount++;
|
|
662
1039
|
}
|
|
663
1040
|
else {
|
|
664
|
-
const deduction =
|
|
1041
|
+
const deduction = effectiveCritical ? -20 : -8;
|
|
665
1042
|
bindingScore += deduction;
|
|
666
1043
|
findings.push({
|
|
667
1044
|
criterion: "binding_completeness",
|
|
668
|
-
severity:
|
|
669
|
-
message: `Service.${expected.fieldName} not bound (expected ${expected.targetType})`,
|
|
1045
|
+
severity: effectiveCritical ? "critical" : "warning",
|
|
1046
|
+
message: `Service.${expected.fieldName} not bound (expected ${expected.targetType})${expected.fieldName === "arbitrations" ? (compensationBalanceForBind > 0 ? " — required by contract when compensation_fund > 0" : " — optional when compensation_fund is 0") : ""}`,
|
|
670
1047
|
deduction,
|
|
671
1048
|
});
|
|
672
1049
|
}
|
|
@@ -688,19 +1065,19 @@ function evaluateCompleteness(input) {
|
|
|
688
1065
|
let workflowScore = 80;
|
|
689
1066
|
const machines = byType.get("Machine") ?? [];
|
|
690
1067
|
if (machines.length === 0) {
|
|
691
|
-
workflowScore -=
|
|
1068
|
+
workflowScore -= 20;
|
|
692
1069
|
findings.push({
|
|
693
1070
|
criterion: "workflow_rationality",
|
|
694
|
-
severity: "
|
|
695
|
-
message: "No Machine object —
|
|
696
|
-
deduction: -
|
|
1071
|
+
severity: "info",
|
|
1072
|
+
message: "No Machine object — bind one before publishing (deploy-time requirement)",
|
|
1073
|
+
deduction: -20,
|
|
697
1074
|
});
|
|
698
1075
|
}
|
|
699
1076
|
else {
|
|
700
1077
|
for (const m of machines) {
|
|
701
1078
|
const data = parseCacheData(objectCache.get(m.objectAddress));
|
|
702
1079
|
if (data) {
|
|
703
|
-
const nodeCount =
|
|
1080
|
+
const nodeCount = getNumberField(data, "node_count") || countArrayField(data, "nodes");
|
|
704
1081
|
if (nodeCount > 0) {
|
|
705
1082
|
strengths.push(`Machine has ${nodeCount} node(s)`);
|
|
706
1083
|
}
|
|
@@ -716,7 +1093,16 @@ function evaluateCompleteness(input) {
|
|
|
716
1093
|
}
|
|
717
1094
|
}
|
|
718
1095
|
}
|
|
719
|
-
const objectAddresses = new Set(
|
|
1096
|
+
const objectAddresses = new Set();
|
|
1097
|
+
for (const o of objects) {
|
|
1098
|
+
if (o.objectAddress)
|
|
1099
|
+
objectAddresses.add(o.objectAddress);
|
|
1100
|
+
if (o.objectName) {
|
|
1101
|
+
objectAddresses.add(o.objectName);
|
|
1102
|
+
const shortType = shortTypeName(o.objectType).toLowerCase();
|
|
1103
|
+
objectAddresses.add(`${shortType}:${o.objectName}`);
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
720
1106
|
let danglingCount = 0;
|
|
721
1107
|
for (const e of edges) {
|
|
722
1108
|
if (e.edgeSource === "potential")
|
|
@@ -742,10 +1128,26 @@ function evaluateCompleteness(input) {
|
|
|
742
1128
|
if (published) {
|
|
743
1129
|
strengths.push("Service already published — deployment complete");
|
|
744
1130
|
readinessScore = 100;
|
|
1131
|
+
const machineAddr = data.machine;
|
|
1132
|
+
if (typeof machineAddr === "string" && machineAddr.startsWith("0x")) {
|
|
1133
|
+
const machineData = parseCacheData(objectCache.get(machineAddr));
|
|
1134
|
+
if (machineData && machineData.bPublished === false) {
|
|
1135
|
+
readinessScore -= 10;
|
|
1136
|
+
findings.push({
|
|
1137
|
+
criterion: "deployment_readiness",
|
|
1138
|
+
severity: "info",
|
|
1139
|
+
message: `Bound Machine ${machineAddr.slice(0, 8)} is not published — order_progress will fail (machine.move E_NOT_PUBLISHED)`,
|
|
1140
|
+
deduction: -10,
|
|
1141
|
+
});
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
745
1144
|
}
|
|
746
1145
|
else {
|
|
747
1146
|
const hasMachine = bindingIndex.get(`${services[0].objectAddress}#machine`)?.size;
|
|
748
1147
|
const hasPermission = bindingIndex.get(`${services[0].objectAddress}#permission`)?.size;
|
|
1148
|
+
const hasOrderAllocators = bindingIndex.get(`${services[0].objectAddress}#order_allocators`)?.size;
|
|
1149
|
+
const compensationBalanceForReady = getBalanceValue(data, "compensation_fund");
|
|
1150
|
+
const hasArbitrations = bindingIndex.get(`${services[0].objectAddress}#arbitrations`)?.size;
|
|
749
1151
|
if (!hasMachine) {
|
|
750
1152
|
readinessScore -= 30;
|
|
751
1153
|
findings.push({
|
|
@@ -764,8 +1166,31 @@ function evaluateCompleteness(input) {
|
|
|
764
1166
|
deduction: -30,
|
|
765
1167
|
});
|
|
766
1168
|
}
|
|
767
|
-
if (
|
|
768
|
-
|
|
1169
|
+
if (!hasOrderAllocators) {
|
|
1170
|
+
readinessScore -= 30;
|
|
1171
|
+
findings.push({
|
|
1172
|
+
criterion: "deployment_readiness",
|
|
1173
|
+
severity: "critical",
|
|
1174
|
+
message: "Service has no order_allocators — cannot publish (service.move:493 E_NO_ALLOCATOR)",
|
|
1175
|
+
deduction: -30,
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
if (compensationBalanceForReady > 0 && !hasArbitrations) {
|
|
1179
|
+
readinessScore -= 25;
|
|
1180
|
+
findings.push({
|
|
1181
|
+
criterion: "deployment_readiness",
|
|
1182
|
+
severity: "critical",
|
|
1183
|
+
message: `Service has compensation_fund (${compensationBalanceForReady}) but no arbitrations — cannot publish (service.move:495 E_ARBITRATION_NOT_SET_WITH_COMPENSATION_FUND)`,
|
|
1184
|
+
deduction: -25,
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
if (hasMachine && hasPermission && hasOrderAllocators) {
|
|
1188
|
+
if (compensationBalanceForReady > 0 && hasArbitrations) {
|
|
1189
|
+
strengths.push("Service ready to publish (Machine + Permission + order_allocators + arbitrations bound)");
|
|
1190
|
+
}
|
|
1191
|
+
else if (compensationBalanceForReady === 0) {
|
|
1192
|
+
strengths.push("Service ready to publish (Machine + Permission + order_allocators bound; arbitrations optional when compensation_fund is 0)");
|
|
1193
|
+
}
|
|
769
1194
|
}
|
|
770
1195
|
}
|
|
771
1196
|
}
|
|
@@ -788,6 +1213,9 @@ function evaluateCompleteness(input) {
|
|
|
788
1213
|
: overall >= 80
|
|
789
1214
|
? `Complete project (score ${overall}/100). Ready for deployment.`
|
|
790
1215
|
: `Partially complete (score ${overall}/100). ${findings.length} gap(s) to address.`;
|
|
1216
|
+
const industryPattern = evaluateIndustryPattern(project, objects, objectCache, edges);
|
|
1217
|
+
strengths.push(...industryPattern.strengths);
|
|
1218
|
+
findings.push(...industryPattern.findings);
|
|
791
1219
|
return {
|
|
792
1220
|
evaluationType: "completeness",
|
|
793
1221
|
score: overall,
|
|
@@ -799,7 +1227,7 @@ function evaluateCompleteness(input) {
|
|
|
799
1227
|
evaluatedAt: Date.now(),
|
|
800
1228
|
};
|
|
801
1229
|
}
|
|
802
|
-
function evaluateWorthBuyingStub(
|
|
1230
|
+
function evaluateWorthBuyingStub(_input, llmPrompt) {
|
|
803
1231
|
return {
|
|
804
1232
|
evaluationType: "worth_buying",
|
|
805
1233
|
score: 0,
|
|
@@ -812,7 +1240,7 @@ function evaluateWorthBuyingStub(input, llmPrompt) {
|
|
|
812
1240
|
evaluatedAt: Date.now(),
|
|
813
1241
|
};
|
|
814
1242
|
}
|
|
815
|
-
function evaluateValueStub(
|
|
1243
|
+
function evaluateValueStub(_input, llmPrompt) {
|
|
816
1244
|
return {
|
|
817
1245
|
evaluationType: "value",
|
|
818
1246
|
score: 0,
|
|
@@ -826,21 +1254,78 @@ function evaluateValueStub(input, llmPrompt) {
|
|
|
826
1254
|
};
|
|
827
1255
|
}
|
|
828
1256
|
export function runEvaluation(input, evaluationType, llmPrompt) {
|
|
1257
|
+
const filtered = filterTemplateObjects(input);
|
|
829
1258
|
switch (evaluationType) {
|
|
830
1259
|
case "risk":
|
|
831
|
-
return evaluateRisk(
|
|
1260
|
+
return evaluateRisk(filtered);
|
|
832
1261
|
case "trust":
|
|
833
|
-
return evaluateTrust(
|
|
1262
|
+
return evaluateTrust(filtered);
|
|
834
1263
|
case "completeness":
|
|
835
|
-
return evaluateCompleteness(
|
|
1264
|
+
return evaluateCompleteness(filtered);
|
|
836
1265
|
case "worth_buying":
|
|
837
|
-
return evaluateWorthBuyingStub(
|
|
1266
|
+
return evaluateWorthBuyingStub(filtered, llmPrompt);
|
|
838
1267
|
case "value":
|
|
839
|
-
return evaluateValueStub(
|
|
1268
|
+
return evaluateValueStub(filtered, llmPrompt);
|
|
840
1269
|
default:
|
|
841
1270
|
throw new Error(`Unknown evaluation type: ${evaluationType}`);
|
|
842
1271
|
}
|
|
843
1272
|
}
|
|
1273
|
+
function filterTemplateObjects(input) {
|
|
1274
|
+
const isDraftAddr = (addr) => {
|
|
1275
|
+
if (!addr)
|
|
1276
|
+
return true;
|
|
1277
|
+
return addr.startsWith("draft:");
|
|
1278
|
+
};
|
|
1279
|
+
const isDraftObject = (o) => {
|
|
1280
|
+
if (isDraftAddr(o.objectAddress))
|
|
1281
|
+
return true;
|
|
1282
|
+
const meta = o.metadata;
|
|
1283
|
+
return !!(meta && meta.status === "draft");
|
|
1284
|
+
};
|
|
1285
|
+
const draftObjects = [];
|
|
1286
|
+
const realObjects = [];
|
|
1287
|
+
for (const o of input.objects) {
|
|
1288
|
+
if (isDraftObject(o)) {
|
|
1289
|
+
draftObjects.push(o);
|
|
1290
|
+
}
|
|
1291
|
+
else {
|
|
1292
|
+
realObjects.push(o);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
const realIds = new Set();
|
|
1296
|
+
for (const o of realObjects) {
|
|
1297
|
+
if (o.objectAddress)
|
|
1298
|
+
realIds.add(o.objectAddress);
|
|
1299
|
+
if (o.objectName) {
|
|
1300
|
+
realIds.add(o.objectName);
|
|
1301
|
+
const shortType = shortTypeName(o.objectType).toLowerCase();
|
|
1302
|
+
realIds.add(`${shortType}:${o.objectName}`);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
const draftIds = new Set();
|
|
1306
|
+
for (const o of draftObjects) {
|
|
1307
|
+
if (o.objectAddress)
|
|
1308
|
+
draftIds.add(o.objectAddress);
|
|
1309
|
+
if (o.objectName) {
|
|
1310
|
+
draftIds.add(o.objectName);
|
|
1311
|
+
const shortType = shortTypeName(o.objectType).toLowerCase();
|
|
1312
|
+
draftIds.add(`${shortType}:${o.objectName}`);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
const realEdges = input.edges.filter((e) => {
|
|
1316
|
+
const meta = e.metadata;
|
|
1317
|
+
if (meta && meta.blueprint === true)
|
|
1318
|
+
return false;
|
|
1319
|
+
const sourceIsDraft = isDraftAddr(e.sourceObject) || draftIds.has(e.sourceObject);
|
|
1320
|
+
const targetIsDraft = isDraftAddr(e.targetObject) || draftIds.has(e.targetObject);
|
|
1321
|
+
return !sourceIsDraft && !targetIsDraft;
|
|
1322
|
+
});
|
|
1323
|
+
return {
|
|
1324
|
+
...input,
|
|
1325
|
+
objects: realObjects,
|
|
1326
|
+
edges: realEdges,
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
844
1329
|
export function compareProjects(entries, evaluationType) {
|
|
845
1330
|
const ranking = [...entries].sort((a, b) => {
|
|
846
1331
|
if (a.score === null && b.score === null)
|