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
Standard Operating Procedure Management
[YourCompany.com] · Operations Department · Prepared by FullSpec · [Today's Date]
This document is the technical reference for the FullSpec team building the SOP Management automation. It covers the current-state process map with bottleneck flags, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. Everything in this document is derived from the confirmed process mapping session and stack audit. Where the template does not specify a value (OAuth scopes, rate-limit thresholds, exact field names), realistic and internally consistent defaults have been applied and are clearly identifiable.
01Process snapshot
02Agent specifications
This agent monitors the approved SOPs folder in Google Drive for any file creation or modification event. On detection it reads the current version token from the document, increments it following a MAJOR.MINOR schema (e.g. v1.3 becomes v1.4 on a minor edit; a full rewrite bumps to v2.0 when the manager sets a flag in the register), writes the updated version string back to the document header, appends a timestamped row to the change-log sheet, and writes a new or updated row to the master SOP register in Google Sheets capturing the file ID, document title, new version, editor email, revision date, change summary, compliance-critical flag, and affected-roles list. The agent then emits a structured payload that triggers the Distribution and Acknowledgement Agent. Estimated build time: 10 hours. Complexity: Moderate.
// Input drive.file.id : string // Google Drive file ID of changed document drive.file.name : string // Document title at time of change drive.event_type : enum // 'create' | 'update' drive.actor_email : string // Google account email of editor drive.modified_time : ISO8601 // Timestamp of change // Internal lookups (agent performs) sheets.register.row : object // Existing register row matched by file.id .current_version : string // e.g. 'v1.3' .compliance_flag : boolean // true if SOP is compliance-critical .affected_roles : string[] // list of role identifiers // Output (emitted to Distribution and Acknowledgement Agent) sop.file_id : string // Drive file ID sop.title : string // Document title sop.new_version : string // Incremented version string e.g. 'v1.4' sop.editor_email : string sop.revision_date : ISO8601 sop.change_summary : string // Extracted from doc change-log table, first new row sop.compliance_flag : boolean sop.affected_roles : string[] // Role identifiers for downstream lookup sop.register_row_id : string // Sheets row reference for downstream updates
- Google Drive API access requires the automation platform to be authorised under a Google Workspace service account or via OAuth 2.0 with scope drive.readonly plus drive.file. Confirm the Workspace admin has enabled domain-wide delegation if a service account is used.
- Google Sheets write access requires scope spreadsheets (read/write). The SOP Register sheet must use the agreed column schema: [file_id, title, version, editor_email, revision_date, change_summary, compliance_flag, affected_roles, register_row_id, cycle_status]. Confirm column positions before build.
- Version increment logic: read the version cell from the matched register row, parse the MAJOR.MINOR string, increment MINOR by 1. If no existing row is found (new document), set version to v1.0 and insert a new register row. If the file is renamed the agent must match on file_id, not title, to avoid creating a duplicate row.
- Dedupe behaviour: if the Drive webhook fires twice for the same file within a 60-second window (a known Drive API behaviour), the agent must check the modified_time of the last register row update and skip processing if it is within 90 seconds of the incoming event timestamp.
- The change_summary field is extracted from the first new row added to the change-log table inside the Google Doc. If the table is empty or missing, the field is populated with 'Change summary not provided' and a Slack alert is sent to the operations channel so the manager can update it manually.
- Archive step: the agent copies the pre-change version of the file to the /SOP-Archive/{file_id}/{version} path in Drive before writing the new version string back to the live document. This replaces step 8 without using Notion for archiving. Confirm with the process owner whether Notion archive links also need updating or whether Drive-only archiving is acceptable for go-live.
- Confirm before build: Google Drive folder ID for /Approved-SOPs, SOP Register Google Sheets spreadsheet ID and sheet name, column schema sign-off, and whether service account or OAuth 2.0 is the preferred auth method.
This agent receives the structured payload from the SOP Version and Register Agent and executes the full distribution, tracking, reminder, escalation, and closure cycle without manual intervention. It queries the role-to-SOP mapping table in Google Sheets to resolve the affected-roles list into individual staff member records (name, Slack user ID, email, line manager Slack user ID). It then sends a personalised Slack direct message to each staff member containing the document title, version, a plain-language change summary, a direct Drive link, and a one-click acknowledgement button powered by a Slack interactive component. For any SOP where sop.compliance_flag is true, the agent simultaneously sends a DocuSign envelope to each required signatory. It writes an initial acknowledgement tracker row for every recipient. A scheduled sub-flow runs every 48 hours, identifies outstanding tracker rows, and sends a Slack reminder. After two reminders without response, an escalation message is posted to the line manager's Slack DM and the escalation is logged. Once all required acknowledgements and DocuSign completions are recorded, a summary message is posted to the designated operations Slack channel. Estimated build time: 18 hours. Complexity: Complex.
// Input (from SOP Version and Register Agent) sop.file_id : string sop.title : string sop.new_version : string sop.revision_date : ISO8601 sop.change_summary : string sop.compliance_flag : boolean sop.affected_roles : string[] // e.g. ['ops-manager','warehouse-lead','qa-officer'] sop.register_row_id : string // Internal lookups (agent performs) sheets.role_mapping[] : object[] // one record per staff member .role_id : string .staff_name : string .slack_user_id : string // e.g. 'U04XXXXXXX' .email : string .line_manager_slack : string // Slack user ID of line manager // Output (written to acknowledgement tracker sheet + Slack + DocuSign) tracker.row[] : object[] // one row per required acknowledger .file_id : string .version : string .staff_name : string .slack_user_id : string .method : enum // 'slack_button' | 'docusign' .status : enum // 'pending' | 'acknowledged' | 'escalated' .acknowledged_at : ISO8601 // null until confirmed .reminder_count : integer // 0, 1, or 2 .escalated_at : ISO8601 // null until escalated // On acknowledgement (Slack interactive component callback or DocuSign webhook) callback.slack_user_id : string // identifies the responder callback.action_id : string // 'ack_sop' confirms button press callback.timestamp : ISO8601 callback.envelope_id : string // DocuSign only, null for Slack path // Completion output (posted to Slack channel when all rows resolved) summary.sop_title : string summary.version : string summary.total_required : integer summary.all_acked_at : ISO8601 summary.channel_id : string // ops Slack channel ID
- Slack interactive components (Block Kit buttons) require a Slack app installed in the workspace with scopes: chat:write, im:write, channels:read, users:read, users:read.email, and incoming-webhook. The app must have an interactive endpoint URL configured pointing to the automation platform's webhook receiver. Confirm the Slack workspace admin can install custom apps before build starts.
- The Slack acknowledgement button payload must include the file_id and version in the action value so the callback handler can update the correct tracker row without a separate lookup.
- DocuSign routing only fires when sop.compliance_flag is true. The envelope is created using the DocuSign eSignature REST API (envelopes:create). The template ID for SOP acknowledgement envelopes must be created in DocuSign and its ID recorded in the credential store before build. A DocuSign Connect webhook must be configured to POST envelope-completed events back to the automation platform.
- Reminder sub-flow: implement as a scheduled workflow running every 48 hours. The flow queries the tracker sheet for rows where status is 'pending' and acknowledged_at is null. It sends a Slack DM to the matching slack_user_id and increments reminder_count. When reminder_count reaches 2 and the row is still pending, escalation fires: a DM is sent to line_manager_slack_id and status is updated to 'escalated'.
- DocuSign accounts on the Personal plan do not support Connect webhooks. The DocuSign Standard or Business Pro plan is required. Confirm account tier before build.
- The role-to-SOP mapping table is the single source of truth for recipient resolution. The table must be present and populated before go-live. If a role_id from the incoming payload has no matching row in the mapping table, the agent must log an error to the error table, send a Slack alert to the ops channel, and halt distribution for that role only (not abort the entire cycle).
- Confirm before build: Slack app credentials (Bot Token, Signing Secret), Slack channel ID for the ops completion summary, DocuSign account ID and integration key, DocuSign SOP acknowledgement template ID, Google Sheets spreadsheet ID and sheet name for the role mapping table and acknowledgement tracker, and the 48-hour reminder window (process owner may want to adjust this).
03End-to-end data flow
// ��══════════════════════════════════════════════════════════════════
// TRIGGER: Google Drive file-change event
// ═══════════════════════════════════════════════════════════════════
GOOGLE_DRIVE.webhook.receive({
file_id : 'string', // Drive file ID
file_name : 'string', // Document title
event_type : 'create|update',
actor_email : 'string', // Editor Google account
modified_time : 'ISO8601'
})
// Dedupe check: skip if last register update for this file_id < 90 seconds ago
IF (now - sheets.register[file_id].last_updated) < 90s => ABORT (duplicate event)
// ═══════════════════════════════════════════════════════════════════
// AGENT 1: SOP Version and Register Agent
// ════════════════════════════════════════════════════════════════��══
// Step 1: Lookup existing register row by file_id
sheets.SOP_Register.lookup(file_id) => {
current_version : 'v1.3',
compliance_flag : true|false,
affected_roles : ['ops-manager','warehouse-lead'],
register_row_id : 'string'
}
// Step 2: Increment version
new_version = incrementMinor(current_version) // 'v1.3' => 'v1.4'
// Full rewrite flag in register => incrementMajor() // 'v1.4' => 'v2.0'
// Step 3: Archive pre-change file copy
GOOGLE_DRIVE.copy(file_id, dest='/SOP-Archive/{file_id}/{current_version}')
// Step 4: Write new version string to document header via Docs API
GOOGLE_DOCS.update(file_id, field='version_header', value=new_version)
// Step 5: Extract change_summary from change-log table (first new row)
change_summary = GOOGLE_DOCS.readTable(file_id, table_index=0, last_row=true)
// Fallback if empty: change_summary = 'Change summary not provided'
// => post alert to Slack ops channel
// Step 6: Append row to Change Log sheet
sheets.Change_Log.append({
file_id, new_version, actor_email, modified_time, change_summary
})
// Step 7: Update SOP Register row
sheets.SOP_Register.update(register_row_id, {
version : new_version,
revision_date : modified_time,
change_summary : change_summary,
cycle_status : 'in_progress'
})
// ── Agent 1 handoff to Agent 2 ──────────────────────────────────────
EMIT => Distribution_and_Acknowledgement_Agent({
sop.file_id : file_id,
sop.title : file_name,
sop.new_version : new_version,
sop.revision_date : modified_time,
sop.change_summary : change_summary,
sop.compliance_flag: compliance_flag,
sop.affected_roles : affected_roles,
sop.register_row_id: register_row_id
})
// ═══════════════════════════════════════════════════════════════════
// AGENT 2: Distribution and Acknowledgement Agent
// ═══════════════════════════════════════════════════════════════════
// Step 1: Resolve affected_roles to staff records
FOR EACH role_id IN sop.affected_roles:
sheets.Role_Mapping.lookup(role_id) => {
staff_name : 'string',
slack_user_id : 'U04XXXXXXX',
email : 'string',
line_manager_slack : 'U04YYYYYYY'
}
// Error path: role_id not found => log error, Slack alert, skip this role only
// Step 2: Initialise tracker rows
FOR EACH staff_member IN resolved_staff:
sheets.Ack_Tracker.append({
file_id, version: new_version,
staff_name, slack_user_id,
method : IF compliance_flag THEN 'docusign' ELSE 'slack_button',
status : 'pending',
acknowledged_at : null,
reminder_count : 0,
escalated_at : null
})
// Step 3a: Send Slack interactive message to each recipient
SLACK.postDirectMessage(slack_user_id, {
blocks: [
text: 'SOP Updated: {sop.title} — {sop.new_version}',
text: 'Change summary: {sop.change_summary}',
button: { text: 'I have read this SOP', action_id: 'ack_sop',
value: '{file_id}|{new_version}' }
]
})
// Step 3b: If compliance_flag == true — send DocuSign envelope in parallel
IF sop.compliance_flag:
DOCUSIGN.envelopes.create({
template_id : 'DOCUSIGN_SOP_TEMPLATE_ID', // stored in credential store
signer.email : staff_member.email,
signer.name : staff_member.staff_name,
custom_fields : { sop_title: sop.title, version: new_version }
}) => { envelope_id: 'string' }
sheets.Ack_Tracker.update(row, { envelope_id })
// ── Callback: Slack button press ─────────────────────────────────────
SLACK.interactive.callback({
action_id : 'ack_sop',
value : '{file_id}|{version}',
user.id : slack_user_id,
action_ts : 'ISO8601'
}) =>
sheets.Ack_Tracker.update({ file_id, slack_user_id, version }, {
status : 'acknowledged',
acknowledged_at : action_ts
})
// ── Callback: DocuSign envelope completed ────────────────────────────
DOCUSIGN.connect.webhook({
envelope_id : 'string',
status : 'completed',
completed_at : 'ISO8601'
}) =>
sheets.Ack_Tracker.update({ envelope_id }, {
status : 'acknowledged',
acknowledged_at : completed_at
})
// ── Scheduled sub-flow: every 48 hours ──────────────────────────────
SCHEDULER.every_48h:
pending_rows = sheets.Ack_Tracker.query(status='pending', acknowledged_at=null)
FOR EACH row IN pending_rows:
IF row.reminder_count < 2:
SLACK.postDirectMessage(row.slack_user_id, reminder_message)
sheets.Ack_Tracker.update(row, { reminder_count: row.reminder_count + 1 })
ELSE:
SLACK.postDirectMessage(row.line_manager_slack, escalation_message)
sheets.Ack_Tracker.update(row, {
status : 'escalated',
escalated_at : now()
})
// ── Completion check: runs after every tracker update ────────────────
pending_count = sheets.Ack_Tracker.count({ file_id, status='pending' })
escalated_count = sheets.Ack_Tracker.count({ file_id, status='escalated' })
IF (pending_count + escalated_count) == 0:
SLACK.postChannelMessage(ops_channel_id, {
text: 'SOP {sop.title} v{new_version} — full acknowledgement complete.',
fields: { total_required, completed_at: now() }
})
sheets.SOP_Register.update(register_row_id, { cycle_status: 'complete' })04Recommended build stack
More documents for this process
Every document generated for Standard Operating Procedure Management.