@wowok/agent-mcp 2.5.3 → 2.5.4

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.
@@ -28,6 +28,9 @@ export interface WorkflowAnalysis {
28
28
  guard_gated_forwards: number;
29
29
  has_refund_path: boolean;
30
30
  has_arbitration_path: boolean;
31
+ has_arbitration_binding: boolean;
32
+ has_wonder_node: boolean;
33
+ has_dispute_anti_pattern: boolean;
31
34
  has_milestone_nodes: boolean;
32
35
  dead_end_nodes: string[];
33
36
  terminal_nodes: string[];
@@ -77,6 +80,82 @@ export interface MarketContext {
77
80
  price_percentile: number;
78
81
  supply_demand_ratio: number;
79
82
  }
83
+ export type UserMetricEvaluatorType = "threshold" | "ratio" | "boolean" | "presence" | "regex_match" | "custom_query";
84
+ export interface UserMetric {
85
+ name: string;
86
+ label: string;
87
+ evaluator: UserMetricEvaluatorType;
88
+ params: {
89
+ path?: string;
90
+ threshold?: number | string;
91
+ invert?: boolean;
92
+ min?: number;
93
+ max?: number;
94
+ pattern?: string;
95
+ query_key?: string;
96
+ };
97
+ weight: number;
98
+ advice_on_fail?: string;
99
+ advice_on_pass?: string;
100
+ }
101
+ export interface UserMetricsConfig {
102
+ version: number;
103
+ source_file?: string;
104
+ metrics: UserMetric[];
105
+ adjustment_cap?: number;
106
+ }
107
+ export interface UserMetricResult {
108
+ name: string;
109
+ label: string;
110
+ passed: boolean;
111
+ score: number;
112
+ observed_value?: string | number | boolean;
113
+ weight: number;
114
+ advice?: string;
115
+ }
116
+ export interface UserMetricsResult {
117
+ metrics: UserMetricResult[];
118
+ score: number;
119
+ adjustment: number;
120
+ source_file?: string;
121
+ evaluated_at: number;
122
+ }
123
+ export interface ServiceNestingEvaluation {
124
+ depth_reached: number;
125
+ truncated: boolean;
126
+ bindings: {
127
+ machine?: {
128
+ object_id: string;
129
+ published: boolean;
130
+ risk_level: "low" | "medium" | "high" | "critical";
131
+ issues: string[];
132
+ };
133
+ arbitrations: Array<{
134
+ object_id: string;
135
+ voting_guard_count: number;
136
+ fee_balance: string;
137
+ paused: boolean;
138
+ risk_level: "low" | "medium" | "high" | "critical";
139
+ issues: string[];
140
+ }>;
141
+ rewards: Array<{
142
+ object_id: string;
143
+ balance: string;
144
+ guard_count: number;
145
+ risk_level: "low" | "medium" | "high" | "critical";
146
+ issues: string[];
147
+ }>;
148
+ repositories: Array<{
149
+ object_id: string;
150
+ risk_level: "low" | "medium" | "high" | "critical";
151
+ issues: string[];
152
+ }>;
153
+ };
154
+ overall_risk_level: "low" | "medium" | "high" | "critical";
155
+ overall_risk_score: number;
156
+ total_bindings_evaluated: number;
157
+ evaluated_at: number;
158
+ }
80
159
  export interface InfoPuzzle {
81
160
  service_id: string;
82
161
  service_basics: ServiceBasics;
@@ -85,6 +164,8 @@ export interface InfoPuzzle {
85
164
  trust_signals?: TrustSignals;
86
165
  merchant_behavior?: MerchantBehavior;
87
166
  market_context?: MarketContext;
167
+ user_metrics?: UserMetricsResult;
168
+ service_nesting?: ServiceNestingEvaluation;
88
169
  completeness: number;
89
170
  gaps: string[];
90
171
  assembled_at: number;
@@ -107,8 +188,12 @@ export interface RiskDimensionScore {
107
188
  export type RiskLevel = "low" | "medium_low" | "medium_high" | "high";
108
189
  export interface RiskScore {
109
190
  total: number;
191
+ base_total: number;
110
192
  level: RiskLevel;
111
193
  dimensions: RiskDimensionScore[];
194
+ user_metrics_adjustment?: number;
195
+ service_nesting_risk?: number;
196
+ service_nesting_level?: "low" | "medium" | "high" | "critical";
112
197
  red_flags: string[];
113
198
  advice: string[];
114
199
  }
@@ -29,25 +29,24 @@ export const MACHINE_SCENES = [
29
29
  id: "ecommerce_with_exceptions",
30
30
  name: "E-commerce Flow with Exception Branches",
31
31
  industry: ["ecommerce"],
32
- scenario: "Paid (entry) → Ship → {Receive → Complete | Lost → Arbitration | Return → Refund}",
32
+ scenario: "Paid (entry) → Ship → {Receive → Complete | Lost (terminal) | Return → Refund}",
33
33
  participants: [
34
- { role: "customer", permission_type: "OrderHolder", involved_in: ["Receive", "Return"] },
35
- { role: "merchant", permission_type: "Permission", involved_in: ["Ship", "Refund"] },
36
- { role: "arbitrator", permission_type: "NamedOperator", involved_in: ["Arbitration"] },
34
+ { role: "customer", permission_type: "OrderHolder", involved_in: ["Receive", "Return", "Lost"] },
35
+ { role: "merchant", permission_type: "Permission", involved_in: ["Ship", "Refund", "Lost"] },
37
36
  ],
38
37
  recommended_patterns: ["P-M-SEQ", "P-M-COMPETING"],
39
38
  recommended_topology: ["P-M-LINEAR", "P-M-COMPETING"],
40
39
  guard_requirements: [
41
40
  { forward: "Lost", scene: "machine_forward_guard", purpose: "Dual-sig to confirm lost" },
42
- { forward: "Arbitration", scene: "arbitration_usage_guard", purpose: "Verify arbitration eligibility" },
43
41
  ],
44
- allocation_integration: "Complete->merchant | Refund->customer | Arb->arbitrator",
42
+ allocation_integration: "Complete->merchant | Refund->customer | Lost->escrow or partial refund (Allocator only). Disputes are NOT modeled here — they flow through Order + Arbitration object off-Machine.",
45
43
  special_constraints: [
46
44
  "First node 'Paid' represents post-payment state — buy() completes payment atomically before Progress is created",
47
45
  "No separate 'Order' or 'Pay' nodes needed — payment is handled by Service.buy(), not Machine",
48
- "Lost node threshold=2 (customer + merchant dual-sig)",
49
- "Competing Pair: Receive vs Lost (first-wins)",
50
- "Arbitration flow independent of main flow",
46
+ "Lost node threshold=2 (customer + merchant dual-sig) — Lost is a TERMINAL node, not a path to arbitration",
47
+ "Competing Pair: Receive vs Lost vs Return (first-wins)",
48
+ "DISPUTE INDEPENDENCE: arbitration is NOT a Machine node. If a Lost-terminal order later needs dispute resolution, the customer files arbitration::dispute(order, arbitration) against the Arbitration object bound to the Service — completely off-Machine. Arbitrators live in Arbitration.voting_guard, never in Permission or NamedOperator.",
49
+ "Refund Allocation must use sharing.who=OrderHolder (refund to customer, NOT merchant)",
51
50
  ],
52
51
  first_node_semantics: "Paid — order has been created and paid via Service.buy(); Machine starts at post-payment state",
53
52
  mutable_after_publish: false,
@@ -207,31 +206,32 @@ export const MACHINE_SCENES = [
207
206
  m_rounds_focus: ["M1", "M3", "M8"],
208
207
  },
209
208
  {
210
- id: "arbitration_flow",
211
- name: "Arbitration Flow",
212
- industry: ["ecommerce", "service", "rental"],
213
- scenario: "Dispute → Submit → Vote → Ruling → Execute",
209
+ id: "wonder_feedback",
210
+ name: "Wonder Feedback Flow (Post-Completion Rating)",
211
+ industry: ["ecommerce", "service", "rental", "subscription", "education"],
212
+ scenario: "Paid (entry) → ... → Completed → {Wonder | NoWonder} (rating terminals)",
214
213
  participants: [
215
- { role: "disputant", permission_type: "OrderHolder", involved_in: ["Submit Dispute"] },
216
- { role: "arbitrator", permission_type: "NamedOperator", involved_in: ["Vote"] },
217
- { role: "system", permission_type: "Permission", involved_in: ["Execute Ruling"] },
214
+ { role: "customer", permission_type: "OrderHolder", involved_in: ["Wonder", "NoWonder"] },
215
+ { role: "merchant", permission_type: "Permission", involved_in: ["Completed"] },
218
216
  ],
219
- recommended_patterns: ["P-M-VOTE", "P-M-SEQ"],
220
- recommended_topology: ["P-M-LINEAR"],
217
+ recommended_patterns: ["P-M-SEQ", "P-M-COMPETING"],
218
+ recommended_topology: ["P-M-LINEAR", "P-M-COMPETING"],
221
219
  guard_requirements: [
222
- { forward: "Submit Dispute", scene: "arbitration_usage_guard", purpose: "Verify dispute eligibility + prevent double-dispute" },
223
- { forward: "Vote", scene: "arbitration_voting_guard", purpose: "Verify voting eligibility + extract weight" },
220
+ { forward: "Praise", scene: "machine_forward_guard", purpose: "Verify Progress.current == 'Completed' (rating only after completion)" },
221
+ { forward: "Complain", scene: "machine_forward_guard", purpose: "Verify Progress.current == 'Completed' (rating only after completion)" },
224
222
  ],
225
- allocation_integration: "After ruling -> distribute funds according to ruling",
223
+ allocation_integration: "Completed -> merchant allocation (terminal); Wonder/NoWonder are reputation terminals — no fund allocation. Reward object (independent) claims fire when Progress.current == 'wonder'",
226
224
  special_constraints: [
227
- "Arbitration object must be already created",
228
- "Prevent double-dispute (R-X1-14 HIGH)",
229
- "Prevent double-vote (R-X1-14 CRITICAL)",
230
- "MAX_DISPUTE_COUNT=10 per order",
225
+ "Dispute MUST NOT be modeled as machine nodes — disputes flow through Order + Arbitration object, independent of this Machine",
226
+ "Wonder/NoWonder are POST-completion terminals: order is already settled when customer rates",
227
+ "Both Wonder and NoWonder forwards have named_operator='' (OrderHolder) — rating is a customer-only action",
228
+ "Reward object (if bound) MUST use a Guard verifying Progress.current == 'wonder' AND order.holder == signer — no Permission needed for reward claim",
229
+ "Competing Pair: Praise vs Complain (first-wins; one rating per order)",
230
+ "No dispute/refund/arbitration nodes in this Machine — those flows live off-Machine",
231
231
  ],
232
- first_node_semantics: "DisputeOpened — order has been created and paid via Service.buy(); Machine starts at post-payment dispute state",
232
+ first_node_semantics: "Paid — order has been created and paid via Service.buy(); Machine starts at post-payment state, same as e-commerce baseline",
233
233
  mutable_after_publish: false,
234
- m_rounds_focus: ["M1", "M3", "M4", "M5", "M8"],
234
+ m_rounds_focus: ["M1", "M2", "M3", "M5", "M8"],
235
235
  },
236
236
  {
237
237
  id: "subscription",
@@ -281,8 +281,11 @@ export function inferSceneFromFlow(description) {
281
281
  if (lower.includes("supply chain") || lower.includes("cross-machine")) {
282
282
  return findScene("cross_machine_supply_chain");
283
283
  }
284
- if (lower.includes("arbitration") || lower.includes("dispute")) {
285
- return findScene("arbitration_flow");
284
+ if (lower.includes("wonder") || lower.includes("praise") || lower.includes("complain")
285
+ || lower.includes("thumbs_up") || lower.includes("thumbs_down")
286
+ || lower.includes("rating") || lower.includes("feedback")
287
+ || lower.includes("好评") || lower.includes("差评")) {
288
+ return findScene("wonder_feedback");
286
289
  }
287
290
  if (lower.includes("reward") || lower.includes("incentive")) {
288
291
  return findScene("reward_incentive");
@@ -109,6 +109,29 @@ export const MACHINE_RISK_RULES = [
109
109
  auto_fixable: false,
110
110
  related_constraint: "MC-C-04",
111
111
  },
112
+ {
113
+ id: "R-M1-11",
114
+ name: "Dispute anti-pattern — machine illegally models dispute flow",
115
+ category: "structural",
116
+ severity: "critical",
117
+ description: "Machine contains nodes named 'disputed', 'refunded', 'arb', 'arbitration', " +
118
+ "'dispute', or 'refund'. This is a CRITICAL design anti-pattern because " +
119
+ "dispute is INDEPENDENT of the machine workflow. Dispute flows through: " +
120
+ "Order → Arbitration.dispute() → Arb object → arbitration() ruling → " +
121
+ "arb_withdraw by order holder. Arbitrators are configured in " +
122
+ "Arbitration.voting_guard, NOT in Permission. Merchants who model " +
123
+ "dispute in the machine (e.g., 'disputed → refunded' forward guarded " +
124
+ "by Permission index 1500) create dead-end paths because no one has " +
125
+ "Permission 1500 — arbitrators are NOT Permission holders.",
126
+ detection: "Match node names against regex /^(disputed|refunded|arb|arbitration|dispute|refund)$/i",
127
+ recommendation: "Remove all dispute/refund nodes from the machine. Instead: (1) bind an " +
128
+ "Arbitration object to the Service via Service.arbitrations, (2) configure " +
129
+ "arbitrators via Arbitration.voting_guard_add, (3) deposit compensation_fund " +
130
+ "to the Service. Disputes are then filed via arbitration::dispute(order, " +
131
+ "arbitration) — completely independent of the machine workflow.",
132
+ auto_fixable: false,
133
+ related_constraint: "dispute_independence_principle",
134
+ },
112
135
  {
113
136
  id: "R-M2-01",
114
137
  name: "Guard not created",
@@ -993,142 +993,174 @@ export const MACHINE_TEMPLATES = [
993
993
  ],
994
994
  },
995
995
  {
996
- id: "tpl_machine_arbitration",
997
- scene_id: "arbitration_flow",
998
- name: "Arbitration Flow",
999
- description: "Dispute -> Submit -> Vote -> Ruling -> Execute. Uses arbitration_usage_guard + arbitration_voting_guard. " +
1000
- "MAX_DISPUTE_COUNT=10 per order. Dual-vote prevention mandatory.",
1001
- recommended_patterns: ["P-M-VOTE", "P-M-SEQ"],
1002
- recommended_topology: ["P-M-LINEAR"],
996
+ id: "tpl_machine_wonder_feedback",
997
+ scene_id: "wonder_feedback",
998
+ name: "Wonder Feedback Flow (Post-Completion Rating)",
999
+ description: "Post-completion rating flow: Paid (entry) → Shipped → Received → Completed → {Wonder | NoWonder}. " +
1000
+ "After the order reaches 'Completed' (terminal business state, funds already allocated to merchant), " +
1001
+ "the OrderHolder (namedOperator='') can advance Progress to 'wonder' (positive review) or 'no_wonder' " +
1002
+ "(negative review). A bound Reward object's claim_guard verifies Progress.current == 'wonder' before " +
1003
+ "paying out — reward claim needs NO Permission. Dispute is NEVER modeled here: it flows through " +
1004
+ "Order → Arbitration.dispute() → Arb → arbitration() ruling → arb_withdraw, fully off-Machine.",
1005
+ recommended_patterns: ["P-M-SEQ", "P-M-COMPETING"],
1006
+ recommended_topology: ["P-M-LINEAR", "P-M-COMPETING"],
1003
1007
  parameters: [
1004
1008
  {
1005
- name: "arbitration_ref",
1006
- type: "arbitration_ref",
1009
+ name: "merchant_permission",
1010
+ type: "permission_ref",
1007
1011
  required: true,
1008
- description: "Arbitration object address",
1012
+ description: "Merchant Permission object (index 2 = merchant, index 3 = admin)",
1009
1013
  },
1010
1014
  {
1011
- name: "max_disputes",
1012
- type: "number",
1015
+ name: "wonder_node_name",
1016
+ type: "string",
1013
1017
  required: true,
1014
- default: "10",
1015
- description: "Hard limit: MAX_DISPUTE_COUNT=10 per order",
1018
+ default: "wonder",
1019
+ description: "Node name for positive feedback (好评) terminal",
1020
+ constraint: "Must match /^(wonder|praise|thumbs_up)$/i OR be a custom name; must NOT match dispute anti-pattern regex",
1016
1021
  },
1017
1022
  {
1018
- name: "voting_threshold",
1019
- type: "number",
1023
+ name: "no_wonder_node_name",
1024
+ type: "string",
1020
1025
  required: true,
1021
- default: "100",
1022
- description: "Weight threshold required for a voting decision to pass",
1026
+ default: "no_wonder",
1027
+ description: "Node name for negative feedback (差评) terminal",
1028
+ constraint: "Must match /^(no_wonder|complain|thumbs_down)$/i OR be a custom name; must NOT match dispute anti-pattern regex",
1029
+ },
1030
+ {
1031
+ name: "reward_ref",
1032
+ type: "reward_ref",
1033
+ required: false,
1034
+ description: "Reward object address (optional). If bound, its claim_guard should verify Progress.current == wonder_node_name AND order.holder == signer. Use wonder_praise or wonder_feedback reward template.",
1035
+ },
1036
+ {
1037
+ name: "rating_time_limit_days",
1038
+ type: "number",
1039
+ required: false,
1040
+ default: "30",
1041
+ description: "Days after completion during which the customer can rate (enforced by Guard on Praise/Complain forwards via context(Clock))",
1042
+ constraint: "Must be > 0; reasonable range 7-90",
1023
1043
  },
1024
1044
  ],
1025
1045
  example_nodes: [
1026
- { name: "DisputeOpened" },
1027
- { name: "EvidenceSubmitted" },
1028
- { name: "Voting" },
1029
- { name: "Ruling" },
1030
- { name: "Executed" },
1046
+ { name: "Paid", description: "Entry: order created and paid via Service.buy(); Machine starts here" },
1047
+ { name: "Shipped", description: "Merchant dispatched goods/service" },
1048
+ { name: "Received", description: "Customer confirmed receipt" },
1049
+ { name: "Completed", description: "Terminal business state — triggers merchant Allocation. Funds settled." },
1050
+ { name: "${wonder_node_name}", description: "Positive feedback terminal (好评). OrderHolder-only. Reward-eligible." },
1051
+ { name: "${no_wonder_node_name}", description: "Negative feedback terminal (差评). OrderHolder-only. Optional Repository write for analytics." },
1031
1052
  ],
1032
1053
  example_pairs: [
1033
1054
  {
1034
1055
  prev_node: "",
1035
- next_node: "DisputeOpened",
1056
+ next_node: "Paid",
1036
1057
  threshold: 1,
1037
- forwards: [
1038
- {
1039
- name: "OpenDispute",
1040
- weight: 1,
1041
- named_operator: "",
1042
- guard: "arb_usage_guard",
1043
- retained_submission: "dispute_record",
1044
- },
1045
- ],
1058
+ forwards: [{ name: "Enter", weight: 1, named_operator: "" }],
1046
1059
  },
1047
1060
  {
1048
- prev_node: "DisputeOpened",
1049
- next_node: "EvidenceSubmitted",
1061
+ prev_node: "Paid",
1062
+ next_node: "Shipped",
1050
1063
  threshold: 1,
1051
- forwards: [{ name: "SubmitEvidence", weight: 1, named_operator: "" }],
1064
+ forwards: [{ name: "Ship", weight: 1, permission_index: 2, named_operator: "" }],
1052
1065
  },
1053
1066
  {
1054
- prev_node: "EvidenceSubmitted",
1055
- next_node: "Voting",
1067
+ prev_node: "Shipped",
1068
+ next_node: "Received",
1069
+ threshold: 1,
1070
+ forwards: [{ name: "ConfirmReceive", weight: 1, named_operator: "" }],
1071
+ },
1072
+ {
1073
+ prev_node: "Received",
1074
+ next_node: "Completed",
1056
1075
  threshold: 1,
1057
- forwards: [{ name: "StartVote", weight: 1, permission_index: 3 }],
1076
+ forwards: [{ name: "Finish", weight: 1, permission_index: 2, named_operator: "" }],
1058
1077
  },
1059
1078
  {
1060
- prev_node: "Voting",
1061
- next_node: "Ruling",
1062
- threshold: "${voting_threshold}",
1079
+ prev_node: "Completed",
1080
+ next_node: "${wonder_node_name}",
1081
+ threshold: 1,
1063
1082
  forwards: [
1064
1083
  {
1065
- name: "ChiefVote",
1066
- weight: 60,
1067
- permission_index: 2,
1068
- guard: "arb_voting_guard",
1069
- },
1070
- {
1071
- name: "MemberVote",
1072
- weight: 8,
1073
- named_operator: "arbitrator",
1074
- guard: "arb_voting_guard",
1084
+ name: "Praise",
1085
+ weight: 1,
1086
+ named_operator: "",
1087
+ guard: "rating_window_guard",
1088
+ retained_submission: "rating_record",
1075
1089
  },
1076
1090
  ],
1077
1091
  },
1078
1092
  {
1079
- prev_node: "Ruling",
1080
- next_node: "Executed",
1093
+ prev_node: "Completed",
1094
+ next_node: "${no_wonder_node_name}",
1081
1095
  threshold: 1,
1082
- forwards: [{ name: "Execute", weight: 1, permission_index: 3 }],
1096
+ forwards: [
1097
+ {
1098
+ name: "Complain",
1099
+ weight: 1,
1100
+ named_operator: "",
1101
+ guard: "rating_window_guard",
1102
+ retained_submission: "rating_record",
1103
+ },
1104
+ ],
1083
1105
  },
1084
1106
  ],
1085
1107
  required_guards: [
1086
1108
  {
1087
- guard_template_id: "tpl_usage_guard_threshold",
1088
- bound_to_forward: "OpenDispute",
1089
- purpose: "Verify dispute count < MAX_DISPUTE_COUNT (10)",
1090
- },
1091
- {
1092
- guard_template_id: "tpl_voting_guard_numeric_weight",
1093
- bound_to_forward: "ChiefVote",
1094
- purpose: "Verify voting eligibility + weight extraction",
1109
+ guard_template_id: "tpl_forward_node_check",
1110
+ bound_to_forward: "Praise",
1111
+ purpose: "Verify Progress.current == 'Completed' (rating only allowed after completion) AND context(Clock) <= progress.entry_time + rating_time_limit_days",
1095
1112
  },
1096
1113
  {
1097
- guard_template_id: "tpl_voting_guard_numeric_weight",
1098
- bound_to_forward: "MemberVote",
1099
- purpose: "Verify voting eligibility + prevent double-vote (R-X1-14 CRITICAL)",
1114
+ guard_template_id: "tpl_forward_node_check",
1115
+ bound_to_forward: "Complain",
1116
+ purpose: "Verify Progress.current == 'Completed' (rating only allowed after completion) AND context(Clock) <= progress.entry_time + rating_time_limit_days",
1100
1117
  },
1101
1118
  ],
1102
1119
  required_permissions: [
1103
- { index: 2, name: "chief_arbitrator", description: "High-weight voter" },
1104
- { index: 3, name: "admin", description: "Vote starter / executor" },
1120
+ { index: 2, name: "merchant", description: "Merchant role for Ship + Finish (NOT for rating — rating is OrderHolder-only via named_operator='')" },
1121
+ { index: 3, name: "admin", description: "Admin override (optional)" },
1105
1122
  ],
1106
1123
  required_allocators: [
1107
1124
  {
1108
- name: "RulingAllocation",
1109
- trigger_node: "Executed",
1110
- description: "Allocation based on ruling outcome",
1125
+ name: "MerchantAllocation",
1126
+ trigger_node: "Completed",
1127
+ description: "100% to merchant Treasury or personal address. Fires at Completion — NOT at Wonder/NoWonder (those are reputation-only terminals with no fund flow).",
1111
1128
  },
1112
1129
  ],
1113
- forkable_fields: ["voting_threshold", "voter_count", "evidence_steps"],
1114
- applicable_industries: ["arbitration", "ecommerce", "service", "rental"],
1130
+ forkable_fields: ["wonder_node_name", "no_wonder_node_name", "rating_time_limit_days", "additional_rating_nodes"],
1131
+ applicable_industries: ["ecommerce", "service", "rental", "subscription", "education"],
1115
1132
  relevant_risks: [
1116
- "R-M1-08", "R-M2-05", "R-M3-04", "R-M5-04",
1117
- "R-X1-14",
1133
+ "R-M1-01", "R-M1-02", "R-M1-08", "R-M1-11",
1134
+ "R-M2-01", "R-M2-05",
1135
+ "R-M3-02", "R-M3-04",
1136
+ "R-M4-01", "R-M5-03",
1118
1137
  ],
1119
1138
  creation_notes: [
1120
- "Arbitration object MUST be created before this Machine",
1121
- "Anti-double-dispute: query arb.dispute_count < 10 (R-X1-14 HIGH)",
1122
- "Anti-double-vote: query arb.voted has [Signer] (R-X1-14 CRITICAL)",
1123
- "MAX_DISPUTE_COUNT=10 hard limit per order (on-chain constant)",
1124
- ],
1125
- example_use_case: "E-commerce dispute: customer claims item not as described. Arbitration panel (1 chief + 3 members) " +
1126
- "votes to rule. Up to 10 disputes per order allowed.",
1139
+ "DISPUTE INDEPENDENCE: This Machine MUST NOT contain 'disputed', 'refunded', 'arb', 'arbitration' nodes (R-M1-11 critical). Disputes are filed off-Machine via arbitration::dispute(order, arbitration).",
1140
+ "Build order: Permission → Machine (unpublished) → Guards → Bind guards → Publish → Bind to Service → Bind Reward (optional)",
1141
+ "First node 'Paid' represents post-payment state — Service.buy() completes payment atomically before Progress is created",
1142
+ "No separate 'Order' or 'Pay' nodes needed — payment is handled by Service.buy(), not Machine",
1143
+ "Wonder/NoWonder forwards have named_operator='' (empty) — this delegates to the OrderHolder (customer). No Permission index needed for rating.",
1144
+ "Completed node triggers MerchantAllocation — funds are settled BEFORE the rating window opens. Rating does NOT affect fund flow.",
1145
+ "Competing Pair: Praise vs Complain (first-wins) — one rating per order, enforced by Guard + retained_submission audit trail.",
1146
+ "Reward object (if bound) is INDEPENDENT of Allocator: Reward.claim() requires Guard+passport, not Permission. Guard checks Progress.current == 'wonder' AND order.holder == signer.",
1147
+ "rating_window_guard enforces time limit: context(Clock) <= progress.entry_time + rating_time_limit_days. After window closes, no rating possible (Completed becomes true terminal).",
1148
+ "Optional: bind a Repository to record feedback node for analytics. Repository write happens via Guard's data extraction (NOT auto-passed — see 06 §14.4).",
1149
+ ],
1150
+ example_use_case: "Photography service: Paid → Shipped (booking confirmed) → Received (session delivered) → Completed " +
1151
+ "(photos delivered, funds released to photographer) → customer advances to 'wonder' (loved the work) " +
1152
+ "or 'no_wonder' (didn't love it). Bound Reward pays 1 WOW to customers who advance to 'wonder'. " +
1153
+ "Merchant's wonder/no_wonder ratio becomes an on-chain reputation signal. Disputes (if any) are " +
1154
+ "filed separately via Arbitration object — never enter this Machine.",
1127
1155
  publish_checklist: [
1128
- "Arbitration object created and tested",
1129
- "Both anti-double Guards tested (dispute + vote)",
1130
- "Voting threshold verified with different weight distributions",
1131
- "MAX_DISPUTE_COUNT boundary tested (10th dispute OK, 11th fails)",
1156
+ "All 6 nodes verified: no 'disputed'/'refunded'/'arb'/'arbitration' names (R-M1-11 PASS)",
1157
+ "Entry Pair exists (prev_node='' targets 'Paid')",
1158
+ "Competing Pair tested: Praise vs Complain — first-wins semantics verified on testnet",
1159
+ "rating_window_guard tested: rating blocked after time limit expires",
1160
+ "MerchantAllocation fires ONLY at Completed (NOT at Wonder/NoWonder)",
1161
+ "Reward object (if bound): claim_guard tested — Progress.current == 'wonder' required, claim fails at 'no_wonder' or 'Completed'",
1162
+ "named_operator='' on Praise/Complain forwards verified (OrderHolder-only operation)",
1163
+ "machineNode2file export as backup",
1132
1164
  ],
1133
1165
  },
1134
1166
  {
@@ -1,4 +1,4 @@
1
- export type RewardTemplateType = "first_order" | "referral" | "loyalty" | "flash_sale" | "bulk_discount" | "milestone" | "cashback";
1
+ export type RewardTemplateType = "first_order" | "referral" | "loyalty" | "flash_sale" | "bulk_discount" | "milestone" | "cashback" | "wonder_praise" | "wonder_feedback";
2
2
  export interface RewardTemplate {
3
3
  type: RewardTemplateType;
4
4
  name: string;
@@ -92,6 +92,68 @@ export const REWARD_TEMPLATES = [
92
92
  example: "Service reaches the 100-order milestone; the first 50 customers each receive 5 WOW extra reward.",
93
93
  relevantGuardOps: ["query", "repository_read"],
94
94
  },
95
+ {
96
+ type: "wonder_praise",
97
+ name: "Wonder-node praise reward (post-completion positive feedback)",
98
+ description: "Rewards customers who advance their order Progress to the 'wonder' node after normal completion — " +
99
+ "a user-driven positive-feedback mechanism. The machine design pattern is: " +
100
+ "place_order → booked → in_progress → completed → (wonder | no_wonder). " +
101
+ "After the order reaches 'completed' (terminal business state), the OrderHolder " +
102
+ "(namedOperator='') can advance Progress to 'wonder' (positive review) or 'no_wonder' " +
103
+ "(negative review). The Reward's claim_guard verifies Progress.current == 'wonder' " +
104
+ "AND order.service == this Service, so only satisfied customers can claim. " +
105
+ "This creates an on-chain reputation signal that benefits both the merchant " +
106
+ "(public proof of quality) and the customer (monetary reward for honest feedback).",
107
+ guardCondition: "Guard verifies: (1) Progress.current == 'wonder' (customer advanced to praise node), " +
108
+ "(2) Progress.task == order_address AND order.service == this Service, " +
109
+ "(3) Progress.machine == service.machine (snapshot match), " +
110
+ "(4) claimer is the order holder (order.holder == signer). " +
111
+ "Recommended Guard ops: query Progress object, query Order object, " +
112
+ "verify Progress.current field equals 'wonder'.",
113
+ amountRange: { min: "0.1WOW", max: "20WOW" },
114
+ allocationStrategy: "fixed",
115
+ parameters: [
116
+ { name: "amount", type: "string", description: "Reward amount per praise (e.g., '1WOW')", required: true },
117
+ { name: "budget", type: "string", description: "Total budget (e.g., '500WOW')", required: true },
118
+ { name: "expiration_time", type: "number", description: "Claim expiration timestamp (ms); 0 = no expiration", required: false },
119
+ { name: "max_claims_per_user", type: "number", description: "Max claims per user (default 1, prevents duplicate praise rewards)", required: false },
120
+ ],
121
+ example: "A photography service machine: place_order → booked → in_progress → completed → wonder. " +
122
+ "After delivery, the customer advances Progress to 'wonder' if satisfied. " +
123
+ "The Reward's Guard verifies Progress.current == 'wonder', and the customer claims 1 WOW. " +
124
+ "Merchant benefits from on-chain proof of customer satisfaction.",
125
+ relevantGuardOps: ["query", "progress_node_check"],
126
+ },
127
+ {
128
+ type: "wonder_feedback",
129
+ name: "Wonder-node feedback collection (covers wonder & no_wonder)",
130
+ description: "A feedback-collection Reward that pays a small amount for EITHER positive (wonder) OR " +
131
+ "negative (no_wonder) feedback, encouraging customers to leave honest reviews. " +
132
+ "The machine design pattern is the same as wonder_praise: " +
133
+ "place_order → booked → in_progress → completed → (wonder | no_wonder). " +
134
+ "The Reward's claim_guard verifies Progress.current ∈ {'wonder', 'no_wonder'} — both nodes " +
135
+ "trigger the reward. This is useful for merchants who want to gather feedback signal " +
136
+ "regardless of sentiment, e.g., for product improvement or public reputation metrics. " +
137
+ "Pair with a Repository that records the feedback node for historical analysis.",
138
+ guardCondition: "Guard verifies: (1) Progress.current ∈ {'wonder', 'no_wonder'}, " +
139
+ "(2) Progress.task == order_address AND order.service == this Service, " +
140
+ "(3) claimer is the order holder. " +
141
+ "The Guard should query the Progress object and check that current node is one of the " +
142
+ "two feedback nodes. Optional: write the feedback node to a Repository for analytics.",
143
+ amountRange: { min: "0.05WOW", max: "5WOW" },
144
+ allocationStrategy: "fixed",
145
+ parameters: [
146
+ { name: "amount", type: "string", description: "Reward amount per feedback (e.g., '0.5WOW')", required: true },
147
+ { name: "budget", type: "string", description: "Total budget (e.g., '200WOW')", required: true },
148
+ { name: "feedback_nodes", type: "string", description: "Comma-separated list of feedback node names (default: 'wonder,no_wonder')", required: false },
149
+ { name: "expiration_time", type: "number", description: "Claim expiration timestamp (ms); 0 = no expiration", required: false },
150
+ ],
151
+ example: "A freelance design service: place_order → ... → completed → (wonder | no_wonder). " +
152
+ "Customer advances to 'wonder' (liked the work) or 'no_wonder' (didn't like it). " +
153
+ "Both trigger a 0.5 WOW feedback reward. Merchant tracks the wonder/no_wonder ratio " +
154
+ "in a Repository to identify quality issues and improve service.",
155
+ relevantGuardOps: ["query", "progress_node_check", "repository_write"],
156
+ },
95
157
  ];
96
158
  const TEMPLATE_INDEX = new Map(REWARD_TEMPLATES.map((t) => [t.type, t]));
97
159
  export function getRewardTemplate(type) {
@@ -102,22 +164,32 @@ export function listRewardTemplates() {
102
164
  }
103
165
  export function suggestRewardTemplate(intent) {
104
166
  const lower = intent.toLowerCase();
167
+ if (lower.includes("wonder") || lower.includes("praise") || lower.includes("thumbs_up")
168
+ || lower.includes("好评") || lower.includes("positive review")
169
+ || lower.includes("satisfaction reward")) {
170
+ return getRewardTemplate("wonder_praise");
171
+ }
172
+ if (lower.includes("feedback") || lower.includes("review collection")
173
+ || lower.includes("no_wonder") || lower.includes("thumbs_down")
174
+ || lower.includes("差评") || lower.includes("both sentiment")) {
175
+ return getRewardTemplate("wonder_feedback");
176
+ }
105
177
  if (lower.includes("first order") || lower.includes("new") || lower.includes("first")) {
106
178
  return getRewardTemplate("first_order");
107
179
  }
108
- if (lower.includes("referral") || lower.includes("viral") || lower.includes("referral")) {
180
+ if (lower.includes("referral") || lower.includes("viral")) {
109
181
  return getRewardTemplate("referral");
110
182
  }
111
- if (lower.includes("repeat") || lower.includes("loyalty") || lower.includes("loyalty")) {
183
+ if (lower.includes("repeat") || lower.includes("loyalty")) {
112
184
  return getRewardTemplate("loyalty");
113
185
  }
114
- if (lower.includes("limited-time") || lower.includes("flash") || lower.includes("flash")) {
186
+ if (lower.includes("limited-time") || lower.includes("flash")) {
115
187
  return getRewardTemplate("flash_sale");
116
188
  }
117
- if (lower.includes("cashback") || lower.includes("cashback")) {
189
+ if (lower.includes("cashback")) {
118
190
  return getRewardTemplate("cashback");
119
191
  }
120
- if (lower.includes("milestone") || lower.includes("milestone")) {
192
+ if (lower.includes("milestone")) {
121
193
  return getRewardTemplate("milestone");
122
194
  }
123
195
  return undefined;