@crediolabs/policy-synth 0.1.2 → 0.1.3

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,7 +1,7 @@
1
- // src/adapters/oz/adapter.ts - the OZ Accounts CustodyAdapter (Path-A lowering).
1
+ // src/adapters/oz/adapter.ts - the OZ Accounts CustodyAdapter.
2
2
  //
3
3
  // Compiles a PolicyIR to an OZ `ProposedPolicy` using OZ built-in policy
4
- // primitives (Path A). Only the constructs OZ can express natively are lowered:
4
+ // primitives. Only the constructs OZ can express natively are lowered:
5
5
  // scope.contract -> ContextRuleType.call_contract (else default)
6
6
  // expiry.validUntilLedger -> ContextRuleDraft.validUntilLedger
7
7
  // window_spent(t,w) <= L -> `spending_limit` primitive
@@ -9,7 +9,7 @@
9
9
  // Anything needing a capability this backend lacks (oracle price, invocation
10
10
  // count, per-arg comparison/allowlist, guard, nested boolean predicate) is NOT
11
11
  // emitted: it is named in `uncovered` and `covered` is set false. Nothing is
12
- // silently dropped - the uncovered constructs are the Path-B markers.
12
+ // silently dropped - the uncovered constructs are named instead.
13
13
  //
14
14
  // OZ built-in policy instance addresses are per-network deploy artifacts we do
15
15
  // not have yet (install is a later phase). They are injected via config; week-1
@@ -84,8 +84,8 @@ function lowerRule(rule, config) {
84
84
  const uncovered = [];
85
85
  const policyRefs = [];
86
86
  // scope -> context rule type. OZ scopes by contract (CallContract); a finer
87
- // method-level restriction is a predicate concern and must be flagged Path-B
88
- // because CallContract alone permits other methods on the same contract
87
+ // method-level restriction is a predicate concern and must be flagged as not
88
+ // covered because CallContract alone permits other methods on the same contract
89
89
  // (e.g. an unbounded approve alongside a capped transfer).
90
90
  const contextRuleType = rule.scope.contract !== undefined
91
91
  ? { kind: 'call_contract', contract: rule.scope.contract }
@@ -110,11 +110,11 @@ function lowerRule(rule, config) {
110
110
  uncovered.push('time expiry given as a unix timestamp (OZ context rules expire by ledger sequence; supply expiry.validUntilLedger)');
111
111
  }
112
112
  }
113
- // guard -> Path B (applicability predicates are not an OZ built-in).
113
+ // guard -> not covered (applicability predicates are not an OZ built-in).
114
114
  if (rule.guard) {
115
115
  uncovered.push(`guard: ${describeCondition(rule.guard)}`);
116
116
  }
117
- // constraints -> spending_limit where they match; else Path B. The OZ
117
+ // constraints -> spending_limit where they match; else not covered. The OZ
118
118
  // spending_limit policy takes `{ spending_limit: i128, period_ledgers: u32 }`
119
119
  // and has NO token param: it only accepts a CallContract context rule
120
120
  // (OnlyCallContractAllowed) and limits transfers of that context's contract,
@@ -144,7 +144,7 @@ function lowerRule(rule, config) {
144
144
  }
145
145
  // approval.threshold -> simple/weighted threshold primitive. A threshold < 1
146
146
  // is not a real M-of-N gate (0 approvals authorises everything), so refuse to
147
- // emit a no-op primitive and flag it Path-B instead.
147
+ // emit a no-op primitive and flag it as not covered instead.
148
148
  if (rule.approval) {
149
149
  if (!Number.isInteger(rule.approval.threshold) || rule.approval.threshold < 1) {
150
150
  uncovered.push(`approval threshold ${rule.approval.threshold} is not a positive integer (a 0 or negative threshold is not an M-of-N gate)`);
@@ -203,17 +203,17 @@ function matchSpendingLimit(c) {
203
203
  return null;
204
204
  return { token: selector.token, limit: value, windowSeconds: selector.windowSeconds };
205
205
  }
206
- /** Human-readable descriptor for a construct the OZ Path-A backend cannot
207
- * express, used to populate `uncovered` (the Path-B markers). */
206
+ /** Human-readable descriptor for a construct the OZ built-in-primitive backend
207
+ * cannot express, used to populate `uncovered`. */
208
208
  function describeCondition(cond) {
209
209
  switch (cond.op) {
210
210
  case 'in':
211
- return `value allowlist on ${describeSelector(cond.selector)} (arg allowlist, Path B)`;
211
+ return `value allowlist on ${describeSelector(cond.selector)} (arg allowlist)`;
212
212
  case 'not':
213
- return 'negated condition (predicate DSL, Path B)';
213
+ return 'negated condition (predicate DSL)';
214
214
  case 'and':
215
215
  case 'or':
216
- return `nested ${cond.op} condition (predicate DSL, Path B)`;
216
+ return `nested ${cond.op} condition (predicate DSL)`;
217
217
  case 'compare': {
218
218
  const s = cond.compare.selector;
219
219
  switch (s.kind) {
@@ -224,16 +224,16 @@ function describeCondition(cond) {
224
224
  case 'window_spent':
225
225
  return `spend-window comparison with operator '${cond.compare.operator}' (only 'lte' lowers to spending_limit)`;
226
226
  case 'amount':
227
- return `per-call amount comparison on ${s.token} (predicate DSL, Path B)`;
227
+ return `per-call amount comparison on ${s.token} (predicate DSL)`;
228
228
  case 'arg':
229
- return `argument comparison on arg ${s.argIndex} (predicate DSL, Path B)`;
229
+ return `argument comparison on arg ${s.argIndex} (predicate DSL)`;
230
230
  case 'calldata':
231
- return 'EVM calldata comparison (predicate DSL, Path B)';
231
+ return 'EVM calldata comparison (predicate DSL)';
232
232
  case 'value':
233
- return 'tx.value comparison (predicate DSL, Path B)';
233
+ return 'tx.value comparison (predicate DSL)';
234
234
  case 'now':
235
235
  case 'valid_until':
236
- return 'time comparison (predicate DSL, Path B)';
236
+ return 'time comparison (predicate DSL)';
237
237
  }
238
238
  }
239
239
  }
@@ -6,14 +6,14 @@
6
6
  // contract/method -> rule.scope
7
7
  // spendingLimit -> a `window_spent(token,window) <= limit` compare
8
8
  // approvalThreshold-> rule.approval.threshold
9
- // recipients -> an `in` condition on the recipient arg (Path-B flagged
10
- // by the OZ adapter; that is expected)
9
+ // recipients -> an `in` condition on the recipient arg (flagged as not
10
+ // covered by the OZ adapter; that is expected)
11
11
  // expiry -> rule.expiry
12
12
  // The top-level default is `deny_all` (OZ context rules are deny-by-default).
13
13
  /** Arg index the recipient allowlist constrains. Pinned to the SEP-41
14
14
  * `transfer(from, to, amount)` convention where `to` is arg 1. This condition
15
- * is flagged Path-B by the OZ adapter (no built-in primitive expresses an arg
16
- * allowlist), so the index only needs to be deterministic in week-1. */
15
+ * is flagged as not covered by the OZ adapter (no built-in primitive expresses
16
+ * an arg allowlist), so the index only needs to be deterministic in week-1. */
17
17
  const RECIPIENT_ARG_INDEX = 1;
18
18
  export function mandateToPolicyIR(spec) {
19
19
  const scope = { contract: spec.contract };
@@ -10,7 +10,7 @@ export interface MandateSpec {
10
10
  };
11
11
  /** -> OZ `simple_threshold` / `weighted_threshold` primitive. */
12
12
  approvalThreshold?: number;
13
- /** Recipient allowlist -> an `in` arg condition (flagged Path-B in week-1). */
13
+ /** Recipient allowlist -> an `in` arg condition (not covered by an OZ built-in). */
14
14
  recipients?: string[];
15
15
  /** -> OZ context-rule expiry. */
16
16
  expiry?: {
@@ -36,7 +36,7 @@ export interface CustodyCapabilities {
36
36
  export interface CompileResult {
37
37
  /** false => some IR construct this backend cannot express (see `uncovered`). */
38
38
  covered: boolean;
39
- /** Human-readable list of unsupported constructs (the Path-B markers). */
39
+ /** Human-readable list of unsupported constructs. */
40
40
  uncovered: string[];
41
41
  /** The backend-native installable policy, assembled when a rule lowered. */
42
42
  proposed?: ProposedPolicy;
@@ -22,7 +22,7 @@ export interface ComposeOptions {
22
22
  userResponses?: ComposeUserResponses;
23
23
  }
24
24
  /** Result of composition: the PolicyIR, any ambiguities surfaced during
25
- * inference, and descriptive Path-B warnings for needs that are NOT expressed
25
+ * inference, and descriptive warnings for needs that are NOT expressed
26
26
  * as an IR node (so no fabricated constraint is emitted). The orchestrator
27
27
  * carries ambiguities into `ProposedPolicy.ambiguities` and merges warnings
28
28
  * into `ProposedPolicy.warnings`. */
@@ -1,17 +1,17 @@
1
- // src/synth/compose-from-recording.ts - facts + scope -> PolicyIR (Path A).
1
+ // src/synth/compose-from-recording.ts - facts + scope -> PolicyIR.
2
2
  //
3
- // Composes the canonical IR rule the OZ Path-A backend can compile. Fail-closed
3
+ // Composes the canonical IR rule the OZ built-in-primitive backend can compile. Fail-closed
4
4
  // composition rules:
5
5
  //
6
6
  // - identify the protocol of the top-level call (registry.identifyProtocol).
7
7
  // When it is unknown (null), emit NO OZ-primitive-producing IR node: the
8
8
  // scope is kept (CallContract + method) and every inferred bound is surfaced
9
- // as a descriptive Path-B warning. An unrecognised call never compiles to a
9
+ // as a descriptive warning. An unrecognised call never compiles to a
10
10
  // permissive OZ primitive.
11
11
  //
12
12
  // - carry the recorded top-level function into `rule.scope.method` so the OZ
13
- // adapter flags per-method scoping Path-B (CallContract permits every method
14
- // on the contract; a per-method restriction needs the interpreter predicate).
13
+ // adapter flags per-method scoping as not covered (CallContract permits every
14
+ // method on the contract; a per-method restriction needs the interpreter predicate).
15
15
  //
16
16
  // - a `spending_limit` (window_spent(token, w) <= limit) is emitted ONLY when
17
17
  // the caller supplies BOTH the limit (userResponses.limitAmount) and the
@@ -30,7 +30,7 @@
30
30
  // - the IR carries ONLY constraints justified by the recording (observed
31
31
  // recipient allowlist) + explicit user input. Nothing invented: no oracle
32
32
  // price fabricated from a slippage bound, no synthetic exact-path compare.
33
- // Those needs are surfaced as descriptive Path-B warnings instead.
33
+ // Those needs are surfaced as descriptive warnings instead.
34
34
  //
35
35
  // The default behavior is `deny_all` (OZ context rules are deny-by-default).
36
36
  import { identifyProtocol } from "../registry/identify.js";
@@ -57,7 +57,7 @@ export function composeFromRecording(facts, scopeContract, topLevel, opts) {
57
57
  if (observed === undefined)
58
58
  continue;
59
59
  if (!known) {
60
- warnings.push(`spend of ${observed} (token ${token}) not bounded: unrecognised protocol, spend cap needs the interpreter predicate (Path B)`);
60
+ warnings.push(`spend of ${observed} (token ${token}) not bounded: unrecognised protocol, spend cap needs the interpreter predicate`);
61
61
  continue;
62
62
  }
63
63
  const limit = spendTokens.length === 1 ? limitAmount : undefined;
@@ -106,11 +106,11 @@ export function composeFromRecording(facts, scopeContract, topLevel, opts) {
106
106
  code: 'FREQUENCY_BOUND_MISSING',
107
107
  question: 'Incoming-only flow - what max invocations per window should the policy enforce?',
108
108
  });
109
- warnings.push('frequency bound needed for the incoming-only flow (interpreter/Path B); no invocation cap inferred');
109
+ warnings.push('frequency bound needed for the incoming-only flow (needs the interpreter predicate); no invocation cap inferred');
110
110
  }
111
111
  }
112
112
  // Observed recipient allowlist (SEP-41) is a real, recorded constraint the OZ
113
- // adapter flags Path-B. Unknown protocols emit nothing here.
113
+ // adapter flags as not covered. Unknown protocols emit nothing here.
114
114
  if (topLevel && protocol !== null) {
115
115
  appendProtocolSpecificConstraints(constraints, warnings, facts, topLevel, protocol);
116
116
  }
@@ -134,11 +134,11 @@ export function composeFromRecording(facts, scopeContract, topLevel, opts) {
134
134
  }
135
135
  /** Add the constraints justified by the recording that the OZ backend cannot
136
136
  * express natively (the SEP-41 recipient the transfer moved to). The OZ adapter
137
- * flags them Path-B. SoroSwap's slippage / oracle / exact-path needs are
137
+ * flags them as not covered. SoroSwap's slippage / oracle / exact-path needs are
138
138
  * surfaced as descriptive warnings, NOT as fabricated comparison nodes. */
139
139
  function appendProtocolSpecificConstraints(constraints, warnings, facts, topLevel, protocol) {
140
140
  // SEP-41 transfer / mint: the `to` arg (index 1) is the recipient. Emit it as
141
- // a single-element allowlist; the OZ adapter flags it Path-B.
141
+ // a single-element allowlist; the OZ adapter flags it as not covered.
142
142
  if (protocol.protocol === 'sep41' && (protocol.fn === 'transfer' || protocol.fn === 'mint')) {
143
143
  const toArg = topLevel.args[1];
144
144
  if (toArg && toArg.type === 'address') {
@@ -157,6 +157,6 @@ function appendProtocolSpecificConstraints(constraints, warnings, facts, topLeve
157
157
  const paths = facts.allowedPaths?.[topLevel.contract];
158
158
  const route = paths?.length === 1 ? paths[0] : undefined;
159
159
  const pathText = route && route.length > 0 ? `; observed path: ${route.join(' -> ')}` : '';
160
- warnings.push(`SoroSwap swap: slippage / oracle price bound and exact hop path need the interpreter predicate (Path B)${pathText}`);
160
+ warnings.push(`SoroSwap swap: slippage / oracle price bound and exact hop path need the interpreter predicate${pathText}`);
161
161
  }
162
162
  }
@@ -4,7 +4,7 @@ export interface IntentFacts {
4
4
  callTargets: string[];
5
5
  functionsByContract: Record<string, string[]>;
6
6
  /** Aggregate outgoing spend per token (i128-safe decimal string). A single
7
- * token entry is the Path-A prerequisite for `spending_limit`. Movements
7
+ * token entry is the prerequisite for `spending_limit`. Movements
8
8
  * with from != sourceAccount are NOT counted as "spend" by this synth
9
9
  * (incoming yield, refund, etc.). */
10
10
  spendByToken: Record<string, string>;
@@ -15,7 +15,7 @@ export interface IntentFacts {
15
15
  sharedRouter?: string;
16
16
  /** Router hop paths, keyed by target contract. SoroSwap exposes its `path`
17
17
  * arg directly on the top-level call; this is the input the compose step
18
- * passes to a per-arg `in` condition (flagged Path-B by the OZ adapter). */
18
+ * passes to a per-arg `in` condition (flagged as not covered by the OZ adapter). */
19
19
  allowedPaths?: Record<string, string[][]>;
20
20
  }
21
21
  /** Lower a recorded transaction to the canonical IntentFacts. Pure (no
@@ -4,12 +4,12 @@
4
4
  // MandateSpec is lowered deterministically to a PolicyIR and compiled by the OZ
5
5
  // adapter to a ProposedPolicy. No decoding, no inference, so parseConfidence is
6
6
  // the full/not-applicable value. Constructs the OZ built-in primitives cannot
7
- // express (compile's `uncovered`) are surfaced in `ProposedPolicy.warnings` as
8
- // Path-B markers rather than failing the call - the covered primitives still
9
- // install; the uncovered ones are reported.
7
+ // express (compile's `uncovered`) are surfaced in `ProposedPolicy.warnings`
8
+ // rather than failing the call - the covered primitives still install; the
9
+ // uncovered ones are reported.
10
10
  import { createOzAdapter } from "../adapters/oz/adapter.js";
11
11
  import { mandateToPolicyIR } from "../mandate/to-ir.js";
12
- const PATH_B_PREFIX = 'Path B (not covered by OZ built-in primitives): ';
12
+ const UNCOVERED_PREFIX = 'Not covered by OZ built-in primitives: ';
13
13
  export function synthesizeFromMandate(spec, ozConfig) {
14
14
  const ir = mandateToPolicyIR(spec);
15
15
  const adapter = createOzAdapter(ozConfig);
@@ -28,7 +28,7 @@ export function synthesizeFromMandate(spec, ozConfig) {
28
28
  }
29
29
  const proposed = {
30
30
  ...result.proposed,
31
- warnings: result.uncovered.map((u) => `${PATH_B_PREFIX}${u}`),
31
+ warnings: result.uncovered.map((u) => `${UNCOVERED_PREFIX}${u}`),
32
32
  };
33
33
  return { ok: true, data: proposed };
34
34
  }
@@ -11,7 +11,7 @@
11
11
  // 4. `composeFromRecording(facts, scope, opts)` -> PolicyIR + ambiguities.
12
12
  // 5. `ozAdapter.compile(ir)` -> CompileResult.
13
13
  // 6. Assemble ProposedPolicy carrying parseConfidence (from tx), warnings
14
- // (the OZ `uncovered` Path-B markers, prefixed), and ambiguities
14
+ // (the OZ `uncovered` markers, prefixed), and ambiguities
15
15
  // (DURATION_UNSPECIFIED etc.).
16
16
  //
17
17
  // Determinism: same (tx, opts, ozConfig) -> byte-identical ProposedPolicy.
@@ -21,7 +21,7 @@ import { SOROBAN_LIMITS, } from "../types.js";
21
21
  import { composeFromRecording, } from "./compose-from-recording.js";
22
22
  import { lower } from "./lower.js";
23
23
  import { decideScope } from "./scope.js";
24
- const PATH_B_PREFIX = 'Path B (not covered by OZ built-in primitives): ';
24
+ const UNCOVERED_PREFIX = 'Not covered by OZ built-in primitives: ';
25
25
  /** Synthesize a ProposedPolicy from a recorded transaction. */
26
26
  export function synthesizeFromRecording(tx, opts, ozConfig) {
27
27
  // 0. validate inputs (fail closed - never synthesize from garbage).
@@ -68,7 +68,7 @@ export function synthesizeFromRecording(tx, opts, ozConfig) {
68
68
  return scopeRes;
69
69
  const scope = scopeRes.data;
70
70
  if (scope.kind !== 'call_contract') {
71
- // A default scope on the recording-path is not currently a Path-A flow;
71
+ // A default scope on the recording-path is not currently a covered flow;
72
72
  // surface SYNTHESIS_ERROR so the orchestrator never silently emits an
73
73
  // over-broad default-scoped policy.
74
74
  return {
@@ -108,8 +108,8 @@ export function synthesizeFromRecording(tx, opts, ozConfig) {
108
108
  ...compileRes.proposed,
109
109
  parseConfidence: { ...tx.parseConfidence },
110
110
  warnings: [
111
- ...compileRes.uncovered.map((u) => `${PATH_B_PREFIX}${u}`),
112
- ...composed.warnings.map((w) => `${PATH_B_PREFIX}${w}`),
111
+ ...compileRes.uncovered.map((u) => `${UNCOVERED_PREFIX}${u}`),
112
+ ...composed.warnings.map((w) => `${UNCOVERED_PREFIX}${w}`),
113
113
  ],
114
114
  ambiguities: mergeAmbiguities(composed.ambiguities, scope.ambiguities),
115
115
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crediolabs/policy-synth",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "license": "MIT",
5
5
  "description": "Off-chain TypeScript synthesis core for the OZ Accounts Policy Builder. Records Soroban transactions, synthesises the minimal policy that permits exactly that flow, verifies it, and returns an unsigned install transaction.",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
- // src/adapters/oz/adapter.ts - the OZ Accounts CustodyAdapter (Path-A lowering).
1
+ // src/adapters/oz/adapter.ts - the OZ Accounts CustodyAdapter.
2
2
  //
3
3
  // Compiles a PolicyIR to an OZ `ProposedPolicy` using OZ built-in policy
4
- // primitives (Path A). Only the constructs OZ can express natively are lowered:
4
+ // primitives. Only the constructs OZ can express natively are lowered:
5
5
  // scope.contract -> ContextRuleType.call_contract (else default)
6
6
  // expiry.validUntilLedger -> ContextRuleDraft.validUntilLedger
7
7
  // window_spent(t,w) <= L -> `spending_limit` primitive
@@ -9,7 +9,7 @@
9
9
  // Anything needing a capability this backend lacks (oracle price, invocation
10
10
  // count, per-arg comparison/allowlist, guard, nested boolean predicate) is NOT
11
11
  // emitted: it is named in `uncovered` and `covered` is set false. Nothing is
12
- // silently dropped - the uncovered constructs are the Path-B markers.
12
+ // silently dropped - the uncovered constructs are named instead.
13
13
  //
14
14
  // OZ built-in policy instance addresses are per-network deploy artifacts we do
15
15
  // not have yet (install is a later phase). They are injected via config; week-1
@@ -132,8 +132,8 @@ function lowerRule(rule: IRPolicyRule, config: OzAdapterConfig): LoweredRule {
132
132
  const policyRefs: PolicyRef[] = []
133
133
 
134
134
  // scope -> context rule type. OZ scopes by contract (CallContract); a finer
135
- // method-level restriction is a predicate concern and must be flagged Path-B
136
- // because CallContract alone permits other methods on the same contract
135
+ // method-level restriction is a predicate concern and must be flagged as not
136
+ // covered because CallContract alone permits other methods on the same contract
137
137
  // (e.g. an unbounded approve alongside a capped transfer).
138
138
  const contextRuleType: ContextRuleDraft['contextRuleType'] =
139
139
  rule.scope.contract !== undefined
@@ -171,12 +171,12 @@ function lowerRule(rule: IRPolicyRule, config: OzAdapterConfig): LoweredRule {
171
171
  }
172
172
  }
173
173
 
174
- // guard -> Path B (applicability predicates are not an OZ built-in).
174
+ // guard -> not covered (applicability predicates are not an OZ built-in).
175
175
  if (rule.guard) {
176
176
  uncovered.push(`guard: ${describeCondition(rule.guard)}`)
177
177
  }
178
178
 
179
- // constraints -> spending_limit where they match; else Path B. The OZ
179
+ // constraints -> spending_limit where they match; else not covered. The OZ
180
180
  // spending_limit policy takes `{ spending_limit: i128, period_ledgers: u32 }`
181
181
  // and has NO token param: it only accepts a CallContract context rule
182
182
  // (OnlyCallContractAllowed) and limits transfers of that context's contract,
@@ -209,7 +209,7 @@ function lowerRule(rule: IRPolicyRule, config: OzAdapterConfig): LoweredRule {
209
209
 
210
210
  // approval.threshold -> simple/weighted threshold primitive. A threshold < 1
211
211
  // is not a real M-of-N gate (0 approvals authorises everything), so refuse to
212
- // emit a no-op primitive and flag it Path-B instead.
212
+ // emit a no-op primitive and flag it as not covered instead.
213
213
  if (rule.approval) {
214
214
  if (!Number.isInteger(rule.approval.threshold) || rule.approval.threshold < 1) {
215
215
  uncovered.push(
@@ -281,17 +281,17 @@ function matchSpendingLimit(c: IRCondition): SpendingLimitMatch | null {
281
281
  return { token: selector.token, limit: value, windowSeconds: selector.windowSeconds }
282
282
  }
283
283
 
284
- /** Human-readable descriptor for a construct the OZ Path-A backend cannot
285
- * express, used to populate `uncovered` (the Path-B markers). */
284
+ /** Human-readable descriptor for a construct the OZ built-in-primitive backend
285
+ * cannot express, used to populate `uncovered`. */
286
286
  function describeCondition(cond: IRCondition): string {
287
287
  switch (cond.op) {
288
288
  case 'in':
289
- return `value allowlist on ${describeSelector(cond.selector)} (arg allowlist, Path B)`
289
+ return `value allowlist on ${describeSelector(cond.selector)} (arg allowlist)`
290
290
  case 'not':
291
- return 'negated condition (predicate DSL, Path B)'
291
+ return 'negated condition (predicate DSL)'
292
292
  case 'and':
293
293
  case 'or':
294
- return `nested ${cond.op} condition (predicate DSL, Path B)`
294
+ return `nested ${cond.op} condition (predicate DSL)`
295
295
  case 'compare': {
296
296
  const s = cond.compare.selector
297
297
  switch (s.kind) {
@@ -302,16 +302,16 @@ function describeCondition(cond: IRCondition): string {
302
302
  case 'window_spent':
303
303
  return `spend-window comparison with operator '${cond.compare.operator}' (only 'lte' lowers to spending_limit)`
304
304
  case 'amount':
305
- return `per-call amount comparison on ${s.token} (predicate DSL, Path B)`
305
+ return `per-call amount comparison on ${s.token} (predicate DSL)`
306
306
  case 'arg':
307
- return `argument comparison on arg ${s.argIndex} (predicate DSL, Path B)`
307
+ return `argument comparison on arg ${s.argIndex} (predicate DSL)`
308
308
  case 'calldata':
309
- return 'EVM calldata comparison (predicate DSL, Path B)'
309
+ return 'EVM calldata comparison (predicate DSL)'
310
310
  case 'value':
311
- return 'tx.value comparison (predicate DSL, Path B)'
311
+ return 'tx.value comparison (predicate DSL)'
312
312
  case 'now':
313
313
  case 'valid_until':
314
- return 'time comparison (predicate DSL, Path B)'
314
+ return 'time comparison (predicate DSL)'
315
315
  }
316
316
  }
317
317
  }
@@ -6,8 +6,8 @@
6
6
  // contract/method -> rule.scope
7
7
  // spendingLimit -> a `window_spent(token,window) <= limit` compare
8
8
  // approvalThreshold-> rule.approval.threshold
9
- // recipients -> an `in` condition on the recipient arg (Path-B flagged
10
- // by the OZ adapter; that is expected)
9
+ // recipients -> an `in` condition on the recipient arg (flagged as not
10
+ // covered by the OZ adapter; that is expected)
11
11
  // expiry -> rule.expiry
12
12
  // The top-level default is `deny_all` (OZ context rules are deny-by-default).
13
13
 
@@ -16,8 +16,8 @@ import type { MandateSpec } from './types.ts'
16
16
 
17
17
  /** Arg index the recipient allowlist constrains. Pinned to the SEP-41
18
18
  * `transfer(from, to, amount)` convention where `to` is arg 1. This condition
19
- * is flagged Path-B by the OZ adapter (no built-in primitive expresses an arg
20
- * allowlist), so the index only needs to be deterministic in week-1. */
19
+ * is flagged as not covered by the OZ adapter (no built-in primitive expresses
20
+ * an arg allowlist), so the index only needs to be deterministic in week-1. */
21
21
  const RECIPIENT_ARG_INDEX = 1
22
22
 
23
23
  export function mandateToPolicyIR(spec: MandateSpec): PolicyIR {
@@ -14,7 +14,7 @@ export interface MandateSpec {
14
14
  spendingLimit?: { token: string; limit: string; windowSeconds: number }
15
15
  /** -> OZ `simple_threshold` / `weighted_threshold` primitive. */
16
16
  approvalThreshold?: number
17
- /** Recipient allowlist -> an `in` arg condition (flagged Path-B in week-1). */
17
+ /** Recipient allowlist -> an `in` arg condition (not covered by an OZ built-in). */
18
18
  recipients?: string[]
19
19
  /** -> OZ context-rule expiry. */
20
20
  expiry?: { validUntilLedger?: number; validUntilUnixSeconds?: number }
@@ -48,7 +48,7 @@ export interface CustodyCapabilities {
48
48
  export interface CompileResult {
49
49
  /** false => some IR construct this backend cannot express (see `uncovered`). */
50
50
  covered: boolean
51
- /** Human-readable list of unsupported constructs (the Path-B markers). */
51
+ /** Human-readable list of unsupported constructs. */
52
52
  uncovered: string[]
53
53
  /** The backend-native installable policy, assembled when a rule lowered. */
54
54
  proposed?: ProposedPolicy
@@ -1,17 +1,17 @@
1
- // src/synth/compose-from-recording.ts - facts + scope -> PolicyIR (Path A).
1
+ // src/synth/compose-from-recording.ts - facts + scope -> PolicyIR.
2
2
  //
3
- // Composes the canonical IR rule the OZ Path-A backend can compile. Fail-closed
3
+ // Composes the canonical IR rule the OZ built-in-primitive backend can compile. Fail-closed
4
4
  // composition rules:
5
5
  //
6
6
  // - identify the protocol of the top-level call (registry.identifyProtocol).
7
7
  // When it is unknown (null), emit NO OZ-primitive-producing IR node: the
8
8
  // scope is kept (CallContract + method) and every inferred bound is surfaced
9
- // as a descriptive Path-B warning. An unrecognised call never compiles to a
9
+ // as a descriptive warning. An unrecognised call never compiles to a
10
10
  // permissive OZ primitive.
11
11
  //
12
12
  // - carry the recorded top-level function into `rule.scope.method` so the OZ
13
- // adapter flags per-method scoping Path-B (CallContract permits every method
14
- // on the contract; a per-method restriction needs the interpreter predicate).
13
+ // adapter flags per-method scoping as not covered (CallContract permits every
14
+ // method on the contract; a per-method restriction needs the interpreter predicate).
15
15
  //
16
16
  // - a `spending_limit` (window_spent(token, w) <= limit) is emitted ONLY when
17
17
  // the caller supplies BOTH the limit (userResponses.limitAmount) and the
@@ -30,7 +30,7 @@
30
30
  // - the IR carries ONLY constraints justified by the recording (observed
31
31
  // recipient allowlist) + explicit user input. Nothing invented: no oracle
32
32
  // price fabricated from a slippage bound, no synthetic exact-path compare.
33
- // Those needs are surfaced as descriptive Path-B warnings instead.
33
+ // Those needs are surfaced as descriptive warnings instead.
34
34
  //
35
35
  // The default behavior is `deny_all` (OZ context rules are deny-by-default).
36
36
 
@@ -62,7 +62,7 @@ export interface ComposeOptions {
62
62
  }
63
63
 
64
64
  /** Result of composition: the PolicyIR, any ambiguities surfaced during
65
- * inference, and descriptive Path-B warnings for needs that are NOT expressed
65
+ * inference, and descriptive warnings for needs that are NOT expressed
66
66
  * as an IR node (so no fabricated constraint is emitted). The orchestrator
67
67
  * carries ambiguities into `ProposedPolicy.ambiguities` and merges warnings
68
68
  * into `ProposedPolicy.warnings`. */
@@ -104,7 +104,7 @@ export function composeFromRecording(
104
104
 
105
105
  if (!known) {
106
106
  warnings.push(
107
- `spend of ${observed} (token ${token}) not bounded: unrecognised protocol, spend cap needs the interpreter predicate (Path B)`
107
+ `spend of ${observed} (token ${token}) not bounded: unrecognised protocol, spend cap needs the interpreter predicate`
108
108
  )
109
109
  continue
110
110
  }
@@ -156,13 +156,13 @@ export function composeFromRecording(
156
156
  question: 'Incoming-only flow - what max invocations per window should the policy enforce?',
157
157
  })
158
158
  warnings.push(
159
- 'frequency bound needed for the incoming-only flow (interpreter/Path B); no invocation cap inferred'
159
+ 'frequency bound needed for the incoming-only flow (needs the interpreter predicate); no invocation cap inferred'
160
160
  )
161
161
  }
162
162
  }
163
163
 
164
164
  // Observed recipient allowlist (SEP-41) is a real, recorded constraint the OZ
165
- // adapter flags Path-B. Unknown protocols emit nothing here.
165
+ // adapter flags as not covered. Unknown protocols emit nothing here.
166
166
  if (topLevel && protocol !== null) {
167
167
  appendProtocolSpecificConstraints(constraints, warnings, facts, topLevel, protocol)
168
168
  }
@@ -189,7 +189,7 @@ export function composeFromRecording(
189
189
 
190
190
  /** Add the constraints justified by the recording that the OZ backend cannot
191
191
  * express natively (the SEP-41 recipient the transfer moved to). The OZ adapter
192
- * flags them Path-B. SoroSwap's slippage / oracle / exact-path needs are
192
+ * flags them as not covered. SoroSwap's slippage / oracle / exact-path needs are
193
193
  * surfaced as descriptive warnings, NOT as fabricated comparison nodes. */
194
194
  function appendProtocolSpecificConstraints(
195
195
  constraints: IRCondition[],
@@ -199,7 +199,7 @@ function appendProtocolSpecificConstraints(
199
199
  protocol: IdentifiedProtocol
200
200
  ): void {
201
201
  // SEP-41 transfer / mint: the `to` arg (index 1) is the recipient. Emit it as
202
- // a single-element allowlist; the OZ adapter flags it Path-B.
202
+ // a single-element allowlist; the OZ adapter flags it as not covered.
203
203
  if (protocol.protocol === 'sep41' && (protocol.fn === 'transfer' || protocol.fn === 'mint')) {
204
204
  const toArg = topLevel.args[1]
205
205
  if (toArg && toArg.type === 'address') {
@@ -220,7 +220,7 @@ function appendProtocolSpecificConstraints(
220
220
  const route = paths?.length === 1 ? paths[0] : undefined
221
221
  const pathText = route && route.length > 0 ? `; observed path: ${route.join(' -> ')}` : ''
222
222
  warnings.push(
223
- `SoroSwap swap: slippage / oracle price bound and exact hop path need the interpreter predicate (Path B)${pathText}`
223
+ `SoroSwap swap: slippage / oracle price bound and exact hop path need the interpreter predicate${pathText}`
224
224
  )
225
225
  }
226
226
  }
@@ -16,7 +16,7 @@ export interface IntentFacts {
16
16
  callTargets: string[]
17
17
  functionsByContract: Record<string, string[]>
18
18
  /** Aggregate outgoing spend per token (i128-safe decimal string). A single
19
- * token entry is the Path-A prerequisite for `spending_limit`. Movements
19
+ * token entry is the prerequisite for `spending_limit`. Movements
20
20
  * with from != sourceAccount are NOT counted as "spend" by this synth
21
21
  * (incoming yield, refund, etc.). */
22
22
  spendByToken: Record<string, string>
@@ -27,7 +27,7 @@ export interface IntentFacts {
27
27
  sharedRouter?: string
28
28
  /** Router hop paths, keyed by target contract. SoroSwap exposes its `path`
29
29
  * arg directly on the top-level call; this is the input the compose step
30
- * passes to a per-arg `in` condition (flagged Path-B by the OZ adapter). */
30
+ * passes to a per-arg `in` condition (flagged as not covered by the OZ adapter). */
31
31
  allowedPaths?: Record<string, string[][]>
32
32
  }
33
33
 
@@ -4,9 +4,9 @@
4
4
  // MandateSpec is lowered deterministically to a PolicyIR and compiled by the OZ
5
5
  // adapter to a ProposedPolicy. No decoding, no inference, so parseConfidence is
6
6
  // the full/not-applicable value. Constructs the OZ built-in primitives cannot
7
- // express (compile's `uncovered`) are surfaced in `ProposedPolicy.warnings` as
8
- // Path-B markers rather than failing the call - the covered primitives still
9
- // install; the uncovered ones are reported.
7
+ // express (compile's `uncovered`) are surfaced in `ProposedPolicy.warnings`
8
+ // rather than failing the call - the covered primitives still install; the
9
+ // uncovered ones are reported.
10
10
 
11
11
  import type { OzAdapterConfig } from '../adapters/oz/adapter.ts'
12
12
  import { createOzAdapter } from '../adapters/oz/adapter.ts'
@@ -15,7 +15,7 @@ import { mandateToPolicyIR } from '../mandate/to-ir.ts'
15
15
  import type { MandateSpec } from '../mandate/types.ts'
16
16
  import type { ProposedPolicy } from '../types.ts'
17
17
 
18
- const PATH_B_PREFIX = 'Path B (not covered by OZ built-in primitives): '
18
+ const UNCOVERED_PREFIX = 'Not covered by OZ built-in primitives: '
19
19
 
20
20
  export function synthesizeFromMandate(
21
21
  spec: MandateSpec,
@@ -40,7 +40,7 @@ export function synthesizeFromMandate(
40
40
 
41
41
  const proposed: ProposedPolicy = {
42
42
  ...result.proposed,
43
- warnings: result.uncovered.map((u) => `${PATH_B_PREFIX}${u}`),
43
+ warnings: result.uncovered.map((u) => `${UNCOVERED_PREFIX}${u}`),
44
44
  }
45
45
  return { ok: true, data: proposed }
46
46
  }
@@ -11,7 +11,7 @@
11
11
  // 4. `composeFromRecording(facts, scope, opts)` -> PolicyIR + ambiguities.
12
12
  // 5. `ozAdapter.compile(ir)` -> CompileResult.
13
13
  // 6. Assemble ProposedPolicy carrying parseConfidence (from tx), warnings
14
- // (the OZ `uncovered` Path-B markers, prefixed), and ambiguities
14
+ // (the OZ `uncovered` markers, prefixed), and ambiguities
15
15
  // (DURATION_UNSPECIFIED etc.).
16
16
  //
17
17
  // Determinism: same (tx, opts, ozConfig) -> byte-identical ProposedPolicy.
@@ -34,7 +34,7 @@ import {
34
34
  import { lower } from './lower.ts'
35
35
  import { decideScope, type ScopeDecision } from './scope.ts'
36
36
 
37
- const PATH_B_PREFIX = 'Path B (not covered by OZ built-in primitives): '
37
+ const UNCOVERED_PREFIX = 'Not covered by OZ built-in primitives: '
38
38
 
39
39
  /** Top-level orchestrator inputs. `userResponses` carries the LLM-collected
40
40
  * answers to the ambiguity prompts (windowSeconds, validUntilLedger,
@@ -98,7 +98,7 @@ export function synthesizeFromRecording(
98
98
  if (!scopeRes.ok) return scopeRes
99
99
  const scope: ScopeDecision = scopeRes.data
100
100
  if (scope.kind !== 'call_contract') {
101
- // A default scope on the recording-path is not currently a Path-A flow;
101
+ // A default scope on the recording-path is not currently a covered flow;
102
102
  // surface SYNTHESIS_ERROR so the orchestrator never silently emits an
103
103
  // over-broad default-scoped policy.
104
104
  return {
@@ -142,8 +142,8 @@ export function synthesizeFromRecording(
142
142
  ...compileRes.proposed,
143
143
  parseConfidence: { ...tx.parseConfidence },
144
144
  warnings: [
145
- ...compileRes.uncovered.map((u) => `${PATH_B_PREFIX}${u}`),
146
- ...composed.warnings.map((w) => `${PATH_B_PREFIX}${w}`),
145
+ ...compileRes.uncovered.map((u) => `${UNCOVERED_PREFIX}${u}`),
146
+ ...composed.warnings.map((w) => `${UNCOVERED_PREFIX}${w}`),
147
147
  ],
148
148
  ambiguities: mergeAmbiguities(composed.ambiguities, scope.ambiguities),
149
149
  }