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
Incident and Issue Reporting Automation
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document is the primary technical reference for the FullSpec team building the Incident and Issue Reporting automation. It covers the full current-state process map, both agent specifications with IO contracts and build constraints, the end-to-end data flow, and the recommended build stack. Everything needed to configure, connect, and test the two-agent workflow is contained here. The process owner's role during build is limited to approving the severity rubric and confirming tool access. FullSpec handles all configuration, integration, and testing.
01Process snapshot
02Agent specifications
The Incident Triage Agent is the first automated step in the workflow. It fires on every new Google Form submission, reads the full response payload, and applies the agreed severity scoring rubric to produce a structured classification record. The agent assigns a numeric severity score (1 to 5), maps the incident to one of the defined category labels, determines the appropriate owner based on a routing table, and calculates the resolution deadline from severity-tier SLA rules. It then writes a complete incident row to the Google Sheets log and creates a corresponding structured page in Notion. If any required field is missing from the form response, the agent flags the record for human review rather than proceeding with partial data. This agent is Moderate complexity and is estimated at 14 hours to build and test.
// Input: Google Form response payload form_response_id : string // unique Form submission ID submitted_at : ISO8601 // timestamp of submission reporter_email : string // submitting staff member email incident_description : string // free-text description field incident_location : string // site or system identifier reporter_severity_hint : enum // staff-selected: Low | Medium | High | Critical category_hint : enum // staff-selected category from Form dropdown attachments : array // optional photo or file URLs from Forms upload // Output: Classified incident record written to Sheets + Notion incident_id : string // auto-generated: INC-YYYYMMDD-NNN severity_score : integer // 1-5 from rubric evaluation severity_label : enum // Low | Medium | High | Critical category : string // confirmed category label from rubric map assigned_owner_email : string // looked up from owner routing table assigned_owner_slack : string // Slack member ID for owner resolution_deadline : ISO8601 // calculated from severity SLA rules triage_status : enum // Auto-classified | Flagged-for-review notion_page_id : string // ID of created Notion page sheets_row_index : integer // row number in incident log sheet // On flagged-for-review: incomplete or ambiguous form response flag_reason : string // e.g. 'missing incident_location' flag_notified_to : string // operations manager email
- Google Forms must be configured with all required fields marked mandatory except attachments. The Sheets destination tab must be the live response sheet linked directly to the Form; do not use a copied or imported sheet.
- The severity scoring rubric must be finalised and approved by the operations manager before this agent is configured. The rubric maps keyword patterns in incident_description plus reporter_severity_hint to a numeric score. Build cannot proceed on this agent without the signed-off rubric document.
- The owner routing table must be stored in a dedicated Sheets tab (named 'OwnerRouting') with columns: category, severity_label, owner_email, owner_slack_id. The agent reads this tab on each run. Changes to routing are made by editing this tab, not by modifying agent logic.
- Notion integration requires a Notion internal integration token with insert access to the target database. The database must have the following properties pre-created: Incident ID (title), Severity (select), Category (select), Reporter Email (email), Assigned Owner (person), Deadline (date), Status (select: Open | In Progress | Resolved), Description (text), Attachments (files). Confirm database ID before build.
- Dedupe behaviour: if a form_response_id already exists in the Sheets log (column: Form Response ID), the agent must skip processing and log a warning to the error log. This prevents double-writes on platform retry events.
- Fallback for missing required fields: write a partial row to Sheets with triage_status set to 'Flagged-for-review', send an alert email to the operations manager email address (stored in platform credentials store as OPS_MANAGER_EMAIL), and halt further processing for that record until manually reviewed.
- The Notion page template for each category must be pre-built before this agent is activated. Confirm template page IDs are stored in a config tab ('NotionTemplates') in the incident log Sheets file.
- Google Sheets API tier required: standard read/write access. Confirm the service account has Editor access to the incident log spreadsheet. Notion API version: 2022-06-28 or later.
The Notification and Escalation Agent fires immediately after the Triage Agent writes a classified incident record to Sheets and Notion. It handles all outbound communication for the incident: a Slack direct message to the assigned owner with the incident summary, severity, deadline, and Notion page link; a Gmail acknowledgement to the original reporter confirming receipt, incident ID, assigned owner, and expected resolution window; and, if the severity_label is High or Critical, a PagerDuty incident alert to the on-call manager. The agent also schedules reminder messages to the assigned owner at configurable intervals (default: 24 hours before deadline and again at deadline) until the incident status in Sheets is updated to Resolved. This agent is Moderate complexity and is estimated at 14 hours to build, test, and configure reminder scheduling.
// Input: Classified incident record from Triage Agent output incident_id : string // e.g. INC-20240617-001 severity_label : enum // Low | Medium | High | Critical severity_score : integer // 1-5 category : string // confirmed category label incident_description : string // original free-text description reporter_email : string // for Gmail acknowledgement assigned_owner_email : string // for Gmail CC and Slack DM assigned_owner_slack : string // Slack member ID resolution_deadline : ISO8601 // for deadline comms and reminder scheduling notion_page_url : string // link embedded in Slack and Gmail messages triage_status : enum // Auto-classified | Flagged-for-review // Output: Outbound notifications dispatched slack_message_ts : string // Slack message timestamp (for thread replies) gmail_message_id : string // Gmail sent message ID for audit trail pagerduty_incident_id : string // PD incident ID (High/Critical only, else null) reminder_schedule_id : string // scheduled job ID for follow-up nudges notifications_log_row : integer // row written to 'NotificationsLog' Sheets tab // On approval / resolution check (reminder loop) sheets_status_check : enum // Open | In Progress | Resolved reminder_sent_count : integer // increments per reminder dispatched reminder_cancelled_at : ISO8601 // timestamp when status becomes Resolved
- Slack integration requires a Slack bot token (xoxb-) with scopes: chat:write, users:read, users:read.email. The bot must be installed to the workspace and invited to any channels used for broadcast notifications. Owner DMs are sent via users.lookupByEmail to resolve assigned_owner_slack from assigned_owner_email if the Sheets value is missing.
- Gmail integration uses a service account with domain-wide delegation or an OAuth 2.0 token for the sending address. The sending address must be confirmed before build and stored in the credentials store as GMAIL_SENDER_ADDRESS. All outbound Gmail messages must include the incident_id in the subject line for auditability.
- PagerDuty integration requires a PagerDuty Events API v2 routing key (integration key) for the target escalation service. The routing key must be stored in the credentials store as PAGERDUTY_ROUTING_KEY. PagerDuty alerts are fired only when severity_label is High or Critical. Confirm the severity threshold with the operations manager before build: the default is score 4 or 5 on the 1-5 rubric.
- PagerDuty alert fatigue is a documented risk for small teams. The default threshold (score 4+) should be confirmed in writing before go-live. Overly sensitive thresholds must be avoided. Include a note in the config tab ('AgentConfig' in Sheets) so the operations manager can request threshold changes without a code change.
- Reminder scheduling: the agent must check the incident's Sheets status column at each reminder interval. If status is Resolved, cancel remaining reminders. If status is still Open or In Progress, send the Slack reminder to assigned_owner_slack and increment reminder_sent_count. Maximum reminder count before escalating to operations manager: 3 (configurable via AgentConfig tab).
- The 'NotificationsLog' tab in the incident log Sheets file must be created before this agent is activated. Columns required: Incident ID, Notification Type (Slack/Gmail/PagerDuty/Reminder), Sent At, Recipient, Status (Sent/Failed).
- Fallback for Slack DM failure (e.g. user not found): send the owner notification via Gmail to assigned_owner_email and log the Slack failure to the error log. Do not silently drop the notification.
- Gmail acknowledgement template must be reviewed and approved by the operations manager before activation. Store the approved template text in the 'EmailTemplates' Sheets tab, keyed by notification type.
03End-to-end data flow
// ============================================================
// INCIDENT AND ISSUE REPORTING: END-TO-END DATA FLOW
// ============================================================
// --- TRIGGER ---
// Staff member submits Google Form
TRIGGER: Google Form submission
-> form_response_id : string // e.g. 'ACvB...' (Forms internal ID)
-> submitted_at : ISO8601 // e.g. '2024-06-17T09:14:33Z'
-> reporter_email : string // e.g. 'jane.smith@yourcompany.com'
-> incident_description : string // free-text, up to 2000 chars
-> incident_location : string // e.g. 'Warehouse B - Loading Bay'
-> reporter_severity_hint : enum // Low | Medium | High | Critical
-> category_hint : enum // Equipment | Safety | Service | Near-miss | Other
-> attachments : array // [] or [Drive file URL, ...]
// --- AGENT HANDOFF 1: Form response to Incident Triage Agent ---
// Platform reads new row from connected Sheets response tab
// Field names map directly from Sheets column headers to agent input
AGENT: Incident Triage Agent
INPUT:
form_response_id, submitted_at, reporter_email,
incident_description, incident_location,
reporter_severity_hint, category_hint, attachments
STEP 1: Dedupe check
-> query Sheets column 'Form Response ID' for form_response_id
-> if EXISTS: log warning to ErrorLog tab, HALT
-> if NOT EXISTS: continue
STEP 2: Validate required fields
-> required: reporter_email, incident_description, incident_location
-> if ANY missing:
triage_status = 'Flagged-for-review'
flag_reason = 'Missing: [field_name]'
write partial row to Sheets
send alert to OPS_MANAGER_EMAIL
HALT further processing
STEP 3: Apply severity rubric
-> score_from_keywords : integer // keyword match against rubric table
-> score_from_hint : integer // reporter_severity_hint mapped to 1-5
-> severity_score = weighted_average(score_from_keywords, score_from_hint)
-> severity_label = rubric_tier_map[severity_score]
// 1-2 = Low, 3 = Medium, 4 = High, 5 = Critical
STEP 4: Assign category and owner
-> category : string // confirmed from OwnerRouting tab lookup
-> assigned_owner_email : string // from OwnerRouting[category][severity_label]
-> assigned_owner_slack : string // from OwnerRouting[category][severity_label]
-> resolution_deadline : ISO8601 // submitted_at + SLA_hours[severity_label]
// Low=72h, Medium=48h, High=24h, Critical=4h (configurable via AgentConfig)
STEP 5: Generate incident ID
-> incident_id = 'INC-' + YYYYMMDD + '-' + zero_padded_sequence
// Sequence sourced from Sheets row count on incident log tab
STEP 6: Write to Google Sheets incident log
-> target tab: 'IncidentLog'
-> columns written:
incident_id, form_response_id, submitted_at, reporter_email,
incident_description, incident_location, category,
severity_score, severity_label, assigned_owner_email,
resolution_deadline, triage_status, notion_page_id (pending)
STEP 7: Create Notion incident page
-> template page ID from NotionTemplates tab, keyed by category
-> properties written:
'Incident ID' (title) = incident_id
'Severity' (select) = severity_label
'Category' (select) = category
'Reporter Email' (email)= reporter_email
'Assigned Owner' (person)= resolved from assigned_owner_email
'Deadline' (date) = resolution_deadline
'Status' (select) = 'Open'
'Description' (text) = incident_description
'Attachments' (files) = attachments
-> notion_page_id, notion_page_url returned
-> update Sheets row: notion_page_id = notion_page_id
OUTPUT: incident_id, severity_score, severity_label, category,
assigned_owner_email, assigned_owner_slack,
resolution_deadline, triage_status, notion_page_id, notion_page_url
// --- AGENT HANDOFF 2: Triage Agent output to Notification and Escalation Agent ---
// Triggered by new row present in IncidentLog with triage_status set
// All Triage Agent output fields passed as input
AGENT: Notification and Escalation Agent
INPUT:
incident_id, severity_label, severity_score, category,
incident_description, reporter_email, assigned_owner_email,
assigned_owner_slack, resolution_deadline, notion_page_url, triage_status
BRANCH A: Severity >= 4 (High or Critical)
-> fire PagerDuty Events API v2
routing_key = PAGERDUTY_ROUTING_KEY (from credentials store)
event_action = 'trigger'
payload.summary= incident_id + ': ' + severity_label + ' - ' + category
payload.source = 'FullSpec Incident Automation'
payload.custom_details.notion_url = notion_page_url
payload.custom_details.reporter = reporter_email
payload.custom_details.deadline = resolution_deadline
-> capture pagerduty_incident_id from response
BRANCH B: All incidents (parallel to Branch A where applicable)
-> send Slack DM to assigned_owner_slack
text: 'New incident assigned: [incident_id]\n
Severity: [severity_label] | Category: [category]\n
Deadline: [resolution_deadline]\n
Record: [notion_page_url]'
-> capture slack_message_ts
-> send Gmail acknowledgement to reporter_email
subject: 'Incident received: [incident_id]'
body: template from EmailTemplates tab, key='reporter_ack'
merge fields: incident_id, assigned_owner_email, resolution_deadline
-> capture gmail_message_id
STEP: Write to NotificationsLog tab
-> row per notification type: Slack, Gmail, PagerDuty (if fired)
-> fields: incident_id, notification_type, sent_at, recipient, status
STEP: Schedule reminder loop
-> reminder_1_at = resolution_deadline - 24h
-> reminder_2_at = resolution_deadline
-> at each scheduled time:
check Sheets IncidentLog row status column
if status == 'Resolved': cancel remaining reminders, HALT
if status != 'Resolved': send Slack DM to assigned_owner_slack
reminder text: 'Reminder: [incident_id] is due [resolution_deadline].'
increment reminder_sent_count
if reminder_sent_count >= 3: notify OPS_MANAGER_EMAIL via Gmail
OUTPUT: slack_message_ts, gmail_message_id, pagerduty_incident_id,
reminder_schedule_id, notifications_log_row
// --- RESOLUTION UPDATE (triggered externally) ---
// When owner updates Notion page Status to 'Resolved':
// -> Notion webhook or polling detects status change
// -> Update Sheets IncidentLog row:
// status = 'Resolved'
// resolved_at = ISO8601 timestamp
// resolution_notes = Notion page resolution_notes field value
// -> Cancel reminder schedule for incident_id
// -> Optionally send Gmail to reporter_email confirming closure
// ============================================================
// END OF DATA FLOW
// ============================================================04Recommended build stack
More documents for this process
Every document generated for Incident & Issue Reporting.