Skip to content

feat(workspace): Workspace Projects — seams, MCP tools and gated UI (ent#661 v1–v3) - #3119

Merged
vybe merged 12 commits into
devfrom
feature/ent661-projects-seam
Sep 30, 2026
Merged

vybe merged 12 commits into
devfrom
feature/ent661-projects-seam

Conversation

@dolho

@dolho dolho commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Workspace Projects: the open-core seams, the agent MCP tools, and the gated Workspace UI for Abilityai/trinity-enterprise#661 (v1–v3). The project logic lives in the private module, which lands in its own PR. This PR has the edition-agnostic seams and the surface the module fills in. On an OSS build every seam returns nothing, and the UI is hidden.

Fixes Abilityai/trinity-enterprise#661

Seams (backend)

MCP (tools/projects.ts)

  • Read: list_projects, get_project, list_project_tasks, get_project_log.
  • Write: create_project_task, update_project_task, add_project_task_note, add_project_log_entry, link_to_project (a file, report, decision, or an ask the agent raised).
  • Steward: get_steward_digest, set_project_health.

The tools are license-blind (404 means no module; a plain-string 403 means unlicensed). Every call forwards the platform-supplied X-Trinity-Execution-Id, never a tool parameter. The module answers only in a turn whose audience is internal.

Workspace UI (gated on projects_available)

  • A Projects list with an "I steward" tab, and a project page with Overview, Tasks, Log, and Files & reports.
  • Overview covers:
    • health, set by the steward or creator;
    • where it stands: tasks by status and what needs attention;
    • linked agent metrics: value, trend and the platform's stale verdict;
    • Needs you: the viewer's own asks on the project, answered in place. PortalAsks gains an askIds filter that can only narrow the viewer's own list.
  • The members, guests and import dialogs.
  • A rail tab and chat/room header controls (link, detach, make a project, Wrap up).
  • A narrow guest view for invited outside clients.
  • Tokens only. Light and dark. Fits a 390px phone.

Docs

docs/memory/requirements/public-access.md §48.4 (FR-1…FR-7), plus architecture/backend.md and architecture/workspace.md. They describe the seams only.

Screenshots

To be attached by the author: the v3 overview (health, where it stands, metrics); Needs you with an ask raised in a project chat; the I steward tab; an agent working inside a linked chat; the project log after Wrap up; the guest view; and an outside client asking the agent about the project and being refused.

Test plan

  • Backend: ent661 unit tests (turn context, wiring, ask addressee, ask turn, roster capability) and the ask/queue/turn-audience set: 1,141 passed.
  • MCP: 604 passed. tsc --noEmit is clean.
  • Frontend: 3,889 passed, including mount tests for every write gate (Safety-critical UI logic keeps landing in the one tier with no executable coverage — and the stated reason is false #2918) and the raw-colour, loading-gate, source-text and theme-switch ratchets. The design-token check and npm run build both pass.
  • Mutation checks: disabling the health gate and the "only my asks" filter each turns a test red.
  • Live click-through on a local stack, with a real agent turn:
    • agents create tasks and write the log through the tools;
    • Wrap up;
    • import;
    • guest view;
    • an outside client is refused by the tools;
    • an ask raised in a project chat appears under Needs you;
    • an agent steward runs the digest and sets health.
  • Enterprise docs guard grep over the diff: no hits.

🤖 Generated with Claude Code

dolho and others added 12 commits September 30, 2026 10:40
…(ent#661)

An edition-agnostic provider registry (services/turn_context.py) that lets a
module add a line to a Workspace turn. Both composers call it: the 1:1 chat on
both the resumed and cold arms, the room ahead of the file manifest. The
context is resolved by the platform (chat row, room membership), including
internal_audience, so an internal-only line never reaches a turn an outside
client can read. No provider in an OSS build: turns are byte-for-byte unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
raise_ask(addressee=...) bypasses role resolution for a platform raise, so a
consent decision (an agent owner approving a project link) reaches that owner
rather than whoever an assignment provider maps primary to. An agent's raise
that names an addressee is a programming error: an agent never picks who is
asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w seam (ent#661)

PortalRoster.projects_available is true only when the module is entitled and
the principal is a platform user, so an outside client is never told the
capability exists. Defaults False (fails closed on an older backend). The
roster stays the Workspace's only capability channel (#2128).

The enterprise-docs guard now scans services/turn_context.py like the other
open-core seam files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-facing reads over the Workspace Projects agent routes: the projects the
calling agent is active on (its owner consented), goal/status/steward/links,
and linked chat/room counts only — never other people's chats. License-blind
like credential_vault.ts: 404 = OSS build, string 403 = unlicensed, code 403 =
agent-key gate; never throws.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…trols (ent#661)

Gated on the roster's projects_available (strict === true), so an outside
client and a build without the module never see it.

- /workspace/projects: the person's list across agents (all / member /
  company, archived toggle, search); /workspace/projects/:id: goal, status,
  steward, Work on it (reopen my newest chat, or start one and link it),
  my chats, others' chats as a count only, agents with consent state,
  members, rooms. Creator: status, archive/restore, Members & access.
- Rail Projects tab (platform door + capability, checked fail-closed).
- Chat header: the project badge + Detach, or one Project button that adds
  the chat to a project or makes a new one from it. Never in Main.
- stores/projects.js on portalHttp; four honest states via viewState.
- Mount tests for every destructive or store-writing predicate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t view (ent#661 v3)'

Requirements 48.4 and the architecture entries for the turn-context seam,
the addressed gate ask, the roster capability and the gated Workspace UI.
Describes the seam only; the module design stays private.

Renames the objective-join references from the retired 'project hub' to
'the project view (ent#661 v3)' as asked on the issue.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uests, import, Wrap up (ent#661)

Backend (open-core seams):
- services/portal_capabilities.py: a per-person capability seam (fails
  closed); the roster's projects_available now also reaches an outside
  client invited to a project. Guarded by enterprise-docs-guard.

Workspace UI (gated on the roster capability):
- Project page tabs: Overview / Tasks (ent#673 fields, grouped open /
  awaiting verification / done; a done task only reopens) / Log (append-
  only timeline + add) / Files & reports (add from what you can see, share
  with guests, ReportRenderer view).
- Guests: invite in Members & access; a narrow page, list and rail for an
  invited outside client; no Project button or Wrap up for them.
- Import a folder project from an agent (one-shot).
- Wrap up in a linked chat: a visible message asking the agent to record
  the chat's outcomes with its project tools.
- v1 gaps: Edit (name, goal, person/agent steward, tracker, visibility);
  link a room from its header.

MCP: list_project_tasks, get_project_log, create_project_task,
update_project_task (assignee -> agent), add_project_task_note,
add_project_log_entry, link_to_project.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every Workspace-project call now carries the request's X-Trinity-Execution-Id
(#2392), taken from the auth context and never from a tool parameter, so the
backend can decide who the turn is for. A refusal for an outside audience, or
for a turn it can't identify, comes back as words the agent can act on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s kind (ent#661)

At 390px the Overview grid's implicit column and the tab strip sized to their
content and pushed the cards past the viewport. Explicit single-column grids,
plus a bounded box around OverflowTabs so "More" collapses the extra tabs.
The task log also shows each entry's kind (Created, Done claim, Reopened…).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ask_operator forwards the request's X-Trinity-Execution-Id (#2392). When that
id is the raising agent's own execution, raise_ask stamps it as the ask's
execution, winning over an agent-written context.execution_id. `manual`, an
unknown id or another agent's id changes nothing. This is how an ask raised
in a project's chat is found on the project; before this, the row carried
only what the agent chose to write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
get_steward_digest and set_project_health for a steward agent, and
link_to_project now accepts an ask the agent raised. Both forward the turn id
like the other project tools, and have policy rows in access.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eeds you, I steward (ent#661)

Overview gains the hub:
- the steward's health (set in a dialog);
- tasks by status, with what needs attention (opening Tasks);
- linked agent metrics, each with its value, trend and stale state;
- Needs you: the viewer's own asks on the project, answered in place with
  PortalAsks, narrowed by the new askIds prop, which can only narrow.

The list shows health and an attention count per row, and gains an "I steward"
tab. A health update reads as Status in the log. Mount tests cover every write
gate (#2918); FR-5, FR-7 and the ask-turn note are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vybe

vybe commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

merge-train: this PR is on today's train with #3122, #3120 and #3085. It validated READY at lane C: /review and /cso --diff found nothing blocking. The docs-guard change only adds coverage, and on its own this PR does nothing until the enterprise module registers projects.

Merge order: this PR, then abilityai/trinity-enterprise#732, then the submodule pin bump. ent#732 imports turn_context, portal_capabilities and raise_ask(addressee=) from here.

Follow-ups. None of them block:

  • docs/memory/architecture/mcp-server.md has no row for projects.ts, which adds 11 tools.
  • The chat arm's internal_audience=bool(include_owned) in client_portal/service.py is only asserted through inspect.getsource. It decides whether an internal-only project line can reach a turn a client can read, so a test that drives a turn through portal_chat would pin it.
  • The strict is True in portal_capabilities.has isn't pinned: changing it to bool(...) leaves everything green.
  • There's no feature-flow doc for Projects.
  • abilityai/trinity-enterprise#661 needs status-in-dev set by hand after the merge, because cross-tracker keywords don't get promoted.

@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/20260930-1429 (#3126, all gates green)

@vybe
vybe merged commit c355489 into dev Sep 30, 2026
27 checks passed
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.

2 participants