Skip to content

March 2026 prod release for projects-api-v6#77

Open
jmgasper wants to merge 21 commits intomasterfrom
develop
Open

March 2026 prod release for projects-api-v6#77
jmgasper wants to merge 21 commits intomasterfrom
develop

Conversation

@jmgasper
Copy link
Contributor

@jmgasper jmgasper commented Mar 10, 2026

process.env.SUBMISSIONS_API_URL || "https://api.topcoder-dev.com/v5/submissions",
MEMBERS_API_URL: process.env.MEMBERS_API_URL || "https://api.topcoder-dev.com/v6/members",
REVIEW_SUMMATIONS_API_URL: process.env.REVIEW_SUMMATIONS_API_URL || "https://api.topcoder-dev.com/v6/reviewSummations",
REVIEWS_API_URL: process.env.REVIEWS_API_URL || "https://api.topcoder-dev.com",

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The REVIEWS_API_URL is set to https://api.topcoder-dev.com, which is inconsistent with the other API URLs that specify a version (e.g., /v5 or /v6). Consider adding a version to ensure consistency and avoid potential issues with future API changes.

@@ -4,5 +4,6 @@ AUTH0_URL=https://topcoder-dev.auth0.com/oauth/token
AUTH0_AUDIENCE=https://m2m.topcoder-dev.com/
DATABASE_URL="postgresql://topcoder:NUDvFEZzrey2x2og2!zn_kBzcaJ.fu_njAJcD*z2q@topcoder-services.ci8xwsszszsw.us-east-1.rds.amazonaws.com:5432/topcoder-services?schema=challenges"

Choose a reason for hiding this comment

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

[❗❗ security]
The DATABASE_URL contains hardcoded credentials, which is a security risk. Consider using environment variables or a secure vault to manage sensitive information.

'2025-03-10T13:08:02.378Z',
'topcoder user'
)
ON CONFLICT DO NOTHING;

Choose a reason for hiding this comment

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

[⚠️ correctness]
Using ON CONFLICT DO NOTHING can silently ignore conflicts without logging or handling them, which might lead to data inconsistencies or missed updates. Consider specifying a conflict target and handling the conflict appropriately to ensure data integrity.

* @param {Function} logDebugMessage optional logging function
*/
async addAIScreeningPhaseForChallengeCreation(challenge, prisma, logDebugMessage = () => {}) {
if (!challenge || !challenge.phases || !Array.isArray(challenge.reviewers)) {

Choose a reason for hiding this comment

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

[⚠️ maintainability]
The function addAIScreeningPhaseForChallengeCreation mutates the challenge object in-place. Consider returning a new object or using a more functional approach to avoid potential side effects, which can improve maintainability.

);

if (!submissionPhaseName) {
throw new errors.BadRequestError(

Choose a reason for hiding this comment

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

[💡 maintainability]
Throwing an error when no submission phase is found is correct, but consider logging this event before throwing to aid in debugging and tracing issues in production.

([_, phase]) => phase.name === "AI Screening"
);

if (!aiScreeningPhaseDefEntry) {

Choose a reason for hiding this comment

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

[💡 maintainability]
The error message 'AI Screening phase definition not found in the system' could be more informative by including the context or identifier of the challenge to help with debugging.


// Recalculate phase dates to keep timeline in sync
if (submissionPhase.scheduledEndDate) {
aiScreeningPhase.scheduledStartDate = submissionPhase.scheduledEndDate;

Choose a reason for hiding this comment

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

[💡 performance]
The use of moment for date manipulation is fine, but consider using native JavaScript Date methods or a lighter library like date-fns if moment's full functionality is not needed, to reduce bundle size.

@@ -98,9 +100,27 @@ class ChallengePhaseHelper {
// to incorrectly push earlier phases forward. Sorting by template order prevents that.
const orderIndex = new Map();

Choose a reason for hiding this comment

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

[💡 performance]
The variable orderIndex is created using new Map() but is populated using _.each. Consider using native JavaScript methods like forEach for consistency and potentially better performance.

const submissionPhase = submissionPhaseName
? _.find(challengePhases, (phase) => phase.name === submissionPhaseName)
: null;
const submissionOrderIndex = _.isNil(submissionPhase)

Choose a reason for hiding this comment

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

[💡 readability]
The use of _.isNil to check for null or undefined is correct, but consider using submissionPhase === null for clarity since submissionPhase is explicitly set to null when not found.

return updatedPhase;
});

const aiScreeningPhase = _.find(updatedPhases, (phase) => phase.name === "AI Screening");

Choose a reason for hiding this comment

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

[⚠️ correctness]
The logic for updating the predecessor of aiScreeningPhase and review phases assumes that updateSubmissionPhase is not null. Ensure that this logic is covered by tests to prevent potential issues if updateSubmissionPhase is unexpectedly null.


- name: Run Trivy scanner in repo mode
uses: aquasecurity/trivy-action@0.33.1
uses: aquasecurity/trivy-action@latest

Choose a reason for hiding this comment

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

[⚠️ maintainability]
Using @latest for the Trivy action can lead to unexpected behavior if a breaking change is introduced in future versions. Consider specifying a specific version to ensure consistent behavior.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Comment on lines +611 to +638
// Recalculate phase dates to keep timeline in sync
if (submissionPhase.scheduledEndDate) {
aiScreeningPhase.scheduledStartDate = submissionPhase.scheduledEndDate;
aiScreeningPhase.scheduledEndDate = moment(aiScreeningPhase.scheduledStartDate)
.add(aiScreeningPhase.duration, "seconds")
.toDate()
.toISOString();

logDebugMessage(
`AI screening phase dates calculated (start=${aiScreeningPhase.scheduledStartDate}, end=${aiScreeningPhase.scheduledEndDate})`
);

// Update dates for review phases that now depend on AI Screening
reviewPhases.forEach((phase) => {
if (_.isNil(phase.actualStartDate)) {
phase.scheduledStartDate = aiScreeningPhase.scheduledEndDate;
if (phase.duration) {
phase.scheduledEndDate = moment(phase.scheduledStartDate)
.add(phase.duration, "seconds")
.toDate()
.toISOString();

logDebugMessage(
`Updated ${phase.name} phase dates (start=${phase.scheduledStartDate}, end=${phase.scheduledEndDate})`
);
}
}
});

Choose a reason for hiding this comment

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

🔴 Incomplete phase date cascade after AI Screening insertion causes stale downstream dates

When addAIScreeningPhaseForChallengeCreation inserts the AI Screening phase during challenge activation (src/services/ChallengeService.js:2725-2737), it only recalculates dates for the AI Screening phase itself and its immediate successor review phases (lines 624-638). Downstream phases whose predecessor chain passes through a review phase (e.g., Appeals → depends on Review, Appeals Response → depends on Appeals, Post-Mortem, etc.) retain their stale scheduledStartDate/scheduledEndDate values that were computed by populatePhasesForChallengeUpdate before the AI Screening was inserted. This means those downstream phases will have dates that overlap with or precede the shifted review phase, producing an incorrect timeline that is persisted via syncChallengePhases (src/services/ChallengeService.js:3031-3038).

Example of the stale date problem

Before AI Screening insertion (dates from populatePhasesForChallengeUpdate):

  • Submission: ends T+3d
  • Review: starts T+3d, ends T+6d
  • Appeals: starts T+6d, ends T+7d

After AI Screening insertion (only Review shifted):

  • Submission: ends T+3d
  • AI Screening: starts T+3d, ends T+3d+4h
  • Review: starts T+3d+4h, ends T+6d+4h ← updated
  • Appeals: starts T+6d, ends T+7d ← NOT updated, overlaps with Review
Prompt for agents
In src/common/challenge-helper.js, the addAIScreeningPhaseForChallengeCreation method (around lines 611-638) only updates dates for the immediate review phases that were re-linked as successors of AI Screening. It needs to cascade date recalculation through the entire downstream predecessor chain. After updating review phase dates, iterate through ALL remaining phases in challenge.phases and for each phase whose predecessor matches any already-updated phase's phaseId (and whose actualStartDate is nil), set its scheduledStartDate to the predecessor's scheduledEndDate and recompute scheduledEndDate. Continue this cascade until no more phases are updated. Alternatively, refactor to reuse the same predecessor-chain date propagation loop used in populatePhasesForChallengeUpdate (src/common/phase-helper.js lines 203-230) after inserting the AI Screening phase.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +241 to +243
if (Array.isArray(challenge.reviewers) && challenge.reviewers.length > 0) {
return;
}

Choose a reason for hiding this comment

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

🟡 Duplicate early-return guard check in applyDefaultMemberReviewersForChallengeCreation

The exact same guard check if (Array.isArray(challenge.reviewers) && challenge.reviewers.length > 0) { return; } appears twice in succession at lines 237-239 and 241-243. The second check is dead code that can never be reached (if the first check passes, the function already returned). This is harmless but suggests a copy-paste error.

Suggested change
if (Array.isArray(challenge.reviewers) && challenge.reviewers.length > 0) {
return;
}
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

3 participants