Back to Data Quality 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

Data Quality Monitoring

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

This document gives the FullSpec build team everything needed to implement the three-agent Data Quality Monitoring automation. It covers the current-state process 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 retains ownership of complex record fixes (Step 8) and threshold sign-off; the FullSpec team builds, wires, and tests everything else. Read every implementation note in Section 02 before starting any integration work, as several pre-build confirmations must happen before code is written.

01Process snapshot

Step
Name
Description
1
Schedule and Initiate Manual Check
IT analyst opens spreadsheet checklist and informally decides which sources to audit. Decision relies on memory or recent complaints. Time cost: 15 min/run.
2
Export Data from Source System
Analyst pulls a fresh export from PostgreSQL via CSV download or manual SQL query, repeated for each source. Time cost: 20 min/run.
3
Run Checks in Spreadsheet
Exported data is pasted into a Google Sheets template with formulas checking blanks, duplicates, and value ranges. Analyst scrolls highlighted cells manually. BOTTLENECK. Time cost: 35 min/run.
4
Document Issues Found
Each issue is copied into a log tab with source, field name, error type, and affected row count. Time cost: 20 min/run.
5
Classify Issue Severity
Analyst manually assigns critical, moderate, or low priority based on record volume and downstream report impact. No consistent scoring logic. BOTTLENECK. Time cost: 15 min/run.
6
Create Jira Ticket for Each Issue
Analyst manually creates a Jira ticket per issue, copying field name, error type, affected row count, and fix description. Time cost: 25 min/run.
7
Notify Stakeholders via Slack
Analyst posts a freeform message to the IT Slack channel. Format varies; detail is inconsistent. Time cost: 10 min/run.
8
Apply Manual Fixes to Records
Analyst writes and runs correction queries against PostgreSQL for straightforward issues. RETAINED step, not automated. Time cost: 30 min/run.
9
Update Log and Close Tickets
Analyst updates Jira ticket status and marks the tracking row resolved. Often delayed under time pressure. Time cost: 10 min/run.
10
Produce Weekly Summary Report
Analyst compiles totals from tracking sheet into a Slack or email summary for the IT manager. Time cost: 20 min/run.
Time cost summary: Total manual time per cycle is 200 minutes (3 hours 20 min). At one run per day across a five-day week, that totals approximately 7 hours of manual effort per week. Steps 1, 2, 3, 4, and 5 are replaced by the Data Check Agent. Steps 6, 7, and 9 are replaced by the Issue Routing and Notification Agent. Step 10 is replaced by the Reporting and Metrics Agent. Step 8 (Apply Manual Fixes) is retained by the IT Analyst.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Data Check Agent

Connects to PostgreSQL and Airtable on a daily schedule, or immediately on receipt of a data import event. Runs the full configured quality ruleset against the latest records in all watched tables and fields. Evaluates each record for null values, format mismatches, out-of-range figures, and duplicate keys. Assigns a severity score (Critical, High, Moderate, Low) to every issue found based on the pre-agreed scoring thresholds. Produces a structured issue list and passes it downstream to the Issue Routing and Notification Agent. Replaces the four most time-consuming manual steps in the current process and eliminates the scheduling dependency entirely.

Trigger
Daily cron schedule (configurable, default 06:00 UTC) OR webhook payload received from a watched data source on import completion.
Tools
PostgreSQL (read-only service account), Airtable (Issues Log base), Google Sheets (ruleset config reference).
Replaces steps
1 (Schedule and Initiate), 2 (Export Data), 3 (Run Checks in Spreadsheet), 4 (Document Issues), 5 (Classify Severity).
Estimated build
20 hours. Complexity: Moderate.
// Input
trigger_type: 'schedule' | 'import_event'
import_event.source_table: string          // e.g. 'customers', 'orders'
import_event.row_count: integer
import_event.timestamp: ISO8601
ruleset_config: GoogleSheets.tab('Rules')  // field, rule_type, threshold, severity_weight

// Processing
PostgreSQL.query(watched_tables, watched_fields) -> raw_records[]
Airtable.select(base='IssueLog', view='ActiveSources') -> source_metadata[]
rules_engine.evaluate(raw_records, ruleset_config) -> flagged_records[]
severity_scorer.score(flagged_records, severity_weights) -> scored_issues[]

// Output
issue_list[]: {
  source_table: string,
  field_name: string,
  error_type: 'null' | 'format' | 'duplicate' | 'out_of_range' | 'referential',
  affected_row_count: integer,
  severity: 'Critical' | 'High' | 'Moderate' | 'Low',
  severity_score: float,
  sample_record_id: string,
  detected_at: ISO8601
}
  • PostgreSQL connection must use a dedicated read-only service account. Confirm schema-level SELECT grants cover all watched tables before build starts. Do not use an admin credential.
  • Database firewall rules must allow outbound TCP connections from the automation platform IP range. This must be confirmed with the infrastructure owner before scoping the build timeline.
  • The quality ruleset must be fully documented in the Google Sheets config tab before the agent is built. Each rule must include the target table, field name, rule type, threshold value, and severity weight. Vague descriptions cannot be translated into executable logic.
  • Airtable base must be on the Team plan or above to support API writes at the required volume (up to 284 runs/month). Confirm Airtable tier before build starts.
  • The rules engine must be stateless per run. Each execution reads the current ruleset from Google Sheets at runtime so rule changes take effect without a redeploy.
  • Deduplication: if the same field-and-error combination is detected on two consecutive runs with no resolution, the agent must not create a duplicate issue row in Airtable. Match on (source_table, field_name, error_type) and update the existing row's last_seen_at timestamp instead.
  • Fallback: if the PostgreSQL query times out or returns zero rows when rows are expected, the agent must log a 'source_unreachable' error to the Airtable log and skip scoring. Do not pass an empty issue list to the downstream agent as a false all-clear.
Issue Routing and Notification Agent

Receives the completed issue list from the Data Check Agent at the end of each check run. Creates Jira tickets for all Critical and High severity issues, pre-filled with the source table, field name, error type, affected row count, severity score, and a direct link to the Airtable log row. Writes every issue, regardless of severity, to the Airtable Issues Log as a new or updated row. Posts a formatted summary message to the designated IT Slack channel listing severity counts, the top three Critical issues by score, and links to all Jira tickets created in the run. Replaces the three manual communication and logging steps that previously consumed 45 minutes per run.

Trigger
Receives structured issue_list[] payload from the Data Check Agent on completion of each check run.
Tools
Jira (ticket creation via REST API), Slack (incoming webhook to IT channel), Airtable (Issues Log base write).
Replaces steps
6 (Create Jira Ticket), 7 (Notify Stakeholders via Slack), 9 (Update Log and Close Tickets).
Estimated build
14 hours. Complexity: Moderate.
// Input
issue_list[]: {                            // received from Data Check Agent output
  source_table, field_name, error_type,
  affected_row_count, severity, severity_score,
  sample_record_id, detected_at
}
jira_project_key: string                  // e.g. 'DQ' — must be confirmed pre-build
slack_channel_id: string                  // e.g. 'C08XXXXX' — IT channel webhook
airtable_base_id: string                  // Issues Log base ID

// Processing
filter(issue_list, severity IN ['Critical','High']) -> ticket_candidates[]
Jira.createIssue(ticket_candidates) -> jira_tickets[]: { ticket_id, ticket_url }
Airtable.upsert(issue_list + jira_ticket_urls) -> airtable_rows[]
Slack.postMessage(channel_id, format_summary(issue_list, jira_tickets)) -> message_ts

// Output
jira_tickets[]: {
  ticket_id: string,
  ticket_url: string,
  linked_issue: { source_table, field_name, severity }
}
airtable_rows[]: {
  airtable_record_id: string,
  status: 'Open' | 'Updated',
  jira_ticket_url: string | null
}
slack_message_ts: string                  // for thread replies if needed
  • Jira project key and issue type must be confirmed before build. The target project (e.g. 'DQ') must exist and the service account must have Create Issue permission in that project. Do not hardcode a project key; read it from the credential store.
  • Jira API authentication uses OAuth 2.0 (3LO) for Jira Cloud or a personal access token for Jira Server/Data Center. Confirm which deployment the client runs before selecting the auth method.
  • Slack integration uses an incoming webhook URL scoped to a single channel. Confirm the IT channel ID and that the webhook is installed in the correct workspace. Do not use a bot token with broad scopes unless the webhook approach is unavailable.
  • Airtable upsert logic: match on (source_table, field_name, error_type, detected_date). If a matching open row exists, update last_seen_at and increment occurrence_count. If no match, insert a new row. Never delete rows; the log is the permanent audit trail.
  • Slack message template must be agreed with the IT Manager before build. At minimum it must include: total issue count, count by severity, top three Critical issues with field name and affected row count, and a list of Jira ticket links.
  • Fallback: if Jira ticket creation fails for any issue, write the failure to the Airtable row (jira_status: 'creation_failed') and include the failed items in the Slack message so the analyst can create manually.
  • If the issue_list received is empty after a successful run (no issues found), the agent must still post a brief Slack confirmation ('No issues detected in this run') and log the clean run to Airtable. Silence must not be the only signal for a clean result.
Reporting and Metrics Agent

Fires on a weekly schedule after the final daily check of the week completes. Queries the Airtable Issues Log to aggregate the past seven days of data, computing total issues detected, issues by severity, issues resolved within SLA, and open carry-over count. Pushes all metrics to the Datadog dashboard via the Datadog API, updating the agreed metric series so the IT Manager has a live trend view without any manual compilation. Replaces the 20-minute weekly summary report that the analyst previously assembled by hand from spreadsheet totals.

Trigger
Weekly cron schedule, firing after the last daily check run of the week completes (e.g. Friday 07:00 UTC, configurable).
Tools
Airtable (Issues Log base read), Datadog (metrics API write).
Replaces steps
10 (Produce Weekly Summary Report).
Estimated build
8 hours. Complexity: Simple.
// Input
airtable_base_id: string                  // Issues Log base
date_range: { start: ISO8601, end: ISO8601 } // past 7 days
datadog_api_key: string                   // from credential store
datadog_app_key: string                   // from credential store
metric_namespace: string                  // e.g. 'dq.weekly'

// Processing
Airtable.select(base=airtable_base_id, filter='detected_at >= start AND detected_at <= end')
  -> weekly_issues[]: { severity, status, resolution_time_hours, source_table }
aggregate(weekly_issues) -> metrics: {
  total_issues_detected: integer,
  critical_count: integer,
  high_count: integer,
  moderate_count: integer,
  low_count: integer,
  resolved_count: integer,
  open_count: integer,
  avg_resolution_hours: float,
  issues_by_source: { [source_table]: integer }
}
Datadog.submitMetrics(metric_namespace, metrics, timestamp=week_end)

// Output
datadog_series_ids[]: string[]            // confirmation of accepted metric series
summary_log_row: {                        // written back to Airtable for audit
  week_ending: ISO8601,
  total_detected: integer,
  total_resolved: integer,
  pushed_to_datadog: boolean
}
  • Datadog API key and app key must be stored in the shared credential store and never hardcoded. The API key requires the 'metrics_write' scope. Confirm the Datadog plan supports custom metrics at the required series count before build.
  • The Datadog dashboard must be created and named before the agent is built so the agent can target the correct dashboard ID. Dashboard layout and widget definitions should be agreed with the IT Manager during the discovery phase.
  • Metric namespace (e.g. 'dq.weekly.critical_count') must be agreed and documented before build. Changing the namespace after go-live breaks historical chart continuity in Datadog.
  • Airtable query for the weekly window must use the detected_at field, not a creation timestamp, to ensure re-opened or updated issues within the window are counted correctly.
  • Fallback: if Datadog submission fails, write the aggregated metrics to a holding row in Airtable marked 'pending_push' and retry on the next scheduled trigger. Alert the FullSpec monitoring channel if three consecutive weekly pushes fail.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full data journey: trigger to output, with agent handoff points and exact field names at each stage.
// ============================================================
// TRIGGER LAYER
// ============================================================
TRIGGER: cron('0 6 * * *')                // daily at 06:00 UTC
      OR webhook.receive(event='import_complete', source_table, row_count, timestamp)

trigger_context = {
  trigger_type: 'schedule' | 'import_event',
  source_table: string | null,
  row_count: integer | null,
  fired_at: ISO8601
}

// ============================================================
// AGENT 1: DATA CHECK AGENT
// ============================================================

// Step 1: Load ruleset from Google Sheets
GoogleSheets.read(sheet='Rules', tab='ActiveRules')
  -> ruleset[]: { field_name, rule_type, threshold, severity_weight, source_table }

// Step 2: Query data sources
PostgreSQL.query(
  tables: ruleset[].source_table (distinct),
  fields: ruleset[].field_name (per table),
  limit: configurable (default 50000 rows per table)
) -> raw_records[]: { record_id, source_table, field_name, field_value }

Airtable.select(
  base: airtable_base_id,
  view: 'ActiveSources'
) -> source_metadata[]: { source_name, last_checked_at, expected_row_floor }

// Step 3: Run rules engine
rules_engine.evaluate(raw_records, ruleset)
  -> flagged_records[]: {
       record_id, source_table, field_name,
       rule_violated: rule_type,
       observed_value: string,
       expected_constraint: string
     }

// Step 4: Score severity
severity_scorer.score(flagged_records, severity_weights)
  -> scored_issues[]: {
       source_table, field_name, error_type,
       affected_row_count, severity, severity_score,
       sample_record_id, detected_at: ISO8601
     }

// -- HANDOFF: Data Check Agent -> Issue Routing Agent --
// Payload: scored_issues[]
// Condition: always fires on run completion (even if scored_issues is empty)

// ============================================================
// AGENT 2: ISSUE ROUTING AND NOTIFICATION AGENT
// ============================================================

// Step 5: Write all issues to Airtable Issues Log
Airtable.upsert(
  base: airtable_base_id,
  table: 'IssuesLog',
  match_fields: ['source_table','field_name','error_type','detected_date'],
  payload: scored_issues[]
) -> airtable_rows[]: { airtable_record_id, status: 'Open'|'Updated', jira_ticket_url }

// Step 6: Create Jira tickets for Critical and High issues
ticket_candidates = filter(scored_issues, severity IN ['Critical','High'])
Jira.createIssue(
  project_key: jira_project_key,
  issue_type: 'Bug',
  summary: '[DQ] {source_table}.{field_name} — {error_type} ({affected_row_count} rows)',
  description: { field_name, error_type, affected_row_count, severity_score,
                  airtable_row_url, sample_record_id, detected_at },
  priority: severity -> Jira priority map { Critical:'Highest', High:'High' },
  labels: ['data-quality', source_table]
) -> jira_tickets[]: { ticket_id, ticket_url, linked_issue }

// Step 7: Update Airtable rows with Jira ticket URLs
Airtable.update(
  records: airtable_rows (Critical + High only),
  fields: { jira_ticket_url, jira_status: 'Created' }
)

// Step 8: Post Slack summary to IT channel
Slack.postMessage(
  channel: slack_channel_id,
  blocks: [
    header: 'Data Quality Check — {fired_at date}',
    counts: { critical, high, moderate, low },
    top_critical: scored_issues.filter(Critical).sortDesc(severity_score).slice(0,3)
               .map({ field_name, source_table, affected_row_count, ticket_url }),
    footer: 'Full log: {airtable_log_url}'
  ]
) -> slack_message_ts: string

// -- HANDOFF: Issue Routing Agent ends daily cycle --
// Retained step: IT Analyst reviews Jira tickets, applies manual fixes to PostgreSQL

// ============================================================
// AGENT 3: REPORTING AND METRICS AGENT (weekly trigger only)
// ============================================================

TRIGGER: cron('0 7 * * 5')               // Friday at 07:00 UTC (after daily run)

// Step 9: Aggregate weekly data from Airtable
Airtable.select(
  base: airtable_base_id,
  table: 'IssuesLog',
  filter: 'detected_at >= week_start AND detected_at <= week_end'
) -> weekly_issues[]: { severity, status, resolution_time_hours, source_table }

metrics = {
  'dq.weekly.total_detected': count(weekly_issues),
  'dq.weekly.critical_count': count(severity='Critical'),
  'dq.weekly.high_count':     count(severity='High'),
  'dq.weekly.moderate_count': count(severity='Moderate'),
  'dq.weekly.low_count':      count(severity='Low'),
  'dq.weekly.resolved_count': count(status='Resolved'),
  'dq.weekly.open_count':     count(status='Open'),
  'dq.weekly.avg_resolution_hours': avg(resolution_time_hours)
}

// Step 10: Push metrics to Datadog
Datadog.submitMetrics(
  api_key: datadog_api_key,
  series: metrics.map({ metric: key, points: [[week_end_unix, value]], type: 'gauge' })
) -> datadog_series_ids[]

// Step 11: Write audit row to Airtable
Airtable.insert(
  table: 'WeeklyReports',
  row: { week_ending, total_detected, total_resolved, pushed_to_datadog: true }
)
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool configured with one workflow per agent (three workflows total): 'DQ - Data Check Agent', 'DQ - Issue Routing and Notification Agent', 'DQ - Reporting and Metrics Agent'. All credentials are stored in a shared credential store and referenced by name, never hardcoded. Workflows are version-controlled and exportable as JSON definitions.
Webhook configuration
A single inbound webhook endpoint is provisioned on the automation platform to receive import-complete events from connected source systems. The endpoint must be protected with a shared secret header (X-Webhook-Secret). The Data Check Agent webhook listener validates the secret before processing. The daily cron trigger and the webhook trigger share the same workflow entry point, resolved by a trigger_type branch at the start of the flow.
Templating approach
Slack message blocks are built using a JSON template stored as a workflow variable, with dynamic fields (date, severity counts, ticket links) injected at runtime via expression mapping. Jira issue summaries and descriptions use string templates defined in the credential store so they can be updated without redeploying the workflow. The quality ruleset is read from Google Sheets at runtime on every execution, making rule changes immediate with no deployment step required.
Error logging
All agent-level errors (PostgreSQL timeout, Jira creation failure, Datadog push failure, Airtable write failure) are caught by a top-level error handler in each workflow and written to a dedicated 'ErrorLog' table in the Airtable base. Each error row captures: workflow_name, error_type, error_message, affected_record_id (where available), and occurred_at timestamp. An error count threshold (3 consecutive failures on any single workflow) triggers a Slack alert to the FullSpec monitoring channel and an email to support@gofullspec.com.
Testing approach
All three agents are built and validated against a sandbox PostgreSQL schema containing a copy of production table structures with anonymised or synthetic data. Airtable sandbox base is a duplicate of the production base with a separate base ID. Jira sandbox uses a separate project key (e.g. 'DQT'). Slack sandbox uses a private test channel. No workflow touches production credentials until end-to-end QA is signed off. A one-week parallel run is required before go-live: the automation runs alongside the manual process and results are compared daily with the IT Analyst.
Estimated total build time
Data Check Agent: 20 hours. Issue Routing and Notification Agent: 14 hours. Reporting and Metrics Agent: 8 hours. End-to-end integration testing, parallel run monitoring, and handover documentation: 6 hours. Total: 48 hours.
Pre-build blockers: four items must be resolved before any development begins. (1) Signed-off quality ruleset in the Google Sheets config tab, with field-level thresholds and severity weights. (2) Confirmed PostgreSQL read-only service account credentials and firewall approval for outbound connections from the automation platform. (3) Confirmed Airtable plan tier (Team or above) and base IDs for IssuesLog and WeeklyReports tables. (4) Confirmed Jira project key, issue type, and service account permissions. Raise any blockers with the FullSpec team at support@gofullspec.com before the build phase start date.
Item
Tool / Credential
Required Scope or Permission
Status
PostgreSQL connection
PostgreSQL service account
SELECT on all watched schemas and tables
Confirm before build
Airtable Issues Log
Airtable API key or OAuth token
Read and write on IssuesLog and WeeklyReports tables
Confirm before build
Jira ticket creation
Jira OAuth 2.0 or PAT
Create Issue, Edit Issue in target project
Confirm before build
Slack IT channel webhook
Slack incoming webhook URL
Post messages to #it-alerts (or equivalent)
Confirm before build
Datadog metrics write
Datadog API key + app key
metrics_write, dashboard_read
Confirm before build
Google Sheets ruleset
Google service account
Spreadsheets.readonly on ruleset file
Confirm before build
Automation platform credential store
Platform-level secret management
All above credentials stored by reference name
FullSpec configures
Error log Airtable table
Airtable API key (same as Issues Log)
Write on ErrorLog table in same base
FullSpec creates
Questions or blockers during the build phase should be directed to the FullSpec team at support@gofullspec.com. Include the process name (Data Quality Monitoring) and the specific agent or integration in the subject line to ensure fast routing.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Data Quality 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