Skip to content

Commit 796955c

Browse files
committed
docs(objectql): document treatAsHistorical audit-timeline preservation (#3493)
The state-machine doc already covered the FSM-skip half of `treatAsHistorical` (#3479); extend the same bullet with the audit-timeline half that shipped in #3497: under the flag the write context also carries `preserveAudit`, so `updated_at`/`updated_by` become client-preferred and a whitelist (audit family + author-declared business `readonly` fields) survives the upsert readonly strip, while platform-managed tenancy/system columns stay stripped. Keeps the one place `treatAsHistorical` is documented accurate and discoverable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B5rdfBKjkbcoEif4KUV6xE
1 parent ad4af62 commit 796955c

1 file changed

Lines changed: 1 addition & 0 deletions

File tree

content/docs/protocol/objectql/state-machine.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ transitions: {
101101
- Only a rule with `severity: 'error'` (the default) blocks the write; `warning`/`info` are logged.
102102
- **Seed writes are exempt** (#3433). Curated seed data — package bootstrap fixtures, marketplace templates, per-org replay, all loaded by `SeedLoaderService` — is a snapshot of established facts, not a record walking its lifecycle, so it bypasses the `state_machine` rule entirely: a seed may be born mid-lifecycle (a `completed` project, a `closed_won` opportunity) and neither `initialStates` (insert) nor `transitions` (update) is enforced. Every *other* validation still runs, so a seed must still satisfy field shape, `format`, `script`, and the rest. `os lint` warns when a seeded value is not a state the machine declares, so a typo is still caught before boot.
103103
- **A "historical" data import is exempt too** (#3479). Migrating established facts — a batch of already-`closed` tickets, `closed_won` deals — is the same "snapshot, not a lifecycle event" situation. Set `treatAsHistorical: true` on the import request (default **off**) and the runner puts `skipStateMachine` on the write context, so `initialStates` doesn't reject those mid-lifecycle rows. A normal import leaves it off and still walks the FSM — the strict behavior is the default, so the exemption is always an explicit opt-in.
104+
- **`treatAsHistorical` also preserves the original audit timeline** (#3493). Skipping the FSM is only half of migrating established facts; the other half is keeping *when* they happened and *who* did them. Under the same flag the write context also carries `preserveAudit`, which (1) makes `updated_at` / `updated_by` **client-preferred** — a supplied historical last-modified survives instead of being stamped with the import instant, symmetric with how `created_at` / `created_by` already behave on insert — and (2) admits a **whitelist** through the static-`readonly` write strip: the audit/timestamp family plus author-declared business `readonly` fields (`closed_at`, `resolved_by`, …), so an `upsert` refresh no longer silently drops them. Platform-managed `system` columns outside that family (`organization_id` and other tenancy/generated columns) stay stripped — a historical import reinstates facts, it does not forge tenancy. Like the FSM exemption this is opt-in: a normal write still auto-stamps `updated_at`/`updated_by` and strips `readonly` exactly as before, and permissions / RLS / field-level security are unchanged.
104105

105106
### Conditional transitions
106107

0 commit comments

Comments
 (0)