Back to Team Performance Dashboard

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

Team Performance Dashboard

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

This document gives the FullSpec build team everything needed to construct, wire, and ship the Team Performance Dashboard automation. It covers the current-state step map, full agent specifications with IO contracts, the end-to-end data flow, and the recommended build stack. The owner side of this process is handled separately in the Launch Plan and SOP. Everything in this document is internal to the FullSpec team and is written at implementation depth.

01Process snapshot

Step
Name
Description
1
Identify Metrics and Data Sources
Manager confirms which KPIs are due and which tools each lives in, typically from memory rather than a documented spec. Time cost: 10 min.
2
Export Data from HubSpot
Sales and pipeline metrics exported manually from HubSpot, either as a CSV or typed off the dashboard into the spreadsheet. Time cost: 20 min.
3
Export Data from Jira
Sprint velocity, open tickets, and cycle time copied from Jira reports into the master spreadsheet. Time cost: 15 min.
4
Chase Missing Team Submissions
BOTTLENECK. Any team lead who has not submitted metrics receives a manual Slack message; the manager then waits for responses before continuing. Time cost: 30 min.
5
Compile Data into Master Spreadsheet
BOTTLENECK. All figures entered into the Google Sheets master template, formulas checked, and layout inconsistencies from the prior week corrected. Time cost: 40 min.
6
Sense-Check and Spot Errors
Manager reads every figure, compares to last week's values, and investigates anything that looks wrong before the report leaves. Time cost: 20 min.
7
Build or Update the Looker Studio Report
Charts and summary panels in Looker Studio refreshed or manually tweaked if the data range or layout needs adjusting. Time cost: 25 min.
8
Write Weekly Summary Commentary
Short written summary of key movements, risks, and wins drafted manually and added to a linked Notion page. Time cost: 20 min.
9
Distribute Report to Stakeholders
Final report link and summary text shared via Slack and email to the management team and relevant team leads. Time cost: 10 min.
Time cost summary: Total manual time per cycle is 190 minutes (3 hours 10 min). At 4 runs per month this is approximately 760 minutes (12.7 hours) per month, or 3.5 hours per week annualised. Steps 2, 3, and 4 are replaced by the Data Collection Agent. Steps 5, 6, and 8 are replaced by the Validation and Commentary Agent. Steps 7 and 9 are handled by Looker Studio refresh and the automated Slack post respectively. Step 1 is eliminated as metric definitions are codified at build time. Step 9 becomes the automated Slack distribution post manager approval. Only the manager review gate (a deliberate retention) and the team-lead manual data entry into Google Sheets remain human-owned.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Data Collection Agent

Runs on a fixed weekly schedule, firing at 07:00 AM every Monday before the team starts work. It calls the HubSpot and Jira APIs in sequence, writes all retrieved metric values into the designated Google Sheets staging rows, then evaluates which metrics are flagged as requiring manual team-lead input and dispatches targeted Slack reminders to the relevant individuals with a direct link to their input cell. The agent waits up to 90 minutes for manual inputs before handing off to the Validation and Commentary Agent regardless of completion status. Complexity: Moderate. Estimated build: 14 hours.

Trigger
Fixed cron schedule: every Monday at 07:00 AM (timezone must be confirmed with owner; assume UTC-5 EST unless specified)
Tools
HubSpot (REST API v3), Jira (REST API v3), Google Sheets (Sheets API v4), Slack (Web API)
Replaces steps
Steps 2, 3, and 4 (HubSpot export, Jira export, and submission chasing)
Estimated build
14 hours / Moderate complexity
// Input
trigger: { type: 'schedule', day: 'Monday', time: '07:00', tz: 'America/New_York' }

// HubSpot pull — expected fields
hubspot.deals.properties: [ 'dealname', 'amount', 'closedate', 'dealstage', 'pipeline' ]
hubspot.pipeline_summary: { open_deal_count: int, weighted_revenue: float, won_this_week: float }

// Jira pull — expected fields
jira.sprint.current: { sprint_id: str, sprint_name: str, velocity: int }
jira.issues: { open_count: int, closed_this_week: int, cycle_time_avg_days: float }

// Manual input check — Sheets read
sheets.staging_row.manual_fields: [ 'team_lead_metric_A', 'team_lead_metric_B', ... ]
sheets.staging_row.status_flag: 'pending' | 'submitted'

// Output
sheets.staging_row: {
  week_commencing: date,
  hs_deal_count: int,
  hs_weighted_revenue: float,
  hs_won_revenue: float,
  jira_velocity: int,
  jira_open_issues: int,
  jira_cycle_time: float,
  manual_fields_status: 'complete' | 'partial',
  agent_handoff_ready: bool
}
slack.reminder_sent_to: [ '@team_lead_1', '@team_lead_2' ]  // only where status = 'pending'
  • HubSpot API tier: requires at minimum a Starter plan with private app token scoped to crm.objects.deals:read and crm.pipelines:read. Confirm with owner that no UI-only report fields are relied upon; if so, an alternative data source must be agreed before build.
  • Jira API tier: Jira Cloud Basic or above. Use a dedicated service-account API token (not a personal token) scoped to read:jira-work. Sprint data requires the Jira Software product to be active on the workspace.
  • Google Sheets: the staging sheet tab must be named 'weekly_staging' and must exist before first run. Column headers must match the field names in the IO contract exactly. The automation platform credential must hold a service account JSON with Sheets API Editor scope on the specific spreadsheet ID.
  • Slack reminders: use the Slack Web API chat.postMessage method targeting individual user IDs, not channel names, to avoid message noise. User ID to team-lead name mapping must be codified in a config object or a lookup row in the Sheets 'config' tab before build.
  • Fallback behaviour for late manual inputs: if the 90-minute wait elapses and one or more manual fields still carry status 'pending', the agent sets manual_fields_status to 'partial' and writes a 'LATE_DATA' flag to the staging row. The handoff to Validation and Commentary Agent proceeds; the commentary agent must surface the flag in the Notion draft.
  • Dedupe guard: if the scheduled trigger fires more than once in a Monday window (e.g. after a retry), the agent must check for an existing staging row with the current week_commencing date and skip re-write if found. Use an upsert pattern keyed on week_commencing.
  • Confirm before build: exact list of manual-input metrics, the Slack user IDs of all team leads, the Google Sheets spreadsheet ID, and the HubSpot pipeline ID to scope the deal pull.
Validation and Commentary Agent

Activates after the Data Collection Agent sets agent_handoff_ready to true on the staging row, or after the 90-minute wait window closes. It reads the full staging row from Google Sheets, runs field-level validation (null checks and week-on-week delta checks against the prior week's row), flags any metric that has moved more than 25% in either direction, and then calls an LLM to draft a plain-English weekly summary. The draft is written to a dated Notion page in the designated performance commentary database. The page is set to status 'Awaiting Approval'. A Slack direct message is sent to the manager containing the Notion page URL and a summary of any flagged anomalies. Once the manager marks the Notion page as 'Approved', the agent triggers the Looker Studio data refresh and then posts the dashboard link plus the approved commentary to the management Slack channel. Complexity: Complex. Estimated build: 14 hours.

Trigger
Google Sheets staging row: agent_handoff_ready = true (polled every 5 minutes by the orchestration layer), OR 90-minute elapsed timer from Data Collection Agent completion
Tools
Google Sheets (Sheets API v4), Notion (API v1), Slack (Web API), Google Looker Studio (Data Source refresh via Sheets connector), LLM endpoint (OpenAI GPT-4o or equivalent via platform-native integration)
Replaces steps
Steps 5, 6, and 8 (compile spreadsheet, sense-check, and write commentary). Also handles step 7 (Looker Studio refresh) and step 9 (Slack distribution) post-approval.
Estimated build
14 hours / Complex complexity
// Input
sheets.staging_row: { week_commencing, hs_deal_count, hs_weighted_revenue, hs_won_revenue,
  jira_velocity, jira_open_issues, jira_cycle_time, manual_fields_status, agent_handoff_ready }
sheets.prior_week_row: { same fields, offset by -7 days on week_commencing }

// Validation step — computed fields
validation.null_fields: [ string ]           // list of field names where value is null or empty
validation.anomaly_flags: [
  { field: str, current: float, prior: float, pct_change: float, direction: 'up'|'down' }
]  // only entries where abs(pct_change) > 25
validation.late_data_flag: bool

// LLM prompt context supplied
llm.context: { current_row, prior_row, anomaly_flags, late_data_flag, commentary_tone: 'concise' }

// Output — Notion page written
notion.page: {
  database_id: '<performance_commentary_db_id>',
  title: 'Weekly Report — {week_commencing}',
  properties: { Status: 'Awaiting Approval', Week: date },
  body_blocks: [ metrics_table, anomaly_callout, llm_draft_commentary ]
}

// Slack DM to manager
slack.dm: { user_id: '<manager_slack_id>', text: 'Draft ready for review', notion_url: str,
  anomaly_count: int, late_data: bool }

// On approval — Notion page status set to 'Approved' by manager
looker_studio.refresh: { data_source_id: '<sheets_connector_id>' }
slack.channel_post: {
  channel: '<management_channel_id>',
  text: approved_commentary_text,
  attachments: [ { title: 'Live Dashboard', url: '<looker_studio_report_url>' } ]
}
  • Notion API: requires an internal integration token with Insert content and Update content capabilities on the performance commentary database. The database ID must be captured during discovery and stored in the credential store. The Notion page template (with metrics table and commentary block structure) must be agreed with the owner before build.
  • Approval polling: the orchestration layer must poll the Notion page's Status property every 2 minutes after the DM is sent. Set a hard timeout of 4 hours; if no approval is received, send a second Slack DM to the manager and halt the distribution step. Log the timeout event to the error table.
  • Anomaly threshold: the 25% week-on-week delta is the default. This must be stored as a configurable variable in the platform's environment or in the Sheets 'config' tab so the owner can adjust it without a code change.
  • LLM commentary: use a system prompt that enforces a maximum of 200 words, a bullet-point structure, and a neutral professional tone. The prompt template must be stored as a version-controlled string in the credential/config store, not hardcoded in the workflow node.
  • Looker Studio refresh: Looker Studio does not expose a direct refresh API endpoint. The refresh is triggered by the Sheets data source being updated (which occurs when the Data Collection Agent writes the staging row). Confirm during discovery whether the Looker Studio report uses a direct Sheets connector or a BigQuery intermediary; if BigQuery is in play, the refresh strategy changes and additional build time is required.
  • Confirm before build: Notion database ID, manager's Slack user ID, management Slack channel ID, Looker Studio report URL and data source connector type, and the list of manual metric field names expected in the staging row.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with agent handoff points and exact field names
// ─────────────────────────────────────────────────────────────────
// TRIGGER
// ─────────────────────────────────────────────────────────────────
schedule.fire({ day: 'Monday', time: '07:00', tz: 'America/New_York' })
  -> event: { trigger_ts: ISO8601, week_commencing: date }

// ──────────────────────────────────────────────��──────────────────
// DATA COLLECTION AGENT — begins
// ─────────────────────────────────────────────────────────────────

// Step 1: Dedupe guard
sheets.read('weekly_staging', filter: { week_commencing: event.week_commencing })
  -> IF row_exists AND agent_handoff_ready = true: HALT (duplicate run detected)
  -> ELSE: continue

// Step 2: HubSpot API call
hubspot.GET /crm/v3/objects/deals
  params: { properties: ['dealname','amount','closedate','dealstage','pipeline'],
            filterGroups: [{ filters: [{ propertyName: 'closedate',
                                         operator: 'BETWEEN',
                                         value: week_start, highValue: week_end }] }] }
  -> raw: deals[]
  -> mapped: { hs_deal_count: int, hs_weighted_revenue: float, hs_won_revenue: float }

// Step 3: Jira API call
jira.GET /rest/agile/1.0/board/{boardId}/sprint
  params: { state: 'active' }
  -> sprint_id: str, sprint_name: str

jira.GET /rest/agile/1.0/sprint/{sprint_id}/issue
  params: { fields: ['status','resolutiondate','created'] }
  -> mapped: { jira_velocity: int, jira_open_issues: int, jira_cycle_time: float }

// Step 4: Sheets staging write (initial — API-sourced fields)
sheets.upsert('weekly_staging', key: 'week_commencing', values: {
  week_commencing,
  hs_deal_count, hs_weighted_revenue, hs_won_revenue,
  jira_velocity, jira_open_issues, jira_cycle_time,
  manual_fields_status: 'pending',
  agent_handoff_ready: false
})

// Step 5: Manual input check and Slack reminders
sheets.read('weekly_staging', week_commencing)
  -> for each manual_field WHERE value IS NULL:
       slack.postMessage({ user_id: config.team_lead_map[field],
                           text: 'Please enter {field} — link: {sheets_input_url}' })

// Step 6: Wait up to 90 minutes for manual inputs
timer.wait(90_minutes)
  -> sheets.read('weekly_staging', week_commencing)
  -> IF all manual_fields populated: manual_fields_status = 'complete'
  -> ELSE: manual_fields_status = 'partial', write LATE_DATA_FLAG = true

// Step 7: Set handoff flag
sheets.update('weekly_staging', { agent_handoff_ready: true })

// ─────────────────────────────────────────────────────────────────
// AGENT HANDOFF: Data Collection -> Validation and Commentary
// ─────────────────────────────────────────────────────────────────

// ─────────────────────────────────────────────────────────────────
// VALIDATION AND COMMENTARY AGENT — begins
// ─────────────────────────────────────────────────────────────────

// Step 8: Read current and prior week rows
sheets.read('weekly_staging', filter: { week_commencing: current_week }) -> current_row
sheets.read('weekly_staging', filter: { week_commencing: prior_week })   -> prior_row

// Step 9: Null validation
validation.null_fields = [ field FOR field IN current_row WHERE value IS NULL ]

// Step 10: Anomaly detection
FOR each numeric field IN current_row:
  pct_change = ((current - prior) / prior) * 100
  IF abs(pct_change) > config.anomaly_threshold (default: 25):
    validation.anomaly_flags.append({ field, current, prior, pct_change, direction })

// Step 11: LLM commentary draft
llm.complete({
  system: config.commentary_prompt_template,
  user: { current_row, prior_row, anomaly_flags, late_data_flag, max_words: 200 }
}) -> draft_commentary: str

// Step 12: Write Notion page
notion.pages.create({
  parent: { database_id: config.notion_db_id },
  properties: { Title: 'Weekly Report — {week_commencing}',
                Status: 'Awaiting Approval', Week: week_commencing },
  children: [ metrics_table_block, anomaly_callout_block,
               paragraph_block(draft_commentary) ]
}) -> notion_page_url: str

// Step 13: Slack DM to manager
slack.postMessage({ user_id: config.manager_slack_id,
  text: 'Draft ready. Anomalies: {anomaly_count}. Late data: {late_data_flag}',
  blocks: [{ type: 'button', text: 'Review in Notion', url: notion_page_url }] })

// Step 14: Poll for manager approval
POLL every 2 minutes, timeout 4 hours:
  notion.pages.retrieve(notion_page_url) -> page.properties.Status
  IF Status = 'Approved': proceed
  IF timeout: slack.postMessage(config.manager_slack_id, 'Approval overdue') -> HALT

// ─────────────────────────────────────────────────────────────────
// POST-APPROVAL: Looker Studio refresh + Slack distribution
// ────────────────────────────────────────────────────���────────────

// Step 15: Looker Studio refresh (Sheets connector — data already written at Step 4)
// No explicit API call required if using Sheets connector; verify connector type.
// If BigQuery intermediary: call bq.jobs.insert (load job) before this step.
looker_studio.verify_refresh({ report_url: config.looker_studio_report_url })

// Step 16: Slack channel post — distribution
slack.postMessage({
  channel: config.management_channel_id,
  text: notion.pages.retrieve(notion_page_url).commentary_text,
  attachments: [{ title: 'Live Dashboard', url: config.looker_studio_report_url }]
})

// ─────────────────────────────────────────────────────────────────
// END OF FLOW
// ─────────────────────────────────────────────────────────────────
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool (platform TBD at build kickoff). Structure as two discrete workflows: one per agent. Both workflows share a single credential store so API tokens are not duplicated across nodes. Each workflow is independently versioned and can be paused or re-run without affecting the other.
Webhook and schedule configuration
Data Collection Agent: cron trigger configured for Monday 07:00 AM in the owner's local timezone (confirm at discovery; default to America/New_York). Validation and Commentary Agent: event-based trigger watching the Google Sheets 'weekly_staging' tab for agent_handoff_ready = true, polled on a 5-minute interval. Approval poll: internal loop node checking Notion page Status property every 2 minutes with a 4-hour hard timeout.
Credential store
All secrets stored in the platform's native encrypted credential store, never in workflow node parameters. Required entries: HUBSPOT_PRIVATE_APP_TOKEN (scopes: crm.objects.deals:read, crm.pipelines:read), JIRA_API_TOKEN + JIRA_EMAIL + JIRA_BASE_URL (scope: read:jira-work), GOOGLE_SERVICE_ACCOUNT_JSON (Sheets API Editor on target spreadsheet), SLACK_BOT_TOKEN (scopes: chat:write, users:read), NOTION_INTEGRATION_TOKEN (Insert content, Update content on performance DB), OPENAI_API_KEY (or equivalent LLM provider key), LOOKER_STUDIO_REPORT_URL (stored as environment variable, not a secret).
Templating approach
The LLM system prompt is stored as a named string variable in the platform's environment settings (key: COMMENTARY_PROMPT_TEMPLATE). The anomaly threshold is stored as ANOMALY_THRESHOLD_PCT (default: 25). The Slack reminder message body and the Notion page title format are stored as template strings with {variable} placeholders resolved at runtime. No hardcoded strings in workflow nodes.
Error logging
All caught errors (API failures, null handoff flags, LLM timeouts, approval timeouts) are written to a Supabase table 'automation_errors' with columns: id (uuid), workflow_name (str), step_name (str), error_code (str), error_message (str), payload_snapshot (jsonb), occurred_at (timestamptz). A Slack alert fires to a dedicated '#automation-alerts' channel on any new error row insert, using a Supabase webhook or a database trigger and a lightweight HTTP call to the Slack Incoming Webhook URL.
Testing approach
All API integrations built and verified in sandbox or test environments first. HubSpot: use a HubSpot developer sandbox account. Jira: use a test project on the same Jira Cloud workspace with synthetic sprint data. Google Sheets: use a duplicate 'weekly_staging_TEST' tab. Notion: use a duplicate test database. Slack: post reminders to a private '#automation-test' channel instead of individual DMs. Promote to production credentials only after the QA parallel run (two full Monday cycles) passes sign-off.
Estimated total build time
Data Collection Agent: 14 hours. Validation and Commentary Agent: 14 hours. End-to-end integration, Looker Studio verification, error logging setup, credential wiring, and two-cycle QA run: 4 hours. Total: 32 hours across a 4-week delivery window.
The Looker Studio refresh behaviour is the highest-risk unknown in this build. If the report uses a direct Google Sheets connector, no additional API call is needed because Looker Studio re-reads the sheet on the next viewer load. If the report routes data through BigQuery, a BigQuery load job must be inserted into the flow before the Slack distribution step and the build estimate for that stage increases. Confirm the connector type during the discovery and access setup week before writing any Looker Studio-related workflow nodes.
Questions to resolve before build starts: (1) Exact list of manual-input metric fields and the Slack user IDs of all contributing team leads. (2) HubSpot pipeline ID to scope the deal pull. (3) Google Sheets spreadsheet ID and tab structure. (4) Notion performance commentary database ID and agreed page template structure. (5) Looker Studio connector type (Sheets direct or BigQuery). (6) Manager's Slack user ID and the management channel ID for distribution. (7) Owner's preferred timezone for the Monday 07:00 AM trigger. Contact support@gofullspec.com to schedule the discovery call and begin access provisioning.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Team Performance Dashboard.

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