Skip to content

fix(rest): the dispatcher's /packages domain is the one implementation of the package read and delete routes (#14503) - #16628

Draft
claude[bot] wants to merge 11 commits into
mainfrom
claude/issue-14503-package-routes-single-implementation
Draft

fix(rest): the dispatcher's /packages domain is the one implementation of the package read and delete routes (#14503)#16628
claude[bot] wants to merge 11 commits into
mainfrom
claude/issue-14503-package-routes-single-implementation

Conversation

@claude

@claude claude Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Fixes #14503

Summary

GET /api/v1/packages, GET /api/v1/packages/:id and DELETE /api/v1/packages/:id now have one implementation: the runtime dispatcher's /packages domain (packages/runtime/src/domains/packages.ts). @objectstack/rest's registerPackageRoutes no longer mounts its own copies of those three routes; it mounts POST /api/v1/packages/publish and nothing else. This executes the maintainer's ruling on #14503 (2026-09-02, verbatim 「同意」, option A), in the ruled order: measure → verify compositions → delete with the survivor pinned.

The SHADOWS sentence the card is about, the opposite "mounts FIRST in the production stack" claim in the same file, the #7563 docblock's "independent of composition order" claim, and the two module headers calling the ?version= refusal "dormant behind a Hono collapse" are all taken with the deletion.

Step 1 — the census, re-taken on today's tree (ce8caba914, the branch base)

The prior census (comment 5544498087) was measured before PR #15859; every verdict below is a fresh reading with its control on the same corpus.

behaviour objectstack (production TS) Console (objectui @ pin a472b07) content/docs verdict
source: 'registry' | 'database' | 'both' readers 0 (10 grep hits: 3 unrelated source: 'database' on metadata-loader items, 5 in the producer itself, 2 SDK comments) — control .writable = 17 0 — control .writable = 26 0 EMPTY → deliberately removed, recorded in the changeset
?version= senders to a packages door 1ScopedEnvironmentClient.packages.get(id, version?) (packages/client/src/index.ts) — control ?overwrite= = 2 0 — control ?keepData= = 1 (the Console's DELETE sends the dispatcher's parameter) 0 one sender, of the parameter, not of the repeated-parameter refusal (nobody repeats it)

The SDK sender is already carded: #12034 (open, pm:awaiting-maintainer) holds the maintainer's 2026-08-26 ruling that packages.get binds the bare row; its remaining half is exactly the scoped binding this PR now makes bindable. Nothing is ported into the dispatcher: a version-scoped durable lookup was a behaviour only the REST copy had, and porting it would change the dispatcher's data source — materially bigger than what was ruled.

Step 2 — compositions, and the fork

Every in-tree composition that mounts the REST package registrar also mounts the dispatcher: packages/cli/src/commands/serve.ts (createRestApiPlugin :4019, createDispatcherPlugin :4039), packages/verify/src/harness.ts (:594/:595), packages/plugins/plugin-dev/src/dev-plugin.ts (:856/:873). None turns enableProjectScoping on (mergeBootConfig forces it false on the standalone boot).

The deciding reading — driven through the real @objectstack/hono catch-all on the built runtime dist, at ce8caba914 (kernel: session-backed identity, ObjectQL engine with a real SchemaRegistry holding com.acme.crm and the permission-set tables the shared authz resolver reads):

404  CONTROL unscoped GET /api/v1/packages/no.such.package
       RESOURCE_NOT_FOUND :: Package 'no.such.package' not found
404  SCOPED   GET /api/v1/environments/env_alpha/packages/no.such.package
       RESOURCE_NOT_FOUND :: Package 'no.such.package' not found
200  SCOPED   GET /api/v1/environments/env_alpha/packages/com.acme.crm
       data={"manifest":{"id":"com.acme.crm",...},"status":"installed","enabled":true,...}   (bare row)
200  SCOPED   GET /api/v1/environments/env_alpha/packages            data={"packages":[...],"total":1}
404  SCOPED   DELETE /api/v1/environments/env_alpha/packages/no.such.package
       RESOURCE_NOT_FOUND :: Package 'no.such.package' not found
404  NEGATIVE CONTROL scoped   /environments/env_alpha/no-such-domain   ROUTE_NOT_FOUND
404  NEGATIVE CONTROL unscoped /no-such-domain                          ROUTE_NOT_FOUND
404  RETIRED /projects/ spelling                                        ROUTE_NOT_FOUND

⇒ A scoped /packages URL reaches the dispatcher's /packages domain through the catch-all (the dispatcher's spelling, single quotes, no trailing period). The fork the prior census reported has dissolved on today's tree; the three REST routes can be removed without any composition losing /packages.

Boundary — corrected by the at-tier review (fork report 5571983259), and the reason this PR is held in draft. The composition that loses scoped /packages under the deletion is reachable from the open-core CLI, not only from the cloud repo: serve.ts skips the standalone boot when shouldBootWithLibrary() is false (plugin-detection.ts — any host config, or OS_MODE=off) and then forwards api.enableProjectScoping verbatim to the REST plugin and the dispatcher plugin on HonoServerPlugin. With plugin-hono-server but without createHonoApp, the dispatcher's explicit mounts register /packages* at the unscoped prefix only and the transport's sole route into the dispatcher (setFallbackHandler) serves declarative apis: endpoints, so on that composition the REST mirror was the only door for scoped package reads and deletes — and after this PR there is none. My earlier boundary statement attributed that region to "a repo not in this session"; that attribution was wrong and is withdrawn here. Every consumer population reachable from this repo is zero for the scoped mount (measured with controls in the fork report); the residue is now stated in the changeset. The ruling's step 2 makes this the maintainer's call (options A′ / B′ / C′ in the fork report) — ⛔ no scoped mountPackagesRoute variant is added here.

Step 3 — the deletion, and what pins the survivor

  • packages/rest/src/package-routes.ts: the three service-gated routes, their mount gate, the protocol option (only they read it), the read cohort of the gate, the registry projection helper and the compile-time pins for the protocol seam are removed; the docblocks say what happened and why. direct-mount-composition.ts and rest-api-plugin.ts stop threading protocol.
  • packages/rest/src/rest-route-ledger.ts: the three direct-mount rows are gone; the runtime route ledger carries the family.
  • packages/rest/src/rest-server.ts: /discovery's routes.packages is now derived from the publish mount (POST {base}/packages/publish{base}/packages) instead of from the registrar's own GET {base}/packages copy. Measured consequence worth knowing: on a stock objectstack serve boot that copy was never mounted (the package service registers after the REST plugin starts), so REST's discovery omitted routes.packages while the dispatcher served the family; it is advertised on every boot now.
  • packages/runtime/src/domains/packages-single-door.test.ts (new) pins the surviving door — 404 wording Package 'ID' not found with RESOURCE_NOT_FOUND, the retired spelling asserted absent, the bare-row envelope { data, meta, success } with no package wrapper and no source, the list rows without source — unscoped and environment-scoped, driven through dispatch() with the catch-all-derived subpath (the fix(runtime): the dispatcher's scope strip matches /environments/, the prefix its own hint parser reads #15859 pattern; the hono package's suite aliases runtime to a mock, so the real-adapter drive above is the measurement and this file is the durable pin).
  • packages/runtime/src/domains/packages.ts: the stale "which shadows it only when a package service is registered" sentence and the two "sibling REST door" references are corrected; behaviour untouched (main's fix(runtime): key package lifecycle state by project, not by environment alone #16572 merged cleanly beside it).
  • packages/rest/src/query-multiplicity.ts header: the "dormant" claim replaced with the measured fact (plugin-hono-server's readQuery keeps arrays since 两个 IHttpServer 适配器对「重复的查询参数」给出不同形状:Hono 折叠成第一个值,node:http 给数组 #6878 / PR test(http-conformance): 「重复查询参数」的跨适配器分歧落锁为已知事实 (#6878 路线 1) #6941, pinned by the http-conformance suite).
  • 8 REST pin files whose only subject was the removed routes are deleted; 12 are re-pointed at the one route left (the resolver-instrument suites now drive POST /packages/publish — same resolver, same gate, same three wire answers).
  • scripts/check-undeclared-dep-imports.mjs: the type-only ledger row for package-routes.ts@objectstack/metadata-protocol is deleted (the import went with the delete route; the gate reds on a stale row).
  • content/docs/permissions/system-context.mdx and scripts/doc-authoring-prose-id.baseline.json: line-rot re-anchoring and the shrink-only baseline regeneration, both by the gates' own --fix / --census-ledger remedies.

Changeset

@objectstack/rest minor (.changeset/rest-package-routes-single-implementation.md), per the ruling. It states FROM → TO for GET /packages/:id (data.packagedata), records the source stamp and the ?version= read + multiplicity refusal as deliberately removed, the 404 wording change, the DELETE semantics, and the discovery change.

Verification

Every build/test ran through scripts/pm/os-verify-lock.sh; VERDICT lines read, never a bare $?. Heads are named per reading because the branch grew across the run (final head 60f7d46b49; the two commits after 7647624a00 touch only a docs page and a baseline JSON).

  • Closure build (pnpm --workspace-concurrency=2 --filter '@objectstack/hono^...' build, tree ce8caba914): build-exit=0, 97 "Build success" lines.
  • @objectstack/rest, full suite at 7647624a00 (after merging origin/main f2f6684cd5): Test Files 179 passed (179) · Tests 3008 passed (3008), exit 0.
  • @objectstack/runtime, full suite at 7647624a00: Test Files 239 passed (239) · Tests 3330 passed (3330), exit 0.
  • Touched pins, targeted at 74b480158b (18 rest files + the new runtime pin): 18 passed / 360 tests and 1 passed / 10 tests.
  • @objectstack/client ledger consumers at 60f7d46b49, against the rebuilt rest dist (rest-route-ledger-coverage, client-url-conformance, route-ledger-response-schema, packages-write-envelope, return-type-precision): 5 passed / 14 tests.
  • Typecheck (pnpm --filter @objectstack/rest typecheck, pnpm --filter @objectstack/runtime typecheck = tsc + check:test-typecheck) at 60f7d46b49: both exit 0. (A first attempt was NOT MEASURED — TS7016 against a packages/spec/dist that had lost its declarations mid-run; rebuilt with pnpm --filter @objectstack/spec build, check-dts-emitted: 34/34, then measured.)
  • Spec after the merge: pnpm --filter @objectstack/spec build && check:generated → "All 15 generated artifacts are up to date".
  • Ablation at 60f7d46b49 (committed state; trap-restored; both blobs hash-equal to HEAD afterwards, git diff HEAD empty): mutating the dispatcher's 404 spelling at its 6 sites turned the new runtime pin RED (4 failed / 6 passed — exactly the four wording cases, scoped and unscoped); resurrecting a GET /packages route in the registrar turned the REST absence pins RED (12 failed / 18 passed across package-publish-mount, direct-mount-introspection, rest-route-ledger.conformance). Both modules resolve from src through relative imports, so no dist sits on the path and no build leg was owed.
  • Gates: roster derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths); 79 families at f904e2d62e, 106 at 60f7d46b49 once the docs page joined the diff. Reconciliation --ran: 106 derived, 106 run, 0 UNRUN. Green, except four NOT MEASURED (reported as such, not as passes): check:dual-build-cjs-loads (exit 3, PREREQUISITE NOT MET — needs a full pnpm build), check:type-check-debt (exit 3, PREREQUISITE NOT MET — needs the full built closure), check:pm-dispatch-gates (timed out at the 170 s and 280 s per-command budgets), check:skill-examples (exit 1 — refuses without @objectstack/client-react built; unrelated to this diff). Three gates were red on the raw diff and are green after their own remedies, committed here: check:system-context-census (line rot, --fix), check:doc-authoring (prose-id baseline over-pinned the deleted ledger row, --census-ledger shrink), check:undeclared-dep-imports (stale type-only row deleted with the import; its --self-test and real run both pass). The dispatch-named check:nul-bytes: green.
  • Lint, narrowed and proven: pnpm exec eslint --no-inline-config --format json over every .ts/.mjs this diff adds or modifies — ① population: the repo's eslint . --no-inline-config with ignores: [...NEVER_LINTED] (eslint.config.mjs); ② --format json count: 44 files (the changed set after the merge, a superset of this branch's own 27), 0 errors, 0 warnings, exit 0, at 60f7d46b49; ③ invariance: the config enables no parserOptions.project and no typed @typescript-eslint rules (eslint.config.mjs:328), so nothing in this diff can move an untouched file's verdict.
  • Declared narrowing, for CI: turbo ls --affected lists 19 packages; only rest, runtime and the client ledger consumers were run here. The others (cli, client-react, cloud-connection, dogfood, downstream-contract, four examples, hono, http-conformance, organizations, plugin-auth, plugin-dev, service-sms, verify) consume rest/runtime and are left to CI — the behavioural change is confined to the REST registrar and its discovery projection, and the hono adapter's own suite aliases runtime to a mock.
  • CI: in_progress at the time of this body — head ba6b8311ed (round 4: the census note's tracker id moved from the runtime string to an adjacent comment; pnpm check:doc-authoring exit 0 measured on this exact tree before the push) started 31 check runs (11 success, 18 in progress, 2 skipped, read 80 s after the push). On c1344677dc everything else was green — Dogfood Regression Gate passed (the authz-conformance discover() reaches 1 of 17 route registrars in rest-server.ts — the ratchet's route-completeness guarantee is false for 66 of 85 mounted routes #13260 census fix confirmed by CI) — and Lint & Repo Gates was red only on check:doc-authoring for that id-in-string. ⚠️ My round-3 report quoted check:doc-authoring 0; that reading was taken on the docs tree (edcb5808e7) before the census note existed and was carried forward unchecked — an ordering mistake on my side, not a CI/local divergence. Earlier history: 40c7679eec red on Dogfood 3/3 (the census); edcb5808e7 cancelled by the next push; the initial draft head 60f7d46b49 had 0 check runs.

Round 2 — contract-review follow-ups (all owed under any ruling outcome)

  1. Merge conflictorigin/main (8341ed21ce) merged; the one conflict, content/docs/permissions/system-context.mdx, resolved by taking main's side (symbolic file#function anchors) and re-running pnpm gen:system-context-census on the merged tree (no further re-anchoring needed; gate green). rest-server.ts auto-merged — main's change there is a comment hunk near the /meta read door, far from the discovery hunks. ⛔ No hand-edited anchors, no rebase, no force-push.
  2. Changeset accuracy — three additions, each spelled as deliberately removed / narrowed, never silently dropped: the catch-all sentence now carries its residue (the plugin-hono-server-without-createHonoApp composition above); the uninstall's tenancy width narrows from the REST copy's deletePackage({ packageId, allTenants: true }) (package-wide, protocol.deletePackage finds zero sys_metadata rows the data plane finds 3 of — uninstall leaves orphaned rows (persistence half of #7557) #7705 case 4) to the dispatcher's { packageId, organizationId?, keepData? } resolved for the caller's organization; GET /packages (not only /:id) no longer merges durable sys_packages rows, so a published-but-not-installed artifact is neither listed nor gettable on the surviving door. Level unchanged: @objectstack/rest minor.
  3. Census row 54 — the REST package gate is described as the write-only POST /packages/publish gate (manage_metadata); the read cohort is cited to the dispatcher domain's own gate by symbol (the census gate holds numeric row references to declared keys, so no row number is used).
  4. Clause-② pair check — re-run after the push; exit code in the report.

Round 3 — docs-drift (bot comment on this PR, 34 hand-written pages + 4 release-owned)

Re-derived on this tree with node scripts/docs-audit/affected-docs.mjs --json 8341ed21ce (38 rows, anchors read per row). Fixed only what this diff falsified:

  • content/docs/kernel/contracts/metadata-service.mdx — the REST endpoints table said the REST layer "mounts package routes under /api/v1/packages" and listed GET /api/v1/packages as "List all packages (registry + database)". The family is the dispatcher domain's now, the list is the installed set (published-but-not-installed artifacts are not merged in), the read answers the bare row with the dispatcher's 404 wording, the uninstall is organization-scoped. While editing that table, its POST /api/v1/packages "Publish" row was corrected to POST /api/v1/packages/publish — pre-existing (the bare POST is the install route), not something this diff falsified, fixed because the row is the one route this PR keeps.
  • content/docs/permissions/permission-sets.mdx — "the uninstall response reports the revocation under cleanups" described the removed REST envelope; on the surviving door it is data.persisted.cleanups.

Judged accurate and left alone (listed because they name a symbol or literal this diff touched, not because they went false): api/metadata-api.mdx (its GET /packages "List installed packages", GET /packages/:id, DELETE /packages/:id, PATCH …/enable|disable sections describe the surviving door; its POST /packages/publish response is shown flat — pre-existing, untouched); api/client-sdk.mdx (the client.packages.* snippet is the dispatcher door; its VALIDATION_ERROR row is the code's general meaning); api/environment-routing.mdx (env.packages.list() on the scoped client — the residue is a maintainer decision, stated in the changeset, not a docs edit); api/error-catalog.mdx, api/error-handling-client.mdx, api/error-handling-server.mdx, automation/webhooks.mdx, data-modeling/drivers.mdx, protocol/kernel/error-handling.mdx, protocol/objectql/types.mdx, ui/forms.mdx (anchored by the RESOURCE_NOT_FOUND / VALIDATION_ERROR literals — vocabulary pages); automation/flows.mdx, protocol/objectui/actions.mdx, ui/actions.mdx (anchored by errorMessage, a field name of theirs unrelated to the removed projection list); concepts/metadata-lifecycle.mdx, deployment/environment-variables.mdx, kernel/services-checklist.mdx (getMetaItems — the protocol verb is unchanged; the checklist's "packages is answered kernel-side by the /packages dispatcher domain" is now exactly true); automation/approvals.mdx, deployment/cli.mdx, permissions/capabilities.mdx, permissions/access-recipes.mdx, ui/audience-based-interfaces.mdx (manage_metadata — the write cohort is unchanged; none states the REST package read gate); permissions/authentication.mdx, plugins/packages.mdx (createRestApiPlugin — signature unchanged; the service-package blurb describes the service, not the route); data-modeling/formulas.mdx, deployment/publish-and-preview.mdx (/api/v1/cloud/packages is a different surface), deployment/self-hosting.mdx, getting-started/examples.mdx (GET /api/v1/packages lists the installed set — true), permissions/record-view-auditing.mdx, plugins/adding-a-metadata-type.mdx, protocol/kernel/http-protocol.mdx (the discovery example's "packages": "/api/v1/packages" is what is advertised), ui/apps.mdx (an endpoint: '/api/v1/packages' example — served); permissions/system-context.mdx (row 54 already corrected in round 2).

Release-owned, not touched (content/docs/releases/): implementation-status.mdx (anchored by registerDiscoveryEndpoints; its discovery sentence is generic and still true), v15.mdx (PATCH /packages/:id history), v16.mdx (manage_metadata), v17.mdx (the #7780 allTenants note is about the protocol verb, still true). None reads false on this diff; nothing for a docs-only PR.

The two declared blind spots, swept by hand: no hand-written page documents the ?version= repeated-parameter refusal on the package routes (query-multiplicity.ts yielded no anchor; the only repeated query param mention is client-sdk.mdx's general VALIDATION_ERROR row, which the /meta and /data read points still honour); the package client methods appear only in client-sdk.mdx (unchanged behaviour), metadata-api.mdx (packages.install) and environment-routing.mdx (above).

Docs gates on the tree: check:doc-anchors, check:docs-single-h1, check:doc-authoring, check-doc-route-spelling --advisory, check:docs-transcript-drift, check:docs-redirects, check:system-context-census, check:nul-bytes — exits in the report.

Round 3b — the #13260 authz-probe blind-spot census (CI red on 40c7679eec, Dogfood Regression Gate 3/3)

packages/qa/dogfood/test/authz-probe-blind-spot.census.ts pins population / reachable / blindSpot per probe file; the three ledger rows this PR removes moved the packages/rest/src/rest-route-ledger.ts row. The census has no generator, so all three numbers were re-derived from the tree with the module's own deriveProbeFileCensus() (a tsx script over every row, diffed against the recorded table) — ⛔ not back-solved from the assertion: derived population 91, reachable 91, controls route: ' 91 / family: ' 91 / RestRouteLedgerEntry 2. reachable moved with population (94 → 91 on both), because each removed row carried family: 'packages' and was reached by that family's key; the blind spot stays 0, the 19 families are unchanged (the publish row keeps the family), and BLIND_SPOT_TOTAL_STATIC / _RUNTIME (75 / 80, summed over the route surfaces, not the ledger rows) are untouched. After the edit every one of the 13 rows derives same. The row's note and the header's "94 audited rows" record the re-measurement. The aggregator's second red (dogfood-3-of-3 MISSING) is this shard's missing attestation, not a second cause.

Acceptance notes

  • File surface, beyond the four files the claim names (each a consequence of the deletion, stated here rather than left implicit): direct-mount-composition.ts and rest-api-plugin.ts (the protocol option only the removed routes read); rest-route-ledger.ts (the three rows); rest-server.ts (the routes.packages projection was keyed on the removed list route); scripts/check-undeclared-dep-imports.mjs (stale row); content/docs/permissions/system-context.mdx and scripts/doc-authoring-prose-id.baseline.json (gate remedies); 8 REST pin files deleted, 13 re-pointed, 1 runtime pin added; the changeset.
  • noted, not filed — history prose that names the retired door as its instrument and is left as history: packages/rest/src/rest-server.ts (two measurement narratives on GET /api/v1/packages), packages/runtime/src/package-door-error-parity.test.ts (header says the registrar "mounts FIRST in the production stack").
  • noted, already carded (client.packages declares envelopes two mounted surfaces do not emit: get diverges between them, and install/enable/disable declare { package } where the only serving surface sends the bare row #12034, pm:awaiting-maintainer)packages/client/src/index.ts still says only the REST registrar serves the scoped mount and declares ScopedEnvironmentClient.packages.get as { package: InstalledPackage } with a version? argument; both describe the removed door. That card holds the maintainer's 2026-08-26 ruling to bind the bare row; this PR is the producer-side convergence it was waiting on.
  • noted, not filed — mid-run, packages/spec/dist was rewritten without declaration files (dist mtime inside the check:* batch window; not attributed to a specific gate), which reds any later typecheck in the same tree with TS7016 until the package is rebuilt. Recovered by rebuilding spec; recorded so the next tree does not read the TS7016 as its own.
  • #12034 remains open — this PR does not close it.

维护者速读(草稿)

改了什么:/api/v1/packages 的读与删三条路由只剩一套实现(runtime dispatcher 的 /packages domain);@objectstack/rest 只再挂 POST /packages/publish。REST 那一套自带的 source 戳与 ?version= 读取随之删除,并在 changeset 里记为有意去掉。REST /discovery 改由 publish 挂载点推导 routes.packages

为什么改:同一网址两套实现且行为已分歧(404 措辞、data.package 外壳、source 戳),注释宣称的「遮蔽」在标准启动上从未发生(服务注册晚于 REST 挂载判定,三条路由根本没挂)。2026-09-02 裁决 A:删一套,不修优先级。

风险与代价(含回滚):曾经真的走到 REST 那一套的部署(package 服务先于 REST 启动注册的宿主)会看到 GET /packages/:id 的响应从 data.package 变成 data、404 措辞变化、?version= 不再被读取;in-tree 的所有组合均不受影响(标准启动本来就是 dispatcher 在答)。SDK 的 scoped packages.get(id, version?) 声明的 { package } 类型随之失真,归 #12034 那张已裁未合的卡。回滚 = revert 本 PR(纯删除 + 注释,无数据迁移)。

席位意见:(留空)

你要做的:确认 changeset 的 minor 定级与「有意去掉」两项;决定 #12034 的 SDK 收口是否随本 PR 排队。


Generated by Claude Code

…ackageRoutes (#14503)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…t; pins follow the one route

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…lot-lookups pin follows

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
… baseline after the ledger row deletion

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…ckage-routes-single-implementation

# Conflicts:
#	content/docs/permissions/system-context.mdx
… 54 names the write-only gate

- changeset: the scoped mount is served by the dispatcher only where the
  @objectstack/hono catch-all is mounted; the uninstall's tenancy width
  narrows from allTenants to the caller's organization; GET /packages no
  longer merges durable sys_packages rows — each stated as deliberately
  removed, never silently dropped
- content/docs/permissions/system-context.mdx row 54: the REST package gate
  guards POST /packages/publish (manage_metadata) only; the read cohort is
  row 55's

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
The REST registrar mounts POST /packages/publish only (#14503); the read
cohort is the dispatcher domain's to enforce and is named in plain words,
since the census gate reads a path#symbol in row prose as an anchor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/rest, @objectstack/runtime, touching 46 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/rest/src/query-multiplicity.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

34 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json bc0ac1d1f5775659e72ad35452c463443a71f1be.

4 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/rest/src/query-multiplicity.ts) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 29 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json bc0ac1d1f5775659e72ad35452c463443a71f1bepackageMentionDocs.

Which tree this was computed on

This run read content/docs from c833a3ea5f1ca62e306498786e11764e0d8f8ba5 — the merge of head ba6b8311ed1957b1a3d398a27095b82ed4615d39 into base bc0ac1d1f5775659e72ad35452c463443a71f1be, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c833a3ea5f1ca62e306498786e11764e0d8f8ba5 && git checkout c833a3ea5f1ca62e306498786e11764e0d8f8ba5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bc0ac1d1f5775659e72ad35452c463443a71f1be ba6b8311ed1957b1a3d398a27095b82ed4615d39 && git checkout -B drift-repro bc0ac1d1f5775659e72ad35452c463443a71f1be && git merge --no-ff ba6b8311ed1957b1a3d398a27095b82ed4615d39

node scripts/docs-audit/affected-docs.mjs --json bc0ac1d1f5775659e72ad35452c463443a71f1be

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs bc0ac1d1f5775659e72ad35452c463443a71f1be → pass the list as
args.docs, on the commit named under Which tree this was computed on.

- kernel/contracts/metadata-service.mdx: the /api/v1/packages family is the
  dispatcher domain's; the REST registrar contributes POST /packages/publish
  (the table had it at the bare POST /packages, which is the install route);
  the list is the installed set, not registry + database; the read answers
  the bare row and the dispatcher's 404 wording; the uninstall is
  organization-scoped
- permissions/permission-sets.mdx: the uninstall's revocation report sits
  under data.persisted.cleanups on the surviving envelope

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
… 91/91 after the three package rows left (#14503)

population 94 -> 91 and reachable 94 -> 91, derived from the tree with
deriveProbeFileCensus(), not back-solved: each removed row carried
family: 'packages', so the family key reached it; the blind spot stays 0
and the 19 families are unchanged (the publish row keeps the family).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
…es the tracker id to a comment

check:doc-authoring forbids an issue id inside a runtime string literal; the
adjacent comment carries it for the reader who can resolve it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YFY46JydE1gMxQG1TqBcMZ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant