Checks ARE dynamically reportable (POST …/api/checks/statuses/{sha}) but the API docs and discovery document don't mention them — surface the checks API #271

Closed
opened 2026-09-10 11:03:36 +00:00 by crueber · 3 comments
Owner

Question asked

"Are there any ways to report checks dynamically? It isn't even mentioned in the API docs."

Answer: YES — the feature exists and is complete. The gap is documentation/discovery.

The checks surface (internal/checks) implements the full commit-status model with dynamic external reporting:

  • POST /{owner}/{repo}/api/checks/statuses/{sha} (internal/checks/http.go:186-196, 235-266) — external CI posts per-commit results: {context, state, target_url, description, started_at, completed_at} (strict decode, unknown keys 400). Server side: Service.ReportStatus (internal/checks/service.go:137), CAS'd checks index, notification fan-out post-write (service.go:130, 262).
  • Auth for CI: the wct_ token shape (internal/checks/auth.go) — repo-scoped CI tokens created via GET|POST …/api/checks/tokens + DELETE …/api/checks/tokens/{id} (http.go:198-221), authenticable as a shape that falls through to the server chain. This is the designed credential for exactly this workflow.
  • Read side: GET …/api/checks (list), GET …/api/checks/{sha} (combined state + counts), GET …/api/checks/statuses/{sha} (per-context) — all served, verified live: GET https://hub.packden.us/crueber/walhub/api/checks → 200 {"checks":[],"more":false}.
  • SDK: web/sdk/src/checks.js already wraps all of it.

The checks UI page (/checks) and the PR merge gating consume these. Nothing needs to be built to make checks dynamically reportable.

What's actually broken: the API docs and discovery omit it

  • The discovery document (GET /api/v1, live-checked) lists 23 endpoints — zero checks entries (no /checks, no /checks/statuses/{sha}, no /checks/tokens).
  • The static route table in web/src/pages/Apidocs.jsx:13-44 (the /api page) likewise has no checks rows — while it does include older surfaces (policy, settings, ops, tasks, git transport).
  • So an integrator reading the API section cannot discover the one feature designed for them. RegisterExposed/ExposedTemplates (the law-12 lane registration used by imports — cmd/walhub/repoimport.go:29-33) exists as the mechanism; checks never registered its templates.

Acceptance criteria

  • Discovery (GET /api/v1) lists the checks endpoints (list/combined/statuses GET+POST/tokens CRUD) with their auth requirements (principal write vs wct_ CI token).
  • The /api docs page shows the checks routes with request/response shapes, including a worked CI example (create wct_ token → POST a status → GET combined).
  • The checks UI page links to the docs section (so someone staring at /checks can find the reporting contract).
  • A contract test asserts every server.ExtraRoutes handler's routes appear in discovery (prevents the next feature package from silently missing — see the companion API-documentation issue).
## Question asked "Are there any ways to report checks dynamically? It isn't even mentioned in the API docs." ## Answer: YES — the feature exists and is complete. The gap is documentation/discovery. The checks surface (`internal/checks`) implements the full commit-status model with dynamic external reporting: - **`POST /{owner}/{repo}/api/checks/statuses/{sha}`** (`internal/checks/http.go:186-196, 235-266`) — external CI posts per-commit results: `{context, state, target_url, description, started_at, completed_at}` (strict decode, unknown keys 400). Server side: `Service.ReportStatus` (`internal/checks/service.go:137`), CAS'd checks index, notification fan-out post-write (`service.go:130, 262`). - **Auth for CI:** the `wct_` token shape (`internal/checks/auth.go`) — repo-scoped CI tokens created via `GET|POST …/api/checks/tokens` + `DELETE …/api/checks/tokens/{id}` (http.go:198-221), authenticable as a *shape* that falls through to the server chain. This is the designed credential for exactly this workflow. - **Read side:** `GET …/api/checks` (list), `GET …/api/checks/{sha}` (combined state + counts), `GET …/api/checks/statuses/{sha}` (per-context) — all served, verified live: `GET https://hub.packden.us/crueber/walhub/api/checks` → `200 {"checks":[],"more":false}`. - **SDK:** `web/sdk/src/checks.js` already wraps all of it. The checks UI page (`/checks`) and the PR merge gating consume these. Nothing needs to be *built* to make checks dynamically reportable. ## What's actually broken: the API docs and discovery omit it - The discovery document (`GET /api/v1`, live-checked) lists 23 endpoints — **zero checks entries** (no `/checks`, no `/checks/statuses/{sha}`, no `/checks/tokens`). - The static route table in `web/src/pages/Apidocs.jsx:13-44` (the `/api` page) likewise has no checks rows — while it *does* include older surfaces (policy, settings, ops, tasks, git transport). - So an integrator reading the API section cannot discover the one feature designed for them. `RegisterExposed`/`ExposedTemplates` (the law-12 lane registration used by imports — `cmd/walhub/repoimport.go:29-33`) exists as the mechanism; checks never registered its templates. ## Acceptance criteria - [ ] Discovery (`GET /api/v1`) lists the checks endpoints (list/combined/statuses GET+POST/tokens CRUD) with their auth requirements (principal write vs `wct_` CI token). - [ ] The `/api` docs page shows the checks routes with request/response shapes, including a worked CI example (create `wct_` token → POST a status → GET combined). - [ ] The checks UI page links to the docs section (so someone staring at `/checks` can find the reporting contract). - [ ] A contract test asserts every `server.ExtraRoutes` handler's routes appear in discovery (prevents the next feature package from silently missing — see the companion API-documentation issue).
Author
Owner

Fix ready for review: PR #293 (fix/issue-271) — surfaces the checks API in GET /api/v1 discovery (5 templates via ExposedTemplates + RegisterExposed, live-verified: 28 endpoints) and the /api docs page (routes, auth incl. wct_ usage, shapes, worked CI example at #checks-ci, linked from /checks). Contract tests pin template↔route correspondence both ways. Docs + discovery wiring only, no behavior change.

Fix ready for review: PR #293 (fix/issue-271) — surfaces the checks API in GET /api/v1 discovery (5 templates via ExposedTemplates + RegisterExposed, live-verified: 28 endpoints) and the /api docs page (routes, auth incl. wct_ usage, shapes, worked CI example at #checks-ci, linked from /checks). Contract tests pin template↔route correspondence both ways. Docs + discovery wiring only, no behavior change.
Author
Owner

Review of PR #293 (fix/issue-271), verified in scratch worktree at origin/fix/issue-271 (removed afterward). Main worktree untouched (still clean on main apart from pre-existing untracked .opencode/).

ROUTES vs IMPLEMENTATION (spot-checked internal/checks/http.go:184-245) — no drift:

  • GET /checks (list, :189-196) -> template /{owner}/{repo}/api/checks ✓
  • GET /checks/{sha} (combined, :234-243) -> /{owner}/{repo}/api/checks/{sha} ✓
  • GET+POST /checks/statuses/{sha} (:198-211, one template for two methods as documented) ✓
  • GET+POST /checks/tokens + DELETE /checks/tokens/{id} (:212-233) ✓
    7 method-routes collapse to exactly the 5 ExposedTemplates (http.go:29-35). Both lanes served at Handle (:64, api|api-browser); templates use the /api spelling with laneStrip normalization in test — matches 14 §14.12 lane rule and repoimport/mirror precedent. Tokens included. No handler logic touched (http.go diff is only the var block).

SEAM (law 8): cmd/walhub/checks.go:28 registers via api.RegisterExposed(checks.ExposedTemplates...) inside newChecksService — same shape as cmd/walhub/repoimport.go:30, mirror.go:39, collab.go:86. No new seam invented. RegisterExposed is additive + render-deduped (internal/api/discovery.go:67-72), so per-process repeat calls are safe.

CONTRACT TESTS pin both directions, genuine:

  • internal/checks/exposed_test.go TestExposedTemplatesExact pins exact 5-entry list/order (law-12 extension rule).
  • TestExposedCoversRoutes drives Handler.Handle() directly: both lanes, .git suffix, 405-recognized routes count as served, deep/extra-segment and non-checks paths assert Handle=false; every template must cover ≥1 route (no phantoms).
  • cmd/walhub/checks_discovery_test.go TestNewChecksServiceRegistersDiscovery reads the shipped api.Mount document (GET /api/v1 endpoints[]) post-call. Real assertions, not tautologies.

APIDOCS (web/src/pages/Apidocs.jsx): route rows + #checks-ci section accurate — verified: wct_. + checks:write (auth.go:17, service.go:165-181), strict-decode 400 (http.go:149-175), state enum pending|success|failure|error → 409 via ErrInvalidState→statusFor 409 (model.go:55-56, service.go:149), unknown sha → 404 via ResolveCommit/ErrNotFound (service.go:125-127, model.go:43-44), token mint 201 {id,token,scopes} (http.go:362) / revoke 204 idempotent (http.go:389, service.go:771), reads no-store + 401 WWW-Authenticate: Bearer (http.go:103-109). Worked example (mint → POST statuses → GET combined) matches wire shapes; SDK pointers repo.checks.list/combined/statuses/report + repo.ciTokens.create/list/revoke match web/sdk/src/checks.js:23-59.

CHECKS.jsx link: href="/api#checks-ci" (Checks.jsx:164) → anchor id="checks-ci" exists (Apidocs.jsx:166). ✓

NO BEHAVIOR CHANGE: diff = ExposedTemplates var + 1 RegisterExposed line + tests + Apidocs/Checks.jsx + 3 doc amendments. No handler/service/auth logic altered.

COVERAGE/DEPS/DOCS: internal/checks 96.4%, internal/api 95.2% (≥95% gate holds). No go.mod/package.json changes. Doc amendments accurate: 05_checks_statuses.md §4 + impl notes + Decisions (supersedes no-discovery-entries for checks only), 14_extensibility.md #271 amendment (scoped to checks; 01/02/03/C2/04/06/07 stay unlisted), 07_api.md §8 + Decisions (registered-template list matches actual RegisterExposed call sites).

TESTS (scratch, web/dist copied from main for the embed shell — fresh worktrees lack the built artifact, pre-existing infra, not PR-caused):

  • go test -race ./internal/checks/... ./internal/api/... → ok (both)
  • go test -race ./cmd/walhub/... (incl. new TestNewChecksServiceRegistersDiscovery) → ok
  • node --test sdk-checks + nav-api-right → 6 pass; FULL web/test/unit/*.test.js timed out at 120s in this env (PR reports 554 pass; change adds no logic modules, only static JSX rows/section + link — risk minimal)
  • gofmt clean, go vet clean (checks+api). No browser run (tests + reasoning, per task).

MERGE RECOMMENDATION: ready to merge.

Review of PR #293 (fix/issue-271), verified in scratch worktree at origin/fix/issue-271 (removed afterward). Main worktree untouched (still clean on main apart from pre-existing untracked .opencode/). ROUTES vs IMPLEMENTATION (spot-checked internal/checks/http.go:184-245) — no drift: - GET /checks (list, :189-196) -> template /{owner}/{repo}/api/checks ✓ - GET /checks/{sha} (combined, :234-243) -> /{owner}/{repo}/api/checks/{sha} ✓ - GET+POST /checks/statuses/{sha} (:198-211, one template for two methods as documented) ✓ - GET+POST /checks/tokens + DELETE /checks/tokens/{id} (:212-233) ✓ 7 method-routes collapse to exactly the 5 ExposedTemplates (http.go:29-35). Both lanes served at Handle (:64, api|api-browser); templates use the /api spelling with laneStrip normalization in test — matches 14 §14.12 lane rule and repoimport/mirror precedent. Tokens included. No handler logic touched (http.go diff is only the var block). SEAM (law 8): cmd/walhub/checks.go:28 registers via api.RegisterExposed(checks.ExposedTemplates...) inside newChecksService — same shape as cmd/walhub/repoimport.go:30, mirror.go:39, collab.go:86. No new seam invented. RegisterExposed is additive + render-deduped (internal/api/discovery.go:67-72), so per-process repeat calls are safe. CONTRACT TESTS pin both directions, genuine: - internal/checks/exposed_test.go TestExposedTemplatesExact pins exact 5-entry list/order (law-12 extension rule). - TestExposedCoversRoutes drives Handler.Handle() directly: both lanes, .git suffix, 405-recognized routes count as served, deep/extra-segment and non-checks paths assert Handle=false; every template must cover ≥1 route (no phantoms). - cmd/walhub/checks_discovery_test.go TestNewChecksServiceRegistersDiscovery reads the shipped api.Mount document (GET /api/v1 endpoints[]) post-call. Real assertions, not tautologies. APIDOCS (web/src/pages/Apidocs.jsx): route rows + #checks-ci section accurate — verified: wct_<id>.<secret> + checks:write (auth.go:17, service.go:165-181), strict-decode 400 (http.go:149-175), state enum pending|success|failure|error → 409 via ErrInvalidState→statusFor 409 (model.go:55-56, service.go:149), unknown sha → 404 via ResolveCommit/ErrNotFound (service.go:125-127, model.go:43-44), token mint 201 {id,token,scopes} (http.go:362) / revoke 204 idempotent (http.go:389, service.go:771), reads no-store + 401 WWW-Authenticate: Bearer (http.go:103-109). Worked example (mint → POST statuses → GET combined) matches wire shapes; SDK pointers repo.checks.list/combined/statuses/report + repo.ciTokens.create/list/revoke match web/sdk/src/checks.js:23-59. CHECKS.jsx link: href="/api#checks-ci" (Checks.jsx:164) → anchor id="checks-ci" exists (Apidocs.jsx:166). ✓ NO BEHAVIOR CHANGE: diff = ExposedTemplates var + 1 RegisterExposed line + tests + Apidocs/Checks.jsx + 3 doc amendments. No handler/service/auth logic altered. COVERAGE/DEPS/DOCS: internal/checks 96.4%, internal/api 95.2% (≥95% gate holds). No go.mod/package.json changes. Doc amendments accurate: 05_checks_statuses.md §4 + impl notes + Decisions (supersedes no-discovery-entries for checks only), 14_extensibility.md #271 amendment (scoped to checks; 01/02/03/C2/04/06/07 stay unlisted), 07_api.md §8 + Decisions (registered-template list matches actual RegisterExposed call sites). TESTS (scratch, web/dist copied from main for the embed shell — fresh worktrees lack the built artifact, pre-existing infra, not PR-caused): - go test -race ./internal/checks/... ./internal/api/... → ok (both) - go test -race ./cmd/walhub/... (incl. new TestNewChecksServiceRegistersDiscovery) → ok - node --test sdk-checks + nav-api-right → 6 pass; FULL web/test/unit/*.test.js timed out at 120s in this env (PR reports 554 pass; change adds no logic modules, only static JSX rows/section + link — risk minimal) - gofmt clean, go vet clean (checks+api). No browser run (tests + reasoning, per task). MERGE RECOMMENDATION: ready to merge.
Author
Owner

Fixed by PR #293 (review clean; routes match implementation, seam precedent honored; gates green), merged. Closing.

Fixed by PR #293 (review clean; routes match implementation, seam precedent honored; gates green), merged. Closing.
crueber added this to the v1 milestone 2026-09-10 22:27:09 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
crueber/walhub#271
No description provided.