Reference

Getting started

Sign in, then ask the Advisor org-scoped architecture questions grounded in your team's ADRs — the full command reference for the actual CLI.

Quick Start

actual connects your terminal to the Advisor: sign in once, then ask org-scoped architecture questions and get answers grounded in your team's Architectural Decision Records (ADRs).

1

Install

Recommended Install the Actual Skill, a Claude Code plugin. Claude Code then checks your plans and code changes against your repository's decisions, and the skill helps you install the CLI the first time it runs. Run each command on its own, in a Claude Code session:

/plugin marketplace add actual-software/actual-skill
/plugin install actual-cli@actual-cli-skills

Then load the plugin into the running session:

/reload-plugins

Prefer your terminal? Run claude plugin marketplace add actual-software/actual-skill and then claude plugin install actual-cli@actual-cli-skills, and start a new Claude Code session (or run /reload-plugins in one that is already open).

Or install the CLI directly:

# Homebrew (macOS & Linux)
brew install actual-software/actual/actual

# Zero-install (Node required) — run any command with npx
npx @actualai/actual <command>
2

Sign in

Authenticate with browser-based OAuth. This establishes your Actual AI platform identity; credentials are stored locally and tokens are never printed.

actual login
3

Ask the Advisor

From anywhere, ask an architecture question:

actual advisor "Which router do we use for new Next.js pages?"

The Advisor answers from your organization's ADRs and lists the records it drew on, each with a confidence score.

Upgrading

The Actual Skill and the actual CLI are versioned separately, so upgrade each one the way you installed it. Plan and implementation checks need a CLI that has plan-check and impl-check; when the installed CLI is too old, the skill says so at the start of a session and offers to upgrade it.

Upgrade the Actual Skill

Refresh the marketplace listing, then update the plugin. Run each command on its own:

claude plugin marketplace update actual-cli-skills
claude plugin update actual-cli@actual-cli-skills

Start a new Claude Code session, or run /reload-plugins in one that is already open, so it picks up the new version.

Upgrade the CLI

Use the same tool you installed it with:

# Homebrew (macOS & Linux)
brew upgrade actual-software/actual/actual

# npm (global install)
npm install -g @actualai/actual@latest

Running through npx needs no upgrade step: pin @latest to be sure you get the newest release, as in npx @actualai/actual@latest login. If you installed through Clawhub, run clawhub install actual again.

Check what you have

actual --version

# A current CLI prints help for these; an old one reports an unknown subcommand
actual plan-check --help
actual impl-check --help

Your sign-in and .actual/rules/ files are untouched by an upgrade, so you do not need to run actual login again.

Commands

The advisor commands and their flags.

Command

actual login

Sign in to your Actual AI account with browser-based OAuth. This establishes your platform identity and is the first step before actual advisor. The sign-in requests the adr:query and adr:review scopes; credentials are stored locally and tokens are never printed.

# Sign in via browser OAuth
actual login

# Print the sign-in URL instead of opening a browser (e.g. over SSH)
actual login --no-browser
FlagTypeDescription
--org <ORG_ID>stringOrganization to sign in to. Required for accounts in more than one org; single-org accounts are auto-selected.
--no-browserboolPrint the sign-in URL instead of opening a browser — useful over SSH.
--api-url <URL>stringAuth server base URL. Falls back to ACTUAL_AUTH_URL.

Example output

Opening your browser to sign in to Actual AI…
If it doesn't open, visit this URL:

  https://app.actual.ai/authorize?…

✔ Signed in to Actual AI
  Organization:  org_3f1a…
  Account:       acct_2c00…
  Member:        mbr_8a91…
Command

actual advisor

Ask the Advisor an org-scoped architecture question and get an answer grounded in your team's ADRs. Requires a prior actual login. The Advisor shows a thinking… indicator while it works, then prints the answer followed by the related ADRs it drew on, each with a confidence score. Queries run against the production api-service by default.

actual advisor "<question>"
FlagTypeDescription
--org <ORG>stringOrganization to scope the query to. Defaults to the signed-in org.
--repo <REPO>stringScope the query to one connected repository (UUID). Omitted, the query runs at org level.
--api-url <URL>stringAdvisor API base URL. Falls back to ACTUAL_API_URL, else the production api-service.

Examples

# Ask an org-scoped architecture question
actual advisor "Which router do we use for new Next.js pages?"

# Scope the question to one connected repo
actual advisor "What is our API error-handling convention?" --repo <REPO_UUID>

Example output

Output for actual advisor "How should authentication and authorization be handled?", trimmed to the first matched ADR:

advisor thinking…
## Relevant ADRs (5 found)

### ADR: Standardize External API Client Initialization with Explicit Credential Management for Applications

**Policy:** Applications must implement explicit credential management at external service boundaries with dependency injection of authenticated clients.

**Instructions:** Create clients in designated initialization modules or script entry points with explicit credential management through centralized secrets systems. Pass client instances through function signatures using dependency injection rather than creating clients inline or using global singletons…

**Scope:** In scope: All Python scripts and modules that interact with external HTTP APIs, integration with third-party services (Slack, GitHub, Claude, etc.), authentication and authorization flows for external services… Out of scope: Internal service-to-service communication, database connection management, file system access…

**Confidence:** High

_(4 more related ADRs returned)_
Command

actual whoami

Show the signed-in Actual AI account, organization, member id, and granted scopes. Reads the locally cached credentials with no network call, and errors when you are signed out.

actual whoami

Example output

✔ Signed in to Actual AI
  Organization:  org_3f1a…
  Account:       acct_2c00…
  Member:        mbr_8a91…
  Scopes:        openid profile offline_access adr:query adr:review

When you are signed out, it prints an error and exits non-zero:

╭─ Error ───────────────────────────────────╮
│                                           │
│ Error: Not signed in to Actual AI         │
│                                           │
│ Fix: actual login                         │
│                                           │
╰───────────────────────────────────────────╯
Command

actual logout

Sign out and clear local credentials. It makes a best-effort attempt to revoke the session server-side, then always removes the local tokens, even offline, so the machine is never left holding a credential you meant to drop.

actual logout

Example output

✔ Signed out of Actual AI.
Command

actual rules ls

List the rule documents under .actual/rules/ — the corpus actual adr-bot generates from your team's ADRs, and the corpus plan-check and impl-check (below) judge plans and diffs against.

actual rules ls [PATH]
FlagTypeDescription
[PATH]positionalRepository root to scan. Defaults to the current directory.
--jsonboolPrint the parsed rule set as JSON instead of a panel.

Example output

╭─ Rules ──────────────────────────────────────────────────────────────────────╮
│ ./.actual/rules                                                              │
├──────────────────────────────────────────────────────────────────────────────┤
│ oauth-signing-a1b2: MUST 1 · MUST_NOT 1                                      │
├──────────────────────────────────────────────────────────────────────────────┤
│ 1 document · 2 rules · 0 warnings · 0 errors                                 │
╰──────────────────────────────────────────────────────────────────────────────╯
Command

actual rules index

Build or refresh the scope index over .actual/rules/ — a cached term/path index that rules select, plan-check, and impl-check use to prefilter candidate documents before ranking or judging them. Indexing is incremental and cached automatically; run this directly only to force a rebuild or inspect what the index sees.

actual rules index [PATH]
FlagTypeDescription
[PATH]positionalRepository root to scan. Defaults to the current directory.
--rebuildboolRebuild the index even when the cached one is still valid.
--clearboolRemove every cached scope index, including those left by other repositories, then rebuild this one.
--jsonboolPrint the index summary as JSON instead of a panel.

Example output

╭─ Scope index ────────────────────────────────────────────────────────────────╮
│ ./.actual/rules                                                              │
├──────────────────────────────────────────────────────────────────────────────┤
│ Documents: 1                                                                 │
│ Source: (cached)                                                             │
│ Path globs: 1                                                                │
│ Distinct terms: 10                                                           │
╰──────────────────────────────────────────────────────────────────────────────╯
Command

actual rules select

Select the rule documents that govern a plan, without judging conformance — useful for testing which ADRs a change would be checked against before running plan-check or impl-check. Selection runs a deterministic prefilter first, then an optional runner-backed rank (skip it with --no-rank for an offline, reproducible result).

actual rules select "<plan>" [--file <path>]...
FlagTypeDescription
<PLAN>...positionalThe plan to match against the rule set.
--repo <PATH>stringRepository root to scan. Defaults to the current directory.
--file <PATH>stringA file or directory the plan touches. Repeatable.
--limit <N>numberMaximum number of rule documents to return. Default 10.
--candidates <N>numberCandidates the deterministic prefilter retrieves before stage 2 judges them. Raised to --limit when smaller. Default 30.
--no-rankboolSkip stage 2 and return the deterministic prefilter alone. Offline, and exactly reproducible.
--runner <RUNNER>stringRunner to use for stage 2: claude-cli, anthropic-api, openai-api, codex-cli, or cursor-cli. Probed automatically when omitted.
--model <MODEL>stringModel for stage 2, overriding the configured one.
--explainboolShow the signal behind every hit, and what the filename scan would have chosen instead.
--jsonboolPrint the selection as JSON instead of a panel.
--rebuildboolRebuild the index before selecting.

Example output

For actual rules select "Sign new access tokens using an HMAC shared secret for speed" --no-rank:

╭─ Rule selection ─────────────────────────────────────────────────────────────╮
│ Plan: Sign new access tokens using an HMAC shared secret for speed           │
│ Indexed documents: 1                                                         │
│ Stage 2: not needed — the prefilter returned 1 candidate(s), inside the cap  │
├──────────────────────────────────────────────────────────────────────────────┤
│ 1.00: oauth-signing-a1b2                                                     │
│       scope and title match on access, token                                │
├──────────────────────────────────────────────────────────────────────────────┤
│ 1 of 1 documents shown                                                       │
╰──────────────────────────────────────────────────────────────────────────────╯
Command

actual rules brief

Name the decisions that govern one file and their MUST rules. This is the hook form of rules select: under --claude-hook it reads a Claude Code envelope from stdin and returns the brief as additionalContext on a Read (before the agent's first edit) or on an Edit/Write of a file it never read. It never grants or denies a tool call, uses no runner and no network, and every failure is empty output with exit 0, so a broken brief never breaks an edit loop. --file runs the same selection at a terminal.

actual rules brief --file <path>
actual rules brief --claude-hook
actual rules brief --claude-session-start
FlagTypeDescription
--file <PATH>stringThe file to brief, in direct mode. Ignored under --claude-hook, which takes the path from the hook envelope.
--claude-hookboolRead a Claude Code hook envelope from stdin and emit the hook's JSON contract on stdout. PostToolUse on Read is the main path; PreToolUse on Edit or Write covers files that were never read. Every failure degrades to empty stdout and exit 0.
--claude-session-startboolAnswer a Claude Code SessionStart envelope: after a compact or clear, forget which decisions were briefed so they are briefed again. Prints nothing and always exits 0.
--repo <PATH>stringRepository root. Defaults to the envelope's cwd under --claude-hook, else the current directory.
--rules-dir <PATH>stringRules directory, when it is not <repo>/.actual/rules.
--limit <N>numberMaximum number of decisions to brief. Default 2.
--rules-per-decision <N>numberMaximum number of rules each decision carries. Default 8.
--max-chars <N>numberMaximum size of the whole brief, in characters. Rules are dropped whole, lowest-ranked first, and the brief says how many decisions it left out. Default 4000.
--min-score <S>numberScore a document must reach to be briefed at all. Defaults to the repository's rules_min_score, then the user-wide rules_min_score config key, or no floor.

Budgets and repeats

The brief is capped three ways: --limit counts decisions, --rules-per-decision caps each decision's rules, and --max-chars caps the whole text, dropping the lowest-ranked decisions first and saying how many it left out. Under --claude-hook each decision is briefed once per session, so re-reading a file is silent. Matching is by path first, falling back to text only when the path finds nothing. A SessionStart hook running --claude-session-start resets that memory after a compaction.

Why a brief stayed quiet

In hook mode stdout is empty whenever there is nothing to brief; the reason is one stderr line, actual rules brief: no-brief=<token> <path>. Tokens: ungoverned, already-briefed, outside-root, no-rules (an index that will not build), and nothing-to-show. --file prints the matching sentence on stdout instead.

Example output

For actual rules brief --file src/auth/tokens.ts:

This repository's own rules that apply to `src/auth/tokens.ts`:

## oauth-signing-a1b2
- [oauth-signing-a1b2/R-AUTH-001] MUST Sign access tokens with RS256 using the service private key.
- [oauth-signing-a1b2/R-AUTH-002] MUST NOT Sign access tokens with a shared HMAC secret.

For a file no rule governs, such as README.md:

No rule document governs README.md.
Command

actual plan-check

Check an implementation plan against the selected .actual/rules/ documents before code gets written. Run it directly from a terminal, or in --claude-hook mode, which is what the actual Claude Code plugin's plan-gate hook drives on every ExitPlanMode call, checking the plan against your .actual/rules/ corpus before any code gets written.

actual plan-check "<plan>"
actual plan-check --plan-file plan.md
echo '<hook envelope json>' | actual plan-check --claude-hook --rules-dir .actual/rules
FlagTypeDescription
[PLAN]...positionalThe plan to check. Omit to read from --plan-file or stdin. Ignored under --claude-hook, which resolves the plan from the hook envelope.
--plan-file <PATH>stringRead the plan from this file instead of the positional argument or stdin. Ignored under --claude-hook.
--repo <PATH>stringRepository root to resolve rules and paths against. Defaults to the current directory.
--rules-dir <PATH>stringRules directory to score against, overriding <repo>/.actual/rules.
--claude-hookboolParse a Claude Code PreToolUse hook envelope from stdin and emit the hook's JSON contract on stdout instead of a panel. This is the mode hooks/plan-gate.sh drives.
--limit <N>numberMaximum number of rule documents to judge the plan against. Default 20.
--candidates <N>numberCandidates the deterministic prefilter retrieves before the limit is applied. Default 30.
--no-rankboolSkip stage 2 document ranking; select with the deterministic prefilter alone. --claude-hook always does this regardless of this flag, to keep its one model call for the conformance judge.
--runner <RUNNER>stringRunner for stage 2 selection and the conformance judge: claude-cli, anthropic-api, openai-api, codex-cli, or cursor-cli. Probed automatically when omitted.
--model <MODEL>stringModel for stage 2 selection and the judge, overriding the configured one.
--jsonboolPrint the result as JSON instead of a panel. Ignored under --claude-hook, which always emits the hook's own JSON contract.
--rebuildboolRebuild the scope index before selecting.
--max-rounds <N>numberHow many times a single rule may be denied within one Claude Code session (--claude-hook only) before the gate stops blocking on it specifically. Env: ACTUAL_PLAN_CHECK_MAX_ROUNDS. Default 3.

Exit codes

0 for conforming, requires_decision, and not_checked (no runner available, no applicable rules, or the judge call itself failed) — only a real conflicting verdict exits nonzero. A CI job that gates on exit code alone can't distinguish "checked and clean" from "could not check"; read --json's status field for that distinction.

Example output — direct mode, conflicting plan

Checked against a rules document requiring RS256-signed access tokens, using a real claude-cli judge call:

╭─ Plan check ─────────────────────────────────────────────────────────────────╮
│ Plan: Sign new access tokens using an HMAC shared secret for speed           │
│ Rules dir: ./.actual/rules                                                   │
│ Documents selected: 1                                                        │
│ Runner: claude-cli (claude-sonnet-4-6)                                       │
│ Rules checked: 2                                                             │
├──────────────────────────────────────────────────────────────────────────────┤
│ CONFLICT: R-OAUTH-001 (MUST)                                                 │
│       R-OAUTH-001 requires RS256 with a JWKS-resolved key, but the plan u…   │
│       "Sign new access tokens using an HMAC shared secret for speed"        │
│ DECISION: R-OAUTH-002 (MUST_NOT)                                            │
│       R-OAUTH-002 prohibits HMAC shared secrets, and the plan explicitly …  │
│       "Sign new access tokens using an HMAC shared secret for speed"        │
╰──────────────────────────────────────────────────────────────────────────────╯
Error: plan does not conform: R-OAUTH-001: R-OAUTH-001 requires RS256 with …

$ echo $?
1

Example output — direct mode, conforming plan

╭─ Plan check ─────────────────────────────────────────────────────────────────╮
│ Plan: Rotate JWKS keys and sign new access tokens with RS256, resolving k…   │
│ Rules dir: ./.actual/rules                                                   │
│ Documents selected: 1                                                        │
│ Runner: claude-cli (claude-sonnet-4-6)                                       │
│ Rules checked: 2                                                             │
├──────────────────────────────────────────────────────────────────────────────┤
│ Conforming: no selected rule was violated.                                   │
╰──────────────────────────────────────────────────────────────────────────────╯
$ echo $?
0

Example — --claude-hook mode

What hooks/plan-gate.sh pipes to actual plan-check on stdin (Claude Code's own PreToolUse envelope, trimmed):

{"session_id":"sess-a1b2","tool_input":{"plan":"Sign new access tokens using an HMAC shared secret for speed"}}

And the deny JSON it emits on stdout, which the hook forwards verbatim:

{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"CONFLICT R-OAUTH-001 (MUST): R-OAUTH-001 requires RS256 with JWKS-resolved key, but the plan uses HMAC instead. — rule: \"Sign access tokens using RS256, resolving the signing key id from the published JWKS document.\" — plan: \"Sign new access tokens using an HMAC shared secret for speed\"\nDECISION R-OAUTH-002 (MUST_NOT): R-OAUTH-002 forbids HMAC shared secrets, and the plan explicitly chooses this approach (citing speed as the reason), indicating a deliberate supersession. — rule: \"Sign access tokens with a shared HMAC secret.\" — plan: \"Sign new access tokens using an HMAC shared secret for speed\"\nA revised plan is re-checked automatically. Session: sess-a1b2. A human reviewing this — not the agent — can override a specific rule explicitly by running `actual check-override` from an interactive terminal (see `actual check-override --help` for the exact flags); that command refuses to run non-interactively."}}

The Session in the deny message is what actual check-override (below) takes as --session.

A conforming plan under --claude-hook prints nothing at all — no permission decision means Claude Code's own approval flow proceeds unaffected.

Command

actual impl-check

plan-check's implementation-stage counterpart: the same rule selection, conformance judge, four outcomes, and exit codes, judged against a git diff instead of plan text. By default it diffs the current repository's working tree against HEAD, including untracked files that .gitignore doesn't hide, without touching your git index. Pass --diff-file or pipe a diff on stdin to check something else. The judge only looks at the files and hunks the diff touches: unmodified code is out of scope, and a requirement it can't assess from the hunks counts as conforming.

In --claude-hook mode it's what the actual Claude Code plugin's impl-gate hook runs when Claude Code finishes each turn. It runs every turn, whether or not the session went through plan mode, so work that skipped plan-check is still checked before the agent hands back control.

actual impl-check                        # working tree vs HEAD, incl. untracked files
actual impl-check --diff-file change.diff --json
git diff main...HEAD | actual impl-check  # check a branch instead
echo '<hook envelope json>' | actual impl-check --claude-hook --rules-dir .actual/rules
FlagTypeDescription
--diff-file <PATH>stringRead the diff from this file instead of stdin or the working-tree diff. Ignored under --claude-hook, which always resolves the working-tree diff.
--repo <PATH>stringRepository root to resolve rules, paths, and the working-tree diff against. Defaults to the current directory.
--rules-dir <PATH>stringRules directory to score against, overriding <repo>/.actual/rules.
--claude-hookboolRead a Claude Code hook envelope from stdin (only session_id is used) and emit the hook's JSON contract on stdout instead of a panel. The diff is always the working tree vs HEAD, including untracked files. This is the mode hooks/impl-gate.sh drives.
--limit <N>numberMaximum number of rule documents to judge the diff against. Default 20.
--candidates <N>numberCandidates the deterministic prefilter retrieves before the limit is applied. Default 30.
--no-rankboolSkip stage 2 document ranking; select with the deterministic prefilter alone. --claude-hook always does this regardless of this flag.
--runner <RUNNER>stringRunner for stage 2 selection and the conformance judge: claude-cli, anthropic-api, openai-api, codex-cli, or cursor-cli. Probed automatically when omitted.
--model <MODEL>stringModel for stage 2 selection and the judge, overriding the configured one.
--jsonboolPrint the result as JSON instead of a panel. Ignored under --claude-hook.
--rebuildboolRebuild the scope index before selecting.
--max-rounds <N>numberHow many times a single rule may be denied within one Claude Code session (--claude-hook only) before the gate stops blocking on it. Budgeted separately from plan-check's. Env: ACTUAL_IMPL_CHECK_MAX_ROUNDS. Default 3.

Exit codes

Same as plan-check: only a real conflicting verdict exits nonzero. In CI, read --json's status field to tell "checked and clean" from "could not check".

Example output — direct mode, conflicting diff

A working-tree change that swaps RS256 token signing for an HMAC secret, checked against the same rules document as above with a real claude-cli judge call (trimmed to one verdict):

$ actual impl-check --json
{
  "status": "conflicting",
  "runner": "claude-cli (claude-sonnet-4-6)",
  "documents_selected": 1,
  "verdicts": [
    {
      "doc_slug": "oauth-signing-a1b2",
      "rule_id": "R-OAUTH-002",
      "level": "MUST_NOT",
      "statement": "Sign access tokens with a shared HMAC secret.",
      "verdict": "conflicting",
      "span": "+  const sig = createHmac(\"sha256\", process.env.TOKEN_SECRET!).update(body).digest(\"base64url\");",
      "reason": "R-OAUTH-002 forbids signing access tokens with a shared HMAC secret, but the diff does exactly that using process.env.TOKEN_SECRET."
    }
  ]
}
$ echo $?
1

Example — --claude-hook mode

hooks/impl-gate.sh runs on Claude Code's Stop event and pipes the envelope through. Only session_id is read; the diff always comes from the working tree:

{"session_id":"sess-c3d4","hook_event_name":"Stop","stop_hook_active":false}

The CLI emits the same deny JSON as plan-check --claude-hook. Because Stop hooks use a different decision shape, the hook re-renders it as a block, which sends the reason back to the agent and keeps the turn going until the working tree conforms:

{"decision":"block","reason":"CONFLICT R-OAUTH-002 (MUST_NOT): R-OAUTH-002 forbids signing access tokens with a shared HMAC secret, but the diff introduces exactly that via createHmac with TOKEN_SECRET. — rule: \"Sign access tokens with a shared HMAC secret.\" — diff: \"+  const sig = createHmac(\"sha256\", process.env.TOKEN_SECRET!).update(body).digest(\"base64url\");\"\nA revised working tree is re-checked automatically. Session: sess-c3d4. A human reviewing this — not the agent — can override a specific rule explicitly by running `actual check-override` from an interactive terminal (see `actual check-override --help` for the exact flags); that command refuses to run non-interactively."}

A conforming diff prints nothing and the turn ends normally. As with plan-check, a rule already cleared for the same diff isn't re-judged, and a rule stops blocking after --max-rounds denied rounds. That budget is separate from plan-check's, so plan-stage denials don't use it up. Every infrastructure failure (no CLI, no runner, a failed judge call) fails open with a notice instead of blocking.

Command

actual check-override

Explicitly override one or more rules that plan-check or impl-check has denied, for a specific session — a human action, never the agent's. One override covers both commands for that session. It refuses to run unless a real terminal is attached and the process doesn't carry Claude Code's own environment markers, so an agent's own tool call can't clear its own denial: if you're seeing the refusal from inside Claude Code's integrated terminal, run the command from a separate, plain terminal window instead. Formerly plan-check-override, which still works as an alias.

actual check-override \
  --session <SESSION_ID> \
  --rule <doc-slug>::<rule-id> \
  --reason "<why>"
FlagTypeDescription
--session <SESSION_ID>stringThe session to override, as named in the deny message.
--rule <DOC_SLUG::RULE_ID>stringA rule to clear, as <doc-slug>::<rule-id> (printed in the deny message's suggested override command). Repeatable.
--reason <TEXT>stringWhy this rule is being overridden. Required.
--repo <PATH>stringRepository root to resolve the rules directory against. Defaults to the current directory.
--rules-dir <PATH>stringRules directory this override applies to, overriding <repo>/.actual/rules. Must match what the hook resolved for this session.

Example

actual check-override \
  --session sess-a1b2 \
  --rule oauth-signing-a1b2::R-OAUTH-001 \
  --reason "Migrating legacy service off HMAC in phase 2; RS256 rollout tracked in JIRA-123"

Example output

An overridden rule is excluded from judging for that session going forward, regardless of plan text or diff, and recorded to ~/.actualai/actual/plan-check-overrides.log as a durable audit trail (the log keeps its original name and is shared by both commands):

╭─ Check override recorded ──────────────────────────────────────────────────────╮
│ Session: sess-a1b2                                                             │
│ Rules dir: .actual/rules                                                       │
│ Reason: Migrating legacy service off HMAC in phase 2; RS256 rollout tracked…  │
│ Rule: oauth-signing-a1b2::R-OAUTH-001                                          │
├─────────────────────────────────────────────────────────────────────────────────┤
│ This rule will not be re-checked for this session, and every round from       │
│ now on will say so — an override is recorded, not silent.                     │
╰─────────────────────────────────────────────────────────────────────────────────╯