feat(media): plugin-declared model capabilities drive the console - #13
Merged
Merged
Conversation
packages/database/src/migrate.ts had grown into one 1143-line script that mixes the original CREATE TABLE baseline with twenty-one feature eras of incremental ALTERs, data backfills and destructive drops, and re-executes all of it on every deploy. Nothing could tell an operator which parts had actually been applied, and nothing guarded a future edit from losing a statement. This commit adds the sequential migration set and its runner without switching anything over: migrate.ts still runs exactly as before, so the change is inert. - migrations/0001..0026.sql reproduce the historical statement stream verbatim and in order. Cuts never separate an unguarded DROP from the ADD that depends on it, and the ALTER TYPE ADD VALUE stays in its own committed transaction because a later CHECK uses the new enum value. - src/migrations/loader.ts normalizes line endings before hashing, so the CRLF working tree and the LF container agree on a checksum. - src/migrations/runner.ts holds one session advisory lock for the whole run and applies each file in its own transaction. - migrations.test.ts proves the split is lossless against a frozen golden master of the old statement stream, with no database required, so a dropped statement fails CI in milliseconds.
… from them
Media parameters were invented by the host instead of by the plugins. The API
synthesized size/quality descriptors from flat model_configs columns, route.ts
validated image requests against a `{type:'text',maxLength:32}` stub, and the
console hardcoded five size options -- two of which no configured model accepts.
`size:"9999x9999"` therefore passed the boundary, queued a job, and failed at the
vendor, so users saw a dead task instead of a form error naming the field.
Provider manifests are now the single source of truth. Each media model declares
its parameter descriptors, geometry limits, dependencies and edit capabilities,
and one validator in @musecanvas/contracts is shared by the browser, the API, the
domain normalizer and the plugin adapters -- so a rendered control and an
acceptable value are the same set by construction rather than three lists that
can disagree.
- image-size descriptors carry presets plus a custom-size band; the picker
validates live and blocks submit before a 400 comes back
- dependsOn and cross-field rules hide output_compression for lossless formats
and reject background=transparent with jpeg, in the API and adapter too
- model switches reconcile to the new contract instead of sending stale values
- retries re-validate against the revision the job is pinned to
- a model with no declared contract is refused, never guessed at
- migrate.ts appends manifest-declared revisions; historical jobs keep rendering
the parameters they were created with
apps/web-next also stopped hand-copying the descriptor types from contracts, and
CI's integration step now takes a glob instead of one filename, so new tests under
tests/integration/ actually run.
This commit bundles in-flight work from concurrent sessions (region-select image
editing, the plugin-upload catalog, and the route.ts -> modules/router split).
Their hunks are interleaved with these changes inside shared files and this tree
does not compile without their untracked modules, so the threads could not be
separated; that work is not verified by me beyond the suite passing as a whole.
Co-Authored-By: Qoder <noreply@qoder.com>
…eout `importBundle` unrefs its timeout on purpose, so a bundle that never settles leaves the test file holding no pending handle at all: the never-resolving import promise owns none and the unref'd timer refuses to own one. The loop drains, Node exits, and every in-flight test is reported as "Promise resolution is still pending but the event loop has already resolved". libuv kept lingering fs handles alive on Windows, which masked it; on Linux CI four loader tests were cancelled by the parent process exiting. A ref'd interval cleared in `after` reproduces the invariant the real worker has -- something always owns the loop -- without weakening the production unref that stops a wedged plugin from holding the process open.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GET /api/modelsas parameter descriptors; the console renders, validates and submits from that metadata with no provider/model special-casing anywhere inapps/web-next.@musecanvas/contractsis shared by the browser, the API, the domain normalizer and the plugin adapters, so a rendered control and an accepted value are the same set by construction. Addsimage-sizepresets + custom-size bands,dependsOn, cross-field rules and capability flags; extends the existingParameterDescriptorunion rather than creating a parallel type.1024x768/768x1024, which no configured model accepts;route.tsvalidated imagesize/qualityas{type:'text',maxLength:32}so9999x9999reached the vendor and failed there; andqualityhad no UI at all (its setter had zero call sites).apps/web-nextno longer hand-copies the descriptor types from contracts, andpublicModelDtois now contract-typed, so frontend/backend drift is a compile error instead of a silent cast.route.ts->modules/routersplit). Those hunks are interleaved inside shared files and the tree does not compile without their untracked modules.Test plan
pnpm typecheck— 0 errors repo-widepnpm test— 429 passing, 0 failingtests/integration/*.test.ts— 23 passing, 0 failing (CI step changed from a single filename to a glob so these actually run)next build— succeeds,/generate22.7 kB.envconfirmed ignoredmigrate.tssection 10b has never been executed; verify before deployingNotes
1280x720on Seedream 4.5 previously passed the API and failed at the plugin; it is now rejected at submit with a named-parameter business error.dall-e-3is markeddeprecatedand sorts last;gpt-image-1/gpt-image-1-miniare deliberately absent because no authoritative parameter table could be confirmed.