Skip to content

feat(personas): personas can run with full access - #469

Merged
bryantderosier merged 17 commits into
j5/mainfrom
feat/persona-full-access
Oct 7, 2026
Merged

bryantderosier merged 17 commits into
j5/mainfrom
feat/persona-full-access

Conversation

@bryantderosier

@bryantderosier bryantderosier commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

A persona can now run with full access outside a Crew seat. It declares a full-access authority policy, which maps to the ordinary full-access runtime mode with no J5 restriction. The policy travels with the persona, so the composer, @persona: / delegate_task, spawn_agent and Crew seats all pick it up; no launch path changed.

What the app reads from the server now accepts any policy name, so the next new policy costs an older app one Unsupported row instead of the whole app. What clients send and what the server validates stay on the closed enum.

Apps must be updated with the server. A v0.0.48 app cannot read the new policy and stops loading once a full-access persona has run on the server it connects to; j5/main before this PR also loses its persona library against this server before then, because the catalog's enforcement table always lists the new policy. See Merge Danger.

 AgentPersonaAuthorityPolicy
   read-only | critic-review | workspace-write | critic-fix | diagnostic | publish-only
+  full-access

 translate(policy) -> AgentPersonaProviderPolicy
   readOnly    { sandboxPolicy, approvalPolicy }
   workspaceWrite { sandboxPolicy, approvalPolicy }
+  unrestricted  { runtimeMode: "full-access" }        # no sandbox/approval fields

 ENFORCING_DRIVERS["full-access"]
+  codex, claudeAgent, cursor, opencode, grok, antigravity, pi, acpRegistry

 invoke(parent persona -> child persona)
   read-only parent: denied unless child is read-only
+  judged by the parent's EFFECTIVE policy (real resolveRuntimeMode), not its stored thread mode
+  runtimeMode "inherit" only after that check clears a child broader than the stored mode

 persona library row
+  [Full access] badge (web)  /  pill (mobile)       # from allowed policies, any origin

read side (catalog entries, policyEnforcement, definition read, assignment in shell/detail/events)
-  authorityPolicy: AgentPersonaAuthorityPolicy      # closed enum: one unknown value rejects the payload
+  authorityPolicy: string                           # unknown default -> "Unsupported" row, not launchable/editable
command side (launch request, create, edit, server YAML validation, thread.create / delegated_task.request)
   authorityPolicy: AgentPersonaAuthorityPolicy      # strict (commands use OrchestrationV2AgentPersonaCommandAssignment)

Closes #432. Stacked on #391, which rewrote the same policy table; this PR targets that branch.

Evidence

  • Before: a persona could only get a network-off workspace-write sandbox (or read-only); full access needed a Crew seat with an explicit access mode.
    After: the effective policy of a persona launched through each path resolves to { runtimeMode: "full-access" } with no sandboxPolicy or approvalPolicy:
composer launch (stored thread mode approval-required)  -> effective full-access
delegate_task to a full-access persona                  -> child effective full-access
spawn_agent with a full-access persona                  -> Peer Agent effective full-access
Crew seat, no override                                  -> full-access ("Full access" in the preview)
Crew seat, explicit approval-required override          -> approval-required (override wins)
  • Delegation ceiling: a read-only parent, and a workspace-write parent even when its stored mode is full-access (a composer launch), are denied a full-access child; a full-access persona whose stored mode is narrower can delegate. Mutation-checked: removing the effective check, or removing "inherit", fails the tests.
  • Drivers: each of the eight listed drivers has a test that calls the real adapter helper with the translated policy (Codex approvalPolicy: never + dangerFullAccess, Claude bypassPermissions, Cursor sandbox off, OpenCode allow-all, Grok --always-approve, Antigravity yolo, Pi T3_PI_RUNTIME_MODE=full-access, ACP allow). Unknown drivers stay blocked.
  • Tolerant read side: a focused test decodes, from JSON, a catalog and a shell snapshot that each contain an invented policy. The catalog gives one Unsupported row naming the policy (not offered by the composer picker or @persona, no draft preview, no edit, duplicate refused) and the other row intact; the shell snapshot decodes both threads, and the unknown thread's persona label reads Scout · sandboxed-network (unsupported) on web and mobile chips. Mutation-checked: putting the closed enum back on the read side fails it. Contracts tests show the launch request, create input, and the server's thread.create / delegated_task.request commands still reject the invented policy (mutation-checked); the server refuses to run a thread whose stored policy it does not know, on every path (legacy assignments and Crew overrides included), rather than translating it into another policy.
  • Live, isolated state: this PR's server on a copied database with one full-access persona thread. j5/main's client contract (9745f1b) rejects the real /api/orchestration/shell response at threads[5].agentPersonaAssignment.authorityPolicy and the catalog at policyEnforcement[0].policy. With that thread's policy replaced by an invented sandboxed-network, this PR's client contract decodes all 6 threads, the invented policy preserved. Not run in a browser; the review host had no browser automation.
  • Tests and checks: 24 test files / 197 tests pass (server agents, Crew launch and preview, MCP handlers, client-runtime personas, contracts); tsc --noEmit is clean in contracts, server, client-runtime, web and mobile; fmt clean, lint shows only warnings that were already there. Two independent reviews re-ran the targeted tests and typechecks from clean clones.
  • UI pass: web, run in isolated dev environments (see UI changes). Mobile was not run; there is no Xcode or Android SDK on the machine that did this work.
1-before-full-access-persona-rejected 2-before-persona-row-no-policy 3-after-full-access-badge

Merge Danger

Door: One-way once a full-access persona has run

Reverting the code is easy; reverting the data is not. Once a full-access persona has run, its definition snapshot, its thread's persona assignment and its event history keep full-access. A server without this PR cannot decode that data and retries with failures, and a persona definition that declares full-access fails to load until it is edited.

Apps must be updated with the server. A v0.0.48 app is already built and cannot be fixed: against a server where a full-access persona has run, a fresh profile never gets past "Still connecting"; an existing profile keeps its cached threads but loses the full-access thread, its sidebar stops updating, and Settings → Personas shows "Personas unavailable". j5/main before this PR fails its persona catalog against this server even before any full-access persona exists. From this PR on, an app shows a policy it does not know as one Unsupported row (with the policy's name) and keeps working.

Blast Radius: Moderate

  • A persona in a shared git folder or an imported YAML can now declare full-access. The only mitigation is visibility: the library row shows a "Full access" badge/pill and the editor says "Unsandboxed".
  • spawn_agent with a persona has no parent permission ceiling (a recorded decision, Jackson, 2026-09-16), and the docs now say so. Whether a human sees a prompt first depends on the caller: J5 pre-approves spawn_agent on Claude in every mode and on Codex when the caller runs with approvals off (a Full access thread or any persona's runtime policy, read-only included); an ordinary Supervised Codex thread still gets the harness's approval prompt. So once any full-access persona exists, most agents that can spawn can start it as a Peer Agent without a prompt. Jackson accepted this as is (2026-10-05); visibility gaps are Personas: make full access visible where personas arrive and where they are used #473. delegate_task is bounded (above), and Crew seats still need roster approval.
  • For the maintainer to confirm: that acceptance is read here as also covering agent-authored or agent-modified folder definitions (BastiHu's scenario): a workspace-write persona whose workspace contains a configured persona folder can write a YAML that declares full-access, and folder definitions are reread on launch and on by default, so it can then spawn that persona unsandboxed with no further human step; a shared-folder update can likewise upgrade an existing persona for future launches. This PR adds no confirmation step for it. Please confirm or say otherwise.
  • Pre-existing, not changed here: a persona thread's stored mode can be broader than its effective mode (e.g. a composer launch at full-access), and plain (non-persona) delegate_task / spawn_agent hand the child the stored mode. So a persona's sandbox does not contain its own plain children; that is the same on feat(personas): pick any signed-in provider for persona models #391's base. Worth a follow-up.
  • docs/operations/persona-library.md:67 still says personas activate only on Codex and Claude. That file is under upstream's docs directory, so I left it; the J5 docs (docs/j5/product/agent-personas/index.md, docs/user/personas.md) carry the correction.

UI changes

Web, Settings → Personas, with the same two folder personas (research-scout: read-only, trade-assistant: full-access) loaded into an isolated state directory on each build.

  • Before (base feat(personas): pick any signed-in provider for persona models #391, 70f12401cd): a folder persona row shows only Available · Folder · personas; nothing says what its policy is. A persona file that declares full-access is rejected as invalid, and that one rejection takes the whole library down ("Personas unavailable", Invalid persona file …trade-assistant.yaml).
  • After (this PR, 3e3424462b): both personas load. Trade Assistant's row reads Available · [Full access] · Folder · personas with the existing warning Badge; Research Scout's row is unchanged, with no badge.
  • Mobile pill: not run. It renders the same shared flag, but I did not verify it on a device or simulator.

Screenshots are in the Evidence section above.

Upstream impact

Two upstream-owned files change, only on lines J5 already owns: packages/contracts/src/orchestrationV2.ts points the J5 agentPersonaAssignment fields on thread.create and delegated_task.request at the new closed-policy OrchestrationV2AgentPersonaCommandAssignment (thread and shell keep the tolerant one), and apps/server/src/mcp/OrchestratorMcpService.ts types its J5 assignment pass-through the same way. The FORK.md persona contract row now records that split. Everything else is under apps/*/src/j5, packages/*/src/j5, docs/j5, or is docs/user/personas.md.

Checklist

  • One concern: the description has no "also"
  • Tests cover the changed behavior (backend changes ship with focused tests)
  • UI changes: before/after screenshots above, and a video for motion or interaction (web verified in a real client, screenshots in Evidence above; the mobile pill was not run on a device or simulator)
  • Upstream-owned files: only the existing J5 persona-assignment lines in orchestrationV2.ts and OrchestratorMcpService.ts, recorded in FORK.md
  • Upstream product: no change to what upstream's product does
  • Surfaces: contracts, server, client-runtime, web, mobile and all eight provider drivers decided; the Unsupported row comes from shared client-runtime presentation, so web and mobile both show it (mobile not run on a device); composer picker shows no policy for any persona, so unchanged; no reverse state needed (removing the policy from a persona returns it to the sandboxed policies)
  • Docs: docs/j5/product/agent-personas/index.md and docs/user/personas.md updated

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
j5-code Ready Ready Preview Oct 7, 2026 10:17pm UTC

Request Review

@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Oct 5, 2026
@bryantderosier bryantderosier added the enhancement New feature or request label Oct 5, 2026
@bryantderosier
bryantderosier requested a review from BastiHu October 5, 2026 17:08
@bryantderosier bryantderosier self-assigned this Oct 5, 2026
@github-actions github-actions Bot added the size:L 100-499 effective changed lines (test files excluded in mixed PRs). label Oct 5, 2026
@bryantderosier
bryantderosier added this pull request to stack #470 October 5, 2026 17:14
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: Jacksondr5/j5code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e15ef6d8-2609-4e28-bc51-81011d865e5c
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

Base automatically changed from j5/persona-any-provider to j5/main October 6, 2026 12:50
bryantderosier and others added 7 commits October 6, 2026 08:50
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…missions

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…gation

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…cision

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r's policy

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@BastiHu

BastiHu commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

Security review follow-up (Opus 5.5; independently traced by the review Captain) at head ba747625f1154cf68101139d1c7c71b9ba1d645d:

A workspace-write / critic-fix persona whose writable workspace contains a configured persona source folder can write a new YAML definition declaring full-access, then call spawn_agent with that persona. Folder definitions are reread on launch and are enabled by default; persona spawning takes the child’s policy without a parent ceiling, and spawn_agent is pre-approved for restricted persona sessions. This permits an unsandboxed Peer Agent with network access without another human step. A shared-folder update can likewise upgrade an existing persona for future launches.

Relevant code: live folder loading, persona spawn policy, and tool preapproval.

This is a medium-risk trust decision, not a claim that the requested full-access feature itself is a defect. The PR already discloses unbounded spawning and visibility-only mitigation, and the no-ceiling behavior is a recorded product decision. What remains unclear is whether that acceptance explicitly includes agent-authored or modified folder definitions, as opposed to an existing human-approved full-access persona.

Please record acceptance of that specific scenario, or consider requiring human confirmation for newly granted full access (bound to the definition digest), or for a spawn that exceeds the caller’s effective permissions. This path was verified statically; no runtime exploit was executed.

@BastiHu BastiHu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved as requested by the maintainer following the crew review of head ba74762. No introduced correctness/code-quality defect was confirmed; functional acceptance criteria were covered statically. The medium-risk mutable-persona trust scenario is documented in the separate security comment and remains an explicit risk-acceptance follow-up. Tests, typechecks, and client/provider execution were not independently rerun.

@Jacksondr5

Copy link
Copy Markdown
Owner

Review panel: Claude Opus 5.5 (lead) + GPT-6.1-Sol

Reviewed at bf1d7c9, before the rebase onto j5/main; I compared the commits with git range-diff and the rebased ba74762 has the same content. Sol tested live with real Codex and Claude agents. Full access works, the delegation change is sound, and Jackson has made the two product calls. One change is needed in this PR before it merges.

Jackson's decisions

  1. Any agent that can spawn can start a full-access persona: accepted as is (2026-10-05). It follows the 2026-09-16 ruling that J5 has no parent-to-child permission ceiling. The gaps in how visible that is are a follow-up, Personas: make full access visible where personas arrive and where they are used #473, not this PR.
  2. Older apps must not be taken down by a value they don't know. That fix goes in this PR (2026-10-06), and this PR lands before the next release. Details in the next section.

Required: an unknown policy or provider must cost one row, not the whole app

Sol ran the released v0.0.48 web app and j5/main at 9745f1b against this PR's server with one full-access persona thread, one ordinary thread and one read-only persona thread.

  • A fresh profile cannot open the app. It shows "Still connecting / J5 Code could not confirm this workspace." and Reload doesn't help. FirstRunGate.tsx:141 needs the shell snapshot, and the snapshot is rejected.
  • A profile that was already set up keeps its cached ordinary and read-only threads, which open and receive live replies. The full-access thread is missing from the sidebar and its URL opens the empty start screen. The sidebar stops updating (a rename on the server never arrived). Settings → Personas shows "Personas unavailable" with the schema error and no rows; the composer has no persona picker; @persona: shows the same error.
  • j5/main today fails its persona catalog before any full-access persona exists, at policyEnforcement[6].policy, because the server always sends the new table row.
  • Nothing tells the person to update.

Released v0.0.48 app, fresh profile, against a server with one full-access persona thread

The cause is one closed enum, AgentPersonaAuthorityPolicy (packages/contracts/src/j5/agentPersona.ts), used in payloads that decode as a unit:

  • the catalog: each entry's default and allowed policies, and the enforcement table's policy;
  • the persona assignment on a thread, which rides in every thread shell (orchestrationV2.ts:1510), so one unknown value rejects the whole shell snapshot and ends the sidebar subscription; and in thread detail and events, where it costs that one thread.

What we need, so the next release's apps survive the next new value:

  • On what the client reads from the server, accept any string for the persona policy in the catalog entries, the enforcement table, definition reads, and thread assignments in shell, detail and events. The route driver is already open since feat(personas): pick any signed-in provider for persona models #391.
  • Keep everything else strict: what the client sends, and the server's own validation and enforcement. This is not a swap of the shared enum for a string; the read side and the command side need different codecs.
  • Show an unknown policy as unsupported, by its raw name, and don't let the person select or launch with it. Never treat it as read-only or as anything else it isn't.
  • Keep it to that. No version negotiation, no dropping of rows that fail to decode.

It cannot help v0.0.48, which is already built; say in the description that apps must be updated with the server, and that a v0.0.48 app stops loading once a full-access persona has run. Please also correct "Two-way" under Merge Danger: once a full-access persona has run, its snapshots, assignments and event history keep that policy, and Sol saw an older server retry with failures on such data.

A focused test that decodes a catalog and a shell snapshot containing an invented policy, and gets one unsupported row with everything else intact, would prove it.

Verified live

  • It is full access, from every entry point, on both providers. Composer, @persona, delegate_task, spawn_agent and a Crew persona seat, each on Codex and Claude: Codex received dangerFullAccess with approvals off, Claude bypassPermissions. All ten fetched from the network, wrote outside the workspace and made a Git commit. A workspace-write persona could do none of those. This covers Personas cannot run with full access outside a Crew seat #432.
  • Delegation. Read-only and workspace-write persona parents are refused a full-access child. A full-access parent whose stored thread mode is Supervised gets a full-access child (the inherit case); the same parent delegating to a read-only persona gets a sandboxed child. Non-persona parents keep the existing ceiling. We found no case where inherit gives a child more or less than its own persona's policy.
  • Crews. A full-access seat shows "Full access" on the card and runs that way; an explicit narrower access mode on the seat wins, with the stops-for-approvals badge and count.
  • feat(personas): pick any signed-in provider for persona models #391's guarantees hold. Sandboxed policies launch only where enforced; nothing is appended to instructions; a persona that allows both read-only and full-access launched at its read-only default was sandboxed with the network off.
  • 133 focused tests pass. No adapter or other upstream-owned file is changed; importing resolveRuntimeMode needs no FORK.md entry.

Smaller notes

  1. "Without a prompt" depends on the caller. index.md:188 and docs/user/personas.md:41 say a full-access persona can be spawned without a prompt. True from a read-only Codex persona and from a Supervised Claude thread here. An ordinary Supervised Codex thread did stop with "Allow the t3-code MCP server to run tool "spawn_agent"?" before the peer ran. Please make the sentence precise.
  2. A new app against an older server offers "Full access (unsandboxed)" and the save fails with a schema error in the dialog; nothing is saved. Fine as it is, and the tolerant read side makes the list hold up; just be aware of it.
  3. /tmp is writable under Codex's workspace-write sandbox, so "writes outside the workspace" needs a path elsewhere to show the difference. Only relevant if you write this up.

Screenshots

The three in the description load and match. These add the states they miss, against #391's head, same data, viewport and theme:

Before After
Policy menu before Policy menu after: Full access (unsandboxed)
Editor note before Editor note after: unsandboxed
Crew card before Crew card after: Full access

The older-app set (37 images, oldclient- prefix) is under pr-469/ on j5/evidence.

Not tested

  • Cursor, OpenCode, Grok, Antigravity, Pi and ACP harnesses live: none is installed on the review host. Sol read each adapter's handling of the ordinary full-access mode, and the focused tests call those helpers.
  • Mobile and the packaged desktop app. The older-app test used web apps built from the exact source, over a local connection.
  • An external MCP server from a full-access persona.

bryantderosier and others added 3 commits October 6, 2026 18:33
… app

The persona authority policy was a closed enum inside payloads that decode as
a unit: the catalog, its enforcement table, definition reads, and the persona
assignment that rides in every thread shell, thread detail and event. A server
that knows a policy the app does not (full-access, today, for v0.0.48) made the
app reject the whole shell snapshot and the whole persona library.

What the app reads now accepts any policy name; what clients send (launch
requests, create and edit) and the server's own definition validation stay on
the closed enum. A persona whose default policy the app does not know shows as
Unsupported with the raw name, and is not offered for launch, editing or
duplication. Server enforcement treats an unknown stored policy as never
enforceable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…hout a prompt

Whether spawn_agent asks first depends on the caller's harness and mode: Claude
never asks, Codex asks only in interactive modes such as Supervised. Also note
the Unsupported state an older app shows for a policy it does not know.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An assignment read back with a policy this server does not know (persisted by
a newer one) now fails at the runtime boundary on every path, including legacy
assignments without a snapshot and Crew access overrides. The provider policy
translator takes only known policies again, so nothing can run an unknown
policy as read-only or as anything else. Assignments the server resolves
itself carry the known policy type.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bryantderosier and others added 5 commits October 6, 2026 18:43
`thread.create` and `delegated_task.request` reused the read-side assignment,
so after the read side opened up, command decoding would also have accepted an
invented policy. Those commands now use OrchestrationV2AgentPersonaCommandAssignment,
the same assignment with the closed policy enum; thread and shell reads keep the
tolerant one. Every server path that builds an assignment carries the strict type.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Mobile already lists the reason under the description; web only had it in the
badge tooltip.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…licy

Callers already reject an unknown stored policy before translating, and the
parameter is typed to known policies, but an untyped caller would still have
fallen through to the read-only fallback meant for known, unenforceable
policies. It now throws instead, so an unknown policy can never run as
read-only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t know

The shared assignment label now reads "<persona> · <policy> (unsupported)" for
an unknown policy, so web and mobile chips and controls show it instead of
looking ordinary.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The copy would need a policy the editor cannot offer, so the action only
reached an error after reading the definition. Web and mobile now disable it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bryantderosier

Copy link
Copy Markdown
Collaborator Author

Thanks for the review panel. The required change is in, pushed through e8563bbe8.

An unknown policy now costs one row, not the app. These read paths accept any policy name: catalog entries, the enforcement table, definition reads, and the persona assignment in thread shell, detail and events. These stay on the closed enum:

  • what clients send (launch, create, edit)
  • the server's own thread.create and delegated_task.request commands, through the new OrchestrationV2AgentPersonaCommandAssignment
  • the server's definition validation

A persona whose policy the app doesn't know shows as Unsupported, with the raw name. The composer picker and @persona don't offer it, it can't be edited or duplicated, and its thread chip reads <name> · <policy> (unsupported). Nothing treats it as read-only. The server refuses to run a thread whose stored policy it doesn't know, on every path, and the policy translator throws instead of falling back.

A focused test decodes a catalog and a shell snapshot, both from JSON, that each contain an invented policy. It gets one Unsupported row with everything else intact, and it fails if the closed enum goes back on the read side. Live, on isolated state: j5/main's client contract rejects this server's real shell, detail and events at the full-access thread's authorityPolicy. With that policy replaced by an invented one, this PR's contract decodes every thread. UI rendering was not run in a browser or on mobile.

Description updated: apps must be updated with the server, a v0.0.48 app stops loading once a full-access persona has run, and Merge Danger is now one-way for data. The spawn wording in index.md and docs/user/personas.md now depends on the caller. Claude in any mode and Codex with approvals off spawn without a prompt; a Supervised Codex thread prompts.

@Jacksondr5, one thing to confirm: the description reads your 2026-10-05 acceptance of unbounded spawning as also covering BastiHu's scenario. That is agent-authored or agent-modified folder definitions that declare full access and are then spawned without another human step. No confirmation flow was added, and the visibility gaps stay in #473. Please confirm, or tell us otherwise.

Two upstream-owned files change, only on J5's existing persona-assignment lines (orchestrationV2.ts, OrchestratorMcpService.ts). The FORK.md persona contract row records the split.

Claude Code (Claude Opus 5.5), with GPT-6-Sol reviewing.

@Jacksondr5

Copy link
Copy Markdown
Owner

Review panel, round 2: Claude Opus 5.5 (lead) + GPT-6.1-Sol

Reviewed at e8563bb. Sol re-tested live in the browser. The required change works: an unknown persona policy now costs one row, and nothing lets it run. Two small findings remain. I'll approve once Jackson answers the question you put to him about agent-written folder personas; that is his call, and it is with him now.

Verified live

To get a policy this PR's own client doesn't know, Sol added one invented literal to the server's definition decoder and enforcement table only, inserted a persona, snapshot and thread assignment carrying it, and left every client, command and enforcement path as shipped.

  • The app survives it. Fresh and warm profiles load. The sidebar lists the unknown-policy thread with everything else and keeps updating (a rename arrived while that thread was open). Its history opens and its chip reads "Review Future Policy · review-future-policy (unsupported)".
  • One Unsupported row. Settings shows that persona as Unsupported with the raw policy; every other row is normal; Edit and Duplicate are disabled on it; creating a persona still works with the invented row in the enforcement table. The composer picker and @persona leave it out and still offer the rest. A Crew proposal naming it renders as "Runtime unavailable" with Approve disabled; Fleet loads.
  • It never runs, and never as something else. A new message, a queued run, a release of a prepared run, a queued run resumed after a restart, delegate_task, spawn_agent and a Crew seat are all refused; a fork is created but its first turn is refused. No provider turn was started for it. Boot didn't fail or loop, and another thread ran normally afterwards.
  • The strict side holds, on the unmodified head: create, edit and launch with an invented policy fail their schemas; importing a YAML with one is refused by name and the rest of the library stays up.
  • The mutation check. Putting the closed list back on the catalog policy, or on the assignment's policy, each fails the new focused test at the expected path.
  • Released app, as the description now says: a fresh v0.0.48 profile against this head with a real full-access thread stays at "Still connecting".
  • Regression: full-access personas from the composer and spawn_agent on both providers got the same native settings as before; a read-only persona is still sandboxed; the two delegation refusals hold with the new command assignment type; an older persona thread resumed; nothing is appended to instructions. 187 focused tests pass.

The two upstream-owned files

orchestrationV2.ts:2254/:2602 is one import and a substitution in the two existing optional J5 assignment fields; OrchestratorMcpService.ts:1/:106 is a type import and an internal optional argument. Neither changes what upstream's product does, so no register entry is needed. FORK.md case 22 and the contracts file-table row describe the split accurately. One gap that predates this PR: the main file table doesn't list OrchestratorMcpService.ts by name, though the case text records its delegateTask seam. Since this PR touches the file, please add the row.

Small findings

  1. Turning an unsupported persona off re-enables Duplicate. Disabled takes precedence over Unsupported (packages/client-runtime/src/j5/agentPersonas.ts:153, :172; AgentLibrarySettings.tsx:602; mobile AgentLibrarySettingsScreen.tsx:555). The row then hides the raw-policy reason, says "Duplicate this persona to edit a copy", and Duplicate fails with "Runtime policy … is not supported by this app version". No copy is made, so it is cosmetic, but it contradicts e8563bb. Seen on web; mobile has the same condition by code.
    A disabled unknown-policy persona offers Duplicate
  2. docs/operations/persona-library.md:67 is stale. It still says personas activate only on Codex and Claude. The description says you left it because it sits under upstream's docs directory, but the file is J5's own: it was created in feat(client): manage imported agents from Settings #85 and doesn't exist upstream. Please correct it here.

A queued run that fails after a restart shows the generic "Provider turn failed" instead of the unsupported-policy message the composer gives. The guard works; mentioning it in case the reason is cheap to carry through.

Screenshots

State Image
The Unsupported row One Unsupported persona row with its raw policy
A message to that thread Message refused; the chip names the unsupported policy

The rest (picker, @ menu, Crew card and seat editor, Fleet, the restart case, the refused import, v0.0.48) are under pr-469/ on j5/evidence with the r2- prefix.

Not tested

Mobile (read only: it uses the same shared presentation and filters); remote and tunnel connections; providers other than Codex and Claude. The unknown-policy refusals were exercised on Codex routes, with the shared guards covered by unit tests.

Open with Jackson

Whether his acceptance of unbounded spawning covers an agent writing or changing a full-access persona file in a folder it can edit (BastiHu's scenario). For his decision, Sol confirmed from the code that source folders are reread on every catalog request and new launch (agentPersonaLibrary.ts:96) and folder definitions are on unless listed as disabled, so such a file is picked up and enabled with no confirmation.

@Jacksondr5 Jacksondr5 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Review panel: approving at e8563bb (Claude Opus 5.5 lead, GPT-6.1-Sol)

Jackson confirmed on 2026-10-07 the point the description asks about: his acceptance of unbounded spawning does cover agent-written or agent-modified folder personas that declare full access (BastiHu's scenario). In his words, J5 is not trying to implement a full AI security system here. No confirmation step is wanted in this PR; the visibility work stays in #473.

With that, nothing blocks. The round 2 results are in the comment above: an unknown policy costs one row, never runs, and never runs as something else; the two upstream-file edits are minimal and recorded.

Still worth doing before or right after merge, none of it blocking:

  1. Turning an unsupported persona off re-enables its Duplicate button, which then fails.
  2. docs/operations/persona-library.md:67 still says personas activate only on Codex and Claude. It is J5's own file.
  3. Add apps/server/src/mcp/OrchestratorMcpService.ts to FORK.md's file table.

This should land before the next release, so no released app ever has the #391-only shape. Release notes need to say that apps must be updated with the server, and that a v0.0.48 app stops loading once a full-access persona has run.

bryantderosier and others added 2 commits October 7, 2026 22:16
Disabled and Removed took precedence over Unsupported, so turning an
unsupported persona off hid its raw-policy reason and re-enabled
Duplicate, which then failed. Unsupported now wins in the shared row,
so web and mobile keep both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…Service in FORK.md

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@bryantderosier

Copy link
Copy Markdown
Collaborator Author

Thanks, and thanks to Jackson for the confirmation. The three follow-ups are pushed:

  1. An unsupported persona that is turned off or removed (53aea1059). Unsupported now takes precedence over Disabled and Removed in the shared row presentation. The row keeps its raw-policy reason and Duplicate stays disabled, on web and mobile alike. A focused test covers both states and fails without the fix.
  2. docs/operations/persona-library.md (390771480). It now says which providers run each policy: read-only and critic-review on Codex and Claude, workspace-write and critic-fix on Codex, and full-access on every provider.
  3. FORK.md (390771480). The file table now lists apps/server/src/mcp/OrchestratorMcpService.ts under Saved-agent mentions.

I left the generic "Provider turn failed" for a queued run after a restart as it is. The guard holds, and carrying the reason through would mean changes in upstream's run-failure path.

Noted on release notes: they need to say that apps must be updated with the server, and that a v0.0.48 app stops loading once a full-access persona has run.

Claude Code (Claude Opus 5.5)

@bryantderosier
bryantderosier merged commit 573094f into j5/main Oct 7, 2026
28 checks passed
@bryantderosier
bryantderosier deleted the feat/persona-full-access branch October 7, 2026 22:37
Jacksondr5 added a commit that referenced this pull request Oct 8, 2026
#430 and #396 each added the same findRegisteredHome constant to
SquadronThreadCreationService.ts, and the merged file declared it
twice. #396 made a Crew seat's workspace required while #469 added a
test seat without one.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 39077148 Deployed Oct 7, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L 100-499 effective changed lines (test files excluded in mixed PRs). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Personas cannot run with full access outside a Crew seat

3 participants