Back to Weekly Business Review

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

Weekly Business Review Automation

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

This pack gives the FullSpec build team everything needed to construct, wire, and test the three-agent Weekly Business Review automation. It covers the current-state step map with bottlenecks identified, full agent specifications with IO contracts, an end-to-end data flow trace, and the recommended build stack. The process owner's responsibilities are limited to providing API credentials, confirming metric thresholds in the reference tab, and approving the pack before go-live. The FullSpec team handles all build, integration, and testing work.

01Process snapshot

Step
Name
Description
1
Pull Revenue Figures from Xero
Operations Manager logs into Xero and exports last week's invoiced revenue, payments received, and outstanding balance into a spreadsheet. Time cost: 20 min.
2
Pull Pipeline Data from HubSpot
Operations Manager opens HubSpot and manually notes open deal count, total pipeline value, deals closed last week, and stage movement. Time cost: 15 min.
3
Pull Project Status from Project Tool
Key project milestones, tasks overdue, and percentage completion are checked and noted manually from the project management tool. Time cost: 20 min.
4
Consolidate Data into Master Spreadsheet
BOTTLENECK: All pulled figures are entered into the master Google Sheet alongside prior weeks for comparison. High error risk from manual copy-paste across four sources. Time cost: 25 min.
5
Check Metrics Against Targets
Operations Manager manually compares each metric to the agreed weekly target and highlights any that are off track. Time cost: 15 min.
6
Update the Review Slide Deck
BOTTLENECK: Figures are copied from the spreadsheet into the Google Slides template, charts are refreshed, and commentary is typed in manually. Time cost: 30 min.
7
Write the Summary Commentary
A short written summary of the week's performance, key risks, and decisions needed is typed into the deck or email body. Time cost: 20 min.
8
Email Pack to Leadership Team
The completed deck is attached to an email and sent manually to the leadership group. Time cost: 5 min.
9
Post Reminder in Slack
A manual Slack message is sent to the leadership channel confirming the pack has been sent. Time cost: 5 min.
10
Archive the Completed Pack
The finished slide deck is saved manually into the correct Google Drive folder with the date in the file name. Time cost: 5 min.
Time cost summary: Total manual time per cycle is 160 minutes (approximately 2 hours 40 minutes). At 52 cycles per year that equals approximately 138 hours per year of manual assembly time. Steps 1, 2, and 4 are replaced by the Data Collection Agent. Steps 5 and 7 are replaced by the Review Narrative Agent. Steps 6, 8, 9, and 10 are replaced by the Distribution Agent. Step 3 (project status pull) is not in scope for this automation build. The one retained human step is a 10-minute owner review gated on any critical metric being flagged off target.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Data Collection Agent

Connects to Xero and HubSpot on a Sunday 8 PM weekly schedule, retrieves last week's financial and pipeline figures using authenticated API calls, and appends a new consolidated row to the master Google Sheet. This agent has no user-facing output; its only responsibility is reliable, structured data delivery to the sheet so downstream agents have a single source of truth. It must handle partial API failures gracefully and not write an incomplete row.

Trigger
Scheduled weekly trigger, Sunday 8:00 PM (configurable timezone, default to the business's local timezone confirmed during setup).
Tools
Xero (Accounting API), HubSpot (CRM Deals API), Google Sheets (Sheets API v4)
Replaces steps
Steps 1, 2, and 4 of the current manual process.
Estimated build
8 hours, Moderate complexity
// Input
schedule_trigger: { fired_at: ISO8601, week_label: 'YYYY-WW' }

// Xero fetch
xero.invoices.get({ date_from: week_start, date_to: week_end })
  -> { invoiced_revenue: float, payments_received: float, outstanding_ar: float }

// HubSpot fetch
hubspot.deals.search({ closedate_gte: week_start, closedate_lte: week_end })
  -> { open_deal_count: int, pipeline_value: float, deals_closed: int, stage_movements: int }

// Output
google_sheets.append_row(sheet_id, 'WeeklyData', {
  week_label: string,           // e.g. '2025-W16'
  week_start_date: date,
  week_end_date: date,
  invoiced_revenue: float,
  payments_received: float,
  outstanding_ar: float,
  open_deal_count: int,
  pipeline_value: float,
  deals_closed: int,
  stage_movements: int,
  data_status: 'complete' | 'partial_failure'
})

// On failure
IF xero_call fails: set invoiced_revenue = null, log error, set data_status = 'partial_failure'
IF hubspot_call fails: set pipeline fields = null, log error, set data_status = 'partial_failure'
IF data_status == 'partial_failure': halt pipeline, post error alert to Slack ops channel
  • Xero connection requires OAuth 2.0 with scope accounting.transactions.read and accounting.reports.read. The refresh token must be stored in the shared credential store. Confirm the Xero plan is Business or above as the Starter plan rate-limits API calls to 60 per minute.
  • HubSpot connection requires a Private App token with scopes crm.objects.deals.read and crm.objects.contacts.read. Confirm the HubSpot tier is Starter or above as the Free tier does not expose the Deals search endpoint.
  • The Google Sheets master file ID and the exact tab name 'WeeklyData' must be confirmed before build. The sheet must have a header row matching the field names above. Do not hardcode sheet IDs in the workflow; use environment variables.
  • Dedupe logic: before appending, check whether a row with the current week_label already exists in the sheet. If it does, skip the append and log a warning rather than creating a duplicate row.
  • The agent must be confirmed as the entry point for all downstream agents. Do not allow the Review Narrative Agent to fire unless data_status is 'complete' in the most recent row.
  • Confirm the business timezone with the process owner before setting the cron expression. Default assumption is UTC+10 (AEST) unless stated otherwise.
Review Narrative Agent

Reads the most recently appended row in the master Google Sheet, compares each metric against the target thresholds stored in a separate reference tab named 'Targets', and uses an AI language model call to produce a concise plain-English commentary. The commentary names any off-target metrics, states the direction and magnitude of the variance, and suggests one sentence of context or attention per flagged metric. It also outputs a boolean flag used by the conditional branch that determines whether a human review step is inserted before distribution.

Trigger
Fires automatically after Data Collection Agent writes a row with data_status = 'complete' to the Google Sheet. Implemented as a workflow continuation, not a separate schedule.
Tools
Google Sheets (Sheets API v4), AI language model API (GPT-4o or equivalent, called via the automation platform's HTTP request node)
Replaces steps
Steps 5 and 7 of the current manual process.
Estimated build
7 hours, Moderate complexity
// Input
google_sheets.get_row(sheet_id, 'WeeklyData', row = latest)
  -> current_week_data: { week_label, invoiced_revenue, payments_received,
       outstanding_ar, open_deal_count, pipeline_value, deals_closed, stage_movements }

google_sheets.get_range(sheet_id, 'Targets', 'A2:C20')
  -> targets: [{ metric_name: string, target_value: float, direction: 'min'|'max' }]

// Threshold comparison (internal logic)
FOR EACH metric IN current_week_data:
  variance_pct = (actual - target) / target * 100
  off_target = (direction == 'min' && actual < target) ||
               (direction == 'max' && actual > target)
  critical = abs(variance_pct) >= 10   // threshold configurable in Targets tab

// AI prompt payload
llm.chat({ model: 'gpt-4o', messages: [
  { role: 'system', content: 'You are a business analyst writing a brief weekly review.' },
  { role: 'user', content: build_prompt(current_week_data, flagged_metrics) }
]})

// Output
{
  narrative_text: string,          // 150-250 words plain English
  flagged_metrics: [{ metric_name, actual, target, variance_pct, critical: bool }],
  has_critical_flags: bool,
  week_label: string
}

// On approval (when has_critical_flags == true)
Insert human review gate: notify process owner via email with deck preview link.
Pipeline pauses until owner approves or overrides within 2 hours.
IF no response after 2 hours: escalate to Slack DM and wait 30 min, then auto-proceed.
  • The AI model API key must be stored in the shared credential store. Use the platform's secret manager, never inline in the workflow definition.
  • The system prompt and the narrative output length (150 to 250 words) are defined as workflow variables so the process owner can tune them without a code change.
  • The 'Targets' reference tab schema must be confirmed with the process owner before build. Columns must be: metric_name (string matching WeeklyData headers), target_value (number), direction ('min' or 'max'). FullSpec will supply a pre-filled template tab.
  • The critical flag threshold of 10% variance is the default. This must be documented in the Targets tab as an editable cell so the process owner can change it post-launch without involving FullSpec.
  • Fallback behaviour: if the AI call fails or times out after 15 seconds, generate a structured fallback narrative listing only the raw variance figures without prose commentary, set a fallback_used flag, and continue the pipeline. Do not halt distribution for an AI failure alone.
  • Test the narrative output against at least three weeks of historical data before sign-off. Confirm with the process owner that the tone and level of detail are appropriate for the leadership audience.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical
Distribution Agent

Takes the completed narrative and metric data from the Review Narrative Agent, pushes updated figures and commentary into named placeholders in the standing Google Slides deck, sends the deck link via Gmail to the leadership distribution list, posts a formatted summary to the Slack leadership channel, and archives the final deck to the designated Google Drive folder. This agent is the terminal step in the pipeline and must confirm successful send and post before marking the run complete.

Trigger
Fires after the Review Narrative Agent outputs its payload and, if has_critical_flags is true, after the human review gate is cleared by the process owner.
Tools
Google Slides (Slides API v1), Gmail (Gmail API v1), Slack (Web API, chat.postMessage), Google Drive (Drive API v3)
Replaces steps
Steps 6, 8, 9, and 10 of the current manual process.
Estimated build
5 hours, Simple complexity
// Input
{
  narrative_text: string,
  flagged_metrics: [{ metric_name, actual, target, variance_pct, critical }],
  has_critical_flags: bool,
  week_label: string,
  current_week_data: { invoiced_revenue, payments_received, outstanding_ar,
    open_deal_count, pipeline_value, deals_closed, stage_movements }
}

// Google Slides update
slides.batchUpdate(presentation_id, [
  replaceAllText({ '{{WEEK_LABEL}}': week_label }),
  replaceAllText({ '{{INVOICED_REVENUE}}': format_currency(invoiced_revenue) }),
  replaceAllText({ '{{PAYMENTS_RECEIVED}}': format_currency(payments_received) }),
  replaceAllText({ '{{OUTSTANDING_AR}}': format_currency(outstanding_ar) }),
  replaceAllText({ '{{OPEN_DEALS}}': open_deal_count }),
  replaceAllText({ '{{PIPELINE_VALUE}}': format_currency(pipeline_value) }),
  replaceAllText({ '{{DEALS_CLOSED}}': deals_closed }),
  replaceAllText({ '{{NARRATIVE}}': narrative_text })
])

// Gmail send
gmail.send({
  to: env.LEADERSHIP_EMAIL_LIST,       // comma-separated, stored as env var
  subject: 'Weekly Business Review - ' + week_label,
  body: render_email_template(narrative_text, flagged_metrics),
  attachments: []                       // link only, no attachment
})

// Slack post
slack.chat.postMessage({
  channel: env.LEADERSHIP_SLACK_CHANNEL_ID,
  text: 'Weekly pack ready for ' + week_label,
  blocks: build_slack_blocks(flagged_metrics, slides_url)
})

// Archive to Drive
drive.copy(presentation_id,
  { name: 'WeeklyReview_' + week_label, parents: [env.ARCHIVE_FOLDER_ID] }
)

// Output
{
  gmail_message_id: string,
  slack_ts: string,
  archived_file_id: string,
  run_status: 'success' | 'partial_failure',
  completed_at: ISO8601
}
  • All named placeholders in the Google Slides template (e.g. {{INVOICED_REVENUE}}) must exist in the deck before build starts. FullSpec will provide a placeholder audit list; the process owner must confirm the deck is not redesigned after this point without notifying the FullSpec team.
  • Gmail send requires OAuth 2.0 with scope gmail.send. The sending account must be a service account or a shared mailbox, not a personal account, to avoid token expiry issues when the account owner is away.
  • The leadership email distribution list and Slack channel ID must be supplied as environment variables before build. Do not hardcode recipient addresses in the workflow definition.
  • The Slack bot must be installed in the workspace and invited to the leadership channel. Confirm the channel ID (not the display name) during the access setup stage.
  • The archive folder ID in Google Drive must be confirmed and the automation platform's service account must have Editor access to that folder.
  • If the Gmail send fails, retry twice with a 5-minute interval. If both retries fail, post an error alert to a designated Slack ops channel and do not attempt the archive step until the send is confirmed.

03End-to-end data flow

Full trigger-to-output data flow. All field names match the Google Sheets schema and Slides placeholder names.
// ============================================================
// WEEKLY BUSINESS REVIEW: END-TO-END DATA FLOW
// Trigger to final output, all field names and agent handoffs
// ============================================================

// TRIGGER
SCHEDULE cron('0 20 * * 0', tz=env.BUSINESS_TIMEZONE)
  -> { fired_at: ISO8601, week_label: 'YYYY-WW',
       week_start: date, week_end: date }

// ============================================================
// AGENT 1: Data Collection Agent
// ============================================================

// Step 1: Xero API call
GET https://api.xero.com/api.xro/2.0/Reports/ProfitAndLoss
  ?fromDate={week_start}&toDate={week_end}
  Authorization: Bearer {xero_access_token}
  -> raw_xero: { invoiced_revenue: float,
                 payments_received: float,
                 outstanding_ar: float }

// Step 2: HubSpot API call
POST https://api.hubapi.com/crm/v3/objects/deals/search
  Authorization: Bearer {hubspot_private_app_token}
  body: { filterGroups: [{ filters: [
    { propertyName: 'closedate', operator: 'BETWEEN',
      value: week_start, highValue: week_end }]}]}
  -> raw_hubspot: { open_deal_count: int,
                    pipeline_value: float,
                    deals_closed: int,
                    stage_movements: int }

// Step 3: Write to Google Sheets
POST https://sheets.googleapis.com/v4/spreadsheets/{SHEET_ID}/values
  /WeeklyData!A:J:append?valueInputOption=USER_ENTERED
  body: { values: [[
    week_label, week_start, week_end,
    invoiced_revenue, payments_received, outstanding_ar,
    open_deal_count, pipeline_value, deals_closed,
    stage_movements, 'complete'
  ]] }
  -> { updatedRange: string, updatedRows: 1 }

// HANDOFF: Data Collection Agent -> Review Narrative Agent
// Condition: data_status == 'complete' in appended row
// Payload passed: { week_label, sheet_id, latest_row_index }

// ============================================================
// AGENT 2: Review Narrative Agent
// ============================================================

// Step 4: Read latest data row
GET https://sheets.googleapis.com/v4/spreadsheets/{SHEET_ID}/values
  /WeeklyData!A{latest_row}:K{latest_row}
  -> current_week_data: { week_label, invoiced_revenue,
       payments_received, outstanding_ar, open_deal_count,
       pipeline_value, deals_closed, stage_movements }

// Step 5: Read targets reference tab
GET https://sheets.googleapis.com/v4/spreadsheets/{SHEET_ID}/values
  /Targets!A2:C20
  -> targets: [{ metric_name, target_value, direction }]

// Step 6: Threshold comparison (internal)
FOR metric IN current_week_data:
  variance_pct = (actual - target) / target * 100
  critical = abs(variance_pct) >= env.CRITICAL_THRESHOLD_PCT  // default 10
  -> flagged_metrics: [{ metric_name, actual, target,
                         variance_pct, critical: bool }]
  -> has_critical_flags: bool

// Step 7: AI language model call
POST https://api.openai.com/v1/chat/completions
  Authorization: Bearer {llm_api_key}
  body: { model: 'gpt-4o', max_tokens: 400,
    messages: [system_prompt, user_prompt(current_week_data, flagged_metrics)] }
  -> narrative_text: string  // 150-250 words

// Step 8: Conditional gate
IF has_critical_flags == true:
  NOTIFY process_owner via gmail (preview link + flagged list)
  PAUSE pipeline max 2 hours
  IF no_response: DM via Slack, pause 30 min, then auto-proceed
ELSE:
  Continue immediately

// HANDOFF: Review Narrative Agent -> Distribution Agent
// Payload: { narrative_text, flagged_metrics, has_critical_flags,
//            week_label, current_week_data }

// ============================================================
// AGENT 3: Distribution Agent
// ============================================================

// Step 9: Update Google Slides placeholders
POST https://slides.googleapis.com/v1/presentations/{PRESENTATION_ID}:batchUpdate
  body: { requests: [replaceAllText per placeholder] }
  -> { presentationId, replies }
  -> slides_url: 'https://docs.google.com/presentation/d/{PRESENTATION_ID}'

// Step 10: Send Gmail
POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
  body: { to: env.LEADERSHIP_EMAIL_LIST,
    subject: 'Weekly Business Review - ' + week_label,
    body_html: render_email_template(narrative_text, flagged_metrics, slides_url) }
  -> { gmail_message_id: string }

// Step 11: Post Slack message
POST https://slack.com/api/chat.postMessage
  Authorization: Bearer {slack_bot_token}
  body: { channel: env.LEADERSHIP_SLACK_CHANNEL_ID,
    blocks: build_slack_blocks(week_label, flagged_metrics, slides_url) }
  -> { ts: string, channel: string }

// Step 12: Archive to Google Drive
POST https://www.googleapis.com/drive/v3/files/{PRESENTATION_ID}/copy
  body: { name: 'WeeklyReview_' + week_label,
          parents: [env.ARCHIVE_FOLDER_ID] }
  -> { archived_file_id: string }

// FINAL OUTPUT
run_log.write({
  week_label, fired_at, completed_at: ISO8601,
  gmail_message_id, slack_ts, archived_file_id,
  has_critical_flags, flagged_metrics_count: int,
  run_status: 'success' | 'partial_failure'
})
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool implementing one workflow per agent (three workflows total): DataCollectionWorkflow, ReviewNarrativeWorkflow, and DistributionWorkflow. Workflows are chained via internal webhook or event trigger, not by schedule. All three workflows share a single credential store so API tokens are rotated in one place. Workflow definitions are stored in version control. No build platform is prescribed at this stage; the architecture is platform-agnostic.
Webhook configuration
DataCollectionWorkflow is triggered by a cron schedule (Sunday 20:00, business timezone). On successful sheet write, it fires an internal webhook to ReviewNarrativeWorkflow passing { week_label, latest_row_index, data_status }. ReviewNarrativeWorkflow fires an internal webhook to DistributionWorkflow on narrative completion, passing the full narrative payload. All internal webhook endpoints must use a shared secret header (X-FullSpec-Secret: {env.INTERNAL_WEBHOOK_SECRET}) for authentication. External-facing webhooks are not required for this process.
Templating approach
Google Slides placeholders use double-brace syntax ({{FIELD_NAME}}) to allow replaceAllText API calls without a custom parser. The email body is rendered from an HTML template stored as a workflow variable, with Handlebars-style substitution for narrative_text, week_label, and flagged_metrics list. Slack blocks are built programmatically using the Block Kit section and context block pattern. All templates are stored as editable workflow variables, not hardcoded strings, so they can be updated post-launch without a code change.
Error logging
All agent run results are written to a Supabase table named automation_run_log with columns: run_id (uuid), week_label (text), agent_name (text), status (text), error_message (text), created_at (timestamptz). On any status other than 'success', the orchestration layer posts an alert to the designated Slack ops channel (env.OPS_SLACK_CHANNEL_ID) with the run_id, agent_name, and error_message. The Supabase table is accessible to the FullSpec team for monitoring during the first four weeks post-launch.
Testing approach
All three agents must be built and tested in a sandbox environment before any production credentials are connected. Sandbox testing uses a duplicate Google Sheet with historical data (minimum three prior weeks), a Xero demo company, a HubSpot sandbox portal, and a non-production Slack channel. Each agent is tested in isolation first, then end-to-end as a chained run. The Review Narrative Agent narrative output is tested against three historical weeks and reviewed by the process owner before the agent is promoted to production.
Estimated total build time
Data Collection Agent: 8 hours. Review Narrative Agent: 7 hours. Distribution Agent: 5 hours. End-to-end integration testing and QA: 2 hours. Total: 22 hours. This matches the confirmed build_effort_hours in the process snapshot and the Standard build scope.
Before build starts, the FullSpec team requires the following from the process owner: Xero API credentials (OAuth client ID and secret, tenant ID), HubSpot Private App token, the master Google Sheet file ID and confirmation that the 'WeeklyData' and 'Targets' tabs exist with the correct schema, the Google Slides presentation ID with all named placeholders in place, the Gmail sending address authorised for OAuth, the Slack bot token and the leadership channel ID, the Google Drive archive folder ID, and the leadership email distribution list. Any delay in providing these items will extend the build timeline beyond the four-week delivery estimate. Contact support@gofullspec.com to initiate the credential handover.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Weekly Business Review.

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