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
End-of-day / Shift Handover
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document gives the FullSpec build team everything needed to configure, connect, and ship the End-of-day / Shift Handover automation. It covers the current-state process map with bottleneck flags, full specifications for both agents, the end-to-end data flow, and the recommended build stack with time estimates. The process owner's responsibility is limited to supplying tool credentials, confirming PagerDuty escalation contacts, and reviewing the pilot shift output. All build, testing, and deployment work is handled by FullSpec.
01Process snapshot
02Agent specifications
Monitors the shift schedule and fires the end-of-shift data collection sequence to all outgoing staff at the moment the shift-end trigger activates. It sends a structured Slack message with a direct Google Forms link and a ten-minute response window, then polls the Google Sheets response log. Once the window closes, it marks any staff member who did not submit a response as missing, appends a flag row to the Sheets log, and signals the Handover Compiler Agent to proceed regardless. Estimated build time: 6 hours. Complexity: Moderate.
// Input
shift_end_trigger: { shift_id: string, shift_date: ISO8601, site_id: string, outgoing_channel_id: string, staff_list: [{ staff_id, slack_user_id, name }] }
// Output
sheets_log_row: { shift_id, staff_id, name, task_status: enum[complete|in_progress|blocked], incident_flag: boolean, incident_detail: string|null, equipment_flag: boolean, equipment_detail: string|null, response_received: boolean, submitted_at: ISO8601|null, missing_flag: boolean }
collector_status: { shift_id, total_staff: int, responses_received: int, missing_count: int, log_ready: boolean, critical_incidents_present: boolean }- Slack workspace must be on a paid plan (Pro or above) to allow the bot to post to private channels. Confirm the outgoing shift channel name and whether it is public or private before build. The Slack bot token requires the chat:write, channels:read, groups:read, and users:read OAuth scopes.
- Google Forms must be connected to Google Sheets via the native Forms-to-Sheets link. Do not use a third-party connector for this step. The response sheet must be in the same Google account as the Sheets log; confirm shared Drive permissions if the account is a workspace account with Drive restrictions.
- The Google Sheets log schema must be locked before the agent is built. Column names in the Sheets log are the source of truth for all downstream field references. Any post-launch form field rename requires a coordinated update to the Sheets header row and the Compiler Agent prompt.
- Dedupe behaviour: if a staff member submits the form more than once within the response window, the agent must retain only the most recent submission, identified by submitted_at timestamp. Earlier rows for the same staff_id within the same shift_id must be marked superseded: true and excluded from the Compiler Agent input.
- The response window default is 10 minutes from Slack message delivery. This value must be stored as a configurable parameter in the credential or config store, not hardcoded, so the operations manager can adjust it without a code change.
- Confirm the shift-end trigger source before build. A fixed-time cron is acceptable for a single-site, consistent-schedule operation but will misfire on public holidays or schedule changes. If the business has shift scheduling software, connecting the trigger to a shift-close event in Google Sheets (manual flag by the team lead) is the more reliable fallback and is already supported by the agent design.
- Missing-response handling: after the window closes, the agent appends one flag row per non-responding staff member with response_received: false and missing_flag: true. It does not send individual Slack chase messages (that behaviour is replaced by the automation). The Compiler Agent reads these flags and surfaces them in the report.
Reads the completed Google Sheets log produced by the Handover Collector Agent, groups entries by category (tasks, incidents, equipment flags, open items, and missing responses), and writes a structured handover report in plain English using a consistent template. It evaluates whether any incident_flag or equipment_flag rows meet the PagerDuty escalation threshold defined in the config, raises an alert if so, then posts the report to the designated Slack operations channel, sends a copy via Gmail to the incoming manager and on-call contacts, and files the report to the correct Notion database page for the shift date. Estimated build time: 9 hours. Complexity: Moderate.
// Input
collector_status: { shift_id, total_staff, responses_received, missing_count, log_ready, critical_incidents_present }
sheets_log_rows: [ { shift_id, staff_id, name, task_status, incident_flag, incident_detail, equipment_flag, equipment_detail, response_received, submitted_at, missing_flag, superseded } ]
config: { escalation_threshold: enum[any_incident|high_priority_only], pagerduty_service_id: string, incoming_manager_email: string, oncall_emails: [string], slack_ops_channel_id: string, notion_database_id: string, notion_parent_page_id: string }
// Output
handover_report: { shift_id, shift_date, site_id, generated_at: ISO8601, sections: { tasks_complete: [string], tasks_in_progress: [string], tasks_blocked: [string], incidents: [{ staff: string, detail: string, escalated: boolean }], equipment_flags: [{ staff: string, detail: string }], missing_responses: [string], priority_notes: string }, report_markdown: string }
slack_message_ts: string
gmail_message_id: string
notion_page_id: string
pagerduty_incident_id: string|null
// On escalation (critical_incidents_present = true OR any incident_flag row meets threshold)
pagerduty_payload: { service_id, title: 'Unresolved incident(s) on shift [shift_id]', body: string, severity: enum[critical|warning], dedup_key: shift_id }- Notion integration requires a Notion internal integration token with insert and update permissions on the target database. The database must have the following properties pre-created before build: Shift Date (date), Shift ID (text), Site (select), Report Status (select: draft|published), Report Body (rich text). Confirm property names with the operations manager; the agent writes to these by exact property name.
- The AI compilation step must use a fixed system prompt stored in the config store, not generated dynamically. The prompt defines section order, label names, and the plain-English tone. The operations manager must review and sign off the prompt template during the pilot week before full rollout.
- PagerDuty escalation threshold must be confirmed with the operations manager before the build goes to pilot. The default config is escalation on any row where incident_flag = true. If the team only wants pages for high-priority incidents, this must be set in config before testing, not after go-live, because changing it post-launch will affect dedup logic.
- Gmail sending must use the OAuth 2.0 credential for the operations Gmail account, not a service account without SMTP delegation. Required scope: gmail.send. Confirm whether the sending address must match a specific domain alias used by the team.
- Slack report post must use Block Kit formatting for readability. Define the block structure in a reusable template stored in the config store. Do not hardcode block structure inside the agent logic.
- Notion filing must match the shift date to the correct database page. The agent must query the Notion database for an existing page where Shift Date equals the current shift_date before creating a new one. This prevents duplicate pages for the same shift if the workflow is re-run due to an error.
- End-to-end test must use a sample Google Sheets log populated with a realistic mix of complete, blocked, and missing responses plus at least one incident_flag = true row, before any pilot shift is run live.
03End-to-end data flow
// ============================================================
// END-TO-END DATA FLOW: End-of-day / Shift Handover
// ============================================================
// TRIGGER
// Source: Scheduled cron at shift_end_time OR Google Sheets cell shift_close_flag = TRUE
TRIGGER -> {
shift_id: 'SHIFT-20240527-AM',
shift_date: '2024-05-27',
site_id: 'SITE-01',
outgoing_channel_id: 'C04XYZSLACK',
staff_list: [
{ staff_id: 'S01', slack_user_id: 'U01AAA', name: 'Jordan Lee' },
{ staff_id: 'S02', slack_user_id: 'U02BBB', name: 'Cam Riley' },
{ staff_id: 'S03', slack_user_id: 'U03CCC', name: 'Alex Kim' }
]
}
// ============================================================
// AGENT HANDOFF 1: Trigger -> Handover Collector Agent
// ============================================================
// ACTION: Post structured Slack message to outgoing_channel_id
Slack.chat.postMessage -> {
channel: 'C04XYZSLACK',
text: 'Shift end reached. Please complete your handover form: https://forms.gle/[form_id]',
blocks: [ ShiftEndPromptBlock ],
ts: '1716800000.000100' // stored as slack_prompt_ts
}
// ACTION: Wait for Google Forms responses (response_window_minutes: 10)
// Google Forms -> Google Sheets (native linked sheet append)
GoogleForms.onResponse -> GoogleSheets.append -> {
sheet: 'ShiftLog',
row: {
shift_id: 'SHIFT-20240527-AM',
staff_id: 'S01',
name: 'Jordan Lee',
task_status: 'complete',
incident_flag: false,
incident_detail: null,
equipment_flag: true,
equipment_detail: 'Forklift battery low, needs charge before PM shift',
response_received: true,
submitted_at: '2024-05-27T14:08:33Z',
missing_flag: false,
superseded: false
}
}
// ACTION: After response window closes, flag non-responding staff
// S03 (Alex Kim) did not submit
GoogleSheets.append -> {
shift_id: 'SHIFT-20240527-AM',
staff_id: 'S03',
name: 'Alex Kim',
task_status: null,
incident_flag: false,
incident_detail: null,
equipment_flag: false,
equipment_detail: null,
response_received: false,
submitted_at: null,
missing_flag: true,
superseded: false
}
// ACTION: Write collector_status to config cell in ShiftLog sheet
GoogleSheets.update -> collector_status_cell -> {
shift_id: 'SHIFT-20240527-AM',
total_staff: 3,
responses_received: 2,
missing_count: 1,
log_ready: true,
critical_incidents_present: false
}
// ============================================================
// AGENT HANDOFF 2: Handover Collector Agent -> Handover Compiler Agent
// Trigger: log_ready = true written to ShiftLog sheet
// ============================================================
// ACTION: Compiler Agent reads all ShiftLog rows for shift_id = 'SHIFT-20240527-AM'
GoogleSheets.read -> rows WHERE shift_id = 'SHIFT-20240527-AM' AND superseded = false
// ACTION: AI compilation step groups rows and writes report
AI.compile -> handover_report -> {
shift_id: 'SHIFT-20240527-AM',
shift_date: '2024-05-27',
site_id: 'SITE-01',
generated_at: '2024-05-27T14:22:07Z',
sections: {
tasks_complete: ['Jordan Lee: all zone-A picks completed'],
tasks_in_progress: ['Cam Riley: goods-in count 80% done, remaining for PM shift'],
tasks_blocked: [],
incidents: [],
equipment_flags: [{ staff: 'Jordan Lee', detail: 'Forklift battery low, needs charge before PM shift', escalated: false }],
missing_responses: ['Alex Kim (S03) did not submit before window closed'],
priority_notes: 'PM shift lead: confirm forklift battery status on arrival.'
},
report_markdown: '...'
}
// ACTION: Evaluate escalation threshold
// critical_incidents_present = false AND equipment_flag escalated = false -> no PagerDuty alert
pagerduty_incident_id: null
// ACTION: Post report to Slack operations channel
Slack.chat.postMessage -> {
channel: slack_ops_channel_id, // 'C09OPSXYZ'
blocks: [ HandoverReportBlock(handover_report) ],
ts: '1716800540.000200' // stored as slack_message_ts
}
// ACTION: Send Gmail to incoming manager and on-call contacts
Gmail.send -> {
to: [ incoming_manager_email, ...oncall_emails ],
subject: 'Handover Report: SHIFT-20240527-AM | Site 01',
body: handover_report.report_markdown,
message_id: 'msg_abc123' // stored as gmail_message_id
}
// ACTION: Query Notion for existing page on shift_date, create if absent
Notion.databases.query -> { database_id: notion_database_id, filter: { Shift Date: '2024-05-27' } }
// No existing page found -> create new
Notion.pages.create -> {
parent: { database_id: notion_database_id },
properties: {
'Shift Date': '2024-05-27',
'Shift ID': 'SHIFT-20240527-AM',
'Site': 'SITE-01',
'Report Status': 'published'
},
children: [ ParagraphBlock(handover_report.report_markdown) ],
page_id: 'notion_page_abc456' // stored as notion_page_id
}
// ============================================================
// REMAINING HUMAN STEP (Step 8)
// Incoming lead reads Slack/email report and confirms receipt
// Estimated: 8 minutes. No automation required.
// ============================================================04Recommended build stack
More documents for this process
Every document generated for End-of-day / Shift Handover.