Commit Graph

337 Commits

Author SHA1 Message Date
Aurélien Sibiril
6887294c9e Rebuild AgentRunSupervisor on go.gearno.de/kit/worker
The supervisor was a hand-rolled polling, semaphore, and wait-group
loop predating the project's adoption of the shared worker kit. Two
sibling workers in pkg/probo already use the kit, and go-worker.md
documents it as the project convention.

This commit introduces agentRunHandler, which implements
worker.Handler[coredata.AgentRun] and worker.StaleRecoverer, and
reduces AgentRunSupervisor to a thin wrapper that owns the handler
plus a worker.Worker and bridges ctx cancellation into a handler-
level shutdown broadcast via context.AfterFunc. The agent stop
channel is now closed by a per-Process forwarder goroutine when the
broadcast fires, so in-flight runs checkpoint at the next turn
boundary and drain through wg.Wait before Run returns.

The stop_requested column, struct field, supporting SQL, and the
LoadRunningStopRequestedIDs function are removed end-to-end. None
of it was ever wired to an external surface; it existed purely to
let the supervisor find runs the operator wanted to halt. With the
kit handling the polling cadence and the AfterFunc bridging
shutdown, per-row flagging is dead weight.

The supervisor's public API (NewAgentRunSupervisor, Run, the With*
option helpers, and the error sentinels) stays intact so probod.go
needs no change. The integration test now triggers stop by
cancelling the supervisor context, which is the actual production
path through SIGTERM rather than a synthetic DB flag. Prometheus
counters and OTel spans labelled worker="agent-run-supervisor"
come for free.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-04-26 22:58:21 +02:00
Aurélien Sibiril
2f2b8fe6aa Add AgentRun entity and PG checkpoint store
Create agent_runs table with lease-based concurrency control.
AgentRun entity follows standard coredata patterns with Scoper,
StrictNamedArgs, and cursor pagination. PGCheckpointStore implements
agent.CheckpointStore backed by the checkpoint JSONB column with
version validation and 10 MiB size guard. Register AgentRunEntityType
as entity type 75.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-04-26 22:58:21 +02:00
Émile Ré
dd42affd98 Drop SQL column defaults after migration for cookie_policy_url and sdk_version
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 19:01:27 +04:00
Émile Ré
d7eec08cd2 Add banner ID to probo_consent cookie
Include a `bid` field in the consent cookie so it explicitly
identifies which cookie banner it belongs to, making validation
direct instead of relying on the visitor ID as an implicit
discriminator. Existing cookies without `bid` self-heal on the
next load by falling through to the API fetch.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:40 +04:00
Émile Ré
598c6b112c Add cookie_policy_url field to cookie banners
Introduce a required cookie_policy_url alongside the existing
privacy_policy_url (now optional) so banners can link directly to a
dedicated cookie policy — a compliance best practice recommended by
CNIL, ICO, and the EDPB. Existing rows are seeded from their current
privacy_policy_url value.

Both {{cookie_policy_link}} and {{privacy_policy_link}} placeholders
are supported independently in banner description translations.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 18:48:39 +04:00
Bryan Frimin
976acbd114 Surface domain provisioning errors to users
When DNS verification, CAA checks, or HTTP challenge completion
fail during certificate provisioning, the error is now stored on
the custom domain record and exposed via GraphQL. The console
displays it in both the domain card and domain detail dialog so
users can diagnose configuration issues without checking logs.

Previously these failures returned an error that was only logged
server-side, leaving users with no visibility into why their
domain was stuck in a pending state.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-04-24 16:05:11 +02:00
Émile Ré
9fbb716b00 Constrain PostHog consent to one normal category per banner
Add a partial unique index ensuring only one category per banner can
have posthog_consent enabled. Default it to the analytics category on
banner creation, clear the previous mapping before setting a new one,
and restrict the toggle to NORMAL categories in both the service layer
and the console UI.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 17:13:12 +04:00
Émile Ré
7f1dffad80 Add PostHog consent integration and extract integration plugin system
Add PostHog opt-in/opt-out consent support mirroring the existing Google
Consent Mode integration: database column, GraphQL field, console UI
toggle, and client-side posthog-js calls.

Extract both GCM and PostHog logic from CookieBannerClient into a
ConsentIntegration plugin interface so future integrations can be added
without modifying the client core.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 17:13:12 +04:00
Émile Ré
2b15a1684c Add gcm_consent_types column to cookie_categories
Stores the Google Consent Mode v2 consent type mapping
per cookie category so each category can declare which
GCM signals it controls (e.g. analytics_storage,
ad_storage).

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 17:13:11 +04:00
Émile Ré
2b6f131f43 Fix PR review comments on cookie banner i18n
Address locale normalization for region-tagged values, guard
language detection for non-DOM runtimes, validate DefaultLanguage
on update, pass translated texts through the deactivation flow,
handle slug collisions in migration, add organizations FK, fix
consent migration from name-keyed to slug-keyed data, render all
template placeholders in previews, and wrap helper text for i18n.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:12 +04:00
Émile Ré
165b9ad9d3 Add slug to cookie categories for stable consent identifiers
The category slug provides a stable, URL-safe key used as the
data-cookie-consent attribute value and consent data key, replacing
the fragile category name. This prevents breakage when categories
are renamed.

- Add slug column with unique-per-banner constraint and backfill migration
- Add Slug validator (lowercase alphanumeric + hyphens)
- Propagate slug through GraphQL schema, service layer, and snapshot
- Update console UI with slug field in create/edit forms
- Switch cookie-banner widget to use slug as consent data keys

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:10 +04:00
Émile Ré
d189913ba3 Add i18n data structures for cookie banner
Add cookie_banner_translations table to store per-language
translations as JSONB, and a default_language column on
cookie_banners. Extend the version snapshot types to carry
translated UI strings and category content per language.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-24 12:18:08 +04:00
Sacha Al Himdani
25c590ffe6 Add background PDF generation for published document versions
Move PDF generation from synchronous publish flow to a background polling
job. Published versions with file_id IS NULL are picked up by the job,
which generates the PDF, uploads to S3, and links the file. Export PDF
now serves stored files for published versions (with optional signature
page and watermark) and generates on the fly for drafts.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-23 20:50:25 +02:00
Bryan Frimin
f505e23cb0 Add IAM session and SCIM event indexes
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-22 17:17:30 +02:00
Sacha Al Himdani
7b40d9801b Rename migration 20260416T120000Z to 20260416T120001Z
Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-22 17:17:29 +02:00
Émile Ré
d71915abd4 Fix PR review comments on cookie banner branding
- Add scope parameter to UpdateShowBranding to prevent cross-tenant updates
- Use cmd.Context() instead of context.Background() in proboctl CLI
- Drop SQL column default after backfill in migration
- Add bounds check for int-to-int32 conversion in PG_POOL_SIZE
- Update branding link to getprobo.com homepage

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 15:22:03 +04:00
Émile Ré
3994ab1d8b Add show_branding column to cookie banners
Internal-only flag (defaults to true) that controls whether
"Powered by Probo" branding appears on the cookie banner.
Read directly from the live row (not the version snapshot)
since branding is cosmetic, not consent-relevant.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-22 15:22:01 +04:00
Sacha Al Himdani
b603d04d8d Assets as document: replace snapshot with publish workflow
Remove assets from the snapshot system and replace with a publish-based
document workflow that generates versioned ProseMirror documents.

- Remove snapshot_id/source_id from asset and asset_vendor models
- Delete AssetFilter (no longer needed without snapshot filtering)
- Add PublishAssetList service, GraphQL mutation, MCP tool, CLI command,
  and n8n operation
- Add asset_list_document_id column to generated_documents table
- Generate ProseMirror documents with asset inventory tables
  (name, type, amount, data types stored, owner, vendors)
- Add AssetListDocument resolver on Organization type
- Update frontend to remove snapshot routes/params and add publish dialog
- Add e2e tests for asset publish (immediate, with approvers, reuse, RBAC)
- Add migration script for converting legacy asset snapshots to documents
- Exclude ASSETS from snapshot type lists and e2e snapshot tests
- Move generated_documents SQL to coredata methods on Datum and Asset
- Clear generated document and SOA references on soft delete and archive

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-21 19:36:17 +02:00
Émile Ré
29c0d55e75 Create a db table for cookies for easiest management
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-21 14:18:32 +04:00
Sacha Al Himdani
e1148f812e Replace implemented column with CMMI maturity level
Drop the boolean implemented/not-implemented state in favor of a
mandatory CMMI maturity level enum (NONE, INITIAL, MANAGED, DEFINED,
QUANTITATIVELY_MANAGED, OPTIMIZING) stored as a Postgres enum type.

The migration backfills existing rows (NOT_IMPLEMENTED → NONE,
IMPLEMENTED → INITIAL), makes the column NOT NULL, and drops the old
implemented column and its enum type.

- maturityLevel is required on CreateControlInput and non-nullable (!)
  in the GraphQL schema
- CLI displays human-readable CMMI labels instead of raw enum tokens
- SOA table and published document use a single Maturity column in
  place of the old Implemented + Maturity columns
- Remove ControlImplementationState type and all implemented references
  across backend, frontend, CLI, MCP, n8n, and E2E tests

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-21 11:06:36 +02:00
Alejandro Juan
da91afc2a7 Add CMMI maturity level to compliance controls
Adds an optional CMMI 0-5 maturity level field to Control to support
ISO 27001 clause 9.1 effectiveness measurement and HITRUST CSF maturity
requirements. The field is nullable, framework-agnostic, and exposed
across all four API surfaces (GraphQL, MCP, CLI, n8n) plus the
generated SoA document.

Signed-off-by: Alejandro Juan <alejandrojuan@alejandrojuan.com>
2026-04-21 11:06:36 +02:00
Émile Ré
7cd8c516b9 Add uncategorised cookie category
Replace the `required` boolean column on cookie_categories with a `kind`
enum (NORMAL, NECESSARY, UNCATEGORISED). The Necessary category remains
undeletable and always-on for consent; the new Uncategorised category is
also undeletable but users can opt out of it.

When a category is deleted, its cookies are merged into the Uncategorised
category (lazy-created for legacy banners that don't have one yet).

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-21 11:40:21 +04:00
Sacha Al Himdani
6c5c1fa818 Remove meeting feature
Drop meetings and meeting_attendees tables, remove all meeting-related
code across GraphQL, MCP, CLI, N8N, webhooks, frontend, and e2e tests.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-20 15:37:39 +02:00
Sacha Al Himdani
c32aff5e9e Add migration to deduplicate connectors and enforce uniqueness
Delete duplicate connectors per (organization_id, provider), keeping the
oldest one, then add a unique index to prevent future duplicates.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-20 15:07:30 +02:00
Sacha Al Himdani
55563226cb Data as document: replace snapshot with publish workflow
Mirror the SOA-to-document migration for the data list. Remove data
from the snapshot system and add a publish workflow that generates a
ProseMirror document for the full organization data inventory.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-20 14:44:00 +02:00
Bryan Frimin
11770b4058 Add OAuth2/OpenID Connect authorization server
Implement a full OAuth2 2.0 and OpenID Connect 1.0 authorization
server with support for authorization code flow (with PKCE),
refresh token rotation, device authorization grant, dynamic
client registration, token introspection, and token revocation.

Includes database schema, coredata layer, service logic, HTTP
handlers, OIDC discovery endpoint, and JWKS publishing.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-04-19 12:00:53 +02:00
Sacha Al Himdani
c635492f75 SOA as document: replace export with publish workflow
Statements of Applicability are no longer exported as one-off PDFs.
Instead, each SOA owns a persistent document that accumulates versions
over time, following the same publish/approve lifecycle as authored
documents.

Publishing without approvers publishes immediately; publishing with
approvers creates a draft pending approval via the existing quorum
system. SOAs can also store default approvers that are pre-populated in
the publish dialog.

The SOA is removed from the snapshot system — applicability statements
are now queried directly (snapshot_id IS NULL) rather than through
snapshot copies.

A standalone migration script (cmd/migrate-soa-snapshots-to-documents)
converts existing SOA snapshots into documents with proper ProseMirror
content, preserving version history and approval decisions.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-16 00:01:13 +02:00
Sacha Al Himdani
0b9686fc43 Prevent duplicate active document versions
Replace the draft-only unique index with one covering both DRAFT and
PENDING_APPROVAL statuses, ensuring a document can have at most one
active version at a time. Deduplicate any existing data before creating
the index, prioritizing PENDING_APPROVAL over DRAFT.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-15 14:39:27 +02:00
Émile Ré
01ca89de4a Rename migration
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-15 10:21:00 +04:00
Émile Ré
d05c3591d3 Handle cookie banner origin validation + unicity
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-15 10:20:59 +04:00
Sacha Al Himdani
eeee93e464 Drop legacy document columns and approver tables
The title, classification, and document_type columns have been moved to
document_versions. The document_approvers and document_version_approvers
tables have been replaced by approval quorums and decisions.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-13 18:10:14 +02:00
Émile Ré
61cd3244df Denormalize organization_id onto cookie entities
Add organization_id to cookie_banner_versions, cookie_categories,
and cookie_consent_records so AuthorizationAttributes can return
it directly without JOINing back to cookie_banners. Document the
pattern in contrib/claude/coredata.md.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:52:21 +04:00
Émile Ré
88315f51e1 Add cookie banner versioning with JSONB snapshots
Introduce append-only cookie_banner_versions table with a JSONB
snapshot of consent-relevant configuration (privacy policy URL,
consent mode, expiry, categories and their cookies). Each version
has its own state (DRAFT/PUBLISHED) separate from the banner
lifecycle.

Replace the banner state enum (DRAFT/PUBLISHED/DISABLED) with a
simpler ACTIVE/INACTIVE toggle. Link consent records to the
specific published version the visitor accepted.

Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:52:20 +04:00
Émile Ré
84628683f2 Add migration
Signed-off-by: Émile Ré <emile@getprobo.com>
2026-04-13 14:50:41 +04:00
Sacha Al Himdani
eb6c06e683 Move document search_vector from documents to document_versions
Now that title lives on document_versions, the full-text search vector
belongs there too. Drop the generated column and GIN index from
documents, recreate them on document_versions, and update the document
filter to query the latest version's search_vector.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-09 20:01:32 +02:00
Sacha Al Himdani
221afd8b8d Drop dead description column from documents
The description field on Document and EmployeeDocument was never
populated. Remove it from the database, GraphQL schema, and Go types.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-09 20:01:32 +02:00
Sacha Al Himdani
b63f043849 Move document title ownership from document to version
Title is now owned by document_versions, following the same pattern as
classification and document_type. The documents.title column is made
nullable with a TODO to drop it. Backend loads title from a
latest_versions CTE for ordering purposes only. The frontend resolves
title from the latest version, and DocumentTitleForm now operates on
DocumentVersion using UpdateDocumentVersion mutation.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-09 20:01:32 +02:00
Sacha Al Himdani
ab5f42ad74 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>
2026-04-09 18:18:30 +02:00
Sacha Al Himdani
5fd580e995 Rename State of Applicability to Statement of Applicability
Rename the entity across the full stack: database table
(states_of_applicability → statements_of_applicability), Go model,
GraphQL types, MCP specification, CLI commands, frontend components,
routes, and display labels. Includes a migration to rename the table
and its foreign key column.

Widen sidebar from 260px to 280px to fit the longer label.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-09 13:06:06 +02:00
Sacha Al Himdani
ef2e99d86c Add measure-document linking
Introduce the ability to link measures to documents, following the
existing pattern used by controls and risks. This includes:

- Database migration for measures_documents join table
- Coredata MeasureDocument struct with insert/delete operations
- Document service methods for listing/counting by measure ID
- Measure service CreateDocumentMapping/DeleteDocumentMapping methods
- Cleanup of measure-document mappings on document archive
- GraphQL mutations, inputs, payloads, and Measure.documents field
- DocumentConnection.TotalCount support for measure resolver
- MCP linkMeasure/unlinkMeasure updated to support documents
- MCP listMeasureDocuments tool
- Frontend MeasureDocumentsTab with LinkedDocumentsCard integration
- Authorization actions for measure document mapping
- E2e tests for measure document mapping

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-03 17:51:31 +02:00
Sacha Al Himdani
8adf26ad20 Add in-progress state to tasks
Introduce IN_PROGRESS as a new task state between TODO and DONE across
the full stack: database enum, Go backend, GraphQL, MCP, and frontend.

The task state icon now cycles forward on click (TODO → IN_PROGRESS →
DONE → TODO), and the action dropdown provides explicit "Move to"
options for any state transition. The "All" tab supports drag-and-drop
between state sections to change a task's state.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-03 08:33:37 +02:00
Sacha Al Himdani
9a418a7711 Move document type from document to document version
Follow the same pattern used for classification: document type now lives
exclusively on DocumentVersion. A migration copies existing values from
documents to their versions. The document filter uses a subquery on the
latest version. All three API surfaces (GraphQL, MCP, CLI), resolvers,
frontend, and e2e tests are updated accordingly.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-02 16:32:38 +02:00
Aurélien Sibiril
ff20b383d9 Add access review dependencies and enum tests
Add go-vcr dependency, dev config for new providers,
connector service changes for access review, connect
schema updates, and unit tests for enum Scan/Value.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-04-02 14:49:13 +02:00
Aurélien Sibiril
e4e08f22b7 Add access review data layer and migrations
Add coredata entities for access review campaigns,
access sources, access entries with decision history,
campaign source fetches, and scope systems. Include
migrations, entity type registrations, enum types for
flags, decisions, MFA status, and auth methods.

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
2026-04-02 14:37:29 +02:00
Sacha Al Himdani
324f4ce793 Add task priority enum and rename priority to rank
The existing integer priority field represents positional ordering
within a state, not semantic importance. Rename it to rank and
introduce a new priority field with enum values URGENT, HIGH,
MEDIUM and LOW across the entire stack.

Rank is now scoped to (state, priority) so tasks are ordered
within each priority group. A generated priority_rank column
combines both fields into a single sortable integer for cursor
pagination.

Dragging a task across priority groups updates its priority
automatically based on the drop position neighbors. The backend
first moves the task to the new group then repositions it at the
target rank.

The migration defaults existing rows to MEDIUM priority and
backfills ranks per (state, priority) group.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-04-02 13:35:39 +02:00
Bryan Frimin
a2f0a37b7b Add UNKNOWN and NOT_IMPLEMENTED measure states
Introduce two new measure states across the full stack: database
migration, Go coredata, GraphQL schema, MCP specification, and
frontend UI (labels, badge variants, and colors).

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
2026-04-02 10:59:25 +02:00
Sacha Al Himdani
5d6d0bdd7f Move document classification from document to document version
Classification now lives exclusively on DocumentVersion. The field is
removed from the Document model, all SQL queries, GraphQL Document
type, SignableDocument type, UpdateDocumentInput, and MCP Document
schema.

New documents still accept classification in CreateDocumentInput,
applied to the first version. New drafts inherit classification from
the previous version. PDF generation uses the version classification.

The drawer allows editing classification on draft versions via the
updateDocumentVersion mutation. Classification is read-only on
published versions.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-31 14:11:57 +02:00
Sacha Al Himdani
851e585b9b Add ISC license headers to Go, TypeScript, and SQL files
Add ISC license headers to all .go, .ts, .tsx, and .sql files
using each file's git history to determine the correct copyright
year or year range. Trademarked icons (brand logos, vendor logos,
compliance framework logos) are excluded.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-31 13:31:30 +02:00
Sacha Al Himdani
68bc8cf59a Add major.minor document versioning
Introduce a two-part version scheme (major.minor) for documents.
Drafts start at 0.1 and increment minor on each new draft. Publishing
as minor keeps the current version, publishing as major bumps to the
next major.0. Both current_published_major and current_published_minor
are tracked on the document for exact version lookups.

Signatures and approval quorums aggregate across all versions sharing
the same major number using CTE joins. Approval page mutations spread
the decision fragment so Relay updates the version row state without
requiring a page refresh.

GraphQL, MCP, and service layer expose separate publishMajor and
publishMinor mutations instead of a single mutation with a type enum.

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-30 18:54:28 +02:00
Sacha Al Himdani
999171a626 Add document approval workflow
Introduce a complete approval system for document publishing. Document
versions can now require approval from selected reviewers before being
published, with automatic publishing once all approvers have approved.

- Add approval quorum and decision tables with backfill migration
- Implement request approval, approve, and reject flows with electronic
  signature support for approve decisions
- Add employee approvals page with dedicated tab and pending approvals view
- Add changelog field to publish and request approval flows
- Pre-select previous version's approvers in the publish dialog
- Show quorum approvers in document list with 100 approver hard limit
- Expose approval workflow through GraphQL, MCP, and CLI
- Remove legacy default approvers feature entirely
- Add comprehensive e2e test coverage for approval workflows

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
2026-03-27 19:43:20 +01:00