Repository navigation
feat(personas): personas can run with full access - #469
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configuration
Comment |
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>
bf1d7c9 to
ba74762
Compare
|
Security review follow-up (Opus 5.5; independently traced by the review Captain) at head A workspace-write / critic-fix persona whose writable workspace contains a configured persona source folder can write a new YAML definition declaring 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
left a comment
There was a problem hiding this comment.
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.
Review panel: Claude Opus 5.5 (lead) + GPT-6.1-SolReviewed at bf1d7c9, before the rebase onto Jackson's decisions
Required: an unknown policy or provider must cost one row, not the whole appSol ran the released v0.0.48 web app and
The cause is one closed enum,
What we need, so the next release's apps survive the next new value:
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
Smaller notes
ScreenshotsThe three in the description load and match. These add the states they miss, against #391's head, same data, viewport and theme:
The older-app set (37 images, Not tested
|
… 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>
`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>
|
Thanks for the review panel. The required change is in, pushed through 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:
A persona whose policy the app doesn't know shows as Unsupported, with the raw name. The composer picker and 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: 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 @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 ( Claude Code (Claude Opus 5.5), with GPT-6-Sol reviewing. |
Review panel, round 2: Claude Opus 5.5 (lead) + GPT-6.1-SolReviewed 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 liveTo 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 two upstream-owned files
Small findings
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
The rest (picker, Not testedMobile (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 JacksonWhether 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 ( |
Jacksondr5
left a comment
There was a problem hiding this comment.
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:
- Turning an unsupported persona off re-enables its Duplicate button, which then fails.
docs/operations/persona-library.md:67still says personas activate only on Codex and Claude. It is J5's own file.- Add
apps/server/src/mcp/OrchestratorMcpService.tsto 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.
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>
|
Thanks, and thanks to Jackson for the confirmation. The three follow-ups are pushed:
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) |










Summary
A persona can now run with full access outside a Crew seat. It declares a
full-accessauthority 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_agentand 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.
Closes #432. Stacked on #391, which rewrote the same policy table; this PR targets that branch.
Evidence
workspace-writesandbox (orread-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 nosandboxPolicyorapprovalPolicy: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.approvalPolicy: never+dangerFullAccess, ClaudebypassPermissions, Cursor sandbox off, OpenCode allow-all, Grok--always-approve, Antigravityyolo, PiT3_PI_RUNTIME_MODE=full-access, ACP allow). Unknown drivers stay blocked.@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 readsScout · 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'sthread.create/delegated_task.requestcommands 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.j5/main's client contract (9745f1b) rejects the real/api/orchestration/shellresponse atthreads[5].agentPersonaAssignment.authorityPolicyand the catalog atpolicyEnforcement[0].policy. With that thread's policy replaced by an inventedsandboxed-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.tsc --noEmitis 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.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 declaresfull-accessfails 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/mainbefore 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
full-access. The only mitigation is visibility: the library row shows a "Full access" badge/pill and the editor says "Unsandboxed".spawn_agentwith 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-approvesspawn_agenton 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_taskis bounded (above), and Crew seats still need roster approval.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.delegate_task/spawn_agenthand 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:67still 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.70f12401cd): a folder persona row shows onlyAvailable · Folder · personas; nothing says what its policy is. A persona file that declaresfull-accessis rejected as invalid, and that one rejection takes the whole library down ("Personas unavailable",Invalid persona file …trade-assistant.yaml).3e3424462b): both personas load. Trade Assistant's row readsAvailable · [Full access] · Folder · personaswith the existing warning Badge; Research Scout's row is unchanged, with no badge.Screenshots are in the Evidence section above.
Upstream impact
Two upstream-owned files change, only on lines J5 already owns:
packages/contracts/src/orchestrationV2.tspoints the J5agentPersonaAssignmentfields onthread.createanddelegated_task.requestat the new closed-policyOrchestrationV2AgentPersonaCommandAssignment(thread and shell keep the tolerant one), andapps/server/src/mcp/OrchestratorMcpService.tstypes its J5 assignment pass-through the same way. TheFORK.mdpersona contract row now records that split. Everything else is underapps/*/src/j5,packages/*/src/j5,docs/j5, or isdocs/user/personas.md.Checklist
orchestrationV2.tsandOrchestratorMcpService.ts, recorded inFORK.mddocs/j5/product/agent-personas/index.mdanddocs/user/personas.mdupdated🤖 Generated with Claude Code