Skip to content

fix(state-integrity): make operational state updates transactional and outcome-aware — prevent stale, partial, or truncated results - #773

Closed
unohee wants to merge 6 commits into
mainfrom
swarm/AGT-3489-fix-state-integrity-make-operational-sta
Closed

unohee wants to merge 6 commits into
mainfrom
swarm/AGT-3489-fix-state-integrity-make-operational-sta

Conversation

@unohee

@unohee unohee commented Sep 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Published because this run stopped and needs a human: autonomous execution failed 4 times

It has not been reviewed and is very likely incomplete — this PR is a draft on purpose. It exists so the work is reviewable instead of sitting on a branch that was never pushed.

Change shape

427 file(s): 212 source · 192 test · 6 docs · 17 other

Base freshness

Branch base is 171 commit(s) behind main at publication.

⚠ Conflicts with main in: src/automation/dailyReporter.ts, src/orchestration/workflow.ts
GitHub runs no pull_request workflows on a PR it cannot merge, so any green checks here are not the verification gate. Opened as a draft; rebase before review.

⚠️ File overlap with in-flight work

This branch changes files that other open PRs / active branches also touch. Coordinate before merging to avoid divergent parallel edits (INT-2388 #3):

Linear

Closes AGT-3489


🤖 Generated with OpenSwarm

unohee added a commit that referenced this pull request Sep 28, 2026
…arness (#757 #771 #773 #777) (#786)

* fix(state-integrity): salvage durable state fences and the review quality harness

Salvages the still-unique work from four abandoned draft PRs (#757, #771,
#773, #777) onto current main. Each PR's merge base is 172-247 commits
behind, so only hunks absent from main were taken; work main already
implemented independently was kept as-is.

#757 (automation-state concurrency/recovery)
- storeFileStamp(): the task-state cache stamp now includes the inode, so a
  same-size cross-process replacement (atomic rename) inside one mtime tick
  invalidates the cache instead of serving a stale snapshot forever.
- validateExecution(): rejects incomplete step results and DAG-illegal
  lifecycle states before a workflow execution is persisted.
- dev.ts: the close handler's reporting is wrapped in try/catch/finally so
  onComplete and activeTasks.delete always run.
- memoryBridge.ts: one memory_linked event, not two (linkMemory already
  emits it).
- linearBridge.ts: a failed SDK load no longer pins the rejected init
  promise, so the next call can retry.

#771 (review quality harness)
- New src/verify/qualityHarness.ts: full-tree, fail-closed static scan over
  every tracked source file plus isolated verify commands, wired into
  `review --max`, `--harness-only`, and the markdown audit report.
- The harness test fixture put the empty catch body on its own line, which
  the per-line bs detector can never match — the detection assertion was
  vacuous. The fixture is single-line now and asserts a real finding.

#773 (transactional state updates)
- changeStatus()/updateIssue() read the effective status inside the write
  transaction so the event log's oldValue cannot be stamped stale.
- saveExecution() gains a definitionStamp fence that refuses a snapshot
  whose workflow definition was replaced underneath it.
- dailyReporter: bounded per-project retry; the watermark advances only
  when every project succeeded after that retry.
- projectUpdater: buildBoundedProjectDescription() reserves room for the
  summary before truncating, so the 255-char limit cannot chop it off.
- projectHandler: a failed quarantine surfaces as its own error instead of
  being reported as a successful preserve.

#777 (issue/delivery mutation scope)
- linearBridge: pendingLinearMappings recovery + idempotencyKey, so a
  failed local mapping persist cannot orphan-recreate a Linear issue.
- sqliteStore.createIssue() honors the documented idempotent-id contract.
- backlogGrooming: validIssueIds is mandatory at parse time and apply time,
  so a hallucinated id can never reach a mutation path.
- workerValidationEvidence: EXECUTABLE_SOURCE_FILE_RE keeps shell/sql/etc.
  source under data dirs in scope for validation evidence.
- projectUpdater: fetchProjectOverviewIssues surfaces missing/repeated
  cursors instead of silently truncating the page walk.

* fix(issues): reject an idempotent create whose stored row is a different artifact

CI caught a regression from the salvaged idempotent-createIssue guard:
src/automation/taskSource.test.ts:174 "rejects an idempotent child collision
when a retried plan changed" (6799 passed, 1 failed).

The guard returned the existing row for any caller-supplied id, which erased
SqliteTaskSource's deterministic duplicate-sibling guard (AGT-2908): its
createSubIssue wraps createIssue in try/catch and relies on the UNIQUE(id)
collision to distinguish "the same decomposition retried" (identical title+
description → reuse the child) from "the retried plan changed" (→ report
`existing artifact does not match the requested plan`). Returning the stored
row unconditionally made every changed-plan retry look like a successful
reuse, so the caller got the OLD child's title back instead of `{ error }`.

Reconciled rather than reverted: a caller-supplied id found in the store is
only treated as an idempotent retry when the identity-bearing fields agree
(title, description, parentId, projectId — via sameIssueContent). Otherwise
createIssue throws an explicit collision error naming the id, so:
  - an identical retry returns the existing row  (keeps #777's intent), and
  - a materially different artifact under the same id is rejected and NOT
    overwritten (keeps AGT-2908's invariant and its test green).
The same comparison is applied to the concurrent-create catch branch, which
previously also returned the winner's row unconditionally.

sqliteStore.test.ts's salvaged case asserted the over-permissive behavior
("second.title === 'first'" for a changed title); it now asserts the real
contract instead of being deleted, plus a new case pinning the rejection.

* docs(changelog): record the salvaged state-integrity and quality-harness changes
@unohee

unohee commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: superseded — the salvageable work in this draft was rebased onto current main, verified, and re-published as a reviewed PR: absorbed into #786. Closing the stale draft instead of merging it, because it was 170-250 commits behind, carried scratch files, and (in several cases) reverted main's later hardening.

@unohee unohee closed this Sep 28, 2026
@unohee
unohee deleted the swarm/AGT-3489-fix-state-integrity-make-operational-sta branch September 28, 2026 07:02
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