@objectstack/lint 17.2.0 → 17.3.0

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,9 +1,9 @@
1
- import { Manifest } from '@objectstack/sdui-parser';
2
1
  import ts from 'typescript';
2
+ import { Manifest } from '@objectstack/sdui-parser';
3
3
  import { SearchFieldMeta } from '@objectstack/spec/data';
4
4
  import { Options } from 'ajv';
5
5
  import { AccessMatrixParsed } from '@objectstack/spec/security';
6
- 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 RuntimePackageScope, m as RuntimeStackContext, n as authoringRulesFor, o as buildRuntimeWriteSnapshots, p as narrowObjectsToPackageClosure, r as runAuthoringRules, q as runRuntimeAuthoringRules, s as runtimeAuthoringRulesFor, t as runtimeGatedTypes, u as splitBySeverity, v as stackKeyForType } from './runtime-cV-l_vtC.js';
6
+ 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 RuntimePackageScope, m as RuntimeStackContext, n as authoringRulesFor, o as buildRuntimeWriteSnapshots, p as narrowObjectsToPackageClosure, r as runAuthoringRules, q as runRuntimeAuthoringRules, s as runtimeAuthoringRulesFor, t as runtimeGatedTypes, u as splitBySeverity, v as stackKeyForType } from './runtime-B8F5-Cbg.js';
7
7
 
8
8
  /**
9
9
  * Build-time dashboard widget binding diagnostics (issues #1719, #1721).
@@ -40,13 +40,26 @@ export { A as AUTHORING_COMMANDS, a as AUTHORING_RULES, b as AUTHORING_SURFACES,
40
40
  * - `dashboard-filter-field-unknown` (#3365) — a dashboard-level filter
41
41
  * (`dateRange` or a `globalFilters[]` entry) is wired into EVERY widget's
42
42
  * analytics query (#2501), but its EFFECTIVE field (after any `filterBindings`
43
- * re-target) does not exist on a bound widget's dataset object. The widget's
43
+ * re-target) does not resolve on a bound widget's dataset object. The widget's
44
44
  * query then references a non-existent column and crashes at render time
45
45
  * (`no such column …`) — a build-decidable invariant that previously escaped
46
46
  * the static gate and failed only when a user opened the dashboard. A widget
47
47
  * opts out with `filterBindings: { <name>: false }` or re-targets to a real
48
48
  * field. This is the same field-existence invariant ADR-0032 enforces for
49
49
  * CEL formula / sharing-rule references, applied to dashboard filter fields.
50
+ * Resolution is {@link resolveFieldPath}'s (#14275), so a DOTTED effective
51
+ * field (`account.signed_at`) is walked hop by hop rather than skipped, and a
52
+ * bare name is judged against the object's OWN injected columns rather than
53
+ * the object-independent `SYSTEM_FIELDS` union — see "What #14275 closed"
54
+ * below for why each half was a hole rather than a nicety.
55
+ * - `dashboard-filter-field-not-included` (#14275) — the effective field
56
+ * RESOLVES, but its relationship prefix is not declared in the bound
57
+ * dataset's `include`, so ADR-0021 compiles no join for it and the column is
58
+ * out of the broadcast query's reach. The `include` half of the same two
59
+ * clauses `widget-filter-field-not-included` applies to the widget's own
60
+ * `filter`, at the position one level up — a separate id because the fix is a
61
+ * different edit (declare the join, versus point the filter somewhere real)
62
+ * and because the family keeps one id per class.
50
63
  *
51
64
  * Advisory rules — severity `warning`, build stays green:
52
65
  *
@@ -79,6 +92,91 @@ export { A as AUTHORING_COMMANDS, a as AUTHORING_RULES, b as AUTHORING_SURFACES,
79
92
  * of the same invariant, warned rather than errored because this pass cannot
80
93
  * see the remote schema (#8116's severity reasoning).
81
94
  *
95
+ * ── The widget's OWN two references (#14148) ─────────────────────────────────
96
+ *
97
+ * - `widget-filter-field-unknown` — a KEY of the widget's own `filter` resolves
98
+ * to no column on the bound dataset's object graph.
99
+ * - `widget-filter-field-not-included` — that key RESOLVES, but its relationship
100
+ * prefix is not declared in the dataset's `include`, so ADR-0021 compiles no
101
+ * join for it and the column is out of the query's reach.
102
+ * - `widget-sortby-unselected` — `options.sortBy` names neither a `dimensions[]`
103
+ * nor a `values[]` entry of this widget, so the ordering the author wrote
104
+ * cannot be applied to a result that will not contain that column.
105
+ *
106
+ * All three are `error`, and the reason is the reporting card's, quoted because
107
+ * it is the sharpest statement of it in the family: the dashboard it was
108
+ * measured on leads with a "not moving" tile — open work untouched >14 days —
109
+ * and *"an empty tile is indistinguishable from a healthy team: a missing
110
+ * number reads as zero, and zero is the answer the manager is hoping for."*
111
+ * The failure is not merely silent, it is silent in the direction the reader
112
+ * WANTS to believe, which is why it gates rather than advises.
113
+ *
114
+ * ### Why these two were the surviving holes
115
+ *
116
+ * On the very same node, the TOKEN was checked and the COLUMN was not:
117
+ * `filter-token-unknown` (#3574) fires path-precise at
118
+ * `…widgets[4].filter.due_date.$lte`, so the traversal already walked the
119
+ * filter tree and already knew the widget's dataset. And the identical
120
+ * resolution already existed one key over — `dashboard-filter-field-unknown`
121
+ * resolves a DASHBOARD-level filter's field against each widget's dataset base
122
+ * object. `widgets[].filter` is the same field-existence invariant on the
123
+ * filter an author is MORE likely to write by hand, and it was simply never fed
124
+ * through it. `options.sortBy` is the declared-≠-enforced half:
125
+ * `DashboardWidgetOptionsSchema.sortBy` states its own contract in prose —
126
+ * *"must be one this widget actually selects"* — and nothing enforced it.
127
+ *
128
+ * ### The `include` clause, and why a dotted path is RESOLVED here
129
+ *
130
+ * A widget's `filter` is ANDed into the dataset query as `runtimeFilter`
131
+ * (`DashboardWidgetSchema.filter`; `dataset-executor.ts` `combineFilters(
132
+ * compiled.filter, selection.runtimeFilter)`), and that compiled query carries
133
+ * ONLY the joins the dataset's `include` declared (`dataset-compiler.ts`: joins
134
+ * are derived from `include`, and `assertDeclared` refuses an undeclared
135
+ * relationship path). So a dotted key here is judged on the same two clauses
136
+ * `validate-dataset-references.ts` applies one level down — existence, then
137
+ * joinability — rather than skipped. The runtime is NOT a backstop for it
138
+ * either — `assertDeclared` runs over `dimensions` and `measures` only, never
139
+ * over `runtimeFilter` — so nothing between the author and the empty tile asks
140
+ * this question.
141
+ *
142
+ * ### What #14275 closed, one position up
143
+ *
144
+ * `dashboard-filter-field-unknown` (a1) shipped with the two limitations the
145
+ * paragraph above used to record as deliberate, and #14275 closed both by
146
+ * migrating that branch onto this same seam:
147
+ *
148
+ * 1. it SKIPPED every dotted effective field (`if (field.includes('.'))
149
+ * continue;`), which was accurate when nothing in this package could walk
150
+ * hops and false once {@link resolveFieldPath} landed. The consequence was
151
+ * the sharper one: a dashboard filter is broadcast to EVERY widget on the
152
+ * board, so a single unjudged `filterBindings: { dateRange:
153
+ * 'account.signed_at' }` degraded the whole dashboard rather than one tile
154
+ * — unjudged whether or not `account` existed, whether or not `signed_at`
155
+ * existed on it, and whether or not `account` was in the dataset's
156
+ * `include`;
157
+ * 2. it resolved bare names against the object-independent `SYSTEM_FIELDS`
158
+ * union, which answers "could this be a system column ANYWHERE". The two
159
+ * differ exactly where it matters: on `ownership: 'none'` the platform
160
+ * injects no `owner_id`, so the union answered a real defect as
161
+ * resolvable. Both positions now take skip 3 per object, through
162
+ * {@link resolveFieldPath}'s use of `injectedColumnsFor`.
163
+ *
164
+ * The #8340 provenance question rides on the migrated branch unchanged, and is
165
+ * now GATED by the verdict's `injected` marker rather than by union membership:
166
+ * a leaf that resolved because it is injected is the only leaf whose anchor can
167
+ * be unprovisioned (an author-DECLARED column of the same name is the author's,
168
+ * #7859), and the marker states that fact where the flat union could not. It
169
+ * also travels: the anchor is looked up on the object the LEAF landed on, so a
170
+ * dotted filter path ending on an ADR-0015 `external` object is answered too.
171
+ *
172
+ * Resolution is {@link resolveFieldPath}'s and its `unknowable` verdicts are
173
+ * never reported (ADR-0072 D1), so the three skips every field-existence rule
174
+ * in this package takes apply unchanged: an object this stack does not define,
175
+ * an object with no readable field map (ADR-0015 `external`), and a
176
+ * registry-injected system column — the last resolved PER OBJECT rather than
177
+ * through the flat `SYSTEM_FIELDS` union, which is what lets a reference to
178
+ * `owner_id` on an `ownership: 'none'` object stay a real finding.
179
+ *
82
180
  * Warnings can be deliberately suppressed per widget via
83
181
  * `suppressWarnings: ['<rule-id>']`; errors cannot — they describe a
84
182
  * binding the analytics service cannot satisfy.
@@ -94,6 +192,17 @@ declare const WIDGET_LEGACY_ANALYTICS_SHAPE = "widget-legacy-analytics-shape";
94
192
  declare const WIDGET_LEGACY_ANALYTICS_UNRENDERABLE = "widget-legacy-analytics-unrenderable";
95
193
  declare const DASHBOARD_FILTER_FIELD_UNKNOWN = "dashboard-filter-field-unknown";
96
194
  declare const DASHBOARD_FILTER_FIELD_UNPROVISIONED = "dashboard-filter-field-unprovisioned";
195
+ /**
196
+ * [#14275] A dashboard filter's effective field resolves, but its relationship
197
+ * prefix is not declared in the bound dataset's `include`.
198
+ */
199
+ declare const DASHBOARD_FILTER_FIELD_NOT_INCLUDED = "dashboard-filter-field-not-included";
200
+ /** [#14148] A key of the widget's OWN `filter` that resolves to no column. */
201
+ declare const WIDGET_FILTER_FIELD_UNKNOWN = "widget-filter-field-unknown";
202
+ /** [#14148] A widget filter key whose relationship prefix is not in `include`. */
203
+ declare const WIDGET_FILTER_FIELD_NOT_INCLUDED = "widget-filter-field-not-included";
204
+ /** [#14148] `options.sortBy` names nothing this widget selects. */
205
+ declare const WIDGET_SORTBY_UNSELECTED = "widget-sortby-unselected";
97
206
  type WidgetBindingSeverity = 'error' | 'warning';
98
207
  interface WidgetBindingFinding {
99
208
  /** `error` = unresolvable binding (broken page); `warning` = advisory. */
@@ -109,7 +218,7 @@ interface WidgetBindingFinding {
109
218
  /** How to fix (or deliberately suppress) it. */
110
219
  hint: string;
111
220
  }
112
- type AnyRec$G = Record<string, unknown>;
221
+ type AnyRec$L = Record<string, unknown>;
113
222
  /**
114
223
  * Validate every dashboard widget's dataset binding. Returns the list of
115
224
  * findings (empty = clean). Caller decides how to surface them: `error`
@@ -117,7 +226,7 @@ type AnyRec$G = Record<string, unknown>;
117
226
  * should fail validate/build; `warning` findings are advisory and must
118
227
  * never fail the build on their own.
119
228
  */
120
- declare function validateWidgetBindings(stack: AnyRec$G): WidgetBindingFinding[];
229
+ declare function validateWidgetBindings(stack: AnyRec$L): WidgetBindingFinding[];
121
230
 
122
231
  interface ExprIssue {
123
232
  where: string;
@@ -130,12 +239,293 @@ interface ExprIssue {
130
239
  */
131
240
  severity?: 'error' | 'warning';
132
241
  }
133
- type AnyRec$F = Record<string, unknown>;
242
+ type AnyRec$K = Record<string, unknown>;
243
+ /**
244
+ * A FIELD-level conditional rule (`visibleWhen` / `readonlyWhen` /
245
+ * `requiredWhen`) that reaches for a namespace root the field level does not
246
+ * bind. The field level binds THREE roots and nothing else — `record`,
247
+ * `previous`, and `parent` on a master-detail line item — measured at three
248
+ * independent ends: the server (`rule-validator.ts` binds
249
+ * `{ record, previous, extra: { parent } }` for `readonlyWhen` and
250
+ * `{ record, previous, ...parentScope }` for `requiredWhen`), the client
251
+ * (`evalFieldPredicate` binds `record` + `previous` + a caller `scope` that
252
+ * is only ever `{ parent }` across objectui's five field-level call sites),
253
+ * and the authoring surface (objectui's `FIELD_RULE_ROOTS`, whose comment
254
+ * says "nothing else").
255
+ *
256
+ * ## Why this is a rule of its own rather than a missing root
257
+ *
258
+ * Until #6290 the same rejection fell out of `@objectstack/formula`'s
259
+ * `SCOPE_ROOTS` not listing `current_user` — a global baseline, doing a
260
+ * per-surface job by accident. Two things were wrong with that:
261
+ *
262
+ * 1. The rest of the package said the opposite. `introspectScope` hands
263
+ * `current_user` to authors as a legal root and `checkRoleCatalog`'s four
264
+ * position-membership regexes all lead with it, because ADR-0068 D1 makes
265
+ * it THE canonical spelling and `buildScope` really does mount it. One
266
+ * package, two accounts of one root.
267
+ * 2. The diagnostic was the generic bare-field one, so it prescribed
268
+ * "Write `record.current_user`" — a shape that binds on NO layer. An
269
+ * author who followed it wrote something strictly worse than what they
270
+ * started with, and the failure stayed silent.
271
+ *
272
+ * So the baseline now declares the root (a reference through it never faults
273
+ * platform-wide) and the surface that genuinely cannot bind it says so here,
274
+ * in its own words, with the prescriptions that actually exist.
275
+ *
276
+ * ## Why the membership test is an ALLOWLIST (#6713)
277
+ *
278
+ * Until #6713 this rule matched a hand-written DENYLIST — one root in #6584,
279
+ * three after #6585 (`current_user` / `user` / `ctx`). The denylist was never
280
+ * the truth: the three anchors above pin a three-item ALLOWLIST, and
281
+ * everything outside it is equally unbound, faults identically, and — this is
282
+ * the part that made the hole invisible — is equally SILENT. A root in
283
+ * `SCOPE_ROOTS` resolves in the strict env, so the bare-reference check one
284
+ * line up never fires on it either. #6713 measured 21 roots living in that
285
+ * gap (`input`, `output`, `os`, `vars`, `variables`, `automation`, `context`,
286
+ * `args`, `item`, `env`, `step`, `result`, `trigger`, `event`, `payload`,
287
+ * `data`, `params`, `config`, `settings`, `features`, `current`), two of them
288
+ * highly credible author typos rather than theoretical members:
289
+ *
290
+ * - `os.user.id` — ADR-0068 D1's FOURTH user spelling. #6585 took three and
291
+ * left this one, so the same semantic error stayed silent under one of the
292
+ * four names the platform itself mounts the user object under.
293
+ * - `data.status == 'x'` — `data` is the LEGAL root of this very
294
+ * `visibleWhen` key on a METADATA form (`view.zod.ts`: "Root: `record` …
295
+ * in runtime forms, or `data` in metadata forms"). Two form kinds, one key
296
+ * name, different roots — and the repo's own `*.form.ts` files are full of
297
+ * the `data` spelling for an author to copy.
298
+ *
299
+ * A denylist cannot track `SCOPE_ROOTS`: every root added there is unreported
300
+ * here until somebody remembers to copy it across (`current_user` itself
301
+ * arrived in #6290 and needed #6584 to be noticed). The allowlist inverts the
302
+ * maintenance burden onto the three roots that are pinned by three anchors and
303
+ * change only when the evaluators do.
304
+ *
305
+ * The membership test is {@link FIELD_RULE_JUDGED_ROOTS} minus the allowlist.
306
+ * Its bulk is `SCOPE_ROOTS`, taken from `@objectstack/formula` rather than
307
+ * restated here (#6713 published it for this consumer) — one list, one
308
+ * definition, no drift — plus the ambient roots #13935 measured outside that
309
+ * baseline; see "Why the judged vocabulary is WIDER than `SCOPE_ROOTS`" below.
310
+ * It deliberately is NOT `firstUndeclaredReference`, the declaredness oracle
311
+ * the sibling visibility rule uses, and the difference is a measured false
312
+ * positive rather than a preference: the strict env also declares CEL's TYPE
313
+ * names, so `type(record.x) == string` reports `string` as a root that
314
+ * "resolves". Judging by declaredness would reject that legitimate predicate;
315
+ * judging by membership does not. Everything the oracle owns and this list
316
+ * does not — bare field references, comprehension-macro variables — keeps
317
+ * falling to the bare-reference check, which has the right prescription for
318
+ * it. The two partitions are disjoint and there is no gap between them.
319
+ *
320
+ * ⚠️ That disjointness used to hold for FREE and no longer does. While every
321
+ * judged root was a `SCOPE_ROOTS` member it was declared in the strict env, so
322
+ * the bare-reference check could not fire on it whatever this rule decided.
323
+ * An AMBIENT root is undeclared there, so both checks see it — the walk
324
+ * suppresses the bare-reference verdict for a root this rule has claimed
325
+ * (`claimedRoot` on the `check` closure) to keep the invariant true by
326
+ * construction instead of by coincidence.
327
+ *
328
+ * ## Why it is an error and not a warning
329
+ *
330
+ * Every fault direction available here is silent, and two of the three are
331
+ * the opposite of what the author declared (see the per-slot table below):
332
+ * a `visibleWhen` written to HIDE leaves the field visible to everyone, a
333
+ * `readonlyWhen` written to unlock-under-a-condition locks the field on every
334
+ * write, and a `requiredWhen` simply never fires. None of the three produces
335
+ * a runtime error an author can find; the only signal that exists is this one
336
+ * (#6146).
337
+ *
338
+ * Verdict scope is the field level only. Per-option `visibleWhen` is checked
339
+ * by the loop in the field walk and deliberately NOT passed through here:
340
+ * options resolve against the host's predicate scope, which binds
341
+ * `current_user` (ADR-0068 / objectui#2284) — that surface is where such a
342
+ * predicate belongs, which is why it is also the first prescription below.
343
+ *
344
+ * ## The user roots, and why `ctx` is judged whole-root (#6585)
345
+ *
346
+ * ADR-0068 D1 makes `user` and `ctx.user` ALIASES of `current_user` — one
347
+ * `EvalUser` object under every spelling (`buildScope` in
348
+ * `formula/stdlib.ts` hangs the same reference on `current_user` / `user` /
349
+ * `ctx.user` / `os.user`). Matching only the canonical spelling meant the
350
+ * identical semantic error got a diagnostic under `current_user` and total
351
+ * silence under the aliases — which spelling the author picked decided
352
+ * whether they got the diagnostic, the exact fork AI authors cannot
353
+ * self-check. All four roots now share one verdict and one prescription;
354
+ * the message names the spelling found, nothing else varies. (`os` joined the
355
+ * set in #6713 — it is the fourth name `buildScope` mounts the same object
356
+ * under, and the allowlist would have rejected it anyway; what the user tier
357
+ * decides is only WHICH prescription it gets, and the user one is right for
358
+ * `os.user`. `os.org` / `os.env` land in the same tier because the option
359
+ * surface named by the first prescription binds the whole `os` namespace, not
360
+ * only its `user` member.)
361
+ *
362
+ * `ctx` is judged as a WHOLE root, not only in `ctx.user` form, because at
363
+ * this surface that is simply what is true: `buildScope` creates the `ctx`
364
+ * root ONLY when the evaluation carries a user (`scope.ctx = { user }`
365
+ * inside `if (ctx.user !== undefined)`), and no field-level site passes one
366
+ * — the server binds `record`+`previous`(+`parent`) (`rule-validator.ts`
367
+ * `readonlyWhenBindings` / the `requiredWhen` block) and the client's
368
+ * `evalFieldPredicate` binds `record`+`previous`+caller `scope` (only ever
369
+ * `{ parent }`). So `ctx.locale` faults exactly like `ctx.user.id` here.
370
+ * `ctx` IS ActionEngine's predicate root elsewhere — the platform's real
371
+ * `ctx.user` predicates all sit on action `visible` (`sys-user.object.ts`,
372
+ * `sys-invitation.object.ts`), a surface this helper never reads — and
373
+ * measured usage of field-level `*When` with ANY user root is zero across
374
+ * examples/, packages/ and objectui (#6585's sweep).
375
+ *
376
+ * The rejected alternative was matching `ctx` only in its `ctx.user` form.
377
+ * `collectCelRootIdentifiers` reports ROOTS and drops member names by
378
+ * design, so that reading needs a source-level spelling match — and a
379
+ * spelling match is precisely the defect this rule exists to remove: it
380
+ * would re-open the same fork one level down (`ctx["user"].id` silent,
381
+ * `ctx.user.id` rejected), while leaving a real fail-open fault (`ctx.locale`)
382
+ * unreported for the sake of a narrower rule NAME.
383
+ *
384
+ * ## The message tiers on TWO orthogonal axes
385
+ *
386
+ * The causal half tiers by SLOT (#6716); the prescription half tiers by ROOT
387
+ * (#6713). They are independent — `data` on a `readonlyWhen` needs the
388
+ * metadata-form prescription AND the locked-field consequence — so one
389
+ * skeleton carries both rather than a message per combination.
390
+ *
391
+ * ### Axis 1 — the consequence, by SLOT (#6716, all three cells measured)
392
+ *
393
+ * Until #6713/#6716 all three slots shared ONE sentence: "the predicate
394
+ * faults and falls back to VISIBLE, leaving the field the test was meant to
395
+ * hide showing for everyone". That is precise for exactly one of them. The
396
+ * measurement, at both ends of each slot:
397
+ *
398
+ * - **`visibleWhen` — client-only, and since objectui#6010 it fails in TWO
399
+ * ways rather than one.** The server half is unchanged and still exactly
400
+ * right: it never evaluates a FIELD-level `visibleWhen` at all —
401
+ * `rule-validator.ts`'s `ConditionalFieldDef` has no such member, and
402
+ * `hasFieldRules` gates on `requiredWhen || readonlyWhen ||
403
+ * fieldHasOptionVisibility` — the `visibleWhen` it does evaluate is the
404
+ * per-OPTION one. The RENDERER half moved. `plugin-form`'s
405
+ * `sectionFields.ts` (`fromObjectSchema`) copies an object field's ADR-0036
406
+ * `visibleWhen` / `readonlyWhen` / `requiredWhen` straight onto the runtime
407
+ * form field, and the SDUI form renderer resolves those through
408
+ * `resolveFieldRuleState` **with `predicateScope` bound**. So under a host
409
+ * that publishes a scope the predicate does not fault: it RESOLVES, the
410
+ * control is hidden in that one form, and the server still returns the value
411
+ * to every other reader — a silent enforcement gap. Only where no host
412
+ * publishes a scope (the public `/f/:slug` route, every non-form reader)
413
+ * does the old sentence still describe what happens: unbound root, fault,
414
+ * `fallback: true`, field visible to everyone.
415
+ *
416
+ * ⛔ This is not a reason to relax {@link FIELD_RULE_BOUND_ROOTS}. The
417
+ * verdict is MORE justified than when it was written, not less: trading a
418
+ * loud lint error for a gap nobody can see is the one outcome worse than
419
+ * the error. Only the causal clause was re-measured.
420
+ * - **`readonlyWhen` — the two ends fault in OPPOSITE directions, and the
421
+ * server wins.** Server: `isReadonlyWhenLocked` matches the fault with
422
+ * `unknownVariableOf` and returns `true` — "the declared lock is not
423
+ * waived because it could not be evaluated" (#4889's carve-out, whose
424
+ * trigger is precisely the unbound-ROOT case, not the undeclared-key one)
425
+ * — and `stripReadonlyWhenFields` then DELETES the field from the incoming
426
+ * payload and lets the rest of the write through. Client:
427
+ * `resolveFieldRuleState` passes `fallback: false`, so the form renders the
428
+ * field editable. The standing "server enforces, client is courtesy" rule
429
+ * — ADR-0124 D1 — resolves the disagreement: the author edits
430
+ * the field, the save reports
431
+ * success, and the value silently never lands. The old sentence told this
432
+ * author the field would be VISIBLE TO EVERYONE — the opposite failure, and
433
+ * the opposite troubleshooting direction.
434
+ * - **`requiredWhen` — fail-OPEN at both ends, and never about visibility.**
435
+ * Server: the `requiredWhen` block logs `unknownVariableOf`'s name and
436
+ * `continue`s — #4977 deliberately did not copy #4889's carve-out, so the
437
+ * required-check is skipped for that write. Client: `fallback: false`, so
438
+ * the form does not mark the field required either. Both ends agree and
439
+ * both do nothing: the requirement is never enforced anywhere, and a record
440
+ * saves with the field empty. "Falls back to VISIBLE" was not merely
441
+ * imprecise here, it named the wrong property of the field.
442
+ *
443
+ * `conditionalRequired` also reaches this helper (the field walk still passes
444
+ * it). It is a `retiredKey` in `FieldSchema` — the strict schema rejects it by
445
+ * name — so the branch is inert on the parsed compile path, and it gets a
446
+ * slot-agnostic clause rather than a fabricated fourth measurement.
447
+ *
448
+ * ### Axis 2 — the prescription, by ROOT (#6713)
449
+ *
450
+ * The pre-#6713 prescriptions are user-oriented (move to the option level,
451
+ * declare permission-set FLS) because the pre-#6713 denylist held only user
452
+ * roots. Handed to an author who wrote `data.type == 'select'`, "move it to
453
+ * the option's `visibleWhen`" answers a question nobody asked. Three tiers:
454
+ *
455
+ * - **user roots** (`current_user` / `user` / `ctx` / `os`) keep the existing
456
+ * two user-specific prescriptions plus the `record` rewrite. What changed is
457
+ * the GROUNDING of the first one, not the prescription: it used to read
458
+ * "per-option is the one `*When` surface that binds `current_user`", and
459
+ * since objectui#6010 a form VIEW's field predicate binds those roots too —
460
+ * client-side only, because nothing on the write path evaluates a form-view
461
+ * field `visibleWhen`. So per-option is recommended for the reason that
462
+ * survives the change: the rule validator enforces it on write. The message
463
+ * now says that, and names the view surface only to refuse it as a
464
+ * destination for a server-enforced object rule (trading a loud lint error
465
+ * for a silent enforcement gap is the one outcome worse than the error);
466
+ * - **`data`** gets the metadata-form-vs-runtime-form explanation, because
467
+ * that is what the mistake IS — the same key name, the other form kind's
468
+ * root;
469
+ * - **`app` and the other AMBIENT roots** get the renderer-mounted
470
+ * explanation and an explicit refusal of `record.<root>` (#13935, below);
471
+ * - **everything else** gets the general rewrite, phrased without claiming
472
+ * which other surface the author copied it from.
473
+ *
474
+ * ## Why the judged vocabulary is WIDER than `SCOPE_ROOTS` (#13935)
475
+ *
476
+ * `SCOPE_ROOTS` was the membership test until #13935, and it is the wrong
477
+ * question by one word: it answers "is this root declared PLATFORM-WIDE",
478
+ * while the rule needs "is this root bound at SOME evaluation site". The two
479
+ * agreed for 27 roots and then disagreed for `app` — bound by objectui's
480
+ * `ExpressionProvider` on the very surface an author migrates a rule DOWN
481
+ * from, absent from the baseline. Falling outside the membership test sent it
482
+ * to the bare-reference check, whose prescription is "Write `record.app`" —
483
+ * and following that earns ``unknown field `app` `` from the field-existence
484
+ * pass one line up. A first diagnostic that is actively false about where the
485
+ * root binds, and a wasted correction cycle.
486
+ *
487
+ * `SCOPE_ROOTS`' own docblock made this measurable rather than a matter of
488
+ * taste: its `current_user` entry claims to be "the last one this list was
489
+ * missing (#6290)". `app` is that sentence's second counterexample — the same
490
+ * mechanism (#6713's point: a hand-maintained list doing a per-surface job
491
+ * drifts), a second sighting, not an analogy to the first.
492
+ *
493
+ * ⛔ The repair deliberately does NOT add `app` to `SCOPE_ROOTS`. That list is
494
+ * the published strict-lint accept baseline in `@objectstack/formula`, so
495
+ * adding a root there stops EVERY surface that judges bare identifiers from
496
+ * faulting it — a widened public accept set, to fix one surface's diagnostic.
497
+ * The judged vocabulary is assembled HERE, where the per-surface question is
498
+ * asked, and `SCOPE_ROOTS` is left as the proper subset it already is.
499
+ */
500
+ /**
501
+ * The roots a field-level `*When` predicate binds. Everything else in
502
+ * {@link FIELD_RULE_JUDGED_ROOTS} is rejected — see the allowlist section above.
503
+ */
504
+ declare const FIELD_RULE_BOUND_ROOTS: readonly ["record", "previous", "parent"];
505
+ /**
506
+ * The decision half of the rule documented above, as ONE exported function so
507
+ * every surface that judges a field-level `*When` stands on the same verdict
508
+ * AND the same message (Prime Directive #12). Two consumers today: the
509
+ * metadata walk in {@link validateStackExpressions} below, and the docs-corpus
510
+ * gate `scripts/check-doc-formula-expressions.mjs`, which judges the same three
511
+ * slots where a fenced example's enclosing structure identifies the field layer
512
+ * (#11407). Before that gate existed this lived as a closure inside the walk —
513
+ * fine while there was one caller, and exactly how a second caller comes to own
514
+ * a DIALECT of the rule instead of the rule.
515
+ *
516
+ * `null` = nothing to report: the source does not parse (the syntax pass owns
517
+ * that, and reporting it twice under a worse name is the #4889 mistake), or
518
+ * every root it reads is one the field level binds.
519
+ */
520
+ declare function fieldRuleRootIssue(slot: string, source: string): {
521
+ root: string;
522
+ message: string;
523
+ } | null;
134
524
  /**
135
525
  * Validate every predicate in the stack. Returns the list of issues (empty =
136
526
  * clean). Caller decides how to surface / whether to fail the build.
137
527
  */
138
- declare function validateStackExpressions(stack: AnyRec$F): ExprIssue[];
528
+ declare function validateStackExpressions(stack: AnyRec$K): ExprIssue[];
139
529
 
140
530
  /**
141
531
  * The corrective sentence, lifted **verbatim** from `unevaluableRuleError` in
@@ -328,7 +718,7 @@ interface ListViewModeFinding {
328
718
  hint: string;
329
719
  }
330
720
  declare const LIST_VIEW_FILTERS_IN_VIEWS_MODE = "list-view-filters-in-views-mode";
331
- type AnyRec$E = Record<string, unknown>;
721
+ type AnyRec$J = Record<string, unknown>;
332
722
  /**
333
723
  * Flag ADR-0047 "views" mode violations on an object's built-in named views or a
334
724
  * `defineView` default `list` / named `listViews`: `quickFilters`, or a `tabs`
@@ -338,7 +728,7 @@ type AnyRec$E = Record<string, unknown>;
338
728
  *
339
729
  * Feed the PRE-parse stack (normalizeStackInput output) — see file header.
340
730
  */
341
- declare function validateListViewMode(stack: AnyRec$E): ListViewModeFinding[];
731
+ declare function validateListViewMode(stack: AnyRec$J): ListViewModeFinding[];
342
732
 
343
733
  type FunctionalCompletenessSeverity = 'error' | 'warning';
344
734
  interface FunctionalCompletenessFinding {
@@ -453,7 +843,7 @@ declare const FLOW_TIME_RELATIVE_DESCRIPTOR_UNROUTABLE = "flow-time-relative-des
453
843
  * call sites that each skip it.
454
844
  */
455
845
  declare const FLOW_TRIGGER_UNROUTABLE = "flow-trigger-unroutable";
456
- type AnyRec$D = Record<string, unknown>;
846
+ type AnyRec$I = Record<string, unknown>;
457
847
  /**
458
848
  * Validate auto-launched flow trigger wiring against the stack definition.
459
849
  *
@@ -462,7 +852,7 @@ type AnyRec$D = Record<string, unknown>;
462
852
  * owns the `timeRelative` descriptor's contract, which is the point: the rule
463
853
  * ASKS that schema rather than restating it.
464
854
  */
465
- declare function validateFlowTriggerReadiness(stack: AnyRec$D): FlowTriggerReadinessFinding[];
855
+ declare function validateFlowTriggerReadiness(stack: AnyRec$I): FlowTriggerReadinessFinding[];
466
856
 
467
857
  type FlowTemplatePathSeverity = 'error' | 'warning';
468
858
  interface FlowTemplatePathFinding {
@@ -478,12 +868,12 @@ interface FlowTemplatePathFinding {
478
868
  declare const FLOW_TEMPLATE_UNKNOWN_FIELD = "flow-template-unknown-field";
479
869
  declare const FLOW_TEMPLATE_LOOKUP_TRAVERSAL = "flow-template-lookup-traversal";
480
870
  declare const FLOW_TEMPLATE_FIELD_UNPROVISIONED = "flow-template-field-unprovisioned";
481
- type AnyRec$C = Record<string, unknown>;
871
+ type AnyRec$H = Record<string, unknown>;
482
872
  /**
483
873
  * Validate `{record.<path>}` template references across every record-change
484
874
  * flow. Pure and dependency-free; safe on pre- or post-parse stacks.
485
875
  */
486
- declare function validateFlowTemplatePaths(stack: AnyRec$C): FlowTemplatePathFinding[];
876
+ declare function validateFlowTemplatePaths(stack: AnyRec$H): FlowTemplatePathFinding[];
487
877
 
488
878
  type ReadonlyFlowWriteSeverity = 'error' | 'warning';
489
879
  interface ReadonlyFlowWriteFinding {
@@ -498,12 +888,275 @@ interface ReadonlyFlowWriteFinding {
498
888
  }
499
889
  declare const FLOW_UPDATE_READONLY_FIELD = "flow-update-readonly-field";
500
890
  declare const FLOW_UPDATE_READONLY_WHEN_FIELD = "flow-update-readonly-when-field";
501
- type AnyRec$B = Record<string, unknown>;
891
+ type AnyRec$G = Record<string, unknown>;
502
892
  /**
503
893
  * Validate flow `update_record` writes against target-object readonly
504
894
  * declarations. Pure and dependency-free; safe on pre- or post-parse stacks.
505
895
  */
506
- declare function validateReadonlyFlowWrites(stack: AnyRec$B): ReadonlyFlowWriteFinding[];
896
+ declare function validateReadonlyFlowWrites(stack: AnyRec$G): ReadonlyFlowWriteFinding[];
897
+
898
+ /** What a caller needs to tell an author WHICH source went unread, and where. */
899
+ interface SourceParseFailure {
900
+ /** The first parse diagnostic, in the compiler's own wording, flattened to one line. */
901
+ message: string;
902
+ /** 1-based line, in the AUTHORED source's coordinates (see `synthesizedLinesBefore`). */
903
+ line: number;
904
+ /** 1-based column. */
905
+ column: number;
906
+ /** How many parse diagnostics in total — `message` is the first of `count`. */
907
+ count: number;
908
+ }
909
+ /** A parse plus the verdict on whether it succeeded. `failure` absent ⇒ it parsed. */
910
+ interface CheckedParse {
911
+ /**
912
+ * The tree, ALWAYS returned — including when `failure` is set. Error recovery
913
+ * produces a partial tree, and a caller that already reports findings from it
914
+ * keeps doing so: the fix here is the missing SIGNAL, not the removal of
915
+ * whatever the recovered tree could still be read for.
916
+ */
917
+ sourceFile: ts.SourceFile;
918
+ /** Set when the parser reported at least one syntax diagnostic. */
919
+ failure?: SourceParseFailure;
920
+ }
921
+ interface CheckedParseOptions {
922
+ target: ts.ScriptTarget;
923
+ setParentNodes: boolean;
924
+ scriptKind: ts.ScriptKind;
925
+ /**
926
+ * Lines the CALLER synthesised ahead of the authored source, subtracted from
927
+ * the reported position so it lands in the author's coordinates.
928
+ *
929
+ * `validate-hook-body-writes.ts` parses an L2 hook body wrapped in
930
+ * `async function __body(ctx) {\n…\n}` — the shape the runtime compiles it
931
+ * into — so its diagnostics are one line low. The reported line is clamped to
932
+ * at least 1, so a diagnostic that lands on a synthesised line is attributed
933
+ * to the nearest AUTHORED line and never to a line the author did not write.
934
+ */
935
+ synthesizedLinesBefore?: number;
936
+ }
937
+ /**
938
+ * The one wording every caller's message embeds, so three findings about the
939
+ * same defect do not describe it three ways.
940
+ */
941
+ declare function describeParseFailure(failure: SourceParseFailure): string;
942
+ /**
943
+ * The hint every caller's finding carries. It says what the finding IS — a
944
+ * statement about what the checker could read, not a second syntax verdict —
945
+ * because a source that does not parse is not scored, and an author who reads
946
+ * "no problems found" about it would be reading a green line that lied.
947
+ */
948
+ declare const PARSE_FAILURE_HINT: string;
949
+
950
+ type HookBodyWriteSeverity = 'warning';
951
+ interface HookBodyWriteFinding {
952
+ /** v1 is advisory-only by contract — the type says so. */
953
+ severity: HookBodyWriteSeverity;
954
+ rule: string;
955
+ /** Human-readable location, e.g. `hook "normalize_lead" › body`. */
956
+ where: string;
957
+ /** Config path, e.g. `hooks[0].body.source`. */
958
+ path: string;
959
+ message: string;
960
+ hint: string;
961
+ }
962
+ declare const HOOK_BODY_WRITE_UNKNOWN_FIELD = "hook-body-write-unknown-field";
963
+ /**
964
+ * [#10653] The body did not parse, so its write set is whatever error recovery
965
+ * left readable.
966
+ *
967
+ * Reported rather than skipped for the reason this rule exists at all: a
968
+ * mistake must be visible where it is MADE. An unparseable body reached the
969
+ * extractor, produced fewer matches, and came back as a hook with nothing to
970
+ * report — the same silence the undeclared write itself has at run time, this
971
+ * time wearing the checker's badge. `warning` because the whole rule is
972
+ * advisory and never gates (the severity type admits nothing else).
973
+ */
974
+ declare const HOOK_BODY_SOURCE_UNPARSEABLE = "hook-body-source-unparseable";
975
+ /**
976
+ * [#8663] The write-axis twin of `flow-template-field-unprovisioned` (#8340):
977
+ * the body writes a field {@link IMPLICIT_FIELDS} exempts, but on THIS target
978
+ * the platform registered that anchor without provisioning storage for it.
979
+ *
980
+ * A separate id at `warning` severity rather than a reclassification of
981
+ * {@link HOOK_BODY_WRITE_UNKNOWN_FIELD}, matching #8340's precedent exactly:
982
+ * the existence verdict is unchanged (the name IS addressable), and what is
983
+ * added is a second, independently suppressible finding on the path where the
984
+ * existence check stays silent.
985
+ */
986
+ declare const HOOK_BODY_WRITE_UNPROVISIONED_ANCHOR = "hook-body-write-unprovisioned-anchor";
987
+ /** One syntactic write shape the extractor recognizes. */
988
+ interface HookBodyWritePattern {
989
+ /** Stable pattern id, carried on every extracted write. */
990
+ readonly id: string;
991
+ /** Author-facing syntax summary (for docs/diagnostics, not matching). */
992
+ readonly syntax: string;
993
+ /** Reconciliation fixture: extracting `source` must yield exactly `writes`. */
994
+ readonly example: {
995
+ readonly source: string;
996
+ readonly writes: ReadonlyArray<{
997
+ field: string;
998
+ object?: string;
999
+ }>;
1000
+ };
1001
+ }
1002
+ declare const HOOK_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[];
1003
+ /** A ledger pattern a given rule does NOT consume, and why. */
1004
+ interface BodyWritePatternExclusion {
1005
+ /** The {@link HOOK_BODY_WRITE_PATTERNS} entry id being excluded. */
1006
+ readonly id: string;
1007
+ /** Why the shape does not mean the same thing on this rule's surface. */
1008
+ readonly reason: string;
1009
+ }
1010
+ /**
1011
+ * The ledger shapes THIS rule consumes.
1012
+ *
1013
+ * Declared rather than implied: before the ledger carried a shape the hook
1014
+ * surface does not have, every write with no `object` was necessarily a
1015
+ * `ctx.input` write, and the rule could branch on that alone. It no longer can
1016
+ * — a `record-property-assign` write also carries no object, and would have
1017
+ * been reported as "the hook writes 'stage' to its input", which is false.
1018
+ * Each consumer declaring its own subset is what stops the next added shape
1019
+ * from silently landing in a branch that was never written for it.
1020
+ */
1021
+ declare const HOOK_BODY_WRITE_PATTERN_IDS: readonly string[];
1022
+ /** Ledger shapes this rule leaves alone, each with its reason. */
1023
+ declare const HOOK_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
1024
+ type AnyRec$F = Record<string, unknown>;
1025
+ /** One statically-extracted field write found in an L2 body. */
1026
+ interface ExtractedHookBodyWrite {
1027
+ /** Which {@link HOOK_BODY_WRITE_PATTERNS} entry matched. */
1028
+ patternId: string;
1029
+ /** Target object name; `undefined` = the hook's own target object(s). */
1030
+ object?: string;
1031
+ /** The `ctx.api` method for diagnostics (`insert`/`create`/`update`/`updateById`). */
1032
+ method?: string;
1033
+ field: string;
1034
+ }
1035
+ /** Everything one parse of an L2 body yields. */
1036
+ interface ExtractedHookBodyWriteSet {
1037
+ /** Every literal write the {@link HOOK_BODY_WRITE_PATTERNS} ledger declares. */
1038
+ writes: ExtractedHookBodyWrite[];
1039
+ /**
1040
+ * `ctx.record` is handed to something as a VALUE somewhere in the body — an
1041
+ * argument, an assignment RHS, a spread, a return — rather than only having
1042
+ * its properties read and written, or being truthiness/type tested.
1043
+ *
1044
+ * The action rule needs this to tell a dead snapshot write from a live one:
1045
+ * `ctx.record.stage = 'won'; await ctx.api.object('d').update(ctx.record)`
1046
+ * builds a payload and persists it, so the assignment is not a no-op. When
1047
+ * this is true, no record write in the body can be judged, and none is
1048
+ * reported. (One-level aliasing — `const r = ctx.record` — reads as an
1049
+ * escape too, which is the safe direction: it suppresses findings.)
1050
+ */
1051
+ ctxRecordEscapes: boolean;
1052
+ /**
1053
+ * [#10653] Set when the body did not parse, so `writes` is whatever error
1054
+ * recovery left readable rather than the body's actual write set.
1055
+ *
1056
+ * Absent means one of two things, and they are not the same: the body parsed,
1057
+ * or the cheap pre-filter above rejected it before any parse. The filter is a
1058
+ * raw-text scan for `ctx` / `Object`, and a body containing neither cannot
1059
+ * match any pattern however it parses — so a skipped parse claims nothing and
1060
+ * hides nothing.
1061
+ *
1062
+ * ## Whose fault an unparseable body is — asked, not assumed
1063
+ *
1064
+ * The body is parsed inside a synthesised wrapper (`async function __body(ctx)
1065
+ * { … }`) because that is the shape the runtime compiles it into
1066
+ * (`new AsyncFunction('ctx', source)`). So a parse failure here could in
1067
+ * principle be the WRAPPER's fault rather than the author's, and blaming the
1068
+ * author for the checker's own bug is the failure this whole change is about.
1069
+ * It cannot be: the wrapper is a constant, and `validate-hook-body-writes.
1070
+ * test.ts` pins that it parses clean around an empty body and around every
1071
+ * example in the pattern ledger. Any diagnostic therefore comes from the
1072
+ * body — and its position is reported in the BODY's own coordinates (the
1073
+ * wrapper's line is subtracted, and the result is clamped so it can never
1074
+ * point at a line the author did not write).
1075
+ */
1076
+ parseFailure?: SourceParseFailure;
1077
+ }
1078
+ /**
1079
+ * Extract every literal field write the pattern ledger declares from an L2
1080
+ * body's source. Parse-only (the source is never executed), error-tolerant
1081
+ * (a body with syntax errors simply yields fewer matches), and lazy: the
1082
+ * TypeScript compiler is not loaded when no pattern can possibly match.
1083
+ *
1084
+ * Thin projection of {@link extractHookBodyWriteSet} — use that one when the
1085
+ * `ctx.record` liveness signal matters, so the body is parsed once, not twice.
1086
+ */
1087
+ declare function extractHookBodyWrites(source: string): ExtractedHookBodyWrite[];
1088
+ /** {@link extractHookBodyWrites} plus the `ctx.record` liveness signal, one parse. */
1089
+ declare function extractHookBodyWriteSet(source: string): ExtractedHookBodyWriteSet;
1090
+ /**
1091
+ * Validate L2 hook-body writes against target-object field declarations.
1092
+ * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.
1093
+ */
1094
+ declare function validateHookBodyWrites(stack: AnyRec$F): HookBodyWriteFinding[];
1095
+
1096
+ type ReadonlyHookWriteSeverity = 'error' | 'warning';
1097
+ interface ReadonlyHookWriteFinding {
1098
+ severity: ReadonlyHookWriteSeverity;
1099
+ rule: string;
1100
+ /** Human-readable location, e.g. `hook "touch_account" > body`. */
1101
+ where: string;
1102
+ /** Config path, e.g. `hooks[0].body.source`. */
1103
+ path: string;
1104
+ message: string;
1105
+ hint: string;
1106
+ }
1107
+ declare const HOOK_API_UPDATE_READONLY_FIELD = "hook-api-update-readonly-field";
1108
+ declare const HOOK_API_UPDATE_READONLY_WHEN_FIELD = "hook-api-update-readonly-when-field";
1109
+ /**
1110
+ * The `HOOK_BODY_WRITE_PATTERNS` shapes THIS rule consumes.
1111
+ *
1112
+ * Declared as data rather than implied by a branch, for the reason the sibling
1113
+ * rules declare theirs: a write with no `object` is not a single thing (both
1114
+ * `ctx.input` and `ctx.record` shapes carry none), so a future ledger addition
1115
+ * must not be able to land silently in a branch never written for it.
1116
+ */
1117
+ declare const READONLY_HOOK_WRITE_PATTERN_IDS: readonly string[];
1118
+ /** Ledger shapes this rule leaves alone, each with its reason. */
1119
+ declare const READONLY_HOOK_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
1120
+ type AnyRec$E = Record<string, unknown>;
1121
+ /**
1122
+ * Validate L2 hook-body `ctx.api` writes against target-object readonly
1123
+ * declarations. Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or
1124
+ * post-parse stacks.
1125
+ */
1126
+ declare function validateReadonlyHookWrites(stack: AnyRec$E): ReadonlyHookWriteFinding[];
1127
+
1128
+ type ReadonlyActionWriteSeverity = 'warning';
1129
+ interface ReadonlyActionWriteFinding {
1130
+ /** Advisory by contract - the conditional strip is per record state. */
1131
+ severity: ReadonlyActionWriteSeverity;
1132
+ rule: string;
1133
+ /** Human-readable location, e.g. `action "freeze_invoice" > body`. */
1134
+ where: string;
1135
+ /** Config path, e.g. `actions[0].body.source`. */
1136
+ path: string;
1137
+ message: string;
1138
+ hint: string;
1139
+ }
1140
+ /** Rule id (registry entry). */
1141
+ declare const ACTION_API_UPDATE_READONLY_WHEN_FIELD = "action-api-update-readonly-when-field";
1142
+ /**
1143
+ * The `HOOK_BODY_WRITE_PATTERNS` shapes THIS rule consumes.
1144
+ *
1145
+ * Declared as data rather than implied by a branch, for the reason the sibling
1146
+ * rules declare theirs: a write with no `object` is not a single thing (both
1147
+ * `ctx.input` and `ctx.record` shapes carry none), so a future ledger addition
1148
+ * must not be able to land silently in a branch never written for it.
1149
+ */
1150
+ declare const READONLY_ACTION_WRITE_PATTERN_IDS: readonly string[];
1151
+ /** Ledger shapes this rule leaves alone, each with its reason. */
1152
+ declare const READONLY_ACTION_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
1153
+ type AnyRec$D = Record<string, unknown>;
1154
+ /**
1155
+ * Validate L2 action-body `ctx.api` writes against target-object `readonlyWhen`
1156
+ * declarations. Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or
1157
+ * post-parse stacks.
1158
+ */
1159
+ declare function validateReadonlyActionWrites(stack: AnyRec$D): ReadonlyActionWriteFinding[];
507
1160
 
508
1161
  type ViewContainerSeverity = 'error' | 'warning';
509
1162
  interface ViewContainerFinding {
@@ -540,14 +1193,14 @@ declare const STYLE_CLASSNAME_TAILWIND = "style-classname-tailwind";
540
1193
  declare const STYLE_RESPONSIVE_NO_BASE = "style-responsive-no-base";
541
1194
  declare const STYLE_UNKNOWN_CSS_PROPERTY = "style-unknown-css-property";
542
1195
  declare const STYLE_UNKNOWN_TOKEN = "style-unknown-token";
543
- type AnyRec$A = Record<string, unknown>;
1196
+ type AnyRec$C = Record<string, unknown>;
544
1197
  /**
545
1198
  * Validate every page's component tree for SDUI styling correctness (ADR-0065).
546
1199
  * Returns findings (empty = clean). `error` findings describe styles that are
547
1200
  * silently dropped and should fail validate/build; `warning` findings are
548
1201
  * advisory (typos, drift, footguns).
549
1202
  */
550
- declare function validateResponsiveStyles(stack: AnyRec$A): StyleFinding[];
1203
+ declare function validateResponsiveStyles(stack: AnyRec$C): StyleFinding[];
551
1204
 
552
1205
  type JsxPageSeverity = 'error' | 'warning';
553
1206
  interface JsxPageFinding {
@@ -560,8 +1213,8 @@ interface JsxPageFinding {
560
1213
  message: string;
561
1214
  hint: string;
562
1215
  }
563
- type AnyRec$z = Record<string, unknown>;
564
- declare function validateJsxPages(stack: AnyRec$z, opts?: {
1216
+ type AnyRec$B = Record<string, unknown>;
1217
+ declare function validateJsxPages(stack: AnyRec$B, opts?: {
565
1218
  manifest?: Manifest;
566
1219
  }): JsxPageFinding[];
567
1220
 
@@ -574,8 +1227,8 @@ interface ReactPageFinding {
574
1227
  message: string;
575
1228
  hint: string;
576
1229
  }
577
- type AnyRec$y = Record<string, unknown>;
578
- declare function validateReactPages(stack: AnyRec$y): ReactPageFinding[];
1230
+ type AnyRec$A = Record<string, unknown>;
1231
+ declare function validateReactPages(stack: AnyRec$A): ReactPageFinding[];
579
1232
 
580
1233
  type ReactPropSeverity = 'error' | 'warning';
581
1234
  interface ReactPropFinding {
@@ -586,7 +1239,7 @@ interface ReactPropFinding {
586
1239
  message: string;
587
1240
  hint: string;
588
1241
  }
589
- type AnyRec$x = Record<string, unknown>;
1242
+ type AnyRec$z = Record<string, unknown>;
590
1243
  /**
591
1244
  * The source could not be parsed, so the prop checks below read a partially
592
1245
  * recovered tree (#10653).
@@ -604,65 +1257,21 @@ type AnyRec$x = Record<string, unknown>;
604
1257
  * says the true thing: these checks did not get to run.
605
1258
  */
606
1259
  declare const REACT_PAGE_SOURCE_UNPARSEABLE = "react-page-source-unparseable";
1260
+ /**
1261
+ * [#11284] A prop written in a deprecated react-tier spelling (maintainer
1262
+ * ruling 2026-08-23: the react tier converges on the metadata-tier
1263
+ * vocabulary, deprecate-first). Warning, never error: the old spelling keeps
1264
+ * working for the whole deprecation window — this is the loud half of
1265
+ * "alias + loud deprecation", same shape as `approval-approver-type-deprecated`.
1266
+ */
1267
+ declare const REACT_PROP_DEPRECATED = "react-prop-deprecated";
607
1268
  declare const REACT_CHART_FIELD_UNKNOWN = "react-chart-field-unknown";
608
1269
  declare const REACT_CHART_FIELD_UNPROVISIONED = "react-chart-field-unprovisioned";
609
1270
  declare const REACT_CHART_AGGREGATE_INVALID = "react-chart-aggregate-invalid";
610
1271
  declare const REACT_CHART_AXIS_UNKNOWN = "react-chart-axis-unknown";
611
1272
  declare const REACT_CHART_DRILLDOWN_INVALID = "react-chart-drilldown-invalid";
612
1273
  declare const REACT_BLOCK_NEEDS_RECORD_CONTEXT = "react-block-needs-record-context";
613
- declare function validateReactPageProps(stack: AnyRec$x): ReactPropFinding[];
614
-
615
- /** What a caller needs to tell an author WHICH source went unread, and where. */
616
- interface SourceParseFailure {
617
- /** The first parse diagnostic, in the compiler's own wording, flattened to one line. */
618
- message: string;
619
- /** 1-based line, in the AUTHORED source's coordinates (see `synthesizedLinesBefore`). */
620
- line: number;
621
- /** 1-based column. */
622
- column: number;
623
- /** How many parse diagnostics in total — `message` is the first of `count`. */
624
- count: number;
625
- }
626
- /** A parse plus the verdict on whether it succeeded. `failure` absent ⇒ it parsed. */
627
- interface CheckedParse {
628
- /**
629
- * The tree, ALWAYS returned — including when `failure` is set. Error recovery
630
- * produces a partial tree, and a caller that already reports findings from it
631
- * keeps doing so: the fix here is the missing SIGNAL, not the removal of
632
- * whatever the recovered tree could still be read for.
633
- */
634
- sourceFile: ts.SourceFile;
635
- /** Set when the parser reported at least one syntax diagnostic. */
636
- failure?: SourceParseFailure;
637
- }
638
- interface CheckedParseOptions {
639
- target: ts.ScriptTarget;
640
- setParentNodes: boolean;
641
- scriptKind: ts.ScriptKind;
642
- /**
643
- * Lines the CALLER synthesised ahead of the authored source, subtracted from
644
- * the reported position so it lands in the author's coordinates.
645
- *
646
- * `validate-hook-body-writes.ts` parses an L2 hook body wrapped in
647
- * `async function __body(ctx) {\n…\n}` — the shape the runtime compiles it
648
- * into — so its diagnostics are one line low. The reported line is clamped to
649
- * at least 1, so a diagnostic that lands on a synthesised line is attributed
650
- * to the nearest AUTHORED line and never to a line the author did not write.
651
- */
652
- synthesizedLinesBefore?: number;
653
- }
654
- /**
655
- * The one wording every caller's message embeds, so three findings about the
656
- * same defect do not describe it three ways.
657
- */
658
- declare function describeParseFailure(failure: SourceParseFailure): string;
659
- /**
660
- * The hint every caller's finding carries. It says what the finding IS — a
661
- * statement about what the checker could read, not a second syntax verdict —
662
- * because a source that does not parse is not scored, and an author who reads
663
- * "no problems found" about it would be reading a green line that lied.
664
- */
665
- declare const PARSE_FAILURE_HINT: string;
1274
+ declare function validateReactPageProps(stack: AnyRec$z): ReactPropFinding[];
666
1275
 
667
1276
  type SourceStyleSeverity = 'error' | 'warning';
668
1277
  interface SourceStyleFinding {
@@ -674,8 +1283,8 @@ interface SourceStyleFinding {
674
1283
  hint: string;
675
1284
  }
676
1285
  declare const PAGE_SOURCE_CLASSNAME = "page-source-className-tailwind";
677
- type AnyRec$w = Record<string, unknown>;
678
- declare function validatePageSourceStyling(stack: AnyRec$w): SourceStyleFinding[];
1286
+ type AnyRec$y = Record<string, unknown>;
1287
+ declare function validatePageSourceStyling(stack: AnyRec$y): SourceStyleFinding[];
679
1288
 
680
1289
  /**
681
1290
  * Build-time record-title diagnostics (ADR-0079).
@@ -719,14 +1328,14 @@ interface RecordTitleFinding {
719
1328
  /** How to fix it. */
720
1329
  hint: string;
721
1330
  }
722
- type AnyRec$v = Record<string, unknown>;
1331
+ type AnyRec$x = Record<string, unknown>;
723
1332
  /**
724
1333
  * Validate every object's record-title declaration. Returns the list of
725
1334
  * findings (empty = clean). Both rules are advisory (`warning`): the caller
726
1335
  * must never fail the build on them alone — auto-provision + the `Record #<id>`
727
1336
  * floor guarantee a resolvable title at runtime.
728
1337
  */
729
- declare function validateRecordTitle(stack: AnyRec$v): RecordTitleFinding[];
1338
+ declare function validateRecordTitle(stack: AnyRec$x): RecordTitleFinding[];
730
1339
 
731
1340
  declare const FIELD_GROUP_UNDECLARED = "field-group-undeclared";
732
1341
  declare const FIELD_GROUP_EMPTY = "field-group-empty";
@@ -748,16 +1357,23 @@ interface SemanticRoleFinding {
748
1357
  /** How to fix it. */
749
1358
  hint: string;
750
1359
  }
751
- type AnyRec$u = Record<string, unknown>;
1360
+ type AnyRec$w = Record<string, unknown>;
752
1361
  /**
753
1362
  * Validate every object's semantic-role pointers. Returns the list of
754
1363
  * findings (empty = clean). Advisory only — the caller must never fail the
755
1364
  * build on these alone.
756
1365
  */
757
- declare function validateSemanticRoles(stack: AnyRec$u): SemanticRoleFinding[];
1366
+ declare function validateSemanticRoles(stack: AnyRec$w): SemanticRoleFinding[];
758
1367
 
759
1368
  declare const FORM_FIELD_UNKNOWN = "form-field-unknown";
760
1369
  declare const FORM_COLSPAN_ABSOLUTE = "absolute-colspan-discouraged";
1370
+ /**
1371
+ * [#13855] A section's `group` names a field group the bound object does not
1372
+ * declare. The reference form inherits the section's whole membership from that
1373
+ * group, so a dangling key leaves the section with no members and it does not
1374
+ * render at all. Advisory like both rules above — see `object-field-groups.ts`.
1375
+ */
1376
+ declare const FORM_SECTION_GROUP_UNKNOWN = "form-section-group-unknown";
761
1377
  type FormLayoutSeverity = 'error' | 'warning';
762
1378
  interface FormLayoutFinding {
763
1379
  /** Always `warning` today — both rules are advisory (see module note). */
@@ -773,12 +1389,12 @@ interface FormLayoutFinding {
773
1389
  /** How to fix it. */
774
1390
  hint: string;
775
1391
  }
776
- type AnyRec$t = Record<string, unknown>;
1392
+ type AnyRec$v = Record<string, unknown>;
777
1393
  /**
778
1394
  * Validate authored form-view layout. Returns findings (empty = clean).
779
1395
  * Advisory only — the caller must never fail the build on these alone.
780
1396
  */
781
- declare function validateFormLayout(stack: AnyRec$t): FormLayoutFinding[];
1397
+ declare function validateFormLayout(stack: AnyRec$v): FormLayoutFinding[];
782
1398
 
783
1399
  declare const VISIBILITY_ROOT_MISLAYERED = "visibility-root-mislayered";
784
1400
  declare const VISIBILITY_BARE_IDENTIFIER = "visibility-bare-identifier";
@@ -791,6 +1407,15 @@ declare const VISIBILITY_PREDICATE_SYNTAX = "visibility-predicate-syntax";
791
1407
  * consequence is identical, the FIX is not, and `--json` consumers key on the id.
792
1408
  */
793
1409
  declare const VISIBILITY_PREDICATE_OVER_BUDGET = "visibility-predicate-over-budget";
1410
+ /**
1411
+ * A predicate calling a function the CEL environment does not register —
1412
+ * #13594, the scoped supersession of this file's parse-only ruling (see the
1413
+ * module note's §Function existence). A separate id from
1414
+ * {@link VISIBILITY_PREDICATE_SYNTAX} for the same reason `over-budget` is one:
1415
+ * the source parses perfectly, so the dialect prescription cannot succeed on it
1416
+ * and `--json` consumers key on the id.
1417
+ */
1418
+ declare const VISIBILITY_PREDICATE_UNKNOWN_FUNCTION = "visibility-predicate-unknown-function";
794
1419
  type VisibilitySeverity = 'error' | 'warning';
795
1420
  /**
796
1421
  * Which binding environment the linted surface belongs to (ADR-0089 §Context):
@@ -818,9 +1443,9 @@ interface VisibilityOptions {
818
1443
  interface VisibilityFinding {
819
1444
  /**
820
1445
  * `warning` for the ADR-0089 D3b advisory (`visibility-root-mislayered`);
821
- * `error` for the three rules that gate — `visibility-predicate-syntax`,
822
- * `visibility-predicate-over-budget` and `visibility-bare-identifier` (see
823
- * module note).
1446
+ * `error` for the four rules that gate — `visibility-predicate-syntax`,
1447
+ * `visibility-predicate-over-budget`, `visibility-predicate-unknown-function`
1448
+ * and `visibility-bare-identifier` (see module note).
824
1449
  */
825
1450
  severity: VisibilitySeverity;
826
1451
  /** Diagnostic rule id, e.g. `visibility-root-mislayered`. */
@@ -834,7 +1459,7 @@ interface VisibilityFinding {
834
1459
  /** How to fix it. */
835
1460
  hint: string;
836
1461
  }
837
- type AnyRec$s = Record<string, unknown>;
1462
+ type AnyRec$u = Record<string, unknown>;
838
1463
  /**
839
1464
  * Validate conditional-visibility predicates across authored views and pages.
840
1465
  *
@@ -847,8 +1472,10 @@ type AnyRec$s = Record<string, unknown>;
847
1472
  *
848
1473
  * Returns findings (empty = clean). `visibility-root-mislayered` is advisory
849
1474
  * (`warning`); `visibility-predicate-syntax` (#6253),
850
- * `visibility-predicate-over-budget` (#7217) and `visibility-bare-identifier`
851
- * (#6128) are `error` and the caller is expected to fail the build on them.
1475
+ * `visibility-predicate-over-budget` (#7217),
1476
+ * `visibility-predicate-unknown-function` (#13594) and
1477
+ * `visibility-bare-identifier` (#6128) are `error` and the caller is expected to
1478
+ * fail the build on them.
852
1479
  *
853
1480
  * The binding-root check is layer-directional (ADR-0089 D3). A form view that
854
1481
  * declares `data: { provider: 'schema', schemaId }` is judged at `metadata` on
@@ -857,9 +1484,11 @@ type AnyRec$s = Record<string, unknown>;
857
1484
  * predicate is flagged), or leave it at the `'runtime'` default for `*.view.ts` /
858
1485
  * `*.page.ts` surfaces (so a `data.`-rooted predicate is flagged). The syntax and
859
1486
  * bare-identifier checks are layer-agnostic — but the ROOT their hints prescribe
860
- * is not, which is the second half of what #7815 fixes.
1487
+ * is not, which is the second half of what #7815 fixes. The unknown-function
1488
+ * check (#13594) is layer-agnostic in both halves: it prescribes a NAME, and the
1489
+ * environment registers the same names on every layer.
861
1490
  */
862
- declare function validateVisibilityPredicates(stack: AnyRec$s, opts?: VisibilityOptions): VisibilityFinding[];
1491
+ declare function validateVisibilityPredicates(stack: AnyRec$u, opts?: VisibilityOptions): VisibilityFinding[];
863
1492
 
864
1493
  declare const PREDICATE_PATH_UNRESOLVED = "predicate-path-unresolved";
865
1494
  declare const PREDICATE_PATH_UNROOTED = "predicate-path-unrooted";
@@ -902,7 +1531,7 @@ interface PredicatePathOptions {
902
1531
  */
903
1532
  resolveSchema?: (schemaId: string) => unknown;
904
1533
  }
905
- type AnyRec$r = Record<string, unknown>;
1534
+ type AnyRec$t = Record<string, unknown>;
906
1535
  /**
907
1536
  * Refuse a metadata-form predicate that names a path the target schema does not
908
1537
  * declare (#7010).
@@ -931,7 +1560,7 @@ type AnyRec$r = Record<string, unknown>;
931
1560
  *
932
1561
  * Returns findings (empty = clean).
933
1562
  */
934
- declare function validatePredicatePathRefs(stack: AnyRec$r, opts?: PredicatePathOptions): PredicatePathFinding[];
1563
+ declare function validatePredicatePathRefs(stack: AnyRec$t, opts?: PredicatePathOptions): PredicatePathFinding[];
935
1564
 
936
1565
  declare const CAPABILITY_REFERENCE_UNKNOWN = "capability-reference-unknown";
937
1566
  type CapabilityRefSeverity = 'error' | 'warning';
@@ -949,12 +1578,12 @@ interface CapabilityRefFinding {
949
1578
  /** How to fix it. */
950
1579
  hint: string;
951
1580
  }
952
- type AnyRec$q = Record<string, unknown>;
1581
+ type AnyRec$s = Record<string, unknown>;
953
1582
  /**
954
1583
  * Validate every capability reference in a stack. Returns findings (empty =
955
1584
  * clean). Advisory only — callers must not fail the build on these alone.
956
1585
  */
957
- declare function validateCapabilityReferences(stack: AnyRec$q): CapabilityRefFinding[];
1586
+ declare function validateCapabilityReferences(stack: AnyRec$s): CapabilityRefFinding[];
958
1587
 
959
1588
  declare const APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER = "approval-approver-not-membership-tier";
960
1589
  declare const APPROVAL_APPROVER_TYPE_DEPRECATED = "approval-approver-type-deprecated";
@@ -980,12 +1609,12 @@ interface ApprovalApproverFinding {
980
1609
  /** How to fix it. */
981
1610
  hint: string;
982
1611
  }
983
- type AnyRec$p = Record<string, unknown>;
1612
+ type AnyRec$r = Record<string, unknown>;
984
1613
  /**
985
1614
  * Validate the approvers of every Approval node in the stack's flows.
986
1615
  * Returns findings (empty = clean).
987
1616
  */
988
- declare function validateApprovalApprovers(stack: AnyRec$p): ApprovalApproverFinding[];
1617
+ declare function validateApprovalApprovers(stack: AnyRec$r): ApprovalApproverFinding[];
989
1618
 
990
1619
  type SeedReplaySafetySeverity = 'error' | 'warning';
991
1620
  interface SeedReplaySafetyFinding {
@@ -999,7 +1628,7 @@ interface SeedReplaySafetyFinding {
999
1628
  hint: string;
1000
1629
  }
1001
1630
  declare const SEED_INSERT_MODE_DUPLICATES_ON_REPLAY = "seed-insert-mode-duplicates-on-replay";
1002
- type AnyRec$o = Record<string, unknown>;
1631
+ type AnyRec$q = Record<string, unknown>;
1003
1632
  /**
1004
1633
  * Flag every seed dataset declared with `mode: 'insert'` — the one non-idempotent
1005
1634
  * mode, which duplicates its rows on every replay boot (framework#3434). Returns
@@ -1009,7 +1638,7 @@ type AnyRec$o = Record<string, unknown>;
1009
1638
  * Reads `stack.data` (the `SeedSchema[]` fixtures). Safe on any shape — a stack
1010
1639
  * with no `data` array yields no findings.
1011
1640
  */
1012
- declare function validateSeedReplaySafety(stack: AnyRec$o): SeedReplaySafetyFinding[];
1641
+ declare function validateSeedReplaySafety(stack: AnyRec$q): SeedReplaySafetyFinding[];
1013
1642
 
1014
1643
  type SeedStateMachineSeverity = 'warning';
1015
1644
  interface SeedStateMachineFinding {
@@ -1023,7 +1652,7 @@ interface SeedStateMachineFinding {
1023
1652
  hint: string;
1024
1653
  }
1025
1654
  declare const SEED_VALUE_OUTSIDE_STATE_MACHINE = "seed-value-outside-state-machine";
1026
- type AnyRec$n = Record<string, unknown>;
1655
+ type AnyRec$p = Record<string, unknown>;
1027
1656
  /**
1028
1657
  * Flag every seed record whose `state_machine`-governed field carries a value
1029
1658
  * the machine does not declare (framework#3433 follow-up). Returns the findings
@@ -1036,7 +1665,7 @@ type AnyRec$n = Record<string, unknown>;
1036
1665
  * unresolved `cel` Expression envelope, a number) is skipped: it cannot be
1037
1666
  * statically compared to the declared-state set.
1038
1667
  */
1039
- declare function validateSeedStateMachine(stack: AnyRec$n): SeedStateMachineFinding[];
1668
+ declare function validateSeedStateMachine(stack: AnyRec$p): SeedStateMachineFinding[];
1040
1669
 
1041
1670
  declare const SECURITY_OWD_UNSET = "security-owd-unset";
1042
1671
  declare const SECURITY_OWD_ALIAS = "security-owd-alias";
@@ -1051,6 +1680,7 @@ declare const SECURITY_FLS_UNQUALIFIED_KEY = "security-fls-unqualified-key";
1051
1680
  declare const SECURITY_GRANT_EXPIRED_AT_AUTHORING = "security-grant-expired-at-authoring";
1052
1681
  declare const SECURITY_DELEGATION_MISSING_REASON = "security-delegation-missing-reason";
1053
1682
  declare const SECURITY_CBP_NO_RELATION = "security-controlled-by-parent-no-relation";
1683
+ declare const SECURITY_CBP_AMBIGUOUS_RELATION = "security-controlled-by-parent-ambiguous-relation";
1054
1684
  type SecuritySeverity = 'error' | 'warning' | 'info';
1055
1685
  interface SecurityFinding {
1056
1686
  severity: SecuritySeverity;
@@ -1065,7 +1695,7 @@ interface SecurityFinding {
1065
1695
  /** How to fix it. */
1066
1696
  hint: string;
1067
1697
  }
1068
- type AnyRec$m = Record<string, unknown>;
1698
+ type AnyRec$o = Record<string, unknown>;
1069
1699
  /**
1070
1700
  * Validate the security posture of a stack. Returns findings (empty = clean).
1071
1701
  * `error` findings gate the build in `os compile`; `info` is advisory.
@@ -1073,7 +1703,7 @@ type AnyRec$m = Record<string, unknown>;
1073
1703
  * `opts.nowMs` injects the clock for the ADR-0091 authoring-time expiry rule
1074
1704
  * (tests); production callers omit it.
1075
1705
  */
1076
- declare function validateSecurityPosture(stack: AnyRec$m, opts?: {
1706
+ declare function validateSecurityPosture(stack: AnyRec$o, opts?: {
1077
1707
  nowMs?: number;
1078
1708
  }): SecurityFinding[];
1079
1709
  /**
@@ -1104,7 +1734,7 @@ declare function validateSecurityPosture(stack: AnyRec$m, opts?: {
1104
1734
  * carries `positions`/`apps` and both types are gated — at which point it
1105
1735
  * crosses whole, in one edit, as its own entry.
1106
1736
  */
1107
- declare function validateSecurityRoleWord(stack: AnyRec$m): SecurityFinding[];
1737
+ declare function validateSecurityRoleWord(stack: AnyRec$o): SecurityFinding[];
1108
1738
 
1109
1739
  /**
1110
1740
  * [ADR-0105 D6] The two organization-axis red lines, enforced at authoring time.
@@ -1370,13 +2000,13 @@ interface DashboardActionRefFinding {
1370
2000
  /** How to fix it. */
1371
2001
  hint: string;
1372
2002
  }
1373
- type AnyRec$l = Record<string, unknown>;
2003
+ type AnyRec$n = Record<string, unknown>;
1374
2004
  /**
1375
2005
  * Validate every dashboard header action reference in a stack. Returns
1376
2006
  * findings (empty = clean). `script`/`modal` dead targets are errors; `url`
1377
2007
  * unresolved routes are warnings.
1378
2008
  */
1379
- declare function validateDashboardActionRefs(stack: AnyRec$l): DashboardActionRefFinding[];
2009
+ declare function validateDashboardActionRefs(stack: AnyRec$n): DashboardActionRefFinding[];
1380
2010
 
1381
2011
  /**
1382
2012
  * Build-time filter-placeholder diagnostics (issue #3574).
@@ -1585,12 +2215,12 @@ interface ObjectRefFinding {
1585
2215
  /** How to fix it. */
1586
2216
  hint: string;
1587
2217
  }
1588
- type AnyRec$k = Record<string, unknown>;
2218
+ type AnyRec$m = Record<string, unknown>;
1589
2219
  /**
1590
2220
  * Validate every object-name reference on the surfaces listed in the module
1591
2221
  * header. Returns findings (empty = clean).
1592
2222
  */
1593
- declare function validateObjectReferences(stack: AnyRec$k): ObjectRefFinding[];
2223
+ declare function validateObjectReferences(stack: AnyRec$m): ObjectRefFinding[];
1594
2224
 
1595
2225
  type ReferenceIntegritySeverity = 'error' | 'warning';
1596
2226
  /**
@@ -1748,6 +2378,90 @@ type NavTargetRefFinding = ReferenceIntegrityFinding;
1748
2378
  declare const NAV_TARGET_UNRESOLVED = "nav-target-unresolved";
1749
2379
  declare function validateNavTargetRefs(stack: unknown): NavTargetRefFinding[];
1750
2380
 
2381
+ /**
2382
+ * [#13216 — reference resolvability] A `type: 'page'` list view's `pageName`
2383
+ * must name a page the stack actually declares.
2384
+ *
2385
+ * ## What the schema already settles, and what it cannot
2386
+ *
2387
+ * `ListViewSchema`'s `checkListViewPageMount` refuses a `page` view with no
2388
+ * `pageName`, refuses `pageName` on any other view type, and refuses a
2389
+ * `pageName` outside `SnakeCaseIdentifierSchema`'s grammar — so by the time a
2390
+ * body reaches here the key is present, well-formed, and on the one view type
2391
+ * that reads it. What a per-body parse cannot ask is whether the page EXISTS,
2392
+ * because the answer lives in a sibling collection the body never sees. That is
2393
+ * this rule's whole question, and it is the same question
2394
+ * `validate-nav-target-refs` asks about the identical reference one surface
2395
+ * over — `{ type: 'page', pageName }` on an app navigation item.
2396
+ *
2397
+ * ## Severity: warning, for its twin's reason
2398
+ *
2399
+ * `validate-nav-target-refs` explains why the honest ceiling for a page
2400
+ * reference is advisory: unlike objects, pages have no curated
2401
+ * cross-package registry, so "unresolved here" cannot be told apart from
2402
+ * "provided by a package this stack cannot see". Nothing about mounting the
2403
+ * same page on a view changes that, so the verdict matches its twin rather
2404
+ * than inventing a second severity for one reference kind. `defineStack`'s
2405
+ * `validateCrossReferences` still hard-fails first whenever the stack DOES
2406
+ * declare pages — this rule is what speaks when that check has switched itself
2407
+ * off (`pageNames.size > 0`), which is exactly the state a stack is in when
2408
+ * the target was never written.
2409
+ *
2410
+ * ## Why it runs at the runtime publish gate too (`runtimeTypes: view`)
2411
+ *
2412
+ * The mount this rule guards is reachable in exactly the way #13100 measured:
2413
+ * an agent publishes a page through the metadata API, then writes a view that
2414
+ * mounts it. Both writes go through `PUT /api/v1/meta/view` — no CLI is
2415
+ * involved anywhere on that path, so a build-time-only rule would never speak
2416
+ * to the author who needs it. Crossing the member onto `view` snapshots is
2417
+ * therefore the point, not a bonus.
2418
+ *
2419
+ * It is safe to cross ONLY because the per-write snapshot now carries `pages`
2420
+ * (`RuntimeStackContext.pages`, added with this rule). Without that collection
2421
+ * the member would not go quiet — it would report EVERY page mount as dead,
2422
+ * which is the missing-collection false-positive channel
2423
+ * `ReferenceIntegrityRule.runtimeTypes` exists to keep closed, and the reason
2424
+ * `validateNavTargetRefs` is NOT crossed: nothing carries a snapshot's `apps`.
2425
+ *
2426
+ * ## The rungs, and why the list matches the sort rule's
2427
+ *
2428
+ * A `pageName` can be authored on every shape that carries a list view, and the
2429
+ * #9313 lesson is that declaring `runtimeTypes` is necessary and NOT sufficient
2430
+ * — the WALK has to reach the flattened top-level shape the write door actually
2431
+ * carries, or the crossing is a silent no-op that reads as coverage. So the
2432
+ * rungs are `validate-sortable-fields`' rungs, one key over:
2433
+ *
2434
+ * - `objects[].listViews.<key>` — built-in named list views;
2435
+ * - `views[]` itself on a FLATTENED LIST OVERLAY (`viewKind: 'list'`, no
2436
+ * nested `config`) — the shape the runtime gate snapshots as `views: [item]`;
2437
+ * - `views[].config` on a standalone ViewItem RECORD (`viewKind: 'list'` with
2438
+ * a record-shaped `config`) — the shape a Studio-saved view round-trips as;
2439
+ * - `views[].list` — a `defineView` aggregate's default list;
2440
+ * - `views[].listViews.<key>` — its named list views.
2441
+ *
2442
+ * ## Not covered, deliberately
2443
+ *
2444
+ * An interpolated target (`${…}`) resolves at render time and is skipped — the
2445
+ * conservative exemption `validate-nav-target-refs` and
2446
+ * `validate-object-references` both use (ADR-0072 D1). It cannot currently
2447
+ * arise (the schema's snake_case grammar refuses `$` and `{`), and is kept so
2448
+ * this rule does not become the thing that has to change if the grammar ever
2449
+ * widens.
2450
+ *
2451
+ * The view's `type` is NOT re-checked here. A body reaching a CLI command has
2452
+ * been parsed; a body reaching `os lint` may not have been, and in that state
2453
+ * reading `pageName` wherever it is written is the more useful answer — a
2454
+ * `pageName` on a `grid` view is a schema refusal, not this rule's business,
2455
+ * and reporting the dead reference underneath it as well helps rather than
2456
+ * misleads.
2457
+ */
2458
+
2459
+ type ViewPageRefSeverity = 'error' | 'warning';
2460
+ type ViewPageRefFinding = ReferenceIntegrityFinding;
2461
+ /** Emitted when a `type: 'page'` view mounts a page the stack cannot resolve. */
2462
+ declare const VIEW_PAGE_UNRESOLVED = "view-page-unresolved";
2463
+ declare function validateViewPageRefs(stack: unknown): ViewPageRefFinding[];
2464
+
1751
2465
  type NavObjectServabilityFinding = ReferenceIntegrityFinding;
1752
2466
  /** Emitted when a nav entry targets an object whose `enable` block cannot serve a list. */
1753
2467
  declare const NAV_OBJECT_UNSERVABLE = "nav-object-unservable";
@@ -1903,7 +2617,7 @@ interface SearchableFieldFinding {
1903
2617
  * runtime would refuse are flagged (#4830).
1904
2618
  */
1905
2619
  type SearchableFieldRole = 'canonical' | 'narrowing';
1906
- type AnyRec$j = Record<string, unknown>;
2620
+ type AnyRec$l = Record<string, unknown>;
1907
2621
  /**
1908
2622
  * The slice of an object the search checks resolve against: the authored
1909
2623
  * field map (existence + the `type`/`hidden` meta search resolution reads),
@@ -1934,7 +2648,7 @@ interface ObjectSearchTarget {
1934
2648
  * `validate-react-page-props` — the gate that already parses that source —
1935
2649
  * runs the same `checkSearchableFieldList` core on it (#4329).
1936
2650
  */
1937
- declare function validateSearchableFields(stack: AnyRec$j): SearchableFieldFinding[];
2651
+ declare function validateSearchableFields(stack: AnyRec$l): SearchableFieldFinding[];
1938
2652
 
1939
2653
  declare const SORT_FIELD_UNKNOWN = "sort-field-unknown";
1940
2654
  declare const SORT_FIELD_UNSORTABLE = "sort-field-unsortable";
@@ -1960,7 +2674,7 @@ interface SortableFieldFinding {
1960
2674
  /** How to fix it. */
1961
2675
  hint: string;
1962
2676
  }
1963
- type AnyRec$i = Record<string, unknown>;
2677
+ type AnyRec$k = Record<string, unknown>;
1964
2678
  /**
1965
2679
  * Check ONE authored `sort` declaration against the object it is bound to —
1966
2680
  * the shared core behind every list-view surface that declares an ordering.
@@ -1983,7 +2697,39 @@ declare function checkSortDeclaration(declared: unknown, objectName: string | un
1983
2697
  * over the bound object's own columns. See the module note for the four
1984
2698
  * sort-carrying surfaces that were checked and deliberately left out.
1985
2699
  */
1986
- declare function validateSortableFields(stack: AnyRec$i): SortableFieldFinding[];
2700
+ declare function validateSortableFields(stack: AnyRec$k): SortableFieldFinding[];
2701
+
2702
+ /** A list-view field reference that resolves to no field on the bound object. */
2703
+ declare const LIST_VIEW_FIELD_UNKNOWN = "list-view-field-unknown";
2704
+ /**
2705
+ * [#14282] A list-view field reference written as a DOTTED path at a position
2706
+ * whose name reaches a query door — where the door refuses it by name. See the
2707
+ * second-class section on this module for the scope and the measurements.
2708
+ */
2709
+ declare const LIST_VIEW_FIELD_DOTTED = "list-view-field-dotted";
2710
+ type ListViewFieldRefSeverity = 'error' | 'warning';
2711
+ interface ListViewFieldRefFinding {
2712
+ /** See the two-tier note on this module. */
2713
+ severity: ListViewFieldRefSeverity;
2714
+ /** Diagnostic rule id. */
2715
+ rule: string;
2716
+ /** Human-readable location, e.g. `view "task" › list › kanban`. */
2717
+ where: string;
2718
+ /** Config path, e.g. `views[0].list.kanban.groupByField`. */
2719
+ path: string;
2720
+ /** What is wrong. */
2721
+ message: string;
2722
+ /** How to fix it. */
2723
+ hint: string;
2724
+ }
2725
+ type AnyRec$j = Record<string, unknown>;
2726
+ /**
2727
+ * Validate every list view's field references against the object graph.
2728
+ * Returns findings (empty = clean). Pure `(stack) => Finding[]`; no I/O, and
2729
+ * safe on both the schema-parsed stack and the raw config the `lint` path
2730
+ * carries.
2731
+ */
2732
+ declare function validateListViewFieldRefs(stack: AnyRec$j): ListViewFieldRefFinding[];
1987
2733
 
1988
2734
  declare const ACTION_NAME_UNDEFINED = "action-name-undefined";
1989
2735
  type ActionNameRefSeverity = 'error' | 'warning';
@@ -2001,12 +2747,12 @@ interface ActionNameRefFinding {
2001
2747
  /** How to fix it. */
2002
2748
  hint: string;
2003
2749
  }
2004
- type AnyRec$h = Record<string, unknown>;
2750
+ type AnyRec$i = Record<string, unknown>;
2005
2751
  /**
2006
2752
  * Validate every name-bound action reference in a stack. Returns findings
2007
2753
  * (empty = clean).
2008
2754
  */
2009
- declare function validateActionNameRefs(stack: AnyRec$h): ActionNameRefFinding[];
2755
+ declare function validateActionNameRefs(stack: AnyRec$i): ActionNameRefFinding[];
2010
2756
 
2011
2757
  /**
2012
2758
  * [ADR-0078 Phase 3 — Tier-A `action-locations`] An action nobody placed.
@@ -2073,12 +2819,12 @@ interface ActionLocationsFinding {
2073
2819
  /** How to fix it. */
2074
2820
  hint: string;
2075
2821
  }
2076
- type AnyRec$g = Record<string, unknown>;
2822
+ type AnyRec$h = Record<string, unknown>;
2077
2823
  /**
2078
2824
  * Flag every action that declares no placement and that no view places by
2079
2825
  * name. Returns findings (empty = clean).
2080
2826
  */
2081
- declare function validateActionLocations(stack: AnyRec$g): ActionLocationsFinding[];
2827
+ declare function validateActionLocations(stack: AnyRec$h): ActionLocationsFinding[];
2082
2828
 
2083
2829
  /**
2084
2830
  * Shared page-component traversal for the lint rules that inspect
@@ -2104,11 +2850,11 @@ declare function validateActionLocations(stack: AnyRec$g): ActionLocationsFindin
2104
2850
  * author never wrote, so those pages are skipped here and covered by
2105
2851
  * `validate-jsx-pages` / `validate-react-page-props` instead.
2106
2852
  */
2107
- type AnyRec$f = Record<string, unknown>;
2853
+ type AnyRec$g = Record<string, unknown>;
2108
2854
  /** A visited component plus everything needed to locate and bind it. */
2109
2855
  interface WalkedComponent {
2110
2856
  /** The component record itself. */
2111
- component: AnyRec$f;
2857
+ component: AnyRec$g;
2112
2858
  /** Config path, e.g. `pages[0].regions[1].components[2]`. */
2113
2859
  path: string;
2114
2860
  /**
@@ -2119,14 +2865,92 @@ interface WalkedComponent {
2119
2865
  objectName?: string;
2120
2866
  }
2121
2867
  /** Is this page authored as `source` (so its `regions` must not be linted)? */
2122
- declare function isSourceAuthoredPage(page: AnyRec$f): boolean;
2868
+ declare function isSourceAuthoredPage(page: AnyRec$g): boolean;
2123
2869
  /**
2124
2870
  * Walk every component on a page, depth-first, yielding each with its config
2125
2871
  * path and resolved object binding. Source-authored pages yield nothing.
2126
2872
  *
2127
2873
  * `pagePath` is the caller's path prefix for the page (e.g. `pages[3]`).
2874
+ *
2875
+ * The descent is cycle-safe. Every composition slot below is `z.array(z.unknown())`
2876
+ * authored data, so a component that contains itself — directly, or through a
2877
+ * chain of containers — is LEGAL input, and an unguarded walk recurses until the
2878
+ * stack dies. That crash is not scoped to one rule: this is the one shared
2879
+ * traversal under every page-shaped lint rule and the CLI's i18n object-sections
2880
+ * pass, so it takes all of them down in the same process.
2881
+ *
2882
+ * The guard is an ANCESTOR set, not a visited set, and the difference is
2883
+ * load-bearing rather than stylistic — it is the same predicate
2884
+ * `translatePage` (`packages/spec/src/system/i18n-resolver.ts`) settled on. A
2885
+ * component object reused twice as a SIBLING is two legitimate placements at two
2886
+ * distinct config paths, and every rule built on this walk must see both; a
2887
+ * visited set would yield the first and silently drop the second, converting a
2888
+ * crash into missing lint coverage. Only a node that is its own ancestor is a
2889
+ * cycle.
2890
+ *
2891
+ * A cycle stops the descent SILENTLY — the repeated node is not yielded a second
2892
+ * time and no finding is produced. The guard is a safety property of the walk,
2893
+ * not a verdict about the document: deciding that a self-referential page is
2894
+ * itself an authoring error would be new reject behaviour on authored input, and
2895
+ * that is a contract call, not this walk's to make.
2896
+ *
2897
+ * Deliberately NO depth cap, which is a different instrument (`translatePage`
2898
+ * carries both). A cap bounds a legal-but-absurd document; on a resolver it
2899
+ * leaves copy untranslated, but on a LINT walk it would drop real components
2900
+ * from the walk output and every rule would go quiet about them — a silent loss
2901
+ * of coverage that looks exactly like a clean page. With the cycle guard the
2902
+ * descent is bounded by the document's own finite nesting, so the cap would only
2903
+ * ever fire on acyclic input, which is precisely the input it must not truncate.
2904
+ */
2905
+ declare function walkPageComponents(page: AnyRec$g, pagePath: string): WalkedComponent[];
2906
+
2907
+ /**
2908
+ * object name → its DECLARED field-group keys.
2909
+ *
2910
+ * Declared, not derived: `deriveFieldGroupLayout` drops a group no visible field
2911
+ * joins, and a reference to a declared-but-empty group is a different (and
2912
+ * data-dependent) finding from a reference to a group that was never declared.
2913
+ * This index answers only the second question — the one with a closed oracle.
2914
+ *
2915
+ * `fieldGroups` is an ARRAY on `ObjectSchema`, and `asArray` additionally
2916
+ * resolves the name-keyed map shape that raw (non-`defineStack`) metadata can
2917
+ * carry, the same tolerance every other index in this package extends.
2918
+ */
2919
+ declare function indexObjectFieldGroups(stack: unknown): Map<string, Set<string>>;
2920
+ /** One `section.group` reference, with the path that located it. */
2921
+ interface SectionGroupRef {
2922
+ /** The referenced field-group key, as authored. */
2923
+ key: string;
2924
+ /** Config path of the `group` key itself. */
2925
+ path: string;
2926
+ }
2927
+ /**
2928
+ * Pull `group` references out of a `sections`-shaped value.
2929
+ *
2930
+ * `sep` joins the index onto `basePath` the way the calling surface addresses
2931
+ * itself — `.` for metadata config paths, and the react surface's ` › ` if it
2932
+ * ever grows a section-bearing block.
2933
+ */
2934
+ declare function sectionGroupRefs(sections: unknown, basePath: string, sep?: string): SectionGroupRef[];
2935
+ /** The finding shape both host rules already declare, structurally. */
2936
+ interface SectionGroupFinding {
2937
+ severity: 'warning';
2938
+ rule: string;
2939
+ where: string;
2940
+ path: string;
2941
+ message: string;
2942
+ hint: string;
2943
+ }
2944
+ /**
2945
+ * Check one batch of group references against `objectName`'s declared groups.
2946
+ *
2947
+ * Bails out when the object is not defined in this stack — it may come from
2948
+ * another installed package, and a group cannot be judged on a schema we cannot
2949
+ * see. That is the same skip {@link checkFieldRefs} takes, and for the same
2950
+ * reason: silence about an unknowable object beats a finding the author cannot
2951
+ * act on.
2128
2952
  */
2129
- declare function walkPageComponents(page: AnyRec$f, pagePath: string): WalkedComponent[];
2953
+ declare function checkSectionGroupRefs(refs: readonly SectionGroupRef[], objectName: string | undefined, objectFieldGroups: ReadonlyMap<string, Set<string>>, where: string, rule: string): SectionGroupFinding[];
2130
2954
 
2131
2955
  /**
2132
2956
  * [ADR-0078 — completeness] Field-reference integrity for page components
@@ -2193,6 +3017,14 @@ declare const PAGE_FIELD_UNKNOWN = "page-field-unknown";
2193
3017
  * #8116's severity reasoning, unchanged.
2194
3018
  */
2195
3019
  declare const PAGE_FIELD_UNPROVISIONED = "page-field-unprovisioned";
3020
+ /**
3021
+ * [#13855] A section's `group` names a field group the bound object does not
3022
+ * declare. The reference form inherits the section's whole membership from that
3023
+ * group, so a dangling key leaves the section with no members and it does not
3024
+ * render at all. Always `warning` — see `object-field-groups.ts` for why this
3025
+ * sits with the family's advisory findings rather than gating.
3026
+ */
3027
+ declare const PAGE_SECTION_GROUP_UNKNOWN = "page-section-group-unknown";
2196
3028
  type PageFieldSeverity = 'error' | 'warning';
2197
3029
  interface PageFieldFinding {
2198
3030
  /**
@@ -2214,7 +3046,59 @@ interface PageFieldFinding {
2214
3046
  hint: string;
2215
3047
  }
2216
3048
 
2217
- declare function validatePageFieldBindings(stack: AnyRec$f): PageFieldFinding[];
3049
+ declare function validatePageFieldBindings(stack: AnyRec$g): PageFieldFinding[];
3050
+
3051
+ declare const PAGE_VISUALIZATION_WITHOUT_BINDING = "page/visualization-without-binding";
3052
+ type PageVisualizationSeverity = 'error' | 'warning';
3053
+ interface PageVisualizationFinding {
3054
+ /**
3055
+ * `error` when the entry leads `allowedVisualizations` — it becomes the
3056
+ * forced `viewType` and the runtime reaches its renderer's refusal screen.
3057
+ * `warning` otherwise — the type is filtered out of the switcher silently.
3058
+ */
3059
+ severity: PageVisualizationSeverity;
3060
+ /** Diagnostic rule id. */
3061
+ rule: string;
3062
+ /** Human-readable location, e.g. `page "showcase_task_calendar" · interfaceConfig.appearance`. */
3063
+ where: string;
3064
+ /** Config path, e.g. `pages[0].interfaceConfig.appearance.allowedVisualizations[1]`. */
3065
+ path: string;
3066
+ /** What is wrong. */
3067
+ message: string;
3068
+ /** How to fix it. */
3069
+ hint: string;
3070
+ }
3071
+ /**
3072
+ * One row of the renderer's derivation, transcribed.
3073
+ *
3074
+ * `types` is the TYPE predicate (checked first, in the object's declaration
3075
+ * order); `namePattern` is the NAME fallback's regex SOURCE, or `null` where
3076
+ * the renderer has no name fallback for that visualization. `viewBlocks` are
3077
+ * the keys on the referenced view that supply the binding directly — read
3078
+ * ahead of any derivation, exactly as `view.<viz> ?? derived` does.
3079
+ */
3080
+ interface VisualizationPredicate {
3081
+ visualization: string;
3082
+ /** The binding key the renderer would produce (what the message names). */
3083
+ binding: string;
3084
+ types: readonly string[];
3085
+ namePattern: string | null;
3086
+ viewBlocks: readonly string[];
3087
+ }
3088
+ /**
3089
+ * The mirrored table — objectui `f0f774b0`,
3090
+ * `packages/app-shell/src/views/InterfaceListPage.tsx`:
3091
+ * `SELECT_TYPES`/`DATE_TYPES`/`IMAGE_TYPES`/`LOCATION_TYPES` at `:149-151`
3092
+ * and `:203`, `defaultKanbanFromObject` `:153`, `defaultDateField` `:163`,
3093
+ * `defaultCalendarFromObject` `:170`, `defaultGalleryFromObject` `:175`,
3094
+ * `defaultGanttFromObject` `:185`, `defaultMapFromObject` `:253`.
3095
+ *
3096
+ * ⛔ Pinned verbatim by the fixture test. Editing a row here without editing
3097
+ * that pin is the drift this table's whole purpose is to make loud.
3098
+ */
3099
+ declare const OBJECTUI_DERIVATION_PREDICATES: readonly VisualizationPredicate[];
3100
+ type AnyRec$f = Record<string, unknown>;
3101
+ declare function validatePageVisualizationBindings(stack: AnyRec$f): PageVisualizationFinding[];
2218
3102
 
2219
3103
  /** A key authored in `properties` that the type's props schema does not declare. */
2220
3104
  declare const COMPONENT_PROPS_UNKNOWN_KEY = "component-props-unknown-key";
@@ -2240,7 +3124,24 @@ interface ComponentPropsFinding {
2240
3124
  /** How to fix it. */
2241
3125
  hint: string;
2242
3126
  }
2243
- declare function validateComponentProps(stack: AnyRec$f): ComponentPropsFinding[];
3127
+ declare function validateComponentProps(stack: AnyRec$g): ComponentPropsFinding[];
3128
+
3129
+ /** A component `type` inside a spec-reserved namespace that the vocabulary does not declare. */
3130
+ declare const COMPONENT_TYPE_UNKNOWN = "component-type-unknown";
3131
+ interface ComponentTypeFinding {
3132
+ severity: 'error';
3133
+ /** Diagnostic rule id. */
3134
+ rule: string;
3135
+ /** Human-readable location, e.g. `page "app_launcher" · global:serch`. */
3136
+ where: string;
3137
+ /** Config path, e.g. `pages[0].regions[1].components[0].type`. */
3138
+ path: string;
3139
+ /** What is wrong. */
3140
+ message: string;
3141
+ /** How to fix it. */
3142
+ hint: string;
3143
+ }
3144
+ declare function validateComponentTypes(stack: AnyRec$g): ComponentTypeFinding[];
2244
3145
 
2245
3146
  /**
2246
3147
  * [ADR-0021 — semantic layer] Chart-binding integrity for the surfaces the
@@ -2297,7 +3198,265 @@ interface ChartBindingFinding {
2297
3198
  hint: string;
2298
3199
  }
2299
3200
 
2300
- declare function validateChartBindings(stack: AnyRec$f): ChartBindingFinding[];
3201
+ declare function validateChartBindings(stack: AnyRec$g): ChartBindingFinding[];
3202
+
3203
+ /** An `include[]` entry that does not name a joinable relationship path. */
3204
+ declare const DATASET_INCLUDE_UNKNOWN = "dataset-include-unknown";
3205
+ /** A `dimensions[].field` / `measures[].field` path that resolves to no column. */
3206
+ declare const DATASET_FIELD_UNKNOWN = "dataset-field-unknown";
3207
+ /** A field path whose relationship prefix was never declared in `include`. */
3208
+ declare const DATASET_FIELD_NOT_INCLUDED = "dataset-field-not-included";
3209
+ /** A filter KEY — on the dataset or a measure — that resolves to no column. */
3210
+ declare const DATASET_FILTER_FIELD_UNKNOWN = "dataset-filter-field-unknown";
3211
+ type DatasetRefSeverity = 'error' | 'warning';
3212
+ interface DatasetRefFinding {
3213
+ /** Always `error` today — see the severity note on this module. */
3214
+ severity: DatasetRefSeverity;
3215
+ /** Diagnostic rule id. */
3216
+ rule: string;
3217
+ /** Human-readable location, e.g. `dataset "sales" › dimension "region"`. */
3218
+ where: string;
3219
+ /** Config path, e.g. `datasets[0].dimensions[2].field`. */
3220
+ path: string;
3221
+ /** What is wrong. */
3222
+ message: string;
3223
+ /** How to fix it. */
3224
+ hint: string;
3225
+ }
3226
+ type AnyRec$e = Record<string, unknown>;
3227
+ /**
3228
+ * Validate every ADR-0021 dataset's references against the object graph.
3229
+ * Returns findings (empty = clean). Pure `(stack) => Finding[]`; no I/O, and
3230
+ * safe on both the schema-parsed stack and the raw config the `lint` path
3231
+ * carries.
3232
+ */
3233
+ declare function validateDatasetReferences(stack: AnyRec$e): DatasetRefFinding[];
3234
+
3235
+ /**
3236
+ * Field types that address ANOTHER object, so a `.<hop>` through one is a join
3237
+ * the query compiler derives rather than a column read.
3238
+ *
3239
+ * `user` and `tree` are members alongside the two obvious ones for the reason
3240
+ * `validate-flow-template-paths.ts` lists them: both store a foreign key and
3241
+ * both are traversed the same way. A dataset `include` naming one is joinable
3242
+ * exactly as a `lookup` is.
3243
+ */
3244
+ declare const RELATIONSHIP_FIELD_TYPES: ReadonlySet<string>;
3245
+ /** The slice of one field this module reads. */
3246
+ interface GraphField {
3247
+ /** Declared `type`, when the author wrote one. */
3248
+ type?: string;
3249
+ /**
3250
+ * The object a relationship field addresses.
3251
+ *
3252
+ * `reference` is the ONLY spelling `FieldSchema` declares — `referenceTo` /
3253
+ * `relatedTo` / `target` / `targetObject` / `lookupObject` are REJECTED
3254
+ * aliases the strict error map maps back to it (#5017), so a field spelling
3255
+ * one of them does not parse at all. Re-admitting an alias here would be the
3256
+ * tolerant consumer Prime Directive #12 refuses, so only `reference` is read.
3257
+ */
3258
+ reference?: string;
3259
+ /**
3260
+ * The declared `multiple: true` flag, when the author wrote one.
3261
+ *
3262
+ * Read here because the dotted-path verdict a caller may reach for
3263
+ * ({@link classifyDottedFilterHead} in `@objectstack/spec/data`) is a
3264
+ * function of BOTH `type` and `multiple`: an array-valued head is
3265
+ * deliberately unjudged there, since a numeric-index dotted path genuinely
3266
+ * reaches into it on two of three backends. A caller handed only `type`
3267
+ * would have to re-derive the flag from the raw stack, which is the second
3268
+ * copy this module exists to prevent. Additive (#14282): every existing
3269
+ * consumer that ignores the key keeps its verdicts byte-for-byte.
3270
+ */
3271
+ multiple?: boolean;
3272
+ }
3273
+ /**
3274
+ * One object's resolvable surface. `null` in the index marks an object that
3275
+ * declares no readable field map, keeping "declared nothing" distinguishable
3276
+ * from "not in this stack" — the same distinction
3277
+ * {@link indexObjectSearchTargets} draws for the search axis.
3278
+ */
3279
+ interface GraphObject {
3280
+ /** Authored field names. */
3281
+ names: ReadonlySet<string>;
3282
+ /** name → the slice above. */
3283
+ fields: ReadonlyMap<string, GraphField>;
3284
+ /** Registry-injected columns addressable on THIS object (skip 3). */
3285
+ injected: ReadonlySet<string>;
3286
+ }
3287
+ /** object name → its resolvable surface, or `null` (skip 2). */
3288
+ type ObjectGraph = ReadonlyMap<string, GraphObject | null>;
3289
+ /**
3290
+ * Index a stack's objects once. Callers that resolve many paths build this a
3291
+ * single time and hand it to every {@link resolveFieldPath} call.
3292
+ */
3293
+ declare function indexObjectGraph(stack: unknown): ObjectGraph;
3294
+ /** What a path resolved to, or precisely how it failed. */
3295
+ type FieldPathVerdict =
3296
+ /**
3297
+ * Every hop and the leaf resolved. `object` is the object the LEAF lives on.
3298
+ * `injected` marks a leaf resolved through skip 3 — a registry-injected
3299
+ * column, real at runtime, whose TYPE and relationship target are
3300
+ * registry-owned and invisible here. A caller asking a second question about
3301
+ * the leaf (is it a relationship? is it materialised?) must treat an
3302
+ * `injected` leaf as unanswerable rather than assume the absence of a
3303
+ * declared type means the absence of the property.
3304
+ */
3305
+ {
3306
+ kind: 'ok';
3307
+ object: string;
3308
+ field: string;
3309
+ meta?: GraphField;
3310
+ injected?: true;
3311
+ }
3312
+ /**
3313
+ * The graph cannot answer — one of the three skips. Never report this: it is
3314
+ * the absence of a judgement, not a passing one.
3315
+ */
3316
+ | {
3317
+ kind: 'unknowable';
3318
+ reason: 'object-not-in-stack' | 'no-field-map' | 'injected-hop';
3319
+ object: string;
3320
+ }
3321
+ /**
3322
+ * A hop names nothing on the object it was written against. `at` is the
3323
+ * 0-based segment index, `object` the object the hop was resolved against.
3324
+ */
3325
+ | {
3326
+ kind: 'hop-unknown';
3327
+ at: number;
3328
+ segment: string;
3329
+ object: string;
3330
+ candidates: ReadonlySet<string>;
3331
+ }
3332
+ /**
3333
+ * A hop resolves to a real field that is NOT a relationship, so there is
3334
+ * nothing to traverse through — the author wrote `amount.total` where
3335
+ * `amount` is a number.
3336
+ */
3337
+ | {
3338
+ kind: 'hop-not-relationship';
3339
+ at: number;
3340
+ segment: string;
3341
+ object: string;
3342
+ type?: string;
3343
+ }
3344
+ /**
3345
+ * A relationship hop resolves but declares no `reference`, so the target
3346
+ * object is unknown and the rest of the path cannot be judged. Treated as a
3347
+ * SKIP by callers for the same reason as `unknowable`.
3348
+ */
3349
+ | {
3350
+ kind: 'hop-untargeted';
3351
+ at: number;
3352
+ segment: string;
3353
+ object: string;
3354
+ }
3355
+ /** Every hop resolved; the LEAF names nothing on the object it landed on. */
3356
+ | {
3357
+ kind: 'field-unknown';
3358
+ object: string;
3359
+ field: string;
3360
+ candidates: ReadonlySet<string>;
3361
+ };
3362
+ /**
3363
+ * Resolve `path` against `objectName` in `graph`.
3364
+ *
3365
+ * A bare `field` is the one-segment case and resolves against the root object
3366
+ * directly. A dotted `a.b.field` walks `a`, then `b`, as to-one relationship
3367
+ * hops, and resolves the leaf on whatever object the last hop landed on.
3368
+ *
3369
+ * ⛔ This function answers EXISTENCE only. Whether a hop was DECLARED joinable
3370
+ * — an ADR-0021 dataset joins only what `Dataset.include` names — is a second
3371
+ * question the caller owns, because only the caller knows which declaration
3372
+ * governs the position. Conflating them here would make the seam unusable at
3373
+ * the positions that have no `include` (a list view's field path, #14107).
3374
+ */
3375
+ declare function resolveFieldPath(graph: ObjectGraph, objectName: string | undefined, path: string): FieldPathVerdict | undefined;
3376
+ /**
3377
+ * The relationship prefixes a document declared as joinable.
3378
+ *
3379
+ * ADR-0021: *"Declaring `a.b` implicitly includes the intermediate `a`."* So
3380
+ * every PREFIX of every declared path is joinable, not only the paths as
3381
+ * written — which is why this expands rather than reading `include` verbatim.
3382
+ *
3383
+ * Here rather than in a rule because the SAME `include` governs positions two
3384
+ * different rules judge: a dataset's own `dimensions[].field` / `measures[].field`
3385
+ * / filter keys (#14105), and a dashboard widget's `filter` keys (#14148), whose
3386
+ * condition is ANDed into that same dataset's compiled query as `runtimeFilter`.
3387
+ * Two copies of the prefix expansion would let the two positions drift apart on
3388
+ * a clause that is one sentence of one ADR.
3389
+ */
3390
+ declare function joinablePrefixes(include: unknown): ReadonlySet<string>;
3391
+ /** The two halves of a rendered verdict: the finding's message, and its detail. */
3392
+ interface FieldPathAccount {
3393
+ /** What is wrong, in prose, carrying the "did you mean" when there is one. */
3394
+ message: string;
3395
+ /** The supporting field list, for the finding's hint. */
3396
+ detail: string;
3397
+ }
3398
+ /**
3399
+ * Turn a resolution verdict into the message half of an existence finding, or
3400
+ * `undefined` when the verdict is one no rule may report.
3401
+ *
3402
+ * Shared by every position that resolves a field PATH — a dataset dimension, a
3403
+ * measure, a dataset filter key (#14105), a widget filter key (#14148) — so
3404
+ * they cannot drift into N different accounts of the same miss. The caller
3405
+ * supplies `subject` (how the position is named in prose) and owns the rule id,
3406
+ * the severity, the path and the hint's prescription; this function holds none
3407
+ * of them, matching the rest of this module.
3408
+ */
3409
+ declare function describeFieldPathVerdict(verdict: FieldPathVerdict, path: string, subject: string): FieldPathAccount | undefined;
3410
+ /**
3411
+ * True when the verdict is one no rule may report — the graph could not answer.
3412
+ * Callers spell the skip through this predicate rather than re-listing the
3413
+ * kinds, so a future verdict added to the union defaults to being reported
3414
+ * loudly (a missed skip is a visible false positive; a missed report is
3415
+ * silence, which is the failure mode this whole family exists to end).
3416
+ */
3417
+ declare function isUnjudgeable(verdict: FieldPathVerdict | undefined): boolean;
3418
+ /**
3419
+ * Nearest declared name for a typo'd reference, or `undefined` when nothing is
3420
+ * close enough. The sole shared helper behind every "Did you mean?" hint in
3421
+ * this package (issue #14268 consolidated the four hand-copied instances that
3422
+ * had drifted apart into this one).
3423
+ *
3424
+ * Containment is checked first, ahead of edit distance: the ADR-0021 cutover's
3425
+ * canonical drift is base column → prefixed measure name (`amount` →
3426
+ * `sum_amount`), which is far in edit distance but obvious to a human. A
3427
+ * candidate that contains the target (or vice versa, when both are at least 3
3428
+ * characters) scores on the length delta instead and always wins over an
3429
+ * edit-distance match; otherwise the target must be within budget —
3430
+ * `max(2, floor(len/3))` — of a candidate to be offered at all.
3431
+ */
3432
+ declare function nearestName(target: string, known: Iterable<string>): string | undefined;
3433
+ /** ` Did you mean "x"?`, or the empty string — the platform's message shape. */
3434
+ declare function suggestName(target: string, known: Iterable<string>): string;
3435
+ /** A readable field list for a hint, or `(none)`. */
3436
+ declare function listNames(names: Iterable<string>): string;
3437
+
3438
+ /** One field position inside an authored filter. */
3439
+ interface FilterFieldKey {
3440
+ /**
3441
+ * The field the condition filters BY, exactly as authored — a bare name
3442
+ * (`status`), or a dotted relationship path (`account.region`, whether
3443
+ * spelled that way or reached by descending a nested condition object).
3444
+ */
3445
+ field: string;
3446
+ /** Config path of the position, e.g. `datasets[1].measures[1].filter.last_update_at`. */
3447
+ path: string;
3448
+ }
3449
+ /**
3450
+ * Emit every FIELD KEY inside one authored filter subtree, whatever shape it
3451
+ * was authored in — the key half of what `validate-preset-comparands.ts` does
3452
+ * for values, and the traversal `filter-token-unknown` already performs while
3453
+ * reasoning only about the strings it finds.
3454
+ *
3455
+ * Holds no judgement: it does not know which object the filter is bound to and
3456
+ * emits no findings. Resolution is {@link resolveFieldPath}'s job
3457
+ * (`object-graph.ts`) and the verdict is the caller's.
3458
+ */
3459
+ declare function walkFilterFieldKeys(node: unknown, path: string, visit: (key: FilterFieldKey) => void, depth?: number): void;
2301
3460
 
2302
3461
  type RuleCompilabilitySeverity = 'error';
2303
3462
  interface RuleCompilabilityFinding {
@@ -2322,11 +3481,11 @@ declare const VALIDATION_RULE_SCHEMA_UNCOMPILABLE = "validation-rule-json-schema
2322
3481
  * still says the same thing.
2323
3482
  */
2324
3483
  declare const RUNTIME_AJV_OPTIONS: Options;
2325
- type AnyRec$e = Record<string, unknown>;
3484
+ type AnyRec$d = Record<string, unknown>;
2326
3485
  /** One authored validation rule, located for a finding. */
2327
3486
  interface WalkedValidationRule {
2328
3487
  /** The rule record itself — a top-level entry, or a `conditional` branch. */
2329
- rule: AnyRec$e;
3488
+ rule: AnyRec$d;
2330
3489
  /** The declaring object's `name`, or `(unnamed object)`. */
2331
3490
  objectName: string;
2332
3491
  /** The nesting-aware rule name as prose: `'outer' → 'inner'`. */
@@ -2410,12 +3569,12 @@ interface NavAccessFinding {
2410
3569
  /** How to fix it. */
2411
3570
  hint: string;
2412
3571
  }
2413
- type AnyRec$d = Record<string, unknown>;
3572
+ type AnyRec$c = Record<string, unknown>;
2414
3573
  /**
2415
3574
  * Validate that every object a stack's navigation exposes is readable by at
2416
3575
  * least one permission set the stack declares. Returns findings (empty = clean).
2417
3576
  */
2418
- declare function validateNavAccess(stack: AnyRec$d): NavAccessFinding[];
3577
+ declare function validateNavAccess(stack: AnyRec$c): NavAccessFinding[];
2419
3578
 
2420
3579
  declare const TRANSLATION_TARGET_UNKNOWN = "translation-target-unknown";
2421
3580
  declare const TRANSLATION_OPTION_KEY_UNKNOWN = "translation-option-key-unknown";
@@ -2434,12 +3593,12 @@ interface TranslationRefFinding {
2434
3593
  /** How to fix it. */
2435
3594
  hint: string;
2436
3595
  }
2437
- type AnyRec$c = Record<string, unknown>;
3596
+ type AnyRec$b = Record<string, unknown>;
2438
3597
  /**
2439
3598
  * Validate every reference a translation bundle makes against the metadata it
2440
3599
  * claims to translate. Returns findings (empty = clean).
2441
3600
  */
2442
- declare function validateTranslationReferences(stack: AnyRec$c): TranslationRefFinding[];
3601
+ declare function validateTranslationReferences(stack: AnyRec$b): TranslationRefFinding[];
2443
3602
 
2444
3603
  declare const TRANSLATION_SECTION_NAME_MISSING = "translation-section-name-missing";
2445
3604
  type TranslatableSectionSeverity = 'warning';
@@ -2457,12 +3616,12 @@ interface TranslatableSectionFinding {
2457
3616
  /** How to fix it. */
2458
3617
  hint: string;
2459
3618
  }
2460
- type AnyRec$b = Record<string, unknown>;
3619
+ type AnyRec$a = Record<string, unknown>;
2461
3620
  /**
2462
3621
  * Report every form/detail section that declares a heading but no `name`, on an
2463
3622
  * object the stack actually translates. Returns findings (empty = clean).
2464
3623
  */
2465
- declare function validateTranslatableSections(stack: AnyRec$b): TranslatableSectionFinding[];
3624
+ declare function validateTranslatableSections(stack: AnyRec$a): TranslatableSectionFinding[];
2466
3625
 
2467
3626
  /**
2468
3627
  * [ADR-0064 §3] Skill ↔ agent surface affinity (issue #3820).
@@ -2507,12 +3666,12 @@ interface AiSurfaceAffinityFinding {
2507
3666
  /** How to fix it. */
2508
3667
  hint: string;
2509
3668
  }
2510
- type AnyRec$a = Record<string, unknown>;
3669
+ type AnyRec$9 = Record<string, unknown>;
2511
3670
  /**
2512
3671
  * Validate every in-stack agent→skill binding against the ADR-0064 §3 surface
2513
3672
  * affinity contract. Returns findings (empty = clean).
2514
3673
  */
2515
- declare function validateAiSurfaceAffinity(stack: AnyRec$a): AiSurfaceAffinityFinding[];
3674
+ declare function validateAiSurfaceAffinity(stack: AnyRec$9): AiSurfaceAffinityFinding[];
2516
3675
 
2517
3676
  declare const AI_SKILL_TOOL_UNRESOLVED = "ai-skill-tool-unresolved";
2518
3677
  type AiToolRefSeverity = 'error' | 'warning';
@@ -2530,12 +3689,12 @@ interface AiToolRefFinding {
2530
3689
  /** How to fix it. */
2531
3690
  hint: string;
2532
3691
  }
2533
- type AnyRec$9 = Record<string, unknown>;
3692
+ type AnyRec$8 = Record<string, unknown>;
2534
3693
  /**
2535
3694
  * Validate every `skill.tools[]` reference in a stack. Returns findings
2536
3695
  * (empty = clean).
2537
3696
  */
2538
- declare function validateAiToolReferences(stack: AnyRec$9): AiToolRefFinding[];
3697
+ declare function validateAiToolReferences(stack: AnyRec$8): AiToolRefFinding[];
2539
3698
 
2540
3699
  /**
2541
3700
  * [ADR-0063 §2] `stack.agents` is a platform-internal slot (issue #3820).
@@ -2579,10 +3738,41 @@ declare function validateAiToolReferences(stack: AnyRec$9): AiToolRefFinding[];
2579
3738
  * at warning tier, reusing `PLATFORM_AGENT_NAMES` rather than narrowing the
2580
3739
  * schema to an enum (a breaking authoring change ADR-0063 already walked
2581
3740
  * back once).
3741
+ *
3742
+ * ## Why the value limb no longer reads the same roster (issue #14461)
3743
+ *
3744
+ * `PLATFORM_AGENT_NAMES` holds FOUR names, and reusing it for the value limb
3745
+ * meant this gate accepted `defaultAgent: 'metadata_assistant'` — the exact
3746
+ * spelling `skills/objectstack-ai` tells authors is "not vocabulary". The
3747
+ * platform then taught it from its own only live example: `studio.app.ts`
3748
+ * pinned the alias. So an AI author copying the one working example in the
3749
+ * repo wrote the forbidden spelling and this rule waved it through — the
3750
+ * silent-tolerance shape ADR-0078 exists to close, committed by the gate
3751
+ * itself.
3752
+ *
3753
+ * The maintainer ruling on #14461 (2026-09-03) re-pins Studio to `build` and
3754
+ * SPLITS the two limbs' rosters, keeping both of #6041's operative decisions
3755
+ * intact (warning tier, no Zod enum):
3756
+ *
3757
+ * - the DECLARATION limb still reads all four names. Its question is "does
3758
+ * this record shadow a platform record?", and declaring `metadata_assistant`
3759
+ * shadows `build` through the alias exactly as declaring `build` does. That
3760
+ * judgement is unchanged.
3761
+ * - the VALUE limb reads `CANONICAL_AGENT_NAMES` only, and a legacy alias
3762
+ * gets its own rule id and wording ({@link DEFAULT_AGENT_LEGACY_ALIAS}).
3763
+ * Its question is "is this the right thing to WRITE?", and the answer for
3764
+ * an alias is no even though it resolves.
3765
+ *
3766
+ * The alias limb stays `warning`, not `error`, and for a sharper reason than
3767
+ * the roster limb: an aliased pin is not broken. It resolves, the app gets the
3768
+ * agent it meant, and nothing a user can see is wrong — which is precisely why
3769
+ * the signal has to be an authoring-time nudge rather than a build break.
2582
3770
  */
2583
3771
  declare const AGENT_AUTHORING_WITHDRAWN = "agent-authoring-withdrawn";
2584
3772
  /** `app.defaultAgent` names something outside the platform agent roster. */
2585
3773
  declare const DEFAULT_AGENT_OUTSIDE_ROSTER = "default-agent-outside-roster";
3774
+ /** `app.defaultAgent` spells a platform agent by its RETIRED alias (#14461). */
3775
+ declare const DEFAULT_AGENT_LEGACY_ALIAS = "default-agent-legacy-alias";
2586
3776
  type AiAgentAuthoringSeverity = 'error' | 'warning';
2587
3777
  interface AiAgentAuthoringFinding {
2588
3778
  /** Always `warning` — the runtime is the gate; this is the authoring-time signal. */
@@ -2598,158 +3788,12 @@ interface AiAgentAuthoringFinding {
2598
3788
  /** How to fix it. */
2599
3789
  hint: string;
2600
3790
  }
2601
- type AnyRec$8 = Record<string, unknown>;
3791
+ type AnyRec$7 = Record<string, unknown>;
2602
3792
  /**
2603
3793
  * Flag every agent declared in a stack. Returns findings (empty = clean,
2604
3794
  * which is what every app package should be).
2605
3795
  */
2606
- declare function validateAiAgentAuthoring(stack: AnyRec$8): AiAgentAuthoringFinding[];
2607
-
2608
- type HookBodyWriteSeverity = 'warning';
2609
- interface HookBodyWriteFinding {
2610
- /** v1 is advisory-only by contract — the type says so. */
2611
- severity: HookBodyWriteSeverity;
2612
- rule: string;
2613
- /** Human-readable location, e.g. `hook "normalize_lead" › body`. */
2614
- where: string;
2615
- /** Config path, e.g. `hooks[0].body.source`. */
2616
- path: string;
2617
- message: string;
2618
- hint: string;
2619
- }
2620
- declare const HOOK_BODY_WRITE_UNKNOWN_FIELD = "hook-body-write-unknown-field";
2621
- /**
2622
- * [#10653] The body did not parse, so its write set is whatever error recovery
2623
- * left readable.
2624
- *
2625
- * Reported rather than skipped for the reason this rule exists at all: a
2626
- * mistake must be visible where it is MADE. An unparseable body reached the
2627
- * extractor, produced fewer matches, and came back as a hook with nothing to
2628
- * report — the same silence the undeclared write itself has at run time, this
2629
- * time wearing the checker's badge. `warning` because the whole rule is
2630
- * advisory and never gates (the severity type admits nothing else).
2631
- */
2632
- declare const HOOK_BODY_SOURCE_UNPARSEABLE = "hook-body-source-unparseable";
2633
- /**
2634
- * [#8663] The write-axis twin of `flow-template-field-unprovisioned` (#8340):
2635
- * the body writes a field {@link IMPLICIT_FIELDS} exempts, but on THIS target
2636
- * the platform registered that anchor without provisioning storage for it.
2637
- *
2638
- * A separate id at `warning` severity rather than a reclassification of
2639
- * {@link HOOK_BODY_WRITE_UNKNOWN_FIELD}, matching #8340's precedent exactly:
2640
- * the existence verdict is unchanged (the name IS addressable), and what is
2641
- * added is a second, independently suppressible finding on the path where the
2642
- * existence check stays silent.
2643
- */
2644
- declare const HOOK_BODY_WRITE_UNPROVISIONED_ANCHOR = "hook-body-write-unprovisioned-anchor";
2645
- /** One syntactic write shape the extractor recognizes. */
2646
- interface HookBodyWritePattern {
2647
- /** Stable pattern id, carried on every extracted write. */
2648
- readonly id: string;
2649
- /** Author-facing syntax summary (for docs/diagnostics, not matching). */
2650
- readonly syntax: string;
2651
- /** Reconciliation fixture: extracting `source` must yield exactly `writes`. */
2652
- readonly example: {
2653
- readonly source: string;
2654
- readonly writes: ReadonlyArray<{
2655
- field: string;
2656
- object?: string;
2657
- }>;
2658
- };
2659
- }
2660
- declare const HOOK_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[];
2661
- /** A ledger pattern a given rule does NOT consume, and why. */
2662
- interface BodyWritePatternExclusion {
2663
- /** The {@link HOOK_BODY_WRITE_PATTERNS} entry id being excluded. */
2664
- readonly id: string;
2665
- /** Why the shape does not mean the same thing on this rule's surface. */
2666
- readonly reason: string;
2667
- }
2668
- /**
2669
- * The ledger shapes THIS rule consumes.
2670
- *
2671
- * Declared rather than implied: before the ledger carried a shape the hook
2672
- * surface does not have, every write with no `object` was necessarily a
2673
- * `ctx.input` write, and the rule could branch on that alone. It no longer can
2674
- * — a `record-property-assign` write also carries no object, and would have
2675
- * been reported as "the hook writes 'stage' to its input", which is false.
2676
- * Each consumer declaring its own subset is what stops the next added shape
2677
- * from silently landing in a branch that was never written for it.
2678
- */
2679
- declare const HOOK_BODY_WRITE_PATTERN_IDS: readonly string[];
2680
- /** Ledger shapes this rule leaves alone, each with its reason. */
2681
- declare const HOOK_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[];
2682
- type AnyRec$7 = Record<string, unknown>;
2683
- /** One statically-extracted field write found in an L2 body. */
2684
- interface ExtractedHookBodyWrite {
2685
- /** Which {@link HOOK_BODY_WRITE_PATTERNS} entry matched. */
2686
- patternId: string;
2687
- /** Target object name; `undefined` = the hook's own target object(s). */
2688
- object?: string;
2689
- /** The `ctx.api` method for diagnostics (`insert`/`create`/`update`/`updateById`). */
2690
- method?: string;
2691
- field: string;
2692
- }
2693
- /** Everything one parse of an L2 body yields. */
2694
- interface ExtractedHookBodyWriteSet {
2695
- /** Every literal write the {@link HOOK_BODY_WRITE_PATTERNS} ledger declares. */
2696
- writes: ExtractedHookBodyWrite[];
2697
- /**
2698
- * `ctx.record` is handed to something as a VALUE somewhere in the body — an
2699
- * argument, an assignment RHS, a spread, a return — rather than only having
2700
- * its properties read and written, or being truthiness/type tested.
2701
- *
2702
- * The action rule needs this to tell a dead snapshot write from a live one:
2703
- * `ctx.record.stage = 'won'; await ctx.api.object('d').update(ctx.record)`
2704
- * builds a payload and persists it, so the assignment is not a no-op. When
2705
- * this is true, no record write in the body can be judged, and none is
2706
- * reported. (One-level aliasing — `const r = ctx.record` — reads as an
2707
- * escape too, which is the safe direction: it suppresses findings.)
2708
- */
2709
- ctxRecordEscapes: boolean;
2710
- /**
2711
- * [#10653] Set when the body did not parse, so `writes` is whatever error
2712
- * recovery left readable rather than the body's actual write set.
2713
- *
2714
- * Absent means one of two things, and they are not the same: the body parsed,
2715
- * or the cheap pre-filter above rejected it before any parse. The filter is a
2716
- * raw-text scan for `ctx` / `Object`, and a body containing neither cannot
2717
- * match any pattern however it parses — so a skipped parse claims nothing and
2718
- * hides nothing.
2719
- *
2720
- * ## Whose fault an unparseable body is — asked, not assumed
2721
- *
2722
- * The body is parsed inside a synthesised wrapper (`async function __body(ctx)
2723
- * { … }`) because that is the shape the runtime compiles it into
2724
- * (`new AsyncFunction('ctx', source)`). So a parse failure here could in
2725
- * principle be the WRAPPER's fault rather than the author's, and blaming the
2726
- * author for the checker's own bug is the failure this whole change is about.
2727
- * It cannot be: the wrapper is a constant, and `validate-hook-body-writes.
2728
- * test.ts` pins that it parses clean around an empty body and around every
2729
- * example in the pattern ledger. Any diagnostic therefore comes from the
2730
- * body — and its position is reported in the BODY's own coordinates (the
2731
- * wrapper's line is subtracted, and the result is clamped so it can never
2732
- * point at a line the author did not write).
2733
- */
2734
- parseFailure?: SourceParseFailure;
2735
- }
2736
- /**
2737
- * Extract every literal field write the pattern ledger declares from an L2
2738
- * body's source. Parse-only (the source is never executed), error-tolerant
2739
- * (a body with syntax errors simply yields fewer matches), and lazy: the
2740
- * TypeScript compiler is not loaded when no pattern can possibly match.
2741
- *
2742
- * Thin projection of {@link extractHookBodyWriteSet} — use that one when the
2743
- * `ctx.record` liveness signal matters, so the body is parsed once, not twice.
2744
- */
2745
- declare function extractHookBodyWrites(source: string): ExtractedHookBodyWrite[];
2746
- /** {@link extractHookBodyWrites} plus the `ctx.record` liveness signal, one parse. */
2747
- declare function extractHookBodyWriteSet(source: string): ExtractedHookBodyWriteSet;
2748
- /**
2749
- * Validate L2 hook-body writes against target-object field declarations.
2750
- * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.
2751
- */
2752
- declare function validateHookBodyWrites(stack: AnyRec$7): HookBodyWriteFinding[];
3796
+ declare function validateAiAgentAuthoring(stack: AnyRec$7): AiAgentAuthoringFinding[];
2753
3797
 
2754
3798
  type ActionBodyWriteSeverity = 'warning';
2755
3799
  interface ActionBodyWriteFinding {
@@ -2966,6 +4010,40 @@ declare const FLOW_INERT_NODE_CONDITION = "flow-inert-node-condition";
2966
4010
  * how it divides labour with the #3810 run-time guard.
2967
4011
  */
2968
4012
  declare const FLOW_MULTI_WRITE_UNFILTERED = "flow-multi-write-unfiltered";
4013
+ /**
4014
+ * #13681 / #14394 — a `loop` body that runs a node which can fail, with no
4015
+ * `try_catch` between the loop and that node. The first failing item kills the
4016
+ * whole sweep: `loop-node.ts:123-135` iterates with a bare `await` and no
4017
+ * `try`/`catch` anywhere in the file, so the body's failure propagates out of
4018
+ * the container and the remaining items are never processed.
4019
+ *
4020
+ * A **warning**, per the severity policy at the top of this file: a loop whose
4021
+ * body is deliberately allowed to die (fail fast on the first bad row) is a
4022
+ * legitimate reading, and the rule cannot prove the author did not mean it. What
4023
+ * it can do is make the choice visible at authoring time — today it is silent,
4024
+ * and the measured consequence is a 5-row sweep that processes 3 rows and
4025
+ * reports a run that "completed" nothing unusual.
4026
+ *
4027
+ * See {@link scanUncontainedLoopBodies} for the containment judgement, and
4028
+ * {@link FALLIBLE_NODE_TYPES} for what counts as fallible and why.
4029
+ */
4030
+ declare const FLOW_LOOP_BODY_UNCONTAINED = "flow-loop-body-uncontained";
4031
+ /**
4032
+ * #13681 / #14394 — the near-miss shape: a `try_catch` that declares no `catch`
4033
+ * region. `catch` is optional in the schema (`control-flow.zod.ts:315`) and
4034
+ * omitting it makes the container **fail** (`try-catch-node.ts:190`), so the
4035
+ * wrapped region dies exactly like an unwrapped one — measured side by side, the
4036
+ * no-`catch` run and the no-wrapper control produce identical output.
4037
+ *
4038
+ * This is the family's first target for the containment pair, not an extra: an
4039
+ * author who reaches for `try_catch` has recognised the hazard and stopped one
4040
+ * key short, and before this rule got **zero containment and zero diagnostics**.
4041
+ * A warning rather than an error under the same policy — a `retry`-only
4042
+ * `try_catch` (retry the region, then fail loudly) is a legitimate reading.
4043
+ *
4044
+ * See {@link scanTryCatchWithoutCatch}.
4045
+ */
4046
+ declare const FLOW_TRY_CATCH_WITHOUT_CATCH = "flow-try-catch-without-catch";
2969
4047
  /**
2970
4048
  * Lint every flow for known authoring anti-patterns — its own graph AND every
2971
4049
  * nested ADR-0031 region (#5383). Returns a (possibly empty) list of findings;
@@ -2982,13 +4060,44 @@ interface LivenessLintFinding {
2982
4060
  }
2983
4061
  declare const LIVENESS_DEAD_PROPERTY = "liveness-dead-property";
2984
4062
  declare const LIVENESS_EXPERIMENTAL_PROPERTY = "liveness-experimental-property";
4063
+ declare const LIVENESS_PLANNED_PROPERTY = "liveness-planned-property";
4064
+ declare const LIVENESS_LIVE_ELSEWHERE_PROPERTY = "liveness-live-elsewhere-property";
2985
4065
  type AnyRec$2 = Record<string, unknown>;
4066
+ /**
4067
+ * The property paths of `type`'s ledger that warn an author for authoring them
4068
+ * — i.e. exactly the set {@link lintLivenessProperties} raises a finding on for
4069
+ * that type, exposed as a decision procedure.
4070
+ *
4071
+ * WHY THIS IS EXPORTED RATHER THAN RE-DERIVED. A second tool wanted the same
4072
+ * fact and the first attempt at it re-read the ledger itself, which is how the
4073
+ * two halves of one `os lint` run came to point in opposite directions: the
4074
+ * CLI's i18n coverage walker demanded `flows.*` translation keys for a group
4075
+ * whose ledger row is `planned` + `authorWarn`, so omitting the keys drew
4076
+ * `i18n/missing-flow` and authoring them drew `liveness-planned-property` — in
4077
+ * the SAME run, with no third option for the author. A demand side and a warn
4078
+ * side that disagree can only be kept honest by reading ONE verdict, so this
4079
+ * returns the very map `checkItem` iterates rather than a parallel reading of
4080
+ * the same JSON.
4081
+ *
4082
+ * Keys are the ledger's own property paths, `children` flattened one level as
4083
+ * `parent.child` — the shape `checkItem` resolves. Unreadable or absent ledger
4084
+ * ⇒ the empty set, which is also the state in which `lintLivenessProperties`
4085
+ * warns on nothing: the two sides go quiet together rather than one of them
4086
+ * going quiet alone.
4087
+ *
4088
+ * Deliberately NOT memoized, for the same reason `lintLivenessProperties`
4089
+ * re-reads on every call: a cached verdict outlives the ledger edit that
4090
+ * changes it, and a stale "nothing warns here" is the silent state this whole
4091
+ * family exists to prevent.
4092
+ */
4093
+ declare function authorWarnedProperties(type: string): ReadonlySet<string>;
2986
4094
  /**
2987
4095
  * Lint the compiled stack for authored properties the liveness ledger flags as
2988
4096
  * misleading. Advisory only — returns findings, never throws. Covers every
2989
4097
  * governed metadata type: objects (incl. `enable.*`) and their fields walk
2990
- * bespoke nesting; the remaining types are flat stack collections. Container
2991
- * properties fan out over arrays (each flow node, each dataset measure). The
4098
+ * bespoke nesting, and translation bundles walk their locale entries (#11288);
4099
+ * the remaining types are flat stack collections. Container properties fan out
4100
+ * over arrays (each flow node, each dataset measure). The
2992
4101
  * mechanism stays ledger-driven — coverage grows by marking more entries
2993
4102
  * `authorWarn` rather than touching this code.
2994
4103
  */
@@ -3023,6 +4132,7 @@ type AnyRec = Record<string, any>;
3023
4132
  declare const VIEW_KEY_COLLISION = "view-key-collision";
3024
4133
  declare const VIEW_REF_FORM_TARGET_MISSING = "view-ref-form-target-missing";
3025
4134
  declare const VIEW_REF_FORM_TARGET_KIND = "view-ref-form-target-kind";
4135
+ declare const VIEW_REF_NAV_VIEW_MISSING = "view-ref-nav-view-missing";
3026
4136
  declare function lintViewRefs(stack: AnyRec): ViewRefFinding[];
3027
4137
 
3028
4138
  /**
@@ -3184,4 +4294,64 @@ declare function lintLegacyOrganizationComposites(objects: any[]): LocatedLintIs
3184
4294
  */
3185
4295
  declare function lintDataModel(objects: any[]): LintIssue[];
3186
4296
 
3187
- export { ACTION_BODY_SOURCE_UNPARSEABLE, ACTION_BODY_WRITE_EXCLUSIONS, ACTION_BODY_WRITE_PATTERNS, ACTION_BODY_WRITE_PATTERN_IDS, ACTION_BODY_WRITE_UNKNOWN_FIELD, ACTION_BODY_WRITE_UNPROVISIONED_ANCHOR, 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_APPROVER_TYPE_UNSUPPORTED, 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, COMPONENT_PROPS_INVALID, COMPONENT_PROPS_UNKNOWN_KEY, type CapabilityRefFinding, type CapabilityRefSeverity, type ChartBindingFinding, type ChartBindingSeverity, type CheckedParse, type CheckedParseOptions, type ComponentPropsFinding, type ComponentPropsSeverity, DASHBOARD_ACTION_ROUTE_UNRESOLVED, DASHBOARD_ACTION_TARGET_UNDEFINED, DASHBOARD_FILTER_FIELD_UNKNOWN, DASHBOARD_FILTER_FIELD_UNPROVISIONED, DEFAULT_AGENT_OUTSIDE_ROSTER, type DashboardActionRefFinding, type DashboardActionRefSeverity, type EmptyCombinatorFinding, type EmptyCombinatorSeverity, type ExprIssue, type ExtractedHookBodyWrite, type ExtractedHookBodyWriteSet, FIELD_GROUP_EMPTY, FIELD_GROUP_SHADOWED, FIELD_GROUP_UNDECLARED, FILTER_EMPTY_COMBINATOR, FILTER_EMPTY_NODE, FILTER_PRESET_COMPARAND, FILTER_TOKEN_UNKNOWN, FLOW_APPROVAL_REVISE_DEAD_END, FLOW_APPROVAL_REVISE_DISABLED, FLOW_APPROVAL_REVISE_TARGET_NOT_SERVICE_OWNED, 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_MULTI_WRITE_UNFILTERED, FLOW_NODE_WRITE_UNKNOWN_FIELD, FLOW_NODE_WRITE_UNPROVISIONED_ANCHOR, FLOW_PHANTOM_AGGREGATION, FLOW_RUNAS_UNSCOPED, FLOW_TEMPLATE_FIELD_UNPROVISIONED, FLOW_TEMPLATE_LOOKUP_TRAVERSAL, FLOW_TEMPLATE_UNKNOWN_FIELD, FLOW_TIME_RELATIVE_ANTIPATTERN, FLOW_TIME_RELATIVE_DESCRIPTOR_INVALID, FLOW_TIME_RELATIVE_DESCRIPTOR_UNROUTABLE, FLOW_TRIGGER_UNKNOWN_EVENT, FLOW_TRIGGER_UNKNOWN_OBJECT, FLOW_TRIGGER_UNROUTABLE, 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_SOURCE_UNPARSEABLE, HOOK_BODY_WRITE_EXCLUSIONS, HOOK_BODY_WRITE_PATTERNS, HOOK_BODY_WRITE_PATTERN_IDS, HOOK_BODY_WRITE_UNKNOWN_FIELD, HOOK_BODY_WRITE_UNPROVISIONED_ANCHOR, 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, type LocatedLintIssue, MANAGED_API_METHOD_UNAFFORDABLE, MAX_SCHEMA_WALK_DEPTH, MEASURE_AGGREGATE_INCOHERENT, type ManagedApiMethodFinding, NAV_OBJECT_UNGRANTED, NAV_OBJECT_UNSERVABLE, NAV_TARGET_UNRESOLVED, NULL_GUARD_HINT, type NavAccessFinding, type NavAccessSeverity, type NavObjectServabilityFinding, type NavTargetRefFinding, type NavTargetRefSeverity, type NullGuardFinding, type NullGuardOptions, OBJECT_REFERENCE_UNKNOWN, OBJECT_REFERENCE_UNREGISTERED_PLATFORM, OPEN_VOCABULARY_PROBES, ORG_AXIS_CROSS_ORG_BU_GRANT, ORG_AXIS_PERMISSION_INHERITANCE, type ObjectRefFinding, type ObjectRefSeverity, type OrgAxisFinding, type OrgAxisSeverity, PAGE_FIELD_UNKNOWN, PAGE_FIELD_UNPROVISIONED, PAGE_SOURCE_CLASSNAME, PARSE_FAILURE_HINT, PREDICATE_PATH_UNRESOLVED, PREDICATE_PATH_UNROOTED, PREDICATE_RHS_PATH_SHAPED, PRE_SEAL_PHASES, type PageFieldFinding, type PageFieldSeverity, type PredicatePathFinding, type PredicatePathOptions, type PredicatePathSeverity, type PresetComparandFinding, type PresetComparandSeverity, REACT_BLOCK_NEEDS_RECORD_CONTEXT, REACT_CHART_AGGREGATE_INVALID, REACT_CHART_AXIS_UNKNOWN, REACT_CHART_DRILLDOWN_INVALID, REACT_CHART_FIELD_UNKNOWN, REACT_CHART_FIELD_UNPROVISIONED, REACT_PAGE_SOURCE_UNPARSEABLE, REFERENCE_INTEGRITY_RULES, RLS_PREDICATE_OVER_BUDGET, RLS_PREDICATE_UNENFORCEABLE, RLS_PREDICATE_UNPARSEABLE, RUNTIME_AJV_OPTIONS, type ReactPageFinding, type ReactPageSeverity, type ReactPropFinding, type ReactPropSeverity, type ReadonlyFlowWriteFinding, type ReadonlyFlowWriteSeverity, type RecordTitleFinding, type RecordTitleSeverity, type ReferenceIntegrityFinding, type ReferenceIntegrityRule, type ReferenceIntegritySeverity, type RlsPredicateFinding, type RlsPredicateSeverity, type RuleCompilabilityFinding, type RuleCompilabilitySeverity, type RuleSchemaFormatFinding, type RuleSchemaFormatSeverity, SEAL_MARKERS, SEARCHABLE_FIELD_UNKNOWN, SEARCHABLE_FIELD_UNPROVISIONED, SEARCHABLE_FIELD_UNSEARCHABLE, SECURITY_ANCHOR_HIGH_PRIVILEGE, SECURITY_BOOK_AUDIENCE_UNKNOWN_SET, SECURITY_CBP_NO_RELATION, SECURITY_DELEGATION_MISSING_REASON, SECURITY_EXTERNAL_WIDER, SECURITY_FLS_UNQUALIFIED_KEY, 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, SEMANTIC_ROLE_FIELD_UNPROVISIONED, SHARING_RULE_OBJECT_CONTROLLED_BY_PARENT, SHARING_RULE_OBJECT_NOT_SHAREABLE, SHARING_RULE_RUNTIME_VARIABLE_CONDITION, SHARING_RULE_UNLOWERABLE_CONDITION, SORT_FIELD_UNKNOWN, SORT_FIELD_UNPROVISIONED, SORT_FIELD_UNSORTABLE, STARTUP_OPEN_VOCABULARY_VERDICT, STARTUP_SOURCE_UNPARSEABLE, STARTUP_VERDICT_ASSERTIVE_WORDING, STARTUP_VERDICT_HINT, 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 SharingRuleEnforceabilityFinding, type SharingRuleEnforceabilitySeverity, type SortableFieldFinding, type SortableFieldSeverity, type SourceParseFailure, type SourceStyleFinding, type SourceStyleSeverity, type StartupRegistryVerdictFinding, type StartupRegistryVerdictOptions, type StartupRegistryVerdictSeverity, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, TRANSLATION_OPTION_KEY_UNKNOWN, TRANSLATION_SECTION_NAME_MISSING, TRANSLATION_TARGET_UNKNOWN, type TranslatableSectionFinding, type TranslatableSectionSeverity, type TranslationRefFinding, type TranslationRefSeverity, UNIQUE_DOUBLE_DECLARATION, UNIQUE_LEGACY_ORGANIZATION_COMPOSITE, UNIQUE_UNSCOPED_DECLARED_INDEX, VALIDATION_RULE_REGEX_UNCOMPILABLE, VALIDATION_RULE_SCHEMA_UNCOMPILABLE, VALIDATION_RULE_SCHEMA_UNKNOWN_FORMAT, VIEW_CONTAINER_SHAPE, VIEW_KEY_COLLISION, VIEW_REF_FORM_TARGET_KIND, VIEW_REF_FORM_TARGET_MISSING, VISIBILITY_BARE_IDENTIFIER, VISIBILITY_PREDICATE_OVER_BUDGET, VISIBILITY_PREDICATE_SYNTAX, VISIBILITY_ROOT_MISLAYERED, type ViewContainerFinding, type ViewContainerSeverity, type ViewRefFinding, type VisibilityFinding, type VisibilityLayer, type VisibilityOptions, type VisibilitySeverity, WIDGET_DATASET_UNKNOWN, WIDGET_DIMENSION_UNKNOWN, WIDGET_LEGACY_ANALYTICS_SHAPE, WIDGET_LEGACY_ANALYTICS_UNRENDERABLE, WIDGET_MEASURE_UNKNOWN, type WalkedComponent, type WalkedValidationRule, type WidgetBindingFinding, type WidgetBindingSeverity, buildAccessMatrix, checkSortDeclaration, describeParseFailure, diffAccessMatrix, extractHookBodyWriteSet, extractHookBodyWrites, findStartupRegistryVerdicts, findUnguardedNullableOperands, isSourceAuthoredPage, lintAutonumberFormats, lintDataModel, lintFlowPatterns, lintLegacyOrganizationComposites, lintLivenessProperties, lintUniqueDeclarations, lintUnscopedDeclaredIndexes, lintViewRefs, nearestRegisteredFormat, nullGuardMessage, validateActionBodyWrites, validateActionLocations, validateActionNameRefs, validateAiAgentAuthoring, validateAiSurfaceAffinity, validateAiToolReferences, validateApprovalApprovers, validateCapabilityReferences, validateChartBindings, validateComponentProps, validateDashboardActionRefs, validateEmptyCombinators, validateFilterTokens, validateFlowNodeWrites, validateFlowTemplatePaths, validateFlowTriggerReadiness, validateFormLayout, validateFunctionalCompleteness, validateHookBodyWrites, validateJsxPages, validateListViewMode, validateManagedApiMethods, validateNavAccess, validateNavObjectServability, validateNavTargetRefs, validateObjectReferences, validateOrgAxisRedLines, validatePageFieldBindings, validatePageSourceStyling, validatePredicatePathRefs, validatePresetComparands, validateReactPageProps, validateReactPages, validateReadonlyFlowWrites, validateRecordTitle, validateReferenceIntegrity, validateResponsiveStyles, validateRlsPredicateEnforceability, validateRuleCompilability, validateRuleSchemaFormats, validateSearchableFields, validateSecurityPosture, validateSecurityRoleWord, validateSeedReplaySafety, validateSeedStateMachine, validateSemanticRoles, validateSharingRuleEnforceability, validateSortableFields, validateStackExpressions, validateTranslatableSections, validateTranslationReferences, validateViewContainers, validateVisibilityPredicates, validateWidgetBindings, walkPageComponents };
4297
+ /** Which parse produced a finding — see the door table in the module header. */
4298
+ type EnvelopeAuditDoor = 'PageSchema' | 'PageComponentSchema' | 'ComponentPropsMap';
4299
+ /** One bare-expression position: authored as a string, normalized by a parse. */
4300
+ interface BareExpressionFinding {
4301
+ /** The consumer's page label, e.g. `<ExportName> (<page.name>)`. */
4302
+ page: string;
4303
+ /** Authored path from the page root, e.g. `slots.alerts[0].visibleWhen`. */
4304
+ path: string;
4305
+ /** The bare string the author wrote — what reaches the wire verbatim. */
4306
+ authored: string;
4307
+ /** Set when the parse also RENAMED the key (deprecated alias). */
4308
+ normalizedTo?: string;
4309
+ door: EnvelopeAuditDoor;
4310
+ }
4311
+ /** The three-door union over one page, findings and door preconditions both. */
4312
+ interface PageEnvelopeAudit {
4313
+ /** Every bare-expression position any door found, deduped by path, sorted. */
4314
+ findings: BareExpressionFinding[];
4315
+ /** Door 1 could not run — the page itself does not parse. */
4316
+ pageParseError?: string;
4317
+ /** Door 2 could not run for these components. */
4318
+ componentParseErrors: {
4319
+ path: string;
4320
+ type: string;
4321
+ issues: string;
4322
+ }[];
4323
+ /** Door 3 has no schema to parse these components' `properties` with. */
4324
+ unmappedTypes: {
4325
+ path: string;
4326
+ type: string;
4327
+ }[];
4328
+ /** Door 3 could not run — the authored bag is refused by its props schema. */
4329
+ unreadableProps: {
4330
+ path: string;
4331
+ type: string;
4332
+ issues: string;
4333
+ }[];
4334
+ /** How many components the walk reached (coverage floor for doors 2 and 3). */
4335
+ componentCount: number;
4336
+ }
4337
+ /**
4338
+ * Run all three parse doors over one authored page and union their findings.
4339
+ *
4340
+ * Read `findings` for the verdict, and assert the four precondition channels
4341
+ * separately (see the module header — a door that could not open reports
4342
+ * there, never as a silently smaller `findings`).
4343
+ *
4344
+ * Exported findings are deduped by path: a top-level component's
4345
+ * `visibleWhen` is legitimately seen by doors 1 AND 2, and reporting it twice
4346
+ * would read as two defects.
4347
+ *
4348
+ * @param page - The authored page object, exactly as exported (never parsed
4349
+ * first — the audit's whole subject is what the raw export serves).
4350
+ * @param pageLabel - How findings should name the page; consumers use
4351
+ * `<ExportName> (<page.name>)`.
4352
+ */
4353
+ declare function auditPageExpressionEnvelopes(page: unknown, pageLabel: string): PageEnvelopeAudit;
4354
+ /** Render findings as the actionable red an author reads in CI. */
4355
+ declare function renderBareExpressionFindings(findings: readonly BareExpressionFinding[]): string;
4356
+
4357
+ export { ACTION_API_UPDATE_READONLY_WHEN_FIELD, ACTION_BODY_SOURCE_UNPARSEABLE, ACTION_BODY_WRITE_EXCLUSIONS, ACTION_BODY_WRITE_PATTERNS, ACTION_BODY_WRITE_PATTERN_IDS, ACTION_BODY_WRITE_UNKNOWN_FIELD, ACTION_BODY_WRITE_UNPROVISIONED_ANCHOR, 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_APPROVER_TYPE_UNSUPPORTED, 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 BareExpressionFinding, type BodyWritePatternExclusion, CAPABILITY_REFERENCE_UNKNOWN, CHART_AXIS_NOT_SELECTED, CHART_CONFIG_MISSING, CHART_DATASET_UNKNOWN, CHART_DIMENSION_UNKNOWN, CHART_FIELD_UNKNOWN, CHART_MEASURE_UNKNOWN, COMPONENT_PROPS_INVALID, COMPONENT_PROPS_UNKNOWN_KEY, COMPONENT_TYPE_UNKNOWN, type CapabilityRefFinding, type CapabilityRefSeverity, type ChartBindingFinding, type ChartBindingSeverity, type CheckedParse, type CheckedParseOptions, type ComponentPropsFinding, type ComponentPropsSeverity, type ComponentTypeFinding, DASHBOARD_ACTION_ROUTE_UNRESOLVED, DASHBOARD_ACTION_TARGET_UNDEFINED, DASHBOARD_FILTER_FIELD_NOT_INCLUDED, DASHBOARD_FILTER_FIELD_UNKNOWN, DASHBOARD_FILTER_FIELD_UNPROVISIONED, DATASET_FIELD_NOT_INCLUDED, DATASET_FIELD_UNKNOWN, DATASET_FILTER_FIELD_UNKNOWN, DATASET_INCLUDE_UNKNOWN, DEFAULT_AGENT_LEGACY_ALIAS, DEFAULT_AGENT_OUTSIDE_ROSTER, type DashboardActionRefFinding, type DashboardActionRefSeverity, type DatasetRefFinding, type DatasetRefSeverity, type EmptyCombinatorFinding, type EmptyCombinatorSeverity, type EnvelopeAuditDoor, type ExprIssue, type ExtractedHookBodyWrite, type ExtractedHookBodyWriteSet, FIELD_GROUP_EMPTY, FIELD_GROUP_SHADOWED, FIELD_GROUP_UNDECLARED, FIELD_RULE_BOUND_ROOTS, FILTER_EMPTY_COMBINATOR, FILTER_EMPTY_NODE, FILTER_PRESET_COMPARAND, FILTER_TOKEN_UNKNOWN, FLOW_APPROVAL_REVISE_DEAD_END, FLOW_APPROVAL_REVISE_DISABLED, FLOW_APPROVAL_REVISE_TARGET_NOT_SERVICE_OWNED, 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_LOOP_BODY_UNCONTAINED, FLOW_MULTIPLE_DEFAULT_EDGES, FLOW_MULTI_WRITE_UNFILTERED, FLOW_NODE_WRITE_UNKNOWN_FIELD, FLOW_NODE_WRITE_UNPROVISIONED_ANCHOR, FLOW_PHANTOM_AGGREGATION, FLOW_RUNAS_UNSCOPED, FLOW_TEMPLATE_FIELD_UNPROVISIONED, FLOW_TEMPLATE_LOOKUP_TRAVERSAL, FLOW_TEMPLATE_UNKNOWN_FIELD, FLOW_TIME_RELATIVE_ANTIPATTERN, FLOW_TIME_RELATIVE_DESCRIPTOR_INVALID, FLOW_TIME_RELATIVE_DESCRIPTOR_UNROUTABLE, FLOW_TRIGGER_UNKNOWN_EVENT, FLOW_TRIGGER_UNKNOWN_OBJECT, FLOW_TRIGGER_UNROUTABLE, FLOW_TRY_CATCH_WITHOUT_CATCH, FLOW_UPDATE_READONLY_FIELD, FLOW_UPDATE_READONLY_WHEN_FIELD, FLOW_WRITE_NODE_TYPES, FLOW_WRITE_NODE_TYPES_DEFERRED, FORM_COLSPAN_ABSOLUTE, FORM_FIELD_UNKNOWN, FORM_SECTION_GROUP_UNKNOWN, type FieldPathAccount, type FieldPathVerdict, type FilterFieldKey, 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, type GraphField, type GraphObject, HOOK_API_UPDATE_READONLY_FIELD, HOOK_API_UPDATE_READONLY_WHEN_FIELD, HOOK_BODY_SOURCE_UNPARSEABLE, HOOK_BODY_WRITE_EXCLUSIONS, HOOK_BODY_WRITE_PATTERNS, HOOK_BODY_WRITE_PATTERN_IDS, HOOK_BODY_WRITE_UNKNOWN_FIELD, HOOK_BODY_WRITE_UNPROVISIONED_ANCHOR, type HookBodyWriteFinding, type HookBodyWritePattern, type HookBodyWriteSeverity, type JsxPageFinding, type JsxPageSeverity, LIST_VIEW_FIELD_DOTTED, LIST_VIEW_FIELD_UNKNOWN, LIST_VIEW_FILTERS_IN_VIEWS_MODE, LIVENESS_DEAD_PROPERTY, LIVENESS_EXPERIMENTAL_PROPERTY, LIVENESS_LIVE_ELSEWHERE_PROPERTY, LIVENESS_PLANNED_PROPERTY, type LintIssue, type ListViewFieldRefFinding, type ListViewFieldRefSeverity, type ListViewModeFinding, type ListViewModeSeverity, type LivenessLintFinding, type LocatedLintIssue, MANAGED_API_METHOD_UNAFFORDABLE, MAX_SCHEMA_WALK_DEPTH, MEASURE_AGGREGATE_INCOHERENT, type ManagedApiMethodFinding, NAV_OBJECT_UNGRANTED, NAV_OBJECT_UNSERVABLE, NAV_TARGET_UNRESOLVED, NULL_GUARD_HINT, type NavAccessFinding, type NavAccessSeverity, type NavObjectServabilityFinding, type NavTargetRefFinding, type NavTargetRefSeverity, type NullGuardFinding, type NullGuardOptions, OBJECTUI_DERIVATION_PREDICATES, OBJECT_REFERENCE_UNKNOWN, OBJECT_REFERENCE_UNREGISTERED_PLATFORM, OPEN_VOCABULARY_PROBES, ORG_AXIS_CROSS_ORG_BU_GRANT, ORG_AXIS_PERMISSION_INHERITANCE, type ObjectGraph, type ObjectRefFinding, type ObjectRefSeverity, type OrgAxisFinding, type OrgAxisSeverity, PAGE_FIELD_UNKNOWN, PAGE_FIELD_UNPROVISIONED, PAGE_SECTION_GROUP_UNKNOWN, PAGE_SOURCE_CLASSNAME, PAGE_VISUALIZATION_WITHOUT_BINDING, PARSE_FAILURE_HINT, PREDICATE_PATH_UNRESOLVED, PREDICATE_PATH_UNROOTED, PREDICATE_RHS_PATH_SHAPED, PRE_SEAL_PHASES, type PageEnvelopeAudit, type PageFieldFinding, type PageFieldSeverity, type PageVisualizationFinding, type PageVisualizationSeverity, type PredicatePathFinding, type PredicatePathOptions, type PredicatePathSeverity, type PresetComparandFinding, type PresetComparandSeverity, REACT_BLOCK_NEEDS_RECORD_CONTEXT, REACT_CHART_AGGREGATE_INVALID, REACT_CHART_AXIS_UNKNOWN, REACT_CHART_DRILLDOWN_INVALID, REACT_CHART_FIELD_UNKNOWN, REACT_CHART_FIELD_UNPROVISIONED, REACT_PAGE_SOURCE_UNPARSEABLE, REACT_PROP_DEPRECATED, READONLY_ACTION_WRITE_EXCLUSIONS, READONLY_ACTION_WRITE_PATTERN_IDS, READONLY_HOOK_WRITE_EXCLUSIONS, READONLY_HOOK_WRITE_PATTERN_IDS, REFERENCE_INTEGRITY_RULES, RELATIONSHIP_FIELD_TYPES, RLS_PREDICATE_OVER_BUDGET, RLS_PREDICATE_UNENFORCEABLE, RLS_PREDICATE_UNPARSEABLE, RUNTIME_AJV_OPTIONS, type ReactPageFinding, type ReactPageSeverity, type ReactPropFinding, type ReactPropSeverity, type ReadonlyActionWriteFinding, type ReadonlyActionWriteSeverity, type ReadonlyFlowWriteFinding, type ReadonlyFlowWriteSeverity, type ReadonlyHookWriteFinding, type ReadonlyHookWriteSeverity, type RecordTitleFinding, type RecordTitleSeverity, type ReferenceIntegrityFinding, type ReferenceIntegrityRule, type ReferenceIntegritySeverity, type RlsPredicateFinding, type RlsPredicateSeverity, type RuleCompilabilityFinding, type RuleCompilabilitySeverity, type RuleSchemaFormatFinding, type RuleSchemaFormatSeverity, SEAL_MARKERS, SEARCHABLE_FIELD_UNKNOWN, SEARCHABLE_FIELD_UNPROVISIONED, SEARCHABLE_FIELD_UNSEARCHABLE, SECURITY_ANCHOR_HIGH_PRIVILEGE, SECURITY_BOOK_AUDIENCE_UNKNOWN_SET, SECURITY_CBP_AMBIGUOUS_RELATION, SECURITY_CBP_NO_RELATION, SECURITY_DELEGATION_MISSING_REASON, SECURITY_EXTERNAL_WIDER, SECURITY_FLS_UNQUALIFIED_KEY, 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, SEMANTIC_ROLE_FIELD_UNPROVISIONED, SHARING_RULE_OBJECT_CONTROLLED_BY_PARENT, SHARING_RULE_OBJECT_NOT_SHAREABLE, SHARING_RULE_RUNTIME_VARIABLE_CONDITION, SHARING_RULE_UNLOWERABLE_CONDITION, SORT_FIELD_UNKNOWN, SORT_FIELD_UNPROVISIONED, SORT_FIELD_UNSORTABLE, STARTUP_OPEN_VOCABULARY_VERDICT, STARTUP_SOURCE_UNPARSEABLE, STARTUP_VERDICT_ASSERTIVE_WORDING, STARTUP_VERDICT_HINT, 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 SectionGroupFinding, type SectionGroupRef, type SecurityFinding, type SecuritySeverity, type SeedReplaySafetyFinding, type SeedReplaySafetySeverity, type SeedStateMachineFinding, type SeedStateMachineSeverity, type SemanticRoleFinding, type SemanticRoleSeverity, type Severity, type SharingRuleEnforceabilityFinding, type SharingRuleEnforceabilitySeverity, type SortableFieldFinding, type SortableFieldSeverity, type SourceParseFailure, type SourceStyleFinding, type SourceStyleSeverity, type StartupRegistryVerdictFinding, type StartupRegistryVerdictOptions, type StartupRegistryVerdictSeverity, type StyleFinding, type StyleSeverity, TABLE_COUNT_ONLY, TITLE_FORMAT_RETIRED, TITLE_UNRESOLVABLE, TRANSLATION_OPTION_KEY_UNKNOWN, TRANSLATION_SECTION_NAME_MISSING, TRANSLATION_TARGET_UNKNOWN, type TranslatableSectionFinding, type TranslatableSectionSeverity, type TranslationRefFinding, type TranslationRefSeverity, UNIQUE_DOUBLE_DECLARATION, UNIQUE_LEGACY_ORGANIZATION_COMPOSITE, UNIQUE_UNSCOPED_DECLARED_INDEX, VALIDATION_RULE_REGEX_UNCOMPILABLE, VALIDATION_RULE_SCHEMA_UNCOMPILABLE, VALIDATION_RULE_SCHEMA_UNKNOWN_FORMAT, VIEW_CONTAINER_SHAPE, VIEW_KEY_COLLISION, VIEW_PAGE_UNRESOLVED, VIEW_REF_FORM_TARGET_KIND, VIEW_REF_FORM_TARGET_MISSING, VIEW_REF_NAV_VIEW_MISSING, VISIBILITY_BARE_IDENTIFIER, VISIBILITY_PREDICATE_OVER_BUDGET, VISIBILITY_PREDICATE_SYNTAX, VISIBILITY_PREDICATE_UNKNOWN_FUNCTION, VISIBILITY_ROOT_MISLAYERED, type ViewContainerFinding, type ViewContainerSeverity, type ViewPageRefFinding, type ViewPageRefSeverity, type ViewRefFinding, type VisibilityFinding, type VisibilityLayer, type VisibilityOptions, type VisibilitySeverity, type VisualizationPredicate, WIDGET_DATASET_UNKNOWN, WIDGET_DIMENSION_UNKNOWN, WIDGET_FILTER_FIELD_NOT_INCLUDED, WIDGET_FILTER_FIELD_UNKNOWN, WIDGET_LEGACY_ANALYTICS_SHAPE, WIDGET_LEGACY_ANALYTICS_UNRENDERABLE, WIDGET_MEASURE_UNKNOWN, WIDGET_SORTBY_UNSELECTED, type WalkedComponent, type WalkedValidationRule, type WidgetBindingFinding, type WidgetBindingSeverity, auditPageExpressionEnvelopes, authorWarnedProperties, buildAccessMatrix, checkSectionGroupRefs, checkSortDeclaration, describeFieldPathVerdict, describeParseFailure, diffAccessMatrix, extractHookBodyWriteSet, extractHookBodyWrites, fieldRuleRootIssue, findStartupRegistryVerdicts, findUnguardedNullableOperands, indexObjectFieldGroups, indexObjectGraph, isSourceAuthoredPage, isUnjudgeable, joinablePrefixes, lintAutonumberFormats, lintDataModel, lintFlowPatterns, lintLegacyOrganizationComposites, lintLivenessProperties, lintUniqueDeclarations, lintUnscopedDeclaredIndexes, lintViewRefs, listNames, nearestName, nearestRegisteredFormat, nullGuardMessage, renderBareExpressionFindings, resolveFieldPath, sectionGroupRefs, suggestName, validateActionBodyWrites, validateActionLocations, validateActionNameRefs, validateAiAgentAuthoring, validateAiSurfaceAffinity, validateAiToolReferences, validateApprovalApprovers, validateCapabilityReferences, validateChartBindings, validateComponentProps, validateComponentTypes, validateDashboardActionRefs, validateDatasetReferences, validateEmptyCombinators, validateFilterTokens, validateFlowNodeWrites, validateFlowTemplatePaths, validateFlowTriggerReadiness, validateFormLayout, validateFunctionalCompleteness, validateHookBodyWrites, validateJsxPages, validateListViewFieldRefs, validateListViewMode, validateManagedApiMethods, validateNavAccess, validateNavObjectServability, validateNavTargetRefs, validateObjectReferences, validateOrgAxisRedLines, validatePageFieldBindings, validatePageSourceStyling, validatePageVisualizationBindings, validatePredicatePathRefs, validatePresetComparands, validateReactPageProps, validateReactPages, validateReadonlyActionWrites, validateReadonlyFlowWrites, validateReadonlyHookWrites, validateRecordTitle, validateReferenceIntegrity, validateResponsiveStyles, validateRlsPredicateEnforceability, validateRuleCompilability, validateRuleSchemaFormats, validateSearchableFields, validateSecurityPosture, validateSecurityRoleWord, validateSeedReplaySafety, validateSeedStateMachine, validateSemanticRoles, validateSharingRuleEnforceability, validateSortableFields, validateStackExpressions, validateTranslatableSections, validateTranslationReferences, validateViewContainers, validateViewPageRefs, validateVisibilityPredicates, validateWidgetBindings, walkFilterFieldKeys, walkPageComponents };