# Organization workspace implementation and acceptance

Updated: 17 October 2026. Implementation is local on the existing `main` working tree. Organization activation remains disabled by default. This report supplements the [approved integration guide](organization-workspace-production-integration.md); it does not authorize deployment or changing the application database.

Staging follow-up: an opt-in `ORGANIZATIONS_BYPASS_EMAIL_VERIFICATION=true` skips organization creation/acceptance verification only with `APP_ENV=staging`. Production always requires verification. It does not mark accounts verified or bypass invitation identity/token checks. See the [staging setup instructions](ubuntu-apache-deployment.md#staging-organization-email-verification-bypass). Focused backend verification: 23 tests / 151 assertions passed, including default enforcement, staging-only gating, unverified creation/acceptance and unchanged verification records. The frontend follow-up passed all 168 tests, the production build and lint; its four new cases cover the default gate, verified API boolean, staging creation and invitation acceptance. Regression output is in `documents_generator_tools_frontend/out/organization-verification-regression.log`.

## What was implemented

The Laravel application now owns organization data and authorization. React uses authenticated APIs rather than prototype fixtures. No Demo controls are included in production screens.

| Area | Implementation |
| --- | --- |
| Foundations | Additive migration, organizations, memberships, invitations, assignments, organization folders, entitlements, usage events and retained audit history. Existing conversion UUIDs and nullable personal ownership remain compatible. |
| Identity | Personal workspace plus multiple organizations, verified-email creation, signed verification, seven-day hashed invitations, rotating resend secrets and matching-email acceptance. |
| Access | Central `ProjectAccessService` checks organization ownership first. Removed creators retain no implicit access. Member assignments independently grant editing and package downloading. Suspension retains assignments; removal clears them. |
| Administration | Organization-row locking protects the last active admin. Super-admin organization/account suspension, processing visibility, entitlements and application narration fallback controls are audited. |
| Projects | Admin-only creation, six-type chooser with the existing availability, organization folders, assignments, archive/restore/delete and sharing controls. Members see assigned projects. |
| Sharing | Disabled initially. Disabling requires explicit confirmation and revokes existing grants and links. Re-enabling does not recreate them. Global sharing and entitlement switches also apply. |
| Narration and work | Encrypted organization credentials, write-only secrets, platform-controlled fallback, actor revalidation in queued work and before publication, generation-specific package publication retaining the previous valid package on cancellation. |
| Usage | Server-derived project/member/storage totals and conversion/AI operation accounting. Free/unlimited commercial defaults retain existing technical processing limits. Reconciliation inspects stale reservations without inferring provider charges. |
| Interface | Navy/teal organization overview, projects/details/assignments, members/invitations, activity, independent settings sections, member information/leave, onboarding and super-admin workspace administration. English/Arabic text, RTL and responsive styles are included. |
| Editor | Existing editor URLs remain canonical. The server project response determines its workspace. Pickers and tab metadata are scoped by organization. Workspace departure waits for successful saves or explicit discard, disposes session timers and closes previous tabs. Unknown Back/Forward targets remount to resolve their context. |

Personal routes, private-file delivery, settings revisions, editor appearance and existing H5P work were retained. Backend Stage 10 references were adapted on the current branch; no branch switch or wholesale merge was performed.

## Code map

- Backend schema: `documents_generator_tools_backend/database/migrations/2026_10_11_000001_add_organization_workspaces.php`.
- Models: `app/Models/Organization*.php`; conversion and user extensions remain in their existing models.
- Authorization and consistency: `ProjectAccessService`, `WorkspaceService`, `OrganizationMembershipService`, `OrganizationInvitationService`, `WorkspaceEntitlementService`, `ConversionWriteLock`, `OrganizationWriteLock`, and `Jobs/Concerns/ChecksWorkspaceAccess`.
- Controllers: `OrganizationController`, `OrganizationFolderController`, `WorkspaceAdminController`, and `EmailVerificationController`, plus the existing project/editor controllers.
- Production React: `documents_generator_tools_frontend/app/organizations/`. Styles are scoped to organization surfaces/dialogs. Shared workspace switcher and project chooser retain personal support.
- Editor integration: `app/tools/powerpoint-to-scorm/studio/editor-workspace.tsx`, `use-editor-session.ts`, `project-picker.tsx`, and studio context/API types.
- Focused tests: backend `OrganizationWorkspaceTest`, `OrganizationExperienceTest`, `OrganizationMigrationTest`; frontend `organization-workspace.test.mjs` and organization cases in `editor-workspace.test.mjs`.

## API and compatibility notes

All paths below use `/api/v1` and the existing authenticated session/CSRF flow. Organization writes are authorized and serialized on the server; hiding controls is supplementary.

- `GET /auth/features`, `GET /workspaces`, `POST /auth/email-verification`, and signed `GET /auth/verify-email/{id}/{hash}`.
- `POST /organizations`, `GET/PATCH /organizations/{id}`, organization overview/activity/members/invitations/leave, logo, narration, usage, folders and project assignment endpoints defined in `routes/api.php`.
- Invitation inspection is **POST** `/organization-invitations/{id}/inspect` with the secret in the JSON body. Acceptance is POST `/organization-invitations/{id}/accept`. The frontend receives the invitation secret in the URL fragment and preserves it in tab storage through login. It is not a query-string inspection API.
- Project lists accept `organization_id` alongside status, folder, search, sort, unassigned and pagination filters. Uploads accept `organization_id`; the server requires organization-admin authority. Project responses include organization/workspace identity, creator and capabilities.
- Sharing disable uses `confirm_revoke: true`. Independent general-information saves omit sharing fields and do not revoke access.
- `/admin/organizations`, `/admin/users`, `/admin/workspace-audit`, `/admin/processing`, and `/admin/workspaces/{workspace}/entitlements` require platform super-admin authority.
- Existing conversion URLs, private asset URLs and personal project routes stay valid. Already issued signed asset/preview URLs retain their expiry semantics; this implementation does not promise immediate invalidation of bytes already downloaded or signed URLs already issued.

## Local walkthrough

Use an isolated database and private-storage root for acceptance. The application `.env` and its configured database were not migrated during implementation. Never run `migrate:fresh` against the normal application environment.

The disposable review instance uses [http://localhost:5186](http://localhost:5186), with its API on port 8012, `out/organization-integration/review.sqlite`, a router that checks that exact database path and redirects private storage beneath the same review directory, and process-only settings:

```text
APP_URL=http://localhost:8012
FRONTEND_URL=http://localhost:5186
SANCTUM_STATEFUL_DOMAINS=localhost:5186
DB_CONNECTION=sqlite
DB_DATABASE=<repository>/out/organization-integration/review.sqlite
DB_URL=
ORGANIZATIONS_ENABLED=true
MAIL_MAILER=array
ELEVENLABS_TTS_ENABLED=false
VITE_API_URL=http://localhost:8012/api/v1
```

These settings belong only to disposable review processes. The review helper/SQLite files are local artifacts, not deployment assets. Reuse an existing review database without recreating or truncating its file.

While those local processes are running, the synthetic review accounts are `admin@atlas.example` and `member@atlas.example`, with password `Review-only-2026!`. These identities exist only in the disposable SQLite database. Its two sample organizations are Atlas Learning and Northstar Studio. Seeded projects demonstrate access and layout; they contain no rendered slide deck. Use an actual disposable PPTX for native-rendering acceptance.

1. Sign in as an organization admin. Use the workspace switcher, open Overview, then Projects. Choose New project to inspect all six project types; only the currently supported PPTX-to-SCORM tool is available.
2. Open project Details. Assign a member with edit enabled and download disabled, then another assignment with viewing/download enabled. Check the editor and download endpoints as each member, including direct URLs.
3. In People, inspect Members and Invitations, send a test invitation using a captured local mail transport, exercise expiration/resend/revocation/email mismatch and accept with the matching verified account. Do not use live SMTP for this walkthrough.
4. Try demoting, suspending, removing or leaving as the final active admin. Add another active admin before retrying an otherwise permitted change.
5. Edit General and Narration drafts together. Save one section and confirm the other draft remains. Enable Sharing, create disposable grants, disable with confirmation and confirm the grants remain revoked after re-enabling.
6. Open an assigned project, edit it, then switch workspace. Cancel preserves the draft; failed Save keeps the editor open; Discard closes old sessions and cancels their delayed autosaves. Check browser Back/Forward between personal and organization projects.
7. Use Account settings separately from organization Settings. As a member, use Organization information to inspect membership and leave. Projects remain organization-owned.
8. Review super-admin Workspace administration with a disposable support identity. Confirm ordinary accounts receive denial from its APIs.

## Validation evidence

Before integration: frontend 152 passing tests with a successful build; backend 117 tests / 2,011 assertions with one renderer-dependent skip. These are the captured local baseline, not production results.

Final local results:

| Check | Result |
| --- | --- |
| Production frontend | 164 tests passed; TypeScript and Vite production build passed. |
| Frontend ESLint | Passed with zero errors and zero warnings. |
| Backend full suite | 145 tests / 2,197 assertions; zero failures, one renderer-dependent skip. SQLite was isolated in memory. |
| Standalone prototype | 48 JavaScript files passed syntax validation; all 80 tests passed. |
| Patch whitespace | `git diff --check` passed. |

Logs are local artifacts in `documents_generator_tools_frontend/out/organization-final-tests.log`, `organization-final-lint.log`, and `out/organization-integration/{backend-tests,prototype-tests}.log`. Commands used:

```powershell
# From documents_generator_tools_frontend
npm test
npm run lint

# From documents_generator_tools_backend: isolated in-memory database only
$env:APP_ENV='testing'
$env:DB_CONNECTION='sqlite'
$env:DB_DATABASE=':memory:'
$env:DB_URL=''
& 'C:/laragon/bin/php/php8.3/php.exe' -d extension=php_pdo_sqlite.dll -d extension=php_sqlite3.dll vendor/bin/phpunit

# From frontend_prototype
npm run check
npm test
```

Focused coverage includes workspace isolation, creator removal, independent edit/download combinations, invitation lifecycle, last-admin protection, sharing revocation, failed saves, draft-safe switching, discarded autosave disposal, direct project context, and cancellation before package publication. The migration rehearsal verifies existing conversion fields and a private-file checksum survive and that rerunning migrations is safe.

SQLite tests do **not** establish concurrent MySQL row-lock behavior. Provider/worker cancellation tests use controlled doubles and do not constitute native rendering, provider billing or Moodle acceptance.

Contrast samples saved in [contrast.json](organization-workspace-evidence/contrast.json): primary text on white 14.16:1, muted text on white 5.53:1, white on primary teal 4.86:1, navigation text 9.70:1, badge text 5.56:1 and focus against canvas 4.52:1. This is a sample of the palette, not an exhaustive contrast audit of every control/state.

### Screenshots and browser limitations

Actual local React/Laravel desktop captures are available in:

- [Overview](organization-workspace-evidence/overview-desktop.png)
- [Projects](organization-workspace-evidence/projects-desktop.png)
- [Assignments](organization-workspace-evidence/assignments-desktop.png)
- [Settings](organization-workspace-evidence/settings-desktop.png)

These captures precede the final sidebar identity consolidation and final navigation fixes. `people-desktop.png` captured the loading screen and is excluded from UI evidence. Browser automation later lost its Chrome connection and returned no available browsers. Final People/member screenshots, Arabic/RTL visual review, tablet/390px layouts, actual 200% zoom, full keyboard/focus restoration and page-overflow review remain outstanding. Automated DOM coverage is not a substitute for that review. Prototype screenshots are not presented as production screenshots.

## Deployment acceptance and rollback

See the [deployment runbook](ubuntu-apache-deployment.md). Keep `ORGANIZATIONS_ENABLED=false` until acceptance is complete. A normal dev launch will intentionally not expose organization creation while this flag is off.

Before an authorized deployment:

1. Back up the database, private storage and `APP_KEY`; rehearse the additive migration against a representative MySQL copy. Test simultaneous last-admin changes and queued-work/member changes with real concurrent connections.
2. Deploy backend and built frontend together in the existing maintenance sequence. Apply additive migrations only to the explicitly selected database. Do not roll back populated organization schema.
3. Verify real mail signatures/expiry, frontend/API origins, CSRF, throttling and SMTP delivery in staging. Review that no invitation secrets or narration keys appear in logs or responses.
4. Run native workers with the existing timeout/retry contract. Revoke assignment or suspend membership during queued and running conversion, media, AI narration and packaging; verify late output cannot replace a valid published package. Schedule usage reconciliation only after inspecting its dry-run output.
5. With explicit provider approval, test organization credentials, application fallback policy, refusal to fallback after provider failure and usage accounting. Reconciliation cannot infer charges for ambiguous provider timeouts.
6. Complete the remaining browser matrix above; test personal dashboard/profile/editor regressions and Moodle/another LMS with a real generated package.
7. Enable organization access only after those checks pass, then clear/cache configuration and restart PHP/queue processes together. Existing H5P/native worker release requirements continue to apply independently.

Safe feature rollback: set `ORGANIZATIONS_ENABLED=false`, rebuild configuration cache and restart application/worker processes. This hides organization access for ordinary accounts and blocks organization editing/generation; audited support administration remains available. Retain organization ownership, memberships, files, audit and usage records. Never remove `organization_id` or revert to creator-only authorization as a rollback shortcut.

`php artisan workspaces:reconcile-usage` is a dry run. `--apply` releases cancelled stale reservations only. Inspect the selected environment/database before using it; do not use it to reset active work or recover uncertain provider charges.

No deployment, application-database migration, live invitation, paid-provider request or production organization activation was performed during implementation.
