Skip to content

feat(AF-992): map ticket resolution onto a deployment decision - #1184

Merged
babltiga merged 1 commit into
mainfrom
feature/AF-992-deployment-ticket-decision
Oct 22, 2026
Merged

babltiga merged 1 commit into
mainfrom
feature/AF-992-deployment-ticket-decision

Conversation

@babltiga

Copy link
Copy Markdown
Contributor

Closes #992 (part of epic #989).

What

A signed ServiceNow / Jira status callback now reaches deployment change tickets as well as query tickets.

  • TicketingInboundService checks query_tickets first (a query ticket still wins), then falls back to deployment_tickets. If neither matches, the result is ignored.
  • Every verified callback stamps the mapped decision (APPROVED / REJECTED / null) on the deployment_tickets row, whether or not bidirectional_sync is on. It also writes TICKET_STATUS_SYNCED against deployment_ticket.
  • With bidirectional_sync on, the still-PENDING_REVIEW deployment is decided through the new workflow.api.ExternalDecisionService.applyDeploymentTicketDecision. That method delegates to the new deploygov.api.DeploymentExternalDecisionService.
  • The façade lives in workflow because workflow → deploygov already exists, and deploygov uses core.api.DecisionType.
  • The decision is system-attributed:
    • no deployment_review_decisions row
    • a DEPLOYMENT_APPROVED / DEPLOYMENT_REJECTED audit row with a null actor, trigger=ticket, and ticket_system / external_key / channel_id / actor / pipeline_id / version
    • DeploymentDecidedEvent(..., "ticket:<SYSTEM>:<key>"), so the submitter is notified with no listener change
    • a lost race returns false → the webhook answers synced

Deviations from the issue sketch

  • The deploygov method takes a DeploymentTicketProvenance record instead of a bare reason string, so the audit metadata the issue requires (ticket_system, external_key, channel_id, actor) is exact.
  • The request is loaded with a new DeploymentRequestRepository.findByIdForUpdate (PESSIMISTIC_WRITE) instead of stateService.require. This means a missing row returns false, and a concurrent manual decision can never leave a ticket-attributed audit row behind for a transition that then fails to commit. The audit log writes on its own connection, so without the lock that row would commit even when the status change did not.

Docs / website

  • docs/03-data-model.md, docs/18-deployment-governance.md: the ticket audit trigger
  • docs/04-api-spec.md: webhook resolution order
  • docs/05-backend.md: deployment paragraph under External ticket decisions, and the ticket trigger
  • docs/08-notifications.md: the deployment section, including the self-approval caveat (the actor is free text, so a submitter approving their own change ticket is not stopped; keep sync off if you need that guarantee). It also says the gate conjunct that reads the stamp is deploygov: gate requires an approved ticket on change_ticket_required environments #993, not built yet
  • website/docs/configuration/notifications/, website/docs/guides/notifications/ (dates bumped in JSON-LD, visible date and sitemap), and help-corpus/ regenerated
  • CLAUDE.md deploygov entry

Tests

  • New DefaultDeploymentExternalDecisionServiceTest
  • Extended DefaultExternalDecisionServiceTest and TicketingInboundServiceTest (unknown in both tables, query ticket wins, sync off still stamps, unmapped status stamps null, approve and reject applied, raced decision → synced, audit failure swallowed)
  • New DeploymentTicketWebhookIntegrationTest: signed HTTP callback to /api/v1/integrations/jira/webhook/{channelId}, sync on and sync off, asserting the ticket decision, the request status and the audit rows

Verified locally: spotless + checkstyle; all deploygov, notifications and workflow tests plus ApplicationModulesTest / ApiPackageDependencyTest (3064 tests, green); MessagesParityTest; website guards (npm run test:coverage); help-corpus drift (byte-identical). Not run: the full mvn verify -Pcoverage.

No e2e spec covers the ticketing webhook and there is no frontend change, so no e2e update. No UI change, so no screenshots.

Review notes

  • af-content-reviewer: flagged a Blocker: docs/08 described the gate reading the stamp as if it already happened. Fixed: the docs now mark that as deploygov: gate requires an approved ticket on change_ticket_required environments #993. Its wording concerns on the website pages were also addressed.
  • af-java-reviewer / af-reviewer: both flagged that a commit-time optimistic-lock race could leave a false DEPLOYMENT_* audit row. Fixed with the row lock above, and the inbound-service catch for that case was removed. af-reviewer also asked for the ticket trigger in docs 03/05/18, which is done. Its other point, that docs/18 has no change-ticket chapter yet, is left for the epic's docs sweep.
  • af-java-reviewer nit: the test mocked a decision repository the service never touches, so that check could never fail. Removed.
  • Concern still open: DeploymentRequestStateService.markTimedOut and DeploymentReviewStateMachine have the same audit-before-commit ordering. That existed before this PR and is not touched here.

@github-actions

Copy link
Copy Markdown
Contributor

Frontend Test Results

    1 files    341 suites   9m 49s ⏱️
2 968 tests 2 968 ✅ 0 💤 0 ❌
2 969 runs  2 969 ✅ 0 💤 0 ❌

Results for commit 6ae02ba.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report for Frontend Coverage (frontend)

Status Category Percentage Covered / Total
🟢 Lines 96.56% (🎯 90%) 4326 / 4480
🟢 Statements 95.31% (🎯 90%) 4778 / 5013
🟢 Functions 94.83% (🎯 90%) 1322 / 1394
🟢 Branches 89.11% (🎯 80%) 3104 / 3483
File CoverageNo changed files found.
Generated in workflow #1593 for commit 6ae02ba by the Vitest Coverage Report Action

@github-actions

Copy link
Copy Markdown
Contributor

Backend Test Results

11 391 tests  +22   11 391 ✅ +22   9m 15s ⏱️ - 3m 11s
 1 219 suites + 2        0 💤 ± 0 
 1 219 files   + 2        0 ❌ ± 0 

Results for commit 6ae02ba. ± Comparison against base commit ab42554.

@github-actions

Copy link
Copy Markdown
Contributor

Backend Code Coverage

Overall Project 95.15% 🍏
Files changed 100% 🍏

File Coverage
DefaultExternalDecisionService.java 100% 🍏
TicketingInboundService.java 100% 🍏
DefaultDeploymentExternalDecisionService.java 100% 🍏
DeploymentDecidedEvent.java 100% 🍏
DeploymentTicketProvenance.java 100% 🍏

@babltiga
babltiga merged commit 6332106 into main Oct 22, 2026
35 checks passed
@babltiga
babltiga deleted the feature/AF-992-deployment-ticket-decision branch October 22, 2026 09:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

workflow: map ticket resolution onto a deployment decision

1 participant