Back to Regulatory Change Monitoring

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

Regulatory Change Monitoring

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

This document is the authoritative technical reference for the FullSpec team building the Regulatory Change Monitoring 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. Everything needed to begin implementation without further discovery is contained here. Where a decision has not yet been locked down, a constraint or confirmation requirement is called out explicitly so the FullSpec team can resolve it before touching the relevant agent.

01Process snapshot

Step
Name
Description
01
Compile Source List for the Week
Compliance Coordinator manually assembles the list of regulatory websites, newsletters, and RSS feeds to check. Lives in personal notes or a spreadsheet; rarely shared with the wider team. Time cost: 20 min/week.
02
Visit Each Regulatory Source
Coordinator opens each website or inbox one by one, scanning for new publications, amendments, or consultation papers since the last check. No automated diff or change detection exists. Time cost: 90 min/week. BOTTLENECK.
03
Read and Assess Relevance
Each potentially relevant item is opened and read to judge whether it applies to the business based on industry, jurisdiction, and subject matter. Marginal items are frequently skipped under time pressure. Time cost: 60 min/week. BOTTLENECK.
04
Summarise Relevant Changes
Items assessed as relevant are summarised in plain language for the broader team: what changed, effective date, and likely business impact. Written manually per item in Notion. Time cost: 45 min/week.
05
Log the Change in the Register
Each confirmed change is manually entered into the Airtable compliance register with source, dates, topic, and required action. Often deferred and done in batches, creating lag in the register. Time cost: 30 min/week.
06
Route to Responsible Owner
Coordinator decides who needs to act and sends a summary by Gmail or direct message. No standard routing logic exists; ownership is frequently ambiguous. Time cost: 20 min/week.
07
Follow Up on Overdue Actions
One or two weeks later the coordinator manually checks which action items have been acknowledged and chases owners who have not responded, relying on memory or a personal task list. Time cost: 25 min/week.
08
Prepare Monthly Compliance Digest
At month end the coordinator reconstructs all flagged items into a summary report for management by scrolling back through notes, emails, and the register. Time cost: 50 min/cycle (monthly). BOTTLENECK.
Time cost summary: Total manual time per weekly cycle is 290 minutes (4 hours 50 minutes) across steps 01 to 07, plus 50 minutes for the monthly digest (step 08), which annualises to approximately 340 minutes of effort in a digest week. The automation replaces steps 01 through 08 in full for the repetitive data-gathering and communication work. One human review checkpoint is retained for borderline items flagged by the Relevance and Summary Agent; the Compliance Coordinator's remaining weekly effort is estimated at 60 minutes for digest review only. Steps 02 and 03 (source visiting and relevance reading) account for 150 of the 290 weekly minutes and are the primary bottleneck pair driving the 5.5 hours/week saving.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Source Monitor Agent

Runs on a daily schedule to fetch new content from all configured regulatory feeds and websites via Feedly, deduplicates items already seen in the Airtable intake table, and passes only net-new content downstream to the Relevance and Summary Agent. This agent eliminates the need for any manual website visiting and is the entry point for the entire automation pipeline. It must be stable before any downstream agent build begins.

Trigger
Scheduled daily at a configurable time (default 07:00 local). Also activates immediately on a Feedly webhook event if real-time push is enabled on the Feedly Pro+ plan.
Tools
Feedly (feed aggregation and article retrieval), Airtable (deduplication lookup and intake record creation)
Replaces steps
Step 01 (Compile Source List) and Step 02 (Visit Each Regulatory Source)
Estimated build
10 hours, Moderate complexity
// Input
feedly.stream_id       : string   // Feedly collection ID for regulatory sources
feedly.newer_than      : unix_ms  // Timestamp of last successful poll (stored in Airtable config row)
feedly.count           : integer  // Max items per poll, default 50

// Deduplication lookup (Airtable)
airtable.table         : 'Regulatory_Intake'
airtable.filter        : {feedly_article_id} = '{article.id}'

// Output (passed to Relevance and Summary Agent)
article.id             : string   // Feedly article fingerprint
article.title          : string
article.source_name    : string   // Feed label as configured in Feedly collection
article.source_url     : string
article.published_at   : ISO8601
article.full_text      : string   // Extracted body text, max 4000 chars
article.canonical_url  : string
article.is_duplicate   : boolean  // true = skip; false = pass downstream
  • Feedly plan must be Pro+ or Business to enable the Collections API with full-text extraction. The Teams plan (from $18/month) covers this; confirm the account tier before build.
  • The Feedly collection named 'Regulatory Sources' must be pre-populated with all 42 confirmed sources before agent testing begins. The source list was signed off on Apr 15.
  • Deduplication key is the Feedly article ID stored in the Airtable Regulatory_Intake table field feedly_article_id. On first run, seed the table with the last 30 days of article IDs to prevent a flood of historic items being treated as new.
  • If a source is not available via RSS and requires a custom web watcher, that watcher must be configured and tested separately before the source is added to the Feedly collection or an equivalent polling step. Flag any login-gated or JavaScript-rendered sources to FullSpec before build; they require a separate scraping approach.
  • The agent must write a last_polled_at timestamp to the Airtable config row after each successful run so the next poll uses the correct newer_than value.
  • If the Feedly API returns a 429 rate-limit response, the agent must back off for 60 seconds and retry once before writing an error log entry and halting that run gracefully.
  • Items with is_duplicate = true must be silently archived in the automation log; no downstream call should be made for them.
Relevance and Summary Agent

Receives each net-new article from the Source Monitor Agent and applies an AI classification step to score relevance against the business's configured jurisdiction profile and topic tag list. Items that score below the configured threshold are discarded silently. Items above the threshold receive a two-to-three sentence plain-language summary, an extracted or inferred effective date, and a suggested internal owner derived from the topic-to-owner routing table stored in Airtable. Borderline items (within a configurable band around the threshold) are held and routed to the Compliance Coordinator for a 5-minute manual review before any further action is taken.

Trigger
Activated for each new article record passed by the Source Monitor Agent where is_duplicate = false.
Tools
Notion (classification prompt context and team knowledge base reference), Airtable (routing table lookup and final record write)
Replaces steps
Step 03 (Read and Assess Relevance), Step 04 (Summarise Relevant Changes), and Step 05 (Log the Change in the Register)
Estimated build
16 hours, Complex
// Input (from Source Monitor Agent)
article.id             : string
article.title          : string
article.source_name    : string
article.published_at   : ISO8601
article.full_text      : string
article.canonical_url  : string

// Classification prompt context (retrieved from Notion page 'Relevance Config')
config.jurisdictions   : string[] // e.g. ['AU', 'NZ', 'UK']
config.topic_tags      : string[] // e.g. ['AML', 'Privacy', 'Financial Services', 'OH&S']
config.keywords        : string[] // Boosted signal terms
config.threshold_pass  : float    // default 0.70
config.threshold_review: float    // default 0.50 (below this = discard)

// Routing table lookup (Airtable table: 'Routing_Rules')
routing.topic_tag      : string   // matched tag
routing.owner_name     : string
routing.owner_slack_id : string
routing.owner_email    : string

// Output (written to Airtable table: 'Regulatory_Register')
register.article_id    : string
register.title         : string
register.source_name   : string
register.source_url    : string
register.published_at  : ISO8601
register.effective_date: ISO8601 | null  // extracted by AI; null if not found
register.topic_tag     : string
register.relevance_score: float
register.summary       : string   // 2-3 sentence plain-language summary
register.suggested_owner: string
register.owner_slack_id: string
register.status        : enum('pending_review', 'approved', 'discarded')
register.flagged_borderline: boolean

// On borderline (threshold_review <= score < threshold_pass)
// Pause pipeline; send borderline alert to Compliance Coordinator Slack DM
// Await manual status update in Airtable (approved | discarded) before Alert Agent fires
  • The AI classification model must use a structured prompt that includes jurisdiction list, topic tags, and keywords retrieved fresh from the Notion 'Relevance Config' page at each invocation, not cached at build time. This allows the business to update rules without a code change.
  • Relevance score must be returned as a float between 0.0 and 1.0 with the reasoning in a separate field for logging. The reasoning field is written to Airtable but not surfaced to end users.
  • Default threshold_pass is 0.70 and threshold_review is 0.50. These must be stored as editable Airtable config fields, not hard-coded, so the business can tune them post-launch.
  • Effective date extraction should use a secondary AI prompt pass on the article text. If no explicit effective date is found, the field is set to null and the summary must note 'Effective date not specified'.
  • The topic-to-owner routing table in Airtable (table: Routing_Rules) must have at least one catch-all row where topic_tag = 'Unclassified' with a default owner set to the Compliance Coordinator. Build must not proceed to Alert Agent testing until this row is present.
  • If the classification API call fails (timeout or error), the item must be written to the register with status = 'pending_review' and flagged_borderline = true so it surfaces for human review rather than being silently dropped.
  • Confirm with the FullSpec team whether the Notion integration uses an internal integration token or OAuth; the workspace admin must authorise the Notion connection before this agent can read the Relevance Config page.
Alert and Digest Agent

Handles all outbound communication for the pipeline. It fires immediately after a new record with status = 'approved' is written to the Airtable Regulatory_Register, posting a structured Slack alert tagged to the responsible owner in the correct topic channel. On a Monday morning schedule it assembles the previous week's approved items into a digest email and sends it via Gmail to the configured management recipients. It also monitors the register for action items that remain unacknowledged beyond a configurable number of days and sends overdue follow-up Slack messages to the responsible owner.

Trigger
Immediate: Airtable record written with status = 'approved' (via Airtable automation webhook or polling interval of 5 minutes). Scheduled: Monday 08:00 local for weekly digest. Scheduled: Daily 09:00 check for overdue action items (acknowledged_at is null and created_at is older than overdue_days config value).
Tools
Slack (real-time owner alerts and overdue reminders), Gmail (weekly digest email), Airtable (record reads and acknowledged_at field update)
Replaces steps
Step 06 (Route to Responsible Owner), Step 07 (Follow Up on Overdue Actions), and Step 08 (Prepare Monthly Compliance Digest)
Estimated build
10 hours, Moderate
// Input (real-time alert path)
register.record_id     : string
register.title         : string
register.summary       : string
register.effective_date: ISO8601 | null
register.topic_tag     : string
register.relevance_score: float
register.source_url    : string
register.owner_slack_id: string
register.suggested_owner: string

// Input (weekly digest path)
// Query Airtable Regulatory_Register WHERE created_at >= last Monday 00:00
// AND status = 'approved'
// ORDER BY relevance_score DESC

// Input (overdue follow-up path)
// Query Airtable Regulatory_Register WHERE acknowledged_at IS NULL
// AND status = 'approved'
// AND created_at < NOW() - overdue_days
config.overdue_days    : integer  // default 7; stored in Airtable config table

// Output (real-time alert)
slack.channel          : string   // resolved from topic_tag via Routing_Rules
slack.mention          : string   // owner_slack_id formatted as <@U...>
slack.blocks           : object[] // Slack Block Kit message with title, summary, effective date, source link, and acknowledge button

// Output (weekly digest)
gmail.to               : string[] // management recipient list from Airtable config
gmail.subject          : string   // 'Weekly Regulatory Digest – {ISO week label}'
gmail.html_body        : string   // Templated HTML listing each item: title, summary, owner, effective date, register link

// Output (overdue follow-up)
slack.dm_to            : string   // owner_slack_id
slack.message          : string   // Plain text overdue reminder with item title and register deep link

// On acknowledge (Slack interactive button callback)
airtable.update        : {acknowledged_at: ISO8601, acknowledged_by: slack_user_id}
  • Slack Block Kit must be used for real-time alerts, not plain text, so the acknowledge button can be rendered inline. The Slack app must have the chat:write and chat:write.public OAuth scopes and the interactivity webhook URL must be registered before end-to-end testing.
  • The acknowledge button callback must write acknowledged_at and acknowledged_by back to the Airtable record. This is the mechanism that prevents overdue follow-ups for items that have been seen.
  • Gmail sending must use a dedicated service account or an OAuth2 refresh token associated with the compliance team's Gmail workspace, not a personal account. Confirm the Gmail workspace tier supports third-party OAuth app connections before build.
  • The weekly digest email template must be built as a reusable HTML template with inline CSS only (no external stylesheets) to ensure compatibility across mail clients. Sections: header with week label, ranked item list, footer with register link and unsubscribe note.
  • The digest recipient list must be stored in an Airtable config table field (digest_recipients), not hard-coded, so it can be updated without a deployment change.
  • The overdue_days config value defaults to 7 and must also be stored in the Airtable config table. Build must include a guard to ensure the overdue check never sends more than one follow-up per item per 24-hour window to avoid spamming owners.
  • If a Slack alert fails to post (channel not found, user not in workspace), the error must be logged to the error log table and a fallback email must be sent to the owner's address stored in Routing_Rules.
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
// ============================================================
// REGULATORY CHANGE MONITORING: END-TO-END DATA FLOW
// ============================================================

// TRIGGER
// Scheduled daily at 07:00 local (configurable)
// OR Feedly webhook fires on new item (Pro+ plan required)
TRIGGER -> Source Monitor Agent
  inputs:
    feedly.stream_id        = 'user/{feedly_user_id}/category/Regulatory_Sources'
    feedly.newer_than       = airtable.config['last_polled_at']  // unix_ms
    feedly.count            = 50

// ============================================================
// AGENT HANDOFF 1: Source Monitor Agent -> Deduplication
// ============================================================
  for each article in feedly.response.items:
    article.id              -> airtable.lookup('Regulatory_Intake', feedly_article_id)
    IF match found:
      article.is_duplicate  = true  // skip; no downstream call
      log('duplicate_skipped', article.id)
    ELSE:
      airtable.insert('Regulatory_Intake', {
        feedly_article_id   : article.id,
        title               : article.title,
        source_name         : article.source_name,
        source_url          : article.canonical_url,
        published_at        : article.published_at,
        full_text           : article.full_text[0:4000],
        intake_status       : 'queued'
      })
      article.is_duplicate  = false
      -> pass to Relevance and Summary Agent

  airtable.config['last_polled_at'] = NOW()  // update poll timestamp

// ============================================================
// AGENT HANDOFF 2: Source Monitor Agent -> Relevance and Summary Agent
// ============================================================
Relevance and Summary Agent receives:
  article.id, article.title, article.source_name,
  article.published_at, article.full_text, article.canonical_url

  // Step A: Fetch classification config from Notion
  notion.page('Relevance Config') ->
    config.jurisdictions    : string[]
    config.topic_tags       : string[]
    config.keywords         : string[]
    config.threshold_pass   : float    // from Airtable config table
    config.threshold_review : float    // from Airtable config table

  // Step B: AI classification call
  prompt_input:
    system  : 'You are a regulatory compliance classifier. Score relevance 0.0-1.0.'
    context : config.jurisdictions, config.topic_tags, config.keywords
    content : article.title + article.full_text
  prompt_output:
    relevance_score         : float
    matched_topic_tag       : string
    reasoning               : string   // logged, not surfaced

  // Step C: Route on score
  IF relevance_score >= threshold_pass:
    classification_status   = 'approved'
  ELSE IF relevance_score >= threshold_review:
    classification_status   = 'borderline'  // hold for human review
  ELSE:
    classification_status   = 'discarded'
    log('item_discarded', article.id, relevance_score)
    STOP

  // Step D: Summary generation (approved + borderline only)
  prompt_input:
    system  : 'Write a 2-3 sentence plain-language summary of this regulatory change.'
    content : article.full_text
  prompt_output:
    summary                 : string
    effective_date          : ISO8601 | null

  // Step E: Routing table lookup
  airtable.lookup('Routing_Rules', {topic_tag: matched_topic_tag}) ->
    routing.owner_name      : string
    routing.owner_slack_id  : string
    routing.owner_email     : string
  FALLBACK: topic_tag = 'Unclassified' -> Compliance Coordinator

  // Step F: Write to Regulatory_Register
  airtable.insert('Regulatory_Register', {
    article_id              : article.id,
    title                   : article.title,
    source_name             : article.source_name,
    source_url              : article.canonical_url,
    published_at            : article.published_at,
    effective_date          : effective_date,
    topic_tag               : matched_topic_tag,
    relevance_score         : relevance_score,
    reasoning               : reasoning,
    summary                 : summary,
    suggested_owner         : routing.owner_name,
    owner_slack_id          : routing.owner_slack_id,
    owner_email             : routing.owner_email,
    status                  : classification_status,
    flagged_borderline      : (classification_status == 'borderline'),
    acknowledged_at         : null,
    acknowledged_by         : null,
    created_at              : NOW()
  })

  IF classification_status == 'borderline':
    slack.dm(owner_slack_id = COMPLIANCE_COORDINATOR_SLACK_ID,
             message = 'Borderline item requires review: {title} | {airtable_record_link}')
    PAUSE pipeline for this item; await manual status update in Airtable
  ELSE IF classification_status == 'approved':
    -> pass record_id to Alert and Digest Agent (immediate path)

// ============================================================
// AGENT HANDOFF 3: Relevance and Summary Agent -> Alert and Digest Agent
// ============================================================
Alert and Digest Agent: immediate path
  trigger          : Airtable record status updated to 'approved'
  inputs           : register.record_id -> fetch full record

  // Post Slack Block Kit alert
  slack.post({
    channel        : resolve_channel(matched_topic_tag),  // from Routing_Rules
    blocks: [
      header       : 'New Regulatory Change: {title}',
      section      : summary,
      fields       : [effective_date, source_name, topic_tag, relevance_score],
      actions      : [button('Acknowledge', value=record_id)]
    ]
  })

Alert and Digest Agent: weekly digest path
  trigger          : Monday 08:00 scheduled run
  query            : Airtable Regulatory_Register WHERE
                     created_at >= last_monday AND status = 'approved'
                     ORDER BY relevance_score DESC
  gmail.send({
    to             : airtable.config['digest_recipients'],
    subject        : 'Weekly Regulatory Digest – {ISO_week_label}',
    html_body      : render_digest_template(items)
  })

Alert and Digest Agent: overdue follow-up path
  trigger          : Daily 09:00 scheduled run
  query            : Airtable Regulatory_Register WHERE
                     acknowledged_at IS NULL AND status = 'approved'
                     AND created_at < NOW() - config.overdue_days
  for each record:
    IF last_overdue_sent_at < NOW() - 24h OR last_overdue_sent_at IS NULL:
      slack.dm(owner_slack_id, 'Overdue action: {title} | {airtable_record_link}')
      airtable.update(record_id, {last_overdue_sent_at: NOW()})

// ============================================================
// SLACK INTERACTIVE CALLBACK (acknowledge button)
// ============================================================
  slack.callback.payload.actions[0].value = record_id
  slack.callback.user.id                  = slack_user_id
  -> airtable.update('Regulatory_Register', record_id, {
       acknowledged_at  : NOW(),
       acknowledged_by  : slack_user_id
     })

// ============================================================
// ERROR LOGGING (all agents)
// ============================================================
  ON any agent error:
    airtable.insert('Error_Log', {
      agent_name       : string,
      error_type       : string,
      error_message    : string,
      article_id       : string | null,
      timestamp        : ISO8601
    })
    IF severity == 'critical':
      slack.dm(FULLSPEC_ALERT_CHANNEL, 'Automation error: {error_message}')
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool with native HTTP request nodes, scheduled trigger support, and a shared credential store. One workflow per agent is the recommended structure: Workflow 1 = Source Monitor Agent, Workflow 2 = Relevance and Summary Agent, Workflow 3 = Alert and Digest Agent (with sub-paths for real-time alert, weekly digest, and overdue follow-up). All API credentials (Feedly API key, Airtable personal access token, Slack bot token, Gmail OAuth2 refresh token, Notion internal integration token) are stored in the platform's shared credential store, not in workflow node parameters, so they can be rotated without touching workflow logic.
Webhook configuration
Two inbound webhooks are required. First: a Slack interactivity webhook to receive acknowledge button callbacks from Slack Block Kit messages; this URL must be registered in the Slack app manifest under 'Interactivity and Shortcuts > Request URL'. Second: an optional Feedly webhook (Pro+ plan) for real-time article push to Workflow 1, supplementing the daily scheduled poll. Both webhook URLs are generated by the orchestration platform and must be stored in the project environment config before agent testing begins.
Templating approach
AI classification and summary prompts are constructed dynamically at runtime by fetching the Relevance Config Notion page and the Routing_Rules Airtable table. Prompt text is not hard-coded in workflow nodes. The weekly digest Gmail body uses a reusable HTML template with handlebars-style variable substitution ({{item.title}}, {{item.summary}}, {{item.effective_date}}, {{item.source_url}}) rendered by the orchestration layer's templating node. The Slack Block Kit message structure is defined as a JSON template within the workflow and populated at send time.
Error logging
All agent errors are written to an Airtable table named Error_Log with fields: agent_name (string), error_type (string: api_failure | timeout | parse_error | routing_miss), error_message (long text), article_id (string, nullable), and timestamp (ISO8601). Critical errors (Feedly API down, Airtable write failure, Slack post failure) trigger a Slack DM to the designated FullSpec monitoring channel immediately. Non-critical errors (single article parse failure, null effective date) are logged only. The Error_Log table is reviewed weekly during the first month post-launch and monthly thereafter.
Testing approach
All agents are built and tested against sandbox environments first. Feedly: use a test collection with 5 known sources. Airtable: use a duplicate base named '[YourCompany.com] Regulatory Monitor SANDBOX' with identical schema. Slack: post to a private #automation-testing channel. Gmail: send digest to a test distribution list. Each agent is tested in isolation before end-to-end pipeline testing. End-to-end testing runs for two weeks in parallel with the manual process, per the delivery plan, before go-live on May 13.
Estimated total build time
Source Monitor Agent: 10 hours. Relevance and Summary Agent: 16 hours. Alert and Digest Agent: 10 hours. Subtotal (agent builds): 36 hours. End-to-end integration testing and error-handling hardening: 2 hours additional (included in the 38-hour total build effort). Grand total: 38 hours.
Before any build work begins, the following must be confirmed: (1) Feedly account tier is Pro+ or Business to enable the Collections API. (2) The 42 regulatory sources are loaded into the 'Regulatory Sources' Feedly collection. (3) The Airtable workspace has tables created for Regulatory_Intake, Regulatory_Register, Routing_Rules, and Error_Log with the schema defined in this document. (4) Notion internal integration token is authorised for the 'Relevance Config' page. (5) Slack app is created with chat:write, chat:write.public, and im:write scopes and the interactivity webhook URL is registered. (6) Gmail OAuth2 credentials are issued for the compliance team's workspace account. Raise any blockers to support@gofullspec.com before the Week 2 agent build window opens.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Regulatory Change Monitoring.

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