@objectstack/lint 17.0.0-rc.1 → 17.0.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Manifest } from '@objectstack/sdui-parser';
2
2
  import { AccessMatrix } from '@objectstack/spec/security';
3
+ export { A as AUTHORING_COMMANDS, a as AUTHORING_RULES, b as AUTHORING_SURFACES, c as AuthoringCommand, d as AuthoringFinding, e as AuthoringRule, f as AuthoringRuleContext, g as AuthoringRuleInputTier, h as AuthoringRuleRun, i as AuthoringRuleTier, j as AuthoringSeverity, k as AuthoringSurface, E as EXPRESSION_INVALID, R as RuntimeGateResult, l as RuntimeStackContext, m as authoringRulesFor, r as runAuthoringRules, n as runRuntimeAuthoringRules, o as runtimeAuthoringRulesFor, p as runtimeGatedTypes, s as splitBySeverity, q as stackKeyForType } from './runtime-Cs64ShwN.js';
3
4
 
4
5
  /**
5
6
  * Build-time dashboard widget binding diagnostics (issues #1719, #1721).
@@ -94,7 +95,7 @@ interface WidgetBindingFinding {
94
95
  /** How to fix (or deliberately suppress) it. */
95
96
  hint: string;
96
97
  }
97
- type AnyRec$x = Record<string, unknown>;
98
+ type AnyRec$C = Record<string, unknown>;
98
99
  /**
99
100
  * Validate every dashboard widget's dataset binding. Returns the list of
100
101
  * findings (empty = clean). Caller decides how to surface them: `error`
@@ -102,7 +103,7 @@ type AnyRec$x = Record<string, unknown>;
102
103
  * should fail validate/build; `warning` findings are advisory and must
103
104
  * never fail the build on their own.
104
105
  */
105
- declare function validateWidgetBindings(stack: AnyRec$x): WidgetBindingFinding[];
106
+ declare function validateWidgetBindings(stack: AnyRec$C): WidgetBindingFinding[];
106
107
 
107
108
  interface ExprIssue {
108
109
  where: string;
@@ -115,12 +116,56 @@ interface ExprIssue {
115
116
  */
116
117
  severity?: 'error' | 'warning';
117
118
  }
118
- type AnyRec$w = Record<string, unknown>;
119
+ type AnyRec$B = Record<string, unknown>;
119
120
  /**
120
121
  * Validate every predicate in the stack. Returns the list of issues (empty =
121
122
  * clean). Caller decides how to surface / whether to fail the build.
122
123
  */
123
- declare function validateStackExpressions(stack: AnyRec$w): ExprIssue[];
124
+ declare function validateStackExpressions(stack: AnyRec$B): ExprIssue[];
125
+
126
+ /**
127
+ * The corrective sentence, lifted **verbatim** from `unevaluableRuleError` in
128
+ * `packages/objectql/src/validation/rule-validator.ts` so the publish-time and
129
+ * runtime messages read identically — an author who hits one and then the other
130
+ * must not have to reconcile two phrasings of one rule (#4763).
131
+ */
132
+ declare const NULL_GUARD_HINT: string;
133
+ interface NullGuardOptions {
134
+ /** Field names of the object that may hold `null` at evaluation time. */
135
+ nullableFields: ReadonlySet<string>;
136
+ /** Roots bound to the object's record shape. Defaults to `record`/`previous`. */
137
+ roots?: readonly string[];
138
+ }
139
+ interface NullGuardFinding {
140
+ /** The operand as written, e.g. `record.end_date`. */
141
+ operand: string;
142
+ /** The declared field the operand resolves to, e.g. `end_date`. */
143
+ field: string;
144
+ /** The operator with no null overload, e.g. `<`. */
145
+ operator: string;
146
+ /**
147
+ * True when the predicate "guards" this operand with `has()` and nothing
148
+ * else — the exact trap #4763 exists to reject. Used only to sharpen the
149
+ * message; the verdict is the same either way.
150
+ */
151
+ hasOnlyGuard: boolean;
152
+ }
153
+ /**
154
+ * Find every ordering/arithmetic operand that resolves to a nullable declared
155
+ * field and is not dominated by a real null guard. Returns `[]` for anything
156
+ * that does not parse (syntax is reported by `validateExpression`, not here) —
157
+ * this pass never invents a second syntax verdict.
158
+ */
159
+ declare function findUnguardedNullableOperands(source: string, opts: NullGuardOptions): NullGuardFinding[];
160
+ /**
161
+ * The publish-time message for one finding. Names the rule, the operand and the
162
+ * `!= null` fix (the three things the author needs), then closes with the
163
+ * verbatim runtime sentence so the two gates speak with one voice.
164
+ *
165
+ * @param subject How the site names itself, e.g. `validation rule 'end_after_start'`.
166
+ * @param objectName The object whose field list decided nullability.
167
+ */
168
+ declare function nullGuardMessage(subject: string, objectName: string | undefined, finding: NullGuardFinding): string;
124
169
 
125
170
  type ListViewModeSeverity = 'error' | 'warning';
126
171
  interface ListViewModeFinding {
@@ -134,7 +179,7 @@ interface ListViewModeFinding {
134
179
  hint: string;
135
180
  }
136
181
  declare const LIST_VIEW_FILTERS_IN_VIEWS_MODE = "list-view-filters-in-views-mode";
137
- type AnyRec$v = Record<string, unknown>;
182
+ type AnyRec$A = Record<string, unknown>;
138
183
  /**
139
184
  * Flag ADR-0047 "views" mode violations on an object's built-in named views or a
140
185
  * `defineView` default `list` / named `listViews`: `quickFilters`, or a `tabs`
@@ -144,7 +189,25 @@ type AnyRec$v = Record<string, unknown>;
144
189
  *
145
190
  * Feed the PRE-parse stack (normalizeStackInput output) — see file header.
146
191
  */
147
- declare function validateListViewMode(stack: AnyRec$v): ListViewModeFinding[];
192
+ declare function validateListViewMode(stack: AnyRec$A): ListViewModeFinding[];
193
+
194
+ type FunctionalCompletenessSeverity = 'error' | 'warning';
195
+ interface FunctionalCompletenessFinding {
196
+ severity: FunctionalCompletenessSeverity;
197
+ /** Stable rule id from the shared predicate (e.g. `field/summary-without-operations`). */
198
+ rule: string;
199
+ /** Human-readable location, e.g. `object "order" › fields.total`. */
200
+ where: string;
201
+ /** Config path, e.g. `objects[2].fields.total.summaryOperations`. */
202
+ path: string;
203
+ message: string;
204
+ hint: string;
205
+ }
206
+ /**
207
+ * Walk every field definition and every list-view definition in the stack
208
+ * through the shared completeness predicate.
209
+ */
210
+ declare function validateFunctionalCompleteness(stack: unknown): FunctionalCompletenessFinding[];
148
211
 
149
212
  type FlowTriggerReadinessSeverity = 'error' | 'warning';
150
213
  interface FlowTriggerReadinessFinding {
@@ -159,12 +222,12 @@ interface FlowTriggerReadinessFinding {
159
222
  }
160
223
  declare const FLOW_TRIGGER_UNKNOWN_OBJECT = "flow-trigger-unknown-object";
161
224
  declare const FLOW_DRAFT_STATUS_AMBIGUOUS = "flow-draft-status-ambiguous";
162
- type AnyRec$u = Record<string, unknown>;
225
+ type AnyRec$z = Record<string, unknown>;
163
226
  /**
164
227
  * Validate auto-launched flow trigger wiring against the stack definition.
165
228
  * Pure and dependency-free; safe on pre- or post-parse stacks.
166
229
  */
167
- declare function validateFlowTriggerReadiness(stack: AnyRec$u): FlowTriggerReadinessFinding[];
230
+ declare function validateFlowTriggerReadiness(stack: AnyRec$z): FlowTriggerReadinessFinding[];
168
231
 
169
232
  type FlowTemplatePathSeverity = 'error' | 'warning';
170
233
  interface FlowTemplatePathFinding {
@@ -179,12 +242,12 @@ interface FlowTemplatePathFinding {
179
242
  }
180
243
  declare const FLOW_TEMPLATE_UNKNOWN_FIELD = "flow-template-unknown-field";
181
244
  declare const FLOW_TEMPLATE_LOOKUP_TRAVERSAL = "flow-template-lookup-traversal";
182
- type AnyRec$t = Record<string, unknown>;
245
+ type AnyRec$y = Record<string, unknown>;
183
246
  /**
184
247
  * Validate `{record.<path>}` template references across every record-change
185
248
  * flow. Pure and dependency-free; safe on pre- or post-parse stacks.
186
249
  */
187
- declare function validateFlowTemplatePaths(stack: AnyRec$t): FlowTemplatePathFinding[];
250
+ declare function validateFlowTemplatePaths(stack: AnyRec$y): FlowTemplatePathFinding[];
188
251
 
189
252
  type ReadonlyFlowWriteSeverity = 'error' | 'warning';
190
253
  interface ReadonlyFlowWriteFinding {
@@ -199,12 +262,12 @@ interface ReadonlyFlowWriteFinding {
199
262
  }
200
263
  declare const FLOW_UPDATE_READONLY_FIELD = "flow-update-readonly-field";
201
264
  declare const FLOW_UPDATE_READONLY_WHEN_FIELD = "flow-update-readonly-when-field";
202
- type AnyRec$s = Record<string, unknown>;
265
+ type AnyRec$x = Record<string, unknown>;
203
266
  /**
204
267
  * Validate flow `update_record` writes against target-object readonly
205
268
  * declarations. Pure and dependency-free; safe on pre- or post-parse stacks.
206
269
  */
207
- declare function validateReadonlyFlowWrites(stack: AnyRec$s): ReadonlyFlowWriteFinding[];
270
+ declare function validateReadonlyFlowWrites(stack: AnyRec$x): ReadonlyFlowWriteFinding[];
208
271
 
209
272
  type ViewContainerSeverity = 'error' | 'warning';
210
273
  interface ViewContainerFinding {
@@ -241,14 +304,14 @@ declare const STYLE_CLASSNAME_TAILWIND = "style-classname-tailwind";
241
304
  declare const STYLE_RESPONSIVE_NO_BASE = "style-responsive-no-base";
242
305
  declare const STYLE_UNKNOWN_CSS_PROPERTY = "style-unknown-css-property";
243
306
  declare const STYLE_UNKNOWN_TOKEN = "style-unknown-token";
244
- type AnyRec$r = Record<string, unknown>;
307
+ type AnyRec$w = Record<string, unknown>;
245
308
  /**
246
309
  * Validate every page's component tree for SDUI styling correctness (ADR-0065).
247
310
  * Returns findings (empty = clean). `error` findings describe styles that are
248
311
  * silently dropped and should fail validate/build; `warning` findings are
249
312
  * advisory (typos, drift, footguns).
250
313
  */
251
- declare function validateResponsiveStyles(stack: AnyRec$r): StyleFinding[];
314
+ declare function validateResponsiveStyles(stack: AnyRec$w): StyleFinding[];
252
315
 
253
316
  type JsxPageSeverity = 'error' | 'warning';
254
317
  interface JsxPageFinding {
@@ -261,8 +324,8 @@ interface JsxPageFinding {
261
324
  message: string;
262
325
  hint: string;
263
326
  }
264
- type AnyRec$q = Record<string, unknown>;
265
- declare function validateJsxPages(stack: AnyRec$q, opts?: {
327
+ type AnyRec$v = Record<string, unknown>;
328
+ declare function validateJsxPages(stack: AnyRec$v, opts?: {
266
329
  manifest?: Manifest;
267
330
  }): JsxPageFinding[];
268
331
 
@@ -275,8 +338,8 @@ interface ReactPageFinding {
275
338
  message: string;
276
339
  hint: string;
277
340
  }
278
- type AnyRec$p = Record<string, unknown>;
279
- declare function validateReactPages(stack: AnyRec$p): ReactPageFinding[];
341
+ type AnyRec$u = Record<string, unknown>;
342
+ declare function validateReactPages(stack: AnyRec$u): ReactPageFinding[];
280
343
 
281
344
  type ReactPropSeverity = 'error' | 'warning';
282
345
  interface ReactPropFinding {
@@ -287,11 +350,12 @@ interface ReactPropFinding {
287
350
  message: string;
288
351
  hint: string;
289
352
  }
290
- type AnyRec$o = Record<string, unknown>;
353
+ type AnyRec$t = Record<string, unknown>;
291
354
  declare const REACT_CHART_FIELD_UNKNOWN = "react-chart-field-unknown";
292
355
  declare const REACT_CHART_AGGREGATE_INVALID = "react-chart-aggregate-invalid";
293
356
  declare const REACT_CHART_AXIS_UNKNOWN = "react-chart-axis-unknown";
294
- declare function validateReactPageProps(stack: AnyRec$o): ReactPropFinding[];
357
+ declare const REACT_BLOCK_NEEDS_RECORD_CONTEXT = "react-block-needs-record-context";
358
+ declare function validateReactPageProps(stack: AnyRec$t): ReactPropFinding[];
295
359
 
296
360
  type SourceStyleSeverity = 'error' | 'warning';
297
361
  interface SourceStyleFinding {
@@ -303,8 +367,8 @@ interface SourceStyleFinding {
303
367
  hint: string;
304
368
  }
305
369
  declare const PAGE_SOURCE_CLASSNAME = "page-source-className-tailwind";
306
- type AnyRec$n = Record<string, unknown>;
307
- declare function validatePageSourceStyling(stack: AnyRec$n): SourceStyleFinding[];
370
+ type AnyRec$s = Record<string, unknown>;
371
+ declare function validatePageSourceStyling(stack: AnyRec$s): SourceStyleFinding[];
308
372
 
309
373
  /**
310
374
  * Build-time record-title diagnostics (ADR-0079).
@@ -348,14 +412,14 @@ interface RecordTitleFinding {
348
412
  /** How to fix it. */
349
413
  hint: string;
350
414
  }
351
- type AnyRec$m = Record<string, unknown>;
415
+ type AnyRec$r = Record<string, unknown>;
352
416
  /**
353
417
  * Validate every object's record-title declaration. Returns the list of
354
418
  * findings (empty = clean). Both rules are advisory (`warning`): the caller
355
419
  * must never fail the build on them alone — auto-provision + the `Record #<id>`
356
420
  * floor guarantee a resolvable title at runtime.
357
421
  */
358
- declare function validateRecordTitle(stack: AnyRec$m): RecordTitleFinding[];
422
+ declare function validateRecordTitle(stack: AnyRec$r): RecordTitleFinding[];
359
423
 
360
424
  /**
361
425
  * Build-time semantic-role diagnostics (ADR-0085).
@@ -392,13 +456,13 @@ interface SemanticRoleFinding {
392
456
  /** How to fix it. */
393
457
  hint: string;
394
458
  }
395
- type AnyRec$l = Record<string, unknown>;
459
+ type AnyRec$q = Record<string, unknown>;
396
460
  /**
397
461
  * Validate every object's semantic-role pointers. Returns the list of
398
462
  * findings (empty = clean). Advisory only — the caller must never fail the
399
463
  * build on these alone.
400
464
  */
401
- declare function validateSemanticRoles(stack: AnyRec$l): SemanticRoleFinding[];
465
+ declare function validateSemanticRoles(stack: AnyRec$q): SemanticRoleFinding[];
402
466
 
403
467
  /**
404
468
  * Build-time form-layout diagnostics (#2578).
@@ -441,12 +505,12 @@ interface FormLayoutFinding {
441
505
  /** How to fix it. */
442
506
  hint: string;
443
507
  }
444
- type AnyRec$k = Record<string, unknown>;
508
+ type AnyRec$p = Record<string, unknown>;
445
509
  /**
446
510
  * Validate authored form-view layout. Returns findings (empty = clean).
447
511
  * Advisory only — the caller must never fail the build on these alone.
448
512
  */
449
- declare function validateFormLayout(stack: AnyRec$k): FormLayoutFinding[];
513
+ declare function validateFormLayout(stack: AnyRec$p): FormLayoutFinding[];
450
514
 
451
515
  /**
452
516
  * Build-time conditional-visibility diagnostics (ADR-0089 D3b).
@@ -508,7 +572,7 @@ interface VisibilityFinding {
508
572
  /** How to fix it. */
509
573
  hint: string;
510
574
  }
511
- type AnyRec$j = Record<string, unknown>;
575
+ type AnyRec$o = Record<string, unknown>;
512
576
  /**
513
577
  * Validate conditional-visibility keys across authored views and pages.
514
578
  *
@@ -523,7 +587,7 @@ type AnyRec$j = Record<string, unknown>;
523
587
  * `*.view.ts` / `*.page.ts` surfaces (so a `data.`-rooted predicate is flagged). The
524
588
  * alias-deprecated check is layer-agnostic.
525
589
  */
526
- declare function validateVisibilityPredicates(stack: AnyRec$j, opts?: VisibilityOptions): VisibilityFinding[];
590
+ declare function validateVisibilityPredicates(stack: AnyRec$o, opts?: VisibilityOptions): VisibilityFinding[];
527
591
 
528
592
  declare const CAPABILITY_REFERENCE_UNKNOWN = "capability-reference-unknown";
529
593
  type CapabilityRefSeverity = 'error' | 'warning';
@@ -541,12 +605,12 @@ interface CapabilityRefFinding {
541
605
  /** How to fix it. */
542
606
  hint: string;
543
607
  }
544
- type AnyRec$i = Record<string, unknown>;
608
+ type AnyRec$n = Record<string, unknown>;
545
609
  /**
546
610
  * Validate every capability reference in a stack. Returns findings (empty =
547
611
  * clean). Advisory only — callers must not fail the build on these alone.
548
612
  */
549
- declare function validateCapabilityReferences(stack: AnyRec$i): CapabilityRefFinding[];
613
+ declare function validateCapabilityReferences(stack: AnyRec$n): CapabilityRefFinding[];
550
614
 
551
615
  declare const APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER = "approval-approver-not-membership-tier";
552
616
  declare const APPROVAL_APPROVER_TYPE_DEPRECATED = "approval-approver-type-deprecated";
@@ -571,12 +635,12 @@ interface ApprovalApproverFinding {
571
635
  /** How to fix it. */
572
636
  hint: string;
573
637
  }
574
- type AnyRec$h = Record<string, unknown>;
638
+ type AnyRec$m = Record<string, unknown>;
575
639
  /**
576
640
  * Validate the approvers of every Approval node in the stack's flows.
577
641
  * Returns findings (empty = clean).
578
642
  */
579
- declare function validateApprovalApprovers(stack: AnyRec$h): ApprovalApproverFinding[];
643
+ declare function validateApprovalApprovers(stack: AnyRec$m): ApprovalApproverFinding[];
580
644
 
581
645
  type SeedReplaySafetySeverity = 'error' | 'warning';
582
646
  interface SeedReplaySafetyFinding {
@@ -590,7 +654,7 @@ interface SeedReplaySafetyFinding {
590
654
  hint: string;
591
655
  }
592
656
  declare const SEED_INSERT_MODE_DUPLICATES_ON_REPLAY = "seed-insert-mode-duplicates-on-replay";
593
- type AnyRec$g = Record<string, unknown>;
657
+ type AnyRec$l = Record<string, unknown>;
594
658
  /**
595
659
  * Flag every seed dataset declared with `mode: 'insert'` — the one non-idempotent
596
660
  * mode, which duplicates its rows on every replay boot (framework#3434). Returns
@@ -600,7 +664,7 @@ type AnyRec$g = Record<string, unknown>;
600
664
  * Reads `stack.data` (the `SeedSchema[]` fixtures). Safe on any shape — a stack
601
665
  * with no `data` array yields no findings.
602
666
  */
603
- declare function validateSeedReplaySafety(stack: AnyRec$g): SeedReplaySafetyFinding[];
667
+ declare function validateSeedReplaySafety(stack: AnyRec$l): SeedReplaySafetyFinding[];
604
668
 
605
669
  type SeedStateMachineSeverity = 'warning';
606
670
  interface SeedStateMachineFinding {
@@ -614,7 +678,7 @@ interface SeedStateMachineFinding {
614
678
  hint: string;
615
679
  }
616
680
  declare const SEED_VALUE_OUTSIDE_STATE_MACHINE = "seed-value-outside-state-machine";
617
- type AnyRec$f = Record<string, unknown>;
681
+ type AnyRec$k = Record<string, unknown>;
618
682
  /**
619
683
  * Flag every seed record whose `state_machine`-governed field carries a value
620
684
  * the machine does not declare (framework#3433 follow-up). Returns the findings
@@ -627,7 +691,7 @@ type AnyRec$f = Record<string, unknown>;
627
691
  * unresolved `cel` Expression envelope, a number) is skipped: it cannot be
628
692
  * statically compared to the declared-state set.
629
693
  */
630
- declare function validateSeedStateMachine(stack: AnyRec$f): SeedStateMachineFinding[];
694
+ declare function validateSeedStateMachine(stack: AnyRec$k): SeedStateMachineFinding[];
631
695
 
632
696
  declare const SECURITY_OWD_UNSET = "security-owd-unset";
633
697
  declare const SECURITY_OWD_ALIAS = "security-owd-alias";
@@ -654,7 +718,7 @@ interface SecurityFinding {
654
718
  /** How to fix it. */
655
719
  hint: string;
656
720
  }
657
- type AnyRec$e = Record<string, unknown>;
721
+ type AnyRec$j = Record<string, unknown>;
658
722
  /**
659
723
  * Validate the security posture of a stack. Returns findings (empty = clean).
660
724
  * `error` findings gate the build in `os compile`; `info` is advisory.
@@ -662,7 +726,7 @@ type AnyRec$e = Record<string, unknown>;
662
726
  * `opts.nowMs` injects the clock for the ADR-0091 authoring-time expiry rule
663
727
  * (tests); production callers omit it.
664
728
  */
665
- declare function validateSecurityPosture(stack: AnyRec$e, opts?: {
729
+ declare function validateSecurityPosture(stack: AnyRec$j, opts?: {
666
730
  nowMs?: number;
667
731
  }): SecurityFinding[];
668
732
 
@@ -796,13 +860,13 @@ interface DashboardActionRefFinding {
796
860
  /** How to fix it. */
797
861
  hint: string;
798
862
  }
799
- type AnyRec$d = Record<string, unknown>;
863
+ type AnyRec$i = Record<string, unknown>;
800
864
  /**
801
865
  * Validate every dashboard header / widget action reference in a stack. Returns
802
866
  * findings (empty = clean). `script`/`modal` dead targets are errors; `url`
803
867
  * unresolved routes are warnings.
804
868
  */
805
- declare function validateDashboardActionRefs(stack: AnyRec$d): DashboardActionRefFinding[];
869
+ declare function validateDashboardActionRefs(stack: AnyRec$i): DashboardActionRefFinding[];
806
870
 
807
871
  /**
808
872
  * Build-time filter-placeholder diagnostics (issue #3574).
@@ -900,14 +964,208 @@ interface ObjectRefFinding {
900
964
  /** How to fix it. */
901
965
  hint: string;
902
966
  }
903
- type AnyRec$c = Record<string, unknown>;
967
+ type AnyRec$h = Record<string, unknown>;
904
968
  /**
905
969
  * Validate every object-name reference on the surfaces listed in the module
906
970
  * header. Returns findings (empty = clean).
907
971
  */
908
- declare function validateObjectReferences(stack: AnyRec$c): ObjectRefFinding[];
972
+ declare function validateObjectReferences(stack: AnyRec$h): ObjectRefFinding[];
973
+
974
+ type ReferenceIntegritySeverity = 'error' | 'warning';
975
+ /**
976
+ * The shape every rule in the suite already returns. Declared here so callers
977
+ * can hold one type instead of a six-way union.
978
+ */
979
+ interface ReferenceIntegrityFinding {
980
+ /** `error` = the reference is dead; `warning` = it may resolve elsewhere, or the miss is inert. */
981
+ severity: ReferenceIntegritySeverity;
982
+ /** Diagnostic rule id (stable; used by allowlists and docs). */
983
+ rule: string;
984
+ /** Human-readable location. */
985
+ where: string;
986
+ /** Config path. */
987
+ path: string;
988
+ /** What is wrong. */
989
+ message: string;
990
+ /** How to fix it. */
991
+ hint: string;
992
+ }
993
+ /** One member of the suite. `name` is the exported function's name — the id a wiring test can assert on. */
994
+ interface ReferenceIntegrityRule {
995
+ name: string;
996
+ run: (stack: Record<string, unknown>) => ReferenceIntegrityFinding[];
997
+ }
998
+ /**
999
+ * Every reference-integrity rule, in the order their findings are reported.
1000
+ *
1001
+ * ADDING A RULE: append it here and it runs on `validate`, `lint` and
1002
+ * `compile` at once. Do not re-wire the commands.
1003
+ */
1004
+ declare const REFERENCE_INTEGRITY_RULES: readonly ReferenceIntegrityRule[];
1005
+ /**
1006
+ * Run every reference-integrity rule over a stack. Returns the concatenated
1007
+ * findings (empty = clean). Pure: no I/O, safe on both the schema-parsed stack
1008
+ * and the raw/normalized config the `lint` path carries.
1009
+ */
1010
+ declare function validateReferenceIntegrity(stack: Record<string, unknown>): ReferenceIntegrityFinding[];
1011
+
1012
+ /**
1013
+ * [ADR-0072 — reference resolvability] App-navigation targets that are not
1014
+ * object names: `page`, `report`, `dashboard`.
1015
+ *
1016
+ * ## The hole this closes is inside an EXISTING check, not a missing one
1017
+ *
1018
+ * `defineStack`'s `validateCrossReferences` already validates these three
1019
+ * (`stack.zod.ts`, the "Validate app navigation → object/dashboard/page/report
1020
+ * references" block). But each of the three is guarded on the collection being
1021
+ * non-empty:
1022
+ *
1023
+ * ```ts
1024
+ * if (nav.type === 'page' && typeof nav.pageName === 'string'
1025
+ * && pageNames.size > 0 && !pageNames.has(nav.pageName)) { … }
1026
+ * ```
1027
+ *
1028
+ * So a stack that declares **no `pages` at all** has its page-nav check
1029
+ * silently switched off, and `{ type: 'page', pageName: 'anything' }` sails
1030
+ * through. That is precisely the state a stack is in when the target was never
1031
+ * written — the most likely way to get here, not the least.
1032
+ *
1033
+ * Note the asymmetry the guard creates. The `object` arm of the same block has
1034
+ * no size gate: it errors unless the item carries `requiresObject`, an
1035
+ * EXPLICIT opt-in to "another package provides this". Objects therefore say so
1036
+ * out loud; pages, reports and dashboards get an implicit exemption that
1037
+ * depends on an unrelated property of the stack.
1038
+ *
1039
+ * This rule restores the coverage with the ADR-0072 severity posture rather
1040
+ * than by tightening the parse-time throw — a throw has no escape hatch for a
1041
+ * legitimately cross-package page, and ADR-0072 D1's rule is that one dead
1042
+ * finding costs more than a missed one.
1043
+ *
1044
+ * ## Severity: warning, and why it is not error
1045
+ *
1046
+ * `validate-object-references` can say ERROR for an unresolved *object*
1047
+ * because it resolves against a curated `PLATFORM_PROVIDED_OBJECT_NAMES`
1048
+ * registry — it knows which cross-package names are real. No such registry
1049
+ * exists for pages, reports or dashboards, so "unresolved" genuinely cannot be
1050
+ * distinguished from "provided by a package we cannot see from here".
1051
+ * Advisory is the honest ceiling. When `defineStack`'s own check is live (the
1052
+ * collection is non-empty) it still hard-fails first; this rule is what speaks
1053
+ * when that check has switched itself off.
1054
+ *
1055
+ * ## Deliberately NOT covered — each verified, not assumed
1056
+ *
1057
+ * - **`action`** (`actionDef.actionName`) — already owned by
1058
+ * `validate-action-name-refs`, which walks app navigation explicitly. Adding
1059
+ * it here would double-report the same finding.
1060
+ * - **`component`** (`componentRef`) — verified a NON-rule. An unregistered ref
1061
+ * does NOT fail silently: `ComponentNavView` renders a named diagnostic
1062
+ * ("Component not registered … Ensure the plugin that provides this surface
1063
+ * is installed and has called `registerAppComponent()`"), and the registry
1064
+ * exists precisely so plugin-provided surfaces may legitimately be absent.
1065
+ * Flagging it would break valid plugin nav and prescribe a fix for something
1066
+ * already reported better at runtime.
1067
+ * - **`url`** — external by definition; nothing to resolve against.
1068
+ */
1069
+
1070
+ type NavTargetRefSeverity = 'error' | 'warning';
1071
+ type NavTargetRefFinding = ReferenceIntegrityFinding;
1072
+ /** Emitted when a nav item targets a page/report/dashboard the stack cannot resolve. */
1073
+ declare const NAV_TARGET_UNRESOLVED = "nav-target-unresolved";
1074
+ declare function validateNavTargetRefs(stack: unknown): NavTargetRefFinding[];
1075
+
1076
+ /**
1077
+ * [ADR-0061 — searchable set] `searchableFields` entries must name a field the
1078
+ * object actually has — and, on a list view, one the runtime will agree to scan.
1079
+ *
1080
+ * `searchableFields` is `z.array(z.string())` in both `object.zod.ts` and the
1081
+ * list-view schema, so nothing checks that an entry resolves to anything. Rename
1082
+ * a field and the old name stays behind: Zod-valid, shipped, and pointing at a
1083
+ * column that no longer exists.
1084
+ *
1085
+ * ── Why a stale entry is not merely inert ────────────────────────────────
1086
+ *
1087
+ * The engine tolerates it. `resolveSearchFields` (`@objectstack/objectql`)
1088
+ * filters the declaration down to fields that exist —
1089
+ * `searchableFields?.filter((f) => all[f])` — so a stale name is dropped
1090
+ * without a word. That tolerance is what makes the drift invisible, and it
1091
+ * fails in the direction nobody expects:
1092
+ *
1093
+ * - Some entries stale → `$search` quietly scans a NARROWER set than the
1094
+ * object declares. Records that should match do not, and the response is
1095
+ * indistinguishable from "no such record".
1096
+ * - EVERY entry stale → the filtered set is empty, so resolution falls
1097
+ * through to the AUTO-DEFAULT (name/title + short-text fields). A
1098
+ * declaration whose entire purpose is to CHOOSE the searchable set ends up
1099
+ * selecting a set the author never wrote — the same "asked narrower,
1100
+ * answered wider" inversion #4226 closed on the projection axis.
1101
+ *
1102
+ * And it does not stay quiet downstream. objectui's list search echoes
1103
+ * `schema.searchableFields` verbatim as the `$searchFields` override, so once
1104
+ * the REST read path validates that override against the object (#4254), a
1105
+ * stale declaration the engine had been silently skipping becomes a `400
1106
+ * INVALID_FIELD` on every list search for that object — a request-time break
1107
+ * whose cause is an authoring typo made long before.
1108
+ *
1109
+ * Hence `error`, not the advisory level the other field-existence rules use
1110
+ * (`page-field-unknown`, `form-field-unknown`, `semantic-role-field-unknown`
1111
+ * are all warnings). Those describe a consumer that SKIPS an unknown name and
1112
+ * renders the rest; this one describes a declaration that either selects the
1113
+ * wrong set or refuses the request outright. It is the same call
1114
+ * `validate-flow-template-paths` makes for a filter-position token: gating when
1115
+ * the miss widens the query rather than shrinking the page.
1116
+ *
1117
+ * ── What is checked ──────────────────────────────────────────────────────
1118
+ *
1119
+ * 1. EXISTENCE, on every surface (`searchable-field-unknown`): an entry that
1120
+ * resolves to no field at all is drift, whatever declared it.
1121
+ *
1122
+ * 2. RUNTIME ADMISSIBILITY, on view-level narrowings only
1123
+ * (`searchable-field-unsearchable`, #4830): a list view's
1124
+ * `searchableFields` is echoed verbatim by clients as the `$searchFields`
1125
+ * override, and the #4254 ingress gate
1126
+ * (`assertSearchFieldsAreSearchable`, `@objectstack/metadata-protocol`)
1127
+ * refuses any entry outside the object's server-resolved allowed set — so
1128
+ * ONE lookup-typed entry 400s EVERY toolbar search on that list, for every
1129
+ * role, and until #4830 `compile`/`validate` passed it in silence. The
1130
+ * allowed set here is computed by the very function the runtime gate and
1131
+ * the engine consult — {@link resolveSearchFieldResolution}
1132
+ * (`@objectstack/spec/data`) — never by a second copy of its type list, so
1133
+ * linter, gate and engine cannot drift apart (the same one-source move
1134
+ * #4254 made between gate and engine).
1135
+ *
1136
+ * The OBJECT's own `searchableFields` stays existence-only: the runtime's
1137
+ * declared branch filters by existence, never by type, so a json or lookup
1138
+ * column declared THERE is a choice the engine executes (a `$contains` over
1139
+ * the raw column), not a 400. Flagging it would reject metadata the runtime
1140
+ * accepts — the false finding that makes authors stop trusting the linter
1141
+ * (ADR-0072 D1).
1142
+ *
1143
+ * Three skips keep false positives near zero (ADR-0072 D1):
1144
+ *
1145
+ * 1. An object this stack does not define. It may come from another package,
1146
+ * and a field map we cannot see cannot be judged (the same skip the
1147
+ * page/flow/widget rules take).
1148
+ * 2. An object that declares no field map at all — external objects and
1149
+ * datasource-introspected schemas whose columns are resolved at runtime.
1150
+ * 3. Registry-injected system columns, which exist at runtime but never
1151
+ * appear in authored `fields` — the package-shared `SYSTEM_FIELDS`
1152
+ * (`system-fields.ts`), derived from the spec's own declarations rather
1153
+ * than hand-copied (#4330). The admissibility check also skips them:
1154
+ * their runtime field metadata (type, hidden) is registry-owned and not
1155
+ * visible here, and judging a column we cannot see risks the false
1156
+ * positive this list exists to avoid. (Cost asymmetry: a system column
1157
+ * the runtime would refuse — `created_by` in a view's narrowing — is a
1158
+ * missed finding, not a wrong one.)
1159
+ *
1160
+ * Dotted paths are NOT skipped here, unlike every sibling rule. Elsewhere
1161
+ * `owner_id.name` is left alone because the query engine resolves the traversal;
1162
+ * search does not — `resolveSearchFields` matches the field map by exact string,
1163
+ * so a dotted entry is dropped exactly like a typo. Skipping it would exempt the
1164
+ * one wrong spelling most likely to be borrowed from `select`/`sort`.
1165
+ */
909
1166
 
910
1167
  declare const SEARCHABLE_FIELD_UNKNOWN = "searchable-field-unknown";
1168
+ declare const SEARCHABLE_FIELD_UNSEARCHABLE = "searchable-field-unsearchable";
911
1169
  type SearchableFieldSeverity = 'error' | 'warning';
912
1170
  interface SearchableFieldFinding {
913
1171
  /** Always `error` — a stale entry narrows, widens or refuses the search (see module note). */
@@ -923,7 +1181,19 @@ interface SearchableFieldFinding {
923
1181
  /** How to fix it. */
924
1182
  hint: string;
925
1183
  }
926
- type AnyRec$b = Record<string, unknown>;
1184
+ /**
1185
+ * Which runtime judgment applies to the declaration being checked:
1186
+ *
1187
+ * - `'canonical'` — the object's own `searchableFields`. The runtime honors
1188
+ * any entry that exists (existence-filtered, never type-filtered), so only
1189
+ * existence is checked.
1190
+ * - `'narrowing'` — a list view's `searchableFields` (metadata or react
1191
+ * surface). Clients echo it as the `$searchFields` override, which the
1192
+ * #4254 ingress gate intersects with the object's allowed set — entries the
1193
+ * runtime would refuse are flagged (#4830).
1194
+ */
1195
+ type SearchableFieldRole = 'canonical' | 'narrowing';
1196
+ type AnyRec$g = Record<string, unknown>;
927
1197
  /**
928
1198
  * Validate every `searchableFields` declaration in the stack — the object's own
929
1199
  * (the canonical set, ADR-0061) and the list views that narrow it. Returns
@@ -934,7 +1204,7 @@ type AnyRec$b = Record<string, unknown>;
934
1204
  * `validate-react-page-props` — the gate that already parses that source —
935
1205
  * runs the same `checkSearchableFieldList` core on it (#4329).
936
1206
  */
937
- declare function validateSearchableFields(stack: AnyRec$b): SearchableFieldFinding[];
1207
+ declare function validateSearchableFields(stack: AnyRec$g): SearchableFieldFinding[];
938
1208
 
939
1209
  declare const ACTION_NAME_UNDEFINED = "action-name-undefined";
940
1210
  type ActionNameRefSeverity = 'error' | 'warning';
@@ -952,12 +1222,84 @@ interface ActionNameRefFinding {
952
1222
  /** How to fix it. */
953
1223
  hint: string;
954
1224
  }
955
- type AnyRec$a = Record<string, unknown>;
1225
+ type AnyRec$f = Record<string, unknown>;
956
1226
  /**
957
1227
  * Validate every name-bound action reference in a stack. Returns findings
958
1228
  * (empty = clean).
959
1229
  */
960
- declare function validateActionNameRefs(stack: AnyRec$a): ActionNameRefFinding[];
1230
+ declare function validateActionNameRefs(stack: AnyRec$f): ActionNameRefFinding[];
1231
+
1232
+ /**
1233
+ * [ADR-0078 Phase 3 — Tier-A `action-locations`] An action nobody placed.
1234
+ *
1235
+ * `locations` is an action's placement declaration. An action that omits it —
1236
+ * and that no view names in `bulkActions` / `bulkActionDefs` / `rowActions` —
1237
+ * has no surface at all: it parses, it publishes, Setup lists it, and no user
1238
+ * can ever click it. ADR-0078 names this shape in its opening paragraph ("a
1239
+ * `summary` with no `summaryOperations`; **an `action` with no `locations`**;
1240
+ * … Each parses, 'renders', reports success — and does nothing") and Phase 3
1241
+ * asks for exactly this rule, one verified shape at a time.
1242
+ *
1243
+ * The renderer half is now unambiguous: objectui#3142 collapsed four
1244
+ * disagreeing renderers onto one predicate — an action renders at a location
1245
+ * only if it DECLARES that location. Before that, `action:bar` and the record
1246
+ * header showed an undeclared action *everywhere*, which is what made this
1247
+ * shape look alive; it is measurably inert as of objectui 17.1.
1248
+ *
1249
+ * ## What is NOT flagged, and why
1250
+ *
1251
+ * **`locations: []` — a deliberate headless action.** `content/docs/ui/
1252
+ * actions.mdx` ("Headless actions: declare it, then hide it") documents the
1253
+ * empty array as a first-class shape: the action stays callable over REST /
1254
+ * MCP / AI and keeps its capability gate, param contract and audit trail,
1255
+ * while claiming no UI surface. ADR-0110 D3 refuses an *undeclared* handler,
1256
+ * so a headless declaration is the only legal way to expose such an action —
1257
+ * flagging it would fight that ADR. The distinction this rule draws is
1258
+ * therefore between an author who said "nowhere, deliberately" (`[]`) and one
1259
+ * who never said anything at all (key absent).
1260
+ *
1261
+ * **Actions a view places by NAME.** Naming an action in a list view's
1262
+ * `bulkActions` or `bulkActionDefs` IS its placement — the selection bar is
1263
+ * driven by the view, never by `locations` (that is what the retired
1264
+ * `action.bulkEnabled` tombstone prescribes, and what objectui#3139's
1265
+ * aggregate bulk mode relies on: an action that only makes sense over a
1266
+ * selection has no single-record location by construction). `rowActions` is
1267
+ * exempted on the same zero-false-positive posture (ADR-0072 D1): it is the
1268
+ * same field pair on the same container, and an author who named an action
1269
+ * there has stated an intent — a name that resolves to nothing is already
1270
+ * `action-name-undefined`'s job, not this rule's.
1271
+ *
1272
+ * Scope note: this rule asks only "did anyone place this action?". It
1273
+ * deliberately does NOT check that a declared location is one a renderer
1274
+ * actually serves, nor that a view's named action belongs to that view's
1275
+ * object — distinct classes with their own rules. Cross-package placement (a
1276
+ * view in another installed package naming this action) is the one legitimate
1277
+ * miss, which is why this is a **warning**: like every other "declared but
1278
+ * does nothing" finding in this package (`validateSemanticRoles`,
1279
+ * `lintLivenessProperties`), it is high-signal and never fatal.
1280
+ */
1281
+ declare const ACTION_NO_PLACEMENT = "action-no-placement";
1282
+ type ActionLocationsSeverity = 'error' | 'warning';
1283
+ interface ActionLocationsFinding {
1284
+ /** Always `warning` — cross-package placement is a legitimate miss. */
1285
+ severity: ActionLocationsSeverity;
1286
+ /** Diagnostic rule id. */
1287
+ rule: string;
1288
+ /** Human-readable location, e.g. `action "crm_convert_lead"`. */
1289
+ where: string;
1290
+ /** Config path, e.g. `actions[2]` or `objects[0].actions[1]`. */
1291
+ path: string;
1292
+ /** What is wrong. */
1293
+ message: string;
1294
+ /** How to fix it. */
1295
+ hint: string;
1296
+ }
1297
+ type AnyRec$e = Record<string, unknown>;
1298
+ /**
1299
+ * Flag every action that declares no placement and that no view places by
1300
+ * name. Returns findings (empty = clean).
1301
+ */
1302
+ declare function validateActionLocations(stack: AnyRec$e): ActionLocationsFinding[];
961
1303
 
962
1304
  /**
963
1305
  * Shared page-component traversal for the lint rules that inspect
@@ -983,7 +1325,7 @@ declare function validateActionNameRefs(stack: AnyRec$a): ActionNameRefFinding[]
983
1325
  * author never wrote, so those pages are skipped here and covered by
984
1326
  * `validate-jsx-pages` / `validate-react-page-props` instead.
985
1327
  */
986
- type AnyRec$9 = Record<string, unknown>;
1328
+ type AnyRec$d = Record<string, unknown>;
987
1329
 
988
1330
  /**
989
1331
  * [ADR-0078 — completeness] Field-reference integrity for page components
@@ -1057,7 +1399,7 @@ interface PageFieldFinding {
1057
1399
  hint: string;
1058
1400
  }
1059
1401
 
1060
- declare function validatePageFieldBindings(stack: AnyRec$9): PageFieldFinding[];
1402
+ declare function validatePageFieldBindings(stack: AnyRec$d): PageFieldFinding[];
1061
1403
 
1062
1404
  /**
1063
1405
  * [ADR-0021 — semantic layer] Chart-binding integrity for the surfaces the
@@ -1114,7 +1456,7 @@ interface ChartBindingFinding {
1114
1456
  hint: string;
1115
1457
  }
1116
1458
 
1117
- declare function validateChartBindings(stack: AnyRec$9): ChartBindingFinding[];
1459
+ declare function validateChartBindings(stack: AnyRec$d): ChartBindingFinding[];
1118
1460
 
1119
1461
  declare const NAV_OBJECT_UNGRANTED = "nav-object-ungranted";
1120
1462
  type NavAccessSeverity = 'error' | 'warning';
@@ -1132,12 +1474,12 @@ interface NavAccessFinding {
1132
1474
  /** How to fix it. */
1133
1475
  hint: string;
1134
1476
  }
1135
- type AnyRec$8 = Record<string, unknown>;
1477
+ type AnyRec$c = Record<string, unknown>;
1136
1478
  /**
1137
1479
  * Validate that every object a stack's navigation exposes is readable by at
1138
1480
  * least one permission set the stack declares. Returns findings (empty = clean).
1139
1481
  */
1140
- declare function validateNavAccess(stack: AnyRec$8): NavAccessFinding[];
1482
+ declare function validateNavAccess(stack: AnyRec$c): NavAccessFinding[];
1141
1483
 
1142
1484
  declare const TRANSLATION_TARGET_UNKNOWN = "translation-target-unknown";
1143
1485
  declare const TRANSLATION_OPTION_KEY_UNKNOWN = "translation-option-key-unknown";
@@ -1156,12 +1498,12 @@ interface TranslationRefFinding {
1156
1498
  /** How to fix it. */
1157
1499
  hint: string;
1158
1500
  }
1159
- type AnyRec$7 = Record<string, unknown>;
1501
+ type AnyRec$b = Record<string, unknown>;
1160
1502
  /**
1161
1503
  * Validate every reference a translation bundle makes against the metadata it
1162
1504
  * claims to translate. Returns findings (empty = clean).
1163
1505
  */
1164
- declare function validateTranslationReferences(stack: AnyRec$7): TranslationRefFinding[];
1506
+ declare function validateTranslationReferences(stack: AnyRec$b): TranslationRefFinding[];
1165
1507
 
1166
1508
  /**
1167
1509
  * [ADR-0064 §3] Skill ↔ agent surface affinity (issue #3820).
@@ -1206,12 +1548,12 @@ interface AiSurfaceAffinityFinding {
1206
1548
  /** How to fix it. */
1207
1549
  hint: string;
1208
1550
  }
1209
- type AnyRec$6 = Record<string, unknown>;
1551
+ type AnyRec$a = Record<string, unknown>;
1210
1552
  /**
1211
1553
  * Validate every in-stack agent→skill binding against the ADR-0064 §3 surface
1212
1554
  * affinity contract. Returns findings (empty = clean).
1213
1555
  */
1214
- declare function validateAiSurfaceAffinity(stack: AnyRec$6): AiSurfaceAffinityFinding[];
1556
+ declare function validateAiSurfaceAffinity(stack: AnyRec$a): AiSurfaceAffinityFinding[];
1215
1557
 
1216
1558
  declare const AI_SKILL_TOOL_UNRESOLVED = "ai-skill-tool-unresolved";
1217
1559
  type AiToolRefSeverity = 'error' | 'warning';
@@ -1229,12 +1571,12 @@ interface AiToolRefFinding {
1229
1571
  /** How to fix it. */
1230
1572
  hint: string;
1231
1573
  }
1232
- type AnyRec$5 = Record<string, unknown>;
1574
+ type AnyRec$9 = Record<string, unknown>;
1233
1575
  /**
1234
1576
  * Validate every `skill.tools[]` reference in a stack. Returns findings
1235
1577
  * (empty = clean).
1236
1578
  */
1237
- declare function validateAiToolReferences(stack: AnyRec$5): AiToolRefFinding[];
1579
+ declare function validateAiToolReferences(stack: AnyRec$9): AiToolRefFinding[];
1238
1580
 
1239
1581
  /**
1240
1582
  * [ADR-0063 §2] `stack.agents` is a platform-internal slot (issue #3820).
@@ -1279,12 +1621,12 @@ interface AiAgentAuthoringFinding {
1279
1621
  /** How to fix it. */
1280
1622
  hint: string;
1281
1623
  }
1282
- type AnyRec$4 = Record<string, unknown>;
1624
+ type AnyRec$8 = Record<string, unknown>;
1283
1625
  /**
1284
1626
  * Flag every agent declared in a stack. Returns findings (empty = clean,
1285
1627
  * which is what every app package should be).
1286
1628
  */
1287
- declare function validateAiAgentAuthoring(stack: AnyRec$4): AiAgentAuthoringFinding[];
1629
+ declare function validateAiAgentAuthoring(stack: AnyRec$8): AiAgentAuthoringFinding[];
1288
1630
 
1289
1631
  type HookBodyWriteSeverity = 'warning';
1290
1632
  interface HookBodyWriteFinding {
@@ -1336,7 +1678,7 @@ interface BodyWritePatternExclusion {
1336
1678
  declare const HOOK_BODY_WRITE_PATTERN_IDS: readonly string[];
1337
1679
  /** Ledger shapes this rule leaves alone, each with its reason. */
1338
1680
  declare const HOOK_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
1339
- type AnyRec$3 = Record<string, unknown>;
1681
+ type AnyRec$7 = Record<string, unknown>;
1340
1682
  /** One statically-extracted field write found in an L2 body. */
1341
1683
  interface ExtractedHookBodyWrite {
1342
1684
  /** Which {@link HOOK_BODY_WRITE_PATTERNS} entry matched. */
@@ -1381,7 +1723,7 @@ declare function extractHookBodyWriteSet(source: string): ExtractedHookBodyWrite
1381
1723
  * Validate L2 hook-body writes against target-object field declarations.
1382
1724
  * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.
1383
1725
  */
1384
- declare function validateHookBodyWrites(stack: AnyRec$3): HookBodyWriteFinding[];
1726
+ declare function validateHookBodyWrites(stack: AnyRec$7): HookBodyWriteFinding[];
1385
1727
 
1386
1728
  type ActionBodyWriteSeverity = 'warning';
1387
1729
  interface ActionBodyWriteFinding {
@@ -1423,12 +1765,12 @@ declare const ACTION_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[]
1423
1765
  declare const ACTION_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[];
1424
1766
  /** The subset the discarded-record-write check sees. */
1425
1767
  declare const ACTION_RECORD_WRITE_PATTERNS: readonly HookBodyWritePattern[];
1426
- type AnyRec$2 = Record<string, unknown>;
1768
+ type AnyRec$6 = Record<string, unknown>;
1427
1769
  /**
1428
1770
  * Validate L2 action-body writes against target-object field declarations.
1429
1771
  * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.
1430
1772
  */
1431
- declare function validateActionBodyWrites(stack: AnyRec$2): ActionBodyWriteFinding[];
1773
+ declare function validateActionBodyWrites(stack: AnyRec$6): ActionBodyWriteFinding[];
1432
1774
 
1433
1775
  type FlowNodeWriteSeverity = 'error';
1434
1776
  interface FlowNodeWriteFinding {
@@ -1468,51 +1810,13 @@ interface FlowWriteNodeDeferral {
1468
1810
  * Deleting this array would turn that forced decision back into a default.
1469
1811
  */
1470
1812
  declare const FLOW_WRITE_NODE_TYPES_DEFERRED: readonly FlowWriteNodeDeferral[];
1471
- type AnyRec$1 = Record<string, unknown>;
1813
+ type AnyRec$5 = Record<string, unknown>;
1472
1814
  /**
1473
1815
  * Validate flow write-node `fields` keys against the target object's declared
1474
1816
  * fields. Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse
1475
1817
  * stacks.
1476
1818
  */
1477
- declare function validateFlowNodeWrites(stack: AnyRec$1): FlowNodeWriteFinding[];
1478
-
1479
- type ReferenceIntegritySeverity = 'error' | 'warning';
1480
- /**
1481
- * The shape every rule in the suite already returns. Declared here so callers
1482
- * can hold one type instead of a six-way union.
1483
- */
1484
- interface ReferenceIntegrityFinding {
1485
- /** `error` = the reference is dead; `warning` = it may resolve elsewhere, or the miss is inert. */
1486
- severity: ReferenceIntegritySeverity;
1487
- /** Diagnostic rule id (stable; used by allowlists and docs). */
1488
- rule: string;
1489
- /** Human-readable location. */
1490
- where: string;
1491
- /** Config path. */
1492
- path: string;
1493
- /** What is wrong. */
1494
- message: string;
1495
- /** How to fix it. */
1496
- hint: string;
1497
- }
1498
- /** One member of the suite. `name` is the exported function's name — the id a wiring test can assert on. */
1499
- interface ReferenceIntegrityRule {
1500
- name: string;
1501
- run: (stack: Record<string, unknown>) => ReferenceIntegrityFinding[];
1502
- }
1503
- /**
1504
- * Every reference-integrity rule, in the order their findings are reported.
1505
- *
1506
- * ADDING A RULE: append it here and it runs on `validate`, `lint` and
1507
- * `compile` at once. Do not re-wire the commands.
1508
- */
1509
- declare const REFERENCE_INTEGRITY_RULES: readonly ReferenceIntegrityRule[];
1510
- /**
1511
- * Run every reference-integrity rule over a stack. Returns the concatenated
1512
- * findings (empty = clean). Pure: no I/O, safe on both the schema-parsed stack
1513
- * and the raw/normalized config the `lint` path carries.
1514
- */
1515
- declare function validateReferenceIntegrity(stack: Record<string, unknown>): ReferenceIntegrityFinding[];
1819
+ declare function validateFlowNodeWrites(stack: AnyRec$5): FlowNodeWriteFinding[];
1516
1820
 
1517
1821
  /**
1518
1822
  * [ADR-0090 D6] Access-matrix snapshot — authoring-time companion to the
@@ -1529,13 +1833,211 @@ declare function validateReferenceIntegrity(stack: Record<string, unknown>): Ref
1529
1833
  * AI may draft grants freely; it cannot silently change who can do what.
1530
1834
  */
1531
1835
 
1532
- type AnyRec = Record<string, unknown>;
1836
+ type AnyRec$4 = Record<string, unknown>;
1533
1837
  /** Build the sorted access matrix for a normalized stack. */
1534
- declare function buildAccessMatrix(stack: AnyRec): AccessMatrix;
1838
+ declare function buildAccessMatrix(stack: AnyRec$4): AccessMatrix;
1535
1839
  /**
1536
1840
  * Semantic diff between two matrices — human-review lines, empty = identical.
1537
1841
  * Ordered: removals, additions, then per-entry bit/scope changes.
1538
1842
  */
1539
1843
  declare function diffAccessMatrix(before: AccessMatrix, after: AccessMatrix): string[];
1540
1844
 
1541
- export { ACTION_BODY_WRITE_EXCLUSIONS, ACTION_BODY_WRITE_PATTERNS, ACTION_BODY_WRITE_PATTERN_IDS, ACTION_BODY_WRITE_UNKNOWN_FIELD, ACTION_NAME_UNDEFINED, ACTION_RECORD_WRITE_DISCARDED, ACTION_RECORD_WRITE_PATTERNS, ACTION_RECORD_WRITE_PATTERN_IDS, AGENT_AUTHORING_WITHDRAWN, AI_SKILL_SURFACE_MISMATCH, AI_SKILL_TOOL_UNRESOLVED, APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY, APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED, APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER, APPROVAL_APPROVER_TYPE_DEPRECATED, APPROVAL_APPROVER_TYPE_UNKNOWN, APPROVAL_DECISION_OUTPUTS_RESERVED, APPROVAL_ESCALATION_REASSIGN_NO_TARGET, APPROVAL_EXPRESSION_INVALID, APPROVAL_EXPRESSION_NO_EMPTY_POLICY, type ActionBodyWriteExclusion, type ActionBodyWriteFinding, type ActionBodyWriteSeverity, type ActionNameRefFinding, type ActionNameRefSeverity, type AiAgentAuthoringFinding, type AiAgentAuthoringSeverity, type AiSurfaceAffinityFinding, type AiSurfaceAffinitySeverity, type AiToolRefFinding, type AiToolRefSeverity, type ApprovalApproverFinding, type ApprovalApproverSeverity, type BodyWritePatternExclusion, CAPABILITY_REFERENCE_UNKNOWN, CHART_AXIS_NOT_SELECTED, CHART_CONFIG_MISSING, CHART_DATASET_UNKNOWN, CHART_DIMENSION_UNKNOWN, CHART_FIELD_UNKNOWN, CHART_MEASURE_UNKNOWN, type CapabilityRefFinding, type CapabilityRefSeverity, type ChartBindingFinding, type ChartBindingSeverity, DASHBOARD_ACTION_ROUTE_UNRESOLVED, DASHBOARD_ACTION_TARGET_UNDEFINED, DASHBOARD_FILTER_FIELD_UNKNOWN, type DashboardActionRefFinding, type DashboardActionRefSeverity, type ExprIssue, type ExtractedHookBodyWrite, type ExtractedHookBodyWriteSet, FIELD_GROUP_EMPTY, FIELD_GROUP_UNDECLARED, FILTER_TOKEN_UNKNOWN, FLOW_DRAFT_STATUS_AMBIGUOUS, FLOW_NODE_WRITE_UNKNOWN_FIELD, FLOW_TEMPLATE_LOOKUP_TRAVERSAL, FLOW_TEMPLATE_UNKNOWN_FIELD, FLOW_TRIGGER_UNKNOWN_OBJECT, FLOW_UPDATE_READONLY_FIELD, FLOW_UPDATE_READONLY_WHEN_FIELD, FLOW_WRITE_NODE_TYPES, FLOW_WRITE_NODE_TYPES_DEFERRED, FORM_COLSPAN_ABSOLUTE, FORM_FIELD_UNKNOWN, type FilterTokenFinding, type FilterTokenSeverity, type FlowNodeWriteFinding, type FlowNodeWriteSeverity, type FlowTemplatePathFinding, type FlowTemplatePathSeverity, type FlowTriggerReadinessFinding, type FlowTriggerReadinessSeverity, type FlowWriteNodeDeferral, type FormLayoutFinding, type FormLayoutSeverity, HOOK_BODY_WRITE_EXCLUSIONS, HOOK_BODY_WRITE_PATTERNS, HOOK_BODY_WRITE_PATTERN_IDS, HOOK_BODY_WRITE_UNKNOWN_FIELD, type HookBodyWriteFinding, type HookBodyWritePattern, type HookBodyWriteSeverity, type JsxPageFinding, type JsxPageSeverity, LIST_VIEW_FILTERS_IN_VIEWS_MODE, type ListViewModeFinding, type ListViewModeSeverity, MEASURE_AGGREGATE_INCOHERENT, NAV_OBJECT_UNGRANTED, type NavAccessFinding, type NavAccessSeverity, OBJECT_REFERENCE_UNKNOWN, OBJECT_REFERENCE_UNREGISTERED_PLATFORM, ORG_AXIS_CROSS_ORG_BU_GRANT, ORG_AXIS_PERMISSION_INHERITANCE, type ObjectRefFinding, type ObjectRefSeverity, type OrgAxisFinding, type OrgAxisSeverity, PAGE_FIELD_UNKNOWN, PAGE_SOURCE_CLASSNAME, type PageFieldFinding, type PageFieldSeverity, REACT_CHART_AGGREGATE_INVALID, REACT_CHART_AXIS_UNKNOWN, REACT_CHART_FIELD_UNKNOWN, REFERENCE_INTEGRITY_RULES, type ReactPageFinding, type ReactPageSeverity, type ReactPropFinding, type ReactPropSeverity, type ReadonlyFlowWriteFinding, type ReadonlyFlowWriteSeverity, type RecordTitleFinding, type RecordTitleSeverity, type ReferenceIntegrityFinding, type ReferenceIntegrityRule, type ReferenceIntegritySeverity, SEARCHABLE_FIELD_UNKNOWN, SECURITY_ANCHOR_HIGH_PRIVILEGE, SECURITY_BOOK_AUDIENCE_UNKNOWN_SET, SECURITY_DELEGATION_MISSING_REASON, SECURITY_EXTERNAL_WIDER, SECURITY_GRANT_EXPIRED_AT_AUTHORING, SECURITY_MASTER_DETAIL_UNGRANTED, SECURITY_OWD_ALIAS, SECURITY_OWD_UNSET, SECURITY_PRIVATE_NO_READSCOPE, SECURITY_ROLE_WORD, SECURITY_WILDCARD_VAMA, SEED_INSERT_MODE_DUPLICATES_ON_REPLAY, SEED_VALUE_OUTSIDE_STATE_MACHINE, SEMANTIC_ROLE_FIELD_UNKNOWN, STYLE_CLASSNAME_TAILWIND, STYLE_NODE_MISSING_ID, STYLE_RESPONSIVE_NO_BASE, STYLE_UNKNOWN_CSS_PROPERTY, STYLE_UNKNOWN_TOKEN, type SearchableFieldFinding, type SearchableFieldSeverity, type SecurityFinding, type SecuritySeverity, type SeedReplaySafetyFinding, type SeedReplaySafetySeverity, type SeedStateMachineFinding, type SeedStateMachineSeverity, type SemanticRoleFinding, type SemanticRoleSeverity, type SourceStyleFinding, type SourceStyleSeverity, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, TRANSLATION_OPTION_KEY_UNKNOWN, TRANSLATION_TARGET_UNKNOWN, type TranslationRefFinding, type TranslationRefSeverity, VIEW_CONTAINER_SHAPE, VISIBILITY_ALIAS_DEPRECATED, VISIBILITY_ROOT_MISLAYERED, type ViewContainerFinding, type ViewContainerSeverity, type VisibilityFinding, type VisibilityLayer, type VisibilityOptions, type VisibilitySeverity, WIDGET_DATASET_UNKNOWN, WIDGET_DIMENSION_UNKNOWN, WIDGET_MEASURE_UNKNOWN, type WidgetBindingFinding, type WidgetBindingSeverity, buildAccessMatrix, diffAccessMatrix, extractHookBodyWriteSet, extractHookBodyWrites, validateActionBodyWrites, validateActionNameRefs, validateAiAgentAuthoring, validateAiSurfaceAffinity, validateAiToolReferences, validateApprovalApprovers, validateCapabilityReferences, validateChartBindings, validateDashboardActionRefs, validateFilterTokens, validateFlowNodeWrites, validateFlowTemplatePaths, validateFlowTriggerReadiness, validateFormLayout, validateHookBodyWrites, validateJsxPages, validateListViewMode, validateNavAccess, validateObjectReferences, validateOrgAxisRedLines, validatePageFieldBindings, validatePageSourceStyling, validateReactPageProps, validateReactPages, validateReadonlyFlowWrites, validateRecordTitle, validateReferenceIntegrity, validateResponsiveStyles, validateSearchableFields, validateSecurityPosture, validateSeedReplaySafety, validateSeedStateMachine, validateSemanticRoles, validateStackExpressions, validateTranslationReferences, validateViewContainers, validateVisibilityPredicates, validateWidgetBindings };
1845
+ /**
1846
+ * Build-time lint for flow authoring ANTI-PATTERNS — metadata that is valid
1847
+ * (passes schema + expression checks) but is semantically a footgun at runtime.
1848
+ * Most are emitted as WARNINGS: they guide the author (very often an AI
1849
+ * generating templates) toward the robust pattern without failing the build on
1850
+ * a technically-legal construct.
1851
+ *
1852
+ * A finding carrying `severity: 'error'` FAILS the build. The bar is: **no
1853
+ * reading of the author's metadata does what it says, deterministically, on
1854
+ * every run.** Warning about such a shape is just a slower way of finding out.
1855
+ * That covers two kinds, and only these:
1856
+ *
1857
+ * - **The runtime refuses.** {@link FLOW_RUNAS_UNSCOPED} — a user-less trigger
1858
+ * with `runAs:'user'` has no identity to scope to, so the data operation is
1859
+ * refused outright (#3760).
1860
+ * - **The declaration is inert and the route silently differs from what is
1861
+ * written.** {@link FLOW_BRANCH_LABEL_UNMATCHED} — a decision computes a
1862
+ * branch no out-edge carries, so the branch is discarded and every out-edge
1863
+ * is considered instead. {@link FLOW_DEFAULT_EDGE_WITH_CONDITION} — an edge
1864
+ * that is both the default and conditional; the condition wins and the
1865
+ * marker routes nothing. Neither *fails*; both are wrong every time, and
1866
+ * silently, which is worse (#4414).
1867
+ *
1868
+ * The bar is deliberately about *provability*, not severity of consequence. A
1869
+ * shape with a legitimate reading stays a warning even when it is usually a
1870
+ * mistake — {@link FLOW_DECISION_UNCONDITIONAL_BRANCH} is normally a guard that
1871
+ * does not guard, but a decision with one guarded and one unconditional out-edge
1872
+ * is a legal "maybe notify, always continue" fan-out, and
1873
+ * {@link FLOW_MULTIPLE_DEFAULT_EDGES} can genuinely mean "when nothing matched,
1874
+ * do both". Failing a customer's build on a shape we cannot prove wrong is a
1875
+ * worse trade than letting the warning be ignored.
1876
+ *
1877
+ * #1874 — time-relative rules via record-change date-EQUALITY. A start-node
1878
+ * trigger condition like `end_date == daysFromNow(60)` on a `record-*` trigger
1879
+ * only fires if the record happens to be written on that exact day; the robust
1880
+ * shape is a daily SCHEDULE trigger + a range query. We flag the equality form
1881
+ * specifically (range operators `>=`/`<=` are not flagged — they're the building
1882
+ * block of the correct pattern), keeping false positives near zero.
1883
+ */
1884
+ interface FlowLintFinding {
1885
+ where: string;
1886
+ message: string;
1887
+ hint: string;
1888
+ rule: string;
1889
+ /**
1890
+ * `'error'` FAILS the build; `'warning'` (the default when absent) prints and
1891
+ * continues. Most rules here flag a technically-legal footgun and stay
1892
+ * advisory. A rule is only promoted to `'error'` when the shape it flags is a
1893
+ * *guaranteed* runtime failure — then a warning would just be a slower way of
1894
+ * finding out (#3760).
1895
+ *
1896
+ * `os build` and `os validate` both filter on this field, so promoting a rule
1897
+ * gates both surfaces at once — neither can report clean while the other
1898
+ * rejects the same stack (#3782).
1899
+ */
1900
+ severity?: 'error' | 'warning';
1901
+ }
1902
+ type AnyRec$3 = Record<string, unknown>;
1903
+ declare const FLOW_TIME_RELATIVE_ANTIPATTERN = "flow-time-relative-antipattern";
1904
+ declare const FLOW_DATE_EQUALITY_FILTER = "flow-date-equality-filter";
1905
+ declare const FLOW_PHANTOM_AGGREGATION = "flow-phantom-aggregation";
1906
+ declare const FLOW_DOUBLE_BRACE_INTERP = "flow-double-brace-interpolation";
1907
+ declare const FLOW_BARE_DOLLAR_REF = "flow-bare-dollar-reference";
1908
+ declare const FLOW_APPROVAL_REVISE_DEAD_END = "flow-approval-revise-dead-end";
1909
+ declare const FLOW_APPROVAL_REVISE_UNMARKED_BACKEDGE = "flow-approval-revise-unmarked-backedge";
1910
+ declare const FLOW_APPROVAL_REVISE_DISABLED = "flow-approval-revise-disabled";
1911
+ /**
1912
+ * #3760 — renamed from `flow-schedule-runas-unscoped`. The old id named the
1913
+ * *schedule*, which was never the boundary: the rule is about a trigger that
1914
+ * resolves NO USER, and a schedule is only the most obvious such trigger.
1915
+ */
1916
+ declare const FLOW_RUNAS_UNSCOPED = "flow-runas-unscoped";
1917
+ declare const FLOW_ERROR_LABEL_NOT_FAULT = "flow-error-label-not-fault";
1918
+ /** #4414 — the four ways a decision's declared branching fails to route. */
1919
+ declare const FLOW_BRANCH_LABEL_UNMATCHED = "flow-branch-label-unmatched";
1920
+ declare const FLOW_DECISION_UNCONDITIONAL_BRANCH = "flow-decision-unconditional-branch";
1921
+ declare const FLOW_DEFAULT_EDGE_WITH_CONDITION = "flow-default-edge-with-condition";
1922
+ declare const FLOW_MULTIPLE_DEFAULT_EDGES = "flow-multiple-default-edges";
1923
+ /** #4414 — `config.condition` on a node whose executor never reads it. */
1924
+ declare const FLOW_INERT_NODE_CONDITION = "flow-inert-node-condition";
1925
+ /**
1926
+ * Lint every flow's start node for known authoring anti-patterns. Returns a
1927
+ * (possibly empty) list of advisory findings — never throws, never fails a build.
1928
+ */
1929
+ declare function lintFlowPatterns(stack: AnyRec$3): FlowLintFinding[];
1930
+
1931
+ interface LivenessLintFinding {
1932
+ where: string;
1933
+ message: string;
1934
+ hint: string;
1935
+ rule: string;
1936
+ }
1937
+ declare const LIVENESS_DEAD_PROPERTY = "liveness-dead-property";
1938
+ declare const LIVENESS_EXPERIMENTAL_PROPERTY = "liveness-experimental-property";
1939
+ type AnyRec$2 = Record<string, unknown>;
1940
+ /**
1941
+ * Lint the compiled stack for authored properties the liveness ledger flags as
1942
+ * misleading. Advisory only — returns findings, never throws. Covers every
1943
+ * governed metadata type: objects (incl. `enable.*`) and their fields walk
1944
+ * bespoke nesting; the remaining types are flat stack collections. Container
1945
+ * properties fan out over arrays (each flow node, each dataset measure). The
1946
+ * mechanism stays ledger-driven — coverage grows by marking more entries
1947
+ * `authorWarn` rather than touching this code.
1948
+ */
1949
+ declare function lintLivenessProperties(stack: AnyRec$2): LivenessLintFinding[];
1950
+
1951
+ interface AutonumberLintFinding {
1952
+ where: string;
1953
+ message: string;
1954
+ hint: string;
1955
+ rule: string;
1956
+ severity: 'error' | 'warning';
1957
+ }
1958
+ type AnyRec$1 = Record<string, unknown>;
1959
+ declare const AUTONUMBER_UNKNOWN_FIELD = "autonumber-references-unknown-field";
1960
+ declare const AUTONUMBER_OPTIONAL_FIELD = "autonumber-references-optional-field";
1961
+ declare const AUTONUMBER_SELF_REFERENCE = "autonumber-references-self";
1962
+ declare const AUTONUMBER_LITERAL_TOKEN = "autonumber-unrecognized-token";
1963
+ /**
1964
+ * Lint every `autonumber` field's format for unresolvable / fragile `{field}`
1965
+ * interpolation. Returns a (possibly empty) list of findings; never throws.
1966
+ */
1967
+ declare function lintAutonumberFormats(stack: AnyRec$1): AutonumberLintFinding[];
1968
+
1969
+ interface ViewRefFinding {
1970
+ where: string;
1971
+ message: string;
1972
+ hint: string;
1973
+ rule: string;
1974
+ severity: 'error' | 'warning';
1975
+ }
1976
+ type AnyRec = Record<string, any>;
1977
+ declare const VIEW_KEY_COLLISION = "view-key-collision";
1978
+ declare const VIEW_REF_FORM_TARGET_MISSING = "view-ref-form-target-missing";
1979
+ declare const VIEW_REF_FORM_TARGET_KIND = "view-ref-form-target-kind";
1980
+ declare function lintViewRefs(stack: AnyRec): ViewRefFinding[];
1981
+
1982
+ /**
1983
+ * Data-model best-practice lint rules.
1984
+ *
1985
+ * These rules encode the relationship / master-detail / roll-up conventions the
1986
+ * platform ships (see the objectstack-data and objectstack-ui skills, ADR-0035).
1987
+ * They run over the normalized object set and flag anti-patterns that an
1988
+ * author — human OR an AI generator — commonly produces. They are intentionally
1989
+ * heuristic: structural problems are `error`, likely-wrong choices are
1990
+ * `warning`, and "you probably want this" nudges are `suggestion`. None of them
1991
+ * block on a judgement call.
1992
+ *
1993
+ * The same rules double as the automated rubric for the metadata-generation
1994
+ * eval (see `score.ts`): a generated stack scores well exactly when it is
1995
+ * schema-valid AND lint-clean here.
1996
+ */
1997
+ type Severity = 'error' | 'warning' | 'suggestion';
1998
+ interface LintIssue {
1999
+ severity: Severity;
2000
+ rule: string;
2001
+ message: string;
2002
+ path: string;
2003
+ fix?: string;
2004
+ }
2005
+ declare const UNIQUE_DOUBLE_DECLARATION = "unique/double-declaration";
2006
+ /**
2007
+ * R10 — the same column carries BOTH a field-level `unique: true` and an
2008
+ * object-level single-column unique index (#3991).
2009
+ *
2010
+ * The two spellings are deliberately different (see `IndexSchema`): field-level
2011
+ * `unique: true` is tenant-scoped since #3696 — it materializes as
2012
+ * `(organization_id, col)`, unique *within* the tenant — while a declared index
2013
+ * is materialized over exactly the columns listed, i.e. platform-wide. Both are
2014
+ * legitimate on their own; together on one column they are never right:
2015
+ *
2016
+ * - On a tenant-scoped object they CONTRADICT. The stricter one wins
2017
+ * physically, so the global index enforces uniqueness and the tenant
2018
+ * composite becomes a constraint nothing can ever trip. One of the two
2019
+ * intents the author wrote is silently discarded.
2020
+ * - On a tenancy-less object they are exactly REDUNDANT — both describe the
2021
+ * same single-column unique index, under the same generated name.
2022
+ *
2023
+ * Tenancy is deliberately NOT inferred here: `organization_id` is injected by
2024
+ * the kernel at registration rather than authored, so an authoring-time guess
2025
+ * would be wrong half the time. The combination is worth flagging either way,
2026
+ * and the message names both readings so the author picks the one they meant.
2027
+ *
2028
+ * A field declared `unique: 'global'` is exempt: it already says
2029
+ * platform-wide, so the declared index restates the same intent rather than
2030
+ * contradicting it (still redundant, but not a silent loss of meaning).
2031
+ *
2032
+ * Advisory. The resulting stack is well-defined — the cost is an intent that
2033
+ * never takes effect, not a broken artifact — so this never fails a build.
2034
+ */
2035
+ declare function lintUniqueDeclarations(objects: any[]): LintIssue[];
2036
+ /**
2037
+ * Lint the relationship / data-modeling conventions across the full object set.
2038
+ * Pure and deterministic — safe to call from both the `lint` command and the
2039
+ * metadata-generation scorer.
2040
+ */
2041
+ declare function lintDataModel(objects: any[]): LintIssue[];
2042
+
2043
+ export { ACTION_BODY_WRITE_EXCLUSIONS, ACTION_BODY_WRITE_PATTERNS, ACTION_BODY_WRITE_PATTERN_IDS, ACTION_BODY_WRITE_UNKNOWN_FIELD, ACTION_NAME_UNDEFINED, ACTION_NO_PLACEMENT, ACTION_RECORD_WRITE_DISCARDED, ACTION_RECORD_WRITE_PATTERNS, ACTION_RECORD_WRITE_PATTERN_IDS, AGENT_AUTHORING_WITHDRAWN, AI_SKILL_SURFACE_MISMATCH, AI_SKILL_TOOL_UNRESOLVED, APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY, APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED, APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER, APPROVAL_APPROVER_TYPE_DEPRECATED, APPROVAL_APPROVER_TYPE_UNKNOWN, APPROVAL_DECISION_OUTPUTS_RESERVED, APPROVAL_ESCALATION_REASSIGN_NO_TARGET, APPROVAL_EXPRESSION_INVALID, APPROVAL_EXPRESSION_NO_EMPTY_POLICY, AUTONUMBER_LITERAL_TOKEN, AUTONUMBER_OPTIONAL_FIELD, AUTONUMBER_SELF_REFERENCE, AUTONUMBER_UNKNOWN_FIELD, type ActionBodyWriteExclusion, type ActionBodyWriteFinding, type ActionBodyWriteSeverity, type ActionLocationsFinding, type ActionLocationsSeverity, type ActionNameRefFinding, type ActionNameRefSeverity, type AiAgentAuthoringFinding, type AiAgentAuthoringSeverity, type AiSurfaceAffinityFinding, type AiSurfaceAffinitySeverity, type AiToolRefFinding, type AiToolRefSeverity, type ApprovalApproverFinding, type ApprovalApproverSeverity, type AutonumberLintFinding, type BodyWritePatternExclusion, CAPABILITY_REFERENCE_UNKNOWN, CHART_AXIS_NOT_SELECTED, CHART_CONFIG_MISSING, CHART_DATASET_UNKNOWN, CHART_DIMENSION_UNKNOWN, CHART_FIELD_UNKNOWN, CHART_MEASURE_UNKNOWN, type CapabilityRefFinding, type CapabilityRefSeverity, type ChartBindingFinding, type ChartBindingSeverity, DASHBOARD_ACTION_ROUTE_UNRESOLVED, DASHBOARD_ACTION_TARGET_UNDEFINED, DASHBOARD_FILTER_FIELD_UNKNOWN, type DashboardActionRefFinding, type DashboardActionRefSeverity, type ExprIssue, type ExtractedHookBodyWrite, type ExtractedHookBodyWriteSet, FIELD_GROUP_EMPTY, FIELD_GROUP_UNDECLARED, FILTER_TOKEN_UNKNOWN, FLOW_APPROVAL_REVISE_DEAD_END, FLOW_APPROVAL_REVISE_DISABLED, FLOW_APPROVAL_REVISE_UNMARKED_BACKEDGE, FLOW_BARE_DOLLAR_REF, FLOW_BRANCH_LABEL_UNMATCHED, FLOW_DATE_EQUALITY_FILTER, FLOW_DECISION_UNCONDITIONAL_BRANCH, FLOW_DEFAULT_EDGE_WITH_CONDITION, FLOW_DOUBLE_BRACE_INTERP, FLOW_DRAFT_STATUS_AMBIGUOUS, FLOW_ERROR_LABEL_NOT_FAULT, FLOW_INERT_NODE_CONDITION, FLOW_MULTIPLE_DEFAULT_EDGES, FLOW_NODE_WRITE_UNKNOWN_FIELD, FLOW_PHANTOM_AGGREGATION, FLOW_RUNAS_UNSCOPED, FLOW_TEMPLATE_LOOKUP_TRAVERSAL, FLOW_TEMPLATE_UNKNOWN_FIELD, FLOW_TIME_RELATIVE_ANTIPATTERN, FLOW_TRIGGER_UNKNOWN_OBJECT, FLOW_UPDATE_READONLY_FIELD, FLOW_UPDATE_READONLY_WHEN_FIELD, FLOW_WRITE_NODE_TYPES, FLOW_WRITE_NODE_TYPES_DEFERRED, FORM_COLSPAN_ABSOLUTE, FORM_FIELD_UNKNOWN, type FilterTokenFinding, type FilterTokenSeverity, type FlowLintFinding, type FlowNodeWriteFinding, type FlowNodeWriteSeverity, type FlowTemplatePathFinding, type FlowTemplatePathSeverity, type FlowTriggerReadinessFinding, type FlowTriggerReadinessSeverity, type FlowWriteNodeDeferral, type FormLayoutFinding, type FormLayoutSeverity, type FunctionalCompletenessFinding, type FunctionalCompletenessSeverity, HOOK_BODY_WRITE_EXCLUSIONS, HOOK_BODY_WRITE_PATTERNS, HOOK_BODY_WRITE_PATTERN_IDS, HOOK_BODY_WRITE_UNKNOWN_FIELD, type HookBodyWriteFinding, type HookBodyWritePattern, type HookBodyWriteSeverity, type JsxPageFinding, type JsxPageSeverity, LIST_VIEW_FILTERS_IN_VIEWS_MODE, LIVENESS_DEAD_PROPERTY, LIVENESS_EXPERIMENTAL_PROPERTY, type LintIssue, type ListViewModeFinding, type ListViewModeSeverity, type LivenessLintFinding, MEASURE_AGGREGATE_INCOHERENT, NAV_OBJECT_UNGRANTED, NAV_TARGET_UNRESOLVED, NULL_GUARD_HINT, type NavAccessFinding, type NavAccessSeverity, type NavTargetRefFinding, type NavTargetRefSeverity, type NullGuardFinding, type NullGuardOptions, OBJECT_REFERENCE_UNKNOWN, OBJECT_REFERENCE_UNREGISTERED_PLATFORM, ORG_AXIS_CROSS_ORG_BU_GRANT, ORG_AXIS_PERMISSION_INHERITANCE, type ObjectRefFinding, type ObjectRefSeverity, type OrgAxisFinding, type OrgAxisSeverity, PAGE_FIELD_UNKNOWN, PAGE_SOURCE_CLASSNAME, type PageFieldFinding, type PageFieldSeverity, REACT_BLOCK_NEEDS_RECORD_CONTEXT, REACT_CHART_AGGREGATE_INVALID, REACT_CHART_AXIS_UNKNOWN, REACT_CHART_FIELD_UNKNOWN, REFERENCE_INTEGRITY_RULES, type ReactPageFinding, type ReactPageSeverity, type ReactPropFinding, type ReactPropSeverity, type ReadonlyFlowWriteFinding, type ReadonlyFlowWriteSeverity, type RecordTitleFinding, type RecordTitleSeverity, type ReferenceIntegrityFinding, type ReferenceIntegrityRule, type ReferenceIntegritySeverity, SEARCHABLE_FIELD_UNKNOWN, SEARCHABLE_FIELD_UNSEARCHABLE, SECURITY_ANCHOR_HIGH_PRIVILEGE, SECURITY_BOOK_AUDIENCE_UNKNOWN_SET, SECURITY_DELEGATION_MISSING_REASON, SECURITY_EXTERNAL_WIDER, SECURITY_GRANT_EXPIRED_AT_AUTHORING, SECURITY_MASTER_DETAIL_UNGRANTED, SECURITY_OWD_ALIAS, SECURITY_OWD_UNSET, SECURITY_PRIVATE_NO_READSCOPE, SECURITY_ROLE_WORD, SECURITY_WILDCARD_VAMA, SEED_INSERT_MODE_DUPLICATES_ON_REPLAY, SEED_VALUE_OUTSIDE_STATE_MACHINE, SEMANTIC_ROLE_FIELD_UNKNOWN, STYLE_CLASSNAME_TAILWIND, STYLE_NODE_MISSING_ID, STYLE_RESPONSIVE_NO_BASE, STYLE_UNKNOWN_CSS_PROPERTY, STYLE_UNKNOWN_TOKEN, type SearchableFieldFinding, type SearchableFieldRole, type SearchableFieldSeverity, type SecurityFinding, type SecuritySeverity, type SeedReplaySafetyFinding, type SeedReplaySafetySeverity, type SeedStateMachineFinding, type SeedStateMachineSeverity, type SemanticRoleFinding, type SemanticRoleSeverity, type Severity, type SourceStyleFinding, type SourceStyleSeverity, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, TRANSLATION_OPTION_KEY_UNKNOWN, TRANSLATION_TARGET_UNKNOWN, type TranslationRefFinding, type TranslationRefSeverity, UNIQUE_DOUBLE_DECLARATION, VIEW_CONTAINER_SHAPE, VIEW_KEY_COLLISION, VIEW_REF_FORM_TARGET_KIND, VIEW_REF_FORM_TARGET_MISSING, VISIBILITY_ALIAS_DEPRECATED, VISIBILITY_ROOT_MISLAYERED, type ViewContainerFinding, type ViewContainerSeverity, type ViewRefFinding, type VisibilityFinding, type VisibilityLayer, type VisibilityOptions, type VisibilitySeverity, WIDGET_DATASET_UNKNOWN, WIDGET_DIMENSION_UNKNOWN, WIDGET_MEASURE_UNKNOWN, type WidgetBindingFinding, type WidgetBindingSeverity, buildAccessMatrix, diffAccessMatrix, extractHookBodyWriteSet, extractHookBodyWrites, findUnguardedNullableOperands, lintAutonumberFormats, lintDataModel, lintFlowPatterns, lintLivenessProperties, lintUniqueDeclarations, lintViewRefs, nullGuardMessage, validateActionBodyWrites, validateActionLocations, validateActionNameRefs, validateAiAgentAuthoring, validateAiSurfaceAffinity, validateAiToolReferences, validateApprovalApprovers, validateCapabilityReferences, validateChartBindings, validateDashboardActionRefs, validateFilterTokens, validateFlowNodeWrites, validateFlowTemplatePaths, validateFlowTriggerReadiness, validateFormLayout, validateFunctionalCompleteness, validateHookBodyWrites, validateJsxPages, validateListViewMode, validateNavAccess, validateNavTargetRefs, validateObjectReferences, validateOrgAxisRedLines, validatePageFieldBindings, validatePageSourceStyling, validateReactPageProps, validateReactPages, validateReadonlyFlowWrites, validateRecordTitle, validateReferenceIntegrity, validateResponsiveStyles, validateSearchableFields, validateSecurityPosture, validateSeedReplaySafety, validateSeedStateMachine, validateSemanticRoles, validateStackExpressions, validateTranslationReferences, validateViewContainers, validateVisibilityPredicates, validateWidgetBindings };