Repository navigation
fix(goal): retry stale writes against the freshest snapshot - #276
Conversation
ByBrawe
left a comment
There was a problem hiding this comment.
Thanks for the careful root-cause report, the shared fresh-snapshot retry approach, and tests for BOTH V1 chat and V2 work tools. The underlying stale_write symptom is real (#275). I reviewed all 15 changed paths. Before merging this broad patch, please address and test the following:
- New Goal/revision guard for foreground V1 steering: in
src/opencode/plugin.tsthe new steeringmutateGoal(store, event.sessionID, latest => ...)currently checks onlylatest.status === "active". If the original Goal was edited/replaced (same session) between the earlierloadand a retry, this hook can attach the previous turn's execution agent/model to a different Goal. Capture the originalgoal.id/revision (and owner where appropriate) before retry, reject a changed snapshot. Add deterministic cross-revision and new-Goal-same-session tests. Our separately green narrow V1 fix #280 contains that guard. - Completion after a competing writer: in
src/opencode2/work-tools.ts, host checks and independent semantic evaluation are computed fromsnapshotbeforemutateGoal, but the final retry can load a newer Goal with the same Goal ID/revision and a differentprogressRevision/evidence/Todo. Please prove stale verification cannot close freshly changed work: bind the verifier/check evidence to the exact intended progress snapshot, or fail closed and re-run verification on a changed relevant revision. Regression: mutate requirements/progress/target artifacts while verification or the store save is pending. - Retry semantics/side effects: keep callbacks re-entrant/idempotent. Validate
maxAttemptsas a positive bounded integer, and test non-stale lock/integrity/owner errors still propagate. For async evidence capture that can re-run on a stale collision, explicitly test whether re-reading files after a change is safe. - CI/rebase: all eleven checks on fork PR #276 currently read
action_required, not success. The existing reviewed narrower #280 has eight green checks and a competingmutateGoalhelper; reconcile on a fresh main base and ensure main's V1 fix is not reverted. Run Linux/Windows + native 2.1.4 stable + Goal/Loop joint lifecycle before merging this broader V2 migration.
This is a request for targeted changes and verified evidence, not a rejection of the contribution. #275 stays open until the cross-writer V2 and published consumer paths are proven.
|
Follow-up to the source review: the narrower V1 chat-only collision fix has now merged in #280 as |
d1239ba to
cb62b59
Compare
|
@ByBrawe Addressed the review on head
Local: All 11 workflows on |
|
Follow-up: head is now |
|
Maintainer status update (2026-10-27): PR #286 is now merged as |
Reject stale V1/V2 completion when the verified Goal progress, evidence, requirements, turn baseline or Todo plan changed under the same id/revision. Re-run bounded verification for V2 and re-read declared target files before finalizing; fail closed otherwise. Validate mutateGoal retry budgets, keep async evidence capture re-entrant across stale collisions, and add cross-revision/new-Goal V1 plus V2 progress/Todo/artifact/save-race regressions.
eab0f93 to
6e988e5
Compare
|
Rebased onto new Preserved the #286 verifier guards rather than reintroducing pre-#286 behavior: runtime-counter-only proof rejection, mixed turn+deliverable audits, target-matched file/command evidence, and symlink-safe host-observed deletion/absence with post-verifier recheck all remain in Local Node 22.22.3: All 11 workflows on |
…current main This maintenance mirror preserves exact contributor source/test blob hashes from muhammad-luay/opencode-goal@6e988e5, against Goal main including #286 and #287, without importing fork CI workflows. This is a *test branch*, not a PR merge or published release. Co-authored-by: muhammad-luay <muhammad-luay@users.noreply.github.com>
|
Thank you @muhammad-luay for rebasing and expanding #276 to cover same-session Goal replacement, progress/Todo changes during verification, file mutation replay, and bounded stale-write retries. I verified the exact blob hashes of all 18 changed source/test files from your latest |
ByBrawe
left a comment
There was a problem hiding this comment.
Thank you @muhammad-luay for addressing the previous requested changes. The V1 same-session ID/revision steering guard, bounded/validated shared retry helper, V2 re-verification against progress/Todo/requirements/evidence snapshots, asynchronous file-evidence refresh and deterministic cross-writer tests are now present. I verified all 18 changed source/test Git blob hashes against your exact PR head 6e988e5, replayed them on owner main (including #286 completion proof and #287 strict OpenTUI peers) in draft test PR #288, and obtained 11/11 genuinely successful maintainer GitHub Actions workflows, including Windows/Linux package Release Readiness, Current OpenCode 2 stable, experimental host, real Goal/Loop coexistence, restart, Todo, and native sidebar. This replaces the unavailable fork workflow approval evidence; it is not claiming the fork's action_required runs passed. The original issue #275 remains open for published npm consumer and affected-reporter replay. Thank you for the excellent thorough engineering contribution!
Refs #275 (expanded V2 coverage; #280 already landed the narrow V1 fix). Do not auto-close #275: release consumer replay remains outstanding.
State
Rebased onto
mainc70c127so the branch includes #280 (V1 foreground guard) and #283 (pinned OpenCode 2.1.4 lifecycle). The duplicatemutateGoalhelper is reconciled onto the reviewed #280 implementation. #280'slatest.id/latest.revisionsteering guard and its foreground regression test are preserved, not reverted.Problem
Multiple writers can persist the same Goal in one plugin instance. Core writes are serialized by
plugin.ts's per-session queue, but wrapper writers each construct their ownGoalStoreand are not on that queue. A wrapper commit between a coreloadandsavecorrectly triggers the store'sstale_writeguard, but several call sites do not retry.Fix
mutateGoal(store, sessionID, mutator, { maxAttempts })insrc/persistence/store.tsnow validatesmaxAttemptsas an integer in1..8(default 6), retries onlystale_write, and propagates lock/integrity/live-replacement failures unchanged. Mutators are documented and tested as re-entrant; only a successful save commits their effect.chat.message(owned + steering),chat.params,message.updatedaccounting, patch/tool progress, Goal tools, agent boundary, model resume, startup recovery, infrastructure recovery, budget/contract UX and todo binding.goal.id/goal.revisionis captured before retry and a changed revision or same-session replacement is rejected.completionSnapshotIdentity): Goal id/revision/status, requirements, evidence, checks, revision turn baseline,progressRevision, progress fingerprints and the current Todo plan. Notes/timestamps/non-turn usage do not force a replay.opencode_goal_evidence_filere-reads the target file and the persisted evidence reflects the fresh read.Tests (added)
mutateGoalrejects invalid retry budgets before loading, propagates lock/integrity/live-replacement errors without retrying, and re-applies a re-entrant mutator without duplicating updates.Local evidence
npm run check: pass.npm test: 505 tests, 502 pass, 3 skipped, 0 fail (Node 22 and Node 26/macOS).npm run eval: 182/182, weighted score 100%.git diff --check: clean.npm run package:smokefails locally on this macOS host; the identical failure reproduces on unmodifiedmain(native-entry isolation), so it is pre-existing and unrelated to this branch.CI
The 11 upstream checks on head
eab0f93are currentlyaction_required(fork workflow approval), not success. A maintainer needs to approve the workflow runs before the Linux/Windows CI, native 2.1.4 stable host and Goal/Loop lifecycle jobs can execute. I have not claimed CI green.