Back to Project Milestone Tracking

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

Project Milestone Tracking

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

This document is the primary technical reference for the FullSpec team building the Project Milestone Tracking automation. It covers the current-state process map and its bottlenecks, full specifications for each of the three agents, the complete end-to-end data flow with field-level detail, and the recommended build stack with estimated hours. The FullSpec team uses this document to scope, build, and test the automation. The process owner's role is to confirm configuration decisions flagged in the agent specifications before build begins.

01Process snapshot

Step
Name
Description
1
Review Project Board for Due Items
Operations Manager opens Asana each morning and manually scans every active project for milestones due today or overdue. Time cost: 25 min/day.
2
Copy Overdue Milestones to Tracking Sheet
Overdue or at-risk milestones are manually copied into the shared Google Sheet for tracking and reporting. Time cost: 20 min/day.
3
Send Individual Chase Emails to Owners
A personalised email is drafted and sent to each task owner asking for a status update, with context pulled manually from project notes. Time cost: 30 min/day. BOTTLENECK.
4
Log Responses and Update Tracking Sheet
Owner replies are read and manually entered into the tracking spreadsheet against the relevant milestone row. Time cost: 20 min/day.
5
Identify Escalations Needing Manager Attention
The Operations Manager reviews responses and flags milestones that are significantly late or blocked, deciding which items need escalation without a consistent ruleset. Time cost: 15 min/day. BOTTLENECK.
6
Notify Relevant Stakeholders of Escalations
For escalated items, a Slack message or email is sent manually to the appropriate senior stakeholder or department lead. Time cost: 15 min/day.
7
Compile Weekly Status Report
Once per week the Operations Manager consolidates all milestone data from the tracking sheet into a formatted Notion status report. Time cost: 45 min/week. BOTTLENECK.
8
Distribute Status Report to Leadership
The completed report is posted in a Slack channel for leadership and relevant stakeholders to review. Time cost: 10 min/week.
9
Archive Completed Milestones
Milestones marked complete are manually tagged in Asana and removed from the active tracking sheet. Time cost: 10 min/week.
Time cost summary: Steps 1 to 6 run daily (5 days/week), totalling 125 min/day or 625 min/week from those steps alone. Steps 7, 8, and 9 run once per week, adding 65 min/week. Combined manual time per cycle (one full week): approximately 190 min (just over 3 hours for a single daily cycle plus weekly overhead). Across a working week the Operations Manager spends approximately 7 hours on this process. The automation replaces steps 1, 2, 3, 4, 5, 6, 7, and 8 entirely. Step 9 (archive completed milestones) remains a light manual step in scope but is not a primary bottleneck. The single retained human step is reviewing items the Milestone Monitor Agent has flagged as blocked or high-risk before a decision is made.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Milestone Monitor Agent

Runs on a daily schedule at 8:00 am, querying Asana via its REST API for all tasks across the configured project scope whose due date is on or before today and whose completion status is not marked complete. For each overdue item the agent extracts the task name, assignee, project name, original due date, days overdue, and current custom field values. It applies the configured escalation threshold rules, for example a milestone more than three days overdue or with no assignee response within 24 hours of the prior reminder triggers an escalation flag. All results, both flagged and unflagged, are written or updated in the central Google Sheets tracking log. This agent is the upstream dependency for both the Owner Reminder Agent and the Report Builder Agent; neither should fire until this agent has completed its run and confirmed a write to the sheet. Complexity: Moderate.

Trigger
Scheduled cron at 08:00 daily (local timezone of the Ops team). Secondary trigger: Asana task due date reached with no completion status, polled at the same schedule.
Tools
Asana (REST API, task search endpoint), Google Sheets (Sheets API v4, append and update row operations).
Replaces steps
Step 1 (board review), Step 2 (copy to sheet), Step 4 (log responses), Step 5 (identify escalations).
Estimated build
14 hours. Complexity: Moderate.
// Input
Asana project GIDs (array, configured per workspace scope)
Escalation threshold: days_overdue_threshold (integer, default 3)
Escalation threshold: no_response_hours (integer, default 24)
Google Sheets spreadsheet_id, sheet_name (tracking log)

// Asana task fields extracted per overdue item
task.gid, task.name, task.due_on, task.assignee.name,
task.assignee.email, task.projects[].name,
task.custom_fields[status], task.modified_at

// Output (written to Google Sheets tracking log)
row.task_gid           // Asana task GID (primary key for deduplication)
row.task_name          // Milestone display name
row.project_name       // Parent project name
row.owner_name         // Assignee full name
row.owner_email        // Assignee email address
row.due_date           // Original ISO due date (YYYY-MM-DD)
row.days_overdue       // Calculated integer: today minus due_date
row.escalation_flag    // Boolean: true if threshold breached
row.last_reminder_sent // ISO datetime of most recent reminder
row.status             // Current status string from Asana custom field
row.last_updated       // ISO datetime of this agent run
  • Asana API tier required: Asana Business or above to access custom fields and advanced search. Confirm account tier with the process owner before build.
  • Google Sheets must have a named sheet tab (e.g. 'Milestone Tracker') with a header row matching the output field names above. The agent uses task_gid as the deduplication key: if a row for that GID already exists it updates in place rather than appending a duplicate.
  • Escalation thresholds (days_overdue_threshold and no_response_hours) must be confirmed with the Operations Manager before build. These are stored as environment variables in the automation platform credential store so they can be adjusted without a rebuild.
  • Asana project GIDs to monitor must be enumerated explicitly in the configuration. A wildcard 'all projects' scope risks including archived or irrelevant workspaces and should not be used.
  • If Asana milestone naming is inconsistent across projects, the agent will still capture all overdue tasks, but grouping and reporting accuracy will degrade. A data cleanup sprint covering milestone naming conventions is required before go-live.
  • Fallback behaviour: if the Asana API returns a rate-limit error (429), the agent should implement exponential back-off with a maximum of three retries before logging the failure to the error table and sending a Slack alert to the FullSpec monitoring channel.
  • The agent must record a run_completed_at timestamp in a separate 'Agent Run Log' sheet tab after each successful execution. The Owner Reminder Agent polls this timestamp to confirm it is safe to proceed.
Owner Reminder Agent

Fires immediately after the Milestone Monitor Agent confirms its daily write to the tracking sheet. Reads all rows in the tracking log where escalation_flag is false and last_reminder_sent is either null or more than 24 hours ago, then sends a personalised Gmail reminder to each milestone owner. For rows where escalation_flag is true, the agent constructs a structured Slack message and posts it to the configured escalation channel, tagging the relevant stakeholder and including a direct hyperlink to the Asana task. The agent then updates last_reminder_sent in the sheet for each processed row. It does not make any decisions about blocked milestones: it routes according to the flag value written by the Milestone Monitor Agent. Complexity: Moderate.

Trigger
Downstream trigger: fires when the Milestone Monitor Agent run_completed_at timestamp is updated for the current day. Implemented as a polling check on the Agent Run Log sheet tab, running every 5 minutes from 08:00 to 08:30.
Tools
Gmail (Gmail API, users.messages.send with OAuth 2.0), Slack (Web API, chat.postMessage), Asana (task permalink construction only, no write operations), Google Sheets (Sheets API v4, read and update row).
Replaces steps
Step 3 (send chase emails), Step 6 (notify stakeholders of escalations).
Estimated build
12 hours. Complexity: Moderate.
// Input (read from Google Sheets tracking log)
row.task_gid, row.task_name, row.project_name,
row.owner_name, row.owner_email, row.due_date,
row.days_overdue, row.escalation_flag, row.last_reminder_sent,
row.status

// Input (environment config)
gmail_sender_address      // Authenticated sender (e.g. ops@[YourCompany.com])
slack_escalation_channel  // Channel ID for escalation alerts
slack_stakeholder_map     // JSON map: project_name -> Slack user ID to tag
reminder_email_template   // Inline template string with merge fields

// Output: Gmail reminder (non-escalated rows)
to: row.owner_email
subject: 'Action needed: [row.task_name] is [row.days_overdue] days overdue'
body: personalised template merging task_name, project_name, due_date, days_overdue,
      direct Asana task URL (https://app.asana.com/0/{project_gid}/{task_gid})

// Output: Slack escalation post (escalation_flag = true rows)
channel: slack_escalation_channel
text: ':red_circle: Escalation | [task_name] | [project_name] | [days_overdue] days overdue'
blocks: structured message with task link, owner name, days overdue, stakeholder mention

// Output: Google Sheets update
row.last_reminder_sent = ISO datetime of send
row.status updated to 'Reminder Sent' or 'Escalated' accordingly
  • Gmail OAuth 2.0 scope required: gmail.send (https://www.googleapis.com/auth/gmail.send). The authenticated account must be a real mailbox, not an alias, to avoid deliverability issues. Confirm the sender address with the process owner.
  • Slack Web API token scope required: chat:write, chat:write.public. The bot must be invited to the escalation channel before the first run. Confirm the channel ID (not display name) and any additional project-specific channels before build.
  • The slack_stakeholder_map (project name to Slack user ID) is a configurable JSON object stored in the credential store. It must be populated before UAT. If a project name in the sheet has no entry in the map, the agent posts the alert without a user mention and logs a warning rather than failing.
  • Deduplication: the agent must not send a second reminder to the same owner on the same day. The check is last_reminder_sent date part versus today's date. If they match, skip that row.
  • If a task owner's email address in Asana is missing or malformed, the agent logs the row to the error table, skips the send, and includes the task_gid in the daily error digest.
  • Email template merge fields must be finalised with the process owner before build. The template is stored as an environment variable so it can be updated without a code change.
  • Fallback: if the Gmail API returns a quota error (429 or 500), the agent retries after 60 seconds up to three times. If all retries fail, the task_gid is written to the error log and the Slack monitoring alert fires.
Report Builder Agent

Fires every Monday at 07:30, after confirming the Milestone Monitor Agent has completed its first run of the week. Reads the full current state of the Google Sheets tracking log, groups milestones by project and status (on track, at risk, overdue, escalated), and writes a structured, formatted status report to a designated Notion page using the Notion API. The page is either created fresh each week under a parent page or appended as a new child page with the Monday date as the title, depending on the configuration agreed with the process owner. Once the Notion write is confirmed, the agent posts a summary digest to the leadership Slack channel containing the headline counts and a hyperlink to the full Notion page. Complexity: Simple.

Trigger
Scheduled cron at 07:30 every Monday. Conditional gate: checks Agent Run Log sheet for a run_completed_at value on the current Monday before proceeding. Polls every 5 minutes from 07:30 to 08:00 if the Milestone Monitor Agent has not yet completed.
Tools
Google Sheets (Sheets API v4, full sheet read), Notion (Notion API v1, pages.create and blocks.children.append), Slack (Web API, chat.postMessage to leadership channel).
Replaces steps
Step 7 (compile weekly status report), Step 8 (distribute report to leadership).
Estimated build
10 hours. Complexity: Simple.
// Input (read from Google Sheets tracking log, full data pull)
All rows where row.status != 'Complete'
Fields used: task_name, project_name, owner_name, due_date,
             days_overdue, escalation_flag, status, last_reminder_sent

// Input (environment config)
notion_parent_page_id       // ID of the parent Notion page for weekly reports
notion_database_id          // Optional: if reports are stored in a Notion DB
slack_leadership_channel    // Channel ID for leadership digest
report_title_template       // e.g. 'Milestone Status Report - {YYYY-MM-DD}'

// Computed aggregates before write
count_on_track    // rows where days_overdue <= 0 and escalation_flag = false
count_at_risk     // rows where days_overdue in [1,3] and escalation_flag = false
count_overdue     // rows where days_overdue > 0
count_escalated   // rows where escalation_flag = true

// Output: Notion page structure
Page title: report_title_template with Monday date substituted
Heading 1: 'Weekly Milestone Status Report'
Callout block: headline counts (on track, at risk, overdue, escalated)
Heading 2 per project_name group
Table block per group: columns task_name, owner_name, due_date, days_overdue, status
Notion page URL returned after create for use in Slack digest

// Output: Slack digest message
channel: slack_leadership_channel
text: 'Weekly Milestone Report | On track: {n} | At risk: {n} | Overdue: {n} | Escalated: {n}'
blocks: button linking to Notion page URL
  • Notion API integration token requires the integration to be invited to the parent page in the Notion workspace. The parent_page_id must be confirmed before build. Do not use a workspace-root page as the parent without explicit owner approval.
  • Notion API version header must be set to '2022-06-28' or later. Block children for the report table use the Notion table block type (not a simple bulleted list) to ensure readability in the Notion UI.
  • If the Monday 07:30 cron fires and the Milestone Monitor Agent has not yet completed its run by 08:00, the Report Builder Agent should log a warning, skip the report for that week, and post a Slack alert to the FullSpec monitoring channel rather than generating a report from stale data.
  • The Notion page creation is idempotent by design: if a page with the same title already exists under the parent (checked via a Notion search query before creation), the agent appends a timestamp suffix to the title rather than overwriting the existing page.
  • Slack leadership channel ID and the Notion parent page ID are both stored as environment variables. Confirm both values with the process owner during the pre-build configuration session.
  • The agent has no write-back to Asana or Google Sheets. It is read-only on both upstream sources.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow with field names at each agent handoff
// ============================================================
// TRIGGER
// ============================================================
// Automation platform cron fires at 08:00 daily (Monday-Friday)
// Milestone Monitor Agent begins execution

// ============================================================
// AGENT 1: Milestone Monitor Agent
// ============================================================

// Step 1: Query Asana for overdue tasks
GET https://app.asana.com/api/1.0/tasks
  params:
    project: <project_gid>          // iterated per configured project list
    completed_since: 'now'          // filters to incomplete tasks only
    opt_fields: gid, name, due_on, assignee.name, assignee.email,
                projects.name, custom_fields, modified_at
  filter (post-response):
    task.due_on <= today            // only overdue or due-today items
    task.completed == false

// Step 2: Apply escalation threshold rules
for each overdue_task:
  days_overdue = today - task.due_on           // integer
  escalation_flag = (days_overdue >= days_overdue_threshold)  // default: 3
               OR  (hours_since_last_reminder >= no_response_hours)  // default: 24

// Step 3: Deduplicate and write to Google Sheets
// Dedup key: task.gid matched against existing sheet column 'task_gid'
// If row exists: PATCH (update) existing row
// If row is new: POST (append) new row
Google Sheets row written:
  task_gid, task_name, project_name, owner_name, owner_email,
  due_date, days_overdue, escalation_flag,
  last_reminder_sent, status, last_updated

// Step 4: Write run completion timestamp
Agent Run Log sheet: run_completed_at = ISO datetime (UTC)
                     agent_name = 'Milestone Monitor Agent'
                     rows_written = integer count

// ============================================================
// AGENT HANDOFF 1 -> 2
// Owner Reminder Agent polls Agent Run Log for today's run_completed_at
// Condition: run_completed_at date part == today AND agent_name == 'Milestone Monitor Agent'
// Poll interval: every 5 minutes from 08:00 to 08:30
// If not confirmed by 08:30: log error, halt, alert monitoring channel
// ============================================================

// ============================================================
// AGENT 2: Owner Reminder Agent
// ============================================================

// Step 5: Read tracking log -- non-escalated rows needing reminder
Google Sheets read filter:
  escalation_flag == false
  AND (last_reminder_sent IS NULL
       OR last_reminder_sent date part < today)

// Step 6: Send Gmail reminder per qualifying row
Gmail API: users.messages.send
  to:      row.owner_email
  subject: 'Action needed: {row.task_name} is {row.days_overdue} days overdue'
  body:    template merged with task_name, project_name, due_date,
           days_overdue, asana_task_url
  asana_task_url = 'https://app.asana.com/0/{project_gid}/{task_gid}'

// Step 7: Read tracking log -- escalated rows
Google Sheets read filter:
  escalation_flag == true

// Step 8: Post Slack escalation alert per escalated row
Slack API: chat.postMessage
  channel: slack_escalation_channel
  text:    ':red_circle: Escalation | {task_name} | {project_name} | {days_overdue} days overdue'
  blocks:  rich text with owner_name, days_overdue, stakeholder_mention (@user_id from map),
           button linking to asana_task_url

// Step 9: Update Google Sheets rows
For each processed row:
  row.last_reminder_sent = ISO datetime (UTC)
  row.status = 'Reminder Sent'    // non-escalated
            OR 'Escalated'         // escalation_flag = true

// ============================================================
// MONDAY-ONLY BRANCH: AGENT HANDOFF 2 -> 3
// Report Builder Agent cron fires at 07:30 every Monday
// Condition: Agent Run Log has run_completed_at for today (Monday)
//            AND agent_name == 'Milestone Monitor Agent'
// Poll interval: every 5 minutes from 07:30 to 08:00
// If not confirmed by 08:00: skip report, alert monitoring channel
// ============================================================

// ============================================================
// AGENT 3: Report Builder Agent (Monday only)
// ============================================================

// Step 10: Full read of Google Sheets tracking log
Google Sheets read: all rows where status != 'Complete'
Fields consumed: task_name, project_name, owner_name, due_date,
                 days_overdue, escalation_flag, status, last_reminder_sent

// Step 11: Compute aggregates
count_on_track  = rows where days_overdue <= 0 AND escalation_flag = false
count_at_risk   = rows where days_overdue in [1, 3] AND escalation_flag = false
count_overdue   = rows where days_overdue > 0
count_escalated = rows where escalation_flag = true

// Step 12: Create Notion page
Notion API: pages.create
  parent.page_id: notion_parent_page_id
  properties.title: 'Milestone Status Report - {YYYY-MM-DD}'
  // Idempotency check: search existing pages first;
  // if title exists, append timestamp suffix
Notion blocks appended (blocks.children.append):
  heading_1:  'Weekly Milestone Status Report'
  callout:    'On track: {n} | At risk: {n} | Overdue: {n} | Escalated: {n}'
  for each project_name group:
    heading_2: project_name
    table:     columns [task_name, owner_name, due_date, days_overdue, status]
notion_page_url = response.url   // captured for Slack digest

// Step 13: Post Slack leadership digest
Slack API: chat.postMessage
  channel: slack_leadership_channel
  text:    'Weekly Milestone Report | On track: {n} | At risk: {n} | Overdue: {n} | Escalated: {n}'
  blocks:  button with label 'View full report' linking to notion_page_url

// ============================================================
// ERROR HANDLING (all agents)
// ============================================================
// On any unrecoverable API error after max retries:
//   Write to Supabase table: agent_errors
//   Fields: agent_name, task_gid (if applicable), error_code,
//           error_message, timestamp, retry_count
//   Post alert to Slack monitoring channel: #fullspec-alerts
// ============================================================
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform (no specific tool decided at this stage). Structure: one workflow per agent (three workflows total). All API credentials stored in a shared credential store within the platform so they are accessible across all three workflows without duplication. Workflows are named consistently: 'Milestone Monitor Agent', 'Owner Reminder Agent', 'Report Builder Agent'.
Webhook configuration
No inbound webhooks are required for this build. All triggers are schedule-based (cron) or polling-based (Agent Run Log sheet check). If Asana real-time webhooks are introduced in a future iteration, the platform's inbound webhook receiver should be used with HMAC signature validation using the Asana webhook secret header (X-Hook-Signature). For the current build, the 08:00 daily cron poll is sufficient and avoids webhook registration maintenance overhead.
Templating approach
Email body and Slack message templates are stored as environment variables (plain text strings with {merge_field} placeholders). Merge is handled by the orchestration layer's string interpolation at runtime. Notion page structure is defined as a static block schema in the Report Builder Agent workflow, parameterised only on data values. No external templating engine is required. Template strings must be reviewed and approved by the process owner before the Owner Reminder Agent build begins.
Error logging
All unrecoverable errors across all three agents are written to a Supabase table named 'agent_errors' with columns: id (uuid), agent_name (text), task_gid (text, nullable), error_code (integer), error_message (text), timestamp (timestamptz), retry_count (integer), resolved (boolean, default false). On each error write, a Slack alert fires to the designated FullSpec monitoring channel (#fullspec-alerts or equivalent) with the agent name, error code, and timestamp. A daily digest of unresolved errors is posted each morning before the 08:00 cron run.
Testing approach
All three agents are built and tested in sandbox mode first. For Asana and Google Sheets this means using a dedicated test project and a test sheet tab named 'Milestone Tracker TEST' with synthetic overdue milestone rows. Gmail sends during sandbox testing use a single internal test address only. Slack posts during sandbox testing go to a private #automation-test channel rather than production channels. Notion writes during sandbox testing target a dedicated test parent page. Each agent is validated in isolation before end-to-end chained testing begins. See the FullSpec Test and QA Plan for full test case coverage.
Estimated total build time
Milestone Monitor Agent: 14 hours. Owner Reminder Agent: 12 hours. Report Builder Agent: 10 hours. End-to-end integration testing and UAT: 10 hours (included in the 36-hour total from the project snapshot). Total: 36 hours across a 3 to 4 week delivery window.
Pre-build blockers to resolve before any agent build starts: (1) Confirm Asana account tier supports custom fields and advanced search API access. (2) Obtain and validate all OAuth tokens for Gmail, Slack, Google Sheets, and Notion in the target workspace. (3) Confirm escalation thresholds (days_overdue_threshold, no_response_hours) with the Operations Manager. (4) Confirm the Asana project GID list for the monitored scope. (5) Confirm the Slack escalation channel ID, leadership channel ID, and stakeholder-to-project Slack user ID map. (6) Confirm the Notion parent page ID and whether reports are stored as child pages or in a Notion database. (7) Complete the Asana milestone naming consistency audit. None of these can be defaulted or assumed. Reach out to the process owner via support@gofullspec.com to schedule the pre-build configuration session.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Project Milestone Tracking.

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