docs(rest,qa): record the deliberate absence of a REST action-invoke route (#7680) - #7913
Conversation
…route (#7680) Per the maintainer ruling of 2026-08-12 on #7680: record the doctrine, do not build the route. `@objectstack/rest` mounts no action-invoke route, so there is no REST surface on which an action's `requiredPermissions` could be enforced. A QA probe (#7637) measured all four candidate spellings 404 and read the `data-actions` family as clone / import / import-jobs / export. That is a surface which does not exist, not a gate which fails open — but nothing in the tree said so, and the checklist item scored against it as if it did. Two records, no behaviour change (the TS diff is comment lines only): - `rest-route-ledger.ts`, the `data-actions` block: state that the missing invoke route is deliberate, name the platform paths that DO enforce ADR-0066 D4 (`actionPermissionError` via the dispatcher `/actions` domain, MCP `run_action`, `ql.executeAction`) and note they are ledgered in `packages/runtime/src/route-ledger.ts` rather than here — including the trap that `actionPermissionError`'s own docstring says "the REST `/actions/...` route" and means the DISPATCHER's surface. Written for the next author of that route: if one ever ships for real demand, server-side `requiredPermissions` enforcement is a day-one requirement, not a follow-up, or #3923's client-side fail-open returns server-side. - `access-security.capability-declaration-lifecycle` (rev 1 → 2): re-point the REQUIRE clause at the surface that enforces it. It asked for a 403 from a REST resource carrying `requiredPermissions`; unrunnable as written, so the run fell back to a platform capability. Now grant / deny / regrant on the platform action path against the four shipped predicate-matrix specimens (held / missing / and / empty), with the fail-open, stale-grant, over-block and AND-vs-OR failure modes spelled out in `negative`, and a REST 404 explicitly scoring nothing either way. The clause is re-pointed, not softened: it still goes red if the gate breaks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ECUjuBFnZHLAfkf2ZQh2rd
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 3 release-owned page(s) also reference the affected code. These are read-only:
|
Fixes #7680
Executes the maintainer ruling of 2026-08-12 (comment
5261755580) as written: 记录口径,不建路由 — record the doctrine, do not build the route. No REST action-invoke route is added here; the ruling declines to build one, and adding it would reverse the decision.Premise re-verified on current
origin/main(e3a6f6e)data-actionsfamily inrest-route-ledger.ts…/actions/<name>invoke rowpackages/rest/src/api/v1/actions/:n,/api/v1/action/:n,/api/v1/objects/:o/actions/:n,/api/v1/data/:o/actions/:n)actions-shaped REST rowGET /api/v1/approvals/requests/:id/actions— an approvals audit trail, unrelatedactionPermissionError@packages/runtime/src/action-execution.ts:383(ADR-0066 D4), reached fromruntime/src/domains/actions.ts(dispatcher/actions→ql.executeAction) andruntime/src/domains/mcp.ts:433(MCPrun_action)Premise holds. Line numbers were re-derived by text search, not trusted from the issue.
One correction to the issue's framing, which the ledger comment now records. The issue reads as though nothing requires a package capability. Four shipped
type:'script'actions do —showcase_zoo_perm_held/_missing/_and/_emptyinexamples/app-showcase/src/ui/actions/predicate-matrix.action.ts— and beingscriptactions on thezooobject, the platform action path can invoke them. What they lack is a REST door, not a requiring resource. That is what makes the re-pointed clause measurable against real fixtures rather than themanage_metadatafallback the QA run had to use.1 —
packages/rest/src/rest-route-ledger.ts,data-actionsblock36 added lines, all comment; zero code lines changed (
git difffiltered for non-comment+/-lines in the TS file returns empty). It records that the missing invoke route is deliberate, names the platform paths that do enforce ADR-0066 D4 and where they are ledgered (packages/runtime/src/route-ledger.ts, not this file), and carries the ruling's forward-looking clause verbatim in substance:It also disambiguates a live trap for that author:
actionPermissionError's own docstring says it is single-sourced "so the REST/actions/...route and the MCPrun_actionbridge enforce the SAME declaration" — that "REST" is the dispatcher's HTTP surface, not a route@objectstack/restmounts. Read without the split, that docstring reads as a promise this package already keeps.2 —
access-security.capability-declaration-lifecycle, revision 1 → 2Re-pointed, not deleted and not softened.
Before (acceptance clause 2):
After:
The old clause was unrunnable, which is why the run fell back to a platform capability: it demanded a 403 from a surface with no door to knock on. The new one names four shipped fixtures and a surface that exists.
The re-pointed clause still has a failure mode — exactly what turns it red
Enumerated in
negativeso a runner cannot tick it on vibes:showcase_zoo_perm_heldsucceeds. This is ADR-0066 D4 failing open, the server-side shape of Action.requiredPermissions 在 Console 端形同虚设:会话不下发 systemPermissions + 客户端 fail-open(17.0.0-rc.0) #3923. Also red:showcase_zoo_perm_missingexecuting for anyone (nobody is grantedshowcase.restricted_ops), orshowcase_zoo_perm_andexecuting for a caller holding onlyshowcase.export_data(the gate is an AND, not an OR).showcase.export_datais withdrawn fromOpsPermissionSet, the former holder still executes. The gate is reading a cached grant.showcase_zoo_perm_emptyis refused for anyone. "Declares nothing" is not "denies everything".Concretely: delete the
if (missing.length === 0) return null;guard's inverse — i.e. makeactionPermissionErrorreturnnullunconditionally — and legs 1 and 2 go red. That is the property the item is pinning.Supporting edits:
variantsgains a REGRANT leg;steps3 splits into surface-identification + the three legs + the missing/empty cross-checks;trapsgainsdispatcher-vs-hono-route(RUNNER.md's own name for "route exists in unit tests, 404s on the real server" — the precise trap this card is about);knownGapsmoves from "no shipped resource REQUIREs it" (false — four do) to "which platform surface the build exposes, recorded per run";sourcegains the runtime enforcement files and this ledger block.Verification
This is a comment + checklist-prose card, so there is no behaviour to pin and I have not invented a test that pretends otherwise. The TS diff is comment lines only. What was run:
node scripts/pm/dispatch-gates.mjs <changed paths>check:authz-resolver,check:meta-type-normalizedpnpm check:authz-resolverpnpm check:meta-type-normalizedpnpm check:nul-bytespnpm check:platform-checklistpnpm --filter "@objectstack/rest..." buildpackages/resttest suitelint.yml's full farm is left to CI, per dispatch.Changeset: none — a comment/prose diff releases nothing. Needs the
skip-changesetlabel rather than an empty changeset, which would strand the release.Explicitly not done
skippedPlatform) and the boot-time Zod hard-reject are untouched; the ruling confirms both as expected.rest-server.ts,runtime/src/domains/**,runtime/src/sandbox/**,packages/mcp/**, orcontent/docs/releases/**.Generated by Claude Code