How to make every background AI job accountable to a human owner
Accountable background AI: why every job in our queue names the human it works for.
Stijn Hendrikse · Sep 17, 2026
A fractional CMO managing four clients can already lose about 1.5 unbillable hours a day reloading context. Background AI should remove that cost, not create a second problem: work running across client accounts with no clear human owner. Our rule is simple: every background AI job names the person it works for before it enters the queue.
We apply that rule across 32 job types through one queue seam. Scheduled crons and system callers resolve a human owner explicitly. Burst signals are deduplicated by payload hash. Progress streams to the user, with polling and an outage breaker as fallbacks.
After reading this, you can audit an agentic job queue using five controls: required ownership, explicit caller resolution, payload deduplication, owner-scoped updates and bounded outage recovery.
Accountability starts when the job enters the queue
Background AI work rarely begins with a button click. It may start when a schedule fires, a document changes or an agent decides another step is needed.
That creates an attribution problem. A session can tell you who is currently browsing. It cannot reliably tell you who owns a scheduled analysis at 3:00 a.m.
In our queue, requestedBy is a required argument. The queue never derives it from a session.
enqueue({
type,
payload,
requestedBy
})
That one constraint covers all 32 job types. The content draft, recurring analysis and agent loop may behave differently, but each passes through the same ownership check.
This matters for fractional CMOs because one workspace may hold several client engagements. A job needs enough ownership information to reach the correct person without borrowing context from whichever account was opened last. A fractional running four clients is already re-entering context about five times a day; an unowned 3:00 a.m. job adds a sixth.
Your first audit question is therefore mechanical: Can any code path enqueue work without supplying requestedBy?
If the answer is yes, accountability is optional.
Cron and system callers still resolve a real person
A cron has no human session. A system process has no browser cookie. Letting either use a generic “system” owner makes the queue easier to wire and harder to explain.
What worked for us was making non-human callers resolve a real human before enqueueing the job.
const owner = resolveHumanOwner({
workspaceId,
jobType
})
enqueue({
type: jobType,
payload,
requestedBy: owner.id
})
The tradeoff is extra resolution logic for scheduled and system work. The gain is concrete: every resulting draft, analysis or agent action belongs to someone who can see it and assess it. Across all 32 job types, none are exempt from that resolution step.
This is especially important for recurring marketing work. Content drafts, scheduled crons and agent loops may run all day without frontier reasoning. Moving those workloads into a local bullpen can improve the cost curve, but lower compute cost does not lower the accountability standard.
For each non-human caller, record three things:
- Which workspace produced the signal
- Which human owns that class of work
- What happens when no eligible owner can be resolved
In our experience, the safer unresolved state is no enqueue. An unassigned job cannot be streamed to the person it belongs to because that person was never established.
Payload-hash deduplication turns signal bursts into one job
Background signals arrive in bursts. A single underlying change can produce several near-identical requests before the first job finishes.
We address that at the queue seam with a payload hash. The queue hashes the meaningful job payload and checks for an equivalent active job before accepting another.
const dedupeKey = hash({
type,
payload,
requestedBy
})
Including the job type and owner prevents unrelated work from collapsing together. Hashing the meaningful payload catches duplicate signals without depending on arrival time alone.
The audit question is specific: If the same signal arrives five times, does the queue create five AI runs or one? With a payload hash at the seam, five identical signals produce one job and one notification.
The right dedupe window depends on the job. A document-triggered analysis and a daily cron have different timing. The mechanism stays consistent: define equivalence, calculate the hash at enqueue time and reject or reuse an active match.
This cuts duplicate compute and duplicate notifications while preserving separate work for separate people.
Owner-scoped streaming makes background work observable
Once ownership is required, progress can follow the same boundary.
Our queue streams job updates live to the user named by requestedBy. The interface does not need to expose every internal event. It needs to show enough state for that person to answer four questions:
- What started?
- Why did it start?
- What state is it in?
- What did it produce?
Realtime delivery is the primary path. Polling is the fallback when the stream is unavailable. An outage breaker prevents a failed realtime connection from retrying without a bound.
This creates one operational chain:
signal → owner resolution → payload hash → queue → owner-scoped updates
If realtime fails, polling keeps the visible state current. If the outage continues, the breaker limits recovery attempts according to the configured threshold. The job can continue without pretending the live connection is healthy.
Audit the whole queue at its narrowest seam
Checking 32 job implementations one by one invites drift. Checking the shared enqueue seam gives every job type the same minimum standard: one seam instead of 32 separate reviews.
Use this five-part review:
requestedByis required by the queue interface- Session state is never used to infer ownership
- Cron and system callers resolve a human explicitly
- Equivalent bursts share a payload-based dedupe key
- Updates stream to that owner, with polling and a breaker behind them
This is the accountability plumbing behind proactive AI. It does not decide whether an output is good. It makes sure the output has an owner, duplicate work is contained and failures remain visible.
One seam can govern every proactive agent
The useful standard is not that background AI can run unattended. It is that unattended execution still has human ownership.
T2D3 OS applies that standard across 32 job types in one queue seam. For teams evaluating an AI marketing operating system, ask to see this layer. The answer should identify where ownership becomes required, how duplicate signals collapse and how the named user sees progress when realtime delivery fails.
This article is part of “AI Teammates You Can See Working,” a series on four small proactive-agent visibility features that form one coherent accountability narrative.