Compliance & Assurance Standard
This standard governs how pitlab maps its internal controls to external regulatory frameworks — APRA CPS 234 and APRA CPS 230 today, with ISO/IEC 27001 Annex A and NIST CSF 2.0 declared but not launched — and what evidence a control mapping owes before it may claim assurance. It exists because that mapping was already being done (the /grillme design interview reasons every design against these frameworks and produces a per-design control table) but was never held anywhere: the mappings lived inside individual design docs, so no corpus-level position could be stated, and the July 2026 CPS 230/234 audit was a one-off with no recurring successor. The decision and rejected alternatives are recorded in ADR-0352.
It is the external-facing half of pitlab’s assurance layer. The internal half — measuring whether the estate adheres to its own standards — belongs to the Standard-Enforcement Standard, which this standard consumes rather than duplicates.
The control catalogue is code, and every mapping resolves against the live corpus
Section titled “The control catalogue is code, and every mapping resolves against the live corpus”The catalogue is ansible/scripts/doc_gen/compliance_controls.yml — one row per external control, naming the internal standard(s) and mechanism(s) that satisfy it, the evidence an auditor would be shown, and the residual risk. It is not prose: validate_compliance_catalogue.py is a blocking CI guard that resolves every reference against the live repos — each internal_standards: entry to a real file under docs/standards/ (an #anchor suffix is permitted), each mechanisms: entry by basename across the same four indexed roots the standards meta-gate uses, and each evidence: path to a real doc.
A mapping that cites something which does not exist is worse than no mapping. It manufactures assurance that survives every audit question until someone actually looks. This is the Config-Reference Validation Gate rule — resolve against the live authority, never a hand-maintained mirror (ADR-0142) — applied to control mappings. Indexing the same four roots as the meta-gate is deliberate: if the two gates saw different trees, a mechanism could resolve for one and not the other, and the corpus would carry two contradictory claims about what exists.
A control cannot be met by nothing — status carries obligations
Section titled “A control cannot be met by nothing — status carries obligations”Every row’s status is one of five values, and three of them oblige the author:
| Status | Meaning | What it obliges |
|---|---|---|
satisfied | An internal standard and a live mechanism cover it. | At least one backing standard or mechanism. Gate-enforced. |
partial | Covered in part. | At least one backing item and the shortfall named in residual_risk. |
gap | Nothing internal covers it. | residual_risk states the exposure. Claims nothing, so needs no backing. |
not-applicable | Genuinely out of scope for this estate. | A written justification. Gate-enforced. |
not-assessed | Not yet examined. The honest default. | Nothing — but it is counted and reported, never hidden. |
satisfied or partial naming neither a standard nor a mechanism fails the build, because that is assurance from thin air — the same defect as an assertion satisfiable without the feature (PD12).
Coverage needs an honest denominator, or the percentage is a lie by omission
Section titled “Coverage needs an honest denominator, or the percentage is a lie by omission”A control absent from the catalogue is indistinguishable from a control that passed. So every non-deferred framework declares a coverage_accounting.total — the real number of controls in scope — and the gate fails a framework that omits it or declares a total smaller than the rows assessed against it. ISO 27001 Annex A therefore carries total: 93 even though far fewer rows are populated, and the monthly report states “N of 93 assessed” rather than reporting 100% of what happens to be written down.
This is what makes the catalogue an ISO 27001 Statement of Applicability in all but name: an SoA is precisely a complete control list with an applicability determination and a reason for each, and a partial list presented as complete is the failure mode auditors look for first.
Theme-level not-applicable is legitimate; silent omission is not
Section titled “Theme-level not-applicable is legitimate; silent omission is not”A whole ISO Annex A theme may be disposed of at once where the estate genuinely has no subject for it — A.6 People (8 controls: no employees, contractors, or joiner/mover/leaver process) and A.7 Physical (14 controls: no organisational premises, visitor management or secure areas). Both carry an explicit theme-level row with its justification and the compensating control that actually matters (encrypted offsite backup for site loss; the Autonomous Remediation Authority Standard as the meaningful analogue of a personnel control for agent access). Disposing of 22 controls in two reasoned rows is honest; letting them go unmentioned is not.
A framework that has not LAUNCHED is declared, named in every report, and referenced by no operational process
Section titled “A framework that has not LAUNCHED is declared, named in every report, and referenced by no operational process”Two frameworks are currently in this state. NIST CSF 2.0 carries deferred_to_epic: 1861 with no controls at all. ISO/IEC 27001 Annex A carries deferred_to_epic: 2263 with 17 populated control rows — a deliberate and important distinction: assessment has started (17 of 93, plus two theme-level not-applicable determinations disposing of 22 more) but it has not launched, and 76 controls remain unassessed. The gate permits both shapes. Epic #1861 (Pre-go-live estate audit — NIST CSF 2.0 + agent-readiness) already owns the initial pass and is deliberately sequenced behind Encryption-in-Transit and the APRA resilience uplift. Every monthly report names the deferral, so the absence reads as a known gap rather than as coverage. Partial assessment is not a position. Publishing a monthly ISO report from 17 of 93 controls would state a compliance position the estate does not hold — the exact “assurance from thin air” this standard’s own gate exists to block, arriving through the front door instead. So a deferred framework’s report: is null, no report page is generated for it, and no runbook, skill or standard may cite it as a live position. Its rows are a head start for the Epic that owns it, never a claim.
The schema is framework-agnostic: rows drop in with no change to the engine or the gate, and launching a framework is a two-line edit — remove deferred_to_epic, restore report:.
Residual risk is stated, never rounded away
Section titled “Residual risk is stated, never rounded away”Every partial and gap row carries a residual_risk in plain words, including the uncomfortable ones — that the monthly review is self-assessment by the same agent that builds the controls (ISO A.5.35 independent review is recorded as a gap, not finessed); that provider control effectiveness is asserted from attestations rather than independently tested; that exit plans for material service providers are documented but never rehearsed. An assurance artifact that reports only strength teaches nothing and, under audit, destroys the credibility of the rows that were accurate.
The reporting obligation — one report per framework, monthly, driven from the same engine
Section titled “The reporting obligation — one report per framework, monthly, driven from the same engine”Each non-deferred framework owns exactly one report page, published monthly by the /gapanalysis routine from this catalogue plus the adherence engine’s per-clause liveness. A control mapped to a mechanism that has not demonstrably run is reported as degraded, not satisfied — which is the whole point of joining the two halves, and is what CPS 234’s requirement to regularly test control effectiveness actually demands. The reports are published as docs pages (git-versioned, retrievable, trendable) with a single monthly email digest linking them, from the Claude Reports persona per the Notification Channel Policy.
Conformance checklist — adding or changing a control mapping
Section titled “Conformance checklist — adding or changing a control mapping”- The row names a
frameworkthat exists underframeworks:in the catalogue. - Every
internal_standards:,mechanisms:andevidence:reference resolves — the gate proves it; never hand-verify. -
satisfied/partialname at least one backing standard or mechanism. -
not-applicablecarries a written justification stating why the estate has no subject for the control, and what compensating control covers the residual exposure. -
partialandgapcarry aresidual_riskin plain words, including the uncomfortable admissions. - The framework’s
coverage_accounting.totalstill reflects the real control count; assessed rows never exceed it. - A newly-added framework is either populated or carries
deferred_to_epic:with the Epic that owns its first pass — never silently empty. - A framework that has not launched carries
report: nulland is cited by no operational process (runbook, skill, standard) as a live position — partial assessment is a head start, not a compliance position. - The catalogue gate and its regression test both run green locally before the push (the gate is proven red against the real catalogue, not only fixtures).
Enforcement
Section titled “Enforcement”| Obligation | Class | Layer | Mechanism | Dead-man |
|---|---|---|---|---|
| Every control mapping resolves against the live corpus | machine | deploy | validate_compliance_catalogue.py, blocking guard in ansible_ci.yml; proven-red by test_validate_compliance_catalogue.py | n/a |
not-applicable carries a written justification | machine | deploy | validate_compliance_catalogue.py status rules | n/a |
satisfied/partial names ≥1 backing standard or mechanism | machine | deploy | validate_compliance_catalogue.py status rules | n/a |
| Each non-deferred framework declares an honest coverage denominator | machine | deploy | validate_compliance_catalogue.py accounting check | n/a |
| Per-framework position is recomputed monthly from live clause liveness | machine | scheduled | standards_adherence_gap.py, monthly cron declared in schedule_config.yml | standards.gapanalysis.age |
Residual risk on every partial/gap row is reviewed at the monthly run | review | — | the monthly gap-analysis routine’s published report, reviewed by the owner at the email digest | n/a |
| A deferred framework’s absence is named in every report it affects, and it is cited by no operational process as a live position | review | — | the monthly gap-analysis routine’s report template; /code-review when a framework is launched or deferred | n/a |
| Mapping judgement (is this control really satisfied) is sound | advisory | — | justification: an expert judgement with no reliable machine signal; the gate can prove a reference resolves, never that a control is genuinely met | n/a |