diff --git a/docs/design-project-metadata-rework-2026-05-20.md b/docs/design-project-metadata-rework-2026-05-20.md index b6d278f..2fe3345 100644 --- a/docs/design-project-metadata-rework-2026-05-20.md +++ b/docs/design-project-metadata-rework-2026-05-20.md @@ -144,10 +144,10 @@ Existing `'court'`/`'both'` switch arms get deleted (no live rows; if a stale `our_side='court'` slipped through somehow, the function returns `""` — same fallback as today for unknown values). -### §2.3 Migration `111_client_role_rework` +### §2.3 Migration `112_client_role_rework` ```sql --- 111_client_role_rework.up.sql (renumbered 2026-05-20 — mig 110 was claimed by m/paliad#51 project_type_other) +-- 112_client_role_rework.up.sql (renumbered 2026-05-20 — mig 110 was claimed by m/paliad#51, mig 111 by m/paliad#48) -- t-paliad-222 / m/paliad#47. -- Widens projects.our_side CHECK to seven sub-role values and drops -- the legacy 'court' / 'both' entries. Backfill is a no-op on the @@ -442,10 +442,10 @@ projects), introduce a materialised view `paliad.projects_derived_codes(project_id, derived_code)` refreshed by trigger on `projects` writes. Don't pre-optimise. -### §3.3 Migration `112_projects_opponent_code` +### §3.3 Migration `113_projects_opponent_code` ```sql --- 112_projects_opponent_code.up.sql (renumbered 2026-05-20) +-- 113_projects_opponent_code.up.sql (renumbered 2026-05-20) -- t-paliad-222 / m/paliad#50. -- Add an opponent-code field on litigation projects. Used as the -- middle segment when assembling auto-derived project codes from the @@ -643,8 +643,8 @@ material pushes back. Coder shift only after head signs off.) ## §5 Implementation order (coder phase) -1. **Mig 111** (client role widen + backfill) → mig 112 (opponent_code). - *Renumbered 2026-05-20 — mig 110 was claimed by m/paliad#51 project_type_other; boltzmann's gap-tolerant runner hard-fails on collisions so this is a strict rebump.* +1. **Mig 112** (client role widen + backfill) → mig 113 (opponent_code). + *Renumbered twice on 2026-05-20 — mig 110 claimed by m/paliad#51 project_type_other; mig 111 claimed by m/paliad#48 project_admin_and_select; boltzmann's gap-tolerant runner hard-fails on collisions so this is a strict rebump.* Run `ls internal/db/migrations/ | tail` first to verify slot availability (boltzmann's gap-tolerant runner means 110 is fine even if 109 was the last applied). diff --git a/frontend/src/i18n-keys.ts b/frontend/src/i18n-keys.ts index e6c73e2..ecdf235 100644 --- a/frontend/src/i18n-keys.ts +++ b/frontend/src/i18n-keys.ts @@ -2163,6 +2163,19 @@ export type I18nKey = | "projects.field.billing_reference" | "projects.field.case_number" | "projects.field.client_number" + | "projects.field.client_role" + | "projects.field.client_role.appellant" + | "projects.field.client_role.applicant" + | "projects.field.client_role.claimant" + | "projects.field.client_role.defendant" + | "projects.field.client_role.group.active" + | "projects.field.client_role.group.other" + | "projects.field.client_role.group.reactive" + | "projects.field.client_role.hint" + | "projects.field.client_role.other" + | "projects.field.client_role.respondent" + | "projects.field.client_role.third_party" + | "projects.field.client_role.unset" | "projects.field.clientmatter.hint" | "projects.field.collaborators" | "projects.field.collaborators.hint" @@ -2180,13 +2193,21 @@ export type I18nKey = | "projects.field.matter_number" | "projects.field.netdocuments_url" | "projects.field.office" + | "projects.field.opponent_code" + | "projects.field.opponent_code.hint" + | "projects.field.opponent_code.placeholder" | "projects.field.our_side" + | "projects.field.our_side.appellant" + | "projects.field.our_side.applicant" | "projects.field.our_side.both" | "projects.field.our_side.claimant" | "projects.field.our_side.court" | "projects.field.our_side.defendant" | "projects.field.our_side.hint" | "projects.field.our_side.none" + | "projects.field.our_side.other" + | "projects.field.our_side.respondent" + | "projects.field.our_side.third_party" | "projects.field.our_side.unset" | "projects.field.parent" | "projects.field.parent.hint" diff --git a/internal/db/migrations/111_client_role_rework.down.sql b/internal/db/migrations/112_client_role_rework.down.sql similarity index 95% rename from internal/db/migrations/111_client_role_rework.down.sql rename to internal/db/migrations/112_client_role_rework.down.sql index 578c599..3f3dfd2 100644 --- a/internal/db/migrations/111_client_role_rework.down.sql +++ b/internal/db/migrations/112_client_role_rework.down.sql @@ -1,4 +1,4 @@ --- Down migration for 111_client_role_rework. +-- Down migration for 112_client_role_rework. -- -- Restores the original 4-value CHECK ('claimant','defendant', -- 'court','both', NULL) and backfills any rows that landed on a new diff --git a/internal/db/migrations/111_client_role_rework.up.sql b/internal/db/migrations/112_client_role_rework.up.sql similarity index 96% rename from internal/db/migrations/111_client_role_rework.up.sql rename to internal/db/migrations/112_client_role_rework.up.sql index 73e80ad..080deac 100644 --- a/internal/db/migrations/111_client_role_rework.up.sql +++ b/internal/db/migrations/112_client_role_rework.up.sql @@ -1,4 +1,4 @@ --- mig 111 — t-paliad-222 / m/paliad#47 — Client Role rework. +-- mig 112 — t-paliad-222 / m/paliad#47 — Client Role rework. -- -- Widens paliad.projects.our_side CHECK to seven sub-role values and -- drops the legacy 'court' / 'both' entries. The DB column name stays diff --git a/internal/db/migrations/112_projects_opponent_code.down.sql b/internal/db/migrations/113_projects_opponent_code.down.sql similarity index 77% rename from internal/db/migrations/112_projects_opponent_code.down.sql rename to internal/db/migrations/113_projects_opponent_code.down.sql index 4f39ae9..16fc578 100644 --- a/internal/db/migrations/112_projects_opponent_code.down.sql +++ b/internal/db/migrations/113_projects_opponent_code.down.sql @@ -1,4 +1,4 @@ --- Down migration for 112_projects_opponent_code. +-- Down migration for 113_projects_opponent_code. BEGIN; diff --git a/internal/db/migrations/112_projects_opponent_code.up.sql b/internal/db/migrations/113_projects_opponent_code.up.sql similarity index 96% rename from internal/db/migrations/112_projects_opponent_code.up.sql rename to internal/db/migrations/113_projects_opponent_code.up.sql index 18bdb72..c59513d 100644 --- a/internal/db/migrations/112_projects_opponent_code.up.sql +++ b/internal/db/migrations/113_projects_opponent_code.up.sql @@ -1,4 +1,4 @@ --- mig 112 — t-paliad-222 / m/paliad#50 — auto-derived project codes. +-- mig 113 — t-paliad-222 / m/paliad#50 — auto-derived project codes. -- -- Adds an opponent-code slug field on litigation projects. Used as -- the middle segment when BuildProjectCode assembles an auto-derived diff --git a/internal/models/models.go b/internal/models/models.go index c4d0f4c..a433eb6 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -161,7 +161,7 @@ type Project struct { // chip from the project context (t-paliad-164). NULL = unknown / // not set; Determinator falls back to free-pick. // - // Allowed sub-roles (mig 111, t-paliad-222): + // Allowed sub-roles (mig 112, t-paliad-222): // Active : claimant, applicant, appellant // Reactive : defendant, respondent // Other : third_party, other @@ -177,7 +177,7 @@ type Project struct { // assembles an auto-derived project code from the ancestor tree — // e.g. EXMPL.OPNT.567.INF.CFI (t-paliad-222 / m/paliad#50). NULL // → segment skipped silently. Only meaningful on type='litigation' - // rows; CHECK constraint (mig 112) enforces the pairing. + // rows; CHECK constraint (mig 113) enforces the pairing. OpponentCode *string `db:"opponent_code" json:"opponent_code,omitempty"` // Code is the auto-derived (or override) project code, computed at diff --git a/internal/services/project_code_test.go b/internal/services/project_code_test.go index 58f54c1..0c6cdb8 100644 --- a/internal/services/project_code_test.go +++ b/internal/services/project_code_test.go @@ -351,7 +351,7 @@ func TestValidateOpponentCode(t *testing.T) { } } -// TestValidateOurSideSubRoles pins the widened allowlist (mig 111). +// TestValidateOurSideSubRoles pins the widened allowlist (mig 112). func TestValidateOurSideSubRoles(t *testing.T) { valid := []string{ "", "claimant", "defendant", "applicant", "appellant", diff --git a/internal/services/project_service.go b/internal/services/project_service.go index 133bbdd..e9cac54 100644 --- a/internal/services/project_service.go +++ b/internal/services/project_service.go @@ -1437,7 +1437,7 @@ func insertCounterclaimEvent(ctx context.Context, tx *sqlx.Tx, projectID, userID // Third Party / Other (third_party, other) and NULL pass through // unchanged — the flip is meaningless without a clear active / reactive // posture. Legacy 'court' / 'both' no longer exist in the column -// (mig 111) so they have no case arm; if a stale value sneaks in via a +// (mig 112) so they have no case arm; if a stale value sneaks in via a // pre-migration in-memory row it falls through to the default branch // and passes through unchanged, preserving previous behaviour. // @@ -1979,7 +1979,7 @@ func validateProjectStatus(s string) error { // (t-paliad-164, widened in t-paliad-222 / m/paliad#47). Empty string // is the explicit "clear" sentinel — callers pass the value as-is // from the form payload, and the helper accepts it so an Update can -// null the column. The DB-level CHECK constraint (mig 111) enforces +// null the column. The DB-level CHECK constraint (mig 112) enforces // the same set; this validation gives a clearer error than relying // on the constraint to fire. // @@ -1988,7 +1988,7 @@ func validateProjectStatus(s string) error { // Reactive (we defend) : defendant, respondent // Third Party / Other : third_party, other // -// Legacy 'court' / 'both' are no longer accepted (mig 111 backfills +// Legacy 'court' / 'both' are no longer accepted (mig 112 backfills // existing rows to NULL); callers that still send them get a clear // validation error rather than a constraint violation. func validateOurSide(s string) error { @@ -2049,7 +2049,7 @@ func nullableOurSide(p *string) any { } // opponentCodePattern matches the slug shape enforced by the -// projects_opponent_code_check constraint (mig 112): uppercase letters, +// projects_opponent_code_check constraint (mig 113): uppercase letters, // digits, dashes, 1-16 chars. The DB CHECK is the source of truth; this // helper surfaces a friendlier ErrInvalidInput error before the write. var opponentCodePattern = regexp.MustCompile(`^[A-Z0-9-]{1,16}$`) diff --git a/internal/services/submission_render_test.go b/internal/services/submission_render_test.go index afbb5de..284d12a 100644 --- a/internal/services/submission_render_test.go +++ b/internal/services/submission_render_test.go @@ -276,7 +276,7 @@ func TestLegalSourcePretty(t *testing.T) { // mapping used by addProjectVars. Post t-paliad-222: seven sub-role // values + the gender-neutral "-Seite" / "-Partei" suffix shape on // DE. Legacy 'court' / 'both' yield "" (the column no longer accepts -// them after mig 111, but the function defensively handles stale +// them after mig 112, but the function defensively handles stale // in-memory values from older callers). func TestOurSideTranslations(t *testing.T) { cases := []struct {