fix(app listing): attach before polling the scan — a bad image now fails in ~2s, not ~120s - #273
Open
ZacxDev wants to merge 1 commit into
Open
fix(app listing): attach before polling the scan — a bad image now fails in ~2s, not ~120s#273ZacxDev wants to merge 1 commit into
ZacxDev wants to merge 1 commit into
Conversation
…s in ~2s not ~120s
`civitai app listing set-icon|set-cover|add-screenshot` ran
ingest -> pollScan (up to 120s) -> attach. But the platform validates
GEOMETRY, ASPECT, MIME and BYTE SIZE at ATTACH, not at ingest:
`validateListingImage` runs inside `loadValidatedImage` BEFORE the
ingestion-status gate, and all three attach procs pass `allowPending: true`,
so a still-scanning image is written and flagged `scanPending` rather than
refused. An author with a 512x256 icon therefore waited out the entire
content scan before hearing
`icon must be square-ish (aspect 2.00 outside 0.9-1.1)`.
Reordered to ingest -> attach -> pollScan. The CLI vendors no platform
constants for this and does not need to: it asks sooner and relays the
server's own message.
The scan is still waited on, so nothing reports success on a pending or
blocked scan:
- The poll is driven by the server's own `scanPending` flag. The attach proc
sets it only on the still-scanning branch and omits the key once
`ingestion == Scanned`, so an already-scanned attach skips the poll
entirely.
- A terminal-blocked image still throws BAD_REQUEST at attach and that is the
single message the user sees; a scan that flips to Blocked afterwards is
caught by the post-attach poll. Exactly one verdict either way.
- Attaching first means a blocked image can be written before the verdict
arrives, which the old order made impossible. A failure now states what it
left behind: a live listing's revision is not submitted (and says so), and
a blocked screenshot's failure names the `rm-screenshot <id>` that removes
it.
- `{status:"pending"}` (the legacy `allowPending:false` shape) writes nothing.
Today's procs never return it, but attaching first would silently no-op and
print success if that changed, so the CLI falls back to the pre-#270 order
and re-attaches.
Tests: 8 new cases in `internal/cmd/app_listing_test.go`, all measured RED at
origin/main (1eb4095) and GREEN at HEAD. The load-bearing one asserts the
observed call SEQUENCE against the fake server, not that both calls happened
- the pre-change order made both calls too.
`TestAppListingSetIconBlockedNoAttach` is inverted into
`TestAppListingBlockedDuringPostAttachPoll`: its "setIcon must NOT be called
after a blocked scan" premise is what this change reverses.
Mutations verified to kill a test for their own reason: reverting the order
(the sequence guard), always-poll (the fast-path guard), never-poll (four
guards), dropping the legacy-pending fallback, and silencing each
`scanFailure` context line.
Refs #270
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ZacxDev
force-pushed
the
zach/270-attach-before-scan
branch
from
August 7, 2026 21:41
3359296 to
5f49fa7
Compare
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.
Sub-item 4 of #270 — the CLI-side reorder, which needs nothing from the platform.
The problem
internal/cmd/app_listing.goran the media-attach flow as ingest →pollScan(up toscanPollTimeout= 120s) → attach.The platform validates geometry, aspect, MIME and byte size at ATTACH, not at ingest —
validateListingImageruns insideloadValidatedImagebefore the ingestion-status gate, and all three attach procs (setListingIcon/setListingCover/addListingScreenshot) passallowPending: true, so an image whose scan is still in flight is written and flaggedscanPending, not refused.So an author with a 512×256 icon uploaded, waited out the entire content scan, and only then got:
The fix
ingest → attach →
pollScan. The verdict now arrives in about one round-trip.No platform constants are vendored — that is the point. The CLI does not predict the rejection, it asks sooner and relays the server's own message verbatim. The existing local byte caps and the
DecodeImageInfopreflight are untouched.Keeping the scan signal
The scan is still waited on, so success is never reported on a pending or blocked scan.
scanPending.AttachResultalready carried the field; it is now load-bearing rather than diagnostic. The attach proc sets it only on the still-scanning branch and omits the key onceingestion == Scanned, so absent and false mean the same thing and an already-scanned attach skips the poll entirely.Blocked/NotFoundimage still throwsBAD_REQUESTat attach and that error is what the user sees (the CLI's own blocked-scan wording does not also fire). A scan that flips toBlockedafter the attach is caught by the post-attach poll instead. A test asserts the double does not happen.civitai app listing rm-screenshot <id>;{status: "pending"}is the legacyallowPending: falseshape and writes nothing. Today's procs never return it, but attaching first would silently no-op and print success if that ever changed — so the CLI falls back to the pre-App listing media: no published dimension or aspect requirements, and no drift-proof way to learn them #270 order (wait out the scan, then re-attach) rather than lie.Test matrix — red at
origin/main, green at HEADEight new cases in
internal/cmd/app_listing_test.go, each run against unmodifiedorigin/mainproduction code (1eb4095) with the new test file dropped in:origin/mainTestAppListingAttachIsObservedBeforeScanPoll… ingest scan scan setIcon …TestAppListingGeometryRejectionDoesNotWaitOutTheScansetIconnever calledTestAppListingBlockedAtAttachIsReportedTestAppListingBlockedDuringPostAttachPollsetIconnever calledTestAppListingScreenshotBlockedNamesItsRemovaladdScreenshotnever calledTestAppListingSkipsScanPollWhenAttachReportsScannedTestAppListingLegacyPendingAttachFallsBackToScanFirstsetIconcall, no retryTestAppListingLiveScanFailureLeavesTheRevisionUnsubmittedBase run:
RUN=21 PASS=8 FAIL=8. HEAD:RUN=21 PASS=21 FAIL=0.The load-bearing assertion is the sequence, not a pair of booleans — the pre-change order made both calls too.
callLog.requireBeforefails if either call is missing, so it cannot pass vacuously.TestAppListingSetIconBlockedNoAttachis inverted intoTestAppListingBlockedDuringPostAttachPoll: its premise ("setIcon must NOT be called after a blocked scan") is exactly what this change reverses, and the replacement pins that the blocked signal survives the move.Mutations (each killed by the test that owns it)
scanPending)…SkipsScanPollWhenAttachReportsScannedonlySetIconDraft,…AttachIsObservedBeforeScanPoll,…BlockedDuringPostAttachPoll,…ScreenshotBlockedNamesItsRemoval,…LiveScanFailure…pendingfallback…LegacyPendingAttachFallsBackToScanFirstonlyscanFailurecontext line…ScreenshotBlockedNamesItsRemoval+…LiveScanFailure…Full suite
make cigreen.go test ./... -v -count=1: RUN=2699, PASS=2695, FAIL=0, SKIP=4 (the 4 skips are the pre-existing env-gatedTestScanDirFromEnv,TestSubmissionsCapDriftAgainstLiveAPI,TestScaffoldPinsSatisfyPublished,TestScaffoldedReadyAckActuallyFires). Nopanic: test timed outin the log.gofmt -s -lprints nothing.Also updated
README.mdand theapp listinghelp text described the old order ("wait for the content scan, and attach it"). The README now says the platform's dimension/aspect rules are the platform's, that the CLI deliberately does not publish or enforce them, and that the order is what makes the rejection fast.Refs #270
🤖 Generated with Claude Code
Rebase note. The branch was originally cut from a stale
main(aeceb6b) and has been rebased onto1eb4095. #268 had trimmed theapp listingcommand-reference row in the same lines this PR touched — a real textual conflict, resolved in favour of #268's shortened row (the attach-order detail belongs in the prose section, where it now lives, not in the scannable table). Full suite re-run on the rebased tree: RUN=2717, PASS=2713, FAIL=0, SKIP=4,gofmt -s -lclean.