docs(spec): ApproverType.describe() qualifies manager instead of offering it as a bare allowed value - #17640
Conversation
…fering it bare `ApproverType` carried no `.describe()` at all, so the generated reference page `content/docs/references/automation/approval.mdx` rendered `## ApproverType` with nothing but an `### Allowed Values` list: `manager` — the one rung an author cannot operate on a stock install, because `sys_user.manager_id` has no product write surface — read exactly like the nine members that work. The describe qualifies it and POINTS at the remedy rather than restating it: `MANAGER_ONLY_REMEDY` / `MANAGER_ONLY_ROUTES` in `packages/lint/src/validate-approval-approvers.ts` stay the single authoritative copy, and that file's `DEPENDENCY` docblock now names this new string among the lines that go stale if the column ever gains a write surface. No enum member is added, removed or renamed. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
`pnpm --filter @objectstack/spec gen:docs` rewrote all 222 reference pages; exactly one changed. The `## ApproverType` section now carries the qualifying describe above its `### Allowed Values` list, and the `type` rows of `ApprovalNodeApprover` and `ApprovalNodeConfig.approvers` — previously blank — carry it too. The page was never hand-edited. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 135 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 10ff2592b80ac969d9eaf7b0c968c1533c0a5923 && git checkout 10ff2592b80ac969d9eaf7b0c968c1533c0a5923
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 76c9fab30ca406b7b1f06b8ca3db286af9f2bf8b 1aca42a2d7599ada6b901c9a56a85f7293b8d757 && git checkout -B drift-repro 76c9fab30ca406b7b1f06b8ca3db286af9f2bf8b && git merge --no-ff 1aca42a2d7599ada6b901c9a56a85f7293b8d757
node scripts/docs-audit/affected-docs.mjs --json 76c9fab30ca406b7b1f06b8ca3db286af9f2bf8b
|
Part of #17579
ApproverType's accept set is byte-identical (no member added, removed or renamed),check:api-surfaceis green on the rebuiltdist/*.d.ts, andcheck:authorable-surfaceleft the checked-in artifacts untouched. The only new bytes are prose.What this changes
ApproverTypecarried no.describe()at all, so the generated reference page rendered## ApproverTypewith nothing but an### Allowed Valueslist.manager— the one rung an author cannot operate on a stock install — read exactly like the nine members that work.packages/spec/src/automation/approval.zod.ts— the enum gains a.describe()that qualifiesmanagerand points at the remedy instead of restating it.content/docs/references/automation/approval.mdx— regenerated, never hand-edited (see the generator evidence below).packages/lint/src/validate-approval-approvers.ts— one paragraph added to theDEPENDENCYdocblock aboveMANAGER_ONLY_REMEDY, naming the new describe among the lines that go stale ifmanager_idever gains a write surface. The triage comment called this out as the part a round is most likely to skip; it is a comment only.The describe, verbatim:
Route (iii), the triage default: it points, it does not restate.
MANAGER_ONLY_REMEDY/MANAGER_ONLY_ROUTESstay the single authoritative copy of the 667-character remedy. No third copy was written, so there is nothing new to keep in step — a pointer cannot drift into disagreement with what it points at. ⛔ No export was added topackages/spec(route (ii) was fenced).Prerequisite readings — taken by state on
origin/main@ea2940d1c4, not inherited from the card1. The⚠️ Correction to the card:
managerdescribe.ApproverTypehad no.describe()to read unqualified — it had none. Its JSDoc (approval.zod.ts:24-30) renders nowhere:renderSchemaSection(packages/spec/scripts/lib/schema-section.ts:328-330) printsmainDef.description, which only.describe()/.meta({description})fills. Positive control that the mechanism is real:HttpMethod's.describe()(packages/spec/src/shared/http.zod.ts:45) renders atcontent/docs/references/shared/http.mdx:46. The card's substance stands — the page soldmanagerunqualified — but the fix is a describe added, not a describe edited.2. The generated page.
content/docs/references/automation/approval.mdx:113is## ApproverType,:115is### Allowed Values,:117is themanagerbullet — the exact window the card names. Its banner (:6) still reads⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate.and the source callout (:9-10) still namespackages/spec/src/automation/approval.zod.ts. Both hold.3. ⭐
sys_user.manager_idhas no product write surface — re-measured, with the negative leg lit.A throwaway probe drove the identity write guard's real
beforeUpdatehandler in a user context, then was deleted (working tree verified clean afterwards); both legs went through the same handler:{ id, name, manager_id }came back as{ id, name }—manager_idnever reaches the row.{ id, manager_id }alone is refused loudly:PERMISSION_DENIED/403.locale— a field that IS writable — passed the same probe unchanged. ⇒ the instrument is not a refusal that refuses everything.identity-write-guard.test.ts:132(/Editable fields: name, image, locale/) and:186(getManagedUpdateWhitelist('sys_user')equalsnew Set(['name','image','locale'])). Both green in the same run.readonly: trueon the column, with a discriminating control —readonlyoccurrences inside each field block ofpackages/platform-objects/src/identity/sys-user.object.ts:readonlyoccurrencesmanager_idprimary_business_unit_idlocale(control — writable)name(control — writable)No accepting route: across the 59 non-test
packages/plugins/plugin-auth/src/*.tsfiles (enumerated withgit ls-tree, not by grepping contents for a filename),manager_idoccurs 5 times against a firing control ofphone_numberat 28 — and all 5 sit inmanaged-extension-fields.tsandsys-user-writable-fields.ts, both of which name the column only to record that it is not writable (MANAGED_EXTENSION_EDITABLE_FIELDS.sys_userisnew Set(['locale'])). Counts aregrep -o | wc -l, notgrep -c.⇒ The premise holds.
premise_still_valid: true.4. What PR #17575 already fixed — not redone. The
managercallout incontent/docs/automation/approvals.mdx:65-87and the qualification incontent/docs/capabilities/approvals.mdx:14-19are both present and untouched by this PR (git diffnames neither file).The page was regenerated, not hand-edited
The generator rewrote all 222 reference pages and exactly one moved. Beyond the
## ApproverTypesection, the describe also reached the previously blank Description cells of thetyperows inApprovalNodeApproverandApprovalNodeConfig.approvers— measured on the regenerated output, not predicted from the zod registry semantics (which read the other way).Verification
pnpm --filter @objectstack/spec check:generated— all 15 green, includingcheck:api-surface,check:authorable-surface,check:docs.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackover this diff: 100 families derived, 98 run green, 0 unrun, 2 NOT MEASURED. Reconciled with--rancarrying each recorded exit code.pnpm check:dual-build-cjs-loadsandpnpm check:lean-entry-closureboth exit 3 — PREREQUISITE NOT MET (they load built entry points repo-wide and this worktree has no fullpnpm build). ⛔ Recorded as NOT MEASURED, not as green; CI builds and runs both.check:skill-examplesfirst exited 1 for want ofpackages/client-react/dist, a prerequisite refusal, not a red. Afterpnpm --filter @objectstack/client-react buildit is green —✅ 258 prose examples type-check across 3 surface(s).pnpm --filter … run test --concurrency=2forwarded--concurrencyinto the vitest script and failed on flag parsing. Re-run correctly aspnpm --workspace-concurrency=2 --filter … run test.pnpm --workspace-concurrency=2 --filter @objectstack/spec --filter @objectstack/lint run test— spec 473 files / 13429 tests passed, lint 103 files / 3747 tests passed.pnpm --workspace-concurrency=2 --filter @objectstack/spec --filter @objectstack/lint run typecheck— exit 0, both test-layer debt ledgers held.pnpm lint(eslint . --no-inline-config, the whole repo) — exit 0. No narrowing was needed, so none is claimed.pnpm check:nul-bytesgreen, plus a direct control-character scan over the four touched files (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'→ no match).Heavy runs went through
scripts/pm/os-verify-lock.sh; the verdicts above are itsVERDICT command-exitlines, with each exit code captured before any pipe.Changeset
.changeset/17579-approver-type-manager-describe.md—@objectstack/spec:patch. The describe ships:@objectstack/spec'sfiles[]carriesdist,json-schemaandsrc/**/*.zod.ts, and the new string is measured in all three on the built tree —dist/automation/index.js+.mjs(2 files, against a lit control of an existing describe from the same module, also 2), fourjson-schema/documents, and the shippedapproval.zod.tssource. Prose only, no surface widening ⇒patch, notminor.@objectstack/lintis deliberately not graded. It publishesdistonly, and the new docblock sentence is absent from it (0 files) while a runtime string from the same source file is present in 4 and a pre-existing comment from the same docblock is absent in 0 — comments are stripped by construction, so nothing published moves there.Out of scope
managerenum member is not removed. Removing a member is a larger question and needs its own ruling.managerapprover as a directory-sync dependency #17575's callouts, no change toApproverType's shape, no touch toplugin-author the write guards, no export added topackages/spec.content/docs/releases/untouched.{ type: 'manager' }resolves a column no product surface can write:sys_user.manager_idis refused by the data API and absent from the auth admin endpoints #16678 holds the open question of whethermanager_idgains a product write surface. This wording is correct under every one of its options — the column still needs populating beforemanagerresolves — and theDEPENDENCYdocblock now lists this string among the lines that would need updating in the same change that opens such a surface.Generated by Claude Code