Repository navigation
fix(agents): Record whose work a child agent does in its session log - #2181
Open
sentry-junior[bot] wants to merge 1 commit into
Open
sentry-junior[bot] wants to merge 1 commit into
sentry-junior[bot] wants to merge 1 commit into
Conversation
A watch, timer, or result Turn of a child agent runs as the event system Actor. Action review used that Run Actor to read the root Conversation, so the root user's own requests were labeled as another person, and routine steps on the child's own work were blocked. spawnAgent and messageAgent now put the calling Turn's person in the meta of the task message that they write into the child, as onBehalfOf. Each child Turn saves it in the provenance of its instruction, and a Turn without a stamped task keeps the value of the child's latest instruction. Action review reads the root Conversation as onBehalfOf. The Run Actor, credentials, and child user intent do not change, and agent text still has no author Actor. Nested results and messages now take the Actor and credentials of the child's latest task, not of its newest agent invocation, so the grants of a timer Turn never pass to later work. Supersedes #2175. Co-Authored-By: David Cramer <david@sentry.io>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Oct 30, 2026
dcramer
added a commit
that referenced
this pull request
Oct 31, 2026
Refs #2439 This adds the plan for #2439 in `openspec/changes/actor-identity-end-state/`. It also adds **Owner** and **Trusted author** to `TERMINOLOGY.md`, and says that an Automation has a creator, not an owner (also in the dashboard Automation editor README). There is no code change. The plan fixes the target and the rules. It does not fix the implementation. At the start of each step, the step re-checks the plan against current `main` and settles its own details (SQL, keys, field names, signatures, readers, tests) in its PR. A change to a decided rule needs David's OK. - `proposal.md`: scope, David's answers, plan decisions, open questions, behavior changes, and risks. - `design.md`: the model and rules, the current proposals that a step must confirm, and the known gaps. - `tasks.md`: how to use the plan, release rules, dependencies, and steps 1–7. Each step has a goal, a done outcome, constraints, and what to decide in that step. - `specs/conversation-identity/spec.md`: the end-state requirements and scenarios. **Decided by David Cramer in the Slack thread** - The Owner is the person who started the Conversation, or the system for an automation. Owners never transfer. - There should be no unknown actors on any Message from now on. - The local CLI user can be the system Identity. - A web user is the User they sign in with, through the normal User and Identity tables. - The schema should be normalized. - Assistant output is just an event. It needs no author Identity. - Web users always have an Identity. The mock viewer mocks one. - Child spend rolls up to the root Owner. - Slack bots have an Identity but no User. - For an inbound event, Junior finds the correct Identity and verifies it the same way as everywhere else. Junior ignores the instructions of someone without access. - Most events are system events, with the system Identity as author. PR feedback and comments are authored by the person or bot who wrote them. - Legacy `junior/<email>` rows: leave them as they are, unless a migration path is clearly correct. - Participants: "same as today: users." Verified event authors as participants: "not right now maybe we will change our mind later." - "also we'd allow list usernames in juniors config for github events (so we could allow list some bots)" - "call it trusted authors or sometihng", then: "yes "trusted" is what we want here, but you can use the suffix that makes sense" - "any sender who doesnt have the allowed domain email address or is unknown isnt trusted. bots and or other users could be listed under trusted" - On what trusted text may do: "trusted authors are the same as normal authors... its just that untrusted authors we dont run" His answers to the 14 earlier proposals: (1) "bots need to be annotated as bots, thats important"; (2) "you can find what makes sense"; (3) "yes"; (4) "correct"; (5) "yes but an event *is* a message so"; (6) "yes"; (7) "sure defer to you but needs to be correct"; (8) "yep"; (9) "yep. and users have many identities associated to them"; (10) "thats meaningless to me, make sure its good arch and ill trust you"; (11) "yep"; (12) "idk up to you make it correct"; (13) "sure, we dont really have "owners" on automations anyways"; (14) "ok". **Plan decisions David handed off (2, 7, 10, 12).** Each is a current proposal that its step confirms: the local auth bypass gets an Identity that cannot match a production User (step 3); the Owner repair runs only when it is clearly correct, after a dry run (step 4b); one system Identity with a key that no plugin or OAuth link can write (#2443); the mock viewer Identity is in memory only (step 3). **Target, from those answers:** one Owner per root, set at insert, human or system, never transferred. Children read the root Owner, and child spend rolls up to it. One system Identity; "which system" is provenance. Web users use their sign-in Identity; the mock viewer has a mock Identity. Bots are kind `bot` with no User. Every new user Message has an author Identity; assistant output has none. Events are user Messages and only set the Message author. An event author is trusted when Junior verifies them on an allowed domain or their login is on `trustedAuthors`; untrusted or unknown authors do not run. Event authors are not participants. Legacy rows stay unless a migration is clearly correct. **Release rules:** each step ships alone and is safe under a rolling deploy. Readers ship before writers. A column drop ships one release after the code stops using it. No production backfill without explicit approval, and a dry run before any repair. **Steps:** (1) one Actor-to-Identity mapping, built in #2443 (in review); (2) carry the Identity id; (3) real Identities and the trust rule; (4a) write the Owner; (4b) backfill, enforce, and read the Owner (riskiest); (5) require authors and retire stored Actors; (6) stop using the old columns; (7) drop them. #2441 before 2, #2260 before 3, #2382 before 4a, #2181 before 5. **Open questions for David:** 1. Mixed event batch: are untrusted entries dropped, or kept as context that does not start a run? Plan reading: dropped. 2. Does Cursor Bugbot go on `trustedAuthors`? Plan reading: not listed yet, so its reviews stop starting runs when step 3 ships. **Behavior changes:** automation-run spend leaves personal spend. A human who replies to an automation Conversation becomes a participant, not the Owner. Child spend goes to the root Owner. Trusted author text counts like a chat message; untrusted or unknown authors do not run. The participant list stops showing system Identities. Locations show one row per User. <!-- junior-request-attribution:start --> via **David Cramer**. <!-- junior-request-attribution:end --> <!-- junior-session-footer:start --> <!-- junior-conversation-id:agent%3Af31d0b3589d3ee280ff739bacb901c33 --> -- [View Session](https://junior-prod.sentry.dev/conversations/agent%3Af31d0b3589d3ee280ff739bacb901c33) [[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3Af31d0b3589d3ee280ff739bacb901c33/?project=4510944073809921) <!-- junior-session-footer:end --> --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com> Co-authored-by: David Cramer <david@sentry.io>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #2175.
Problem
A watch, timer, or result Turn of a child agent runs as the event system Actor. Guardian read the root Conversation as that Run Actor. So the root user's own messages were labeled
other person:, and Guardian blocked routine steps on the child's own work, such as a 👍 on review feedback or resolving a thread on the child's own PR.Design
Whose work a child does is now session-log metadata. It is not a column, and it is not a field that only subagents have.
onBehalfOf: Actor.actorstill means "who wrote this", and agent-written text still has noactor, so the fix(agents): Never take parent agent text as user approval in a child #2147 protection holds.onBehalfOfmeans "whose work this instruction serves". Only the runtime sets it.spawnAgentandmessageAgentput the calling Turn's person in themetaof the taskmessageevent that they already write into the child:automation:authority still works.onBehalfOf.onBehalfOffrom the child's latest instructionuser_message(loadLatestInstruction). The Turn writes the value into its own instruction provenance, so watch, timer, and result Turns keep it. Provenance passes unchanged through projection, compaction, and handoff.readRootConversationreads asonBehalfOf, not as the Run Actor. The Run Actor, credential context,CHILD_AGENT_USER_INTENT, and the Guardian prompt do not change.onBehalfOf. A message with no stamp keeps the running value.on-behalf-of.tsowns these rules.agent-invocations/README.mddocuments them.Why this replaces #2175
#2175 added
turn_started.taskActor,subagent_started.causingTurn, a watchturnId, a mailboxwatchTurnId, andresolveCausingTurn, which walked back from each Turn to its cause. Its audit found two problems. Batched event input from different people produced "no person". Watch replacement changed the creator.This PR removes all of that. Each Turn does not find its cause. The person is fixed when a parent gives a task, and it stays in the child's log until a new task changes it. Event batching and watch ownership have no effect on it.
Also fixed (from #2175)
Nested results and messages took the Actor and credentials of the child's newest agent invocation. That row can be a timer Turn, so timer grants could pass to later work.
child-input.tsnow uses the child's latest task fromspawnAgentormessageAgent. The regression test is inagent-invocation-nested.test.ts.Legacy data
A child that started before this deploy has no
onBehalfOf. On its event Turns, Guardian sees nouser:lines until a new task stamps one. There is no fallback to the Run Actor, the thread owner, oractor_json. This is on purpose: when the cause is not known, there is no person.Evals
The held Guardian eval in
child-agent-approval.eval.tsnow has two cases: a PR-watch Turn in a child marks feedback addressed and resolves the handled thread. I could not run it in the sandbox because no model or Roach credentials were available, so it needs a recorded run in CI.via David Cramer.
--
View Junior Session [Sentry]