Skip to content

[LFXV2-1505] Fix past meeting artifact FGA access to use per-artifact viewer relations#146

Merged
andrest50 merged 6 commits intomainfrom
fix/LFXV2-1505-artifact-access-check-object-v2
Apr 23, 2026
Merged

[LFXV2-1505] Fix past meeting artifact FGA access to use per-artifact viewer relations#146
andrest50 merged 6 commits intomainfrom
fix/LFXV2-1505-artifact-access-check-object-v2

Conversation

@andrest50
Copy link
Copy Markdown
Contributor

@andrest50 andrest50 commented Apr 14, 2026

Summary

  • Moves all artifact FGA update_access logic from per-artifact publishers into PublishPastMeetingEvent, since recording_access, transcript_access, and ai_summary_access live on the past meeting record — not the artifacts.
  • Introduces three new named relations on v1_past_meeting: recording_viewer, transcript_viewer, ai_summary_viewer. Each supports direct user:* assignment for public access and role-based access via self-referential past_meeting_for_*_view tuples for host/attendee/participant access.
  • Updates docs/indexer-contract.md access_check_relation for Recording, Transcript, and Summary to use the new relation names.
  • FGA blocks removed from PublishPastMeetingRecordingEvent, PublishPastMeetingTranscriptEvent, and PublishPastMeetingSummaryEvent — these now only publish the indexer message.

Note: The companion FGA model change (adding the new relations to v1_past_meeting in lfx-v2-helm) is tracked separately. This PR should be merged after that one is deployed, or together if deploying helm and service simultaneously.

Ticket

LFXV2-1505

🤖 Generated with Claude Code

… viewer relations

Move all artifact FGA update_access logic into PublishPastMeetingEvent so that
recording_access, transcript_access, and ai_summary_access (which live on the past
meeting record) drive the FGA tuples rather than being scattered across per-artifact
publishers.

New named relations on v1_past_meeting — recording_viewer, transcript_viewer,
ai_summary_viewer — support both direct user:* assignment (public) and role-based
access via self-referential past_meeting_for_*_view tuples (host/attendee/participant).
The indexer's access_check_relation is updated to match for each artifact type.

FGA blocks removed from PublishPastMeetingRecordingEvent,
PublishPastMeetingTranscriptEvent, and PublishPastMeetingSummaryEvent; they now
only publish the indexer message.

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Andres Tobon <[email protected]>
Copilot AI review requested due to automatic review settings April 14, 2026 20:35
@andrest50 andrest50 requested a review from a team as a code owner April 14, 2026 20:35
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 14, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Consolidates FGA access publishing for past-meeting artifacts: a single publisher now emits per-artifact relations (recording_viewer, transcript_viewer, ai_summary_viewer) tied to a meeting UID; artifact-specific publishers now only emit indexer messages; docs and rulesets updated to match new relations.

Changes

Cohort / File(s) Summary
Documentation - Access Control Configuration
docs/indexer-contract.md
Updated IndexingConfig.access_check_relation for three resources: v1_past_meeting_recording (viewerrecording_viewer), v1_past_meeting_transcript (viewertranscript_viewer), and v1_past_meeting_summary (viewerai_summary_viewer). Removed parent-past-meeting access-check comments and adjusted public derivation text for summary.
Event Publishing - FGA Relation Consolidation
internal/infrastructure/eventing/nats_publisher.go
PublishPastMeetingEvent now requires MeetingAndOccurrenceID, builds/publishes a single FGA update_access with UID set to that ID, includes conditional per-artifact relations and meeting-scoped reference tuples, and sets ExcludeRelations for host/invitee/attendee. Per-artifact publishers (PublishPastMeetingRecordingEvent, PublishPastMeetingTranscriptEvent, PublishPastMeetingSummaryEvent) no longer publish FGA messages and now return after indexer publication; indexer relations updated to the new relation names.
Authorization Rules
charts/lfx-v2-meeting-service/templates/ruleset.yaml
Updated OpenFGA checks for summary rules: changed checked relation from viewerai_summary_viewer and changed the checked object to v1_past_meeting:{{ .Request.URL.Captures.past_meeting_id }}.

Sequence Diagram(s)

sequenceDiagram
    participant Publisher as Publisher\n(PublishPastMeetingEvent)
    participant FGA as FGA Service
    participant Indexer as Indexer Queue
    participant ArtifactPub as Artifact Publishers\n(Recording/Transcript/Summary)
    Publisher->>FGA: Publish FGA update_access\nUID = MeetingAndOccurrenceID\nRelations: recording_viewer, transcript_viewer, ai_summary_viewer\nReference tuples: v1_past_meeting:<MeetingAndOccurrenceID>\nExcludeRelations: host,invitee,attendee
    FGA-->>Publisher: Ack
    Publisher->>Indexer: Publish indexer messages for artifacts\n(updated AccessCheckRelation per artifact)
    ArtifactPub->>Indexer: Publish indexer-only messages\n(no FGA call)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and specifically describes the main change: fixing past meeting artifact FGA access by using per-artifact viewer relations.
Description check ✅ Passed The description clearly explains the purpose of the changes, including moving FGA logic, introducing new relations, and updating documentation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/LFXV2-1505-artifact-access-check-object-v2

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/infrastructure/eventing/nats_publisher.go (1)

436-454: 🛠️ Refactor suggestion | 🟠 Major

Access-only updates still leave the artifact Public bit stale in the indexer.

The FGA tuples now move with PublishPastMeetingEvent, but IndexingConfig.Public for recordings/transcripts/summaries is still only recomputed when each artifact is republished. After a parent past-meeting access change, authorization and search visibility can diverge until every artifact document is reindexed.

Also applies to: 472-489, 508-525

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/infrastructure/eventing/nats_publisher.go` around lines 436 - 454,
The indexer "Public" bit for child artifacts (recordings/transcripts/summaries)
isn't updated when a parent past-meeting access change is handled by
PublishPastMeetingEvent, so search/authorization can diverge; update
PublishPastMeetingEvent to recompute IndexingConfig.Public for all child
artifacts and emit indexer updates (use p.publish) for
IndexV1PastMeetingRecordingSubject, IndexV1PastMeetingTranscriptSubject and
IndexV1PastMeetingSummarySubject (or the same indexer message path used
elsewhere) with an indexerMsg that sets the corrected IndexingConfig.Public
value (keeping existing identifiers like recording.MeetingAndOccurrenceID,
ParentRefs(), SortName(), NameAndAliases(), Fulltext() as needed) so the
artifact documents’ Public flag is kept in sync without requiring full
republish.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/infrastructure/eventing/nats_publisher.go`:
- Around line 330-338: The past-meeting update_access message in
nats_publisher.go (constructed as pastMeetingAccessMsg with GenericAccessData
and pastMeetingRelations) currently includes host/invitee/attendee tuples and
can overwrite memberships managed separately by
PublishPastMeetingParticipantEvent/member_put; modify the code that builds or
assigns pastMeetingRelations (used in pastMeetingAccessMsg) to filter out
relation types "host", "invitee", and "attendee" (i.e., remove any relation
entries where Relation == "host" || "invitee" || "attendee") so the
update_access publish does not wipe those membership tuples while leaving
pastMeetingRefs unchanged.
- Around line 293-337: The code uses meeting.ID for the FGA UID and selfRef but
the rest of the system expects the artifact tuples on the
meeting_and_occurrence_id; update all usages of meeting.ID in this block to the
meeting.MeetingAndOccurrenceID field (use the exact struct field name
MeetingAndOccurrenceID) so UID in GenericAccessData and selfRef
("v1_past_meeting:"+meeting.MeetingAndOccurrenceID) point to the same
v1_past_meeting object where PublishPastMeetingParticipantEvent stores
host/attendee/invitee tuples; ensure you update pastMeetingAccessMsg.Data.UID
and every pastMeetingRefs assignment to reference the new selfRef value.

---

Outside diff comments:
In `@internal/infrastructure/eventing/nats_publisher.go`:
- Around line 436-454: The indexer "Public" bit for child artifacts
(recordings/transcripts/summaries) isn't updated when a parent past-meeting
access change is handled by PublishPastMeetingEvent, so search/authorization can
diverge; update PublishPastMeetingEvent to recompute IndexingConfig.Public for
all child artifacts and emit indexer updates (use p.publish) for
IndexV1PastMeetingRecordingSubject, IndexV1PastMeetingTranscriptSubject and
IndexV1PastMeetingSummarySubject (or the same indexer message path used
elsewhere) with an indexerMsg that sets the corrected IndexingConfig.Public
value (keeping existing identifiers like recording.MeetingAndOccurrenceID,
ParentRefs(), SortName(), NameAndAliases(), Fulltext() as needed) so the
artifact documents’ Public flag is kept in sync without requiring full
republish.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8d1b8b2-5352-4f1a-926a-2fbf5794a91a

📥 Commits

Reviewing files that changed from the base of the PR and between c9146d9 and 3f9a12c.

📒 Files selected for processing (2)
  • docs/indexer-contract.md
  • internal/infrastructure/eventing/nats_publisher.go

Comment thread internal/infrastructure/eventing/nats_publisher.go
Comment thread internal/infrastructure/eventing/nats_publisher.go
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates authorization for past meeting artifacts to use per-artifact viewer relations on the v1_past_meeting object, and centralizes FGA access tuple publishing into the past meeting publisher so access stays in sync when the past meeting record changes.

Changes:

  • Add per-artifact viewer tuple publishing (recording_viewer, transcript_viewer, ai_summary_viewer) to PublishPastMeetingEvent.
  • Update indexer access_check_relation for recording/transcript/summary artifacts to use the new past-meeting relations.
  • Remove per-artifact FGA update_access publishing from recording/transcript/summary publishers (they now only publish indexer messages).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
internal/infrastructure/eventing/nats_publisher.go Centralizes past-meeting artifact access tuple publishing into the past meeting publisher and updates artifact indexer access-check relations.
docs/indexer-contract.md Updates the indexer contract to reflect the new access-check relations for past meeting artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/infrastructure/eventing/nats_publisher.go
Comment thread internal/infrastructure/eventing/nats_publisher.go
Comment thread docs/indexer-contract.md
…uples

- Use MeetingAndOccurrenceID (not ID) as the FGA object UID and selfRef, so
  artifact-view tuples land on the same v1_past_meeting object where
  PublishPastMeetingParticipantEvent stores host/attendee/invitee memberships.
- Add ExcludeRelations: ["host", "invitee", "attendee"] to the update_access
  message so past-meeting republish does not wipe participant tuples managed
  separately by PublishPastMeetingParticipantEvent.

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Andres Tobon <[email protected]>
@andrest50
Copy link
Copy Markdown
Contributor Author

Addressed the two inline comments in the follow-up commit (143f018):

  1. FGA UID — changed selfRef and Data.UID to use meeting.MeetingAndOccurrenceID so the artifact-view tuples land on the same object as the participant tuples.
  2. ExcludeRelations — added ["host", "invitee", "attendee"] to protect memberships managed by PublishPastMeetingParticipantEvent.

Re: indexer Public bit divergence — acknowledged as a real gap. PublishPastMeetingEvent only receives PastMeetingEventData, which does not carry the artifact objects (recording/transcript/summary) needed to re-emit full indexer messages. Fixing this properly would require either passing artifact data through to this function or a separate reindex trigger — tracked as follow-up work and out of scope for this immediate fix.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
internal/infrastructure/eventing/nats_publisher.go (1)

293-296: ⚠️ Potential issue | 🔴 Critical

Missing validation for MeetingAndOccurrenceID will create malformed FGA tuples.

PublishPastMeetingParticipantEvent validates MeetingAndOccurrenceID (lines 353-355) before using it, but this function uses it directly at line 295 without validation. If empty, selfRef becomes "v1_past_meeting:" and the FGA message at line 334 will have an empty UID.

🐛 Proposed fix to add validation
 	// Per-artifact access: self-referential references enable role-based access
 	// via the existing host/attendee/invitee tuples on the same v1_past_meeting object.
+	if meeting.MeetingAndOccurrenceID == "" {
+		return domain.NewValidationError("meeting_and_occurrence_id is required for past meeting FGA access update")
+	}
 	selfRef := "v1_past_meeting:" + meeting.MeetingAndOccurrenceID

As per coding guidelines, "internal/**/*.go: Use domain-specific error types defined in internal/domain/errors.go for error handling throughout the application".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/infrastructure/eventing/nats_publisher.go` around lines 293 - 296,
The code constructs selfRef := "v1_past_meeting:"+meeting.MeetingAndOccurrenceID
without validating MeetingAndOccurrenceID, which can produce an empty UID; add
an explicit non-empty check for meeting.MeetingAndOccurrenceID at the top of the
function that builds selfRef (same validation logic as
PublishPastMeetingParticipantEvent) and return the appropriate domain-specific
validation error from internal/domain/errors.go when it's empty; ensure the
check runs before forming selfRef and before any FGA tuple/message construction
(references: selfRef and PublishPastMeetingParticipantEvent).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/infrastructure/eventing/nats_publisher.go`:
- Around line 293-296: The code constructs selfRef :=
"v1_past_meeting:"+meeting.MeetingAndOccurrenceID without validating
MeetingAndOccurrenceID, which can produce an empty UID; add an explicit
non-empty check for meeting.MeetingAndOccurrenceID at the top of the function
that builds selfRef (same validation logic as
PublishPastMeetingParticipantEvent) and return the appropriate domain-specific
validation error from internal/domain/errors.go when it's empty; ensure the
check runs before forming selfRef and before any FGA tuple/message construction
(references: selfRef and PublishPastMeetingParticipantEvent).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 007eb0ad-1e23-4bab-a19c-8fdc62a8d540

📥 Commits

Reviewing files that changed from the base of the PR and between 3f9a12c and 143f018.

📒 Files selected for processing (1)
  • internal/infrastructure/eventing/nats_publisher.go

…_meeting

The summary GET rule was checking viewer on v1_past_meeting_summary, which no
longer has access tuples written to it since FGA was consolidated onto
v1_past_meeting in this PR.

- GET: relation: ai_summary_viewer, object: v1_past_meeting:{past_meeting_id}
- PUT: relation: organizer, object: v1_past_meeting:{past_meeting_id}
  (organizer already delegates correctly from v1_past_meeting)

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Andres Tobon <[email protected]>
… relations

fga-sync prepends "user:" to every principal in the Relations map, so
sending "user:*" produced "user:user:*" which is invalid. Changed all
three public-access artifact relations (recording_viewer, transcript_viewer,
ai_summary_viewer) to send "*" instead, yielding the correct "user:*" tuple.

Generated with [Claude Code](https://claude.com/claude-code)

Signed-off-by: Andres Tobon <[email protected]>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/infrastructure/eventing/nats_publisher.go (1)

293-340: ⚠️ Potential issue | 🟠 Major

Fail fast when meeting_and_occurrence_id is missing.

Like PublishPastMeetingParticipantEvent, Line 295 and Line 334 now rely on meeting.MeetingAndOccurrenceID. If it is empty, this publishes an empty UID and v1_past_meeting: self-references after the indexer event may already have been sent.

🛡️ Suggested fix
 func (p *NATSPublisher) PublishPastMeetingEvent(ctx context.Context, action string, meeting *models.PastMeetingEventData) error {
+	if meeting.MeetingAndOccurrenceID == "" {
+		return domain.NewValidationError("meeting_and_occurrence_id is required for past meeting access update")
+	}
+
 	p.logger.InfoContext(ctx, "publishing past meeting event", "action", action, "past_meeting_id", meeting.ID)

As per coding guidelines, "internal/**/*.go: Use domain-specific error types defined in internal/domain/errors.go for error handling throughout the application"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/infrastructure/eventing/nats_publisher.go` around lines 293 - 340,
Check for an empty meeting.MeetingAndOccurrenceID early and fail fast before
building fgatypes.GenericFGAMessage or creating selfRef (the same guard used in
PublishPastMeetingParticipantEvent); if empty, return a domain-specific error
from internal/domain/errors.go (e.g., ErrMissingMeetingAndOccurrenceID) instead
of publishing an empty UID/self-reference so the indexer event never gets sent
with invalid data. Ensure the check is placed before any use of
meeting.MeetingAndOccurrenceID and that the function returns the appropriate
domain error type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/infrastructure/eventing/nats_publisher.go`:
- Around line 272-274: The indexer currently leaves artifact documents' Public
boolean stale when parent past-meeting access flags change, which can cause
visibility bugs if search queries trust Public; update the behavior in
PublishPastMeetingEvent (and related nats_publisher logic) to either a) ensure
downstream search does not treat artifact.Public as authoritative by forcing
queries to evaluate AccessCheckObject against FGA (confirm and document this
contract where search code reads artifacts), or b) implement a republish path
inside PublishPastMeetingEvent that enqueues or triggers the existing artifact
publisher(s) to re-index artifacts (recording/transcript/ai_summary) when
recording_access/transcript_access/ai_summary_access change; locate references
to Public on artifact documents, AccessCheckObject checks, and the artifact
publisher functions to wire the chosen fix.

---

Duplicate comments:
In `@internal/infrastructure/eventing/nats_publisher.go`:
- Around line 293-340: Check for an empty meeting.MeetingAndOccurrenceID early
and fail fast before building fgatypes.GenericFGAMessage or creating selfRef
(the same guard used in PublishPastMeetingParticipantEvent); if empty, return a
domain-specific error from internal/domain/errors.go (e.g.,
ErrMissingMeetingAndOccurrenceID) instead of publishing an empty
UID/self-reference so the indexer event never gets sent with invalid data.
Ensure the check is placed before any use of meeting.MeetingAndOccurrenceID and
that the function returns the appropriate domain error type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d648b0d5-c291-406a-a5b7-b408900b3b9e

📥 Commits

Reviewing files that changed from the base of the PR and between a901880 and b7930c3.

📒 Files selected for processing (1)
  • internal/infrastructure/eventing/nats_publisher.go

Comment thread internal/infrastructure/eventing/nats_publisher.go

// Per-artifact access: self-referential references enable role-based access
// via the existing host/attendee/invitee tuples on the same v1_past_meeting object.
selfRef := "v1_past_meeting:" + meeting.MeetingAndOccurrenceID
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I see meeting.MeetingAndOccurrenceID is used multiple times, also for the access. Can we add a validation

if meeting.MeetingAndOccurrenceID == "" {
    return domain.NewValidationError("meeting_and_occurrence_id is required for past meeting FGA access update")
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's a good point. And actually when we send the indexer message using meeting.ID, that is the same value as the meeting.MeetingAndOccurrenceID, so I switched to using meeting.MeetingAndOccurrenceID in all places in the code and moved the validation to the top of the function. There aren't any cases where this validation has failed currently since all meetings have an ID, but it is still a good safety check.

prabodhcs
prabodhcs previously approved these changes Apr 15, 2026
…D explicitly

Use meeting.MeetingAndOccurrenceID instead of meeting.ID in log and
IndexingConfig fields. Also add a validation guard so publishing fails
early with a clear error when MeetingAndOccurrenceID is empty.

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Andres Tobon <[email protected]>
@andrest50 andrest50 force-pushed the fix/LFXV2-1505-artifact-access-check-object-v2 branch from 8aa79f8 to 8f12040 Compare April 15, 2026 16:18
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
internal/infrastructure/eventing/nats_publisher.go (2)

243-247: Move the info log below the validation guard.

If MeetingAndOccurrenceID is empty, this still emits a "publishing past meeting event" log even though the function returns immediately. Logging after the guard keeps the audit trail aligned with actual publish attempts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/infrastructure/eventing/nats_publisher.go` around lines 243 - 247,
The info log is emitted before the validation guard, causing "publishing past
meeting event" to be logged even when MeetingAndOccurrenceID is empty; move the
p.logger.InfoContext(ctx, "publishing past meeting event", "action", action,
"past_meeting_id", meeting.MeetingAndOccurrenceID) call so it occurs after the
if check that returns domain.NewValidationError("meeting_and_occurrence_id is
required for publishing messages about the past meeting"), ensuring the log only
runs when meeting.MeetingAndOccurrenceID is present and a publish is actually
attempted.

301-332: Consider extracting the per-artifact access mapping into a helper.

These three switch blocks are structurally identical, so the next relation rename or access-policy tweak will have to stay in sync in three places. A small helper here would reduce drift risk.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/infrastructure/eventing/nats_publisher.go` around lines 301 - 332,
The three identical switch blocks handling meeting.RecordingAccess,
meeting.TranscriptAccess, and meeting.AISummaryAccess should be extracted into a
helper to avoid duplication and drift; create a function (e.g.,
applyArtifactAccess(access string, artifactPrefix string, pastMeetingRelations
map[string][]string, pastMeetingRefs map[string][]string, selfRef string)) that
receives the access value and an artifact identifier ("recording", "transcript",
"ai_summary") and encapsulates the switch logic to set either
pastMeetingRelations[artifact+"_viewer"] = {"*"} for "public" or the three/one
pastMeetingRefs keys (e.g., "past_meeting_for_host_<artifact>_view",
"past_meeting_for_attendee_<artifact>_view",
"past_meeting_for_participant_<artifact>_view" or default host-only) using
selfRef; replace the three switch blocks with three calls to
applyArtifactAccess(meeting.RecordingAccess, "recording", ...),
applyArtifactAccess(meeting.TranscriptAccess, "transcript", ...), and
applyArtifactAccess(meeting.AISummaryAccess, "ai_summary", ...).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/infrastructure/eventing/nats_publisher.go`:
- Around line 243-247: The info log is emitted before the validation guard,
causing "publishing past meeting event" to be logged even when
MeetingAndOccurrenceID is empty; move the p.logger.InfoContext(ctx, "publishing
past meeting event", "action", action, "past_meeting_id",
meeting.MeetingAndOccurrenceID) call so it occurs after the if check that
returns domain.NewValidationError("meeting_and_occurrence_id is required for
publishing messages about the past meeting"), ensuring the log only runs when
meeting.MeetingAndOccurrenceID is present and a publish is actually attempted.
- Around line 301-332: The three identical switch blocks handling
meeting.RecordingAccess, meeting.TranscriptAccess, and meeting.AISummaryAccess
should be extracted into a helper to avoid duplication and drift; create a
function (e.g., applyArtifactAccess(access string, artifactPrefix string,
pastMeetingRelations map[string][]string, pastMeetingRefs map[string][]string,
selfRef string)) that receives the access value and an artifact identifier
("recording", "transcript", "ai_summary") and encapsulates the switch logic to
set either pastMeetingRelations[artifact+"_viewer"] = {"*"} for "public" or the
three/one pastMeetingRefs keys (e.g., "past_meeting_for_host_<artifact>_view",
"past_meeting_for_attendee_<artifact>_view",
"past_meeting_for_participant_<artifact>_view" or default host-only) using
selfRef; replace the three switch blocks with three calls to
applyArtifactAccess(meeting.RecordingAccess, "recording", ...),
applyArtifactAccess(meeting.TranscriptAccess, "transcript", ...), and
applyArtifactAccess(meeting.AISummaryAccess, "ai_summary", ...).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0351ada9-2205-48d0-b457-ba15bfa3f288

📥 Commits

Reviewing files that changed from the base of the PR and between b7930c3 and 8aa79f8.

📒 Files selected for processing (1)
  • internal/infrastructure/eventing/nats_publisher.go

Copilot AI review requested due to automatic review settings April 23, 2026 20:37
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/infrastructure/eventing/nats_publisher.go
Comment thread internal/infrastructure/eventing/nats_publisher.go
Comment thread docs/indexer-contract.md
@andrest50 andrest50 merged commit 0faeede into main Apr 23, 2026
14 checks passed
@andrest50 andrest50 deleted the fix/LFXV2-1505-artifact-access-check-object-v2 branch April 23, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants