mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-08-25 00:14:40 +00:00
payload: add-only stale marker for read-only stale menubar serves (#771)
isSessionHydrationComplete() (parser.ts, PR #937) already reports when a read-only serve skipped or staled real on-disk changes; thread it through buildMenubarPayload as an optional stale field, present and true only on a stale serve, always absent otherwise, so older/newer CLI-app pairs stay compatible. Mirrors the field into the desktop renderer types and the macOS menubar's Codable payload model (data layer only, no view change). Drafted with minimax/MiniMax-M3 via local gateway.
This commit is contained in:
parent
9cfa957e3e
commit
affd40e230
6 changed files with 35 additions and 3 deletions
|
|
@ -127,6 +127,9 @@ export type ClaudeConfigSelector = {
|
|||
|
||||
export type MenubarPayload = {
|
||||
generated: string
|
||||
// Optional: older CLIs omit it. Present and true only on a stale read-only
|
||||
// serve; absent otherwise. Absence must always be read as "assume fresh."
|
||||
stale?: boolean
|
||||
current: {
|
||||
label: string
|
||||
cost: number
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import Foundation
|
|||
/// `current` is scoped to the requested period; the whole payload reflects that slice.
|
||||
struct MenubarPayload: Codable, Sendable {
|
||||
let generated: String
|
||||
/// Present and `true` only when this payload was assembled from a
|
||||
/// read-only stale serve. Absent — never `false` — on a fresh payload;
|
||||
/// absence must be read as "assume fresh," including for payloads from
|
||||
/// a CLI version that predates this field.
|
||||
let stale: Bool?
|
||||
let current: CurrentBlock
|
||||
let optimize: OptimizeBlock
|
||||
let history: HistoryBlock
|
||||
|
|
@ -15,8 +20,10 @@ struct MenubarPayload: Codable, Sendable {
|
|||
optimize: OptimizeBlock,
|
||||
history: HistoryBlock,
|
||||
combined: CombinedUsage?,
|
||||
claudeConfigs: ClaudeConfigSelector? = nil) {
|
||||
claudeConfigs: ClaudeConfigSelector? = nil,
|
||||
stale: Bool? = nil) {
|
||||
self.generated = generated
|
||||
self.stale = stale
|
||||
self.current = current
|
||||
self.optimize = optimize
|
||||
self.history = history
|
||||
|
|
@ -25,12 +32,13 @@ struct MenubarPayload: Codable, Sendable {
|
|||
}
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case generated, current, optimize, history, combined, claudeConfigs
|
||||
case generated, stale, current, optimize, history, combined, claudeConfigs
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws {
|
||||
let c = try decoder.container(keyedBy: CodingKeys.self)
|
||||
generated = try c.decode(String.self, forKey: .generated)
|
||||
stale = try c.decodeIfPresent(Bool.self, forKey: .stale)
|
||||
current = try c.decode(CurrentBlock.self, forKey: .current)
|
||||
optimize = try c.decode(OptimizeBlock.self, forKey: .optimize)
|
||||
history = try c.decode(HistoryBlock.self, forKey: .history)
|
||||
|
|
|
|||
|
|
@ -178,6 +178,12 @@ export type ClaudeConfigSelector = {
|
|||
|
||||
export type MenubarPayload = {
|
||||
generated: string
|
||||
/// Optional. Present and `true` only when this payload was assembled from a
|
||||
/// read-only stale serve (see `isSessionHydrationComplete` in `parser.ts`).
|
||||
/// Omitted — never `false` — on a fresh/complete payload, so absence always
|
||||
/// means "assume fresh," including for payloads from a CLI version that
|
||||
/// predates this field.
|
||||
stale?: boolean
|
||||
current: {
|
||||
label: string
|
||||
cost: number
|
||||
|
|
@ -507,6 +513,7 @@ export function buildMenubarPayload(
|
|||
breakdowns?: BreakdownArrays,
|
||||
claudeConfigs?: ClaudeConfigSelector,
|
||||
granularHistory?: GranularHistory,
|
||||
stale?: boolean,
|
||||
): MenubarPayload {
|
||||
const payload: MenubarPayload = {
|
||||
generated: new Date().toISOString(),
|
||||
|
|
@ -557,5 +564,8 @@ export function buildMenubarPayload(
|
|||
if (claudeConfigs && claudeConfigs.options.length > 1) {
|
||||
payload.claudeConfigs = claudeConfigs
|
||||
}
|
||||
if (stale) {
|
||||
payload.stale = true
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
|
|
|||
|
|
@ -944,5 +944,5 @@ export async function buildMenubarPayloadForRange(periodInfo: PeriodInfo, opts:
|
|||
const optimize = opts.optimize === false ? null : await scanAndDetect(scanProjects, scanRange, opts.provider)
|
||||
const granularRange = opts.daysSelection?.range ?? scanRange
|
||||
const granularHistory = opts.timeline === false ? undefined : buildGranularHistory(scanProjects, granularRange)
|
||||
return buildMenubarPayload(currentData, providers, optimize, dailyHistory, retryTax, routingWaste, breakdowns, claudeConfigs, granularHistory)
|
||||
return buildMenubarPayload(currentData, providers, optimize, dailyHistory, retryTax, routingWaste, breakdowns, claudeConfigs, granularHistory, isSessionHydrationComplete() ? undefined : true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -413,4 +413,14 @@ describe('buildMenubarPayload', () => {
|
|||
],
|
||||
})
|
||||
})
|
||||
|
||||
it('sets stale:true when the caller reports an incomplete hydration', () => {
|
||||
const payload = buildMenubarPayload(emptyPeriod('Today'), [], null, undefined, undefined, undefined, undefined, undefined, undefined, true)
|
||||
expect(payload.stale).toBe(true)
|
||||
})
|
||||
|
||||
it('omits stale on a normal fresh build', () => {
|
||||
const payload = buildMenubarPayload(emptyPeriod('Today'), [], null)
|
||||
expect(payload.stale).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -23,5 +23,6 @@ describe('buildMenubarPayloadForRange', () => {
|
|||
expect(payload.current.codexCredits).toBeGreaterThanOrEqual(0)
|
||||
// optimize:false => scanAndDetect skipped => empty optimize block regardless of data
|
||||
expect(payload.optimize).toEqual({ findingCount: 0, savingsUSD: 0, topFindings: [] })
|
||||
expect(payload.stale).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue