docs(client): say where a plain name IS honoured when registering an OAuth client (#17210) - #17755
Conversation
…n OAuth client `oauth.applications.register` posts the vendor route `/oauth2/create-client`, which strips a body member spelled `name` and requires `redirect_uris` already split. The one door that honours the plain spelling — `POST /api/v1/auth/sys-oauth-application/register`, the Console's session-required wrapper — is `disposition: server-only` in the auth route ledger and stays that way: no SDK method builds it. Prose only. The JSDoc on the method and the `create_oauth_application` example in the objectui actions page each state that asymmetry where their own reader meets it. No SDK method is added, no request or response type moves, and the ledger row is untouched. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude <noreply@anthropic.com>
… dist/*.d.ts Measured rather than assumed: `@objectstack/client`'s `files[]` is ["dist","README.md","CHANGELOG.md"], and after `pnpm --filter @objectstack/client build` the new docblock prose is present in both dist/index.d.ts and dist/index.d.mts (positive control: the pre-existing "the vendor's RECORD vocabulary" prose, same files, same grep; negative control: an absent marker, 0 hits). Published bytes move, so `patch`, not `skip-changeset`. Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 15 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 4fae4b749c3eb1578ac099c7bc8816e37cacb709 && git checkout 4fae4b749c3eb1578ac099c7bc8816e37cacb709
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e9efc403a9aeae715e0a56f8628e79e5ea25b4eb ae5f9fe257b8fd70a1d4fdbe83f0d95b5240ab09 && git checkout -B drift-repro e9efc403a9aeae715e0a56f8628e79e5ea25b4eb && git merge --no-ff ae5f9fe257b8fd70a1d4fdbe83f0d95b5240ab09
node scripts/docs-audit/affected-docs.mjs --json e9efc403a9aeae715e0a56f8628e79e5ea25b4eb
|
ACCEPT — reviewed on head
|
| the ruling said | the reading |
|---|---|
| the SDK gains no method | the request type is untouched: client_name?: string / redirect_uris: string[], zero bare name members, against a same-file positive control of 14 for ^\s+name\??: ⇒ the zero is a reading, and PR #17209's removal still holds |
| the ledger row is untouched | git diff --stat origin/main ae5f9fe2 -- packages/plugins/plugin-auth/src/auth-route-ledger.ts is EMPTY — stronger than "absent from the file list" |
the JSDoc states where name is honoured |
landed: 「There is exactly one door that reads a body member spelled name, and this method does not build it: POST /api/v1/auth/sys-oauth-application/register」, with the #15447 socket evidence restated at its measured strength (200 / mapped to client_name / column set — versus 201 / stripped / absent from get and list / null) |
…and that redirect_uris must be pre-split |
landed: 「⛔ This route performs no such split — redirect_uris must arrive pre-split, one entry per URL, which is what an SDK caller holds anyway」 |
| prose only | no packages/spec, no content/docs/releases/, no intersection with the lane files PRs #17725 or #17718 hold; check-clause2-carriers --pair 17755 prints 「both carriers agree, and its diff carries no widening tell」 |
Two declared deviations — both ACCEPTED, one of which corrects THIS seat's reasoning
patch, notskip-changeset. ⭐ The reason is right and it is one this seat would have got wrong: a docblock lands inpackages/client/dist/index.d.ts, and that package'stypesandexports["."].typesboth point at it, insidefiles: ["dist","README.md","CHANGELOG.md"]. ⇒files[]cannot be used to argue that a comment-only diff publishes nothing — JSDoc ships through.d.ts. (This seat's earlier use offiles[]on test(cli): pin the generatedappsi18n leaf that no provenance predicate judges #17726 stays correct: atest/file really does ship nowhere. The two cases are not the same shape, and it would have been easy to treat them as one.)⚠️ packages/client/dist/index.d.tsdoes not exist in the shared checkout, so the docblock's landing into it is NOT MEASURED here, ⛔ not a zero — but the structural basis holds andpatchis the conservative direction.- Wording, ⛔ not a misreport. The report reads 「Diff is exactly three paths … 33 inserted lines」. Measured: three paths total +61/−0; the 33 is the two named artefacts only (
actions.mdx+6,client/src/index.ts+27), with the changeset's +28 excluded. Every number is right; the sentence reads as if 33 covered all three. Noted so a later reader does not re-derive it as a discrepancy.
⭐ Two judgement calls worth naming, because both could have gone the lazy way
- 「ABLATION: none applies and none was faked.」 This diff adds no executable behaviour, so there is nothing to mutate — and rather than fabricate a passing leg, the report says so and puts a positive and a negative control on the measurement that actually needed one (the changeset question). ⭐ A fabricated green ablation is worse than an honest "not applicable"; this is the right call and is recorded as such.
- The third artefact was left alone, with reasons. The dispatch left
docs/qa/platform-checklist/areas/identity-auth.jsonto the dev's judgement. It read all three sites, found none of them states anything false (:1008already records this mount's distinguishing behaviour;:1017's step is about theclient_secret, not the name;:1078is a source row), and declined to edit — because a semantic edit there owes arevisionbump, which would invalidate that item's recorded runs for a clarification its steps never exercise. ⇒ correct, and stated in the PR's Acceptance notes rather than left silent.
What this PR does not do
It adds no SDK method and reverses nothing: POST /api/v1/auth/sys-oauth-application/register stays disposition: 'server-only', exactly as ruled. The trap is now visible where a caller reads, which was the whole of option B.
Flipping out of draft and enqueueing now.
domain:cli execution PM seat · #6024 · session session_01TSf4DV7ziu4V5j73e46b7c · 2026-09-12T02:55Z
Generated by Claude Code
Fixes #17210
Implements the director seat's ruling on this card (
#issuecomment-5634036848), option B: the SDK gains no method; the ledger row is untouched. The one measured trap is made visible in prose where its reader meets it. No SDK method is added, no request or response type moves, no accept set moves, andpackages/plugins/plugin-auth/src/auth-route-ledger.tsis not in this diff.Premises — falsified first, on
origin/mainat396eae33All four rows of the dispatch's premise table hold, plus the falsification trigger it named.
oauth.applications.registerstill existsapplications: {at:3997;register: async (req: {at:4043; pin named at:4041client_name?: stringat:4044andredirect_uris: string[]at:4045in the request type; the same two members at:947/:960inOAuthApplicationserver-onlyauth-route-ledger.ts:241,disposition: 'server-only', note verbatim as quoted on the carddispositionreturns 70 in that ledger — the probe is livenamein the typed request^\s*name\??:in lines4043-4055. Positive control, same file, same regex: 14 barenamemembers elsewhere (:1062,:1218,:1267,:1305,:1741, …)Every
client_namehit inpackages/client/src/index.tswas read in context, not counted::947(anOAuthApplicationmember),:1008(inside theOAuthApplicationPublicPick),:4025/:4027(the existing #15447 docblock),:4044(the request type). Premise not falsified —premise_still_valid: true.1. The JSDoc — landed bytes
Added to the
oauth.applications.registerdocblock, after themetadataparagraph and before thePinned byline:Both halves the ruling asked for are stated:
nameis honoured only by the Console's server-only mount, and this route requiresredirect_urispre-split.2.
content/docs/protocol/objectui/actions.mdx— the same fact, that page's own idiomNot a copy-paste of the JSDoc. The page is an action-authoring reference, so the note is written to an action author, about the
targetof thecreate_oauth_applicationexample, and it is addressed at the:424neighbourhood — a new#### ⚠️ That target is not interchangeable with the SDK's registration doorimmediately after that example'sresultDialogexplanation, inside the same Result Dialog section. It says why the YAML above is that short (the mount does the plain-namemapping and the textarea split on the action's behalf), that the ledger carries itserver-onlyfor exactly that reason, and what an author would have to change — rename the name param toclient_name, pre-split the redirect URLs — if they re-pointedtargetat the provider route.The docs-drift tool cannot surface this page for this diff (it states the rule by its YAML inputs, so it shares no identifier with the SDK file), which is why it was edited by hand.
3. The measured evidence, restated and not overstated
From the #15447 round, on a real socket, driven through the real client:
POST /api/v1/auth/sys-oauth-application/registerwith a body spellednameanswered 200, the value came back asclient_name, and thesys_oauth_applicationrow'snamecolumn was set./oauth2/create-clientanswered 201 with the value stripped: absent from the response, absent fromoauth.applications.get, absent fromoauth.applications.list,nullin the DB column.Nothing in this PR claims more than those two readings. In particular it does not claim the mount is reachable from the SDK, nor that any behaviour changed.
4. Changeset —
patch, measured, not assumedskip-changesetwas tested and refused.@objectstack/client'sfiles[]is["dist","README.md","CHANGELOG.md"]; afterpnpm --filter @objectstack/client build, the new docblock prose is present in bothdist/index.d.tsanddist/index.d.mts— it ships as editor hover to every consumer.grep -c "A plain \name` IS honoured somewhere"→1` in each of the two filesgrep -c "the vendor's RECORD vocabulary"(pre-existing docblock prose) →1in each0Published bytes move ⇒
patch..changeset/17210-oauth-register-name-trap-prose.md.Gates
Derived mechanically with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no hand-written path list), then reconciled with--rancarrying each command's own exit code, captured before any pipe.pnpm lint(whole repo,eslint . --no-inline-config) — exit 0.pnpm --filter @objectstack/client test— 42 files / 506 tests passed, including theoauth-applications-register-request-members.test.tspin.pnpm --filter @objectstack/client typecheck— exit 0, test layer included (check:test-typecheck: 0 files / 0 errors).pnpm --filter '@objectstack/client^...' build— exit 0. Heavy runs went throughscripts/pm/os-verify-lock.sh; verdicts read from itsVERDICT command-exitlines.grep -naPover both edited files, zero hits, on top ofcheck:nul-bytesgreen.NOT MEASURED:
pnpm check:dual-build-cjs-loads, reason: it exited 3 withPREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ … ⛔ This is NOT a pass: nothing was measured. It wants a whole-repopnpm build(34 packages listed, none of them@objectstack/client, which is built). That is CI'sBuild Corerun, and the gate reads emitted runtime JS where comments are stripped, so a docblock-only diff has no way to move it. Declared rather than run.Clause-②: no, re-derived from the delivered diff: three files, 33 inserted lines plus a changeset, zero deletions; no declaration, export, accept set or published symbol moves.Acceptance notes
docs/qa/platform-checklist/areas/identity-auth.json(:1008,:1017,:1078) — read, judged out of scope, not edited. The dispatch left this call to me, so here is the reasoning rather than the verdict alone.All three lines were read in context. They belong to
identity-auth.oauth-app-consent-loop, and none of them states anything false.:1008enumerates the three registration doors and already records this mount's distinguishing behaviour ("the session-required wrapper that splits the redirect-URL textarea into redirect_uris");:1017offers two of those doors as alternatives for a step whose object is theclient_secret, not the client's name;:1078is asourcerow naming the ledger. Nothing there claims the provider route honours a plainname. The item never names an application, at any step, in any acceptance clause or negative — so the trap is not reachable from it, and there is no assertion to correct.Against that, editing it has a real cost in the checklist's own machinery: a semantic edit owes a
revisionbump (scripts/check-platform-checklist.mjsholdsrevisionequal to the lasthistoryentry precisely so that a changed item cannot silently re-validate old run results), which would invalidate the item's recorded runs for a clarification its steps never exercise.So: read, found accurate, left alone. It is not orphaned — the carriers that will next open it are the
checklist-authorcoverage sweep and anychecklist-testrun selectingidentity-auth.oauth-app-consent-loop.Red lines held. No
packages/specpath in the diff.auth-route-ledger.tsuntouched. Nocontent/docs/releases/path. No file held by #17725, #17718 or #17093 is reached — the diff ispackages/client/src/index.ts,content/docs/protocol/objectui/actions.mdxand one new changeset.Generated by Claude Code