Skip to content

opencode-usage-export: --window-hours / --fresh-hours are never validated — a junk window publishes a zero-filled status:ok document, a junk freshness reports a fresh store as moved #4739

Open

Description

@dezren39

opencode-usage-export: --window-hours / --fresh-hours are never validated — a junk window publishes a zero-filled status: "ok" document, a junk freshness reports a fresh store as moved

pm-stack/opencode-usage-export/opencode-usage-export.mjs (slice 1 of #2072, merged in PR #4680) parses its two numeric flags with a bare Number(...) and never checks the result:

  • parseArgs (:311-322): --window-hours → Number(argv[++i]) (:317), --fresh-hours → Number(argv[++i]) (:318). No finite/positive check; a missing value is Number(undefined) = NaN; an unknown flag throws, but a known flag with a bad value does not.
  • The window flows unchecked into sinceMs = now - args.windowHours * 3600000 (:348) and the session query WHERE time_updated >= ? (:221 via :349).
  • The freshness flows unchecked into freshMs: args.freshHours * 3600000 (:343) and the liveness comparison now - p.newestUpdated <= freshMs (:165).

Verified this run (laptop probe, verbatim origin/main @ cf74b1e, file md5 13c24364)

Fixture store with one fresh session (updated 1 min before now) and one 48 h-old session, exporter extracted byte-identical from origin/main (~/git/fleet/probe-2026-11-02-laneA-usagewindow/ on the laptop):

Invocation Result
control (defaults) exit 0, status: "ok", 1 session
--window-hours banana exit 0, status: "ok", 0 sessions, windowHours: NaN
--window-hours (value missing) exit 0, status: "ok", 0 sessions, windowHours: NaN
--window-hours -5 exit 0, status: "ok", 0 sessions
--window-hours Infinity exit 0, status: "ok", 2 sessions — the 48 h-old session outside any real window is included
--fresh-hours banana exit 4, store-stale: "no candidate store has session_v2 activity within NaNh; newest activity [1 minute ago] — the live store may have moved"
--fresh-hours -1 exit 4, store-stale, same misleading moved-store diagnosis for a store updated 1 minute ago

Why it matters

The module's own contract is fail-closed: the header (:29-31) promises a tick that cannot read the live store "writes a failure document and exits non-zero — never a zero-filled success", and the README repeats that a failed tick can never look like a reading. An invalid --window-hours breaks exactly that promise: latest.json becomes a status: "ok" document with zero sessions and zero totals, indistinguishable downstream (slices 2–7 of #2072 consume this document) from "OpenCode was genuinely idle for 24 h". An invalid --fresh-hours fails closed but with the wrong diagnosis — it tells the operator the store moved when the store is fine and the flag is wrong, sending them hunting for a moved database. The systemd unit passes neither flag today, so this bites manual ticks, debugging runs, and any future unit/timer change that starts passing them.

Distinct from #1566 (meta-exporter window_duration_secs, a different tool, closed) and #4381 (cloudflare-export threshold accepting 0): no open or closed issue covers this exporter's argument validation (searched usage-export in:title, --window-hours, exporter/NaN, 2026-11-02).

Acceptance criteria

  • --window-hours and --fresh-hours values that are missing, non-numeric, NaN, infinite, zero, or negative are rejected before any store read: a failure document with a status/detail that names the invalid argument (not store-stale), and a non-zero exit.
  • A valid positive finite value behaves exactly as today (existing tests green).
  • New tests in opencode-usage-export.test.mjs cover at least: junk window, missing window value, negative window, junk freshness — each asserting no status: "ok" document is produced and the failure detail names the bad argument.
  • No ok document can ever carry a non-finite or non-positive windowHours.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    effort/xsTrivial: expected under 30 minutes of work
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    app/opencode-usage-exportPackage/app: opencode-usage-export (pm-stack in this repo)
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator job (lane A, new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a run 2026-11-02 20:35 CST
    • Found via: filed this run
    • Task: Filed after verifying on the laptop against origin/main @ cf74b1e (probe-2026-11-02-laneA-usagewindow): junk/missing/negative --window-hours publishes a zero-filled status:"ok" document; junk --fresh-hours misreports a fresh store as moved/stale. Enqueued as a repo-task/v1 triage packet on repo-health-code-pilot.
    • Expectation: effort/xs — validate both numeric flags (finite, positive) in parseArgs before any store read, fail with an argument-naming failure document, add regression tests per the issue's acceptance criteria.
    • Links: packet creator-a-2026-11-02-2035/packets/packet-4739-triage.json (hidden_files)
  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  4. 52 remaining items

  5. added
    status/parkedDeliberately paused with a recorded resume condition
    work/assignedQueue worker lifecycle state
    on Nov 4, 2026
  6. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    task: 8444
    stage: handed_off
    lane: code-l
    execution_lane: opencode
    issue_state: open (verified fresh 2026-11-04)
    next: claimant launches the bound opencode session
    note: task 8444 attempts 15/16 — near cap; if the lease expires unlaunched a cap raise may be needed. Issue #4739 OPEN fresh via gh, no hard-stop labels.

  7. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 4, 2026
  8. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  9. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Picked up as task 8444 by lane code-c (repo-queue worker) and handed off to the opencode execution lane per the execution-lane assignment for task 8444. The laptop claimant launches the bound opencode session for task 8444; this lane does not work issue #4739 directly. Note: task 8444 is at attempts 19/32.

  10. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  11. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  12. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor
  13. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor
  14. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: handed off
    task: 8444 (repo-health-code-pilot, worker repo-worker-code)
    session: ses_e5e5589f378ffejE3h9MHlWsSx
    found via: queue claim; execution lane opencode
    launch: session created on the laptop OpenCode server at verified primary location /home/user/git/code, bound atomically before the prompt was sent.
    next: the bound session revalidates issue #4739 and current main, works only this task, heartbeats, and records the terminal outcome with the same session ID.
    links: task 8444 · issue #4739

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/opencode-usage-exportPackage/app: opencode-usage-export (pm-stack in this repo)app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientqueue/claimedQueue worker lifecycle statesource/creatorFiled by the new-issue-creator jobstatus/in-progressQueue worker lifecycle statetask/small-fixQueue packet kind: bounded fix with reproduction and targeted testswork/assignedQueue worker lifecycle state

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions