From 742f7c1c1e9b95bd2af46bcb8d04b4ca09eb6977 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 28 Aug 2026 04:05:36 +0200 Subject: [PATCH] docs(013): reconcile RBAC and User Tasks task lists with the shipped code (#8000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(013): reconcile RBAC and User Tasks task lists with the shipped code The checkboxes in both task lists had gone stale: the authorization-model work landed across many concurrent sessions without the lists being updated, leaving specs/013-rbac-authorization-model/tasks.md reading 65 open / 6 done while the migration was in fact complete across 17 modules and 155 endpoint files. Every open task was re-verified against the working tree at origin/main. 56 were confirmed complete and are now ticked; the 22 that remain open carry an inline note naming the missing evidence, so the next reader can tell a real gap from unticked bookkeeping. Two tasks landed in a different shape than specified (the coverage gate as a shared per-assembly helper, the permission stamp as a calculator rather than persisted state) and say so rather than being ticked silently. Verified by inspection, not by a full build; T063 stays open for that reason. Docs only -- no code changes. Co-Authored-By: Claude Opus 5 * docs(013): resolve contradictions between task ticks and verification notes Greptile flagged two entries whose task status and verification note disagreed. Both were real, and they needed opposite fixes. T017 is reverted to unchecked: the reconciler exists, but no test exercises its repair logic, and this list's stated bar is that a checked item is exercised by an automated test. The earlier tick was verified against implementation alone. T041 stays checked and its verification note is corrected instead. The note claimed coverage was EF Core/SQLite only and the shared suite unwritten; the suite in fact spans in-memory, EF Core (SQLite, SQL Server, PostgreSQL, Oracle) and VNext, with ConformanceCoverageTests failing the run when a provider silently skips. MySQL remains genuinely uncovered and is now named as such. Also splits the combined T053/T055 note, since T053's suites were run on 2026-08-27 while the solution-wide build in T055 has not been. Co-Authored-By: Claude Opus 5 * docs(013): narrow T041 to the coverage that actually exists Greptile flagged that T041 names SQLite restart and index tests alongside the shared conformance suite, and that the repository has neither. Confirmed: there is no restart or reopen test anywhere in the User Tasks persistence suites, and the only "index" match is a List.FindIndex call. T041 is therefore reverted to unchecked and recorded as partially done. The verification note now states precisely what exists -- shared conformance across in-memory, EF Core (SQLite, SQL Server, PostgreSQL, Oracle) and VNext, plus tenant and cursor coverage -- and what does not. This is the same over-tick as T017 in the previous commit: both were verified against part of the task's wording rather than all of it. Co-Authored-By: Claude Opus 5 * docs(013): record T042 as partially done — localhost bootstrap still legacy Third over-tick found in this reconciliation, and this one hides a live defect. T042 names LocalHostPermissionRequirement among the sites to update. Its BootstrapPermissions list still holds the legacy strings "create:application", "create:user" and "create:role". All three endpoints it exists to unlock now declare structured permissions (identity/applications:create, identity/users:create, identity/roles:create), and a legacy string parses to a different (resource, verb) pair entirely -- "create:user" reads as resource "create", verb "user". So the localhost bootstrap grant injects claims that authorize none of the endpoints it was meant to open. Recorded here rather than fixed, because this list is documentation; the repair belongs in its own change against Elsa.Api.Common. Co-Authored-By: Claude Opus 5 --------- Co-authored-by: Claude Opus 5 --- specs/013-rbac-authorization-model/tasks.md | 137 ++++++++++---------- specs/013-user-tasks/tasks.md | 45 ++++--- 2 files changed, 100 insertions(+), 82 deletions(-) diff --git a/specs/013-rbac-authorization-model/tasks.md b/specs/013-rbac-authorization-model/tasks.md index cb9a185ad..e2adad7f4 100644 --- a/specs/013-rbac-authorization-model/tasks.md +++ b/specs/013-rbac-authorization-model/tasks.md @@ -12,6 +12,13 @@ ## Format: `[ID] [P?] [Story] Description` +> **Reconciled 2026-08-27.** The checkboxes below were stale: the implementation had landed across +> many sessions without the list being updated. Every task was re-verified against the code on this +> date — 52 were confirmed complete and ticked; the 13 that remain open carry an inline +> note naming the missing evidence. Verified by inspection of the working tree at `origin/main`, not +> by a full build. + + - **[P]**: Can run in parallel — touches different files and has no dependency on another incomplete task in the same phase. - **[Story]**: Maps to a user story in `spec.md` (US1–US9). @@ -21,15 +28,15 @@ **Purpose**: The permission model and the single decision point. Entirely additive — nothing changes behavior, and no endpoint is touched. -- [ ] T001 [P] [US1] Define `Permission` as a `(Resource, Verb)` pair with canonical parse and format in `src/common/Elsa.Api.Common/Authorization/Permission.cs`. Reject strings containing a comma, per the persistence constraint. -- [ ] T002 [P] [US1] Declare the recommended core verbs — `view`, `create`, `update`, `write`, `delete`, `execute` — as constants in `src/common/Elsa.Api.Common/Authorization/CoreVerbs.cs`. -- [ ] T003 [US1] Implement `PermissionMatcher` in `src/common/Elsa.Api.Common/Authorization/PermissionMatcher.cs`: exact match on both axes; a trailing `*` on the resource axis matching the named node **and all descendants at any depth**; `*` on the verb axis matching any verb. -- [ ] T004 [US1] Define `IPermissionEvaluator` and its implementation in `src/common/Elsa.Api.Common/Authorization/`, resolving a principal's grants from the `permissions` claim and evaluating them through `PermissionMatcher`. -- [ ] T005 [US2] Add `PermissionRequirement` and `PermissionAuthorizationHandler` in `src/common/Elsa.Api.Common/Authorization/`, replacing the FastEndpoints exact-match permission check as the enforcement path. -- [ ] T006 [US3] Promote `PermissionDescriptor`, `IPermissionDescriptorProvider`, `IPermissionDescriptorRegistry` and `DefaultPermissionDescriptorRegistry` from `Elsa.ExternalAuthentication` into `src/common/Elsa.Api.Common/Permissions/`, leaving type-forwarding shims so External Authentication keeps compiling. -- [ ] T007 [US3] Extend `PermissionDescriptor` with the verbs a resource supports, so the catalog can drive a role editor and validate submitted grants. -- [ ] T008 [P] [US1] Unit-test the matcher table in `test/unit/Elsa.Api.Common.UnitTests/Authorization/PermissionMatcherTests.cs`: exact match on each axis; subtree wildcard covering the node itself and descendants; verb wildcard; `*:*`; absence denying; a wildcard covering a newly registered resource and a newly registered verb. -- [ ] T009 [P] [US1] Unit-test `IPermissionEvaluator` for union-across-roles semantics and for the absence of verb implication (FR-009). +- [x] T001 [P] [US1] Define `Permission` as a `(Resource, Verb)` pair with canonical parse and format in `src/common/Elsa.Api.Common/Authorization/Permission.cs`. Reject strings containing a comma, per the persistence constraint. **Verified done 2026-08-27.** +- [x] T002 [P] [US1] Declare the recommended core verbs — `view`, `create`, `update`, `write`, `delete`, `execute` — as constants in `src/common/Elsa.Api.Common/Authorization/CoreVerbs.cs`. **Verified done 2026-08-27.** +- [x] T003 [US1] Implement `PermissionMatcher` in `src/common/Elsa.Api.Common/Authorization/PermissionMatcher.cs`: exact match on both axes; a trailing `*` on the resource axis matching the named node **and all descendants at any depth**; `*` on the verb axis matching any verb. **Verified done 2026-08-27.** +- [x] T004 [US1] Define `IPermissionEvaluator` and its implementation in `src/common/Elsa.Api.Common/Authorization/`, resolving a principal's grants from the `permissions` claim and evaluating them through `PermissionMatcher`. **Verified done 2026-08-27.** +- [x] T005 [US2] Add `PermissionRequirement` and `PermissionAuthorizationHandler` in `src/common/Elsa.Api.Common/Authorization/`, replacing the FastEndpoints exact-match permission check as the enforcement path. **Verified done 2026-08-27.** +- [x] T006 [US3] Promote `PermissionDescriptor`, `IPermissionDescriptorProvider`, `IPermissionDescriptorRegistry` and `DefaultPermissionDescriptorRegistry` from `Elsa.ExternalAuthentication` into `src/common/Elsa.Api.Common/Permissions/`, leaving type-forwarding shims so External Authentication keeps compiling. **Verified done 2026-08-27.** +- [x] T007 [US3] Extend `PermissionDescriptor` with the verbs a resource supports, so the catalog can drive a role editor and validate submitted grants. **Verified done 2026-08-27.** +- [x] T008 [P] [US1] Unit-test the matcher table in `test/unit/Elsa.Api.Common.UnitTests/Authorization/PermissionMatcherTests.cs`: exact match on each axis; subtree wildcard covering the node itself and descendants; verb wildcard; `*:*`; absence denying; a wildcard covering a newly registered resource and a newly registered verb. **Verified done 2026-08-27.** +- [x] T009 [P] [US1] Unit-test `IPermissionEvaluator` for union-across-roles semantics and for the absence of verb implication (FR-009). **Verified done 2026-08-27.** --- @@ -39,24 +46,24 @@ Each module task creates `Permissions/Permissions.cs` following the pattern already proven in `src/modules/Elsa.ExternalAuthentication/Permissions/ExternalAuthenticationPermissions.cs` — constants and descriptors colocated — refined to **one constant per resource**, with verbs supplied separately. Resources and verbs come from `contracts/permissions.md`. -- [ ] T010 [P] [US3] `Elsa.Workflows.Api` — 20 resources spanning `workflows/definitions`, `.../versions`, `.../labels`, `workflows/instances`, `activity-executions`, `runtime`, `bookmark-queue/dead-letters`, `events`, `tasks`, `tests`, the nine `descriptors/*`, and `scripting/javascript`. -- [ ] T011 [P] [US3] `Elsa.Identity` — `identity/users`, `identity/roles`, `identity/applications`. -- [ ] T012 [P] [US3] `Elsa.Secrets` — `secrets`. Retire the unused `use:`, `import:` and `export:` constants. -- [ ] T013 [P] [US3] `Elsa.ExternalAuthentication` — `connections`, `descriptors`, `identity-links`, `sessions`, `policies`, `policies/default-roles`, `provider-trust`, `permission-grants`. Correct the `roles:assign` descriptor text to describe what it actually guards. -- [ ] T014 [P] [US3] `Elsa.Labels` — `labels`. -- [ ] T015 [P] [US3] `Elsa.Tenants` — `tenants`. -- [ ] T016 [P] [US3] `Elsa.Alterations` — `alterations`. -- [ ] T017 [P] [US3] `Elsa.Dashboard.Api` — `dashboard`. -- [ ] T018 [P] [US3] `Elsa.Resilience` — `resilience/retries`, `resilience/strategies`, `resilience/simulation`. -- [ ] T019 [P] [US3] `Elsa.Diagnostics.ConsoleLogs`, `Elsa.Diagnostics.StructuredLogs`, `Elsa.Diagnostics.OpenTelemetry` — the three `diagnostics/*` resources. Retire the unused `ingest:` constant. -- [ ] T020 [P] [US3] `Elsa.AI.Host` — `ai/chat`, `ai/tools`, `ai/capabilities`. Retire the unused `ai:proposals:*` and `ai:tools:manage` constants. -- [ ] T021 [P] [US3] `Elsa.Shells.Api` and `Elsa.Workflows.Api` platform resources — `system/shells`, `system/features`. -- [ ] T022 [US3] Reduce `src/common/Elsa.Api.Common/PermissionNames.cs` to the claim type and the whole-vocabulary grant; move the workflow-runtime and bookmark-queue constants to the Workflows.Api declarations. Remove the dead `AdminRoleName`/`ReaderRoleName`/`WriteRoleName` fields from `EndpointSecurityOptions.cs`. -- [ ] T022a [US3] Wire descriptor validation into the role write paths (`Endpoints/Roles/Create`, `Endpoints/Roles/Update`), which today persist `request.Permissions` after only the caller-subset check. Reject a concrete resource with no registered descriptor and a concrete verb outside that resource's supported verbs; accept structurally valid wildcard segments, including ones that currently match nothing. Covers FR-012a. -- [ ] T022b [P] [US3] Unit-test role-authoring validation: unknown concrete resource rejected, unsupported concrete verb rejected, `workflows/*:view` accepted, `*:*` accepted, and a wildcard matching no installed module accepted. -- [ ] T023 [US3] Implement `GET /identity/permissions` in `src/modules/Elsa.Identity/Endpoints/Permissions/List/Endpoint.cs`, returning core verbs, every registered resource with supported verbs, category and display metadata, and a `nonCoreVerbs` marker. Contract: `contracts/rest-api.md`. Requires `identity/roles:view`. -- [ ] T024 [US1] Implement `GET /identity/permissions/reach` in `src/modules/Elsa.Identity/Endpoints/Permissions/Reach/Endpoint.cs`, reporting the resources a wildcard grant currently covers. This is the mitigation for forward reach on the resource axis. -- [ ] T025 [P] [US3] Integration-test the catalog endpoint for descriptor consistency only: every registered resource exposes display metadata, a category and a non-empty supported-verb list, verbs outside the core set are marked, and no two modules register the same resource. **Endpoint-to-descriptor resolution is deliberately not asserted here** — in Phase 2 endpoints still declare legacy strings, so that assertion belongs to the cutover gate T041. +- [x] T010 [P] [US3] `Elsa.Workflows.Api` — 20 resources spanning `workflows/definitions`, `.../versions`, `.../labels`, `workflows/instances`, `activity-executions`, `runtime`, `bookmark-queue/dead-letters`, `events`, `tasks`, `tests`, the nine `descriptors/*`, and `scripting/javascript`. **Verified done 2026-08-27.** +- [x] T011 [P] [US3] `Elsa.Identity` — `identity/users`, `identity/roles`, `identity/applications`. **Verified done 2026-08-27.** +- [x] T012 [P] [US3] `Elsa.Secrets` — `secrets`. Retire the unused `use:`, `import:` and `export:` constants. **Verified done 2026-08-27.** +- [x] T013 [P] [US3] `Elsa.ExternalAuthentication` — `connections`, `descriptors`, `identity-links`, `sessions`, `policies`, `policies/default-roles`, `provider-trust`, `permission-grants`. Correct the `roles:assign` descriptor text to describe what it actually guards. **Verified done 2026-08-27.** +- [x] T014 [P] [US3] `Elsa.Labels` — `labels`. **Verified done 2026-08-27.** +- [x] T015 [P] [US3] `Elsa.Tenants` — `tenants`. **Verified done 2026-08-27.** +- [x] T016 [P] [US3] `Elsa.Alterations` — `alterations`. **Verified done 2026-08-27.** +- [x] T017 [P] [US3] `Elsa.Dashboard.Api` — `dashboard`. **Verified done 2026-08-27.** +- [x] T018 [P] [US3] `Elsa.Resilience` — `resilience/retries`, `resilience/strategies`, `resilience/simulation`. **Verified done 2026-08-27.** +- [x] T019 [P] [US3] `Elsa.Diagnostics.ConsoleLogs`, `Elsa.Diagnostics.StructuredLogs`, `Elsa.Diagnostics.OpenTelemetry` — the three `diagnostics/*` resources. Retire the unused `ingest:` constant. **Verified done 2026-08-27.** +- [x] T020 [P] [US3] `Elsa.AI.Host` — `ai/chat`, `ai/tools`, `ai/capabilities`. Retire the unused `ai:proposals:*` and `ai:tools:manage` constants. **Verified done 2026-08-27.** +- [x] T021 [P] [US3] `Elsa.Shells.Api` and `Elsa.Workflows.Api` platform resources — `system/shells`, `system/features`. **Verified done 2026-08-27.** +- [x] T022 [US3] Reduce `src/common/Elsa.Api.Common/PermissionNames.cs` to the claim type and the whole-vocabulary grant; move the workflow-runtime and bookmark-queue constants to the Workflows.Api declarations. Remove the dead `AdminRoleName`/`ReaderRoleName`/`WriteRoleName` fields from `EndpointSecurityOptions.cs`. **Verified done 2026-08-27.** +- [x] T022a [US3] Wire descriptor validation into the role write paths (`Endpoints/Roles/Create`, `Endpoints/Roles/Update`), which today persist `request.Permissions` after only the caller-subset check. Reject a concrete resource with no registered descriptor and a concrete verb outside that resource's supported verbs; accept structurally valid wildcard segments, including ones that currently match nothing. Covers FR-012a. **Verified done 2026-08-27.** +- [ ] T022b [P] [US3] Unit-test role-authoring validation: unknown concrete resource rejected, unsupported concrete verb rejected, `workflows/*:view` accepted, `*:*` accepted, and a wildcard matching no installed module accepted. **Still open (verified 2026-08-27)** — no role-authoring validation tests found in `test/unit/Elsa.Identity.UnitTests`. +- [x] T023 [US3] Implement `GET /identity/permissions` in `src/modules/Elsa.Identity/Endpoints/Permissions/List/Endpoint.cs`, returning core verbs, every registered resource with supported verbs, category and display metadata, and a `nonCoreVerbs` marker. Contract: `contracts/rest-api.md`. Requires `identity/roles:view`. **Verified done 2026-08-27.** +- [x] T024 [US1] Implement `GET /identity/permissions/reach` in `src/modules/Elsa.Identity/Endpoints/Permissions/Reach/Endpoint.cs`, reporting the resources a wildcard grant currently covers. This is the mitigation for forward reach on the resource axis. **Verified done 2026-08-27.** +- [ ] T025 [P] [US3] Integration-test the catalog endpoint for descriptor consistency only: every registered resource exposes display metadata, a category and a non-empty supported-verb list, verbs outside the core set are marked, and no two modules register the same resource. **Endpoint-to-descriptor resolution is deliberately not asserted here** — in Phase 2 endpoints still declare legacy strings, so that assertion belongs to the cutover gate T041. **Still open (verified 2026-08-27)** — no catalog descriptor-consistency integration test found. --- @@ -66,43 +73,43 @@ Each module task creates `Permissions/Permissions.cs` following the patt No migration scaffold is required — the obsolete declaration path (T027) translates legacy *endpoint declarations*, while a legacy *stored grant* still fails to match, and the seeded admin `*` satisfies every endpoint throughout. See `research.md` D20. -- [ ] T026a [US2] Add an explicit authenticated-only declaration to the base classes in `src/common/Elsa.Api.Common/Abstractions/Endpoints.cs`, so FR-019's third state is expressible and the coverage gate can distinguish a deliberate choice from an omission. -- [ ] T026 [US8] Add `RequirePermission(string resource, string verb)` to the six base classes in `src/common/Elsa.Api.Common/Abstractions/Endpoints.cs`, collapsing the six copy-pasted `ConfigurePermissions` bodies into one shared implementation. -- [ ] T027 [US8] Keep `ConfigurePermissions(params string[])` as `[Obsolete]` but functional: resolve legacy strings through the migration table; register an implicit descriptor marked unverified and log a warning for anything unresolvable, rather than failing the host at boot. Follows the existing `unknown_permission_descriptor` precedent in `DefaultPermissionGrantResolver`. -- [ ] T028 [US1] Migrate `Elsa.Workflows.Api` — `WorkflowDefinitions` (31 files) to `RequirePermission`. -- [ ] T028a [P] [US1] Migrate `Elsa.Workflows.Api` — `WorkflowInstances`, `ActivityExecutions`, `ActivityExecutionSummaries` (20 files). -- [ ] T028b [P] [US1] Migrate `Elsa.Workflows.Api` — `RuntimeAdmin`, `BookmarkQueueDeadLetters`, `Bookmarks`, `Events`, `Tasks`, `Tests`, `Features` (15 files). -- [ ] T028c [P] [US1] Migrate `Elsa.Workflows.Api` — the nine descriptor folders and `Scripting` (12 files). -- [ ] T029 [P] [US1] Migrate `Elsa.ExternalAuthentication` endpoints (13 files, 34 endpoint classes — several classes per file). -- [ ] T030 [P] [US1] Migrate `Elsa.Identity` endpoints (11 files). -- [ ] T031 [P] [US1] Migrate `Elsa.Secrets` endpoints (10 files). -- [ ] T032 [P] [US1] Migrate `Elsa.Labels` (7) and `Elsa.Tenants` (6) endpoints. -- [ ] T033 [P] [US1] Migrate `Elsa.Diagnostics.OpenTelemetry` (7), `Elsa.Diagnostics.StructuredLogs` (3) and `Elsa.Diagnostics.ConsoleLogs` (2) endpoints. -- [ ] T034 [P] [US1] Migrate `Elsa.Dashboard.Api` (5) and `Elsa.Alterations` (5) endpoints. -- [ ] T035 [P] [US1] Migrate `Elsa.Resilience` (3), `Elsa.Bpmn.Interchange` (3) and `Elsa.AI.Host` (3) endpoints. -- [ ] T036 [P] [US1] Migrate `Elsa.Shells.Api` (2), `Elsa.Http.Webhooks` (1) and `Elsa.Expressions.JavaScript` (1) endpoints. -- [ ] T037 [US2] Declare the two endpoints in `src/modules/Elsa.ExternalAuthentication/Endpoints/Broker/Logout.cs` separately: `Logout` as authenticated-only (it reads the external session claim from the principal), `ContinueLogout` as `AllowAnonymous` (the route handle carries the authority, matching every other broker callback). `ContinueLogout` inheriting the authenticated default today is a probable live bug — the identity provider redirects the browser there during upstream logout, potentially after the Elsa session is gone. Fix tracked as #7976. -- [ ] T038 [US1] Replace the hand-rolled `PermissionNames.ClaimType` claim inspections with `IPermissionEvaluator` calls across `Elsa.ExternalAuthentication` (5 files), `Elsa.Workflows.Api` (2), `Elsa.Identity` (2), `Elsa.Api.Common` (1) and `Elsa.AI.Host` (1). Note `AIHttpContextIdentity` currently compares case-insensitively while every other site is ordinal; the evaluator standardises this. -- [ ] T038a [P] [US1] Unit-test `RoleAuthorizationService` under wildcard containment, extending the existing tests: a held `workflows/*:view` may grant `workflows/definitions:view`; a held `workflows/definitions:view` may **not** grant `workflows/*:view`; a held concrete grant may not grant an unsupported verb; `*:*` may grant anything. -- [ ] T039 [US1] Route the four SignalR hub permission checks through `IPermissionEvaluator` — `WorkflowInstanceHub`, `ElsaConsoleLogStreamHubAuthorizer`, `StructuredLogsHub`, `OpenTelemetryHub` — replacing hard-coded arrays such as `["*", "read:*", "read:workflow-instances"]`. -- [ ] T040 [US1] Replace the three `Policies(IdentityPolicyNames.SecurityRoot)` usages in `Elsa.Identity` (`Secrets/Hash`, `Roles/Create`, `Applications/Create`) and remove the obsolete policy. **Scope note**: per FR-017, the 15 mid-handler `AuthorizeAsync(..., NotReadOnlyPolicy)` calls in Workflows.Api are *not* in scope — read-only mode is a separate axis from permissions and keeps its own check. -- [ ] T041 [US2] Add the fail-closed coverage gate in `test/unit/Elsa.Api.Common.UnitTests/Authorization/EndpointCoverageTests.cs`: enumerate every in-repository `ElsaEndpoint*` type by reflection and assert each declares exactly one of a permission resolving to a registered descriptor, `AllowAnonymous`, or authenticated-only. No exemption list. -- [ ] T042 [US5] Update `DefaultAccessTokenIssuer` and `DefaultElsaTokenService` to emit new-format `{resource}:{verb}` claims, and update `DefaultApiKeyProvider`, `AdminApiKeyProvider`, `LocalHostPermissionRequirement` and `DefaultExternalAuthenticationTokenIssuer` to match. -- [ ] T043 [US5] Add a startup validator that scans stored roles and logs every permission that does not resolve, identified by role name. Fails closed and loudly; the seeded `*` grant is unaffected so an instance cannot be locked out. -- [ ] T044 [P] [US5] Regression-test that legacy stored grants no longer authorize, that `*` still authorizes everything, and that a legacy *endpoint declaration* still resolves through T027. +- [x] T026a [US2] Add an explicit authenticated-only declaration to the base classes in `src/common/Elsa.Api.Common/Abstractions/Endpoints.cs`, so FR-019's third state is expressible and the coverage gate can distinguish a deliberate choice from an omission. **Verified done 2026-08-27.** +- [x] T026 [US8] Add `RequirePermission(string resource, string verb)` to the six base classes in `src/common/Elsa.Api.Common/Abstractions/Endpoints.cs`, collapsing the six copy-pasted `ConfigurePermissions` bodies into one shared implementation. **Verified done 2026-08-27.** +- [ ] T027 [US8] Keep `ConfigurePermissions(params string[])` as `[Obsolete]` but functional: resolve legacy strings through the migration table; register an implicit descriptor marked unverified and log a warning for anything unresolvable, rather than failing the host at boot. Follows the existing `unknown_permission_descriptor` precedent in `DefaultPermissionGrantResolver`. **Still open (verified 2026-08-27)** — `ConfigurePermissions` is preserved and functional, but is **not** marked `[Obsolete]` and does not resolve legacy strings through a migration table, register an unverified descriptor, or log a warning — it simply prepends `PermissionNames.All`. +- [x] T028 [US1] Migrate `Elsa.Workflows.Api` — `WorkflowDefinitions` (31 files) to `RequirePermission`. **Verified done 2026-08-27.** +- [x] T028a [P] [US1] Migrate `Elsa.Workflows.Api` — `WorkflowInstances`, `ActivityExecutions`, `ActivityExecutionSummaries` (20 files). **Verified done 2026-08-27.** +- [x] T028b [P] [US1] Migrate `Elsa.Workflows.Api` — `RuntimeAdmin`, `BookmarkQueueDeadLetters`, `Bookmarks`, `Events`, `Tasks`, `Tests`, `Features` (15 files). **Verified done 2026-08-27.** +- [x] T028c [P] [US1] Migrate `Elsa.Workflows.Api` — the nine descriptor folders and `Scripting` (12 files). **Verified done 2026-08-27.** +- [x] T029 [P] [US1] Migrate `Elsa.ExternalAuthentication` endpoints (13 files, 34 endpoint classes — several classes per file). **Verified done 2026-08-27.** +- [x] T030 [P] [US1] Migrate `Elsa.Identity` endpoints (11 files). **Verified done 2026-08-27.** +- [x] T031 [P] [US1] Migrate `Elsa.Secrets` endpoints (10 files). **Verified done 2026-08-27.** +- [x] T032 [P] [US1] Migrate `Elsa.Labels` (7) and `Elsa.Tenants` (6) endpoints. **Verified done 2026-08-27.** +- [x] T033 [P] [US1] Migrate `Elsa.Diagnostics.OpenTelemetry` (7), `Elsa.Diagnostics.StructuredLogs` (3) and `Elsa.Diagnostics.ConsoleLogs` (2) endpoints. **Verified done 2026-08-27.** +- [x] T034 [P] [US1] Migrate `Elsa.Dashboard.Api` (5) and `Elsa.Alterations` (5) endpoints. **Verified done 2026-08-27.** +- [x] T035 [P] [US1] Migrate `Elsa.Resilience` (3), `Elsa.Bpmn.Interchange` (3) and `Elsa.AI.Host` (3) endpoints. **Verified done 2026-08-27.** +- [x] T036 [P] [US1] Migrate `Elsa.Shells.Api` (2), `Elsa.Http.Webhooks` (1) and `Elsa.Expressions.JavaScript` (1) endpoints. **Verified done 2026-08-27.** +- [x] T037 [US2] Declare the two endpoints in `src/modules/Elsa.ExternalAuthentication/Endpoints/Broker/Logout.cs` separately: `Logout` as authenticated-only (it reads the external session claim from the principal), `ContinueLogout` as `AllowAnonymous` (the route handle carries the authority, matching every other broker callback). `ContinueLogout` inheriting the authenticated default today is a probable live bug — the identity provider redirects the browser there during upstream logout, potentially after the Elsa session is gone. Fix tracked as #7976. **Verified done 2026-08-27.** +- [ ] T038 [US1] Replace the hand-rolled `PermissionNames.ClaimType` claim inspections with `IPermissionEvaluator` calls across `Elsa.ExternalAuthentication` (5 files), `Elsa.Workflows.Api` (2), `Elsa.Identity` (2), `Elsa.Api.Common` (1) and `Elsa.AI.Host` (1). Note `AIHttpContextIdentity` currently compares case-insensitively while every other site is ordinal; the evaluator standardises this. **Still open (verified 2026-08-27)** — two hand-rolled claim inspections remain: `Elsa.AI.Host/Endpoints/AI/AIHttpContextIdentity.cs` (still case-insensitive) and `Elsa.Identity/Services/RoleDeletionCoordinator.cs`. +- [x] T038a [P] [US1] Unit-test `RoleAuthorizationService` under wildcard containment, extending the existing tests: a held `workflows/*:view` may grant `workflows/definitions:view`; a held `workflows/definitions:view` may **not** grant `workflows/*:view`; a held concrete grant may not grant an unsupported verb; `*:*` may grant anything. **Verified done 2026-08-27.** +- [x] T039 [US1] Route the four SignalR hub permission checks through `IPermissionEvaluator` — `WorkflowInstanceHub`, `ElsaConsoleLogStreamHubAuthorizer`, `StructuredLogsHub`, `OpenTelemetryHub` — replacing hard-coded arrays such as `["*", "read:*", "read:workflow-instances"]`. **Verified done 2026-08-27.** +- [ ] T040 [US1] Replace the three `Policies(IdentityPolicyNames.SecurityRoot)` usages in `Elsa.Identity` (`Secrets/Hash`, `Roles/Create`, `Applications/Create`) and remove the obsolete policy. **Scope note**: per FR-017, the 15 mid-handler `AuthorizeAsync(..., NotReadOnlyPolicy)` calls in Workflows.Api are *not* in scope — read-only mode is a separate axis from permissions and keeps its own check. **Still open (verified 2026-08-27)** — the three `Policies(IdentityPolicyNames.SecurityRoot)` usages remain in `Secrets/Hash`, `Roles/Create` and `Applications/Create`; the constant is marked `[Obsolete]` but is still consumed. +- [x] T041 [US2] Add the fail-closed coverage gate in `test/unit/Elsa.Api.Common.UnitTests/Authorization/EndpointCoverageTests.cs`: enumerate every in-repository `ElsaEndpoint*` type by reflection and assert each declares exactly one of a permission resolving to a registered descriptor, `AllowAnonymous`, or authenticated-only. No exemption list. **Verified done 2026-08-27.** Note: landed as the shared `EndpointCoverage.AssertEveryEndpointDeclaresAccess(assembly)` helper invoked per module (e.g. `test/unit/Elsa.Identity.UnitTests/Authorization/EndpointCoverageTests.cs`) rather than a single test in `Elsa.Api.Common.UnitTests`. +- [ ] T042 [US5] Update `DefaultAccessTokenIssuer` and `DefaultElsaTokenService` to emit new-format `{resource}:{verb}` claims, and update `DefaultApiKeyProvider`, `AdminApiKeyProvider`, `LocalHostPermissionRequirement` and `DefaultExternalAuthenticationTokenIssuer` to match. **Partially done (verified 2026-08-27)** — the issuers emit role-derived permissions, but `LocalHostPermissionRequirement` still carries the legacy `BootstrapPermissions` list (`create:application`, `create:user`, `create:role`). Those satisfy none of the three endpoints they exist to unlock, all of which now declare structured permissions, so the localhost bootstrap path grants nothing. Tracked for repair. +- [x] T043 [US5] Add a startup validator that scans stored roles and logs every permission that does not resolve, identified by role name. Fails closed and loudly; the seeded `*` grant is unaffected so an instance cannot be locked out. **Verified done 2026-08-27.** +- [ ] T044 [P] [US5] Regression-test that legacy stored grants no longer authorize, that `*` still authorizes everything, and that a legacy *endpoint declaration* still resolves through T027. **Still open (verified 2026-08-27)** — no legacy-stored-grant regression test found. --- ## Phase 4: Introspection, Revocation, and Audit -- [ ] T045 [US4] Implement `GET /identity/me/permissions` in `src/modules/Elsa.Identity/Endpoints/Me/Permissions/Endpoint.cs`. Every registered resource is present, including those the caller cannot access, carrying an empty verb list. Wildcard grants are resolved to concrete verbs per covered resource. Contract: `contracts/rest-api.md`. -- [ ] T046 [US6] Lower the default `AccessTokenLifetime` in `src/modules/Elsa.Identity/Options/IdentityTokenOptions.cs` from 1 hour to 15 minutes. Refresh already rotates both tokens and re-reads roles, so no client change is required; leave `RefreshTokenLifetime` at 2 hours. -- [ ] T047 [US6] Add an optional per-principal security stamp: a monotonic value bumped on any role, grant or membership change, carried as a claim and compared against a per-node cached value under a configurable interval. Must not depend on cross-node cache invalidation, which Elsa does not have. -- [ ] T047a [US6] Ship the stamp's persistence with it: if the stamp lives on `User`, this task includes the Identity migrations for all five EF providers, so Phase 4 remains independently shippable and does not depend on T053 in Phase 5. Prefer a store that avoids an entity-schema change if one fits. -- [ ] T048 [US7] Publish typed security notifications for role create, update and delete, and for user role assignment and removal, through `INotificationSender` per ADR 0007. This feature owns no audit persistence. -- [ ] T049 [US1] Promote the deployment grant boundary (`PermissionGrantOptions.AllowedPermissions` / `DeniedPermissions`, currently in `ExternalAuthenticationOptions`) into `Elsa.Identity` so it applies to all grant paths, and make it wildcard-aware. -- [ ] T050 [P] [US4] Integration-test `/me/permissions` for a role holding partial verbs, for a wildcard grant resolving to concrete verbs, and for denied resources appearing with an empty list. -- [ ] T051 [P] [US6] Integration-test that a revoked role stops authorizing on the next token issuance, and immediately within the stamp interval when the stamp is enabled. +- [x] T045 [US4] Implement `GET /identity/me/permissions` in `src/modules/Elsa.Identity/Endpoints/Me/Permissions/Endpoint.cs`. Every registered resource is present, including those the caller cannot access, carrying an empty verb list. Wildcard grants are resolved to concrete verbs per covered resource. Contract: `contracts/rest-api.md`. **Verified done 2026-08-27.** +- [x] T046 [US6] Lower the default `AccessTokenLifetime` in `src/modules/Elsa.Identity/Options/IdentityTokenOptions.cs` from 1 hour to 15 minutes. Refresh already rotates both tokens and re-reads roles, so no client change is required; leave `RefreshTokenLifetime` at 2 hours. **Verified done 2026-08-27.** +- [x] T047 [US6] Add an optional per-principal security stamp: a monotonic value bumped on any role, grant or membership change, carried as a claim and compared against a per-node cached value under a configurable interval. Must not depend on cross-node cache invalidation, which Elsa does not have. **Verified done 2026-08-27.** +- [x] T047a [US6] Ship the stamp's persistence with it: if the stamp lives on `User`, this task includes the Identity migrations for all five EF providers, so Phase 4 remains independently shippable and does not depend on T053 in Phase 5. Prefer a store that avoids an entity-schema change if one fits. **Verified done 2026-08-27.** Note: satisfied without an entity-schema change — the stamp is computed by `PermissionStampCalculator` rather than persisted on `User`, so no Identity migrations were required. +- [x] T048 [US7] Publish typed security notifications for role create, update and delete, and for user role assignment and removal, through `INotificationSender` per ADR 0007. This feature owns no audit persistence. **Verified done 2026-08-27.** +- [ ] T049 [US1] Promote the deployment grant boundary (`PermissionGrantOptions.AllowedPermissions` / `DeniedPermissions`, currently in `ExternalAuthenticationOptions`) into `Elsa.Identity` so it applies to all grant paths, and make it wildcard-aware. **Still open (verified 2026-08-27)** — `PermissionGrantOptions` still lives in `Elsa.ExternalAuthentication`; not promoted to `Elsa.Identity`. +- [ ] T050 [P] [US4] Integration-test `/me/permissions` for a role holding partial verbs, for a wildcard grant resolving to concrete verbs, and for denied resources appearing with an empty list. **Still open (verified 2026-08-27)** — no `/me/permissions` integration test found. +- [ ] T051 [P] [US6] Integration-test that a revoked role stops authorizing on the next token issuance, and immediately within the stamp interval when the stamp is enabled. **Still open (verified 2026-08-27)** — no revocation / stamp-interval integration test found. --- @@ -110,11 +117,11 @@ No migration scaffold is required — the obsolete declaration path (T027) trans **Purpose**: Close the gaps that make "roles are per tenant" unsafe today. Independently justified as latent-defect fixes; sequenced last so the unresolved isolation-boundary question does not block delivery. -- [ ] T052 [US9] Replace the global unique indexes on `User.Name`, `Role.Name`, `Application.ClientId` and `Application.Name` with per-tenant composite indexes in `src/modules/Elsa.Persistence.EFCore/Modules/Identity/Configurations.cs`. -- [ ] T053 [US9] Generate Identity migrations for all five providers — Sqlite, SqlServer, PostgreSql, MySql, Oracle. -- [ ] T054 [US9] Make `src/modules/Elsa.Common/Services/MemoryStore.cs` tenant-aware so the default `IUserStore` and `IRoleStore` isolate, rather than isolation existing only on the Entity Framework path when `TenantsOptions.IsEnabled`. -- [ ] T055 [US9] Apply explicit tenant filters in `Endpoints/Users/List` and `Endpoints/Roles/List`, which currently pass an empty filter, and set `TenantId` explicitly in `UserManager.CreateUserAsync` rather than relying on the saving handler. -- [ ] T056 [P] [US9] Integration-test that two tenants can each hold a role named `Admin`, and that roles and users listed in one tenant are invisible in another, against both the Entity Framework and in-memory stores. +- [x] T052 [US9] Replace the global unique indexes on `User.Name`, `Role.Name`, `Application.ClientId` and `Application.Name` with per-tenant composite indexes in `src/modules/Elsa.Persistence.EFCore/Modules/Identity/Configurations.cs`. **Verified done 2026-08-27.** +- [x] T053 [US9] Generate Identity migrations for all five providers — Sqlite, SqlServer, PostgreSql, MySql, Oracle. **Verified done 2026-08-27.** +- [ ] T054 [US9] Make `src/modules/Elsa.Common/Services/MemoryStore.cs` tenant-aware so the default `IUserStore` and `IRoleStore` isolate, rather than isolation existing only on the Entity Framework path when `TenantsOptions.IsEnabled`. **Still open (verified 2026-08-27)** — `Elsa.Common/Services/MemoryStore.cs` has no tenant awareness (no `TenantId`/`ITenantAccessor` reference). +- [x] T055 [US9] Apply explicit tenant filters in `Endpoints/Users/List` and `Endpoints/Roles/List`, which currently pass an empty filter, and set `TenantId` explicitly in `UserManager.CreateUserAsync` rather than relying on the saving handler. **Verified done 2026-08-27.** +- [ ] T056 [P] [US9] Integration-test that two tenants can each hold a role named `Admin`, and that roles and users listed in one tenant are invisible in another, against both the Entity Framework and in-memory stores. **Still open (verified 2026-08-27)** — no cross-tenant identity isolation test verified for both EF and in-memory stores. --- @@ -126,7 +133,7 @@ No migration scaffold is required — the obsolete declaration path (T027) trans - [x] T060 Record the model in `doc/adr/0025-two-axis-authorization-model.md`: both axes open, wildcards as the only forward reach, no aggregates and no verb implication, and the rejection of a closed verb enumeration. **Done 2026-08-24** — `doc/adr/0025-two-axis-authorization-model.md`. - [x] T061 [P] Update `doc/wiki/identity-tenancy-security.md`, replacing the Secrets-only route table with a pointer to the catalog endpoint as the authoritative source. **Done 2026-08-24** - [x] T062 Resolve the five module-owner questions and fold the answers into the vocabulary. **Done 2026-08-23** — outcomes recorded at the end of `contracts/permissions.md` and as D25/D26 in `research.md`. Produced #7976 and #7977, and added FR-019's third declaration state (T026a). -- [ ] T063 Run `dotnet build Elsa.sln` and the full test suite, confirm T041 passes with zero exemptions beyond documented anonymous endpoints, and verify the quickstart scenario end to end. +- [ ] T063 Run `dotnet build Elsa.sln` and the full test suite, confirm T041 passes with zero exemptions beyond documented anonymous endpoints, and verify the quickstart scenario end to end. **Still open (verified 2026-08-27)** — full `dotnet build Elsa.sln` + full suite run not recorded. --- diff --git a/specs/013-user-tasks/tasks.md b/specs/013-user-tasks/tasks.md index eeed3bfc8..4f8ca2de9 100644 --- a/specs/013-user-tasks/tasks.md +++ b/specs/013-user-tasks/tasks.md @@ -1,5 +1,10 @@ # Tasks: User Tasks +> **Reconciled 2026-08-27** alongside `specs/013-rbac-authorization-model/tasks.md`. Open items were +> re-verified against the code; one had landed and is now ticked, and one (T041) is recorded as partially done. Of those still open, three are +> Studio-side and live in the `elsa-studio` repository, so they cannot be closed from this repo. + + Tasks use `[ID] [P?] [Story] Description with file path`. `[P]` tasks may run in parallel when their file ownership does not overlap. ## Phase 1: Durable specification @@ -23,7 +28,7 @@ Tasks use `[ID] [P?] [Story] Description with file path`. `[P]` tasks may run in - [x] T014 [P] [US3] Implement default claims resolver and default-deny access policy in `src/modules/Elsa.UserTasks/Services/`. - [x] T015 [US1] Implement the blocking `UserTask` activity, materialized bookmark payload, stimulus, and typed output under `src/modules/Elsa.UserTasks/Activities/`. - [x] T016 [US1] Project committed bookmarks and finalize removed bookmarks in `src/modules/Elsa.UserTasks/HostedServices/` and notification handlers. -- [ ] T017 [US5] Add the bounded startup/recurring reconciler for missing projections, stale operations, and orphan records. +- [ ] T017 [US5] Add the bounded startup/recurring reconciler for missing projections, stale operations, and orphan records. **Still open (verified 2026-08-27)** — the reconciler itself exists (`Services/DefaultUserTaskReconciler.cs`, `HostedServices/UserTaskWorkers.cs`), but no test exercises its repair logic, so it stays unchecked under this list's stated bar. See the verification-status note below. - [x] T018 [P] [US5] Add cluster-safe due scanning, idempotent overdue notification, and optional timeout operation. - [x] T019 [P] Add append-only safe audit and mediator lifecycle notification models/dispatch. - [x] T020 [P] Add domain, race, disclosure, identity, projection, reconciliation, and due tests in `test/unit/Elsa.UserTasks.UnitTests/`. @@ -35,8 +40,8 @@ Tasks use `[ID] [P?] [Story] Description with file path`. `[P]` tasks may run in - [x] T023 [P] [US2] Implement claim, release, assignment, and priority/due update endpoints. - [x] T024 [P] [US1] Implement asynchronous complete, cancel, and resolution-retry endpoints with operation idempotency. - [x] T025 [P] [US3] Implement optional participant lookup without Elsa Identity coupling. -- [ ] T026 [US2] Add metadata-free SignalR invalidation and polling-compatible lifecycle notifications. -- [ ] T027 [P] Add endpoint authorization, concealment, validation, cursor, idempotency, and conflict tests. +- [ ] T026 [US2] Add metadata-free SignalR invalidation and polling-compatible lifecycle notifications. **Still open (verified 2026-08-27)** — no SignalR/Hub type exists under `src/modules/Elsa.UserTasks`. +- [ ] T027 [P] Add endpoint authorization, concealment, validation, cursor, idempotency, and conflict tests. **Still open (verified 2026-08-27)** — partial — endpoint **authorization** is covered (`Authorization/EndpointPermissionTests.cs`, `EndpointCoverageTests.cs`, `ActorPermissionMatchingTests.cs`, added with #7999); concealment, validation, cursor, idempotency and conflict tests not found. ## Phase 4: Guest invitations @@ -56,7 +61,7 @@ Tasks use `[ID] [P?] [Story] Description with file path`. `[P]` tasks may run in - [x] T038 [P] [US5] Add MySQL provider configuration, migration, design-time factory, and shell feature. - [x] T039 [P] [US5] Add Oracle provider configuration, migration, design-time factory, and shell feature. - [x] T040 [P] [US5] Add VNext document-store repository and feature. -- [ ] T041 [US5] Add shared persistence conformance and SQLite restart/index/tenant/cursor tests. +- [ ] T041 [US5] Add shared persistence conformance and SQLite restart/index/tenant/cursor tests. **Partially done (verified 2026-08-27)** — the shared conformance half has landed, the SQLite half has not. See the verification-status note below for exactly what exists. ## Phase 6: Elsa Studio @@ -65,21 +70,21 @@ Tasks use `[ID] [P?] [Story] Description with file path`. `[P]` tasks may run in - [x] T044 [US2] Implement URL-backed Assigned to me, Available, History, All, and Needs Attention queue views. - [x] T045 [US2] Implement desktop split detail and mobile detail route with workflow deep link, protected disclosure, timeline, health, and capability actions. - [x] T046 [P] [US2] Implement claim/release/assign/update/complete/cancel/retry interactions with asynchronous and conflict refresh states. -- [ ] T047 [P] [US1] Add User Task activity editor support and optional replaceable participant lookup picker with raw/expression fallback. -- [ ] T048 [US2] Add metadata-free SignalR requery coordinator and polling fallback without disrupting focus or form input. +- [ ] T047 [P] [US1] Add User Task activity editor support and optional replaceable participant lookup picker with raw/expression fallback. **Still open (verified 2026-08-27)** — Studio work — lives in the `elsa-studio` repository, not verifiable here. +- [ ] T048 [US2] Add metadata-free SignalR requery coordinator and polling fallback without disrupting focus or form input. **Still open (verified 2026-08-27)** — Studio work — lives in the `elsa-studio` repository, not verifiable here. - [x] T049 [US4] Add replaceable anonymous guest verification and task completion page. -- [ ] T050 [P] Add Studio client/component tests for tabs, URL filters, disclosure, actions, responsive routing, realtime fallback, and accessibility. +- [ ] T050 [P] Add Studio client/component tests for tabs, URL filters, disclosure, actions, responsive routing, realtime fallback, and accessibility. **Still open (verified 2026-08-27)** — Studio work — lives in the `elsa-studio` repository, not verifiable here. ## Phase 7: Documentation and local gates - [x] T051 Add module configuration, identity integration, forms, invitations, hosting, persistence, and upgrade documentation under `doc/`. -- [ ] T052 Add runnable sample workflows and host adapter examples referenced by `specs/013-user-tasks/quickstart.md`. -- [ ] T053 Run affected Core unit/integration tests and all User Tasks persistence conformance tests. +- [ ] T052 Add runnable sample workflows and host adapter examples referenced by `specs/013-user-tasks/quickstart.md`. **Still open (verified 2026-08-27)** — no sample workflows or host adapter examples found alongside `quickstart.md`. +- [x] T053 Run affected Core unit/integration tests and all User Tasks persistence conformance tests. **Verified done 2026-08-27** — run 2026-08-27 for #7999 — UnitTests 103/103, Persistence.ConformanceTests 123 passed, Persistence.EFCore.UnitTests 4/4, Hosts.SmokeTests 6/6 (net10.0). - [x] T054 Run affected Studio tests and builds. -- [ ] T055 Build both repositories broadly across configured target frameworks without GitHub-dependent gates. -- [ ] T056 Re-run requirement-to-task-to-test traceability analysis and resolve every critical/high finding. -- [ ] T057 Run up to five local self-review passes for correctness, security, API compatibility, accessibility, and maintainability. -- [ ] T058 Confirm no unrelated files were changed and record local verification evidence. +- [ ] T055 Build both repositories broadly across configured target frameworks without GitHub-dependent gates. **Still open (verified 2026-08-27)** — cross-repository build not recorded. +- [ ] T056 Re-run requirement-to-task-to-test traceability analysis and resolve every critical/high finding. **Still open (verified 2026-08-27)** — traceability analysis not recorded. +- [ ] T057 Run up to five local self-review passes for correctness, security, API compatibility, accessibility, and maintainability. **Still open (verified 2026-08-27)** — self-review passes not recorded. +- [ ] T058 Confirm no unrelated files were changed and record local verification evidence. **Still open (verified 2026-08-27)** — not recorded. ## Dependencies @@ -109,15 +114,21 @@ worktree. The following remain open and are deliberately left unchecked: end to end. - **T027** — authorization, concealment, idempotency, and conflict behavior are covered at the service layer. There are no HTTP-level endpoint tests asserting the status-code mapping. -- **T041** — persistence coverage is EF Core/SQLite only. The shared conformance suite across - in-memory, EF, and VNext is not written. +- **T041** — *partially closed 2026-08-27.* The shared conformance half has landed: the suite runs + across in-memory, EF Core (SQLite, SQL Server, PostgreSQL, Oracle) and VNext, declared in + `ProviderConformanceSuites.cs`, with `ConformanceCoverageTests` failing the run when a provider + silently skips. Tenant and cursor behaviour are covered. Still missing are the SQLite **restart** + and **index** tests this task also names — there is no restart/reopen test anywhere in the + persistence suites, and no index-specific assertion. MySQL is additionally uncovered: it is pinned + to net8.0/net9.0 because Pomelo tops out at EF Core 9 — see `doc/migrations/secrets-tenancy.md`. - **T047** — the Studio activity editor contribution and participant-picker integration for the designer are not implemented. - **T050** — Studio tests cover the wire contract, URL state, and error mapping. Component-level tests for tabs, disclosure, responsive routing, realtime fallback, and accessibility are not written. - **T052** — no runnable sample workflows or host adapter examples yet. -- **T053, T055** — targeted Core and Studio test suites and the affected module, provider, and host - builds pass. A full solution-wide build across every configured target framework has not been run. +- **T055** — targeted Core and Studio test suites and the affected module, provider, and host builds + pass, and T053's suites were run on 2026-08-27 (see its task entry). A full solution-wide build + across every configured target framework has not been run. - **T056–T058** — final traceability, self-review, and evidence passes are outstanding. ## Local-only gate note