Data model and ownership
Principal graph
Section titled “Principal graph”User ── OrganizationMembership ── Organization ── OrgSettings │ └── OrganizationMembershipGrant │ │ ├── IntegrationConnection ── provider links/cursors/jobs │ └── Project ── WorkItem ── WorkItemTransition │ └── Iteration └── TimeEntry ─────────────────────────────────────────┘ ├── Screenshot ├── ActivitySnapshot └── AppUsage
User ── AuthSessionUser ── DesktopInstallation ├── ComplianceEnrollment ── posture snapshots/evaluations/findings/exceptions/refresh ├── AiPackInstallationReport └── RemoteActionDeviceCapability ── executions/terminal sessionsAuthChallengeIdempotencyRecordIntegrationConnection ── ImportJob ── segments/issuesOrganization ── SyncJobAiModelPack ── AiPackCatalogEventOrganization ── Customer ── CustomerContact └── CustomerProject ── ProjectWorkItem ── WorkItemCustomerVisibility ── CustomerProjectTimeEntry ── TimeEntryCustomerAllocation ── CustomerProjectDeveloperWorkSession ── AgentRun/AgentRunSegment └── ProjectTimeAllocation ── TimeEntry(AGENT_ALLOCATION)AgentIngestionOutbox ── AgentTelemetryProjectorCursorOrganization ── TelemetryGovernanceJobOrganization ── ReportDefinition/Revision ── ReportModelBinding └── ReportTemplate/Version └── ReportRun ── ReportSnapshot/Artifact/Delivery/DeliveryAttempt └── ReportSchedule/ReportAuditEvent/ReportObjectDeletionThe automatic-time authority is DeveloperWorkSession plus revisioned ProjectTimeAllocation. Finalization creates closed TimeEntry(source = AGENT_ALLOCATION) projections for existing timesheet consumers; corrections supersede projections and stale dependent customer allocation without mutating frozen report snapshots. Customer visibility and exact customer-second allocation are separate, fail-closed relations. Report objects are tracked independently from their immutable database ledger so retryable deletion and orphan reconciliation never erase historical audit state.12
Ownership invariants
Section titled “Ownership invariants”OrganizationMembershipis active only while at least one non-revokedOrganizationMembershipGrantsurvives; the highest active grant determines effective authorization.Project.organizationIdis the tenant anchor for project work data.- A
TimeEntrybelongs to a user and either a project or an explicit organization for automatic tracking. A work item requires a project. Unattributed allocation projections reference theirDeveloperWorkSession;manualAllocationpreserves developer corrections from late automatic reconciliation.3 - Screenshot, activity and app usage inherit ownership and tenant from their time entry.
- Work items, iterations and transitions inherit tenant from their project. Provider identity exists only in explicit external-link rows.
- An import mapping for a tenant may reference only users and projects belonging to that tenant.
- Device feature rows reference one shared
DesktopInstallation; feature policy, audit and revocation remain separate. AiModelPackis a global immutable catalog record.AiPackCatalogEventrecords operator identity, previous/next status and evidence for every supported publication transition independently of organization assignment audit.
These invariants are enforced by centralized application policies and database predicates. Generic project mutation cannot silently transfer a project between organizations.
Concurrency invariant
Section titled “Concurrency invariant”A user may have at most one running time entry. PostgreSQL enforces this with a partial unique index, while application conflict handling returns the existing state safely. Activity snapshots and screenshot storage paths have uniqueness constraints where a retry would otherwise duplicate a logical write.
Session and challenge state
Section titled “Session and challenge state”AuthChallengeis the expiring, atomically consumed authority for native identity recovery, OAuth PKCE and installation registration purposes.AuthSessionowns refresh-family state, expiry, revocation and reuse detection;User.tokenVersioninvalidates previously issued access claims.- Access-token role claims are hints only: administrative access rechecks the current database session and membership.
Durable work and idempotency
Section titled “Durable work and idempotency”ImportJobandSyncJobpersist status, attempt count, lease owner/expiry, heartbeat, checkpoint, cancellation and terminal error. Partial unique constraints prevent duplicate active synchronization and overlapping incremental Hubstaff work for one connection.IdempotencyRecordbinds an actor, operation key and request fingerprint to a recorded response and expiry.- Workers reclaim expired leases, advance checkpoints and never rely on process memory as the authoritative job state.
Privacy and object lifecycle
Section titled “Privacy and object lifecycle”Organization settings hold explicit external-AI, window-context and retention controls with privacy-preserving defaults. Screenshot rows retain a scoped storage path and deletion state so object deletion can be retried and orphaned objects can be reported instead of hiding an inconsistent delete.
Schema authority
Section titled “Schema authority”apps/backend/prisma/schema.prisma and migrations define executable persistence. This article explains intent and must be updated with every meaningful model or ownership change.
Database engineering review — 2026-09-08
Section titled “Database engineering review — 2026-09-08”The database engineering review distinguishes intended invariants from observed implementation gaps. Live PostgreSQL contains 117 application models plus migration metadata and is approximately 45 MB. Screenshot asset reuse conflicts with the still-unique occurrence storage path; direct/derived TimeEntry tenant ownership leaves projectless data outside some retention and analytics queries; nullable composite unique keys require domain-specific review. These are findings and recommendations, not implemented schema changes.4
Factory command persistence (issue109)
Section titled “Factory command persistence (issue109)”Local implementation adds FactoryProject (unique immutable project/repository binding) and FactoryCommand (UUID, original actor, issue/role, repository snapshot, delivery state, validated receipt and fenced delivery lease). Both retain project references with restricted deletion. Their additive migration does not alter existing rows. See Factory control and snapshot; deployment remains open.
Factory request persistence (issue113)
Section titled “Factory request persistence (issue113)”FactoryRequest retains the original actor, immutable text and UUID before a GitHub issue exists. It references FactoryProject with restricted deletion and holds the validated remote receipt plus delivery lease/backoff fields. It is separate from FactoryCommand, which requires an existing issue and execution role. The additive generated migration preserves existing rows. See implementation snapshot; online acceptance remains open.