Let a run's own people read it, with a read scope the site, tenant or workflow sets
A run had exactly one front-end page, the requester's, gated to the initiator. An operator who works one of its steps had no read surface for it at all: the task lists showed them the task, but the run behind it was reachable only through the admin trace, which is access orchestra instances gated.
One address per run
/orchestra/instance/{orchestra_instance} replaces /orchestra/my-instances/{id}, so a link to a run works for whoever is entitled to open it: an operator can send it to the requester and the other way round. The path says instance, not request: "request" is the requester's framing and the page serves both. The gate is InstanceReadAccessCheck (renamed from InstanceInitiatorAccessCheck): the initiator always, plus operators as far as the run's read scope allows. No permission gates the route, only being logged in, because being one of the run's own people is the stronger requirement. Still tenant-scoped, still no administrator bypass.
Read scope, configurable per workflow and per tenant
Who else may read a run depends on what the run is about, so it is a setting with three values: Only the person who filed it, Its workers (whoever holds or completed a step, plus a reassign orchestra tasks holder for a step somebody else holds) and Its whole audience (everyone a step is offered to, the inbox's own visibility rule).
Workers is the shipped default. Audience would mean a step pooled to a role opens every run of that workflow to everyone in the role, which is too wide to default to; initiator is there for a workflow whose operators have no business reading the file behind their task.
Site-wide under Runs are read by on the settings page, overridable per tenant (its Reading operation on the Tenants list) and per workflow (its Reading tab), resolved workflow, then tenant, then site: the same precedence, shape and plumbing as retention (ReadScopeConfigInterface beside RetentionConfigInterface, InstanceReadScope beside RetentionManager). Whatever the scope, the answer outlives the work: a completion releases the hold but records the completer, so an operator who processed a request can still look it up.
No hand-typed URLs in shipped views
Every Orchestra path typed into a view is gone, replaced by fields that build links from routes:
- Reference gains a Link to the run checkbox. It links only for a viewer the page admits (plain text otherwise, never a link to a 403) and carries the list it was clicked on as the return target, so the run's page leads back to that tab with its filters and pager. Used by My tasks (both tabs) and My workflow instances; the excluded id field those links needed is gone.
- Trace link is a new field replacing the custom-text column the two dashboards used, which typed the trace path in each view and linked every row whether or not the viewer could open the trace.
Both share InstanceLinkFieldBase. A typed path cannot check access and rots the day a route moves, and every site that installed the view keeps the stale path until the view is re-imported, which is exactly what a route name avoids.
The controller split
With the detail no longer the requester's own page, MyInstancesController was the wrong home for it: it keeps the requester's list alone, the detail moves to InstanceReadController, and the two shared column builders move to CardColumnsTrait (the pattern PendingActionLinksTrait already uses). The page follows the reader in two ways only: an operator is told whose run it is (Beneficiary) and goes back to the list they came from, the requester goes back to My requests. The orchestra_return argument is vetted as a rooted internal path, so a crafted link cannot point the back link off-site. The source view's "View full submission" link renders only when the reader may open it. The my_instances CSS library, which after #3613841 held only detail and timeline rules, is renamed instance_detail.
Tests
WorkItemParticipationTest covers the rule under all three scopes and the workflow/tenant/site precedence, plus holder, completer, reassigner, tenant and per-run scoping. InstanceReadControllerTest covers the page for both readers and the whole access matrix including the external-return refusal. OperatorInstanceReadTest covers it over HTTP for the operator, the requester at the same URL and the outsiders. RequesterDetailTest is retargeted and kept as the proof the initiator branch did not widen. MyTasksViewTest asserts the Reference links only where the run is readable, and renders plain text otherwise.
Note for sites already running these views
The shipped views are config, so a site that installed them keeps its stored copy: re-import views.view.my_tasks, views.view.my_workflow_instances, views.view.orchestra_processes and views.view.orchestra_completed to pick up the route-based links. Pre-release, so no update hook.
The page is a template
The run's page was a render array assembled in PHP, so a theme could only recolor it. It is now a theme hook and orchestra-instance.html.twig, overridden by placing a file of that name in a theme, and the controller hands it the run's parts as data (back, title, meta, status, milestones, source) rather than markup to unpick. Its classes are still the shared orchestra/cards component, so restyling alone needs only CSS. A kernel test pins the hook, the shipped file, and that the variables the controller passes and the ones the template reads agree.
The back link leads where the reader came from
It used to be hardcoded per audience: the initiator always got the card page, so a requester arriving from the My requests View was bounced to a page they may never use, and an operator with no threaded target got a list of actions unrelated to where they were. Now every surface that links a run threads its own URL as an orchestra_return target (the card page included, which threaded none), the page leads back to that exact page, tab, filters and pager, and with no target there is simply no link rather than a guess. The argument is still vetted as a rooted internal path, so a crafted link cannot point it off-site.