docs(#270): publish the real listing-media requirements, scaffold assets/, fix the broken quickstart - #275
Open
ZacxDev wants to merge 2 commits into
Open
docs(#270): publish the real listing-media requirements, scaffold assets/, fix the broken quickstart#275ZacxDev wants to merge 2 commits into
ZacxDev wants to merge 2 commits into
Conversation
…ents, scaffold assets/ The quickstart handed authors `civitai app listing set-icon ./assets/icon.png` — a path no template creates, so the one copy-pasteable command in that step could not work (measured: exit 2, `no such file: ./assets/icon.png`, on a fresh scaffold of all three templates). - Every template now scaffolds `assets/` with a README of the requirements, and deliberately NO placeholder images: a placeholder passes format + byte validation and uploads cleanly, which is how a stub icon reaches a public listing, whereas a missing file fails loudly at the step that can still fix it. - README: new "Listing media requirements" section (TOC-linked) splitting what the CLI enforces locally (format + byte cap) from what the platform enforces server-side at attach (aspect + minimum dimension), with the current bounds read from `civitai/civitai` `src/server/schema/blocks/app-listing.schema.ts` (identical on main and release). Also records that icons are re-encoded/downscaled to <=1024px server-side while covers and screenshots are not. - AGENTS.md item 25: those bounds stay prose. Stale guidance costs one round-trip carrying the server's current bound; a stale local gate refuses valid images. Also records that the CLI's 2 MiB icon cap and the platform's 1 MiB icon cap measure DIFFERENT bytes (source file vs the server's re-encoded PNG), so the two must not be conflated. Refs #270
Both surfaces were individually fine and broken together: the README named
./assets/icon.png, no template created assets/, and every test was scoped to one
side. So these guards pin the RELATIONSHIP.
- internal/cmd/listing_media_docs_test.go
- TestREADMEListingMediaPathsExistInEveryScaffoldedProject: every relative path
the README hands an author must have its parent directory present in a
project the CLI actually scaffolds, for EVERY template. It asserts the
DIRECTORY, never the file — demanding assets/icon.png would be demanding the
placeholder hazard. Positive control: >= 4 extracted invocations, and a
failure if every path collapses to the project root.
- TestREADMEListingByteCapsMatchTheCLIConstants: code<->prose drift guard. The
aspect/dimension bounds are platform constants the CLI does not vendor, so
nothing can check them; the BYTE caps are this CLI's own gate, so the README
quoting a number the binary does not enforce is checkable and is checked.
- internal/scaffold/assets_dir_test.go: every template scaffolds assets/README.md
and NO image file (extension ledger, so logo.png is caught as well as
icon.png), plus the per-kind bounds an author sizes artwork from.
Red at origin/main (1eb4095), green at HEAD: 12 RUN / 0 PASS / 12 FAIL there vs
2719 RUN / 2715 PASS / 0 FAIL / 4 SKIP (pre-existing env-gated) here.
Mutation-verified, each killed by its OWN message: planting assets/icon.png ->
"scaffolds an IMAGE at assets/icon.png"; README icon cap 2 -> 3 MiB ->
"README says ... but the CLI enforces 2 MiB"; assets/README.md renamed away ->
"has assets/ but no assets/README.md"; dropping "server-side" from the README
section and "no images, on purpose" from the template README -> their own
framing errors.
Also records in assets/README.md that the directory is not excluded from
`civitai app submit`, so its contents ride along in the source bundle (measured:
37 files with an icon and cover present). Not excluding it is deliberate — a
static app may legitimately serve images from assets/.
Refs #270
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.
Closes the CLI-side half of #270: the documentation and the
assets/scaffold. The attach-order reorder is a separate lane and is not in here.The issue's premise was half wrong, and this PR follows the corrected version
The issue says the platform's dimension requirements "are not published". They are — they are named constants in
civitai/civitaisrc/server/schema/blocks/app-listing.schema.ts, enforced server-side at attach byvalidateListingImage, with rejection messages that name the bound and the measured value. Re-read for this PR and byte-identical onmainandrelease, so these are what an author hits today:One correction to those numbers, because they cannot go in the docs as written. The icon's 1 MiB (
MAX_LISTING_ICON_SIZE_BYTES) and the CLI's 2 MiB (maxIconBytes) measure different bytes, and conflating them would have documented a cap that does not apply to the author's file. The icon rides the inline data-URI path: the server decodes it, downscales to ≤1024px and re-encodes to PNG, then storesmetadata.size = png.byteLength(listing-meta.service.ts) — and that is whatvalidateListingImagecompares against 1 MiB. The CLI's 2 MiB is the source file, mirroringINLINE_ICON_MAX_DECODED_BYTES. Cover and screenshot take the full-res path where the CLI sendssizeBytes: len(data), so there the two caps do describe the same bytes. The README documents the CLI's caps for the file you pass, and the aspect/dimension table separately as server-side. AGENTS.md item 25 records why.What changed
assets/is scaffolded by all three templates, with a README of the requirements and no placeholder images. A placeholder passes format + byte validation and uploads cleanly — that is how a stub icon reaches a public listing, whereas a missing file fails loudly at the one step that can still fix it../shot.pngbecame./assets/screenshot-1.png.internal/cmdorinternal/validate.assets/README.md.Verification
Executed the quickstart rather than reading it, with the binary built from this branch:
Then, after saving a real 512×512 icon as
assets/README.mdinstructs, the same command clears every local check and reaches the network (run against an offline base URL so nothing touched prod):On
origin/main(1eb4095) that same command exited 2 withno such fileon a fresh scaffold of all three templates — that is the bug, reproduced and then removed. Positive controls on the local gate, which is what the README now claims it does: a non-image getsunrecognized image (want png, jpeg, or webp), and a 2.9 MB icon getslarger than the 2.0 MB max for a icon.Red/green matrix for the new guards — red at
origin/main(1eb4095) with the test files copied into a clean worktree of that ref, green at HEAD:The 4 skips are pre-existing environment-gated guards (
CIVITAI_ANTIPATTERN_SCAN_DIR,CIVITAI_CHECK_SUBMISSIONS_CAP,CIVITAI_CHECK_PUBLISHED_PINS,CIVITAI_CHECK_SCAFFOLD_RUNTIME). No timeout panics.Mutation-verified, each mutant killed by its own message, not a neighbour's:
assets/icon.pngin a templatetemplate "page-vite" scaffolds an IMAGE at assets/icon.pngREADME says "…≤ 3 MiB…" for icon, but the CLI enforces 2 MiBassets/README.md.tmplawayhas assets/ but no assets/README.md (entries: [NOTES.txt])the Listing media requirements section never says "server-side"does not explain that the directory ships with no images on purposeThe one direction I did not mutate is changing
maxIconBytesitself, because that lives ininternal/cmd/app_listing.go, which another lane is editing. The guard derives its expectation from the constant, so both directions diverge by construction, but only the README side is empirically demonstrated.Other gates:
gofmt -s -l .clean,go vet ./...clean, golangci-lint v2.12.2 (the exact CI pin, run from nixpkgs) → 0 issues, and the offline half ofscaffold-currencyreproduced locally (app init --template page-money→TestScanDirFromEnv→ no anti-patterns found). The npm halves ofscaffold-currency/template-page-vite/template-page-moneyare untouched by adding one markdown file.Two things I found and did not fix
internal/cmd/app_listing.gostill says "a square-ish image" inset-icon --help(and "a landscape hero image" inset-cover). Deliberately left alone — another lane holds that file for the attach-order reorder. Follow-up owed once it merges: replace bothShortstrings with what the CLI actually enforces plus a pointer to the README section, so--helpstops being the last surface repeating the phrase this PR removed everywhere else.humanByteslabels MiB values as MB — the CLI prints2.0 MBfor2*1024*1024. The README's "2 MiB" is the correct label. Cosmetic, and the fix belongs indownload.go, which is shared with the download UI, so it is not smuggled in here.Also worth knowing:
assets/is not excluded fromcivitai app submit, so whatever an author leaves there ships in the source bundle (measured: 37 files with an icon and cover present). Left that way on purpose — astaticapp may legitimately serve images fromassets/— and stated inassets/README.mdrather than silently changing packaging.Refs #270
🤖 Generated with Claude Code