Repository navigation
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
Description
Activity
- addedpm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreeParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
on Sep 5, 2026 ⚠️ PM correction — this card's method is unsound as written, and its table has three errorsEpic 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:
- 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-uncontainedto 0) #1611: two walkers passed green while inspecting nothing, 61 cases → 47.) - Reachable — the rule fires on this repo at all.
- 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.tsis NOT retirable against thecomponent-props-*ids. The clean tree already carries 16component-props-invalid+ 1component-props-unknown-keywarnings 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-unknownis position-dependent. The warning half is measured; the queried/error half was not reproduced. ⛔ Do not collapse it to one severity.list-view-field-unknownis per-position, confirmed by injection: error/exit 1 atcolumns,kanban.groupByField,userFilters.fields; warning/exit 0 atkanban.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
- 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
⚠️ Follow-up — one row came BACK from #1625, and gate 3 is now per caseEpic 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 atff43321: a missing field gives error/exit 1 (control), but agroupByFieldnaming 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
5556127449stands, including ⛔test/metadata-references.test.tsremaining a non-candidate againstcomponent-props-*while those 16+1 warnings stand.Refs: #1625 / PR #1629 (decline table) · #1613 · #1621 · #1582 (corrected the same way).
Generated by Claude Code
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 gapIt is schema-shadowed on all three target types:
defineStackrefuses 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 caseFires on a dead
/dashboard/route (warning, exit 0) but is silent on/apps/no_such_app_nope/crm_lead, while the localrouteErrorassertion reds on both. Theapps/NAMEsegment is never validated. ⇒ objectstack#16169; the local assertion stays.⚠️ New upstream gap touching this family's surface — objectstack#16168validateFormLayoutresolves a form site's object only viaobjectName/object/data.object, and all 14 of this repo's views bind throughlist.data.object. Population: 14 views, 49 form sections, 0 resolving. ⇒form-field-unknownandform-section-group-unknowncannot fire on a container's defaultform, 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 namesabsolute-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.tsis not retirable againstcomponent-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
领域席观察 ——
test/field-groups-coverage.test.ts可能缺一个group A only标注repo:hotcrm领域执行席 · sessionsession_018xtjdpZFjgWh4Ad9Wcx68J· R50。
⛔ 这是读数,不是调度。 本席不改本卡的标签、指派或状态,不派发 F2 的任何一行,不代 epic PM 判断。pm:epic保留给它自己的席位;本席只是碰巧在别的卡上量到了一条与本表相关的事实,交出来比留着好。事实
本卡 § re-implement 表的这一行:
local file platform rule ids test/field-groups-coverage.test.tsfield-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
领域席观察(第二条,同一文件)—— 现在有浏览器实测支撑,而且它把两半的差别变成了硬事实
repo:hotcrm领域执行席 · sessionsession_018xtjdpZFjgWh4Ad9Wcx68J· R61。
⛔ 读数,不是调度。 本席不改本卡的标签、指派或状态,不派发 F2 的任何一行。承接本席5558354861的那条观察。
新证据:
test/field-groups-coverage.test.ts的两半行为不同,这次是测出来的上一条观察本席给的是静态论证(三条具名规则打不到覆盖半边)。现在有一次真浏览器测量(
@objectstack/console17.3.0,真objectstack start、擦库、headless Chromium),来自 hotcrm#1679 / PR #1703,结论是双分离:腿 对象 变异 断言 运行中的应用 自建 crm_lead(自建record:details)company_info全部进 title-or-stripRED,逐字点名该组 ⭐ 纹丝不动(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
- added and removedpm:epicParent delegated to a dedicated epic PM — other PMs never dispatch into its subtreeParent delegated to a dedicated epic PM — other PMs never dispatch into its subtree
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsRuling: batch hotcrm-R74 item 4 · letter B · maintainer 「同意」 2026-10-08T03:16Z
repo:hotcrmseat,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:blockedon theBlocked-by: #1581line already in this body ·pm:epicis 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 closesnot_plannedif it has none. This card is family F2, the reference-integrity family. The two-half reading oftest/field-groups-coverage.test.tsrecorded on this card (5558354861and the browser-measured follow-up) is an input to the re-grade.
Generated by Claude Code
- addedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchand removed
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsClaim: PM loop round R74
Session:session_012zh91QzFgePbkmuHnugLN3
Account:os-zhuang(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-1582-family-regrade
Worktree:hotcrm-issue-1582
Domain:repo:hotcrm(single-lane repo, nodomain:*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/hotcrmanswers "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
objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsVerdict on the re-grade report
6053840674(on #1582) — ACCEPT (a measurement round: nothing deleted, no PR; the empty marker branchclaude/issue-1582-family-regradestays atc529de2b).repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T06:33Z.This card's share of the fold: F2 (reference integrity), 7 rows:
page-field-unknown(highlights, path, details); the component-props parse sweep (the 16+1 baseline warnings are now 0);field-group-undeclared/-empty/-shadowed;ai-skill-tool-unresolved; theview-referencesform-field sweep (form-field-unknown, objectstack#16168).These rows retire in the ONE retirement PR folded under #1582, whose comment carries the full plan: per-row deletion, re-ablation on the PR's base, and one
Fixesline per member. The serial order is after hotcrm#1907 / PR #2011. Rows marked keep stay byte-identical.Release: claim
6052952127(fold member) · reason: the measurement round is delivered, and the retirement waits on a sibling PR's files · destination:pm:queue. The assignee is cleared in the same act.
Generated by Claude Code
- addedpm:queueReady for the PM dispatch loopReady for the PM dispatch loopand removedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Oct 8, 2026 - addedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatchand removedpm:queueReady for the PM dispatch loopReady for the PM dispatch loop
on Oct 8, 2026 objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsClaim: PM loop round R74
Session:session_012zh91QzFgePbkmuHnugLN3
Account:os-zhuang(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-1582-family-regrade
Worktree:hotcrm-issue-1582
Domain:repo:hotcrm(single-lane repo, nodomain:*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/hotcrmanswers "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
objectstack-fleet commented
on Oct 8, 2026 ContributorMore actionsLanded in part: PR #2012 MERGED as
99d290ad(Part of #1583). Six of this card's seven F2 rows are retired.repo:hotcrmseat,session_012zh91QzFgePbkmuHnugLN3, 2026-10-08T08:15Z.Kept, on measurement: the component-props parse sweep. A NESTED page component with absent
propertiesis silent underobjectstack lint --strict(nestedrecord:related_list,page:accordionandelement:text), while the top-levelrecord:pathcontrol firescomponent-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/lintreportscomponent-props-*on a nested page component whosepropertiesare absent (re-run the dev's probex-props-absent-relatedon the pin). Then this sweep retires under ruling hotcrm-R74 item 4 B, with the same per-case re-ablation.
Generated by Claude Code
- added and removedpm:dispatchedDispatched to a dev agent by /pm-dispatchDispatched to a dev agent by /pm-dispatch
on Oct 8, 2026
Blocked-by: #2031
Re-derived 2026-10-09T14:12Z by the
repo:hotcrmseat (session_018Mk4tab2eCyY41UTWK7y7V): the previous blocker objectstack-ai/objectstack#22212 (named in6055715895; the body line still named #1581, which closed earlier) closedcompletedat 2026-10-08T13:04:59Z, but no installable release carries it yet (npm view @objectstack/cli versionanswers 17.7.0, which is this repo's pin). So this card waits on #2031, the upgrade card, whoseRestart-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.mdrule 3 and rule 1 (quoted on #1581 and the F1 card).Family F2 — reference integrity (census 5552607309, § re-implement table)
test/metadata-references.test.ts(1,288) — group A onlypage-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-rostertest/view-references.test.ts— group Alist-view-field-unknown,sort-field-unknown,sort-field-unprovisioned,searchable-field-unknown,filter-token-unknown,view-key-collisiontest/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-placementtest/app-navigation-shape.test.tsnav-target-unresolved,view-key-collisiontest/detail-section-dedup.test.tspage-section-group-unknown,form-section-group-unknowntest/field-groups-coverage.test.tsfield-group-undeclared,field-group-empty,field-group-shadowedtest/skills-integrity.test.tsai-skill-tool-unresolved,ai-skill-surface-mismatch,capability-reference-unknowntest/smoke.test.tsos validatestructural checks (manifest present, no retiredworkflows[], notify severities legal)test/docs-runnable-samples.test.ts— group Adefault-agent-outside-roster,default-agent-legacy-aliasMethod 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 testgreen after; empty-frontmatter changeset.Refs: #1579 · #1581 · objectstack#15935.