A ticket that changes column while nobody on the team touched it reads as a bug in the integration, and it is usually the integration working. Two systems writing the same field both have the right to be right, so somebody loses every tie. A system that never named the loser picks one anyway, by arrival order.
Nobody on the team moved the ticket
The report reaches bug triage as a question about trust, not about a defect. A ticket sat in QA on Monday and sits in WIP on Tuesday, the engineer who owns it did not move it, and the trail names something nobody recognises. The instinct is to hunt for the fault in the delivery: a webhook that fired twice, a retry that replayed an old payload.
That instinct sends people to the wrong place. The delivery did its job, and both systems did what they were configured to do. A developer dragged the issue in the tracker at roughly the moment a support lead dragged the ticket on the board, and each then told the other. What is missing is a rule naming which of those two gestures survives, written down before either happened. Every integration that writes state in both directions has such a rule, and most hold it by accident, in the shape of whichever update was processed second.
Somebody has to lose, and deciding late is deciding by accident
There are three honest answers, and they are not equally good.
The first is that the most recent write wins. It sounds neutral, and it is what falls out of writing no rule at all. What it does instead is hand the decision to two clocks that were never synchronised and to a queue depth nobody controls, so a team gets one answer on a slow afternoon and the opposite one on a fast morning.
The second is to dissolve the conflict instead of resolving it, and let a convergent data structure settle it later. That works for a set of labels or a text buffer. A pipeline stage is one field with human meaning behind it: QA and Done do not merge, because two people decided two different things and no data structure turns a disagreement into an agreement.
The third is to name an authority in advance and let it win every tie. We name the tracker, and the reasoning is about where the work happens, not where the evidence lives. A feedback tool that overrules a tracker transition comes off the stack, and being right about one ticket is not worth that.
Picking has a price. The other side loses, and somebody has to be told.
A tie is narrower than two people disagreeing
The word conflict does more damage here than it should, because two edits landing close together are not automatically one. A tie has to satisfy both halves of a narrower test: the local value has drifted away from the last value both sides agreed on, and it is not already where the incoming value wants to put it. A support lead who moved a ticket to Done a moment before the tracker said Done lost nothing, and warning them otherwise trains a team to ignore the warning that matters.
Order needs the same narrowing. An event reporting an older change must never overwrite a newer one, and the comparison deciding which is older cannot be a clock on either machine. It is the tracker’s own record of when that issue last changed, carried forward on every apply, so an event that lost a race in the network arrives late and moves nothing.
The loop closes on two such facts rather than on identity: an outbound push is skipped when the status already matches what both sides last agreed on, and a status write that changes nothing records nothing: the row is touched, and no activity entry, no notification and no webhook follows it. Detecting your own updates by actor instead depends on every provider handing that identity back, and one that does not is an echo nobody catches until a ticket has bounced all afternoon.
Every tracker gets the same rule, and none of them agrees what a state is
The rule is identical across the seven trackers the adapter contract covers. The thing being ruled on is not, which is where a state map stops being configuration and becomes a design decision.
| Tracker | What it treats as a state | What that costs the map |
|---|---|---|
| Linear | Workflow states carrying a type | The closest thing to a real state machine, so mapping is nearly a rename |
| Jira | Statuses defined per project, pooled across issue types | The map belongs to one project; a second project needs its own |
| GitHub | Nothing. An issue is open or closed | Intermediate stages exist only as labels the app mints and owns; the repository’s own labels are left untouched |
| Trello | Which list the card sits in | A board with one Doing list cannot express two stages, so one stays unmapped |
| Asana | Sections of a project, with completion tracked apart from them | A task completed without changing section is a move the map cannot see |
| ClickUp | Status strings scoped to one list, where the identifier is the visible name | Renaming a status breaks the map, because the name was the identifier |
| monday.com | Groups on a board | The map is per board, so a second board needs its own |
Set side by side, the state map stops looking like configuration. Three of these express a stage as the container a card sits in, so the pipeline has to fit inside a vocabulary somebody else designed for another purpose; a fourth has no vocabulary for an intermediate stage at all, which is why every intermediate stage becomes a label the app mints on issues in GitHub, and the last one is the issue being closed. A tie can only ever be about a stage both sides can name, and the right-hand column is the list of stages one side cannot.
An admin sets that map once for each connected tracker, and a map that is not one to one does not survive being saved: the inbound direction reads it backwards, so a state a second stage tries to claim is dropped, because one tracker state pointing at two stages has no unambiguous answer. A state arriving from the tracker that the map does not cover is recorded against the ticket and then ignored. The alternative is a guess, and a guess moves tickets nobody asked to move.
The board can disagree with its tracker, and it says so
So the trade is stated instead of buried. When two people move the same ticket on both boards inside one window, the tracker wins and the Notarus move is applied and then replaced. What becomes of the losing decision is the part worth knowing.
| What becomes of the losing move | Where it goes |
|---|---|
| The value the tracker replaced | Kept on the ticket’s activity record, and named in the chat message |
| The link between ticket and issue | Marked in conflict, so the board card carries the marker |
| The team | A message on the project’s chat channel, when one is connected |
None of those three is a prompt: nobody is asked to pick a winner afterwards, because the picking happened when the connection was configured.
The limit worth planning around is ours rather than the practice in general: status gets managed in one place, and that place is the tracker. A team treating the Notarus board as where a stage is decided will keep meeting the tracker at ties and losing them, correctly, and every loss is noise a stand-up absorbs. The board is where the evidence is read and the tracker is where the stage is agreed, which is the shape of every project management integration here.
One more thing decides how much any of this matters. Webhook delivery is the fast path and not the only one: every fifteen minutes a reconciliation pass re-reads a bounded batch of linked issues, least recently checked first, and applies whatever the tracker currently says. A board that drifted quietly gets repaired on that rota without anybody filing a second report, though a link already marked in conflict sits outside the rota and waits for the tracker’s next delivery instead. The guarantee is a rota, not a deadline, and the batch is drawn across the whole installation rather than one project, so the wait stretches with every link queued ahead of yours.