Skip to content

feat(exercise): English-only exercise data - #90

Merged
mohammadp1001 merged 1 commit into
mainfrom
feat/75-english-only-exercises
Aug 22, 2026
Merged

feat(exercise): English-only exercise data#90
mohammadp1001 merged 1 commit into
mainfrom
feat/75-english-only-exercises

Conversation

@mohammadp1001

Copy link
Copy Markdown
Owner

Closes #75

Stacked on #89 (the catalog). Retarget once that merges.

Why

Exercise records carried paired Persian and English columns for name,
description, tips and mistakes; Program and ProgramDay carried paired names
too. The catalog makes that redundant and expensive - all 649 of its entries
would need both languages written and maintained by hand, and the upstream
source has only English.

Scope limit, deliberate

The app's interface stays bilingual. Buttons, labels and screens still
switch to Persian from messages/, Persian is still the default locale, and RTL
layout is untouched. Only content collapses.

Columns are dropped rather than merged - there is no data to preserve.

The two things worth more than the column change

1. The name resolver collapses to a single findUnique.

It used to be a two-step dance: nameFa by unique key, then nameEn with an
explicit orderBy: { id: "asc" } - because nameEn was not unique, and without
that tie-break a program could silently rebind to a different exercise between
uploads (#45). One name under @@unique([userId, name]) removes the ambiguity
that bug lived in, so the tie-break has nothing left to protect.

2. AmbiguousExerciseError is deleted outright.

It existed because a non-unique nameEn could match several rows and the strict
resolver had to refuse rather than quietly report the wrong lift's numbers. A
name now matches at most one row, so the situation cannot arise. The tests that
described it are rewritten to assert the constraint that replaced it.

InfoPanel also loses its locale prop, which existed only to pick a language
per field.

Verified in the running app

Not only in tests. Uploaded the real strength_upper_v9.yaml, then checked:

  • Persian UI chrome renders (لاگ وزنه, پیشرفت, پروفایل, یادداشت برای مربی)
  • dir="rtl" on the Persian page, dir="ltr" on the English one
  • English exercise names sit correctly inside the Persian page
  • no undefined, NaN or [object Object] reaches the rendered markup

That last check matters: a mechanical rename across 25 files is exactly the kind
of change that types accept and screens quietly break on.

Tests

363 passing, 0 failing. tsc --noEmit and eslint clean.

Obsolete two-name tests are rewritten rather than deleted - each now asserts the
property that survived (per-account name scoping, binding to an existing row
instead of minting a duplicate, the unique constraint refusing a second row).

@mohammadp1001
mohammadp1001 changed the base branch from feat/72-exercise-catalog to main August 22, 2026 22:27
@mohammadp1001
mohammadp1001 force-pushed the feat/75-english-only-exercises branch from 46084bd to 7f33432 Compare August 22, 2026 22:27
Exercise records carried paired Persian and English columns for name,
description, tips and mistakes, and `Program`/`ProgramDay` carried paired names
too. The catalog makes the pairing redundant and expensive: all 649 of its
entries would need both languages written and maintained by hand, and the
upstream source has only English.

The app's *interface* stays bilingual. Buttons, labels and screens still switch
to Persian from `messages/`, Persian is still the default locale, and RTL layout
is untouched. Only content collapses.

Columns are dropped rather than merged - there is no data to preserve.

Two things fall out of this that are worth more than the column change itself.

The name resolver collapses to a single `findUnique`. It used to be a two-step
dance: `nameFa` by unique key, then `nameEn` with an explicit `orderBy: { id:
"asc" }`, because `nameEn` was not unique and without that tie-break a program
could silently rebind to a different exercise between uploads (#45). One name
under `@@unique([userId, name])` removes the ambiguity that bug lived in, so the
tie-break has nothing left to protect.

`AmbiguousExerciseError` is deleted outright. It existed because a non-unique
`nameEn` could match several rows and the strict resolver had to refuse rather
than quietly report the wrong lift's numbers. A name now matches at most one
row, so the situation cannot arise. The tests that described it are rewritten to
assert the constraint that replaced it.

`InfoPanel` loses its `locale` prop, which existed only to choose a language per
field.

Verified in the running app, not only in tests: uploaded the real program YAML,
then checked that Persian UI chrome still renders, `dir="rtl"` is intact, and
English exercise names sit correctly inside the Persian page with no `undefined`
or `[object Object]` reaching the markup.
@mohammadp1001
mohammadp1001 force-pushed the feat/75-english-only-exercises branch from 7f33432 to 784d0f1 Compare August 22, 2026 22:37
DROP COLUMN "tipsFa",
ADD COLUMN "description" TEXT NOT NULL DEFAULT '',
ADD COLUMN "mistakes" TEXT[] DEFAULT ARRAY[]::TEXT[],
ADD COLUMN "name" TEXT NOT NULL,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: Adding name as TEXT NOT NULL without DEFAULT will fail on populated tables

After dropping nameFa/nameEn, existing rows would receive NULL for the new column, violating the NOT NULL constraint. Add a DEFAULT '' or make the column nullable first, populate it, then set NOT NULL.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

-- AlterTable
ALTER TABLE "Program" DROP COLUMN "nameEn",
DROP COLUMN "nameFa",
ADD COLUMN "name" TEXT NOT NULL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: Adding name as TEXT NOT NULL without DEFAULT will fail on populated tables

After dropping nameFa/nameEn, existing rows would receive NULL for the new column, violating the NOT NULL constraint. Add a DEFAULT '' or make the column nullable first, populate it, then set NOT NULL.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

-- AlterTable
ALTER TABLE "ProgramDay" DROP COLUMN "nameEn",
DROP COLUMN "nameFa",
ADD COLUMN "name" TEXT NOT NULL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CRITICAL: Adding name as TEXT NOT NULL without DEFAULT will fail on populated tables

After dropping nameFa/nameEn, existing rows would receive NULL for the new column, violating the NOT NULL constraint. Add a DEFAULT '' or make the column nullable first, populate it, then set NOT NULL.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread lib/db/exercises.ts
{ nameEn: { contains: needle, mode: "insensitive" } },
{ nameFa: { contains: needle, mode: "insensitive" } },
{ name: { contains: needle, mode: "insensitive" } },
{ name: { contains: needle, mode: "insensitive" } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Duplicate condition in OR array - second entry is identical to the first

Both nameEn and nameFa were collapsed into a single name field, but both OR conditions were kept. The second condition is redundant.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread lib/db/exercises.ts
{ nameEn: { contains: longestWord, mode: "insensitive" } },
{ nameFa: { contains: longestWord, mode: "insensitive" } },
{ name: { contains: longestWord, mode: "insensitive" } },
{ name: { contains: longestWord, mode: "insensitive" } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Duplicate condition in OR array - second entry is identical to the first

Both nameEn and nameFa were collapsed into a single name field, but both OR conditions were kept. The second condition is redundant.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread lib/db/exercises.ts
{ nameEn: { contains: search, mode: "insensitive" } },
{ nameFa: { contains: search, mode: "insensitive" } },
{ name: { contains: search, mode: "insensitive" } },
{ name: { contains: search, mode: "insensitive" } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: Duplicate condition in OR array - second entry is identical to the first

Both nameEn and nameFa were collapsed into a single name field, but both OR conditions were kept. The second condition is redundant.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

@kilo-code-bot

kilo-code-bot Bot commented Aug 22, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 6 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 3
WARNING 3
Issue Details (click to expand)

CRITICAL

File Line Issue
prisma/migrations/20260823090000_english_only_exercise_data/migration.sql 20 ADD COLUMN "name" TEXT NOT NULL without DEFAULT fails on populated tables
prisma/migrations/20260823090000_english_only_exercise_data/migration.sql 26 ADD COLUMN "name" TEXT NOT NULL without DEFAULT fails on populated tables
prisma/migrations/20260823090000_english_only_exercise_data/migration.sql 31 ADD COLUMN "name" TEXT NOT NULL without DEFAULT fails on populated tables

WARNING

File Line Issue
lib/db/exercises.ts 118 Duplicate condition in OR array after nameFa/nameEn rename
lib/db/exercises.ts 143 Duplicate condition in OR array after nameFa/nameEn rename
lib/db/exercises.ts 197 Duplicate condition in OR array after nameFa/nameEn rename
Files Reviewed (30 files)
  • prisma/migrations/20260823090000_english_only_exercise_data/migration.sql - 3 issues
  • lib/db/exercises.ts - 3 issues
  • 28 other files - no issues

Fix these issues in Kilo Cloud


Reviewed by free · Input: 149.3K · Output: 24.5K · Cached: 1.6M

@mohammadp1001
mohammadp1001 merged commit 60880e5 into main Aug 22, 2026
2 checks passed
@mohammadp1001
mohammadp1001 deleted the feat/75-english-only-exercises branch August 22, 2026 23:03
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.

English-only exercise data

1 participant