Skip to content

Documents module: policies, versions, review cycle and acknowledgement campaigns - #183

Merged
frousselet merged 8 commits into
mainfrom
feat/ui-audit-framework
Nov 3, 2026
Merged

frousselet merged 8 commits into
mainfrom
feat/ui-audit-framework

Conversation

@frousselet

Copy link
Copy Markdown
Owner

Summary

A new Documents module (module 7) : the register of the ISMS's documented information (ISO/IEC 27001:2022 clause 5.2, 7.5, A.5.1). Policies, procedures, standards, charters and plans with frozen, approved versions (one in force at a time), a review cycle with reminders, and acknowledgement campaigns whose confirmations are kept as write-once evidence. Integrated with the management review, the Trust Center, the calendar, the dashboard, the kanban, search and the indicators, and exposed on the web, the REST API and MCP. Specified first in docs/specs/m7-documents/.

Related issue

None.

Type of change

  • Bug fix
  • New feature
  • Refactor / tech debt
  • Documentation
  • Build / CI / tooling

Changes

  • 6efb48b Spec: docs/specs/m7-documents/ (README + four entity files) and the related specs (Trust Center document, ISMS change, contract, index).
  • 342d414 Domain: Policy (PLCY), PolicyVersion (PLVR), AcknowledgementCampaign (ACKC), PolicyAcknowledgement; three lifecycles with an explicit, guarded archived exit and no restore edge; gates and side effects in transition_to() (supersession in the same transaction, approval stamps, frozen content, PDF with SHA-256, audience resolved once at launch, write-once acknowledgements only the addressee can give); a partial unique constraint for "one version in force"; 10 permission codenames on the six system groups; five notification types.
  • 2483610 REST API under /api/v1/documents/, plus three domain gaps it surfaced (row creation history, no new version of a retired document, frozen effective date).
  • 62e4b0c Security: the advanced filter of every list rendered its relation picker options unscoped (names of records outside the reader's scopes in the page); lifecycle_name_for("app.Model") silently fell back to the default lifecycle.
  • 4c374a6 Web interface: registers, detail pages, drawer forms, delete shells, "To acknowledge" (own rows only), PDF download, sidebar, module accent, help banners, user guide, French catalogue.
  • b84e8b3 Integrations: IsmsChange.affected_documents and a "Documents due for review" card on the review; TrustCenterDocument.policy serving the version in force behind the publication gate; search, calendar, dashboard widget, kanban, two predefined indicators; the documents_sweep reminders command; the Voltara seed.
  • bb9b8de MCP: CRUD / transitions / history for the three entities, plus new_policy_version, record_policy_review, get_policy_in_force, get_campaign_progress, list_my_pending_acknowledgements, acknowledge_policy_version and the campaign row tools; assistant catalogue entries; regenerated reference.
  • 58b9cc9 Docs: README, screenshots, spec marked implemented.

Screenshots

docs/screenshots/documents.png, document-detail.png, acknowledgement-campaigns.png, to-acknowledge.png (2560x1440). Every documents page was captured in light / dark at 1600 px and 390 px during development: HTTP 200, no horizontal overflow, no console error.

Checklist

  • Tests added or updated, and pytest passes
  • ruff check passes
  • New feature exposed as MCP tools in mcp/tools.py (accurate docstrings and parameter descriptions)
  • New feature has REST API endpoints under the app's api/ (serializers, viewsets, routes under /api/v1/)
  • UI renders correctly in light and dark mode
  • UI renders correctly on mobile (multi-selects, sticky bars, form layouts)
  • User-facing strings use _() / {% trans %} with French translations in locale/fr/LC_MESSAGES/django.po (no duplicate msgid; compilemessages passes)
  • Lifecycle / workflow respected (state metadata and generic stepper UI; no hardcoded status values)
  • README.md updated (features, MCP tools, tech stack, install) when behaviour changed
  • CHANGELOG.md updated under ## [Unreleased]
  • Brand guidelines respected (docs/brand/brand-guidelines.md); no em dash characters
  • Relevant spec under docs/specs/ updated in the same PR
  • Project / documentation screenshots captured at 2560x1440 (16:9, 1440p)
  • Audit-grade rigor preserved (approval workflows, versioning, history, permission checks not bypassed)

Verification: full suite 3,457 passed, ruff clean, generate_docs --check and build_wiki.py --check clean, compilemessages clean.

Known limits (spec §11): no rendered diff between versions, no exceptions register, no electronic signature, no future-dated entry into force. PDF rendering of rich text needs WeasyPrint's system libraries (present in the Docker image); without them the download falls back to a message instead of a 500.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC

…ledgement)

Proposed for v0.37.0: a register of the ISMS's documented information
(ISO 27001 clause 5.2, 7.5, A.5.1) in a new documents app.

- Policy: the document identity (type, owner, classification, review
  cycle, scopes, requirement links, parent / child structure) running a
  draft / active / retired lifecycle.
- PolicyVersion: frozen-once-submitted content (rich text and / or a
  PDF with its SHA-256), change summary, approval stamped by the
  transition, at most one version in force with automatic, recorded
  supersession (backed by a partial unique constraint).
- AcknowledgementCampaign and PolicyAcknowledgement: an audience of
  users, groups and roles resolved once at launch into write-once rows
  only the addressee can acknowledge, reminders, completion rate.
- Review cycle derived from the version in force, "reviewed, no
  change" as a recorded act, reminders, calendar, dashboard, kanban.
- IsmsChange.affected_documents and a Trust Center document source
  that always serves the version in force.

The related specs (Trust Center document, ISMS change, contract, the
specs index) are updated to point at it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
A new documents app (module 7) with its four models, three lifecycles,
permissions and notifications, implementing the RG-DOC rules of
docs/specs/m7-documents/:

- Policy (PLCY): identity, type, classification, owner / approver,
  parent document, requirement and role links, a derived next review
  date, record_review() and new_version(); draft / active / retired.
- PolicyVersion (PLVR): rich text sanitized while draft and a PDF with
  its SHA-256, frozen from submission; putting a version in force
  stamps the approval and supersedes the previous one in the same
  transaction through its own recorded transition, backed by a partial
  unique constraint (one version in force per document).
- AcknowledgementCampaign (ACKC): audience of users, groups and roles
  resolved once at launch into write-once PolicyAcknowledgement rows
  that only the addressee can acknowledge; closing or superseding
  expires the pending rows; excusing needs a reason.

Gates and side effects live in each model's transition_to() and raise
DomainRefusalError, so the generic stepper endpoint, REST and MCP all
apply them. Each lifecycle declares its archived exit explicitly (no
unguarded archive / restore edge) and no restore transition.

The documents permission features (10 codenames) are attached to the
six system groups by migration; five notification types are added.
The web views are placeholders until the interface lands. The spec is
aligned with these choices.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
/api/v1/documents/ with policies, policy-versions,
acknowledgement-campaigns and acknowledgements, on the house pattern
(ModulePermission, scope filter, /transition/, history, batch create):

- record-review and new-version (minor / major) on a document,
  add-newcomers on a campaign, mine / acknowledge / excuse on the
  acknowledgement rows; acknowledging is the addressee's own act
  (channel "api"), refused to anyone else, administrators included;
- a submitted version refuses changes to its frozen fields with a 400
  naming the field, a launched campaign its version, statement and
  audience; file_content is never serialized (has_pdf, pdf_sha256,
  document_url instead); DomainRefusalError becomes a 400 with its
  reason.

Three domain gaps surfaced by the API are closed in the models:
acknowledgement rows are created with their history
(bulk_create_with_history), a retired document refuses a new version,
and an approved version's effective date is frozen.

generate_docs now includes the documents app in models.md and an MCP
module title for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
AdvancedFilterMixin listed up to 200 rows of every related model as the
options of its relation pickers, unscoped, and rendered them into the
list page: a user restricted to one scope read the names of the risk
assessments, documents, suppliers... of every other scope. The options
of a model with a scope lookup are now filtered through the reader's
allowed scopes (superusers and unrestricted users unchanged; people and
tags, which have no scope, are listed as before). Found while building
the documents lists; test on the risk register's assessment picker.

Also: lifecycle_name_for() given an "app_label.Model" string resolved
nothing and fell back to the default lifecycle, so
reportable_states("documents.Policy") answered {"validated"}; it now
resolves the label to its model. The Trust Center certification gate
passed such a label and only worked because frameworks run the default
lifecycle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
Every route of documents/urls.py, on the framework anatomies:

- the document register and the campaign list (rail tiles, facets
  including "review overdue", scoped relation filters);
- document, version and campaign detail pages with the lifecycle
  stepper, the version in force rendered with its approval line, PDF
  and SHA-256, the versions table, structure, requirements, campaigns,
  "Reviewed, no change" and "New version" actions, the audience table
  with "Add newcomers" and "Excuse";
- drawer create / edit forms with one-line page fallbacks (creating a
  document drafts its version 1.0), delete confirmations on the shared
  shells; a submitted version, a launched campaign and a retired
  document open read-only;
- "To acknowledge", every user's own pending rows, reachable from the
  user menu with a count: a row is only ever the current user's own
  (anyone else's is a 404, superusers included);
- the PDF download (attached PDF or the rich text rendered by
  WeasyPrint), permission and scope checked, also open to the addressee
  of a campaign on that version.

Wiring: the Documents sidebar section, NAV_TREE entries, the
"documents" module accent in both themes, an acknowledgement_status
badge scale, the five help banners, the French catalogue for the whole
module, and docs/user-guide/documents.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
…nd the seed

- Management review: IsmsChange.affected_documents (M2M -> Policy,
  linkable_or_linked picker, the legacy free-text field kept), exposed
  in the API; the review page lists each change's documents and a
  "Documents due for review" card (due in the period or overdue at the
  review date, within the review's and the reader's scopes).
- Trust Center: TrustCenterDocument.policy as a third exclusive source
  (public classification, active document); the published() gate drops
  an entry whose policy is retired, reclassified or has no version in
  force; the download serves the version in force through render_pdf().
- Global search (documents and versions), a "documents" calendar
  category (review dates, campaign due dates), a Documents dashboard
  widget with its context and cache invalidation, the current user's
  pending acknowledgements in the kanban To do column, and the
  policies_overdue_review / acknowledgement_rate predefined indicators.
- documents_sweep: review due / overdue and acknowledgement reminders,
  each sent once, with --dry-run; scheduled as a host cron line like
  the other sweeps (operations and installation guides).
- The Voltara seed gains a documents phase: seven documents, ten
  versions (superseded ones included, one with a PDF), a draft, an
  overdue review, a running and a closed campaign, an ISMS change and a
  Trust Center publication, all through the recorded transitions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
_register_documents_tools() exposes the module to MCP clients:

- CRUD, transition, allowed transitions and history for documents,
  versions and campaigns, with scope checks and the frozen-field rule on
  version and campaign updates;
- new_policy_version, record_policy_review, get_policy_in_force,
  get_campaign_progress, add_campaign_newcomers,
  list_policy_acknowledgements, excuse_policy_acknowledgement;
- list_my_pending_acknowledgements and acknowledge_policy_version, the
  caller's own act (channel "mcp"), which need no permission by design;
- the PLCY / PLVR / ACKC reference prefixes and a documents help topic.

The management review and Trust Center tools accept and return the new
affected_document_ids / policy fields, and the assistant catalogue
gains three read-only documents tools. The generated reference is
regenerated for the module (models, permissions, lifecycles, REST
routes, MCP tools, dashboard widget).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
The README feature table gains the Documents module; the user guide
page shows the register, a document, the campaigns and the "To
acknowledge" page, captured at 2560x1440 by capture_screenshots.py
(which now includes them); the module spec is marked implemented with
its wiring checklist ticked, and the related specs drop their
"proposed" notes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaEkJnhS3VNDeb6jrdn6MC
@frousselet
frousselet merged commit 5b9c741 into main Nov 3, 2026
7 checks passed
@frousselet
frousselet deleted the feat/ui-audit-framework branch November 3, 2026 20:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants