Redesign document approval flow

Replace the per-approver add/remove model with a quorum-based approval
system. Documents now have default approvers that are pre-populated when
requesting approval, and the publish dialog lets users adjust the list
before submitting.

Key changes:

- Add PENDING_APPROVAL document version status with dedicated transitions
- Introduce approval quorums with request/approve/reject/void lifecycle
- Add default approvers per document (stored in document_default_approvers)
  with MERGE-based upsert for efficient sync
- Add NoDuplicates validator for slice fields
- Split ALTER TYPE ADD VALUE migrations into separate files (required by
  PostgreSQL when run inside transactions)
- Use VOIDED consistently for both quorum status and decision state enums
- Expose void/approve/reject through GraphQL and MCP, with e2e tests
- Add approval management UI: publish dialog with approver selection,
  approval list with void support, and external approve/reject page

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-04-09 11:17:51 +02:00
parent 17f579b8a2
commit ab5f42ad74
38 changed files with 2125 additions and 922 deletions

View File

@@ -2095,6 +2095,7 @@ func (r *Resolver) AddDocumentTool(ctx context.Context, req *mcp.CallToolRequest
Classification: input.Classification,
DocumentType: input.DocumentType,
TrustCenterVisibility: trustCenterVisibility,
DefaultApproverIDs: input.DefaultApproverIds,
},
)
if err != nil {
@@ -2109,12 +2110,18 @@ func (r *Resolver) UpdateDocumentTool(ctx context.Context, req *mcp.CallToolRequ
svc := r.ProboService(ctx, input.ID)
var defaultApproverIDs *[]gid.GID
if input.DefaultApproverIds != nil {
defaultApproverIDs = &input.DefaultApproverIds
}
document, err := svc.Documents.Update(
ctx,
probo.UpdateDocumentRequest{
DocumentID: input.ID,
Title: input.Title,
TrustCenterVisibility: input.TrustCenterVisibility,
DefaultApproverIDs: defaultApproverIDs,
},
)
if err != nil {
@@ -3928,3 +3935,18 @@ func (r *Resolver) ListMeasureDocumentsTool(ctx context.Context, req *mcp.CallTo
return nil, types.NewListMeasureDocumentsOutput(docPage), nil
}
func (r *Resolver) VoidDocumentVersionApprovalTool(ctx context.Context, req *mcp.CallToolRequest, input *types.VoidDocumentVersionApprovalInput) (*mcp.CallToolResult, types.VoidDocumentVersionApprovalOutput, error) {
r.MustAuthorize(ctx, input.DocumentVersionID, probo.ActionDocumentVersionVoidApproval)
svc := r.ProboService(ctx, input.DocumentVersionID)
_, documentVersion, err := svc.DocumentApprovals.VoidApproval(ctx, input.DocumentVersionID)
if err != nil {
panic(fmt.Errorf("cannot void document version approval: %w", err))
}
return nil, types.VoidDocumentVersionApprovalOutput{
DocumentVersion: types.NewDocumentVersion(documentVersion),
}, nil
}

View File

@@ -5159,6 +5159,7 @@ components:
type: string
enum:
- DRAFT
- PENDING_APPROVAL
- PUBLISHED
go.probo.inc/mcpgen/type: go.probo.inc/probo/pkg/coredata.DocumentVersionStatus
@@ -5495,6 +5496,11 @@ components:
trust_center_visibility:
$ref: "#/components/schemas/TrustCenterVisibility"
description: Trust center visibility
default_approver_ids:
type: array
items:
$ref: "#/components/schemas/GID"
description: Default approver profile IDs
AddDocumentOutput:
type: object
@@ -5522,6 +5528,11 @@ components:
trust_center_visibility:
$ref: "#/components/schemas/TrustCenterVisibility"
description: Trust center visibility
default_approver_ids:
type: array
items:
$ref: "#/components/schemas/GID"
description: Default approver profile IDs
UpdateDocumentOutput:
type: object
@@ -5860,6 +5871,23 @@ components:
$ref: "#/components/schemas/GID"
description: Deleted document version signature ID
VoidDocumentVersionApprovalInput:
type: object
required:
- document_version_id
properties:
document_version_id:
$ref: "#/components/schemas/GID"
description: Document version ID
VoidDocumentVersionApprovalOutput:
type: object
required:
- document_version
properties:
document_version:
$ref: "#/components/schemas/DocumentVersion"
MeetingOrderField:
type: string
enum:
@@ -8474,6 +8502,15 @@ tools:
$ref: "#/components/schemas/CancelSignatureRequestInput"
outputSchema:
$ref: "#/components/schemas/CancelSignatureRequestOutput"
- name: voidDocumentVersionApproval
description: Void a pending document version approval request
hints:
readonly: false
destructive: true
inputSchema:
$ref: "#/components/schemas/VoidDocumentVersionApprovalInput"
outputSchema:
$ref: "#/components/schemas/VoidDocumentVersionApprovalOutput"
- name: listMeetings
description: List all meetings for the organization
hints: