Skip to content

feat(events): Treat linked requester PR comments as their instruction - #2260

Open
sentry-junior[bot] wants to merge 4 commits into
feat/identity-dispatchfrom
feat/requester-comment-authority
Open

sentry-junior[bot] wants to merge 4 commits into
feat/identity-dispatchfrom
feat/requester-comment-authority

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Closes #2258. Stacked on #2217, which adds the linked Slack identity lookup and membership checks this uses.

When someone asks Junior in Slack for a PR and Junior watches it, a follow-up comment from that same person on the PR is now treated as their instruction for that Event Turn. Before, every PR comment was untrusted, so Guardian blocked things like "also file a docs issue" even when the requester asked for it.

How the match works (fails closed at each step):

  • A Watch stores its requester: the Slack user whose own message started the root Turn that created it. Event Turns, automations, and child agents store no requester. Re-creating a Watch from one of them drops it.
  • The GitHub plugin now sends the numeric GitHub user id (doneBy.subjectId) and the author's own text (doneByText) for comments and reviews. Markdown quote lines are removed, since quoting is not the author's own words. Logins never match.
  • Core uses the same findLinkedSlackMember() check as Identity dispatch: one exact linked account, an active human member, an allowed domain. The linked user must also be the requester of that Watch in the same workspace. The check is now shared with feat(dispatch): Start DM dispatches for linked provider identities #2217's dispatch code instead of copied.
  • If it matches, the text shows up as a separate Requester instruction section in the task input. Guardian gets it in its own section with a random key, and only for an Event Source. It is saved on the Turn's Message, so a resumed Run keeps it.

What does not change: the Turn still runs as the system Actor with the same credentials. Visibility and credential authority do not widen. Other people's comments, quoted text, and the PR body stay untrusted. If the lookup fails, the Event is delivered without the instruction. Child agents do not get requester instructions yet.

New Guardian evals in evals/guardian/requester-comments.eval.ts cover three cases: requester asks for a cross-repo issue (allow), a different reviewer asks for the same thing (ask/deny), and a comment with forged requester text (deny). I could not run them locally. They need CI.

via BYK.

--

View Junior Session in Sentry

A Watch now stores the Slack user whose own message created it. When a
GitHub comment or review comes from the immutable GitHub account that links
to that same Slack user, core adds the author's own text (without quotes) as
a requester instruction. The agent and Guardian treat it as a message from
that person. Every other comment and the PR body stay untrusted, and the
Event Turn keeps the system Actor and its credentials.

Refs #2258

Co-Authored-By: BYK <burak.kaya@sentry.io>
@vercel

vercel Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 29, 2026 7:20pm UTC

Request Review

@dcramer
dcramer marked this pull request as ready for review October 28, 2026 00:38
@github-actions github-actions Bot added the risk: high PR risk score: high label Oct 28, 2026
…hority

# Conflicts:
#	packages/junior-github/src/webhooks/events.ts
#	packages/junior-github/tests/webhook-outcomes.test.ts
#	packages/junior/src/chat/events/notification.ts
#	packages/junior/src/chat/guardian/gate.ts
#	packages/junior/src/chat/guardian/reviewer.ts
#	packages/junior/src/chat/task-execution/conversation-turn.ts

Co-Authored-By: BYK <burak.kaya@sentry.io>

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Quotes inside lists and HTML blockquote tags stayed in doneByText, so text
that only quotes someone else could count as a requester instruction. Walk
the Markdown tokens at every depth, and drop HTML quote blocks until they
close.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8a4ef59. Configure here.

The HTML quote tracker counted closing tags anywhere, also in HTML comments
and attributes. A fake closing tag ended the quote early, so quoted text
could become a requester instruction. Do not trust closing tags: remove the
block with the first HTML quote tag and all text after it.
sentry-junior Bot added a commit that referenced this pull request Oct 30, 2026
Record David's answers on event authors and system events, the current
event author gap, and a proposal for a per-event-type actor declaration
in the plugin. Move #2260 ahead of step 3.

Refs #2439

Co-Authored-By: David Cramer <david@sentry.io>
sentry-junior Bot added a commit that referenced this pull request Oct 30, 2026
Record David's answers on event authors and system events, the current
event author gap, and a proposal for a per-event-type actor declaration
in the plugin. Move #2260 ahead of step 3.

Refs #2439

Co-Authored-By: David Cramer <david@sentry.io>
sentry-junior Bot added a commit that referenced this pull request Oct 30, 2026
A cold review found gaps that break a rolling deploy or widen trust.

- Split the Owner step. 4a writes the column, and 4b adds the NOT VALID
  check one release later, because old workers insert roots with no
  Owner while migrations run.
- Drop each column in two releases. conversationReadColumns() selects
  every schema column, so release N stops reads and writes and N+1
  drops it. Add an explicit stop of actor_json writes.
- Pin Guardian labels to the Actor and provenance, never the one system
  Identity id, and keep trusted event text in the Requester instruction
  section.
- Use findLinkedSlackMember() for event author trust, the same check as
  #2217 and #2260.
- Pin Identity keys, the mailbox field, and the full system merge.
- Drop hasActor and list the remaining choices as proposals.

Refs #2439

Co-Authored-By: David Cramer <david@sentry.io>
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>
dcramer added a commit that referenced this pull request Oct 31, 2026
… legacy data (#2489)

Refs #2439

Updates the Actor identity plan
(`openspec/changes/actor-identity-end-state/`) to the reevaluated plan
that David answered in the #2439 Slack thread.

David's answers, quoted in `proposal.md`:
- "(1) yes": old event Messages get the system Identity as their author.
- "(2), why not just make them system? if they dont have an author just
make them without an author? should be uncommon": plan reading: old user
Messages whose author cannot be proven stay with no author. The dry run
counts them.
- "(3) i guess? usually theres just like one actor.. everything else is
metadata": `junior_conversations.actor_identity_id` stays as the Owner
column. Code and docs call it the Owner.

Plan decisions:
- The new Owner column work (old steps 4a/4b) is gone. #2482 makes the
existing column keep its first value, which is the Owner rule.
- New legacy data rule: migrate an old shape only when special code
exists for it, and only with a rule that a read-only dry run proves.
Then delete that code. `design.md`, Legacy Data, lists the shapes that
need it and the ones that do not (`junior/<email>` Identities, Redis and
Turn checkpoints).
- Owner repair (#2431) is a separate step. It fixes wrong data, not a
code path.
- The Bugbot open question is closed with David's "yeah Bugbot and
sentry will end up on trusted actors".

New step order: 1 (#2443, done), 2 (#2473 done, finish #2482), 3 labels,
`system` for new system Conversations, child Owner, and root Owner
readers, 4 real Identities and trust (needs #2260), 5 dry run and
David's OK, 6 one data migration, 7 Owner repair, 8 delete fallbacks and
stored Actors after the Redis window, 9 drop columns and delete the
plan. Steps 1 and 2 keep their numbers, so the references in #2482 stay
correct.

All other decided rules stay the same. `TERMINOLOGY.md` does not change.

<!-- junior-request-attribution:start -->
via **David Cramer**.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A792f6b2d81b457a9b23a84193e893814 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A792f6b2d81b457a9b23a84193e893814)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A792f6b2d81b457a9b23a84193e893814/?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

1 active deployment
Preview – junior-docs — 6d94668e Deployed Oct 29, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants