Skip to content

fix(registry,aggregator): specificity-sorted priority + soft-cap enforcement + Twilio price docs (#13, #21)#38

Merged
AndresL230 merged 9 commits into
mainfrom
feat/13-21-provider-registry-overhaul
May 19, 2026
Merged

fix(registry,aggregator): specificity-sorted priority + soft-cap enforcement + Twilio price docs (#13, #21)#38
AndresL230 merged 9 commits into
mainfrom
feat/13-21-provider-registry-overhaul

Conversation

@AndresL230
Copy link
Copy Markdown
Contributor

@AndresL230 AndresL230 commented May 15, 2026

Summary

Wave 4 — three correctness fixes in the provider registry / aggregator plus Twilio pricing source-of-truth comments, bundled as one PR. Also marks Wave 3 done with its merged PR link.

  • Provider registry: custom catch-alls shadow built-ins; cardinality leaks; soft bucket cap #13.1 — Custom catch-all shadowed built-ins. A user-provided rule like { hostPattern: "api.openai.com", provider: "openai-custom" } (no pathPrefix) was prepended unconditionally and silently disabled every built-in OpenAI path-specific rule. Fix: at construction, merge custom + built-in rules and stably sort by specificity descending — rules with pathPrefix before those without; longer pathPrefix first; exact host before *. wildcard; custom-wins-on-tie. The custom catch-all now only matches paths no built-in covers.
  • Provider registry: custom catch-alls shadow built-ins; cardinality leaks; soft bucket cap #13.2 — Catch-all leaked high-cardinality paths. match() returned the raw pathname as endpointCategory for host-only catch-alls, so every Twilio account SID became a unique aggregator bucket. Fix: catch-all returns the literal "other". Twilio's refiner still produces "sms" / "voice_calls" for known paths; its fallback also changed from raw pathname to "other".
  • Provider registry: custom catch-alls shadow built-ins; cardinality leaks; soft bucket cap #13.3 — Soft cap bypassed by async gap. init.ts calls aggregator.wouldOverflow(event) and then flushAndSend() (async) before falling through to aggregator.ingest(event). Events ingested between the hint and the resolved flush still landed in the full map. Fix: Aggregator.ingest() now synchronously redirects over-cap new-key events into a per-provider _overflow bucket. Counts / latencies / bytes / cost are still accumulated; only endpointCategory cardinality is bounded. New overflowCount getter reports redirects, resets on flush. wouldOverflow() keeps its early-flush hint role.
  • Twilio pricing constants (0.79¢, 1.3¢) have no source-of-truth comment #21 — Twilio pricing comments. Added source URLs and reviewed 2026-05-15 notes above the three Twilio cost constants (0.79¢ SMS, 1.3¢ voice, 0.5¢ default).

No public API additions beyond Aggregator.overflowCount. No changes to init.ts or any other file.

Roadmap

First commit also flips Wave 3 to done with merged PR link (#35) and Wave 4 to in-progress with this plan — leftover docs maintenance bundled per the Wave 3 -> Wave 4 handoff convention.

Test plan

Plan

See docs/superpowers/plans/2026-05-15-provider-registry-overhaul.md.

Closes #13, closes #21.

Summary by CodeRabbit

  • New Features

    • Custom provider rules now merge with built-ins and sort by specificity (pathPrefix, host type, custom wins ties)
    • Added overflow tracking for ingested events when bucket cap is reached
  • Bug Fixes

    • Unknown provider paths now categorize as "other" for consistent bucketing
  • Documentation

    • Updated README with custom provider priority rules and sorting precedence
    • Added Wave 4 implementation plan and roadmap updates

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Warning

Rate limit exceeded

@AndresL230 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 58 minutes and 45 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 20610be6-26d4-4593-b766-b6a2811f4358

📥 Commits

Reviewing files that changed from the base of the PR and between 90c2e4d and 3da79a7.

📒 Files selected for processing (2)
  • CLAUDE.md
  • docs/superpowers/plans/2026-05-15-provider-registry-overhaul.md
📝 Walkthrough

Walkthrough

This PR implements Wave 4 of the provider registry overhaul, fixing custom rule shadowing, endpoint cardinality leaks, and async bucket cap gaps. Provider registry rules are now specificity-sorted by pathPrefix presence and length, exact host matching, and custom tie-break semantics. Unknown endpoint paths normalize to "other" to prevent cardinality growth. The aggregator enforces the bucket cap synchronously via per-provider _overflow bucket redirection.

Changes

Provider Registry and Aggregator Wave 4 Overhaul

Layer / File(s) Summary
Wave 4 plan and roadmap alignment
docs/superpowers/plans/2026-05-15-provider-registry-overhaul.md, docs/superpowers/roadmap-2026-05-13-issue-waves.md
Wave 4 plan document details the four-part implementation (rule sorting, catch-all normalization, soft cap enforcement, pricing documentation) with step-by-step task workflows and expected test deltas. Roadmap updated: Wave 3 marked done with merged PR link; Wave 4 marked in-progress with plan reference.
Provider registry rule sorting and endpoint normalization
src/core/provider-registry.ts
Rules are merged (custom + built-in) and sorted by specificity: pathPrefix presence/length, exact host vs *. wildcard, custom wins on tie. match() returns "other" for unknown paths instead of raw pathname. Twilio refinement preserves known-path categorization (/Messagessms) but returns "other" for unrecognized paths. Updated documentation and Twilio pricing comments.
Provider registry test validation
tests/provider-registry.test.ts
Tests updated: catch-all categories now expect "other"; custom provider tie-breaker covers equal-specificity override semantics; new suite validates custom catch-alls do not shadow built-in path-specific rules, custom pathPrefix beats catch-alls, exact-host beats wildcard-host, and list() ordering is deterministic and specificity-driven; Twilio SMS/voice refinement regression guard retained.
Aggregator soft cap enforcement
src/core/aggregator.ts
ingest() now enforces maxBuckets cap synchronously by redirecting new keys into per-provider _overflow bucket and incrementing _overflowCount. Field resets to 0 on flush(). Public overflowCount getter exposes the redirect count. wouldOverflow() JSDoc clarified as a hint only, not a guarantee.
Aggregator soft cap tests
tests/aggregator.test.ts
New test suite validates soft-cap ingest-time behavior: new keys redirect to _overflow endpoint at cap, existing keys ingest normally, multiple overflow events collapse per (provider, method), and overflowCount getter resets after flush().
User documentation and README updates
README.md
customProviders description clarified: rules are merged with built-ins and sorted by specificity, with custom tie-break. New "Custom provider priority" subsection documents the exact precedence and includes example showing custom catch-all does not shadow built-in path-specific rules but custom pathPrefix does.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

A rabbit hops through registry rules, 🐰
Sorting with care, no sorting duels,
Specificity wins, ties fairly split—
Overflows now capped, no more data spit!
Wave 4 complete, the provider's outfit! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: specificity-sorted priority in the registry, soft-cap enforcement in the aggregator, and Twilio pricing documentation, with issue references.
Linked Issues check ✅ Passed All coding requirements from #13 (specificity sorting, cardinality prevention via 'other' fallback, soft-cap enforcement) and #21 (Twilio pricing source comments) are implemented and tested.
Out of Scope Changes check ✅ Passed All changes directly address the linked issues: registry/aggregator fixes, Twilio pricing comments, README docs, tests, and roadmap updates. No unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/13-21-provider-registry-overhaul

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/superpowers/plans/2026-05-15-provider-registry-overhaul.md`:
- Around line 1237-1255: Several fenced code blocks in the
provider-registry-overhaul plan lack language tags, causing markdownlint MD040
and downstream parsing issues; update the code fences around the "init({
customProviders... })" example to use ```ts, change the surrounding
narrative/code blocks that show headings to ```markdown, and change the
changelog-style block to ```text (apply the same fixes to the other occurrences
noted around the Cleanup/teardown and changelog sections referenced in the
comment); ensure each opening triple-backtick has an appropriate language
identifier matching the block content.

In `@src/core/provider-registry.ts`:
- Around line 191-201: The constructor currently mixes customProviders and
BUILTIN_PROVIDERS together and sorts them jointly, changing the declared
precedence; restore "custom prepended before built-ins" by sorting
customProviders and BUILTIN_PROVIDERS separately with compareRules and then
assigning this._rules to the concatenation of the sorted custom list followed by
the sorted builtin list (use the existing compareRules, ProviderDef type,
BUILTIN_PROVIDERS, and _rules names to locate and implement this change).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 78932933-beef-4cc8-b317-559446161f39

📥 Commits

Reviewing files that changed from the base of the PR and between c2ad485 and 90c2e4d.

📒 Files selected for processing (7)
  • README.md
  • docs/superpowers/plans/2026-05-15-provider-registry-overhaul.md
  • docs/superpowers/roadmap-2026-05-13-issue-waves.md
  • src/core/aggregator.ts
  • src/core/provider-registry.ts
  • tests/aggregator.test.ts
  • tests/provider-registry.test.ts

Comment thread docs/superpowers/plans/2026-05-15-provider-registry-overhaul.md Outdated
Comment on lines +191 to +201
* @param customProviders - Optional extra rules. Merged with built-ins and
* sorted by specificity (longer `pathPrefix` first, exact host before
* wildcard, custom-wins-on-tie). See the class JSDoc for the full rule.
*/
constructor(customProviders: ProviderDef[] = []) {
this._rules = [...customProviders, ...BUILTIN_PROVIDERS];
const tagged: { rule: ProviderDef; custom: boolean }[] = [
...customProviders.map((rule) => ({ rule, custom: true })),
...BUILTIN_PROVIDERS.map((rule) => ({ rule, custom: false })),
];
tagged.sort(compareRules);
this._rules = tagged.map((t) => t.rule);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Custom-provider precedence contract changed away from prepended-first behavior.

The constructor no longer prepends customProviders before built-ins; it globally sorts both lists, which changes the declared precedence model for this module. If this behavior change is intentional, the repository rule for this file should be updated in the same PR; otherwise restore prepended custom priority to match the contract.

As per coding guidelines, src/core/provider-registry.ts: “Implement Provider Registry with 34 built-in rules covering 14 providers, wildcard host matching, and custom provider priority prepended before built-ins”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/provider-registry.ts` around lines 191 - 201, The constructor
currently mixes customProviders and BUILTIN_PROVIDERS together and sorts them
jointly, changing the declared precedence; restore "custom prepended before
built-ins" by sorting customProviders and BUILTIN_PROVIDERS separately with
compareRules and then assigning this._rules to the concatenation of the sorted
custom list followed by the sorted builtin list (use the existing compareRules,
ProviderDef type, BUILTIN_PROVIDERS, and _rules names to locate and implement
this change).

- CLAUDE.md: update registry contract to reflect specificity-sorted
  rules with custom-wins-on-tie (the PR intentionally moved away from
  unconditional custom-prepended priority to stop custom catch-alls
  shadowing built-in path-specific rules)
- plan doc: use 4-backtick outer fences around the README-replacement
  blocks so the nested ```ts examples parse correctly, and tag the
  expected-commit-list block as ```text to satisfy MD040
@AndresL230 AndresL230 merged commit c6c2a64 into main May 19, 2026
1 check passed
@AndresL230 AndresL230 deleted the feat/13-21-provider-registry-overhaul branch May 21, 2026 04:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants