Skip to content

docs(agents): record the three app metrics decisions that look wrong - #191

Merged
ZacxDev merged 1 commit into
mainfrom
zach/agents-md-metrics-seam
Aug 3, 2026
Merged

docs(agents): record the three app metrics decisions that look wrong#191
ZacxDev merged 1 commit into
mainfrom
zach/agents-md-metrics-seam

Conversation

@ZacxDev

@ZacxDev ZacxDev commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Adds items 5–7 to AGENTS.md → "Intentional decisions that look wrong (read before 'fixing')", and updates the section's framing sentence (items 1–3 mirrors, 4 a deliberate non-mirror, 5–7 the analytics read path).

⚠️ Merge order: this depends on #190

internal/cmd/app_metrics.go and internal/appapi/analytics.go exist only on feat/app-metrics — they are not on main. Merge this after #190, or AGENTS.md documents a command the tree does not have. Flagging rather than stacking, since stacking has its own hazards.

5. It calls tRPC, not REST — because there is no REST route to call

Owner analytics exist only as blocks.getMyAppAnalytics (civitai/civitai → src/server/routers/blocks.router.ts); there is no /api/v1 equivalent. So the command resolves <slug>appBlockId through the existing REST GET /api/v1/blocks/submissions, then issues the non-batched tRPC GET (?input={"json":{…}}, unwrapping result.data.json) reusing the authedDo + envelope-unwrap pattern GetForgejoCloneInfo already established. Written down so nobody "fixes" the two-hop shape into a REST call that does not exist.

6. notOwned is a cross-repo contract

The proc sits behind the appBlocksAuthor feature flag and answers a caller who isn't entitled (or doesn't own the app) with HTTP 200 and every counter zeroed, not an error — so a renderer that ignores the field prints a plausible empty dashboard for what is really a permission failure. runAppMetrics refuses to render when it's true. Whoever changes that payload server-side has to keep the field. --json deliberately passes the payload through and still exits 0, so scripts must branch on notOwned themselves.

7. The exit-code contract needs errors.Is, not message assertions

The classification sentinels carry no visible text — Tag/TagStatus attach them while Error() stays byte-for-byte identical (pkg/civitai/errkind.go) — so a message assertion says nothing about the exit code. Measured on #190: stripping the classification while leaving every message identical left the entire suite green, and the README's 403 → exit 3 / not-found → exit 4 promise was unpinned. Generalised in the text to any command claiming an exit code.

One correction to the brief I was given, made from the code rather than paraphrased: there is no civitai.ErrUsage. The HTTP kinds (ErrUnauthorized, ErrNotFound) live in pkg/civitai/errkind.go; the usage sentinel is ErrUsage in internal/cmd/usage_error.go. The item states the split explicitly so the guidance is actionable rather than a name that won't compile.

Verification

make ci green in a clean worktree off origin/main: go mod tidy and go vet silent, 16/16 packages ok, zero FAIL, build succeeded. gofmt -s -l . prints nothing. Docs-only change, so no behaviour is claimed to be verified beyond the gate.

🤖 Generated with Claude Code

https://claude.ai/code/session_01858ymA3tEJQi83435u7npi

Adds items 5-7 to "Intentional decisions that look wrong (read before
'fixing')" and updates the section's framing sentence.

5. `civitai app metrics` calls tRPC, not REST — because there is no REST route
   to call. Owner analytics exist only as `blocks.getMyAppAnalytics`; there is
   no /api/v1 equivalent. The command therefore resolves slug -> appBlockId via
   the existing REST GET /api/v1/blocks/submissions and then issues the
   non-batched tRPC GET, reusing the authedDo + result.data.json unwrap pattern
   GetForgejoCloneInfo established. Documented so nobody "fixes" it into a REST
   call that does not exist.

6. `notOwned` is a cross-repo contract. The proc sits behind the
   `appBlocksAuthor` flag and answers a non-entitled caller with HTTP 200 and
   every counter zeroed, so a renderer that ignores it prints a plausible empty
   dashboard for a permission failure. The human view refuses to render on
   `notOwned`; `--json` deliberately passes it through and still exits 0, so
   scripts must branch on it themselves. Whoever changes the payload
   server-side has to keep the field.

7. The exit-code contract is pinned by errors.Is, never by message text. The
   sentinels carry no visible text (Tag/TagStatus preserve Error() byte-for-
   byte), so a message assertion says nothing about the exit code. Measured on
   the metrics PR: stripping the classification while leaving every message
   identical left the ENTIRE suite green, and the README's 403 -> exit 3 /
   not-found -> exit 4 promise was unpinned. Generalised to every command that
   claims an exit code.

Sentinel names verified against the code, not paraphrased: the HTTP kinds are
`civitai.ErrUnauthorized`/`ErrNotFound` in pkg/civitai/errkind.go, but the
usage sentinel is `cmd.ErrUsage` in internal/cmd/usage_error.go (there is no
`civitai.ErrUsage`) — the item says so explicitly.

DEPENDS ON #190: internal/cmd/app_metrics.go and internal/appapi/analytics.go
exist only on `feat/app-metrics` and are NOT on main. Merge this AFTER #190 or
AGENTS.md will describe a command the tree does not have.

`make ci` green: tidy + vet clean, 16/16 packages ok, 0 FAIL; `gofmt -s -l`
prints nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01858ymA3tEJQi83435u7npi
ZacxDev added a commit that referenced this pull request Aug 3, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01858ymA3tEJQi83435u7npi
@ZacxDev
ZacxDev merged commit e16bb67 into main Aug 3, 2026
10 checks passed
ZacxDev added a commit that referenced this pull request Aug 3, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01858ymA3tEJQi83435u7npi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant