Repository navigation
Description
Out-of-scope by-product of the skills optimization flight on #14305 (audit finding "incidental falsehood 3"). Filed unassigned for triage — no doc edit was made for it, because the contradiction is between the skill's advice and shipped platform code, and which side moves is a ruling, not a rewrite.
The two sides
The published skill (unchanged by the flight, both before and after):
data_chattoaskandmetadata_assistanttobuildresolve through the alias table for old bookmarks and persistedagent_ids; they are not vocabulary — always writeask/build.
The platform's own shipped app — packages/platform-objects/src/apps/studio.app.ts:50:
// Studio is the metadata-authoring host, so its ambient copilot is
// pinned to the schema-architect agent. Resolved by the ambient chat
// endpoint via `app.defaultAgent` — no UI-side `?agent=` override
// needed. Every other app falls back to the data-query agent.
defaultAgent: 'metadata_assistant',Measured at origin/main d16df74: this is the only app.defaultAgent usage in the repo (the audit's real-usage census found 1). So the single live example of the key an author would copy spells the alias the catalog tells them never to write.
Why it needs grading rather than a patch
Three readings, and they lead to different edits:
- The code is residue. Studio should be re-pinned to
'build', and the alias table keeps working for persistedagent_ids only. Cheapest, and makes the one live example match the advice. - The alias is load-bearing here. If the cloud AI-Studio plugin registers under the legacy id and the alias resolves at read time, re-pinning is a behaviour change in a repo that cannot see the consumer (
service-ai-studiolives in the closedcloudrepo). Then the doc advice is right for third parties and the platform pin is a deliberate internal exception that should say so in a comment. - Neither. The retired-spelling ledger should simply carry this as a known residue with an owner.
Reading 2 is not disprovable from this repo — which is exactly why this is a finding and not a fix.
Suggested triage inputs
- If reading 1: one-line change in
studio.app.ts, plus whatever pin test asserts the current value. - Either way, the retired-spelling ledger should see the residue (that was the audit's own recommendation).
- Related, both closed, neither a duplicate:
skills/objectstack-ui/SKILL.md的 App 例子仍教defaultAgent: 'sales_copilot'—— ADR-0063 之后该绑定解析不到任何 agent #5985 (a skill example teaching adefaultAgentthat resolves to nothing) and [决策] 是否给app.defaultAgent的取值加 lint(平台 agent 名单外即 warning)—— #5985 实测门禁对该类缺陷结构性失明 #6041 (the decision on whether to lintapp.defaultAgentvalues against the platform agent list —skills/objectstack-ui/SKILL.md的 App 例子仍教defaultAgent: 'sales_copilot'—— ADR-0063 之后该绑定解析不到任何 agent #5985 measured that the gates are structurally blind to this defect class). If reading 1 wins, [决策] 是否给app.defaultAgent的取值加 lint(平台 agent 名单外即 warning)—— #5985 实测门禁对该类缺陷结构性失明 #6041's lint is what would have caught it.
Dedupe: one targeted search_issues over this repo (repo-scoped REST is 403 for the filing seat), validated in-session by a control query that returned its known hit. Nothing open covers this.
Triage addendum — there is a THIRD side, and it is maintainer-ruled
Measured at origin/main ed44512. Two corrections to the card above, both material to whichever way this is decided.
(a) #6041's lint landed, and it passes this value by design. The card's closing line — "If reading 1 wins, #6041's lint is what would have caught it" — does not hold. The rule shipped as packages/lint/src/validate-ai-agent-authoring.ts, and its docblock at :30-45 records the outcome verbatim: "the maintainer ruling on #6041 (2026-08-07, reaffirmed 2026-08-09) is option A: add the value check at warning tier, reusing PLATFORM_AGENT_NAMES rather than narrowing the schema to an enum". And :91 is
const PLATFORM_AGENT_NAMES = new Set(['ask', 'build', 'data_chat', 'metadata_assistant']);with :139-140 short-circuiting on membership. So defaultAgent: 'metadata_assistant' is not merely unlinted — it is deliberately accepted, under a ruling, with a docblock at :83-90 explaining that all four names are legitimate references to a platform record "directly or through its alias".
⇒ The platform has treated the alias as a legal authored value twice: once in the Studio pin, once in a maintainer-ruled gate roster. The published skill is the lone dissenter. Reading 1 is therefore not a one-line change plus a pin test; it is a two-site change that reopens #6041's ruling.
(b) Reading 2's unfalsifiability is now documented in-repo, not just suspected. validate-ai-agent-authoring.ts:85 states the aliases are "registered via the cloud alias registry — ADR-0063 §2". So alias resolution is confirmed to be a cloud-side mechanism this repo cannot inspect. That does not prove service-ai-studio registers under the legacy id, but it does establish that the question the card flagged as undecidable really is undecidable from here.
Why this is needs-user-decision and not dispatchable
Every limb is above the seat. Editing published skill text is a change to the authoring contract every agent and third party reads; skills changes are ADR-class and run through the dedicated skills seat with the maintainer. Narrowing the lint roster reopens a standing maintainer ruling. And reading 2 turns on a closed repo. The seat can measure the sides — it cannot pick one.
<!-- os-decision-facets -->
- ① 项目长远合理性(权重 ≥50%,领起推荐) —— 今天一个助手有两个名字,平台要永远同时认这两个名字。别名表是特例,不是词表:让它只兜住旧数据(用户书签里、库里存着的旧
agent_id),它就是一个有终点的搬迁垫片;让它同时当作者写新代码时的合法拼法,它就是一条永久多出来的契约。缩小特例的方向 = 技能手册那句话是对的,两处代码是残留。 - ② 实际业务拉动 —— 今天没有客户撞上:Studio 两种拼法都能跑起来,别名照解。拉动在作者一侧,而且是实测过的 —— 这次技能优化航班就从一份同类过期注释里继承了一处错误([finding]
MCPServerPlugin's own docblock still teaches the deprecated stdio trigger —plugin.ts:112-122disagrees withplugin.ts:234-239twelve lines below it #14473),在合同评审第一轮才被抓住(PR skills(ai): optimization flight — the closed agent surface cut to its retirement rows, the flagship defineSkill example made to resolve, open-edition MCP wiring and the tool registry taught (net −1,315 tokens) #14463)。拉动非零,但不是线上事故级。 - ③ 防 AI 犯错 —— 最响的一棱。一个 AI 作者要给自己的应用钉一个副驾,他会去抄仓里唯一活着的那个例子,于是写下手册明令禁止的拼法,而平台自己的检查器一路绿灯放行 —— 静默容忍。把作者面的可选值收成闭合枚举(
ask/build),同一个错误就变成写代码当场的响亮拒绝。 - ④ 创业阶段不扩散 —— 每一个已声明的键都是永久义务。一个助手维持两个官方拼法,意味着往后每一份文档、每一条 lint、每一次评审都要记得两个名字都合法。remove 优于 declare-and-maintain:收成一个拼法。
推荐:A —— 手册那句话原地不动;studio.app.ts:50 改回 'build';lint 的 defaultAgent 取值那一限用 ask / build(声明-遮蔽那一限继续认全部四个名字,它判的是另一回事)。四棱同向,②有拉动⇒按分歧推荐序荐①的长远终态。
回退:B —— 若 cloud 侧确实按旧 id 注册(读数 2),则手册对第三方是对的,平台这一钉是有意的内部例外,就在 studio.app.ts 写清楚为什么,并把这条残留登进退役拼法台账。
置信缺口(本分析看不见什么): 看不见 cloud 仓。validate-ai-agent-authoring.ts:85 只说别名注册在 cloud 的别名注册表里,没有说 service-ai-studio 用哪个 id 注册 —— 这一条正是能把 A 翻成 B 的唯一变量,本仓无法证伪。另外 A 会重开 #6041 的既有裁决(2026-08-07 裁、08-09 重申),这一点不是本席能代裁的。
Activity
huangyiirene commentedon Sep 2, 2026
Triage attribution marker: the Triage addendum and the four-facet decision block appended to this issue's body were written by me (triage seat) this round. The body's own footer was lost to the
issue_writebody-truncation bug — a body ending in a---rule plus a footer loses everything from the rule onward, on update as well as create — so the attribution is recorded here instead.Generated by Claude Code
hotlong commentedon Sep 3, 2026
Claiming: session
03f8247b-ebbb-4e40-9ffb-8fdb40ebaa7b, branchclaude/skills-metadata-assistant-conflict-55aa6c. Measuring both sides before anything is edited; this carriesneeds-user-decision, so the ruling goes to the maintainer first.Generated by Claude Code
hotlong commentedon Sep 3, 2026
The confidence gap is closed by measurement: reading 2 is falsified
The card and the triage addendum both stop at the same wall — "
validate-ai-agent-authoring.ts:85only says the aliases are registered in cloud's alias registry, it does not say which idservice-ai-studioregisters under, and this repo cannot falsify it." That is true of this repo. It is not true of the seat: thecloudandobjectuicheckouts are readable locally, so the variable that decides A-vs-B was measured directly rather than left open.Measured at
cloudmain@3856fbf7andobjectuimain@e3784607f.service-ai-studioregisters underbuild, not under the legacy idpackages/service-ai-studio/src/agents/metadata-assistant-agent.ts(cloud):and
packages/service-ai-studio/src/plugin.ts:58:The alias table is one-way legacy→canonical and resolution-only —
service-ai/src/agents/agent-aliases.ts:20-23: "Aliases are resolution-only: they are NOT separate metadata records, so the agent list still shows each agent exactly once under its canonical name."⇒ The premise reading 2 rests on — that the cloud plugin registers under
metadata_assistantand re-pinning would be a behaviour change — is false. The record's id isbuild. Studio's current pin reaches it by taking the back-compat detour.'build'also resolves on the UI side — it is the friendly-first memberobjectui/packages/plugin-chatbot/src/agentAliases.ts:23-26groups the names bidirectionally, canonical first:objectui/packages/app-shell/src/hooks/surfaceAgent.ts:82boundsapp.defaultAgentthroughisBuiltinAgentName, which is group membership — so both spellings are accepted identically, then resolved against whichever the live catalog serves. And:51-54already hasSURFACE_DEFAULT['studio-build'] = 'build': the Studio surface wantsbuildwith or without the pin.The re-pin is not cosmetic — it drops a load-order dependency
'metadata_assistant'only resolves ifregisterAgentAliashas actually run. Cloud carries two separate defensive docblocks about that registration silently no-op'ing for real:service-ai-studio/src/plugin.ts:44-57— the alias must be registered with the string constant, neverMETADATA_ASSISTANT_AGENT.name, because under bundle load ordering / a circular import.namereadsundefined, thelegacy && canonicalguard no-ops, "which dropsbuildfrom both the legacy alias AND the framework'slistAgentscatalog". Re-asserted insideinit()as belt-and-suspenders.service-ai/src/agent-runtime.ts:30-41— catalog membership was deliberately moved onto an intrinsic persisted signal so it does "NOT depend on an in-memoryregisterAgentAliascall having run (a missed alias must never hide a real platform agent likebuild)".defaultAgent: 'build'never touches the alias table. So reading 1 is strictly more robust than the status quo, on the platform's own flagship authoring surface — not merely tidier.A third in-repo site the census missed
The card's census found one
app.defaultAgentusage. Correct — butmetadata_assistantis taught as author-facing example vocabulary at a second live site, and it is the louder one because it is served to LLM clients at runtime:packages/mcp/src/mcp-server-runtime.ts:1349— theagent_promptprompt'sagentNameargument description:Both examples are retired aliases; neither canonical id appears. Every MCP client asking what to pass is told the two spellings the skill forbids. This site is uncontroversial under either reading — the aliases resolve, so nothing breaks, but the suggested spelling should be
"ask"/"build".Two corrections to the suggested triage inputs
grep defaultAgent packages/platform-objectsfinds onlystudio.app.ts:48,50— the comment and the value. Reading 1's first limb is a one-line change plus its comment; there is no assertion to update.packages/spec/src/migrations/entries/retired-keys/(76 entries) is keyed<domain>/<Type>:<key>— e.g.api/AuthFeaturesConfig:magicLink. It records retired keys.metadata_assistantis a retired value of a live key, which that registry has no shape for. The residue has to be recorded where it is read instead — at the pin site and in the lint docblock — or the ledger needs a value-level shape, which is its own card.What is left for the maintainer
Reading 2 is gone and A's evidential blocker with it. Under A the published skill text does not change, so the "skills changes are ADR-class" limb is also moot. The one limb that genuinely remains above the seat is the #6041 half: whether the lint's
defaultAgentvalue limb narrows its roster toask/build(with legacy aliases warned under their own rule id and wording), while the declaration-shadowing limb keeps recognising all four. That refines which roster the value limb reads; it keeps both of the #6041 ruling's operative decisions intact — warning tier, and no Zod enum narrowing. But it is still a change to a gate settled by a standing ruling (2026-08-07, reaffirmed 08-09), so it is being put to the maintainer rather than assumed.Generated by Claude Code
hotlong commentedon Sep 3, 2026
Ruling: A (full). Maintainer, 2026-09-03 — the skill text stands unchanged; the two code sites move, and the third one the census missed moves with them.
PR: #14780
studio.app.ts:50→'build'(+ comment recording the measurement and the load-order reason).mcp-server-runtime.ts:1349→ theagentNameexample now names"ask"/"build"instead of the two retired aliases.validate-ai-agent-authoring→ the value limb takesask/build; a legacy alias warns under a new exported rule iddefault-agent-legacy-aliaswith its own wording (an alias resolves, an unknown name does not — collapsing them would misdescribe both). The declaration limb still reads all four names; that judgement is a different question and is untouched. [决策] 是否给app.defaultAgent的取值加 lint(平台 agent 名单外即 warning)—— #5985 实测门禁对该类缺陷结构性失明 #6041's two operative decisions — warning tier, no Zod enum — both stand.Residue disposition: the retired-keys registry can't carry it (it is keyed on retired keys; this is a retired value of a live key), so it is recorded at the pin site, in the rule's docblock, and in tests that pin the alias/unknown distinction. A value-level ledger shape would be its own card.
Generated by Claude Code
fix(ai): author the canonical agent id everywhere the platform teache…