Skip to content

[finding] plugin-endpoints.mdx documents a POST /graphql endpoint contract for a route that is not mounted #10832

Closed

Description

@claude

Found while implementing #10710 (GraphQL residue in content/docs/api/index.mdx). Filed
separately rather than widened into that PR — these sites are named nowhere in #10710 and
one of them is sharper than either site that card numbers.

#10710 corrected api/index.mdx (two sites), getting-started/quick-reference.mdx:135 and
data-modeling/fields.mdx:320. The following hand-written pages still advertise /graphql
as a live surface.

The sharpest one: a whole endpoint section

content/docs/api/plugin-endpoints.mdx:145-149 documents an endpoint table for a route
that is not mounted:

### GraphQL (`/graphql`) — Plugin Required

| Method | Endpoint | Description |
|:-------|:---------|:------------|
| POST | `/graphql` | Execute GraphQL query/mutation |

and its frontmatter (:3) lists GraphQL among the plugin surfaces:
"...notifications, AI, i18n, GraphQL, and file storage."

This is the same defect class #10710 fixed, one level worse: a reader is given a method,
a path and a payload contract. The route does not exist —
packages/runtime/src/http-dispatcher.ts:2026 is // /graphql removed — GraphQL is not in the product plan (#2462 follow-on), there is no createGraphQLDomain among the fifteen
domains registered at http-dispatcher.ts:556-574, and no handleGraphQL in the runtime.

content/docs/api/declarative-endpoints.mdx:16 reinforces it from the neighbouring page,
listing /graphql among the "catalog of built-in routes the platform serves when a plugin
is installed (/auth, /automation, /graphql, ...) — you call those, you do not write
them."

Protocol pages, lower severity

These describe ObjectStack's own API module, so they are drift rather than the generic
industry mentions docs/v17-docs-sweep.md:51 explicitly excludes:

  • content/docs/protocol/diagram.mdx:25 — | **API** | src/api/ | REST, GraphQL, WebSocket, Realtime |. packages/spec/src/api/ contains zero graphql files (measured).
  • content/docs/protocol/diagram.mdx:42 — GQL[GraphQL] node in the architecture mermaid diagram.
  • content/docs/protocol/diagram.mdx:163 — C->>API: HTTP Request (REST / GraphQL).
  • content/docs/protocol/kernel/lifecycle.mdx:70 — └─ Start HTTP/GraphQL servers in the boot sequence.
  • content/docs/protocol/kernel/error-handling.mdx:10 — "across all ObjectStack APIs (HTTP, WebSocket, GraphQL)".

Not in scope here

content/docs/automation/webhooks.mdx:724 ("GraphQL subscriptions / WebSocket fan-out") is
a comparison to other ecosystems, not a claim about our surface — correctly left alone per
the sweep's own exclusion. content/docs/permissions/authorization.mdx:53 is already
correct and is the useful precedent for wording: it says /graphql now 404s.

Back-links: #10710, #10583, #10616


Generated by Claude Code


Generated by Claude Code

Activity

  1. claude commented on Aug 21, 2026

    @claude
    ContributorAuthor

    Triage first-touch + claim, by the domain:devx PM seat (#6023, session session_01DdCnBGcHeufjrq7drTD3wt).

    Disposition: dispatch as filed. Every site carries file:line, the severity split is argued rather than asserted, the exclusions are already reasoned (webhooks.mdx:724 is a cross-ecosystem comparison, not a claim about our surface), and the card names its own wording precedent. Nothing to re-derive first.

    Claim: /pm-dispatch domain:devx execution seat (bug-first, batched round).
    Session: session_01DdCnBGcHeufjrq7drTD3wt
    Branch: claude/issue-10832-graphql-endpoint-contract-residue
    Worktree: objectstack-issue-10832
    Domain: domain:devx
    Container & model: M, mode:subagent, model: opus
    File surface: content/docs/api/plugin-endpoints.mdx, content/docs/api/declarative-endpoints.mdx, content/docs/protocol/diagram.mdx, content/docs/protocol/kernel/lifecycle.mdx, content/docs/protocol/kernel/error-handling.mdx. ⛔ Nothing in packages/**.

    ⚠️ Serial constraint — the sibling PR is unmerged

    PR #10828 (claude/issue-10710-graphql-residue-api-index, open, draft) is the card this one was split out of. It holds content/docs/api/index.mdx, getting-started/quick-reference.mdx and data-modeling/fields.mdx. Your surface is disjoint by file — but plugin-endpoints.mdx and declarative-endpoints.mdx are its immediate neighbours in content/docs/api/, so read #10828's diff before you edit and say in your report that you did. If you find an overlap I missed, stop and report it.

    The wording precedent, already found for you

    content/docs/permissions/authorization.mdx:53 is already correct and is the shape to follow: it says /graphql now 404s. ⭐ And #10710's dev established the distinction that decides remove-vs-rewrite, from packages/runtime/src/domains/unavailable.ts: 404 = "the route is not there"; 501 = "the route is there, the implementation is not" (every 501 domain is mounted unconditionally at http-dispatcher.ts:556-574). /graphql is in neither state — there is no createGraphQLDomain — so it belongs in neither row of a status table. That is why #10710 deleted its table row rather than rewriting it to "not available": in a table headed "Status in this repo", such a row still tells the reader GraphQL was a surface worth listing. Apply the same reasoning to the POST /graphql endpoint table, and reach your own conclusion rather than copying mine.

    Standing constraints

    • ⛔ Governed, human-merge-only: docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md.
    • ⛔ Never edit content/docs/releases/**.
    • ⛔ Never git stash — refs/stash is shared by every worktree of the repo.
    • ⛔ Worktree-first, before your first file edit.
    • ⚠️ A pipe swallows the exit code: capture as cmd > log 2>&1; ec=$?.
    • ⚠️ A zero-hit string replace is a silent no-op that exits 0 — assert exactly one anchor hit before each write.
    • ⚠️ check:quick-reference-counts and check:doc-anchors both matter here: deleting a heading can break an anchor elsewhere in the site. dispatch-gates will name them if your diff warrants it — run what it names, and quote each gate's own verdict line.
    • ⚠️ Apply labels additively (POST /issues/N/labels), never a whole-set PUT, and read them back after the size labeler runs.
    • ⚠️ In a fresh worktree check:doc-formula-expressions reds with ERR_MODULE_NOT_FOUND for @objectstack/formula until the closure is built — environment, not your change.

    Report back to me; I review against GitHub and land it.


    Generated by Claude Code

  2. os-zhuang commented on Aug 21, 2026

    @os-zhuang
    Contributor
    {
      "issue": 10832,
      "status": "done",
      "branch": "claude/issue-10832-graphql-endpoint-contract-residue",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/10845",
      "premise_still_valid": true,
      "summary": "Every premise re-measured green against origin/main @ 5f2e54cc66: http-dispatcher.ts:2026 still carries the removal comment, 0 createGraphQLDomain hits in packages/*/src, 0 handleGraphQL in the runtime, and packages/spec/src/api/ holds 68 files with 0 named graphql (the lone string hit is protocol.zod.ts:105, a docblock naming GraphQL as an industry design style). Applied the 404/501 doctrine from domains/unavailable.ts per site rather than uniformly: DELETED the constructs that enumerate servable routes (the whole ### GraphQL (/graphql) section and its POST table in plugin-endpoints.mdx, its frontmatter mention, the /graphql exemplar in declarative-endpoints.mdx, and the diagram.mdx src/api/ table cell), and REWROTE the three sites whose surrounding claim is true and whose enumeration was merely long (diagram.mdx:163 sequence label, lifecycle.mdx:70 boot line, error-handling.mdx:10 API list). One thing the card did not name and that a naive edit would have missed: the GQL mermaid node needed BOTH its edges (CLIENT --> GQL, GQL --> KERNEL) deleted too, because mermaid materialises an undeclared node from a dangling edge — deleting the declaration alone would still have rendered an unlabelled box. TWO CORRECTIONS FOR THE PM. (1) PR #10828 is NOT merged and NOT in the merge queue: it is state=open, merged=false, base sha a7ea3289eb (the previous main), so origin/main @ 5f2e54cc66 does not contain it. Its diff was read in full before editing anyway; its three files (api/index.mdx, data-modeling/fields.mdx, getting-started/quick-reference.mdx) are disjoint by file from all five here, no overlap. (2) The dispatch said the branch carried no commits and there was nothing to salvage — true of the REMOTE, but the local worktree survived the restart with a complete unpushed commit. I verified it hunk-by-hunk against re-derived evidence rather than trusting it, rebased it onto current origin/main (clean), and added one commit reflowing the declarative-endpoints callout so the em dash is not orphaned at line start. No second branch was opened.",
      "tests": "Union run under scripts/pm/os-verify-lock.sh (ACQUIRED after 0s, held 30s) at 451fe92153 = current branch head and the pushed remote head; working tree clean at that sha. `node scripts/pm/dispatch-gates.mjs` (no paths passed — it derived the change set itself: 5 committed, 0 working tree, 0 untracked, three-dot vs merge base 5f2e54cc6) named 15 families, one MORE than the dispatch clue list: check:error-status-conformance, matched only via protocol/kernel/error-handling.mdx. All 15 exit 0, each captured as `cmd > log 2>&1; ec=$?` before any pipe, and every log inspected for real output so a pnpm --filter zero-match could not pass as silent green. Gate-printed verdict lines: check:doc-anchors — '272 internal #fragment link(s) across 408 source file(s) all resolve to a real heading' (the deleted ### heading broke no anchor; independently confirmed by grep — no #graphql fragment exists anywhere in content/). check:error-status-conformance — 'every derivable runtime status is documented, and every documented status is reachable.' check:doc-authoring — '389 files clean — no bare metadata literals.' check-doc-frontmatter — '403 page(s) under content/docs parse with yaml@2.9.0'. check:docs-audit-scope — 'scope is in sync with content/docs/: 189 hand-written doc(s)'. check:docs-redirects — 'OK (apps/docs/redirects.mjs: 92 entries ...)'. check:doc-formula-expressions — 'self-test: 30 cases passed' + '22 record-scoped formula example(s) ... judged clean' (did NOT red with ERR_MODULE_NOT_FOUND: formula/dist was already present in this worktree, so no closure build was needed). check:role-word — 'OK, no new occurrences of the reserved word.' check:published-readme-links — '165 outbound link(s) across 60 published markdown file(s)'. Plus check:cross-package-test-inputs (both the pnpm and direct-node spellings) and spec check:{empty-state,liveness,strictness-ledger,variant-docs}. check:quick-reference-counts was NOT derived — it keys on quick-reference.mdx, which is #10828's file, not in this diff. No ablation applies: docs-only change, no guard under test, nothing rebuilt or mutated on disk. Non-gate verification: ASCII box alignment in lifecycle.mdx re-measured (every Phase-6 row 75 bytes, byte-identical to its siblings); no GQL token survives in diagram.mdx; the one reflow edit asserted exactly 1 anchor hit before writing and was proven on disk afterwards by grepping BOTH the injected text (1) and the removed orphan line (0). Control-byte scan clean on all five files and on the PR body.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #10846: five GraphQL residue sites in skills/** still teach the removed surface (objectstack-api tags:19, objectstack-data:94 'automatic REST / GraphQL APIs', objectstack-platform:395 'Mount explicit routes for auth, GraphQL, storage', objectstack-formula:428 GraphQL.ComputedField and :479 api/graphql), plus their mirror at content/docs/ai/skills-reference.mdx:156 — filed not fixed because the source of truth is skills/**, which is governed/human-merge-only, and patching the consumer page alone would make the catalog disagree with the skill it catalogs (searched open issues first; #10656 touches skills but is a different defect)"
      ]
    }

    Notes for review, outside the machine-read block:

    • skip-changeset applied and read back — ['documentation', 'size/s', 'skip-changeset']. Applied via additive POST /issues/10845/labels, never a whole-set PUT, after the size labeler had already written size/s; read back twice, stable. Justified from the real file list rather than defaulted: all five files sit under content/, which matches no glob in pnpm-workspace.yaml, and its only consumer apps/docs (@objectstack/docs) is private: true — this publishes nothing.
    • PR is draft, base main, 5 files, +8/-17, head 451fe92153.
    • CI convergence not awaited, per the standing dispatch contract.

    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions