@wowok/agent-mcp 2.5.5 → 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/config/runtime.js +3 -6
- 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/harness/checkpoint.js +2 -2
- package/dist/knowledge/deployment-scanner.d.ts +3 -0
- package/dist/knowledge/deployment-scanner.js +64 -3
- package/dist/knowledge/flywheel-loop.js +2 -5
- 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/overrides-loader.js +2 -4
- package/dist/knowledge/progress-ledger.js +3 -0
- package/dist/knowledge/progress-translation.js +5 -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/loop-engineering/improve.js +2 -4
- package/dist/project/deployment-bridge.d.ts +5 -0
- package/dist/project/deployment-bridge.js +119 -0
- 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 +578 -86
- package/dist/project/graph-builder.d.ts +4 -1
- package/dist/project/graph-builder.js +141 -63
- package/dist/project/graph.d.ts +1 -0
- package/dist/project/handlers.d.ts +221 -5
- package/dist/project/handlers.js +933 -14
- package/dist/project/index.js +2 -6
- package/dist/project/project-store.js +2 -5
- 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/config/index.d.ts +12 -12
- package/dist/schema/local/index.d.ts +140 -143
- package/dist/schema/local/index.js +44 -23
- package/dist/schema/messenger/index.d.ts +290 -62
- package/dist/schema/messenger/index.js +2 -2
- package/dist/schema/operations.d.ts +680 -545
- package/dist/schema/operations.js +22 -0
- package/dist/schema/project/index.d.ts +2064 -84
- package/dist/schema/project/index.js +354 -12
- package/dist/schema/query/index.d.ts +715 -354
- 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/trust/index.d.ts +8 -8
- 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 +14 -22
- package/dist/schemas/account_operation.schema.json +11 -25
- 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 +10 -10
- 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 +1352 -20
- package/dist/schemas/project_operation.schema.json +53 -4
- package/dist/schemas/query_toolkit.output.json +111 -82
- package/dist/schemas/query_toolkit.schema.json +9 -13
- package/dist/schemas/schema_query.output.json +7 -3
- package/dist/schemas/schema_query.schema.json +17 -3
- package/dist/telemetry/storage.js +2 -2
- package/dist/tools/handlers/local.js +20 -5
- package/dist/tools/handlers/onchain.js +36 -0
- package/dist/tools/handlers/project.js +72 -1
- 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 +212 -8
- 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,11 +60,21 @@ 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)));
|
|
41
66
|
}
|
|
67
|
+
function shortTypeName(objectType) {
|
|
68
|
+
if (!objectType)
|
|
69
|
+
return "unknown";
|
|
70
|
+
const base = objectType.replace(/<.*>/, "").trim();
|
|
71
|
+
const segments = base.split("::");
|
|
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;
|
|
77
|
+
}
|
|
42
78
|
function parseCacheData(cached) {
|
|
43
79
|
if (!cached)
|
|
44
80
|
return null;
|
|
@@ -58,6 +94,104 @@ function countArrayField(data, field) {
|
|
|
58
94
|
return v.length;
|
|
59
95
|
return 0;
|
|
60
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
|
+
}
|
|
61
195
|
function detectCircularRefs(edges) {
|
|
62
196
|
const adj = new Map();
|
|
63
197
|
for (const e of edges) {
|
|
@@ -71,14 +205,36 @@ function detectCircularRefs(edges) {
|
|
|
71
205
|
const visited = new Set();
|
|
72
206
|
const recursionStack = new Set();
|
|
73
207
|
const path = [];
|
|
74
|
-
const
|
|
75
|
-
visited.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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++;
|
|
80
229
|
if (!visited.has(neighbor)) {
|
|
81
|
-
|
|
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
|
+
});
|
|
82
238
|
}
|
|
83
239
|
else if (recursionStack.has(neighbor)) {
|
|
84
240
|
const cycleStart = path.indexOf(neighbor);
|
|
@@ -86,12 +242,6 @@ function detectCircularRefs(edges) {
|
|
|
86
242
|
cycles.push(cycle.join("→"));
|
|
87
243
|
}
|
|
88
244
|
}
|
|
89
|
-
path.pop();
|
|
90
|
-
recursionStack.delete(node);
|
|
91
|
-
};
|
|
92
|
-
for (const node of adj.keys()) {
|
|
93
|
-
if (!visited.has(node))
|
|
94
|
-
dfs(node);
|
|
95
245
|
}
|
|
96
246
|
return cycles;
|
|
97
247
|
}
|
|
@@ -138,15 +288,171 @@ function buildBindingIndex(edges) {
|
|
|
138
288
|
idx.set(key, new Set());
|
|
139
289
|
idx.get(key).add(e.targetObject);
|
|
140
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
|
+
}
|
|
141
303
|
return idx;
|
|
142
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
|
+
}
|
|
143
449
|
function evaluateRisk(input) {
|
|
144
450
|
const findings = [];
|
|
145
451
|
const strengths = [];
|
|
146
452
|
const { objects, edges, objectCache, project } = input;
|
|
147
453
|
const byType = new Map();
|
|
148
454
|
for (const obj of objects) {
|
|
149
|
-
const t = obj.objectType
|
|
455
|
+
const t = shortTypeName(obj.objectType);
|
|
150
456
|
if (!byType.has(t))
|
|
151
457
|
byType.set(t, []);
|
|
152
458
|
byType.get(t).push(obj);
|
|
@@ -155,19 +461,24 @@ function evaluateRisk(input) {
|
|
|
155
461
|
const bindingIndex = buildBindingIndex(edges);
|
|
156
462
|
let structuralScore = 100;
|
|
157
463
|
const serviceObj = services[0];
|
|
464
|
+
const svcDataForStruct = serviceObj ? parseCacheData(objectCache.get(serviceObj.objectAddress)) : null;
|
|
465
|
+
const compensationBalanceForStruct = serviceObj ? getBalanceValue(svcDataForStruct, "compensation_fund") : 0;
|
|
158
466
|
if (serviceObj) {
|
|
159
467
|
for (const expected of CRITICAL_BINDINGS) {
|
|
160
468
|
if (expected.sourceType !== "Service")
|
|
161
469
|
continue;
|
|
470
|
+
const effectiveCritical = expected.fieldName === "arbitrations"
|
|
471
|
+
? (compensationBalanceForStruct > 0 ? true : expected.critical)
|
|
472
|
+
: expected.critical;
|
|
162
473
|
const key = `${serviceObj.objectAddress}#${expected.fieldName}`;
|
|
163
474
|
const bindings = bindingIndex.get(key);
|
|
164
475
|
if (!bindings || bindings.size === 0) {
|
|
165
|
-
const deduction =
|
|
476
|
+
const deduction = effectiveCritical ? -25 : -10;
|
|
166
477
|
structuralScore += deduction;
|
|
167
478
|
findings.push({
|
|
168
479
|
criterion: "structural_risk",
|
|
169
|
-
severity:
|
|
170
|
-
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") : ""}`,
|
|
171
482
|
deduction,
|
|
172
483
|
});
|
|
173
484
|
}
|
|
@@ -188,27 +499,72 @@ function evaluateRisk(input) {
|
|
|
188
499
|
structuralScore = clampScore(structuralScore);
|
|
189
500
|
let fundScore = 100;
|
|
190
501
|
const treasuries = byType.get("Treasury") ?? [];
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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 { }
|
|
202
513
|
}
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
fundScore -= 20;
|
|
514
|
+
if (treasuries.length === 0 && compensationBalance === 0) {
|
|
515
|
+
if (industryRequiresFund) {
|
|
516
|
+
fundScore -= 30;
|
|
207
517
|
findings.push({
|
|
208
518
|
criterion: "fund_safety",
|
|
209
519
|
severity: "warning",
|
|
210
|
-
message: "Service
|
|
211
|
-
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,
|
|
212
568
|
});
|
|
213
569
|
}
|
|
214
570
|
}
|
|
@@ -267,18 +623,27 @@ function evaluateRisk(input) {
|
|
|
267
623
|
if (serviceObj) {
|
|
268
624
|
const data = parseCacheData(objectCache.get(serviceObj.objectAddress));
|
|
269
625
|
if (data && !isAddress(data.um)) {
|
|
270
|
-
governanceScore -=
|
|
626
|
+
governanceScore -= 5;
|
|
271
627
|
findings.push({
|
|
272
628
|
criterion: "governance_risk",
|
|
273
|
-
severity: "
|
|
274
|
-
message: "Service has no UM (manager) —
|
|
275
|
-
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,
|
|
276
632
|
});
|
|
277
633
|
}
|
|
278
634
|
}
|
|
279
635
|
governanceScore = clampScore(governanceScore);
|
|
280
636
|
let coherenceScore = 100;
|
|
281
|
-
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
|
+
}
|
|
282
647
|
let danglingCount = 0;
|
|
283
648
|
for (const e of edges) {
|
|
284
649
|
if (e.edgeSource === "potential")
|
|
@@ -340,7 +705,7 @@ function evaluateTrust(input) {
|
|
|
340
705
|
const { objects, edges, objectCache, reputation, project } = input;
|
|
341
706
|
const byType = new Map();
|
|
342
707
|
for (const obj of objects) {
|
|
343
|
-
const t = obj.objectType
|
|
708
|
+
const t = shortTypeName(obj.objectType);
|
|
344
709
|
if (!byType.has(t))
|
|
345
710
|
byType.set(t, []);
|
|
346
711
|
byType.get(t).push(obj);
|
|
@@ -416,15 +781,10 @@ function evaluateTrust(input) {
|
|
|
416
781
|
const bgKey = `${services[0].objectAddress}#buy_guard`;
|
|
417
782
|
if (bindingIndex.get(bgKey)?.size) {
|
|
418
783
|
guardScore = clampScore(guardScore + 15);
|
|
419
|
-
strengths.push("Service has buy_guard — purchase validation active");
|
|
784
|
+
strengths.push("Service has buy_guard bound — purchase validation active");
|
|
420
785
|
}
|
|
421
|
-
else {
|
|
422
|
-
|
|
423
|
-
criterion: "guard_quality",
|
|
424
|
-
severity: "warning",
|
|
425
|
-
message: "Service buy_guard not bound — unvalidated purchases",
|
|
426
|
-
deduction: -15,
|
|
427
|
-
});
|
|
786
|
+
else if (guards.length > 0) {
|
|
787
|
+
strengths.push("Guard objects present (buy_guard binding optional)");
|
|
428
788
|
}
|
|
429
789
|
}
|
|
430
790
|
guardScore = clampScore(guardScore);
|
|
@@ -484,14 +844,16 @@ function evaluateTrust(input) {
|
|
|
484
844
|
case "E3":
|
|
485
845
|
passed = (byType.get("Machine") ?? []).some(m => {
|
|
486
846
|
const d = parseCacheData(objectCache.get(m.objectAddress));
|
|
487
|
-
return d && (countArrayField(d, "nodes") > 0);
|
|
847
|
+
return d && (getNumberField(d, "node_count") > 0 || countArrayField(d, "nodes") > 0);
|
|
488
848
|
});
|
|
489
849
|
break;
|
|
490
850
|
case "E4":
|
|
491
|
-
passed = !!bindingIndex2.get(`${serviceObj2.objectAddress}#buy_guard`)?.size
|
|
851
|
+
passed = !!bindingIndex2.get(`${serviceObj2.objectAddress}#buy_guard`)?.size
|
|
852
|
+
|| (byType.get("Guard") ?? []).length > 0;
|
|
492
853
|
break;
|
|
493
854
|
case "E5":
|
|
494
|
-
passed =
|
|
855
|
+
passed = getBalanceValue(svcData, "compensation_fund") > 0
|
|
856
|
+
|| (byType.get("Treasury") ?? []).length > 0;
|
|
495
857
|
break;
|
|
496
858
|
case "E6":
|
|
497
859
|
passed = (byType.get("Arbitration") ?? []).length > 0;
|
|
@@ -517,20 +879,32 @@ function evaluateTrust(input) {
|
|
|
517
879
|
passed = !!rep && rep.likes >= rep.dislikes;
|
|
518
880
|
}
|
|
519
881
|
break;
|
|
520
|
-
case "E13":
|
|
521
|
-
passed = true;
|
|
522
|
-
break;
|
|
523
882
|
}
|
|
524
883
|
if (passed) {
|
|
525
884
|
diligencePassed.push(check.id);
|
|
526
885
|
}
|
|
527
886
|
else {
|
|
528
|
-
|
|
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
|
+
: "";
|
|
529
903
|
findings.push({
|
|
530
904
|
criterion: "consumer_diligence",
|
|
531
|
-
severity:
|
|
532
|
-
message: `${check.id}: ${check.check} —
|
|
533
|
-
deduction:
|
|
905
|
+
severity: effectiveSeverity,
|
|
906
|
+
message: `${check.id}: ${check.check} — FAIL: condition not met${detailSuffix} [severity: ${effectiveSeverity}]`,
|
|
907
|
+
deduction: effectiveDeduction,
|
|
534
908
|
});
|
|
535
909
|
}
|
|
536
910
|
}
|
|
@@ -578,14 +952,19 @@ function evaluateCompleteness(input) {
|
|
|
578
952
|
const { objects, edges, objectCache, project } = input;
|
|
579
953
|
const byType = new Map();
|
|
580
954
|
for (const obj of objects) {
|
|
581
|
-
const t = obj.objectType
|
|
955
|
+
const t = shortTypeName(obj.objectType);
|
|
582
956
|
if (!byType.has(t))
|
|
583
957
|
byType.set(t, []);
|
|
584
958
|
byType.get(t).push(obj);
|
|
585
959
|
}
|
|
586
960
|
const services = byType.get("Service") ?? [];
|
|
587
961
|
const bindingIndex = buildBindingIndex(edges);
|
|
588
|
-
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);
|
|
589
968
|
const requiredCount = requiredItems.filter(r => r.required).length;
|
|
590
969
|
const optionalItems = requiredItems.filter(r => !r.required);
|
|
591
970
|
let requiredMet = 0;
|
|
@@ -643,23 +1022,28 @@ function evaluateCompleteness(input) {
|
|
|
643
1022
|
let bindingScore = 100;
|
|
644
1023
|
if (services[0]) {
|
|
645
1024
|
const serviceAddr = services[0].objectAddress;
|
|
1025
|
+
const svcDataForBind = parseCacheData(objectCache.get(serviceAddr));
|
|
1026
|
+
const compensationBalanceForBind = getBalanceValue(svcDataForBind, "compensation_fund");
|
|
646
1027
|
let boundCount = 0;
|
|
647
1028
|
let totalCount = 0;
|
|
648
1029
|
for (const expected of CRITICAL_BINDINGS) {
|
|
649
1030
|
if (expected.sourceType !== "Service")
|
|
650
1031
|
continue;
|
|
651
1032
|
totalCount++;
|
|
1033
|
+
const effectiveCritical = expected.fieldName === "arbitrations"
|
|
1034
|
+
? (compensationBalanceForBind > 0 ? true : expected.critical)
|
|
1035
|
+
: expected.critical;
|
|
652
1036
|
const key = `${serviceAddr}#${expected.fieldName}`;
|
|
653
1037
|
if (bindingIndex.get(key)?.size) {
|
|
654
1038
|
boundCount++;
|
|
655
1039
|
}
|
|
656
1040
|
else {
|
|
657
|
-
const deduction =
|
|
1041
|
+
const deduction = effectiveCritical ? -20 : -8;
|
|
658
1042
|
bindingScore += deduction;
|
|
659
1043
|
findings.push({
|
|
660
1044
|
criterion: "binding_completeness",
|
|
661
|
-
severity:
|
|
662
|
-
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") : ""}`,
|
|
663
1047
|
deduction,
|
|
664
1048
|
});
|
|
665
1049
|
}
|
|
@@ -681,19 +1065,19 @@ function evaluateCompleteness(input) {
|
|
|
681
1065
|
let workflowScore = 80;
|
|
682
1066
|
const machines = byType.get("Machine") ?? [];
|
|
683
1067
|
if (machines.length === 0) {
|
|
684
|
-
workflowScore -=
|
|
1068
|
+
workflowScore -= 20;
|
|
685
1069
|
findings.push({
|
|
686
1070
|
criterion: "workflow_rationality",
|
|
687
|
-
severity: "
|
|
688
|
-
message: "No Machine object —
|
|
689
|
-
deduction: -
|
|
1071
|
+
severity: "info",
|
|
1072
|
+
message: "No Machine object — bind one before publishing (deploy-time requirement)",
|
|
1073
|
+
deduction: -20,
|
|
690
1074
|
});
|
|
691
1075
|
}
|
|
692
1076
|
else {
|
|
693
1077
|
for (const m of machines) {
|
|
694
1078
|
const data = parseCacheData(objectCache.get(m.objectAddress));
|
|
695
1079
|
if (data) {
|
|
696
|
-
const nodeCount =
|
|
1080
|
+
const nodeCount = getNumberField(data, "node_count") || countArrayField(data, "nodes");
|
|
697
1081
|
if (nodeCount > 0) {
|
|
698
1082
|
strengths.push(`Machine has ${nodeCount} node(s)`);
|
|
699
1083
|
}
|
|
@@ -709,7 +1093,16 @@ function evaluateCompleteness(input) {
|
|
|
709
1093
|
}
|
|
710
1094
|
}
|
|
711
1095
|
}
|
|
712
|
-
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
|
+
}
|
|
713
1106
|
let danglingCount = 0;
|
|
714
1107
|
for (const e of edges) {
|
|
715
1108
|
if (e.edgeSource === "potential")
|
|
@@ -735,10 +1128,26 @@ function evaluateCompleteness(input) {
|
|
|
735
1128
|
if (published) {
|
|
736
1129
|
strengths.push("Service already published — deployment complete");
|
|
737
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
|
+
}
|
|
738
1144
|
}
|
|
739
1145
|
else {
|
|
740
1146
|
const hasMachine = bindingIndex.get(`${services[0].objectAddress}#machine`)?.size;
|
|
741
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;
|
|
742
1151
|
if (!hasMachine) {
|
|
743
1152
|
readinessScore -= 30;
|
|
744
1153
|
findings.push({
|
|
@@ -757,8 +1166,31 @@ function evaluateCompleteness(input) {
|
|
|
757
1166
|
deduction: -30,
|
|
758
1167
|
});
|
|
759
1168
|
}
|
|
760
|
-
if (
|
|
761
|
-
|
|
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
|
+
}
|
|
762
1194
|
}
|
|
763
1195
|
}
|
|
764
1196
|
}
|
|
@@ -781,6 +1213,9 @@ function evaluateCompleteness(input) {
|
|
|
781
1213
|
: overall >= 80
|
|
782
1214
|
? `Complete project (score ${overall}/100). Ready for deployment.`
|
|
783
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);
|
|
784
1219
|
return {
|
|
785
1220
|
evaluationType: "completeness",
|
|
786
1221
|
score: overall,
|
|
@@ -792,7 +1227,7 @@ function evaluateCompleteness(input) {
|
|
|
792
1227
|
evaluatedAt: Date.now(),
|
|
793
1228
|
};
|
|
794
1229
|
}
|
|
795
|
-
function evaluateWorthBuyingStub(
|
|
1230
|
+
function evaluateWorthBuyingStub(_input, llmPrompt) {
|
|
796
1231
|
return {
|
|
797
1232
|
evaluationType: "worth_buying",
|
|
798
1233
|
score: 0,
|
|
@@ -805,7 +1240,7 @@ function evaluateWorthBuyingStub(input, llmPrompt) {
|
|
|
805
1240
|
evaluatedAt: Date.now(),
|
|
806
1241
|
};
|
|
807
1242
|
}
|
|
808
|
-
function evaluateValueStub(
|
|
1243
|
+
function evaluateValueStub(_input, llmPrompt) {
|
|
809
1244
|
return {
|
|
810
1245
|
evaluationType: "value",
|
|
811
1246
|
score: 0,
|
|
@@ -819,21 +1254,78 @@ function evaluateValueStub(input, llmPrompt) {
|
|
|
819
1254
|
};
|
|
820
1255
|
}
|
|
821
1256
|
export function runEvaluation(input, evaluationType, llmPrompt) {
|
|
1257
|
+
const filtered = filterTemplateObjects(input);
|
|
822
1258
|
switch (evaluationType) {
|
|
823
1259
|
case "risk":
|
|
824
|
-
return evaluateRisk(
|
|
1260
|
+
return evaluateRisk(filtered);
|
|
825
1261
|
case "trust":
|
|
826
|
-
return evaluateTrust(
|
|
1262
|
+
return evaluateTrust(filtered);
|
|
827
1263
|
case "completeness":
|
|
828
|
-
return evaluateCompleteness(
|
|
1264
|
+
return evaluateCompleteness(filtered);
|
|
829
1265
|
case "worth_buying":
|
|
830
|
-
return evaluateWorthBuyingStub(
|
|
1266
|
+
return evaluateWorthBuyingStub(filtered, llmPrompt);
|
|
831
1267
|
case "value":
|
|
832
|
-
return evaluateValueStub(
|
|
1268
|
+
return evaluateValueStub(filtered, llmPrompt);
|
|
833
1269
|
default:
|
|
834
1270
|
throw new Error(`Unknown evaluation type: ${evaluationType}`);
|
|
835
1271
|
}
|
|
836
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
|
+
}
|
|
837
1329
|
export function compareProjects(entries, evaluationType) {
|
|
838
1330
|
const ranking = [...entries].sort((a, b) => {
|
|
839
1331
|
if (a.score === null && b.score === null)
|