Skip to content

fix: 单组件根围栏被静默拒绝(根 items 门控与文档词汇表矛盾) - #11

Open
linshule wants to merge 1 commit into
omdsh-dev:mainfrom
linshule:fix/single-component-root-spec
Open

fix: 单组件根围栏被静默拒绝(根 items 门控与文档词汇表矛盾)#11
linshule wants to merge 1 commit into
omdsh-dev:mainfrom
linshule:fix/single-component-root-spec

Conversation

@linshule

Copy link
Copy Markdown

问题

单组件根围栏永远渲染不出来{"type":"callout","tone":"info","title":"…","content":"…"} 这种直接把组件作为围栏主体的写法(注入的围栏词汇表明确允许,SKILL.md/宿主提示均以单组件示例教学)——JSON 完全有效,但渲染器报一次 [dsh-genui] settled dsh-ui fence body does not parse保持代码块

根因

三处入口都强制根节点必须带 items 数组,与文档词汇表(单组件即合法根)矛盾:

  • src/client/parse-partial.tstrySpecisGenuiSpec(value) ? value : null(DOM 通道路径)
  • src/client/spec.tsparseGenuiSpecisGenuiSpec(value) ? value : null
  • src/client/guard.tsrepairGenuiSpec / validateGenuiSpec!Array.isArray(v.items) 直接拒绝

isGenuiSpec 的结构门控 if (!Array.isArray(v.items)) return false 把单组件根挡在解析之外,Sn/Cn 语法修复器帮不上忙(JSON 语法没问题)→ 保持代码块。

修复

新增 wrapSingleComponentRootsrc/client/spec.ts):单组件根自动包裹为 colpanel/append 提升到包裹层(面板路由不受影响)。三条路径统一归一化:

  • parse-partial.ts trySpec → 包裹后返回(DOM 通道立即生效)
  • spec.ts parseGenuiSpec → 包裹后返回
  • guard.ts repairGenuiSpec → 包裹后递归修复;validateGenuiSpec → 包裹后递归校验(validate_dsh_ui 工具与渲染器行为一致)

非组件对象(无 type / 无 items 的杂物)依旧拒绝,行为不回退。

变更文件

文件 内容
src/client/spec.ts wrapSingleComponentRoot + parseGenuiSpec 包裹
src/client/parse-partial.ts trySpec 包裹(DOM 通道路径)
src/client/guard.ts repair/validate 单组件根包裹
tests/genui-guard.spec.ts +7 回归(包裹/panel-append 提升/非组件拒绝/幂等/校验/解析/垃圾拒绝)
tests/genui-partial.spec.tsx +3 回归(DOM 通道解析包裹/提升/拒绝)
lib/client.js 重建产物
CHANGELOG.md 变更记录

验证

  1. 构建:tsc + tsdown 通过
  2. jsdom 端到端(真实 shell MarkdownText + DOM 通道):旧失败的三种形状全部水合——callout(核心观察)、steps(排序分组)、keyvalue(难度)均渲染为卡片
  3. 测试套件:380 项中 +10 新增全绿(guard 7 + partial 3);本地其余失败均为宿主源码树依赖(install-script 需 chmod、skill-md 需宿主 yaml 版本),与本次变更无关,CI(Linux + DSH 源码 checkout)全量运行

复现

任何宿主上发一条单组件围栏:{"type":"callout","tone":"info","title":"T","content":"c"} → 代码块 + 控制台「does not parse」告警(修复后渲染为卡片)。

@taekchef taekchef left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@linshule 单组件根这个 bug 抓得对,文档词汇表和实现门控打架,修复方案(包 col + 提升 panel/append + 三入口统一归一化)也很干净。本地全套 278 passed / 102 skipped 全绿 ✅,测试写得不错。

合之前有两个必改项:

  1. 补完整构建产物:PR 只提交了 lib/client.js,但 src/client/spec.ts / guard.ts 的改动会生成 lib/index.jslib/types/client/{spec,guard}.d.ts。目前本地跑一次 pnpm run build 后这三处都会变——CI 的「lib 与 src 不一致」门会直接挂(PR #9 已经踩了这个坑)。请运行 pnpm run build 并把全部 lib 变更一起提交。

  2. rebase 到最新 main:main 已合入 #14(issue #13 修复),当前分支和 main 在 CHANGELOG.mdlib/client.jstests/dom-fence.spec.tsx 上有冲突。另外本 PR 叠在 #10 的提交上,建议等 #10ctx.inject 改法敲定后一起 rebase;如果 #11 先合,会连 #10 一起带进来。

非阻塞的小观察(不需要改,除非顺手):wrapSingleComponentRoot 对任何带非空 type 字符串的对象都会包成 col,未知 type 会渲染成空 col 而不是保持代码块。目前和嵌套未知节点的既有行为一致,可以先接受。

这两项补齐后我们立刻合,感谢你的两连发 PR 🙏

注入的围栏词汇表把单组件({"type":"callout",...} 直接作根)列为合法
写法,但解析/修复/校验三处入口都强制根节点必须带 items 数组:
JSON 完全有效、渲染器却拒绝 —— DOM 通道报一次「does not parse」
后保持代码块,页面无任何效果。

修复:新增 wrapSingleComponentRoot(spec.ts),单组件根自动包裹为
col(panel/append 提升到包裹层),parsePartialGenuiSpec(DOM 通道)、
repairGenuiSpec(渲染归一化)、validateGenuiSpec(validate_dsh_ui
工具)三条路径统一归一化,渲染器与工具行为一致。

- src/client/spec.ts: wrapSingleComponentRoot + parseGenuiSpec 包裹
- src/client/parse-partial.ts: trySpec 包裹(DOM 通道路径)
- src/client/guard.ts: repair/validate 单组件根包裹
- tests: +10 回归(guard +7 / partial +3)
- lib/client.js: 重建产物
- CHANGELOG.md: 变更记录
@linshule
linshule force-pushed the fix/single-component-root-spec branch from 1a29209 to 3106940 Compare August 15, 2026 23:37
@linshule

Copy link
Copy Markdown
Author

收到,两点都已补齐 ✅

1. 完整构建产物已提交

跑了完整构建(tsc + tsdown)后补齐了缺失的产物,现在 PR 包含全部 lib 变更:

  • lib/client.js(原有)
  • lib/index.js(新增,host 侧重建)
  • lib/types/client/spec.d.tslib/types/client/guard.d.ts(新增,导出签名变化)

commit 3106940(amend 进原提交,rebase 历史保持干净)。

2. rebase 已是最新 main + 不再叠 #10

关于非阻塞观察(未知 type 会包成空 col):接受现状,与嵌套未知节点行为保持一致,暂不改。

本地验证:构建通过、单组件根 jsdom 端到端全绿。可以合了,感谢 🙏

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants