Add document archiving

Documents can be archived and unarchived. Archived documents are
read-only, excluded from the trust center, and moved to a dedicated
Archived tab in the document list.

- Add archived_at timestamp and status (ACTIVE/ARCHIVED) PG enum column
- Rename DocumentStatus → DocumentVersionStatus, introduce DocumentStatus
- Archive/unarchive mutations in GraphQL, MCP, and CLI
- Bulk archive/unarchive mutations with Active/Archived tabs in the list
- ABAC policies: write actions denied on archived docs, unarchive denied
  on active docs
- Remove control/risk mappings and reset trust center visibility on archive
- Exclude archived documents from mapping dialogs and trust center tab

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-03-19 14:15:16 +01:00
parent 2e12c11c0c
commit 1db8e7133e
29 changed files with 1194 additions and 199 deletions

View File

@@ -799,7 +799,7 @@ func (r *Resolver) AddFindingTool(ctx context.Context, req *mcp.CallToolRequest,
Kind: input.Kind,
Description: input.Description,
Source: input.Source,
IdentifiedOn: input.IdentifiedOn,
IdentifiedOn: input.IdentifiedOn,
RootCause: input.RootCause,
CorrectiveAction: input.CorrectiveAction,
OwnerID: input.OwnerID,
@@ -830,7 +830,7 @@ func (r *Resolver) UpdateFindingTool(ctx context.Context, req *mcp.CallToolReque
ID: input.ID,
Description: UnwrapOmittable(input.Description),
Source: UnwrapOmittable(input.Source),
IdentifiedOn: UnwrapOmittable(input.IdentifiedOn),
IdentifiedOn: UnwrapOmittable(input.IdentifiedOn),
RootCause: UnwrapOmittable(input.RootCause),
CorrectiveAction: UnwrapOmittable(input.CorrectiveAction),
OwnerID: input.OwnerID,
@@ -3201,3 +3201,43 @@ func (r *Resolver) ListFindingAuditsTool(ctx context.Context, req *mcp.CallToolR
return nil, types.NewListFindingAuditsOutput(auditPage), nil
}
func (r *Resolver) ArchiveDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.ArchiveDocumentInput) (*mcp.CallToolResult, types.ArchiveDocumentOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionDocumentArchive)
svc := r.ProboService(ctx, input.ID)
document, err := svc.Documents.Archive(ctx, input.ID)
if err != nil {
return nil, types.ArchiveDocumentOutput{}, fmt.Errorf("cannot archive document: %w", err)
}
approverPage, err := svc.Documents.ListApprovers(ctx, input.ID, allApproversCursor())
if err != nil {
return nil, types.ArchiveDocumentOutput{}, fmt.Errorf("cannot list document approvers: %w", err)
}
return nil, types.ArchiveDocumentOutput{
Document: types.NewDocument(document, profileIDs(approverPage)),
}, nil
}
func (r *Resolver) UnarchiveDocumentTool(ctx context.Context, req *mcp.CallToolRequest, input *types.UnarchiveDocumentInput) (*mcp.CallToolResult, types.UnarchiveDocumentOutput, error) {
r.MustAuthorize(ctx, input.ID, probo.ActionDocumentUnarchive)
svc := r.ProboService(ctx, input.ID)
document, err := svc.Documents.Unarchive(ctx, input.ID)
if err != nil {
return nil, types.UnarchiveDocumentOutput{}, fmt.Errorf("cannot unarchive document: %w", err)
}
approverPage, err := svc.Documents.ListApprovers(ctx, input.ID, allApproversCursor())
if err != nil {
return nil, types.UnarchiveDocumentOutput{}, fmt.Errorf("cannot list document approvers: %w", err)
}
return nil, types.UnarchiveDocumentOutput{
Document: types.NewDocument(document, profileIDs(approverPage)),
}, nil
}

View File

@@ -5057,11 +5057,18 @@ components:
- SECRET
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.DocumentClassification
DocumentStatus:
DocumentVersionStatus:
type: string
enum:
- DRAFT
- PUBLISHED
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.DocumentVersionStatus
DocumentStatus:
type: string
enum:
- ACTIVE
- ARCHIVED
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.DocumentStatus
DocumentVersionSignatureState:
@@ -5142,6 +5149,7 @@ components:
- document_type
- classification
- trust_center_visibility
- status
- created_at
- updated_at
properties:
@@ -5173,6 +5181,15 @@ components:
trust_center_visibility:
$ref: "#/components/schemas/TrustCenterVisibility"
description: Trust center visibility
status:
$ref: "#/components/schemas/DocumentStatus"
description: Document status
archived_at:
type:
- string
- "null"
format: date-time
description: Archive timestamp
created_at:
type: string
format: date-time
@@ -5228,8 +5245,8 @@ components:
type: string
description: Changelog
status:
$ref: "#/components/schemas/DocumentStatus"
description: Document status
$ref: "#/components/schemas/DocumentVersionStatus"
description: Document version status
published_at:
type:
- string
@@ -5434,6 +5451,40 @@ components:
document:
$ref: "#/components/schemas/Document"
ArchiveDocumentInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Document ID
ArchiveDocumentOutput:
type: object
required:
- document
properties:
document:
$ref: "#/components/schemas/Document"
UnarchiveDocumentInput:
type: object
required:
- id
properties:
id:
$ref: "#/components/schemas/GID"
description: Document ID
UnarchiveDocumentOutput:
type: object
required:
- document
properties:
document:
$ref: "#/components/schemas/Document"
ListDocumentVersionsInput:
type: object
required:
@@ -7133,6 +7184,22 @@ tools:
$ref: "#/components/schemas/UpdateDocumentInput"
outputSchema:
$ref: "#/components/schemas/UpdateDocumentOutput"
- name: archiveDocument
description: Archive a document to prevent further modifications
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/ArchiveDocumentInput"
outputSchema:
$ref: "#/components/schemas/ArchiveDocumentOutput"
- name: unarchiveDocument
description: Unarchive a document to allow modifications again
hints:
readonly: false
inputSchema:
$ref: "#/components/schemas/UnarchiveDocumentInput"
outputSchema:
$ref: "#/components/schemas/UnarchiveDocumentOutput"
- name: listDocumentVersions
description: List all versions for a document
hints:

View File

@@ -30,6 +30,8 @@ func NewDocument(d *coredata.Document, approverIDs []gid.GID) *Document {
Classification: d.Classification,
CurrentPublishedVersion: d.CurrentPublishedVersion,
TrustCenterVisibility: d.TrustCenterVisibility,
Status: d.Status,
ArchivedAt: d.ArchivedAt,
CreatedAt: d.CreatedAt,
UpdatedAt: d.UpdatedAt,
}