Skip to content

Show the policy rule that would flip an existing allow decision - #292

Merged
karagozemin merged 3 commits into
Fortexa-stellar:mainfrom
blocorbitstech:fix/issue-214-flip-rule-report
Oct 1, 2026
Merged

karagozemin merged 3 commits into
Fortexa-stellar:mainfrom
blocorbitstech:fix/issue-214-flip-rule-report

Conversation

@blocorbitstech

Copy link
Copy Markdown
Contributor

Closes #214

What

GET /api/policy/history now accepts a read-only ?candidate=<version> query and reports, before that candidate is ever activated, which open allow decisions it would turn into a denial — and which rule id is responsible.

  • Compare by rule id — diffDecisionImpact (src/lib/validation/diff.ts) diffs the candidate against the active policy through generatePolicyDiff, so every change is keyed by a rule id (blockedDomains[api.safe-research.ai], perTxCapXLM, allowedHours, …).
  • Re-run the open allow decisions — openAllowDecisions collects the newest recorded result per action from the audit history and keeps the ones still allowed (APPROVE/WARN), each with its usage snapshot. Every one is re-run through the decision engine under both the active and the candidate policy.
  • List the flipped payment ids and the responsible rule — for a payment that would flip, the changed rules are applied one at a time until one of them turns the allow into a denial; that rule id is reported next to the payment id.
  • Read-only — the candidate is evaluated, never activated: no policy write, no history append, no usage consumed.

Response shape:

{
  "entries": [ ... ],
  "decisionImpact": {
    "candidateVersion": 3,
    "activeVersion": 4,
    "flippedIds": ["payment-allow-1"],
    "flips": [{ "paymentId": "payment-allow-1", "ruleId": "blockedDomains[api.safe-research.ai]", "from": "APPROVE", "to": "BLOCK" }],
    "hasDecisionChange": true,
    "status": "decision change"
  }
}

Without ?candidate= the response keeps decisionImpact: null, so existing consumers are unaffected. An invalid candidate returns 400, an unknown one 404.

Acceptance criteria

  • A rule edit that flips an allow is listed before activation
  • A rule edit that flips nothing is reported as no decision change
  • The history route and the diff helper return the same flipped ids (asserted directly in the route test)
  • The test does not write a new active policy (active version and policy asserted identical before/after the read)

Validation

  • npm test → 69 files, 589 tests passing (including the new src/lib/validation/diff.test.ts and the extended history route tests)
  • npm run lint → 0 errors

Note on the first commit

main contained merge damage that stopped everything from compiling (unbalanced braces in src/lib/security/analyzer.ts, a useCallback-style close on a plain arrow in use-auth-session.ts, a duplicated binding in the audit export test, plus bare @ts-expect-error directives). No suite importing the decision engine could run at all, so the repair is included as a separate commit to keep the feature commit reviewable.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff noreply@codebuff.com

blocorbitstech and others added 2 commits September 30, 2026 08:45
Bad merges left three files uncompilable (unbalanced braces in
fetchBlocklistWithTimeout, a refresh arrow function still closed like a
useCallback, and a duplicated response binding in the audit export test),
which stopped every suite importing the decision engine from running.
Also restore the "timeout" blocklist status the analyzer tests assert and
add the required descriptions to bare @ts-expect-error directives.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Operators could activate a candidate policy that silently turns a live
allow into a denial because diff, history and the decision engine each
described changes differently. The history route now accepts a
read-only ?candidate=<version> query and returns the payment ids whose
result would change plus the rule id responsible, sourced from the same
diff helper so both always agree. Nothing is activated by the read.

Closes Fortexa-stellar#214

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Sep 30, 2026

Copy link
Copy Markdown

@blocorbitstech Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@karagozemin
karagozemin merged commit b002335 into Fortexa-stellar:main Oct 1, 2026
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.

Show the policy rule that would flip an existing allow decision

2 participants