@objectstack/lint 16.1.0 → 17.0.0-rc.1
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/CHANGELOG.md +3627 -0
- package/dist/index.cjs +3889 -287
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +902 -35
- package/dist/index.d.ts +902 -35
- package/dist/index.js +3820 -281
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
package/dist/index.d.ts
CHANGED
|
@@ -94,7 +94,7 @@ interface WidgetBindingFinding {
|
|
|
94
94
|
/** How to fix (or deliberately suppress) it. */
|
|
95
95
|
hint: string;
|
|
96
96
|
}
|
|
97
|
-
type AnyRec$
|
|
97
|
+
type AnyRec$x = Record<string, unknown>;
|
|
98
98
|
/**
|
|
99
99
|
* Validate every dashboard widget's dataset binding. Returns the list of
|
|
100
100
|
* findings (empty = clean). Caller decides how to surface them: `error`
|
|
@@ -102,7 +102,7 @@ type AnyRec$h = Record<string, unknown>;
|
|
|
102
102
|
* should fail validate/build; `warning` findings are advisory and must
|
|
103
103
|
* never fail the build on their own.
|
|
104
104
|
*/
|
|
105
|
-
declare function validateWidgetBindings(stack: AnyRec$
|
|
105
|
+
declare function validateWidgetBindings(stack: AnyRec$x): WidgetBindingFinding[];
|
|
106
106
|
|
|
107
107
|
interface ExprIssue {
|
|
108
108
|
where: string;
|
|
@@ -115,12 +115,12 @@ interface ExprIssue {
|
|
|
115
115
|
*/
|
|
116
116
|
severity?: 'error' | 'warning';
|
|
117
117
|
}
|
|
118
|
-
type AnyRec$
|
|
118
|
+
type AnyRec$w = Record<string, unknown>;
|
|
119
119
|
/**
|
|
120
120
|
* Validate every predicate in the stack. Returns the list of issues (empty =
|
|
121
121
|
* clean). Caller decides how to surface / whether to fail the build.
|
|
122
122
|
*/
|
|
123
|
-
declare function validateStackExpressions(stack: AnyRec$
|
|
123
|
+
declare function validateStackExpressions(stack: AnyRec$w): ExprIssue[];
|
|
124
124
|
|
|
125
125
|
type ListViewModeSeverity = 'error' | 'warning';
|
|
126
126
|
interface ListViewModeFinding {
|
|
@@ -134,7 +134,7 @@ interface ListViewModeFinding {
|
|
|
134
134
|
hint: string;
|
|
135
135
|
}
|
|
136
136
|
declare const LIST_VIEW_FILTERS_IN_VIEWS_MODE = "list-view-filters-in-views-mode";
|
|
137
|
-
type AnyRec$
|
|
137
|
+
type AnyRec$v = Record<string, unknown>;
|
|
138
138
|
/**
|
|
139
139
|
* Flag ADR-0047 "views" mode violations on an object's built-in named views or a
|
|
140
140
|
* `defineView` default `list` / named `listViews`: `quickFilters`, or a `tabs`
|
|
@@ -144,7 +144,7 @@ type AnyRec$f = Record<string, unknown>;
|
|
|
144
144
|
*
|
|
145
145
|
* Feed the PRE-parse stack (normalizeStackInput output) — see file header.
|
|
146
146
|
*/
|
|
147
|
-
declare function validateListViewMode(stack: AnyRec$
|
|
147
|
+
declare function validateListViewMode(stack: AnyRec$v): ListViewModeFinding[];
|
|
148
148
|
|
|
149
149
|
type FlowTriggerReadinessSeverity = 'error' | 'warning';
|
|
150
150
|
interface FlowTriggerReadinessFinding {
|
|
@@ -159,12 +159,52 @@ interface FlowTriggerReadinessFinding {
|
|
|
159
159
|
}
|
|
160
160
|
declare const FLOW_TRIGGER_UNKNOWN_OBJECT = "flow-trigger-unknown-object";
|
|
161
161
|
declare const FLOW_DRAFT_STATUS_AMBIGUOUS = "flow-draft-status-ambiguous";
|
|
162
|
-
type AnyRec$
|
|
162
|
+
type AnyRec$u = Record<string, unknown>;
|
|
163
163
|
/**
|
|
164
164
|
* Validate auto-launched flow trigger wiring against the stack definition.
|
|
165
165
|
* Pure and dependency-free; safe on pre- or post-parse stacks.
|
|
166
166
|
*/
|
|
167
|
-
declare function validateFlowTriggerReadiness(stack: AnyRec$
|
|
167
|
+
declare function validateFlowTriggerReadiness(stack: AnyRec$u): FlowTriggerReadinessFinding[];
|
|
168
|
+
|
|
169
|
+
type FlowTemplatePathSeverity = 'error' | 'warning';
|
|
170
|
+
interface FlowTemplatePathFinding {
|
|
171
|
+
severity: FlowTemplatePathSeverity;
|
|
172
|
+
rule: string;
|
|
173
|
+
/** Human-readable location, e.g. `flow "notify_lead" node "notify"`. */
|
|
174
|
+
where: string;
|
|
175
|
+
/** Config path, e.g. `flows[0].nodes[2]`. */
|
|
176
|
+
path: string;
|
|
177
|
+
message: string;
|
|
178
|
+
hint: string;
|
|
179
|
+
}
|
|
180
|
+
declare const FLOW_TEMPLATE_UNKNOWN_FIELD = "flow-template-unknown-field";
|
|
181
|
+
declare const FLOW_TEMPLATE_LOOKUP_TRAVERSAL = "flow-template-lookup-traversal";
|
|
182
|
+
type AnyRec$t = Record<string, unknown>;
|
|
183
|
+
/**
|
|
184
|
+
* Validate `{record.<path>}` template references across every record-change
|
|
185
|
+
* flow. Pure and dependency-free; safe on pre- or post-parse stacks.
|
|
186
|
+
*/
|
|
187
|
+
declare function validateFlowTemplatePaths(stack: AnyRec$t): FlowTemplatePathFinding[];
|
|
188
|
+
|
|
189
|
+
type ReadonlyFlowWriteSeverity = 'error' | 'warning';
|
|
190
|
+
interface ReadonlyFlowWriteFinding {
|
|
191
|
+
severity: ReadonlyFlowWriteSeverity;
|
|
192
|
+
rule: string;
|
|
193
|
+
/** Human-readable location, e.g. `flow "approve_deal" › node "Mark approved"`. */
|
|
194
|
+
where: string;
|
|
195
|
+
/** Config path, e.g. `flows[0].nodes[3].config.fields.approval_status`. */
|
|
196
|
+
path: string;
|
|
197
|
+
message: string;
|
|
198
|
+
hint: string;
|
|
199
|
+
}
|
|
200
|
+
declare const FLOW_UPDATE_READONLY_FIELD = "flow-update-readonly-field";
|
|
201
|
+
declare const FLOW_UPDATE_READONLY_WHEN_FIELD = "flow-update-readonly-when-field";
|
|
202
|
+
type AnyRec$s = Record<string, unknown>;
|
|
203
|
+
/**
|
|
204
|
+
* Validate flow `update_record` writes against target-object readonly
|
|
205
|
+
* declarations. Pure and dependency-free; safe on pre- or post-parse stacks.
|
|
206
|
+
*/
|
|
207
|
+
declare function validateReadonlyFlowWrites(stack: AnyRec$s): ReadonlyFlowWriteFinding[];
|
|
168
208
|
|
|
169
209
|
type ViewContainerSeverity = 'error' | 'warning';
|
|
170
210
|
interface ViewContainerFinding {
|
|
@@ -201,14 +241,14 @@ declare const STYLE_CLASSNAME_TAILWIND = "style-classname-tailwind";
|
|
|
201
241
|
declare const STYLE_RESPONSIVE_NO_BASE = "style-responsive-no-base";
|
|
202
242
|
declare const STYLE_UNKNOWN_CSS_PROPERTY = "style-unknown-css-property";
|
|
203
243
|
declare const STYLE_UNKNOWN_TOKEN = "style-unknown-token";
|
|
204
|
-
type AnyRec$
|
|
244
|
+
type AnyRec$r = Record<string, unknown>;
|
|
205
245
|
/**
|
|
206
246
|
* Validate every page's component tree for SDUI styling correctness (ADR-0065).
|
|
207
247
|
* Returns findings (empty = clean). `error` findings describe styles that are
|
|
208
248
|
* silently dropped and should fail validate/build; `warning` findings are
|
|
209
249
|
* advisory (typos, drift, footguns).
|
|
210
250
|
*/
|
|
211
|
-
declare function validateResponsiveStyles(stack: AnyRec$
|
|
251
|
+
declare function validateResponsiveStyles(stack: AnyRec$r): StyleFinding[];
|
|
212
252
|
|
|
213
253
|
type JsxPageSeverity = 'error' | 'warning';
|
|
214
254
|
interface JsxPageFinding {
|
|
@@ -221,8 +261,8 @@ interface JsxPageFinding {
|
|
|
221
261
|
message: string;
|
|
222
262
|
hint: string;
|
|
223
263
|
}
|
|
224
|
-
type AnyRec$
|
|
225
|
-
declare function validateJsxPages(stack: AnyRec$
|
|
264
|
+
type AnyRec$q = Record<string, unknown>;
|
|
265
|
+
declare function validateJsxPages(stack: AnyRec$q, opts?: {
|
|
226
266
|
manifest?: Manifest;
|
|
227
267
|
}): JsxPageFinding[];
|
|
228
268
|
|
|
@@ -235,8 +275,8 @@ interface ReactPageFinding {
|
|
|
235
275
|
message: string;
|
|
236
276
|
hint: string;
|
|
237
277
|
}
|
|
238
|
-
type AnyRec$
|
|
239
|
-
declare function validateReactPages(stack: AnyRec$
|
|
278
|
+
type AnyRec$p = Record<string, unknown>;
|
|
279
|
+
declare function validateReactPages(stack: AnyRec$p): ReactPageFinding[];
|
|
240
280
|
|
|
241
281
|
type ReactPropSeverity = 'error' | 'warning';
|
|
242
282
|
interface ReactPropFinding {
|
|
@@ -247,8 +287,11 @@ interface ReactPropFinding {
|
|
|
247
287
|
message: string;
|
|
248
288
|
hint: string;
|
|
249
289
|
}
|
|
250
|
-
type AnyRec$
|
|
251
|
-
declare
|
|
290
|
+
type AnyRec$o = Record<string, unknown>;
|
|
291
|
+
declare const REACT_CHART_FIELD_UNKNOWN = "react-chart-field-unknown";
|
|
292
|
+
declare const REACT_CHART_AGGREGATE_INVALID = "react-chart-aggregate-invalid";
|
|
293
|
+
declare const REACT_CHART_AXIS_UNKNOWN = "react-chart-axis-unknown";
|
|
294
|
+
declare function validateReactPageProps(stack: AnyRec$o): ReactPropFinding[];
|
|
252
295
|
|
|
253
296
|
type SourceStyleSeverity = 'error' | 'warning';
|
|
254
297
|
interface SourceStyleFinding {
|
|
@@ -260,8 +303,8 @@ interface SourceStyleFinding {
|
|
|
260
303
|
hint: string;
|
|
261
304
|
}
|
|
262
305
|
declare const PAGE_SOURCE_CLASSNAME = "page-source-className-tailwind";
|
|
263
|
-
type AnyRec$
|
|
264
|
-
declare function validatePageSourceStyling(stack: AnyRec$
|
|
306
|
+
type AnyRec$n = Record<string, unknown>;
|
|
307
|
+
declare function validatePageSourceStyling(stack: AnyRec$n): SourceStyleFinding[];
|
|
265
308
|
|
|
266
309
|
/**
|
|
267
310
|
* Build-time record-title diagnostics (ADR-0079).
|
|
@@ -305,14 +348,14 @@ interface RecordTitleFinding {
|
|
|
305
348
|
/** How to fix it. */
|
|
306
349
|
hint: string;
|
|
307
350
|
}
|
|
308
|
-
type AnyRec$
|
|
351
|
+
type AnyRec$m = Record<string, unknown>;
|
|
309
352
|
/**
|
|
310
353
|
* Validate every object's record-title declaration. Returns the list of
|
|
311
354
|
* findings (empty = clean). Both rules are advisory (`warning`): the caller
|
|
312
355
|
* must never fail the build on them alone — auto-provision + the `Record #<id>`
|
|
313
356
|
* floor guarantee a resolvable title at runtime.
|
|
314
357
|
*/
|
|
315
|
-
declare function validateRecordTitle(stack: AnyRec$
|
|
358
|
+
declare function validateRecordTitle(stack: AnyRec$m): RecordTitleFinding[];
|
|
316
359
|
|
|
317
360
|
/**
|
|
318
361
|
* Build-time semantic-role diagnostics (ADR-0085).
|
|
@@ -349,13 +392,13 @@ interface SemanticRoleFinding {
|
|
|
349
392
|
/** How to fix it. */
|
|
350
393
|
hint: string;
|
|
351
394
|
}
|
|
352
|
-
type AnyRec$
|
|
395
|
+
type AnyRec$l = Record<string, unknown>;
|
|
353
396
|
/**
|
|
354
397
|
* Validate every object's semantic-role pointers. Returns the list of
|
|
355
398
|
* findings (empty = clean). Advisory only — the caller must never fail the
|
|
356
399
|
* build on these alone.
|
|
357
400
|
*/
|
|
358
|
-
declare function validateSemanticRoles(stack: AnyRec$
|
|
401
|
+
declare function validateSemanticRoles(stack: AnyRec$l): SemanticRoleFinding[];
|
|
359
402
|
|
|
360
403
|
/**
|
|
361
404
|
* Build-time form-layout diagnostics (#2578).
|
|
@@ -398,12 +441,12 @@ interface FormLayoutFinding {
|
|
|
398
441
|
/** How to fix it. */
|
|
399
442
|
hint: string;
|
|
400
443
|
}
|
|
401
|
-
type AnyRec$
|
|
444
|
+
type AnyRec$k = Record<string, unknown>;
|
|
402
445
|
/**
|
|
403
446
|
* Validate authored form-view layout. Returns findings (empty = clean).
|
|
404
447
|
* Advisory only — the caller must never fail the build on these alone.
|
|
405
448
|
*/
|
|
406
|
-
declare function validateFormLayout(stack: AnyRec$
|
|
449
|
+
declare function validateFormLayout(stack: AnyRec$k): FormLayoutFinding[];
|
|
407
450
|
|
|
408
451
|
/**
|
|
409
452
|
* Build-time conditional-visibility diagnostics (ADR-0089 D3b).
|
|
@@ -465,7 +508,7 @@ interface VisibilityFinding {
|
|
|
465
508
|
/** How to fix it. */
|
|
466
509
|
hint: string;
|
|
467
510
|
}
|
|
468
|
-
type AnyRec$
|
|
511
|
+
type AnyRec$j = Record<string, unknown>;
|
|
469
512
|
/**
|
|
470
513
|
* Validate conditional-visibility keys across authored views and pages.
|
|
471
514
|
*
|
|
@@ -480,7 +523,7 @@ type AnyRec$5 = Record<string, unknown>;
|
|
|
480
523
|
* `*.view.ts` / `*.page.ts` surfaces (so a `data.`-rooted predicate is flagged). The
|
|
481
524
|
* alias-deprecated check is layer-agnostic.
|
|
482
525
|
*/
|
|
483
|
-
declare function validateVisibilityPredicates(stack: AnyRec$
|
|
526
|
+
declare function validateVisibilityPredicates(stack: AnyRec$j, opts?: VisibilityOptions): VisibilityFinding[];
|
|
484
527
|
|
|
485
528
|
declare const CAPABILITY_REFERENCE_UNKNOWN = "capability-reference-unknown";
|
|
486
529
|
type CapabilityRefSeverity = 'error' | 'warning';
|
|
@@ -498,17 +541,22 @@ interface CapabilityRefFinding {
|
|
|
498
541
|
/** How to fix it. */
|
|
499
542
|
hint: string;
|
|
500
543
|
}
|
|
501
|
-
type AnyRec$
|
|
544
|
+
type AnyRec$i = Record<string, unknown>;
|
|
502
545
|
/**
|
|
503
546
|
* Validate every capability reference in a stack. Returns findings (empty =
|
|
504
547
|
* clean). Advisory only — callers must not fail the build on these alone.
|
|
505
548
|
*/
|
|
506
|
-
declare function validateCapabilityReferences(stack: AnyRec$
|
|
549
|
+
declare function validateCapabilityReferences(stack: AnyRec$i): CapabilityRefFinding[];
|
|
507
550
|
|
|
508
551
|
declare const APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER = "approval-approver-not-membership-tier";
|
|
509
552
|
declare const APPROVAL_APPROVER_TYPE_DEPRECATED = "approval-approver-type-deprecated";
|
|
510
553
|
declare const APPROVAL_APPROVER_TYPE_UNKNOWN = "approval-approver-type-unknown";
|
|
511
554
|
declare const APPROVAL_ESCALATION_REASSIGN_NO_TARGET = "approval-escalation-reassign-no-target";
|
|
555
|
+
declare const APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY = "approval-approvers-may-resolve-empty";
|
|
556
|
+
declare const APPROVAL_EXPRESSION_INVALID = "approval-expression-invalid";
|
|
557
|
+
declare const APPROVAL_EXPRESSION_NO_EMPTY_POLICY = "approval-expression-no-empty-policy";
|
|
558
|
+
declare const APPROVAL_DECISION_OUTPUTS_RESERVED = "approval-decision-outputs-reserved";
|
|
559
|
+
declare const APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED = "approval-approver-cross-org-unsupported";
|
|
512
560
|
type ApprovalApproverSeverity = 'error' | 'warning' | 'info';
|
|
513
561
|
interface ApprovalApproverFinding {
|
|
514
562
|
severity: ApprovalApproverSeverity;
|
|
@@ -523,12 +571,63 @@ interface ApprovalApproverFinding {
|
|
|
523
571
|
/** How to fix it. */
|
|
524
572
|
hint: string;
|
|
525
573
|
}
|
|
526
|
-
type AnyRec$
|
|
574
|
+
type AnyRec$h = Record<string, unknown>;
|
|
527
575
|
/**
|
|
528
576
|
* Validate the approvers of every Approval node in the stack's flows.
|
|
529
577
|
* Returns findings (empty = clean).
|
|
530
578
|
*/
|
|
531
|
-
declare function validateApprovalApprovers(stack: AnyRec$
|
|
579
|
+
declare function validateApprovalApprovers(stack: AnyRec$h): ApprovalApproverFinding[];
|
|
580
|
+
|
|
581
|
+
type SeedReplaySafetySeverity = 'error' | 'warning';
|
|
582
|
+
interface SeedReplaySafetyFinding {
|
|
583
|
+
severity: SeedReplaySafetySeverity;
|
|
584
|
+
rule: string;
|
|
585
|
+
/** Human-readable location, e.g. `seed "showcase_project_membership"`. */
|
|
586
|
+
where: string;
|
|
587
|
+
/** Config path, e.g. `data[12].mode`. */
|
|
588
|
+
path: string;
|
|
589
|
+
message: string;
|
|
590
|
+
hint: string;
|
|
591
|
+
}
|
|
592
|
+
declare const SEED_INSERT_MODE_DUPLICATES_ON_REPLAY = "seed-insert-mode-duplicates-on-replay";
|
|
593
|
+
type AnyRec$g = Record<string, unknown>;
|
|
594
|
+
/**
|
|
595
|
+
* Flag every seed dataset declared with `mode: 'insert'` — the one non-idempotent
|
|
596
|
+
* mode, which duplicates its rows on every replay boot (framework#3434). Returns
|
|
597
|
+
* the findings (empty = clean). The caller decides how to surface them / whether
|
|
598
|
+
* to fail the build; the CLI folds them in as advisory warnings.
|
|
599
|
+
*
|
|
600
|
+
* Reads `stack.data` (the `SeedSchema[]` fixtures). Safe on any shape — a stack
|
|
601
|
+
* with no `data` array yields no findings.
|
|
602
|
+
*/
|
|
603
|
+
declare function validateSeedReplaySafety(stack: AnyRec$g): SeedReplaySafetyFinding[];
|
|
604
|
+
|
|
605
|
+
type SeedStateMachineSeverity = 'warning';
|
|
606
|
+
interface SeedStateMachineFinding {
|
|
607
|
+
severity: SeedStateMachineSeverity;
|
|
608
|
+
rule: string;
|
|
609
|
+
/** Human-readable location, e.g. `seed "showcase_project" ("Legacy Sunset")`. */
|
|
610
|
+
where: string;
|
|
611
|
+
/** Config path, e.g. `data[4].records[3].status`. */
|
|
612
|
+
path: string;
|
|
613
|
+
message: string;
|
|
614
|
+
hint: string;
|
|
615
|
+
}
|
|
616
|
+
declare const SEED_VALUE_OUTSIDE_STATE_MACHINE = "seed-value-outside-state-machine";
|
|
617
|
+
type AnyRec$f = Record<string, unknown>;
|
|
618
|
+
/**
|
|
619
|
+
* Flag every seed record whose `state_machine`-governed field carries a value
|
|
620
|
+
* the machine does not declare (framework#3433 follow-up). Returns the findings
|
|
621
|
+
* (empty = clean). The caller decides how to surface them; the CLI folds them in
|
|
622
|
+
* as advisory warnings.
|
|
623
|
+
*
|
|
624
|
+
* Reads `stack.objects` (for the state-machine rules) and `stack.data` (the
|
|
625
|
+
* `SeedSchema[]` fixtures). Safe on any shape — a stack with no objects or no
|
|
626
|
+
* `data` array yields no findings. A value that is not a plain string (an
|
|
627
|
+
* unresolved `cel` Expression envelope, a number) is skipped: it cannot be
|
|
628
|
+
* statically compared to the declared-state set.
|
|
629
|
+
*/
|
|
630
|
+
declare function validateSeedStateMachine(stack: AnyRec$f): SeedStateMachineFinding[];
|
|
532
631
|
|
|
533
632
|
declare const SECURITY_OWD_UNSET = "security-owd-unset";
|
|
534
633
|
declare const SECURITY_OWD_ALIAS = "security-owd-alias";
|
|
@@ -555,7 +654,7 @@ interface SecurityFinding {
|
|
|
555
654
|
/** How to fix it. */
|
|
556
655
|
hint: string;
|
|
557
656
|
}
|
|
558
|
-
type AnyRec$
|
|
657
|
+
type AnyRec$e = Record<string, unknown>;
|
|
559
658
|
/**
|
|
560
659
|
* Validate the security posture of a stack. Returns findings (empty = clean).
|
|
561
660
|
* `error` findings gate the build in `os compile`; `info` is advisory.
|
|
@@ -563,10 +662,68 @@ type AnyRec$2 = Record<string, unknown>;
|
|
|
563
662
|
* `opts.nowMs` injects the clock for the ADR-0091 authoring-time expiry rule
|
|
564
663
|
* (tests); production callers omit it.
|
|
565
664
|
*/
|
|
566
|
-
declare function validateSecurityPosture(stack: AnyRec$
|
|
665
|
+
declare function validateSecurityPosture(stack: AnyRec$e, opts?: {
|
|
567
666
|
nowMs?: number;
|
|
568
667
|
}): SecurityFinding[];
|
|
569
668
|
|
|
669
|
+
/**
|
|
670
|
+
* [ADR-0105 D6] The two organization-axis red lines, enforced at authoring time.
|
|
671
|
+
*
|
|
672
|
+
* ADR-0105 gives organizations a reporting/grouping dimension
|
|
673
|
+
* (`sys_organization.parent_organization_id`, sibling ordering). That dimension
|
|
674
|
+
* is load-bearing for consolidated reporting — and dangerous, because it LOOKS
|
|
675
|
+
* like a permission hierarchy. Two lines keep it from becoming one:
|
|
676
|
+
*
|
|
677
|
+
* | Rule | Red line |
|
|
678
|
+
* |-----------------------------------------|-----------------------------------|
|
|
679
|
+
* | org-axis-permission-inheritance (error) | D6 ①: no inheritance along the org tree |
|
|
680
|
+
* | org-axis-cross-org-bu-grant (error) | D6 ②: business-unit trees stay org-internal |
|
|
681
|
+
*
|
|
682
|
+
* **① No permission inheritance along the org axis.** Cross-organization
|
|
683
|
+
* visibility comes from membership union (`accessible_org_ids`, ADR-0105 D2) —
|
|
684
|
+
* the engine's own Layer 0 wall — never from walking a parent reference. An RLS
|
|
685
|
+
* policy or sharing rule that reads `parent_organization_id` builds a SECOND
|
|
686
|
+
* permission hierarchy beside the business-unit tree: exactly the dual-hierarchy
|
|
687
|
+
* mistake ADR-0057 D5 retired and ADR-0090 D3 finalized for positions. It also
|
|
688
|
+
* silently outranks the wall it sits behind, since a Layer-1 policy cannot widen
|
|
689
|
+
* Layer 0 (W1) — so the author gets a rule that appears to grant access and
|
|
690
|
+
* does not. Fail at authoring, not in a support ticket.
|
|
691
|
+
*
|
|
692
|
+
* **② Business-unit trees remain org-internal.** `sys_business_unit` is
|
|
693
|
+
* org-scoped and every BU mechanism (`unit_and_subordinates` sharing,
|
|
694
|
+
* `adminScope` delegation, depth scopes) resolves within ONE organization. A
|
|
695
|
+
* business-unit sharing rule on a PLATFORM-GLOBAL object (`tenancy.enabled:
|
|
696
|
+
* false`) has no organization column to scope against, so the grant spans every
|
|
697
|
+
* organization in the database — a cross-org BU grant by construction, and the
|
|
698
|
+
* "cross-org BU mega-tree" the ADR rejected, arrived at by accident.
|
|
699
|
+
*
|
|
700
|
+
* Both are `error`, per ADR-0049 discipline: each mirrors a real enforcement
|
|
701
|
+
* property (the Layer 0 wall's independence; the org-predicated BU resolver),
|
|
702
|
+
* so the lint moves the failure from silent-wrong-answer to author-time fix-it.
|
|
703
|
+
*
|
|
704
|
+
* Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input.
|
|
705
|
+
*/
|
|
706
|
+
declare const ORG_AXIS_PERMISSION_INHERITANCE = "org-axis-permission-inheritance";
|
|
707
|
+
declare const ORG_AXIS_CROSS_ORG_BU_GRANT = "org-axis-cross-org-bu-grant";
|
|
708
|
+
type OrgAxisSeverity = 'error' | 'warning';
|
|
709
|
+
interface OrgAxisFinding {
|
|
710
|
+
severity: OrgAxisSeverity;
|
|
711
|
+
/** Diagnostic rule id (`org-axis-*`). */
|
|
712
|
+
rule: string;
|
|
713
|
+
/** Human-readable location, e.g. `permission set "plant_reader"`. */
|
|
714
|
+
where: string;
|
|
715
|
+
/** Config path, e.g. `permissions[2].rowLevelSecurity[0].using`. */
|
|
716
|
+
path: string;
|
|
717
|
+
/** What is wrong. */
|
|
718
|
+
message: string;
|
|
719
|
+
/** How to fix it. */
|
|
720
|
+
hint: string;
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Lint an ObjectStack config for the ADR-0105 D6 organization-axis red lines.
|
|
724
|
+
*/
|
|
725
|
+
declare function validateOrgAxisRedLines(stack: unknown): OrgAxisFinding[];
|
|
726
|
+
|
|
570
727
|
/**
|
|
571
728
|
* [ADR-0049 — references] Reference-integrity for dashboard header & widget
|
|
572
729
|
* action targets (issue #3367).
|
|
@@ -639,13 +796,723 @@ interface DashboardActionRefFinding {
|
|
|
639
796
|
/** How to fix it. */
|
|
640
797
|
hint: string;
|
|
641
798
|
}
|
|
642
|
-
type AnyRec$
|
|
799
|
+
type AnyRec$d = Record<string, unknown>;
|
|
643
800
|
/**
|
|
644
801
|
* Validate every dashboard header / widget action reference in a stack. Returns
|
|
645
802
|
* findings (empty = clean). `script`/`modal` dead targets are errors; `url`
|
|
646
803
|
* unresolved routes are warnings.
|
|
647
804
|
*/
|
|
648
|
-
declare function validateDashboardActionRefs(stack: AnyRec$
|
|
805
|
+
declare function validateDashboardActionRefs(stack: AnyRec$d): DashboardActionRefFinding[];
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Build-time filter-placeholder diagnostics (issue #3574).
|
|
809
|
+
*
|
|
810
|
+
* Filter values travel as JSON, so a user-scoped or time-scoped slice cannot
|
|
811
|
+
* call code inline — it writes a placeholder that the client resolves just
|
|
812
|
+
* before querying:
|
|
813
|
+
*
|
|
814
|
+
* { owner_id: '{current_user_id}', created_at: { $gte: '{week_start}' } }
|
|
815
|
+
*
|
|
816
|
+
* Exactly two vocabularies resolve inside a filter value: context tokens
|
|
817
|
+
* (`{current_user_id}`, `{current_org_id}` — see `context-tokens.zod.ts`) and
|
|
818
|
+
* date macros (`{today}`, `{30_days_ago}` — see `date-macros.zod.ts`).
|
|
819
|
+
* Anything else is passed to the data engine **verbatim**, matches no row, and
|
|
820
|
+
* the surface renders an empty result.
|
|
821
|
+
*
|
|
822
|
+
* ## Why this is an error, not a warning
|
|
823
|
+
*
|
|
824
|
+
* The runtime failure mode is silent and indistinguishable from success. A
|
|
825
|
+
* metric widget filtered on an unresolved `{current_user}` renders `0`, which
|
|
826
|
+
* looks exactly like a metric that is legitimately zero — no console error, no
|
|
827
|
+
* server log, nothing for a human reviewer to notice. Issue #3574 found a
|
|
828
|
+
* dashboard that had been broken this way since the day it was written.
|
|
829
|
+
*
|
|
830
|
+
* That failure mode is worse for AI authors than for humans. An AI reads a
|
|
831
|
+
* successful query returning `0` as a correct answer and builds on it — it has
|
|
832
|
+
* no instinct that the number looks wrong. Its correction loop is
|
|
833
|
+
* "author → validate → fix", so a diagnostic only reaches it if the diagnostic
|
|
834
|
+
* can fail the build. A runtime warning in a server log is invisible to it.
|
|
835
|
+
* Hence: authoring-time error.
|
|
836
|
+
*
|
|
837
|
+
* The near-miss spellings this catches are not hypothetical. Each is a correct
|
|
838
|
+
* spelling *somewhere else* in the platform, which is precisely why authors
|
|
839
|
+
* reach for them:
|
|
840
|
+
*
|
|
841
|
+
* - `{current_user}` — `current_user.id` is the RLS expression root
|
|
842
|
+
* - `{user_id}` — `{user_id}` is valid `titleFormat` field interpolation
|
|
843
|
+
* - `{current_organization_id}` — `organization_id` is the real column name
|
|
844
|
+
*
|
|
845
|
+
* `CONTEXT_TOKEN_SUGGESTIONS` maps each to what the author meant, so the
|
|
846
|
+
* diagnostic names the fix instead of only reporting the symptom.
|
|
847
|
+
*
|
|
848
|
+
* ## Scope — filter subtrees only
|
|
849
|
+
*
|
|
850
|
+
* The walk descends into `filter` / `filters` / `runtimeFilter` subtrees and
|
|
851
|
+
* classifies string values inside them. It deliberately does NOT check
|
|
852
|
+
* navigation `recordId` / `params`, which resolve an additional vocabulary —
|
|
853
|
+
* `AppContextSelector` ids such as `{active_package}` — that is meaningless in
|
|
854
|
+
* a filter because filters are not evaluated with the sidebar's selector
|
|
855
|
+
* state. Restricting the walk keeps that legitimate usage out of the rule and
|
|
856
|
+
* holds false positives at zero.
|
|
857
|
+
*
|
|
858
|
+
* Only whole-string placeholders are considered (`'{token}'` / `'${token}'`,
|
|
859
|
+
* anchored). A value that merely contains braces is left alone.
|
|
860
|
+
*/
|
|
861
|
+
declare const FILTER_TOKEN_UNKNOWN = "filter-token-unknown";
|
|
862
|
+
type FilterTokenSeverity = 'error' | 'warning';
|
|
863
|
+
interface FilterTokenFinding {
|
|
864
|
+
/** Always `error` today — an unresolved placeholder silently matches nothing. */
|
|
865
|
+
severity: FilterTokenSeverity;
|
|
866
|
+
/** Diagnostic rule id. */
|
|
867
|
+
rule: string;
|
|
868
|
+
/** Human-readable location, e.g. `dashboard "sales" · widget "my_deals"`. */
|
|
869
|
+
where: string;
|
|
870
|
+
/** Config path, e.g. `dashboards[0].widgets[2].filter.owner_id`. */
|
|
871
|
+
path: string;
|
|
872
|
+
/** What is wrong. */
|
|
873
|
+
message: string;
|
|
874
|
+
/** How to fix it. */
|
|
875
|
+
hint: string;
|
|
876
|
+
}
|
|
877
|
+
/**
|
|
878
|
+
* Validate filter placeholders across a schema-parsed stack.
|
|
879
|
+
*
|
|
880
|
+
* Pure `(stack) => Finding[]`; no I/O. Covers dashboards (widget + global
|
|
881
|
+
* filters), objects (list views), top-level view containers, reports,
|
|
882
|
+
* datasets, and pages.
|
|
883
|
+
*/
|
|
884
|
+
declare function validateFilterTokens(stack: Record<string, unknown> | undefined | null): FilterTokenFinding[];
|
|
885
|
+
|
|
886
|
+
declare const OBJECT_REFERENCE_UNKNOWN = "object-reference-unknown";
|
|
887
|
+
declare const OBJECT_REFERENCE_UNREGISTERED_PLATFORM = "object-reference-unregistered-platform";
|
|
888
|
+
type ObjectRefSeverity = 'error' | 'warning';
|
|
889
|
+
interface ObjectRefFinding {
|
|
890
|
+
/** `error` for an unresolvable own-stack name; `warning` for an unknown platform name. */
|
|
891
|
+
severity: ObjectRefSeverity;
|
|
892
|
+
/** Diagnostic rule id. */
|
|
893
|
+
rule: string;
|
|
894
|
+
/** Human-readable location, e.g. `action "mass_update" · param "owner"`. */
|
|
895
|
+
where: string;
|
|
896
|
+
/** Config path, e.g. `actions[2].params[0].reference`. */
|
|
897
|
+
path: string;
|
|
898
|
+
/** What is wrong. */
|
|
899
|
+
message: string;
|
|
900
|
+
/** How to fix it. */
|
|
901
|
+
hint: string;
|
|
902
|
+
}
|
|
903
|
+
type AnyRec$c = Record<string, unknown>;
|
|
904
|
+
/**
|
|
905
|
+
* Validate every object-name reference on the surfaces listed in the module
|
|
906
|
+
* header. Returns findings (empty = clean).
|
|
907
|
+
*/
|
|
908
|
+
declare function validateObjectReferences(stack: AnyRec$c): ObjectRefFinding[];
|
|
909
|
+
|
|
910
|
+
declare const SEARCHABLE_FIELD_UNKNOWN = "searchable-field-unknown";
|
|
911
|
+
type SearchableFieldSeverity = 'error' | 'warning';
|
|
912
|
+
interface SearchableFieldFinding {
|
|
913
|
+
/** Always `error` — a stale entry narrows, widens or refuses the search (see module note). */
|
|
914
|
+
severity: SearchableFieldSeverity;
|
|
915
|
+
/** Diagnostic rule id. */
|
|
916
|
+
rule: string;
|
|
917
|
+
/** Human-readable location, e.g. `object "crm_lead"`. */
|
|
918
|
+
where: string;
|
|
919
|
+
/** Config path, e.g. `objects[0].searchableFields[2]`. */
|
|
920
|
+
path: string;
|
|
921
|
+
/** What is wrong. */
|
|
922
|
+
message: string;
|
|
923
|
+
/** How to fix it. */
|
|
924
|
+
hint: string;
|
|
925
|
+
}
|
|
926
|
+
type AnyRec$b = Record<string, unknown>;
|
|
927
|
+
/**
|
|
928
|
+
* Validate every `searchableFields` declaration in the stack — the object's own
|
|
929
|
+
* (the canonical set, ADR-0061) and the list views that narrow it. Returns
|
|
930
|
+
* findings (empty = clean).
|
|
931
|
+
*
|
|
932
|
+
* The react page surface (`<ListView searchableFields={…}>`) is deliberately
|
|
933
|
+
* NOT walked here: its declaration lives inside JSX source, and
|
|
934
|
+
* `validate-react-page-props` — the gate that already parses that source —
|
|
935
|
+
* runs the same `checkSearchableFieldList` core on it (#4329).
|
|
936
|
+
*/
|
|
937
|
+
declare function validateSearchableFields(stack: AnyRec$b): SearchableFieldFinding[];
|
|
938
|
+
|
|
939
|
+
declare const ACTION_NAME_UNDEFINED = "action-name-undefined";
|
|
940
|
+
type ActionNameRefSeverity = 'error' | 'warning';
|
|
941
|
+
interface ActionNameRefFinding {
|
|
942
|
+
/** Always `error` — a name-bound action that resolves nowhere is a dead button. */
|
|
943
|
+
severity: ActionNameRefSeverity;
|
|
944
|
+
/** Diagnostic rule id. */
|
|
945
|
+
rule: string;
|
|
946
|
+
/** Human-readable location, e.g. `view "crm_lead" · list "all" · bulkActions`. */
|
|
947
|
+
where: string;
|
|
948
|
+
/** Config path, e.g. `views[0].list.bulkActions[1]`. */
|
|
949
|
+
path: string;
|
|
950
|
+
/** What is wrong. */
|
|
951
|
+
message: string;
|
|
952
|
+
/** How to fix it. */
|
|
953
|
+
hint: string;
|
|
954
|
+
}
|
|
955
|
+
type AnyRec$a = Record<string, unknown>;
|
|
956
|
+
/**
|
|
957
|
+
* Validate every name-bound action reference in a stack. Returns findings
|
|
958
|
+
* (empty = clean).
|
|
959
|
+
*/
|
|
960
|
+
declare function validateActionNameRefs(stack: AnyRec$a): ActionNameRefFinding[];
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Shared page-component traversal for the lint rules that inspect
|
|
964
|
+
* `PageComponent.properties` (issue #3583).
|
|
965
|
+
*
|
|
966
|
+
* Getting this walk right is subtle enough that duplicating it has already
|
|
967
|
+
* produced one dead rule, so it lives here once:
|
|
968
|
+
*
|
|
969
|
+
* - Components hang off `page.regions[].components[]` and `page.slots.<slot>`
|
|
970
|
+
* — there is NO top-level `page.components`. A walker that reads
|
|
971
|
+
* `page.components` silently visits nothing on a schema-parsed stack.
|
|
972
|
+
* - `slots.<slot>` is `PageComponent | PageComponent[]` — a single component
|
|
973
|
+
* is legal and must be normalized.
|
|
974
|
+
* - `PageComponentSchema` is `.strict()`, so a component carries no `children`
|
|
975
|
+
* key of its own. Sub-trees live INSIDE the untyped `properties` bag:
|
|
976
|
+
* `page:tabs` → `properties.items[].children`, `page:accordion` →
|
|
977
|
+
* `properties.items[].children`, `page:card` → `properties.body` /
|
|
978
|
+
* `properties.footer`. All are `z.array(z.unknown())`, so the recursion is
|
|
979
|
+
* untyped and has to be done by hand.
|
|
980
|
+
* - `kind: 'html' | 'react' | 'jsx'` pages are authored as `source`, which is
|
|
981
|
+
* authoritative; their `regions` hold at most a DERIVED cache that the
|
|
982
|
+
* source wins over. Linting that cache reports findings about metadata the
|
|
983
|
+
* author never wrote, so those pages are skipped here and covered by
|
|
984
|
+
* `validate-jsx-pages` / `validate-react-page-props` instead.
|
|
985
|
+
*/
|
|
986
|
+
type AnyRec$9 = Record<string, unknown>;
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* [ADR-0078 — completeness] Field-reference integrity for page components
|
|
990
|
+
* (issue #3583, assessment R3).
|
|
991
|
+
*
|
|
992
|
+
* `PageComponent.properties` is `z.record(z.string(), z.unknown())` — an
|
|
993
|
+
* untyped bag. The typed prop schemas exist (`ComponentPropsMap` in
|
|
994
|
+
* `@objectstack/spec/ui`) but nothing validates `properties` against them, so
|
|
995
|
+
* every field name a component references ships exactly as typed. The HotCRM
|
|
996
|
+
* audit found KPI cards and page headers bound to fields the object does not
|
|
997
|
+
* have; each renders blank or falls back, and nothing reports the miss.
|
|
998
|
+
*
|
|
999
|
+
* Field-existence lint already exists for forms (`FORM_FIELD_UNKNOWN`),
|
|
1000
|
+
* semantic roles, and flow templates. This is the same check for pages, at the
|
|
1001
|
+
* same advisory severity: every consumer degrades gracefully (a missing field
|
|
1002
|
+
* is skipped, not crashed on), so a warning is the honest level.
|
|
1003
|
+
*
|
|
1004
|
+
* ── Which object a component binds ──────────────────────────────────────
|
|
1005
|
+
*
|
|
1006
|
+
* `dataSource.object` → `properties.object` → the page's `object`. A per-element
|
|
1007
|
+
* `dataSource` exists precisely so one page can bind several objects, and the
|
|
1008
|
+
* `element:*` family declares its own `object`; both must win over the page's.
|
|
1009
|
+
*
|
|
1010
|
+
* ── Why a hand-written descriptor table ─────────────────────────────────
|
|
1011
|
+
*
|
|
1012
|
+
* `ComponentPropsMap` cannot drive this rule: a Zod schema does not say which
|
|
1013
|
+
* of its `z.string()` props is a FIELD NAME (`RecordPathProps.statusField` and
|
|
1014
|
+
* `AIChatWindowProps.agentId` are both plain strings), and the type universe is
|
|
1015
|
+
* open anyway — `PageComponent.type` is `z.union([PageComponentType,
|
|
1016
|
+
* z.string()])`, so unregistered types like `record:line_items` parse and are
|
|
1017
|
+
* authored in the wild. The table below names the field-bearing props
|
|
1018
|
+
* explicitly; an unknown component type is SKIPPED silently, never flagged.
|
|
1019
|
+
*
|
|
1020
|
+
* The table also covers shapes the props schemas do not yet describe but real
|
|
1021
|
+
* pages authored anyway (they pass only because `properties` is unvalidated):
|
|
1022
|
+
* `record:details` `sections[].fields[]` and `hideFields[]`, and the record
|
|
1023
|
+
* picker's `labelField`. Linting the schema's shape alone would find nothing on
|
|
1024
|
+
* the actual corpus.
|
|
1025
|
+
*
|
|
1026
|
+
* ── Shared with the react page surface ──────────────────────────────────
|
|
1027
|
+
*
|
|
1028
|
+
* A `kind:'react'` page authors the SAME components, one surface over, as JSX
|
|
1029
|
+
* props instead of a `properties` bag (`<RecordHighlights fields={…}>`). The
|
|
1030
|
+
* extraction and the check are therefore exported — `COMPONENT_FIELD_SPECS`,
|
|
1031
|
+
* {@link componentFieldRefs}, {@link relatedListFieldRefs},
|
|
1032
|
+
* {@link indexObjectFields}, {@link checkFieldRefs} — and
|
|
1033
|
+
* `validate-react-page-props` runs them on the parsed JSX (#4340). Same table,
|
|
1034
|
+
* same skips, same rule id: the two surfaces agree on what counts as a field by
|
|
1035
|
+
* construction rather than by two lists that happen to match, which is the
|
|
1036
|
+
* drift #4330 had just finished removing from the system-field lists.
|
|
1037
|
+
*/
|
|
1038
|
+
declare const PAGE_FIELD_UNKNOWN = "page-field-unknown";
|
|
1039
|
+
type PageFieldSeverity = 'error' | 'warning';
|
|
1040
|
+
interface PageFieldFinding {
|
|
1041
|
+
/**
|
|
1042
|
+
* `warning` on every surface this rule itself walks — page renderers skip an
|
|
1043
|
+
* unknown field rather than fail. The shared {@link checkFieldRefs} core also
|
|
1044
|
+
* serves the react page surface, where one batch of refs reaches a QUERY
|
|
1045
|
+
* rather than a renderer and gates instead; see {@link FieldRefConsequence}.
|
|
1046
|
+
*/
|
|
1047
|
+
severity: PageFieldSeverity;
|
|
1048
|
+
/** Diagnostic rule id. */
|
|
1049
|
+
rule: string;
|
|
1050
|
+
/** Human-readable location, e.g. `page "task_detail" · record:highlights`. */
|
|
1051
|
+
where: string;
|
|
1052
|
+
/** Config path, e.g. `pages[0].regions[1].components[0].properties.fields[2]`. */
|
|
1053
|
+
path: string;
|
|
1054
|
+
/** What is wrong. */
|
|
1055
|
+
message: string;
|
|
1056
|
+
/** How to fix it. */
|
|
1057
|
+
hint: string;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
declare function validatePageFieldBindings(stack: AnyRec$9): PageFieldFinding[];
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* [ADR-0021 — semantic layer] Chart-binding integrity for the surfaces the
|
|
1064
|
+
* dashboard rule does not reach (issue #3583, assessment R4).
|
|
1065
|
+
*
|
|
1066
|
+
* `validate-widget-bindings` already resolves a dashboard widget's
|
|
1067
|
+
* `chartConfig` axes against its dataset's declared dimensions and measures
|
|
1068
|
+
* (`chart-field-unknown`). It is scoped to `stack.dashboards`, so three other
|
|
1069
|
+
* chart surfaces ship unchecked — and the HotCRM audit found exactly the bug
|
|
1070
|
+
* that scoping allows: an axis naming a RAW FIELD instead of a dataset measure.
|
|
1071
|
+
* Post-ADR-0021 the result rows are keyed by measure NAME (`sum_amount`), not
|
|
1072
|
+
* the base column (`amount`), so the axis renders and the series is empty.
|
|
1073
|
+
*
|
|
1074
|
+
* Surfaces covered here:
|
|
1075
|
+
*
|
|
1076
|
+
* 1. **Report charts** — `report.chart` and `report.blocks[].chart`.
|
|
1077
|
+
* `ReportChartSchema` narrows `xAxis`/`yAxis` from ChartConfig's
|
|
1078
|
+
* object/array shapes to bare STRINGS, which is why simply pointing the
|
|
1079
|
+
* dashboard rule at reports would find nothing: its `Array.isArray(yAxis)`
|
|
1080
|
+
* guard skips a string silently. `series[].name` keeps the array shape.
|
|
1081
|
+
* 2. **List-view charts** — `ListChartConfigSchema` (`dataset` +
|
|
1082
|
+
* `dimensions` + `values`), reachable through `views[].list`,
|
|
1083
|
+
* `views[].listViews.<key>`, and `objects[].listViews.<key>`.
|
|
1084
|
+
* 3. **Dataset-bound page chart components** — a `PageComponent` whose
|
|
1085
|
+
* `properties` carry a `dataset` (the `object-chart` component). Same
|
|
1086
|
+
* binding shape as a list chart, but it arrives through the untyped
|
|
1087
|
+
* `properties` bag.
|
|
1088
|
+
*
|
|
1089
|
+
* Not covered HERE, and deliberately so: the react `<ObjectChart>` block. It is
|
|
1090
|
+
* OBJECT-bound (`objectName` + an inline `aggregate`), so its result rows are
|
|
1091
|
+
* keyed by the RAW FIELD NAMES rather than by a measure name — the opposite
|
|
1092
|
+
* convention, which would make `chart-measure-unknown`'s message a lie. It also
|
|
1093
|
+
* arrives as JSX rather than config, so it needs the TypeScript compiler this
|
|
1094
|
+
* rule has no business loading. It is checked by `validate-react-page-props`
|
|
1095
|
+
* instead, against the naming convention `chartAggregateResultKeys`
|
|
1096
|
+
* (`@objectstack/spec/ui`) now pins down (#3701).
|
|
1097
|
+
*/
|
|
1098
|
+
declare const CHART_DIMENSION_UNKNOWN = "chart-dimension-unknown";
|
|
1099
|
+
declare const CHART_MEASURE_UNKNOWN = "chart-measure-unknown";
|
|
1100
|
+
declare const CHART_DATASET_UNKNOWN = "chart-dataset-unknown";
|
|
1101
|
+
declare const CHART_AXIS_NOT_SELECTED = "chart-axis-not-selected";
|
|
1102
|
+
type ChartBindingSeverity = 'error' | 'warning';
|
|
1103
|
+
interface ChartBindingFinding {
|
|
1104
|
+
severity: ChartBindingSeverity;
|
|
1105
|
+
/** Diagnostic rule id. */
|
|
1106
|
+
rule: string;
|
|
1107
|
+
/** Human-readable location, e.g. `report "hours_by_status" · chart`. */
|
|
1108
|
+
where: string;
|
|
1109
|
+
/** Config path, e.g. `reports[2].chart.yAxis`. */
|
|
1110
|
+
path: string;
|
|
1111
|
+
/** What is wrong. */
|
|
1112
|
+
message: string;
|
|
1113
|
+
/** How to fix it. */
|
|
1114
|
+
hint: string;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
declare function validateChartBindings(stack: AnyRec$9): ChartBindingFinding[];
|
|
1118
|
+
|
|
1119
|
+
declare const NAV_OBJECT_UNGRANTED = "nav-object-ungranted";
|
|
1120
|
+
type NavAccessSeverity = 'error' | 'warning';
|
|
1121
|
+
interface NavAccessFinding {
|
|
1122
|
+
/** Always `warning` — a grant may come from a package this stack cannot see. */
|
|
1123
|
+
severity: NavAccessSeverity;
|
|
1124
|
+
/** Diagnostic rule id. */
|
|
1125
|
+
rule: string;
|
|
1126
|
+
/** Human-readable location, e.g. `app "crm" · nav "nav_forecast"`. */
|
|
1127
|
+
where: string;
|
|
1128
|
+
/** Config path, e.g. `apps[0].navigation[3].objectName`. */
|
|
1129
|
+
path: string;
|
|
1130
|
+
/** What is wrong. */
|
|
1131
|
+
message: string;
|
|
1132
|
+
/** How to fix it. */
|
|
1133
|
+
hint: string;
|
|
1134
|
+
}
|
|
1135
|
+
type AnyRec$8 = Record<string, unknown>;
|
|
1136
|
+
/**
|
|
1137
|
+
* Validate that every object a stack's navigation exposes is readable by at
|
|
1138
|
+
* least one permission set the stack declares. Returns findings (empty = clean).
|
|
1139
|
+
*/
|
|
1140
|
+
declare function validateNavAccess(stack: AnyRec$8): NavAccessFinding[];
|
|
1141
|
+
|
|
1142
|
+
declare const TRANSLATION_TARGET_UNKNOWN = "translation-target-unknown";
|
|
1143
|
+
declare const TRANSLATION_OPTION_KEY_UNKNOWN = "translation-option-key-unknown";
|
|
1144
|
+
type TranslationRefSeverity = 'warning';
|
|
1145
|
+
interface TranslationRefFinding {
|
|
1146
|
+
/** Always `warning` — an orphan translation key is inert, not broken. */
|
|
1147
|
+
severity: TranslationRefSeverity;
|
|
1148
|
+
/** Diagnostic rule id. */
|
|
1149
|
+
rule: string;
|
|
1150
|
+
/** Human-readable location, e.g. `locale "zh-CN" · object "crm_lead"`. */
|
|
1151
|
+
where: string;
|
|
1152
|
+
/** Config path, e.g. `translations[0]["zh-CN"].objects.crm_lead.fields.campaign`. */
|
|
1153
|
+
path: string;
|
|
1154
|
+
/** What is wrong. */
|
|
1155
|
+
message: string;
|
|
1156
|
+
/** How to fix it. */
|
|
1157
|
+
hint: string;
|
|
1158
|
+
}
|
|
1159
|
+
type AnyRec$7 = Record<string, unknown>;
|
|
1160
|
+
/**
|
|
1161
|
+
* Validate every reference a translation bundle makes against the metadata it
|
|
1162
|
+
* claims to translate. Returns findings (empty = clean).
|
|
1163
|
+
*/
|
|
1164
|
+
declare function validateTranslationReferences(stack: AnyRec$7): TranslationRefFinding[];
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* [ADR-0064 §3] Skill ↔ agent surface affinity (issue #3820).
|
|
1168
|
+
*
|
|
1169
|
+
* An agent binds a product surface (`'ask'` | `'build'`, ADR-0063 §1) and a
|
|
1170
|
+
* skill declares which surface it belongs to (`'ask'` | `'build'` | `'both'`,
|
|
1171
|
+
* ADR-0063 §3). A skill may only attach to an agent whose surface it matches —
|
|
1172
|
+
* `'both'` attaches to either. The runtime treats a violation as a FAST LOAD
|
|
1173
|
+
* ERROR: `resolveActiveSkills()` throws on the first incompatible binding, so
|
|
1174
|
+
* an agent shipping one mismatched skill reference fails at **chat time** with
|
|
1175
|
+
* a 500 — after parse, after validate, after deploy.
|
|
1176
|
+
*
|
|
1177
|
+
* Both sides of the check are declared in the same stack, so the contradiction
|
|
1178
|
+
* is statically provable and this rule carries severity **error** with zero
|
|
1179
|
+
* false positives by construction. Both sides default to `'ask'` when the
|
|
1180
|
+
* `surface` field is absent (mirroring the runtime's defaults), so the rule is
|
|
1181
|
+
* safe on the raw/normalized config the `lint` path carries as well as the
|
|
1182
|
+
* schema-parsed stack.
|
|
1183
|
+
*
|
|
1184
|
+
* Scope note: this rule deliberately does NOT check that `agent.skills[]`
|
|
1185
|
+
* names resolve at all. Kernel skills (`schema_reader`, the `ask`/`build`
|
|
1186
|
+
* bundles) are runtime-registered and statically invisible, and whether
|
|
1187
|
+
* app-stack tool/skill namespaces get a platform-name registry is an open
|
|
1188
|
+
* decision (#3820 D0/D2) — resolving names against `stack.skills` alone would
|
|
1189
|
+
* flag every kernel-skill reference. An unresolved name is therefore skipped
|
|
1190
|
+
* here; only a reference that resolves in-stack AND contradicts the affinity
|
|
1191
|
+
* contract is reported.
|
|
1192
|
+
*/
|
|
1193
|
+
declare const AI_SKILL_SURFACE_MISMATCH = "ai-skill-surface-mismatch";
|
|
1194
|
+
type AiSurfaceAffinitySeverity = 'error' | 'warning';
|
|
1195
|
+
interface AiSurfaceAffinityFinding {
|
|
1196
|
+
/** Always `error` — the runtime throws on this binding at chat time. */
|
|
1197
|
+
severity: AiSurfaceAffinitySeverity;
|
|
1198
|
+
/** Diagnostic rule id. */
|
|
1199
|
+
rule: string;
|
|
1200
|
+
/** Human-readable location, e.g. `agent "sales_copilot" · skills`. */
|
|
1201
|
+
where: string;
|
|
1202
|
+
/** Config path, e.g. `agents[0].skills[2]`. */
|
|
1203
|
+
path: string;
|
|
1204
|
+
/** What is wrong. */
|
|
1205
|
+
message: string;
|
|
1206
|
+
/** How to fix it. */
|
|
1207
|
+
hint: string;
|
|
1208
|
+
}
|
|
1209
|
+
type AnyRec$6 = Record<string, unknown>;
|
|
1210
|
+
/**
|
|
1211
|
+
* Validate every in-stack agent→skill binding against the ADR-0064 §3 surface
|
|
1212
|
+
* affinity contract. Returns findings (empty = clean).
|
|
1213
|
+
*/
|
|
1214
|
+
declare function validateAiSurfaceAffinity(stack: AnyRec$6): AiSurfaceAffinityFinding[];
|
|
1215
|
+
|
|
1216
|
+
declare const AI_SKILL_TOOL_UNRESOLVED = "ai-skill-tool-unresolved";
|
|
1217
|
+
type AiToolRefSeverity = 'error' | 'warning';
|
|
1218
|
+
interface AiToolRefFinding {
|
|
1219
|
+
/** Always `warning` — see the header for why this rule starts advisory. */
|
|
1220
|
+
severity: AiToolRefSeverity;
|
|
1221
|
+
/** Diagnostic rule id. */
|
|
1222
|
+
rule: string;
|
|
1223
|
+
/** Human-readable location, e.g. `skill "revenue_forecasting" · tools`. */
|
|
1224
|
+
where: string;
|
|
1225
|
+
/** Config path, e.g. `skills[3].tools[1]`. */
|
|
1226
|
+
path: string;
|
|
1227
|
+
/** What is wrong. */
|
|
1228
|
+
message: string;
|
|
1229
|
+
/** How to fix it. */
|
|
1230
|
+
hint: string;
|
|
1231
|
+
}
|
|
1232
|
+
type AnyRec$5 = Record<string, unknown>;
|
|
1233
|
+
/**
|
|
1234
|
+
* Validate every `skill.tools[]` reference in a stack. Returns findings
|
|
1235
|
+
* (empty = clean).
|
|
1236
|
+
*/
|
|
1237
|
+
declare function validateAiToolReferences(stack: AnyRec$5): AiToolRefFinding[];
|
|
1238
|
+
|
|
1239
|
+
/**
|
|
1240
|
+
* [ADR-0063 §2] `stack.agents` is a platform-internal slot (issue #3820).
|
|
1241
|
+
*
|
|
1242
|
+
* ADR-0063 §2 withdrew tenant/app-package custom agents: the kernel ships
|
|
1243
|
+
* exactly two agents (`ask`, `build`), the surface the user is in binds one,
|
|
1244
|
+
* and third parties extend the platform by authoring **skills**, never
|
|
1245
|
+
* `*.agent.ts`. The `agent` metadata type carries the decision
|
|
1246
|
+
* (`allowRuntimeCreate: false, allowOrgOverride: false`), and the runtime
|
|
1247
|
+
* enforces it on both paths — `listAgents()` filters non-platform records out
|
|
1248
|
+
* of the catalog, and `loadAgent()` refuses them outright (cloud#904), so a
|
|
1249
|
+
* stack-authored agent 404s on chat and cannot be pinned via
|
|
1250
|
+
* `app.defaultAgent`.
|
|
1251
|
+
*
|
|
1252
|
+
* What was missing is the AUTHORING-time signal. `defineStack` still accepts
|
|
1253
|
+
* an `agents` array, so an app package could declare agents that parse,
|
|
1254
|
+
* validate, and build into the artifact — and then do nothing at runtime.
|
|
1255
|
+
* HotCRM shipped two of them for months. That is the ADR-0078 shape this rule
|
|
1256
|
+
* closes: loud at the producer, tolerant at the consumer (Prime Directive
|
|
1257
|
+
* #12).
|
|
1258
|
+
*
|
|
1259
|
+
* Severity is **warning**, not error, for one reason: the platform's own
|
|
1260
|
+
* packages legitimately author agent records, and this rule cannot tell a
|
|
1261
|
+
* platform package from an app package by reading the stack alone. A warning
|
|
1262
|
+
* that names the runtime consequence is honest for both readers; the runtime
|
|
1263
|
+
* is what actually gates. Deliberately NOT a Zod refine — an existing stack
|
|
1264
|
+
* must keep parsing (ADR-0078 non-goal #1).
|
|
1265
|
+
*/
|
|
1266
|
+
declare const AGENT_AUTHORING_WITHDRAWN = "agent-authoring-withdrawn";
|
|
1267
|
+
type AiAgentAuthoringSeverity = 'error' | 'warning';
|
|
1268
|
+
interface AiAgentAuthoringFinding {
|
|
1269
|
+
/** Always `warning` — the runtime is the gate; this is the authoring-time signal. */
|
|
1270
|
+
severity: AiAgentAuthoringSeverity;
|
|
1271
|
+
/** Diagnostic rule id. */
|
|
1272
|
+
rule: string;
|
|
1273
|
+
/** Human-readable location, e.g. `agent "sales_copilot"`. */
|
|
1274
|
+
where: string;
|
|
1275
|
+
/** Config path, e.g. `agents[0]`. */
|
|
1276
|
+
path: string;
|
|
1277
|
+
/** What is wrong. */
|
|
1278
|
+
message: string;
|
|
1279
|
+
/** How to fix it. */
|
|
1280
|
+
hint: string;
|
|
1281
|
+
}
|
|
1282
|
+
type AnyRec$4 = Record<string, unknown>;
|
|
1283
|
+
/**
|
|
1284
|
+
* Flag every agent declared in a stack. Returns findings (empty = clean,
|
|
1285
|
+
* which is what every app package should be).
|
|
1286
|
+
*/
|
|
1287
|
+
declare function validateAiAgentAuthoring(stack: AnyRec$4): AiAgentAuthoringFinding[];
|
|
1288
|
+
|
|
1289
|
+
type HookBodyWriteSeverity = 'warning';
|
|
1290
|
+
interface HookBodyWriteFinding {
|
|
1291
|
+
/** v1 is advisory-only by contract — the type says so. */
|
|
1292
|
+
severity: HookBodyWriteSeverity;
|
|
1293
|
+
rule: string;
|
|
1294
|
+
/** Human-readable location, e.g. `hook "normalize_lead" › body`. */
|
|
1295
|
+
where: string;
|
|
1296
|
+
/** Config path, e.g. `hooks[0].body.source`. */
|
|
1297
|
+
path: string;
|
|
1298
|
+
message: string;
|
|
1299
|
+
hint: string;
|
|
1300
|
+
}
|
|
1301
|
+
declare const HOOK_BODY_WRITE_UNKNOWN_FIELD = "hook-body-write-unknown-field";
|
|
1302
|
+
/** One syntactic write shape the extractor recognizes. */
|
|
1303
|
+
interface HookBodyWritePattern {
|
|
1304
|
+
/** Stable pattern id, carried on every extracted write. */
|
|
1305
|
+
readonly id: string;
|
|
1306
|
+
/** Author-facing syntax summary (for docs/diagnostics, not matching). */
|
|
1307
|
+
readonly syntax: string;
|
|
1308
|
+
/** Reconciliation fixture: extracting `source` must yield exactly `writes`. */
|
|
1309
|
+
readonly example: {
|
|
1310
|
+
readonly source: string;
|
|
1311
|
+
readonly writes: ReadonlyArray<{
|
|
1312
|
+
field: string;
|
|
1313
|
+
object?: string;
|
|
1314
|
+
}>;
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
declare const HOOK_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[];
|
|
1318
|
+
/** A ledger pattern a given rule does NOT consume, and why. */
|
|
1319
|
+
interface BodyWritePatternExclusion {
|
|
1320
|
+
/** The {@link HOOK_BODY_WRITE_PATTERNS} entry id being excluded. */
|
|
1321
|
+
readonly id: string;
|
|
1322
|
+
/** Why the shape does not mean the same thing on this rule's surface. */
|
|
1323
|
+
readonly reason: string;
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* The ledger shapes THIS rule consumes.
|
|
1327
|
+
*
|
|
1328
|
+
* Declared rather than implied: before the ledger carried a shape the hook
|
|
1329
|
+
* surface does not have, every write with no `object` was necessarily a
|
|
1330
|
+
* `ctx.input` write, and the rule could branch on that alone. It no longer can
|
|
1331
|
+
* — a `record-property-assign` write also carries no object, and would have
|
|
1332
|
+
* been reported as "the hook writes 'stage' to its input", which is false.
|
|
1333
|
+
* Each consumer declaring its own subset is what stops the next added shape
|
|
1334
|
+
* from silently landing in a branch that was never written for it.
|
|
1335
|
+
*/
|
|
1336
|
+
declare const HOOK_BODY_WRITE_PATTERN_IDS: readonly string[];
|
|
1337
|
+
/** Ledger shapes this rule leaves alone, each with its reason. */
|
|
1338
|
+
declare const HOOK_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
|
|
1339
|
+
type AnyRec$3 = Record<string, unknown>;
|
|
1340
|
+
/** One statically-extracted field write found in an L2 body. */
|
|
1341
|
+
interface ExtractedHookBodyWrite {
|
|
1342
|
+
/** Which {@link HOOK_BODY_WRITE_PATTERNS} entry matched. */
|
|
1343
|
+
patternId: string;
|
|
1344
|
+
/** Target object name; `undefined` = the hook's own target object(s). */
|
|
1345
|
+
object?: string;
|
|
1346
|
+
/** The `ctx.api` method for diagnostics (`insert`/`create`/`update`/`updateById`). */
|
|
1347
|
+
method?: string;
|
|
1348
|
+
field: string;
|
|
1349
|
+
}
|
|
1350
|
+
/** Everything one parse of an L2 body yields. */
|
|
1351
|
+
interface ExtractedHookBodyWriteSet {
|
|
1352
|
+
/** Every literal write the {@link HOOK_BODY_WRITE_PATTERNS} ledger declares. */
|
|
1353
|
+
writes: ExtractedHookBodyWrite[];
|
|
1354
|
+
/**
|
|
1355
|
+
* `ctx.record` is handed to something as a VALUE somewhere in the body — an
|
|
1356
|
+
* argument, an assignment RHS, a spread, a return — rather than only having
|
|
1357
|
+
* its properties read and written, or being truthiness/type tested.
|
|
1358
|
+
*
|
|
1359
|
+
* The action rule needs this to tell a dead snapshot write from a live one:
|
|
1360
|
+
* `ctx.record.stage = 'won'; await ctx.api.object('d').update(ctx.record)`
|
|
1361
|
+
* builds a payload and persists it, so the assignment is not a no-op. When
|
|
1362
|
+
* this is true, no record write in the body can be judged, and none is
|
|
1363
|
+
* reported. (One-level aliasing — `const r = ctx.record` — reads as an
|
|
1364
|
+
* escape too, which is the safe direction: it suppresses findings.)
|
|
1365
|
+
*/
|
|
1366
|
+
ctxRecordEscapes: boolean;
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Extract every literal field write the pattern ledger declares from an L2
|
|
1370
|
+
* body's source. Parse-only (the source is never executed), error-tolerant
|
|
1371
|
+
* (a body with syntax errors simply yields fewer matches), and lazy: the
|
|
1372
|
+
* TypeScript compiler is not loaded when no pattern can possibly match.
|
|
1373
|
+
*
|
|
1374
|
+
* Thin projection of {@link extractHookBodyWriteSet} — use that one when the
|
|
1375
|
+
* `ctx.record` liveness signal matters, so the body is parsed once, not twice.
|
|
1376
|
+
*/
|
|
1377
|
+
declare function extractHookBodyWrites(source: string): ExtractedHookBodyWrite[];
|
|
1378
|
+
/** {@link extractHookBodyWrites} plus the `ctx.record` liveness signal, one parse. */
|
|
1379
|
+
declare function extractHookBodyWriteSet(source: string): ExtractedHookBodyWriteSet;
|
|
1380
|
+
/**
|
|
1381
|
+
* Validate L2 hook-body writes against target-object field declarations.
|
|
1382
|
+
* Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.
|
|
1383
|
+
*/
|
|
1384
|
+
declare function validateHookBodyWrites(stack: AnyRec$3): HookBodyWriteFinding[];
|
|
1385
|
+
|
|
1386
|
+
type ActionBodyWriteSeverity = 'warning';
|
|
1387
|
+
interface ActionBodyWriteFinding {
|
|
1388
|
+
/** Advisory-only by contract, exactly like the hook rule — the type says so. */
|
|
1389
|
+
severity: ActionBodyWriteSeverity;
|
|
1390
|
+
rule: string;
|
|
1391
|
+
/** Human-readable location, e.g. `action "close_deal" › body`. */
|
|
1392
|
+
where: string;
|
|
1393
|
+
/** Config path, e.g. `actions[0].body.source`. */
|
|
1394
|
+
path: string;
|
|
1395
|
+
message: string;
|
|
1396
|
+
hint: string;
|
|
1397
|
+
}
|
|
1398
|
+
declare const ACTION_BODY_WRITE_UNKNOWN_FIELD = "action-body-write-unknown-field";
|
|
1399
|
+
declare const ACTION_RECORD_WRITE_DISCARDED = "action-record-write-discarded";
|
|
1400
|
+
/** @deprecated alias — the exclusion shape is shared with the hook rule. */
|
|
1401
|
+
type ActionBodyWriteExclusion = BodyWritePatternExclusion;
|
|
1402
|
+
/**
|
|
1403
|
+
* Ledger shapes the UNKNOWN-FIELD check consumes — resolved against the named
|
|
1404
|
+
* object's declared fields.
|
|
1405
|
+
*
|
|
1406
|
+
* Include-list, not exclude-list, on purpose: an unclassified new pattern is
|
|
1407
|
+
* then inert here (a missed finding) rather than live against a context it was
|
|
1408
|
+
* never reasoned about (a false one) — the same asymmetry the extractor's
|
|
1409
|
+
* silent bails follow.
|
|
1410
|
+
*/
|
|
1411
|
+
declare const ACTION_BODY_WRITE_PATTERN_IDS: readonly string[];
|
|
1412
|
+
/**
|
|
1413
|
+
* Ledger shapes the DISCARDED-RECORD-WRITE check consumes — never resolved
|
|
1414
|
+
* against anything, because no field name can make a discarded write land.
|
|
1415
|
+
*/
|
|
1416
|
+
declare const ACTION_RECORD_WRITE_PATTERN_IDS: readonly string[];
|
|
1417
|
+
/** Shared-ledger patterns neither check consumes, each with its reason. */
|
|
1418
|
+
declare const ACTION_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
|
|
1419
|
+
/**
|
|
1420
|
+
* The subset of the shared ledger the unknown-field check sees — the published
|
|
1421
|
+
* answer to "which writes does the action lint resolve against fields?".
|
|
1422
|
+
*/
|
|
1423
|
+
declare const ACTION_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[];
|
|
1424
|
+
/** The subset the discarded-record-write check sees. */
|
|
1425
|
+
declare const ACTION_RECORD_WRITE_PATTERNS: readonly HookBodyWritePattern[];
|
|
1426
|
+
type AnyRec$2 = Record<string, unknown>;
|
|
1427
|
+
/**
|
|
1428
|
+
* Validate L2 action-body writes against target-object field declarations.
|
|
1429
|
+
* Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.
|
|
1430
|
+
*/
|
|
1431
|
+
declare function validateActionBodyWrites(stack: AnyRec$2): ActionBodyWriteFinding[];
|
|
1432
|
+
|
|
1433
|
+
type FlowNodeWriteSeverity = 'error';
|
|
1434
|
+
interface FlowNodeWriteFinding {
|
|
1435
|
+
/** Always `error` — a literal key against a literal object is a certainty (see module note). */
|
|
1436
|
+
severity: FlowNodeWriteSeverity;
|
|
1437
|
+
rule: string;
|
|
1438
|
+
/** Human-readable location, e.g. `flow "close_deal" › node "Mark won"`. */
|
|
1439
|
+
where: string;
|
|
1440
|
+
/** Config path, e.g. `flows[0].nodes[3].config.fields.stagee`. */
|
|
1441
|
+
path: string;
|
|
1442
|
+
message: string;
|
|
1443
|
+
hint: string;
|
|
1444
|
+
}
|
|
1445
|
+
declare const FLOW_NODE_WRITE_UNKNOWN_FIELD = "flow-node-write-unknown-field";
|
|
1446
|
+
/** Flow node types whose `config.fields` keys this rule resolves. */
|
|
1447
|
+
declare const FLOW_WRITE_NODE_TYPES: readonly string[];
|
|
1448
|
+
/** A `fields`-bearing node type this rule does NOT cover yet, and why. */
|
|
1449
|
+
interface FlowWriteNodeDeferral {
|
|
1450
|
+
/** The `FlowNode.type` left uncovered. */
|
|
1451
|
+
readonly type: string;
|
|
1452
|
+
/** Why it is not covered, in terms a reviewer can act on. */
|
|
1453
|
+
readonly reason: string;
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* `fields`-bearing CRUD node types deliberately left uncovered.
|
|
1457
|
+
*
|
|
1458
|
+
* **Empty, and that is the point.** #4369 shipped `update_record` alone and
|
|
1459
|
+
* parked `create_record` here with its reason — a gating rule earning its
|
|
1460
|
+
* severity one measured surface at a time — rather than leaving the other half
|
|
1461
|
+
* as silence. #4371 measured the INSERT path (`table deal has no column named
|
|
1462
|
+
* stagee`, and the row never created at all), found it strictly worse than the
|
|
1463
|
+
* UPDATE one, and moved it across.
|
|
1464
|
+
*
|
|
1465
|
+
* The slot stays because the partition test derives the full `fields`-write-map
|
|
1466
|
+
* set from the spec's own config schemas: a node type that grows one later
|
|
1467
|
+
* belongs to neither list and fails that test until someone puts it in one.
|
|
1468
|
+
* Deleting this array would turn that forced decision back into a default.
|
|
1469
|
+
*/
|
|
1470
|
+
declare const FLOW_WRITE_NODE_TYPES_DEFERRED: readonly FlowWriteNodeDeferral[];
|
|
1471
|
+
type AnyRec$1 = Record<string, unknown>;
|
|
1472
|
+
/**
|
|
1473
|
+
* Validate flow write-node `fields` keys against the target object's declared
|
|
1474
|
+
* fields. Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse
|
|
1475
|
+
* stacks.
|
|
1476
|
+
*/
|
|
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[];
|
|
649
1516
|
|
|
650
1517
|
/**
|
|
651
1518
|
* [ADR-0090 D6] Access-matrix snapshot — authoring-time companion to the
|
|
@@ -671,4 +1538,4 @@ declare function buildAccessMatrix(stack: AnyRec): AccessMatrix;
|
|
|
671
1538
|
*/
|
|
672
1539
|
declare function diffAccessMatrix(before: AccessMatrix, after: AccessMatrix): string[];
|
|
673
1540
|
|
|
674
|
-
export { APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER, APPROVAL_APPROVER_TYPE_DEPRECATED, APPROVAL_APPROVER_TYPE_UNKNOWN, APPROVAL_ESCALATION_REASSIGN_NO_TARGET, type ApprovalApproverFinding, type ApprovalApproverSeverity, CAPABILITY_REFERENCE_UNKNOWN, CHART_CONFIG_MISSING, CHART_FIELD_UNKNOWN, type CapabilityRefFinding, type CapabilityRefSeverity, DASHBOARD_ACTION_ROUTE_UNRESOLVED, DASHBOARD_ACTION_TARGET_UNDEFINED, DASHBOARD_FILTER_FIELD_UNKNOWN, type DashboardActionRefFinding, type DashboardActionRefSeverity, type ExprIssue, FIELD_GROUP_EMPTY, FIELD_GROUP_UNDECLARED, FLOW_DRAFT_STATUS_AMBIGUOUS, FLOW_TRIGGER_UNKNOWN_OBJECT, FORM_COLSPAN_ABSOLUTE, FORM_FIELD_UNKNOWN, type FlowTriggerReadinessFinding, type FlowTriggerReadinessSeverity, type FormLayoutFinding, type FormLayoutSeverity, type JsxPageFinding, type JsxPageSeverity, LIST_VIEW_FILTERS_IN_VIEWS_MODE, type ListViewModeFinding, type ListViewModeSeverity, MEASURE_AGGREGATE_INCOHERENT, PAGE_SOURCE_CLASSNAME, type ReactPageFinding, type ReactPageSeverity, type ReactPropFinding, type ReactPropSeverity, type RecordTitleFinding, type RecordTitleSeverity, 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, SEMANTIC_ROLE_FIELD_UNKNOWN, STYLE_CLASSNAME_TAILWIND, STYLE_NODE_MISSING_ID, STYLE_RESPONSIVE_NO_BASE, STYLE_UNKNOWN_CSS_PROPERTY, STYLE_UNKNOWN_TOKEN, type SecurityFinding, type SecuritySeverity, type SemanticRoleFinding, type SemanticRoleSeverity, type SourceStyleFinding, type SourceStyleSeverity, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, 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, validateApprovalApprovers, validateCapabilityReferences, validateDashboardActionRefs, validateFlowTriggerReadiness, validateFormLayout, validateJsxPages, validateListViewMode, validatePageSourceStyling, validateReactPageProps, validateReactPages, validateRecordTitle, validateResponsiveStyles, validateSecurityPosture, validateSemanticRoles, validateStackExpressions, validateViewContainers, validateVisibilityPredicates, validateWidgetBindings };
|
|
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 };
|