Skip to content

Show Muse Code as unavailable on iPhone with a truthful schema-1 allowance reader - #525

Merged
HemSoft merged 3 commits into
mainfrom
t3/issue-522-muse-provider
Oct 27, 2026
Merged

HemSoft merged 3 commits into
mainfrom
t3/issue-522-muse-provider

Conversation

@HemSoft

@HemSoft HemSoft commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Closes #522.

There's no supported way to read a Muse Code subscription allowance on iPhone. Meta restricts the subscription credential to the Muse Code CLI ("This credential is for use with Muse Code only"). It documents no OAuth, device-code or client registration for apps, and no network usage endpoint. muse usage and MSP usage/read are CLI and local-host only. Sources and the two missing items are in the issue research comment and MUSE-CODE.md. No auth path is invented, no CLI client is reused, nothing is scraped, and token activity is never quota.

What ships:

  • User-visible: Add Account says "Muse Code isn't available on iPhone" with the reason. Nothing else changes for users.

  • Muse Code can't become an account. ProviderID.museCode exists, but one predicate (isAvailableOnIPhone) blocks it everywhere:

    • the Add Account list;
    • store insertion through both addAccount and update, on Free and Pro, with no paywall;
    • canRefresh and refresh();
    • permitsRefresh and permitsInsertion, so it never takes the free account slot.

    It isn't a widget focus choice and has no demo or App Store screenshot sample.

  • Schema-1 reader: MuseCodeAllowance follows the shipped producer contract (hs-pi-dashboard#71, codexbar#274), so a future supported source has a tested path.

    • allowance:null is unavailable, not 0.
    • reason/hint are never read.
    • Windows are validated independently: used % 0-100, capped for display with the raw forecast kept, and resets bounded by the window.
    • Undocumented windows, token totals and billing objects never become bars.
    • Tier and source are allowlisted. Wonder Full is never inferred.
  • Distinct unavailable states: not on iPhone, allowance not reported, unsupported format, unreadable, measurement missing or in the future, no valid windows. Sign-in expired, entitlement, PAYG, rate limit, timeout and transport are constructed states, because their wire shape is unknown. None of them shows zero or healthy.

  • Freshness: a reading measured more than 7 minutes earlier keeps its bars as last known (barsFetchedAt = measurement time). It raises no new alerts, writes no history and gets no forecast.

  • No SUS: no per-tile badge (including on unavailable tiles), no fleet entry and no price.

  • UI fixture: the DEBUG simulator scenario muse-code-states renders synthetic states through the real dashboard for the evidence below. The shipping app can't reach these cards.

Acceptance criteria

  • Schema 1 with nullable allowance. Null is distinct from a numeric 0. reason/hint never surface (a synthetic email and API-key fixture proves it). Unsupported or missing schema versions fail safely. Billing, tier, scope, unit and per-model fields aren't reported by schema 1; documented as not reported rather than preserved.
  • Named Muse Code. The allowlisted tier, observation time ("Measured") and source are kept. Wonder Full isn't shown because the source doesn't identify it. OpenCode Muse Spark and PAYG pools are untouched.
  • Short-window and weekly used % and resets are kept independently, with direction and units from the pinned contract (used, 0-100). No caps are derived. Undocumented windows produce a message, never a bar. Reset rollover drops only the elapsed window.
  • Missing, stale, malformed, unsupported and acquisition-blocked states are distinct and unavailable. Valid sibling windows survive. Last-good bars keep their original time (preserveCachedBarsOnFailure).
  • Tokens and activity never become quota (tokensOnly and extraWindow fixtures). No SUS, projection or alert without a current, valid window.
  • Blocked, as the issue allows: no supported iPhone authorization-to-allowance path exists. The exact missing items are recorded. No desktop export, CLI, PAYG key or undocumented endpoint is required or used.
  • The capability limit is shown to users, live quota stays blocked, and the native state and schema work is complete without fabricated data.
  • Surfaces: fixture round trips through the widget snapshot, the watch snapshot (encode and decode) and history. No credential reaches an extension. Existing providers' raw values and saved configurations are unchanged.

Validation

Head 6c4f7f7 on base f2ce58a (main). 6c4f7f7 only adds a fixture-coverage unit test after the independent audit; the app source and screenshots are unchanged from bd7fda5. Run on Air with Xcode, iPhone 17 simulator (iOS latest).

  • Canonical SwiftLint gate (swift package plugin … swiftlint lint): 0 violations in 287 files.
  • Targeted unit run (MuseCodeAllowanceTests, AppAndWidgetTests, DashboardAndSettingsTests, AccountPlanAccessTests, UsageAlertTests) at a37725a: 285/285 passed.
  • Full CodexBarIOSTests at bd7fda5: 1,182/1,182 passed, 0 skipped.
  • SwiftPM smoke harness CodexBarIOSSmokeTests: passed.
  • At 6c4f7f7: MuseCodeAllowanceTests with coverage passed. Every function in UITestMuseCodeFixture.swift and MuseCodeAllowance.swift is at 100% except one trivial getter at 75%, so the release function-risk gate stays under 30. The full pinned measure.py run is left to the release gate.
  • Watch tests not run: no watch target source changed; the watch snapshot round trip is covered in MuseCodeAllowanceTests.
  • Release UI gate not dispatched (routine PR).
  • Automatic CI is unchanged: no workflow, trigger, job, matrix, retry or timeout changes. New tests live in CodexBarIOSTests, which only runs on manual dispatch.
  • Live checks: none claimed. Franz's real-account phone sign-in and quota comparison stay pending and aren't possible until Meta ships the two items above.

Screenshots

iPhone 17 simulator, light theme, default text size. The source is identical to bd7fda5 plus a local throwaway UI driver that only taps and scrolls (not committed). All Muse cards are synthetic muse-code-states fixtures that the shipping app can't reach. The green header dot on failed and stale cards is the existing shared card behaviour for every provider. Recording omitted: the only shipping change is static footer text with no interaction.

Shipping change. Add Account explains that Muse Code isn't available on iPhone:

Add Account footer: Muse Code isn't available on iPhone

Fixture: allowance null (fixed text, no reason/hint, no SUS on the unavailable weekly tile), then exhausted (5-hour capped at 100%, red):

Fixture: allowance null and exhausted

Fixture: exhausted weekly forecast, then measured 15% / 5% with plan pill and resets:

Fixture: exhausted weekly and measured

Fixture: not on iPhone (the acquisition-limit state):

Fixture: not on iPhone

Fixture: partial (5-hour invalid and named, weekly kept), then sign-in expired asking to update credentials:

Fixture: partial and sign-in expired

Fixture: stale. Bars greyed as last known, no forecast, measurement age stated:

Fixture: stale

Council

Council: none consulted · missing: GPT 6.1 Sol (Codex weekly pause, 188% projected, usage.py 20:00 ET), Muse Wonder Full 1.0 (muse-code Weekly pause, 245% projected) · Council unavailable.

A fresh, independent Claude Opus 5.5 design critique ran as a P0 safeguard; it isn't a seat. Adopted 9 points:

  • one predicate in place of a single gate;
  • refresh and smoke exclusion with no demo sample;
  • a per-tile SUS opt-out;
  • no widget focus;
  • a plain-text notice in place of a disabled row;
  • a reset-rollover fixture;
  • metric-choice and Pro/account tests;
  • no over-claim of billing, scope or unit fields;
  • the parser listed under Developer Experience.

Declined 1: an out-of-order observation guard. No acquisition path exists to produce one; it's recorded as a requirement for a future source in MUSE-CODE.md.

🤖 Generated with Claude Code

HemSoft and others added 2 commits October 26, 2026 20:15
…der (#522)

Meta documents no sign-in or usage service an iPhone app can use for Muse
Code subscriptions, so Add Account explains the limit and Muse Code can't be
added, refreshed, scored or hold an account slot. A native reader for
`muse usage --json` schema 1 maps fixtures to distinct, truthful states for
any future supported source. Contract and limits are in MUSE-CODE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…like refreshes (#522)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T03:49:17.408302Z 6c4f7f7 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@HemSoft

HemSoft commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

1 similar comment
@HemSoft

HemSoft commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@HemSoft HemSoft left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Independent Claude audit-pr review of bd7fda5 (Codex review hung >1h after re-request)

Reviewed head bd7fda54e99e25e54d3cf66ab56413435d74e39d against base and merge-base f2ce58a (main tip, so the branch is up to date). I read the full diff, the issue #522 body and research comment, and the pinned hs-pi-dashboard contract at 588be88.

Findings

1. Medium: the new DEBUG fixture adds an uncovered production function with CRAP 56, which will fail the manual iOS tests function-risk gate before release

  • Anchor: CodexBarIOS/Services/UITestMuseCodeFixture.swift:60-80, private static func result(for:configuration:now:).
  • Complexity: the pinned SwiftLint 0.65.1 with scripts/function-risk/complexity.yml measures CC = 7 for this function (seven switch cases). I ran this locally.
  • Coverage: the file is compiled into the CodexBarIOS app Sources phase. scripts/function-risk/policy.json does not exclude it; it excludes only UITestFixtures.swift and AppStoreScreenshotFixtures.swift. Nothing in CodexBarIOSTests references UITestMuseCodeFixture or the muse-code-states scenario, so its executable-line coverage in the unit-test result bundle is 0.
  • Score: CRAP = 7² × (1 − 0)³ + 7 = 56, above 30. measure.py will report New production risk above 30 for this function. baseline.json has no iOS high_risk ceilings to absorb it. The iOS tests job in a release or on-demand ci.yml dispatch will fail, and AGENTS.md requires that job to pass before a release.
  • Precedent: the other two non-excluded UI fixture files, UITestOpenCodeRenewalProvider and UITestSubscriptionBillingProtocol, are both exercised from CodexBarIOSTests. This new file is the only one that isn't.
  • Evidence basis: inspection plus the measured complexity. I did not run the full coverage suite.
  • Fix, either:
    • add a unit test that calls UITestMuseCodeFixture.seed and results(in:scenario:"muse-code-states") on an isolated store and asserts each state's outcome; this also locks the screenshot evidence to real behavior; or
    • add a reviewed excluded_files entry with a rationale. That would need a FUNCTION-RISK.md note, per its change policy.
  • Verify: run-pro-release-validation.sh native followed by measure.py --platform ios reports no errors.

No other actionable findings survived verification.

Verified (no finding)

  • Gating holds. No production path can create, refresh or publish a Muse Code account:
    • allowAccountInsertion blocks both addAccount and new-account update/save paths before the Pro check, so no paywall appears;
    • canRefresh, refresh(), permitsRefresh and permitsInsertion all check isAvailableOnIPhone, so a Muse record never holds the free slot;
    • a deep link only scrolls to existing cards;
    • every ProviderID.allCases consumer is filtered, apart from keychain cleanup, which is harmless.
  • Compatibility. Widget and watch targets don't compile ProviderID. Existing raw values are unchanged and tested, and old persisted configurations still decode.
  • Contract match. The parser follows the pinned contract:
    • field names, 0–100 used direction, cap at 100 with the raw forecast kept;
    • reset after both asOfMs and now, and within the window duration;
    • 60 s future tolerance, and the tier and source allowlist regexes;
    • undocumented windows are named or counted, never shown as bars;
    • reason and hint are never read;
    • JSONValue keeps "5" and true from becoming numbers.
  • Fixtures. measured, partial, malformed-values and extra-window match the producer fixtures byte for byte.
  • SUS. SUS is suppressed at every badge site, in the tile accessibility text and in the fleet entry.
  • Freshness. A stale reading gets barsFetchedAt = asOf, so hasCurrentBars is false, there's no projection, and no alert or history is written (tested).
  • Changelog and docs. The user-facing line is under Added and the parser is under Developer Experience. README and MUSE-CODE.md match the code.
  • Delivery.
    • SwiftLint, the only required ruleset check, passes on a pull_request run for bd7fda5.
    • Automatic CI is unchanged: no workflow edits.
    • The Validation section has inline current-head screenshots, and the reason for omitting a recording is stated.

Notes (non-blocking)

  • Owner acceptance needed. Issue AC 1 and 2 ask to "preserve" unknown billing, scope and unit fields. The PR deliberately drops them; for example, billing.mode in extra-window is ignored. The PR body states this, but the issue owner should accept the deviation.
  • Message wording. A window whose reset has passed is reported as "Muse Code sent an invalid value". The producer contract does the same, and the code is unreachable in production today, but a future source may want a distinct message.
  • Merge controls.
    • Codex review for bd7fda5 is still running.
    • Live account checks remain pending for the account owner.
    • The manual release gates (iOS tests with function risk, watch tests, full UI validation) have not run on this head.

…s within limits (#522)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HemSoft

HemSoft commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the audit finding (Medium, function risk) in 6c4f7f7:

  • testUIEvidenceFixtureRendersEveryStateThroughTheProductionMapping seeds muse-code-states on an isolated store, checks all seven states, and covers the off-scenario guards and presentFailures.

  • xccov on MuseCodeAllowanceTests with -enableCodeCoverage YES:

    • every function in UITestMuseCodeFixture.swift is at 100%, including result(for:configuration:now:) (21/21) and parse(observedAgo:…) (12/12);
    • every function in MuseCodeAllowance.swift is at 100%, except JSONValue.stringValue at 75% (complexity 2, CRAP ≈ 2).

    CRAP for the fixture function is now 7, against the limit of 30.

  • The full measure.py release run (pinned Xcode 26.6 native validation) remains part of the release gate. It wasn't rerun for this routine PR.

  • Canonical SwiftLint: 0 violations.

The two notes are unchanged by design:

  • Schema 1 reports no billing, scope or unit fields, so they're documented as not reported. billing.mode and token totals are ignored rather than shown.
  • A window whose reset has passed is reported as invalid, which matches the producer contract.

@HemSoft

HemSoft commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

Reviewed commit: 6c4f7f7bea

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@HemSoft
HemSoft merged commit fc55494 into main Oct 27, 2026
8 checks passed
@HemSoft
HemSoft deleted the t3/issue-522-muse-provider branch October 27, 2026 03:50
HemSoft added a commit that referenced this pull request Oct 27, 2026
…ui-shards

Integrate #525 (Muse Code allowance reader). Resolve the CHANGELOG
Developer Experience conflict by keeping both the #517 and #522 entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Establish Muse Code quota acquisition and truthful native provider states

1 participant