ShrubberyDocs
Sign in

Audit Trail & Event Logging

Overview

Every state change on a Shrubbery — from creation to completion — is recorded as an immutable event in the Audit Trail. The Trail answers "who did what and when" without requiring anyone to remember; it is the accountability spine of the system. This guide explains what gets recorded, how actor attribution works, and where the Trail surfaces in the product.

Actors

  • Lead — initiates most events (created, renegotiated, edited, completed). Their user_id is the actor_id.
  • Knight — initiates Handshake events (handshake_accepted, handshake_refused) and may mark a row completed. Their user_id is the actor_id.
  • External System — initiates events via the Sync API. The actor_kind is api_token; actor_id is the token's ID, not a user ID.
  • Squire — does not generate audit events. Squire extraction is silent; the created event is attributed to the Lead who saved the Draft.

Flow

  1. Any actor performs an action that causes a state transition (Lead sends a Draft, Knight Handshakes, Lead renegotiates, etc.).
  2. System writes an event to shrubbery_events synchronously within the same database transaction as the state update. If the transaction rolls back, the event does not persist.
  3. Event record includes: event_type, actor_id, actor_kind (user or api_token), shrubbery_id, created_at, and an optional metadata JSON blob for field-level detail.
  4. Lead or Knight can view the Timeline — the per-row render of the Audit Trail — inside the Detail Sheet at any time. The Timeline is newest-first and always up to date.

Event types and when they fire

Event typeWho triggersWhen
createdLead (or API token)Shrubbery row inserted (any capture path)
handshake_acceptedKnightPending_HandshakeActive
handshake_refusedKnightPending_HandshakeRefused
renegotiatedLeadSubstantive edit on Active row → Pending_Handshake
editedLeadNon-substantive edit on Active row
completedLead, Knight, or API tokenActiveCompleted
status_syncedAPI token (Sync API)External status update applied
nudge_sentSystem (Inngest)Nudge delivered to Knight

Edge cases

  • API-token attribution — when the Sync API closes a row, the actor_kind = api_token field identifies the automation rather than a user. The Dossier surfaces this label ("API" next to the completion event) so Leads understand the row was not manually marked done. CFS arithmetic is unchanged.
  • Renegotiation metadata — the renegotiated event's metadata blob carries { oldObjective, newObjective, oldDeadline, newDeadline }. The Timeline renders this as a visible diff, not raw JSON.
  • Soft-deleted rows — deleting a Shrubbery sets a deleted_at flag but preserves the row and all its events. The Audit Trail is intact; the row simply no longer appears in active views.
  • Multiple renegotiations — each renegotiation cycle writes a separate renegotiated event. The Timeline shows the full chain, making it clear how many times terms changed before the final Handshake.
  • Nudge events — Nudge delivery events are written by Inngest after the fact. A gap between created and nudge_sent is normal; delivery failures trigger Inngest retries, each of which writes its own event.

Last updated: 22 June 2026