---
title: 'Policy and Compliance Automation'
source: 'https://academia.sh/en/courses/platform-engineering/policy-and-compliance-automation'
course: 'Platform Engineering'
language: en
updated: '2026-09-13T05:08:29+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Measures the same rule set in two arrangements: publishing it as a document versus embedding it in the pipeline'
    - 'Turns the real measure of abstraction into a number: how many teams comply without ever seeing the rule'
    - 'Measures how long exemptions last and counts how a temporary exception becomes permanent, and who stays outside'
---

# Policy and Compliance Automation

The same six rules are run against the same twelve service definitions in two arrangements: publishing the rule as a document and embedding the rule in the pipeline. Compliance rate, violation count, and time teams spend are compared; the real measure is invisibility — how many teams comply without ever seeing the rule, how many ask for an exemption, and how long that exemption's lifespan runs.

The catalog says who is responsible, but it sets no rule anyone is required to follow. For the
measurement network's services carrying personal data, log masking, retention period, and signed
packages are mandatory. Today these rules are written in a document, and every team is expected to read
and apply them. Expecting is not a mechanism; this lesson's question is where the rule actually stands.

**Policy automation** takes a rule out of being text to be read and moves it into a gate that accepts a
declaration. How rules are scanned — the fitness function, the scan order, caught and missed violations
— was measured in M20/K04 and in M22/K05's validation lesson, and is not repeated here. This lesson's
unit of measure is **invisibility**: once a rule is embedded in the pipeline, how many teams comply with
it without ever seeing it.

The measurement runs on the **regional measurement network**: **fictional** software that collects
water-meter readings, validates them, and opens field work orders. The rule set and the definition set
are both a **model** written in `node`.

**PC19.** The rule set is six rules, and it is defined within the lesson in **our own small format**.
The definition set is twelve services; each definition carries eight fields.
**PC20.** In the document arrangement, a team reads the document and applies it to its own services.
Durations are written in advance: reading the document is 20 minutes per team, review is 15 minutes per
service, and fixing a violation caught in an audit is 25 minutes.
**PC21.** In the pipeline-embedded arrangement, four rules are of the kind the pipeline fixes itself,
and two rules produce a reject. Fixing a reject is 8 minutes, and applying for an exemption is 30
minutes.
**PC22.** Exemption records, durations granted, and reasons are written in advance; today is referred to
by the number of days elapsed since the day it was granted.
**PC23.** The one-time cost of embedding the rules in the pipeline is 90 minutes per rule.
**PC24.** A team **seeing** a rule means either getting a reject message or filing for an exemption; a
violation the pipeline silently fixes never becomes visible to the team at all.

## Two Arrangements, One Rule Set

```js
// rules.mjs — six rules and twelve service definitions; both defined within the lesson. Model.
export const FIELD = ["team", "data-class", "masking", "signing", "health-endpoint",
  "log-level", "retention-days", "smoke"];
const RAW = {
  "reading-collector":   ["meter-reading", "measurement", "off", "yes", "/health", "summary", 365, "yes"],
  "reading-resolver":    ["meter-reading", "measurement", "off", "yes", "/health", "summary", 365, "yes"],
  "validation-flow":     ["validation", "measurement", "off", "yes", "/health", "summary", 400, "yes"],
  "rule-store":          ["validation", "measurement", "off", "no", "/health", "summary", 365, "yes"],
  "billing-calculator":  ["billing", "personal", "on", "yes", "/health", "summary", 400, "yes"],
  "tariff-service":      ["billing", "summary", "off", "no", "none", "detail", 700, "no"],
  "invoice-writer":      ["billing", "personal", "off", "no", "/health", "summary", 500, "no"],
  "field-order":         ["field", "personal", "off", "no", "none", "detail", 800, "no"],
  "map-edge":            ["field", "summary", "off", "no", "none", "summary", 365, "no"],
  "report-compiler":     ["reporting", "summary", "off", "yes", "/health", "summary", 365, "yes"],
  "dashboard-edge":      ["reporting", "summary", "off", "no", "/health", "summary", 365, "no"],
  "measurement-store":   ["meter-reading", "personal", "off", "yes", "/health", "summary", 400, "yes"] };
export const DEF = Object.fromEntries(Object.entries(RAW).map(([a, d]) =>
  [a, Object.fromEntries(FIELD.map((x, i) => [x, d[i]]))]));

// rule: [name, check, type, fix]  — type "automatic" means the pipeline fixes it itself, "reject" means the team fixes it
export const RULE = [
  ["K1 masking", (t) => t["data-class"] !== "personal" || t.masking === "on", "reject",
    (t) => ({ ...t, masking: "on" })],
  ["K2 signed package", (t) => t.signing === "yes", "automatic", (t) => ({ ...t, signing: "yes" })],
  ["K3 health endpoint", (t) => t["health-endpoint"] !== "none", "automatic",
    (t) => ({ ...t, "health-endpoint": "/health" })],
  ["K4 log level", (t) => t["log-level"] !== "detail", "automatic",
    (t) => ({ ...t, "log-level": "summary" })],
  ["K5 retention period", (t) => t["retention-days"] <= 400, "reject", (t) => ({ ...t, "retention-days": 400 })],
  ["K6 smoke test", (t) => t.smoke === "yes", "automatic", (t) => ({ ...t, smoke: "yes" })]];

// exemption: [service, rule, day granted (days ago from today), duration granted (days), reason]
export const EXEMPTION = [
  ["measurement-store", "K1 masking", 210, 30, "masking raw measurements breaks validation"],
  ["field-order", "K5 retention period", 120, 90, "field records must be kept for 800 days"],
  ["tariff-service", "K4 log level", 240, 14, "an open bug investigation is ongoing"],
  ["map-edge", "K6 smoke test", 45, 60, "the external endpoint has no test harness"]];

export const TEAM = ["meter-reading", "validation", "billing", "field", "reporting"];
export const DURATION = { read: 20, review: 15, fix: 25, reject: 8, apply: 30, embed: 90 };
```

The rule set is the same in both arrangements; the only thing that changes is where the rule stands. In
the document arrangement, the rule is text, and enforcement depends on the team's attention. In the
pipeline-embedded arrangement, the rule sits in a gate, and for four rules the gate does the fixing
itself, while for two rules it produces a reject. The distinction between rules is not technical — it is
informational: the platform knows the health endpoint's path, but only the service's own team knows the
retention period.

```js
// arrangement.mjs — the same rule set is run in two arrangements: published as a document and embedded in the pipeline.
import { DEF, RULE, EXEMPTION, TEAM, DURATION } from "./rules.mjs";

const SERVICE = Object.keys(DEF), pairs = SERVICE.length * RULE.length;
const exempt = (s, k) => EXEMPTION.some(([a, b]) => a === s && b === k);

const document = () => {                             // the rule is written in a document; the team reads and applies it
  let violations = 0;
  const teamViolations = Object.fromEntries(TEAM.map((e) => [e, 0]));
  for (const s of SERVICE) for (const [name, check] of RULE)
    if (!check(DEF[s])) { violations++; teamViolations[DEF[s].team]++; }
  const time = TEAM.length * DURATION.read + SERVICE.length * DURATION.review + violations * DURATION.fix;
  return { violations, compliant: pairs - violations, time, teamViolations, seen: pairs };
};

const pipeline = () => {                             // the rule is embedded in the pipeline: automatic fix or reject
  let automatic = 0, reject = 0, exempted = 0;
  const aware = new Set();
  for (const s of SERVICE) {
    let t = DEF[s];
    for (const [name, check, type, fix] of RULE) {
      if (check(t)) continue;
      if (exempt(s, name)) { exempted++; aware.add(t.team); continue; }   // an exemption makes the rule visible
      if (type === "automatic") { t = fix(t); automatic++; }
      else { t = fix(t); reject++; aware.add(t.team); }
    }
  }
  const time = reject * DURATION.reject + EXEMPTION.length * DURATION.apply;
  return { violations: exempted, compliant: pairs - exempted, time, automatic, reject, aware,
    seen: reject + EXEMPTION.length };
};

const B = document(), H = pipeline();
console.log(`rules ${RULE.length}  services ${SERVICE.length}  pairs checked ${pairs}` +
  `  teams ${TEAM.length}`);
console.log(`document arrangement : compliant ${B.compliant}/${pairs} (${(B.compliant / pairs * 100).toFixed(0)}%)` +
  `  violations ${B.violations}  team time spent ${B.time} min`);
console.log(`embedded in pipeline  : compliant ${H.compliant}/${pairs} (${(H.compliant / pairs * 100).toFixed(0)}%)` +
  `  recorded exemptions ${H.violations}  team time spent ${H.time} min`);
console.log(`the pipeline fixed ${H.automatic} violations without ever asking a team, rejected ${H.reject} violations`);
console.log(`violations per team (document arrangement): ` +
  TEAM.map((e) => `${e} ${B.teamViolations[e]}`).join("  "));

const neverSaw = TEAM.filter((e) => !H.aware.has(e));
console.log(`\ninvisibility: ${pairs - H.seen}/${pairs} rule-pairs complied with, never seen at all` +
  `  (what teams saw ${H.seen}: ${H.reject} rejects + ${EXEMPTION.length} exemptions)`);
console.log(`teams that never saw the rule ${neverSaw.length}/${TEAM.length}: ${neverSaw.join(", ")}` +
  `   in the document arrangement all five of five teams read all ${RULE.length} rules`);
const embed = RULE.length * DURATION.embed;
console.log(`embedding cost ${embed} min (one-time, ${RULE.length} rules x ${DURATION.embed} min);` +
  ` gain per period ${B.time - H.time} min; pays off in ${(embed / (B.time - H.time)).toFixed(2)} periods`);
```

```
rules 6  services 12  pairs checked 72  teams 5
document arrangement : compliant 50/72 (69%)  violations 22  team time spent 830 min
embedded in pipeline  : compliant 68/72 (94%)  recorded exemptions 4  team time spent 152 min
the pipeline fixed 14 violations without ever asking a team, rejected 4 violations
violations per team (document arrangement): meter-reading 1  validation 1  billing 9  field 9  reporting 2

invisibility: 64/72 rule-pairs complied with, never seen at all  (what teams saw 8: 4 rejects + 4 exemptions)
teams that never saw the rule 2/5: validation, reporting   in the document arrangement all five of five teams read all 6 rules
embedding cost 540 min (one-time, 6 rules x 90 min); gain per period 678 min; pays off in 0.80 periods
```

Three numbers move in the same direction. The compliance rate climbs from 69 percent to 94 percent,
violations drop from 22 to 4 recorded exemptions, and the time teams spend falls from 830 minutes to 152
minutes. The one-time cost of embedding the rules in the pipeline is 540 minutes, and the gain from a
single audit period covers it.

The real number is on the fourth line: **the pipeline fixed fourteen violations without ever asking a
team.** In sixty-four of seventy-two pairs, the rule was met without ever becoming visible; what teams
saw was only eight pairs, and of those, four were rejects and four were exemptions. Two of the five
teams — validation and reporting — saw none of the rules this period, while in the document arrangement
all five had to read all six rules in full.

How violations distribute across teams shows something else. Eighteen of the document arrangement's 22
violations come from two teams: billing 9, field 9. The document was written equally for everyone, and
not everyone read it equally. In the pipeline-embedded arrangement, this inequality does not disappear —
it **becomes invisible**: because the pipeline fixes it silently, which team was careless is no longer
written down anywhere. The hidden layer here hides not just the rule, but the information about whose
hands the rule was violated in.

The hidden layer leaks back somewhere. When the fix the pipeline makes silently changes the behavior a
team was counting on, the team learns about it from the outcome, not from the rule's text: when the log
level is pulled from detail down to summary, an open bug investigation loses the detail it was working
from. That leak is exactly what leads to filing for an exemption; it is why the tariff service's
exemption was requested. The silent fix raises the compliance rate and, at the same time, opens up an
area in the team's own system where it does not know what changed or why.

## An Exemption's Lifespan

```js
// exemption.mjs — the lifespan of exemptions: duration granted, time elapsed, expired and still-open ones.
import { DEF, RULE, EXEMPTION, DURATION } from "./rules.mjs";

const automatic = new Set(RULE.filter(([, , t]) => t === "automatic").map(([a]) => a));
console.log("service            rule                 granted  elapsed  overdue  status");
let grantedTotal = 0, elapsedTotal = 0, overdueOpen = 0;
for (const [s, k, elapsed, granted] of EXEMPTION) {
  const overdue = elapsed - granted;
  grantedTotal += granted; elapsedTotal += elapsed; overdueOpen += overdue > 0 ? 1 : 0;
  console.log(`${s.padEnd(19)}${k.padEnd(21)}${String(granted).padStart(5)} d${String(elapsed).padStart(7)}` +
    `${String(Math.max(0, overdue)).padStart(9)}  ${overdue > 0 ? "expired, still open" : "within duration"}`);
}
const n = EXEMPTION.length;
console.log(`average duration granted ${(grantedTotal / n).toFixed(1)} days, average elapsed ` +
  `${(elapsedTotal / n).toFixed(1)} days -> lifespan ${(elapsedTotal / grantedTotal).toFixed(1)} times the duration granted`);
console.log(`expired and still open ${overdueOpen}/${n}`);

const autoExempt = EXEMPTION.filter(([, k]) => automatic.has(k));
const teams = [...new Set(EXEMPTION.map(([s]) => DEF[s].team))];
console.log(`exemptions on automatic rules ${autoExempt.length}/${n}` +
  ` (${autoExempt.map(([s, k]) => `${s}/${k.split(" ")[0]}`).join(", ")});` +
  ` these rules became visible to their team only because of the exemption`);
console.log(`services that would not pass the pipeline with no exemption regime ${n}/${Object.keys(DEF).length}` +
  ` (${Math.round(n / Object.keys(DEF).length * 100)}%), ${teams.length} teams: ${teams.join(", ")}`);

const pairs = Object.keys(DEF).length * RULE.length;
console.log(`\napplication time ${n * DURATION.apply} min; the exemption record makes ${n} violations visible,` +
  ` the same ${n} violations were indistinguishable within the 22 violations of the document arrangement`);
for (const q of [0, 1, 2, 3, 4]) {                  // assumption: one new exemption per quarter
  const compliant = pairs - (n + q);
  if (q === 0 || q === 4) console.log(`${q === 0 ? "today" : "quarter " + q}: open exemptions ` +
    `${n + q}, compliant ${compliant}/${pairs} (${(compliant / pairs * 100).toFixed(0)}%)`);
}
```

```
service            rule                 granted  elapsed  overdue  status
measurement-store  K1 masking              30 d    210      180  expired, still open
field-order        K5 retention period     90 d    120       30  expired, still open
tariff-service     K4 log level            14 d    240      226  expired, still open
map-edge           K6 smoke test           60 d     45        0  within duration
average duration granted 48.5 days, average elapsed 153.8 days -> lifespan 3.2 times the duration granted
expired and still open 3/4
exemptions on automatic rules 2/4 (tariff-service/K4, map-edge/K6); these rules became visible to their team only because of the exemption
services that would not pass the pipeline with no exemption regime 4/12 (33%), 3 teams: meter-reading, field, billing

application time 120 min; the exemption record makes 4 violations visible, the same 4 violations were indistinguishable within the 22 violations of the document arrangement
today: open exemptions 4, compliant 68/72 (94%)
quarter 4: open exemptions 8, compliant 64/72 (89%)
```

An exemption is the one recorded way to step outside the pipeline, and it has been used on four
services. The durations granted are short — an average of 48.5 days — but the time that has actually
elapsed averages 153.8 days: **an exemption's lifespan runs three times its granted duration**, and
three of the four are still open even though their duration has expired. The longest is overdue by two
hundred twenty-six days, and its reason is a supposedly temporary bug investigation.

Two of the exemptions sit on rules the pipeline fixes itself. These two rules became visible to their
teams **only because of the exemption**: an exemption is the only way to make an invisible rule visible.
A team that complies without ever seeing a rule also cannot argue with it; only the team that requests
an exemption ever notices the rule is wrong.

The count of who stays outside is also read from here. Without the exemption regime, these four
services would never pass the pipeline at all: four of twelve services, thirty-three percent, and three
teams would sit entirely outside the golden path. The exemption's cost is 120 minutes of application
time, and in exchange four violations become visible by name; in the document arrangement, those same
four violations were indistinguishable within the 22 violations. But the record does not produce closure
on its own: if one new exemption opens per quarter and none of them close, compliance drops from 94
percent to 89 percent by the fourth quarter. Closing an exemption is the job of whoever granted it, not
the pipeline's.

## Summary

- The same 6 rules were run against the same 12 service definitions: compliance was 50/72 (69%) in the
  document arrangement and 68/72 (94%) in the pipeline-embedded arrangement; violations dropped from 22
  to 4 recorded exemptions.
- Team time spent fell from 830 minutes to 152 minutes; embedding's 540-minute one-time cost was
  covered within a single audit period.
- Invisibility was measured: in 64 of 72 pairs, the rule was met without ever becoming visible; 2 of 5
  teams saw none of the rules this period, while in the document arrangement all 5 of 5 teams had to
  read all 6 rules.
- 18 of the 22 violations in the document arrangement came from two teams; the pipeline fixed them, but
  at the same time made invisible the information about which team was careless.
- The 4 exemptions had an average granted duration of 48.5 days and an average elapsed time of 153.8
  days — a lifespan 3.2 times the duration granted, and 3 of the 4 open even though their duration had
  expired.
- Without the exemption regime, 4 of 12 services (33%) and 3 teams would stay entirely outside the
  golden path; an exemption is the one way that shows a team the rule the pipeline has hidden.

## Next Step

The environment now arrives the moment it is requested, the scaffold starts new services, the catalog
shows an owner, and the rules are embedded in the pipeline. All four capabilities hid something, and
somebody paid the cost of what they hid — but who that cost gets charged to has not been asked yet. The
measurement network has a shared repository, a shared pipeline pool, and a shared dashboard stack; their
bill is written today to a single line item, and no team sees what it itself consumes. The next lesson
takes up team-based accounting: allocating shared resources, the unallocatable line item, and the effect
of making cost visible on behavior.
