* feat(licensing): turn the SellMerge write paths on
The contract half of yesterday's expand/contract. Every pod has known the label
for eleven hours and prod carries zero rows using it, so the paths that were
held back can open: the schema @default and the upload form's default set go
back to five values, the "Sell merges using this model" option returns, and the
refine that refused the member at the upsert contract is deleted.
Measured before writing this, prod replica: 424,641 models hold Sell and lack
SellMerge, and 0 hold SellMerge. Nothing wrote it while the paths were shut.
The two decision cases are INVERTED rather than deleted, because what they guard
was never really about SellMerge. Restrictions are emitted by ABSENCE, so a
member missing from a default set produces no clause and therefore GRANTS the
permission -- silently, with no badge a creator would notice and no type error.
Both now derive the expected set from the enum (Object.values minus None) rather
than from a hand-written list, so the next member added without wiring reddens.
A list would have needed updating by whoever forgot.
Both cases are renamed to what they now assert; a stale test name is a comment
that survives every refactor. The decision docblock is deleted rather than
reworded, since the decision expires with this PR.
Controls: dropping SellMerge from the schema default, from the form default, or
from the option list each redden, as does re-adding a withholding refine, as
does the field ceasing to validate members at all -- that last one is what stops
the accept-loop passing vacuously. An innocent comment reword stays green.
The backfill does NOT ride this PR. It runs after this is deployed, because
until then the refine rejects a save carrying the member and the edit form
resubmits it untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(licensing): assert the three sites AGREE, not that every member is granted
Five lanes on #4874; two findings were against my own work.
The docblock stated the absence rule backwards -- it claimed a member missing from
the granted array produces no clause and so GRANTS the permission. The emitter
returns '' when the array INCLUDES the member, so absence emits the clause and
RESTRICTS. The real hazard is one file over and about the clause map, not the
default set. The parent's commit message repeated the inversion.
The guard asserted a policy: both defaults equal to every enum member requires the
next CommercialUse addition to be granted to every model by default, which forbids
the expand/contract manoeuvre shipped eleven hours ago. Rewritten as the invariant
-- the two defaults and the option list must AGREE -- so withholding a new member
everywhere at once stays legal, while a member granted by default and missing from
the options is caught.
Three attempts, each caught by a control: a whole-file count broke because Rent is
a substring of RentCivit and Sell of SellMerge; a per-member count misfired on the
cascade's legitimate references.
The render-site guard pins that the list is mapped whole. It does NOT cover the
disabled predicate -- no text guard expresses that without naming members, which
reintroduces the policy. Stated in the test as a gap.
From the other lanes: the local-dev DDL was missing the enum value, so a fresh
checkout would reject the first model create; the migration header and the backfill
endpoint carried claims this PR made false.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* chore(seed): seed the five-value permission shape alongside the legacy ones
No seeded row granted SellMerge, so the sell/merge split was unexercisable in
local data. Tracked despite scripts/local-dev being gitignored -- the ignore
rule does not apply to already-tracked files.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(contests): gate model entries on allowed base models
Contest collections can now declare `metadata.baseModels`; a model entry
only qualifies if it has a live version on one of them. Empty/absent means
no gating, so existing contests are unaffected.
The date rule and the base-model rule are satisfied by a SINGLE version
predicate rather than two independent checks — otherwise a two-year-old
SDXL LoRA could qualify by pairing an old allowed-base-model version with
a throwaway version pushed during the submission window. The model block
also moved out of `if (metadata.submissionStartDate)` so base-model gating
still applies to windowless contests.
Moderators are not exempt, matching the neighbouring date/window checks
(the eligibility family) rather than `maxItemsPerUser` (an anti-spam quota
mods legitimately bypass when curating). A mod who wants an exception edits
the contest's allowed base models instead of side-stepping the rule.
* fix(contests): harden base-model gating and lock the where shape
Review follow-ups on the base-model gate:
- Guard the model-row date filter on submissionStartDate being set. It
was unreachable with undefined today, but Prisma's lt is optional so
undefined type-checks, and loosening the outer guard later would
silently drop the filter and let every model through.
- Validate metadata.baseModels against the known base models. The value
has to match ModelVersion.baseModel exactly; an unrecognized one like
'FluxKrea' matched no version and locked the contest to zero entries
while reporting a plausible-looking error.
- Spell out in the field description that with a submission window the
same version must satisfy both rules.
Adds tests covering the where shape, most importantly that the
no-gating path is unchanged -- a live contest with accepted entries
depends on it -- and that the date and base-model conditions stay in
one modelVersions.none object.
Draft versions still qualify. Requiring Published would re-block the 69
in-window draft Krea 2 versions the preceding commits deliberately
unblocked, and entries pass through moderator review anyway.
* fix(contests): source base models from basemodel.constants
The gate read the legacy base-model.constants.ts, whose list stops
short of current ecosystems -- 'Krea 2' is absent from it, so the very
contest this was built for could not be configured, and the zod refine
would have rejected the value outright.
basemodel.constants.ts is the live source (95 importers vs 4, and it
carries a compatibility layer explicitly marked as the migration target
for the old file). Points the schema and the picker at it, moves the
two remaining importers over, and deletes the legacy file.
The picker now offers every base model rather than the non-hidden ones.
Hidden means "not offered on upload", which is a different question
from whether a contest can target it -- SD 3.5, SDXL Turbo, Kling and
Sora 2 are all populated and were unselectable. The field is
moderator-only.
* docs: drop references to the removed base-model.constants
Both docs described the legacy module as current. The plan doc keeps
its original snippets with a note, since rewriting a historical plan
would misrepresent what was proposed.
The Prisma schema/migrations moved to packages/civitai-db-schema; point the build and migration tooling at the new path.
- Dockerfile: workspace-aware deps layer (copy pnpm-workspace.yaml + packages/*), schema paths -> packages/civitai-db-schema/prisma
- prisma-migrate-with-views-workaround / mark-migration-applied / prepare-programmability / local-dev/run_migrations: updated hardcoded prisma/ paths
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- switch back verbiage to featured
- adjust homeblock links and metadata
- move popularity to description table
- add isfeatured filter for models
- only show bid button if available and published
- insert winners into covered checkpoints
- adjust auction to handle run/valid for days
- fix some seed script data
- adjust generate button to always show bid or create
- hide markup for popularity
- update the tour
- remove coveredCheckpointDetails
- show resources like checkpoints
- blur nsfw model names
- fix delete bid setData
- fix top 3 featured style
- show mv popularity on model page
- add cache for popularity / isFeatured
- add pop/feat to model index
- fix additional price needed logic
- update civ client
- add a basic test
- add some test-ids
- add new log in provider in dev only for testing
- dont rate limit in dev
- remove testids from production builds
- add test npm scripts
- user auth utils
- setup script
- example tests
- playwright config