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

Closed

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. self-assigned this
    on Sep 22, 2026
  2. os-justin commented on Sep 22, 2026

    @os-justin
    Collaborator

    Claim: PM loop round 1
    Session: session_01Sfe5YjBLwB9J3y8fvm2xq1
    Branch: claude/issue-18697-semver-canon
    Worktree: objectstack-issue-18697
    Domain: domain:spec
    Seat: domain:spec#5
    File surface: packages/spec/src/kernel/manifest.zod.ts, packages/spec/src/kernel/plugin.zod.ts, packages/spec/src/marketplace/package-version.zod.ts, packages/runtime/src/domains/packages.ts, the new shared grammar constant's home in packages/spec, ADR-0087 entries, .changeset/, and the regenerated spec artifacts those imply (stop on breach; explain in the report)
    Container & model: L, mode:cloud, model: claude-opus-5 (this act's --tier run reports 「no path-derived mandate … floor sonnet · default opus · ceiling opus」 plus a clause-② SUSPECT surface on three packages/spec/src/** carriers; dispatched at the default judgement tier for a ruling-implementation card of this size)
    Clause-②: yes
    Thread-read: 5749112309
    Serial constraints cleared: all four named carriers — kernel/manifest.zod.ts, kernel/plugin.zod.ts, marketplace/package-version.zod.ts, runtime/src/domains/packages.ts — read FREE across ALL 13 open PRs; region packages/spec/src/kernel/ co-occupied only by #19610 (plugin-registry*, plugin-security-advanced* — different files, different contract), region packages/runtime/src/domains/ only by #19493 (automation.ts); the prior blocker PR #18319 verified MERGED first-hand; ⚠️ generated-artifact contention named below

    The blocker release, verified first-hand rather than inherited from the card body

    The body records Blocked-by: #17534 as RELEASED because PR #18319 merged. ⛔ Not taken on trust — re-verified in this act on the local clone:

    097d26859 feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had (#18319)
    git merge-base --is-ancestor 097d2685 origin/main -> TRUE
    

    ⇒ the release is real. The body's own reasoning is also endorsed: the hold rested on the PR, and card #17534 still being open + pm:dispatched is a half-state on that card, ⛔ not a live block on this one. ⛔ No state written to #17534 from here.

    Occupancy census — taken first-hand in this act

    git diff --name-only <merge-base> <head> over all 13 open PRs, 151 file rows. Four PRs had no local merge-base on the shallow clone; ⛔ that gap was not read as clean — the clone was deepened until each resolved (#19373 then yields exactly 22 files, matching its own changed_files). Controls: LIT packages/spec/src/data/object.zod.ts ⇢ #19618 and LIT packages/spec/src/api/package-api.zod.ts ⇢ #19373 both resolve; DARK packages/spec/src/zzz-no-such.zod.ts ⇢ 0.

    ⚠️ Generated-artifact contention, named at dispatch rather than discovered at landing. Phase 2 moves accept sets, so the regenerated JSON-schema / authorable-surface artifacts will move with them. packages/spec/dropped-refinements.baseline.json is already held by #19373 AND #19335, and this seat's own #19297 is expected to add an entry there too. All of it is the recomputable class: whoever lands second re-runs the spec build and re-applies the printed delta. ⛔ Not a semantic collision, ⛔ not a reason to defer — but ⛔ also not to be hand-guessed.

    Why this is claimed now and not left as 「提案 + 升维」

    Seat 2's note 5723612289 and seat 3's read-only round both correctly refused to BUILD this card — at the time it had no ruling, and every canonicalisation direction moves a published accept set. That condition is gone: ruling 5749112309 (batch #194 item 2 · letter A · maintainer 「同意」) picks the canon, and the measurement report 5736025846 is named by the ruling as the specification. ⇒ this is now a ruling-implementation card, ⛔ not a measurement round.

    Stamp: 2026-09-22T01:16Z


    Generated by Claude Code

  3. os-justin commented on Sep 22, 2026

    @os-justin
    Collaborator

    Dispatch backend recorded — cloud session id, so this card is recoverable without this seat

    domain:spec seat 5, session_01Sfe5YjBLwB9J3y8fvm2xq1, 2026-09-22T01:18Z. Addendum to claim 5769855382; ⛔ no state change, ⛔ no label written.

    backend mode:cloud (create_session, ⛔ not create_trigger+fire)
    dev session session_014aHq2gi6yvckJhDVbCUuYJ
    parent (this seat) session_01Sfe5YjBLwB9J3y8fvm2xq1
    model claude-opus-5
    branch claude/issue-18697-semver-canon

    Recorded here because a cloud dev outlives nothing if its id lives only in the dispatching seat's memory: 「状态只经 GitHub 标签、assignee、正文行与座位贴读写;循环须能从全新会话恢复」. Whoever inherits this seat probes that session id first, ⛔ and does not judge the dev dead from silence alone — the archiving obligation on a cloud card is this seat's.


    Generated by Claude Code

  4. os-justin commented on Sep 22, 2026

    @os-justin
    Collaborator

    os-dev-report

    {
      "issue": 18697,
      "status": "done",
      "branch": "claude/issue-18697-semver-canon",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/19637",
      "session": "session_014aHq2gi6yvckJhDVbCUuYJ",
      "premise_still_valid": true,
      "premise_note": "Valid but MOVED, and the move is the headline. PM assumption 3 falsified in the intended direction: PHASE 1 HAD ALREADY LANDED as PR #19478 (2cac3636, the PHASE-1 stamp declared under this block) — packages/spec/src/kernel/version-grammar.ts exports three constants and all eight in-repo regex literals already reference them. So this PR is phase 2 ALONE, not the two-commit shape the suggested route described. Blocker re-verified on my tree: 097d2685 IS an ancestor of origin/main, exit 0 (self-proving, so shallowness does not weaken it); a control commit at depth 400 also read exit 0. The clone was shallow at 50 commits and was deepened, then --unshallow'd for a gate whose fixture is pinned to a commit.",
      "summary": "Phase 2 of the version canon: @objectstack/spec/kernel now exports ONE grammar, SEMVER_2_0_0_VERSION_PATTERN (semver.org's published expression without named capture groups), and all nine in-repo carriers reference it. The three phase-1 constants are retired because each named an accept set that no longer exists — keeping them would leave two names actively lying (MAJOR_MINOR_PATCH accepting prereleases, LOWERCASE accepting uppercase), which is the class-(b) defect this card closes. Prose moved with the grammar: every .describe(), nine generated reference-doc rows, the PATCH door refusal, and manifest.test.ts's 'should enforce semantic versioning' case which stops listing 1.0.0-beta as invalid. PluginLoader.isSemverShapedVersion becomes isSemverVersion. Four ADR-0087 D3 semantic entries under protocol 18; changeset minor with the BREAKING banner and Clause-②: yes (narrowing). Draft PR, left draft.",
      "carrier_count_rederived": {
        "count": "9 in-repo carriers (10 total; the tenth is objectui's, out of this repository's reach)",
        "query": "git grep -n 'SEMVER_2_0_0_VERSION_PATTERN' -- 'packages/*/src/**' 'packages/*/*/src/**' | grep -v '\\.test\\.ts' | grep -v 'version-grammar.ts'",
        "carriers": [
          "ManifestSchema.version — packages/spec/src/kernel/manifest.zod.ts",
          "MetadataPluginManifestSchema.version — packages/spec/src/kernel/metadata-plugin.zod.ts",
          "PluginRegistryEntrySchema.version — packages/spec/src/kernel/plugin-registry.zod.ts",
          "PluginMetadataSchema.version — packages/spec/src/kernel/plugin-validator.zod.ts",
          "PluginSchema.version — packages/spec/src/kernel/plugin.zod.ts",
          "PackageManifestSchema.version — packages/spec/src/marketplace/package-version.zod.ts",
          "PackageVersionSchema.version — packages/spec/src/marketplace/package-version.zod.ts",
          "PluginLoader.isSemverVersion — packages/core/src/plugin-loader.ts (boot path)",
          "PATCH /api/v1/packages/:id — packages/runtime/src/domains/packages.ts"
        ],
        "not_a_tenth_declaration": "CreatePackageVersionRequestSchema.version is PackageVersionSchema.shape.version — a reference.",
        "lit_control": "git grep -n -E '\\\\d\\+\\\\\\.\\\\d\\+\\\\\\.\\\\d\\+' over the same paths, non-test: 4 hits, ALL four the concepts the measurement report bounded out by name — engines.platform (a RANGE), the npm dependency-prefix test and the docker image tag pin in packages/create-objectstack, and one plugin.zod.ts comment quoting the pre-#16365 regex as history. ZERO residual carriers.",
        "line_pointers": "Every carrier located BY CONTENT. The card's pointers had drifted again: manifest.zod.ts:322 -> :415 -> :426 across this PR's own commits."
      },
      "per_carrier_accept_set_delta": {
        "G1 family (ManifestSchema + MetadataPluginManifest + PluginRegistryEntry + PluginMetadata + the PATCH door)": {
          "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"]
        },
        "G2 (PluginSchema.version + the boot path)": {
          "starts_accepting": [],
          "starts_refusing": ["01.1.1", "1.01.1", "1.1.01", "1.0.0-0123", "1.0.0-alpha..1", "1.0.0-alpha..", "1.0.0-.", "1.0.0+."]
        },
        "G3 (PackageVersionSchema.version)": {
          "starts_accepting": ["1.0.0-Beta.1", "1.0.0+Build.5"],
          "starts_refusing": ["01.1.1", "1.01.1", "1.1.01", "1.0.0-0123", "1.0.0-alpha..1", "1.0.0-alpha..", "1.0.0-.", "1.0.0+."]
        },
        "G4 (PackageManifestSchema.version, was a 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.01.1", "1.1.01", "1.0.0-0123", "1.0.0-alpha..1", "1.0.0-alpha..", "1.0.0-.", "1.0.0+."]
        },
        "matches_the_ruling": "This is the measurement report's option-A table, reproduced. The #16365 fence HOLDS and is pinned, not claimed: plugin-loader.test.ts gains a 16-string case asserting every valid prerelease and build form the wider grammar admitted still LOADS, and plugin.test.ts keeps the same list on the schema side. No valid prerelease the loader accepts today is refused."
      },
      "gates": {
        "derived": 114,
        "derivation_command": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack",
        "run": 114,
        "exit_0": 114,
        "first_pass": "104 of 114 at exit 0; 10 non-zero, ALL prerequisite-not-met after later source edits staled packages/spec/dist — 9 at exit 3 (the gates' own PREREQUISITE NOT MET code, 'nothing was measured') plus check:generated at exit 1 naming api-surface stale. Read as NOT MEASURED, never as red.",
        "after_full_build": "pnpm build (VERDICT command-exit 0), then all 10 re-run: 10 of 10 at exit 0. check-plugin-teardown-shape --self-test additionally needed the clone deepened — it refuses on a shallow checkout because its positive control is pinned to a commit.",
        "exit_codes_recorded": "Every exit code captured into a TSV before any pipe, via `eval \"$cmd\" > log 2>&1; EXIT=$?`. No verdict read from a bare $? after a pipe.",
        "reconciliation_verdict": "Run reconciliation — 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN. The tool's own caveat, reported rather than dropped: --ran takes a command list with no exit codes, so it prints '⛔ CLAIMED — 114 of 114 recorded no exit code, so this zero is what the runner declared, not what the record shows'. My record is the TSV.",
        "gate_list_re_derived_at_final_head": "IDENTICAL to the list run — no family appeared or left between derivation and final HEAD.",
        "outside_the_derivation_and_declared_not_covered": "The tool names these as outside the 114: 49 artifact-roster families, 10 declared-wide-population families, 7 families taking a value from the workflow, and 6 path-scheduled CI jobs (Test Core, Temporal Conformance, Dogfood Regression Gate, Dogfood Verify CLI, Build Core, Build Docs). NOT MEASURED locally, by the tool's own declaration — CI owns them."
      },
      "tests": {
        "packages/spec": "14906 passed / 2 failed (509 files) BEFORE regeneration; both failures were export-surface artifacts (scripts/export-origins.test.ts, src/automation/state-machine.test.ts) naming the three retired constants. After `check:generated --fix`, check:api-surface / check:export-origins / check:docs all green.",
        "consumer_suites_ran": "Required by the spec lane charter and named individually: @objectstack/core 1337 passed (51 files) · @objectstack/metadata 792 passed (53 files) · @objectstack/objectql 5050 passed (303 files) · @objectstack/rest 3254 passed + 1 skipped (194 files) · @objectstack/runtime 3796 passed / 3 failed (272 files). The sweep is what found the consumer fixture asserting the other way — a run inside packages/spec alone could not have seen it.",
        "consumer_fixture_that_asserted_the_other_way": "packages/runtime/src/domains/packages-install-manifest-version.test.ts §0. It was WRITTEN to go red here: 'If the version-grammar canon moves, THIS is the assertion that goes red... only this fixture needs re-choosing.' It pinned 2.0.0-beta.1 as refused BY THE DECLARATION; the declaration now accepts it. Fixture re-chosen to 01.1.1 — still version-shaped, still refused on the grammar alone, so every claim that file makes about the DOOR is unchanged. Nothing about the door was weakened.",
        "verify_lock": "Every heavy run through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-18697, verdicts read from the wrapper's `VERDICT command-exit` line. ⚠️ One divergence worth naming: the consumer sweep was moved to the background by the harness, whose completion summary said 'exit code 0' while the wrapper's own VERDICT line read 1. The VERDICT line is the authority and is what I acted on.",
        "ablation": "Cross-package reverse verification, both legs symmetric, through scripts/ablation-replace.mjs (never the -i family). MUTATION: canon regex -> the pre-canon boot-path grammar in packages/spec/src/kernel/version-grammar.ts. On-disk proof: anchor hits 1 -> 0, injected text 0 -> 1, blob 3cb7d178a3b7 -> 119d148bf45c. Then `pnpm --filter @objectstack/spec build` (exit 0) and `ablation-dist-preflight @objectstack/spec` (exit 0 — the mutation REACHED dist, so the observation is about the rebuilt artifact and not a cache). Result: @objectstack/core's plugin-loader.test.ts + plugin-contract-enforcement.test.ts 24 failed / 77 passed, and the 24 are exactly the eight-form refusal rows on both the loader and schema halves. RESTORE: blob back to 3cb7d178a3b7 == HEAD with `git diff HEAD` empty, rebuild, preflight --absent confirmed the marker gone from dist, suites 101/101 passed. Direction predicted before running and observed as predicted: RED.",
        "lint_narrowing_three_pieces": "① Population read from eslint's own config, not guessed: eslint.config.mjs declares files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'] plus six narrower blocks. ② File count read from --format json: 21 files linted, 0 errors, 0 warnings, exit 0, over the 21 lintable files of my diff. ③ Invariance: type-aware linting is NOT enabled — there is no parserOptions.project anywhere in eslint.config.mjs (the file says so itself at :328, 'no parserOptions.project, no typed @typescript-eslint rules'), so my diff cannot move the verdict on any untouched file. Union run taken at final commit 60e673666.",
        "not_measured": "objectstack-ai/cloud (not mounted, out of repo scope) · objectui (not mounted; the sibling clone under /home/user/objectui does not exist in this session) · the 6 path-scheduled CI jobs and the roster/wide-population families named above."
      },
      "generated_artifacts": {
        "stale_and_regenerated": "check:generated reported exactly 3 of 15 stale — api-surface/, export-origins/, content/docs/references/** — and --fix regenerated those 3 from a dist it built. api-surface delta is exactly 3 names out, 1 in.",
        "did_not_move_and_why": "packages/spec/json-schema/** is GITIGNORED and its tracked roster (json-schema.manifest/) records schema NAMES only, so no tracked JSON Schema artifact moves even though the published pattern did change (verified present in the generated kernel/Manifest.json). dropped-refinements.baseline.json is untouched — the contention the dispatch named does NOT bite, because this change is a .regex() and that ledger tracks .refine() projection gaps. No os-regen-merge was needed: no merge of origin/main was performed.",
        "adr_0087_projections": "spec-changes.json and docs/protocol-upgrade-guide.md are byte-unchanged after gen:spec-changes / gen:upgrade-guide, and that is CORRECT rather than a missed regeneration. Measured with a lit control in both directions: no protocol-18 semantic entry reaches either projection — manifest-id-reverse-domain-required, metadata-item-name-grammar-enforced and automation-runs-cursor-retired are each in registry.ts and absent from both — while the protocol-17 entry actor-user-roles-to-positions appears 14x and 7x in them."
      },
      "adr_0087": {
        "entries_registered": ["manifest-version-semver-2-0-0", "plugin-version-semver-2-0-0", "package-version-row-semver-2-0-0", "package-manifest-version-grammar-enforced"],
        "deviation_from_the_ruling_letter": "The ruling asked for entries on the G2 and G4 narrowings. G1 also narrows (01.1.1) and G3 also narrows (the eight), and the ledger is the only notification channel a narrowing has, so all four surfaces are registered rather than the named two. More complete than the floor, never less.",
        "changeset": ".changeset/18697-version-grammar-canon-semver.md — minor on spec/core/runtime, BREAKING banner, `Clause-②: yes (narrowing)`, the adr-0087 registered marker naming all four ids, and per-carrier FROM -> TO migration text including the three retired imports."
      },
      "objectui_pin_risk_measured_not_assumed": "Retiring three published exports could have turned the Console Pin Gate red — that job builds objectui at .objectui-sha against THIS tree's spec via OBJECTSTACK_SPEC_DIST, so an objectui import of a removed symbol would break main. objectui is not mounted here, so the question was answered locally and decisively by dates: .objectui-sha was last moved at the PIN stamp declared under this block (fbc12be3), and PR #19478 landed the three constants at the PHASE-1 stamp declared under this block — three hours and nineteen minutes LATER. No source at that pin can reference a symbol that did not exist when it was written.",
      "out_of_repository_named_in_the_pr_body": "cloud's publish route follows through its pin, as the ruling directs; its accept set moves and the subset relation it was built on disappears because the disagreement it protected against is gone. objectui is the tenth carrier — PackageFormDialog.tsx's hand-copied VERSION_RE, package-schema.ts's z.toJSONSchema(ManifestSchema), and PackageFormDialog.helpTextI18n.test.tsx which pins this repo's .describe() text VERBATIM and therefore goes red until the objectui card lands. ⛔ Not attempted; the ruling assigns it to the spec seat at this PR's ACCEPT.",
      "labels": "ZERO label writes. The dispatch named no labels and the skip-changeset criterion does not apply (this PR publishes @objectstack/spec, @objectstack/core and @objectstack/runtime, and carries a changeset), so the scope is empty. ⛔ needs:contract-review not hung, not removed, not waited on — it is the seat's. READINGS, as asked: the PR carries NO labels at all; `node scripts/pm/check-clause2-carriers.mjs --pair 19637` exits 4, row C1 — `needs:contract-review` is on card #18697 while the delivering open PR #19637 does not carry it. That row is report-only by its own text.",
      "clause_2": "Declared `Clause-②: yes` on the card's claim and `Clause-②: yes (narrowing)` in both the PR body and the changeset. An at-tier contract review is owed BEFORE enqueue; the PM arranges it.",
      "landing_posture": "PR opened as a DRAFT and left draft. ⛔ Not flipped ready, ⛔ no auto-merge, ⛔ not enqueued, ⛔ not merged. Diff touches NO governed surface (no docs/adr/**, no .claude/**, no skills/**, no AGENTS.md, no CLAUDE.md); 33 files, 1331 changed lines, under the 5000-line human-merge threshold.",
      "mcp_calls": "0 — no MCP tool was called, read or write. Card, comments and PR read and written through the REST proxy with curl and GITHUB_TOKEN.",
      "api_writes": "2 — POST /repos/objectstack-ai/objectstack/pulls (HTTP 201, draft) and POST /repos/objectstack-ai/objectstack/issues/18697/comments (this report). Plus 3 `git push` to claude/issue-18697-semver-canon and the initial empty-branch probe push. ⛔ No PATCH of the PR body: it was written once, on creation, and read back byte-identical (10856 stored, one footer, session-URL form).",
      "claim_check": "Verified: the newest `Claim:` on #18697 is 5769855382, naming branch claude/issue-18697-semver-canon. Comment 5769866717 is an addendum, not a claim. ⛔ No second Claim posted; ⛔ assignee untouched.",
      "deviations": [
        "PHASE 1 ALREADY LANDED — the dispatch's suggested two-commit route (phase 1 neutral, then phase 2) does not apply. PR #19478 landed it the PHASE-1 stamp declared under this block, after the card, the measurement report AND the ruling were all written. This PR is phase 2 alone, in three commits: the grammar and its carriers, the regenerated artifacts, the ADR-0087 ledger and changeset.",
        "THE THREE PHASE-1 CONSTANTS ARE RETIRED, not repointed. The ruling says 'the constant becomes SemVer 2.0.0' (singular), written expecting phase 1 to have produced ONE constant; it produced three, because the accept sets genuinely differed. After phase 2 they are equal, so keeping three names would leave two of them false — MAJOR_MINOR_PATCH accepting prereleases, LOWERCASE accepting uppercase — which is the exact class-(b) defect this card closes. Removing published exports is a breaking change, so it carries its import migration in the changeset and the pin risk was measured (above) rather than assumed.",
        "FOUR ADR-0087 entries where the ruling named two (see adr_0087.deviation_from_the_ruling_letter).",
        "The clone arrived SHALLOW at 50 commits. Deepened to 550, then --unshallow'd (14550 commits) for check-plugin-teardown-shape, whose positive-control fixture is pinned to a commit and which refuses rather than answering wrongly on a shallow tree.",
        "The gate sweep and the consumer sweep each exceeded the container's ~10 minute foreground cap and were moved to the background by the harness. Both were waited out IN-ROUND with a foreground blocking wait and their real outputs read; neither was left running."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed: packages/spec/src/contracts/plugin-validator.test.ts declares its own `validateVersion: (version) => /^\\d+\\.\\d+\\.\\d+$/.test(version)` as a test double implementing an interface. It is a stub, not a carrier of the concept, and is deliberately left alone. Recorded in the PR's Acceptance notes. Successor: whoever next edits that contract's fakes.",
        "noted, not filed: the install-door residual the measurement round already filed as class (b) — POST /api/v1/packages answering 201 to a manifest with no version at all — is untouched by this card and remains open. Successor: that finding's own card; packages-install-manifest-version.test.ts closed only the version leg and fences the other four by name."
      ]
    }

    Written 2026-09-22T02:39Z — dev round for the domain:spec seat 5 dispatch, under claim 5769855382. Draft PR #19637 is left draft; landing is the PM's.

    The two stamps the report above refers to by name are readings taken from this tree's git history with git log --format=%ci, not this act's clock, and are declared here rather than typed inside the quotation: the PHASE-1 stamp is 2026-09-21T03:37:40Z, when PR #19478 landed phase 1; the PIN stamp is 2026-09-21T00:18:44Z, when .objectui-sha was last moved. Their ORDER — the pin moved first, the constants landed three hours and nineteen minutes later — is the whole of the Console Pin Gate argument for retiring three published exports.


    Generated by Claude Code

  5. os-justin commented on Sep 22, 2026

    @os-justin
    Collaborator

    ACCEPT — PR #19637. domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1), R1, 2026-09-22T03:12Z. Verified against GitHub, the branch source and the changeset; ⛔ not against the report's narrative.

    Form and scope. draft: true · base: main · head 60e673666 · 33 files computed from the merge-base 0b4022b41 (⛔ not the two-dot form, which mixes in main's own drift), matching the API's changed_files. Body line 1 Fixes #18697, Clause-②: yes, 速读 draft and Acceptance notes present.

    Spot readings I took myself:

    • The retirement is real: version-grammar.ts exports three constants on origin/main and one (SEMVER_2_0_0_VERSION_PATTERN) on the branch.
    • Four ADR-0087 semantic entries present in the diff, where the ruling named two — G1 and G3 also narrow, so this is more complete than the floor.
    • Changeset: minor on spec/core/runtime with an explicit **BREAKING** banner, per-carrier FROM → TO, and the retired imports given their replacement line.
    • CI green after one re-run: 39 runs, 34 success, 5 skipped, zero non-green.

    Contract review: taken in-seat at tier, recorded at PR comment 5770666558. VERDICT: PASS.

    The one judgement worth your eye — the ruling says 「the constant becomes SemVer 2.0.0」 (singular) because it was written expecting phase 1 to yield ONE constant; it yielded three. After phase 2 they are equal, so keeping three names would be two aliases for one grammar — which the standing 阶段姿态 ruling forbids (「废弃别名/拼写…默认立即退休,不留双拼写宽限」). The dev retired them and argued it in the changeset; I adopt that reading. ⚠️ It is still a removal of exports published very recently, so it is flagged rather than buried.

    Premise falsified in the intended direction: this seat's order assumed phase 1 was still to be built; it had already landed as PR #19478. The dev reported the move instead of forcing the two-commit shape I suggested.

    Owed at this ACCEPT, and filed now: the ruling assigns the objectui half to this seat at this point. objectui is the tenth carrier — PackageFormDialog.tsx's hand-copied regex, package-schema.ts's z.toJSONSchema(ManifestSchema), and PackageFormDialog.helpTextI18n.test.tsx, which pins this repo's .describe() text verbatim and therefore reddens once objectui picks this up. That card carries a Blocked-by: on this PR.

    Landing: the diff touches no governed surface (no docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md) and is far under the 5000-line human-merge threshold ⇒ landing is this seat's once the enqueue check passes. ⛔ Not flipped ready in the same act as this reading.


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    The objectui half has landed. objectstack-ai/objectui#10207 is closed by PR objectstack-ai/objectui#10329, merged to objectui main as c84221daa. Reported by the domain:spec @ objectui seat, session session_01877XiBYSaRCk2CU7cMSg3S. ⛔ This comment changes no label or state on this card; that stays with this lane's seat.

    What landed, verified by content on objectui origin/main c84221daa:

    • PackageFormDialog.tsx: the hand-copied VERSION_RE = /^\d+\.\d+\.\d+$/ is gone. It now reads 0 hits on main. versionOk asks the installed spec's ManifestSchema.shape.version.safeParse, via isSpecPackageVersion at :50 and :241-242. The dialog therefore follows whatever grammar @objectstack/spec declares, and it picks up SemVer 2.0.0 automatically once a release carries objectstack#19637.
    • PackageFormDialog.helpTextI18n.test.tsx: the verbatim Package version (semantic versioning) pin now reads the expected text from ManifestSchema.shape.version.description. It no longer fails by construction when feat(spec)!: the canon for a package version is SemVer 2.0.0 — nine carriers, one grammar #19637 changes the describe string.
    • On the installed 17.4.0 nothing changes: the accept sets are identical over every sample.

    Unlock criterion of this card (「本卡落地且 objectstack 的 .objectui-sha 覆盖它」). The objectui half is now satisfied. The remaining step is on this side: bump .objectui-sha to c84221daa or later, so that the Console Pin Gate builds an objectui that carries this change. After that, PR #19637 can merge without turning that gate red on main.


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    Contributor

    Blocker re-derived: the objectui half landed; the wait is now the console pin bump · 2026-09-25T02:11Z

    Acting on the maintainer's instruction. Provenance: who — the maintainer; verbatim — 「D 组:真的需要你拍板(2 张) 给我具体解释,其他同意。」, approving this session's per-card proposal for this card; where — the maintainer's chat with session session_013RWUA7bNq5bRhehLPqXwMg, 2026-09-25. ⛔ Not a claim; the assignee and PR #19637 stay with the domain:spec seat.

    Blocked-by: #20029


    Generated by Claude Code

  8. 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

  9. 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

  10. 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

  11. 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

  12. 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