@wowok/skills 1.1.12 → 1.2.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/LICENSE +1 -2
- package/examples/Insurance/Insurance.md +426 -70
- package/examples/MyShop/MyShop.md +349 -228
- package/examples/MyShop/myshop_machine_nodes.json +14 -8
- package/examples/MyShop_Advanced/MyShop_Advanced.md +472 -105
- package/examples/ThreeBody_Signature/ThreeBody_Signature.md +623 -121
- package/examples/Travel/Travel.md +298 -77
- package/package.json +1 -1
- package/wowok-guard/SKILL.md +317 -20
- package/wowok-machine/SKILL.md +72 -0
- package/wowok-order/APPENDIX.md +33 -33
- package/wowok-provider/SKILL.md +26 -12
- package/wowok-scenario/SKILL.md +10 -0
- package/wowok-tools/SKILL.md +2 -2
package/package.json
CHANGED
package/wowok-guard/SKILL.md
CHANGED
|
@@ -53,7 +53,125 @@ Every Guard is built from three layers, each with a distinct role:
|
|
|
53
53
|
|
|
54
54
|
**The root is the question**: It must return Bool. Intermediate nodes return numbers, strings, addresses, or vectors. Guard is **strongly typed** — the type system is strictly enforced at creation time. Type mismatches (e.g., passing a string to a numeric comparison node) will cause validation errors and prevent Guard creation.
|
|
55
55
|
|
|
56
|
-
**The rely is composition**: Up to 4 dependent Guards. When `rely.logic_or` is false (default), all dependencies must pass (AND). When true, any passing is sufficient (OR). A Guard can only depend on Guards with `rep: true` — `rep`
|
|
56
|
+
**The rely is composition**: Up to 4 dependent Guards. When `rely.logic_or` is false (default), all dependencies must pass (AND). When true, any passing is sufficient (OR). A Guard can only depend on Guards with `rep: true` — `rep` indicates the Guard's `repository.data` queries (query 1167) do not depend on runtime submissions, so results are deterministic and the Guard can serve as a dependency. Guards with `rep: false` cannot appear in `rely` lists. Violations are caught by the contract layer at creation time.
|
|
57
|
+
|
|
58
|
+
### Data Source 4 Classification — The Foundation of Guard Semantics
|
|
59
|
+
|
|
60
|
+
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.
|
|
61
|
+
|
|
62
|
+
| Type | Name | SDK Manifestation | Native Opcode | Trust Level | Typical Scenario |
|
|
63
|
+
|------|------|-------------------|---------------|-------------|------------------|
|
|
64
|
+
| **Type 1** | OnChainConstant | `query` + `identifier` (`b_submission: false`, no witness) | TYPE_QUERY + TYPE_CONSTANT | Highest | Query fields of already-published Service/Machine/Reward objects |
|
|
65
|
+
| **Type 2** | WitnessDerived | `query` + `identifier` + `convert_witness` (100-108) | TYPE_QUERY + witness_byte | High (source trusted + deterministic derivation) | Order → Progress query via witness=100 |
|
|
66
|
+
| **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 |
|
|
67
|
+
| **Type 4** | SystemContext | `context` (Signer/Clock/Guard) | TYPE_SIGNER / TYPE_CLOCK / TYPE_GUARD | Highest | Identity verification, time-locks |
|
|
68
|
+
|
|
69
|
+
**Key insights**:
|
|
70
|
+
1. **Type 1 and Type 3 are isomorphic at the native layer** — both use TYPE_QUERY+TYPE_CONSTANT; the only difference is the `b_submission` flag (false vs true)
|
|
71
|
+
2. **Type 2 can overlay on Type 1 or Type 3** — the source object can be a constant (Type 1) or a submission (Type 3), then witness derives the target object
|
|
72
|
+
3. **Type 4 is fully independent** — does not depend on the table
|
|
73
|
+
4. **Except for Type 4, all data must be declared in the table** — the table is the complete data contract between Guard and caller
|
|
74
|
+
|
|
75
|
+
**The table as data contract**: The table declares:
|
|
76
|
+
- **Deterministic data** (`b_submission: false`): type + value, baked at creation, immutable
|
|
77
|
+
- **Submitted data** (`b_submission: true`): type only (1-byte placeholder), value provided by caller at runtime — **must have constraint rules designed, otherwise empty data is meaningless**
|
|
78
|
+
|
|
79
|
+
**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. The semantics are deterministic — 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").
|
|
80
|
+
|
|
81
|
+
### Verifier Constraint Levels — Designing Who Can Pass
|
|
82
|
+
|
|
83
|
+
When a Guard uses `context(Signer)`, the designer chooses how strictly to constrain the verifier's identity. Three levels exist, trading off **security** against **convenience**. Evaluating designer intent and dismantling the semantic execution plan across these levels is a core capability — choose the right level for each scene.
|
|
84
|
+
|
|
85
|
+
#### Level 1 — Strict Single-Identity Binding (avoid unless justified)
|
|
86
|
+
|
|
87
|
+
**Pattern**: `logic_equal[context(Signer), identifier[N](fixed_address)]`
|
|
88
|
+
|
|
89
|
+
- **Maximally secure**: only ONE address can pass
|
|
90
|
+
- **Maximally inconvenient**: if that address is unavailable (key loss, personnel change, rotation), the Guard permanently blocks the operation — Guards are **immutable** after publish
|
|
91
|
+
- **Use only when**: the role is permanently tied to one address AND the designer explicitly accepts the lock-in risk
|
|
92
|
+
- **Risk rule**: `R-C4-04` (info) flags this pattern with a convenience reminder
|
|
93
|
+
- **Example**: `logic_equal[context(Signer), identifier[5](myshop_merchant)]`
|
|
94
|
+
|
|
95
|
+
> ⚠️ **Avoid Level 1 unless you understand the lock-in risk.** Prefer Level 2 (identity-set) or Level 3 (scene-combined) whenever possible.
|
|
96
|
+
|
|
97
|
+
#### Level 2 — Identity-Set Binding (recommended for role-based access)
|
|
98
|
+
|
|
99
|
+
**Pattern**: `logic_or` of multiple identity checks — Signer is ANY of a valid set.
|
|
100
|
+
|
|
101
|
+
**Key semantic insight — Address vs Bool return types**:
|
|
102
|
+
Guard queries that verify identity fall into two categories based on their return type, and this determines how they participate in a `logic_or`:
|
|
103
|
+
|
|
104
|
+
| Return Type | Query Examples | Construction in `logic_or` |
|
|
105
|
+
|-------------|---------------|---------------------------|
|
|
106
|
+
| **Address** | `1562 order.owner`, `1002 permission.owner`, `1488 service.permission` | Must wrap each in `logic_equal[query, context(Signer)]` to produce a Bool |
|
|
107
|
+
| **Bool** (suffix "has") | `1567 order.agent has`, `1004 permission.admin has`, `1006 permission.entity has` | Use **directly** as a `logic_or` child — they already return a verdict; pass `context(Signer)` as a parameter |
|
|
108
|
+
|
|
109
|
+
**Example — Order-holder identity set** (Signer is owner OR agent):
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"type": "logic_or",
|
|
113
|
+
"nodes": [
|
|
114
|
+
{
|
|
115
|
+
"type": "logic_equal",
|
|
116
|
+
"nodes": [
|
|
117
|
+
{"type": "query", "query": 1562, "object": {"identifier": 0}, "parameters": []},
|
|
118
|
+
{"type": "context", "context": "Signer"}
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"type": "query",
|
|
123
|
+
"query": 1567,
|
|
124
|
+
"object": {"identifier": 0},
|
|
125
|
+
"parameters": [{"type": "context", "context": "Signer"}]
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
Here `1562` returns Address → wrapped in `logic_equal`; `1567` returns Bool → used directly with `context(Signer)` as its parameter.
|
|
131
|
+
|
|
132
|
+
**Example — Service-provider identity set** (Signer is permission.owner OR has admin):
|
|
133
|
+
|
|
134
|
+
Three sub-patterns with increasing flexibility:
|
|
135
|
+
|
|
136
|
+
1. **Static permission address** (simplest, breaks if Service rotates permission):
|
|
137
|
+
Query `1002`/`1004` against a table-constant permission address. If the Service changes its permission (like a company changing its board), the Guard must be rebuilt.
|
|
138
|
+
|
|
139
|
+
2. **Dynamic permission address** (survives rotation — **RECOMMENDED**):
|
|
140
|
+
The caller submits a permission address; the Guard verifies `query(1488: service.permission) == submitted_perm`, then checks `1002`/`1004` against the submitted permission. This survives permission rotation without rebuilding the Guard.
|
|
141
|
+
|
|
142
|
+
3. **Repository-based address set** (most flexible, most complex — **only for extreme flexibility needs**):
|
|
143
|
+
The permission address set is stored in a Repository and queried dynamically. This allows runtime configuration of the authorized set, but adds significant complexity.
|
|
144
|
+
|
|
145
|
+
> 💡 **`logic_or` wrapping of the Signer check suppresses R-C4-04** — this is the recommended Level 2 alternative to Level 1 strict binding.
|
|
146
|
+
|
|
147
|
+
#### Level 3 — Scene-Combined Constraint (verify whether Signer binding is even needed)
|
|
148
|
+
|
|
149
|
+
Before adding any Signer binding, evaluate the Guard's usage scene. **Many scenes do not need Signer binding at all** — the scene itself ensures safety through other mechanisms.
|
|
150
|
+
|
|
151
|
+
| Scene | Is Signer binding needed? | Why |
|
|
152
|
+
|-------|--------------------------|-----|
|
|
153
|
+
| Service `order_allocators` + `sharing.who=Entity` | **NO** | Funds flow to a fixed Treasury/address regardless of caller — `sharing.who` already guarantees recipient safety (R-C3-06 safe) |
|
|
154
|
+
| Service `order_allocators` + `sharing.who=Signer` | **YES (Level 2 dynamic)** | Funds flow to caller — must bind Signer to authorized recipient (e.g., `order.owner`) |
|
|
155
|
+
| Machine `forward` guard | **MAYBE** | Forward's `permissionIndex`/`namedOperator` already verifies operator permission; Signer binding only needed if submitted data must belong to operator |
|
|
156
|
+
| Service `buy_guard` | **Usually NO** | The customer is the caller; identity checks via whitelist/credentials suffice |
|
|
157
|
+
| Reward `guard` | **Depends** | If claim is one-time (record count), no Signer binding needed; if claim amount is submitted, bind Signer |
|
|
158
|
+
| Arbitration `voting_guard` | **NO** (weight from query, not Signer) | Weight must come from on-chain EntityRegistrar, not submission (R-C3-02) |
|
|
159
|
+
|
|
160
|
+
**Decision flow**:
|
|
161
|
+
1. Does the scene's host object already verify the operator? (e.g., Machine forward) → Signer binding may be redundant
|
|
162
|
+
2. Does `sharing.who` route funds to a fixed recipient? → Signer binding unnecessary (R-C3-06 safe)
|
|
163
|
+
3. Does the resource flow matter more than who triggers it? → Focus on flow safety, not caller identity
|
|
164
|
+
4. Only if resources flow to the caller AND no other layer verifies identity → add Level 2 Signer binding
|
|
165
|
+
|
|
166
|
+
#### One Guard One Purpose Principle
|
|
167
|
+
|
|
168
|
+
Each Guard should serve **ONE specific purpose**, documented in its `description`:
|
|
169
|
+
- **Scenario**: where the Guard is attached (which host object, which binding field)
|
|
170
|
+
- **Verification rules**: concise statement of what conditions are checked
|
|
171
|
+
- **Risk notes**: which risks are mitigated (R-C3-01/05/06, etc.) and which trade-offs apply
|
|
172
|
+
- **Verifier constraint level**: which Level (1/2/3) is used and why
|
|
173
|
+
|
|
174
|
+
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.
|
|
57
175
|
|
|
58
176
|
### Where Guards Attach in the Ecosystem
|
|
59
177
|
|
|
@@ -138,7 +256,7 @@ The Guard table is the **complete declaration of information** the Guard consume
|
|
|
138
256
|
|-------|---------|---------------|
|
|
139
257
|
| `identifier` | Unique index (0–255). The computation tree uses this number to reference the entry. | Always |
|
|
140
258
|
| `b_submission` | Whether the **caller** must provide this value at runtime. `true` = runtime submission; `false` = pre-set constant. | Always |
|
|
141
|
-
| `value_type` | The type of the value: Bool, Address, String, U8–U256, or vector types.
|
|
259
|
+
| `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 |
|
|
142
260
|
| `value` | The constant value when `b_submission` is false; a placeholder when `b_submission` is true. | When `b_submission` is false |
|
|
143
261
|
| `name` | Human-readable label describing what this entry represents. | Always |
|
|
144
262
|
|
|
@@ -148,26 +266,43 @@ The Guard table is the **complete declaration of information** the Guard consume
|
|
|
148
266
|
- **No duplicate identifiers.** Each index number must appear exactly once.
|
|
149
267
|
- **Non-submission entries must have a value.** These are baked into the Guard immutably.
|
|
150
268
|
- **Submission entries use placeholder values.** The actual value is provided by the caller at runtime.
|
|
151
|
-
- **Query target objects must be of type Address in the table.**
|
|
269
|
+
- **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.).
|
|
152
270
|
- **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.
|
|
153
271
|
- **Maximum 256 table entries** (identifiers 0–255). The total serialized table size must not exceed 40000 bytes.
|
|
154
272
|
- **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.
|
|
155
273
|
|
|
156
274
|
### The convert_witness Mechanism
|
|
157
275
|
|
|
158
|
-
`convert_witness` transforms a
|
|
276
|
+
`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.
|
|
159
277
|
|
|
160
|
-
**Core principle**: Caller submits what they have (e.g., Order ID); Guard queries what it needs (e.g., Progress state) via witness conversion.
|
|
278
|
+
**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.
|
|
161
279
|
|
|
162
280
|
**Rules**:
|
|
163
281
|
- Witness type encodes source→target transformation
|
|
164
|
-
- Table entry's `object_type` must match witness source type
|
|
282
|
+
- Table entry's `object_type` (auto-filled by SDK) must match witness source type
|
|
165
283
|
- Query instruction's object type must match witness target type
|
|
166
284
|
- Type mismatches cause Guard creation to fail
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
285
|
+
- Multi-hop witnesses (106-108) require intermediate objects to exist
|
|
286
|
+
|
|
287
|
+
**Complete 9 witness types** (defined in `guard.rs#L34-L65`):
|
|
288
|
+
|
|
289
|
+
| Code | Name | Source → Target | Derivation | Hops |
|
|
290
|
+
|------|------|-----------------|------------|------|
|
|
291
|
+
| 100 | TypeOrderProgress | Order → Progress | read order.progress field | 1 |
|
|
292
|
+
| 101 | TypeOrderMachine | Order → Machine | read order.machine field | 1 |
|
|
293
|
+
| 102 | TypeOrderService | Order → Service | read order.service field | 1 |
|
|
294
|
+
| 103 | TypeProgressMachine | Progress → Machine | read progress.machine field | 1 |
|
|
295
|
+
| 104 | TypeArbOrder | Arb → Order | read arb.order field | 1 |
|
|
296
|
+
| 105 | TypeArbArbitration | Arb → Arbitration | read arb.arbitration field | 1 |
|
|
297
|
+
| 106 | TypeArbProgress | Arb → Progress | arb.order → order.progress | 2 (multi-hop) |
|
|
298
|
+
| 107 | TypeArbMachine | Arb → Machine | arb.order → order.machine | 2 (multi-hop) |
|
|
299
|
+
| 108 | TypeArbService | Arb → Service | arb.order → order.service | 2 (multi-hop) |
|
|
300
|
+
|
|
301
|
+
**Key notes**:
|
|
302
|
+
- **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.
|
|
303
|
+
- **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.
|
|
304
|
+
- **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.
|
|
305
|
+
- **Available witness types** are also discoverable via `wowok_buildin_info` with info `"guard instructions"`.
|
|
171
306
|
|
|
172
307
|
---
|
|
173
308
|
|
|
@@ -281,6 +416,8 @@ Machine's forward `guard` validates state transitions. If `retained_submission`
|
|
|
281
416
|
|
|
282
417
|
Repository's `write_guard` validates writes. If `id_from_submission` is set, reads entity ID from that submission index — **the index must be Address type**. If `data_from_submission` is set, reads data value from that index — **the value type must match the Repository's declared value_type**. Guard table must include entries for the data being extracted.
|
|
283
418
|
|
|
419
|
+
**Important — 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.
|
|
420
|
+
|
|
284
421
|
---
|
|
285
422
|
|
|
286
423
|
**Key Principle**: 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.
|
|
@@ -303,25 +440,185 @@ Each object extracts Guard data with precise type expectations. Mismatches cause
|
|
|
303
440
|
|
|
304
441
|
### Common Pitfalls
|
|
305
442
|
|
|
306
|
-
|
|
443
|
+
> 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`.
|
|
444
|
+
|
|
445
|
+
#### Creation-Phase Constraints (22 items, enforced by SDK + native `validate_guard_data`)
|
|
446
|
+
|
|
447
|
+
**Root (1 item)**
|
|
448
|
+
|
|
449
|
+
1. **ROOT_01 — Root must return Bool**: The outermost node of the tree must produce Bool. Logic and comparison nodes return Bool; arithmetic, conversion, and string operation nodes do not. Ensure your tree terminates at a logic or comparison node — the creation validation will reject non-Bool roots.
|
|
450
|
+
|
|
451
|
+
**Table (5 items)**
|
|
452
|
+
|
|
453
|
+
2. **TABLE_01 — Identifier uniqueness**: Every `identifier` in the table must be unique (0–255). Duplicate identifiers cause creation failure. SDK uses `lodash.groupBy` to detect duplicates.
|
|
454
|
+
|
|
455
|
+
3. **TABLE_02 — Identifier referential integrity**: Every `identifier` node in the computation tree must match an entry in the table. Missing entries cause creation failure — validate your tree against your table before submitting.
|
|
456
|
+
|
|
457
|
+
4. **TABLE_03 — Constant value non-empty**: When `b_submission=false`, the `value` field must be non-empty. These values are baked into the Guard immutably at creation time.
|
|
458
|
+
|
|
459
|
+
5. **TABLE_04 — Table size limits**: Maximum 256 table entries (identifiers 0–255), and the total serialized table size must not exceed 40000 bytes (BCS). Enforced by Move `guard::new`.
|
|
460
|
+
|
|
461
|
+
6. **TABLE_05 — Submission value is 1-byte type code**: When `b_submission=true`, the `value` field is only a 1-byte type code placeholder (the actual value is provided at runtime). Enforced by native `deserialize_constants`.
|
|
462
|
+
|
|
463
|
+
**Query (4 items)**
|
|
464
|
+
|
|
465
|
+
7. **QUERY_01 — Query instruction ID valid**: Query instruction IDs are system-defined. Always discover them through `wowok_buildin_info` with info `"guard instructions"`. Invalid IDs cause creation failure.
|
|
466
|
+
|
|
467
|
+
8. **QUERY_02 — Parameter count matches**: The parameter count and types in your query node must match the instruction exactly — off-by-one parameter counts are a common failure.
|
|
468
|
+
|
|
469
|
+
9. **QUERY_03 — Return type compatible**: The query node's return type must be compatible with the parent node's expected input. A `logic_equal` comparing a String to a U64 fails validation. Use explicit conversion nodes (`convert_string_number`, `convert_number_string`) when types differ. Numeric comparisons use `logic_as_u256_*` variants which auto-widen to U256.
|
|
470
|
+
|
|
471
|
+
10. **QUERY_04 — Object identifier is Address type**: The table entry referenced by a query node's `object.identifier` must have `value_type: Address`. SDK `buildNode` validates this at L603-609.
|
|
472
|
+
|
|
473
|
+
**Witness (3 items)**
|
|
474
|
+
|
|
475
|
+
11. **WITNESS_01 — Witness type valid (100-108)**: The `convert_witness` value must be one of the 9 valid witness types (100-108). Invalid witness types cause creation failure.
|
|
476
|
+
|
|
477
|
+
12. **WITNESS_02 — Witness target matches query objectType**: The witness's target object type must match the query instruction's expected object type. For example, `TypeOrderProgress` (100) derives a Progress, so the query must target a Progress object. SDK `buildNode` validates this at L613-619.
|
|
307
478
|
|
|
308
|
-
|
|
479
|
+
13. **WITNESS_03 — Witness source matches table object_type**: If the table entry has an `object_type` declared (auto-filled by SDK), it must match the witness's source object type. For example, `TypeOrderProgress` (100) expects an Order source, so the table entry's `object_type` must be Order. SDK `buildNode` validates this at L622-631. **Missing convert_witness** is a related failure: when accessing Progress data from an Order ID, the query node needs `convert_witness` with the appropriate witness type. Without it, the runtime looks for a Progress at the Order's address — which does not exist as a Progress object.
|
|
309
480
|
|
|
310
|
-
3
|
|
481
|
+
**Rely (3 items)**
|
|
311
482
|
|
|
312
|
-
|
|
483
|
+
14. **RELY_01 — Dependency count ≤ 4**: A Guard can depend on at most 4 other Guards (`MAX_DEPENDED_COUNT`). SDK `reliesAdd` enforces this limit.
|
|
313
484
|
|
|
314
|
-
|
|
485
|
+
15. **RELY_02 — Dependencies must have rep=true**: A Guard's `rely` entries must reference Guards with `rep: true` — meaning their `repository.data` queries do not depend on runtime submissions. Guards that depend on a Repository via submitted addresses (`rep: false`) cannot serve as dependencies. Move `guard::relies_add` catches violations at creation time.
|
|
315
486
|
|
|
316
|
-
|
|
487
|
+
16. **RELY_03 — No self-reference**: A Guard cannot depend on itself. SDK `reliesAdd` prevents self-referential rely entries.
|
|
317
488
|
|
|
318
|
-
|
|
489
|
+
**Binding (3 items)**
|
|
319
490
|
|
|
320
|
-
|
|
491
|
+
17. **BINDING_01 — voting_guard GuardIdentifier must be numeric**: When using `GuardIdentifier` for Arbitration `voting_guard`, the referenced table entry must have a numeric `value_type` (U8–U256). The system checks this when the VotingGuard is added to the Arbitration — if the identifier does not exist or is non-numeric, the operation reverts with `E_GUARD_IDENTIFIER_NOT_NUMBER`.
|
|
321
492
|
|
|
322
|
-
|
|
493
|
+
18. **BINDING_02 — Repository id_from_submission must be Address**: When a Repository `write_guard` uses `id_from_submission`, the referenced table entry must have `value_type: Address`. Move Repository enforces this at binding time.
|
|
494
|
+
|
|
495
|
+
19. **BINDING_03 — Repository data_from_submission type must match**: When a Repository `write_guard` uses `data_from_submission`, the referenced table entry's `value_type` must match the Repository's declared `value_type`. Move Repository enforces this at binding time.
|
|
496
|
+
|
|
497
|
+
**Input (2 items)**
|
|
498
|
+
|
|
499
|
+
20. **INPUT_01 — Root bytecode non-empty**: The serialized root computation tree must not be empty. SDK `newGuard` validates this before submission.
|
|
500
|
+
|
|
501
|
+
21. **INPUT_02 — Root bytecode size ≤ MAX_INPUT_SIZE**: The serialized root computation tree must not exceed `MAX_INPUT_SIZE`. SDK `newGuard` validates this before submission.
|
|
502
|
+
|
|
503
|
+
**Immutable (1 item)**
|
|
504
|
+
|
|
505
|
+
22. **IMMUTABLE_01 — Guard becomes immutable after creation**: Once `guard::create` is called, the Guard's `immutable` flag is set to `true` and no further modifications are possible. This is the foundation of the immutability contract — to change a Guard, export via `guard2file`, create a new Guard, and update all references.
|
|
506
|
+
|
|
507
|
+
#### Practical Tips (not strict constraints, but strongly recommended)
|
|
508
|
+
|
|
509
|
+
- **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.
|
|
510
|
+
- **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.
|
|
511
|
+
- **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.
|
|
512
|
+
|
|
513
|
+
#### Runtime-Phase Constraints (enforced by native `verify_guard`)
|
|
514
|
+
|
|
515
|
+
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:
|
|
516
|
+
|
|
517
|
+
- **RUN_IMMUTABLE_01**: Guard must have `immutable=true` to be verified. A Guard that hasn't been finalized via `guard::create` cannot be used.
|
|
518
|
+
- **RUN_SUB_01**: The submission's `value[0]` (type byte) must match the table declaration. A submission with the wrong type byte is rejected.
|
|
519
|
+
- **RUN_SUB_02**: Every table entry with `b_submission=true` must have a corresponding submission value. Missing submissions cause failure.
|
|
520
|
+
- **RUN_SUB_03**: Total submission bytes must be ≤ `MAX_SUBMISSION_SIZE` (256).
|
|
521
|
+
- **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.
|
|
522
|
+
- **RUN_QUERY_01**: The query target object must exist and its type must match.
|
|
523
|
+
- **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`.
|
|
524
|
+
- **RUN_TX_01**: The Passport's `tx_hash` must match the current transaction.
|
|
525
|
+
- **RUN_RELY_01**: Rely guards must have been added to the passport.
|
|
526
|
+
|
|
527
|
+
### Readability Conventions — Prefer String Names Over Numeric IDs
|
|
528
|
+
|
|
529
|
+
> **Built-in MCP convention**: Whenever the schema accepts BOTH a numeric ID and a human-readable string name for the same field, **always prefer the string name** in examples, documentation, and AI-generated JSON. Numeric IDs are accepted for backward compatibility and compact machine output, but string names are the canonical form for any human-readable artifact.
|
|
530
|
+
|
|
531
|
+
This convention is grounded in the user-experience principle: *"Wherever the interface supports it, optimize from the user's understanding as the starting point."* A reader who sees `"query": "order.service"` understands the intent immediately; `"query": 1563` requires an extra lookup against the `GUARDQUERY` registry.
|
|
532
|
+
|
|
533
|
+
#### Fields Affected
|
|
534
|
+
|
|
535
|
+
| Field | String form (preferred) | Numeric form (avoid) | Source of truth |
|
|
536
|
+
|-------|-------------------------|----------------------|-----------------|
|
|
537
|
+
| `query` | `"order.service"` | `1563` | `GUARDQUERY` registry (375 entries, exported by `@wowok/wowok`) |
|
|
538
|
+
| `value_type` | `"Address"`, `"U64"`, `"String"` | `1`, `6`, `2` | `ValueType` enum |
|
|
539
|
+
| `context` | `"Signer"`, `"Clock"`, `"Guard"` | (numeric not accepted) | `Context` enum |
|
|
540
|
+
| `convert_witness` | `"OrderProgress"`, `"OrderMachine"` | `100`, `101` | `WitnessType` enum (SDK `parseWitnessType` / `witnessTypeToString`) |
|
|
541
|
+
|
|
542
|
+
#### Why String Names
|
|
543
|
+
|
|
544
|
+
1. **Self-documenting JSON** — `"query": "order.service"` reads as intent; `1563` is opaque.
|
|
545
|
+
2. **Stable across registry renumbering** — names are part of the public contract; numeric IDs are an implementation detail.
|
|
546
|
+
3. **Case-insensitive matching** — `isValidGuardQueryName` lowercases both sides, so `"Order.Service"` and `"order.service"` are equivalent. Use the canonical lowercase form in examples.
|
|
547
|
+
4. **Lint-friendly** — string names survive refactoring; numeric IDs silently rot when an ID is deprecated.
|
|
548
|
+
|
|
549
|
+
#### Canonical Name Examples
|
|
550
|
+
|
|
551
|
+
The most common queries used in allocator Guards (R-C3-05 / R-C3-06 protection):
|
|
552
|
+
|
|
553
|
+
| Numeric ID | Canonical name | Returns | Typical use |
|
|
554
|
+
|------------|----------------|---------|-------------|
|
|
555
|
+
| `1253` | `progress.current` | String | Verify order is at a named node (with `convert_witness: 100`) |
|
|
556
|
+
| `1563` | `order.service` | Address | Cross-service theft protection (R-C3-05) |
|
|
557
|
+
| `1562` | `order.owner` | Address | Signer binding for refunds (R-C3-06 Level 2 dynamic) |
|
|
558
|
+
| `1272` | `progress.current_time` | U64 | Time-lock comparisons |
|
|
559
|
+
| `1488` | `service.permission` | Address | Verify Service's Permission object |
|
|
560
|
+
| `1002` | `permission.owner` | Address | Signer == permission owner (Level 1 fixed) |
|
|
561
|
+
| `1004` | `permission.admin has` | Bool | Caller is in admin set (Level 2 identity-set) |
|
|
562
|
+
| `1567` | `order.agent has` | Bool | Verify caller is an order agent |
|
|
563
|
+
|
|
564
|
+
#### WitnessType String Names
|
|
565
|
+
|
|
566
|
+
The `convert_witness` field accepts both numeric IDs (100-108) and string names. String matching is case-insensitive and accepts both the full enum name (`"TypeOrderProgress"`) and the shortened form without the `"Type"` prefix (`"OrderProgress"`). The shortened form is preferred for readability.
|
|
567
|
+
|
|
568
|
+
| Numeric ID | Shortened name (preferred) | Full enum name | Source → Target |
|
|
569
|
+
|------------|----------------------------|----------------|-----------------|
|
|
570
|
+
| `100` | `"OrderProgress"` | `"TypeOrderProgress"` | Order → Progress |
|
|
571
|
+
| `101` | `"OrderMachine"` | `"TypeOrderMachine"` | Order → Machine |
|
|
572
|
+
| `102` | `"OrderService"` | `"TypeOrderService"` | Order → Service |
|
|
573
|
+
| `103` | `"ProgressMachine"` | `"TypeProgressMachine"` | Progress → Machine |
|
|
574
|
+
| `104` | `"ArbOrder"` | `"TypeArbOrder"` | Arb → Order |
|
|
575
|
+
| `105` | `"ArbArbitration"` | `"TypeArbArbitration"` | Arb → Arbitration |
|
|
576
|
+
| `106` | `"ArbProgress"` | `"TypeArbProgress"` | Arb → Progress |
|
|
577
|
+
| `107` | `"ArbMachine"` | `"TypeArbMachine"` | Arb → Machine |
|
|
578
|
+
| `108` | `"ArbService"` | `"TypeArbService"` | Arb → Service |
|
|
579
|
+
|
|
580
|
+
SDK functions: `parseWitnessType(input)` converts string→number; `witnessTypeToString(type)` converts number→string (returns the shortened form). The SDK's Guard deserialization (`parseQueryNode`) already returns `convert_witness` as a string name.
|
|
581
|
+
|
|
582
|
+
#### Deserialization Output — All Protocol Constants Return Strings
|
|
583
|
+
|
|
584
|
+
The SDK's Guard deserialization (`query_objects` / `guard2file`) returns ALL protocol constants as user-friendly strings, not numeric IDs:
|
|
585
|
+
|
|
586
|
+
| Field | Returned as | Example | Conversion function |
|
|
587
|
+
|-------|-------------|---------|---------------------|
|
|
588
|
+
| `query` | String name | `"order.service"` | `GUARDQUERY` registry lookup |
|
|
589
|
+
| `value_type` | String name | `"Address"`, `"U64"` | `valueTypeToString()` |
|
|
590
|
+
| `convert_witness` | String name | `"OrderProgress"` | `witnessTypeToString()` |
|
|
591
|
+
| `context` | String literal | `"Signer"`, `"Clock"`, `"Guard"` | Hardcoded string |
|
|
592
|
+
| `object_type` | String name | `"Order"`, `"Service"` | `numberToObjectType` mapping |
|
|
593
|
+
| `type` (node discriminator) | String literal | `"logic_and"`, `"query"` | Always string |
|
|
594
|
+
|
|
595
|
+
This means `guard2file` JSON/Markdown output and `query_objects` results are fully self-documenting without requiring numeric ID lookups.
|
|
596
|
+
|
|
597
|
+
#### Example — Before and After
|
|
598
|
+
|
|
599
|
+
**Before (numeric, opaque):**
|
|
600
|
+
```json
|
|
601
|
+
{
|
|
602
|
+
"type": "logic_equal",
|
|
603
|
+
"nodes": [
|
|
604
|
+
{"type": "query", "query": 1563, "object": {"identifier": 0, "convert_witness": 100}, "parameters": []},
|
|
605
|
+
{"type": "identifier", "identifier": 2}
|
|
606
|
+
]
|
|
607
|
+
}
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
**After (string, self-documenting):**
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"type": "logic_equal",
|
|
614
|
+
"nodes": [
|
|
615
|
+
{"type": "query", "query": "order.service", "object": {"identifier": 0, "convert_witness": "OrderProgress"}, "parameters": []},
|
|
616
|
+
{"type": "identifier", "identifier": 2}
|
|
617
|
+
]
|
|
618
|
+
}
|
|
619
|
+
```
|
|
323
620
|
|
|
324
|
-
|
|
621
|
+
Both forms are schema-valid and produce identical on-chain behavior; the string form is preferred for all human-readable output.
|
|
325
622
|
|
|
326
623
|
---
|
|
327
624
|
|
package/wowok-machine/SKILL.md
CHANGED
|
@@ -36,6 +36,78 @@ Design, build, and operate automated workflow templates.
|
|
|
36
36
|
|
|
37
37
|
Machines are **immutable after `publish: true`**; Guards are **CREATE-only**. Design the complete workflow before publishing.
|
|
38
38
|
|
|
39
|
+
## Semantic Layer (Knowledge Modules)
|
|
40
|
+
|
|
41
|
+
The Machine knowledge system provides 8 TypeScript modules that encode the canonical design knowledge for Machine generation. These modules power the L4 Harness Plan Loop and are the **source of truth** for scene/template selection, pattern matching, risk evaluation, topology analysis, and the fail-closed publish gate.
|
|
42
|
+
|
|
43
|
+
> **Design reference**: [Machine Design Reference](../references/machine-design-reference.md) — covering architecture, core elements, puzzle model, translation patterns, risk rules, and publish flow. Verification notes (06 §14) are embedded in code as `MACHINE_VERIFICATION_NOTES`.
|
|
44
|
+
|
|
45
|
+
### Module API Reference
|
|
46
|
+
|
|
47
|
+
| Module | Entry Points | Purpose |
|
|
48
|
+
|--------|--------------|---------|
|
|
49
|
+
| `machine-ledger.ts` | `MACHINE_SCENES`, `inferSceneFromFlow(flow)`, `findScene(id)` | 10 industry scenes + keyword-based scene inference |
|
|
50
|
+
| `machine-templates.ts` | `MACHINE_TEMPLATES`, `getTemplateById(id)`, `fillTemplate(id, params)`, `forkTemplate(id, overrides)`, `walkDecisionTree(answers)` | 10 starter templates + parameter fill + Q1-Q9 decision tree |
|
|
51
|
+
| `machine-translation.ts` | `SEMANTIC_TO_MACHINE_RULES`, `MACHINE_CONSTRAINT_RULES`, `matchSemanticPattern(text)`, `identifyExecutionMode(threshold, fwdCount)`, `identifyTopology(pairs)`, `explainForwardPermission(fwd)` | 12 semantic patterns + 28 on-chain constraints (creation/structure/node_operation/publish/runtime) |
|
|
52
|
+
| `machine-topology.ts` | `analyzeTopology(nodes, pairs)`, `identifyTopologyPattern(topology)`, `formatTopologySummary(topology)`, `MAX_NODE_COUNT=200`, `MAX_NODE_PAIR_COUNT=40`, `MAX_FORWARD_COUNT=20` | DAG analysis: entry/terminal/orphaned/dead-branch/cycle, on-chain limit checks |
|
|
53
|
+
| `machine-risk.ts` | `MACHINE_RISK_RULES` (39 rules), `assessMachineRisks(machine, ctx)`, `getRiskRulesByCategory(cat)`, `getRiskSummary(assessment)` | 5 risk dimensions (R-M1 structural, R-M2 guard, R-M3 permission, R-M4 immutability, R-M5 environment) |
|
|
54
|
+
| `machine-puzzle.ts` | `initPuzzleFromIntent(intent)`, `initPuzzleFromTemplate(id)`, `checkPuzzleCompleteness(puzzle)`, `recommendNextStep(puzzle)`, `derivePuzzleFromMachineJson(json)`, `generateConfirmationText(puzzle)` | 8-dimension information model (A-H): business_flow, node_design, guard_acceptance, permission_model, threshold_weight, branch_topology, overall_objective, risk_assessment |
|
|
55
|
+
| `machine-confirm.ts` | `PUBLISH_CHECKLIST` (10 items C-01..C-10), `CONFIRMATION_QUESTIONS` (6 items Q1-Q6), `runStaticChecklist(machine)`, `integrateRiskAssessment(machine, ctx)`, `parseUserConfirmation(input)`, `verifyEnvironment(machine, ctx, isMainnet)`, `confirmPublish(machine, ctx, isMainnet, userInput)`, `progressiveCheck(machine, scope)`, `overrideBlockWithReason(result, reason)`, `generateFullConfirmationText(...)` | 4-layer fail-closed gate: checklist → risk → user → environment. **CRITICAL risks cannot be overridden.** |
|
|
56
|
+
| `machine-context.ts` | `injectMachineContext(intent, mode, options)`, `suggestWorkflow(intent)`, `buildPromptFromContext(bundle)`, `getMachineKnowledgeSnapshot()`, `getRelatedGuardScenes(sceneId)`, `getVerificationNotes(mode?)`, `CONTEXT_PRESETS.{newUserIntent, midDesignReview, prePublishFull, crossMachine, guardIntegration, allocationIntegration}` | Aggregator module. **8 injection modes** select curated subset of knowledge based on user task. Use this as the main entry point. |
|
|
57
|
+
|
|
58
|
+
### The 8 Context Injection Modes
|
|
59
|
+
|
|
60
|
+
The user's current task determines what context to surface (never dump everything):
|
|
61
|
+
|
|
62
|
+
| Mode | When to Use | What It Surfaces |
|
|
63
|
+
|------|-------------|------------------|
|
|
64
|
+
| `initial_design` | User just described intent | Matched scene + top 3 templates + initial puzzle |
|
|
65
|
+
| `mid_design_review` | User is mid-design | Missing puzzle dimensions + structure/node_op constraints |
|
|
66
|
+
| `pre_publish_check` | User about to publish | Full risk assessment + progressive checklist (R5 subset) + verification notes |
|
|
67
|
+
| `post_publish_runtime` | Machine already published | Runtime-only constraints + environment risks |
|
|
68
|
+
| `intent_match` | Lightweight intent inference | Top 2 templates + creation constraints (no verification notes) |
|
|
69
|
+
| `cross_machine` | Cross-Machine dependency design | P-M-CROSS-MACHINE pattern + structure/publish constraints |
|
|
70
|
+
| `guard_integration` | Deep-dive on Guard binding | Guard bridge to `guard-ledger.ts` + R-M2 risks |
|
|
71
|
+
| `allocation_integration` | Deep-dive on Allocator at terminals | P-M-ALLOCATION-INTEGRATED pattern + Allocator-related risks |
|
|
72
|
+
|
|
73
|
+
### Recommended Usage Pattern
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
// 1. First user message — lightweight workflow suggestion
|
|
77
|
+
const suggestion = suggestWorkflow(userIntent);
|
|
78
|
+
// suggestion.matched_scene, suggestion.suggested_templates, suggestion.initial_puzzle, suggestion.next_step
|
|
79
|
+
|
|
80
|
+
// 2. Mid-design — inject context to surface missing info
|
|
81
|
+
const bundle = injectMachineContext(intent, 'mid_design_review', { existing_puzzle: puzzle });
|
|
82
|
+
|
|
83
|
+
// 3. Pre-publish — full ConfirmGate
|
|
84
|
+
const gate = confirmPublish(machineJson, projectContext, isMainnet, userConfirmationInput);
|
|
85
|
+
if (gate.status !== 'approved') {
|
|
86
|
+
// STOP — do not call onchain_operations with publish: true
|
|
87
|
+
console.log(gate.block_reason);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 4. Or use the preset wrapper for common cases
|
|
91
|
+
const preset = CONTEXT_PRESETS.prePublishFull(intent, machineJson, projectContext);
|
|
92
|
+
const prompt = buildPromptFromContext(preset); // inject into AI prompt
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Verification Notes (06 §14)
|
|
96
|
+
|
|
97
|
+
Five topics were verified at the code level. These notes are embedded in `MACHINE_VERIFICATION_NOTES` and surfaced whenever the consumer's mode touches them:
|
|
98
|
+
|
|
99
|
+
| Topic | Judgment | Verified Semantic |
|
|
100
|
+
|-------|----------|-------------------|
|
|
101
|
+
| `bReplace` | YES | Acts at pairs-list level of an existing node (NOT whole-Machine). `false`=merge, `true`=replace that node's pairs list. |
|
|
102
|
+
| `um` | PARTIAL | Messenger Contact reference (off-chain metadata). Omitting has zero on-chain impact. |
|
|
103
|
+
| `consensus_repositories` | PARTIAL | Declarative-only field. Not consumed by on-chain flow. Hint for off-chain tooling. |
|
|
104
|
+
| `namedOperator_assignment` | PARTIAL | Move `progress::new` does NOT accept namedOperator. MCP/SDK translates to separate `namedOperator_add` tx (permission index 221). |
|
|
105
|
+
| `cross_machine_dependency` | PARTIAL | Indirect via `progress::new -> assert_published`. Use `convert_witness` TypeOrderProgress (100) / TypeOrderProgressSession (103). |
|
|
106
|
+
|
|
107
|
+
> **Test coverage**: 8 `.spec.ts` files covering machine-ledger, machine-templates, machine-translation, machine-topology, machine-risk, machine-puzzle, machine-confirm, and machine-context — over 400 tests, all passing.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
39
111
|
## Machine Architecture
|
|
40
112
|
|
|
41
113
|
```
|