Skip to content

feat(asks): an ask raised during a Workspace chat turn attaches to that chat (ent#734) - #3137

Merged
vybe merged 5 commits into
devfrom
AndriiPasternak31/ent734
Oct 1, 2026
Merged

vybe merged 5 commits into
devfrom
AndriiPasternak31/ent734

Conversation

@AndriiPasternak31

@AndriiPasternak31 AndriiPasternak31 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Refs abilityai/trinity-enterprise#734
Refs abilityai/trinity-enterprise#610

Data half of ent#734; the chat tile is drawn by #3101, which lands with or after this. Covers AC 1 (record the raising chat), AC 4 (platform-written, never agent-named) and AC 5 (the backend test). AC 2/3 (tile answerable in place, ended marker) are #3101's.

No migration taken. The execution↔chat link already exists: both portal turn-creation sites (and portal_chat's cold retry) stamp source_channel="portal", source_channel_chat_id and source_channel_client on the execution row at creation (ent#457/#2426). No column, no Alembic revision, and no interaction with #3021's pending 0083.

What changes

Trust bound (review r1)

The header is set by the platform, but the agent's own process holds its key and can send any of its executions' ids. After r1, the most a forged id can do is move an ask into another running turn of the same agent for the same addressee. That is the #2392 trust level turn_audience already accepts. It can never reach another person's chat, another agent's chat, or a finished turn. The docstrings and §26.12 now say exactly this and no more. An archived (reset) Main is deliberately not excluded: it stays listed and resumable, and Reset is refused mid-turn.

Tests

  • tests/unit/test_ent734_ask_raising_chat.py: 31 tests, written red-first.
  • 18 named mutations, each turning the suite red:
    • chat lookup: drop the running, trigger, channel, client, session or email-case check; drop the fail-soft; let a gate read the turn; never pass the turn; exclude archived chats;
    • r2: never write the flag; write it whenever there is a thread; strip only the thread key at the native boundary, the file boundary, the replay compare or the file-sync compare; project a truthy value instead of only true; claim in-turn on the Main fallback.
  • 128 related unit files: 3554 passed, 3 skipped. lint_sys_modules is at its baseline.
  • test_ent611_native_ask.py: the fixture now stubs _workspace_attachment, the new core.

Docs

  • security.md §26.12 (OPS-001-THREAD)
  • operating-room.md "Which chat an addressed ask attaches to" + the feature-flows.md index row
  • a pointer in workspace-agents-at-the-centre.md
  • architecture/workspace.md (landing rule)

…at chat (Abilityai/trinity-enterprise#734)

Every addressed ask was filed under the pair's Main chat because nothing at
raise time knew which execution raised it. The native raise now hands the
platform-injected execution id (#2392, validated as the agent's own) to
`_workspace_thread_for`; `client_portal.service.chat_for_execution` returns
that turn's chat iff the row is a Workspace chat turn (`triggered_by="public"`,
`source_channel="portal"`) of the same agent and addressee and the session
belongs to that pair. The link is the one both portal creation sites already
stamp (ent#457/#2426), so no column and no migration.

Everything else keeps Main, unchanged: schedules (including one delivering
into the Workspace, which carries the portal stamp), loops, rooms, delegated
children, gate raises, no/unknown/foreign executions, another addressee's
chat, and every file-ingested ask (an agent-cited execution id is never
read). Fail-soft: a lookup failure attaches to Main with a warning. The
ent#429 strip of an agent-authored workspace_session_id stays.

Data half only (AC 1, 4, 5); the chat tile and the ended marker (AC 2/3)
belong to #3101.
…#734 review r1)

cso/review r1: the X-Trinity-Execution-Id header is platform-set, but the
agent's own process holds its key and can send any of its executions' ids.
chat_for_execution accepted any of them, so a finished turn of another chat
(same addressee) could file an ask there. It now requires the turn to be
RUNNING. The remaining bound — another live turn of the same agent for the
same addressee — is the #2392 trust level turn_audience already accepts, and
the docstrings/requirement no longer claim more than that.

An archived (reset) Main is deliberately NOT excluded: it stays listed,
readable and resumable, and Reset is refused mid-turn, so a running turn there
is a person talking in a chat they can see. Pinned, with the email-case match
(both mutation-checked).
… ask (ent#734 r2)

After ent#734 a chat-turn ask raised in MAIN and a background ask (schedule /
loop / gate) both carry chat_id = Main, but the ent#610 amendment draws the
first as a tile in Main and the second in no chat, so #3101 could not tell
them apart.

The row now carries one more platform-written fact,
context.workspace_raised_in_turn = true, written only when the raising turn's
chat matched (`_workspace_attachment` returns `(chat, raised_in_turn)`;
`_workspace_thread_for` stays as the file path's no-turn view). The client
projection names it as WorkspaceAsk.raised_in_turn (only a literal true
counts).

Both workspace keys are now `_PLATFORM_CONTEXT_KEYS`: stripped from
agent-authored context on the native and the file path, and ignored by the
replay `differs` and the #2915 file-sync comparison, so a planted flag is
neither honoured nor read as a rewrite. 8 named mutations, each red.
AndriiPasternak31 added a commit that referenced this pull request Sep 30, 2026
…ent610-a2 (ent#610 PR A2 stacks on ent#734)

# Conflicts:
#	docs/memory/feature-flows/workspace-agents-at-the-centre.md
…no frontend reads raised_in_turn yet, drop a repeated clause, _comparable_context docstring names both platform keys
AndriiPasternak31 added a commit that referenced this pull request Sep 30, 2026
…hread; nothing sits above the composer (ent#610 PR A2, 09-30 ruling item 1)

The ruling as amended the same day, on ent#734's data (#3137): an ask with
raised_in_turn === true is drawn in the chat its chat_id names, as a row of
the thread placed by time among the messages (portalChatAsks.placeAsksInThread:
before the first row strictly newer than the ask; a row with no time of its
own is a reply just received, so the newest; the person's own message is
stamped as it is sent). While it waits it is the one PortalAsks card,
answerable in place (thread link off: it IS in its chat). An ask seen waiting
on this visit keeps its card after it ends (the ent#468 confirmation); any
other ended ask is one muted history row: kind · title · ending with who ·
when. It stays for as long as the asks read returns it (7 days).

A background ask (raised_in_turn false: schedule, loop, gate) draws in no
chat, Main included; an unattached ask no longer falls to Main.

Removed: the pinned splitChatAsks box above the composer and the "N more
asks" line (principle 30), with splitChatAsks / pinnedAskIds /
chatAsksLabel / chatAsksElsewhere. Their specs are rewritten for the new
homes (round 4 §4, round 5, #3115 strip, B1 residual, single-source,
agent-page-ux), not deleted.

Mutations, each run once and restored from a scratch copy:
  M1 raised_in_turn truthy instead of === true   -> 1 red
  M2 a tie places the ask before the row         -> 1 red
  M3 an undated row is the oldest, not newest    -> 1 red
  M4 seen-waiting ignored (ended = row at once)  -> 3 red
  M5 the tile keeps "Open the conversation"      -> 1 red
  M6 history rows lose their persisted time      -> 2 red
FE unit 4375/4375.
AndriiPasternak31 added a commit that referenced this pull request Sep 30, 2026
@AndriiPasternak31
AndriiPasternak31 marked this pull request as ready for review September 30, 2026 22:11
@AndriiPasternak31 AndriiPasternak31 self-assigned this Oct 1, 2026
@AndriiPasternak31
AndriiPasternak31 requested a review from vybe October 1, 2026 08:37
@vybe

vybe commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

merge-train (#3143): on this train, merging second, right after #3054. /validate-pr and /review came back READY with no criticals. The tests run the real raise_ask → _workspace_attachment → chat_for_execution path, and breaking it on purpose fails tests.

Edited for you: the Refs lines are now fully qualified so GitHub links them. Once #3054 lands, I'll merge dev into this branch to resolve the paragraph conflict in workspace-agents-at-the-centre.md, keeping both paragraphs with this PR's first, the same order as #3101.

# Conflicts:
#	docs/memory/feature-flows/workspace-agents-at-the-centre.md

@vybe vybe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

merge-train: batch validated on train/#3143 (all Tier 1 + Tier 2 green); dev merged in for the #3054 doc paragraph

@vybe
vybe merged commit 0ea80a5 into dev Oct 1, 2026
26 checks passed
vybe pushed a commit that referenced this pull request Oct 1, 2026
#3054 and #3137 landed as squashes, so dev re-carries content this branch
already contains. Each conflict resolved to this branch's side as integrated on
train #3143; the result tree is byte-identical to the CI-green train tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants