You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] Three data-model index rules put a positional config path in the finding's human-readable where slot, so the CLI renders the location as objects[44].indexes[1] #9600
Recorded while taking the false-positive-budget measurement on #4716. Not fixed there — that dispatch changes no production file. Filed unassigned for triage.
The deviation
AuthoringFinding declares two distinct slots (packages/lint/src/authoring-rules.ts):
where — "Human-readable location, e.g. object "leave_request""
path — "Config path, e.g. objects[3].sharingModel"
Three registry adapters set both from the config path:
rule
registry entry
adapter line
lintUnscopedDeclaredIndexes
authoring-rules.ts
where: f.path
lintUniqueDeclarations
authoring-rules.ts
where: f.path
lintLegacyOrganizationComposites
authoring-rules.ts
where: f.path
All three implement in packages/lint/src/data-model-rules.ts and all three are tier: 'advisory'.
Measured
origin/main @ 1c6da6eaf, over the 45 object declarations @objectstack/platform-objects (44) and @objectstack/metadata-core (1) ship. 36 findings from these rules render their location as a bare index:
unique/unscoped-declared-index where = objects[44].indexes[1]
unique/legacy-organization-composite where = objects[44].indexes[0]
The object's name is not in where at any point. It is in the message ("sys_account" declares index [provider_id, account_id] with bare unique: true …), so no information is lost — but the location column is unreadable on its own, and every other object-scoped rule in the table spells it object "sys_account".
Why this reads differently than when it was written
All three carry surfaceReason: RUNTIME_OBJECT_WRITES_P2 — they are inside the group [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716's first bullet proposes to put on the object write path. At that surface the index is an index into a per-write snapshot, not into the author's stack: their own object is objects[0] (or the last slot) no matter where it sits in their tenant. The number would be not merely unhelpful but wrong-looking.
A second, measured consequence
Any consumer that diffs findings across two stack shapes sees one finding under two identities, because the index moves. Taking the #4716 budget, comparing the per-write gate's added findings against the whole-stack verdict reported 36 phantom findings; normalising [digits] out of the fingerprint flipped all 36 to exact matches. They were the same findings at a shifted index.
⚠️ To be precise about blast radius: the runtime gate's own differential is not affected.buildRuntimeWriteSnapshots derives baseline and candidate from the same filtered array, so every non-written item keeps its index across both passes and its findings cancel correctly. This bites downstream consumers and anyone measuring the gate, not the gate.
Suggested shape
Give the three adapters the where the rest of the table builds (object "name", derivable from the same walk that produces f.path), and leave path exactly as it is. Low risk: where is display text, and the only in-repo consumer that keys on it is the gate's fingerprint, which reads whereandpath together and would be unchanged in behaviour by making where more specific rather than less.
Recorded while taking the false-positive-budget measurement on #4716. Not fixed there — that dispatch changes no production file. Filed unassigned for triage.
The deviation
AuthoringFindingdeclares two distinct slots (packages/lint/src/authoring-rules.ts):where— "Human-readable location, e.g.object "leave_request""path— "Config path, e.g.objects[3].sharingModel"Three registry adapters set both from the config path:
lintUnscopedDeclaredIndexesauthoring-rules.tswhere: f.pathlintUniqueDeclarationsauthoring-rules.tswhere: f.pathlintLegacyOrganizationCompositesauthoring-rules.tswhere: f.pathAll three implement in
packages/lint/src/data-model-rules.tsand all three aretier: 'advisory'.Measured
origin/main@1c6da6eaf, over the 45 object declarations@objectstack/platform-objects(44) and@objectstack/metadata-core(1) ship. 36 findings from these rules render their location as a bare index:The object's name is not in
whereat any point. It is in the message ("sys_account" declares index [provider_id, account_id] with bare unique: true …), so no information is lost — but the location column is unreadable on its own, and every other object-scoped rule in the table spells itobject "sys_account".Why this reads differently than when it was written
Two things changed under it:
SaveMetaItemResponseSchema.advisoriesships ([P2/P3] 运行时授权门的 advisory findings 要进响应并被 Studio 渲染 + docs「the gate」补第四列(#4463 收尾) #4717) and objectui PRsys_migration由 service-storage 持有,但它是平台级账本 —— 第二个消费者出现后这层耦合该解开 #4236 renders it. These strings are no longer CLI-only output beside a config file the author is looking at.surfaceReason: RUNTIME_OBJECT_WRITES_P2— they are inside the group [P2] 运行时授权门扩到 object 写入 + 全量 runtime-safe 规则快照(#4463 P1 之后) #4716's first bullet proposes to put on theobjectwrite path. At that surface the index is an index into a per-write snapshot, not into the author's stack: their own object isobjects[0](or the last slot) no matter where it sits in their tenant. The number would be not merely unhelpful but wrong-looking.A second, measured consequence
Any consumer that diffs findings across two stack shapes sees one finding under two identities, because the index moves. Taking the #4716 budget, comparing the per-write gate's added findings against the whole-stack verdict reported 36 phantom findings; normalising
[digits]out of the fingerprint flipped all 36 to exact matches. They were the same findings at a shifted index.buildRuntimeWriteSnapshotsderives baseline and candidate from the same filtered array, so every non-written item keeps its index across both passes and its findings cancel correctly. This bites downstream consumers and anyone measuring the gate, not the gate.Suggested shape
Give the three adapters the
wherethe rest of the table builds (object "name", derivable from the same walk that producesf.path), and leavepathexactly as it is. Low risk:whereis display text, and the only in-repo consumer that keys on it is the gate'sfingerprint, which readswhereandpathtogether and would be unchanged in behaviour by makingwheremore specific rather than less.