Repository navigation
Description
Found by enumerating the /ai/** family for #3708 — the last dynamic family of the #3563 audit, and the one the capstone's wildcard row had been covering for.
The two sets
client.ai methods |
exactly 3: nlq, suggest, insights |
| URLs they build | /api/v1/ai/{nlq,suggest,insights} |
| Places any repo mounts those paths | zero |
Routes buildAIRoutes() really mounts |
12: chat, chat/stream, complete, models, status, effective-model, conversations ×6 |
| Of those 12, expressed by the SDK | zero |
They do not overlap at any point. The SDK's AI namespace is entirely dead; the AI service that was actually built is entirely unreachable through the SDK.
Evidence
service-ai/buildAIRoutes()live in thecloudrepo (Cloud/EE package). Its returned table is the 12 routes above;nlq,suggest,insightsare not among them — the strings do not appear inpackages/service-ai/at all.- In this repo,
/nlq/suggest/insightsappear in exactly three places:client/src/index.ts(the calls),client/src/client.test.ts(which assertsexpect(url).toContain('/api/v1/ai/nlq')— the URL the client builds, not whether anything answers it), andspec/src/api/plugin-rest-api.zod.ts(DEFAULT_AI_ROUTES). DEFAULT_AI_ROUTEShas no runtime consumer. The only references outside its own definition are inplugin-rest-api.test.ts, which validates its shape. Nothing mounts from it.aiNlq/aiSuggest/aiInsightsare declared as optional protocol methods (protocol.zod.ts:1259,aiNlq?(…)). No service in either repo implements them.
Declared in the spec, expressed by the client, mounted by nothing. Instances 7–9 of the the method exists ≠ the method can be called class, after analytics.explain / analytics.meta (#3584), meta.getView (#3611), i18n.getTranslations / getFieldLabels (#3636), projects.listTemplates (#3702).
Why the guard missed it until now
The capstone (#3642) matched these three against the * /ai/** wildcard row. A wildcard asserts the family is claimed, not that the URL resolves — so all three counted as matched, and the wildcard-only ratchet (3) read as "weak evidence pending enumeration".
It was not weak evidence. It was wrong evidence. The row's note even asserted the client "expresses nlq/suggest/insights against the REST AI routes" — never verified, and false.
#3708 replaces it with a bounded prefix exemption plus a real ledger in cloud, and drops the ratchet to 0: every remaining matched call now rests on an exact enumerated route.
Also user-visible
The console's developer API-discovery panel (objectui apps/console/src/pages/developer/hooks/useApiDiscovery.ts) lists /nlq, /suggest and /insights alongside the real endpoints with "try it" bodies. It is advertising three endpoints that always 404 — Prime Directive #10 in the literal sense. Worth fixing with whichever option below is chosen.
What needs deciding
This is a product decision, not a cleanup, which is why it is filed rather than fixed:
- Implement the three endpoints in
service-ai. NLQ (natural language → query AST), field suggestions and record insights are genuine capabilities with a declared request/response contract already in spec, and an LLM adapter already wired. If they are on the roadmap, the SDK surface and the spec are already written. - Remove the namespace — delete
client.ai.*,DEFAULT_AI_ROUTES, and the three optional protocol methods, and drop the three entries from the console's discovery panel. Correct if these were an early design that thechat/conversationssurface superseded (which the shape of what was actually built suggests). - Express the real surface instead — give the SDK
ai.chat/ai.conversations.*/ai.models, which is where the 10gaprows in cloud's newai-route-ledger.tspoint. This is a superset of option 2 and the only one that makes the SDK useful for AI at all.
Recommendation: 2 + 3 — retire the three unbuilt methods and express what exists. Option 1 only if NLQ/suggest/insights are genuinely planned; shipping three endpoints to satisfy methods nobody can be calling (they 404 today) is the wrong order.
Either way the guard is already in place: cloud's UNMOUNTED_AI_CLIENT_METHODS asserts all three still match nothing, so implementing any of them fails the test and forces an sdk ledger row.
Refs: #3708 (the enumeration), #3642 (the capstone whose wildcard hid this), #3563 (the audit family), objectstack-ai/cloud packages/service-ai/src/ai-route-ledger.ts.
Activity
docs(api): the published AI surface was inverted — correct it (#3718)
test(client,runtime): the last wildcard was wrong evidence, not weak …
feat(client)!: remove the `ai` namespace — three methods, none of whi…
chore(docs): baseline `role` in the client-sdk AI examples — it is th…
chore(spec): regenerate the public API surface snapshot for the AI re…
feat(client,spec)!: express the AI surface that exists, retire the de…
os-zhuang commentedon Jul 28, 2026
Resolved as 2 + 3 — the recommendation in the issue. Three PRs, one per repo that held a piece of the disjointness:
client.ai.{nlq,suggest,insights},DEFAULT_AI_ROUTES,AiProtocol, and the six deadAi{Nlq,Suggest,Insights}*spec schemas; gave the SDK the 10 methods the real routes answer8b9d71e)ai.*method against the tablebuildAIRoutes()returns;gap10 → 1,UNMOUNTED_AI_CLIENT_METHODS3 → 0b98d601)/nlq/suggest/insightswith "try it" bodies; its AI group now mirrors all twelve mounted routesThe two sets are no longer disjoint:
client.aiwent from 3 methods that nothing answered to 10 that mounted routes do, and the AI surface went from 0/12 expressed to 10/12.The two routes still unexpressed are recorded rather than forgotten.
GET /statusandGET /effective-modelareserver-only(operator diagnostics — and they now appear in the console panel, which is the audience they were written for).POST /chat/streamis the single remaininggap: the generic-SSE twin of/chatwithout the tool loop or persistence, left unexpressed deliberately becauseai.chatStreamalready covers streaming against the fuller route. Express it if a caller ever needs raw model parts without the tool loop.On the guard:
/api/v1/ai/is now a bounded prefix exemption in the capstone (#3642) — bounded from both ends, onlyai.*may use it and the reachability check lives in the repo that mounts the routes, next to them. The wildcard-only ratchet stays 0; these URLs are matched beforematches()is consulted, so they never touch the* /ai/**row that certified three dead methods for years. Mutation-checked: repointingai.modelsat/modelzfails two tests in cloud, naming the method and the URL it built.Closing on the strength of the two merged PRs and the decision being fully executed. objectstack-ai/objectui#2921 is the last mechanical step and is open with CI running — if it does not land, reopen this rather than filing fresh, since the panel is the one place a user can still click a 404.
Generated by Claude Code
docs(spec,client): regenerate the protocol reference and document the…
feat(client,spec): `ai.agents.*` and `ai.pendingActions.*` — the AI r…
docs(client): the README's AI example shows the surface that exists
docs(client): the README's AI example shows the surface that exists (o…
test(spec): the second api/ file group's test titles state each cited…