Skip to content

Deterministic Process Automation Under Strong Security Constraints

A local, rule-based robot that turns daily PDF reports, a business web portal without public API and hand-kept spreadsheets into reviewed Outlook drafts for four recurring processes — with no language model in the loop.

Context

A client operating under strong security constraints manages several hundred sites and runs four recurring back-office processes every day: maintaining field equipment, handling recurring spurious equipment events, writing intervention reports for its customers, and checking that each site’s periodic health check has arrived. The inputs are scattered across three systems with very different access paths: PDF reports delivered by e-mail, a business web portal with no public API, and Excel workbooks maintained by hand over many years.

The goal: automate these processes on an office workstation while keeping a human operator as the final authority on every customer message.

Design constraints

  • No language model at run time. Every decision comes from explicit rules and every sentence from a template. This makes the robot’s output reproducible: the same inputs always produce the same drafts, so a new version can be compared word for word against production, and any change traces back to a rule or a data source. No customer data goes to an external model service. A vision-language model was used once, offline, during design, to map recorded operator screen sessions; it is not part of the deployed system.
  • Drafts, not sends. The robot only creates Outlook drafts; the operator reviews and sends them. A global kill switch forces draft mode regardless of per-process settings.
  • Local execution, no new infrastructure. Python on a Windows workstation, scheduled tasks, a shared NAS folder for state and configuration.
  • No plaintext secrets. Credentials and tokens live in the Windows Credential Manager (DPAPI); configuration is YAML the operator can edit without a rebuild.

Architecture

The four processes

Equipment maintenance. Fault lines parsed from the daily PDF reports are matched against each device’s battery and repair history. An ordered decision tree (first match wins) chooses between ignoring, waiting, battery replacement, device replacement or radio-range testing, and drafts a technician work order; each device on a multi-device order keeps its own decision.

Recurring spurious events. Event counts per site and per device over 24 hours are compared with thresholds to detect recurring spurious events. Portal data (an incident confirmed as real, the system being switched off minutes after a burst) overrides the counts; the outcome is an advisory e-mail to the customer, a work order, or nothing.

Intervention reports. For each on-site intervention, the robot rebuilds a single timeline from the portal’s event log, call log and field-staff records, handles events that straddle midnight, and drafts a customer report with the media files attached.

Periodic health checks. Sites whose periodic health check is missing are identified, the status of each connectivity channel is checked on the portal, and a customer e-mail, a technician work order or an internal note is drafted.

Composing an e-mail: from parameters to a draft

Each draft is the end of a long chain of explicit decisions. Counted in the source code (October 2026), the four rule engines have 53 terminal outcomes (each place where a rule returns a decision), mapped onto 37 decision types and 13 draft builders.

The customer advisory e-mail of the spurious-events process shows the scale. A site reaches it only after up to 14 ordered tests in the rule engine (16 terminal outcomes for this process alone). The composer then reads 21 inputs — 17 fields of the site’s decision record and customer record, the recipient list, the attachments, and two learned resources (zone names from the portal and a spelling dictionary learned from the operator’s own history workbook). Between the decision and the final text there are 108 conditional branch points (if statements, conditional expressions and boolean operands, counted on the syntax tree of the composer and the ten helpers it calls, before the HTML pass).

The wording space of this single e-mail: 2 subject forms, 3 greeting forms, 2 opening-sentence forms, 7 reachable cause wordings (two of them with or without a reference to attached media), 2 requested actions, and an optional device list of 2 to 6 lines. Every threshold above lives in the YAML configuration, not in the code.

ParameterSourceEffect on the e-mail
Event count over 24 hdaily reportsfigure in the opening sentence; ≥ 30 adds an urgent prefix to the subject
Rule that firedrule engineselects the cause wording; gates the handling-error and site-in-activity checks
Devices involved (cleaned, de-duplicated)daily reportsone named device, or a generic phrase plus a list of up to 6 lines ordered by count
Device code per report linedaily reportsappended in parentheses to each listed device, only when known
Zone name of the main deviceportalreplaces the raw report label when available
Learned spellingsoperator’s history workbookrestores accents and casing of device names
Title and contact-name cellcustomer recordone of 3 greeting forms; several people, a role or an organisation name fall back to the generic form
Company namecustomer recordlegal form removed, re-cased with acronyms kept, used in subject and body
Site type, learned opening hours, interventions in the windowtracking workbook, portal journalenable or suppress the site-in-activity wording
System switched on/off around the burstportal journalhandling-error wording (2 variants), overriding every other cause
Site flagscustomer recordchoice between the 2 requested actions
Attachments presentportal mediareference to the attachments, or nothing
Recipient addressescustomer recordevery address found; otherwise an internal fallback mailbox

The other drafts draw on the same shared French-language helpers: the preposition before a site name agrees with its first noun (6 forms, from chez to à l’ and aux); report titles agree in gender and number; dates are written in full French form; a “Contact” heading becomes plural when several contacts are listed; and an 8-entry map turns technical equipment jargon into customer vocabulary.

Engineering for reliability

  • Idempotence. A daily ledger keyed on site, message type and subject hash prevents duplicate drafts; a cross-day register prevents re-issuing a work order before the previous intervention has happened. Missed days are caught up automatically.
  • Concurrency on a shared NAS. Exclusive lock files (O_CREAT|O_EXCL) with a time-to-live for orphans; two workstations publish heartbeats, and the secondary takes over only when the primary has been silent for two minutes.
  • Safe spreadsheet writes. Writes go through a temporary file and an atomic replace, with retries when the workbook is open by a user; inserted rows shift footer formulas so totals stay correct.
  • Network robustness. Graph calls handle token refresh, HTTP 429 Retry-After and 5xx back-off.
  • Explicit failure. A crash wrapper with no dependency on the main package sends a redacted alert and a dead-man ping even when imports fail; a sentinel raises an alert when the portal returns no data at all instead of reporting success.
  • Shadow benchmarking. A sandbox build replays a day in read-only mode and diffs its drafts against production. The acceptance metric is the share of drafts the operator sends without changing a single word.

Results

  • 1,766 automated tests in the repository (October 2026), built on mock mail and portal adapters, with live-system tests marked separately.
  • An audit of the corpus of received reports showed that 9.5 % of event lines were being silently discarded, because event codes and labels are concatenated in the extracted PDF text; the parser was rewritten to recognise the code forms actually emitted.
  • Caching the fault-history workbook (1.49 MB, 15,533 rows) cut the equipment maintenance process on a replay of real reports from 161.1 s to 23.3 s; workbook opens fell from 26 to 3.
  • First clean shadow comparison against production: 14 drafts on each side, 14 in common, 0 missed, 0 added.

Stack

Python 3.11 · Microsoft Graph · MSAL · httpx · Playwright · pypdf · openpyxl · pydantic · keyring · pytest · PyInstaller