Skip to content

Add List 'submittedAt' field#317

Merged
ianpaschal merged 2 commits intomainfrom
add-list-submitted-at-field
Apr 12, 2026
Merged

Add List 'submittedAt' field#317
ianpaschal merged 2 commits intomainfrom
add-list-submitted-at-field

Conversation

@ianpaschal
Copy link
Copy Markdown
Owner

@ianpaschal ianpaschal commented Apr 11, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Lists now track their submission timestamp for more accurate tracking of when submissions occur.
  • Bug Fixes

    • Improved submission timing logic to use recorded submission data when available, with fallback to document creation time.
  • Refactor

    • Simplified internal code for computing list approval status.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 11, 2026

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

Project Deployment Actions Updated (UTC)
combat-command Ready Ready Preview, Comment Apr 12, 2026 0:23am

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d715a72c-c285-4dfe-af86-b9f26b9a711b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This pull request introduces a submittedAt timestamp field to the lists domain. The schema is extended with an optional submittedAt numeric field. New lists are created with submittedAt initialized to the current time. A migration backfills the submittedAt field for existing documents, defaulting to their creation time. The submission-timing check logic is updated to use submittedAt when available, falling back to creation time otherwise. Additionally, a minor refactor simplifies the approval status computation logic.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is missing entirely, but the required template only specifies a 'Resolves' section that should reference an issue number. Add a pull request description following the template, including at least 'Resolves: #317' to reference the related issue.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a new 'submittedAt' field to lists, which is reflected across all modified files.
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 add-list-submitted-at-field

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@convex/_model/lists/mutations/createList.ts`:
- Around line 17-25: The code calls Date.now() twice when setting
computedFields.submittedAt and later computing computedFields.locked, which can
create inconsistent state; fix by capturing a single const now = Date.now() at
the start of createList (or just before computedFields) and use that same now
when assigning computedFields.submittedAt and when evaluating Date.now() >
tournament.listSubmissionClosesAt (replace that call with now) — update
references in the createList mutation around computedFields, submittedAt, locked
and the tournament/tournamentRegistration logic that uses getDocStrict.

In `@convex/_model/lists/table.ts`:
- Line 14: submittedAt is currently declared in editableFields allowing it into
createListArgs and updateListArgs and thus clients can modify it via the
updateList mutation that applies ...updated; move submittedAt from
editableFields to computedFields in the model (reference symbol submittedAt) so
it is excluded from client-provided args, update any code that builds
createListArgs/updateListArgs to stop expecting submittedAt (references:
createList.ts where submittedAt is set, checkListSubmittedOnTime.ts which reads
it), and ensure updateList mutation no longer applies unfiltered ...updated to
the DB (or filters out submittedAt) to prevent client override.
🪄 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: ASSERTIVE

Plan: Pro

Run ID: 1545ad02-6d5d-4c6a-b441-087b0c05f37e

📥 Commits

Reviewing files that changed from the base of the PR and between 46cbf38 and b9e4f50.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • convex/_model/lists/_helpers/checkListSubmittedOnTime.ts
  • convex/_model/lists/mutations/createList.ts
  • convex/_model/lists/table.ts
  • convex/migrations.ts
  • src/components/TournamentProvider/actions/useToggleListsRevealedAction.tsx

Comment on lines +17 to +25
const computedFields = {
locked: false,
submittedAt: Date.now(),
};

if (args.tournamentRegistrationId) {
const tournamentRegistration = await getDocStrict(ctx, args.tournamentRegistrationId);
const tournament = await getDocStrict(ctx, tournamentRegistration.tournamentId);
computedFields = {
locked: Date.now() > tournament.listSubmissionClosesAt,
};
computedFields.locked = Date.now() > tournament.listSubmissionClosesAt;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use one timestamp for both submittedAt and lock evaluation.

Lines 19 and 25 call Date.now() separately. Around close-time boundaries (especially with async fetch latency), this can create contradictory state (submittedAt before deadline but locked true, or vice versa). Capture now once and reuse it.

Proposed fix
-  const computedFields = {
-    locked: false,
-    submittedAt: Date.now(),
-  };
+  const now = Date.now();
+  const computedFields = {
+    locked: false,
+    submittedAt: now,
+  };

   if (args.tournamentRegistrationId) {
     const tournamentRegistration = await getDocStrict(ctx, args.tournamentRegistrationId);
     const tournament = await getDocStrict(ctx, tournamentRegistration.tournamentId);
-    computedFields.locked = Date.now() > tournament.listSubmissionClosesAt;
+    computedFields.locked = now > tournament.listSubmissionClosesAt;
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/_model/lists/mutations/createList.ts` around lines 17 - 25, The code
calls Date.now() twice when setting computedFields.submittedAt and later
computing computedFields.locked, which can create inconsistent state; fix by
capturing a single const now = Date.now() at the start of createList (or just
before computedFields) and use that same now when assigning
computedFields.submittedAt and when evaluating Date.now() >
tournament.listSubmissionClosesAt (replace that call with now) — update
references in the createList mutation around computedFields, submittedAt, locked
and the tournament/tournamentRegistration logic that uses getDocStrict.

Comment thread convex/_model/lists/table.ts Outdated
storageId: v.id('_storage'),
userId: v.id('users'),
tournamentRegistrationId: v.optional(v.id('tournamentRegistrations')),
submittedAt: v.optional(v.number()),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find all places where lists editableFields are reused for args validation.
rg -n --type=ts -C3 '\b(v\.object\(editableFields\)|v\.partial\(editableFields\)|editableFields)\b' convex/_model/lists convex

# 2) Inspect list mutations that patch/replace list docs to confirm whether args flow directly into writes.
rg -n --type=ts -C4 'ctx\.db\.(patch|replace)\(' convex/_model/lists

Repository: ianpaschal/combat-command

Length of output: 37898


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find all references to submittedAt in the codebase to understand its intent
rg -n --type=ts 'submittedAt' convex/_model/lists

# 2) Check if there's a migration file or initial creation logic setting submittedAt
fd -t f 'migration|Migration' convex/_model/lists && rg -n --type=ts 'submittedAt' convex || true

# 3) Look at the getAvailableActions helper for lists to understand permission model
cat -n convex/_model/lists/_helpers/getAvailableActions.ts 2>/dev/null || echo "File not found"

Repository: ianpaschal/combat-command

Length of output: 3581


Move submittedAt from editableFields to computedFields to prevent client modification.

submittedAt is server-managed (set only during creation in createList.ts:19) and used for deadline checks in checkListSubmittedOnTime.ts. However, because it's in editableFields, it flows into both createListArgs and updateListArgs. The updateList mutation then patches the document directly with ...updated without filtering, allowing clients to modify the submission timestamp.

Proposed refactor
 export const editableFields = {
   gameSystem,
   storageId: v.id('_storage'),
   userId: v.id('users'),
   tournamentRegistrationId: v.optional(v.id('tournamentRegistrations')),
-  submittedAt: v.optional(v.number()),
   
   // FUTURE:
   // data: listData,
 };

 export const computedFields = {
   modifiedAt: v.optional(v.number()),
+  submittedAt: v.optional(v.number()),
   locked: v.boolean(),
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
submittedAt: v.optional(v.number()),
export const editableFields = {
gameSystem,
storageId: v.id('_storage'),
userId: v.id('users'),
tournamentRegistrationId: v.optional(v.id('tournamentRegistrations')),
// FUTURE:
// data: listData,
};
export const computedFields = {
modifiedAt: v.optional(v.number()),
submittedAt: v.optional(v.number()),
locked: v.boolean(),
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/_model/lists/table.ts` at line 14, submittedAt is currently declared
in editableFields allowing it into createListArgs and updateListArgs and thus
clients can modify it via the updateList mutation that applies ...updated; move
submittedAt from editableFields to computedFields in the model (reference symbol
submittedAt) so it is excluded from client-provided args, update any code that
builds createListArgs/updateListArgs to stop expecting submittedAt (references:
createList.ts where submittedAt is set, checkListSubmittedOnTime.ts which reads
it), and ensure updateList mutation no longer applies unfiltered ...updated to
the DB (or filters out submittedAt) to prevent client override.

@ianpaschal ianpaschal merged commit 8037589 into main Apr 12, 2026
3 checks passed
@ianpaschal ianpaschal deleted the add-list-submitted-at-field branch April 12, 2026 12:26
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.

1 participant