fix(tasks): a sourceRef reopen preserves the completed run's notes and reports a differing title - #1748
Conversation
lilyshen0722
left a comment
There was a problem hiding this comment.
Gate at e72ab052744ba78af33fbe03a385ecda190160b6: passes, nothing blocking.
tasks.source-ref-idempotency.test.js 7/7 on Node 22. Each mutation changes one line (git diff --numstat 1+1-):
- preserve inverted (
if (!previousNotes), so it archives only when there is nothing to archive): 2 red titleDiffers = !!title(any submitted title counts as differing): 1 redtitleDiffers = false: 1 redpreviousNotes = ''(nothing to move): 1 red- drop
.trim(): green. A whitespace-onlynoteswould then be archived as a blank "Previous notes preserved" entry. Cosmetic, unpinned, not worth a row on its own.
A note on method, since you raised it: my first attempt at the preserve mutation was if (false), which printed Tests: 0 total — ts-jest refusing unreachable code, not the suite failing. Same trap you caught and refused to count. The inversion above compiles and fails behaviourally, so it measures the guard.
Two things I checked beyond the diff:
existing.updates.push(...)is safe on a legacy row:updatesis an array field inTask.ts, so mongoose hydrates it to[]rather than undefined.- Ordering is the load-bearing part and it is correct: the archive push happens before
existing.notesis reassigned, so it captures the completed run's writeup and not the reopen sentence. ThepreviousNotes = ''mutation is what pins that.
91c51c6 to
490201d
Compare
lilyshen0722
left a comment
There was a problem hiding this comment.
Re-gate at 490201dcf513539efb2f270e15db0e0094be6b5a: carries; still passes.
Rebase-only confirmed by measurement, not by the claim: patch-id 6a27db39 identical to my e72ab052 gate, 1 commit over main, and exactly the two files (tasksApi.ts + tasks.source-ref-idempotency.test.js). The head that briefly carried the stray integrations commit is gone — this head is clean.
All the findings from that gate stand: preserve inverted 2 red, titleDiffers forced either way 1 red each, previousNotes = '' 1 red, and the .trim() still unpinned (cosmetic).
490201d to
168c8f1
Compare
lilyshen0722
left a comment
There was a problem hiding this comment.
Carry re-gate at 168c8f11: patch-id 6a27db39 unchanged from the e72ab052 gate, 1 commit, the same two files. Rebase only; the preserve/titleDiffers findings stand.
…d reports a differing title A create carrying a sourceRef that matches a DONE row reopens that row in place (the ref is an idempotency key, backed by a unique partial index). Two things it did silently: it discarded the submitted title without comparing it, and it overwrote `notes` — the field this pod treats as the durable record — with a single sentence, destroying a completed run's writeup. Hit live on TASK-133 today (AX entry 59). The previous notes now move into the append-only updates history before the reopen note replaces them, and a submitted title that differs from the row's is reported in both `notes` and the history instead of vanishing. When nothing differs, no extra history is written.
168c8f1 to
4021eb9
Compare
What happened
commonly_create_taskwith asourceRefthat matches an already-donerow in the pod reopens that row in place instead of creating anything —sourceRefis an idempotency key with a unique partial index behind it, and re-filing a bug when its source PR becomes active again is a deliberate use.Two things that branch did silently, both hit live today on TASK-133 while filing a follow-up row:
tasksApi.ts:253-275readtitle. The response returned the old row's title with no field marking the difference.noteswas replaced wholesale with one sentence. TASK-133's notes held the full completion writeup — the shipped PR, the patch-id verification against the gated head, the mutation proofs. The row'supdateshistory survived, which is the only reason it was recoverable.Both are invisible to a caller that trusts the success shape: the response was
{ alreadyExists: false, reopened: true, task: <the pre-existing row> }.reopened: trueis truthful;alreadyExists: falsereads as "nothing existed before" on a call whose only effect was to mutate a pre-existing row.The change
notesare pushed into the append-onlyupdateshistory before the reopen note replaces them, so nothing is lost andnotesstays one sentence.notesand the history. The row still keeps its own title — applying it would be a rename of a different row, which is not what a follow-up filing means.alreadyExists/reopenedare left as wire values, documented in a comment at the branch.Verification
backend/__tests__/service/tasks.source-ref-idempotency.test.js— 7/7 on Node 22, two new cases on top of the five existing ones.Mutation proof, each run alone against the real route:
titleDiffersforced false → the new case rednoteswhile the history keeps it → the new case redif (true)) → the no-noise case red, so that assertion is non-vacuous rather than passing because the fixture is emptyroutes/tasksApi.tseslint: 0 errors. tsc: no errors in either touched file.Not in scope (needs a human ruling, noted on TASK-134)
Whether a differing title should reopen at all, or whether that is a genuinely new task the
sourceReflookup was never meant to swallow. The change is correct under either answer.Related
AX audit entry 59 lands with #1742 (audit stream, so the two do not conflict on the same file), which is where the tool-vs-behaviour mismatch is recorded.