Skip to content

7/30일 usage history·forecast beta #124

Description

@woosublee

사용자 문제와 기대 결과

현재 사용 속도로 quota가 언제 소진될지 보여주되 data gap, freshness, timezone을 명시한다.

Acceptance criteria

  • 이 이슈의 설계 요구사항이 자동 검증 가능한 형태로 구현된다.
  • 실패 시 기존 정상 account·shell·config·proxy 상태를 보존하거나 rollback한다.
  • 관련 상태를 loading, degraded, stale, unavailable 중 명확한 상태로 구분한다.
  • User-facing error에는 원인, 영향, recovery action이 포함된다.
  • Secret·email·prompt가 log, diagnostics, JSON output, test fixture에 포함되지 않는다.
  • 필요한 unit·integration·script test와 development build 검증을 완료한다.
  • 관련 README·troubleshooting·release note를 갱신한다.
  • Source, freshness, data gap, estimate 여부를 사용자에게 표시한다.

검증

  • 자동: 관련 unit·integration·script test와 development build
  • 수동: 앱 실행 또는 UI 확인이 필요한 경우 release checklist에 절차 기록

성공 지표

  • Primary impact: impact: reliability
  • Milestone exit condition에 미친 결과를 완료 comment로 기록

상세 실행 명세

현재 동작과 재현

  • API usage ledger는 월별 파일을 저장하지만 public read contract는 현재 시점의 month 하나만 반환한다. 7/30일 range query와 일별 series가 없다.
  • subscription은 profile별 마지막 snapshot 하나만 cache한다. 시간순 sample history가 없어 quota 증가 속도와 reset 전 소진 시점을 계산할 수 없다.
  • 재현: 7일/30일 trend를 요청해도 UI에는 현재 percent 또는 Day/Mon aggregate만 있고, gap/confidence/timezone을 포함한 forecast를 만들 입력 contract가 없다.

코드 근거

  • Sources/CLIProxyManagerCore/APIUsage/APIUsageLedgerStore.swift:17 — ledger protocol은 readCurrentPeriods만 노출한다.
  • Sources/CLIProxyManagerCore/APIUsage/APIUsageLedgerStore.swift:260 — read는 현재 month file 하나만 load한다.
  • Sources/CLIProxyManagerCore/APIUsage/APIUsageLedgerModels.swift:55 — bucket key에는 localDate/profile/provider/model/price epoch가 있어 range aggregation 기반은 있다.
  • Sources/CLIProxyManagerCore/APIUsage/APIUsageLedgerModels.swift:213 — timezone-aware calendar bounds와 UTC fallback이 이미 있다.
  • Sources/CLIProxyManagerApp/Services/SubscriptionUsageSnapshotCacheFileStore.swift:4 — subscription cache는 profile당 snapshot 하나만 저장한다.
  • Sources/CLIProxyManagerCore/SubscriptionUsage/SubscriptionUsageModels.swift:25 — snapshot은 fetchedAt과 windows를 가져 sample source로 사용할 수 있다.
  • Sources/CLIProxyManagerCore/APIUsage/APIUsageLedgerModels.swift:7 — partial intervals와 last drain metadata로 data gap/confidence를 계산할 수 있다.
  • Sources/CLIProxyManagerCore/Config/ManagedPaths.swift:34 — API monthly ledger와 last-success subscription cache 경로가 이미 분리돼 있다.

구현 범위

  1. API ledger에 timezone-aware readRange(DateInterval) query를 추가한다. interval과 교차하는 month file만 bounded load하고 local calendar day별 cost/token/request/gap을 반환한다.
  2. subscription sample history는 last-success cache와 별도 store/file로 만든다. profile+window별 성공 sample만 저장하고 v1 retention은 35일, window당 최대 128개, 동일 fetchedAt/reset cycle 중복 제거로 제한한다.
  3. last-success cache는 현재 값 복원 책임을 계속 가지며 history가 비어 있거나 손상돼도 대체/삭제되지 않는다. history write 실패는 projection에 gap만 추가한다.
  4. 7/30일 series는 source별로 계산한다.
    • API: complete local-day aggregate와 partial interval overlay, current month 경계를 넘는 range query.
    • Subscription: 같은 reset cycle 내 percent samples, reset 전후 discontinuity 분리.
  5. forecast output은 source, asOf, timezone, observedRange, sampleCount/coverage, gaps, method, confidence(high/medium/low/unavailable), forecastAt을 포함한다.
  6. API forecast는 complete day spend rate로 period-end projected cost를 계산한다. subscription forecast는 monotonic percent slope로 현재 reset cycle의 100% 도달 시각을 계산하되 resetAt 이후 결과는 “reset 전에 소진되지 않음”으로 clamp한다.
  7. confidence 규칙을 code/test에 고정한다: gap 없음+충분한 samples/high coverage만 high, 일부 gap/짧은 history는 medium/low, reset cycle 혼합·역행 slope·timezone 불명은 unavailable.
  8. UI는 7/30일 선택, source, freshness, gaps, timezone, confidence, “estimate”를 함께 보여주며 정확한 미래를 보장하는 문구를 사용하지 않는다.

이슈별 Acceptance criteria

  • API month-range query가 month boundary와 DST를 포함한 7/30일 interval에서 필요한 file만 읽고 local-day series를 반환한다.
  • subscription history가 성공 sample만 저장하고 35일/128개 bound, duplicate/reset-cycle pruning을 deterministic test로 만족한다.
  • history write/read/migration 실패가 last-success cache를 지우거나 대체하지 않는 회귀 test가 있다.
  • API와 subscription forecast가 서로 다른 method/source metadata를 갖고 gap/timezone/confidence를 항상 반환한다.
  • insufficient sample, negative/zero slope, reset cycle 변경, partial interval, unpriced API request는 confidence 저하 또는 unavailable로 처리된다.
  • forecast timestamp가 subscription resetAt을 넘지 않고 API range가 calendar timezone 경계를 지킨다.
  • 7/30일 presentation은 source/freshness/gap/estimate/confidence를 text와 accessibility label로 동일하게 제공한다.
  • swift test --filter APIUsageLedgerStoreTests, 신규 history/forecast tests, swift test --filter SubscriptionUsageSnapshotCacheTests가 통과한다.
  • 전체 swift test, swift build -c debug --product CLIProxyManager가 통과하고 timezone/gap/source별 수동 checklist가 기록된다.

Edge cases와 실패·rollback

  • 월 파일 누락/손상/newer schema, 앱 사용 중 timezone 변경, DST 23/25시간 day, sparse samples, percent 감소, provider resetAt 수정, account rename/delete를 포함한다.
  • sample timestamp가 미래이면 제외하고 gap reason을 남긴다.
  • forecast 계산 실패 시 현재 last-success 숫자는 계속 표시하고 forecast만 unavailable로 내린다.
  • rollback 시 새 history file을 무시할 수 있어야 하며 기존 cache/ledger schema를 파괴적으로 변경하지 않는다.

명시적 비목표

  • invoice/청구 보장, provider 서버에서 과거 subscription usage를 backfill, ML 기반 forecast, notification 자동 발송은 포함하지 않는다.
  • last-success cache를 history로 치환하지 않는다.
  • raw request/prompt/session 로그를 history에 저장하지 않는다.

보안·개인정보·접근성

  • history/series는 stable profile ID, aggregate percent/cost/tokens/time만 저장하고 email, secret, prompt, request ID를 저장하지 않는다.
  • hidden account detail은 chart/tooltip/VoiceOver에도 노출하지 않는다.
  • confidence와 gap은 색상 외 text로 제공하고 Reduce Motion에서 animated chart transition을 제거한다.

테스트·검증

  • Unit: range month selection, calendar/DST, retention/pruning, slope/confidence/clamp.
  • Integration: success samples → relaunch → gap → recovery, previous month + current month query, corrupted history isolation.
  • Presentation: 7/30, source, timezone, confidence, no-data accessibility fixtures.
  • 명령: 관련 filter tests, 전체 swift test, development build. 실제 UI는 사용자가 checklist로 확인한다.

완료 지표

  • 7/30일 query의 calendar-day 누락/중복 0건이다.
  • history failure로 last-success cache가 손실된 사례 0건이다.
  • 모든 forecast에 source/timezone/gap/confidence metadata 존재율 100%다.

불확실성

  • subscription provider sample cadence가 낮으면 slope가 불안정하다. v1은 최소 sample/coverage 미달 시 unavailable을 선택하고 숫자를 억지로 생성하지 않는다.
  • API “소진”은 budget 설정이 없으면 정의할 수 없어 v1은 period-end projected cost를 제공하고 budget exhaustion 연결은 Quota reset·usage threshold·API budget 알림 #122 preference가 있을 때만 후속 적용한다.

연관 이슈

Roadmap

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: usageQuota, API cost, usage state와 forecastimpact: reliability예측 가능성·일관성·release 안정성 개선priority: P2중요하지만 선행 기반 이후 진행하는 작업type: product사용자에게 직접 전달되는 제품 기능·경험

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions