-
Notifications
You must be signed in to change notification settings - Fork 2
Specs Governance History
core.history - the single, audit-grade traceability layer over django-simple-history.
Every domain element records who changed what, and when. The framework turns raw
historical records into one normalized, chronological timeline of events, computed in
exactly one place and rendered identically everywhere: the detail-page panel, the REST
API and the MCP tools all build their view from core.history, so diff logic, hidden
fields and event classification never diverge.
History applies to every BaseModel / ScopedModel subclass: each declares
history = HistoricalRecords() (per-model, not on the base class), so the historical
table captures every field change with the acting user and timestamp. django-simple-history
middleware (HistoryRequestMiddleware) fills history_user from the request.
The authoritative, exhaustive list of history-tracked models is machine-discoverable
(the Historical* model scan used by the system audit log, accounts.views.ActionLogListView).
The unified UI panel is surfaced on the detail page of each BaseModel entity, covering
the context, assets, compliance, risks, reports and trust-center modules.
build_timeline(instance, *, limit, extra) returns a reverse-chronological list of
HistoryEntry. Each entry is one of EntryKind:
| Kind | Source | Shown as |
|---|---|---|
create |
+ record |
field snapshot |
update |
~ record with ordinary field diffs |
per-field old -> new diff |
transition |
~ record whose delta touches workflow_state
|
from -> to state labels (+ comment where one exists) |
delete |
- record |
field snapshot |
Precedence on a modification: transition > update. Version churn (version) is
hidden from ordinary diffs. A transition's is_refusal flag is recomputed from the
registered lifecycle (a backward move along the main flow), never persisted.
HISTORY_SOURCE_HOOKS (keyed by app_label.model) merges extra events into an entity's
timeline via extra_source_for(instance):
-
context.role- each responsibility's history, so adding / editing a responsibility shows on the role timeline (entity_labeldistinguishes the rows). -
compliance.complianceactionplan/reports.managementreview- their dedicated*Transitionlogs, which carry transition comments. For these the genericworkflow_state-diff transitions are suppressed (suppress_generic_transitions) so a transition appears once, with its comment, instead of twice.
Persisting transition comments for all entities (not only the two with a dedicated log) is intentionally out of scope: generic transitions show
from -> to, the actor and the timestamp, but no comment.
All three consumers call core.history; none reimplements diffing.
-
UI (lazy off-canvas panel). Detail views mix in
accounts.mixins.HistoryUrlMixin, which exposeshistory_url+history_available. The trigger (includes/history_trigger.html) lives in the page-header action slot on every detail page; opening it lazily HTMX-loadsincludes/history_panel.html's body fromhistory:partial(core.history_views.HistoryPartialView), which rendersincludes/history_timeline.html. The timeline is never queried on page load, only on first open. The endpoint enforces the entity's.readpermission and the user's scope. -
REST API.
accounts.api.mixins.HistoryAPIMixinaddsGET /<entity>/<id>/history/returning the same entries (?limit=,?offset=). -
MCP.
_register_crudregistersget_<entity>_historyfor every entity with history, gated by<perm_prefix>.read. -
System audit log.
ActionLogListViewclassifies records withcore.history.classify_record(shared with the timeline).
- The history trigger is always the first action in the
{% page_header %}block, iconbi-clock-history, label "History" / "Historique". - The panel is a right-side Bootstrap off-canvas (
offcanvas-end), full-height, internally scrolling, near-full-width on mobile, with native focus-trap / ESC / backdrop. - Never reintroduce per-entity history markup (nav-tabs tab, bottom collapse card,
<details>); converge on the trigger + panel includes.
-
core/history.py- constants,EntryKind,FieldChange,HistoryEntry,classify_record,build_entry,build_timeline,HISTORY_SOURCE_HOOKS. -
core/history_views.py+core/history_urls.py- the lazy panel endpoint. -
accounts/mixins.py(HistoryUrlMixin),accounts/api/mixins.py(HistoryAPIMixin). -
templates/includes/history_{trigger,panel,timeline}.html.
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)