Skip to content

feat(people): access tab on member details, per connected integration#3349

Merged
tofikwest merged 7 commits into
mainfrom
tofik/member-access-tab
Jul 6, 2026
Merged

feat(people): access tab on member details, per connected integration#3349
tofikwest merged 7 commits into
mainfrom
tofik/member-access-tab

Conversation

@tofikwest

@tofikwest tofikwest commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

What

Adds an Access section to the member detail page: for every connected integration whose check is bound to the Employee Access evidence task, shows what this person can access — role, account status, and the raw evidence record — matched deterministically by email.

  • GET /v1/people/:id/access (PeopleAccessService) — consumer of the universal CheckResultsService.
  • EmployeeAccess card on the member page with per-integration rows, expandable to field lists + raw record.
  • Four source states: Access found, No match for this member, No per-person data (the vendor API has no roster), Check not run yet.

How matching works

Person-scoped checks emit one result row per person (resourceType: 'user', resourceId = lowercased email) — the standard shape all employee-access checks now follow. A member's access is exactly the rows whose resourceId equals their email. No evidence parsing, no heuristics.

History: an earlier revision of this PR included an EvidenceExtractionService (deterministic shape ladder + AI fallback) to cope with inconsistent check outputs. The checks themselves have since been normalized to the per-person standard, so that layer was removed — the current diff is the simple deterministic read.

Tests

  • people-access.service.spec — email matching, entry building, all four source states, unconnected-source skip, no-email member.
  • EmployeeAccess.test.tsx — list rendering, state badges, expansion with fields + raw record, empty state.
  • API: people module 89 tests pass. App: 4/4 pass. Typecheck: no new errors (the 8 pre-existing spec-file offenders on main are unchanged).

🤖 Generated with Claude Code

https://claude.ai/code/session_013WLGUUjG6uLd6xvNhUpge1

@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
comp-framework-editor Ready Ready Preview, Comment Jul 6, 2026 2:59am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
app Skipped Skipped Jul 6, 2026 2:59am
portal Skipped Skipped Jul 6, 2026 2:59am

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 13 files

Confidence score: 3/5

  • In apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeAccess.tsx, MATCH_BADGE[source.matchType] has no fallback for unexpected API values, so a new/unknown matchType can produce undefined and break rendering in the employee access view — add a defensive default badge (or guard) and a test for unknown match types before merging.
  • In apps/api/src/integration-platform/services/evidence-extraction.service.ts (evidenceMentionsEmail / buildAiExcerpt), raw includes(email) matching can misidentify mentions when emails overlap as substrings, leading to incorrect evidence excerpts and attribution — switch to boundary-aware/normalized email matching and cover overlapping-address cases with tests.
  • In apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeAccess.tsx, using key={i} for access entries risks React reusing the wrong row after reorder/update, which can show stale or mismatched access details — use a stable unique key from the entry data before merging; the parts.join('\n') loop cost in apps/api/src/integration-platform/services/evidence-extraction.service.ts looks lower-risk and can be a follow-up optimization.

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/api/src/integration-platform/services/evidence-extraction.service.ts Outdated
Comment thread apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeAccess.tsx Outdated
Comment thread apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeAccess.tsx Outdated
Comment thread apps/api/src/integration-platform/services/evidence-extraction.service.ts Outdated
tofikwest and others added 3 commits July 5, 2026 22:20
New tab on the member page aggregating the latest Employee Access check
results from every connected integration bound to the Employee Access
evidence task (CheckResultsService consumer #2). Rows show logo, name,
access summary, and an honest match state (found / no match for this
member / check not run); expanding reveals curated fields plus the raw
record. Extractor tolerates both check shapes: per-user rows and
org-level rosters embedded in evidence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nce extraction

Shared EvidenceExtractionService: deterministic shape matching, then an
email pre-gate (absent email = confident not-found, no AI call), then a
claude-haiku-4-5 structured-output fallback for unknown evidence shapes.
Access tab consumes it; AI-extracted entries are labeled in the UI and
unreadable shapes surface as 'Needs manual review' instead of a blank.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ctly — drop AI extraction

Person-scoped checks now emit one row per person (resourceType 'user',
resourceId = lowercased email), so matching a member's access is a plain
filter. Removes EvidenceExtractionService (deterministic ladder + AI
fallback), its spec, module wiring, and the AI badge / unparsed state in
the UI. New 'no-person-data' state distinguishes sources whose vendor
API has no roster.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WLGUUjG6uLd6xvNhUpge1
@tofikwest tofikwest force-pushed the tofik/member-access-tab branch from af7d03c to 4701387 Compare July 6, 2026 02:24
@vercel vercel Bot temporarily deployed to Preview – portal July 6, 2026 02:24 Inactive
@tofikwest tofikwest changed the title feat(people): member access tab with universal evidence extraction feat(people): access tab on member details, per connected integration Jul 6, 2026
Fallback badge for unknown matchType values (the union is compile-time
only; deserialized JSON can carry states this build doesn't know), and
content-derived entry keys so uncontrolled <details> elements follow
their entry through reorders instead of the array index.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WLGUUjG6uLd6xvNhUpge1
@tofikwest

Copy link
Copy Markdown
Contributor Author

Triage of the 4 findings:

  • MATCH_BADGE fallback — real; fixed. Unknown matchType values now fall back to the neutral 'Check not run yet' badge instead of crashing (?? MATCH_BADGE['no-data']), with a test covering an unrecognized value.
  • key={i} on entries — real; fixed. Entries now use a content-derived key (summary + fields), so the uncontrolled <details> open state follows the entry through reorders.
  • evidenceMentionsEmail substring matching and parts.join cost — obsolete: evidence-extraction.service.ts was removed entirely in the latest revision (matching is now a plain equality filter on resourceType: 'user' rows).

@vercel vercel Bot temporarily deployed to Preview – portal July 6, 2026 02:36 Inactive
@tofikwest

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review it

@cubic-dev-ai

cubic-dev-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review it

@tofikwest I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 8 files

Confidence score: 4/5

  • In apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeAccess.tsx, the row key built from ${entry.summary}::${JSON.stringify(entry.fields)} can collide for distinct results that differ only in nested raw evidence, which may cause React to reuse the wrong row and show stale/mismatched access details—switch to a truly unique key (for example a stable result ID or hash that includes nested evidence) before merging.

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread apps/app/src/app/(app)/[orgId]/people/[employeeId]/components/EmployeeAccess.tsx Outdated
@tofikwest

Copy link
Copy Markdown
Contributor Author

Fixed with the cleaner variant of your first suggestion: rather than growing the composite key (serializing entry.raw on every render), the API now returns a stable unique id per entry (runId + row position), and the row key is entry.id. Unique by construction — no collision surface left. Tests updated on both sides.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread apps/api/src/people/people-access.service.ts Outdated
Expose the IntegrationCheckResult id through the CheckResultRow envelope
and use it as the entry id — unique and stable by construction, no
positional derivation. Also order a run's result rows deterministically
in the repository (Postgres guarantees no order without one).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013WLGUUjG6uLd6xvNhUpge1
@tofikwest

Copy link
Copy Markdown
Contributor Author

Fixed properly this time — took your "expose a true database row id" option: CheckResultRow now carries resultId (the IntegrationCheckResult DB id), and the entry id is that value directly. Unique and stable by construction, no positional or content derivation at all. Also added a deterministic orderBy to the run-results query — you were right that without one Postgres doesn't guarantee row order between fetches of the same run. Note the composite suggested as an alternative (runId+resourceId+title+collectedAt) could still collide for one member's rows within a run, which is why the DB id was the right call.

@tofikwest

Copy link
Copy Markdown
Contributor Author

@cubic-dev-ai review it

@cubic-dev-ai

cubic-dev-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

@cubic-dev-ai review it

@tofikwest I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 10 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Re-trigger cubic

@tofikwest tofikwest merged commit 83f188c into main Jul 6, 2026
10 checks passed
@tofikwest tofikwest deleted the tofik/member-access-tab branch July 6, 2026 03:15
claudfuen pushed a commit that referenced this pull request Jul 6, 2026
# [3.97.0](v3.96.1...v3.97.0) (2026-07-06)

### Bug Fixes

* **people:** polish the redesigned People tab (requirement columns, filters, per-employee 2FA) ([#3347](#3347)) ([75758ec](75758ec))

### Features

* **integrations:** emit per-user rows from the Google Workspace employee-access check ([#3352](#3352)) ([0b54252](0b54252))
* **people:** access tab on member details, per connected integration ([#3349](#3349)) ([83f188c](83f188c)), closes [#2](https://github.com/trycompai/comp/issues/2)
@claudfuen

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.97.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants