feat(agent-setup): the managed block links example apps, as ONE docs URL - #549
Conversation
The block's `### Docs` section reached the guide, the reference and
`llms.txt`, and nothing in it reached a worked example. An author's agent
had four sentences of gotchas and no App Block it could read end to end.
Adds one line:
- Example apps you can read end-to-end:
https://developer.civitai.com/apps/examples
🔴 ONE URL, NEVER A LIST OF REPOSITORIES, AND THAT IS STRUCTURAL. This
block is written to disk in somebody else's project, so a repo URL
embedded here is copied into every directory `agent-setup` has ever run
in and nothing can recall it — only another `agent-setup` run in that
same directory rewrites it, which most authors never do. A docs-site
page is corrected once, for every reader, by the people who own the
list. The CLI ships the address; the docs repo ships the contents.
Measured, anonymously over real HTTP, and recorded in the decision file:
`civitai.com/apps` 404s (while `civitai.com/models` 200s), so a
`civitai.com/apps/run/<slug>` link is unreachable for a logged-out
reader and the page links repositories rather than running instances;
`topic:civitai-app-block` returns 6 repos, a different set from the
seven example apps, so the topic query is not a durable substitute;
and `/apps/showcase` is the COMPONENT showcase, not an example gallery.
🔴 THE LINK IS NOT LIVE YET. `developer.civitai.com/apps/examples`
answers 404, with and without a trailing slash, while the other three
links in the same section answer 200. The docs page is authored in a
separate repository in parallel. DO NOT MERGE until it resolves, and
note that the trailing slash is load-bearing on that host
(`/apps/showcase` is 200, `/apps/showcase/` is 404).
Guards (internal/cmd/agent_setup_docs_test.go):
- TestTheBlocksDocsSectionIsPinnedForEveryProjectKind pins the WHOLE
normalised section for every project shape, since the template's three
`{{ if }}` branches are what an edit can drop it out of. Two
assertions: the link SET, and the whole string (prose reworded is a
changed claim, and a word-level guard is walkable by rewording).
- TestDocsSectionExtractorCanFail is the extractor's control, both ways.
- TestBlockDocsLinksResolve probes the links, opt-in behind
CIVITAI_CHECK_DOCS_LINKS=1 exactly like internal/scaffold's
CIVITAI_CHECK_PUBLISHED_PINS, so an offline `make ci` stays green. It
carries its own must-404 negative control and SKIPS on transport
failure.
AGENTS.md item 36's trigger gains the Docs-section situation (+1 byte;
file is 30,443 of 30,500) rather than minting item 39: the item is the
managed block's content record and already carries two such sections.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018mJg86oQbTwuyPo5zcq3qP
✅ The merge gate is released — the page is live and the link resolvesThe blocker at the top of this PR body ("the link this PR ships does not resolve")
The opt-in probe this PR names — CI does not run itThe guard was mutation-tested, because a green it cannot fail proves nothingThe handoff recorded — and restoring the file returns it to green, with the other three links reporting 200 Gate
|
…the link (#549 audit) (#553) An adversarial audit of #549 found five defects in the GUARDS around the managed block's `### Docs` section, not in the section itself. Each was reproduced on the merged tree before it was fixed. 1. The gate could pass without probing the link it exists to check. The per-link loop did `case err != nil: t.Skipf(...)`, abandoning the whole run on the first transport error. With only the FIRST link's host made unresolvable and `/apps/examples` pointed at a known-404 path, the pre-change test printed one skip line and exited `ok` — the dead link was never fetched. Every link is now probed before anything is diagnosed; transport errors are collected per URL and a run that could not reach them all reports SKIP with `checked n of m`. 2. Negative control present, positive control absent. `mustNotResolve >= 400` is satisfied by an edge that 403s everything — which is also what makes every real link >= 400. Against a local server answering 403 to every path, the pre-change test emitted four `DEAD LINK IN THE MANAGED BLOCK … correct it in internal/cmd/templates/agents-app.md` errors, the exact wrong instruction; this host is known to hard-block non-browser agents with Cloudflare `error code: 1010`. Statuses are now split into gone (404/410) and CANNOT TELL (401/403/429/5xx), with different messages, and the run skips rather than guesses when no link answered < 300. 3. The probe followed redirects while the comment claimed the spelling was measured. Go's default CheckRedirect follows up to 10 hops; `/apps/guide` 301s to `http://developer.civitai.com/apps/guide/` — a different spelling AND a downgrade to cleartext http — which a following probe scores as a clean 200. CheckRedirect now returns ErrUseLastResponse and a 3xx is reported as MOVED, with its Location. The comment at the foot of the file also carried a false measurement: it said `/apps/showcase` is 200 while `/apps/showcase/` 404s (true) "while `/apps/guide/` is the other way round", implying `/apps/guide` 404s. It does not — it 301s. Re-measured all nine paths; the claim is withdrawn, not replaced with a narrower one, because the table shows no rule to state. 4. "Correcting the URL is those two edits and nothing else" was false when it was written — #549 itself added a third spelling, in claudedocs/decisions/36-agents-block-per-project.md. Measured over the whole tree, ignored files included: 9 lines in 4 files name developer.civitai.com/apps/examples. Replaced by `TestEveryDocsURLSpellingIsLedgered`, a bidirectional ledger that walks the repo and fails both when an unledgered file spells a docs URL and when a ledgered one stops. Dated `claudedocs/handoff-` records are exempt: they record what was true when written and are not corrected. 5. The three-branch control counted shapes, not kinds. `len(shapes) < 3` is satisfied by three shapes of one kind. The wanted set is now read from `agent_setup_project.go`'s own `projectKind` constants — which also repairs an adjacent comment the audit flagged: `allProjectShapesForTest` claimed to be "derived from the projectKind constants' own list rather than from a literal" while being a literal. `TestAllProjectShapesCoversEveryDeclaredKind` is the derivation that makes the sentence true. Red-then-green matrix, `make ci` and `make lint` output are in the PR body. Claude-Session: https://claude.ai/code/session_018mJg86oQbTwuyPo5zcq3qP Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
🔴 DO NOT MERGE YET — the link this PR ships does not resolve
https://developer.civitai.com/apps/examplesanswers 404 today, with andwithout a trailing slash, while the other three links in the same section answer
200. The docs page is authored in a separate repository, in parallel, and
is still an open PR there. Merge this only once the page is live, and re-run the
probe below to confirm — including the exact spelling, because the trailing
slash is load-bearing on that host (
/apps/showcaseis200,/apps/showcase/is404). If the docs side lands a different spelling, the URLis in exactly two places (below) and both must move.
What this does
civitai agent-setup's managedAGENTS.mdblock reached the guide, thereference and
llms.txtin its### Docssection, and nothing in it reached aworked example. An author's agent had four sentences of gotchas and no App
Block it could read end to end. One line is added:
🔴 ONE URL, never a list of repositories — and that is structural
The obvious "helpful" edit is to spell the seven example repos into the block.
It is refused because this block is written to disk in somebody else's
project: a repo URL embedded here is copied into every directory
agent-setuphas ever run in, and nothing can recall it — only another
agent-setuprun inthat same directory rewrites the block, which most authors never do. A
docs-site page is corrected once, for every reader, by the people who own the
list. The CLI ships the address; the docs repo ships the contents.
Measured anonymously over real HTTP and recorded in the decision file:
developer.civitai.com/apps/guide/200(positive control)developer.civitai.com/apps/reference/200(positive control)developer.civitai.com/llms.txt200(positive control)developer.civitai.com/apps/examples404— not live yetdeveloper.civitai.com/apps/showcase200(no trailing slash)developer.civitai.com/apps/showcase/404(with one)civitai.com/models200(control: the site answers anonymous GETs)civitai.com/apps404civitai.com/apps/run/gen-matrix404Three residuals follow, each of which killed a different candidate line:
civitai.com/apps/run/<slug>404s for alogged-out reader — and it does so because the whole
/appsroute familydoes, not because that slug is wrong. So the page links repositories, which
an agent can clone and read, not running instances.
/apps/showcaseis the COMPONENT showcase, and was conflated with anexample gallery in the handoff this came from. It answers
200, which isexactly what makes the conflation survivable — a link there resolves and sends
the reader to the wrong thing.
topic" is not a durable substitute for a curated page:
topic:civitai-app-blockreturns 6 repos, a different set from the seven example apps — two of the
seven carry no topics, and one repo the query does return is not one of the
seven.
Where the URL lives
Exactly two places, verified by grep across the repo:
internal/cmd/templates/agents-app.md— the literal that shipswantDocsSectionininternal/cmd/agent_setup_docs_test.go— the goldenBoth failure messages name both files, so correcting the URL is those two edits.
Tests —
internal/cmd/agent_setup_docs_test.goTestTheBlocksDocsSectionIsPinnedForEveryProjectKind— pins the WHOLEnormalised
### Docssection for every project shape (npmwith andwithout recognised scripts,
no-build,none). Per-shape because thetemplate's three
{{ if }}branches are what a mis-nested edit drops thesection out of. Two assertions with different messages: the link SET, and
the whole string — a word-level guard on prose is walkable by rewording,
and the wording is the claim about what a reader gets from following the link.
TestDocsSectionExtractorCanFail— negative control fordocsSectionOf,both directions.
TestBlockDocsLinksResolve— the liveness probe, opt-in behindCIVITAI_CHECK_DOCS_LINKS=1, followinginternal/scaffold'sCIVITAI_CHECK_PUBLISHED_PINSpattern so an offlinemake cistays green. Itcarries its own must-404 negative control and skips on transport failure
("the network is down" and "the page is gone" are different findings).
🔴 No CI job sets it — editing
.github/workflows/*is "ask first" inAGENTS.md— so it is a manual pre-merge check and nothing runs it on aschedule. Saying so is the honest version of "the links are verified".
Red/green matrix — four isolated mutations, each watched fail with its own assertion
origin/main8e7d2dcdocs_test.go:122+:130, all 4 shapesExample apps you can read end-to-end:→Examples:), URLs untouched:130only —:122did NOT fire:122and:130{{ if ne .Kind "no-build" }}:113, forno-buildonly — other 3 shapes greendocsSectionOfreturns("", true)when the heading is absent:156×2Mutation A's exact text:
Mutation C's exact text:
All four restored; green at HEAD
c2c1b7f.Docs
Extends
AGENTS.mditem 36's trigger with the Docs-section situation ratherthan minting item 39: item 36 is the managed block's content record and already
carries two such sections. Net +1 byte —
AGENTS.mdis 30,443 of the30,500 ceiling (
agents_size_test.go), leaving 57 bytes of headroom; thebody goes in
claudedocs/decisions/36-agents-block-per-project.md§"Third", whichcosts zero session bytes.
README: grepped the three surfaces
AGENTS.mdnames — the command table(line 596), the exit-code table (generated from
exitCodeDocs) and theTroubleshooting index — plus every
developer.civitai.comoccurrence. Nothingin the README enumerates the block's
### Docssection or its links, and thischange adds no error string, exit code or flag, so no surface goes stale. The
narrative at §"Set up your coding agent" summarises the block as "the commands and
the gotchas", which already omitted Docs before this change.
Gates
make ci— rc 0, 21 packagesok, build succeededmake lint(nix-shell -p golangci-lint) — rc 0,0 issues.Instrument validated: a deliberately unused func + empty branch made it report
2 issues(staticcheck SA9003 + unused) and exit 1, then was reverted.make ci-shallow— rc 0 atc2c1b7f, 21/21 in a depth-1 clone (thistouches
AGENTS.md, which the git-history tests read)gofmt -s -l .— clean, with a positive control confirming it still flags amalformed file
🤖 Generated with Claude Code
https://claude.ai/code/session_018mJg86oQbTwuyPo5zcq3qP