Skip to content

Fix the tasks eval seed aborting a run once an agent changes the done task - #3787

Merged
malibio merged 2 commits into
mainfrom
issue-3763-seed-reset
Oct 13, 2026
Merged

malibio merged 2 commits into
mainfrom
issue-3763-seed-reset

Conversation

@malibio

@malibio malibio commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes a defect in the eval:tasks seed that landed in #3783 (issue #3763, already closed).

The seed put the reported task back to done with a plain status update. A core play rule refuses a change to done while a checkbox under the task is unchecked, so once an agent had changed that task's status the next group's seed was rejected and the whole run aborted as an environment fault (exit 2, no results file):

Group seeding failed for: bug-not-reproducible
  ... Play rule 'reject-done-with-an-unchecked-criterion' ... rejected the write

The box is now ticked through its content (checked is derived from the content and cannot be written), the status set, and the box unticked again. The task ends as done with one unticked box, which is the inconsistency scenario 1 reports.

Verification

  • The seed was run twice against a live daemon in the state that failed (agent had changed the task): status done, box - [ ], both passes.
  • A 1-rep eval:tasks run on this change: the first scenario's agent changed the done task (update_task_status), and every later group seeded and scored. No environment fault.
  • Scripts tier: 1544 pass.

Result of that 1-rep run (rewritten, developer-workflow scenario content; E4B)

pass^1 1/5, the same shape as the 3-rep baseline in #3783 (that baseline was measured on the previous wording). bug-not-reproducible passes only because its create_node was refused. The other four fail for the reasons recorded in #3784.

Not covered

The seed itself needs a daemon, so the fix is covered by the live runs above and not by a unit test.

🤖 Generated with Claude Code

@malibio

malibio commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Code Review Summary

Net positive. The tick, set status, untick sequence is sound against the reject-done-with-an-unchecked-criterion rule (core_plays.rs ~463): the rule is a reject on a change of the task's own status to done that reads node.has_child.exists(c, c.checked == false), so editing a child's content afterwards is not guarded. It fixes the reported abort. Two latent problems remain in the seed: the box lookup is not scoped to the task, and review and decision have the same class of unresettable-status failure. Neither blocks merge.

Findings

Critical Issues

None.

Suggested Improvements

  • Box lookup is global, not scoped to the done task (ensureDoneTaskWithOpenBox, scripts/eval/fixtures/tasks.ts). listType(env, "checkbox") (limit 200, whole database) is matched by content with the prefix stripped, and --parent is only passed on create. Scenario 1 asks an agent to create a ready task with a checklist about exactly this unticked box, so an agent-created box of the same wording under another task will match (and now matches whether it is [ ] or [x], which is broader than the old exact-[ ] match). The seed then ticks and unticks that stranger's node and never creates the seeded task's own box: the done task ends with no unticked box and the scenario's premise is gone, silently. With --runs N without a purge this is the likely path. Fix: look for the box among the done task's children (has_child edges of task, as graph.ts already reads them) rather than by content over the whole type; or, cheaper, keep the content match but require the candidate to be a child of task.
  • Same latent failure for the review task. review is reset to in_progress while question blocks it (ensureEdge(question, "blocks", review)). reject-starting-a-blocked-task (core_plays.rs ~433) rejects a change to in_progress or in_review while a blocker is neither done nor cancelled, and question is open. So if an agent moves review to any other status (bug-not-reproducible names it), the next group's reset is refused and the run aborts exactly as in this PR. First seed passes only because the edge does not exist yet when the node is created. A reliable reset has to remove the cause (e.g. set the question done, reset review, then set the question back to open, or delete and re-add the blocks edge around the update) or accept that the review task is only reset via a purge.
  • Decision: lock-superseded-decision-status rejects moving a superseded decision back to accepted. declined-request is the scenario most likely to push an agent to supersede it. Lower probability than the review case, but the seed has no answer for it either. If the answer is "purge between groups", state that in the seed doc comment instead of claiming every status is put back.
  • The header comment of seedWorkspace and the file banner still say the seed "puts every seeded status back"; after this PR it is true for stopped, question and done only. Make the claim match.

Verified, no change needed:

  • Interrupted sequence: if the status update throws after the box was ticked, the seed throws and aborts (exit 2), it does not continue. The next run finds the ticked box, strips the prefix, matches, and repeats the unconditional tick, set status, untick, so it self-heals and the bug reproduces again.
  • Status already done: the status write carries an unchanged value (no change, no rule), the box is ticked then unticked. Result identical.
  • New task created via ensureNode(..., {}) is open, then set to done with the box ticked: allowed. Existing task in any state moves to done under the same conditions. reject-done-without-criteria and the lineage rule do not apply (no plan or spec on this task).
  • stopped and question have no rule guarding a move to open; project carries no status.

Nitpicks

  • Nit: the regex /^\s*-\s*\[[ xX]\]\s*/ appears twice; hoist it to a named constant.
  • Nit: listType then .find runs a 200-row query on every group's seed; scoping to the task's children (above) removes that too.

Tests and verification claims

A unit test is not reasonable here: the failure is an interaction with a live daemon's play rules, and the seed's other pieces have the same property. The PR's verification (seed run twice against the failing state, a 1-rep run that went through all groups, scripts tier) is proportionate for this change. The evidence does not cover the two cases above, since the agent in that run only changed the done task. The claim that the reset is idempotent "whatever an agent did" is stronger than what was tested.

AGENTS.md compliance

No lint suppression, no any, no raw console, no issue numbers in comments, no compat code. Comments explain why (the rule, the derived checked). Compliant.

Recommendation

Approve with follow-ups: merge is fine as a net improvement. Fold the box scoping into this PR if cheap (it is a few lines); the review and decision resets are worth a fix or an explicit "purge between groups" statement in the same file.

@malibio malibio left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review posted above. Verdict: approve with follow-ups (net positive; scope the box lookup to the task's children, and the review/decision status resets can still be rejected by core play rules). Not merging.

@malibio

malibio commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator Author

Review addressed (commit 63f26c2)

Addressed

  • 🟡 Box lookup was global. The unticked box is now found among the done task's own has_child children, so a same-worded box an agent made under another task is never ticked in its place.
  • 🟡 Review task reset vs its blocker. A play rule refuses a move to in_progress while a blocker is open. The blocking edge is dropped around the write and recorded again after. An edge that is not there (first seed of a database) is tolerated.
  • 🟡 Decision. Its status applies on creation only. The daemon will not move a superseded decision back to accepted, and no later group reads it, so a reset would only abort the run.
  • 🟡 Doc claims. The seed comment and file banner now say which statuses are put back.
  • Nit: the checkbox marker regex is one constant.

Verification (live daemon, because the seed needs one): after a cancelled review task, a reopened done task, a superseded decision and a same-worded box under another task, three seed passes ran with no abort. Review in_progress, done task done with its own box unticked, decision left superseded, the stranger box untouched, and the question still blocks the review task. The first seed on a fresh database also passes, which exercises the "edge not there" tolerance.

Skipped: none.

Scripts tier: 1544 pass. quality:fix clean.

Re-Review Decision

Decision: NO RE-REVIEW NEEDED

Rationale: The reviewer approved the base change and called it safe to merge. The follow-ups are confined to a test fixture's seed, and each one was exercised directly against a live daemon in the exact failing state the review described, rather than argued from reading the code.

🤖 Generated with Claude Code

malibio and others added 2 commits October 13, 2026 14:36
…seed survives an agent changing it (follows #3763)

The seed set the reported task back to done with a plain status update. A play rule refuses a change to done while a checkbox under the task is unchecked, so once an agent had changed that task's status, the next group's seed was rejected and the whole run aborted as an environment fault. The box is now ticked through its content, the status set, and the box unticked again. Verified twice against a live daemon in the state that failed, and by a 1-rep run in which the first scenario's agent changed the task.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nd decided nodes around their rules

- The unticked box is found among the done task's own children, so a box of the same wording an agent made under another task is never ticked in its place.

- The review task is reset to in_progress with its blocking edge dropped around the write (a play rule refuses a move to in_progress while a blocker is open); the edge is recorded again after.

- The decision's status applies on creation only: the daemon will not move a superseded decision back to accepted, and no later group reads it.

- The doc comments say which statuses are put back. The checkbox marker regex is one constant.

Verified live against a daemon after a cancelled review task, a reopened done task, a superseded decision and a same-worded box under another task: no abort, the right nodes reset, the stranger box untouched.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@malibio
malibio force-pushed the issue-3763-seed-reset branch from 63f26c2 to a61a3c8 Compare October 13, 2026 12:36
@malibio
malibio merged commit 996a479 into main Oct 13, 2026
@malibio
malibio deleted the issue-3763-seed-reset branch October 13, 2026 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant