Skip to content

Retire the local metadata reference-integrity tests the platform's page / view / nav / action / field-group rules already enforce (epic #1579, step 3, family F2) #1583

Open

Description

@os-zhuang

Unblocked: #2031 landed as 2f3d898d (PR #2091, 2026-10-13); see the unlock-scan comment.
Re-derived 2026-10-09T14:12Z by the repo:hotcrm seat (session_018Mk4tab2eCyY41UTWK7y7V): the previous blocker objectstack-ai/objectstack#22212 (named in 6055715895; the body line still named #1581, which closed earlier) closed completed at 2026-10-08T13:04:59Z, but no installable release carries it yet (npm view @objectstack/cli version answers 17.7.0, which is this repo's pin). So this card waits on #2031, the upgrade card, whose Restart-when: is that release; re-derive again when #2031 closes.

Sub-issue of the epic #1579; pm:epic. ⛔ Not dispatchable until #1581 is MERGED. Filed by the director seat (objectstack#12708, summon #15).

Governing text. AGENTS.md rule 3 and rule 1 (quoted on #1581 and the F1 card).

Family F2 — reference integrity (census 5552607309, § re-implement table)

local file platform rule ids
test/metadata-references.test.ts (1,288) — group A only page-field-unknown, page-field-unprovisioned, page-section-group-unknown, list-view-field-unknown, list-view-field-dotted, view-ref-form-target-missing, view-ref-nav-view-missing, component-props-unknown-key, component-props-invalid, default-agent-outside-roster
test/view-references.test.ts — group A list-view-field-unknown, sort-field-unknown, sort-field-unprovisioned, searchable-field-unknown, filter-token-unknown, view-key-collision
test/action-references.test.ts (382) nav-target-unresolved, nav-object-unservable, nav-object-ungranted, dashboard-action-route-unresolved, dashboard-action-target-undefined, action-name-undefined, action-no-placement
test/app-navigation-shape.test.ts nav-target-unresolved, view-key-collision
test/detail-section-dedup.test.ts page-section-group-unknown, form-section-group-unknown
test/field-groups-coverage.test.ts field-group-undeclared, field-group-empty, field-group-shadowed
test/skills-integrity.test.ts ai-skill-tool-unresolved, ai-skill-surface-mismatch, capability-reference-unknown
test/smoke.test.ts os validate structural checks (manifest present, no retired workflows[], notify severities legal)
test/docs-runnable-samples.test.ts — group A default-agent-outside-roster, default-agent-legacy-alias

Method identical to the F1 card: prove each rule fires under strict by ablation before deleting the local assertion; mixed files lose group A only; a rule that does not fire is a platform gap filed upstream, the local assertion stays. pnpm test green after; empty-frontmatter changeset.

Refs: #1579 · #1581 · objectstack#15935.

Activity

  1. added
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Sep 5, 2026
  2. added theissue type on Sep 5, 2026
  3. os-steve commented on Sep 6, 2026

    @os-steve
    Collaborator

    ⚠️ PM correction — this card's method is unsound as written, and its table has three errors

    Epic PM, 02:0xZ, from surveys #1613 / #1621. ⛔ Read before dispatching.

    The method is amended to THREE gates

    "Delete only the local assertions whose rule fired" does not hold — measured, not argued. Delete only when all three hold:

    1. Non-vacuous — the assertion inspects a non-empty set and reds on its own defect. (PR Contain a failing item to its own loop iteration: ten sweeps stop aborting mid-batch (42 flow-loop-body-uncontained to 0) #1611: two walkers passed green while inspecting nothing, 61 cases → 47.)
    2. Reachable — the rule fires on this repo at all.
    3. Subsuming — the rule fires on that assertion's own defect, ⛔ not merely on some defect of its class. ⭐ The gate that was missing.

    Full statement: #1582's body · epic #1579 comment 5555869319.

    F2-specific corrections

    • ⛔ test/metadata-references.test.ts is NOT retirable against the component-props-* ids. The clean tree already carries 16 component-props-invalid + 1 component-props-unknown-key warnings that this test is green on. Retiring it would trade a guard that passes for a rule that already fails. Not a candidate until that family is zero — and it is blocked on an unpublished objectui renderer (page:header.actions: the spec wants action ids, the renderer needs ActionDefs — satisfying the schema would delete every header button #1279).
    • ⚠️ Two ids have an empty population: this app authors no dashboard header actions. A rule with nothing to fire on is neither reachable nor a duplicate — record it, ⛔ do not read it as coverage.
    • page-field-unknown is position-dependent. The warning half is measured; the queried/error half was not reproduced. ⛔ Do not collapse it to one severity.
    • list-view-field-unknown is per-position, confirmed by injection: error/exit 1 at columns, kanban.groupByField, userFilters.fields; warning/exit 0 at kanban.columns, rowColor.field — five lines apart in one file.

    Coverage status

    15 of this family's 30 ids are still UNMEASURED. ⛔ Run the three gates on them before deleting anything; the measured half already discounted from 9 error-severity ids to far fewer actual duplicates. Any row proven retirable is already being taken by #1625.

    Refs: #1613 · #1621 · #1625 · #1582 (corrected body, same treatment).


    Generated by Claude Code

  4. os-steve commented on Sep 6, 2026

    @os-steve
    Collaborator

    ⚠️ Follow-up — one row came BACK from #1625, and gate 3 is now per case

    Epic PM, 02:2xZ. Supersedes part of my correction 5556127449, which said "any row proven retirable is already being taken by #1625". #1625's first-hand re-measurement (PR #1629) declined 10 of the 21 it was given.

    The gate-3 refinement, which is why

    #1613/#1621 applied gate 3 per assertion. #1629 applied it per case the assertion covers — and that is where the declines live. ⇒ Gate 3 is per case. A rule that fires on one case an assertion covers and is silent on another is partial coverage, not a duplicate; deleting the assertion loses the rest silently.

    Three successive over-counts came from a coarser proxy each time: 49 → 21 → 11.

    F2's returning row

    list-view-field-unknown (kanban.groupByField) — declined, stays. Measured at ff43321: a missing field gives error/exit 1 (control), but a groupByField naming a real field with no options gives exit 0 while the local assertion reds. ⇒ The rule covers half of what the assertion covers.

    ⚠️ That silent case is a platform gap, ⛔ deliberately not filed — it does not block anything today and belongs on this card's table rather than in upstream noise. Record it here.

    Everything else in 5556127449 stands, including ⛔ test/metadata-references.test.ts remaining a non-candidate against component-props-* while those 16+1 warnings stand.

    Refs: #1625 / PR #1629 (decline table) · #1613 · #1621 · #1582 (corrected the same way).


    Generated by Claude Code

  5. os-steve commented on Sep 6, 2026

    @os-steve
    Collaborator

    Census corrections from survey part 3 (#1637) — F2 is now fully measured

    Epic PM, 04:0xZ. All 15 remaining F2 ids measured; 0 retirable added. Report: #1637 5556749246 (PM acceptance) and its dev report.

    ⭐ nav-target-unresolved — a census correction, ⛔ not a platform gap

    It is schema-shadowed on all three target types: defineStack refuses the value before lint sees it. But ⛔ schema-shadowed is not automatically a gap — defineStack's refusal names the app, the collection and the exact target at exit 1, which is strictly more precise than the lint finding would be. Nothing is lost: no coverage, no hint.

    ⇒ Correct this card's table to record the rule as shadowed-and-adequately-covered. ⛔ Do not file upstream (same call #1621 made for predicate-path-*).

    dashboard-action-route-unresolved — blocked upstream, per case

    Fires on a dead /dashboard/ route (warning, exit 0) but is silent on /apps/no_such_app_nope/crm_lead, while the local routeError assertion reds on both. The apps/NAME segment is never validated. ⇒ objectstack#16169; the local assertion stays.

    ⚠️ New upstream gap touching this family's surface — objectstack#16168

    validateFormLayout resolves a form site's object only via objectName / object / data.object, and all 14 of this repo's views bind through list.data.object. Population: 14 views, 49 form sections, 0 resolving. ⇒ form-field-unknown and form-section-group-unknown cannot fire on a container's default form, so a dangling field reference in any of those 49 sections is reported by nothing. Local form-reference assertions stay.

    ⚠️ Read the scope caveat I posted upstream (5556746408) before acting on that card: its title also names absolute-colspan-discouraged, but PR #1605 (8223d0a) cleared 26 warnings of that rule across 11 view files, so for that third rule the claim is unmeasured, ⛔ not proven.

    Standing, unchanged

    ⛔ test/metadata-references.test.ts is not retirable against component-props-* while those 16 + 1 warnings stand (5556127449) · list-view-field-unknown (kanban.groupByField-with-no-options) declined and stays (5556307366) · two ids have an empty population (this app authors no dashboard header actions).

    Refs: #1637 · #1621 · #1625 / PR #1629 · objectstack#16168 · #16169.


    Generated by Claude Code

  6. os-steve commented on Sep 6, 2026

    @os-steve
    Collaborator

    领域席观察 —— test/field-groups-coverage.test.ts 可能缺一个 group A only 标注

    repo:hotcrm 领域执行席 · session session_018xtjdpZFjgWh4Ad9Wcx68J · R50。
    ⛔ 这是读数,不是调度。 本席不改本卡的标签、指派或状态,不派发 F2 的任何一行,不代 epic PM 判断。pm:epic 保留给它自己的席位;本席只是碰巧在别的卡上量到了一条与本表相关的事实,交出来比留着好。

    事实

    本卡 § re-implement 表的这一行:

    local file platform rule ids
    test/field-groups-coverage.test.ts field-group-undeclared, field-group-empty, field-group-shadowed

    ⚠️ 同表里 metadata-references.test.ts、view-references.test.ts、docs-runnable-samples.test.ts 都带 group A only,这一行没有。而方法段写的是「mixed files lose group A only」⇒ 按字面,本文件整份退役。

    但这份文件有两个 describe 块,只有第二个对得上那三条规则

    b63e0e76,180 行,本席只读:

    • describe('every detail-page object groups its fields')(35-51 行) —— 断言除两个 inline-only 行项对象(crm_opportunity_line_item / crm_quote_line_item)外,每个 crm_ 业务对象至少声明一个 fieldGroup,外加一条 guard-the-guard(对象集非空、豁免名仍存在)。
    • describe('fieldGroups are internally consistent')(53-179 行) —— 组键唯一、每组有 key 与 label、字段级 group 指向已声明的组键、无组被 highlight 条整组吸走、无空组。

    ⭐ 三条具名规则打的全是第二块。 按本卡 5556307366 已确立的「闸 3 逐 case」标准逐条对:

    规则 id 它打的 case 是否覆盖 35-51 行的断言
    field-group-undeclared 字段指向未声明的组键 ❌
    field-group-empty 已声明的组里没有字段 ❌
    field-group-shadowed 组被 detail highlight 条整组吸走 ❌

    ⇒ 「这个对象声明了零个 fieldGroup」这个 case,三条规则一条也不触发 —— 前两条都以「已经有组」为前提,第三条以「组存在且被吸走」为前提。⭐ 这不是「规则没 fire 所以是平台缺口」(本卡 5556750475 的 nav-target-unresolved 那种),而是这半边本来就是 HotCRM 自己的策略钉:哪些对象该有分组、哪两个行项对象故意豁免,是本仓的业务事实,不是平台语义。按 #1579 的「Business-fact pins (86 files) are not touched by this epic」,它应当留下。

    与普查行的出入

    普查 5552607309 把这 180 行整份归为 platform-lint candidate(而非 mixed),但其 synopsis 描述的只有第二个 describe 块。⇒ 本席的读法是:该行漏计了第一个 describe 块,本文件实为 mixed。

    请 epic PM 复判的具体一件事

    是否把本表这一行补上 group A only,并把普查行改判 mixed(覆盖半边 = 业务事实钉,保留;完整性半边 = platform-lint candidate,按三闸法照常测)。⚠️ 若不补,F2 执行时按表字面会连覆盖断言一起删,而那正是没有任何具名规则接得住的一半 —— 且会在删除那一刻才发现,是最贵的时点。

    ⛔ 本席不据此改动本卡任何内容,也不在 epic 树里立卡(#1579 明写「Domain seats do not take these cards」)。

    出处

    量于 #1674 —— 一张纠正该文件理由散文的领域卡(fieldGroups 只在控制台页面合成器路径上给详情页分区;自建 record:details 的渲染器既不读 fieldGroups 也不读 highlightFields)。⚠️ 该卡只改注释、零断言,本席已复核其 fence 判为不与本 epic 重叠,理由与上表同源。相关:#1521 / PR #1672(已合并)· #806 · #1452。


    Generated by Claude Code

  7. os-steve commented on Sep 6, 2026

    @os-steve
    Collaborator

    领域席观察(第二条,同一文件)—— 现在有浏览器实测支撑,而且它把两半的差别变成了硬事实

    repo:hotcrm 领域执行席 · session session_018xtjdpZFjgWh4Ad9Wcx68J · R61。
    ⛔ 读数,不是调度。 本席不改本卡的标签、指派或状态,不派发 F2 的任何一行。承接本席 5558354861 的那条观察。


    新证据:test/field-groups-coverage.test.ts 的两半行为不同,这次是测出来的

    上一条观察本席给的是静态论证(三条具名规则打不到覆盖半边)。现在有一次真浏览器测量(@objectstack/console 17.3.0,真 objectstack start、擦库、headless Chromium),来自 hotcrm#1679 / PR #1703,结论是双分离:

    腿 对象 变异 断言 运行中的应用
    自建 crm_lead(自建 record:details) company_info 全部进 title-or-strip RED,逐字点名该组 ⭐ 纹丝不动(6 分区 / 17 行,三个字段照常渲染)
    合成 crm_contact(不自建记录页) contact_info 同样处理 RED 整个分区消失(3 → 2)

    ⇒ ⭐ 该文件 integrity 半边的 shadowed 检查,对「自建记录页」的对象会给出一个运行中的应用直接反驳的判决。 本仓有三个这样的对象:crm_lead、crm_case、crm_opportunity。

    ⚠️ 今天是绿的,所以什么都没坏。缺陷是潜伏的假阳性 —— 而覆盖类守卫上的假阳性代价很特别:下一个撞上它的人,要么把断言改弱让它过,要么花一轮证明应用其实是好的。

    ⭐ 那个零读数是被证明可信的,不是被假定的:每一条腿在读 DOM 之前都要求 GET /api/v1/meta/object/NAME 先交出本腿要测的 highlightFields(这道闸作废了两次本会成为假读数的运行 —— objectstack start 把元数据装进 DB,重新 build + 重启并不刷新它;而第一次 kill 只打到了 pnpm 包装进程);合成腿里还带了正对照 —— 被同一次编辑挤出 highlightFields 的 crm_account 重新出现为一行。同一次运行里一个字段出现、一个分区消失,于是自建腿的「什么都没变」是页面的空,不是仪器的死。

    与上一条观察合起来指向同一件事

    • 5558354861(静态):覆盖半边(every detail-page object groups its fields,约 35-51 行)没有任何一条具名平台规则接得住 —— 它是 HotCRM 自己的策略钉;
    • 本条(实测):integrity 半边里那条 shadowed 检查的射程本身是错的。

    ⇒ ⭐ 这个文件是 mixed,而且它的两半需要的处理方向相反**:一半该留(业务事实钉),另一半该收窄射程(而不是退役,因为它在合成路径上确实有效)。**
    ⚠️ 而本卡的表里这一行没有 group A only 标注 ⇒ 按字面它整份退役,那会同时丢掉该留的一半、并把射程错误一并带走而无人发现。

    请 epic PM 复判的具体一件事(与上次相同,现在证据更强)

    给本表这一行补 group A only,并把普查行改判 mixed。⭐ 本条新增的输入是:integrity 半边不是「等平台规则覆盖后退役」那么简单 —— 它有一处需要先修的射程错误。

    hotcrm 侧已把这一条立为 hotcrm#1704,状态 pm:on-hold,Restart-when: 就是本卡这次复判。⛔ 本席不派它,也不在 epic 树里立卡。

    Refs hotcrm#1679 / PR #1703 · hotcrm#1704 · hotcrm#1674 / PR #1680 · 本席 5558354861


    Generated by Claude Code

  8. added and removed
    pm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
    on Oct 8, 2026
  9. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Ruling: batch hotcrm-R74 item 4 · letter B · maintainer 「同意」 2026-10-08T03:16Z

    repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3. Epic #1579 is dissolved by this ruling; the full reading and the destination of every child are in #1579's closing comment.

    Transition: no pm:* state → pm:blocked on the Blocked-by: #1581 line already in this body · pm:epic is removed in the same write · source: the maintainer's 「同意」 to batch hotcrm-R74, in this seat's chat.

    What happens when #1581 lands: this card is re-graded once, under the three-gate, per-case method (non-vacuous, reachable, subsuming: 5555869319, 5556310099). The surviving retirable rows of every family fold into ONE card; this card closes not_planned if it has none. This card is family F2, the reference-integrity family. The two-half reading of test/field-groups-coverage.test.ts recorded on this card (5558354861 and the browser-measured follow-up) is an input to the re-grade.


    Generated by Claude Code

  10. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round R74
    Session: session_012zh91QzFgePbkmuHnugLN3
    Account: os-zhuang (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-1582-family-regrade
    Worktree: hotcrm-issue-1582
    Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
    Seat: repo:hotcrm#1
    File surface: as on the chain head #1582: read-only measurement, nothing committed (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default (dispatch-gates --tier --repo objectstack-ai/hotcrm answers "no path-derived mandate … the tier stays the PM's per-card judgment call")
    Clause-②: no
    Responsibility: n/a — not a defect card (family F2 reference integrity)
    Thread-read: 6051447378
    Serial constraints cleared: member of the folded family dispatch whose chain head is #1582; the full serial check is on that claim. Ruling-ref: hotcrm-R74 item 4 B (on #1579 and on each family card).


    Generated by Claude Code

  11. 2 remaining items

  12. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 8, 2026
  13. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round R74
    Session: session_012zh91QzFgePbkmuHnugLN3
    Account: os-zhuang (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-1582-family-regrade
    Worktree: hotcrm-issue-1582
    Domain: repo:hotcrm (single-lane repo, no domain:* taxonomy)
    Seat: repo:hotcrm#1
    File surface: as on the chain head #1582 (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default (dispatch-gates --tier --repo objectstack-ai/hotcrm answers "no path-derived mandate … the tier stays the PM's per-card judgment call")
    Clause-②: no
    Responsibility: n/a — not a defect card (retirement share, F2: 7 rows (page-field-unknown, the component-props parse sweep, field-group-undeclared/-empty/-shadowed, ai-skill-tool-unresolved, the view-references form-field sweep))
    Thread-read: 6053951901
    Serial constraints cleared: member of the folded family dispatch whose chain head is #1582; the full serial check is on that claim. Ruling-ref: hotcrm-R74 item 4 B.


    Generated by Claude Code

  14. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    Contributor

    Landed in part: PR #2012 MERGED as 99d290ad (Part of #1583). Six of this card's seven F2 rows are retired. repo:hotcrm seat, session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T08:15Z.

    Kept, on measurement: the component-props parse sweep. A NESTED page component with absent properties is silent under objectstack lint --strict (nested record:related_list, page:accordion and element:text), while the top-level record:path control fires component-props-invalid. This is a platform gap, filed as member 6 of the lint blind-spot family.

    Release: claim 6054460760 (fold member) · reason: the retirable rows landed, and the last row waits on the platform · destination: pm:blocked. The assignee is cleared in the same act.
    Blocked-by: objectstack-ai/objectstack#22212
    Restart-when: the installed @objectstack/lint reports component-props-* on a nested page component whose properties are absent (re-run the dev's probe x-props-absent-related on the pin). Then this sweep retires under ruling hotcrm-R74 item 4 B, with the same per-case re-ablation.


    Generated by Claude Code

  15. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    Contributor

    Unlock scan by the repo:hotcrm seat (session_01BCDqr1DqnQjEh5jA9gGkUG), 2026-10-13T12:51Z. ⛔ Not a claim.


    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

No one assigned

    Labels

    pm:queueReady for the PM dispatch loop

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions