docs(client): correct environments.update's accept-set and note updateVisibility's current state - #17948
docs(client): correct environments.update's accept-set and note updateVisibility's current state#17948claude[bot] wants to merge 2 commits into
Conversation
…eVisibility's current state `environments.update`'s JSDoc listed `plan` and `status` as updatable and the namespace route table listed them too. Both are read-only columns on the control plane; the generic PATCH route answers an unknown or read-only key with a 400 rather than dropping it, so the comment was teaching a call that fails. `visibility` was implied writable by the same prose while being server-owned. Prose only, at three sites in packages/client/src/index.ts: - the namespace route-table docblock: the PATCH accept-set is now display_name / is_default / metadata, with the 400-not-silent-drop rule and the redirects (billing routes for plan, lifecycle actions for status). - `update`'s JSDoc: the same accept-set with per-field detail, plus a provenance note that the 400 is an inherited reading no gate in this repo can check. - `updateVisibility`'s JSDoc: a current-state note that the call is refused today and that the 2026-09-12 maintainer ruling keeps visibility server-owned until public listing ships on its own endpoint. `patch` stays `Record<string, unknown>` and `updateVisibility`'s signature and body are byte-for-byte unmoved: narrowing a published accept-set, retiring the method or making it throw are maintainer rulings, not this doc fix's. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
…te doc correction The corrected JSDoc ships: `dist/index.d.ts` is in the package's `files[]` and is its `types` entry, and the new prose was measured present there after a build (positive control: the `updateVisibility` symbol resolves in the same file). So this publishes and `skip-changeset` would be wrong. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
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 196cf0a4a7a737e74d1eff5566d0153e0fad1da6 && git checkout 196cf0a4a7a737e74d1eff5566d0153e0fad1da6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bd25e897dc3cf9cf50af9dd23a7d36948ade6bd9 b379018e70dd0c9068271da51d36e71f407fbbef && git checkout -B drift-repro bd25e897dc3cf9cf50af9dd23a7d36948ade6bd9 && git merge --no-ff b379018e70dd0c9068271da51d36e71f407fbbef
node scripts/docs-audit/affected-docs.mjs --json bd25e897dc3cf9cf50af9dd23a7d36948ade6bd9
|
Contract reviewPR #17948 · card #17825 · head
① Prose-only, proven by diff rather than assertedEvery added and removed line of And the same conclusion from the other direction — ② The maintainer floor was not touched — the thing this card was most at risk of doingTriage fenced two moves as floor: narrowing ⭐ Worth naming because the card's own acceptance text invited the violation ("read-only fields are not part of it"). Triage caught that the parenthetical — "or are marked as such" — was the rulable half, and the delivery took exactly that half. The dev also hashed ③ The accept-set moved in both directions, with a live control⇒ Both old spellings are gone and the new one is present, with a 62-hit control proving the zeros are readings. Relayed on the dev's record, ⛔ not re-measured here: that Fences on the two named neighbours hold — ④ The declared extra — accepted, and the declaration is whyThe dev changed ⑤ Acceptance item 3 — NOT MEASURED, and that is the right answer, not a gapThe order named three outcomes for "report ⇒ ⛔ No status code was inferred from the card, from ⭐ Leg (3) is what makes this a measurement rather than a shrug. "I couldn't reach it" and "I proved I can reach other things and not this one" are different claims, and only the second one rules out a broken container. ⑥ The gate that failed, and whose fault it was
⭐ The dev's decision to stop rather than fix it was correct and is recorded as such. The checker's own text says ⛔ do not fill the line in on the claiming seat's behalf, because the declaration IS the judgement. A dev supplying a seat's judgement is the seat not making one. ⑦ The dev's open question — answeredA, adopted, with one correction to its premise. A new correction comment was not sufficient: the checker re-read and answered that the key was "on the thread but NOT in the card's claim comment". The carrier is the claim comment specifically; it had to be edited. That three-step sequence is already documented on #17800 — filed by this seat on 2026-09-12 — and today's three orders reproducing it is recorded there ( The printed-id discrepancy is a real bug and is filed: comment ⛔ Option C is not on the table and the dev was right to say so — the checker's own text answers it ("a predicate that reads prose is a heuristic"). ⑧ The third site — ⛔ not ridden into this PRThe dev found an orphaned control-plane route-table docblock in ⛔ Correctly not touched: it is a route implementation file, outside the claimed file face, and its ⑨ Changeset
Verdict and what remainsPASS.
Generated by Claude Code |
Part of #17825 — the rulable half. See "What stays open" below for the half this deliberately does not carry.
Clause-②: no
environments.update's JSDoc listedplanandstatusas updatable and the namespace route table listed them too. Both are read-only columns on the control plane; the genericPATCH /api/v1/cloud/environments/:idroute answers an unknown or read-only key with a 400 rather than dropping it, so the comment was actively teaching a call that fails. The same prose impliedvisibilitywas writable while it is server-owned.Prose only. No signature, no type narrowing, no deprecation, no thrown error.
The three sites
Line numbers re-derived on
origin/main84e6b05b6in my own worktree (control: 61environmentsmentions in the file, matching the dispatch order's reading — live instrument).:2775namespace route-table docblockupdate (displayName, plan, status, …)update (display_name, is_default, metadata)+ the 400 rule and the redirects:2936updateJSDocdisplay_name, plan, status, is_default, metadata:3058updateVisibilityJSDocpublicas if it workedvisibilityserver-owned until public listing ships on its own endpointAll four of the card's points are in the prose:
plangoes through the billing routes;statusthrough the lifecycle actions (archive / restore / suspend / resume);visibilityis server-owned andprivatetoday with its own endpoint arriving with public listing; and unknown or read-only keys are answered with a 400, not silently dropped.Two-direction check, at
b379018e7Direction 1 — the refused keys are gone from every accept-set. The
:2775accept-list line containsplan: false,status: false,visibility: false. They now appear only inside sentences that say they are refused.Direction 2 — the accepted keys are still there. The same line contains
display_name: true,is_default: true,metadata: true.Negative control — nobody deleted
updateVisibilityin passing. Its body (signature through closing brace) hashed before and after the edit:Fences held.
git diffis 49 insertions / 2 deletions in one file across exactly the three intended hunks.publishDrafts's visibility-flip line andupdateHostnameare both absent from the diff (grep over the diff exits 1 for each). Nothing inpackages/rest/**,packages/spec/**,content/docs/releases/**, or any route implementation.One declared in-place correction beyond the literal card
The
:2775line saiddisplayName, camelCase. The namespace docblock immediately below it (:2782) records the opposite as measured fact: "The/api/v1/cloud/*control plane this namespace calls speaks snake_case", and namesdisplay_name/is_defaultamong the keys in-repo CLI consumers actually send.updatepassespatchto the body untransformed, sodisplayNameis not a key that route accepts. Rewriting that line to list "only fields the control plane actually accepts" forced a spelling choice; I used snake_case so the two sites agree and so the accepted-key check above is literally true at both. Same defect class, same file, same claimed face, prose only — but it is more than the card's literal text, so it is declared here rather than buried.What stays open
Acceptance item 3 —
updateVisibility's actual status — is NOT MEASURED. The order names three outcomes and this is the third: unreachable. Every leg, with its exact error:/api/v1/cloud/environments. Every in-repo hit is a caller or a fetch-mocking test;packages/client/src/environments-delete-two-step.test.ts:9states the route "is served byobjectstack-ai/cloud", andpackages/runtime/src/http-dispatcher.ts:1274actively skips the path (if (path.includes('/cloud/environments/')) return undefined;).DEFAULT_CLOUD_URL(https://cloud.objectos.ai,packages/cloud-connection/src/cloud-url.ts:12),curlexits 56 withCONNECT tunnel failed, response 403. The session's own proxy logs the same event:connect_rejected · cloud.objectos.ai:443 · gateway answered 403 to CONNECT (policy denial or upstream failure). DNS resolves fine, so this is policy, not addressing.OS_CLOUD_URLand no control-plane token in the environment, and no environment id to PATCH.So the 400 is an inherited reading throughout — the card author's testimony. Triage recorded itself NOT MEASURED on control-plane behaviour and so do I. The comments say so at the point of the claim, in the style the namespace docblock already uses for exactly this constraint, rather than leaving a later reader to try. ⛔ No status code was inferred from the card, from the cloud issue, or from route source, and ⛔ the cloud issue was not read — it is outside this session's repository scope.
The follow-up card carrying "what happens to this published method" is the PM seat's to file, and its disposition — retire / throw / keep-and-note — is the maintainer's. This PR deliberately does not pre-empt it.
Verification
pnpm --filter @objectstack/client testpnpm --filter @objectstack/client typechecktsc --noEmitplus the test-layer check, 0 errorspnpm lint(repo-wide,eslint . --no-inline-config)scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack)pnpm --filter @objectstack/spec run check:skill-examplesclient-react— 258 prose examples type-check, 23 of them on the client SDK surface this PR editspnpm check:dual-build-cjs-loadsPREREQUISITE NOT MET, 33 packages have nodist/. It needs a whole-repo build; its own text says "This is NOT a pass: nothing was measured." CI builds fresh and measures it there.Exit codes were captured into a variable before any pipe; the two gate verdicts above are the gates' own printed verdict lines, not a bare shell status.
Changeset:
patchon@objectstack/client. Measured rather than assumed —dist/index.d.tsis in the package'sfiles[]and is itstypesentry, and the new prose greps present there after a build, with theupdateVisibilitysymbol as the positive control in the same file. The doc text ships to consumers as hover, soskip-changesetwould be wrong.Draft on purpose: ⛔ not flipped ready, ⛔ auto-merge not armed.
Generated by Claude Code