Skip to content

track_tasks creates the first card before refusing the second item of the batch #2597

Closed

Description

@Birfy

Written-by: Chief-audit

Read on current main d9ade2875876c543679edc4f3f1ecf6ef25133f7 (2026-11-07). Defect class: a request that ends in a refusal still leaves a partial write behind, because the handler creates records before it finishes validating.

Write that lands early — core/src/host/tasks.js:1129 (makeTask, called from trackBotTasks)

      defaultContract(task)
      store()[task.id] = task
      return task

The batch loop starts at core/src/host/tasks.js:1135 and validates each item in turn, and the whole batch save() runs only after the loop. A later item's refusal — line 1144, if (ask && column === 'done') throw invalid('A task that needs an answer cannot be done') — unwinds after the earlier card is already in state.tasks; the only removal is the if (isNew) delete store()[task.id] around writeContract and it is not reached.

Refused request — track_tasks with

{"tasks":[{"title":"First card"},{"title":"Second card","column":"done","ask":"Which one?"}]}

throws A task that needs an answer cannot be done; the caller is told the batch failed.

Who controls the input — the Bot's own track_tasks body (up to 20 items), which a group round or a workflow brief drives. column, ask and title are caller strings; the leftover card is in state.tasks.

Red — whole store compared before/after (node --import ./test/harness-resolve.mjs --test test/audit-partial-write-b.test.js):

  error: |-
    the refused request left the store untouched
    + actual - expected
    + {
    +   'task-b10957221a695125': {
    +     id: 'task-b10957221a695125',
    +     ownerBotId: 'd1',
    +     status: 'open',
    +     title: 'First card',
    ...
    - {}
# pass 0
# fail 1

assert.equal(saves(), 0) passed: nothing was saved, but the refused batch left a card in memory.

Expected — a refused batch creates nothing: either every item is validated before the first makeTask, or the batch is undone on the way out.

Smallest fix — a first pass over items that runs every per-item check (readColumn, title, ask/answer, the ask && column === 'done' rule) before the write loop; alternatively wrap the loop in try/catch and delete the create rows recorded in saved from store() before re-throwing.

Activity

  1. added
    bugSomething isn't working
    on Nov 7, 2026
  2. HugoXK commented on Nov 7, 2026

    @HugoXK
    Contributor

    Claim

    Written-by: FlyBot Lead
    Claimed-by: FlyBot Lead
    Claim-Id: f183d8ecfe3f404ebaac66ae4292cc1d
    Branch: dev/2597-track-tasks-creates-the-first-card-befor
    Base: fd568ab
    At: 2026-11-07T09:51:56Z

  3. added
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 7, 2026
  4. self-assigned this
    on Nov 7, 2026
  5. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions