Developer Handover Pack
Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.
Developer Handover Pack
Software Deployment Workflow
[YourCompany.com] · IT Department · Prepared by FullSpec · [Today's Date]
This document gives the FullSpec build team everything needed to construct, connect, and validate the three-agent Software Deployment Workflow automation. It covers the current-state step map with time costs, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. The FullSpec team owns all build, integration, and testing work described here. Your team's responsibilities are limited to confirming API access, approving environment config locations, and accepting the handover once staging tests pass.
01Process snapshot
02Agent specifications
Monitors the GitHub repository for merged pull requests targeting the main branch. On each merge event, the agent queries the GitHub Actions API to confirm all CI checks have passed for the commit SHA. If checks pass, it creates a structured Jira deployment ticket with version, environment, and PR link pre-populated, then posts a Slack Block Kit message to the IT lead with an Approve and a Reject button. If CI checks fail, the agent posts a failure alert to the developer instead and halts the pipeline. Estimated build time: 16 hours. Complexity: Moderate.
// Input
github.webhook.payload {
ref: 'refs/heads/main',
pull_request.id: string,
pull_request.title: string,
pull_request.head.sha: string,
pull_request.merged: true,
pull_request.user.login: string,
repository.full_name: string
}
// Derived from GitHub Actions API
github.check_runs {
commit_sha: string,
check_runs[].name: string,
check_runs[].status: 'completed',
check_runs[].conclusion: 'success' | 'failure' | 'skipped'
}
// Output (on CI pass)
jira.ticket {
project_key: string, // confirmed pre-build
issue_type: 'Deployment',
summary: 'Deploy {pr_title} to {env}',
description: 'PR: {pr_url} | SHA: {commit_sha} | Author: {author}',
labels: ['automated-deploy'],
custom_field.target_env: string,
custom_field.deployment_window: string
}
slack.block_kit_message {
channel: IT_LEAD_SLACK_ID,
blocks: [ header, pr_details_section, jira_link_section, actions_block ],
actions_block.elements: [
{ type: 'button', text: 'Approve', action_id: 'deploy_approve', value: jira_ticket_id },
{ type: 'button', text: 'Reject', action_id: 'deploy_reject', value: jira_ticket_id }
]
}
// Output (on CI failure)
slack.message {
channel: DEVELOPER_SLACK_ID,
text: 'CI checks failed for {pr_title} ({commit_sha}). Fix failing checks before re-merging.'
}- GitHub webhook must be registered at the repository level, not organisation level, to avoid firing on non-main branch merges. The filter condition (merged: true, target branch: main) must be applied in the automation layer, not assumed from the webhook alone.
- GitHub API tier required: any plan exposing the Checks API (including GitHub Free for public repos; GitHub Team or above for private repos). Confirm the repository visibility and plan before build.
- Jira project key and issue type must be confirmed before build. If 'Deployment' is not an existing issue type in the target project, it must be created by a Jira admin before the agent can POST tickets.
- Slack Block Kit interactive messages require the Slack app to have the chat:write and commands scopes, and an interactive endpoint URL (Slack Interactivity webhook URL) registered in the Slack app settings. This endpoint must be publicly reachable.
- Dedupe behaviour: if a merged PR SHA already has a Jira ticket in the project (check by querying labels and commit SHA in description), the agent must skip ticket creation and log a warning rather than creating a duplicate.
- Fallback: if the Jira API returns an error, post a Slack alert to the developer and IT lead, log the error, and halt pipeline advancement. Do not silently proceed.
- Confirm the IT lead's Slack user ID (not display name) before build, as Block Kit messages to a user require the member ID.
Listens for the Approve action from the Slack interactive callback registered in the Deployment Gate Agent message. On receiving an approved action_id, it fetches the Confluence config page for the target environment, parses the structured variable table, validates that all required keys are present, and passes the confirmed values as secrets to the GitHub Actions dispatch API to trigger the deployment workflow. If the Reject button is clicked, the agent updates the Jira ticket to Rejected, posts a rejection notice to the developer, and halts the pipeline. Estimated build time: 14 hours. Complexity: Moderate.
// Input
slack.interactive_callback {
action_id: 'deploy_approve' | 'deploy_reject',
action_value: jira_ticket_id,
user.id: IT_LEAD_SLACK_ID,
message.ts: string, // used to update the original message
payload.response_url: string
}
// Fetched from Confluence on approval
confluence.page {
page_id: ENV_CONFIG_PAGE_ID, // confirmed pre-build per environment
body.storage: {
table_rows: [
{ key: 'DB_HOST', value: string },
{ key: 'DB_PORT', value: string },
{ key: 'API_BASE_URL', value: string },
{ key: 'DEPLOY_ENV', value: 'staging' | 'production' },
{ key: 'FEATURE_FLAGS', value: string }
]
}
}
// Output (on approve)
github.actions_dispatch {
owner: string,
repo: string,
workflow_id: 'deploy.yml', // confirmed pre-build
ref: 'main',
inputs: {
deploy_env: string,
jira_ticket_id: string,
commit_sha: string
}
}
slack.message_update {
response_url: string,
text: 'Deployment approved by {it_lead_name}. Pipeline triggered at {timestamp}.'
}
// Output (on reject)
jira.transition {
ticket_id: string,
transition_name: 'Rejected'
}
slack.message {
channel: DEVELOPER_SLACK_ID,
text: 'Deployment of {pr_title} was rejected by {it_lead_name}. Check Jira ticket {ticket_id} for details.'
}- Confluence environment config pages must use a structured two-column table (Key, Value) on a known, stable page ID per environment. If config is stored in free-form text or across multiple pages, a mapping exercise must be completed before this agent can be built.
- GitHub Actions workflow_dispatch requires the target workflow file (expected: deploy.yml) to declare workflow_dispatch as a trigger and define the input fields (deploy_env, jira_ticket_id, commit_sha) in the workflow manifest.
- GitHub API authentication for workflow_dispatch requires a personal access token or a GitHub App installation token with the Actions: write scope on the target repository. Confirm the credential type and rotation policy before build.
- Confluence API authentication uses Basic Auth with a bot account email and API token, or OAuth 2.0 with the read:confluence-content.all scope. Confirm which authentication method is in use on the target Confluence instance.
- Validation step: before calling GitHub Actions dispatch, the agent must verify that all required environment variable keys are present in the parsed Confluence table. If any key is missing, the agent must halt, post an alert to Slack, and log the missing key names without proceeding to deploy.
- The Slack interactive callback endpoint must respond with HTTP 200 within 3 seconds of receiving the payload, or Slack will retry and display an error to the user. The agent must acknowledge immediately and process asynchronously.
- Confirm the GitHub Actions secrets store already contains values for any sensitive variables (e.g. DB_PASSWORD, API_SECRET) that should not be sourced from Confluence. The agent reads non-sensitive config from Confluence and relies on pre-loaded GitHub secrets for credentials.
Triggered by the GitHub Actions deployment workflow completing, either successfully or with an error, via a status webhook or a final step in the deploy.yml workflow dispatching a completion event. The agent polls Datadog monitors associated with the deployed service for up to 5 minutes, evaluating error rate and p95 response time against agreed thresholds. If any monitor is in an Alert or Warn state beyond tolerance, the agent fires a PagerDuty event via the Events API v2. Regardless of health check outcome, the agent posts a structured deployment summary to the team Slack channel and transitions the Jira ticket to Deployed or Deployment Failed with a timestamp and environment note. Estimated build time: 14 hours. Complexity: Moderate.
// Input
github.workflow_completion {
workflow_run.id: string,
workflow_run.conclusion: 'success' | 'failure',
workflow_run.head_sha: string,
workflow_run.created_at: ISO8601,
inputs.deploy_env: string,
inputs.jira_ticket_id: string
}
// Polled from Datadog (up to 5 min, 60-second intervals)
datadog.monitor_states {
monitor_ids: [ MONITOR_ID_ERROR_RATE, MONITOR_ID_RESPONSE_TIME ],
monitor[].name: string,
monitor[].overall_state: 'OK' | 'Warn' | 'Alert' | 'No Data',
monitor[].query: string
}
// Output (health checks OK)
slack.message {
channel: TEAM_DEPLOY_CHANNEL_ID,
text: 'Deployment of {commit_sha} to {deploy_env} succeeded. All health checks OK. Jira: {ticket_url}'
}
jira.transition {
ticket_id: string,
transition_name: 'Deployed',
comment: 'Deployed at {timestamp}. Environment: {deploy_env}. Health checks: OK.'
}
// Output (health check breach)
pagerduty.event {
routing_key: PAGERDUTY_INTEGRATION_KEY,
event_action: 'trigger',
payload: {
summary: 'Post-deploy health check breach: {monitor_name} in {deploy_env}',
severity: 'critical',
source: 'FullSpec Post-Deploy Monitor Agent',
custom_details: {
commit_sha: string,
jira_ticket_id: string,
monitor_state: string,
deploy_env: string
}
}
}
slack.message {
channel: TEAM_DEPLOY_CHANNEL_ID,
text: 'ALERT: Post-deploy health check failed for {deploy_env}. PagerDuty incident raised. Jira ticket {ticket_id} flagged for rollback review.'
}
jira.transition {
ticket_id: string,
transition_name: 'Deployment Failed',
comment: 'Health check breach detected: {monitor_name}. PagerDuty incident: {incident_url}.'
}- Datadog monitor IDs for error rate and response time must be confirmed with the team before build. The agent targets specific monitor IDs, not monitor names, to avoid ambiguity if naming conventions change.
- PagerDuty integration key (routing key) must be generated from the target PagerDuty service as an Events API v2 integration. Confirm the service name and escalation policy with the IT lead before creating the integration.
- Polling strategy: the agent polls Datadog every 60 seconds for a maximum of 5 iterations (5 minutes total). If all monitors return OK across all polls, the deployment is considered healthy. If any monitor returns Alert or Warn on the final poll, the PagerDuty event fires. Warn-only on early polls does not trigger PagerDuty unless it persists to the final check.
- If the GitHub Actions workflow itself reports a conclusion of 'failure' (not a post-deploy health issue), the agent skips Datadog polling and immediately posts a pipeline failure message to Slack and transitions Jira to Deployment Failed.
- Datadog API authentication uses an API key and Application key pair scoped to the monitors:read permission. Confirm these are available in the target Datadog organisation.
- The Jira transitions 'Deployed' and 'Deployment Failed' must exist in the project workflow before build. Confirm with the Jira admin that these statuses and their transition IDs are available on the Deployment issue type.
- The Slack team deploy channel ID (not channel name) must be confirmed before build. The Slack app must be invited to that channel and have the chat:write scope.
03End-to-end data flow
// ============================================================
// TRIGGER: Pull request merged to main branch in GitHub
// ============================================================
github.webhook_event {
action: 'closed',
pull_request.merged: true,
pull_request.base.ref: 'main',
pull_request.id: PR_ID,
pull_request.title: PR_TITLE,
pull_request.head.sha: COMMIT_SHA,
pull_request.user.login: AUTHOR_LOGIN,
pull_request.html_url: PR_URL,
repository.full_name: REPO_FULL_NAME
}
// ============================================================
// AGENT 1: Deployment Gate Agent
// ============================================================
// Step 1.1: Query GitHub Actions Checks API for COMMIT_SHA
GET /repos/{REPO_FULL_NAME}/commits/{COMMIT_SHA}/check-runs
-> check_runs[].conclusion: all must equal 'success'
// Branch: CI failure
IF any check_run.conclusion != 'success' {
slack.postMessage {
channel: DEVELOPER_SLACK_ID,
text: 'CI failed for {PR_TITLE} ({COMMIT_SHA}). Fix before re-merging.'
}
HALT
}
// Step 1.2: Create Jira deployment ticket
POST /rest/api/3/issue {
fields: {
project.key: JIRA_PROJECT_KEY,
issuetype.name: 'Deployment',
summary: 'Deploy {PR_TITLE} to {TARGET_ENV}',
description: 'PR: {PR_URL} | SHA: {COMMIT_SHA} | Author: {AUTHOR_LOGIN}',
labels: ['automated-deploy'],
customfield_10100: TARGET_ENV, // target_env custom field
customfield_10101: DEPLOY_WINDOW // deployment_window custom field
}
}
-> JIRA_TICKET_ID (stored in workflow context)
-> JIRA_TICKET_URL
// Step 1.3: Post Slack approval request to IT lead
POST /api/chat.postMessage {
channel: IT_LEAD_SLACK_ID,
blocks: [
{ type: 'header', text: 'Deployment Approval Required' },
{ type: 'section', fields: [
{ text: 'PR: {PR_TITLE}' },
{ text: 'SHA: {COMMIT_SHA}' },
{ text: 'Author: {AUTHOR_LOGIN}' },
{ text: 'Jira: {JIRA_TICKET_URL}' }
]},
{ type: 'actions', elements: [
{ action_id: 'deploy_approve', value: JIRA_TICKET_ID },
{ action_id: 'deploy_reject', value: JIRA_TICKET_ID }
]}
]
}
-> SLACK_MESSAGE_TS (stored for later message update)
-> SLACK_RESPONSE_URL
// ---- AGENT 1 -> AGENT 2 HANDOFF ----
// Handoff payload carried in Slack interactive callback:
// action_id, JIRA_TICKET_ID, COMMIT_SHA, TARGET_ENV, SLACK_RESPONSE_URL
// ============================================================
// AGENT 2: Environment and Deploy Agent
// ============================================================
// Step 2.1: Receive Slack interactive callback
POST {SLACK_INTERACTIVITY_ENDPOINT} <- Slack delivers {
action_id: 'deploy_approve' | 'deploy_reject',
action_value: JIRA_TICKET_ID,
user.id: IT_LEAD_SLACK_ID,
response_url: SLACK_RESPONSE_URL
}
// Branch: IT lead rejects
IF action_id == 'deploy_reject' {
POST /rest/api/3/issue/{JIRA_TICKET_ID}/transitions -> 'Rejected'
slack.postMessage { channel: DEVELOPER_SLACK_ID, text: 'Rejected by IT lead.' }
HALT
}
// Step 2.2: Read environment config from Confluence
GET /wiki/rest/api/content/{ENV_CONFIG_PAGE_ID}?expand=body.storage
-> Parse two-column table: Key | Value
-> ENV_VARS {
DB_HOST: string,
DB_PORT: string,
API_BASE_URL: string,
DEPLOY_ENV: 'staging' | 'production',
FEATURE_FLAGS: string
}
-> Validate all required keys present; HALT with Slack alert if any missing
// Step 2.3: Trigger GitHub Actions deployment
POST /repos/{REPO_FULL_NAME}/actions/workflows/deploy.yml/dispatches {
ref: 'main',
inputs: {
deploy_env: ENV_VARS.DEPLOY_ENV,
jira_ticket_id: JIRA_TICKET_ID,
commit_sha: COMMIT_SHA
}
}
-> HTTP 204 accepted
// Update Slack message to confirm dispatch
POST SLACK_RESPONSE_URL {
text: 'Deployment approved by {IT_LEAD_NAME}. Pipeline triggered at {TIMESTAMP}.'
}
// ---- AGENT 2 -> AGENT 3 HANDOFF ----
// Handoff delivered by GitHub Actions final step calling inbound webhook:
// workflow_run.conclusion, COMMIT_SHA, JIRA_TICKET_ID, DEPLOY_ENV, TIMESTAMP
// ============================================================
// AGENT 3: Post-Deploy Monitor Agent
// ============================================================
// Step 3.1: Receive GitHub Actions completion webhook
POST {AUTOMATION_INBOUND_WEBHOOK} <- GitHub delivers {
workflow_run.conclusion: 'success' | 'failure',
workflow_run.head_sha: COMMIT_SHA,
inputs.jira_ticket_id: JIRA_TICKET_ID,
inputs.deploy_env: DEPLOY_ENV
}
// Branch: pipeline failure (not health check)
IF workflow_run.conclusion == 'failure' {
slack.postMessage { channel: TEAM_DEPLOY_CHANNEL_ID, text: 'Pipeline failed for {COMMIT_SHA}.' }
POST /rest/api/3/issue/{JIRA_TICKET_ID}/transitions -> 'Deployment Failed'
HALT
}
// Step 3.2: Poll Datadog monitors (5 iterations x 60 sec)
FOR i IN 1..5 {
GET /api/v1/monitor/{MONITOR_ID_ERROR_RATE} -> monitor.overall_state
GET /api/v1/monitor/{MONITOR_ID_RESPONSE_TIME} -> monitor.overall_state
WAIT 60 seconds
}
-> FINAL_HEALTH_STATE: 'OK' | 'Warn' | 'Alert'
// Branch: health check breach on final poll
IF FINAL_HEALTH_STATE IN ['Warn', 'Alert'] {
POST https://events.pagerduty.com/v2/enqueue {
routing_key: PAGERDUTY_INTEGRATION_KEY,
event_action: 'trigger',
payload.summary: 'Health check breach in {DEPLOY_ENV}: {MONITOR_NAME}',
payload.severity: 'critical',
payload.custom_details.commit_sha: COMMIT_SHA,
payload.custom_details.jira_ticket_id: JIRA_TICKET_ID
}
-> PAGERDUTY_INCIDENT_URL
slack.postMessage { channel: TEAM_DEPLOY_CHANNEL_ID,
text: 'ALERT: Health check breach. PagerDuty incident raised. Jira flagged for rollback.' }
POST /rest/api/3/issue/{JIRA_TICKET_ID}/transitions -> 'Deployment Failed'
POST /rest/api/3/issue/{JIRA_TICKET_ID}/comment -> 'PagerDuty: {PAGERDUTY_INCIDENT_URL}'
HALT
}
// Step 3.3: All checks OK - close out
slack.postMessage {
channel: TEAM_DEPLOY_CHANNEL_ID,
text: 'Deployment of {COMMIT_SHA} to {DEPLOY_ENV} succeeded. Health checks OK. Jira: {JIRA_TICKET_URL}'
}
POST /rest/api/3/issue/{JIRA_TICKET_ID}/transitions -> 'Deployed'
POST /rest/api/3/issue/{JIRA_TICKET_ID}/comment ->
'Deployed at {TIMESTAMP}. Environment: {DEPLOY_ENV}. All Datadog monitors OK.'
// ============================================================
// END OF FLOW
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Software Deployment Workflow.