mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-07 16:09:43 +02:00
fix(usage): address code-review findings in plan-usage telemetry
Review of the plan-usage chip feature (commits since 1.0.0) surfaced several issues; this fixes all confirmed findings: - HIGH: applyStatusLineConfig clobbered a user's hand-authored statusLine on the enable path (the isOurs guard only protected disable). Now bails out when an existing statusLine isn't ours, on both the enable and disable paths. - MED: StatusTelemetrySchema used z.optional() (rejects null) on Claude's undocumented statusline fields — a single stray null 400'd the entire POST and silently killed the chip's data feed. Switched the modeled fields to .nullish(). - MED: dropping the Token Count / Show Cost header toggles left their features reading settings.showTokenCount/showCost, but saveAppSettings rebuilds settings fresh from the DOM, dropping those keys and resetting them to defaults on every save (re-enabling the token chip with no UI to turn it off). Preserve the prior stored preference. - telemetrySignature keyed on contextUsedPercentage (never displayed) and the raw unrounded %, churning a redundant SSE broadcast + localStorage write + identical chip re-render on every assistant message. Now keys on the rounded displayed window values only. - Plan-usage chip flashed hidden on load (no server-side reveal): renderIndexHtml now strips header-plan-usage--hidden when enabled, matching btn-multimonitor; fixes the FOUC and makes the "server renders initial state" comments accurate. - Serialize all settings.local.json read-modify-write writers in hooks-config via a shared per-path mutex (previously lock-free; concurrent session-create + settings-toggle on the same repo could lose writes). - Hardened the chip's innerHTML against any future string field; removed the dead _latestPlanUsage field; clamped ctx% in the footer formatter; corrected the session-create comment (the path is add-only by design — a per-repo settings file is shared by sibling sessions). - Tests: new test/routes/status-telemetry-routes.test.ts (route behavior, dedup, null-tolerance) + NaN/Infinity/fractional and signature-churn unit tests; made server-index-title.test.ts deterministic against the ambient settings.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+19
-11
@@ -195,12 +195,20 @@ export const ResizeSchema = z.object({
|
||||
* subset Codeman displays; unknown keys (session_id, transcript_path, cwd, …)
|
||||
* are stripped by z.object. Auth-exempt like /api/hook-event.
|
||||
*/
|
||||
// NOTE: every modeled field is `.nullish()` (not `.optional()`) on purpose.
|
||||
// Claude's statusline blob is officially shipped but undocumented in exact
|
||||
// shape, and `z.optional()` REJECTS an explicit `null` (accepts only
|
||||
// `undefined`) — a single stray `null` (e.g. `cost:{total_cost_usd:null}`)
|
||||
// would 400 the ENTIRE POST before the deliberately-tolerant parser
|
||||
// (usage-telemetry.ts, which only acts on `typeof === 'number'/'string'`) ever
|
||||
// runs, silently killing the chip's data feed. `.nullish()` keeps the schema
|
||||
// gate as forgiving as the parser it guards.
|
||||
const RateLimitWindowSchema = z
|
||||
.object({
|
||||
used_percentage: z.number().optional(),
|
||||
resets_at: z.number().optional(),
|
||||
used_percentage: z.number().nullish(),
|
||||
resets_at: z.number().nullish(),
|
||||
})
|
||||
.optional();
|
||||
.nullish();
|
||||
|
||||
export const StatusTelemetrySchema = z.object({
|
||||
sessionId: z.string().min(1).max(100),
|
||||
@@ -211,18 +219,18 @@ export const StatusTelemetrySchema = z.object({
|
||||
five_hour: RateLimitWindowSchema,
|
||||
seven_day: RateLimitWindowSchema,
|
||||
})
|
||||
.optional(),
|
||||
.nullish(),
|
||||
context_window: z
|
||||
.object({
|
||||
used_percentage: z.number().optional(),
|
||||
total_input_tokens: z.number().optional(),
|
||||
total_output_tokens: z.number().optional(),
|
||||
used_percentage: z.number().nullish(),
|
||||
total_input_tokens: z.number().nullish(),
|
||||
total_output_tokens: z.number().nullish(),
|
||||
})
|
||||
.optional(),
|
||||
cost: z.object({ total_cost_usd: z.number().optional() }).optional(),
|
||||
model: z.object({ display_name: z.string().max(100).optional() }).optional(),
|
||||
.nullish(),
|
||||
cost: z.object({ total_cost_usd: z.number().nullish() }).nullish(),
|
||||
model: z.object({ display_name: z.string().max(100).nullish() }).nullish(),
|
||||
})
|
||||
.optional(),
|
||||
.nullish(),
|
||||
});
|
||||
|
||||
// ========== Case Routes ==========
|
||||
|
||||
Reference in New Issue
Block a user