Automation Activity and audit policy
How AID explains Automation decisions, attributes actions, preserves investigation evidence, and controls retention.
Decision and implementation status: ADR-0023 is Accepted. It defines the required behavior. The Activity schema and writers are being implemented incrementally in Phase 4; this page does not claim that every screen or fact writer is already available. The cancellation-request command is the first active writer: it atomically records its HTTP actor, reason, Document, request evidence, snapshots, and approved intervention-retention horizon. Other fact kinds and Activity readers remain incomplete.
Why Automation needs its own Activity history
The current Automation rows answer:
What is happening now?
Activity answers:
What happened before, who or what caused it, and what did a person do about it?
Those are different questions. A Step that succeeds today may have been blocked by a name collision yesterday. If AID kept only its current state, the collision and the administrator's intervention could disappear from the explanation.
A realistic example
- The Invoice naming Automation tries to rename a clean invoice.
- A Document with the intended name already exists.
- The Step becomes Action required.
- Farid fixes the conflicting name and chooses Continue after fixing.
- The Automation runs again and succeeds.
The current Step truth eventually says succeeded. Activity preserves the
business story:
- Invoice naming encountered a name collision.
- Farid continued the work after fixing the conflict.
- Invoice naming renamed the Document successfully.
Database retries or queue redeliveries between those facts remain engineering telemetry. They do not clutter the customer's business history.
Activity, current state, and telemetry are different
| Information | Question it answers | Example |
|---|---|---|
| Current dispatch/run/Step truth | What needs attention now? | This Step is blocked by a name collision. |
| Append-only Activity | What business or security event happened? | Farid continued the blocked Step after fixing the collision. |
| Operational telemetry | Why did the software behave slowly or retry? | The worker retried after a database timeout. |
Activity is not used to decide whether a Step may run. The current SQL state machine remains authoritative. Activity is durable evidence explaining how that state changed.
What AID records
The focused Automation stream records business and security facts such as:
- publication, activation, deactivation, pause, and resume;
- capability enablement and disablement;
- dispatch planned, no match, budget blocked, or system failed;
- run and Step blockers and terminal outcomes;
- cancellation request and the actual result at the safe boundary;
- Continue after fixing, permitted Skip, targeted new run, and control release;
- incident recovery; and
- external-effect preparation and resolution when that foundation is enabled.
A technical retry is not a new business fact. Attempts, leases, queue delivery, timings, stack traces, and provider diagnostics remain in job truth, metrics, logs, and traces.
Who performed an automated action?
Automation execution is attributed to the pinned Automation version, not to the person who published it.
For example:
Renamed by Automation “Invoice naming,” version 3, published by Nurul.
It does not say Nurul renamed the Document. She approved the behavior, but the Automation executed it later. If Farid subsequently continues blocked work, that separate Activity fact names Farid.
AID distinguishes:
workspace user | Automation | system | operator | controlThis prevents a system recovery from being presented as a customer action, or a publisher from being blamed for a later automated execution. Workspace users and internal operators are human actors: both need a stable identity and frozen display name. An HTTP-origin actor also needs complete request evidence. A runbook operator instead needs a trusted human identity, unique runbook execution ID, and frozen process principal; AID does not invent an HTTP route, browser session, user agent, or IP address. Automation, system, and control actors use the machine origin and carry no human request or correlation fields.
For example, after a restore Activity can say that operator Priya ran recovery
execution REC-2026-0041 through the recovery-epoch principal. The database role
proves which program executed; it does not replace Priya as the human actor.
Comprehensive investigation snapshots
The product owner selected comprehensive audit snapshots. Depending on the fact kind, Activity can retain:
- Document and Automation names;
- actor display identity;
- bounded user-entered reasons;
- relevant old and new business values;
- request and correlation IDs;
- client IP address and user agent;
- HTTP method, normalized route template, and public session identity; and
- stable Document, definition, dispatch, run, Step, incident, and control IDs.
These snapshots may outlive a rename, member removal, or Document disposal. That is useful during an investigation, but it also increases privacy, breach, access-request, storage, backup, and erasure consequences.
Comprehensive does not mean indiscriminate
Every fact kind has a versioned allowlist and byte limits. Activity never accepts an arbitrary request or database-row dump. It excludes:
- authentication and authorization headers;
- cookies, session tokens, passwords, and secrets;
- raw URLs and query strings;
- complete request bodies;
- file or OCR content;
- raw provider payloads; and
- exception text or stack traces.
A human request receives a server-generated request ID. The direct peer address
is used unless deployment configuration explicitly enables a trusted proxy. A
caller-supplied X-Forwarded-For header is not trusted automatically.
A worker action has no invented IP address, user agent, request ID, or request correlation ID. Its stable job, dispatch, run, and Step identities provide correlation instead. A runbook operator is different from a worker: it retains the authenticated person, execution ID, and process principal, while leaving HTTP-only fields empty.
Access remains current
Historical IDs and snapshots never grant access.
Until the broader access model is ratified:
- workspace-wide Automation issues and definition history are administrator-only;
- Document-specific Activity requires the viewer's current Document-read permission; and
- internal operator diagnostics are not returned through the customer Activity API.
Raw snapshot JSON is not a general-purpose API response. Each endpoint returns only the authorized, stable fields required by its user experience.
Retention has no silent default
Gate A does not write Activity and is not a customer-beta state. Before a workspace advances into Gate B, an authorized product, legal, or records owner must approve a versioned Activity retention policy.
The policy gives each fact category:
- a legal-basis code; and
- a concrete retention horizon.
Different categories may use different approved horizons. A later policy may lengthen existing retention, but it cannot silently shorten facts already written. Ordinary Automation administrators cannot invent the company's legal retention policy through the Automation settings screen.
After Gate B enrollment, a missing or incomplete current policy fails closed:
AID does not perform a mandatory audited business action while silently
dropping its Activity evidence. Before enrollment, a Gate A workspace has no
retention policy, writes no Activity, and keeps its existing Automation
lifecycle actions available. A policy without the atomic
activity.coverage_started fact is an invalid partial enrollment and fails
closed. The ordinary enrollment command deliberately refuses to legitimize an
ambiguous existing policy. Production recovery currently requires database-
operator incident response; a disposable development database should be reset.
Enrolling a workspace into Gate B
Enrollment is a signed operational action. It is not part of db:seed, and an
ordinary workspace administrator cannot invoke it. Deployment must give the
one-shot process a database login whose only capability role is
aid_activity_policy_operator, then provide a short-lived
RUNBOOK_OPERATOR_ASSERTION for action
automation_activity_policy_enroll, bound to process profile
activity-policy-operator.
The rules file is a JSON array containing exactly one object for each of
coverage, governance, consideration, execution, intervention, and
external_effect. Every object has only factCategory, legalBasisCode, and
horizonDays; those values come from the approved policy, not from an
engineering default.
After the matching application artifact has been staged, run its enrollment launcher while Automation is drained:
pnpm --filter @aid/api activity:enroll-policy \
--workspace-id <workspace-uuid> \
--effective-at <approved-ISO-timestamp> \
--approval-reason "<approved reason>" \
--rules-file <approved-rules.json>The migration-owned command atomically creates the complete initial policy and
its activity.coverage_started fact. Retrying the same signed runbook execution
returns the committed policy without duplicating coverage. A workspace with an
existing policy is refused; later immutable policy-generation changes require
their own reviewed command and Activity contract.
Legal-hold interaction, final erasure and disposal procedures, and the production legal-basis vocabulary remain cross-product decisions that must be closed before production promotion.
What happens to older Automation work?
AID does not fabricate a historical timeline from current rows.
After an approved policy is provisioned, AID records a coverage-start fact. The product can then say:
Detailed Automation Activity is available from 15 August 2026.
A cancellation requested before Gate B can be swept again after enrollment if
new work becomes terminalizable. In that case Activity honestly begins with
automation.cancellation_completed and has no preceding
automation.cancellation_requested fact. Readers must use
activity.coverage_started as the history boundary rather than treating that
sequence as missing evidence.
Earlier run and dispatch state may still be shown where useful, but it is not misrepresented as a complete immutable Activity history.
No-match facts and storage growth
When AID considers an event and no Automation matches, the terminal dispatch creates at most one no-match Activity fact. It never creates one fact for every definition.
The pilot collector must count Activity rows and retained bytes. A mandatory review is triggered by:
- more than 100,000 no-match fan-out rows per day;
- more than 1 GiB per month of measured no-match storage growth; or
- the existing loaded latency threshold.
Crossing a threshold starts a design and policy review. It does not automatically authorize deleting evidence, shortening a legally approved horizon, or suppressing real Automation work.
Privacy-hardening backlog
Comprehensive snapshots are the accepted initial policy, but the decision is not forgotten permanently. A privacy-policy review is mandatory:
- within twelve months of the first production Automation rollout; or
- earlier when required by a legal or privacy review, a customer commitment, a data-subject request, or measured storage growth.
The review must consider:
- collecting fewer fields;
- hashing or tokenizing identifiers;
- more granular access tiers;
- shorter category-specific horizons; and
- how to migrate or dispose of snapshots already retained.
Immutable evidence is never silently rewritten during that review.
Engineering source of truth
ADR-0023 is the canonical decision. Database truth belongs in the Drizzle schema
and forward migrations under apps/api; this guide explains the resulting
behavior and must not become a second schema.
Why an Automation refuses to turn on
The nine conditions that refuse an Automation activation, what each one means in plain language, and why a saved Automation can still be running an older version.
Folder system architecture
Data integrity, transaction, concurrency, cursor, and operational behavior for folders.