You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The codebase tracks project membership and manager status in four separate places simultaneously. ProjectTeamMember was designed as the canonical join table for this relationship, but the app never fully committed to it — resulting in redundant, potentially out-of-sync fields on both User and Project that are used in its place.
Action Items
Research and document the current inconsistency
The four representations of the same two facts ("is a member" / "is a manager"):
Field
Model
Tracks
User.projects[]
User
basic project membership
User.managedProjects[]
User
manager status (denormalized on user)
Project.managedByUsers[]
Project
manager status (denormalized on project)
ProjectTeamMember.vrmsProjectAdmin
ProjectTeamMember
manager status (join table, currently unused)
The updateManagedProjects controller keeps User.managedProjects and Project.managedByUsers in sync with each other, but it never touches ProjectTeamMember.vrmsProjectAdmin. Any code reading vrmsProjectAdmin may see stale data.
Evaluate consolidating onto ProjectTeamMember as the single source of truth
ProjectTeamMember already has everything needed:
ProjectTeamMember {
userId,
projectId,
teamMemberStatus, // Active / Inactive
vrmsProjectAdmin, // Boolean — manager flag (already exists, currently bypassed)
roleOnProject, // Developer, PM, UX, etc.
}
If the app read/wrote through this model exclusively:
"Is user a member?" → ProjectTeamMember.findOne({ userId, projectId })
"Is user a manager?" → ProjectTeamMember.findOne({ userId, projectId, vrmsProjectAdmin: true })
"All of a user's projects with manager status?" → ProjectTeamMember.find({ userId }) — one query returns both facts per row
This would allow removing User.managedProjects, Project.managedByUsers, and User.projects as redundant fields.
If consolidation is approved, the migration would involve:
Backfill — for every entry in User.managedProjects, find or create the corresponding ProjectTeamMember record and set vrmsProjectAdmin: true
Update all write paths — the updateManagedProjects controller writes to ProjectTeamMember.vrmsProjectAdmin instead of the denormalized fields
Update all read paths — anything reading User.managedProjects or Project.managedByUsers queries ProjectTeamMember instead
Drop the redundant fields once all paths are migrated
Note: Issue #2149 (manager toggle feature) requires reading from ProjectTeamMember to know a user's non-manager memberships. That ticket should write to vrmsProjectAdmin to move toward the clean model rather than adding another consumer of the redundant fields.
Overview
The codebase tracks project membership and manager status in four separate places simultaneously.
ProjectTeamMemberwas designed as the canonical join table for this relationship, but the app never fully committed to it — resulting in redundant, potentially out-of-sync fields on bothUserandProjectthat are used in its place.Action Items
Research and document the current inconsistency
The four representations of the same two facts ("is a member" / "is a manager"):
User.projects[]User.managedProjects[]Project.managedByUsers[]ProjectTeamMember.vrmsProjectAdminThe
updateManagedProjectscontroller keepsUser.managedProjectsandProject.managedByUsersin sync with each other, but it never touchesProjectTeamMember.vrmsProjectAdmin. Any code readingvrmsProjectAdminmay see stale data.Evaluate consolidating onto
ProjectTeamMemberas the single source of truthProjectTeamMemberalready has everything needed:If the app read/wrote through this model exclusively:
ProjectTeamMember.findOne({ userId, projectId })ProjectTeamMember.findOne({ userId, projectId, vrmsProjectAdmin: true })ProjectTeamMember.find({ userId })— one query returns both facts per rowThis would allow removing
User.managedProjects,Project.managedByUsers, andUser.projectsas redundant fields.If consolidation is approved, the migration would involve:
User.managedProjects, find or create the correspondingProjectTeamMemberrecord and setvrmsProjectAdmin: trueupdateManagedProjectscontroller writes toProjectTeamMember.vrmsProjectAdmininstead of the denormalized fieldsUser.managedProjectsorProject.managedByUsersqueriesProjectTeamMemberinsteadNote: Issue #2149 (manager toggle feature) requires reading from
ProjectTeamMemberto know a user's non-manager memberships. That ticket should write tovrmsProjectAdminto move toward the clean model rather than adding another consumer of the redundant fields.Resources/Instructions
backend/models/projectTeamMember.model.js— join table schemabackend/models/user.model.js—projectsandmanagedProjectsfieldsbackend/models/project.model.js—managedByUsersfieldbackend/controllers/user.controller.js—updateManagedProjects(the current write path)