Skip to content

Develop - #45

Merged
Mayank9056-MM merged 13 commits into
mainfrom
develop
Aug 4, 2026
Merged

Mayank9056-MM merged 13 commits into
mainfrom
develop

Conversation

@Mayank9056-MM

@Mayank9056-MM Mayank9056-MM commented Aug 4, 2026 •

Copy link
Copy Markdown
Owner

Summary by Sourcery

Refactor database seeding into modular scripts with clear responsibilities and add infrastructure for hints and player hint tracking.

New Features:

  • Introduce dedicated scripts for seeding chapters, story scenes, challenges, dev users, and a bootstrap admin account with orchestrated execution via a central seed.ts.
  • Add a reset-db utility that safely resets, migrates, regenerates Prisma client, and re-runs all seed steps with interactive confirmation.
  • Define new Hint and PlayerHint tables to support per-challenge hints and player hint unlock tracking.

Enhancements:

  • Simplify seed-story and seed-challenges to operate as focused content loaders keyed by chapter number using a shared chapter-map utility.
  • Make event seeding idempotent and independent, ensuring the event singleton exists without overwriting configured production details.
  • Standardize all seed-related scripts to load environment variables via tsx --env-file and improve logging for clearer seed runs.
  • Add placeholder seed-hints and seed-announcements scripts as future hooks without affecting current data.

Build:

  • Update package.json scripts to run all database and seeding utilities through tsx with .env loading for consistent configuration.

Summary by CodeRabbit

  • New Features

    • Added foundational support for challenge hints, including hint levels and player-specific hint tracking.
    • Added streamlined setup for chapters, story scenes, challenges, events, users, and administrator access.
  • Bug Fixes

    • Improved database reset and initial data setup by consistently loading environment configuration.
    • Added safeguards to prevent accidental database resets.
  • Chores

    • Simplified development data and startup workflows for more consistent environments.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @Mayank9056-MM, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds hint database tables and replaces the seed system with ordered, environment-aware scripts for chapters, scenes, challenges, users, administrators, events, and guarded database resets.

Changes

Database Seeding and Reset

Layer / File(s) Summary
Hint persistence schema
prisma/migrations/.../migration.sql
Adds HintLevel, hints, and player_hints, with indexes, uniqueness enforcement, and cascading foreign keys.
Chapter and content seeders
scripts/utils/chapter-map.ts, scripts/seed-chapters.ts, scripts/seed-story.ts, scripts/seed-challenges.ts
Adds chapter-number resolution and upserts chapters, scenes, and challenges using chapter-scoped keys.
Operational and account seeders
scripts/seed-event.ts, scripts/seed-users.ts, scripts/seed-admin.ts, scripts/seed-announcements.ts, scripts/seed-hints.ts
Adds event, development-user, and administrator seeders. Announcement and hint seeders report that seeding is skipped.
Seed orchestration and guarded reset
scripts/seed.ts, scripts/reset-db.ts, package.json
Runs seeders sequentially and adds confirmation-controlled database reset commands with explicit .env loading.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SeedRunner
  participant Seeders
  participant PrismaDatabase
  SeedRunner->>Seeders: invoke seed steps sequentially
  Seeders->>PrismaDatabase: upsert event, chapters, scenes, challenges, users, and admin
  PrismaDatabase-->>Seeders: return seed results
  Seeders-->>SeedRunner: report completion or failure
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title "Develop" is too generic and does not identify the database migration and seed-script changes. Replace "Develop" with a concise title that identifies the main changes, such as database seeding and reset tooling.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 88.89% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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.

@sourcery-ai

sourcery-ai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Reviewer's Guide

Refactors the seeding system into clearly scoped scripts with chapter-number → id mapping, environment-aware execution, and simplified story/challenge content, plus introduces initial database structures for future hint support.

Sequence diagram for reset-db and seed orchestration

sequenceDiagram
  actor Dev
  participant reset_db_ts as reset-db.ts
  participant PrismaCLI as prisma_migrate_generate
  participant seed_ts as seed.ts
  participant prisma as PrismaClient

  Dev->>reset_db_ts: run resetDatabase()
  reset_db_ts->>reset_db_ts: confirm()
  reset_db_ts->>PrismaCLI: prisma migrate reset
  PrismaCLI-->>reset_db_ts: migrations reset
  reset_db_ts->>PrismaCLI: prisma generate
  PrismaCLI-->>reset_db_ts: client generated
  reset_db_ts->>seed_ts: main()
  seed_ts->>seed_ts: run seedEvent()
  seed_ts->>seed_ts: run seedChapters()
  seed_ts->>seed_ts: run seedStory()
  seed_ts->>seed_ts: run seedChallenges()
  seed_ts->>seed_ts: run seedUsers()
  seed_ts->>seed_ts: run seedAdmin()
  seed_ts->>prisma: prisma.$disconnect()
  reset_db_ts->>prisma: prisma.$disconnect()
Loading

Entity relationship diagram for hints and player_hints

erDiagram
  users {
    TEXT id PK
  }

  challenges {
    TEXT id PK
  }

  hints {
    TEXT id PK
    TEXT challengeId FK
    VARCHAR title
    TEXT content
    HintLevel level
    INT xpCost
    ContentStatus status
  }

  player_hints {
    TEXT userId FK
    TEXT hintId FK
    INT xpSpent
  }

  challenges ||--o{ hints : has
  users ||--o{ player_hints : unlocks
  hints ||--o{ player_hints : is_unlocked_by
Loading

File-Level Changes

Change Details Files
Refactor story seeding to only own Scene records and use chapter-number based mapping instead of embedding full narrative/challenge/evidence logic.
  • Remove inline character, evidence, challenge, dialogue, validation, and unlock-rule definitions from seed-story script.
  • Introduce SceneSeed typed content keyed by chapter number using SceneType enum and ContentStatus for status.
  • Use buildChapterMap to resolve chapterId from chapter number and upsert scenes by (chapterId, order) unique constraint.
  • Add dotenv/config usage and require-main guard for standalone execution with improved logging.
scripts/seed-story.ts
scripts/utils/chapter-map.ts
Simplify challenge seeding to only own Challenge records keyed by chapter number and display order, dropping prerequisites and XP from this script.
  • Redefine ChallengeSeed to remove chapterId string and xpReward/prerequisites, instead using chapter-number keyed CHALLENGE_CONTENT.
  • Use buildChapterMap to resolve chapterId and upsert challenges via (chapterId, displayOrder) unique constraint while hashing flags.
  • Remove validation and prerequisite wiring logic; focus script strictly on challenge core fields.
  • Add dotenv/config usage and require-main guard with clearer logging.
scripts/seed-challenges.ts
scripts/utils/chapter-map.ts
Make event seeding idempotent and non-destructive, with generated default schedule for fresh environments.
  • Change seed-event to upsert the singleton Event without overwriting existing configuration on update.
  • Generate startsAt/endsAt relative to current time for local/CI instead of hardcoded dates and descriptions.
  • Add explicit logging and require-main guard for standalone execution.
  • Ensure dotenv/config is loaded for env-based configuration.
scripts/seed-event.ts
Introduce a centralized seed orchestrator and a safe database reset script that wraps Prisma commands and seeds with confirmation.
  • Add seed.ts that orchestrates event, chapters, story, challenges, users, and admin seeding in dependency order with shared prisma disconnect.
  • Create reset-db.ts that prompts for a confirmation phrase, runs prisma migrate reset and generate, then calls seed.main.
  • Use execSync for shell commands and support CI/--yes bypass of interactive confirmation.
  • Document SRP and failure-handling philosophy in script headers.
scripts/seed.ts
scripts/reset-db.ts
Split chapter seeding into its own script and introduce chapter-number → id mapping utility for use by other seed scripts.
  • Add seed-chapters.ts to upsert Chapter rows by order/slug with ContentStatus.PUBLISHED, keeping chapter metadata as simple data.
  • Create chapter-map.ts which queries chapters, parses numeric chapter from slug "chapter-N", and exposes getId/chapterNumbers API.
  • Use chapter-map in story and challenge seeds to avoid hardcoded chapter IDs and ensure consistent foreign key resolution.
  • Warn when chapter slugs do not match expected pattern and throw if requested chapter number is missing.
scripts/seed-chapters.ts
scripts/utils/chapter-map.ts
scripts/seed-story.ts
scripts/seed-challenges.ts
Add scripts for seeding dev users and a bootstrap super admin account using environment-driven credentials.
  • Implement seed-users.ts to upsert a small set of deterministic non-admin dev accounts with fixed test password.
  • Implement seed-admin.ts to upsert a SUPER_ADMIN user whose email/password come from SEED_ADMIN_* environment variables, rotating password on re-run.
  • Ensure both scripts log concise status and use require-main guards.
  • Leverage generated Role and UserStatus enums and shared passwordService hashing.
scripts/seed-users.ts
scripts/seed-admin.ts
Wire seed and reset scripts to load environment from .env via tsx and add placeholders for future hints/announcements seeding.
  • Update package.json scripts to run tsx with --env-file=.env for all seed/* and db:reset commands.
  • Add seed-hints.ts and seed-announcements.ts as intentional no-op placeholders to keep orchestration stable for future modules.
  • Ensure these placeholder scripts log that they are skipped rather than performing any side effects.
package.json
scripts/seed-hints.ts
scripts/seed-announcements.ts
scripts/reset-db.ts
scripts/seed.ts
Introduce initial database tables for hints and per-player hint unlocks to support a future hints/advisor module.
  • Create HintLevel enum and hints table with challengeId FK, title/content, level, xpCost, status, and timestamps.
  • Create player_hints join table keyed by (userId,hintId) with unlockedAt and xpSpent fields.
  • Add indexes on hints(challengeId,level) and player_hints(hintId) plus FKs to challenges, users, and hints.
  • Document this as future-module schema without yet wiring into seeds.
prisma/migrations/20260804192440_add_hint/migration.sql

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Mayank9056-MM
Mayank9056-MM merged commit cba3de0 into main Aug 4, 2026
1 of 2 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (3)
prisma/migrations/20260804192440_add_hint/migration.sql (1)

29-33: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Remove the redundant (challengeId, level) index.

hints_challengeId_level_idx and hints_challengeId_level_key cover the same non-null columns. The unique index already supports this lookup. Keeping both indexes increases storage and write maintenance for hints.

Proposed change
-CREATE INDEX "hints_challengeId_level_idx" ON "hints"("challengeId", "level");
 CREATE UNIQUE INDEX "hints_challengeId_level_key" ON "hints"("challengeId", "level");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/migrations/20260804192440_add_hint/migration.sql` around lines 29 -
33, Remove the redundant non-unique index declaration
"hints_challengeId_level_idx" from the migration, while preserving the unique
index "hints_challengeId_level_key" on ("challengeId", "level").
scripts/seed-story.ts (1)

5-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Correct the ownership comment.

The header states this script owns Scene, Character, DialogueLine, Choice, Evidence, and UnlockRule. The script writes Scene rows only. Narrow the comment to the models this script actually seeds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed-story.ts` around lines 5 - 8, Update the ownership header
comment in seed-story.ts to state that this script exclusively seeds Scene rows,
removing Character, DialogueLine, Choice, Evidence, and UnlockRule from the
listed ownership. Preserve the existing clarification that Chapters and
Challenges belong to their respective seed scripts.
scripts/seed.ts (1)

33-40: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Two no-op seeders have no caller. STEPS in scripts/seed.ts omits seedHints and seedAnnouncements, and package.json defines no matching seed:hints or seed:announcements script. Both exports are therefore unreachable, and their skip messages never appear in a seed run. The shared root cause is that the orchestrator and the script list were not extended when these placeholders were added.

  • scripts/seed.ts#L33-L40: either add { name: "hints", run: seedHints } and { name: "announcements", run: seedAnnouncements } to STEPS, or drop both placeholder files until the modules land.
  • scripts/seed-hints.ts#L14-L16: keep seedHints only if scripts/seed.ts runs it; otherwise delete the file.
  • scripts/seed-announcements.ts#L6-L8: keep seedAnnouncements only if scripts/seed.ts runs it; otherwise delete the file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed.ts` around lines 33 - 40, Make both placeholder seeders
reachable by extending the STEPS orchestrator in scripts/seed.ts with seedHints
and seedAnnouncements. Retain seedHints in scripts/seed-hints.ts and
seedAnnouncements in scripts/seed-announcements.ts only because they are now
invoked; no direct changes are required in those files.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Around line 10-17: Update the seed script commands (seed, seed:event,
seed:chapters, seed:story, seed:challenges, seed:users, and seed:admin) to use
tsx --env-file-if-exists=.env instead of the strict --env-file=.env flag,
preserving their existing script paths and behavior.

In `@prisma/migrations/20260804192440_add_hint/migration.sql`:
- Line 11: Update the migration schema to enforce non-negative values for both
hints.xpCost and player_hints.xpSpent by adding database CHECK constraints
requiring each field to be greater than or equal to zero, while preserving their
existing INTEGER NOT NULL DEFAULT 0 definitions.
- Around line 38-45: Update the PlayerHint foreign-key cascade policies in the
migration, specifically player_hints_userId_fkey and player_hints_hintId_fkey,
so deleting a User or Hint does not erase unlock history; use the project’s
intentional preservation strategy, such as RESTRICT, soft deletion, or
archiving, while leaving the hints_challengeId_fkey relationship unchanged.

In `@scripts/reset-db.ts`:
- Around line 72-83: Update resetDatabase to explicitly disconnect Prisma after
runSeed() completes, reusing the existing Prisma client/import used by the seed
flow. Ensure disconnection occurs after successful seeding so the reset command
can exit cleanly, while preserving the existing failure handling.
- Around line 25-67: Add a target-environment validation in resetDatabase before
confirm() or prisma migrate reset runs, rejecting non-development DATABASE_URL
targets even when CI or --yes bypasses confirmation. Reuse the existing
environment/configuration symbols to identify the development database, set a
failure exit code, and return without invoking the destructive reset.

In `@scripts/seed-admin.ts`:
- Around line 38-57: Update the existing-user branch of the upsert in the
seed-admin flow so the account is always restored to the bootstrap administrator
state: set role to Role.SUPER_ADMIN and status to UserStatus.ACTIVE alongside
passwordHash. Keep the create branch and existing email-based lookup unchanged.

In `@scripts/seed-challenges.ts`:
- Around line 62-79: Update the update payload in the challenge.upsert call to
include challenge.slug alongside the other mutable challenge fields, so existing
rows keyed by chapterId and displayOrder synchronize slug changes while
preserving the current create behavior.

In `@scripts/seed-chapters.ts`:
- Around line 36-49: Update the documentation for the chapter seeding loop to
state that the upsert is keyed by chapter order, matching the `where: { order:
chapter.number }` in the `CHAPTERS` iteration; do not change the upsert
implementation.

In `@scripts/seed-hints.ts`:
- Around line 1-16: Update seedHints so its documentation matches the repository
state: remove the claim that no Hint model or migration exists and the
unsupported seed:hints script reference, then state the actual reason the
function remains a no-op. If no valid reason exists, implement seeding using the
newly added hint-related Prisma models instead.

---

Nitpick comments:
In `@prisma/migrations/20260804192440_add_hint/migration.sql`:
- Around line 29-33: Remove the redundant non-unique index declaration
"hints_challengeId_level_idx" from the migration, while preserving the unique
index "hints_challengeId_level_key" on ("challengeId", "level").

In `@scripts/seed-story.ts`:
- Around line 5-8: Update the ownership header comment in seed-story.ts to state
that this script exclusively seeds Scene rows, removing Character, DialogueLine,
Choice, Evidence, and UnlockRule from the listed ownership. Preserve the
existing clarification that Chapters and Challenges belong to their respective
seed scripts.

In `@scripts/seed.ts`:
- Around line 33-40: Make both placeholder seeders reachable by extending the
STEPS orchestrator in scripts/seed.ts with seedHints and seedAnnouncements.
Retain seedHints in scripts/seed-hints.ts and seedAnnouncements in
scripts/seed-announcements.ts only because they are now invoked; no direct
changes are required in those files.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 19fc92f9-74b4-42b8-bca6-929869712d25

📥 Commits

Reviewing files that changed from the base of the PR and between cbd7cd2 and 451812b.

📒 Files selected for processing (13)
  • package.json
  • prisma/migrations/20260804192440_add_hint/migration.sql
  • scripts/reset-db.ts
  • scripts/seed-admin.ts
  • scripts/seed-announcements.ts
  • scripts/seed-challenges.ts
  • scripts/seed-chapters.ts
  • scripts/seed-event.ts
  • scripts/seed-hints.ts
  • scripts/seed-story.ts
  • scripts/seed-users.ts
  • scripts/seed.ts
  • scripts/utils/chapter-map.ts

Comment thread package.json
Comment on lines +10 to +17
"db:reset": "tsx --env-file=.env scripts/reset-db.ts",
"seed": "tsx --env-file=.env scripts/seed.ts",
"seed:event": "tsx --env-file=.env scripts/seed-event.ts",
"seed:chapters": "tsx --env-file=.env scripts/seed-chapters.ts",
"seed:story": "tsx --env-file=.env scripts/seed-story.ts",
"seed:challenges": "tsx --env-file=.env scripts/seed-challenges.ts",
"seed:users": "tsx --env-file=.env scripts/seed-users.ts",
"seed:admin": "tsx --env-file=.env scripts/seed-admin.ts"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Node.js tsx --env-file missing file ENOENT --env-file-if-exists

💡 Result:

When running Node.js (or tools like tsx that pass flags through to Node.js), the --env-file flag requires the specified file to exist; if it is missing, Node.js throws an ENOENT error [1][2][3]. To prevent this error when a file might be missing (e.g., in CI environments or optional configurations), you should use the --env-file-if-exists flag instead [1][4][3]. Key points regarding these flags: - --env-file=file: Loads environment variables from the specified file. It will throw an error and exit if the file does not exist [1][3]. - --env-file-if-exists=file: Provides the same functionality as --env-file but silently ignores the file if it is missing, without throwing an error [1][3]. Since tsx acts as a wrapper that passes unrecognized flags directly to the underlying Node.js process, it fully supports these Node.js built-in flags [5][6][7]. If you are experiencing an ENOENT error with tsx, simply replace --env-file with --env-file-if-exists in your command [1][3]. Additionally, ensure that you place these flags before your script path, as Node.js and tsx interpret them as command-line options [8][7][3]. Placing them after the script name may cause them to be incorrectly parsed as arguments to your script rather than as Node.js options [8][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- package.json relevant scripts ---\n'
python3 - <<'PY'
import json
from pathlib import Path
pkg = json.loads(Path('package.json').read_text())
for name in [
  'db:reset','seed','seed:event','seed:chapters','seed:story',
  'seed:challenges','seed:users','seed:admin'
]:
    print(f'{name}: {pkg.get("scripts",{}).get(name)}')
PY

printf '\n--- seed-related imports ---\n'
rg -n '"dotenv/config"|from ["'+"'"]dotenv/config" scripts package.json || true

printf '\n--- installed tsx/node availability ---\n'
(command -v node && node --version) || true
(command -v tsx && tsx --version) || true

printf '\n--- local flag behavior probe (using installed tsx if present) ---\n'
if command -v tsx >/dev/null 2>&1; then
  tmpdir="$(mktemp -d)"
  cat > "$tmpdir/probe.ts" <<'TS'
console.log('node-options-value:', process.env.SECRET_VAR)
TS
  export SECRET_VAR=fromEnv
  printf 'tsx --env-file=.env --env-file-if-exists=.env: '
  tsx --env-file=.env --env-file-if-exists=.env "$tmpdir/probe.ts" 2>&1 || true
  rm -rf "$tmpdir"
fi

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 259


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- package.json relevant scripts ---'
python3 - <<'PY'
import json
from pathlib import Path
pkg = json.loads(Path("package.json").read_text())
for name in [
  "db:reset","seed","seed:event","seed:chapters","seed:story",
  "seed:challenges","seed:users","seed:admin"
]:
    print(f"{name}: {pkg.get('scripts',{}).get(name)}")
PY

printf '%s\n' ''
printf '%s\n' '--- seed-related imports ---'
rg -n '"dotenv/config"|from ["'"'']dotenv/config' scripts package.json || printf '%s\n' 'no matches'

printf '%s\n' ''
printf '%s\n' '--- installed tsx/node availability ---'
(command -v node && node --version) || printf '%s\n' 'node unavailable'
(command -v tsx && tsx --version) || printf '%s\n' 'tsx unavailable'

printf '%s\n' ''
printf '%s\n' '--- local flag behavior probe (using installed tsx if present) ---'
if command -v tsx >/dev/null 2>&1; then
  tmpdir="$(mktemp -d)"
  cat > "$tmpdir/probe.ts" <<'TS'
console.log("node-options-value:", process.env.SECRET_VAR)
TS
  export SECRET_VAR=fromEnv
  printf '%s ' 'tsx --env-file=.env --env-file-if-exists=.env: '
  tsx --env-file=.env --env-file-if-exists=.env "$tmpdir/probe.ts" 2>&1 || printf '%s\n' '(exited non-zero)'
  rm -rf "$tmpdir"
fi

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 729


Use --env-file-if-exists=.env for these seed scripts.

Node throws ENOENT for a missing --env-file target, while these scripts already import "dotenv/config", which loads .env when present and stays silent when it is not. Replace tsx --env-file=.env with tsx --env-file-if-exists=.env, or drop the flag if the project does not rely on Node-level env-file preprocessing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` around lines 10 - 17, Update the seed script commands (seed,
seed:event, seed:chapters, seed:story, seed:challenges, seed:users, and
seed:admin) to use tsx --env-file-if-exists=.env instead of the strict
--env-file=.env flag, preserving their existing script paths and behavior.

"title" VARCHAR(100) NOT NULL,
"content" TEXT NOT NULL,
"level" "HintLevel" NOT NULL,
"xpCost" INTEGER NOT NULL DEFAULT 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect: all write paths reject negative XP values, or the schema adds checks.
rg -n -C 4 '\bxpCost\b|\bxpSpent\b' --glob '*.{ts,tsx,js,jsx,sql,prisma}'

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "tracked files around prisma:"
git ls-files | sed -n '/prisma/p' | head -200

echo
echo "migration file:"
if [ -f prisma/migrations/20260804192440_add_hint/migration.sql ]; then
  cat -n prisma/migrations/20260804192440_add_hint/migration.sql
fi

echo
echo "schema files:"
fd -a 'schema\.prisma$|.*\.prisma$' prisma . 2>/dev/null | sed 's#^\./##' | head -100

echo
echo "search xpCost/xpSpent broader:"
rg -n -C 4 '\bxpCost\b|\bxpSpent\b' . || true

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 16180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "hint service outline:"
ast-grep outline modules/hint -f '.*service.*|.*guard.*|.*resolver.*' || true

echo
echo "search xpCost/xpSpent in module code with context:"
rg -n -C 8 '\bxpCost\b|\bxpSpent\b' modules prisma || true

echo
echo "database CHECK constraints in migrations:"
rg -n 'CHECK\s*\(|Check\s*\(|constraint|`@unique`|`@id`' --glob '*.sql' prisma/migrations || true

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 22144


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "files likely containing Hint/PlayerHint services or seeds:"
git ls-files | rg '(^modules/hint/seed|hint\.service|hint\.db|hint\.repository|seed-hints|hint.constants)' || true

echo
echo "hints service outline:"
fd -a 'hint\.service\.(tsx?|ts)' . --owner --no-ignore 2>/dev/null | while read -r f; do
  echo "--- $f"
  wc -l "$f"
done

echo
echo "seed/constants relevant sections:"
for f in modules/hint/constants/hint.constants.ts modules/hint/seed-hints.ts; do
  if [ -f "$f" ]; then
    echo "--- $f lines 80-110"
    sed -n '80,110p' "$f" | cat -n -v
  fi
done

echo
echo "write/repo references to hints model fields:"
python3 - <<'PY'
from pathlib import Path
import re
terms = ['xpCost', 'xpSpent', 'PlayerHint', 'hint.xpCost', 'hintSpent', 'HINT_XP_COST_CHANGED']
for p in Path('.').rglob('*'):
    if not p.is_file(): continue
    if p.parts and p.parts[0] in {'.git','node_modules','dist','build','coverage'}: continue
    try:
        txt = p.read_text(errors='ignore')
    except Exception:
        continue
    if any(t in txt for t in terms) or 'hints.update' in txt or 'createPlayerHint' in txt:
        print(f"--- {p}")
        for i,line in enumerate(txt.splitlines(),1):
            if any(t in line for t in terms) or 'hints.update' in line or 'createPlayerHint' in line:
                print(f"{i}:{line[:220]}")
PY

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 392


Enforce non-negative XP fields at the database layer.

xpCost and xpSpent are signed integers with only a default of 0. Treat negative hint costs or negative unlock spend as invalid and add hints.xpCost >= 0 and player_hints.xpSpent >= 0 constraints so future inserts or updates cannot store bad state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/migrations/20260804192440_add_hint/migration.sql` at line 11, Update
the migration schema to enforce non-negative values for both hints.xpCost and
player_hints.xpSpent by adding database CHECK constraints requiring each field
to be greater than or equal to zero, while preserving their existing INTEGER NOT
NULL DEFAULT 0 definitions.

Comment on lines +38 to +45
-- AddForeignKey
ALTER TABLE "hints" ADD CONSTRAINT "hints_challengeId_fkey" FOREIGN KEY ("challengeId") REFERENCES "challenges"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "player_hints" ADD CONSTRAINT "player_hints_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "player_hints" ADD CONSTRAINT "player_hints_hintId_fkey" FOREIGN KEY ("hintId") REFERENCES "hints"("id") ON DELETE CASCADE ON UPDATE CASCADE;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Expect: deletion behavior explicitly treats player hint records as disposable or historical.
rg -n -C 5 '\b(playerHints|PlayerHint|xpSpent|unlockedAt)\b|\.delete(Many)?\(' \
  --glob '*.{ts,tsx,js,jsx,sql,prisma}'

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked files sample:"
git ls-files | sed -n '1,120p'

echo
echo "Migration file:"
cat -n prisma/migrations/20260804192440_add_hint/migration.sql | sed -n '1,120p'

echo
echo "Search for hints/player hints/xp/unlocked wording:"
rg -n -C 3 'hints|player_hints|playerHints|PlayerHint|xpSpent|XP|XP spent|XP spent|unlockedAt|unlocked|xpSpent|xp_spent' -S . || true

echo
echo "Prisma schema:"
fd -a 'schema.prisma|*.prisma' . -d 3 -x sh -c 'echo "--- {}"; cat -n "{}" | sed -n "1,220p"'

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 50386


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Schema file locations:"
fd -a 'schema\.prisma|\.prisma$' . -d 4

echo
echo "Prisma schema:"
SCHEMA="$(fd 'schema\.prisma|.*\.prisma' prisma -d 4 | head -n1)"
[ -n "${SCHEMA:-}" ] && cat -n "${SCHEMA}" | sed -n '1,260p' || true

echo
echo "Hint module services/repositories repository/files:"
fd -a '\.(ts|tsx)$' modules/lib/prisma prisma -d 4 | sort | sed -n '1,220p'

echo
echo "Hint service outline:"
ast-grep outline modules/hint/services/hint.service.ts --view expanded || true

echo
echo "Hint service relevant sections:"
cat -n modules/hint/services/hint.service.ts | sed -n '1,260p'

echo
echo "Hint repository relevant sections:"
cat -n modules/hint/repositories/hint.repository.ts | sed -n '1,220p' || true

echo
echo "Hint DTO mapper relevant sections:"
cat -n modules/hint/mapper/hint.mapper.ts | sed -n '1,180p' || true

echo
echo "Focused searches for hint deletion/update/admin usage:"
rg -n -C 4 '\b(hint|Hint|Hints)\b|\.(delete|destroy|remove|updateMany|updateOne|create|unlock|xpSpent|unlockedAt)\b|deleteMany\(|deleteOne\(' modules/hint scripts package.json prisma -g '*.ts' -g '*.tsx' -g '*.sql' -g '*.prisma' -g 'package.json' || true

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 7676


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Prisma schema Hint/PlayerHint models:"
cat -n prisma/schema.prisma | sed -n '260,420p'

echo
echo "Hint module files:"
find modules/hint -maxdepth 3 -type f \( -name '*.ts' -o - name '*.tsx' \) | sort

echo
echo "Hint module relevant files:"
for f in modules/hint/constants/hint.constants.ts modules/hint/constants/hint.events.ts modules/hint/repositories/hint.repository.ts modules/hint/services/hint.service.ts modules/hint/mapper/hint.mapper.ts modules/hint/types/hint.types.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    cat -n "$f" | sed -n '1,260p'
  fi
done

echo
echo "Focused references to hint unlock/history/XP:"
rg -n -C 4 'hint|Hint|PlayerHint|xpSpent|xpCost|unlockedAt|unlock' modules/hint modules -g '*.ts' -g '*.tsx' -g '*.sql' -g '*.prisma' || true

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 5290


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Hint/PlayerHint model definitions:"
python3 - <<'PY'
from pathlib import Path
p=Path('prisma/schema.prisma')
text=p.read_text()
for i,line in enumerate(text.splitlines(),1):
    if 'model Hint' in line or 'model PlayerHint' in line:
        start=max(1,i-5)
        print(f'--- lines {start}-{i+25} ---')
        lines=text.splitlines()
        for n in range(start, min(len(lines), i+25)+1):
            print(f'{n:4d}: {lines[n-1]}')
PY

echo
echo "Hint module files:"
find modules/hint -maxdepth 3 -type f | sort

echo
echo "Hint module service/repo/mapper if present:"
for f in modules/hint/services/hint.service.ts modules/hint/repositories/hint.repository.ts modules/hint/mapper/hint.mapper.ts; do
  if [ -f "$f" ]; then
    echo "--- $f"
    cat -n "$f" | sed -n '1,260p'
  fi
done

echo
echo "Focused references to PlayerHint/unlock/XP/status change:"
rg -n -C 4 'PlayerHint|PlayerHint|playerHints|xpSpent|xpcost|xpcost|xpcost|unlock|hintId|hintId|status|DRAFT|ARCHIVED|onDelete|`@Relation`|hints' modules/hint modules prisma -g '*.ts' -g '*.tsx' -g '*.sql' -g '*.prisma' -g '!prisma/migrations/**/migration.sql' | sed -n '1,260p' ... || true

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 1900


Make the PlayerHint cascade policy intentional.

PlayerHint records unlockedAt and xpSpent, so onDelete: Cascade deletes a user’s unlock history when the User is deleted and deletes unlock history when a Hint is deleted. If this data is player progression/accounting history, change this before this model is used: use RESTRICT, soft-delete, or archive the row instead of erasing it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prisma/migrations/20260804192440_add_hint/migration.sql` around lines 38 -
45, Update the PlayerHint foreign-key cascade policies in the migration,
specifically player_hints_userId_fkey and player_hints_hintId_fkey, so deleting
a User or Hint does not erase unlock history; use the project’s intentional
preservation strategy, such as RESTRICT, soft deletion, or archiving, while
leaving the hints_challengeId_fkey relationship unchanged.

Comment thread scripts/reset-db.ts
Comment on lines +25 to +67
async function confirm(): Promise<boolean> {
if (process.env.CI === "true" || process.argv.includes("--yes")) {
// CI and explicit --yes both skip the interactive prompt — CI has
// no stdin to read from, and --yes is an intentional opt-out for
// scripted local use (a Makefile target, a pre-test setup script).
return true;
}

const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

console.log(
"\n⚠️ This will PERMANENTLY DELETE all data in the configured database, then re-migrate and re-seed it.",
);
const answer = await rl.question(
`Type "${CONFIRMATION_PHRASE}" to continue: `,
);
rl.close();

return answer.trim() === CONFIRMATION_PHRASE;
}

/**
* Three distinct, individually-loggable steps rather than one combined
* shell command — if `migrate reset` succeeds but `generate` fails
* (a stale node_modules, a version mismatch), the failure is
* unambiguous about which stage broke, rather than a single opaque
* combined-command exit code.
*/
async function resetDatabase(): Promise<void> {
const confirmed = await confirm();
if (!confirmed) {
console.log("[reset-db] Aborted — confirmation phrase did not match.");
process.exitCode = 1;
return;
}

console.log("\n[reset-db] → prisma migrate reset");
execSync("npx prisma migrate reset --force --skip-seed", {
stdio: "inherit",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether CI invokes db:reset and how DATABASE_URL is supplied.
fd -H -e yml -e yaml . .github --exec rg -n 'db:reset|DATABASE_URL|prisma migrate' {} \; 2>/dev/null || echo "no workflow references found"
rg -n 'DATABASE_URL' --hidden -g '!node_modules' -g '!*.lock'

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 197


🏁 Script executed:

#!/bin/bash
set -u

printf 'Tracked files matching reset/db/seed/env docs: '
git ls-files | rg '(^scripts/reset-db\.ts$|(^|/)(package\.json|pnpm-lock\.yaml|yarn.lock|package-lock\.json|Makefile|.*\.env.*|.*\.test\.ts|.*\.spec\.ts).*|(^|/)\.github/workflows/)'

printf '\nscripts/reset-db.ts excerpt:\n'
sed -n '1,140p' scripts/reset-db.ts

printf '\npackage scripts and Prisma version:\n'
if [ -f package.json ]; then jq '.scripts, .devDependencies, .dependencies' package.json; fi

printf '\nRead-only structural/static verifier for reset-db confirmation bypass and database target:\n'
python3 - <<'PY'
from pathlib import Path
p = Path('scripts/reset-db.ts')
s = p.read_text()
checks = {
    "has_confirm": "async function confirm()" in s,
    "ci_bypass": 'process.env.CI === "true"' in s,
    "yes_bypass": 'process.argv.includes("--yes")' in s,
    "bypass_returns_true": 'process.env.CI === "true" || process.argv.includes("--yes")' in s and "return true;" in s[s.index('process.argv.includes("--yes")'):s.index('process.argv.includes("--yes")')+500],
    "migrate_reset_force": 'execSync("npx prisma migrate reset --force --skip-seed"' in s,
    "uses_DATABASE_URL_via_prisma": 'DATABASE_URL' in s or 'prismaClient' in s or '.dotenv/config' in s,
}
for k, v in checks.items():
    print(f"{k}={v}")
print("function reset starts after confirm", s.find('async function resetDatabase') > s.find('async function confirm'))
PY

printf '\nAny explicit guard against CI/production target in reset-db:\n'
rg -n 'CI|NPM|NODE_ENV|DATABASE_URL|production|refus|env=' scripts/reset-db.ts package.json .github 2>/dev/null || true

Repository: Mayank9056-MM/ctf-silk-project

Length of output: 5179


🌐 Web query:

Prisma migrate reset --force --skip-seed deletes all tables configured DATABASE_URL documentation

💡 Result:

Yes, the prisma migrate reset command effectively deletes all tables configured in your database by dropping the entire database or schema (depending on the database provider) and then recreating it [1][2][3]. Key details regarding your query: 1. Data Loss: The command is destructive and results in the loss of all existing data in the database defined by your DATABASE_URL [1][4][5]. It is intended strictly for development environments [1][2][3]. 2. --force: This flag is used to skip the interactive confirmation prompt that normally appears before the destructive action is taken [1][2]. 3. --skip-seed: In earlier versions of Prisma (v6 and older), this flag was used to prevent the automatic execution of seed scripts after the database was reset [2][6]. However, please note that in Prisma ORM v7 and later, automatic seeding during this process has been removed entirely, and seeding must now be triggered explicitly using npx prisma db seed [6]. If you are using a version of Prisma where --skip-seed is still supported, it will prevent the execution of your seed scripts, but the underlying database tables will still be dropped and recreated as part of the reset process [1][2]. Because this command acts on the entire database or schema specified in your configuration, it will remove all tables within that scope, regardless of whether they are managed by Prisma [1][2][3].

Citations:


Authorization Bypass (CWE-863): Incorrect Authorization

Reachability: Internal

Reachability path
● Entry
  scripts/reset-db.ts:56
  resetDatabase
│
▼
● Hop
  scripts/seed.ts
│
▼
● Sink
  scripts/seed-challenges.ts

Add a target database check before the destructive reset.

confirm() returns true for CI === "true" or --yes, so db:reset can skip the confirmation prompt with a non-development DATABASE_URL. prisma migrate reset --force --skip-seed drops the configured database, and Prisma reset is only intended for development.

Gate the destructive path on the target environment before calling it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/reset-db.ts` around lines 25 - 67, Add a target-environment
validation in resetDatabase before confirm() or prisma migrate reset runs,
rejecting non-development DATABASE_URL targets even when CI or --yes bypasses
confirmation. Reuse the existing environment/configuration symbols to identify
the development database, set a failure exit code, and return without invoking
the destructive reset.

Comment thread scripts/reset-db.ts
Comment on lines +72 to +83
console.log("\n[reset-db] → seed.ts");
await runSeed();

console.log("\n[reset-db] Database reset and seeded.");
}

if (require.main === module) {
resetDatabase().catch((error) => {
console.error("\n[reset-db] Failed:", error);
process.exitCode = 1;
});
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Disconnect Prisma after runSeed().

resetDatabase imports main from ./seed and calls it directly. scripts/seed.ts disconnects only inside its own require.main branch, which does not run here. The Prisma connection pool therefore stays open and can keep the event loop alive after a successful reset, so the command hangs instead of exiting.

🔒️ Proposed fix
+import prisma from "`@/lib/prisma`";
 import { execSync } from "node:child_process";
 if (require.main === module) {
-  resetDatabase().catch((error) => {
-    console.error("\n[reset-db] Failed:", error);
-    process.exitCode = 1;
-  });
+  resetDatabase()
+    .catch((error) => {
+      console.error("\n[reset-db] Failed:", error);
+      process.exitCode = 1;
+    })
+    .finally(() => prisma.$disconnect());
 }
📝 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
console.log("\n[reset-db] → seed.ts");
await runSeed();
console.log("\n[reset-db] Database reset and seeded.");
}
if (require.main === module) {
resetDatabase().catch((error) => {
console.error("\n[reset-db] Failed:", error);
process.exitCode = 1;
});
}
console.log("\n[reset-db] → seed.ts");
await runSeed();
console.log("\n[reset-db] Database reset and seeded.");
}
if (require.main === module) {
resetDatabase()
.catch((error) => {
console.error("\n[reset-db] Failed:", error);
process.exitCode = 1;
})
.finally(() => prisma.$disconnect());
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/reset-db.ts` around lines 72 - 83, Update resetDatabase to explicitly
disconnect Prisma after runSeed() completes, reusing the existing Prisma
client/import used by the seed flow. Ensure disconnection occurs after
successful seeding so the reset command can exit cleanly, while preserving the
existing failure handling.

Comment thread scripts/seed-admin.ts
Comment on lines +38 to +57
await prisma.user.upsert({
where: { email },
update: {
// Re-running with a new SEED_ADMIN_PASSWORD rotates the bootstrap
// credential — the one intentional exception to "seeds don't
// overwrite existing data," since a lost/compromised admin
// password needs a way to be reset via this same script.
passwordHash,
},
create: {
username,
email,
fullName: "Platform Administrator",
passwordHash,
role: Role.SUPER_ADMIN,
status: UserStatus.ACTIVE,
},
});

console.log(`[seed-admin] Admin account ready (${email}).`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The update branch never grants SUPER_ADMIN.

If a User row already exists with SEED_ADMIN_EMAIL and a lower role, this upsert rotates that user's password and leaves the role unchanged. The script then logs "Admin account ready" while the platform still has no administrator. A disabled admin row has the same problem, because status is not restored.

🛡️ Proposed fix to make the admin grant authoritative
     update: {
       // Re-running with a new SEED_ADMIN_PASSWORD rotates the bootstrap
       // credential — the one intentional exception to "seeds don't
       // overwrite existing data," since a lost/compromised admin
       // password needs a way to be reset via this same script.
       passwordHash,
+      role: Role.SUPER_ADMIN,
+      status: UserStatus.ACTIVE,
     },
📝 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
await prisma.user.upsert({
where: { email },
update: {
// Re-running with a new SEED_ADMIN_PASSWORD rotates the bootstrap
// credential — the one intentional exception to "seeds don't
// overwrite existing data," since a lost/compromised admin
// password needs a way to be reset via this same script.
passwordHash,
},
create: {
username,
email,
fullName: "Platform Administrator",
passwordHash,
role: Role.SUPER_ADMIN,
status: UserStatus.ACTIVE,
},
});
console.log(`[seed-admin] Admin account ready (${email}).`);
await prisma.user.upsert({
where: { email },
update: {
// Re-running with a new SEED_ADMIN_PASSWORD rotates the bootstrap
// credential — the one intentional exception to "seeds don't
// overwrite existing data," since a lost/compromised admin
// password needs a way to be reset via this same script.
passwordHash,
role: Role.SUPER_ADMIN,
status: UserStatus.ACTIVE,
},
create: {
username,
email,
fullName: "Platform Administrator",
passwordHash,
role: Role.SUPER_ADMIN,
status: UserStatus.ACTIVE,
},
});
console.log(`[seed-admin] Admin account ready (${email}).`);
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 56-56: Avoid logging sensitive data
Context: console.log([seed-admin] Admin account ready (${email}).)
Note: [CWE-532] Insertion of Sensitive Information into Log File.

(log-sensitive-data-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed-admin.ts` around lines 38 - 57, Update the existing-user branch
of the upsert in the seed-admin flow so the account is always restored to the
bootstrap administrator state: set role to Role.SUPER_ADMIN and status to
UserStatus.ACTIVE alongside passwordHash. Keep the create branch and existing
email-based lookup unchanged.

Comment on lines +62 to 79
await prisma.challenge.upsert({
where: {
chapterId_displayOrder: { chapterId, displayOrder: challenge.displayOrder },
},
update: {
title: challenge.title,
chapterId: challenge.chapterId,
displayOrder: challenge.displayOrder,
difficulty: challenge.difficulty,
xpReward: challenge.xpReward,
flagHash,
},
create: {
chapterId,
slug: challenge.slug,
title: challenge.title,
chapterId: challenge.chapterId,
displayOrder: challenge.displayOrder,
difficulty: challenge.difficulty,
xpReward: challenge.xpReward,
displayOrder: challenge.displayOrder,
flagHash,
},
select: { id: true },
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

update omits slug, so slug edits never reach existing rows.

The upsert keys on (chapterId, displayOrder). If you change slug in CHALLENGE_CONTENT and re-run, the existing row keeps the old slug. The seed data and the database then disagree on the public identifier.

♻️ Proposed fix to keep slug in sync
         update: {
+          slug: challenge.slug,
           title: challenge.title,
           difficulty: challenge.difficulty,
           flagHash,
         },
📝 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
await prisma.challenge.upsert({
where: {
chapterId_displayOrder: { chapterId, displayOrder: challenge.displayOrder },
},
update: {
title: challenge.title,
chapterId: challenge.chapterId,
displayOrder: challenge.displayOrder,
difficulty: challenge.difficulty,
xpReward: challenge.xpReward,
flagHash,
},
create: {
chapterId,
slug: challenge.slug,
title: challenge.title,
chapterId: challenge.chapterId,
displayOrder: challenge.displayOrder,
difficulty: challenge.difficulty,
xpReward: challenge.xpReward,
displayOrder: challenge.displayOrder,
flagHash,
},
select: { id: true },
});
await prisma.challenge.upsert({
where: {
chapterId_displayOrder: { chapterId, displayOrder: challenge.displayOrder },
},
update: {
slug: challenge.slug,
title: challenge.title,
difficulty: challenge.difficulty,
flagHash,
},
create: {
chapterId,
slug: challenge.slug,
title: challenge.title,
difficulty: challenge.difficulty,
displayOrder: challenge.displayOrder,
flagHash,
},
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed-challenges.ts` around lines 62 - 79, Update the update payload
in the challenge.upsert call to include challenge.slug alongside the other
mutable challenge fields, so existing rows keyed by chapterId and displayOrder
synchronize slug changes while preserving the current create behavior.

Comment thread scripts/seed-chapters.ts
Comment on lines +36 to +49
for (const chapter of CHAPTERS) {
await prisma.chapter.upsert({
where: { order: chapter.number },
update: {
slug: chapter.slug,
title: chapter.title,
},
create: {
slug: chapter.slug,
title: chapter.title,
order: chapter.number,
status: ContentStatus.PUBLISHED,
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

The documented upsert key does not match the code.

The doc comment states the upsert happens by slug, but line 38 keys on order. This compiles only if Chapter.order is unique. It also rewrites slug on an order-keyed row, which fails if slug is unique and two chapters swap slugs.

Align the comment with the key that the schema actually enforces.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed-chapters.ts` around lines 36 - 49, Update the documentation for
the chapter seeding loop to state that the upsert is keyed by chapter order,
matching the `where: { order: chapter.number }` in the `CHAPTERS` iteration; do
not change the upsert implementation.

Comment thread scripts/seed-hints.ts
Comment on lines +1 to +16
/**
* Intentional no-op.
*
* Hints/Advisor is listed as a "Future module" — no Hint or Advisor
* Prisma model exists yet (the project's own architecture rule: never
* build schema or seed infrastructure ahead of a real, scheduled
* feature — vocabulary is fine, tables are not).
*
* This file exists only so seed.ts's step list and package.json's
* `seed:hints` script don't need to change the moment the Hints module
* actually lands. Replace this body with real seed logic then — not
* before, and not by inventing a model shape now to fill the gap.
*/
export async function seedHints(): Promise<void> {
console.log(" (skipped — no Hint model exists yet)");
} No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

The stated reason for the no-op contradicts this PR's migration.

This PR adds prisma/migrations/20260804192440_add_hint/migration.sql, which creates hint levels, hint records, and player hint records. The comment claims no Hint model exists yet. The comment also references a seed:hints script, which the provided package.json does not define.

Update the comment to state the real reason for the no-op, or implement the seeder against the new hint models.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/seed-hints.ts` around lines 1 - 16, Update seedHints so its
documentation matches the repository state: remove the claim that no Hint model
or migration exists and the unsupported seed:hints script reference, then state
the actual reason the function remains a no-op. If no valid reason exists,
implement seeding using the newly added hint-related Prisma models instead.

This was referenced Aug 5, 2026
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