fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(pricing): resolve fable/mythos canary variants via fuzzy match - #188

Merged
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing
Aug 22, 2026
Merged

fix(pricing): resolve fable/mythos canary variants via fuzzy match#188
pitimon merged 1 commit into
mainfrom
fix/187-fable-mythos-canary-pricing

Conversation

@pitimon

Copy link
Copy Markdown
Owner

PR Goal (one sentence)

Add curated fuzzy pricing rules so Claude Fable/Mythos canary model ids (e.g. claude-auto-pilot-fable-v1-canary) resolve to their pinned price instead of silently billing $0.

Scope

  • CLI (src/)
  • Dashboard (dashboard/)
  • Docs / CI / config

Checklist

  • npm run ci:local passes, or focused test commands are listed below with rationale
  • New dashboard/user-facing strings go through dashboard/src/content/copy.csv (N/A — no new strings)
  • Commits follow conventional style (feat: / fix: / refactor: / docs: / chore: / test: / ci:)
  • PR description explains why, not just what
  • Version bump files are in lockstep when release-bound (package.json, package-lock.json) — deferred to a follow-up publish PR per CLAUDE.md release workflow
  • npm publish state is recorded when release-bound — N/A for this PR, publish handled separately after merge

Why

src/lib/pricing/matcher.js resolves pricing through 7 tiers, and every tier (including the reverse-substring fuzzy fallback) requires the queue model id to contain the known price key as a contiguous substring. claude-auto-pilot-fable-v1-canary does not contain claude-fable-5 contiguously (-auto-pilot- interrupts it, and it ends in -v1-canary instead of -5), so it fell through every tier to misscomputeRowCost() returned 0 → the dashboards formatCost()returnsnullfor cost <= 0, hiding the$column entirely instead of showing$0.00`.

This is the same class of gap that #7 fixed for the literal claude-fable-5/claude-mythos-5 ids, but that fix does not cover vendor-renamed canary variants that wrap the name with extra segments.

Fixes#187.

Fix

Two curated fuzzy rules added to src/lib/pricing/curated-overrides.json, matching the existing kiro/hy3/composer fuzzy pattern:

{ "match": "fable", "ref": "claude-fable-5" },
{ "match": "mythos", "ref": "claude-mythos-5" },

Codex Context (required when requesting @codex review)

  • Delta since last Codex review: N/A, first review on this branch
  • Intended behavior / invariants: any model id containing the substring fable or mythos (case-insensitive) resolves via curated:fuzzy to the pinned Fable 5 / Mythos 5 price. Curated exact match and LiteLLM exact/dot match still take priority over this fuzzy rule per the existing resolve order.
  • Edge cases covered:claude-auto-pilot-fable-v1-canary, claude-mythos-v1-canary (regression test added); existing claude-fable-5/claude-mythos-5 exact ids continue to resolve via curated:exact (higher-priority tier, unaffected).
  • Tests run (command + result):
    • node --test test/pricing.test.js → 34/34 pass (includes new regression case)
    • NODE_ENV=test npm run ci:local → full gate pass (dashboard build, dashboard tests 318/318, product suite 1054+38, copy/UI-hardcode/architecture-guardrail/curated-expiry/outbound/openwiki validators)
  • Known gaps / out of scope: does not add a generic "strip auto-pilot/canary infix" normalizer — scoped to the two known-affected pinned names per issue Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings #187. A more general normalizer would be a separate proposal if more vendor-renamed variants surface.

Risk Layer Trigger (if any)

  • Public exposure / share links / unauthenticated access
  • Auth/session/token handling
  • Cross-endpoint invariants or shared logic
  • External gateway / environment constraints

(No risk-layer trigger — pure pricing-lookup data change, no I/O/auth/exposure surface touched.)

Regression Test Gate

Most likely regression surface

  • Existing fuzzy-matched models get overridden or shadowed by the new fable/mythos fuzzy rules if their ids happen to contain those substrings.

Verification method (choose at least one)

Uncovered scope

  • Future vendor-renamed model families that are not literally named "fable" or "mythos" will need their own curated fuzzy rule when they surface (same as any other model addition).

claude-auto-pilot-fable-v1-canary and claude-mythos-*-canary model ids
wrap the pinned claude-fable-5/claude-mythos-5 names with extra
prefix/suffix segments (auto-pilot-, -v1-canary), so "fable"/"mythos"
is not a contiguous substring of the curated exact key. Every
non-fuzzy resolution tier in matcher.js requires a contiguous
substring match, so these ids fell through to miss and billed $0 —
the dashboard hid the cost column entirely (formatCost() returns null
for cost <= 0).
Add curated fuzzy rules for "fable" and "mythos", matching the
existing kiro/hy3/composer fuzzy pattern.
Fixes#187
@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@pitimon
pitimon merged commit 6ac961d into mainAug 22, 2026
2 checks passed
@pitimon
pitimon deleted the fix/187-fable-mythos-canary-pricing branch August 22, 2026 14:46
@pitimonpitimon mentioned this pull request Aug 22, 2026
11 tasks
pitimon added a commit that referenced this pull request Aug 22, 2026
Version bump for the fable/mythos canary pricing fix (#187, merged in
#188). Updates the version-lockstep test's hardcoded package.json
expectation in step.
npm publish --dry-run verified: 174 files, tarball ok.
Co-authored-by: itarun.p <itarun.p@somapait.com>
pitimon added a commit that referenced this pull request Aug 22, 2026
…closure (#194)
Two follow-ups from the #187/#188 cross-verification review.
1. matcher.js: add a scoped denoise retry (issue #193). #188 fixed the
fable/mythos canary regression with a per-name curated fuzzy rule,
but left the structural gap open — any future auto-pilot/canary
rename of an already-priced model would reproduce the same bug and
require another one-line patch. The denoise retry strips the
'-auto-pilot-' infix and trailing '-vN-canary'/'-canary' suffix and
retries the full lookup ladder exactly once, only after every other
tier has missed on the raw id, so a genuine exact match on the raw
id (however unlikely) is never shadowed.
Also narrows the #188 fuzzy rules from bare 'fable'/'mythos'
substrings to 'claude-fable'/'claude-mythos'. Verified against the
published pricing seed that a real collision risk existed:
claude-mythos-preview is a genuine LiteLLM-priced model that
happens to contain the substring 'mythos' — the bare rule would
have silently mispriced any future model in the same shape that
lacks its own exact LiteLLM/curated entry.
2. dashboard: wire the already-computed fuzzyPricingModels (from
model-breakdown.ts) into ProviderBreakdownCard (issue #192). The
provider card already discloses '{{count}} pricing missing' for
unpriced models; it now also discloses '{{count}} pricing
estimated' when any model resolved via substring/fuzzy match
rather than an exact price, so a user sees when a total like
$3,164.69 is a curated estimate, not a reconciled invoice figure.
Verified: node --test test/pricing.test.js (39/39), dashboard
ProviderBreakdownCard suite (10/10, incl. 2 new), npm run ci:local
full gate (dashboard 320/320, product suite 1059/1059, all
validators) — all green.
Addresses #193, #192.
Co-authored-by: itarun.p <itarun.p@somapait.com>
Sign up for freeto 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.

Claude auto-pilot/canary model ids (fable/mythos) bill $0 — fuzzy pricing match misses non-contiguous substrings

1 participant

@pitimon