A global automotive supplier was buying supplier risk intelligence and getting alerts it could not close. The data landed in a warehouse, the decisions happened in meetings, and nothing connected the two. We built the layer in between.
Two warehouse views ingested nightly, de-duplicated and normalised into one working register.
The five-business-day screening deadline on a Critical alert is enforced by the system, not by a reminder.
Machine-owned and human-owned columns separated at schema level, in both directions.
Seven screening outcomes, five case statuses, three governance routes and six governance outcomes, enforced rather than filed.
A global automotive supplier operating a supply chain risk management programme across four operating divisions.
Commercial risk intelligence services, and the client's own enterprise data warehouse. Two curated views, refreshed on a daily cycle.
Risk specialists, risk owners, segment managers and governance councils, across four operating divisions.
The enterprise reporting estate, which reads the curated register back out of the warehouse.
The programme was already producing judgements. What was missing was anywhere to put them that the next refresh would not destroy.
Commercial risk intelligence is excellent at detection and silent on resolution. Alerts arrived continuously, a factory fire, a credit downgrade, a sanctions listing, a labour dispute, and landed as warehouse rows nobody owned. An alert nobody had looked at was indistinguishable from one that had been cleared.
Nothing distinguished an alert nobody had opened from one reviewed and dismissed. The oldest items were the least likely to be noticed, because the list was sorted by arrival and read from the top. Ageing was a property of the data that nothing in the process measured.
The feed refreshed supplier and event attributes continuously. The team wrote decisions onto the same records. With no ownership boundary the two were in direct competition: either the refresh overwrote judgement, or judgement was kept somewhere the refresh could not reach, which meant somewhere the reporting could not reach either.
Supply chain due diligence is a regulatory obligation in several of the client's markets. "The council approved the mitigation" was not an answer. Which council, on what severity, derived from which impact and likelihood ratings, against which of six defined outcomes, recorded by whom: that is an answer, and none of it was structured data.
The feed is working, the alerts are arriving, the team is busy. But a register where every row is present is not the same as a register where every row has been decided. Ten thousand alerts can still represent one unanswered question.
The client was not starting from nothing. There was a risk management policy, with impact and likelihood criteria in an appendix, a severity matrix in another, a Risk Council with a defined remit, and specialists who knew the work.
A documented policy, rating criteria, a severity matrix, a governance council with a remit, and a live risk feed against supplier master data.
A system that held any of it. The criteria lived in a document. The matrix was applied by hand. The council's decisions were minuted and then effectively unsearchable. The register itself was a spreadsheet that reflected whatever its last editor believed.
Instead of asking a nightly job and a risk specialist to take turns. Every alert row carries two kinds of content, and the boundary between them is enforced from both directions.
The event identifier, title, date identified, source system, priority, affected suppliers and risk type. The nightly sync updates only these columns, and only on machine-created rows.
The screening decision, assigned specialist, case number, risk owner, severity, governance outcome and notes. The API refuses user edits to machine-owned columns, and the sync cannot reach a single one of these.
An alert created by hand inside the application is flagged as such and is fully editable, because no upstream system owns it. Nothing about the feed changes. The past data stays valid. Only the register becomes safe to work in.
Snowflake stays the warehouse. Entra ID stays the identity provider. The client's existing document estate stays the document estate. One PostgreSQL database is the only new store introduced, and the ingestion job ships inside the application's own container image, so the two can never drift apart.
The curated register is then read back into Snowflake by the client's data platform for enterprise reporting, the loop that turns what happened into what was decided about it.
The sync runs once a day because the source does. Snowflake refreshes these views on a daily cycle, so a more frequent poll would re-read identical rows and buy nothing.
The cadence is inherited from the warehouse rather than chosen, and it is what makes a single overnight pass the right shape: by the time the working day starts, the register already holds everything the warehouse knows.
Delivered as one application with a scheduled job, not as a platform.
Each case records its externally hosted document folder, mirrored onto every linked alert. A complete cloud object storage implementation with folder trees, upload, move, rename and replace is built and currently switched off, in favour of the client's existing estate.
Three roles. Viewers read everything. Specialists claim any alert and edit the ones assigned to them. Administrators manage the roster and read the audit log, and hold no extra power over cases whatsoever. An administrator who wants to edit an alert must assign it to themselves first, which leaves a trace. Ownership is evaluated per record on every write, never once at login.
A specialist's display name is captured onto the alert at the moment of assignment, alongside their address. When they leave and are removed from the roster, historical records still read correctly. The audit trail does not develop gaps because somebody changed jobs.
No user can mark an alert overdue, and no user can move one back out of it. The allowance has either elapsed or it has not. Removing the human hand from that one field is what makes the overdue count on the summary strip worth reading.
Read-only cases open read-only, rather than letting a specialist fill in a full assessment and discover the refusal on save. What the interface shows has to match what the system will allow.
The clock and the arithmetic. Deadlines, derived owners, computed severity and escalation routing are automatic.
The judgement. Whether exposure exists, how severe the impact is and what the mitigation should be are never automated, and the audit trail records a person's name against each one.
The impact categories, rating criteria, severity matrix and escalation routes come from the client's existing risk policy appendices. Nothing was invented. The rating criteria are available on screen at the moment of rating, so the policy is applied where the decision is made rather than filed where it can be ignored.
A governance system that needs its own governance is a second problem. This one runs on controls the client already operates.
Separate machine-owned from human-owned data at schema level, enforce the boundary from both directions, compute everything the governing methodology already determines, put a clock on the human step and take that clock out of human hands, and record every mutation against an identity that survives the person leaving.
The Azure Blob Storage layer, with Azure Identity and user-delegation SAS, is complete and switched off in favour of the client's existing document estate.
Detection is the easy half. If the decisions live in meetings and the data lives in a warehouse, the layer in between is the one worth building.