Repository navigation
Commit 34029ef
## Why
Apply a patch on trunk, link a ticket, and choose to take the loose
edits along. The files move onto the ticket branch, because a carry is
`branch` plus `checkout` and the worktree is left alone. The record
describing them does not: it stays at site level, where a patch applied
on trunk is written.
So the two halves end in different places. Return to trunk and the panel
names the patch, counts its files and offers to revert it, over a tree
that holds none of it. Accept, and the revert fails blaming a trunk
update or a discard that never happened. The claim is stored, so it
greets the contributor every time they come back. Fixes #236.
Reproduced on v1.1.0-beta.2 before writing anything; the steps and the
store evidence are [on the
issue](#236 (comment)).
The detail that shaped this change is that the per-branch model is fine:
a patch applied while already on a ticket is recorded against that
branch. Only the carry from trunk fails to move the record, so this is
one missing read-change-write, not a fault in the layer model.
## What changes
`carryAppliedPatch` moves the record onto the branch the carry just took
the files to, in a single `changeSiteMeta`, called from
`sites:set-ticket` when the carry happened. The clear and the write are
indivisible on purpose: split in two, a concurrent write leaves the
record in both places or in neither, and neither is worse — it takes the
patch text with it, which is what Revert reverses with.
It runs after the branch's own meta is written, so the entry exists with
its branch point rather than being created here holding one field
(#172). A branch with no entry is left alone: `workMetaScope` makes such
a ref read from the site anyway, so the record stays visible over the
files that are there.
Two things fall out that nobody had filed. A trunk update run from a
carried ticket used to reach `mergeSiteMeta({ appliedPatch: null })` and
destroy the only copy of the patch text while the patch was still in the
branch's WIP commit; it now clears an already-null field and leaves the
branch record alive. And `appliedPatchSubmissionRefusal` did not fire on
the branch, so a carried patch's hunks could be submitted as the
contributor's own work — the refusal #328 exists for now happens.
## How to test this
Platforms: either macOS or Windows for the manual pass; any for the
suites.
The journey walks the reported path, so run it rather than clicking
through it:
```sh
npx playwright test --project=journeys tests/e2e/journeys/patch-apply.spec.js
```
`work carried into a ticket takes the applied-patch record with it
(#236)` applies a patch on trunk, links a ticket taking the edits along,
checks both halves arrived, then returns to trunk with **Unlink** and
asserts nothing is offered over the clean tree. It was run red against
`trunk`'s `src/main.js` and green with this change.
The unit regressions, in `tests/unit/ipc-wiring.test.cjs`:
```sh
node --test tests/unit/ipc-wiring.test.cjs
```
Three cases. The carry moves the whole record, patch text included.
Declining the carry leaves it on trunk. And a patch recorded against the
branch while the link was running is not overwritten by trunk's older
one.
**What must not have happened:** no record may exist in both places
after a carry; the patch `text` must survive the move, or Revert loses
what it reverses with; declining the carry must move nothing; and a
patch applied on a ticket directly must still be recorded against that
branch as before.
## Risks and limitations
**Sites already in the split state are not repaired.** Trunk keeps its
stale record on those until someone presses Revert. Deliberately out of
scope: dropping a site-level record when trunk is clean is a behaviour
change on existing installs and deserves its own decision, not a quiet
ride-along with a bug fix.
The move makes a carried patch revertible from the ticket, which is what
the issue asks for and is a visible behaviour change: submitting that
ticket is now refused while the layer is applied.
## Related
Fixes #236. #356, which would have dissolved this bug by retiring the
app-held layer, is closed as not planned, so the direct fix is the only
one coming.
---
<details>
<summary>Review outcome (required — see AGENTS.md)</summary>
**0 [fix here] · 3 [follow-up].** Fresh-context judgement pass against
`origin/trunk`, dispatched to a subagent with the diff and
`.github/instructions/code-review.instructions.md` and nothing else from
the session that wrote the code.
Deterministic layer: `npm run lint` clean; `npm test` 1454 pass, 0 fail,
2 expected skips; electron suite 1455 pass; all 30 journeys pass.
The reviewer traced the carry path end to end and confirmed `branchRef`
is the ref HEAD is on, so the record lands where
`readWorkMeta`/`workMetaScope` look for it, and checked every consumer
of the record for a regression: submission refusal, revert, both
discards, trunk update, unlink, ticket switch, branch delete and the
branch list. None regressed; two improved, as described above.
Acted on here:
- The race where a patch applied during the link is overwritten by the
carry. Pre-fix it produced a phantom trunk record; post-fix it would
have destroyed revert data, so it is guarded in this PR with its own
regression test rather than deferred.
- A cosmetic indentation inconsistency in the new helper.
Deferred, as issues rather than as comments:
- The pair `mergeBranchMeta` + `carryAppliedPatch` is two writes, so a
quit landing between them persists the #236 state. The window is two
microtasks with no I/O. Closing it means folding both into one
`changeSiteMeta`, which reshapes the handler.
- `updateIncomplete` is the same class of bug and does not travel with a
carry either, leaving a false "Update incomplete" banner on trunk.
Pre-existing and untouched by this change.
</details>
<details>
<summary>Screenshots or recording</summary>
The change removes something from the screen rather than adding one:
after a carry, trunk no longer shows the applied-patch card. The journey
asserts it by count.
</details>
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_0121dia1RzfskutrzA8iSK1K
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 1c03422 commit 34029ef
3 files changed
Lines changed: 168 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1573 | 1573 | | |
1574 | 1574 | | |
1575 | 1575 | | |
| 1576 | + | |
| 1577 | + | |
| 1578 | + | |
| 1579 | + | |
| 1580 | + | |
| 1581 | + | |
| 1582 | + | |
| 1583 | + | |
| 1584 | + | |
| 1585 | + | |
| 1586 | + | |
| 1587 | + | |
| 1588 | + | |
| 1589 | + | |
| 1590 | + | |
| 1591 | + | |
| 1592 | + | |
| 1593 | + | |
| 1594 | + | |
| 1595 | + | |
| 1596 | + | |
| 1597 | + | |
| 1598 | + | |
| 1599 | + | |
| 1600 | + | |
| 1601 | + | |
| 1602 | + | |
| 1603 | + | |
| 1604 | + | |
| 1605 | + | |
| 1606 | + | |
| 1607 | + | |
| 1608 | + | |
| 1609 | + | |
| 1610 | + | |
| 1611 | + | |
| 1612 | + | |
| 1613 | + | |
| 1614 | + | |
| 1615 | + | |
| 1616 | + | |
| 1617 | + | |
| 1618 | + | |
1576 | 1619 | | |
1577 | 1620 | | |
1578 | 1621 | | |
| |||
2882 | 2925 | | |
2883 | 2926 | | |
2884 | 2927 | | |
2885 | | - | |
| 2928 | + | |
| 2929 | + | |
| 2930 | + | |
| 2931 | + | |
| 2932 | + | |
| 2933 | + | |
2886 | 2934 | | |
2887 | 2935 | | |
2888 | 2936 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
232 | 232 | | |
233 | 233 | | |
234 | 234 | | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4484 | 4484 | | |
4485 | 4485 | | |
4486 | 4486 | | |
| 4487 | + | |
| 4488 | + | |
| 4489 | + | |
| 4490 | + | |
| 4491 | + | |
| 4492 | + | |
| 4493 | + | |
| 4494 | + | |
| 4495 | + | |
| 4496 | + | |
| 4497 | + | |
| 4498 | + | |
| 4499 | + | |
| 4500 | + | |
| 4501 | + | |
| 4502 | + | |
| 4503 | + | |
| 4504 | + | |
| 4505 | + | |
| 4506 | + | |
| 4507 | + | |
| 4508 | + | |
| 4509 | + | |
| 4510 | + | |
| 4511 | + | |
| 4512 | + | |
| 4513 | + | |
| 4514 | + | |
| 4515 | + | |
| 4516 | + | |
| 4517 | + | |
| 4518 | + | |
| 4519 | + | |
| 4520 | + | |
| 4521 | + | |
| 4522 | + | |
| 4523 | + | |
| 4524 | + | |
| 4525 | + | |
| 4526 | + | |
| 4527 | + | |
| 4528 | + | |
| 4529 | + | |
| 4530 | + | |
| 4531 | + | |
| 4532 | + | |
| 4533 | + | |
| 4534 | + | |
| 4535 | + | |
| 4536 | + | |
| 4537 | + | |
| 4538 | + | |
| 4539 | + | |
| 4540 | + | |
| 4541 | + | |
| 4542 | + | |
| 4543 | + | |
| 4544 | + | |
| 4545 | + | |
| 4546 | + | |
| 4547 | + | |
| 4548 | + | |
| 4549 | + | |
| 4550 | + | |
| 4551 | + | |
| 4552 | + | |
| 4553 | + | |
| 4554 | + | |
| 4555 | + | |
| 4556 | + | |
| 4557 | + | |
| 4558 | + | |
| 4559 | + | |
4487 | 4560 | | |
4488 | 4561 | | |
4489 | 4562 | | |
| |||
0 commit comments