Skip to content

lint: no reference-integrity or option-key validation for app metadata #3583

Description

@yinlianghui

A full audit of HotCRM (objectstack-ai/hotcrm, ~18k lines of metadata) found ~20 shipped instances of one bug class — metadata referencing something that doesn't exist — all of which pass objectstack validate and objectstack lint cleanly and fail silently at runtime:

  • object: 'user' in a bulk-action lookup and four dashboard filters (the platform object is sys_user)
  • App navigation targeting sys_approval_process, which @objectstack/plugin-approvals never registers
  • bulkActions: ['mass_update', 'mass_delete', 'assign_owner'] — no such actions defined
  • Page headers and KPI cards referencing fields that don't exist on the object ({account}, total_revenue, …)
  • A hook filtering crm_lead on a campaign field that doesn't exist; another hook writing contact_email/contact_phone to an object without those fields
  • Agent skills listing 11 tools with no definitions, a hand-off to a nonexistent skill, and agents referencing knowledge indexes defined nowhere
  • Chart yAxis naming raw fields instead of dataset measures
  • Navigation-exposed objects (crm_forecast, crm_knowledge_article) granted by no profile — permission-denied for every user including admin
  • Translation bundles keyed to nonexistent fields (assigned_to, budget, image_url, …) and select-option translations keyed by display label or by values that aren't in the field's option list (direct-mail vs direct_mail, planned vs planning)

The existing lint already checks some neighboring classes (validate-dashboard-action-refs, validate-widget-bindings, validate-flow-template-paths, validate-capability-references), but none of the categories above are covered. HotCRM comments document earlier instances of the same class being found and fixed one at a time.

Activity

  1. self-assigned this
    on Jul 27, 2026
  2. os-zhuang commented on Jul 28, 2026

    @os-zhuang
    Contributor

    收口说明 —— 9 类问题里 8 类已有门禁,第 9 类是明确接受并已标注的缺口。完整残留审计见 docs/audits/2026-07-app-metadata-reference-integrity-assessment.md §6a。

    落地

    PR 内容
    #3640 方案文档
    #3657 Phase 0 四处小修 + Phase 1 共享基座(PLATFORM_PROVIDED_OBJECT_NAMES 注册表,取代 sys_ 前缀猜测)+ R1 validate-object-references + R2 validate-action-name-refs
    #3684 R3 validate-page-field-bindings + R4 validate-chart-bindings
    #3698 R5 validate-nav-access
    #3716 D4:hook body 写入集接受为静态不可检查缺口并写入作者界面文档
    #3806 R6 validate-translation-references(标题里的 option-key validation)
    #3818 D5 单一入口 validateReferenceIntegrity + §6.4 残留审计

    残留审计(hotcrm @ 8b28fa2,v2.2.2,~18k 行已发布元数据)

    整套规则跑出 23 条 finding,未见误报,逐条对上本 issue 原文:

    规则 条数 对应类别
    object-reference-unknown 4 四个仪表盘 optionsFrom.object: 'user'
    action-name-undefined 5 mass_update / mass_delete / assign_owner + edit / delete
    chart-measure-unknown 4 报表 yAxis 写原始字段而非 measure
    translation-target-unknown 6 apps.crm_enterprise.navigation.group_products / .group_analytics × 3 locale
    object-reference-unregistered-platform 2 导航 sys_approval_process
    nav-object-ungranted 2 crm_knowledge_article、crm_forecast 无权限集授予

    页面字段绑定与 hook 条件两类报 0,且这个 0 经过验证不是空转(往 record:highlights 注入假字段,页面规则 0 → 3)。

    一个值得记录的教训:R6 在真实语料上暴露出一个误报类(~40 条 _views 键),根因是 view record 是容器、对象绑定在 list.data.object 而非记录根。示例应用漏掉它,是因为它们的 bundle 一条 _views 键都没有 —— 误报地板必须按分支读,不能按规则读。已在合并前修复并补回归测试。

    明确接受的缺口

    • hook body 写入集:不可静态分析(写入集在不透明 JS 里)。已在 content/docs/automation/hook-bodies.mdx 与 ScriptBodySchema TSDoc 标注。
    • skill 交接:hand-off 没有任何 schema 字段,活在自由文本提示词里 —— 无结构可查(方案 §7 非目标)。
    • agent.knowledge.indexes:在 stack 里没有定义位,天然不可解析;给无定义位的命名空间写规则会把缺口制度化。需先做 spec 决策 D1。
    • R8(其他元数据里的选项值字面量):Tier-B 候选,误报面大,需先出验证说明再考虑成为规则。

    后续

    唯一还能建的部分拆成 #3820(R7 validate-ai-references):HotCRM 声明了 8 条 agent→skill、16 条 skill→tool 引用,而该 stack 声明的 tool 数为 0 —— 16 条全是死引用,正是本 issue 原文报告的那类。它需要先定 D1 与 D2(都是 spec 决策而非 lint 决策),所以独立跟踪。

    有了 D5 的单一入口,R7 落地时只需在 REFERENCE_INTEGRITY_RULES 里加一行,validate / lint / compile 三条路径自动生效。


    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

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions