fix(integration-ever-works): an adopted task is applied inbound when tasks sync one way from Ever Works (#11096 review)

Merged
EV
8d ago
+179-4

Addresses CodeRabbit review comment 4308331596 on #11096 (Major): "Apply adopted tasks when outbound sync is disabled".

Scenario

A task Gauzy created in Ever Works whose answer was lost (no link recorded) is adopted back when Ever Works delivers it: Ever Works lists Gauzy's external link on it. adoptOwn (review F81) links the pair with no Gauzy side, records the incoming version, and queues a push so the Gauzy values go out.

With directions.tasks = ew_to_gauzy (one way from Ever Works), apply accepts the event but the queued push is skipped (tasks are not pushed to Ever Works). The delivered change and every other Ever Works value never reached Gauzy. Because the version was already recorded, a repeat delivery was skipped (already applied (version)), and later events left those fields alone because the Ever Works side of the snapshot already held them.

Fix

ever-works-tasks.service.ts, adoptOwn: when tasks are not pushed to Ever Works, the adoption reconciles inbound. The pair is linked with the Gauzy side as it is and no version yet, so a failed apply is retried and not skipped. adoptOwn then returns the link, and the normal updateFromEverWorks applies Ever Works' values. One exception: the title Gauzy sent cut (over 200 characters) counts as Ever Works' already, so its cut copy never overwrites the full Gauzy title (the F81 concern). The two-way adoption (Gauzy values pushed) is unchanged.

Tests

New spec in ever-works-tasks.service.spec.ts: with tasks set to ew_to_gauzy, an adopted task delivered with Ever Works edits (status, priority, label, description; cut title + full-title appendix) gets those edits in Gauzy on the first delivery and keeps its full 250-character title. The link carries the delivered version, no push is queued, and redelivering the same event is skipped as already applied. The existing F81 spec (two-way) still passes unchanged.

  • src/lib/tasks before the fix (new spec on unfixed code): 1 failed, 87 passed, 88 total. The adopted task stayed in-progress / medium / old description / no tags.
  • src/lib/tasks with the fix: 88 passed, 88 total.
  • If the title seed is removed, the new spec fails on the title, so the spec checks it.
  • Whole plugin with the fix: 41/42 suites, 754 passed, 1 failed, 755 total. The failure was client/ever-works-loopback.spec.ts › reaches a loopback Ever Works over HTTP, an HTTP loopback test unrelated to tasks. The suite took 1240 s on a heavily loaded machine (another plugin jest run was going in parallel). Rerun alone it passes: 24 passed, 24 total.
  • tsc -p packages/plugins/integration-ever-works/tsconfig.lib.json --noEmit: no errors in the plugin's files.

Review round 2 (7872a9fd16)

  • CodeRabbit 4308508201: on a one-way adoption, Gauzy's sets and dates are now seeded on the link as Ever Works would hold them (labels, linked team / person / task ids, minutes). Ever Works' sets and dates are then taken as they are: a Gauzy tag, assignee, watcher, team or edge that Ever Works does not have is removed, and a Gauzy-only due date is cleared. A member Ever Works cannot hold (not linked) stays.
  • Greptile 4308509049: the cut-title exception now reads the full title from the description's appendix (isCutTitleCopy) instead of assuming Gauzy's current title. A long Gauzy title renamed before the adoption is kept, and a rename made in Ever Works is still taken.
  • Specs: sets and dates authoritative (tag Old→New, assignee and watcher Alice→Bob, due date cleared); long title renamed in Gauzy kept and Ever Works rename taken; isCutTitleCopy unit test. src/lib/tasks: 2 failed, 88 passed, 90 total on 78dcfb4914 (before the unit test was added), then 91 passed, 91 total. Whole plugin: 42/42 suites, 758 passed, 758 total.

Review round 3 (cc42087322)

  • Greptile 4308585154: isCutTitleCopy now accepts only the copy cut where Gauzy cuts it, i.e. titleForEverWorks(full).title with whitespace collapsed. The one exception is a run-of-spaces title that fills the whole 199-character limit. A title shortened in Ever Works (Investigate…) is a rename and is applied.
  • Specs: the shortened rename is taken at adoption (the exact cut copy still is not), and the unit cases are extended. src/lib/tasks: 2 failed, 89 passed before the fix, then 91 passed, 91 total. Whole plugin: 42/42 suites, 758 passed, 758 total.

Review round 4 (603dee5799)

  • Greptile 4308628942: isCutTitleCopy now uses one rule for the whole class instead of single cases. The title ends with …. Its start, compared with whitespace collapsed, is a prefix of the appendix's full title. The raw start is either exactly 199 characters, or shorter because trimEnd() removed the whitespace run the cut fell on. In that case the full title must continue with a space there, and the raw length must be at least 199 − (whitespace in the start + 1). Collapsing never shortens the raw start, so only that trim needs a window. A shortened rename falls far below the window, and a rename cut mid-word fails the space check.
  • Table-driven unit specs (11 cases: 5 cut copies including the review's boundary case, 6 renames or non-matches). src/lib/tasks: 2 failed, 99 passed on the previous mapper, then 101 passed, 101 total. Whole plugin: 42/42 suites, 768 passed, 768 total.

Review round 5 (7ba68b0cb9, 7370faf6f7)

  • CodeRabbit 4308671740, fixed at the root. The full-title line stored the title with whitespace collapsed while the cut was taken from the raw title, so the cut point could not be reconstructed. titleForEverWorks now collapses whitespace first for a title over the limit. If the collapsed title fits, it is sent whole with no line; otherwise the collapsed title is cut. Titles within the limit are unchanged. isCutTitleCopy is now an exact check against titleForEverWorks(fullTitle), and the window heuristic from round 4 is removed. No legacy cut titles exist (the integration is new and off by default).
  • CodeRabbit 4308671742: the one-way adoption seed now includes parentTaskId (the linked Ever Works id of Gauzy's parent, or none when that parent is unlinked). Ever Works' 'no parent' therefore keeps a Gauzy parent it cannot hold, while a linked parent that Ever Works removed is still cleared.
  • Merged origin/develop (#11103, #11107) and added bytewise and callouts to .cspell.json. cspell reports 0 issues on the 3 docs files and the changed task files.
  • Specs: the title table (both of CodeRabbit's boundaries, A/B/C, double spaces, a cut on a space, renames), the collapsed-title-fits case, and the parent adoption spec. src/lib/tasks: 4 failed, 100 passed on 603dee5799, then 104 passed, 104 total. Whole plugin (with develop merged): 42/42 suites, 776 passed, 776 total.

🤖 Generated with Claude Code


Summary by cubic

Fixes Ever Works tasks adopted back never reaching Gauzy when tasks sync one way from Ever Works (ew_to_gauzy): delivered changes, sets, dates, and title renames now apply on the first delivery.

  • Adoption links the pair with the Gauzy side as-is and no version yet, so a failed apply is retried and a repeat delivery is skipped as already applied.
  • Gauzy's sets, parent, and dates count as Ever Works' previous ones: Ever Works' values are taken as they are, but a member or parent Ever Works cannot hold (not linked) stays.
  • A long Gauzy title is now cut from its collapsed form, so its cut copy is recognizable exactly: only that exact copy stays when it's still in the description's appendix; any other Ever Works title, including a shortened rename, is taken.
  • The spelling dictionary picks up two Ever Works docs terms (bytewise, callouts); two-way adoption (Gauzy values pushed) is unchanged.