docs(cmd): pilot the README-prose -> Long migration on app listing - #274
Open
ZacxDev wants to merge 8 commits into
Open
docs(cmd): pilot the README-prose -> Long migration on app listing#274ZacxDev wants to merge 8 commits into
app listing#274ZacxDev wants to merge 8 commits into
Conversation
… the caps Pilot for the "move the README's command-reference prose into the command definitions" migration, scoped to ONE group before touching ~50 files. Measured on the tree at aeceb6b, across all 53 command nodes: - `Annotations` is set on ZERO nodes today. - 8 nodes have NO `Long` at all, 16 more are under 400 chars. - Three of the eight are in this group (set-icon, set-cover, add-screenshot): `--help` printed a single Short line, and the docs generator publishes exactly what `--help` prints. What changed, all in `app listing`: - set-icon / set-cover / add-screenshot get a Long and an Example (they had neither); status / rm-screenshot / reorder get an Example and, for reorder, the positional-ordering rule. - The accepted formats and the per-kind byte caps — which appeared NOWHERE in the help surface, only in the README — are now stated, and are COMPUTED from maxIconBytes/maxCoverBytes/maxScreenshotBytes through the same humanBytes the refusal message uses. `--help` now predicts the error text rather than quoting a separately-maintained figure. - Four pre-existing body lines that ran past 80 columns are rewrapped. `--help` cost, measured base vs HEAD over all 54 captured nodes: 7 nodes change and 47 are byte-identical; the tree grows 100,451 -> 104,190 bytes (+3.7%) and 1,987 -> 2,065 lines (+3.9%). Three guards, none subsuming the others: the caps guard pins help against the enforced constants (and cross-kind, since icon and screenshot share a cap); the completeness guard requires a Long, Short and Example on every node in the group, walking the real tree with a count floor; the budget guard caps a body at 1400 chars and 80 columns, counting RUNES — a byte count reported four 79-column em-dash lines as 81 and failed prose that was fine. Mutation-measured, checksum-gated so a no-op edit reports NOT-APPLIED rather than surviving: 9/9 mutants killed by the intended guard (stale literal + moved constant, dropped cap sentence, cross-kind copy-paste, group losing a cap, deleted Example, deleted Long, unregistered subcommand, over-wide line, blown budget); a comment-only null mutant survived. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… assumption
Records what the pilot measured, including two findings that reverse the
handoff's own next-step 1:
- `Long` is already 3x RICHER than the README table for the App-authoring
commands (19,017 vs 6,255 chars over the 16 matched rows), so "migrate the
README's prose into Long" was largely already done there. The real gap is
the public read-API group, which has no table row at all.
- `Annotations` cannot reach the docs site. The generator captures only
`--help` and `__complete`, and cobra renders Annotations in neither —
probed with a positive control. Recommendation: drop that half.
- The blocker: `gen-appblocks-cli.mjs:781` parses the whole `Long` and then
publishes only `short`. Measured end-to-end, the pilot's ~4,000 new chars of
`Long` moved `cli.json` by +926 chars, all of it `examples`.
Also marks #253 fixed (#264), re-verified with a positive control, and replaces
the verify block's bare zero with the control that makes it mean something.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Added `longDescription: parseLongDescription(help)` to a scratch copy of gen-appblocks-cli.mjs and ran it against the pilot binary: 52/52 commands gain a longDescription and cli.json goes 42,784 -> 88,747 chars (+107%), publishing 43,460 chars of prose the CLI already carries. The one-line generator change is worth more than the migration it unblocks. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…audit found An adversarial pre-merge audit of #274 falsified three things I had asserted. FALSE PROSE, now removed: - `set-cover` said its larger cap was "because a cover is stored at full resolution rather than inlined". `runSetMedia` sends cover AND screenshot down the identical IngestAssetFullRes path while the screenshot keeps the icon's 2 MiB cap, so the storage path does not explain the difference. An author reasoning from the stated rule would prepare a 3 MiB screenshot and be refused. The clause is deleted rather than reworded — the caps are the server's and do not follow from the path. - The group body hand-typed "(exit 2)", making a THIRD publishing surface for a contract `exitcodes_doc.go` exists to keep in one place, and dropping the exception both governed surfaces carry (an UNREADABLE file exits 1, not 2 — AGENTS.md item 24). Measured on the HEAD binary: oversize/empty/non-image/ directory/missing all exit 2, a mode-000 valid PNG exits 1. The sentence now names the checks and states no code. - `add-screenshot` promoted a hand-typed server constant ("up to 8") into two emphatic statements. It is now attributed to the server and dated. GUARD HOLES, now closed. Both were measured surviving a green suite: - `humanBytes(maxIconBytes)` and `humanBytes(maxScreenshotBytes)` are the SAME STRING ("2.0 MB"), so the group's three bare Contains checks enforced only two of three caps, and a per-kind body asserted to contain "2.0 MB" was satisfied by the other 2 MiB kind's sentence. Deleting the icon cap from the group, deleting the screenshot cap from it, and swapping add-screenshot's rule to kindIcon ALL survived. The group assertion is now cap-in-context and TestListingHelpNamesTheKindItDescribes requires each body to name its own asset and not a differently-capped sibling. - `listingImageFormats` was asserted against a Long built by interpolating the same constant — a constant compared with itself, which can never fail. Narrowing it to "png or jpeg" (understates) and widening it to claim avif (lies) both survived. TestListingImageFormatsMatchesTheDecoder now decodes a real PNG/JPEG/WebP header through appapi.DecodeImageInfo and carries a GIF negative control, so the mirror has a drift check. Also corrects the runes-vs-bytes comment's provenance: it said the byte-based first draft reddened "four PRE-EXISTING bodies", but three of those four bodies had no Long at all before this work. The genuinely pre-existing over-80-COLUMN lines number 3, not 4. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…he proof The audit reported it as a surviving hole. It is not: `humanBytes(maxIconBytes)` and `humanBytes(maxScreenshotBytes)` are both "2.0 MB", so the swap produces a DIFFERENT BINARY and BYTE-IDENTICAL rendered help — measured with `cmp` on the two `--help` outputs, with the differing binaries as the negative control. There is nothing for an assertion to observe, and asserting against a token that cannot vary would be a guard that cannot fail. What DOES protect it was measured separately: under that mutation, moving maxScreenshotBytes to 3 MiB reddens TestListingHelpQuotesTheEnforcedCaps with "does not quote the cap it enforces (3.0 MB)". The swap is invisible exactly while it is harmless. (The first attempt to test this said DIFFERS — because the baseline binary got built from the base clone rather than the worktree. The negative control that the two binaries are distinct builds is what makes the corrected answer evidence.) Also fixes the handoff: the next-steps list read 5,6,5 after an insertion, the retracted #253 heading still asserted "still ships", and step 1 now records that the generator fix shipped as docs PR #49 with its measured numbers and its one unguarded residual. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ted the package The drift check's negative control used `gif.Encode`, which means importing `image/gif`. Its init() REGISTERS the GIF decoder process-wide, so a GIF stopped failing `civitai generate --image` via `image.ErrFormat` and started failing via the allowlist branch instead. That reddened three unrelated tests, one of which (TestSupportedImageFormats_DecoderRegistrationIsInLockstep) exists precisely to catch it: "the unreachability argument for the allowlist branch depends on this being the ErrFormat arm". A test fixture that mutates global decoder state changes the thing it measures. The control is now 13 hand-written GIF87a bytes (magic + logical screen descriptor) with the hazard documented at the fixture, and the three tests are green again. Verified after: `make ci` 18/18 packages ok, 0 FAIL; the GIF control still REFUSES (it is not passing vacuously); 29 subtests pass across the listing guards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Pilot for the "move the README's command-reference prose into the cobra command
definitions" migration, scoped to one group before touching ~50 command files.
Also records what the scoping measured — including two findings that reverse the
plan's own premise.
What the measurement found (whole tree, 53 nodes)
AnnotationsmapLongat allLongunder 400 charsLongon those same 16 nodesLongis already ~3x richer than the README table for the App-authoringcommands, so that half of the migration was largely already done. The real gap is
the public read-API group (
models/images/collections/…), which owns 19 of the24 empty-or-thin nodes and has no table row at all.
🔴
Annotationscannot reach the docs site — measured, not inferredThe docs generator captures exactly two channels per node (
--helpand__complete) and parses the text. Cobra rendersAnnotationsin neither. Probe onworkflows get: the value appears 1x in the binary (strings), 0x in--help, 0x in__complete; positive control (a word that IS in that help)1x. Recommendation: drop the Annotations half.
🔴 The blocker — now fixed upstream
gen-appblocks-cli.mjs:781wasdescription: short || parseLongDescription(help).split('\n')[0] || ''— it parsed the whole
Longand published only theShort. Measured: this PR'snew
Longmovedcli.jsonby +926 chars, all of itexamples.Fixed in civitai-developer-docs#49, which publishes
longDescriptionon 52/52commands (+76.9% artifact). Merge order does not matter — this PR stands alone for
terminal users, and gains a consumer once #49 lands.
What this PR changes —
civitai app listingonlyset-icon/set-cover/add-screenshotgained aLongand anExample;they had neither, so
--helpprinted a singleShortline and the generatorpublished a single line too.
status/rm-screenshot/reordergained anExample;reorderalso statesthe positional-ordering rule.
surface — only in the README. They are now stated, and computed from
maxIconBytes/maxCoverBytes/maxScreenshotBytesthrough the samehumanBytesthe refusal message uses, so
--helppredicts the error text.--helpcost, measured against currentorigin/mainover all 54 captured nodes47 nodes byte-identical, 7 changed. Tree 100,781 → 104,401 bytes (+3.6%),
1,990 → 2,068 lines (+3.9%).
Attribution was checked rather than assumed: an earlier capture showed 9 nodes
moving, and
app metrics+ root turned out to have moved under #268 (merged in),not under this PR.
Guards
Four, none subsuming the others: caps-vs-constants in context (the group's
three caps are asserted next to the kind each applies to); kind-naming (each body
names its own asset and not a differently-capped sibling); formats-vs-decoder (a
real PNG/JPEG/WebP header through
appapi.DecodeImageInfo, with a GIF negativecontrol); completeness (Long+Short+Example on every node, walking the real tree
with a count floor); and a budget (1400 chars / 80 columns, counting runes).
Mutation matrix — 13 killed, 1 declared-equivalent, null mutant survived.
Checksum-gated so a no-op edit reports NOT-APPLIED rather than surviving.
The one that does not die is the
kindScreenshot→kindIconswap, and it is anequivalent mutant, proven: both render "2.0 MB", so the mutation produces a
different binary and byte-identical help (
cmp, with the differing binaries as thenegative control). It becomes observable the moment the caps diverge — measured,
setting
maxScreenshotBytesto 3 MiB under that mutation reddens with "does notquote the cap it enforces (3.0 MB)". It is invisible exactly while it is harmless.
make cigreen on the merged tree: 18/18 packages, 0 FAIL.What the audit caught (all fixed here)
set-coversaid its larger cap was"because a cover is stored at full resolution rather than inlined".
runSetMediasends cover and screenshot down the identical
IngestAssetFullRespath whilethe screenshot keeps the icon's 2 MiB cap. Deleted.
which
exitcodes_doc.goexists to prevent, and dropped the exception bothgoverned surfaces carry (an unreadable file exits 1 — AGENTS item 24). Verified
on the binary: oversize/empty/non-image/directory/missing → 2, mode-000 PNG → 1.
The sentence now names the checks and states no code.
humanBytes(icon)andhumanBytes(screenshot)are the same string, so the group enforced only 2 of 3caps; and
listingImageFormatswas asserted against aLongbuilt byinterpolating that same constant — a constant compared with itself, which can
never fail (narrowing it to "png or jpeg" and widening it to claim avif both
survived). Both closed, both mutants now killed.
guard's own comment, whose provenance claim was also wrong.
One defect found while fixing, not by the audit: the drift check's first GIF control
used
gif.Encode, and importingimage/gifregisters the decoder package-wide,which changed how
generate --imagerefuses a GIF and reddened three unrelatedtests. The repo's own lockstep guard caught it. The control is hand-built bytes now.
🤖 Generated with Claude Code