-
Notifications
You must be signed in to change notification settings - Fork 2
Specs M3 Compliance Compliance Assessment
compliance.models.assessment.ComplianceAssessment
Compliance assessment campaign, multi-framework, covering one or more Framework.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PK, auto-generated | Unique identifier |
reference |
string | auto-generated CAST-N, unique |
Business reference |
scopes |
relation | M2M -> Scope | ISMS scopes covered |
frameworks |
relation | M2M -> Framework | Frameworks assessed in this campaign. The implementation is multi-framework (a single assessment can cover ISO 27001 + GDPR simultaneously), an accepted divergence from the original single-framework spec (see the documented gap below). |
name |
string | required, max 255 | Title of the assessment (e.g. "Annual assessment 2026") |
description |
text | optional, HTML | Context and objective of the assessment |
limitations |
text | optional, HTML | Limitations / excluded scope / reservations of the assessment. Replaces the methodology field of the original spec; the methodology is instead carried at the framework level or in the audit documentation. |
assessment_start_date |
date | optional | Start of the audit period |
assessment_end_date |
date | optional | End of the audit period. The original spec had only a single assessment_date; the implementation uses a period to capture audits that span several days / weeks. |
assessor |
relation | FK -> User, required, PROTECT | Lead assessor |
overall_compliance_level |
decimal(5,2) | calculated, 0-100 | Overall compliance level (%). Recalculated by recalculate_counts(). Excludes NOT_APPLICABLE from both the numerator and the denominator (issue #46). |
total_requirements |
integer | calculated | Total number of applicable requirements covered |
compliant_count |
integer | calculated | Number of compliant requirements |
major_non_conformity_count |
integer | calculated | Number of major_non_conformity requirements |
minor_non_conformity_count |
integer | calculated | Number of minor_non_conformity requirements |
observation_count |
integer | calculated | Number of observation requirements |
improvement_opportunity_count |
integer | calculated | Number of improvement_opportunity requirements |
strength_count |
integer | calculated | Number of strength requirements |
evaluated_count |
integer | calculated | Number of evaluated requirements (placeholder) |
not_assessed_count |
integer | calculated | Number of not_assessed requirements |
not_applicable_count |
integer | calculated | Number of not_applicable requirements |
status |
enum | required, default draft
|
See "Lifecycle" below |
results |
reverse FK | O2M -> AssessmentResult | Per-requirement results |
findings |
reverse FK | <- Finding (Finding.assessment, now optional and SET_NULL) |
Nonconformities raised by this audit. Because the accessor is the reverse of a nullable FK, it never yields a nonconformity raised by a security incident or a management review : audit-scoped scoring stays audit-only by construction, with no filter to remember. Deleting the assessment no longer deletes them. |
version |
int | auto-incremented | Bumped on each major change |
tags |
relation | M2M -> Tag | |
created_by |
relation | FK -> User | Creator |
created_at / updated_at
|
datetime | auto |
status follows the actual workflow: draft -> planned -> in_progress -> completed -> closed, plus cancelled as a terminal branch reachable from draft and planned. Once completed or closed, the assessment can no longer move backwards.
draft -> planned -> in_progress -> completed -> closed
\ \
+---------+----> cancelled (terminal)
| Status | Meaning |
|---|---|
draft |
Configuration draft: business fields editable, not yet launched |
planned |
Configuration validated, scheduled, awaiting start |
in_progress |
In progress: the AssessmentResult entries are being filled in by the assessor |
completed |
Assessment conducted, results entered, awaiting validation / closure |
closed |
Finished and closed (terminal). The RC-06 carry-over (recalculate_counts) is triggered here |
cancelled |
Assessment cancelled (terminal). No carry-over is triggered |
The validated status listed in the original M3 spec §2.4 does not exist in the implemented enum: "validation" is carried by reaching status=closed, the terminal state that counts in reports.
The archived status from the original spec was not carried over either: closed plays the terminal role, and a potential need for explicit archiving can be added later through a flag or a separate status without breaking the current workflow.
| Original spec | Current implementation |
|---|---|
draft |
draft |
in_progress |
planned or in_progress (the implementation distinguishes planning from execution) |
completed |
completed |
validated |
closed |
archived |
closed (terminal). No dedicated status for now |
ComplianceAssessment.transition_to(new_status) validates the transition against ASSESSMENT_STATUS_TRANSITIONS (compliance/constants.py):
draft -> planned, cancelled
planned -> in_progress, cancelled
in_progress -> completed
completed -> closed
closed -> (terminal)
cancelled -> (terminal)
The REST API POST /assessments/<uuid>/transition/ and the MCP update_compliance_assessment (via the status field) enforce these rules. An unauthorized transition raises a ValueError reformatted as a 400 Bad Request.
-
in_progress -> completed: resets theAssessmentResult.compliance_statusentries that remainedEVALUATEDwithout an attached finding toNOT_ASSESSED(consistency: a "planned assessment" placeholder that received no finding reverts to "not assessed"). Callsrecalculate_counts(). -
completed -> closed: triggersrecalculate_counts()(RC-06). TheAssessmentResultentries are carried over to theRequirementrecords;Framework.last_assessment_dateis updated withassessment_end_date.
The carry-over of results onto requirements (RC-06) is triggered on closure (completed -> closed), the point at which the assessment reaches the reportable terminal state.
The M3 spec §2.4 described a single-framework assessment, dated to a single date, carrying a methodology field and without an explicit link to the scope. The implementation has evolved along these four axes; the choices are settled.
An assessment can cover several frameworks simultaneously. Use case: an annual ISO 27001 + GDPR + sector-specific standard audit (e.g. HDS for healthcare) is a single campaign, not three. It allows the field work to be shared (the evidence collected for ISO 27001 §A.5.34 also serves GDPR Article 32) and a single audit window to be shared with the auditees.
Impact on RC-06. On closure, recalculate_counts() propagates each AssessmentResult to its target Requirement, regardless of the framework that requirement belongs to. All attached frameworks thus have their Framework.compliance_level recalculated, and their Framework.last_assessment_date updated with the campaign's assessment_end_date.
On the query side: assessment.frameworks.all() enumerates the covered frameworks; assessment.results.all() enumerates the results across all frameworks; to scope by framework use assessment.results.filter(requirement__framework=fw).
The original spec had only a one-off assessment_date. The implementation uses a period because an audit typically spans several days (field cycle + documentary review + debrief), sometimes several weeks for heavy frameworks. The assessment_end_date serves as the reference for freshness (Framework.last_assessment_date takes this value on closure); the assessment_start_date documents the window start for audit exports.
The original spec had a methodology field to describe the audit method. The implementation replaced it with limitations (reservations, exclusions, scope not covered). The methodology is instead carried at the level of the attached audit documentation (evidence template, audit plan) or at the Framework level (an ISO 27001 framework implies a known methodology, no need to duplicate it for each campaign). limitations captures what must appear in the audit report: "the server room at site B could not be inspected", "the scope excludes the subsidiaries acquired in 2025".
The assessment is a ScopedModel and accepts scope_ids like all other domain entities (cross-cutting attachment RG-01). It makes it possible to compartmentalize audits by ISMS scope: an audit of the "France" scope does not pollute the KPIs of the "Germany" scope. A cross-cutting audit can remain without a scope (empty list).
compliance.models.assessment.AssessmentResult
Assessment result for a requirement within a campaign. One result per (assessment, requirement) pair; the uniqueness constraint is encoded at the database level.
| Field | Type | Constraints | Description |
|---|---|---|---|
id |
UUID | PK, auto-generated | Unique identifier |
assessment_id |
relation | FK -> ComplianceAssessment, required, cascade | Parent assessment |
requirement_id |
relation | FK -> Requirement, required, cascade | Assessed requirement |
compliance_status |
enum | required, default not_assessed
|
See "Compliance statuses" below |
compliance_level |
integer | default 0, 0-100 | Compliance level (%) |
finding |
text | optional, HTML | Audit finding. Replaces the gaps / observations pair of the original spec; the merge matches actual usage: an assessor writes a single block that describes both the gaps and the contextual observations. |
auditor_recommendations |
text | optional, HTML | Recommendations formulated by the auditor. Field added by the implementation to capture improvement leads without duplicating the compliance_finding of the Requirement. |
evidence |
text | optional, HTML | Evidence observed (document citations, screenshots, etc.) |
assessed_by |
relation | FK -> User, required, PROTECT | Assessor |
assessed_at |
datetime | required | Date and time of the assessment |
attachments |
reverse FK | O2M -> AssessmentResultAttachment | Attachments (documentary evidence) |
created_at / updated_at
|
datetime | auto |
Uniqueness constraint: the (
assessment_id,requirement_id) pair must be unique.
AssessmentResult.compliance_status shares exactly the same 11-value enumeration as Requirement.compliance_status: compliance.constants.ComplianceStatus. See requirement.md § Compliance statuses for the full table and the mapping of audit statuses (major_non_conformity, minor_non_conformity, observation, improvement_opportunity, strength, evaluated) to the conformance values used by the RC-01 and RC-02 aggregates.
The consistency between the two enumerations is intentional: an audit produces a result whose compliance_status is directly carried over to the target requirement without an intermediate mapping table (see RC-06 below).
The M3 spec §2.5 listed gaps and observations as two distinct fields, plus a compliance_status reduced to 5 values (not_assessed, non_compliant, partially_compliant, compliant, not_applicable). The implementation has evolved to align the result with the ISO 19011 audit vocabulary and with the Audits module:
-
gaps+observationsare merged into a singlefindingfield (same reasons asRequirement.compliance_finding, see #39). - An
auditor_recommendationsfield is added to capture recommendations distinctly from the finding (thefindingdescribes what is, theauditor_recommendationswhat should be done). -
compliance_statusis extended to the 6 audit values (evaluated,major_non_conformity,minor_non_conformity,observation,improvement_opportunity,strength) in addition to the 5 conformance values.
The decision to align these divergences was settled with the resolution of #44: the spec now follows the implementation.
On closure / validation of a ComplianceAssessment, recalculate_counts() propagates each AssessmentResult to the targeted requirement:
- If
result.compliance_statusisnot_assessed, the target requirement is not modified (preservation of prior assessments, issue #45 resolved). - If
result.compliance_statusisevaluated("planned assessment" placeholder), the last actually assessed result for this requirement across any assessment of the same framework is looked up in its history and carried over instead. - Otherwise,
result.compliance_statusandresult.compliance_levelare carried over as is to the requirement;Requirement.last_assessment_dateandRequirement.last_assessed_byare updated with the result's values.
The shared enum guarantees that no mapping is necessary between result and requirement: a major_non_conformity on the result side becomes a major_non_conformity on the requirement side, which is then aggregated as non_compliant in the RC-01 / RC-02 averages via the mapping documented on the Requirement side.
The Finding records of the nonconformity register can be attached to an AssessmentResult via Finding.requirements. The ComplianceAssessment.apply_findings_to_results() method then aligns the compliance_status of each result with the most severe status among the attached findings (according to FINDING_SEVERITY_ORDER in compliance.constants). This allows an audit to produce findings whose severity mechanically overrides the status of a result without manual entry.
Built from docs/ at v0.36.0. Edits made here are overwritten by the next release : open a pull request against the source instead.
- Administration
- Ask Cairn
- Assets and suppliers
- Compliance
- The dashboard
- Finding your way
- Getting started
- Incidents
- How records move
- Organisational context
- Reports and management review
- Risks
- Trust Center
- Architecture
- Configuration
- Contributing
- The documentation system
- Installation
- Internationalisation
- Operations
- Release process
- Security
- Testing
- Adding an assistant provider
- Adding a dashboard widget
- Adding a domain entity
- Declaring a lifecycle
- Adding an MCP tool
- Adding a REST endpoint
- Adding a report
- Interface conventions
- Dashboard widgets
- Lifecycles
- MCP tools
- MCP tool parameters : Assets
- MCP tool parameters : Compliance
- MCP tool parameters : Governance and context
- MCP tool parameters : General
- MCP tool parameters : Incidents
- MCP tool parameters : Reports and management review
- MCP tool parameters : Risks
- MCP tool parameters : System and administration
- MCP tool parameters : Trust Center
- Management commands
- Models
- Permissions
- REST endpoints
- Environment variables
- MCP server
- REST API
- Assistant module (Ask Cairn)
- Module 0: User Management and Access Control
- Module 1: Context and Organization
- Module 2: Asset Management
- Module 3: Compliance
- Module 4: Risk Management
- Module 4 bis - EBIOS Risk Manager
- Module 5 : Trust Center
- Module 6 : Security Incident Management
- Management review : ISO 27001:2022 compliance (clause 9.3)