@wowok/skills 1.3.4 → 2.0.0

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.
@@ -1,359 +0,0 @@
1
- ---
2
- name: wowok-guard
3
- description: |
4
- WoWok Guard — on-chain programmable validation rules that control access,
5
- verify eligibility, and provide dynamic data to objects. Guards serve as
6
- the trust layer for Services (buy_guard), Arbitration (voting_guard weight,
7
- usage_guard), Machines (forward validation), Demands (recommendation filtering),
8
- Rewards (claim eligibility), and Repositories (write validation with data extraction).
9
- Guards also enable off-chain use cases: generate a Passport via `onchain_operations` (`operation_type: "gen_passport"`) to
10
- obtain a signed, time-bound credential for off-chain permission verification.
11
- when_to_use:
12
- - User wants to create or modify a Guard
13
- - User asks about Guard logic, validation rules, trust rules, programmable conditions
14
- - User encounters Guard validation errors or needs to debug a Guard
15
- - User mentions "guard", "validation", "trust rules", "verify", "condition"
16
- - User asks about buy_guard, allocator guard, reward guard, machine node guard
17
- - User asks about arbitration voting_guard or usage_guard
18
- - User needs identity checks, time-locks, multi-condition verification, or entity queries
19
- - User wants to understand how Guards integrate with other WoWok objects
20
- ---
21
-
22
- # WoWok Guard Design Reference
23
-
24
- > **Role**: Service Provider, Arbitrator, or any builder needing programmable on-chain validation
25
- > **Prerequisites**: Guards are CREATE-only; frozen on-chain once deployed
26
- > **Related Skills**: [wowok-machine](../wowok-machine/SKILL.md) (forward guards), [wowok-provider](../wowok-provider/SKILL.md) (buy_guard, allocator guards), [wowok-order](../wowok-order/SKILL.md) (guard submissions), [wowok-arbitrator](../wowok-arbitrator/SKILL.md) (voting_guard, usage_guard), [wowok-messenger](../wowok-messenger/SKILL.md) (encrypted evidence), [wowok-safety](../wowok-safety/SKILL.md) (naming, confirmation), [wowok-tools](../wowok-tools/SKILL.md) (tool reference)
27
-
28
- ---
29
-
30
- ## Core Concepts
31
-
32
- ### What a Guard IS
33
-
34
- A Guard is an **immutable, on-chain programmable validator** — a single-purpose computational tree that returns a boolean: **pass** or **fail**. Every operation protected by a Guard must satisfy its validation logic before the operation can proceed.
35
-
36
- Think of a Guard as a **computational tree of typed nodes** that query on-chain data, compare values, perform arithmetic, and compute one final answer. It has no side effects. It stores no mutable state. It exists purely to answer: "Should this action be allowed?"
37
-
38
- ### The Immutability Contract
39
-
40
- Guards are **CREATE-only**. Once frozen on-chain, logic cannot be altered — this is the foundation of trust. To change a Guard: export via `guard2file`, create a new Guard, and update all references. See wowok-safety skill for naming conventions.
41
-
42
- ### The Three Structural Layers
43
-
44
- Every Guard is built from three layers, each with a distinct role:
45
-
46
- | Layer | Component | Role | Immutable? |
47
- |-------|-----------|------|------------|
48
- | **Declaration** | `table` | Declares every piece of data the Guard touches — constants and runtime submissions — each with a unique identifier (0–255) | Yes |
49
- | **Computation** | `root` | A computational tree of GuardNode types that computes the final boolean result by combining data sources, comparisons, arithmetic, and logic | Yes |
50
- | **Composition** | `rely` (optional) | References to other Guards; the current Guard's result is AND-ed or OR-ed with dependencies, enabling modular Guard composition | Yes |
51
-
52
- **The table is the contract with callers**: It declares what data they must provide at runtime (`b_submission: true`) versus what the Guard already knows (`b_submission: false`). Every `identifier` node references exactly one table entry. **The root is the question**: It must return Bool; intermediate nodes return numbers/strings/addresses/vectors. Guard is **strongly typed** — type mismatches cause creation failure. **The rely is composition**: Up to 4 dependent Guards (AND by default, OR if `logic_or: true`). A Guard can only depend on Guards with `rep: true` (deterministic `repository.data` queries independent of runtime submissions). `rep: false` Guards cannot appear in `rely` lists — violations caught at creation time.
53
-
54
- ### Data Source 4 Classification — The Foundation of Guard Semantics
55
-
56
- A Guard is fundamentally a **data computation tree**: deterministic data (on-chain constants + system context) + submitted data (runtime, semi-open) → derived through finite operation rules → a single boolean result. Every leaf node in the computation tree draws data from one of **4 data source classifications**. Understanding these 4 classifications is essential for designing and interpreting Guards.
57
-
58
- | Type | Name | SDK Manifestation | Native Opcode | Trust Level | Typical Scenario |
59
- |------|------|-------------------|---------------|-------------|------------------|
60
- | **Type 1** | OnChainConstant | `query` + `identifier` (`b_submission: false`, no witness) | TYPE_QUERY + TYPE_CONSTANT | Highest | Query fields of already-published Service/Machine/Reward objects |
61
- | **Type 2** | WitnessDerived | `query` + `identifier` + `convert_witness` (100-108) | TYPE_QUERY + witness_byte | High (source trusted + deterministic derivation) | Order → Progress query via witness=100 |
62
- | **Type 3** | SubmittedObject | `query` + `identifier` (`b_submission: true`, no witness) | TYPE_QUERY + TYPE_CONSTANT | Medium (requires constraint rules) | User submits Order address for field query |
63
- | **Type 4** | SystemContext | `context` (Signer/Clock/Guard) | TYPE_SIGNER / TYPE_CLOCK / TYPE_GUARD | Highest | Identity verification, time-locks |
64
-
65
- **Key insights**: Type 1 and Type 3 are isomorphic at the native layer (both TYPE_QUERY+TYPE_CONSTANT; difference is `b_submission` flag). Type 2 overlays on Type 1 or Type 3 (source object can be constant or submission, then witness derives target). Type 4 is fully independent (no table dependency). Except for Type 4, all data must be declared in the table — the table is the complete data contract: deterministic data (`b_submission: false`, type+value baked at creation) + submitted data (`b_submission: true`, type only, value from caller at runtime — **must have constraint rules designed, otherwise empty data is meaningless**).
66
-
67
- **Guard essence**: A deterministic data set (Type 1 + Type 4) + submitted data (Type 3, must have constraint rules and defined types) → derived through finite operation rules → a single boolean result. You only need to fill in the "data object source meaning" and "field meaning" (e.g., "the permission address of service A", "the current node time of workflow B").
68
-
69
- ### Verifier Constraint Levels — Designing Who Can Pass
70
-
71
- Three levels trade off **security** vs **convenience** when using `context(Signer)`:
72
-
73
- **Level 1 — Single-Identity** (`logic_equal[context(Signer), identifier[N](fixed_address)]`): Only ONE address passes. ⚠️ Avoid — if address unavailable (key loss, rotation), Guard permanently blocks. Risk rule `R-C4-04` flags this.
74
-
75
- **Level 2 — Identity-Set** (recommended): `logic_or` of multiple identity checks. Key distinction:
76
- - **Address-returning queries** (`order.owner`, `permission.owner`, `service.permission`): wrap in `logic_equal[query, context(Signer)]`
77
- - **Bool-returning queries** (suffix "has": `order.agent has`, `permission.admin has`): use directly as `logic_or` child, pass `context(Signer)` as parameter
78
- - **Dynamic permission** (RECOMMENDED): caller submits permission address; Guard verifies `service.permission == submitted_perm` then checks `permission.owner`/`permission.admin has`. Survives permission rotation.
79
-
80
- **Level 3 — Scene-Combined** (verify if Signer binding is even needed):
81
-
82
- | Scene | Signer needed? | Why |
83
- |-------|---------------|-----|
84
- | Allocators + `sharing.who=Entity` | NO | Funds to fixed recipient (R-C3-06 safe) |
85
- | Allocators + `sharing.who=Signer` | YES (Level 2) | Funds to caller — must bind |
86
- | Machine `forward` guard | MAYBE | `permissionIndex`/`namedOperator` may suffice |
87
- | `buy_guard` | Usually NO | Customer is caller; whitelist/credentials suffice |
88
- | `voting_guard` | NO | Weight from EntityRegistrar, not Signer (R-C3-02) |
89
-
90
- **Decision**: If host object already verifies operator OR `sharing.who` routes to fixed recipient → no Signer binding needed. Only add Level 2 when resources flow to caller AND no other layer verifies identity.
91
-
92
- #### One Guard One Purpose Principle
93
-
94
- Each Guard should serve **ONE specific purpose**, documented in its `description`:
95
- - **Scenario**: where the Guard is attached (which host object, which binding field)
96
- - **Verification rules**: concise statement of what conditions are checked
97
- - **Risk notes**: which risks are mitigated (R-C3-01/05/06, etc.) and which trade-offs apply
98
- - **Verifier constraint level**: which Level (1/2/3) is used and why
99
-
100
- General-purpose Guards designed for `rely` composition are the exception — they need explicit general rules and composition documentation. All other Guards should be single-purpose.
101
-
102
- ### Where Guards Attach in the Ecosystem
103
-
104
- Guards are not standalone — they plug into other WoWok objects as validation rules. Understanding these integration points is essential because the **context** of the Guard determines what data is available to it and what happens when it fails.
105
-
106
- | Host Object | Guard Field | What It Controls | Operator | Who Provides Submission Data |
107
- |-------------|-------------|-----------------|----------|------------------------------|
108
- | **Service** | `buy_guard` | Who can purchase from this service | Customer | Customer (signer, credentials) |
109
- | **Service** | `order_allocators[].guard` | Which fund distribution strategy executes | System (auto-evaluated) | System derives from Progress state |
110
- | **Machine** | Forward `guard` | Who can advance to the next workflow node | Customer or Provider | The party executing the forward |
111
- | **Progress** | Submission guard | Whether submitted data satisfies conditions during forward execution | Customer or Provider | The party submitting data |
112
- | **Reward** | `guard` | Who can claim from the reward pool | Claimant | Claimant (address, credentials) |
113
- | **Repository** | Write/quote guard | Who can write to or read from on-chain storage | Writer/Reader | Writer/Reader |
114
- | **Arbitration** | `usage_guard` | Who can file a dispute against this arbitration | Customer | Customer (Passport credential) |
115
- | **Arbitration** | `voting_guard[]` | Who can vote on arbitration proposals and with what weight | Voters (authenticated via Arbitrator) | Voter (Passport credential with weight data) |
116
- | **Gen Passport** | Guard verification | Generating verified credentials after successful validation | Passport holder | Passport applicant |
117
-
118
- ---
119
-
120
- ## Phase 1: Design — Analyze the Validation Intent
121
-
122
- Before constructing any node, articulate **what** you are validating and **why**. Start from the business requirement, not the data structure.
123
-
124
- ### The Central Questions
125
-
126
- Every Guard answers these questions:
127
-
128
- 1. **What action is being protected?** — Buying a service? Advancing a workflow node? Claiming a reward? Casting a weighted vote? Filing a dispute?
129
-
130
- 2. **What data does the Guard have access to?** — Guards see only:
131
- - Their own `table` (pre-set constants plus runtime submissions from the caller)
132
- - On-chain state queried through `query` nodes targeting live WoWok objects
133
- - Transaction context (the current Clock timestamp, the signer's address, the Guard's own object ID)
134
-
135
- 3. **What should the verdict be?** — A single boolean: pass or fail. The root of every Guard tree must return Bool.
136
-
137
- ### Map Business Requirements to Guard Patterns
138
-
139
- | Business Requirement | Guard Pattern | Key Mechanism | Implementation Notes |
140
- |----------------------|---------------|---------------|---------------------|
141
- | "Only the author can purchase this service" | Identity check | Signer address equals stored authorized address | `context(Signer)` vs table constant via `logic_equal`. Variation: use `vec_contains_address` for allowlists |
142
- | "Customer must wait 8 hours before completing" | Time-lock | Clock timestamp exceeds progress entry time plus duration | `context(Clock)` vs `calc_number_add` of Progress query + duration constant. Use `convert_witness=100` to derive Progress from submitted Order ID |
143
- | "Only sunny weather on the activity date" | Repository data check | External data matches expected value | `query` on Repository with policy name and data key. Timestamp keys may need `convert_number_address` |
144
- | "Customer must confirm delivery via signature" | Progress history check | Specific forward has been accomplished | Chain `query_progress_history_find` → `query_progress_history_session_forward_find`. Check `accomplished` flag with `logic_equal` |
145
- | "User can only claim reward once" | Reward record count check | No prior claims exist | `query_reward_record_count` with recipient filter; compare count to zero via `logic_equal` |
146
- | "Order payment > 1,000,000 and reached 'complete' to claim reward" | Multi-condition | Order amount + progress state + service match | Combine multiple `query` nodes with `logic_and`. Use `convert_witness=TypeOrderProgress` to access Progress from Order |
147
- | "Vote weight equals reputation score" | Dynamic weight | GuardIdentifier extracts numeric value from Passport | Table needs numeric submission entry at referenced index. Guard validates eligibility AND weight range |
148
- | "Only premium members can file disputes" | Membership verification | Entity registration or tier check | `query` on ENTITY_REGISTRAR_ADDRESS or Repository. Combine entity existence check with tier comparison via `logic_and` |
149
-
150
- ### Quick Decision: What Guard Pattern Fits?
151
-
152
- ```
153
- Identity check? → context(Signer) + logic_equal (single address) / vec_contains_address (allowlist)
154
- Time constraint? → context(Clock) + calc_number_* comparisons
155
- External data? → query + table entry declaring target object address
156
- Progress state? → query_progress_history_find + convert_witness
157
- One-time claim? → query_reward_record_count + logic_equal(0)
158
- Dynamic weight? → GuardIdentifier + numeric table entry (b_submission: true)
159
- External Repository? → query + table entry declaring Repository address
160
- Entity reputation? → query + table entry declaring ENTITY_REGISTRAR_ADDRESS(0xaab) / ENTITY_LINKER_ADDRESS(0xaaa)
161
- ```
162
-
163
- ### Circular Dependency Handling (Service ↔ Guard)
164
-
165
- When a Guard needs to query a Service that doesn't exist yet (classic chicken-and-egg: `buy_guard` validates Service fields, but Service creation needs `buy_guard` address), use **LocalMark NAME** (not address) in the Guard's query table. LocalMark is resolved by the SDK at publish time, deferring the address binding until after both objects exist.
166
-
167
- **Workflow** (4-step safe pattern):
168
- 1. **Create Service (no publish)** — Service exists as draft with a LocalMark name
169
- 2. **Create Guard** — In the Guard table, reference the Service via `name_or_address: "<service_local_mark_name>"` (NOT a hardcoded address)
170
- 3. **Update Service** — Set `service.buy_guard = <guard_address>` (now resolvable since Guard is created)
171
- 4. **Publish Service** — SDK resolves LocalMark → on-chain address; both objects now bound
172
-
173
- > **Why LocalMark**: A Guard that hardcodes a Service address fails creation if the Service doesn't exist yet. LocalMark defers resolution, allowing the Guard to be created first (or in either order). This pattern also applies to Allocator guards that query Service fields.
174
-
175
- ### Triple Verification Template (withdraw / refund Guards)
176
-
177
- Withdraw and refund guards protect fund flow — the highest-risk operations. They should verify **three independent conditions** (AND-ed):
178
-
179
- | # | Verification | What to Check | Typical Query |
180
- |---|---------------|---------------|---------------|
181
- | (a) | Order status | Order is in expected state (not cancelled / disputed) | `order.service` + `order.balance` |
182
- | (b) | Progress node completion | Progress.current == expected node (e.g. "completion" / "refund") | `progress.current` (via witness 100 from Order) |
183
- | (c) | Permission / role | Caller is authorized (merchant / arbiter / customer) | `permission.owner` / `permission.admin has` (Level 2 identity-set) |
184
-
185
- > **Why all three**: (a) alone is insufficient — Order may be in correct state but Progress not advanced. (b) alone is insufficient — Progress may be at correct node but Order cancelled. (c) alone is insufficient — caller may be authorized but state wrong. AND-combine all three at the Guard root.
186
-
187
- ### Design Before Building
188
-
189
- **Design thoroughly before calling the create operation** — there is no edit phase after creation (see The Immutability Contract above).
190
-
191
- 1. **Query available query instructions first**: Before designing any Guard that queries on-chain data, use `wowok_buildin_info` with info `"guard instructions"` to retrieve the complete list of available query instructions. Each query has a specific ID, name, parameters, and return type — you MUST verify these details before constructing your Guard. Never guess query instruction names or parameter types.
192
- 2. List every data dependency — what must the caller provide? What constants are baked in?
193
- 3. Sketch the logic tree — what comparisons, arithmetic, and logical combinations produce the final boolean?
194
- 4. Verify types — does every comparison receive compatible operands? Are all conversions explicit?
195
- 5. Test the tree mentally — what happens with edge case inputs? What happens if a query returns empty?
196
-
197
- ---
198
-
199
- ## Phase 2: Declare the Data Table
200
-
201
- The Guard table is the **complete declaration of information** the Guard consumes. Every `identifier` node in the computation tree references exactly one table entry by its index number (0–255). Nothing outside the table is accessible.
202
-
203
- ### Table Entry Fields
204
-
205
- | Field | Meaning | Required When |
206
- |-------|---------|---------------|
207
- | `identifier` | Unique index (0–255). The computation tree uses this number to reference the entry. | Always |
208
- | `b_submission` | Whether the **caller** must provide this value at runtime. `true` = runtime submission; `false` = pre-set constant. | Always |
209
- | `value_type` | The type of the value: Bool, Address, String, U8–U256, or vector types. Accepts both string names (preferred, e.g., `"Address"`, `"U64"`) and numeric codes (e.g., `1`, `6`). Use `wowok_buildin_info` with info `"value types"` for the complete mapping. SDK deserialization returns string names. | Always |
210
- | `value` | The constant value when `b_submission` is false; a placeholder when `b_submission` is true. | When `b_submission` is false |
211
- | `name` | Human-readable label describing what this entry represents. | Always |
212
-
213
- ### Design Rules
214
-
215
- - **Every identifier in the tree must exist in the table.** Missing references cause creation to fail.
216
- - **No duplicate identifiers.** Each index number must appear exactly once.
217
- - **Non-submission entries must have a value.** These are baked into the Guard immutably.
218
- - **Submission entries use placeholder values.** The actual value is provided by the caller at runtime.
219
- - **Query target objects must be of type Address in the table.** The `object_type` field is **automatically filled by the SDK** based on the first query node referencing this identifier (it is NOT a user-provided field). The SDK infers the object type from the query instruction's target object type (Progress, Order, Machine, Reward, etc.).
220
- - **Querying EntityRegistrar or EntityLinker requires system address table entries.** Add entries for `ENTITY_REGISTRAR_ADDRESS` (`0xaab`) or `ENTITY_LINKER_ADDRESS` (`0xaaa`) to the table as Address-type constants when your query instruction targets these global registries. Without them, creation fails.
221
- - **Maximum 256 table entries** (identifiers 0–255). The total serialized table size must not exceed 40000 bytes.
222
- - **Submission entries must have descriptive `name` values.** For `b_submission: true` entries, `name` is the contract between Guard and caller — it tells callers what data they must provide. Use natural language that explains the purpose and necessity: "The order ID that identifies the target Order for verification" not `"order_id"`, "The signer's account address that will be compared against the authorized list" not `"addr"`. This is critical because callers see only this name when submitting data.
223
-
224
- ### The convert_witness Mechanism
225
-
226
- `convert_witness` transforms a source object ID into its associated target object — enabling queries across object relationships without requiring the caller to submit multiple IDs. This is the **Type 2 (WitnessDerived)** data source.
227
-
228
- **Core principle**: Witness is a "read the source object's associated field" mechanism (not a lookup table, not an independent index). It is a one-to-one deterministic derivation of object relationships with only 9 derivation types. Caller submits what they have (e.g., Order ID); Guard queries what it needs (e.g., Progress state) via witness conversion.
229
-
230
- **Rules**:
231
- - Witness type encodes source→target transformation
232
- - Table entry's `object_type` (auto-filled by SDK) must match witness source type
233
- - Query instruction's object type must match witness target type
234
- - Type mismatches cause Guard creation to fail
235
- - Multi-hop witnesses (106-108) require intermediate objects to exist
236
-
237
- **Complete 9 witness types** (defined in `guard.rs#L34-L65`):
238
-
239
- | Code | Name | Source → Target | Derivation | Hops |
240
- |------|------|-----------------|------------|------|
241
- | 100 | TypeOrderProgress | Order → Progress | read order.progress field | 1 |
242
- | 101 | TypeOrderMachine | Order → Machine | read order.machine field | 1 |
243
- | 102 | TypeOrderService | Order → Service | read order.service field | 1 |
244
- | 103 | TypeProgressMachine | Progress → Machine | read progress.machine field | 1 |
245
- | 104 | TypeArbOrder | Arb → Order | read arb.order field | 1 |
246
- | 105 | TypeArbArbitration | Arb → Arbitration | read arb.arbitration field | 1 |
247
- | 106 | TypeArbProgress | Arb → Progress | arb.order → order.progress | 2 (multi-hop) |
248
- | 107 | TypeArbMachine | Arb → Machine | arb.order → order.machine | 2 (multi-hop) |
249
- | 108 | TypeArbService | Arb → Service | arb.order → order.service | 2 (multi-hop) |
250
-
251
- **Key notes**:
252
- - **Type 2 can overlay on Type 1 or Type 3**: The source object can be a constant (Type 1, `b_submission: false`) or a submission (Type 3, `b_submission: true`). The witness then derives the target object.
253
- - **Multi-hop witnesses (106-108)**: Arb → Progress/Machine/Service uses two hops (Arb → Order → target). The intermediate Order object must exist for the derivation to succeed.
254
- - **TypeArbArbitration (105)**: Arb and Arbitration are **different on-chain objects**. The witness queries the Arbitration (parent service) from an Arb (case) address — the binding is set when Arbitration creates the Arb.
255
- - **Available witness types** are also discoverable via `wowok_buildin_info` with info `"guard instructions"`.
256
-
257
- ---
258
-
259
- ## Phase 3: Build the Computational Tree
260
-
261
- The root tree is a computational expression whose terminal nodes read data and whose intermediate nodes transform, compare, and combine that data. The root must return Bool.
262
-
263
- ### Tree Principles
264
-
265
- - **Type safety is enforced at creation time.** Every node validates that its children return types compatible with its operation. A `logic_equal` node that receives a String child and a U64 child will fail validation.
266
- - **Evaluation order is stack-based.** Children are evaluated in reverse, so the first child in the array appears at the top of the evaluation stack.
267
- - **Every `identifier` node's index must exist in the table.** This is validated at creation time.
268
-
269
- ### Discovering Available Node Types
270
-
271
- Query the authoritative `GuardNodeSchema` via `schema_query` (name: `onchain_operations_guard`) — it returns every node type, required fields, input/output types, and validation rules. Categories include: data source nodes (`identifier`, `context`, `query`), logic/arithmetic nodes (`logic_*`, `calc_number_*`), string/conversion/vector nodes (`calc_string_*`, `convert_*`, `vec_*`), and record query nodes (`query_reward_record_*`, `query_progress_history_*`).
272
-
273
- **Key principle**: Every node declares its return type and the types it expects from children. The schema enforces these constraints at Guard creation time — type mismatches cause creation to fail. All numeric comparisons normalize to U256, enabling cross-type comparisons without explicit conversion. For the `query` node, discover available instructions via `wowok_buildin_info` ("guard instructions") with `filter` to narrow by name/return type/parameter count/object type.
274
-
275
- ---
276
-
277
- ## Phase 4: Create the Guard
278
-
279
- Guard creation is a **single atomic operation** — it either succeeds (the Guard is frozen on-chain) or fails (nothing is created). There is no intermediate draft state, no editing phase, and no deletion mechanism.
280
-
281
- Use `onchain_operations` (`operation_type: "guard"`) with `root.type: "node"` (inline tree) or `root.type: "file"` (load from `guard2file`-exported JSON/MD — use this to iterate on existing Guards: export → edit → create new).
282
-
283
- ---
284
-
285
- ## Phase 5: Test, Export, and Query
286
-
287
- **Test**: `gen_passport` verifies one or more Guards (AND-ed) and generates an immutable Passport on success. Omit `info` to auto-fetch submissions, or provide explicitly for custom test inputs.
288
-
289
- **Query**: `query_toolkit` → `onchain_objects` for any Guard. Results include `_guard_node_comments` (human-readable annotations per node). Guards are public consensus — all parties can inspect validation rules.
290
-
291
- **Iteration**: `guard2file` → edit JSON/MD → create new Guard via `root.type:"file"` → update all references.
292
-
293
- ---
294
-
295
- ## Guard Data Flow: How Objects Read Guard Data
296
-
297
- Guards are validators AND data sources. When bound, objects read structured data for decisions. Design your Guard table based on what data the target object needs to read — objects don't just validate, they consume Guard submissions as structured data inputs.
298
-
299
- ### Type Requirements by Object
300
-
301
- Each object extracts Guard data with precise type expectations. Mismatches cause creation or runtime failure:
302
-
303
- | Object | Extraction Field | Table Index Requirement | Type Constraint |
304
- |--------|-----------------|------------------------|-----------------|
305
- | **Arbitration** `voting_guard` | Vote weight via `GuardIdentifier(u8)` | Index must be `b_submission: true` | **Numeric** (U8–U256, cast to u32) |
306
- | **Demand** `ServiceGuard` | `service_identifier` mapping | Index must be `b_submission: true` | Depends on Service validation |
307
- | **Machine** Forward | `retained_submission` | Index must be `b_submission: true`; uniquely located by `node→next_node→forward` triple | As declared in table |
308
- | **Repository** | `id_from_submission` | Index must be `b_submission: true` | **Must be Address** |
309
- | **Repository** | `data_from_submission` | Index must be `b_submission: true` | **Must match Repository's value_type** |
310
-
311
- **Notes**: Arbitration uses `usage_guard` for eligibility (pass/fail) and `voting_guard` for weight (fixed or dynamic via `GuardIdentifier`). Demand's `ServiceGuard` passes the submission value at `service_identifier` to the service for validation (if unset, only pass/fail checked). Machine's `retained_submission` stores submission values in Progress for audit. Repository's `id_from_submission`/`data_from_submission` extract structured data from Guard submissions.
312
-
313
- **⚠️ Critical — impack_list semantics in verify phase**: When a Guard queries Repository data (query 1167 `repository.data`) with a policy that has a `quote_guard` set, the verification checks whether the quote_guard address is in the `impack_list`. However, `impack_list` is **always empty during the `verify_guard` phase** — `Passport.new()` initializes `impack:vector[]` and the verify loop never modifies it; `impack` is only populated in `result_for_permission` (after verify completes). This means a Repository query with `quote_guard = Some(addr)` will always fail with `IMPACK_GUARD_NOT_FOUND` in the gen_passport flow; only `quote_guard = None` passes. Verify the quote_guard mechanism's design intent before relying on it.
314
-
315
- ---
316
-
317
- ## Best Practices
318
-
319
- ### Common Pitfalls
320
-
321
- > The full constraint system has **33 rules: 22 creation-phase + 11 runtime-phase**. The 22 creation-phase constraints below are all enforced by SDK + native `validate_guard_data`; runtime constraints are enforced by native `verify_guard`.
322
-
323
- #### Creation-Phase Constraints (22 rules, all enforced by SDK + `validate_guard_data`)
324
-
325
- **Root**: Must return Bool (logic/comparison nodes only).
326
- **Table**: Unique identifiers (0–255), tree-to-table referential integrity, non-empty constants, max 256 entries / 40KB BCS, submission entries = 1-byte type code.
327
- **Query**: Discover valid IDs via `wowok_buildin_info` ("guard instructions"); parameter count/types must match; return type must be compatible with parent (use `convert_string_number`/`convert_number_string` for type mismatches; `logic_as_u256_*` for numeric comparisons); `object.identifier` must be Address type.
328
- **Witness**: 100–108 only; target type must match query's expected object; source type must match table's `object_type`. **Missing `convert_witness`** when accessing Progress from Order ID = runtime failure (looks for Progress at Order's address).
329
- **Rely**: Max 4; must have `rep:true`; no self-reference.
330
- **Binding**: `voting_guard` identifier must be numeric (U8–U256); Repository `id_from_submission` must be Address; `data_from_submission` type must match Repository's `value_type`.
331
- **Immutable**: Guard frozen after `guard::create` — to change, export via `guard2file` and create new.
332
-
333
- #### Practical Tips (not strict constraints, but strongly recommended)
334
-
335
- - **Testing with production durations**: Set time-lock durations to small values (e.g., 1000 milliseconds) during testing. Increase to production values only after verifying the logic works correctly. A Guard with a 30-day lock tested with real durations cannot produce results for a month.
336
- - **Forgetting to export before recreating**: Guards are immutable. If you need to change one, export it first with `guard2file` so you have the exact on-chain definition as a reference. Then create a new Guard with a versioned name and update all references.
337
- - **Not checking Arbitration pause state**: Even with a valid usage_guard Passport, the dispute fails if the Arbitration is paused (`bPaused: true`). The pause check happens before the guard check — advise customers to verify the Arbitration is active before generating Passports.
338
-
339
- #### Runtime-Phase Constraints (enforced by native `verify_guard`)
340
-
341
- These constraints are checked at Guard verification time (gen_passport or actual usage), not at creation. They are often overlooked because creation succeeds but runtime fails:
342
-
343
- - **RUN_IMMUTABLE_01**: Guard must have `immutable=true` to be verified. A Guard that hasn't been finalized via `guard::create` cannot be used.
344
- - **RUN_SUB_01**: The submission's `value[0]` (type byte) must match the table declaration. A submission with the wrong type byte is rejected.
345
- - **RUN_SUB_02**: Every table entry with `b_submission=true` must have a corresponding submission value. Missing submissions cause failure.
346
- - **RUN_SUB_03**: Total submission bytes must be ≤ `MAX_SUBMISSION_SIZE` (256).
347
- - **RUN_WITNESS_01/02**: When witness conversion runs, the source object must have the associated field, and the derived target object must exist. Multi-hop witnesses (106-108) require the intermediate Order object to exist.
348
- - **RUN_QUERY_01**: The query target object must exist and its type must match.
349
- - **RUN_IMPACK_01/02**: At least one impack guard is required; an impack guard failure fails the entire passport. **Note**: `impack_list` is always empty during verify (see Repository section above), so quote_guard queries will fail unless `quote_guard = None`.
350
- - **RUN_TX_01**: The Passport's `tx_hash` must match the current transaction.
351
- - **RUN_RELY_01**: Rely guards must have been added to the passport.
352
-
353
- ### Readability Convention — Prefer String Names
354
-
355
- **Always use string names** (not numeric IDs) for `query` (`"order.service"` not `1563`), `value_type` (`"Address"` not `1`), `convert_witness` (`"OrderProgress"` not `100`), and `context` (`"Signer"`). Matching is case-insensitive. SDK deserialization (`guard2file`/`query_objects`) returns all constants as strings. Discover valid query names via `wowok_buildin_info` with info `"guard instructions"`.
356
-
357
- Common allocator-guard queries: `order.service` (theft protection), `order.owner` (refund binding), `progress.current` (node check), `progress.current_time` (time-lock), `service.permission`, `permission.owner`, `permission.admin has` (identity-set).
358
-
359
- ---
@@ -1,275 +0,0 @@
1
- ---
2
- name: wowok-safety
3
- description: |
4
- WoWok operational safety and best practices — ensures AI follows correct
5
- conventions for security, transaction confirmation, object building workflow,
6
- and common mistake prevention.
7
-
8
- This skill is AUTOMATICALLY triggered before any on-chain operation.
9
- when_to_use:
10
- - AI is about to execute an on-chain write operation
11
- - AI is about to transfer funds or modify financial parameters
12
- - AI is about to publish a Service or Machine
13
- - AI is building multiple interdependent objects
14
- - User mentions "confirm", "approve", "safe", "warning", "best practice"
15
- always: true
16
- ---
17
-
18
- # WoWok Safety & Best Practices
19
-
20
- ## 1. Core Principles
21
-
22
- ### 1.1 Object Reuse Principle (General Best Practice)
23
-
24
- **ALWAYS prefer reusing existing objects over creating new ones** — this enables centralized permission control and reduces management overhead.
25
-
26
- | Object Type | Reuse Strategy | Why |
27
- |-------------|----------------|-----|
28
- | **Permission** | **Strongly recommended** — ask user for existing Permission name/ID | Centralized permission control across all services |
29
- | Machine | Reuse if workflow fits | Save design time for similar processes |
30
- | Guards | Reuse if validation logic matches | Avoid redundant rules |
31
- | Contact | Reuse existing customer service Contact | Single point of management |
32
- | Arbitration | Always reuse existing Arbitration services | Customers choose from established arbiters |
33
-
34
- **How to Reuse**:
35
- - Ask user: *"Do you have an existing object you'd like to reuse? Provide the name or ID."*
36
- - Use string value `"<name_or_id>"` to reference existing objects
37
- - Use object shape `{ name?, ... }` only when creating new
38
-
39
- **CREATE vs MODIFY Pattern** (SDK-enforced, not Move-level):
40
- | Format | Meaning | Use When |
41
- |--------|---------|----------|
42
- | String `"<name>"` or `"<0x...>"` | **REUSE** existing | Object already exists |
43
- | Object `{ name?, ... }` | **CREATE** new | Need new object |
44
-
45
- The SDK resolves names to addresses via `GetObjectExisted()` — a string that fails resolution triggers a hard error. An object shape always creates.
46
-
47
- ### 1.2 Security & Safety
48
-
49
- - **Hot Wallet Usage**: WoWok never exposes private keys. Treat it as a spending account for transfers, receipts, and commerce. Flag large transactions for explicit user confirmation.
50
- - **Amount-Sensitive Operations**: Any token transfer, payment, or reward distribution MUST be verbally confirmed with the user before execution. Use `Payment` objects for commercial transfers when possible (they offer Guard validation and purpose tracking).
51
-
52
- ### 1.3 LOCAL vs ON-CHAIN
53
-
54
- | Type | Tools | Gas | Confirmation |
55
- |------|-------|-----|--------------|
56
- | **LOCAL ONLY** | `account_operation`, `local_mark_operation`, `local_info_operation` | None | Not needed |
57
- | **ON-CHAIN** | `onchain_operations`, `messenger_operation` (some ops), `wip_file` (sign) | Yes | Required |
58
- | **QUERY** | `query_toolkit`, `onchain_table_data`, `onchain_events`, `guard2file`, `machineNode2file` | Read-only | Not needed |
59
- | **ENCRYPTED** | `messenger_operation` (watch/send messages) | Local encryption | Not needed |
60
-
61
- ### 1.4 Default Account
62
-
63
- Empty string `""` means the default account. Always use `""` when the user does not specify an account.
64
-
65
- ---
66
-
67
- ## 2. Transaction Confirmation Protocol
68
-
69
- **Core rule**: NEVER execute an on-chain write without explicit user confirmation.
70
-
71
- ### 2.1 Confirmation Template
72
-
73
- Present a preview table (Operation, Object, Network, Account) with a warning describing what will happen, then ask: "Proceed with execution?"
74
-
75
- ### 2.2 Amount Verification
76
-
77
- - Always display amounts with token symbol (e.g., "10 WOW" not "10000000000").
78
- - Query token decimals first if unsure.
79
-
80
- **Tool**: `query_toolkit` with `query_type: "token_list"`.
81
-
82
- - **Amounts in operations are ALWAYS submitted as U64 integers**. If the user specifies "2 WOW", do NOT submit the string "2 WOW". Instead, calculate and submit `2000000000` (2 × 10^9, where 9 is WOW's decimals).
83
- - **Never assume token decimals**. If the token's decimals cannot be queried, HALT the amount submission and alert the user. Do not proceed with hardcoded or guessed precision.
84
- - Show both raw and human-readable amounts when clarifying with users.
85
-
86
- ### 2.3 Publish Confirmation
87
-
88
- Before publishing a Service or Machine: (1) Export and review — use `guard2file` to export Guard definitions, `machineNode2file` to export Machine nodes; (2) Verify logic — confirm Guards and Machine nodes match user intent; (3) Warn about immutability — once published, many fields become locked. Present a publish confirmation warning listing what becomes immutable, then ask: "This action cannot be easily undone. Proceed?"
89
-
90
- #### 2.3.1 R-M1-11 Pre-Publish Hard Gate (Refund/Deduction Topology)
91
-
92
- For any Machine touching deposits, refunds, escrow, or deductions (rental, freelance milestone, dispute-aware e-commerce), the AI MUST verify R-M1-11 compliance before allowing publish:
93
-
94
- - **Hard blocker**: any node named `deposit_refunded`, `deposit_deducted`, `refunded`, or any name implying the Machine itself performs fund movement.
95
- - **Required pattern**: refund/deduction flows through an **Allocator** triggered by a routing node (e.g., `return_approved`, `damage_confirmed`). The Allocator watches `progress.current == <routing_node>` and executes fund movement.
96
- - **Dispute path**: `arbiter_rule` (routing) → Arbitration off-Machine — no Allocator; arbitration::dispute handles fund distribution.
97
- - **Verification**: review the `machineNode2file` export and grep node names against the forbidden list. If any match, BLOCK publish and alert the user.
98
-
99
- **Why critical**: A refund terminal node without a bound Allocator causes funds to lock permanently in the Order escrow — there is no primitive to move funds out. This was the root cause of P0-01 (Turo deployment: `deposit_refunded` node locked 0.25 WOW customer refunds). See [wowok-machine](../wowok-machine/SKILL.md) "R-M1-11 Anti-Pattern" and [wowok-scenario](../wowok-scenario/SKILL.md) "Rental Mode Template (R-M1-11 Compliant)".
100
-
101
- ---
102
-
103
- ## 3. Common Mistakes to Avoid
104
-
105
- | Mistake | Why It Happens | Prevention |
106
- |---------|---------------|------------|
107
- | **Forgetting no_cache** | Cache lag in dependency chain | Set `env.no_cache: true` on all operations when building multiple objects |
108
- | **Missing permission indices** | Machine forwards reference non-existent indices | Verify Permission object has required indices before creating Machine |
109
- | **R-M1-11 violation** (refund terminal node) | Designing Machine with `deposit_refunded`/`deposit_deducted`/`refunded` nodes implying the Machine refunds — funds lock in escrow with no Allocator path (root cause of P0-01) | Use routing nodes (`return_approved`/`damage_confirmed`/`arbiter_rule`) + bound Allocators; grep `machineNode2file` output for forbidden names BEFORE publish |
110
-
111
- ---
112
-
113
- ## 4. Naming Conventions
114
-
115
- When users request complex systems without naming, propose this scheme:
116
-
117
- ```
118
- <projectPrefix>_<type>_<purpose>_<version>
119
- ```
120
-
121
- | Part | Example | Purpose |
122
- |------|---------|---------|
123
- | Project prefix | `shopFunny_` | Prevents cross-project collisions |
124
- | Type prefix | `machine_`, `guard_`, `service_` | Clarifies object type |
125
- | Purpose suffix | `serviceWithdraw` | Describes function |
126
- | Version suffix | `_v2` | Enables iteration |
127
-
128
- - Always provide `tags` on object creation for filtering and management
129
- - Use short address form (`0x1234...def`); use names as primary identifiers
130
-
131
- ### 4.1 `replaceExistName` Flag
132
-
133
- Controls name collision behavior. **DISCOURAGED** — prefer versioned names (`_v1`, `_v2`).
134
-
135
- | Value | Effect |
136
- |-------|--------|
137
- | `false` (default) | Throws error if name is in use — safe default |
138
- | `true` | Existing object/account with the same name is **suspended** (deactivated) to free the name |
139
-
140
- **Account-specific rules** (stricter than on-chain objects):
141
- - `replaceExistName: true` on the **default account** (`name=''` or omitted) is **FORBIDDEN** — throws error. The default account is unique and cannot be suspended by replacement; use `suspend()` explicitly if needed.
142
- - On non-default account names, `replaceExistName: true` **suspends** the old account (moves to Suspended tab, clears name + messenger). The old account can be reactivated via `resume(address, new_name)`.
143
- - This prevents the "orphan unnamed account" bug where old accounts leaked into the Active list without a name.
144
-
145
- **On-chain objects** (NamedObjectSchema): `replaceExistName: true` steals the name; the old object becomes unnamed (address-only reference). Prefer versioned names for production.
146
-
147
- ---
148
-
149
- ## 5. Network & Token Defaults
150
-
151
- | Parameter | Default Value | Notes |
152
- |-----------|---------------|-------|
153
- | Network | `testnet` | Override via `env.network` |
154
- | Token | `0x2::wow::WOW` | 9 decimals (1 WOW = 1_000_000_000) |
155
-
156
- ### 5.1 Multi-Token Support & Amount Formats
157
-
158
- - **Multi-Token**: All operations support custom `token_type`. ALWAYS query precision via `query_toolkit` with `query_type: "token_list"` first. Never assume decimals.
159
- - **Amount Formats**:
160
- - With unit: `"2WOW"`, `"10.5USDT"` — auto-converted using token precision.
161
- - Plain number: internal unit (e.g., `1000000000` = 1 WOW). Always clarify with users when displaying plain numbers.
162
- - **U64 integers**: Amounts in operations are ALWAYS submitted as U64 integers. If the user specifies "2 WOW", do NOT submit the string "2 WOW" — calculate and submit `2000000000` (2 × 10^9).
163
- - **Never assume token decimals**: If the token's decimals cannot be queried, HALT the amount submission and alert the user.
164
-
165
- ### 5.2 Payment Objects for Commercial Transfers
166
-
167
- Use `Payment` objects for commercial transfers when possible — they offer Guard validation and purpose tracking beyond a simple `account_operation (transfer)`.
168
-
169
- ### 5.3 Cross-Network Switching (testnet → mainnet)
170
-
171
- WoWok objects are network-specific — the same object name resolves to different addresses on testnet vs mainnet. The `local_mark` system manages this mapping automatically.
172
-
173
- **Prerequisites**:
174
- - testnet deployment verified (all objects created, order lifecycle tested)
175
- - Record all object names (query `local_mark_list` for the full list)
176
- - mainnet account has sufficient WOW balance (faucet is testnet-only; use `account_operation transfer` from a funded mainnet account)
177
-
178
- **Switching Steps**:
179
- 1. Change `env.network` from `"testnet"` to `"mainnet"` in all MCP calls
180
- 2. Re-create all objects on mainnet with `replaceExistName: true` (names stay the same, addresses change)
181
- 3. Order matters — follow the dependency chain: Permission → Machine → Guards → Contact → Service → (Order/Progress/Allocation are created at purchase time)
182
- 4. Verify each object creation succeeds and `local_mark` is updated
183
- 5. Test the critical path: place a test order → advance Progress → verify Allocation
184
-
185
- **Key Mechanism**:
186
- ```
187
- testnet: local_mark["my_service"] = 0x867a... (testnet address)
188
- mainnet: local_mark["my_service"] = 0xdef0... (mainnet address, auto-overwritten)
189
- ```
190
- - All object references in Guard tables, Service bindings, and Machine forwards use NAMES (not addresses)
191
- - SDK's `GetObjectExisted()` resolves names to the current network's address at transaction build time
192
- - `replaceExistName: true` ensures the name mapping is overwritten for the new network
193
-
194
- **Important Notes**:
195
- - testnet objects remain on testnet (they don't disappear) — mainnet is a completely fresh deployment
196
- - Guard tables that reference other objects by NAME will auto-resolve to mainnet addresses
197
- - Service.order_allocators and Machine nodes are immutable after publish — verify design before mainnet publish
198
- - mainnet has no faucet — fund accounts via `account_operation` with `transfer` from an existing funded account
199
-
200
- ### 5.4 Publish Pre-Check Checklist
201
-
202
- Before calling `publish: true` on a Service or Machine, verify:
203
-
204
- **Service Publish Checklist**:
205
- - [ ] `order_allocators` is set and design is verified (PERMANENTLY immutable after publish)
206
- - [ ] `machine` is bound and already published
207
- - [ ] `buy_guard` is bound (if purchase validation is needed)
208
- - [ ] `permission` is bound with all required indexes granted
209
- - [ ] If `compensation_fund > 0`: `arbitrations` MUST be bound (contract enforces: `E_ARBITRATION_NOT_SET_WITH_COMPENSATION_FUND`)
210
- - [ ] All Guard names in `order_allocators` resolve correctly
211
- - [ ] All sharing amounts and recipient types (Entity/Signer/GuardIdentifier) are intended
212
- - [ ] Use `dry_run: true` first to simulate the publish transaction
213
-
214
- **Machine Publish Checklist**:
215
- - [ ] All nodes have at least one forward (except terminal nodes)
216
- - [ ] Each forward has `namedOperator` or `permissionIndex` (or both = OR semantic)
217
- - [ ] `namedOperator: ""` means OrderHolder (customer), NOT "anyone"
218
- - [ ] All `permissionIndex` values are granted in the bound Permission object
219
- - [ ] Entry node has `prev_node: ""` and `threshold: 0`
220
- - [ ] Terminal nodes have empty `pairs: []`
221
-
222
- ---
223
-
224
- ## 6. Query-First Pattern
225
-
226
- - **Query before mutate**: Always query current state before modifications.
227
-
228
- **Tool**: `query_toolkit` with appropriate filters.
229
-
230
- - **Pagination**: All on-chain list queries (events, tables, received) support `cursor`/`limit`. Loop for large datasets.
231
- - **Cache control**: Use `no_cache: true` for time-sensitive reads.
232
-
233
- ---
234
-
235
- ## 7. Error Patterns
236
-
237
- | Error | Likely Cause |
238
- |-------|-------------|
239
- | Guard validation failure | After re-submitting with `submission`, Guard logic evaluated to false. Review Guard's rule tree via `guard2file` and submitted data values. |
240
- | File parsing failure | `machineNode2file` or `guard2file` output format error. Check file format and schema compliance. |
241
- | Cache stale reads | Sequential operations fail unexpectedly (e.g., "object not found" when just created). Retry with `env.no_cache: true`. |
242
- | Permission denied | Operating account lacks the required Permission index. Check the object's Permission configuration. |
243
-
244
- ---
245
-
246
- ## 8. Testing & Validation Workflow
247
-
248
- 1. **Design Phase**: Discover available permissions, Guard instructions, and value types via `wowok_buildin_info` (`info: "built-in permissions"` / `"guard instructions"` / `"value types"`).
249
- 2. **Export & Review**: Before publishing, use `guard2file` and `machineNode2file` to export and review definitions.
250
- 3. **Incremental Testing**: Build objects step-by-step, verifying each step.
251
- 4. **Final Validation**: Test all Guard conditions and Machine transitions before publishing.
252
- 5. **Publish**: Only after thorough testing, publish Service and Machine.
253
-
254
- ### 8.1 Value Types — Built-in Type Annotation System
255
-
256
- Every data field in a Guard table or submission carries a `value_type` annotation. Value types matter when: defining Guard table columns, submitting data to Guards, reading `guard2file` exports, and designing query instructions. **String format is recommended** (`"U64"`, `"Address"`, `"VecString"`) for readability; numeric codes (0–18) are accepted but obscure. `Value` (19) is protocol-internal — never use in user-defined Guards. Available types are queried via `wowok_buildin_info` with `info: "value types"`.
257
-
258
- ---
259
-
260
- ## 9. Incremental Object Building
261
-
262
- For complex objects with many fields (Service, Machine), use **incremental building** instead of creating everything in one call:
263
-
264
- - Each step can be verified before proceeding
265
- - Errors are isolated to specific fields
266
- - Easier to retry failed steps without re-executing successful ones
267
- - Better user feedback at each stage
268
-
269
- ---
270
-
271
- ## Schema Reference
272
-
273
- **Related Skills**: [wowok-tools](../wowok-tools/SKILL.md) | [wowok-guard](../wowok-guard/SKILL.md) | [wowok-machine](../wowok-machine/SKILL.md) | [wowok-order](../wowok-order/SKILL.md) | [wowok-provider](../wowok-provider/SKILL.md) | [wowok-arbitrator](../wowok-arbitrator/SKILL.md) | [wowok-messenger](../wowok-messenger/SKILL.md)
274
-
275
- ---