Skip to content

feat(policy): shared vocabulary + canonicalizer for daemon AND web (#45, C.1) - #68

Merged
SuperstrongBE merged 1 commit into
mainfrom
feat/policy-core-shared
Aug 5, 2026
Merged

feat(policy): shared vocabulary + canonicalizer for daemon AND web (#45, C.1)#68
SuperstrongBE merged 1 commit into
mainfrom
feat/policy-core-shared

Conversation

@SuperstrongBE

Copy link
Copy Markdown
Owner

PR C.1 of the implementation plan (Wave 1, Track C) — first task of #45. Independent of #67 (zero file overlap — mergeable in any order).

The problem it kills

The web editor hand-duplicated the daemon's schema fragments — MATCH_PATH_RE/SELECT_FIELD_RE regex copies in compile.ts and a second canonicalize implementation — with parity asserted by comment only. Every future chain would multiply the copies (the exact coupling debt flagged in #45).

Single source now

  • src/core/policy/vocabulary.ts (new, pure — no imports, no Node APIs): the closed vocabularies (MATCH_PATH, SELECT_FIELD, RULE_ID, CHAIN_ID) as shared patterns. The daemon's Ajv schema consumes them; the editor's compiler imports them directly.
  • src/core/canonical/jcs.ts is now pure: canonicalSha256Hex (node:crypto) moved to canonical/hash.ts (2 importers updated). The web editor now imports the daemon's canonicalize — its local reimplementation is deleted. The pushed policyjson is byte-identical to what verifyStoredPolicy re-canonicalizes by construction, not by boundary rejection.
  • Web wiring: @sbx-core vite alias + tsconfig paths into ../src/core, restricted to leaf dependency-free modules. Consumers (PushModal, EditorView) are untouched — they keep importing through compile.ts's re-export.

Drift-proofing

New test/vocabulary.test.ts: the shared regexes must accept/reject exactly what validatePolicy accepts/rejects (6 accepted + 6 rejected match paths, ids, select fields). A silent divergence between editor validation and daemon validation can't reappear.

Numbers

235/235 root tests · web tsc -b && vite build ✅ · typecheck ✅. No behavior change on either side — the web canonicalize was behaviorally equivalent on policy shapes; now it's the same function.

Noted for C.2 (next)

vocabulary.ts documents that authorization.* and the 64-hex chain id are the XPR dialect's vocabulary — the PolicyDialect extraction moves them behind the chain module while keeping daemon/web in lockstep through this same shared module.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RJ7hqFnC9Nnyf2Db2h3WTb

…, C.1)

The web editor kept hand-copied duplicates of the daemon's policy
schema fragments: the match-path/select regexes in compile.ts and a
second, "canonical-ish" reimplementation of canonicalize. Parity was
asserted by comment and enforced only at the boundary (the daemon
rejecting not_canonical) — and every future chain would multiply the
copies. Now both sides consume the SAME source:

- src/core/policy/vocabulary.ts (new, PURE — no imports, no Node APIs):
  the closed vocabularies as shared patterns — MATCH_PATH, SELECT_FIELD,
  RULE_ID, CHAIN_ID — consumed by schema.ts's Ajv schema on the daemon
  side and imported directly by the editor's compiler on the web side.
- src/core/canonical/jcs.ts is now PURE: canonicalSha256Hex (node:
  crypto) moves to src/core/canonical/hash.ts. The web editor imports
  THE daemon's canonicalize — its local reimplementation is deleted, so
  the pushed policyjson is byte-identical to what verifyStoredPolicy
  re-canonicalizes, by construction.
- web: a "@sbx-core" vite alias + tsconfig paths point into ../src/core
  (leaf, dependency-free modules only); PushModal/EditorView keep their
  imports through compile.ts's re-export.

New test/vocabulary.test.ts pins regex ↔ validator parity (accepted and
rejected match paths agree with validatePolicy's actual behavior), so a
drift cannot reappear silently.

235 tests green · web build green. No behavior change on either side
(the web canonicalize was behaviorally equivalent on policy shapes; now
it is the same function).

Part of #45 — foundation for the lossless editor work (#38).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RJ7hqFnC9Nnyf2Db2h3WTb
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