Skip to main content
Your progress
0 of 4 lessons complete0%
T5 / M5.9 / L4 OF 4 / Engineer TIER / 9 min

Bi-directional sync

Outcome

By the end of this lesson, you will be able to trace status and ownership changes in both directions, explain how the loop guard prevents ping-pong, and name the deferred limits.


TierEngineer
JTBD”Stop reconciling ticket status against recommendation status by hand.”
PersonasPlatform Engineer · SRE · FinOps Lead
PrerequisitesM5.9.L3
Time9 minutes
Bloom verbTrace (Analyze), Explain (Understand), Name (Remember)

1. Concept

Recommendation status and Jira issue status stay in agreement automatically, and it is event-driven: there is no polling cron.

Outbound: ZopNight to Jira

Terminal window
The recommender emits a status-changed event at its
transition chokepoints. The provisioner transitions or
comments the Jira issue.
closed / dismissed / optimised -> Done
applied / observing -> In Progress
drifted -> a COMMENT

The drifted case is deliberately a comment rather than a transition. Drift means the finding came back, and forcing an issue backwards through a workflow is presumptuous: the comment tells the assignee and lets them decide.

Inbound: Jira to ZopNight

Terminal window
A durable Jira admin webhook, HMAC-verified per
integration, with retries deduplicated on Atlassian's
own delivery identifier.
The org and recommendation are resolved by the
GLOBALLY-UNIQUE FINGERPRINT LABEL (L2).
It publishes an event; the recommender applies the
target status through its state machine.
ILLEGAL TRANSITIONS ARE DROPPED, NEVER FORCED.

That last line preserves an invariant from T2.M2.13: the recommender remains the sole writer of recommendation status. Jira can request a transition; it cannot impose one that the state machine does not permit.

The loop guard

Two systems that write to each other will ping-pong unless something stops them.

Terminal window
ZopNight sets status -> pushes to Jira -> Jira emits a
change event -> ZopNight receives it -> sets status ->
pushes to Jira -> ...
THE GUARD keys on the link's LOCALE-INDEPENDENT NEUTRAL
STATUS CATEGORY plus the LAST-PUSHED recommendation
status.
So an inbound event that merely reflects what we just
pushed is recognised and dropped.

The locale-independent part is worth noticing. Jira status names are localised, so a guard comparing display strings would break on a Jira instance running in another language. Comparing the neutral category rather than the label is what makes it work anywhere.

The whole thing is idempotent under at-least-once delivery, which it needs to be: Jira retries webhooks, and the dedup on the delivery identifier plus the loop guard together make reprocessing harmless.

Ownership sync, both directions

Terminal window
ZOPNIGHT -> JIRA
Set an owner on a recommendation, and it updates the
ticket link and RE-ASSIGNS THE SAME ISSUE.
It NEVER MINTS A NEW TICKET.
JIRA -> ZOPNIGHT
A Jira assignee change flows back onto the
recommendation's owner via the inbound webhook.
Prefers email; falls back to display name where
privacy settings hide the email; clears on unassign.
Both directions are loop-guarded.

The “never mints a new ticket” guarantee matters: changing an owner is an update to an existing thing, and a system that created a second ticket on reassignment would double its own queue.

A deleted Jira issue

Terminal window
SOFT-DELETES THE LINK. THE RECOMMENDATION IS UNTOUCHED.

The right behaviour: someone deleting a ticket is saying something about their queue, not about your infrastructure. The finding remains open in ZopNight, where it belongs.

The deferred limits

Worth knowing so you do not design around something absent:

Terminal window
SINGLE SHARED WEBHOOK HMAC SECRET
A per-integration secret is a hardening follow-up.
NO JQL SEARCH-BEFORE-CREATE
Dedup is on the fingerprint, not a search of your
Jira project. A ticket someone created by hand,
without the label, is not found.
NO DAILY BACKSTOP SWEEP
Jira's own webhook retries plus the idempotent
reprocessing carry it. If a webhook is lost past
those retries, that change is not reconciled later.
NO JIRA-REOPEN -> RECOMMENDATION-REOPEN
Reopening a Jira issue does not reopen the
recommendation.
NO ACTIVITY-EVENTS TABLE, and observability counters
are follow-ups.

The second and fourth are the ones that show up in practice.

Terminal window
NO SEARCH-BEFORE-CREATE means a hand-made ticket
without the fingerprint label is invisible to dedup, so
automation can open a second one for the same problem.
If your team creates cost tickets by hand today, either
route them through the integration or accept some
overlap during transition.

2. Demo

A quarter of sync, including the two things that surprised people:

Terminal window
NORMAL FLOW
A rec is applied in ZopNight
-> Jira issue moves to In Progress
Verification later confirms adoption; rec closes
-> issue moves to Done
Nobody touched Jira. That is the point.
INBOUND, WORKING
An engineer closes the Jira issue as Done from their
queue.
-> webhook fires, HMAC verified
-> org and rec resolved from the fingerprint label
-> the recommender applies the status through its
state machine
No ping-pong. The loop guard recognised the subsequent
outbound push as reflecting what had just arrived.
SURPRISE 1: A LOCALISED JIRA
A subsidiary ran Jira in German. Status labels came
through localised.
Sync worked. The loop guard keys on the NEUTRAL
STATUS CATEGORY rather than the display label, which
is exactly the case that would have broken a
string-comparison guard.
SURPRISE 2: A REOPENED TICKET
An engineer reopened a Done issue, expecting the
recommendation to reopen too.
It did not. Jira-reopen to recommendation-reopen is
DEFERRED.
The right mental model: the recommender is the sole
writer of recommendation status, and it reopens a
finding when the RULE FIRES AGAIN, not when a human
reopens a ticket. Jira can request transitions the
state machine permits; it cannot substitute for
detection.
THE HAND-MADE TICKET OVERLAP
During transition, the team had 11 hand-made cost
tickets with no fingerprint label.
Auto-ticketing opened new tickets for 6 of those
problems, because dedup is on the fingerprint and
there is no JQL search-before-create.
Not a bug, and they had been told. They closed the 6
hand-made duplicates and routed everything through
the integration afterwards.
AN ASSIGNEE ROUND TRIP
Rec owner set in ZopNight -> the SAME issue
reassigned, no new ticket.
A week later the Jira assignee changed during a
handover -> the rec's owner updated to match.
Loop-guarded in both directions.

3. Hands-on (6 min)

Terminal window
1. Apply a recommendation that has a linked ticket.
Did the issue move to In Progress? Y / N
How long did it take? ______ seconds
(Event-driven, so seconds rather than a cron cycle.)
2. Close the Jira issue from Jira. Did the rec status
follow? Y / N
Any ping-pong? Y / N
3. Reopen that Jira issue. Did the rec reopen? Y / N
Explain why not, in one line:
______________________________________________
4. Count your hand-made cost tickets that predate the
integration: ______
Those carry no fingerprint label. What will you do?
[ ] close and re-route [ ] accept overlap
5. If you run Jira in a non-English locale, confirm sync
works: Y / N
(The guard uses the neutral category, not labels.)

4. Knowledge check

Q1

What prevents recommendation status and Jira status from ping-ponging endlessly?

A. A cooldown period after each push
B. Outbound sync is disabled the moment inbound sync becomes active, so only one direction is ever live at a time and a loop is impossible by construction in the design itself entirely
C. Jira suppresses webhook events triggered by API calls
D. A loop guard keyed on the link’s locale-independent neutral status category plus the last-pushed recommendation status, so an inbound event that merely reflects what was just pushed is recognised and dropped

Show answer

Correct: D. Using the neutral category rather than display labels is what keeps it working on a Jira instance in another language. The localisation detail is the one that would break a naive implementation: status names are localised, so a guard comparing display strings would fail on a non-English Jira.

Q2

An engineer reopens a Done Jira issue. The linked recommendation:

A. Does not. Jira-reopen to recommendation-reopen is deferred
B. Reopens automatically
C. Reopens after the next evaluation cycle
D. Reopens only if the rec was closed by sync rather than by verification

Show answer

Correct: A. The recommender is the sole writer of recommendation status and reopens a finding when the rule fires again, so Jira can request transitions the state machine permits but cannot substitute for detection. The same invariant explains why illegal transitions arriving from Jira are dropped rather than forced: an external system requests, and the state machine decides.

Q3

A team has hand-made cost tickets predating the integration. When auto-ticketing is enabled:

A. Those tickets are found and reused
B. The hand-made tickets are labelled retroactively
C. The integration refuses to open tickets in any project that already contains untagged issues created by hand previously
D. New tickets may be opened for the same problems, because dedup is on the fingerprint label and there is no JQL search-before-create

Show answer

Correct: D. A hand-made ticket without the label is invisible to dedup. This is a known deferred limit rather than a defect, and it is worth planning for: either route existing tickets through the integration or accept some overlap during the transition and close the duplicates.


5. Apply

Test the round trip once in each direction before relying on it: apply a recommendation and watch the issue move, then close the issue and watch the recommendation follow. Seeing both work is what lets you stop reconciling by hand.

Deal with hand-made tickets before enabling auto-ticketing, since they carry no fingerprint and will not deduplicate.


Glossary terms touched

Loop guard · Neutral status category · HMAC verification · Ownership sync


Start with the bill.

Foundations takes about five hours. The first lesson is nine minutes.

Open curriculum. No login. No paywall. 290 lessons across 7 courses, three publicly verifiable credentials. Read it on the train, take the exam on a Saturday, list the credential on your résumé Monday.

5h median time to finish Foundations
0 logins, paywalls, or marketing forms
open curriculum, public credential verifier
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·