feat: harden workspace and content management workflows
Summary
- Make Assets, Bundles, Workspaces, Users, Dashboard, Chat, Login, and Settings use the same page, form, status, feedback, confirmation, and navigation patterns.
- Make asset preparation, bundle deletion, workspace environment changes, contribution uploads, role changes, and ownership transfers explicit and race-safe.
- Publish the updated API contracts, contributor guidance, CI gate, and regression coverage for these workflows.
Why
Several management flows could show stale status, lose an in-progress draft, accept an out-of-date mutation, or leave users unsure about the effect of a destructive action. Asset and bundle state also lacked consistent metadata and useful explanations for known text-recognition failures. The affected pages used different controls and native browser prompts for equivalent actions, which made review and recovery harder.
What changed
Shared interface and navigation
- Add shared page headers, search fields, refresh controls, status badges, notices, list-row styles, accessible dialogs, destructive confirmations, and presentation helpers. Replace page-specific and native confirmation flows with these components.
- Add a shared unsaved-changes guard. Settings, Users, and Workspaces can save or cancel before navigation, and workspace saves preserve edits that arrive while a request is in flight.
- Keep the shell, dashboard, chat, login, and management pages consistent across desktop and narrow viewports. Cancel obsolete privilege requests during navigation and logout.
Assets and bundles
- Show asset version, size, upload time, authoritative preparation state, and stable
user-facing diagnostics for known text-recognition failures. Serialize status polling,
keep
PARTIALnonterminal, and bound repeatedINCOMPLETEpolling by document identity. - Make add-to-bundle behavior explicit when a bundle already follows the latest version or is pinned to an older version. New versions selected by an unpinned bundle use the backend reconciliation contract; the browser does not issue a second hidden request.
- Canonicalize bundle identifiers, reject unknown mutation fields, preserve strict item validation, and require a valid upstream deletion acknowledgement before local cleanup.
- Show unavailable bundle items, keep large item lists scrollable, and require count-aware confirmations for asset deletion and remove-everywhere bundle deletion. Deleting a bundle leaves its assets intact.
Workspaces and users
- Default new workspace chat environments to Chat with references, call out zero selected reference bundles, and let members add assets directly to a workspace-owned contribution bundle. Preserve successfully uploaded assets when workspace linking fails and report the partial result.
- Require complete, revision-checked workspace environment updates. Serialize ownership, contribution-role, and reference-access mutations; validate assignment targets; and keep refreshes from overwriting local drafts.
- Ask how to handle a contributor's bundle before removing the role that grants direct contributions. The user can keep it as workspace references or remove workspace access without deleting the contributor's assets.
- Default user creation to membership in the active tenant, while keeping deliberate creation without tenant membership available. Keep system administration restricted to global user privileges and preserve form drafts during saves.
Contracts and regression coverage
- Update the OpenAPI snapshot, generated frontend types, BFF validation, service client, public interface guidance, feature documentation, and workspace debug journey for the final request shapes.
- Add focused backend, frontend, PostgreSQL, mocked-browser, and multi-user coverage for reconciliation, deletion, stale writes, concurrent role and ownership changes, grounded references, and the shared interface behavior.
Review notes
- Review
backend/workspace_routes.py,backend/workspace_rbac.py, andbackend/workspace_contributions.pyfirst for revision checks, transaction boundaries, authorization, and mutation serialization. - Review
backend/bundle_routes.pyandbackend/bundle_identifiers.pyfor strict request validation, canonical identifiers, deletion acknowledgements, and cleanup ordering. - Review
src/common/navigation/UnsavedChanges.tsxand the Assets, Bundles, Workspaces, and Users pages for draft ownership, stale-response handling, and destructive-action targets.backend/openapi.yamlandsrc/services/api-types.gen.tsare generated or mirrored contract output. - The branch changes no database schema, migration, runtime dependency, secret, application configuration, container contract, or Kubernetes manifest.
UI evidence
All images below are new after-state captures from Chrome with a local Vite frontend and fully mocked BFF responses. The fixtures use synthetic users, tenants, workspaces, bundles, and assets. They prove rendered behavior only, not server authorization, persistence, transactionality, or backend correctness.
Shared desktop and responsive presentation
Chrome, 1440 x 1000: dashboard, shared shell, quick actions, consistent list metadata, and attention states.
Chrome, 390 x 844: narrow viewport with the application drawer open and primary destinations reachable.
Assets
Chrome, 1440 x 1000: asset metadata and DONE, PARTIAL, and failed preparation states, including the stable text-recognition input-limit explanation.
Chrome, 1440 x 1000: add-to-bundle dialog for an asset pinned to an older version, with the explicit Use latest transition.
Chrome, 1440 x 1000: permanent asset-deletion confirmation with affected bundle and workspace counts.
Bundles
Chrome, 1440 x 1000: scrollable bundle detail with live and unavailable items and the cleanup control enabled.
Chrome, 1440 x 1000: remove-everywhere bundle confirmation with workspace impact and explicit asset retention.
Workspaces
Chrome, 1440 x 1100: default Chat with references environment, zero-reference warning, audience controls, and direct-contribution upload area.
Chrome, 1440 x 1100: partial-success contribution state showing that the asset remains in Assets when workspace linking fails.
Chrome, 1440 x 1000: contributor-role removal decision with keep-as-references and remove-from-workspace choices.
Users and unsaved changes
Chrome, 1440 x 1000: user-creation confirmation with active-tenant membership selected and no internal login.
Chrome, 1440 x 1000: shared navigation guard after editing Settings, with save and cancel choices.
Verification
All source results below apply to clean EthelApp revision
b3ecb74c4fdb99480720d66c2514d273f87ab6b1 unless stated otherwise.
- A focused regression set passed 76 tests.
-
make gatepassed 474 backend tests with 10 configured PostgreSQL skips, OpenAPI and route/client checks, lint, type, generated-type and format checks, and 356 frontend tests. - The mocked browser suite passed all 10 tests. The current final diff also passes
git diff --check. - A clean exact-revision application image passed runtime-content checks and the focused headed grounded-reference story. The complete eight-story headed suite passed seven stories. The remaining direct-image assertion asks a deterministic text-only test provider to infer a marker present only in PNG pixels; its grounded PDF path passed, and the request evidence confirmed one image input without an ambiguous reference.
- A disposable dual-stack check used this EthelApp revision with EthelFlow revision
28200137db16a9005a42f30ec3c93c4fa41c883b. Both isolated stacks reached readiness without restarts and passed persistence and isolation checks; the eight-story result repeated the same seven passes and deterministic text-provider limitation. - The 12 UI images above were rendered from the clean revision with synthetic fixtures, zero unhandled API requests, and visual inspection of every capture.
CI
- Replace the placeholder job with a required
gatejob for merge-request and default- branch pipelines. - Run
make gatewith Python 3.13, Node 22, and PostgreSQL 16, using the pinned backend development requirements and dependency caches.
Docs
- Add
INTERFACE_GUIDELINES.mdas the source for shared frontend interaction patterns. - Update the root, backend, service, feature, test, and multi-user documentation for bundle deletion, derivation diagnostics, workspace contributions, request contracts, and maintained verification journeys.
Out of scope / follow-ups
- Visual interpretation is not added to the deterministic text-only system-test provider. The direct-image transport path is covered, but that provider cannot assert content drawn only in image pixels.
- No database migration or installation-specific rollout change is included.
Developer notes
- Read
INTERFACE_GUIDELINES.mdbefore changing frontend interactions and use the shared components for equivalent page, form, feedback, status, dialog, and navigation states. - Run
make gatebefore review. After intentional backend route or schema changes, runmake openapi-generate, regenerate frontend API types, and rerun the gate. - Send
expected_revwith a complete workspace environment update. Ownership-transfer clients must send the observed current owner, and contribution-role changes must use the contribution-bundle decision contract. - Treat bundle deletion as a remove-everywhere operation. Perform local cleanup only after the upstream acknowledgement identifies the requested bundle; do not duplicate reconciliation or workspace-detachment logic in the browser.
- Keep the workspace debug journey and multi-user stories synchronized with these request shapes when the API changes.











