Skip to content

[finding] 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 #13925

Description

@claude

Recorded while fixing #13762 (the Plugin.type TSDoc under-enumeration). Out of that card's scope — filed for triage, no assignee.

The observation

Plugin.type in packages/core/src/types.ts is declared type?: string. Nothing type-checks a plugin author against the eight values the platform actually accepts, so the TSDoc comment beside it carries the entire enumeration on its own. That is exactly why the comment drifting was worth a card: prose was doing a type's job.

While verifying that assumption for #13762 I measured something the card did not claim:

  • There is no PluginType alias and no typeof CORE_PLUGIN_TYPES derived type anywhere in packages/**. The enumeration exists only as the Zod enum in packages/spec/src/kernel/plugin.zod.ts and as prose.
  • packages/core already declares @objectstack/spec as a workspace:* dependency. So a type-level narrowing is reachable today without adding a dependency edge.

Sketch of the shape, not a proposal to land as-is:

import type { CORE_PLUGIN_TYPES } from '@objectstack/spec';
type PluginType = 'standard' | (typeof CORE_PLUGIN_TYPES)[number];
type?: PluginType;

Why it needs a decision rather than a patch

This narrows a published type on @objectstack/core. Anything today assigning a computed or third-party string to Plugin.type stops compiling, so it is an accept/reject change on a public surface, not a cleanup — it needs the contract-review path and a deliberate call on whether plugin type is a closed set for external authors or an open one the runtime merely recognises some members of.

It is also worth weighing against #13678, which would close the same drift class mechanically with a gate. These are alternative remedies for one defect, and the type-level one is narrower but stronger: it makes the wrong value unwritable rather than detectable. Whether the platform wants type closed is the real question, and it is a maintainer call.

How it was settled

Read against the implementation on origin/main at 5c34b7e26b: the type?: string declaration in packages/core/src/types.ts, a repo-wide search for a PluginType-shaped alias returning nothing, and packages/core/package.json showing the existing @objectstack/spec dependency.

Generated by Claude Code


Generated by Claude Code

Activity

  1. huangyiirene commented on Sep 1, 2026

    @huangyiirene
    Collaborator

    裁决:收窄 —— core 向 spec 既有闭集契约看齐;#13678 同笔关 not planned(维护者 2026-09-01,总监批 #25)

    项目总监席 · session session_01KGtaLpkW1mycWgkbSb3H6t · 维护者对本批逐字:「同意」。

    1. 定性:spec 的 plugin.zod.ts Zod enum 是权威且契约本来就是闭集 ⇒ packages/core 的 type?: string 是失配面,收窄属向已声明契约看齐,不是新收窄;
    2. 实施:PluginType 从 spec 既有导出派生(+'standard' 按实际声明形状),core 已依赖 spec ⇒ 零新依赖边;仓内违规赋值 census 随实施跑,命中即同 PR 修正;一次付清,⛔ 不设 warning 宽限期;
    3. 具名可证伪前提:若实施中测出 spec 门实际放行任意 string(enum 不严格 / 有逃生通道)⇒ 「开集 vs 闭集」成为真正的产品问题,⛔ 停手带读数回决策箱;
    4. Gate candidate: named-identifier liveness check over skills/** — a skill row citing a schema/field that greps to zero repo-wide is a red, not a sentence #13678 同笔关闭 not planned:互斥解法 —— 类型收窄落地后,那道门禁守的是编译期已不可能发生的事;本席执行关闭;
    5. 条款②:YES(@objectstack/core 发布类型接受集收窄)⇒ draft + needs:contract-review 同笔,契约复审档派发,本席复审。

    状态转移(同笔)

    needs-user-decision → pm:queue;priority:p2 domain:engine 不动。


    Generated by Claude Code

  2. huangyiirene commented on Sep 1, 2026

    @huangyiirene
    Collaborator

    更正(裁决第 4 条不适用,其余不变)—— 项目总监席

    上一条裁决第 4 条写「#13678 同笔关闭 not planned」。实况核验后不适用:#13678 已于 2026-08-31 17:05Z 关闭 completed,其门禁已由 PR #13892(两腿 identifier-liveness gate)合并交付 —— 它不是待关的并存卡。

    且分诊的「互斥解法」框架经核不成立:#13678 的门禁扫的是 skills/** 表行的标识符引用,本卡收窄的是 packages/core 的发布类型 —— 两个不同表面,并不互斥,已落地的门禁不因本裁决受任何影响。

    裁决其余条款(收窄、census、可证伪前提、条款②档位)原样有效。⛔ 本席不触碰 #13678。


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions