Skip to content

docs: bind marketing numbers to the published artifact (and fix four that were wrong)#220

Merged
VickyXAI merged 1 commit into
mainfrom
feat/brand-numbers-sync
Jul 26, 2026
Merged

docs: bind marketing numbers to the published artifact (and fix four that were wrong)#220
VickyXAI merged 1 commit into
mainfrom
feat/brand-numbers-sync

Conversation

@VickyXAI

@VickyXAI VickyXAI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Consumes blockrun#288's /brand/numbers.json.

What was wrong

Claim Was Now Note
Savings (README blurb) 92% 87% unqualified "up to", no baseline
Savings (pricing section) 92% 87% / 98% derived from a hand-computed "$2.05/M blended average"
Savings (package.json) 85% 87% a third, different number in the same repo
Model count (7 places) 55+ 66 catalog moved, copy didn't
Scoring dimensions 14 (in code comments) 15 see below

The dimensions one is the interesting failure

I set the artifact to 14 because every comment in the scorer says 14 — config.ts:7, rules.ts:4, rules.ts:148. The README said 15 and I took it for the stale one.

Writing the test proved it backwards. rules.ts builds a 14-entry array and then does this:

const agenticResult = scoreAgenticTask(userText, config.agenticTaskKeywords);
dimensions.push(agenticResult.dimensionScore);

15 dimensions are scored. The README was right; the comments were stale and had been for a while. Comments corrected, artifact publishes 15.

This is the argument for the test living here rather than a number being asserted in blockrun: reading the code got it wrong, running it got it right.

The savings claim now states its method

Instead of an "up to" figure against an unstated baseline:

87% cheaper than pinning Claude Opus 5 for the same traffic, on auto; 98% on eco.

Not an "up to" figure. The baseline, the workload mix and the token ratio are published in savings-mix.json, priced against the live catalog, so anyone can recompute it and get the same answer.

Models withheld from /v1/models are excluded from the mix — pricing a public claim on a model a reader cannot look up is not defensible — which makes the number conservative.

Two surfaces markers cannot reach

scripts/sync-brand-numbers.mjs only rewrites markdown. Two places carry numbers it cannot touch, so src/router/brand-numbers.test.ts asserts them instead:

  • package.json description — npm renders it on the package page; not markdown
  • the hero shields badge — the count is inside an HTML attribute, where a marker would break the tag

Two CI gaps found while wiring this up

  • npm test was never run. The Build & Test job only ran the lifecycle integration test, so all 713 unit tests could fail without failing CI. Now wired in (currently 713 pass, 3 skipped).
  • Brand numbers are now checked offline against the committed snapshot.

Note on the vendored script

scripts/sync-brand-numbers.mjs is byte-for-byte from the source repo and added to .prettierignore — formatting it here would fork it from the copy in the other 36 repos.

Left alone deliberately

  • The tier table under "How It Works" names specific models per tier (free/mistral-large-3-675b, kimi-k2.7, …). Some look stale against config.ts, but model names are not what this artifact owns and verifying them is a routing-config question, not a numbers one. Worth a separate pass.
  • ~44 BlockRun models in the setup warning refers to what gets synced into OpenClaw's allowlist, not the catalog — left as-is rather than guessed at.

Summary by CodeRabbit

  • New Features

    • Added centralized brand-number configuration for model counts, routing metrics, and savings.
    • Added automatic synchronization and validation of published figures across documentation and marketing content.
  • Documentation

    • Updated model counts, savings figures, and scoring information throughout public documentation.
  • Tests

    • Added consistency checks for published metrics and enabled unit tests in CI.
    • Added offline verification to detect outdated brand numbers.

…that were wrong

The savings claim alone existed as three different numbers inside this repo —
92% in the README blurb, the same 92% derived from a hand-computed "$2.05/M
blended average", and 85% in package.json — none of them reproducible, all
against an unstated baseline. Model counts said 55+; the catalog serves 66.

Numbers now regenerate from blockrun.ai/brand/numbers.json via markers:

  reduces AI API costs by up to <!-- br:savings.autoVsBaselinePct -->87<!-- ... -->%

and the savings section states the method instead of an "up to": 87% on auto,
98% on eco, against pinning Claude Opus 5 for 100% of traffic, priced on the
live catalog from a published workload mix.

The dimension count was wrong in the other direction, and only writing the test
found it. Every comment in the scorer says 14 — but rules.ts builds a 14-entry
list and then pushes the agentic dimension, so 15 are actually scored. The
README was right and the code comments were stale. Comments corrected; the
artifact now publishes 15.

src/router/brand-numbers.test.ts pins the four facts BlockRun publishes about
this repo but cannot count from its own catalog. They are asserted over there,
so without a test here a scorer change would quietly make 37 READMEs wrong.
It also covers the two surfaces markers cannot reach: package.json's
description, which npm renders and which is not markdown, and the hero badge,
where the count lives inside an HTML attribute.

Two CI gaps closed while wiring this up:

- npm test was never run. The Build & Test job only ran the lifecycle
  integration test, so all 713 unit tests could fail without failing CI.
- brand numbers are now checked offline against the committed snapshot.

scripts/sync-brand-numbers.mjs is vendored byte-for-byte and added to
.prettierignore — formatting it here would fork it from the source repo.
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds centralized brand-number data, a synchronization CLI for marked documentation, consistency tests, updated dynamic marketing metadata, and CI steps for unit tests and offline drift validation.

Changes

Brand number synchronization

Layer / File(s) Summary
Published numbers and router consistency
brand-numbers.json, src/router/config.ts, src/router/rules.ts, src/router/brand-numbers.test.ts
Defines published model, routing, dimension, and savings counts; updates scoring comments; and adds tests comparing those values with router configuration and repository content.
Marker synchronization CLI
scripts/sync-brand-numbers.mjs
Loads brand-number data, renders marked values, validates markers, traverses eligible text files, updates drifted files, and reports check-mode results.
Dynamic documentation and metadata
README.md, CLAUDE.md, package.json
Replaces fixed model, savings, and dimension values with synchronized markers and updates package description metrics.
Automated workflow validation
.github/workflows/ci.yml, .prettierignore
Runs unit tests and offline brand-number checks in CI, and excludes the vendored synchronization script from formatting.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant BrandSnapshot
  participant SyncCLI
  participant RepositoryFiles
  participant CI
  BrandSnapshot->>SyncCLI: load brand-numbers.json
  SyncCLI->>RepositoryFiles: scan and render br markers
  RepositoryFiles-->>SyncCLI: return updated or drifted content
  SyncCLI-->>CI: return check status
Loading

Suggested reviewers: 1bcmax

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the PR’s main change: syncing marketing numbers to the published artifact and correcting stale claims.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/brand-numbers-sync

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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 `@README.md`:
- Line 37: Update the savings claim in the README description so it does not
present the auto-profile value from br:savings.autoVsBaselinePct as the maximum;
either use the eco savings value for “up to” or explicitly state that 87%
applies to auto, while preserving the existing marker-driven wording.
- Line 715: Update the opening ClawRouter description to use the hyphenated
adjective “MIT-licensed” in the parenthetical phrase, leaving the rest of the
description unchanged.
- Line 207: Update the dimension value in the fenced flow diagram from 14 to 15
so it matches the current weighted scorer dimension count, or move the diagram
outside the fenced block so sync-brand-numbers can detect future drift.

In `@scripts/sync-brand-numbers.mjs`:
- Around line 216-222: Update the main file-processing loop around syncFile so
changed content is only collected in drifted and not written immediately. After
all files are processed, validate problems and exit on failure before any
writes; then perform writeFileSync for the collected changes only when
validation succeeds, preserving check-mode behavior.
- Around line 38-40: Update the argument handling in the sync script around the
check and refresh flags to reject when both --check and --refresh are supplied,
before any refresh or network work begins. Emit a clear usage error and
terminate without modifying brand-numbers.json; preserve the existing behavior
for each flag when used independently.
- Around line 193-200: Update the walk generator to use non-following symlink
metadata when inspecting each path, and skip symlink entries before recursing or
yielding files. Preserve traversal for regular directories and supported text
files while ensuring symlinked directories and files are never followed or
modified.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d88da6d-ff1c-4c23-8726-3ab7771c54d6

📥 Commits

Reviewing files that changed from the base of the PR and between 40c7a2a and 2ab5754.

📒 Files selected for processing (10)
  • .github/workflows/ci.yml
  • .prettierignore
  • CLAUDE.md
  • README.md
  • brand-numbers.json
  • package.json
  • scripts/sync-brand-numbers.mjs
  • src/router/brand-numbers.test.ts
  • src/router/config.ts
  • src/router/rules.ts

Comment thread README.md
</div>

> **ClawRouter** is an open-source smart LLM router that reduces AI API costs by up to 92%. It analyzes each request across 15 dimensions and routes to the cheapest capable model in under 1ms, entirely locally. ClawRouter is the only LLM router built for autonomous AI agents — it uses wallet signatures for authentication (no API keys) and USDC micropayments via the x402 protocol (no credit cards). 55+ models from OpenAI, Anthropic, Google, xAI, DeepSeek, and more. MIT licensed.
> **ClawRouter** is an open-source smart LLM router that reduces AI API costs by up to <!-- br:savings.autoVsBaselinePct -->87<!-- /br:savings.autoVsBaselinePct -->%. It analyzes each request across <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions --> dimensions and routes to the cheapest capable model in under 1ms, entirely locally. ClawRouter is the only LLM router built for autonomous AI agents — it uses wallet signatures for authentication (no API keys) and USDC micropayments via the x402 protocol (no credit cards). <!-- br:models.chatVisible -->66<!-- /br:models.chatVisible --> models from OpenAI, Anthropic, Google, xAI, DeepSeek, and more. MIT licensed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not label the auto figure as the maximum.

This marker renders the auto-profile value (87%), while the same README documents 98% savings for eco. Either use the eco value for “up to” or change the wording to “87% on auto.”

🧰 Tools
🪛 LanguageTool

[grammar] ~37-~37: Ensure spelling is correct
Context: ... to the cheapest capable model in under 1ms, entirely locally. ClawRouter is the on...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 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 `@README.md` at line 37, Update the savings claim in the README description so
it does not present the auto-profile value from br:savings.autoVsBaselinePct as
the maximum; either use the eco savings value for “up to” or explicitly state
that 87% applies to auto, while preserving the existing marker-driven wording.

Comment thread README.md

```
Request → Weighted Scorer (15 dimensions) → Tier → Best Model → Response
Request → Weighted Scorer (<!-- br:clawrouter.dimensions -->14<!-- /br:clawrouter.dimensions --> dimensions) → Tier → Best Model → Response

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the dimension value inside the fenced flow diagram.

Markers inside fenced code blocks are intentionally ignored by scripts/sync-brand-numbers.mjs, so this value will remain 14 and CI will not detect drift. Change it to 15, or move the diagram outside the fenced block.

Proposed fix
-Request → Weighted Scorer (<!-- br:clawrouter.dimensions -->14<!-- /br:clawrouter.dimensions --> dimensions) → Tier → Best Model → Response
+Request → Weighted Scorer (15 dimensions) → Tier → Best Model → Response
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Request → Weighted Scorer (<!-- br:clawrouter.dimensions -->14<!-- /br:clawrouter.dimensions --> dimensions) → Tier → Best Model → Response
Request → Weighted Scorer (15 dimensions) → Tier → Best Model → Response
🤖 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 `@README.md` at line 207, Update the dimension value in the fenced flow diagram
from 14 to 15 so it matches the current weighted scorer dimension count, or move
the diagram outside the fenced block so sync-brand-numbers can detect future
drift.

Comment thread README.md
### What is ClawRouter?

ClawRouter is an open-source (MIT licensed) smart LLM router built for autonomous AI agents. It analyzes each request across 15 dimensions and routes to the cheapest capable model in under 1ms, entirely locally — no external API calls needed for routing decisions.
ClawRouter is an open-source (MIT licensed) smart LLM router built for autonomous AI agents. It analyzes each request across <!-- br:clawrouter.dimensions -->15<!-- /br:clawrouter.dimensions --> dimensions and routes to the cheapest capable model in under 1ms, entirely locally — no external API calls needed for routing decisions.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the hyphenated adjective “MIT-licensed.”

The phrase appears parenthetically before “smart LLM router,” so it should be hyphenated.

🧰 Tools
🪛 LanguageTool

[grammar] ~715-~715: Use a hyphen to join words.
Context: ...uter? ClawRouter is an open-source (MIT licensed) smart LLM router built for aut...

(QB_NEW_EN_HYPHEN)


[grammar] ~715-~715: Ensure spelling is correct
Context: ... to the cheapest capable model in under 1ms, entirely locally — no external API cal...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 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 `@README.md` at line 715, Update the opening ClawRouter description to use the
hyphenated adjective “MIT-licensed” in the parenthetical phrase, leaving the
rest of the description unchanged.

Source: Linters/SAST tools

Comment on lines +38 to +40
const argv = new Set(process.argv.slice(2));
const check = argv.has("--check");
const refresh = argv.has("--refresh");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject --check --refresh together.

This combination takes the refresh path, fetching from the network and overwriting brand-numbers.json, despite --check being documented as offline and non-mutating.

Proposed fix
 const check = argv.has("--check");
 const refresh = argv.has("--refresh");
+if (check && refresh) fail("--check and --refresh cannot be combined");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const argv = new Set(process.argv.slice(2));
const check = argv.has("--check");
const refresh = argv.has("--refresh");
const argv = new Set(process.argv.slice(2));
const check = argv.has("--check");
const refresh = argv.has("--refresh");
if (check && refresh) fail("--check and --refresh cannot be combined");
🧰 Tools
🪛 ESLint

[error] 38-38: 'process' is not defined.

(no-undef)

🤖 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 `@scripts/sync-brand-numbers.mjs` around lines 38 - 40, Update the argument
handling in the sync script around the check and refresh flags to reject when
both --check and --refresh are supplied, before any refresh or network work
begins. Emit a clear usage error and terminate without modifying
brand-numbers.json; preserve the existing behavior for each flag when used
independently.

Comment on lines +193 to +200
function* walk(dir) {
for (const name of readdirSync(dir)) {
if (SKIP_DIRS.has(name)) continue;
const p = join(dir, name);
const s = statSync(p);
if (s.isDirectory()) yield* walk(p);
else if (TEXT_EXT.has(extname(name))) yield p;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not follow symlinks during repository traversal.

statSync() follows source-controlled symlinks. A symlinked directory can recurse outside the repository or loop indefinitely; a symlinked Markdown file can cause normal sync mode to overwrite its target.

Proposed fix
-import { readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
+import { readFileSync, writeFileSync, readdirSync, lstatSync } from "node:fs";

-    const s = statSync(p);
+    const s = lstatSync(p);
+    if (s.isSymbolicLink()) continue;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function* walk(dir) {
for (const name of readdirSync(dir)) {
if (SKIP_DIRS.has(name)) continue;
const p = join(dir, name);
const s = statSync(p);
if (s.isDirectory()) yield* walk(p);
else if (TEXT_EXT.has(extname(name))) yield p;
}
function* walk(dir) {
for (const name of readdirSync(dir)) {
if (SKIP_DIRS.has(name)) continue;
const p = join(dir, name);
const s = lstatSync(p);
if (s.isSymbolicLink()) continue;
if (s.isDirectory()) yield* walk(p);
else if (TEXT_EXT.has(extname(name))) yield p;
}
}
🤖 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 `@scripts/sync-brand-numbers.mjs` around lines 193 - 200, Update the walk
generator to use non-following symlink metadata when inspecting each path, and
skip symlink entries before recursing or yielding files. Preserve traversal for
regular directories and supported text files while ensuring symlinked
directories and files are never followed or modified.

Comment on lines +216 to +222
for (const file of walk(ROOT)) {
const { before, after, changed, used } = syncFile(file, numbers, problems);
used.forEach((k) => everUsed.add(k));
if (!changed) continue;
drifted.push({ file: relative(ROOT, file), before, after });
if (!check) writeFileSync(file, after);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate marker problems before writing any files.

The loop writes each changed file before Lines 224-227 check problems. A nested or unbalanced marker therefore exits with failure after partially updating documentation. Collect drift first, fail on problems, then write only when validation succeeds.

🤖 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 `@scripts/sync-brand-numbers.mjs` around lines 216 - 222, Update the main
file-processing loop around syncFile so changed content is only collected in
drifted and not written immediately. After all files are processed, validate
problems and exit on failure before any writes; then perform writeFileSync for
the collected changes only when validation succeeds, preserving check-mode
behavior.

@VickyXAI
VickyXAI merged commit c6587c1 into main Jul 26, 2026
4 checks passed
@VickyXAI
VickyXAI deleted the feat/brand-numbers-sync branch July 26, 2026 05:21
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