Skip to content

fix: don't flag an in-flight parallel duplicate as crashed (port of #443) - #462

Merged
korivi-CraftOS merged 1 commit into
V1.4.3from
fix/activity-log-parallel-v143
Sep 23, 2026
Merged

korivi-CraftOS merged 1 commit into
V1.4.3from
fix/activity-log-parallel-v143

Conversation

@korivi-CraftOS

Copy link
Copy Markdown
Collaborator

Port of #443 (merged to dev) onto V1.4.3, which carries the identical guard code and the identical bug.

execute_parallel dispatches a whole batch before any call completes, so two identical irreversible calls issue two begin()s with no complete() between them. The second reads the first's INTENT row as a crashed prior attempt, downgrades it to FAILED, and tells the model the action "may have already happened" — while the first is still running.

Verified on V1.4.3 rather than assumed:

  • app/triggers/activity_log.py on V1.4.3 is byte-identical to dev's pre-fix version, so the same bug and the same patch apply
  • the new test fails without the fix and passes with it
  • full suite: 1259 passed, 0 failed

One note on the author's own caveat (a key stuck in _in_flight if complete() never runs): it's narrower than they feared. agent_core/core/impl/action/manager.py wraps execution in try/except Exception, sets status = "error", and calls complete() after the block — including on the CancelledError path, which re-raises only after persistence. So a leak needs a BaseException outside Exception/CancelledError, or process death, which clears the set anyway.

Port of #443, which landed on dev. V1.4.3 carries the identical guard code
and therefore the identical bug: execute_parallel dispatches a whole batch
before any call completes, so two identical irreversible calls issue two
begin()s with no complete() between them. The second saw the first's INTENT
row, read it as an interrupted prior attempt, downgraded it to FAILED and
told the model the action "may have already happened" while the first was
still running.

Age cannot separate the two cases -- an immediate crash-then-restart leaves
an equally young INTENT row, and that one does need the warning. Tracking
in-flight keys on the guard instance distinguishes them, because a restart
builds a fresh guard with an empty set.

Verified on V1.4.3, not assumed: the new test fails against this branch's
parent and passes with the fix. Full suite 1259 passed, 0 failed.

The author's noted caveat -- a key stuck in the in-flight set if complete()
never runs -- is narrower than stated here: manager.py wraps execution in
try/except and calls complete() after it, including on the cancellation
path, so only a BaseException outside Exception/CancelledError or process
death could leak, and process death clears the set anyway.
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