{
  "id": "2026-05-06-b2bea-org-company-workspace-data-spec-661f53ce52",
  "scope": "redkey",
  "source_of_truth": "repo",
  "source_path": "docs/specs/2026-05-06-b2bea-org-company-workspace-data-spec.md",
  "source_kind": "markdown",
  "visibility": "internal",
  "renderer_id": "design_doc.dreamborn-forge.generated.v1",
  "design_system": "dreamborn-design-system:forge",
  "generated_at": "2026-05-09T13:00:55.707Z",
  "artifact_type": "design_doc",
  "schema_version": "design_doc.generated.v1",
  "title": "B2BEA.org V1 Company Workspace Data Spec",
  "summary": "B2BEA.org V1 Company Workspace Data Spec Source of record: RedKey Supabase Studio artifact. Project: B2BEA.org Rebuild Project ID: a820dd0c 6cef 4133 bfbd d802fd806e44 Artifact: company workspace data spec Artifact ID: 05e0ed7c 416a 4d8f 853a bc3dfa3d64f6 Version: 1 Status: draft Updated: 2026 05 07T00:32:06.585172+00:00 Scope Included in V1: Private practit...",
  "format_source": "markdown",
  "sections": [
    {
      "title": "B2BEA.org V1 Company Workspace Data Spec",
      "level": 1,
      "body": "Source of record: RedKey Supabase Studio artifact.\n\n- Project: `B2BEA.org Rebuild`\n- Project ID: `a820dd0c-6cef-4133-bfbd-d802fd806e44`\n- Artifact: `company-workspace-data-spec`\n- Artifact ID: `05e0ed7c-416a-4d8f-853a-bc3dfa3d64f6`\n- Version: `1`\n- Status: `draft`\n- Updated: `2026-05-07T00:32:06.585172+00:00`"
    },
    {
      "title": "Scope",
      "level": 2,
      "body": "Included in V1:\n\n- Private practitioner company workspace only.\n- Company admins and employee/member access.\n- Academy seat assignment and company learning visibility.\n- Company-created jobs submitted for B2BEA admin review.\n- Company profile/account settings for private workspace use.\n- Bounded own-company analytics/export rules.\n\nExcluded from V1:\n\n- Public practitioner company profile pages.\n- Public company directory for practitioner companies.\n- Complex internal admin permissioning beyond the Brett/Sarah/Justin light admin model.\n- Company-owned CRM pipeline; HubSpot remains CRM primary."
    },
    {
      "title": "Live Supabase Check",
      "level": 2,
      "body": "Checked against the active B2BEA Supabase project and current `B2BEA-org-new` code signals.\n\n- Supabase host: `czqxkykbhoyyjccckpqq.supabase.co`\n- Source repo: `/Users/justinking/Vaults/Projects/B2BEA-org-new`\n\n| Table | Rows | Status | Notes |\n|---|---:|---|---|\n| `organizations` | 60 | Exists, populated | Supports name, slug, type, website, location, status, primary contact, careers extension fields. |\n| `organization_members` | 0 | Exists, empty | Supports org-person role `admin/member` plus joined/removed timestamps. Currently unused live. |\n| `memberships` | 1 | Exists, partial | Supports `held_by_person_id` or `held_by_org_id` and `seat_count`. Only one active person-held Pro membership observed. |\n| `membership_seats` | 0 | Exists, empty | Supports membership seat assignment to a person. No active seat assignments observed. |\n| `jobs` | 30 | Exists, populated | Supports `organization_id`, `company_name`, `source`, `status`, `published_at`, `expires_at`. Existing statuses do not include review/submitted. |\n| `courses` | 9 | Exists, populated | Course access exists but needs entitlement normalization before company assignment. |\n| `course_enrollments` | 4 | Exists, partial | Participation exists, but no company assignment/source fields observed. |\n| `person_roles` | 8 | Exists, partial | Global person roles exist; no company-specific role scope beyond `organization_members.role`. |\n| `people` | 6,733 | Exists, populated | Has `organization_id` and profile/careers fields; legacy `persons` references still need cleanup. |\n\nCode signals:\n\n- `src/admin/companies/index.njk` manages organizations and members from the internal admin surface.\n- Admin companies page loads people by `people.organization_id` rather than `organization_members`, so `removed_at` state can drift from visible membership.\n- Admin companies page can upsert `organization_members`, but schema role choices are only `admin/member`; vendor roles are assigned separately through `person_roles`.\n- Admin jobs page can choose organizations for jobs; public company-created job submission workflow is not present.\n- No practitioner company workspace route or portal surface was observed.\n- No company course assignment, invitation, seat availability, or company analytics export implementation was observed."
    },
    {
      "title": "Decisions",
      "level": 2,
      "body": "| ID | Topic | Decision |\n|---|---|---|\n| `CW-DEC-001` | Public surface | Practitioner company accounts are private workspace only in V1. Do not build public company profile pages or a public company directory for practitioner companies. |\n| `CW-DEC-002` | Canonical company account | Use `organizations` as the canonical company account table. Extend it for workspace/account metadata instead of creating a second company table. |\n| `CW-DEC-003` | Company membership | Use org-held `memberships` plus `membership_seats` as the canonical source of company seats and included benefits. |\n| `CW-DEC-004` | Company roles | Use `organization_members` for company-scoped roles. Use `person_roles` only for global/internal/vendor roles. |\n| `CW-DEC-005` | Jobs publishing | Company-created jobs require B2BEA admin review before public publishing in V1. |\n| `CW-DEC-006` | Academy assignments | Company admins can assign available academy content to employees only when the org entitlement grants the content and the employee has an active seat or assignment. |\n| `CW-DEC-007` | Analytics exports | Company analytics and exports are own-company only and must exclude raw platform-wide data and sensitive user-level data unless explicitly consented and permitted. |\n| `CW-DEC-008` | CRM boundary | HubSpot remains CRM primary for lead, pipeline, sales activity, and renewal workflows. Company workspace state lives in B2BEA Supabase. |"
    },
    {
      "title": "Target Capabilities",
      "level": 2,
      "body": "| Capability | Surface | Actor | Description |\n|---|---|---|---|\n| `CompanyWorkspaceView` | Company workspace | company_admin/company_member | View private company dashboard, account status, seats, assigned courses, submitted jobs, and reporting. |\n| `CompanyUpdateProfile` | Company workspace | company_admin | Update private company workspace metadata, logo/banner for careers, website, HQ, and primary contact. |\n| `CompanyInviteEmployee` | Company workspace | company_admin | Invite employee by email, assign role, optionally reserve/assign academy seat. |\n| `CompanyRemoveEmployee` | Company workspace | company_admin | Remove employee access, revoke active seat, preserve audit history. |\n| `CompanyAssignAcademy` | Company workspace | company_admin | Assign eligible course/path/certification to employee or cohort. |\n| `CompanyViewLearningProgress` | Company workspace | company_admin | View aggregate and employee-level progress allowed by policy. |\n| `CompanyCreateJob` | Company workspace | company_admin | Create a job submission for B2BEA admin review. |\n| `CompanyManageJobs` | Company workspace | company_admin | View submitted/published/expired/returned jobs and make updates through review workflow. |\n| `CompanyViewAnalytics` | Company workspace | company_admin | View own-company learning, jobs, and engagement analytics. |\n| `CompanyExportAnalytics` | Company workspace | company_admin | Export bounded own-company reports. |\n| `AdminReviewCompanyJob` | B2BEA admin | b2bea_admin | Review, approve, reject, publish, unpublish, or request changes to company-created jobs. |"
    },
    {
      "title": "Target Data Model",
      "level": 2,
      "body": "Reuse and harden:\n\n| Table | Use | Required Hardening |\n|---|---|---|\n| `organizations` | Canonical company account and workspace profile. | Add or confirm `workspace_status`, `workspace_enabled`, and account type handling beyond `pro_member/vendor` if needed. Keep public profile disabled/not used for practitioner companies in V1. |\n| `organization_members` | Company-scoped membership and roles. | Expand role enum to `owner/admin/member/learner/recruiter` if V1 needs distinct workflows. Use active filter `removed_at is null` everywhere. Add invite/acceptance fields if not using separate invites table. |\n| `memberships` | Org-held membership and seat count. | Use `held_by_org_id` for company accounts. Ensure `status/current_period_end` drives entitlement evaluator. |\n| `membership_seats` | Seat assignment to people. | Add `assigned_by`, `assignment_source`, and `status` if audit cannot be fully covered elsewhere. Enforce active seats <= `memberships.seat_count`. |\n| `jobs` | Published public jobs and company-submitted job records. | Add review lifecycle statuses or separate submission table. Add `submitted_by`, `approved_by`, `reviewed_at`, `rejection_reason`, and change request fields. |\n| `course_enrollments` | Learner course participation. | Add/source link to course assignment if company-assigned courses are V1. |\n\nAdd new:\n\n| Table | Purpose | Core Fields |\n|---|---|---|\n| `organization_invitations` | Invite and acceptance lifecycle for company employees. | `id`, `organization_id`, `email`, `role`, `invited_by_person_id`, `status`, `token_hash`, `expires_at`, `accepted_by_person_id`, `accepted_at`, timestamps |\n| `course_assignments` | Company/admin assignment of courses, paths, or certifications before/alongside enrollment. | `id`, `organization_id`, `course_id`, `assigned_to_person_id`, `assigned_by_person_id`, `source_type`, `status`, `due_at`, timestamps |\n| `company_job_submissions` | Review workflow wrapper for company-created jobs before publishing to `jobs`. | `id`, `organization_id`, `submitted_by_person_id`, `job_id`, `payload`, `status`, `reviewed_by_person_id`, `reviewed_at`, `review_note`, timestamps |\n| `company_analytics_events` | Optional normalized own-company analytics event stream if existing tables are insufficient. | `id`, `organization_id`, `person_id`, `event_type`, `resource_type`, `resource_id`, `metadata`, `created_at` |\n| `company_audit_events` | Workspace audit trail for employee, seat, academy, job, export, and settings changes. | `id`, `organization_id`, `actor_person_id`, `event_type`, `target_type`, `target_id`, `reason`, `metadata`, `created_at` |"
    },
    {
      "title": "Lifecycle Models",
      "level": 2,
      "body": "Employee lifecycle:\n\n1. `invited`\n2. `accepted_active`\n3. `seat_assigned`\n4. `seat_revoked`\n5. `removed`\n6. `reinvited`\n\nJob submission lifecycle:\n\n1. `draft`\n2. `submitted_for_review`\n3. `changes_requested`\n4. `approved`\n5. `published`\n6. `rejected`\n7. `expired`\n8. `removed`\n\nAcademy assignment lifecycle:\n\n1. `assigned`\n2. `enrolled`\n3. `in_progress`\n4. `completed`\n5. `overdue`\n6. `revoked`\n\nCompany membership lifecycle:\n\n1. `prospect_or_inactive`\n2. `active`\n3. `past_due_or_suspended`\n4. `expired`\n5. `cancelled`"
    },
    {
      "title": "Permission Model",
      "level": 2,
      "body": "| Role | Grants | Notes |\n|---|---|---|\n| `company_owner` | `company.workspace.admin`, `company.academy.assign`, `company.careers.submit_job`, `company.analytics.export` | One or more accountable company owners. May manage admins. |\n| `company_admin` | `company.workspace.admin`, `company.academy.assign`, `company.careers.submit_job`, `company.analytics.export` | Operational company admin; can invite/remove members depending policy. |\n| `company_recruiter` | `company.workspace.read`, `company.careers.submit_job` | Optional V1 role if hiring workflow needs separation. |\n| `company_member` | `company.workspace.read` | Employee/member baseline access. |\n| `company_learner` | `company.workspace.read`, `academy.course.enroll.included` | Employee with active academy seat or assignment. |\n| `b2bea_admin` | `admin.platform.manage`, `admin.content.publish` | Brett/Sarah/Justin light admin model for review and support. |"
    },
    {
      "title": "Surface Requirements",
      "level": 2,
      "body": "| Surface | Requirements |\n|---|---|\n| Company workspace dashboard | Current membership/status, seat usage, employee roster, assigned academy content, job submissions, recent audit/activity, exports/downloads available to the account. |\n| Employee roster | Invite employee, role selection, seat assignment/revocation, remove employee, status filters, last active/accepted state. |\n| Academy management | Available courses/paths from entitlement, assign to employee/cohort, progress view, completion/certification status, export allowed reporting. |\n| Careers/jobs | Create job draft, submit for review, see admin review status, respond to change request, expire/remove job. |\n| Admin review | Queue of company job submissions, diff/payload review, approve to `jobs` table, reject/request changes, publish/unpublish, audit event. |"
    },
    {
      "title": "Gaps",
      "level": 2,
      "body": "| ID | Severity | Area | Current | Required |\n|---|---:|---|---|---|\n| `CW-GAP-001` | P0 | No company workspace route | Only internal admin companies page observed. | Build authenticated private company workspace surface for company admins and employees. |\n| `CW-GAP-002` | P0 | Empty company membership tables | `organization_members` and `membership_seats` exist but have zero rows. | Implement invite, membership, and seat assignment lifecycle with active `removed_at` filtering. |\n| `CW-GAP-003` | P0 | Role scoping | `organization_members.role` only supports `admin/member`; `person_roles` is global. | Define company-scoped roles and avoid using global roles for company permissions. |\n| `CW-GAP-004` | P0 | Academy assignment | No `course_assignments`/company assignment model observed. | Add course assignment model tied to org, employee, entitlement, and enrollment. |\n| `CW-GAP-005` | P0 | Company-created jobs | Jobs table has published/draft statuses but no company submission/review lifecycle. | Add review workflow and admin approval before public publishing. |\n| `CW-GAP-006` | P1 | Analytics exports | Own-company analytics export model not defined. | Define bounded event taxonomy, aggregation, and export policy. |\n| `CW-GAP-007` | P1 | Auditability | No company workspace audit trail observed. | Record employee, seat, academy, job, export, and settings changes. |\n| `CW-GAP-008` | P1 | Data drift risk | Admin companies page reads `people.organization_id` for roster visibility while also writing `organization_members`. | Choose roster truth and update all reads to use active `organization_members` or a tested view. |\n| `CW-GAP-009` | P1 | Organization type enum | `organizations.type` only allows `pro_member/vendor`. | Confirm whether practitioner company accounts fit `pro_member` or need `company/customer/employer` enum value. |"
    },
    {
      "title": "Implementation Sequence",
      "level": 2,
      "body": "1. Fix `people`/`persons` references and decide roster truth before building company workspace.\n2. Extend or formalize `organization_members` roles and active roster read model.\n3. Implement `organization_invitations` and acceptance flow.\n4. Wire org-held memberships and `membership_seats` into entitlement evaluator.\n5. Add `course_assignments` and company academy assignment/progress reporting.\n6. Add `company_job_submissions` or extend `jobs` with review fields, then wire admin review.\n7. Add company audit events and notification events for invites, seat changes, assignments, and job review outcomes.\n8. Add own-company analytics aggregation and export checks.\n9. Build workspace UI surfaces against the hardened data contracts."
    },
    {
      "title": "Acceptance Criteria",
      "level": 2,
      "body": "- A company admin can invite an employee, assign a seat, assign a course, and see resulting access through the entitlement evaluator.\n- Removing an employee or revoking a seat removes workspace/academy access within the documented cache window.\n- Company-created jobs cannot become public without B2BEA admin approval.\n- Practitioner company data has no public profile route or public directory exposure in V1.\n- Company exports include only own-company data and are policy-gated.\n- All employee, seat, academy, job, export, and settings changes create audit events."
    },
    {
      "title": "Next Specs",
      "level": 2,
      "body": "- `survey-system-spec`\n- `notification-spec`\n- `academy-certification-spec`\n- `vendor-portal-workflow-spec`"
    }
  ],
  "html_path": "artifacts/2026-05-06-b2bea-org-company-workspace-data-spec-661f53ce52.html",
  "json_path": "artifacts/2026-05-06-b2bea-org-company-workspace-data-spec-661f53ce52.json"
}