Skip to content

feat(policy): PolicyDialect — the engine's chain vocabulary behind the module (#45, C.2) - #69

Merged
SuperstrongBE merged 1 commit into
mainfrom
feat/policy-dialect
Aug 6, 2026
Merged

feat(policy): PolicyDialect — the engine's chain vocabulary behind the module (#45, C.2)#69
SuperstrongBE merged 1 commit into
mainfrom
feat/policy-dialect

Conversation

@SuperstrongBE

Copy link
Copy Markdown
Owner

PR C.2 of the implementation plan (Wave 1, Track C) — with C.1 (#68) this completes the Phase-A dialect work of #45: the core engine no longer hardcodes any chain vocabulary.

What moved where

Was (hardcoded in core) Now (XPR dialect, chains/xpr/dialect.ts)
engine.resolvePathauthorization.actor|permission, data.* dialect.resolvePath
engine.actionAssetdata.quantity {amount,symbol,precision} dialect.assetOf
parseAsset on limit bounds — "1.0000 XPR" grammar dialect.parseAssetLimit
data["to"] quota-recipient convention dialect.recipientOf
providerResolver.resolveOnexpr.rpc.tableRowget_table_rows dialect.resolveProviderQuery (resolver keeps the generic timeout + fail-closed wrapper)
validator patterns (match paths, chain id, select, provider namespace) dialect-driven: validatePolicy(input, dialect) / verifyStoredPolicy(json, hash, dialect), one Ajv validator memoized per dialect

Chain-agnostic skeleton untouched: deny-first control flow, JCS canonicalization + sha256, limits model, quota demands, fail-closed mapping. The multi-authorization refusal stays in core — it's the DecodedAction contract (MVP invariant), not vocabulary.

Plumbing

ChainModule gains dialect (#44's sketch completed); DaemonDependencies, PolicyCache, CLI explain, MCP explain receive it through the registry. EvaluationContext.dialect is required — a policy is always validated/evaluated in ITS chain's dialect (INV-013). The XPR dialect reads its patterns from the same vocabulary.ts the web editor imports (C.1) — daemon, validator, editor in lockstep.

Load-bearing acceptance (issue #45)

  • 241/241 tests green — same decision assertions as before, only ctx/constructor plumbing changed.
  • Live byte-stability: the real testnet policies verify unchanged through the dialect path —
    funagent v7 → VERIFIED (2 rules)
    fuckagent v2 → VERIFIED (1 rules)
    
  • Boundary grep: no authorization.*/data.quantity/data.to/get_table_rows/asset-grammar reference left in core/policy/engine.ts, schema.ts or providerResolver.ts (docs comments aside).

Also

vitest testTimeout → 60s: the Argon2id MODERATE keystore suites (256 MiB per derivation, by design) overran the 5s default under full-suite parallelism — the earlier intermittent single-test failures were exactly this.

A new chain's policy support is now: one dialect + one registry contract, zero core-engine edits (Phase B).

🤖 Generated with Claude Code

https://claude.ai/code/session_01RJ7hqFnC9Nnyf2Db2h3WTb

…e module (#45, C.2)

The policy engine's Antelope-specific vocabulary leaves the core. A new
PolicyDialect (core/policy/dialect.ts) carries exactly what varies per
chain; the skeleton — deny-first control flow, JCS canonicalization,
limits model, fail-closed pipeline — stays chain-agnostic:

- match-path resolution (authorization.actor|permission mapping, data.*
  traversal) moves from engine.resolvePath to dialect.resolvePath;
- asset extraction (data.quantity = {amount,symbol,precision}) moves
  from engine.actionAsset to dialect.assetOf; limit bounds parse through
  dialect.parseAssetLimit ("1.0000 XPR" grammar);
- the quota recipient convention (data.to) becomes dialect.recipientOf;
- the provider query→read-call mapping (xpr.rpc.tableRow →
  get_table_rows) moves from daemon/providerResolver.resolveOne to
  dialect.resolveProviderQuery — the resolver keeps only the generic
  timeout + fail-closed wrapper;
- the validator's per-chain patterns (match paths, chain id, select
  fields, provider namespace) come from the dialect: validatePolicy /
  verifyStoredPolicy take it as a parameter, with one compiled Ajv
  validator memoized per dialect. TableRowProvider.provider widens to
  string (the namespace is enforced per-dialect at load time).

The XPR dialect (chains/xpr/dialect.ts) is the moved code, verbatim in
behavior, reading its patterns from the SAME vocabulary module the web
editor imports (C.1) — daemon, validator and editor stay in lockstep.
ChainModule gains `dialect`; the daemon (DaemonDependencies), the policy
cache, the CLI explain and the MCP explain all receive it through the
registry. EvaluationContext gains `dialect` (required — a policy is
always evaluated in ITS chain's dialect, INV-013).

Load-bearing acceptance verified: all 241 tests green (same decision
assertions, ctx plumbing only), and the LIVE testnet policies (funagent
v7, fuckagent v2) still pass verifyStoredPolicy byte-identically through
the dialect path. vitest testTimeout raised to 60s — the Argon2id
MODERATE suites (256 MiB by design) overran the 5s default under
full-suite parallelism.

Part of #45 — with C.1 this completes the Phase-A dialect work; the
engine no longer hardcodes any chain vocabulary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJ7hqFnC9Nnyf2Db2h3WTb
@SuperstrongBE
SuperstrongBE merged commit 9bf4b26 into main Aug 6, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant