docs(read): retract "not reproducible" — #513's coercion is live, and FlexString does not make the value correct - #556
Merged
Conversation
… FlexString does not make the value correct Two claims in item 37's evidence were false, and one consequence was undocumented. RETRACTED: `flexstring.go` and `37-numeric-username.md` both said the reported shape was not reproducible and that /api/v1/images "could not be exercised". Measured 2026-09-11: it coerces on every request reaching the Meilisearch feed branch of runImageSearch — 51/51 cache-busted responses unquoted, across 55 digit-username accounts with public images. WHY THE EARLIER NEGATIVE WAS WRONG, which is the transferable part: two independent causes. No account it sampled had public images, so the path was never exercised; and the quoted readings it did get were Cloudflare cache HITs, not origin responses. On a fresh cache key, 38 of 38 "quoted" readings flipped. That probe read raw bytes, avoided jq and ran a positive control — none of which can see a CDN. The retracted paragraph is preserved verbatim in a <details> block, because it guessed the mechanism correctly and was defeated only by not being able to measure it. MECHANISM, now known: the Meilisearch image index stored user.username under Meili's dynamic JSON typing. This NARROWS the field-set residual — TagItem.Name is not on that document and is no longer implicated — without closing it, since any other all-digit-capable field on the same document has the same exposure by construction. The trigger to widen is unchanged: an observation, not an argument. NEW RESIDUAL, and the reason this matters beyond bookkeeping: the coercion is NUMERIC, so leading zeros are destroyed upstream. A user named "0222" arrives as 222, and ?username=222 returns 0 items. FlexString decodes it cleanly — its whole job — but cannot recover digits the wire dropped, so it made the failure quieter rather than absent: an opaque exit 1 became a confident wrong username. Item 37 must not be read as "#513 is solved"; the decode half is. Server side filed as civitai/civitai#4768 with a closing condition. #513 stays open deliberately, and the reporter was told this rather than left to infer it from a merged PR. Refs #513 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TRHayuSRJFuHKZXk4GXstU
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.
Item 37's evidence carried two false claims and was missing one consequence. All three are now measured.
1. RETRACTED: "not reproducible"
pkg/civitai/flexstring.go:24-28andclaudedocs/decisions/37-numeric-username.mdboth stated the reported shape was not reproducible, and that/api/v1/images"could not be exercised at all".It reproduces on every request reaching the Meilisearch feed branch of
runImageSearch. Measured 2026-09-11 across 55 digit-username accounts with public images: 51/51 cache-busted responses unquoted, 0 quoted.Same user, same route, different internal path (
image-search.service.ts:127).2. Why the earlier negative was wrong — the part worth keeping
Two independent causes, either sufficient alone:
s-maxage=300. On a fresh cache key, 38 of 38 "quoted" readings flipped to unquoted.That probe read raw bytes, avoided
jq, and ran a positive control — everything our rules ask — and none of those checks can see a CDN. The doc now says to requirecf-cache-status: MISSbefore believing any negative about this API.The retracted paragraph is preserved verbatim in a
<details>block rather than deleted, because it guessed the mechanism correctly ("embeds the user through a different serialiser") and was defeated only by being unable to measure it. The inference was sound; the measurement was missing. That is worth a reader seeing.Note the original justification is untouched and still stands on its own — a client must not hard-fail a
200whose body it can read. The fix was right while the evidence for it was wrong, which is why nothing aboutFlexStringitself changes here.3. The mechanism narrows a residual without closing it
The "MECHANISM is unidentified, so the FIELD SET is a guess" residual is updated. The mechanism is Meili's dynamic JSON typing on the stored image document.
TagItem.Name— the old text's worked example — is not on that document and is no longer implicated.civitai/civitai#4768asks the platform side to enumerate it.4. 🔴 The new residual, and the reason this is more than bookkeeping
The coercion is numeric, so leading zeros are destroyed upstream:
FlexStringdecodes this cleanly and cannot fix it. It made the failure quieter, not absent — an opaque exit 1 became a confident, wrong username. Item 37 must not be read as "#513 is solved"; the decode half is solved, the value half is a server bug.Generalised in the doc: a fix on the consumer side of a lossy producer should state which half it does not cover.
Tracking
Verification
make ci→ green, 22 packages tested (positive control: package count > 0).golangci-lint run ./...→ 0 issues (run undernix-shell;make linterrors on this host as golangci-lint is not on PATH, and per AGENTS.mdlintreports without gating, so it is worth running locally).The
blocks/imagesendpoint is described in #4768 as suspected, not verified — it returns401 Block token required, so I could not exercise it and said so there rather than asserting it.