Maverix Studio
Technical Specification
Brabys
Data Pipeline
Informix → PostgreSQL · AI Verification Staging Layer
ClientBrabys Directory Services
AuthorMaverix Studio
DateMarch 2026
Versionv0.2 Draft
Section 01

Overview

Ingest Brabys subscriber data from Informix file dumps into a PostgreSQL staging database as raw tables — exact mirrors of the Informix schema. AI agents (Claire, General & Scrub) access data through a read layer (PostgreSQL views) that handles phone reconstruction and code resolution at query time. Verified changes are written back to Carl in the same file-dump format for re-import into Informix production.

13
Source Tables
21k
Sample Subscribers
3
AI Agents
6
Output Files

Architecture

INBOUND CARL / INFORMIX Carl's AIX server FILE DUMP ZIP · pipe-delimited IMPORT Parse & load raw POSTGRESQL Staging DB raw tables only PROCESSING READ LAYER Views / API GENERAL Enrich & research CLAIRE Voice verification SCRUB Validate updates OUTBOUND WRITE LAYER Encode to Informix OUTPUT FILES Pipe-delimited export CARL / INFORMIX Production load
Important

This is NOT a live database connection. All data flows via file dumps — inbound and outbound. The staging DB holds raw Informix tables only — no flattening or transformation. All phone reconstruction and address resolution happens in the read layer (PostgreSQL views) at query time.

Section 02

Source Data Format

Informix 11 running on AIX 5.3. Carl provides file dumps as a ZIP archive containing 13 pipe-delimited text files — one file per table, one row per record.

Source Tables

Each dump contains the following 13 tables:

TablePurpose
cosdienrCore subscriber record — address codes, flags, primary key
cosbusnmBusiness name(s) per subscriber
cosphonePhone numbers (multiple per subscriber)
cosinfodEmail addresses and website URLs
cospoboxPO Box address
cosaltadFree-text alternate address (non-standard)
cosstrdcStreet code lookup
cossubrbSuburb code lookup
costownTown/city lookup (includes dialing code)
cosblddcBuilding code lookup
cosregionRegion lookup
coscountryCountry lookup
cosplotPlot type lookup

Sample Data Profile

Sample dump covering the Pretoria / 012 dialing area:

21,872
Subscribers
36,250
Phone Entries
25,313
Email / Web Entries
13
Tables per Dump

File Format

de_alpha_key|de_town_code|de_suburb_code|de_street_code|de_street_no|...
1234567|012|4501|88210|42|...
Section 03

Key Field Reference — cosdienr

The cosdienr table is the core subscriber record. Every other table joins back to it via de_alpha_key. Understanding these fields is essential for import, filtering, and write-back.

Core Fields

FieldTypeDescription
de_alpha_keyINTEGERPrimary key, immutable — links all tables
de_town_codeFKForeign key → costown (tw_town_code)
de_suburb_codeFKForeign key → cossubrb (sb_suburb_code)
de_street_codeFKForeign key → cosstrdc (st_street_code)
de_build_codeFKForeign key → cosblddc (building name)
de_street_noVARCHARStreet number
de_build_noVARCHARBuilding / unit number
de_floorVARCHARFloor (for buildings)
de_alt_bld_noVARCHARNon-standard building number suffix
de_corner_ofVARCHARCorner description (free text)
de_amend_dateDATELast amended date

Filter & Status Flags

FieldTypeDescription
de_print_codeCHARPrint filter flag — NULL / " " = printable
de_z_entryCHARExclude flag — NULL / " " = include
de_specificCHARSpecific-type entry flag
de_unlistedCHARUnlisted flag
de_sch_do_not_callCHARDo Not Call flag — MUST be checked before any outbound call
Exclusion Rules

Records are excluded from the working set if: de_print_code is not null/space, de_z_entry is set, de_specific is set, or bn_bus_name starts with ,, or contains DELETE. These mirror the filters Carl uses in sql.sql.

Section 04

Phone Number Reconstruction

Phone numbers are stored as INTEGER in cosphone (ph_phone), which means leading zeros are stripped on write. Reconstruction logic is derived from address.4gl.

Phone Type Indicators

Two fields determine the phone type: ph_phone_ind and ph_fax_ind.

Phone Types (ph_phone_ind)

CodeTypeReconstruction
"Y"Phone (landline)Prepend tw_dialing_code from costown
"C"Cell / MobileSouth Africa: prepend "0"; otherwise as-is
"V"VOIPSouth Africa: prepend "0"; otherwise as-is
"S"SharecallSouth Africa: prepend "0"; otherwise as-is
"T"Toll-freeSouth Africa: prepend "0"; otherwise as-is

Fax Types (ph_fax_ind)

CodeTypeReconstruction
"Y"FaxPrepend tw_dialing_code
"C"CellFaxSouth Africa: prepend "0"
"V"VOIPFaxSouth Africa: prepend "0"
"X"ShareCallFaxSouth Africa: prepend "0"

Dialing Code Join Path

cosphone.ph_alpha_key = cosdienr.de_alpha_key
  → cosdienr.de_town_code = costown.tw_town_code
  → use costown.tw_dialing_code

Examples

Landline: ph_phone = 123456, tw_dialing_code = "012" → reconstructed = "012123456"

Mobile: ph_phone = 821234567, ph_phone_ind = "C", country = SOUTH AFRICA → "0821234567"

Call Eligibility

Only attempt calls where ph_phone_ind = "Y" or "C" AND de_sch_do_not_call is NULL or " ". This is a hard rule — no exceptions.

Section 05

Address Resolution

Street addresses are assembled from code lookups as defined in address.4gl (get_street_address function). Alternate free-text addresses take priority when present.

Resolution Order

  1. Check cosaltad for alt_address — if present, use free-text address directly
  2. Otherwise resolve codes from cosdienr:
    • de_build_codecosblddc (building name)
    • de_build_no + de_alt_bld_no → building / unit number
    • de_floor → floor
    • de_street_no → street number
    • de_street_codecosstrdc (street name)
    • de_suburb_codecossubrb (suburb name)
    • de_town_codecostown (town name)
Write-Back Rule

Address changes must be written back as code updates to cosdienr, NOT free text. If the new address cannot be resolved to valid Informix codes (cosstrdc / cossubrb / costown), it MUST go to cosaltad instead.

Section 06

PostgreSQL Staging Schema

The staging database keeps raw tables only — exact mirrors of the Informix schema. No flattening or reconstruction is stored in the database. All code resolution and phone reconstruction happens in the read layer (Section 7) at query time.

Raw Tables — Subscriber Data

Exact column-for-column mirrors of the Informix originals. One raw_ table per source file:

TablePurposeJoin Key
raw_cosdienrCore subscriber record — address codes, flags, PKde_alpha_key (PK)
raw_cosbusnmBusiness name(s) per subscriberbn_alpha_keyde_alpha_key
raw_cosphonePhone numbers (multiple per subscriber)ph_alpha_keyde_alpha_key
raw_cosinfodEmail addresses and website URLsinf_alpha_keyde_alpha_key
raw_cospoboxPO Box addresspo_alpha_keyde_alpha_key
raw_cosaltadFree-text alternate address (non-standard)alt_alpha_keyde_alpha_key

Raw Tables — Lookups (Read-Only)

Reference tables used by the read layer to resolve codes into human-readable values. Never modified by agents:

TablePurposePrimary Key
raw_costownTown / city lookup (includes dialing code)tw_town_code
raw_cossubrbSuburb code lookupsb_suburb_code
raw_cosstrdcStreet code lookupst_street_code
raw_cosblddcBuilding code lookupbd_build_code
raw_cosregionRegion lookuprg_region_code
raw_coscountryCountry lookupcy_country
raw_cosplotPlot type lookuppl_plot_code

Operational Tables

import_batches

Tracks each ZIP import for audit and rollback.

CREATE TABLE import_batches (
  id            SERIAL PRIMARY KEY,
  filename      TEXT,
  imported_at   TIMESTAMPTZ,
  record_count  INTEGER,
  notes         TEXT
);

verification_results

AI verification output per subscriber — stores what changed, agent confidence, and current record status.

CREATE TABLE verification_results (
  alpha_key              INTEGER PRIMARY KEY,
  verified_at            TIMESTAMPTZ,
  verified_business_name TEXT,
  verified_phone         TEXT,
  verified_email         TEXT,
  verified_website       TEXT,
  confidence_score       NUMERIC,
  changes_made           JSONB,          -- what changed
  call_transcript        TEXT,
  agent_notes            TEXT,
  record_status          TEXT DEFAULT 'pending'
                         -- pending | in_progress | verified | failed | dnc
);

tracking

Handshake table written back to Carl — tells Informix which records are ready to load.

CREATE TABLE tracking (
  alpha_key    INTEGER,
  verified_at  TIMESTAMPTZ
);
Design Principle

Raw tables are the single source of truth. The staging DB never stores derived or flattened data. This eliminates sync bugs between raw and flattened representations and makes write-back trivial — output the raw tables directly, applying only the agent's changes.

Section 07

Read Layer — Agent Interface

Agents never query raw tables directly. All reads go through PostgreSQL views or a Node.js API layer. The read layer handles phone reconstruction, address code resolution, business name selection, email/web extraction, and DNC flagging — all computed at query time from raw data.

What the Read Layer Resolves

subscriber_view

Primary PostgreSQL view — the single interface agents use to read subscriber data:

CREATE VIEW subscriber_view AS
SELECT
  d.de_alpha_key                    AS alpha_key,
  b.bn_bus_name                     AS business_name,

  -- Phone reconstruction
  CASE
    WHEN p.ph_phone_ind = 'Y'
      THEN tw.tw_dialing_code || p.ph_phone::text
    WHEN p.ph_phone_ind IN ('C','V','S','T')
     AND tw.tw_country = 'SOUTH AFRICA'
      THEN '0' || p.ph_phone::text
    ELSE p.ph_phone::text
  END                               AS phone,
  p.ph_phone_ind                    AS phone_type,
  p.ph_fax_ind                      AS fax_type,

  -- Address (code-resolved)
  COALESCE(a.alt_address,
    CONCAT_WS(', ',
      NULLIF(bd.bd_build_name,''),
      NULLIF(CONCAT_WS(' ', d.de_build_no, d.de_street_no),''),
      st.st_street_name,
      sb.sb_suburb_name,
      tw.tw_town_name
    )
  )                                 AS address,

  -- Email & website
  ie.inf_data                       AS email,
  iw.inf_data                       AS website,

  -- DNC flag
  CASE
    WHEN d.de_sch_do_not_call IS NOT NULL
     AND d.de_sch_do_not_call <> ' '
      THEN true ELSE false
  END                               AS do_not_call,

  -- Status
  COALESCE(v.record_status, 'pending') AS record_status,
  tw.tw_dialing_code                AS dialing_code

FROM raw_cosdienr d
LEFT JOIN raw_cosbusnm  b  ON b.bn_alpha_key   = d.de_alpha_key
                           AND b.bn_line_no     = 1
LEFT JOIN raw_cosphone  p  ON p.ph_alpha_key   = d.de_alpha_key
LEFT JOIN raw_costown   tw ON tw.tw_town_code  = d.de_town_code
LEFT JOIN raw_cossubrb  sb ON sb.sb_suburb_code= d.de_suburb_code
LEFT JOIN raw_cosstrdc  st ON st.st_street_code= d.de_street_code
LEFT JOIN raw_cosblddc  bd ON bd.bd_build_code = d.de_build_code
LEFT JOIN raw_cosinfod  ie ON ie.inf_alpha_key = d.de_alpha_key
                           AND ie.inf_type     = 'E'
LEFT JOIN raw_cosinfod  iw ON iw.inf_alpha_key = d.de_alpha_key
                           AND iw.inf_type     = 'W'
LEFT JOIN raw_cosaltad  a  ON a.alt_alpha_key  = d.de_alpha_key
LEFT JOIN verification_results v ON v.alpha_key = d.de_alpha_key;

Write Path

Agents write proposed changes back via the write layer only (Section 11). They never INSERT or UPDATE raw tables directly. All proposed changes are stored in human-readable form in verification_results, then encoded by the write layer at export time.

Key Rule

Agents see human-readable data (dialable phone numbers, resolved addresses, business names). They propose changes in the same human-readable form. The write layer is responsible for encoding those changes back into Informix-compatible raw format.

Section 08

Import Process

A step-by-step pipeline that takes Carl's ZIP dump from raw files into the PostgreSQL staging database. No flattening or transformation — raw tables mirror Informix exactly.

Pipeline Steps

  1. Receive ZIP from Carl → extract to staging folder
  2. Run import script — loads all .txt files into corresponding raw_* tables (pipe-delimited, handle nulls)
  3. Load all 13 tables:
    • 6 subscriber tables → raw_cosdienr, raw_cosbusnm, raw_cosphone, raw_cosinfod, raw_cospobox, raw_cosaltad
    • 7 lookup tables → raw_costown, raw_cossubrb, raw_cosstrdc, raw_cosblddc, raw_cosregion, raw_coscountry, raw_cosplot
  4. Initialise verification_results — insert a row per de_alpha_key with record_status = 'pending'
  5. Log import batch to import_batches

Exclusion Filter

Applied in the read layer (subscriber_view), not during import. This ensures raw data is preserved exactly as received. The filter mirrors Carl's sql.sql WHERE clause:

WHERE (de_print_code IS NULL OR de_print_code = ' ')
  AND (de_z_entry    IS NULL OR de_z_entry    = ' ')
  AND (de_specific   IS NULL OR de_specific   = ' ')
  AND  bn_bus_name NOT LIKE ',,%%'
  AND  bn_bus_name NOT LIKE '%%DELETE%%'
  AND  bn_line_no  = 1

Note: Import loads ALL records into raw tables unconditionally. Exclusion filtering is applied only when agents query through the read layer. This preserves the complete Informix dataset for accurate write-back generation.

Section 09

Batch Ingestion Strategy

Carl will send updated file dumps periodically. Each dump contains a full export for the relevant dialing area — not a delta. The ingestion strategy must handle new records, updated records, unchanged records, and de-listed records without destroying verification history.

Strategy: Upsert + Change Detection

  1. STEP 1 — Load to temp tables
    Load all incoming .txt files into temporary tables (temp_cosdienr, temp_cosbusnm, temp_cosphone, temp_cosinfod, etc.) in the same pipe-delimited format. Do not touch raw_ tables yet.
  2. STEP 2 — Compare and classify (by alpha_key)
    Compare temp tables vs raw_ tables. Classify every record:
    • NEW: alpha_key exists in temp but not in raw_ → INSERT to raw_, set record_status = 'pending'
    • UPDATED: alpha_key exists in both, but one or more field values differ → UPDATE raw_, reset record_status = 'pending' (re-verification required)
    • UNCHANGED: alpha_key exists in both, all fields identical → no-op, preserve existing verification_results
    • INACTIVE: alpha_key exists in raw_ but NOT in temp → set record_status = 'inactive' (de-listed from Informix, skip verification)
  3. STEP 3 — Pending write-back guard
    Before overwriting any UPDATED record, check the tracking table. If a write-back for this alpha_key exists in tracking but has not yet been acknowledged by Carl (i.e. no confirmation that Carl has loaded it), flag the conflict and do NOT overwrite. Log it in import_batches.notes for manual review.
  4. STEP 4 — Log the batch
    Write a record to import_batches with counts: new_count, updated_count, unchanged_count, inactive_count, conflict_count. Drop temp tables.

Re-verification Queue

After each import batch, only records with record_status = 'pending' enter the verification queue. UNCHANGED records are never re-queued. This keeps agent workload proportional to what actually changed in Informix — not the full dataset.

import_batches Table (Updated Schema)

id              SERIAL PRIMARY KEY
filename        TEXT
imported_at     TIMESTAMPTZ
new_count       INTEGER
updated_count   INTEGER
unchanged_count INTEGER
inactive_count  INTEGER
conflict_count  INTEGER
notes           TEXT

tracking Table (Clarification)

Dual purpose: The tracking table serves two roles — (1) tells Carl which alpha_keys are ready to load back into Informix production, and (2) acts as a write-back guard during subsequent imports. A record in tracking means we have pending changes for that alpha_key that Carl may not have loaded yet.

Section 10

AI Verification Flow

Two AI agents work in sequence: General enriches subscriber data online, then Claire places a voice call to verify. Agents read from subscriber_view (read layer) and write results to verification_results.

Verification Pipeline

  1. General agent queries subscriber_view where record_status = 'pending' AND do_not_call = false
  2. General enriches online — website check, address confirmation, business status
  3. Claire (ElevenLabs voice, ZA female) calls reconstructed phone number from the view
  4. Call outcome written to verification_results
  5. record_status updated: verified / failed / dnc
  6. On completion: alpha_key + timestamp written to tracking table

Call Eligibility Check

ALWAYS enforced before dialling — all conditions must be true:

ConditionCheck
do_not_callMust be false (read layer derives from de_sch_do_not_call)
record_statusMust be 'pending'
phoneMust be NOT NULL (reconstructed by read layer)
Critical

The do_not_call flag in subscriber_view is derived directly from raw_cosdienr.de_sch_do_not_call at query time — there is no stale copy. The read layer is the single source of truth for call eligibility.

Section 11

Scrub — Validation Layer

Scrub runs after Claire completes her call and captures updates. It is a deterministic rules engine — not an AI model — that validates every field Claire modified before it reaches the write layer. Nothing goes to the write layer without passing through Scrub.

Validation Rules

FieldRuleFail Action
Phone Valid SA format: 10 digits, starts with 0 (mobile) or dialcode+number (landline) RED — do not write back
Email Valid format: contains @ and domain, no spaces RED — do not write back
Website Has valid TLD, reachable (optional check) AMBER — write back with warning
Address Street / suburb / town resolvable to Informix codes If unresolvable → route to cosaltad instead
Business Name No leading ,,, no DELETE, not blank RED — do not write back

Outcome Logic

OutcomeConditionAction
GREEN All fields pass Send to write layer
AMBER Soft issues only Send to write layer with flags noted in verification_results
RED Any hard failures Block write-back, set record_status = 'failed', log reason in verification_results.agent_notes
Cross-Reference

Full Scrub validation rules are documented in the Project Outline (Section 7).

Section 12

Write-Back Format

Agents propose changes in human-readable form. The write layer encodes those changes back into Informix-compatible raw format. Unchanged records are output directly from raw_* tables with no transformation. tracking.txt is always included.

Write-Layer Encoding

Data TypeEncoding RuleTarget Table
Phone Strip leading zero or dialing code, store as INTEGER, set ph_phone_ind accordingly raw_cosphone
Address Lookup in raw_cosstrdc / raw_cossubrb / raw_costown; if no matching code found, write to raw_cosaltad as free text raw_cosdienr or raw_cosaltad
Email / Website Update raw_cosinfod directly raw_cosinfod
Business Name Update raw_cosbusnm directly raw_cosbusnm

Output Files

FileContents
tracking.txtAlways included — all alpha_keys verified with timestamps
cosdienr.txtOutput from raw_cosdienr; includes address code changes if any
cosphone.txtOutput from raw_cosphone; phone stored as integer with updated ph_phone_ind
cosinfod.txtOutput from raw_cosinfod; email / website updates
cosbusnm.txtOutput from raw_cosbusnm; business name updates
cosaltad.txtOutput from raw_cosaltad; non-standard addresses that couldn't be resolved to codes

Format Rules

Key Principle

We never invent Informix codes. If an AI-verified address doesn't match existing raw_cosstrdc / raw_cossubrb / raw_costown codes, it goes to raw_cosaltad as free text. Carl decides whether to create new codes on the Informix side.

Section 13

Open Items / TBD

Outstanding decisions and dependencies that affect the pipeline build. Items are tracked here until resolved with Brabys IT or confirmed internally.

ItemStatusNotes
B4 — Merge approval trigger TBD Manual vs scheduled — pending Brabys decision
Phone dialcode table completeness TBD Sample data is 012 (Pretoria) only — need full costown for all regions
Write-back file delivery method Confirmed Same folder-drop method as inbound
SIP credentials for Claire Expected Brabys IT to provide 3CX SIP account (this week)
Full Informix table list Confirmed 13 tables confirmed with Carl
Appendix A

Informix Entity-Relationship Diagram

All 13 Informix tables with foreign-key relationships. cosdienr is the central hub — every subscriber table joins via alpha_key, and address/phone resolution flows through the lookup tables on the right.

SUBSCRIBER TABLES HUB LOOKUP TABLES cosbusnm bn_alpha_key FK bn_bus_name bn_line_no bn_sort_name cosphone ph_alpha_key FK ph_phone INTEGER ph_phone_ind ph_fax_ind ph_sequence cosinfod inf_alpha_key FK inf_data inf_type inf_sequence cospobox po_alpha_key FK po_box_no po_box_type cosaltad alt_alpha_key FK alt_address alt_line_no cosdienr de_alpha_key PK de_town_code FK de_suburb_code FK de_street_code FK de_build_code FK de_street_no de_build_no de_floor de_print_code de_z_entry de_specific de_sch_do_not_call de_amend_date costown tw_town_code PK tw_town_name tw_dialing_code tw_country FK tw_region_code FK cossubrb sb_suburb_code PK sb_suburb_name sb_town_code FK sb_postal_code cosstrdc st_street_code PK st_street_name st_suburb_code FK cosblddc bd_build_code PK bd_build_name bd_street_code FK cosregion rg_region_code PK rg_region_name coscountry cy_country PK cy_country_name cosplot pl_plot_code PK pl_plot_name pl_plot_type bn_alpha_key ph_alpha_key inf_alpha_key po_alpha_key alt_alpha_key de_town_code de_suburb_code de_street_code de_build_code tw_region_code tw_country sb_town_code LEGEND Hub table (cosdienr) Subscriber / Lookup table Foreign key relationship PK = Primary Key FK = Foreign Key