Skip to content

[finding] @objectstack/spec has four grammars for a package version, and ManifestSchema refuses the 2.0.0-beta.1 that PackageVersionSchema's own docstring advertises #18697

Description

@hotlong

Blocked-by: objectstack-ai/objectui#10207

⏱️ PM 加本行(seat 5,2026-09-22)。 工已完、复核 PASS、CI 全绿,PR #19637 卡在本仓之外的一道门:objectui 的 PackageFormDialog.helpTextI18n.test.tsx:63 逐字钉着本 PR 改掉的那句 .describe(),而 Console Pin Gate 在 PR 上跳过、在 main 上运行 ⇒ 合入即把红推进主干。⇒ 按「工已完、PR 被外部门禁卡住」同用 pm:blocked。解锁判据 = objectui#10207 落地且其 pin 覆盖该提交,⛔ 不是时间。

⏹ Blocked-by: #17534 — RELEASED 2026-09-21T00:18Z. ⭐ The line named the card; the condition the hold actually rested on was the PR. The ruling's Execution clause reads 「Serial behind PR #18319 (holds packages/spec/src/kernel/manifest.zod.ts)」, and PR #18319 merged 2026-09-21T00:08:02Z as 097d2685 on origin/main (read twice). Card #17534 is still open and still pm:dispatched — that is a half-state on its card, ⛔ not a live block on this one. ⛔ Leaving the line pointing at an open card would recreate the exact body-vs-state contradiction this card was parked to fix.

@objectstack/spec carries four different grammars for "the version of a package", and the
strictest of them refuses a string that its own sibling advertises as an example.

All four measured on bdea10a185d422ef1f9022e210b87d19882055ee (the commit objectstack-ai/cloud
pins today) via the contents API, 2026-09-17T15:29Z:

carrier regex prerelease case
ManifestSchema.version — packages/spec/src/kernel/manifest.zod.ts:322 ^\d+\.\d+\.\d+$ none at all —
PluginSchema.version — packages/spec/src/kernel/plugin.zod.ts:212 ^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$ yes upper and lower
PackageVersionSchema.version — packages/spec/src/marketplace/package-version.zod.ts:144-146 ^\d+\.\d+\.\d+(-[a-z0-9.-]+)?(\+[a-z0-9.-]+)?$ yes lower only
PackageManifestSchema.version — same file, :80 no regex — bare z.string() unconstrained unconstrained

The declared-contract violation, quoted

PackageVersionSchema.version's own JSDoc, verbatim at that commit:

/** Semantic version string (e.g. 1.2.3, 2.0.0-beta.1). */

ManifestSchema.version's regex is ^\d+\.\d+\.\d+$ ⇒ it refuses 2.0.0-beta.1, the very
string the sibling schema documents as an example of the same concept. That is class (b): a declared
contract contradicted by an enforced one, with the contract text quoted above.

⚠️ PackageManifestSchema.version at :80 is the one nobody has named yet — it is a bare
z.string(), so it constrains nothing at all. A downstream that trusts "spec validated it" gets
no validation from this carrier.

Honest limit on severity — ⛔ do not grade this from the table alone

What is measured here is the schema disagreement. What is NOT measured is whether any live path
judges a real bundle's manifest.version with ManifestSchema — if none does, this is a latent
trap rather than a today-defect, and the grading should say so. Whoever triages this should answer
that question first; it decides the class as much as the table does.

Where this came from

Found while implementing objectstack-ai/cloud#2305 (PR objectstack-ai/cloud#2320), which had to
pick one of these grammars for the cloud package-publish route. It picked
PackageVersionSchema.version deliberately, because that accept set is a strict SUBSET of
PluginSchema.version — the one the runtime plugin loader parses with — so nothing that gets past
the publish gate can fail the runtime parse. That choice is sound whatever happens to this card;
⛔ this card is not a blocker for it.

One consequence worth stating for whoever fixes this: cloud's publish route now refuses a
semver-valid prerelease with an uppercase identifier (1.0.0-Beta.1) that the runtime loader
would have accepted. That is a deliberate, safe-direction narrowing today; if this card converges
the grammars, that fringe disappears with it.

查重词

semver grammar divergence spec · ManifestSchema.version regex · PackageVersionSchema.version ·
PluginSchema version prerelease case · package version schema four spellings


os-decision-facets

  • ① 项目长远合理性:A 让被强制的语法等于仓里已公开宣称的那句话,缩小特例与契约增生(10 载体 → 3 声明);B/C 把假声明搬到另一个载体上,增生不减。
  • ② 实际业务拉动:有实测 —— git tag 483/7611 与 packages/spec/CHANGELOG.md 版本标题 8/170 是预发布,而描述包的那个键表达不出预发布;反向零拉动 —— 退化形态(01.1.1 / 1.0.0-0123 / 1.0.0-alpha..1 / 1.0.0+.)在 examples/** 与 packages/apps/** 里零生产者(亮控:同模式全树 43)。
  • ③ 防 AI 犯错:A 是唯一让「AI 的直觉」与「被强制的语法」一致的选项;B 永不拒绝作者,代价是把 1.0.0-alpha..1 这类无法排序的串永久册封进已发布面 —— 静默容忍胜过响亮拒绝的反面。
  • ④ 创业阶段不扩散:第一阶段(10 → 3 个被引用声明)即本轴一步;本轮新发现的三个「已发布但无人解析」载体证明,语法写成字面量时载体数会自己长。

Prior rulings read: version grammar,semver,manifest.version,prerelease → 1 hits; ADR-0087 D1

⚠️ 那条 ADR 读过了,它不答本卡:ADR-0087 D1 是协议握手(PROTOCOL_VERSION、engines.protocol 的检查与结构化拒绝),命中只是 :106 上的 semver 一词;它⛔ 没有裁定包清单 version 键在十个载体上的文法。⇒ 本卡仍是决策卡,⛔ 不是执行卡。(判据出自 check-prior-rulings.mjs 的警告「Read the decision before presenting the card」—— 本席读了。)

推荐:A(SemVer 2.0.0)。自检:只看①选 A;②③④ 是否翻转:② 不翻(两侧都支持 A:有预发布拉动、退化形态零拉动)· ③ 不翻(A 是唯一直觉=强制的选项)· ④ 不翻(第一阶段本身就是不扩散)。⇒ 四棱同向 A。

置信缺口:objectstack-ai/cloud 未挂载 ⇒ 它的 publish 路由接受集在 A/B/C 下都会动而本轮无法读;且四套文法的判读取自正则字面量而非运行期 Zod 判决(刻意不建构,避开陈旧 dist)。


Generated by Claude Code

Activity

  1. os-bill commented on Sep 18, 2026

    @os-bill
    Collaborator

    串行队登记:本卡现在不可派,面被席位 1 持着

    domain:spec seat 2(座位贴 #18549)。⛔ 不认领、不定级 —— 只把一次实测的持有关系登记下来,免得下一个取卡的人和席位 1 撞上。

    ⏱️ 2026-09-18T01:26Z,对当前全部 31 个 open PR 逐个拉 /pulls/N/files 实测:

    本卡的三个版本文法载体中,packages/spec/src/kernel/manifest.zod.ts
      被 PR #18319 持有(+87/-3)—— 席位 1(os-warren)的 #17534,claude/issue-17534-manifest-id-reverse-domain-regex
    ⭐ 亮控:packages/lint/src/validate-list-view-field-refs.test.ts 读出 [18860] ⇒ 仪器活着,上面那条命中是真的
    

    ⚠️ 而 manifest.zod.ts 恰恰是本卡里最严的那一套(^\d+\.\d+\.\d+$,拒 2.0.0-beta.1)—— 正典化无论朝哪个方向都要动它。⇒ 本卡排在 #18319 之后,⛔ 不并发。

    顺带记两条,给承接的人省一轮

    1. ⭐ 分诊已经把第一件交付物写死了:先裁定哪一套是正典再把其余三套指过去,⛔ 不是「把最松的抄给另外三个」 —— 后者会一次悄悄放宽三个面。
    2. ⚠️ 几乎任何收敛方向都会动已发布接受集:收敛到最严 ⇒ 收紧三个面;收敛到最松 ⇒ 放宽三个面;收敛到中间的 PackageVersionSchema ⇒ 一边紧一边松。⇒ 按分诊那句「若正典化的方向是收紧任一已发布面,那一刻回来找维护者」,本卡大概率以「提案 + 升维」收尾,⛔ 不是一轮落地。派它的人应当按测量先行派,⛔ 不要按施工派。
    3. ⭐ 卡面自己还留了一个未测的关键问:有没有任何活路径真的用 ManifestSchema 判过真实 bundle 的 manifest.version —— 卡面明说这决定它是「今天的缺陷」还是「潜伏的陷阱」,而分诊的 p2 也只给到「有实测违例、无实测事故链」。⇒ 那一问该是承接轮的第一个读数。

    Generated by Claude Code

  2. self-assigned this
    on Sep 18, 2026
  3. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    Claim: PM loop round R9 — a measurement-and-proposal round, ⛔ not a construction round.
    Session: session_019srGWGCBBCBHqcDoRZpQRh
    Branch: claude/issue-18697-version-grammar-canon-measurement
    Worktree: wt-18697
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: ⛔ NONE — read-only round. ⛔ No write to packages/spec/src/kernel/manifest.zod.ts, kernel/plugin.zod.ts, marketplace/package-version.zod.ts or anything else; the deliverable is a report, and a PR is expected to be null
    Container & model: M, mode:subagent, model: opus — dispatch-gates.mjs --repo objectstack-ai/objectstack --tier gives no path-derived mandate on this surface (run 2026-09-18T20:32Z); with zero files written the tier question is the report's rigour, not a build gate
    Clause-②: no
    Thread-read: 5723612289
    Serial constraints cleared: PR #18319 holds packages/spec/src/kernel/manifest.zod.ts (+87 / −3) — live re-read 2026-09-18T20:32Z: open, draft, 92 files, last pushed 2026-09-16T08:49Z. ⇒ this round writes nothing, so the serialisation cannot be breached; the construction round waits for that PR


    Why this is dispatched read-only

    Two seats have already fenced this card and both fences point the same way:

    • Triage (comment 5717463836): 「承接席的第一件交付物是裁定哪一套是正典并把其余三套指过去,⛔ 不是『把最松的那个抄给其他三个』」 and 「⚠️ 若正典化的方向是收紧任一已发布面,那一刻回来找维护者(公开契约收窄)」.
    • Seat 2 (comment 5723612289), which also measured the file holder: 「几乎任何收敛方向都会动已发布接受集 … 本卡大概率以『提案 + 升维』收尾,⛔ 不是一轮落地」 and 「派它的人应当按测量先行派,⛔ 不要按施工派」.

    ⇒ this seat takes both at face value: every canonicalisation direction moves a published accept set (to the strictest ⇒ narrows three surfaces; to the loosest ⇒ widens three; to the middle ⇒ one each way), and picking it is not the dev's and not this seat's. ⛔ So nothing is built this round.

    What the round owes, in order

    1. ⭐ The card's own unmeasured question, which triage and seat 2 both flag as the one that decides the class: does any live path judge a real bundle's manifest.version through ManifestSchema? With lit controls, and a declared radius. If yes ⇒ it is a today-defect; if no ⇒ a latent trap. ⛔ Grading stays triage's either way.
    2. The four grammars re-measured on current origin/main. ⚠️ The card's table was read on the commit objectstack-ai/cloud pins, and triage declared it could not verify that half (cloud is not mounted in its session) ⇒ ⛔ the table is not inherited as fact.
    3. A canonicalisation proposal: which carrier is the canon, what each of the other three becomes, and the accept-set delta per carrier in both directions — as a proposal in the report, ⛔ not as a diff.

    ⚠️ PackageManifestSchema.version is a bare z.string(), so tightening it is the one direction that looks free — it is not: that is a narrowing of a published surface, which triage routed to the maintainer. ⛔ Not this round's to take either.


    Generated by Claude Code

  4. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    os-dev-report

    {
      "issue": 18697,
      "status": "needs_decision",
      "branch": null,
      "pr": null,
      "session": "session_019srGWGCBBCBHqcDoRZpQRh",
      "premise_still_valid": true,
      "summary": "Read-only measurement round as dispatched: zero files written, zero refs pushed, no worktree, no changeset, no PR (pr: null is the expected outcome, and the fence around PR #18319, which holds packages/spec/src/kernel/manifest.zod.ts, is intact because nothing was written). Three deliverables: (1) the card's unmeasured question is answered YES - the strictest grammar is judged on real manifests by four independent live paths through ManifestSchema, one of them the Studio package form in objectui, so this is a today-defect class rather than a latent trap (grading stays triage's); (2) the four grammars are re-measured on origin/main 0f1cd83cb - all four regexes byte-identical to the card, one line pointer drifted, and the card's table is INCOMPLETE: six further carriers of the same concept exist (10 carriers, 4 accept sets, 2 repos); (3) a canonicalisation proposal in two phases, phase 1 accept-set-neutral, phase 2 the single question for the maintainer, with per-carrier accept-set deltas in both directions as concrete strings. Assignee on the card was already set by the PM dispatch pair (os-elon-musk); this round never writes it.",
      "question_1_does_any_live_path_judge_manifest_version_through_ManifestSchema": {
        "answer": "YES - four live judging paths through ManifestSchema, one of them user-facing. Class reading: today-defect, not latent trap. Grading stays the triage seat's.",
        "gating_paths": [
          "packages/cli/src/commands/plugin/build.ts:111 - ManifestSchema.safeParse(rawManifest); the input is objectstack.plugin.json read from disk at :104 (filename constant at packages/cli/src/utils/osplugin.ts:77); on failure printError + this.exit(1) at :113-115. HARD gate on the step that produces the .osplugin bundle.",
          "packages/spec/src/stack.zod.ts:2955 - defineStack() strict mode (default, options.strict !== false at :2933) runs ObjectStackDefinitionSchema.safeParse; that schema's manifest key is ManifestSchema.optional() at stack.zod.ts:1345; failure throws StackSchemaInvalidError at :2963. Real first-party producers, each carrying manifest.version: examples/app-crm/objectstack.config.ts:41 (4.0.0), examples/app-showcase/objectstack.config.ts:82 (0.1.0), examples/app-todo/objectstack.config.ts:41 (2.0.0), examples/app-multi-package/src/packages/core/index.ts:19 and .../orders/index.ts:58 (1.0.0), packages/services/service-i18n/objectstack.config.ts:9 (1.0.0), and the scaffold every new user starts from: packages/create-objectstack/src/templates/blank/objectstack.config.ts:11 (0.1.0).",
          "packages/metadata/src/plugin.ts:915 - the artifact load path (_parseAndRegisterArtifact, boot and reload) runs ObjectStackDefinitionSchema.parse on raw artifact JSON; the two envelope legs at :902 and :919 parse EnvironmentArtifactSchema, whose metadata key IS ObjectStackDefinitionSchema (packages/spec/src/system/environment-artifact.zod.ts:132). Throws on failure. This is a real deployed/compiled artifact, not a fixture.",
          "objectui (sibling repo, mounted, measured on its own origin/main 272a53066049025259c66621a0cecb03a0a65986): packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx:220 - ManifestSchema.safeParse(draft) in the Studio package create/edit dialog. The same file hand-copies the grammar at :39 (const VERSION_RE = the strict regex) and gates the form with it at :227 (versionOk). packages/app-shell/src/views/metadata-admin/package-schema.ts:60 builds the form's JSON Schema via z.toJSONSchema(ManifestSchema), so both the verdict and the help text come from this carrier. USER-FACING."
        ],
        "live_but_non_gating": "packages/objectql/src/registry.ts:3594 - ManifestSchema.parse(item) in Registry.validate('plugin', item), and :3591 InstalledPackageSchema.parse for 'package' (its .manifest is ManifestSchema at packages/spec/src/kernel/package-registry.zod.ts:86). Called from registerItem at :3421 inside try/catch: on failure it logs [metadata_spec_invalid] and registers anyway, deliberately, documented at :3408-3419.",
        "author_facing_cli_doors_parsing_the_same_composed_schema": [
          "packages/cli/src/commands/validate.ts:283",
          "packages/cli/src/commands/compile.ts:347",
          "packages/cli/src/lint/score.ts:89",
          "packages/cli/src/commands/migrate/meta.ts:421",
          "packages/cli/src/utils/scaffold-validate.ts:76"
        ],
        "measured_negatives_each_with_a_lit_control": [
          "INSTALL door does NOT judge the manifest. packages/runtime/src/domains/packages.ts:744-772: the POST branch takes const manifest = body.manifest || body at :746 and hands it to installPackage at :769 / :772 with no parse at all. Lit control on the same instrument and the same file: the identical grep finds SeedLoaderRequestSchema.safeParse at :1851, so a zero here is about this branch, not about the pattern. Independently corroborated by the declaration's own measured residual, packages/spec/src/api/package-api.zod.ts:363-377: 'Measured through HttpDispatcher.handlePackages, the door additionally answers 201 to five classes this schema refuses: 1. a manifest missing type and/or version'.",
          "REST publish route judges PRESENCE only, never grammar: packages/rest/src/package-routes.ts:438 - if (!manifest.id || !manifest.version) -> 400 PACKAGE_MANIFEST_INVALID, and it echoes the accepted version back at :447-450. os plugin publish likewise only reads the string: packages/cli/src/commands/plugin/publish.ts:107 - const version = String(manifest.version ?? '').trim(). Lit control: the ManifestSchema name census (365 hits repo-wide) finds zero hits in either file, while it finds the two real parse sites in build.ts and registry.ts.",
          "PATCH /api/v1/packages/:id judges the version, but NOT through ManifestSchema: packages/runtime/src/domains/packages.ts:1470 tests a hand-copied strict regex and answers 400 'version must be semantic (e.g. 1.0.0)'. Same accept set, separate carrier."
        ],
        "consequence_measured": "Within one bundle lifecycle the BUILD step refuses 2.0.0-beta.1 (exit 1) while the PUBLISH door accepts it (presence-only), and the same string is refused by the Studio form (twice: schema + hand-copied regex), refused by the PATCH door (400), and accepted by the INSTALL door (no parse). The divergence is therefore observable between doors on the same resource, not only between schema files.",
        "incident_chain_still_zero_in_this_repo": "0 prerelease-shaped version literals in examples/** and packages/apps/** (lit control: the same pattern finds 43 tree-wide, including 1.0.0-alpha.1 pinned as ACCEPTED for the loader at packages/core/src/plugin-loader.test.ts:94; and the 5 first-party manifest literals it does find are all plain x.y.z). So triage's p2 reading holds: a measured violation, no measured incident. BUT the business need is measured on the other side: 483 of 7611 git tags carry -rc / -beta / -alpha (e.g. @objectstack/spec@17.0.0-rc.6), and 8 of 170 version headings in packages/spec/CHANGELOG.md are prereleases - this org does cut prereleases of its own packages, and the manifest grammar cannot express one."
      },
      "radius_and_instrument": {
        "instrument": "git grep / git show / git ls-tree against the origin/main TREE OBJECT after git fetch origin main (objectstack 0f1cd83cbf42194bc2543002a94315c3b8c2523e; objectui 272a53066049025259c66621a0cecb03a0a65986). Never the shared working tree - measured reason: the shared checkout's AGENTS.md already differs from origin/main. Name census was word-bounded, then expanded by closure: direct embedders of ManifestSchema, then parse sites of those names, then a regex-literal census. Carrier sources were extracted with git show into the scratchpad and read as SOURCE TEXT; the two node scripts evaluate regex literals pulled out of those files by line with a required marker substring on the line, so a moved line fails loudly instead of reading the wrong thing.",
        "no_truncation": "No head -N anywhere in any counting pipeline; every count is wc -l over a full redirect, and every listing was printed with cat.",
        "stale_dist_hazard_avoided": "Nothing in this round resolves through packages/spec/dist or a turbo cache: no build was run and no built module was imported. Trade-off declared: the grammar verdicts are readings of the regex LITERALS (node RegExp over strings extracted from origin/main source) plus the repo's own pin at packages/spec/src/kernel/manifest.test.ts:29, not a fresh observation of the built Zod schemas.",
        "outside_the_radius": [
          "Dynamic dispatch by string key - a schema reached out of a registry table would not be found by a name grep. Partial mitigation, both measured: the manifest is recorded as absent from BUILTIN_METADATA_TYPE_SCHEMAS in packages/spec/liveness/manifest.json's own note, and Registry.validate's string-keyed dispatch was found anyway because the schema is named at the call site.",
          "objectstack-ai/cloud is NOT mounted in this session (no clone under /home/user; only objectstack, objectui and two unrelated worktrees). It is the only known consumer of PackageVersionSchema (the card's own provenance, cloud#2305 / cloud#2320). So this round cannot verify the cloud half either - the same declaration the triage seat made, repeated rather than quietly dropped. NOT MEASURED: which commit cloud pins today, and whether cloud carries further version carriers.",
          "Runtime behaviour of the built schemas (see stale_dist_hazard_avoided). NOT MEASURED: Zod's actual verdict and message text for a refused version string."
        ]
      },
      "four_grammar_remeasure_on_origin_main": {
        "commit": "0f1cd83cbf42194bc2543002a94315c3b8c2523e",
        "the_cards_four": [
          {
            "carrier": "G1 ManifestSchema.version",
            "file_line_now": "packages/spec/src/kernel/manifest.zod.ts:330",
            "card_said": ":322 (drift +8)",
            "regex": "/^\\d+\\.\\d+\\.\\d+$/",
            "required": true,
            "describe": "Package version (semantic versioning)"
          },
          {
            "carrier": "G2 PluginSchema.version",
            "file_line_now": "packages/spec/src/kernel/plugin.zod.ts:212",
            "card_said": ":212 (no drift)",
            "regex": "/^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.-]+)?(\\+[a-zA-Z0-9.-]+)?$/",
            "required": false,
            "describe": "Version: major.minor.patch, with an optional -prerelease and an optional +build suffix. Looser than SemVer 2.0.0 - leading zeroes (01.1.1) and empty identifiers (1.0.0-alpha..1) are accepted."
          },
          {
            "carrier": "G3 PackageVersionSchema.version",
            "file_line_now": "packages/spec/src/marketplace/package-version.zod.ts:144-147, regex at :146",
            "card_said": ":144-146 (same field, regex line unchanged)",
            "regex": "/^\\d+\\.\\d+\\.\\d+(-[a-z0-9.-]+)?(\\+[a-z0-9.-]+)?$/",
            "required": true,
            "jsdoc_quoted_by_the_card_is_verbatim_at": ":143 - /** Semantic version string (e.g. `1.2.3`, `2.0.0-beta.1`). */"
          },
          {
            "carrier": "G4 PackageManifestSchema.version",
            "file_line_now": "packages/spec/src/marketplace/package-version.zod.ts:80",
            "card_said": ":80 (no drift)",
            "regex": null,
            "source": "version: z.string().describe('Semver version string (e.g. 1.2.3)'),",
            "required": true
          }
        ],
        "where_this_reading_differs_from_the_card": [
          "Regex strings: byte-identical for all four. The card's table is CONFIRMED on origin/main, not merely on the commit cloud pins - so the half triage could not verify is no longer load-bearing for the grammars themselves.",
          "One line pointer drifted: manifest.zod.ts:322 -> :330.",
          "The card's table is INCOMPLETE. Six more carriers of the same concept exist and the card names none of them. No new accept sets: all six restate G1 or G2."
        ],
        "carriers_the_card_did_not_name": [
          {
            "carrier": "MetadataPluginManifestSchema.version",
            "file_line": "packages/spec/src/kernel/metadata-plugin.zod.ts:649",
            "grammar": "G1",
            "required": true,
            "liveness": "published API surface (api-surface/kernel.json:186); zero live parse callers in this repo - only its own tests"
          },
          {
            "carrier": "PluginRegistryEntrySchema.version",
            "file_line": "packages/spec/src/kernel/plugin-registry.zod.ts:158",
            "grammar": "G1",
            "required": true,
            "liveness": "published (kernel.json:309); zero live parse callers - only its own tests"
          },
          {
            "carrier": "PluginMetadataSchema.version",
            "file_line": "packages/spec/src/kernel/plugin-validator.zod.ts:144",
            "grammar": "G1",
            "required": false,
            "liveness": "published (kernel.json:290); zero live parse callers - only its own tests and a type pin"
          },
          {
            "carrier": "PluginLoader.isSemverShapedVersion",
            "file_line": "packages/core/src/plugin-loader.ts:501",
            "grammar": "G2",
            "liveness": "LIVE boot path (checkVersionCompatibility at :448-465). Hand-copied on purpose: :495-498 says it is PluginSchema.version's spelling character for character and 'Change one spelling and you must change both'."
          },
          {
            "carrier": "PATCH /api/v1/packages/:id version check",
            "file_line": "packages/runtime/src/domains/packages.ts:1470",
            "grammar": "G1 (hand-copied)",
            "liveness": "LIVE HTTP door; answers 400 'version must be semantic (e.g. 1.0.0)'"
          },
          {
            "carrier": "objectui PackageFormDialog VERSION_RE",
            "file_line": "objectui packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx:39, used at :227",
            "grammar": "G1 (hand-copied, second repo)",
            "liveness": "LIVE Studio form gate"
          }
        ],
        "the_good_pattern_for_contrast": "CreatePackageVersionRequestSchema.version at packages/spec/src/marketplace/package-version.zod.ts:204 does NOT restate a grammar - it is PackageVersionSchema.shape.version, i.e. one declaration referenced. That is the shape phase 1 of the proposal generalises.",
        "bounded_out_as_a_different_concept": "Named so the claim is bounded rather than silently wide: manifest.zod.ts:766 (engines.platform, a RANGE - the same three-segment core preceded by an optional range-operator class: greater-than, less-than, equals, tilde, caret), package-version.zod.ts:59 (versionRange, bare string), packages/create-objectstack/src/runtime-image.ts:55 (docker image tag pin), packages/create-objectstack/src/pkg-utils.ts:18 (npm dependency version prefix test).",
        "totals": "10 carriers of 'the version of a package or plugin', 4 accept sets, 2 repos. All 7 spec carriers are published API-surface entries: api-surface/kernel.json:160 (Manifest), :186 (MetadataPluginManifest), :290 (PluginMetadata), :309 (PluginRegistryEntry), :312 (Plugin); api-surface/marketplace.json:43 (PackageManifest), :57 (PackageVersion). Every direction therefore moves a PUBLISHED accept set, exactly as seat 2 predicted - and on more surfaces than the card counted.",
        "the_declared_contract_violation_is_rendered_to_users_not_only_in_a_jsdoc": [
          "ManifestSchema.version's .describe() is 'Package version (semantic versioning)' (manifest.zod.ts:330) - no qualifier - and it reaches the published reference doc at content/docs/references/kernel/manifest.mdx:33 ('| **version** | `string` | yes | Package version (semantic versioning) |') and the Studio form's help text.",
          "objectui PINS that exact rendered English sentence: packages/app-shell/src/views/metadata-admin/PackageFormDialog.helpTextI18n.test.tsx:63 expects 'Package version (semantic versioning)' on screen, with the test's own comment naming ManifestSchema's .describe() in this repo as the single producer. The zh sibling is pinned at :43 and lives at i18n.ts:3121.",
          "The refusal message an admin sees is itself the false claim: objectui i18n.ts:3088 - 'engine.packages.create.versionInvalid' renders a zh sentence telling the user to use a semantic version, example 0.1.0, while refusing 2.0.0-beta.1, which IS one.",
          "This repo's own test asserts the contradiction: packages/spec/src/kernel/manifest.test.ts:17 is named 'should enforce semantic versioning' and its invalidVersions list at :29 contains '1.0.0-beta'.",
          "The recorded decisions call the key semver: docs/adr/0025-plugin-package-distribution.md:334 ('Immutable version | `sys_plugin_version` (semver, checksum, signature, ...)') and docs/adr/0016-studio-package-authoring-and-publish.md:113 ('assign immutable semver')."
        ]
      },
      "proposal_report_only_no_diff": {
        "shape": "Two phases. Phase 1 needs no ruling because it moves no accept set. Phase 2 is the single maintainer question and is the only thing in open_questions.",
        "phase_1_accept_set_neutral_de_proliferation": {
          "what": "Export ONE version-string grammar in packages/spec and have every carrier reference it instead of restating it - the pattern CreatePackageVersionRequestSchema already uses (PackageVersionSchema.shape.version).",
          "delta": "ZERO accept-set movement, measured: the five G1 carriers (manifest.zod.ts:330, metadata-plugin.zod.ts:649, plugin-registry.zod.ts:158, plugin-validator.zod.ts:144, runtime/packages.ts:1470) are byte-identical regexes, and plugin-loader.ts:501 is byte-identical to plugin.zod.ts:212. Collapsing byte-identical literals into one referenced constant cannot move a verdict.",
          "result": "10 carriers -> 3 declarations (G1, G2, G3) + 1 unconstrained (G4), with the objectui copy becoming an import of the published constant in the same landing. Phase 2 then becomes a one-line change instead of a ten-site sweep across two repos.",
          "why_it_matters_now": "The proliferation is still growing on its own: three of the ten carriers are published-but-unparsed spec declarations that nobody had named before this round."
        },
        "phase_2_which_accept_set_becomes_the_canon": {
          "recommended": "A - SemVer 2.0.0 exactly (semver.org's published regex), with the four degenerate forms retired as an announced narrowing.",
          "option_A_semver_2_0_0": {
            "ManifestSchema.version and its four G1 copies (incl. the runtime PATCH door and objectui VERSION_RE)": {
              "starts_accepting": [
                "2.0.0-beta.1",
                "17.0.0-rc.5",
                "1.0.0-alpha.1",
                "1.0.0-Beta.1",
                "1.0.0+Build.5",
                "1.0.0+20230101",
                "1.0.0-rc.1+exp.sha.5114f85"
              ],
              "starts_refusing": [
                "01.1.1"
              ]
            },
            "PluginSchema.version and PluginLoader.isSemverShapedVersion": {
              "starts_accepting": [],
              "starts_refusing": [
                "01.1.1",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ]
            },
            "PackageVersionSchema.version": {
              "starts_accepting": [
                "1.0.0-Beta.1",
                "1.0.0+Build.5"
              ],
              "starts_refusing": [
                "01.1.1",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ]
            },
            "PackageManifestSchema.version (bare z.string)": {
              "starts_accepting": [],
              "starts_refusing": [
                "latest",
                "v1.0.0",
                "1.0",
                "(empty string)",
                "1.0.0 (trailing space)",
                "2.0.0-beta.1extra!",
                "01.1.1",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ]
            },
            "needs_maintainer": "YES - it narrows two published surfaces by the four degenerate forms and the G1 carriers by 01.1.1. That is the override of the #16365 widen-never-narrow freeze recorded at packages/spec/src/kernel/plugin.zod.ts:190-199."
          },
          "option_B_canon_is_G2_the_loader_grammar": {
            "ManifestSchema.version and its four G1 copies": {
              "starts_accepting": [
                "2.0.0-beta.1",
                "17.0.0-rc.5",
                "1.0.0-alpha.1",
                "1.0.0-Beta.1",
                "1.0.0+Build.5",
                "1.0.0+20230101",
                "1.0.0-rc.1+exp.sha.5114f85",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ],
              "starts_refusing": []
            },
            "PluginSchema.version and PluginLoader.isSemverShapedVersion": {
              "starts_accepting": [],
              "starts_refusing": []
            },
            "PackageVersionSchema.version": {
              "starts_accepting": [
                "1.0.0-Beta.1",
                "1.0.0+Build.5"
              ],
              "starts_refusing": []
            },
            "PackageManifestSchema.version (bare z.string)": {
              "starts_accepting": [],
              "starts_refusing": [
                "latest",
                "v1.0.0",
                "1.0",
                "(empty string)",
                "1.0.0 (trailing space)",
                "2.0.0-beta.1extra!"
              ]
            },
            "needs_maintainer": "Only for the G4 carrier (tightening a bare z.string is still a published narrowing - triage routed exactly this out). Nothing else is refused anywhere, so B is the literal reading of the #16365 ruling.",
            "cost": "It freezes into the canon the forms the loader's own docblock calls degenerate and SemVer-forbidden (01.1.1, 1.0.0-0123, 1.0.0-alpha..1, 1.0.0+.), and it leaves 'semantic versioning' a false claim in the help text an admin reads - the same false-claim shape #17070 already had to rewrite once on the sibling key (plugin.zod.ts:201-211)."
          },
          "option_C_canon_is_G3_PackageVersionSchema": {
            "ManifestSchema.version and its four G1 copies": {
              "starts_accepting": [
                "2.0.0-beta.1",
                "17.0.0-rc.5",
                "1.0.0-alpha.1",
                "1.0.0+20230101",
                "1.0.0-rc.1+exp.sha.5114f85",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ],
              "starts_refusing": []
            },
            "PluginSchema.version and PluginLoader.isSemverShapedVersion": {
              "starts_accepting": [],
              "starts_refusing": [
                "1.0.0-Beta.1",
                "1.0.0+Build.5"
              ]
            },
            "PackageVersionSchema.version": {
              "starts_accepting": [],
              "starts_refusing": []
            },
            "PackageManifestSchema.version (bare z.string)": {
              "starts_accepting": [],
              "starts_refusing": [
                "1.0.0-Beta.1",
                "1.0.0+Build.5",
                "latest",
                "v1.0.0",
                "1.0",
                "(empty string)",
                "1.0.0 (trailing space)",
                "2.0.0-beta.1extra!"
              ]
            },
            "needs_maintainer": "YES - narrows the live runtime loader by case alone.",
            "cost": "Pays a narrowing of the boot path for no standard: it keeps every degenerate form AND loses uppercase prereleases. It also flattens the subset relation the card records as cloud's reason for choosing G3 (G3 strictly inside G2, so nothing that passes publish can fail the runtime parse) - under C they become equal, and the safety property stops being a property of the choice."
          },
          "option_D_canon_is_G1_the_strictest": {
            "ManifestSchema.version and its four G1 copies": {
              "starts_accepting": [],
              "starts_refusing": []
            },
            "PluginSchema.version and PluginLoader.isSemverShapedVersion": {
              "starts_accepting": [],
              "starts_refusing": [
                "2.0.0-beta.1",
                "17.0.0-rc.5",
                "1.0.0-alpha.1",
                "1.0.0-Beta.1",
                "1.0.0+Build.5",
                "1.0.0+20230101",
                "1.0.0-rc.1+exp.sha.5114f85",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ]
            },
            "PackageVersionSchema.version": {
              "starts_accepting": [],
              "starts_refusing": [
                "2.0.0-beta.1",
                "17.0.0-rc.5",
                "1.0.0-alpha.1",
                "1.0.0+20230101",
                "1.0.0-rc.1+exp.sha.5114f85",
                "1.0.0-0123",
                "1.0.0-alpha..1",
                "1.0.0+."
              ]
            },
            "PackageManifestSchema.version (bare z.string)": {
              "starts_accepting": [],
              "starts_refusing": [
                "every string above plus latest, v1.0.0, 1.0, (empty string), trailing-space forms and 2.0.0-beta.1extra!"
              ]
            },
            "verdict": "RULED OUT by measurement, not preference: it refuses every prerelease the boot path accepts today, and packages/core/src/plugin-loader.test.ts pins those as ACCEPTED while plugin.zod.ts:196-199 records narrowing them as 'the one thing the #16365 ruling forbids'. This is also the direction triage explicitly warned about."
          },
          "option_E_canon_is_G4": "RULED OUT - G4 is the absence of a grammar. Canonising on it would delete enforcement on nine carriers, including two live HTTP doors and the Studio form.",
          "witness_note": "An accept set is infinite, so the strings above are WITNESSES, one per structural difference between two regexes, evaluated mechanically (not hand-reasoned) by the two scratchpad scripts over regexes extracted from origin/main source. The difference CLASSES they stand for: presence of a prerelease suffix; presence of a build suffix; ASCII case in those suffixes; leading zeroes in the numeric core; empty / leading-zero identifiers; and non-version strings."
        },
        "four_axes": {
          "real_measured_business_need": "Measured on both sides. For a prerelease grammar: 483 of 7611 tags and 8 of 170 spec CHANGELOG version headings are prereleases, so first-party packages do cut rc builds, while the manifest key that describes a package cannot express one; plus one already-in-tree cross-door contradiction (build refuses what publish accepts). AGAINST the degenerate forms: zero producers in the authoring corpus - 0 hits for a leading-zero core or an empty identifier in examples/** and packages/apps/**, lit control being the 5 plain literals the same pattern does find.",
          "long_term_soundness_weighted_at_least_half": "A canon named after a standard it does not implement is the defect this card is about, so the only direction that CLOSES the class-(b) violation rather than relocating it is the one where the enforced grammar equals the claim already made in the .describe(), the reference doc, the zh refusal message and two ADRs. It also leaves ordering answerable by the same grammar that admits the string (plugin-loader.ts:490-493 points ordering at dependency-resolver.ts, which cannot order 1.0.0-alpha..1).",
          "structurally_harder_for_an_AI_to_write_wrong_metadata": "An AI filling manifest.version writes SemVer, because the key is called version and every claim around it says semantic versioning. Today the strictest carrier refuses that instinct at build/boot/UI time, and the loosest published carrier accepts strings no tool can order. Option A is the only one where instinct and enforcement agree; option B is the only one where the author can never be refused, at the price of admitting 1.0.0-alpha..1 into a published surface permanently. Phase 1 also removes the trap where an AI reads one of the ten carriers and generalises its grammar to the others.",
          "startup_stage_non_proliferation": "Phase 1 is this axis in one step: 10 carriers -> 3 declarations, and the three published-but-unparsed carriers found this round show the count grows by itself when the grammar is a literal instead of a reference."
        },
        "blast_radius_the_construction_round_must_carry_measured_this_round": [
          "packages/spec/src/kernel/manifest.zod.ts is held by open PR #18319 (live re-read by the dispatching seat 2026-09-18T20:31Z: open, draft, 92 files) - the construction round stays serialised behind it. Writing nothing this round is what kept that fence intact.",
          "packages/spec/src/kernel/manifest.test.ts:17-38 - its invalidVersions list must lose '1.0.0-beta' under any widening canon, and the test's name ('should enforce semantic versioning') is itself part of the false claim.",
          "The three declared-only carriers move with the shared constant (no consumer to migrate).",
          "objectui needs its own card in its own repo, blocked by the spec change: PackageFormDialog.tsx:39 VERSION_RE, :227 versionOk, the pinned English help text at PackageFormDialog.helpTextI18n.test.tsx:63, and the zh strings at i18n.ts:3088 and :3121.",
          "cloud's publish route: unmounted here, unmeasured. Its accept set moves under A, B and C."
        ],
        "interim_that_moves_no_accept_set_at_all": "If phase 2 has to wait, the honest-claim half can land alone the way #17070 did on the sibling key: restate ManifestSchema.version's .describe() to the grammar actually enforced (major.minor.patch, no prerelease) instead of the bare words 'semantic versioning'. ⛔ Not free and ⛔ not this round's: that string is a published surface too - it is generated into content/docs/references/kernel/manifest.mdx:33 and pinned verbatim by an objectui test - so it is its own card with a cross-repo half, not a drive-by edit."
      },
      "agents_md_reading_quoted_from_the_file": {
        "the_cited_anchor_does_not_resolve": "MEASURED: grep -n '0\\.1' over git show origin/main:AGENTS.md exits 1 with 0 hits. Lit control on the same instrument over the same bytes: grep -c 'one strict contract beats N dialects' returns 1. So 'AGENTS.md #0.1' - cited by the triage comment and by .changeset/17499-groupbyfield-non-padded.md:23 and .changeset/grouping-field-non-padded.md:23 - points at nothing in AGENTS.md as of 0f1cd83cb. The reading survives; only the anchor is gone.",
        "prime_directive_7_verbatim": "7. **One Zod source per metadata type.** Each type (`view`, `flow`, `agent`, …) has exactly one schema in `packages/spec/src/{domain}/`. Org overlay opt-in lives only in `allowOrgOverride` on `DEFAULT_METADATA_TYPE_REGISTRY` — no parallel whitelists. See ADR-0005.",
        "prime_directive_12_verbatim_load_bearing_sentence": "⛔ never add a lenient alias or `??` fallback in a consumer (a node executor, the REST layer, a renderer) to tolerate off-spec input: one strict contract beats N dialects, and this is an **internal** contract (we own both ends), so \"be liberal in what you accept\" does **not** apply.",
        "applied_here": "'one strict contract beats N dialects' is the sentence the current state violates: one key, four accept sets, ten carriers, two repos - and the four are not even a producer/consumer split, they are four producers of the verdict on the same string. #12 also fixes the SHAPE of any fix: the canon moves at the producer (the spec declaration), never as tolerance at a door - so ⛔ no '?? fallback' at the install door and ⛔ no widening of one consumer to match another. Phase 1 is #7's mechanical reading for a key rather than a type: exactly one declaration, referenced."
      },
      "tests": "NONE RUN - read-only round by dispatch. No build, no pnpm test, no pnpm typecheck, no check:* gate, and the shared verify lock (scripts/pm/os-verify-lock.sh) was never taken, so no concurrency budget was consumed. Zero files were written in either repo; zero refs pushed. The only executions were read-only git plumbing against origin/main tree objects and two node scripts in the scratchpad over source text extracted with git show. NOT MEASURED, declared rather than implied: (1) Zod runtime verdicts and refusal messages - no dist was built or imported, deliberately, because a sibling seat was fooled three times in one round by a stale packages/spec/dist; (2) CI - nothing was pushed, so there is nothing for CI to say; (3) objectstack-ai/cloud - not mounted in this session. No ablation and no reverse verification apply: nothing was changed, so there is nothing to ablate.",
      "commands": [
        "git fetch origin main; git rev-parse origin/main  -> 0f1cd83cbf42194bc2543002a94315c3b8c2523e (objectstack); git rev-parse --is-shallow-repository -> false",
        "cd /home/user/objectui; git fetch origin main; git rev-parse origin/main -> 272a53066049025259c66621a0cecb03a0a65986",
        "git grep -n -w ManifestSchema origin/main  -> 365 hits, 112 in non-test sources (full listing kept, no head)",
        "git grep -n -E '(PackageInstallRequestSchema|PackageInstallBodySchema|PackageUpgradeRequestSchema|ResolveDependenciesRequestSchema|InstalledPackageSchema|InstallPackageRequestSchema|UpgradeSnapshotSchema|UpgradePackageRequestSchema|ArtifactPackageEntrySchema|AssembledPackageBodySchema|AssembledInstalledPackageSchema|InstalledPackageAtEitherStageSchema|ObjectStackDefinitionSchema|StackSchema)\\s*\\.?\\s*(safeParse|parse)\\(' origin/main  -> 175 hits; the non-test subset is the live path list above",
        "git grep -n -w PluginSchema / PackageVersionSchema / PackageManifestSchema / CreatePackageVersionRequestSchema origin/main  -> 135 / 12 / 15 / 11 hits; PackageVersionSchema and PackageManifestSchema have NO live parse caller in this repo",
        "git grep -n -E '\\\\d\\+\\\\\\.\\\\d\\+\\\\\\.\\\\d\\+' origin/main -- 'packages/*/src/**' 'packages/*/*/src/**' 'scripts/**'  -> 36 hits, 24 outside tests: the carrier census that found the six the card does not name",
        "git show origin/main:PATH | sed -n 'START,END p'  -> the verbatim regex lines and their enclosing schemas, per carrier file",
        "node scratchpad/grammar-matrix.mjs  -> extracts each regex literal from origin/main source BY LINE WITH A REQUIRED MARKER (a moved line throws) and prints the accept matrix; result: 8 regex carriers, 3 distinct grammars, plus the bare z.string carrier",
        "node scratchpad/delta.mjs  -> per-carrier accept-set deltas in both directions for canon options A (SemVer 2.0.0), B (G2), C (G3), D (G1)",
        "git ls-tree -r --name-only origin/main | grep -E 'objectstack\\.plugin\\.json$'  -> 0 (lit control: the same instrument finds 10 objectstack.config.ts)",
        "git grep -n -E \"version: *'[0-9]+\\.[0-9]+\\.[0-9]+[-+]\" origin/main -- 'examples/**' 'packages/apps/**'  -> 0 (lit control: same pattern tree-wide -> 43)",
        "git tag --list | grep -E '-(rc|beta|alpha)' | wc -l  -> 483 of 7611 tags; git show origin/main:packages/spec/CHANGELOG.md | grep -cE '^## [0-9]+\\.[0-9]+\\.[0-9]+-'  -> 8 of 170",
        "git grep -n '0\\.1' over git show origin/main:AGENTS.md  -> exit 1, 0 hits (lit control: 'one strict contract beats N dialects' -> 1 hit)",
        "objectui: git grep -n -E 'ManifestSchema|PackageVersionSchema|PackageManifestSchema|PluginSchema' origin/main -> 17 hits; git grep -n -E '\\\\d\\+\\\\\\.\\\\d\\+\\\\\\.\\\\d\\+' origin/main -- 'packages/**' 'apps/**' -> 1 hit (PackageFormDialog.tsx:39)"
      ],
      "mcp_calls": "0 - no MCP GitHub tool was called, read or write. Card and comments were read through the REST proxy with curl (GET /issues/18697 -> HTTP 200, GET /issues/18697/comments -> HTTP 200).",
      "api_writes": "1 - POST /repos/objectstack-ai/objectstack/issues/18697/comments (this report). No push, no POST /pulls, no POST /issues/18697/labels: the dispatch forbids posting anything to GitHub except this report comment, so ⛔ the needs-user-decision label that would stand the H52 half-state row down was NOT applied - it is the seat's to apply, and this report's non-empty open_questions is what it stands down.",
      "open_questions": [
        {
          "question": "Which accept set becomes the single canon for 'the version of a package or plugin'? Every candidate moves at least one PUBLISHED accept set (all 7 spec carriers are api-surface entries), and two of the four candidates require overriding the #16365 widen-never-narrow freeze recorded in packages/spec/src/kernel/plugin.zod.ts:190-199. Phase 1 (collapse the 10 byte-identical-or-duplicated carriers onto 3 referenced declarations, zero accept-set movement) does NOT need this answer and can land first.",
          "options": [
            "A - SemVer 2.0.0 exactly. G1 carriers gain the whole prerelease/build space (2.0.0-beta.1, 17.0.0-rc.5, 1.0.0+20230101, 1.0.0-Beta.1) and lose 01.1.1; G2 carriers (incl. the live boot path) lose 01.1.1, 1.0.0-0123, 1.0.0-alpha..1, 1.0.0+.; G3 gains uppercase and loses those same four; G4 starts refusing latest / v1.0.0 / 1.0 / empty. Needs the maintainer for two narrowings.",
            "B - the loader grammar G2. Nothing anywhere starts being refused except on G4; G1 carriers widen by the full prerelease/build space INCLUDING 1.0.0-alpha..1 / 1.0.0-0123 / 1.0.0+.; G3 gains uppercase. Literal compliance with #16365; freezes the degenerate fringe into a published canon and keeps 'semantic versioning' a false claim in the Studio help text.",
            "C - the middle grammar G3. G1 widens (lowercase prerelease/build only), G2 and the boot path narrow by uppercase (1.0.0-Beta.1, 1.0.0+Build.5). Needs the maintainer, buys no standard, and flattens the subset relation cloud's publish route was deliberately built on.",
            "D - the strictest grammar G1. Refuses every prerelease the boot path accepts today and that plugin-loader.test.ts pins as accepted. Ruled out by measurement unless the maintainer overturns #16365 wholesale."
          ],
          "recommendation": "A, because it is the only option where the enforced grammar equals the claim the repo already publishes in four places (the .describe(), content/docs/references/kernel/manifest.mdx:33, objectui's pinned help text and zh refusal message, and ADR-0025 / ADR-0016), so it closes the class-(b) violation instead of relocating it - which is what the long-term-soundness axis, weighted at least half, asks for. Its cost is measured and small: the only strings any first-party surface loses are 01.1.1, 1.0.0-0123, 1.0.0-alpha..1 and 1.0.0+., and those have zero producers in the authoring corpus (lit control: the same instrument finds the 5 plain literals). If the maintainer prefers to keep #16365 absolute, B is the fallback - and then the honest-claim half (describe() text, doc row, objectui strings) must land WITH it, or the false claim simply changes which carrier it sits on. Either way, land phase 1 first: it is accept-set-neutral and it makes phase 2 a one-line change."
        }
      ],
      "out_of_scope_findings": [
        "to file (class b; dedupe words: package install door residual / POST api v1 packages 201 without version / handlePackages no safeParse / PackageInstallBodySchema subset description / install door declared vs enforced) - POST /api/v1/packages installs a manifest with NO version at all and answers 201, while the published declaration for that door requires it: PackageInstallRequestSchema declares manifest: ManifestSchema (packages/spec/src/api/package-api.zod.ts:279) and ManifestSchema.version is required (manifest.zod.ts:330). Measured: packages/runtime/src/domains/packages.ts:746 takes body.manifest || body and hands it to installPackage at :769 / :772 with no parse; lit control, same file, same grep: SeedLoaderRequestSchema.safeParse at :1851. The declaration's own docblock already records the residual and routes it away from itself, verbatim at :379-381: 'the residual is RECORDED here so a reader is not told the declaration is the door, and closing it is its own decision with its own card.' Filed here as a finding for the seat because ⛔ dedupe and grading are not the dev's.",
        "noted, not filed: packages/spec/liveness/manifest.json's _note cites ManifestSchema.parse at packages/objectql/src/registry.ts:2950; on 0f1cd83cb that call sits at :3594. A stale line pointer inside a dated, governed ledger note. Successor: that ledger re-attests on a 180-day expiry, so the next attestation pass carries it - it is not orphaned.",
        "noted, not filed: the anchor 'AGENTS.md #0.1' resolves to nothing on origin/main (measured with a lit control, see agents_md_reading). It is cited by the triage comment on this card and by two changesets (.changeset/17499-groupbyfield-non-padded.md:23, .changeset/grouping-field-non-padded.md:23). The reading is intact under Prime Directives #7 and #12. AGENTS.md is a governed surface, so no edit was attempted. Successor: whoever next edits the Prime Directives, or the next changeset that would cite the anchor.",
        "noted, not filed: three published-but-unparsed version carriers (packages/spec/src/kernel/metadata-plugin.zod.ts:649, plugin-registry.zod.ts:158, plugin-validator.zod.ts:144) have zero live parse callers in this repo - ADR-0049 enforce-or-remove material on their face, but they are inside THIS card's subject (same concept, same grammar) and move with the canon, so ⛔ not a separate card. Successor: this card's construction round.",
        "noted, not filed: objectui hand-copies the strict grammar in a second repo (packages/app-shell/src/views/metadata-admin/PackageFormDialog.tsx:39, gating the form at :227) and pins the spec-produced help text verbatim (PackageFormDialog.helpTextI18n.test.tsx:63). Not a card yet: it becomes a Blocked-by card in objectui the moment the canon lands here, filed in the repo where that fix lands. Successor: this card's construction round."
      ]
    }

    Generated by Claude Code

  5. os-elon-musk commented on Sep 18, 2026

    @os-elon-musk
    Collaborator

    只读测量轮交付完成 ⇒ 本卡转决策箱。Seat: domain:spec#3 · 2026-09-18T20:57Z

    Seat: domain:spec#3

    Release: session_019srGWGCBBCBHqcDoRZpQRh · 因 = 测量轮已交付且第二阶段需维护者裁定(⛔ 非弃飞、⛔ 非前提证伪)· 去向 = needs-user-decision,assignee 同笔清空;下一任按新状态重新认领。

    维护者速读(一件事,一个问题)

    「一个包的版本号」这个概念,本仓 + objectui 现在有 10 个载体、4 套互不相同的接受集,而其中最严的那一套正在四条活路径上判真实清单,包含 Studio 里用户看得见的表单。后果今天就可观察:同一个包在 build 这一步被拒、在 publish 那一步被放过 —— 2.0.0-beta.1 在构建时 exit 1,而发布门只看键是否存在。同时 spec 自己的文案、生成的参考文档、objectui 的帮助文字与两份 ADR 都写着「semantic versioning」,而被拒的那个字符串正是一个合法 SemVer。

    第一阶段(把 10 个载体收敛成 3 个被引用的声明)不需要您裁:五处 G1 正则逐字节相同(本席已逐行核过),合并引用动不了任何接受集。真正要您拍的只有第二阶段一句话:哪一套接受集成为唯一正典?

    ⇒ 请选 A / B / C / D(推荐 A)。

    四棱(四轴,按本仓框架)

    • 实际业务需求 —— 两侧都有实测。要预发布语法:git tag 483 / 7611 带 -rc/-beta/-alpha,packages/spec/CHANGELOG.md 的版本标题 8 / 170 是预发布 ⇒ 本组织确实在切自己的预发布,而描述包的那个键表达不出来;外加已在树里的跨门矛盾(build 拒、publish 放)。反对收进退化形态:examples/** 与 packages/apps/** 里带预发布的版本字面量 0 个(亮控:同一支模式在全树命中 43,且找到的 5 个第一方 manifest 字面量全是纯 x.y.z)。
    • 项目长远合理性(权重 ≥50%) —— 一个以某标准命名却不实现它的正典,正是本卡指控的缺陷本身 ⇒ 只有让被强制的语法等于仓里已经公开宣称的那句话的方向才关闭这个 (b) 类违例,其余方向只是把它搬个地方。
    • 防 AI 写错元数据 —— AI 填 manifest.version 会写 SemVer(键名叫 version,周围每句话都说 semantic versioning)。今天最严的载体在 build/boot/UI 三处拒绝这个直觉,而最松的已发布载体接受没有任何工具能排序的字符串(如 1.0.0-alpha..1)。A 是唯一让直觉与强制一致的选项。
    • 创业阶段不扩散 —— 第一阶段就是这条轴的一步:10 → 3。而且本轮新发现的三个「已发布但无人解析」的载体证明:语法写成字面量时,载体数会自己长。

    四个选项(每条都是「谁开始接受 / 谁开始拒绝」的实测差,字符串是逐条见证)

    选项 G1 五处(含运行期 PATCH 门与 objectui 表单) G2(含 boot 路径) G3 G4(裸 z.string) 要维护者?
    A SemVer 2.0.0 + 2.0.0-beta.1 17.0.0-rc.5 1.0.0-Beta.1 1.0.0+20230101 … · − 01.1.1 − 01.1.1 1.0.0-0123 1.0.0-alpha..1 1.0.0+. + 大写;− 同左四个 − latest v1.0.0 1.0 空串 … 是(两处收窄)
    B 以 G2 为正典 + 全部预发布/构建空间,含 1.0.0-alpha..1 1.0.0-0123 1.0.0+. 不变 + 大写 − 非版本串 仅 G4 一处
    C 以 G3 为正典 + 小写预发布/构建 − 1.0.0-Beta.1 1.0.0+Build.5(按大小写收窄 boot 路径) 不变 − 非版本串 + 大写 是
    D 以 G1(最严)为正典 不变 − 今天 boot 路径接受的每一个预发布 − 同理 − 全部 实测已排除

    本席独立核过的四条(⛔ 不采信报告自述)

    核验 读数(origin/main 0f1cd83cb)
    活判据路径抽检 2/4 packages/cli/src/commands/plugin/build.ts:111 ManifestSchema.safeParse(rawManifest) 后 this.exit(1) ✓;packages/spec/src/stack.zod.ts:2955 严格模式 ObjectStackDefinitionSchema.safeParse ✓
    卡面未点名的载体抽检 2/6 metadata-plugin.zod.ts:649 带 G1 正则 ✓;core/src/plugin-loader.ts:501 手抄 G2 正则 ✓
    ⭐ 第一阶段「零接受集移动」的决定性一腿 五处 G1 正则逐行取出、逐字节相同:/^\d+\.\d+\.\d+$/ ×5 ✓
    AGENTS.md #0.1 锚点 0.1 命中 0;亮控 one strict contract beats N dialects 命中 1 ⇒ 锚点悬空,读数成立(该锚被分诊评论与两份 changeset 引用)

    半径与未测(照实申报)

    • ⛔ objectstack-ai/cloud 未挂载本会话 ⇒ 它的 publish 路由接受集在 A/B/C 下都会动,而本轮无法读。这与分诊当初的申报同一条,⛔ 不因重复出现就被静默丢掉。
    • ⛔ 未测 Zod 运行期判决与拒绝文案:本轮刻意不建构、不导入 dist(兄弟席今天被陈旧 dist 骗过三次)⇒ 语法结论是对正则字面量的读数,加上仓内自有的 pin(manifest.test.ts:29)。
    • 施工轮仍排在 PR feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had #18319 之后(它持 manifest.zod.ts;本轮零文件写,栅栏未动)。

    落定后的执行形状(⛔ 非裁定,写在这里省下一轮)

    1. 先落第一阶段:导出一个版本语法常量,10 个载体改为引用(objectui 那份手抄改为 import 同批落地)。零接受集移动。
    2. 再落第二阶段:您选的那一套,一行改动。
    3. objectui 侧另立卡带 Blocked-by:(PackageFormDialog.tsx:39/:227、被钉死的英文帮助文字、i18n.ts:3088/:3121 的中文串)。
    4. manifest.test.ts:17 那个名叫 “should enforce semantic versioning” 的用例,其 invalidVersions 含 1.0.0-beta ⇒ 任何放宽方向都要同批改它,而它的名字本身也是那句假声明的一部分。

    Generated by Claude Code

  6. removed their assignment
    on Sep 18, 2026
  7. os-project-manager commented on Sep 20, 2026

    @os-project-manager
    Collaborator

    Ruling: batch #194 item 2 · letter A · maintainer 「同意」 2026-09-20T10:02Z

    Director seat, summon #25, session_012GcsUbuqFGBibkEDMRC1eE. Presented in detail with the recommendation A; the maintainer agreed 「同意」. Thread re-read to its last comment (5736085724) in the act that wrote this. Facts (measurement round 5736025846, seat-verified 5736085724): 10 carriers, 4 accept sets, 2 repos; the strictest grammar judges real manifests on four live paths (os plugin build, defineStack strict mode, the artifact load path, the Studio package form); 2.0.0-beta.1 is refused at build and accepted at publish; five G1 regexes are byte-identical; prerelease pull is measured (git tags 483/7611, CHANGELOG 8/170) and degenerate forms have zero producers.

    Ruling — A: the canon is SemVer 2.0.0

    • Phase 1, accept-set neutral: @objectstack/spec exports one version-grammar constant; all ten carriers reference it (objectui's hand-copied regex in PackageFormDialog.tsx:39 / :227 and the pinned help text import it — an objectui card with Blocked-by:; the hand-copied strict regex in packages/runtime/src/domains/packages.ts:1470 likewise). Zero accept-set movement, proven by the five byte-identical G1 regexes.
    • Phase 2, one line: the constant becomes SemVer 2.0.0 — prerelease and build identifiers, case-preserving, no leading zeros in numeric identifiers, no empty identifiers. Per carrier: G1 widens to accept valid prereleases and build metadata; G2 (the boot path) narrows only on degenerate forms (01.1.1, 1.0.0-0123, 1.0.0-alpha..1, 1.0.0+.) — ⛔ no valid prerelease the loader accepts today is refused, so the [finding] PluginSchema.version refuses the prerelease and build-metadata forms SemVer defines, while the loader that actually runs accepts them #16365 ruling is honoured; G3 widens to uppercase; G4 (bare z.string()) gains the grammar. B refused (permanently enshrines unsortable strings), C refused, D excluded by measurement.
    • The prose says what the grammar does: describe, generated reference docs, objectui help text and i18n refusal strings say SemVer 2.0.0; manifest.test.ts:17's 「should enforce semantic versioning」 case stops listing 1.0.0-beta as invalid.

    Four-facet reading (this seat's own): ① one grammar, named after the standard it implements, as npm and Cargo do; ② prerelease pull is measured on both sides and the cross-door contradiction is live; ③ an AI writes SemVer for a key named version surrounded by the words 「semantic versioning」 — A is the only option where that intuition is what is enforced; ④ phase 1 is itself the 不扩散 step (10 → 3 referenced declarations).

    Prior rulings read: #16365 (the boot path keeps accepting every valid prerelease — honoured), ADR-0087 D1 (protocol handshake; does not rule this key), thread: 5.

    Execution, same stroke

    needs-user-decision → pm:queue; priority:p2 · domain:spec stand. Clause-②: yes; ADR-0087 entries for the G2 / G4 narrowings; changeset minor with the BREAKING banner per the launch-window convention. Serial behind PR #18319 (holds packages/spec/src/kernel/manifest.zod.ts). The measurement report 5736025846 is the specification; the objectui card is filed by the spec seat at that PR's ACCEPT; cloud's publish route follows through its pin — the PR body names it as out of this repository's reach.


    Generated by Claude Code

  8. 12 remaining items

  9. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Unlock reading: Blocked-by: #20029 is satisfied; what PR #19637 now needs is a base-merge round · director seat · 2026-09-27T05:55Z

    Director seat, summon #30 (续), session_01AsCNgFBs8HCjwhyHQsFbx3, on the maintainer's 「12小时之前的pr什么情况帮我跟进到合并」. ⛔ Not a claim; the assignee and PR #19637 stay with their holder. ⛔ No label is written by this comment: the card's next state is the holder's to set with the round, or the maintainer's word re-dispatches it.


    Generated by Claude Code

  10. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round — director patch round (base merge + regeneration) on PR #19637
    Session: session_01AsCNgFBs8HCjwhyHQsFbx3
    Account: os-zhuang
    Branch: claude/issue-18697-semver-canon
    Worktree: objectstack-issue-18697
    Domain: domain:spec
    Seat: director (summon #30 续), acting on the maintainer's 「以上pr没有人跟进的,你应该负责跟进。」
    File surface: the merge of origin/main into the existing branch, and content/docs/references/api/package-api.mdx regenerated by its own generator (build-docs.ts, named in the file's header) — ⛔ no hand edit of a generated file, ⛔ no change to the PR's own increment (packages/spec, tests, docs) beyond what the merge itself requires. Stop on breach and explain it in the report.
    Container & model: S 级机械卡, mode:subagent, model: fable (dispatch-gates --tier on the surface: no path-derived mandate, floor sonnet · default opus · ceiling fable)
    Clause-②: yes (narrowing) — unchanged from the PR; the PASS 5770666558 carries over a pure-regeneration hop only, so a Regen-provenance: comment is owed on the PR
    Thread-read: 5853165978
    Serial constraints cleared: over the eleven open PRs older than 12 h (file lists read 2026-09-27T05:45Z) none touches packages/spec/src/kernel/manifest.zod.ts or package-api.mdx; PR #19957 touches contract.mdx, a different generated page. Newer open PRs were not read; the dev re-runs git merge-tree after the merge and stops on any second conflict.


    Generated by Claude Code

  11. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Release: on the maintainer's word — prior holder session_01Sfe5YjBLwB9J3y8fvm2xq1 (account os-justin, claim 5769855382) · cause: no own output on this lane for three days while the card's blocker cleared (5853165978) and its PR #19637 fell into conflict with main · destination: re-dispatched by the director seat for one patch round (the Claim: above this comment) · 2026-09-27T11:17Z

    The holder's last own output on this lane was read at 2026-09-24T12:20Z (seat post #19357's last update). Provenance, the three items: who — the maintainer; verbatim — 「以上pr没有人跟进的,你应该负责跟进。」; where — the maintainer's chat with session_01AsCNgFBs8HCjwhyHQsFbx3 (director seat, summon #30 续), 2026-09-27. The prior holder's work stands: PR #19637 and its contract review PASS are untouched; nothing is reverted. The state change was written in the same stroke as the claim: assignee os-justin → os-zhuang (this seat's linked user), pm:blocked → pm:dispatched; the body Blocked-by: line is satisfied and left as history.


    Generated by Claude Code

  12. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 18697,
      "status": "done",
      "branch": "claude/issue-18697-semver-canon",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19637",
      "session": "session_01AsCNgFBs8HCjwhyHQsFbx3 — subagent of the director seat, so this is the parent id; dispatch claim 5855326228 is the newest Claim: on the card and names this branch (read via REST, followed only by Release 5855354265 for the prior holder)",
      "premise_still_valid": true,
      "premise_note": "Confirmed at a newer main tip. A driver-free probe (bare repo, alternates to this object store, no merge.os-regen driver) of 60e6736661 against origin/main 03d6cb04ae printed exactly one CONFLICT (content): content/docs/references/api/package-api.mdx, merge-tree exit 1. The dispatch named 3bd28e2b2; origin/main had moved to 03d6cb04ae by the time of the merge.",
      "summary": "Merged origin/main 03d6cb04ae into the PR branch with a merge commit (11d564d260) by running bash scripts/pm/os-regen-merge.sh. Then did its step 4 in a separate commit (ac733fd2d8): pnpm --filter @objectstack/spec build, gen:migration-registry, and pnpm --filter @objectstack/spec check:generated --fix. That regenerated package-api.mdx (the one driver-free conflict) and the other artifacts the merge left stale. The push was a fast-forward 60e6736661..ac733fd2d8, with no rebase, amend or force-push. The PR now reads mergeable=true: 34 files, +972/-359, still a draft, labels unchanged, no auto-merge. The PR increment is unchanged in substance: for 32 of 34 files the PR own -U0 changed-line sequence is byte-identical before and after (per-file sha256 compare). The file count went from 33 to 34 only because main split package-api.mdx into package-api-assembled.mdx, so 3 of the 8 version-description swaps now render on the new page; the sorted changed-line multiset across that pair is identical. Regen-provenance comment 5855657876 is posted on the PR. It flags that the repo carry test does NOT certify this hop (see open_questions).",
      "files_changed": [
        "merge 11d564d260 (parents 60e6736661, 03d6cb04ae): the origin/main carry-over, with git default message from the script's git merge --no-edit",
        "regeneration ac733fd2d8, 10 files +108/-482, all written by generators, none by hand: content/docs/references/api/package-api.mdx (the driver-free conflict), content/docs/references/api/package-api-assembled.mdx (main-created page, gains the branch version description in 3 rows), content/docs/references/api/protocol.mdx, content/docs/references/kernel/manifest.mdx, content/docs/references/kernel/metadata-plugin.mdx, content/docs/references/kernel/package-registry.mdx, content/docs/references/kernel/package-upgrade.mdx, packages/spec/api-surface/kernel.json, packages/spec/export-origins/kernel.json, packages/spec/src/migrations/registry.ts (generated region only, a pure line permutation: sorted lines identical before and after)",
        "against a driver-free text merge at the same main tip (merge-tree tree 6f088ecf60) the final tree differs in exactly 3 paths: package-api.mdx, package-api-assembled.mdx, registry.ts. The other 7 driver-deferred artifacts are byte-identical to the text merge."
      ],
      "deviations": [
        "Merge shape. The dispatch (steps 3-4) asked to resolve the conflict inside the merge commit and give the merge commit a custom message with the trailer pair. .claude/agents/os-dev.md, which prevails over a dispatch, says never regenerate in MERGE state: commit the merge first, then regenerate, using bash scripts/pm/os-regen-merge.sh. I followed the script. Result: merge commit 11d564d260 carries git default message and no trailer pair. The separate regeneration commit ac733fd2d8 carries Claude-Session plus Co-authored-by (model-free). The pre-push card-trailer check passed on both. No amend was made to add trailers.",
        "Conflict count. With the os-regen driver registered, the local git merge stopped on nothing. The driver deferred 8 generated artifacts that both sides had edited, and script step 2 took main side of each. Step 3 commit was refused by pre-commit as stale, which is the documented collection point. It was then discharged by the regeneration commit; pre-commit printed: all deferred artifacts are current, marker cleared. The single conflict the dispatch expected is confirmed only by the driver-free probe.",
        "Regeneration scope is wider than package-api.mdx. check:generated on the merged tree proved 4 artifacts stale: migration-registry, api-surface, export-origins and references. So the regeneration touched 10 files, including packages/spec/src/migrations/registry.ts, a spec source file with a generated region. Only its generated region changed, by its own generator: a 38-line reorder putting the branch entry package-manifest-version-grammar-enforced after main new entry package-api-contracts-unmounted-entries-retired in id order. Without it, check:migration-registry is red on the merged tree. No hand edit to any spec source.",
        "Regen-provenance line shape. The dispatch template (MERGE-SHA — ...) does not match REGEN_PROVENANCE_LINE in scripts/pm/record-recognisers.mjs, which reads RECORD-ID · OLD-HEAD → NEW-HEAD. I wrote the recogniser shape with the dispatch content as its tail. Read-back confirms it parses as 5770666558 60e6736661 ac733fd2d8. I did not write the template phrase the increment is unchanged; I wrote the measured 34 vs 33 files instead.",
        "The first check:generated --fix under the verify lock ended in VERDICT queue-timeout, exit 99, never acquired: NOT MEASURED, holder pid 8144 running another agent spec test:repo. In the gap I ran gen:migration-registry outside the lock (a single tsx generator, no build or test). The same-slot retry acquired the lock and exited 0.",
        "The spec test call hit the harness 600s tool timeout while still queued, and the harness moved it to the background. I confirmed the lock holder pid 27801 had cwd equal to this worktree, blocked in the foreground on tail --pid until it exited, and read its VERDICT line. Nothing was left running."
      ],
      "tests": "All on ac733fd2d8 unless noted. [1] bash scripts/pm/os-verify-lock.sh -c pnpm --filter @objectstack/spec build: VERDICT command-exit 0 (first build on the merged tree; the --fix run rebuilt again, 36/36 dts present). [2] pnpm --filter @objectstack/spec check:generated before regeneration: exit 1, 4 of 15 stale (migration-registry, api-surface with ./kernel - MAJOR_MINOR_PATCH_VERSION_PATTERN and - SEMVER_SHAPED_LOWERCASE_VERSION_PATTERN, export-origins, docs). [3] check:generated --fix under the lock: VERDICT command-exit 0, printing gen:api-surface ok, gen:export-origins ok, gen:docs ok, and a re-check of the 3 green. [4] pnpm --filter @objectstack/spec check:generated after commit: exit 0, All 15 generated artifacts are up to date. [5] Second pnpm --filter @objectstack/spec gen:docs: exit 0, Generated 226 files, porcelain lines 0 (a no-op). [6] bash scripts/pm/os-verify-lock.sh -c pnpm --filter @objectstack/spec test: VERDICT command-exit 0, Test Files 543 passed (543), Tests 15969 passed, 2 todo. [7] exit 0 for each of: pnpm check:quick-reference-counts (13 sections match their tables and content/docs/references, 14 categories), pnpm check:nul-bytes (9657 text files, no raw control bytes), pnpm check:merge-driver, pnpm check:docs-single-h1, node scripts/check-doc-frontmatter.mjs, pnpm check:docs-spec-enumerations, pnpm check:doc-authoring, pnpm check:corpus-claim-drift, pnpm check:overlay-whitelist-table, pnpm check:role-word, pnpm check:route-envelope, node scripts/check-doc-route-spelling.mjs --advisory, pnpm check:docs-redirects, pnpm check:doc-anchors, node scripts/check-adr-0087-registration.mjs --base origin/main (1 declared-breaking changeset with its disposition, 4 registered ids), pnpm --filter @objectstack/spec run check:migration-registry (current, 261 semantic / 210 retired-key / 199 retired-def), pnpm --filter @objectstack/spec run check:docs (226 generated files in sync). [8] Pre-push: check:commit-card-trailers found 2 commit messages with no card relation and no model identifier; check-regen-pending passed. [9] Driver-free probe of ac733fd2d8 against the newer origin/main 805af4f290: merge-tree exit 0. The only overlapping path there, registry.ts, keeps generator order (main entry dataset-filter-nested-relation-equality-array-refused-at-save has the same neighbours as on main). [10] Shallow-clone control: merge-base --is-ancestor 03d6cb04ae 805af4f290 exit 0. NOT MEASURED, reason: this round changed no file outside packages/spec and content/docs, and the other half is CI on the merge ref. That covers the remaining commands of the 116 that node scripts/pm/dispatch-gates.mjs --commands derives for the whole 34-path PR surface (--ran not reconciled), plus typecheck/test of @objectstack/core and @objectstack/runtime.",
      "mcp_calls": "0",
      "api_writes": "2 — both through scripts/pm/post-stamped.mjs on the fleet relay (repository_dispatch to objectstack-ai/objectstack, and fleet-write.yml runs POST /repos/objectstack-ai/objectstack/issues/{n}/comments as objectstack-fleet[bot]): (1) PR 19637 comment 5855657876, relay run 36317769430 success, read-back identical, 5857 bytes; (2) this os-dev-report comment on card 18697. Plus one git push (not REST): 60e6736661..ac733fd2d8. Zero label, assignee, draft or body writes.",
      "open_questions": [
        {
          "question": "Does contract review PASS 5770666558 carry to ac733fd2d8? The claim assumed a pure-regeneration hop. I ran the ruled carry test unexplainedPathsBetween (scripts/pm/record-recognisers.mjs) on 60e6736661 → ac733fd2d8 against base origin/main. It returns 4 paths: packages/spec/src/kernel/manifest.test.ts, packages/spec/src/kernel/manifest.zod.ts, packages/spec/src/kernel/plugin-registry.zod.ts and packages/spec/src/migrations/registry.ts. These are PR-owned, non-os-regen files that main also edited, and git text-merged them without conflict. The PR own -U0 changed-line sequence in each is byte-identical before and after. The test accepts carry-over only on paths the PR never touched, so regenCarry reads refused and locateReviewOfRecord will answer absent for the new head.",
          "options": [
            "A: request a fresh contract-tier review on ac733fd2d8. Cost: one review round, but the review is cheap because the PR own lines are byte-identical and the diff to review is this round two commits.",
            "B: the director seat records a judgement that the hop is equivalent. Cost: the machine locator still reads absent, so any gate that consumes it disagrees with the record.",
            "C: widen unexplainedPathsBetween so a path whose PR-own delta is byte-identical across the hop counts as the merge carry-over from main, which the ruling text names as arm 2. Cost: a tooling card and its own review; it does not help this PR today."
          ],
          "recommendation": "A, because the ruled criterion is that the unexplained set is empty, and it is not; review cost is minimal given the byte-identity readings above. C is worth a card if merge-forwards over PR-owned files recur."
        }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed. unexplainedPathsBetween (scripts/pm/record-recognisers.mjs) is narrower than the ruling text it cites. The ruling reads: every touched path is a generated artefact OR THE MERGE COMMIT OWN CARRY-OVER FROM MAIN. The function explains a hand-written path only when neither head delta names it, so main carry-over into a PR-owned file is refused even when the PR own delta is byte-identical. Measured on PR 19637 hop 60e6736661 → ac733fd2d8: 4 such paths. Refusing is the conservative direction, so this is not filed as a defect; it is left for the director in open_questions option C. Dedupe words: regen carry, unexplainedPathsBetween, merge-forward, pure regeneration hop, carry-over arm"
      ]
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    pm:dispatched stripped by the director seat (summon #30 续) at 2026-09-27T13:35Z: this card closed completed when PR #19637 merged from the queue at 2026-09-27T13:05Z (Fixes #18697), and a closed card carries no pm state. No other change.


    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions