# Files changed · PR \#22990 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/22990/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[fix\(mobile\): subscription widget no longer expires on a window that already reset](https://git.cafe/pingdotgg/t3code/pull/22990.md)

Version: 32

Head commit: 02932550995a8d55c28a336c343c56a557721c43

Base commit: 8d3b061121fe65669d5d43d011b48cd3027a4108

Comparison base: 8d3b061121fe65669d5d43d011b48cd3027a4108

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/mobile/scripts/seed-widget-fixture-22973.mts

Status: added; +142; −0

```diff
@@ -1,0 +1,142 @@
+// Seeds the Android subscription widget with a fixed fixture for the #22973
+// before/after evidence. It temporarily patches the working tree so the real
+// snapshot -> publishSubscriptionUsage -> widget.updateSnapshot path runs on
+// fixture providers instead of connected environments. Nothing changes until
+// it runs, and `revert` restores the tracked files from HEAD.
+//
+//   node scripts/seed-widget-fixture-22973.mts apply <fixture> [--anchor <iso>] [--before [ref]]
+//   node scripts/seed-widget-fixture-22973.mts revert
+//   node scripts/seed-widget-fixture-22973.mts list
+//
+// Fixture times are minutes from the anchor (default: now, rounded down to the
+// minute). Reuse one anchor for a before/after pair so both render identical
+// reset labels. `--before` swaps in subscriptionUsageSnapshot.ts from the
+// merge-base so the same hook runs over the pre-fix code. Reload the app
+// (Metro picks the change up) after each apply.
+import * as NodeChildProcess from "node:child_process";
+import * as NodeFS from "node:fs";
+import * as NodePath from "node:path";
+import * as NodeURL from "node:url";
+
+const BEFORE_REF = "8d3b061121fe65669d5d43d011b48cd3027a4108";
+const appRoot = NodePath.join(NodePath.dirname(NodeURL.fileURLToPath(import.meta.url)), "..");
+const coordinator = NodePath.join(appRoot, "src/widgets/SubscriptionUsageCoordinator.tsx");
+const snapshotModule = NodePath.join(appRoot, "src/widgets/subscriptionUsageSnapshot.ts");
+const fixtureModule = NodePath.join(appRoot, "src/widgets/widgetFixture22973.ts");
+const tracked = [coordinator, snapshotModule];
+
+const codex = (
+  checked: number,
+  session: number,
+  weekly: number,
+  instance = "codex",
+  email = "a@example.com",
+) =>
+  `prov("codex", ${JSON.stringify(instance)}, ${JSON.stringify(email)}, ${checked}, [W("primary", "session", "5 hours", 0, ${session}, 300), W("secondary", "weekly", "Weekly", 30, ${weekly}, 10080)])`;
+const week = 7 * 1440;
+const fixtures: Record<string, string[]> = {
+  F1: [codex(0, 45, week)],
+  F2: [codex(0, -15, week)],
+  F3: [codex(0, -15, -1)],
+  F4: [codex(0, 0, week)],
+  F5: [codex(0, 5, week)],
+  F6: [codex(-16, 45, week)],
+  F7a: [
+    codex(-5, 45, week, "codex-0", "a@example.com"),
+    codex(0, -3, week, "codex-1", "b@example.com"),
+  ],
+  F7b: [
+    codex(-5, -3, week, "codex-0", "a@example.com"),
+    codex(0, 45, week, "codex-1", "b@example.com"),
+  ],
+  F8: [
+    `prov("claudeAgent", "claude", "c@example.com", 0, [W("five_hour", "session", "Session", 10, -20, 300), W("seven_day", "weekly", "Weekly", 50, ${6 * 1440}, 10080), W("seven_day_claude_opus_4_7_extended_thinking", "weekly", "Weekly · Claude Opus 4.7 Extended Thinking Preview", 95, ${6 * 1440})])`,
+  ],
+  F9: [
+    codex(0, -15, week),
+    `prov("claudeAgent", "claude", "c@example.com", 0, [W("five_hour", "session", "Session", 10, 120, 300)])`,
+  ],
+};
+
+const git = (...args: string[]) =>
+  NodeChildProcess.execFileSync("git", args, { cwd: appRoot, encoding: "utf8" });
+
+function revert() {
+  git("checkout", "HEAD", "--", ...tracked);
+  NodeFS.rmSync(fixtureModule, { force: true });
+}
+
+function apply(name: string, anchor: number, beforeRef: string | undefined) {
+  const providers = fixtures[name];
+  if (!providers)
+    throw new Error(`Unknown fixture ${name}; known: ${Object.keys(fixtures).join(", ")}`);
+  if (!NodeFS.existsSync(fixtureModule) && git("status", "--porcelain", "--", ...tracked).trim())
+    throw new Error("Widget sources have uncommitted changes; commit or revert them first.");
+  revert();
+  if (beforeRef)
+    NodeFS.writeFileSync(
+      snapshotModule,
+      git("show", `${beforeRef}:apps/mobile/src/widgets/subscriptionUsageSnapshot.ts`),
+    );
+  NodeFS.writeFileSync(
+    fixtureModule,
+    `// Generated by scripts/seed-widget-fixture-22973.mts; removed by its revert command.
+const ANCHOR = ${anchor}; // ${new Date(anchor).toISOString()}
+const iso = (minutes: number) => new Date(ANCHOR + minutes * 60_000).toISOString();
+const W = (id: string, kind: string, label: string, usedPercent: number, reset: number, windowDurationMins?: number) => ({
+  id, kind, label, usedPercent, resetsAt: iso(reset), ...(windowDurationMins ? { windowDurationMins } : {}),
+});
+const prov = (driver: string, instanceId: string, email: string, checked: number, windows: unknown[]) => ({
+  instanceId, driver, enabled: true, installed: true, version: null, status: "ready",
+  auth: { status: "authenticated", email }, checkedAt: iso(checked), models: [], slashCommands: [], skills: [],
+  usageLimits: { checkedAt: iso(checked), windows },
+});
+export function widgetFixture22973<T>(): T {
+  return new Map([["env", { entry: { target: { label: "Remote" } }, serverConfig: { providers: [
+    ${providers.join(",\n    ")},
+  ] } }]]) as T;
+}
+`,
+  );
+  const source = NodeFS.readFileSync(coordinator, "utf8");
+  const read = "get(environmentPresentations.presentationsWithProvidersAtom),";
+  if (!source.includes(read))
+    throw new Error("SubscriptionUsageCoordinator.tsx no longer matches the hook");
+  NodeFS.writeFileSync(
+    coordinator,
+    source
+      .replace(read, `widgetFixture22973<Parameters<typeof buildSubscriptionUsageSnapshot>[0]>(),`)
+      .replace(
+        `import { createWidgetTheme } from "./widgetTheme";`,
+        `import { createWidgetTheme } from "./widgetTheme";\nimport { widgetFixture22973 } from "./widgetFixture22973";`,
+      ),
+  );
+  console.log(
+    `Applied ${name} (${beforeRef ? `before: ${beforeRef.slice(0, 10)}` : "after: HEAD"}) anchored at ${new Date(anchor).toISOString()}`,
+  );
+}
+
+const [command, name, ...rest] = process.argv.slice(2);
+const flag = (key: string) => {
+  const index = rest.indexOf(key);
+  if (index === -1) return undefined;
+  const value = rest[index + 1];
+  return value && !value.startsWith("--") ? value : "";
+};
+if (command === "apply" && name) {
+  const anchorFlag = flag("--anchor");
+  const anchor = anchorFlag ? Date.parse(anchorFlag) : Math.floor(Date.now() / 60_000) * 60_000;
+  if (!Number.isFinite(anchor)) throw new Error(`Invalid --anchor ${anchorFlag}`);
+  const before = flag("--before");
+  apply(name, anchor, before === undefined ? undefined : before || BEFORE_REF);
+} else if (command === "revert") {
+  revert();
+  console.log("Reverted widget fixture hook");
+} else if (command === "list") {
+  console.log(Object.keys(fixtures).join("\n"));
+} else {
+  console.error(
+    "Usage: seed-widget-fixture-22973.mts apply <fixture> [--anchor <iso>] [--before [ref]] | revert | list",
+  );
+  process.exit(1);
+}
```

## apps/mobile/src/widgets/subscriptionUsageSnapshot.test.ts

Status: modified; +219; −0

```diff
@@ -9,6 +9,7 @@
 import {
   buildSubscriptionUsageSnapshot,
   createWidgetRefresher,
+  subscriptionUsageTimeline,
   WIDGET_REFRESH_INTERVAL,
 } from "./subscriptionUsageSnapshot";
 
@@ -135,6 +136,224 @@
   });
 });
 
+describe("subscription widget expiry", () => {
+  const observedAt = "2026-10-21T16:45:00.000Z";
+  const observedNow = Date.parse(observedAt);
+  const maxAgeDeadline = Date.parse("2026-10-21T17:00:00.000Z");
+
+  function snapshot(
+    sessionReset = "2026-10-21T16:30:00.000Z",
+    weeklyReset = "2026-10-28T16:45:00.000Z",
+    maxWindowsPerProvider = 6,
+  ) {
+    return buildSubscriptionUsageSnapshot(
+      presentations([
+        provider({
+          usageLimits: {
+            checkedAt: observedAt,
+            windows: [
+              {
+                id: "session",
+                kind: "session",
+                label: "5 hours",
+                usedPercent: 0,
+                resetsAt: sessionReset,
+              },
+              {
+                id: "weekly",
+                kind: "weekly",
+                label: "Weekly",
+                usedPercent: 30,
+                resetsAt: weeklyReset,
+              },
+            ],
+          },
+        }),
+      ]),
+      deepLink,
+      maxWindowsPerProvider,
+    );
+  }
+
+  it.each([
+    { platform: "iOS", maxWindowsPerProvider: 6 },
+    { platform: "Android", maxWindowsPerProvider: Infinity },
+  ])(
+    "$platform keeps both freshly observed rows when only the session reset is in the past",
+    ({ maxWindowsPerProvider }) => {
+      const pastReset = snapshot(undefined, undefined, maxWindowsPerProvider);
+      const futureReset = snapshot("2026-10-21T17:30:00.000Z", undefined, maxWindowsPerProvider);
+      expect(futureReset.providers[0]).toMatchObject({
+        detail: "Subscription remaining",
+        expiresAt: maxAgeDeadline,
+        totalWindows: 2,
+        windows: [
+          { kind: "weekly", remaining: 70 },
+          { kind: "session", remaining: 100 },
+        ],
+      });
+      expect(pastReset.checkedAt).toBe(observedNow);
+      expect(pastReset.providers[0]).toMatchObject({
+        detail: "Subscription remaining",
+        expiresAt: maxAgeDeadline,
+        totalWindows: 2,
+        windows: [
+          { kind: "weekly", remaining: 70 },
+          { kind: "session", remaining: 100 },
+        ],
+      });
+      expect(pastReset.providers[0]?.windows[1]?.reset).toMatch(/^Reset /);
+      expect(futureReset.providers[0]?.windows[1]?.reset).toMatch(/^Next reset /);
+    },
+  );
+
+  it("ignores resets at observation time and preserves reported past-reset usage", () => {
+    const stored = buildSubscriptionUsageSnapshot(
+      presentations([
+        provider({
+          usageLimits: {
+            checkedAt: observedAt,
+            windows: [{ ...window, usedPercent: 40, resetsAt: observedAt }],
+          },
+        }),
+      ]),
+      deepLink,
+    );
+    expect(stored.providers[0]).toMatchObject({
+      expiresAt: maxAgeDeadline,
+      windows: [{ remaining: 60, reset: expect.stringMatching(/^Reset /) }],
+    });
+  });
+
+  it("compares resets against each provider's own observation time", () => {
+    const resetAt = "2026-10-21T16:50:00.000Z";
+    const stored = buildSubscriptionUsageSnapshot(
+      presentations([
+        provider({
+          usageLimits: {
+            checkedAt: observedAt,
+            windows: [{ ...window, resetsAt: resetAt }],
+          },
+        }),
+        provider({
+          instanceId: ProviderInstanceId.make("claude"),
+          driver: ProviderDriverKind.make("claudeAgent"),
+          usageLimits: {
+            checkedAt: "2026-10-21T16:55:00.000Z",
+            windows: [{ ...window, resetsAt: resetAt }],
+          },
+        }),
+      ]),
+      deepLink,
+    );
+    expect(stored.providers).toMatchObject([
+      { name: "Codex", expiresAt: Date.parse(resetAt) },
+      { name: "Claude", expiresAt: Date.parse("2026-10-21T17:10:00.000Z") },
+    ]);
+  });
+
+  it.each([
+    {
+      observation: "newer",
+      resetAccount: 1,
+      expiresAt: "2026-10-21T16:55:00.000Z",
+      resetLabel: /^Reset /,
+    },
+    {
+      observation: "older",
+      resetAccount: 0,
+      expiresAt: "2026-10-21T16:42:00.000Z",
+      resetLabel: /^Next reset /,
+    },
+  ])(
+    "compares a pooled reset with its $observation account observation",
+    ({ resetAccount, expiresAt, resetLabel }) => {
+      const stored = buildSubscriptionUsageSnapshot(
+        presentations(
+          ["2026-10-21T16:40:00.000Z", observedAt].map((accountCheckedAt, index) =>
+            provider({
+              instanceId: ProviderInstanceId.make(`codex-${index}`),
+              auth: { status: "authenticated", email: `account-${index}@example.com` },
+              usageLimits: {
+                checkedAt: accountCheckedAt,
+                windows: [
+                  {
+                    ...window,
+                    resetsAt:
+                      index === resetAccount
+                        ? "2026-10-21T16:42:00.000Z"
+                        : "2026-10-21T17:30:00.000Z",
+                  },
+                ],
+              },
+            }),
+          ),
+        ),
+        deepLink,
+      );
+      expect(stored.checkedAt).toBe(Date.parse("2026-10-21T16:40:00.000Z"));
+      expect(stored.providers[0]).toMatchObject({
+        detail: "2 accounts · pooled",
+        expiresAt: Date.parse(expiresAt),
+        totalWindows: 1,
+        windows: [{ remaining: 60, reset: expect.stringMatching(resetLabel) }],
+      });
+      expect(
+        subscriptionUsageTimeline(stored, observedNow)[0]?.props.providers[0]?.windows,
+      ).toHaveLength(resetAccount === 1 ? 1 : 0);
+    },
+  );
+
+  it("keeps both rows in the current timeline entry despite the past session reset", () => {
+    const timeline = subscriptionUsageTimeline(snapshot(), observedNow);
+    expect(timeline.map((entry) => entry.date.toISOString())).toEqual([
+      observedAt,
+      "2026-10-21T17:00:00.000Z",
+    ]);
+    expect(timeline[0]?.props.providers[0]).toMatchObject({
+      detail: "Subscription remaining",
+      totalWindows: 2,
+      windows: [
+        { kind: "weekly", remaining: 70 },
+        { kind: "session", remaining: 100 },
+      ],
+    });
+    expect(timeline[1]?.props.providers[0]).toMatchObject({
+      detail: "Open T3 to refresh",
+      totalWindows: 0,
+      windows: [],
+    });
+  });
+
+  it("expires a snapshot older than 15 minutes even with future reset times", () => {
+    const stored = snapshot("2026-10-21T17:30:00.000Z");
+    expect(stored.providers[0]?.expiresAt).toBe(maxAgeDeadline);
+    expect(subscriptionUsageTimeline(stored, Date.parse("2026-10-21T17:00:00.001Z"))).toMatchObject(
+      [
+        {
+          props: {
+            providers: [{ detail: "Open T3 to refresh", totalWindows: 0, windows: [] }],
+          },
+        },
+      ],
+    );
+  });
+
+  it("expires at a future reset sooner than the 15 minute age limit", () => {
+    const resetAt = "2026-10-21T16:50:00.000Z";
+    const stored = snapshot(resetAt);
+    expect(stored.providers[0]?.expiresAt).toBe(Date.parse(resetAt));
+    const timeline = subscriptionUsageTimeline(stored, observedNow);
+    expect(timeline.map((entry) => entry.date.toISOString())).toEqual([observedAt, resetAt]);
+    expect(timeline[0]?.props.providers[0]?.windows).toHaveLength(2);
+    expect(timeline[1]?.props.providers[0]).toMatchObject({
+      detail: "Open T3 to refresh",
+      totalWindows: 0,
+      windows: [],
+    });
+  });
+});
+
 describe("widget refresh probes", () => {
   it("does not overlap a slow probe even after the refresh interval", async () => {
     let finish!: () => void;
```

## apps/mobile/src/widgets/subscriptionUsageSnapshot.ts

Status: modified; +11; −2

```diff
@@ -76,7 +76,11 @@
         const checkedAt = Math.min(...pool.accounts.map((a) => Date.parse(a.limits.checkedAt)));
         const expiresAt = Math.min(
           checkedAt + SNAPSHOT_MAX_AGE,
+          ...pool.windows.flatMap((window) =>
+            window.resets
+              .filter((reset) => reset.at > Date.parse(reset.member.account.limits.checkedAt))
+              .map((reset) => reset.at),
-          ...pool.windows.flatMap((window) => window.resets.map((reset) => reset.at)),
+          ),
         );
         const fresh = Number.isFinite(expiresAt) && expiresAt > now;
         const sortedWindows = [...pool.windows].sort(
@@ -107,7 +111,12 @@
                 label: window.label,
                 remaining: Math.round(window.remainingPercent),
                 reset: window.resets[0]
+                  ? `${
+                      window.resets[0].at >
+                      Date.parse(window.resets[0].member.account.limits.checkedAt)
-                  ? `Next reset ${new Date(window.resets[0].at).toLocaleString(undefined, {
+                        ? "Next reset"
+                        : "Reset"
+                    } ${new Date(window.resets[0].at).toLocaleString(undefined, {
                       month: "short",
                       day: "numeric",
                       hour: "numeric",
```
