Releases: frousselet/cairn
Releases · frousselet/cairn
v0.28.2
Added
- Manage role responsibilities from the UI: the role detail page now lets you add, edit and delete responsibilities (description, RACI type, related activity) directly from the Responsibilities section. An Add button in the section header and per-row edit / delete actions open an HTMX drawer; the section refreshes in place after each change. Actions are gated by the
context.role.updatepermission. Previously responsibilities could only be managed through the API / MCP tools (create_responsibility,update_responsibility,delete_responsibility), which remain available. Changing a role's responsibilities now sends the role back to its draft state (resetting approval and bumping the version) so it is re-validated; the demotion is recorded in the role's change history. This applies whether the change is made from the UI or through the REST API, except for roles in a terminal state (archived / cancelled), which are left untouched. The role's History section now also merges its responsibilities' own history into the role timeline, so adding, editing or deleting a responsibility is visible there (a deletion shows the removed responsibility's details, tagged as a "Responsibility" entry). - Generic CSV bulk import (suppliers first): a new reusable, entity-by-entity bulk-import framework (
core/imports) modelled on the framework import. Each importable entity declares anEntityImporterwith its column specification and registers it; the generic views, URLs (/imports/<entity>/), templates and sample-file generation then drive the same upload -> preview -> confirm wizard for every entity. Suppliers are the first consumer: an Import button above the supplier list opens a CSV upload in a modal, the file is validated row by row (type coercion, allowed values, FK/M2M resolution) and a preview lists the rows to import (flagging those that already exist) and the rows skipped with their errors before confirmation. Owners are resolved by email (blank falls back to the importing user), supplier types by name (must exist), scopes by reference or name (must exist) and tags by name (created on the fly). Duplicate handling is decided per row in the preview: a row whose exact name already matches an existing supplier shows a Replace checkbox, so for each match the user chooses to overwrite the existing supplier or keep it unchanged (a name matching several existing suppliers is reported as an ambiguous error). On replacement the supplier's original creation date is preserved. The importer can also carry over the original creation date of newly created suppliers from a legacy tool (acreated_atcolumn written via a post-save update so it is not overwritten byauto_now_add). A downloadable CSV sample with a per-column documentation panel is provided. Suppliers already expose programmatic bulk creation through the existingbatch_create_suppliersMCP tool and the/api/v1/assets/suppliers/batch endpoint.
Fixed
- Role detail page crashed (500): the assigned-users list referenced the non-existent
user.usernameattribute (theUsermodel is email-based with nousernamefield), raising aVariableDoesNotExiston every/context/roles/<id>/view. It now falls back touser.email. - Dashboard progress-bar heights aligned: the "Active objectives progression" bars used the Bootstrap default height (16px) while the "Compliance by framework" bars were 8px. Both now render at 8px for a consistent look.
- Modal select dropdowns clipped / hidden behind the modal: TomSelect dropdowns opened inside a form drawer were clipped by the scrollable modal body and could render behind the modal. They are now attached to
<body>(dropdownParent) with a z-index above the modal, so the full option list is always visible regardless of the field's position in the form.
v0.28.1
Changed
- Administration menu reorganised: the sidebar's Administration section is now structured into four collapsible groups instead of a flat list - General (Company, Tags, Versioning, Calendar subscriptions, Trust Center), Access (Users, Groups, Permissions), Ask Cairn (Semantic index, Feedback) and Logs (Access log, Action log). Each group only renders when the user can see at least one of its items, preserving the existing per-item permission checks. The Django admin link was removed from the sidebar.
- Trust Center moved to Administration: the Trust Center is now entirely an administration concern. Its settings page (publish switch, branding, custom domain and CSS) is reached from the Administration menu, and the standalone Trust Center entry was removed from the Governance / Strategy menu. The content curation hub is now reached through a "Manage content" link on the settings page; it keeps the public-page link and continues to host the day-to-day curation of certifications, subprocessors, measures and documents.
- Frameworks menu simplified and import as a modal: the sidebar "Frameworks" entry is now a direct link to the framework list instead of a collapsible submenu (List / Import). Importing a framework is now triggered by an Import button above the list (next to "New framework") that opens the import form in a modal; submitting it runs the existing analyze -> preview -> confirm wizard. The full-page import view is preserved as the submit target and validation-error fallback.
- Self-documenting framework import samples: the downloadable JSON and Excel sample files now embed their own field-and-values documentation (English-only, as a stable technical reference independent of the user's locale). The JSON sample carries a leading
_instructionsblock (ignored on import) listing every framework / section / requirement field, whether it is required, and the allowed code -> label values for the enumerated fields (framework type and category, requirement type and category). The Excel sample gains header-cell comments and a dedicated Documentation sheet describing each column, which row type it applies to, and the same allowed-values tables. The allowed values are generated from the model choices, so they stay in sync with what the importer accepts. The Excel sample's requirement references were aligned with their section prefixes (e.g.SEC.1.1.1) so the sample now imports cleanly with correct nesting. - Action plans kanban header: the page now follows the standard header convention with a "Compliance" eyebrow and module accent above the title.
Fixed
- Action plans kanban mispositioned after navigation: the kanban board is a fixed-position layer whose top offset was computed by a
DOMContentLoadedhandler, which never fires on HTMX-boosted navigation (e.g. clicking "Action plans" in the sidebar). The board was left anchored to the bottom of the viewport with a large empty gap above it (and drag-and-drop was not wired up). Initialisation now runs on first load, on every boosted swap (htmx:afterSettle) and on resize, so the board is always positioned directly below the header.
Full changelog: v0.28.0...v0.28.1
v0.28.0
Added
- Trust Center: a new public, unauthenticated page that advertises the organisation's security and compliance posture, built directly into Cairn and optionally servable on a separate domain. It is an explicit curation layer: a dedicated
trust_centerapp whose models reference internal frameworks, suppliers and reports through public-only entries (public label, description, ordering), so internal GRC data never reaches the public surface. Four content sections, each governed by atrust_center_publicationlifecycle workflow: certifications, subprocessors, security measures and documents. A dual publish gate means an item is public only when its own publication state is "published" AND its source object is still validated/active; a global publish switch takes the whole Trust Center offline. Data-leakage safety is enforced by dedicated public DRF serializers (hardcoded field whitelist), anonymous rate-limiting, no raw/media/exposure, and strict allowlist sanitization of every rendered SVG logo. Public page at/trust/(read API under/trust/api/); internal curation UI at/trust-center/manage/. Full REST API under/api/v1/trust-center/, MCP tools, and newtrust_center.*permissions assigned to the six system roles. - Trust Center branding and rich text: the public page leads with the company name as the hero title (with its logo) and uses it as the favicon; the browser UI
theme-colorfollows the configured accent. The intro and the certification / measure / document descriptions are authored with the rich-text editor and rendered as sanitized HTML. The settings gain an optional custom CSS field injected into the public page, sanitized to prevent style-tag breakout and active content.
Changed
- Dependency graph header layout: the page now follows the standard header convention with an "Assets" eyebrow and module accent above the title. The stats, the colour legend and the zoom controls were merged into a single compact toolbar on the header row (the legend moved into a popover instead of a full-width inline strip), reclaiming vertical space for the graph canvas, which is now repositioned on window resize. The force layout was tuned so disconnected components stay in frame without overlapping (bounded local repulsion, gentle centre gravity, label-aware collision radius).
Full Changelog: v0.27.2...v0.28.0
v0.27.2
Added
- Dashboard risk treatment flow (Sankey): a new Sankey (cash-flow style) chart on the home dashboard, displayed above the risk matrices, visualises how treatment moves each risk from its current severity level (before treatment) to its residual level (after treatment). Each column lists the severity levels present (highest at the top, lowest at the bottom), coloured with the configured risk-level palette, and each flow's thickness is the number of risks making that transition - so a heavy downward flow reads as effective treatment and a flat flow as untreated risk. Levels are derived from the likelihood/impact pairs (with the same default 5x5 ISO 27005 fallback as the matrices), so the chart stays consistent with the matrices below even when no risk criteria are configured. The chart honours light/dark mode and is rendered with Apache ECharts. Built from a new
build_risk_treatment_flow()helper inrisks/views.py. - Ask Cairn: OpenAI and OpenAI-compatible providers: the assistant gains an
openaibackend (AI_ASSISTANT_PROVIDER=openai) that targets OpenAI (ChatGPT, e.g.gpt-4o-mini) out of the box and, viaAI_ASSISTANT_BASE_URL, any other endpoint implementing the OpenAI/chat/completionsand/embeddingsAPI (vLLM, LiteLLM, LocalAI, Together, Groq...). The shared request/response handling was extracted into a genericOpenAICompatibleClient; the existingMistralClientis now a thin subclass of it (Mistral already exposes an OpenAI-compatible API), so behaviour is unchanged for Mistral users.AI_ASSISTANT_BASE_URLnow defaults to empty and each provider falls back to its own endpoint (mistral->api.mistral.ai,openai->api.openai.com,anthropic->api.anthropic.com); set it only to target a custom gateway. - Ask Cairn: Claude (Anthropic) provider: a native
anthropicbackend (AI_ASSISTANT_PROVIDER=anthropic) talks to Claude through the Messages API (POST /v1/messages,x-api-keyheader, top-levelsystem,contentblock list) - Claude is not OpenAI-compatible, so it has its own client. Routing uses forced tool use (aplantool whoseinput_schemais the routing schema) and notemperature/thinkingis sent (both 400 on the current Opus family). SetAI_ASSISTANT_MODELto a Claude model id (e.g.claude-opus-4-8). Semantic search is not available with this provider, since Anthropic has no embeddings API. - Ask Cairn: automatic semantic index maintenance: the requirement semantic index now stays fresh without a manual command. A
post_deletesignal prunes a deleted requirement's embedding immediately (no provider call); the index is refreshed in a guarded background thread when a server process starts (whenAI_ASSISTANT_SEMANTIC_ENABLED); and a dedicated Administration -> Semantic index page shows an index-status panel (indexed / total requirements, last updated, embedding model) with an "Update the index now" button (gated bysystem.config.update) that triggers a background rebuild. Embedding stays off the request path - requirement saves never embed inline; the documented dailyrebuild_semantic_indexcron remains the self-healing backstop. The rebuild logic was extracted intoassistant.semantic.rebuild_index/rebuild_index_async(cache-locked to dedupe overlapping triggers) and reused by the management command. - Ask Cairn: supplier requirements and dependencies: the assistant can now answer "Quelles sont les exigences du fournisseur X ?" and "De quels fournisseurs dépend <l'actif> ?". New catalog tools
list_supplier_requirements(a supplier's contractual / security requirements, found in two steps: locate the supplier, then list its requirements),list_supplier_dependencys(the suppliers a support asset depends on) andlist_site_supplier_dependencys(the suppliers a site depends on). The two dependency tools now expose read-onlysupplier_name/support_asset_name/site_namecompanion fields (model properties) so the answer names the actual supplier instead of an opaque identifier, and their record cards link to the supplier page. - Ask Cairn: sites, activities and stakeholders are now answerable: the assistant catalog gains
list_sites(physical premises, with their postal address),list_activitys(business activities and processes, filterable by criticality) andlist_stakeholders(interested parties). Previously the planner had no tool for these entities and routed questions like "Quelle est l'adresse du site Lyon HQ ?", "Liste-moi les activités critiques" or "Dis m'en plus sur la partie intéressée Industrial Customers" to the nearest wrong tool (scopes, risks, ISMS changes) and answered with empty data.list_suppliersnow also feeds the supplier's description, country and contract end date to the summary, so "Quelle est l'activité de ce fournisseur ?" is answered from its description instead of reporting it as missing.
Fixed
- Ask Cairn: SWOT analyses and "who is responsible" questions: two more feedback follow-ups, verified live. (1) "Liste-moi les analyses SWOT" had no catalog tool and mis-routed to semantic requirement search; added
list_swot_analysiss. (2) "Qui est responsable de <X> ?" found the record but could not name the owner, which only existed as a strippedowner_id. Suppliers, objectives, activities, essential assets and support assets now expose anowner_namecompanion (the responsible user's display name, same pattern as scopemanager_names), so the assistant answers "David Morel est responsable de FacilEnergie Services" instead of "aucun responsable mentionné". - Ask Cairn: supplier category, expired suppliers and count over-filtering: follow-ups from assistant feedback, verified end to end against the live Mistral planner. (1)
list_suppliersnow surfaces atype_namecompanion (the supplier's category), so "Quelle est l'activité / la catégorie de ce fournisseur ?" is answered ("HRline est un fournisseur SaaS...") instead of reported as missing. Because the newlist_activitystool made the planner mis-route "l'activité de <company>" to internal activities, the activities signature now states it is for internal processes only and a routing example sends a company's line of business tolist_suppliers. (2) A newexpiredfilter on the supplier list tool returns suppliers whose contract has expired (active suppliers with a past contract end date, mirroringSupplier.is_contract_expired), so "Liste-moi les fournisseurs expirés" works; the generic list machinery gained an optional derived-filter hook to express it, a worked routing example steers the planner to theexpiredflag instead of an invalidstatus="expired"guess, and the supplier output now carriesis_contract_expiredso the summary names the expired supplier instead of contradicting itself. The summary prompt also now states present records affirmatively (never "none found" while records are shown). (3) Count questions like "Combien de biens essentiels ?" were over-filtered by the planner (it addedstatus="identified", which matched none); a worked count example in the routing prompt now steers the planner to query broadly and rely on the returned total. - Ask Cairn: "how many" questions now report the real count: the list tools return a
totalcount alongside a sample of records, but the engine dropped it before the summary, so "Combien de biens essentiels ?" could only be answered from the (possibly truncated) sample. The engine now feedstotalto the summary, the summary prompt explains it is the full count, and the planner is told not to add a status filter to count questions unless one is named (so "combien" queries count everything instead of an over-constrained subset). - Ask Cairn: status filters now spell out their allowed values to the planner: the routing-prompt tool signatures for the status-filtered list tools (
list_objectives,list_action_plans,list_compliance_assessments,list_frameworks,list_indicators,list_issues,list_suppliers,list_essential_assets,list_support_assets) now enumerate the exactstatusenum values, like the risk and management-review tools already did. Previously these signatures showed a barestatusparameter, so the planner had to guess: asking "Quels sont les objectifs complétés ?" made it filter on an invalid status and return an empty list instead of the objectives at 100%.list_objectivesnow also documents that a completed / met objective has statusachieved(andnot_achievedmeans finished but the target was missed); the criticality and indicator-type enums are spelled out too. Surfaced by a thumbs-down through the assistant feedback channel.
Full changelog: v0.27.1...v0.27.2
v0.27.1
Fixed
- Command palette no longer opens in a French locale: the Ask Cairn palette script embedded
{% trans %}strings inside single-quoted JS literals, and French translations contain apostrophes (e.g. "L'assistant est injoignable…") that terminated the string, throwing aSyntaxErrorthat killed the whole palette script. Translated strings embedded in the palette JS are now escaped with|escapejs. (Surfaced only in production, which runs in French; English dev was unaffected.) - Clearer error when the Mistral API key is missing:
rebuild_semantic_indexand chat failed with a cryptichttpx.LocalProtocolError: Illegal header value b'Bearer 'whenAI_ASSISTANT_API_KEYwas empty.MistralClientnow validates the key up front and raises a clear "Mistral API key is not configured" message;rebuild_semantic_indexexits with a clean error instead of a traceback.
Full changelog: v0.27.0...v0.27.1
v0.27.0
Added
- Ask Cairn, an optional AI question mode in the command palette: the command palette (Ctrl+K) gains an "Ask Cairn" entry that answers simple natural-language questions like "Quelles décisions ont été prises lors de la dernière revue de direction ?". The LLM backend is a pluggable provider (
AI_ASSISTANT_PROVIDER): Mistral AI (third-party, EU-hosted; default modelmistral-small-latest) by default, with a self-hosted Ollama provider still selectable for an on-premises, no-egress deployment. The model only routes the question to a curated allowlist of 24 read-only MCP tools executed in-process with the requesting user (existing permissions and scope filters apply, nothing is bypassed); the answer shows the real matching records as clickable cards plus a short AI-labeled summary sentence in the user's language. Internal identifiers are stripped before the summary call. The feature is disabled by default (AI_ASSISTANT_ENABLED), degrades gracefully when the backend is unreachable, and ships with a REST endpoint (POST /api/v1/assistant/ask/) and anask_assistantMCP tool. Each answer carries a thumbs up/down feedback control with an optional comment: the captured feedback (prompt, interface language, LLM summary and returned records, provider/model, rating and comment) is stored asAssistantFeedback, browsable and exportable as JSON from the in-app Administration area (sidebar "Assistant feedback") and the Django admin (and viaGET /api/v1/assistant/feedback/export/or thelist_assistant_feedbackMCP tool, gated bysystem.assistant_feedback.read) so a set of feedback can be handed to an LLM to improve the service. Feedback can be marked corrected once acted on (in-app button, admin action, orPOST /api/v1/assistant/feedback/{id}/resolve/), which excludes it from future exports by default (the in-app list defaults to open feedback and the export, RESTexportandlist_assistant_feedbackMCP tool all drop corrected rows unlessinclude_resolved). Optionally (AI_ASSISTANT_SEMANTIC_ENABLED), a semantic, cross-language requirement search (semantic_search_requirements) lets topic questions match control content regardless of language (e.g. French question, English ISO controls): requirement embeddings are stored portably (nopgvector) viamanage.py rebuild_semantic_indexand ranked by in-Python cosine similarity. "Who is responsible for scope X?" questions are now answered: thelist_scopesMCP tool exposes a read-onlymanager_namesfield (from a newScope.manager_namesproperty) and the assistant feeds it to the summary, so the responsible managers are named instead of reported as missing.
Full changelog: v0.26.3...v0.27.0
v0.26.3
Added
- Company identity on the dashboard: when a company name is configured (Company settings page), it replaces the "Dashboard" title in the page header, with the company logo displayed beside it; the page identity moves to the eyebrow. The
{% page_header %}component gains a genericlogoparameter (image rendered in place of the icon). Without a configured name, the header is unchanged. - Demo dataset seed script:
scripts/seed_demo_data.py(withscripts/seed_demo_tables.py) populates a fresh database with the fictional "Voltara Energy" company: users in every system group, scopes, sites, stakeholders, issues, objectives, SWOT, roles, activities, essential and support assets with dependencies and SPOFs, suppliers with contractual requirements, the full ISO/IEC 27001:2022 Annex A plus NIS2, GDPR and an internal baseline (133 requirements), audits with findings and action plans, an ISO 27005 risk assessment (threats, vulnerabilities, analyses, risks, treatment plans, acceptances), an EBIOS RM study (workshops 0-3), indicators with measurement history, and management reviews. Documented indocs/installation.md.
Fixed
- Report downloads rendered as binary in the page: the global
hx-boostintercepted clicks on file-download links (report list, management review PPTX/DOCX exports, risk register and assessment exports, framework import samples) and swapped the binary attachment into the page instead of letting the browser download it. Download links now opt out of boosting (hx-boost="false"+download), and a safety net in the boosted-navigation module cancels the swap and hands the URL to the browser whenever a response carriesContent-Disposition: attachment. - Calendar "Upcoming events" showed negative day counts (#112): ranged items already in progress (action plans, treatment plans, compliance assessments) displayed their range start with badges like "in -131 days" and always floated to the top of the list. The card now consumes a dedicated
/api/calendar-upcoming/endpoint that reuses the dashboard's next-milestone logic (sharedbuild_upcoming_deadlines()helper): ranged items show their start date until they begin, then their end/target date; past-due deadlines carry a red "Overdue" badge; concluded items are excluded; sorting uses the milestone date; and each row names the nature of the date in a small chip, like the dashboard. - Search palette ignored the user language: the navigation and quick-action entries of the command palette (Ctrl+K) were translated once at server start instead of per request, so they always showed up in the import-time language (typically English) regardless of the user's language. The labels are now lazily translated.
Changed
- Search palette contrast: the command palette dialog was 85% translucent and blended into the blurred page behind it, reading as grey-on-grey. It now uses a near-opaque frosted surface (new
--surface-glass-strongtoken, light and dark), a slightly darker backdrop scrim, and stronger group labels. - README rewritten for accessibility: the public README is now a short overview (what Cairn does, quick start, documentation links, tech stack). The detailed content moves to
docs/: feature tables todocs/features.md, the full MCP tool reference todocs/mcp-server.md, installation and scheduled-command details todocs/installation.md, plus a new REST API overview (docs/api.md) and a documentation index (docs/README.md). - Documentation screenshots refreshed: all
docs/screenshots/captures retaken in 4:3 (1440x1080) on the current brand with the demo dataset, and embedded in the README (dashboard) anddocs/features.md(one per module section).
Full Changelog: v0.26.2...v0.26.3
v0.26.2
Fixed
- Predefined compliance indicators disagreed with the dashboard: the "Global compliance rate" predefined indicator averaged the stored
compliance_levelfield over every reportable framework (drafts and superseded included), while the dashboard computes, per active framework, the proportion of applicable requirements whose latest assessment result is compliant. The computation is extracted into a shared service (compliance.services) now used by the dashboard page, the dashboard WebSocket refresh (which averaged the stored field too, so a live update could overwrite the page value with a different number) and both predefined indicators (global_compliance_rateandframework_compliance_rate). - Roles could not be assigned from the UI: the role create / edit modal had no "Assigned users" field, even though the model, the API and the MCP tools expose it (and the dashboard nags about mandatory roles without a user). The second step of the modal becomes "Assignment & status" and gains a multi-select of active users.
Changed
- SPOF and calendar deadlines fold into Today's actions: the dashboard's standalone SPOF warning banner and "Upcoming events" card are gone. Single points of failure now appear as actionable items in the "To plan" group (one entry per dependency type, each linking to its list). Deadlines and events render as a section inside the Today's actions card, server-side (the client-side fetch is removed): upcoming dates for the next 30 days plus overdue deadlines (reviews, target dates, expiries) from the last 90 days. Ranged items (action / treatment plans, audits) now show their next milestone - the start date until they begin, then the target date - instead of always showing the range start, which produced nonsensical "in -131 days" badges; anything past due is flagged with a red "Overdue" badge instead of a negative day count. Overdue deadlines count toward the card's attention counter, and the list collapses beyond five items. Concluded items (closed or cancelled action plans, completed treatment plans, achieved objectives, revoked or renewed acceptances) stay on the calendar but leave the dashboard list: a closed plan is not "overdue". Each row names the nature of the date in a small chip (Review, Expiry, Effective date, Target date, Start date, Valid until, Audit start / end, Assessment or Analysis date) so an entry reads as "what is due", and the title drops the redundant "Review: " / "Expiry: " prefixes there.
- Reports and Management reviews move under a Strategy group: the sidebar's Governance section gains a collapsible "Strategy" group (compass icon) after Indicators, holding the Reports and Management reviews entries, which leave the top-level General block (Dashboard and Calendar remain there).
- Forms adopt the same design language: form fields take the recipe of the sidebar search field - glass card resting on the surface with a soft shadow lift, brightening on hover, surfacing to solid with the accent ring on focus (globally, in the modal form drawer, on Tom Select controls, input-group addons, the image-upload button and the scope tree containers). Fields use dedicated
--field-*tokens: in light mode the fill is more opaque and the hairline border visible (pure glass is invisible on light surfaces), in dark mode they match the sidebar glass. The modal form drawer itself moves to the flat page-level background, like the sidebar panel, so the fields float over it the same way; its header, body and footer become transparent. The Jodit rich-text editor loses its default grey segmented chrome in both themes: the whole editor becomes one soft well that surfaces on focus, with a transparent toolbar, calm icon hovers, fading hairlines between toolbar / canvas / status bar, and themed popups. Tom Select controls follow the same well treatment and their dropdown becomes frosted glass like the other floating menus, as do the icon-picker popover and the scope-count popover. The modal wizard stepper softens: number chips rest on the muted well tones and connectors dissolve toward the next step. The drawer header / footer and the duplicated modal header / footer rules (which were overriding the theme-aware ones) get the fading hairlines, and the scope tree widget becomes a soft well whose embedded search is borderless over a fading hairline - with the selected row tinted like an active sidebar link. Input-group addons and the image-upload button align on the well tones. - The sidebar design language spreads to the rest of the interface: the visual vocabulary introduced by the sidebar redesign (frosted glass, fading hairlines, sentence-case labels) now applies across the UI. Floating elements pick up the translucent backdrop-blurred glass treatment: the mobile menu button, the sidebar collapse toggle, the bulk-actions bar (which loses its heavy accent border - the accent-coloured count is enough), dropdown menus, the global search dialog (which now keeps the material of the sidebar search field it morphs from), the toasts (semantic tints kept but translucent) and the modal backdrop. Hard border lines under headers are replaced by hairlines fading at their ends, echoing the sidebar's right-edge demarcation: card headers, modal header / footer, the search overlay input row, drawer section titles, the calendar timeline month labels and the styleguide section titles. All small UPPERCASE labels move to sentence case like the sidebar section labels did: table column headers, page-header eyebrows, stat / KPI card labels, detail-page card section titles (the
text-uppercaseutility is removed from ~140 headings), SWOT and risk matrix axis labels, calendar weekday headers, drawer and metadata labels, the OAuth consent screen and the collapsed-sidebar flyout titles. Reference codes (.ref) deliberately stay uppercase mono - they are audit-grade identifiers, not labels. The--sidebar-glasstokens are generalized as--glass/--glass-hover, and the notification list inside the sidebar adopts the invisible scrollbar. - CI actions upgraded to Node 24 runtimes: GitHub deprecates Node 20 action runtimes (forced to Node 24 on June 16, 2026; removed from runners on September 16, 2026). The workflows move to the latest majors, all running on Node 24:
actions/checkoutv4 -> v6,actions/setup-pythonv5 -> v6,docker/setup-buildx-actionv3 -> v4,docker/login-actionv3 -> v4,docker/metadata-actionv5 -> v6,docker/build-push-actionv6 -> v7.
Full changelog: v0.26.1...v0.26.2
v0.26.1
Changed
- Dashboard alerts become "Today's actions": the red global-alerts banner at the top of the dashboard is replaced by a calm "Today's actions" card. The same signals (critical risks, non-compliant requirements, overdue action plans, unassigned mandatory roles, ownerless critical activities, end-of-life assets, expired supplier contracts, expiring risk acceptances) are now phrased as actionable to-do items ("Treat 3 critical risk(s)" instead of "3 critical risk(s)"), grouped by priority (Priority / To plan / To watch, each on a soft-tinted panel with a semantic dot), and each item links to the page where the user can act. The card carries a navy top accent, an icon chip and a count badge in the header, and items render as raised rows with a calm hover lift. Renders in three columns on desktop and stacks on mobile, in both themes.
- Bolder overall-compliance score: the large percentage on the dashboard's "Overall compliance" card moves from weight 600 to 700 (the heaviest Inter weight allowed by the brand guidelines) so the headline figure stands out.
- Sidebar redesign: the main menu becomes a flat, flush panel: page-background colour, no border or rounded corners, glued to the window edges, with a subtle hairline on its right edge and an invisible scrollbar. Two floating glass elements (translucent, backdrop-blurred) frame the scrollable menu: a search field at the top showing the platform-aware shortcut (Cmd K on macOS, Ctrl K elsewhere) and opening the global search overlay, and a user footer at the bottom (avatar, name, email) whose ellipsis expands an inline Profile / Sign out menu that closes after use. Menu entries slide beneath the glass while scrolling. Section labels move to sentence case, and the user avatar and its dropdown leave the floating top-right bubble (search, about and notifications stay there). In collapsed mode the search shrinks to an icon and the footer to the avatar.
- Search overlay entrance animation: opening the search from the sidebar field morphs the dialog from the trigger's position and shape to its centered resting place (FLIP transform, 320 ms ease-out-expo). Ctrl+K keeps the plain fade and
prefers-reduced-motiondisables the flight. - Everything moves into the sidebar; the top-right bubble is gone: notifications live in the user footer - the bell (with its unread badge) replaces the ellipsis next to the avatar and expands an inline panel above the user row, animated like the user menu (both are Bootstrap collapses, mutually exclusive, closed by outside clicks and Escape); when the sidebar is collapsed a notification dot shows on the avatar instead. The About entry joins the user menu (Profile / About / Sign out) and the About modal gains a link to the GitHub repository. With search, notifications and About now in the sidebar, the floating top-right button bubble is removed, along with the dashboard's real-time connection dot (the live updates themselves are untouched). The brand area loses its border for a gradient veil under which scrolling entries fade out progressively, and the menu's right hairline fades out toward the top and bottom of the window.
Full changelog: v0.26.0...v0.26.1
v0.26.0
Fixed
- Overall-compliance caption counted every requirement: the dashboard's "Overall compliance" card claimed "N requirements tracked" using the full requirement inventory, while the displayed average only covers validated active frameworks, so a draft framework's requirements inflated the caption. The caption now counts the applicable requirements of the frameworks that actually feed the average (new
tracked_requirement_count), the inventory stat card keeps the full count, and the live WebSocket refresh applies the same reportable filter to the average as the page render. The caption's French translation also gains a real singular / plural form (it previously rendered "3 exigence suivie" because of an illegal filter insideblocktrans). - Modal step gating broken by rich-text fields: the step-completion check of the modal form engine read the first input inside each required field wrapper, which on rich-text fields is an unnamed internal input injected by the Jodit editor (always empty), so forms like the scope create / edit modal refused to advance past a step whose required fields were all filled. The check now reads the first named control of the wrapper (the real, synced form field). Also hardened two sidebar event handlers against non-element event targets (console
TypeErroron text-node hover).
Changed
- List tables show a single lifecycle column: every domain list table now renders one Status column carrying the element's lifecycle state (
workflow_badge), and the legacy Status / Approval column pair is gone. The 27 list views are aligned: the bespoke status badges (context binary toggles, asset ITAM states, compliance and risk workflows, management reviews) are replaced by the unified badge, the per-table "Approval" column (Approved / Pending) is removed everywhere, and the lifecycle column is sortable onworkflow_state(this also repairs the scope / SWOT / risk-criteria headers, whose sort key did not match the view since the publication-status retirement; the risk-criteria list also still rendered the removedstatusfield as a permanently empty badge). The asset, supplier and site dependency lists and the requirement list, which only showed the approval flag, gain the sortable lifecycle column in its place. The now-unusedapproval_enabled_fortemplate tag and itsversioning_tagslibrary are deleted. Operational state filters above the tables (e.g. active / inactive stakeholders) are untouched: they filter the non-governing attribute, which remains visible on detail pages.
Removed
- Legacy approval bar retired: the per-page "Pending approval / Approve" banner is removed from all 25 detail pages, along with the
approval_bannerstyleguide component, its template tag and its CSS - the lifecycle stepper carries the state and the validation action (the Validate step is the approval act). The approve endpoints remain for API / MCP compatibility as deprecated aliases. - Publication
statusfields folded into the lifecycle: the Scope, Site, SWOT analysis and Risk criteria models lose theirstatusfield (and the matching enums) - their draft / active / archived vocabulary is now fully carried by the unified lifecycle (activeandvalidatedfold into thevalidatedstate,archivedstaysarchived, data migrations included with history). Everything that exposed those statuses moves to the lifecycle: forms (the status selects disappear, transitions go through the stepper), scope and site pickers, list filters and sortable columns, Django admin, the REST serializers / filters / ordering (thestatusfield is replaced byworkflow_state), the MCP tools (filter and fields renamed; the state changes throughtransition_*), and the templates now render theworkflow_badge. The scopearchiveAPI action becomes a lifecycle transition (archiving a draft is now correctly rejected) and the SWOTvalidateaction drives the approval axis. Framework and Requirement deliberately keep theirstatus(under_review/deprecated/supersededare versioning semantics the 4-state lifecycle does not cover - spec option (b)).
Added
- Lifecycle workflow documentation: the canonical framework spec lands in
docs/modules/governance/workflow.md(architecture, the default and the fifteen specific workflows with their governance flags, the as-built rules, the REST / MCP / UI surfaces and the recorded decisions); the entity files of the four retired publication statuses point at it, the README feature table describes the unified lifecycle, and the CLAUDE.md development guidelines now reference the generic stepper (WorkflowStepperMixin+includes/workflow_stepper.html) and the workflow registry instead of the removed bespoke implementations. - Workflow framework (foundation): a new
core/workflow.pyintroduces the lifecycle engine that will unify the approval and per-model status systems (see issue #105). AWorkflowis an ordered set ofStateobjects, each carrying governance flags (counts_in_reports,linkable,deletable,is_initial,is_terminal), plus the allowedTransitionobjects (required permission action, optional mandatory comment, declarative side effects). The module ships the default 4-state lifecycle (draft->pending->validated->archived), a workflow registry, permission-aware transition validation (validate_transition/allowed_transitions/apply_transition) and queryset helpers (reportable_states/linkable_states/deletable_states). Pure-Python and fully unit tested (27 tests); no model, database or UI wiring yet (those land in the following phases). - Action plan specific workflow: the compliance action plan is the first entity to run its own registered lifecycle workflow (
action_plan), generated from the existing transition constants so the 8-state machine keeps a single source of truth. Each state carries its governance flags:new/to_defineare deletable drafting states,to_validateonwards count in reports,to_implement/implementation_to_validate/validatedare linkable,closed/cancelledare terminal; refusals keep their mandatory comment and per-step permissions (update,validate,implement,close,cancel) are enforced per transition. The model'stransition_to()is now routed through the framework while preserving its legacy contract (ValueError on an illegal transition or missing refusal comment, completion fields auto-filled on close,ActionPlanTransitionaudit rows); the legacystatusfield andworkflow_stateare kept in sync both ways during the migration period, and a data migration aligns existing rows (including history). Linking and deletion governance now use the real action plan states (e.g. onlyto_implement/implementation_to_validate/validatedplans can be linked to a treatment plan). Workflows can now also be declared per model in code (WORKFLOW_NAME), with the DB assignment still taking precedence. - Compliance assessment specific workflow: the compliance assessment now runs its own registered lifecycle workflow (
compliance_assessment), generated from the existingASSESSMENT_STATUS_TRANSITIONSconstants. Per-state governance: onlydraftassessments can be deleted;planned/in_progress/completed/closedcount in reports and on the calendar whiledraftandcancelleddo not;closed/cancelledare terminal. The model'stransition_to()is routed through the framework while keeping its legacy contract (single status argument, ValueError on an illegal transition, EVALUATED-results reset when completing) andstatus/workflow_statestay in sync both ways, with a data migration aligning existing rows including history. - Management review specific workflow: the management review (ISO 27001 clause 9.3) now runs its own registered lifecycle workflow (
management_review), generated from the existing transition constants. Per-state governance: only aplannedreview can be deleted;cancelledreviews leave reports;closed/cancelledare terminal. The closure transition (held->closed) is declared with theapprovepermission action, matching the rule the API already enforced, and cancellation keeps its mandatory comment, now enforced declaratively. The model'stransition_to()is routed through the framework while keeping its legacy contract (ValueError, closure preconditions viacan_close(),held_dateauto-set,ManagementReviewTransitionaudit rows, snapshot flow untouched);status/workflow_statestay in sync both ways, with a data migration aligning existing rows including history. - Asset specific workflows: essential and support assets now run their own registered lifecycle workflows (
essential_asset,support_asset). Their statuses had no transition constants (freely editable), so the workflows encode the natural ITAM progressions (essential: identified -> active <-> under review -> decommissioned; support: in stock -> deployed -> active <-> under maintenance -> decommissioned -> disposed, with decommissioning reachable from any active state). Governance: every state stays reportable (decommissioned and disposed assets belong to audit history), decommissioned / disposed assets are no longer linkable (declarative version of RS-04) and cannot be deleted; only each model's creation-default states remain deletable (identified for essential assets, in stock / active for support assets). Legacy free status edits keep working through the status / workflow_state sync, now factored into a sharedsync_legacy_status()helper used by all five reconciled entities, and a data migration aligns existing rows including history. - Risk process specific workflows: the risk, treatment plan, risk acceptance and vulnerability now run their own registered lifecycle workflows, encoding the natural ISO 27005 progressions (these statuses were freely editable). ...