Skip to content

feat(rpc): impl hit/set_policy + hit/resolve, closes #228#290

Merged
Destynova2 merged 2 commits intomainfrom
feat/rpc-hit-228
Apr 26, 2026
Merged

feat(rpc): impl hit/set_policy + hit/resolve, closes #228#290
Destynova2 merged 2 commits intomainfrom
feat/rpc-hit-228

Conversation

@Destynova2
Copy link
Copy Markdown
Contributor

Summary

Replaces the last two TODO(#228) stubs in the codebase. Closes issue #228 in conjunction with #286 (P1), #287 (P2), #289 (P3).

Behaviour

hit/set_policy(name, policy_json)

  • Deserializes the JSON into PolicyConfig.
  • Path argument wins over payload name.
  • Upserts in config.policies (replace by name or append).
  • Compiles a fresh PolicyMatcher before the swap so malformed globs are rejected without dirtying the running registry.
  • Atomic-swap rebuilds the matcher via ReloadableState::new.
  • Disk persistence out of scope per Runtime config mutation for RPC namespaces (7 TODOs) #228.

hit/resolve(context)

  • Field-by-field parse of a JSON object into RequestContext (the type has no Deserialize derive).
  • Permissive defaults so partial probes work.
  • Returns the merged ResolvedPolicy as JSON (presence flags for each override category).

Both gated on #[cfg(feature = "policies")] with a graceful no-op fallback.

Tests — 10/10 passing

  • parse_context_extracts_all_fields, parse_context_tolerates_missing_fields, parse_context_rejects_non_object
  • set_policy_rejects_invalid_json (PolicyConfig validation contract)
  • matcher_compile_rejects_invalid_glob (the BEFORE-swap validation gate that protects the running registry)
  • require_role_denies_observer_for_admin_methods, require_role_allows_operator_for_resolve
  • 3 pre-existing HitPolicyInfo tests

Closes

Test plan

  • cargo nextest run -E 'test(hit_ns)' --features mcp,policies — 10/10
  • cargo fmt --all -- --check clean
  • cargo clippy --all-features -- -D warnings clean
  • CI: full suite

🤖 Generated with Claude Code

…228)

Replaces the two `TODO(#228)` stubs in `server::rpc::hit_ns::set_policy`
and `resolve` with working implementations. Closes the last of the four
RPC namespaces tracked by issue #228.

set_policy(name, policy_json):
- Deserializes the JSON payload into `PolicyConfig`.
- The `name` argument always wins over `policy.name` (path-vs-payload).
- Upserts in `config.policies` (replace if same name, append otherwise).
- Compiles a fresh `PolicyMatcher` BEFORE the swap so a malformed glob
  is rejected without dirtying the running registry.
- Atomic-swaps the ReloadableState; the matcher is rebuilt by
  `ReloadableState::new`, so the new policy is live before the RPC
  returns.

resolve(context):
- Builds a `RequestContext` field-by-field from a JSON object (the type
  has no Deserialize derive — it is constructed imperatively in the
  dispatch path; we keep parity here).
- Missing fields fall back to permissive defaults so a partial probe
  ("just match on model") still works.
- Calls `PolicyMatcher::evaluate` and projects the merged
  `ResolvedPolicy` to JSON for transport.

Both functions are gated on `#[cfg(feature = "policies")]` with a
graceful no-op fallback when the feature is off.

# Errors

- `ERR_FORBIDDEN` when the caller is below `Admin` (set_policy) or
  `Operator` (resolve).
- `INVALID_PARAMS_CODE` for empty name, malformed policy JSON,
  malformed glob in match_rules, non-object context.
- `ERR_INTERNAL` when the registry rebuild or atomic swap fails.

Tests: 10/10 passing.

Implements P4 of the cli-forge-chef brigade plan for #228.
With #286 (P1), #287 (P2), #289 (P3), #228 closes when this lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Destynova2 Destynova2 enabled auto-merge April 26, 2026 16:41
…ures

`cargo check --no-default-features` (Feature Powerset core/1) failed:

  error: unused import: `jsonrpsee::types::error::INVALID_PARAMS_CODE`
  error: function `swap_state` is never used

Both items are only reachable from the `policies`-gated `set_policy`
handler. Adding `#[cfg(feature = "policies")]` to the import and to
`swap_state`, plus splitting the `ReloadableState`/`Router`/
`ProviderRegistry` imports along the same gate, makes the no-default
build clean while keeping the all-features path unchanged.

Tests: 10/10 still passing under --features mcp,policies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Destynova2 Destynova2 merged commit 8970abb into main Apr 26, 2026
43 checks passed
@Destynova2 Destynova2 deleted the feat/rpc-hit-228 branch April 26, 2026 17:07
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.

Runtime config mutation for RPC namespaces (7 TODOs)

1 participant