Skip to content

docs(core): list objectql in Plugin.type TSDoc enumeration - #13923

Draft
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13762-plugin-type-tsdoc
Draft

docs(core): list objectql in Plugin.type TSDoc enumeration#13923
claude[bot] wants to merge 1 commit into
mainfrom
claude/issue-13762-plugin-type-tsdoc

Conversation

@claude

@claude claude Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #13762

Plugin.type is typed string in @objectstack/core, so its TSDoc is the only enumeration a plugin author reading the interface ever sees — nothing type-checks them against it. That comment listed seven values; the declared set is eight.

What changed

File Change
packages/core/src/types.ts Plugin.type TSDoc now lists all eight values and names CORE_PLUGIN_TYPES as the authoritative set
content/docs/plugins/anatomy.mdx same one-word omission in the hand-written transcription of this same interface
.changeset/plugin-type-tsdoc-objectql.md patch changeset for @objectstack/core

Every changed line in types.ts sits inside the doc-comment block. type?: string; is untouched; no executable token moved. Evidence that the correction reaches the surface authors actually read:

packages/core/dist/index.d.ts:477
     * Plugin type (standard, ui, driver, server, app, theme, agent, objectql)

and the stale seven-value form returns zero hits anywhere under packages/core/dist/.

Zone-2 verdicts

Assumption Verdict What decided it
A2.1 — the declared set is eight HELD packages/spec/src/kernel/plugin.zod.ts:89-97 still declares CORE_PLUGIN_TYPES as ui, driver, server, app, theme, agent, objectql, and line 121-124 builds type: z.enum(['standard', ...CORE_PLUGIN_TYPES]). Found by symbol name, not line number; set has not moved since the card was filed. packages/objectql/src/plugin.ts:205 still declares type = 'objectql'.
A2.2 — the TSDoc is the only enumeration an author sees HELD The field is type?: string. No PluginType alias, no typeof CORE_PLUGIN_TYPES derived type exists anywhere in packages/**. Nothing type-checks an author against the list. The STOP condition therefore did not fire. Worth recording: packages/core already declares @objectstack/spec as a dependency, so a type-level narrowing is reachable — but it would change accept/reject behaviour on a published surface and is out of this card's scope. Filed as a finding rather than done here.
A2.3packages/core/src/types.ts is the only remaining in-repo hand-written copy FALSIFIED A third hand-written copy exists: content/docs/plugins/anatomy.mdx:122, a verbatim transcription of this same Plugin interface, carrying the identical omission. Corrected here (see below).

The sweep, and its firing positive control

A three-line window scan over all tracked .ts/.tsx/.md/.mdx/.json/.mjs/.mts/.js/.yml files, flagging any window containing five or more of the seven CORE_PLUGIN_TYPES values, then partitioned on whether objectql is present. 32 windows found.

Positive control fired: the sweep returns the two copies PR #13760 already corrected — skills/objectstack-platform/SKILL.md:773 and skills/objectstack-platform/rules/plugin-lifecycle.md:46,48 — both now showing the complete eight-value list. A query that had to return something returned it, so the zero elsewhere is a reading and not a broken search.

Windows missing objectql, all seven triaged:

  • content/docs/plugins/anatomy.mdx:122a real third copy. Fixed here.
  • packages/spec/src/kernel/plugin.zod.ts:103-105 — the CONSUMER_INSTALLABLE_TYPES doc comment; about package types, open-ended with an explicit ellipsis. Not an enumeration claim.
  • docs/design/marketplace-publishing.md:74, docs/design/plugin-distribution.md:51, docs/adr/0025-plugin-package-distribution.md:379 — package-type / consumer-split prose, all ellipsis-terminated. The ADR is a governed surface besides.

The 25 windows that already contain objectql include every content/docs/references/** table — those are generated from the Zod source and are correct.

The second fix, declared

content/docs/plugins/anatomy.mdx is outside the card's literal file surface, so taking it in place is declared rather than assumed. It qualifies as a bounded same-class fix on all four counts:

  1. Same defect class — the same Plugin.type enumeration, the same omitted value, in a verbatim transcription of the same interface.
  2. Mechanical, with the correct form already pinned by CORE_PLUGIN_TYPES.
  3. No competing claim — no open PR and no remote branch touches that file (measured against all 21 open PRs and git ls-remote --heads).
  4. No new verification surface — the content/docs gates run whole-surface on every PR regardless. The one ratchet that reads this file, check:role-word, counts occurrences of the word "role"; the file's count is 7 before and 7 after, exactly matching its scripts/role-word-baseline.json entry.

Leaving it would have kept the customer-facing page teaching seven values, which is the precise harm the card describes.

Clause 2 — contract review self-declaration

Judged from the actual diff, not from the dispatch's prediction:

  • Path limb — NO. The diff touches packages/core/src/, content/docs/, .changeset/. It does not touch packages/spec/src/.
  • Content limb — NO. Comment-only in the .ts file (proven above: every changed line is inside the doc-comment block), one prose line in a docs page, one changeset. PluginSchema is untouched; Plugin.type remains type?: string. No accept/reject behaviour changes and no published surface widens.

Both limbs read NO. The needs:contract-review label is attached anyway, per the unconditional label requirement in the dispatch.

Gates

Family derived from the actual diff with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack49 families. All results below are from commit 3eb14059c9, the branch head.

Green — 46 of 49, including check:role-word, check:corpus-claim-drift, check:doc-anchors, check:doc-authoring, check:docs-single-h1, check:docs-redirects, check:docs-audit-scope, check:published-files, check:published-readme-links, check:cross-package-test-inputs, check:test-source-alias, check:type-source-resolution, check:kernel-hook-pairs, check:objectui-changeset, check:pm-half-states, check-empty-changeset, check-changeset-no-major, and — after building the closure each needs — check:docs ("230 generated files in sync with packages/spec"), check:skill-examples ("260 prose examples type-check across 3 surfaces"), check:doc-formula-expressions and check:doc-security-posture.

Also green outside the derived family: check:nul-bytes (7614 files, no raw control bytes), check:type-check-coverage, check-system-context-census ("145 anchors resolve" — neither edited file is a cited site, so no anchor moved), check:adr-anchors, and targeted ESLint on both changed files (0 errors).

Package verification: @objectstack/core builds with declarations emitted, and vitest run gives 46 test files, 1147 tests, all passed.

NOT MEASURED — 3, each refusing on its own prerequisite, none a finding:

  • check-test-completeness — exit 3, its documented PREREQUISITE-NOT-MET code.
  • check:dual-build-cjs-loads — exit 3; needs every package's dist/, which is a full-farm build.
  • check:type-check-debt — refuses --re-measure while 23 workspace dependencies are unbuilt, on the grounds that measuring there would "silently measure a DIFFERENT WORLD". Its non-re-measure sibling check:type-check-coverage passed.

Declared narrowing — 1: check:query-options-erasure did not run. Two attempts on the shared verify lock returned exit 99 (never acquired; a full-farm build held it 600 seconds with two entries ahead). Rather than a third wait I am declaring the narrowing, because the gate's count provably cannot move here: it is an ESLint-based ratchet over source, my diff changes zero executable tokens, and content/docs/plugins/anatomy.mdx is not in ESLint's population at all — ESLint's own verdict on it is "File ignored because no matching configuration was supplied". The repo runs no type-aware linting (no parserOptions.project, no typed rules), so this diff cannot move the verdict on any file it does not touch. CI runs the gate unconditionally and is the real measurement.

Regression prevention — stated plainly

This change lands no executable pin, so no ablation is claimed or was run. What stands between this and the next drift is only prose: the corrected comment now names CORE_PLUGIN_TYPES as the authoritative set, so the next author is told where truth lives instead of having to guess. That is weaker than a gate and is meant to be read as weaker.

The mechanical fix — a gate walking the comment against CORE_PLUGIN_TYPES — is deliberately not built here. It closes the whole class rather than this instance and belongs to #13678.

Generated by Claude Code


Generated by Claude Code

Plugin.type is typed `string`, so the TSDoc is the only enumeration a plugin
author reading the interface ever sees. It listed seven values; the declared
set in @objectstack/spec is eight — CORE_PLUGIN_TYPES carries `objectql`, the
type packages/objectql/src/plugin.ts declares on the engine plugin.

Also corrects the same omission in the hand-written transcription of this
interface in content/docs/plugins/anatomy.mdx.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F3jdziLbAPGeceVNmSox5L
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 23 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 4902a250439a6b054ccb71ac7d0c12762712cc6fpackageMentionDocs.

Copy link
Copy Markdown
Collaborator

Contract review (Clause ②) — PASS, and the label should not have been attached

Reviewed at head 3eb14059c9b55bdd48a6ddc7b0f0a6df8279ad4b, still the head. Rendered by a CONTRACT_REVIEW_TIER reviewer in an isolated context; transcript tier-verified before adoption (42 harness-stamped assistant turns, 100% at tier, first and last included, service_tier: standard throughout, zero fallback evidence). The triage seat runs below tier and therefore adopts verbatim or voids whole — ⛔ including for a diff this small: the fuse is unconditional, and "obviously fine" is exactly when a below-tier seat would be tempted to self-clear. Adopted verbatim, unedited:

VERDICT: PASS
CLAUSE-2-PATH: no
CLAUSE-2-CONTENT: no
DECLARATION-HONEST: yes
ONE-LINE: PASS — every changed line is documentation (TSDoc comment, docs prose, changeset) against an unchanged declared set, so neither Clause-② limb fires and the `needs:contract-review` label may be cleared.
FINDINGS:
- Path limb NO. The 3 changed files are `.changeset/plugin-type-tsdoc-objectql.md` (added), `content/docs/plugins/anatomy.mdx`, `packages/core/src/types.ts` — none under `packages/spec/src/**`, and `content/docs/releases/` is untouched (all 3 files enumerated from the PR files API; changed_files=3 matches).
- Content limb NO, claim verified literally. In the `types.ts` patch the only removed line is `-     * Plugin type (standard, ui, driver, server, app, theme, agent)` and all 7 added lines begin `+     *` inside the `/** ... */` block; `type?: string;` appears only as an unchanged context line. The `.mdx` change swaps one prose line inside a transcription code block (`One of: standard, ui, driver, server, app, theme, agent.` → `...agent, objectql.`) — prose brought into line with an unchanged declared set, nothing widened. The 2026-08-28 negative boundary (runtime permission/security behaviour) is not in play: nothing here touches behaviour of any kind.
- Underlying claim TRUE. `packages/spec/src/kernel/plugin.zod.ts` declares `export const CORE_PLUGIN_TYPES = [` with exactly `'ui'`, `'driver'`, `'server'`, `'app'`, `'theme'`, `'agent'`, `'objectql'` (7), and `type: z.enum([` `'standard',` `...CORE_PLUGIN_TYPES` `])` = eight; `packages/objectql/src/plugin.ts` declares `type = 'objectql';` beside `name = 'com.objectstack.engine.objectql';`. Seven-listed/eight-declared holds.
- Label attach when both limbs read NO is (c) contrary to 不预挂, not required. `references/contract-review.md`: "⛔ **不预挂**(维护者 2026-08-28 裁定):可复审增量存在前永不挂标 … 开载体恒 = 真实待审"; SKILL.md: the label is attached "由 PR 创建者随**可复审契约增量**同笔挂"; `ensure-pm-labels.sh` defines it as for "a PR whose ACTUAL diff touches the contract surface — or whose card's claim comment declares `Clause-②: yes`". No "unconditional label requirement" exists in SKILL.md, contract-review.md, or dispatch-runbook.md (zero hits for contract-review/Clause in the runbook; control: "派发" hits 26 in the same file). The dispatch prompt itself is not recorded on the card — whether it really said that is NOT MEASURED — but SKILL.md rules "无条件条款只住角色文件(冲突时它胜 … ⛔ 不靠派发词临时覆盖)", so a dispatch cannot create such a requirement anyway. With this review returning both limbs NO, the correct disposition is to clear the label (contract-review.md: "PASS + 无标 + head 未动 = 已清标非被剥" keeps the clearing auditable).
- A2.2 deferred finding EXISTS: issue #13925, open, labeled `finding`, unassigned — "Plugin.type is typed string on a published surface — the eight-value set is enforced by prose only, though packages/core already depends on spec". Matches the PR's "Filed as a finding rather than done here."
- Declared narrowing for `check:query-options-erasure` is sound. The gate (`scripts/check-query-options-erasure-ratchet.mjs`) is a per-file count of ESLint `query-options/no-any-erasure` code sites; comment-only `.ts` lines cannot add an erasure site, the `.mdx`/changeset files are outside ESLint's population, and the script itself states "this repo's ONE `eslint.config.mjs` never sets `parserOptions.project` … so no ESLint pass here is type-aware" — independently corroborating the PR's argument. CI runs it unconditionally, and "Lint & Repo Gates" is `success` at head.
- Changeset honest and correctly graded. `patch` for `@objectstack/core` is right for a published-package doc-comment fix. Every checkable factual sentence verified: "typed `string`", "listed seven values while the declared set is eight", "`z.enum(['standard', ...CORE_PLUGIN_TYPES])`", "`CORE_PLUGIN_TYPES` carries `objectql`", the anatomy.mdx co-fix. The one unverifiable clause — the engine plugin "essentially every runtime loads first" — is hedged prose echoing the triage card ("几乎每个 runtime 最先加载"), not a diff claim. No defect.
- The `dist/` citation is secondary evidence, not a repo reading. `packages/core/dist/` is gitignored (`.gitignore`: `packages/*/dist/`) and `git ls-files packages/core/dist` returns nothing, so `dist/index.d.ts:477` is a local build artifact — legitimate only as propagation evidence given the report's declared fresh build at head ("build emitted declarations"), with the stated zero-hit check for the stale seven-value form guarding staleness. The source diff remains the primary evidence; fine as presented, but it proves nothing about any published or committed artifact.
- Machine spelling ABSENT from the card's claim comment. Comment 5481248571 on #13762 reads in full: "Claiming this card." / "- Session: `session_01F3jdziLbAPGeceVNmSox5L`" / "- Branch: `claude/issue-13762-plugin-type-tsdoc`" plus scope prose — neither `Clause-②: yes` nor `Clause-②: no` appears anywhere (full body read, not a grep). The gate's reading location is confirmed: SKILL.md "声明肢 = 认领评论声明 `Clause-②: yes`" and ensure-pm-labels.sh "whose card's claim comment declares `Clause-②: yes` (the content limb, judged from the card, path-independent)". The declaration lives only in the PR body ("Both limbs read NO") — a location the machine limb never reads. SKILL.md's claim template makes the line a 强制申报, so this is a claim-hygiene defect to record; it does not change this verdict because the content limb was measured here directly from the diff (NO), which is stronger than the missing self-declaration. Recorded, not fixed, per instruction.
- Head and checks. Head is unchanged at `3eb14059c9b55bdd48a6ddc7b0f0a6df8279ad4b` (matches the brief; PR still draft, `mergeable_state: "blocked"`). Check runs at that head: 35 total — 34 completed as `success` or `skipped` (including "Build Core", "Build Docs", "Check Changeset", "Lint & Repo Gates", "TypeScript Type Check"), and "Test Core (1/6)" is `in_progress` — reported as in_progress, not as a pass.

Carrier action

needs:contract-review cleared from both carriers — this PR and card #13762 (which was missing it and was synced on during the round, so the clearing is auditable on both). Provenance: maintainer ruling 2026-08-25 ("审核通过你应该直接负责合并吧,还要等原始的项目经理吗").

⚠️ The landing action is not mine. Contract review belongs to this card's dispatching seat; the triage round is "非放行必要条件,在线时作事后审计/抽查". ⛔ Not flipping ready, not arming auto-merge, not approving. Also note "Test Core (1/6)" was still in_progress at review time — enqueue confirms green at the then-current head.

⭐ The finding worth carrying beyond this PR

This PR's body says the label was attached "per the unconditional label requirement in the dispatch." The review went and read the governing text. There is no such requirement.

  • references/contract-review.md: "⛔ 不预挂(维护者 2026-08-28 裁定):可复审增量存在前永不挂标 … 开载体恒 = 真实待审"
  • SKILL.md: the label is attached "由 PR 创建者随可复审契约增量同笔挂"
  • ensure-pm-labels.sh: it is for "a PR whose ACTUAL diff touches the contract surface — or whose card's claim comment declares Clause-②: yes"
  • Zero hits for contract-review/Clause in dispatch-runbook.md — control: 派发 hits 26 in the same file, so the zero is a reading.

And decisively, SKILL.md forecloses the route the justification took: "无条件条款只住角色文件(冲突时它胜 … ⛔ 不靠派发词临时覆盖)"a dispatch order cannot create an unconditional rule. Whether the dispatch actually said this is NOT MEASURED (the prompt is not recorded on the card), but it would not matter if it did.

⇒ An "open carrier" is supposed to mean a real pending contract question. This one meant "a dispatch said to attach it", and it cost a full review round on a three-file comment-only diff. Recorded on #13922, which is the card for exactly this class of drift between the label and the predicate.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] Plugin.type's TSDoc in packages/core/src/types.ts under-enumerates the value set — omits objectql, which ObjectQLPlugin actually declares

2 participants