mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-06 15:39:41 +02:00
fix(plan-usage): make the usage chip per-device, not synced
The plan-usage header chip (5h/7d %) was a SYNCED setting, so enabling it on desktop turned it on for mobile too — even though the user never enabled it there. Make the chip's DISPLAY purely per-device (default OFF) like the response viewer / skin, while keeping telemetry COLLECTION server-side. Three leak sources fixed: - server.ts renderIndexHtml force-revealed the chip from the synced value (pre-paint), pushing the desktop choice onto every device. Removed — the chip now ships hidden and the client reveals it per-device via applyHeaderVisibilitySettings. - settings-ui.js load-merge let the server value win, writing desktop's `true` into the (separate) mobile settings blob. showPlanUsageLimits is now a displayKey AND is dropped from the server payload on load, so a stale server value is never seeded into a device that didn't enable it. It's also stripped from the save payload so a mobile "off" can't clobber the server. - Collection was gated on the same synced flag. Decoupled via a new `statusLineTelemetry` ACTION field (schema + system-routes): sent on ENABLE only and never persisted, so the exporter is injected when a device turns the chip on but is never yanked when another device has it off (it's shared across sibling sessions). Session-create already reads the per-device blob, so that path was already correct. One-time migration clears a stale synced `true` from the mobile blob so existing mobile installs default to OFF without a manual toggle. Verified end-to-end on an isolated server: with showPlanUsageLimits=true persisted, the rendered HTML ships the chip hidden; a fresh browser context (mobile case) keeps it hidden while a context that explicitly enabled it shows it; the PUT accepts statusLineTelemetry and does not persist it. tsc + frontend-syntax + system-routes/index tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -531,7 +531,10 @@ export function registerSystemRoutes(
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
const merged = { ...existing, ...settings };
|
||||
// statusLineTelemetry is an ACTION field (reconcile the plan-usage exporter),
|
||||
// not a stored setting — strip it before persisting so settings.json stays clean.
|
||||
const { statusLineTelemetry, ...settingsToStore } = settings;
|
||||
const merged = { ...existing, ...settingsToStore };
|
||||
await fs.writeFile(SETTINGS_PATH, JSON.stringify(merged, null, 2));
|
||||
|
||||
// Handle subagent tracking toggle dynamically
|
||||
@@ -547,19 +550,20 @@ export function registerSystemRoutes(
|
||||
}
|
||||
});
|
||||
|
||||
// Plan-usage chip (App Settings → Display → "Plan Usage Limits"): reconcile
|
||||
// the statusLine exporter across all ACTIVE Claude sessions' working dirs the
|
||||
// moment the setting is toggled — server-side and authoritative, so it works
|
||||
// immediately for every user/session without depending on any client's synced
|
||||
// state or on creating a new session. Enable injects; disable removes (ours
|
||||
// only, marker-guarded). Each dir handled once.
|
||||
if ('showPlanUsageLimits' in settings) {
|
||||
const enabled = settings.showPlanUsageLimits === true;
|
||||
// Plan-usage chip: its DISPLAY is per-device (client-side, see settings-ui.js).
|
||||
// Telemetry COLLECTION is server-side and enable-sticky — when a client turns
|
||||
// the chip ON it sends statusLineTelemetry:true and we (re)inject our exporter
|
||||
// into every ACTIVE Claude session's working dir so the live % starts flowing
|
||||
// immediately (no new session needed). We deliberately never auto-REMOVE here:
|
||||
// the exporter is benign/print-through and a per-repo settings.local.json is
|
||||
// shared by sibling sessions, so one device's "off" must not yank the exporter
|
||||
// another device's chip depends on. Each dir handled once.
|
||||
if (statusLineTelemetry === true) {
|
||||
const dirs = new Set<string>();
|
||||
for (const session of ctx.sessions.values()) {
|
||||
if (session.mode === 'claude' && session.workingDir) dirs.add(session.workingDir);
|
||||
}
|
||||
await Promise.all([...dirs].map((dir) => applyStatusLineConfig(dir, enabled).catch(() => {})));
|
||||
await Promise.all([...dirs].map((dir) => applyStatusLineConfig(dir, true).catch(() => {})));
|
||||
}
|
||||
|
||||
// Handle tunnel toggle dynamically
|
||||
|
||||
Reference in New Issue
Block a user