Skip to content

docs(serve): generate the endpoint reference from the app, gate it in CI (#656) - #671

Merged
padak merged 2 commits into
mainfrom
claude/github-issues-655-review-7bf244
Aug 23, 2026
Merged

docs(serve): generate the endpoint reference from the app, gate it in CI (#656)#671
padak merged 2 commits into
mainfrom
claude/github-issues-655-review-7bf244

Conversation

@padak

@padak padak commented Aug 23, 2026

Copy link
Copy Markdown
Member

Closes #656.

The drift

docs/web-server.md claimed "150+ REST endpoints" across a 17-row hand-written table. The app actually serves 227 operations across 29 routers, and ten routers were missing from the doc entirely: billing, token, notifications, feature, stream, dev-portal, ai-chat, semantic-layer (21 routes!), transformations, documentation.

No OpenAPI spec is committed, so only a running server exposed the truth and nothing in CI could catch the gap.

The fix: stop hand-maintaining the list

Rather than re-typing 227 rows that would drift again by 0.92.0, the enumeration is now generated and gated — mirroring the established skill-gen / skill-check pattern.

  • scripts/gen_endpoint_reference.py renders docs/web-server-endpoints.md from create_app().openapi().
  • Section structure is not invented: server/app.py::OPENAPI_TAGS already carries a hand-written description per tag whose bold prefix is a category (**Data.**) and whose tail maps to the CLI (Mirrors `kbagent config *` ). The generator groups by that existing metadata, so prose stays human-authored and only the enumeration is mechanical.
  • No version stamp in the output. gen_command_reference.py stamps one because it publishes a release asset; this file is git-tracked and gated, so a stamp would make every release bump the doc for reasons unrelated to the API.
  • make endpoints-gen / make endpoints-check, wired into make check and ci.yml.

Gate correctness

The check asserts two things, because either alone has a blind spot — both found by mutation-testing the gate:

Condition Why not the other one alone
git ls-files --error-unmatch (file is tracked) git diff --quiet reports nothing for an untracked path — a doc dropped from the index would pass while documenting nothing
git diff --quiet (content matches after regeneration) git status --porcelain alone flags a staged-new file (A ) whose content is perfectly correct — the normal state of the PR that adds a generated file

Verified against all three states: untracked → fail, staged+correct → pass, staged+stale → fail.

web-server.md rewritten around what a generator can't produce

It now maps routers to their OPENAPI_TAGS categories and states which surfaces are deliberately CLI-only (auth, sync, permissions, init) — no route list and no endpoint count left to go stale.

Other stale facts corrected while verifying against code

  • SSE section named 1 of 6 streams and listed three non-existent ones as "designed-for". All six are now listed with why each streams. Also notes that GET /agents/{task_id}/runs/{run_id}/events looks like a stream but replays as one JSON response.
  • Tier-1 file tree pointed at server/__init__.py for create_app() — it moved to app.py with the PEP 562 lazy-export split — and omitted run_broadcaster.py and pricing.py.
  • The agents tag described itself as "Server-only feature (no CLI equivalent)" — untrue since kbagent agent shipped in 0.44.0. What is serve-only is the cron loop. Fixed at the source in app.py, so the Swagger UI improves too. This one had been invisible precisely because it only rendered inside Swagger; putting the tag text in a committed file is what surfaced it.

Not in scope

The missing REST mirrors (#657) and the fact that permissions doesn't constrain serve at all (#655) are referenced from the doc but remain open.

Testing

  • tests/test_gen_endpoint_reference.py (11 tests): asserts the rendered set equals the app's operation set (the assertion the old table could not make), that the totals line is derived rather than claimed, that no version stamp leaks in, that the committed doc is fresh, and that an undeclared tag or empty router is reported rather than silently dropped.
  • Full suite: 6034 passed, 8 skipped. ruff, ruff format, ty, loc-check, command-sync-check, version-gate-check all clean.

No version bump and no changelog entry, per the release process in CLAUDE.md.


Open in Devin Review

… CI (#656)

docs/web-server.md drifted badly: it claimed "150+ REST endpoints" over a
17-row hand-written table while the app served 227 operations across 29
routers, with ten routers (billing, token, notifications, feature, stream,
dev-portal, ai-chat, semantic-layer, transformations, documentation) absent
entirely. No OpenAPI spec is committed, so only a running server showed the
truth and nothing in CI could notice.

Replace the hand-maintained enumeration with a generated one:

- scripts/gen_endpoint_reference.py renders docs/web-server-endpoints.md
  from create_app().openapi(). Sections come from the descriptions already
  in server/app.py::OPENAPI_TAGS (category prefix + "Mirrors kbagent ..."),
  so the prose stays human-authored and only the enumeration is mechanical.
  No version stamp: the file is gated, and a stamp would make every release
  bump it for reasons unrelated to the API.
- make endpoints-gen / endpoints-check, wired into make check and ci.yml.
  The check asserts the file is tracked AND unchanged after regeneration:
  git diff alone reports nothing for an untracked path, and git status
  --porcelain alone rejects a staged-new file whose content is correct.

Rewrite web-server.md around what a generator cannot produce: it now maps
routers to their OPENAPI_TAGS categories and states which surfaces are
CLI-only, with no route list and no endpoint count to go stale.

Also corrected while verifying against the code:

- All six SSE routes are listed; the doc named one and mis-labelled three
  non-existent ones as "designed-for". Notes that /agents/{id}/runs/{id}/
  events looks like a stream but replays as one JSON response.
- The tier-1 file tree pointed at server/__init__.py for create_app (it is
  app.py since the PEP 562 lazy-export split) and omitted run_broadcaster.py
  and pricing.py.
- The `agents` tag described itself as "Server-only feature (no CLI
  equivalent)", untrue since kbagent agent shipped in 0.44.0 -- what is
  serve-only is the cron loop. Fixed at the source, so Swagger improves too.

README.md and docs/build-your-own-client.md point at the generated file;
CONTRIBUTING.md's synchronization map gains both docs with their gate status.

Refs #656

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

Open in Devin Review

Comment thread .github/workflows/ci.yml Outdated
Two robustness fixes to the endpoint-reference gate, from PR review:

- The CI step ran the generator as plain `uv run`, while the Makefile passed
  `--extra server`. The check job is green, so the extra did survive from the
  install step -- but `uv run` re-resolves per invocation, so naming the extra
  is what actually guarantees fastapi is importable. Both callers now match.

- Both callers redirected stderr to /dev/null. `bash -e` and make already fail
  the step when the generator crashes, so this was never a false pass, but it
  discarded the traceback: the failure would surface as a bare non-zero exit
  with no way to see the cause. stdout stays dropped ("Wrote ..." is noise),
  stderr now propagates.
@padak
padak merged commit d150a48 into main Aug 23, 2026
4 checks passed
@padak
padak deleted the claude/github-issues-655-review-7bf244 branch August 23, 2026 22:26
padak added a commit that referenced this pull request Aug 23, 2026
Batches the nine PRs merged since v0.90.0 (#670, #671, #672, #673, #674, #675, #676, #677, #678) into one version bump and one changelog entry.
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.

docs: web-server.md is substantially stale — 10 routers and every 0.89.0 serve addition missing

1 participant