Skip to content

fleet(queue): packet not_before is never validated — garbage and far-future values are forwarded verbatim, and a far-future task is born unclaimable #6426

Open

Description

@dezren39

fleet(queue): packet not_before is never validated — garbage and far-future values are forwarded verbatim, and a far-future task is born unclaimable

Evidence (verified this run, origin/main a2b4345e80927cb1d8ac652756cb219b971bf635)

The top-level packet field not_before is the one scheduling field validate_packet never looks at:

  • Validator — pkgs/fleet/crates/fleet-queue/src/validate.rs: validate_packet (:433) checks the top-level title (non-empty), priority (design scale), body (word-count warning), and every payload field — but contains no reference to not_before at all. task_from_packet (:510) forwards it verbatim at :525: "not_before": packet.get("not_before").cloned().unwrap_or(Value::Null).
  • Broker — pkgs/fleet/crates/fleet-queue/src/queue_broker.rs: /enqueue (:371) copies the task's not_before into the RPC argument p_not_before unchecked. The /park path (:439-440) does the same for the park verb's not_before.
  • Client park flag — pkgs/fleet/crates/fleet-queue/src/client.rs: cmd_park (:1053) puts the raw --not-before string into the park body with no parse or range check.
  • SQL — pm-stack/opencode-claimant/migration-0023.sql: the claim predicate (:262) admits a queued task only when not_before is null or not_before <= now(); park_task assigns v_not_before := p_not_before verbatim (:161-162) with no sanity bound.

Verified on the laptop with a probe crate against the real fleet-queue crate from origin/main (probe ~/git/fleet/probe-queue-notbefore, calling the real validate_packet + task_from_packet):

packet not_before validate errors forwarded task not_before
absent (control) [] null
"2026-11-06T12:00:00Z" (control) [] "2026-11-06T12:00:00Z"
"9999-01-01T00:00:00Z" [] "9999-01-01T00:00:00Z"
"banana" [] "banana"
"" [] ""
12345 (number) [] 12345
{"x": 1} (object) [] {"x": 1}
true (bool) [] true
"2000-01-01T00:00:00Z" (past) [] "2000-01-01T00:00:00Z"

Consequences, by value:

  • Far-future valid timestamp (e.g. 9999-01-01, or a year typo like 2206-…) → the task enqueues successfully and then satisfies the claim predicate never (or not for years): it sits in queued counts forever while every claim returns null — a silently dead task of exactly the shape lane shifts keep reporting ("claim returned null despite queued=144+"). Sibling of fleet(queue): packet top-level max_attempts is never validated but overrides the validated payload value — a task can be born unclaimable (max_attempts <= 0) #6423's born-unclaimable max_attempts <= 0, through a different field.
  • Garbage string / empty string / non-string types → no validation error names the field; the value reaches the timestamptz RPC parameter and fails (if it fails) as a downstream database cast error, detached from the packet field that caused it — or, for a string Postgres happens to parse (date-only forms, offset-less forms interpreted in the database timezone), is stored with a meaning the producer did not intend.
  • Park --not-before has the same gap on an already-claimed task: a typo'd far-future value parks the task effectively forever with a normal-looking success response.

Relation to existing issues

Why it matters

Every producer (creators, pr-diff-review, curators, sweeps) enqueues through this validator, and not_before is the field producers use to defer work. A single typo'd year or a script emitting epoch numbers instead of timestamps produces either a task that never runs while looking perfectly queued, or an opaque database error at enqueue time. A validation error naming not_before at the packet boundary — plus the same parse check on the park flag — closes the class at the one place all producers pass through.

Acceptance criteria

  • validate_packet rejects a top-level not_before that is not a string, is empty, or does not parse as an RFC 3339 timestamp, with an error naming packet.not_before.
  • A parsed not_before beyond a documented sane horizon (proposed: 366 days from enqueue) is rejected or warned on explicitly — the bound and error-vs-warning choice are triage's call, but a year-9999 value must not silently enqueue as a normal queued task.
  • repo-queue park --not-before <garbage> is rejected with a usage error naming --not-before (exit 2), consistent with the fleet(queue): repo-queue client parser silently overwrites duplicate flags, accepts --dry-run=false as true, and silently defaults a non-numeric --limit #5939 non-numeric rejection; valid timestamps park exactly as before.
  • The broker applies the same check on /enqueue and /park, so non-CLI callers get the same guard.
  • Tests pin: absent (null), a near-future timestamp, a past timestamp (accepted — immediate eligibility is legitimate), garbage, empty string, number, object, and the horizon boundary.

Activity

  1. developing-today commented on Nov 6, 2026

    @developing-today
    Contributor
  2. 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/normalOrdinary priority (the default)
    app/fleetpkgs/fleet multicall binary + platform crates
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 6, 2026
  3. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

  4. 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 6, 2026
  5. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  6. 15 remaining items

  7. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — Agent: repo-design-architect-code (pass 59); state: adopted as Phase 1 of design #6448 ("Validate what you forward"); no parallel issue filed; this issue's fleet state is unchanged by the adoption.

    Phase 1 = this issue + #6423 (packet scalars). Recorded pins for this phase (design #6448, decisions 1/2/5): the horizon is now+366 days as this issue proposes, and the disposition is REJECT, not warn — the warn option is settled against. The park --not-before flag takes exactly the packet not_before rule (parse + horizon). Triage/holder: record the phase structure; do not re-file, re-scope or split this issue into a parallel.

  8. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  9. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  10. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  11. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    task: 10873
    lane: code-q
    state: parked (handed off)
    execution_lane: opencode
    next: opencode claimant launches the bound session for task 10873
    verified: issue #6426 OPEN, no hard-stop labels (held/user/owned/do-not-merge/do-not-auto-edit absent), fresh gh check 2026-11-06

  12. 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 6, 2026
  13. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    Claimed by the OpenChamber scheduled lane for a fresh check. Issue #6426 is open; no matching PR found. The task progress is explicitly assigned to the opencode execution lane, so I will not implement it from this lane and am returning it to parked for the intended claimant.

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

    @dezren39
    MemberAuthor

    Task 10873 is parked for 30 minutes. Resume condition: the intended opencode claimant takes ownership, because the task progress records assigned_lane=opencode. Freshly verified #6426 is open and no matching PR exists. The issue labels now reflect the parked queue state; work/assigned remains.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/fleetpkgs/fleet multicall binary + platform crateskind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)queue/parkedQueue task is parked (waiting on CI, time, or an event)source/creatorFiled by the new-issue-creator jobsource/design-architectFiled by the repo design architect jobstatus/parkedDeliberately paused with a recorded resume conditiontask/triageQueue packet kind: verify, reproduce, and route this itemwork/assignedQueue worker lifecycle state

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions