Office Version History
Arinova keeps durable, server-side version history for Workbook, Slide deck, Mindmap, and Forms. It survives refresh, logout, browser changes, and device changes. Doc and Notebook keep their existing version systems and are not migrated to this storage model.
Version history is part of each product’s normal availability. There is no
deployment, build, or ENABLED flag. Access is controlled by the current
resource permission, archive/freeze state, rate limit, and schema readiness.
Four different kinds of history
Section titled “Four different kinds of history”These mechanisms solve different problems and must not be presented as interchangeable:
| Mechanism | Purpose | Lifetime |
|---|---|---|
| Concurrency version | Reject a stale write or restore precondition | Advances with canonical writes; not user-visible history |
| Local Undo/Redo | Fast reversal of recent editor commands | Current mounted editor/session |
| Durable version history | Named, automatic, import, Agent, publish, and restore recovery | PostgreSQL until retention or hard purge |
| Form response revision | Preserve the exact questions/options/settings a response answered | Immutable while referenced by a response |
A durable restore never rewinds a concurrency counter. It writes historical content as a new current state, advances the product’s concurrency state, and adds both a pre-restore checkpoint and a restored checkpoint.
Mindmap keeps its 100-command local Undo/Redo stack. A server restore broadcasts a coarse reload event and invalidates local commands because those commands were based on the replaced graph.
Common contract
Section titled “Common contract”The structured Office products share one history service and wire model.
VersionSummary contains the resource kind and ID, ordered version number,
checkpoint type, optional label, safe actor summary, source concurrency
metadata, bounded structural summary, restore provenance, timestamp, retention
class, and server-derived canRestore/canCopy flags.
- Lists use an opaque cursor ordered by
(created_at DESC, id DESC). - Detail responses contain a bounded safe preview. Compressed snapshot bytes are never returned by Web, API v1, or Agent Actions.
- Labels are trimmed and limited to 255 characters.
- Idempotency keys are scoped to the resource and limited to 255 bytes.
- Restore requires the expected current history head. A stale head returns
HISTORY_CONFLICTinstead of overwriting a newer save. - Copy returns the normal canonical product deep link. A copy starts at version 1 and receives no access to the source history.
- Public share tokens and Form responder tokens never grant history access.
The user route families are:
| Product | Collection | Detail | Mutations |
|---|---|---|---|
| Workbook | GET/POST /api/workbooks/{id}/versions | GET /api/workbooks/{id}/versions/{versionId} | POST .../{versionId}/restore, POST .../{versionId}/copy |
| Slide deck | GET/POST /api/slides/decks/{id}/versions | GET /api/slides/decks/{id}/versions/{versionId} | POST .../{versionId}/restore, POST .../{versionId}/copy |
| Mindmap | GET/POST /api/mindmaps/{id}/versions | GET /api/mindmaps/{id}/versions/{versionId} | POST .../{versionId}/restore, POST .../{versionId}/copy |
| Form | GET/POST /api/forms/{id}/versions | GET /api/forms/{id}/versions/{revisionId} | POST .../{revisionId}/restore, POST .../{revisionId}/copy |
The same additive families are available under /api/v1. They use
CallerIdentity and the same service, errors, bounds, permissions,
idempotency, and restore preconditions as the Web routes.
Permission and safety model
Section titled “Permission and safety model”History can contain content that has since been removed from the live resource, so the permission is intentionally stronger than ordinary read:
- a person needs current edit-grade access to the resource;
- an Agent needs both the triggering person’s edit-grade access and its own current direct or Space write grant;
- a frozen/read-only Space retains authorized history reads, but blocks named checkpoints, copy, and restore;
- an archived resource retains readable history for authorized editors but must be unarchived before restore;
- every detail/restore/copy lookup scopes by resource kind, resource ID, and version ID; possession of a version UUID grants nothing;
- execution re-checks resource and managed-asset access after confirmation.
Version endpoints use a dedicated fail-closed Redis rate-limit class. The
default is 30 requests per minute and may be tuned with
RATE_LIMIT_VERSION_HISTORY_PER_MINUTE; this is an abuse limit, not a feature
switch.
Errors use stable codes:
| Code | Meaning |
|---|---|
VERSION_NOT_FOUND | Missing version or a version from a different resource |
VERSION_FORBIDDEN | Current edit-grade access is absent |
VERSION_PAYLOAD_INVALID | Corrupt, oversized, unsafe, or unsupported snapshot |
HISTORY_CONFLICT | Expected history head is stale |
VERSION_ASSET_UNAVAILABLE | A required historical managed asset is not recoverable |
VERSION_RESTORE_FAILED | Atomic restore rolled back |
FORM_REVISION_IN_USE | A response-bound Form revision cannot be changed/deleted |
FORM_PUBLISH_REQUIRED | Restored Form changes are still an unpublished draft |
Normal logs and metrics never contain version labels, filenames, cell values, question text, slide text, node labels, or snapshot content. Immutable audit rows contain operation, actor type, resource/version IDs, result, safe error code, byte counts, correlation ID, and latency only.
Checkpoints and retention
Section titled “Checkpoints and retention”Structured resources receive an initial snapshot at creation. Legacy resources receive exactly one current-state System baseline through a restartable backfill; Arinova does not invent past versions.
| Checkpoint | Created for | Retention |
|---|---|---|
initial | Creation or first baseline | Protected |
automatic | Changed editor state, coalesced to at most one per 10 minutes | 90 days |
named | Explicit Save version | Protected |
import | Whole-resource import boundaries | Protected |
agent_bulk | One correlated Agent bulk operation | Protected |
published | Immutable Form publication | Protected |
replace | Template/outline/whole-resource replacement | Protected |
pre_restore | Current state immediately before restore | Protected |
restored | New state produced by restore | Protected |
Identical automatic snapshots are skipped. A failed automatic checkpoint does not corrupt or roll back the user’s edit; it writes one durable, coalesced retry signal. Initial, import, replace, Agent bulk, named, publish, pre-restore, and restored checkpoints fail closed.
The hourly pruner is fleet-single, bounded, and uses the same per-resource lock as restore. It never prunes the current baseline or an automatic version used by restore/copy provenance. Form revisions referenced by a response, publication, restore, or copy are pinned. Hard purge removes history, retry and restore-outbox rows, content-free version audit rows, and asset bindings in the same resource purge flow. Soft archive retains all of them.
Product semantics
Section titled “Product semantics”Workbook
Section titled “Workbook”A snapshot includes the workbook name, canonical workbook JSON, schema version, source concurrency version, and safe attribution. Validation reuses current Workbook size/schema limits. Preview is structural: ordered Sheets, used dimensions, cells, formulas, and charts; it never mounts an unbounded grid.
Restore preserves the workbook ID, owner/Space, permissions, archive/share
identity, replaces the whole workbook JSON, and increments workbooks.version.
Per-Sheet restore is not part of this release. Copy follows Office copy naming
and ownership conventions and starts fresh history.
Slide deck
Section titled “Slide deck”A snapshot contains deck title/description/theme/aspect ratio plus every ordered slide’s title, content, notes, and source version. Preview renders bounded, read-only thumbnails and never mounts the editable canvas or starts autosave.
Manual snapshot, copy, and restore flush queued editor saves first. Restore reconciles slides atomically, advances versions of retained slides, safely remaps occupied historical IDs, releases removed bindings, and broadcasts a coarse restored event. Copy generates a new deck and new slide IDs.
Mindmap
Section titled “Mindmap”A snapshot contains map metadata and the complete visible graph: node/parent IDs, sibling order, label/style, collapse state, layout side/manual offset, linked Note identity, and tagged managed/external image identity. Delete-batch bookkeeping is not snapshot content.
Validation accepts an empty map or a forest with any number of roots while requiring an acyclic reachable graph, unique IDs and sibling keys, canonical breadth-first snapshots, and bounded labels/layout. Missing linked Notes remain explicit broken links; restore never recreates a Note or exposes Note content. Copy remaps map/node IDs and parents while preserving only valid references.
Normalized forms and form_fields rows are the editable draft. Publishing
serializes one immutable definition containing title, description,
respondent-affecting settings, ordered active fields, stable IDs, types,
labels, help, required state, options, validation, the cover image asset ID,
and display-image asset IDs. Publication atomically advances
forms.published_revision_id.
Public rendering and validation read the immutable published definition, not
later draft rows. A response is inserted with that exact
form_revision_id in the same transaction as validation and response-count
updates. Response detail, CSV, Workbook, PDF, analytics, and field-label
resolution prefer the bound definition. respondent_meta.fieldSnapshot
remains a compatibility fallback for incomplete legacy evidence.
Editing after publish changes only the draft. The owner UI distinguishes Draft, Live, Unpublished changes, and Closed. Republish creates a new revision; it never edits the previous one. Close/reopen changes acceptance state without rewriting a publication.
Restore replaces the editable draft and creates pre-restore/restored revisions,
but leaves published_revision_id and every response binding unchanged. The
live responder Form therefore stays on the prior publication until an explicit
Publish. Copy creates a draft with no share token, responses, response count,
publication pointer, or source history. A copied Form receives new managed
assets in its own Form scope; it never shares source-scoped image identity.
Legacy backfill is batched and restartable. It:
- creates one evidence-backed current baseline;
- treats published/closed current state as the baseline publication;
- binds matching
fieldSnapshotresponses to that baseline; - creates response-only reconstructed revisions for distinct valid evidence;
- creates explicit
legacyIncompleterevisions for missing/malformed evidence; and - records exact/reconstructed/incomplete counts.
form_responses.form_revision_id remains nullable during deployment. A later
forward migration may enforce NOT NULL only after the deployed verification
reports zero pending/running/failed Forms and zero unbound responses.
Managed assets
Section titled “Managed assets”Structured versions bind every referenced managed image through
office_resource_version_assets. A historical preview/restore first verifies
that the registry row remains ready. The normal asset lifecycle treats version
bindings as durable references; pruning or hard purge releases them
transactionally and schedules normal retirement only when no live binding
remains. External URLs stay explicitly external and are never promoted to
managed assets during restore.
Form revisions use the equivalent dedicated
form_revision_image_assets reference table because their immutable
definitions and response bindings predate the generic Office snapshot store.
The same retention rule applies: live draft bindings may be replaced, while
published and historical revision references keep the exact image bytes
available until that revision is pruned or the Form is hard-purged.
Privacy, account export, backup, and recovery
Section titled “Privacy, account export, backup, and recovery”The current account data-export payload intentionally excludes Office document
contents and all private version snapshots; its includedDomains remain
profile, conversations, messages, memories, wallet ledger, and file metadata.
This is an explicit policy boundary, not an accidental omission. Users recover
Office versions inside the authorized product UI. Hard account deletion uses
the resource purge hooks and removes the private history.
PostgreSQL dump/PITR coverage includes all version, Form revision, retry, outbox, audit, and asset-binding tables. Managed-image object storage must be restored to the same point or later than PostgreSQL. The restore validation in Backup and disaster recovery includes referential and decode checks for this data.
Operational diagnosis and forward recovery are documented in Office version history runbook. Importable rules and dashboard are office-version-history-alerts.prometheus.yml and office-version-history-dashboard.json.
Build a7f47a5ca54ddcf7806cd48b81ce1b9827042766