Back to Risk Register Management

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Risk Register Management

[YourCompany.com] · Management Department · Prepared by FullSpec · [Today's Date]

This document gives the FullSpec build team everything required to configure, wire, and test the three-agent Risk Register Management automation. It covers the current-state step map with bottleneck identification, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. The process owner and risk manager are referenced for context only. All build, integration, and testing work is carried out by the FullSpec team.

01Process snapshot

Step
Name
Description
1
Receive New Risk Notification
Risk manager receives a risk report verbally, via email, or Slack and notes it for later entry. No structured capture occurs at this stage. Time cost: 10 min/occurrence.
2
Manually Enter Risk Into Register
Risk manager opens the Google Sheet and adds a new row with description, category, date raised, and initial owner. Performed in batches, often days after submission. Time cost: 15 min/occurrence.
3
Assign Risk Owner and Due Date
Risk manager decides ownership, types the owner name into the sheet, and manually sends a notification email with the review deadline. Time cost: 10 min/occurrence.
4
Send Weekly Reminder Emails to Risk Owners
Risk manager scans the register, identifies items due for review, and emails each owner individually to request updated scores. Repeated every week across all open items. Time cost: 40 min/week.
5
Chase Overdue Responses
Owners who have not responded receive follow-up emails or Slack messages, often across multiple rounds before a response arrives. Time cost: 30 min/week.
6
Update Risk Scores in Register
As responses arrive by email the risk manager reads each one and manually updates likelihood, impact, and rating columns in the sheet. Time cost: 25 min/week.
7
Review and Escalate High-Rated Risks
Risk manager visually scans the register for items in the high or critical band and decides whether to escalate. No automatic flag exists, so items can be missed. Time cost: 20 min/week.
8
Prepare Board or Leadership Summary Report
Risk manager manually extracts key stats, copies top risks into Notion or a document, and writes a narrative summary before each monthly leadership meeting. Time cost: 60 min/month.
9
Distribute Report to Stakeholders
Finished report is emailed to the leadership team and filed in the shared drive. Recipients have no visibility of register changes between reports. Time cost: 10 min/month.
10
Archive Previous Register Version
Risk manager saves a dated copy of the current sheet as an audit snapshot. Frequently forgotten, leaving an incomplete audit trail. Time cost: 10 min/month.
Time cost summary: Total manual time per weekly cycle is approximately 230 minutes (3 hours 50 min) across all ten steps, scaling to roughly 5.5 hours/week when monthly tasks are amortised. At 4.4 hours saved per week after automation, the process consumes an estimated 22 hours every 30 days. Steps 1, 2, 3, and 6 are replaced by the Risk Intake and Scoring Agent. Steps 4, 5, and 7 are replaced by the Review Chase and Escalation Agent. Steps 8, 9, and 10 are replaced by the Risk Reporting Agent. One manual step is retained: human review of any risk the scoring agent flags as critical before it is promoted.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Risk Intake and Scoring Agent

This agent is the entry point for all new risk submissions. It listens for new Google Forms responses, writes a structured row to the Google Sheets risk register, and then applies the agreed likelihood-by-impact risk matrix to the submission text to propose initial scores. The scored row is flagged for owner assignment and, if the proposed score lands in the critical band, the row is held and routed to the risk manager for manual review before any further action is taken. Estimated build time: 14 hours. Complexity: Moderate.

Trigger
New response submitted via Google Forms (webhook or polling interval no greater than 5 minutes).
Tools
Google Forms, Google Sheets
Replaces steps
1, 2, 3, 6
Estimated build
14 hours | Moderate
// Input
form_response_id       : string   // Google Forms response ID
submitter_email        : string   // From form metadata
risk_title             : string   // Form field: 'Risk name or summary'
risk_description       : string   // Form field: 'Describe the risk in detail'
risk_category          : string   // Form field: dropdown (Strategic, Operational, Financial, Compliance, Reputational)
suggested_owner_email  : string   // Form field: optional
submission_timestamp   : ISO8601

// Output (written to Google Sheets register row)
risk_id                : string   // Auto-generated, format RISK-YYYYMM-NNN
risk_title             : string
risk_category          : string
risk_description       : string
submitter_email        : string
assigned_owner_email   : string   // Resolved from suggested_owner or routing rules
likelihood_score       : integer  // 1-5, proposed by agent
impact_score           : integer  // 1-5, proposed by agent
risk_rating            : string   // Low | Medium | High | Critical (likelihood x impact matrix)
date_raised            : date
next_review_date       : date     // date_raised + configured review cadence
score_status           : string   // 'Proposed' | 'Awaiting human review' | 'Confirmed'
row_timestamp          : ISO8601

// On critical flag
critical_hold          : boolean  // true when risk_rating == 'Critical'
manager_review_url     : string   // Deep link to the register row for the risk manager
  • The risk matrix scoring grid (likelihood 1-5 x impact 1-5 = 25 cells mapped to Low, Medium, High, Critical) must be finalised and documented as a configuration input before this agent is trained. Any post-launch changes to thresholds require agent reconfiguration.
  • Google Forms must use structured field names exactly as listed in the IO block. Free-text field names will break the parser.
  • The Google Sheets register tab must be named 'Risk Register' and columns must match the schema in the IO output block. The sheet ID and tab name are stored as environment variables in the credential store.
  • Owner routing: if suggested_owner_email is empty, the agent applies a category-to-owner lookup table (configured during discovery). If the resolved owner email is not in the lookup table, the row is written with assigned_owner_email = NULL and a Slack alert is sent to the risk manager.
  • Deduplication: on form submission the agent checks for an existing row with an identical risk_title submitted within the previous 7 days by the same submitter_email. If a match is found, the new submission is flagged as a potential duplicate and held for manager review rather than creating a second row.
  • Critical-band hold must be confirmed as active before go-live. The risk manager must acknowledge that no critical-rated item will auto-advance without their approval.
  • Google Workspace tier must support Google Forms API access. Confirm the service account has Forms read scope and Sheets write scope before build begins.
Review Chase and Escalation Agent

This agent runs on a scheduled basis and on event-based triggers to monitor the risk register for items approaching or past their review date. It sends personalised Slack nudges to the assigned owner and, if no response is recorded within 48 hours, sends a formatted escalation email via Gmail to the risk manager and the relevant department head. It also monitors the register continuously for any row where risk_rating is 'Critical' and score_status is 'Confirmed', triggering an immediate escalation email regardless of review date. Estimated build time: 12 hours. Complexity: Moderate.

Trigger
Scheduled poll of the Google Sheets register every 60 minutes; also triggered immediately when a row's score_status transitions to 'Confirmed' with risk_rating = 'Critical'.
Tools
Google Sheets, Slack, Gmail
Replaces steps
4, 5, 7
Estimated build
12 hours | Moderate
// Input (read from Google Sheets register)
risk_id                : string
risk_title             : string
risk_rating            : string
assigned_owner_email   : string
next_review_date       : date
score_status           : string
last_nudge_sent_at     : ISO8601  // null if no nudge sent yet
escalation_sent_at     : ISO8601  // null if no escalation sent yet

// Output: Slack nudge (owner)
slack_channel          : string   // Owner's DM, resolved via Slack user lookup by email
slack_message_text     : string   // Includes risk_id, risk_title, review link, proposed scores
nudge_timestamp        : ISO8601  // Written back to last_nudge_sent_at in the register

// Output: Gmail escalation (overdue or critical)
to_address             : string   // risk_manager@[YourCompany.com]
cc_address             : string   // dept_head@[YourCompany.com], resolved from category-to-dept lookup
subject                : string   // 'Risk Escalation: [risk_id] [risk_title] - Action Required'
body_html              : string   // Formatted summary with scores, owner, days overdue, register link
escalation_timestamp   : ISO8601  // Written back to escalation_sent_at in the register

// Register write-back
last_nudge_sent_at     : ISO8601
escalation_sent_at     : ISO8601
escalation_reason      : string   // 'Overdue 48h' | 'Critical rating confirmed'
  • Slack user lookup depends on assigned_owner_email matching a verified Slack workspace email. If no match is found, the nudge falls back to posting in a designated risk-alerts Slack channel and the risk manager is notified of the unresolved mapping.
  • The 48-hour escalation window is configurable as an environment variable (DEFAULT_ESCALATION_HOURS=48). Confirm the preferred value with the risk manager during discovery.
  • Gmail escalation requires the service account to have Gmail send-as permission for the risk manager's address, or a shared inbox alias must be agreed. Confirm before build begins.
  • The category-to-department-head routing table must be provided before this agent is deployed. Missing entries will cause the cc_address to default to the risk manager only.
  • The agent must not send duplicate nudges within a 24-hour window for the same risk_id. Write last_nudge_sent_at back to the register row immediately after each nudge and check this field before sending.
  • Escalation emails for critical items fire immediately on score_status = 'Confirmed' + risk_rating = 'Critical', bypassing the 48-hour window. This behaviour must be validated in QA before go-live.
Risk Reporting Agent

This agent fires on a scheduled trigger on the last business day of each calendar month. It reads the full current state of the Google Sheets risk register, calculates summary statistics and the risk heat map distribution, writes a structured leadership summary to a pre-configured Notion page template, and then distributes the completed report via Gmail to all stakeholders on the distribution list and posts a summary excerpt to the designated Slack channel. The agent also saves a dated snapshot of the register tab to the archive sheet for audit continuity. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Scheduled trigger fires at 08:00 on the last business day of the month (cron: resolved dynamically, not hardcoded, to account for month-end variation).
Tools
Google Sheets, Notion, Gmail, Slack
Replaces steps
8, 9, 10
Estimated build
10 hours | Moderate
// Input (read from Google Sheets 'Risk Register' tab)
all_register_rows      : array    // Full snapshot of all rows at trigger time
report_month           : string   // YYYY-MM derived from trigger date
stakeholder_list       : array    // Emails, read from 'Config' tab row 'report_recipients'
notion_page_id         : string   // Target Notion page, stored in credential store
slack_report_channel   : string   // Channel ID, stored in credential store

// Computed summary statistics
total_risks            : integer
critical_count         : integer
high_count             : integer
medium_count           : integer
low_count              : integer
overdue_count          : integer
new_this_month         : integer
closed_this_month      : integer
top_risks              : array    // Up to 5 rows where risk_rating IN ('Critical','High'), sorted by likelihood_score DESC

// Output: Notion page
notion_page_title      : string   // 'Risk Summary Report - [Month] [Year]'
notion_page_content    : string   // Structured blocks: summary stats, heat map table, top risks list, narrative
notion_page_url        : string   // Returned by Notion API after write

// Output: Gmail distribution
to_addresses           : array    // stakeholder_list
subject                : string   // 'Monthly Risk Report - [Month] [Year] - [YourCompany.com]'
body_html              : string   // Includes notion_page_url and key stats inline

// Output: Slack excerpt
slack_channel          : string   // slack_report_channel
slack_message_text     : string   // Key headline stats and Notion link

// Output: Archive write-back
archive_tab_name       : string   // Format 'Archive-YYYY-MM'
archive_timestamp      : ISO8601
  • The Notion report template (page structure, property blocks, and heading hierarchy) must be agreed with the leadership team and created manually once before the agent is activated. The agent writes into this template; it does not create the page structure dynamically.
  • Last-business-day calculation must exclude weekends and any public holidays listed in a 'Holidays' tab in the config sheet. This list must be populated for the current year before go-live.
  • Notion API integration requires a Notion internal integration token with write access to the target page and its parent database. Confirm the workspace admin grants this before build begins.
  • The stakeholder distribution list is read from the 'Config' tab in the register sheet rather than being hardcoded. Adding or removing recipients requires only a sheet update, not a workflow change.
  • Archive tab creation: if a tab named 'Archive-YYYY-MM' already exists (e.g. from a re-run), the agent appends a suffix '-v2' rather than overwriting. Log the event to the error log table.
  • Gmail send volume for this agent is low (one batch per month) and within standard sending limits. No throttling logic is required for the reporting agent specifically.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow, Risk Register Management automation
// ─────────────────────────────────────────────────────────────────────
// TRIGGER A: New risk submission via Google Forms
// ─────────────────────────────────────────────────────────────────────
Google Forms (webhook / poll ≤5 min)
  → form_response_id, submitter_email, risk_title, risk_description,
    risk_category, suggested_owner_email, submission_timestamp

// ─────────────────────────────────────────────────────────────────────
// AGENT 1: Risk Intake and Scoring Agent
// ─────────────────────────────────────────────────────────────────────
  [1a] Deduplication check
       Google Sheets read → filter rows WHERE risk_title MATCH
       AND date_raised > (submission_timestamp - 7d)
       AND submitter_email == incoming.submitter_email
       IF match found → set duplicate_flag = true, hold for manager review → EXIT

  [1b] Owner resolution
       suggested_owner_email != NULL → assigned_owner_email = suggested_owner_email
       suggested_owner_email == NULL → lookup category_to_owner_table[risk_category]
       IF no match → assigned_owner_email = NULL, alert_slack(risk_manager), continue

  [1c] Scoring logic
       risk_description + risk_category → risk_matrix[likelihood][impact]
       → likelihood_score (1-5), impact_score (1-5)
       risk_rating = matrix_cell[likelihood_score][impact_score]
       // e.g. likelihood=4, impact=5 → 'Critical'

  [1d] Register write
       Google Sheets append row →
         risk_id, risk_title, risk_category, risk_description,
         submitter_email, assigned_owner_email,
         likelihood_score, impact_score, risk_rating,
         date_raised, next_review_date, score_status='Proposed',
         row_timestamp

  [1e] Critical hold branch
       IF risk_rating == 'Critical'
         → set score_status = 'Awaiting human review'
         → Gmail to risk_manager: subject='Critical Risk Hold: [risk_id]',
           body includes manager_review_url (deep link to register row)
         → WAIT for manual score_status update to 'Confirmed' or 'Overridden'
       ELSE
         → score_status = 'Proposed', continue to Agent 2

// ──────────────────────────────────────────────────���──────────────────
// HANDOFF A→B: Register row written with score_status = 'Proposed'
//              or manager sets score_status = 'Confirmed' after review
// ─────────────────────────────────────────────────────────────────────

// TRIGGER B: Scheduled poll every 60 min OR score_status transition event
// ─────────────────────────────────────────────────────────────────────
// AGENT 2: Review Chase and Escalation Agent
// ─────────────────────────────────────────────────────────────────────
  [2a] Register scan
       Google Sheets read all rows →
         filter WHERE next_review_date <= TODAY() + 1d
         AND score_status NOT IN ('Awaiting human review')
         AND (last_nudge_sent_at == NULL OR last_nudge_sent_at < NOW() - 24h)

  [2b] Slack nudge
       FOR each matched row:
         Slack user lookup: assigned_owner_email → slack_user_id
         IF slack_user_id found → DM owner:
           text includes risk_id, risk_title, likelihood_score,
           impact_score, risk_rating, register_deep_link
         ELSE → post to risk_alerts_channel, notify risk_manager
         Google Sheets write → last_nudge_sent_at = NOW()

  [2c] 48-hour escalation check
       Google Sheets read →
         filter WHERE last_nudge_sent_at < NOW() - 48h
         AND score_status == 'Proposed'
         AND escalation_sent_at == NULL
       FOR each matched row:
         Gmail send →
           to: risk_manager_email
           cc: dept_head_email (from category_to_dept_table[risk_category])
           subject: 'Risk Escalation: [risk_id] [risk_title] - Action Required'
           body_html: risk_id, risk_title, risk_rating, assigned_owner_email,
                      days_overdue, register_link
         Google Sheets write → escalation_sent_at = NOW(),
                               escalation_reason = 'Overdue 48h'

  [2d] Immediate critical escalation
       Google Sheets read →
         filter WHERE risk_rating == 'Critical'
         AND score_status == 'Confirmed'
         AND escalation_sent_at == NULL
       FOR each matched row:
         Gmail send (same format as 2c)
         Google Sheets write → escalation_sent_at = NOW(),
                               escalation_reason = 'Critical rating confirmed'

// ─────────────────────────────────────────────────────────────────────
// HANDOFF B→C: Register continuously updated; Agent 3 fires on schedule
// ─────────────────────────────────────────────────────────────────────

// TRIGGER C: Scheduled cron, 08:00 last business day of month
// ─────────────────────────────────────────────────────────────────────
// AGENT 3: Risk Reporting Agent
// ─────────────────────────────────────────────────────────────────────
  [3a] Register snapshot read
       Google Sheets read all rows →
         all_register_rows (full current state at trigger timestamp)
         report_month = YYYY-MM from trigger date

  [3b] Summary statistics computed
       total_risks, critical_count, high_count, medium_count, low_count,
       overdue_count (next_review_date < TODAY AND score_status != 'Closed'),
       new_this_month (date_raised within report_month),
       closed_this_month (status='Closed' within report_month)
       top_risks = rows WHERE risk_rating IN ('Critical','High')
                   ORDER BY likelihood_score DESC LIMIT 5

  [3c] Notion report write
       Notion API PATCH page_id →
         page_title: 'Risk Summary Report - [Month] [Year]'
         blocks: heading (stats), heat_map_table, top_risks_table, narrative_text
       → returns notion_page_url

  [3d] Gmail distribution
       Gmail send →
         to: stakeholder_list (from Config tab 'report_recipients')
         subject: 'Monthly Risk Report - [Month] [Year] - [YourCompany.com]'
         body_html: key stats inline + notion_page_url

  [3e] Slack excerpt
       Slack post →
         channel: slack_report_channel
         text: headline stats + notion_page_url

  [3f] Archive write-back
       Google Sheets duplicate 'Risk Register' tab →
         new tab name: 'Archive-YYYY-MM' (append '-v2' if exists)
         archive_timestamp written to Config tab 'last_archived'

// ─────────────────────────────────────────────────────────────────────
// END: Leadership report published, archived, and distributed
// ─────────────────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform (not platform-specific at this stage). One workflow per agent is the recommended structure: Workflow 1 = Risk Intake and Scoring Agent, Workflow 2 = Review Chase and Escalation Agent, Workflow 3 = Risk Reporting Agent. All three workflows share a single credential store with named entries for each integration (see credential list below). Workflows are version-controlled and stored in a dedicated project folder within the automation platform.
Credential store entries
GOOGLE_SERVICE_ACCOUNT_JSON (Sheets + Forms read/write, scopes: spreadsheets, drive.file, forms.body.readonly); GMAIL_SEND_AS_ADDRESS (risk manager alias or shared inbox); SLACK_BOT_TOKEN (scopes: chat:write, users:read, users:read.email, channels:read); NOTION_INTEGRATION_TOKEN (write access to report page and parent database); REGISTER_SHEET_ID (Google Sheets file ID); REPORT_NOTION_PAGE_ID; SLACK_REPORT_CHANNEL_ID; SLACK_RISK_ALERTS_CHANNEL_ID. All entries stored as encrypted environment variables, never hardcoded in workflow nodes.
Webhook configuration
Google Forms new-response trigger: configured as a polling node (≤5-minute interval) against the Forms response endpoint, using the service account. No public webhook URL is required for Forms. Review Chase Agent: internal schedule trigger (every 60 minutes) plus an event trigger watching for score_status column changes in the register sheet via Sheets change notification or polling diff. Reporting Agent: cron schedule resolved dynamically at run time using a last-business-day helper function that reads the Holidays tab.
Templating approach
Slack message text and Gmail body HTML are stored as named templates in a 'Templates' tab within the Google Sheets config file. Each template uses double-brace placeholders ({{risk_id}}, {{risk_title}}, etc.) resolved at send time by the workflow. This allows the risk manager to update message copy without touching the automation. Notion report structure is a pre-built page template; the agent writes only into designated property blocks and does not alter page structure.
Error logging
All agent run errors, unresolved owner lookups, duplicate flags, and archive conflicts are written to a Supabase table named 'automation_error_log' with columns: error_id (UUID), workflow_name, error_type, affected_risk_id, error_message, occurred_at (ISO8601), resolved (boolean). A Slack alert is posted to the risk_alerts_channel for any ERROR-level event within 5 minutes of occurrence. The risk manager reviews the error log weekly as part of the retained manual step.
Testing approach
All three agents are built and validated against a sandbox Google Sheet ('Risk Register - TEST') and a sandbox Notion page before any connection to the production register. Gmail sends in test mode use a designated test inbox rather than the live stakeholder list. Slack messages in test mode post to a private #automation-test channel. Sandbox-first policy is mandatory: no production data is touched until end-to-end QA is signed off. See the Test and QA Plan (FS-DOC-06) for the full test case list.
Estimated total build time
Risk Intake and Scoring Agent: 14 hours. Review Chase and Escalation Agent: 12 hours. Risk Reporting Agent: 10 hours. End-to-end integration testing and QA: 6 hours (included in the 38-hour total per the build effort estimate). Discovery and risk matrix configuration: within the delivery schedule (Week 1). Full build effort: 38 hours across a 4-week delivery window.
Before any build node is activated: confirm the Google Workspace service account has been created and granted the correct OAuth scopes listed in the credential store entry above; confirm the Slack app has been installed to the workspace with the listed bot token scopes; confirm the Notion integration has been shared with the target report page by the workspace admin; and confirm the risk matrix scoring thresholds, owner routing table, department head routing table, and stakeholder distribution list are all documented and loaded into the Config tab. Build cannot begin on Agents 2 or 3 until Agent 1 has been validated in the sandbox environment. Contact support@gofullspec.com for any credential or access query.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Risk Register Management.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View