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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/validate-widget-bindings.ts","../src/system-fields.ts","../src/validate-expressions.ts","../src/validate-list-view-mode.ts","../src/validate-flow-trigger-readiness.ts","../src/flow-walk.ts","../src/validate-flow-template-paths.ts","../src/validate-readonly-flow-writes.ts","../src/validate-view-containers.ts","../src/validate-responsive-styles.ts","../src/validate-jsx-pages.ts","../src/validate-react-pages.ts","../src/validate-react-page-props.ts","../src/validate-searchable-fields.ts","../src/page-walk.ts","../src/validate-page-field-bindings.ts","../src/validate-page-source-styling.ts","../src/validate-record-title.ts","../src/validate-semantic-roles.ts","../src/validate-form-layout.ts","../src/validate-visibility-predicates.ts","../src/validate-capability-references.ts","../src/validate-approval-approvers.ts","../src/validate-seed-replay-safety.ts","../src/validate-seed-state-machine.ts","../src/validate-security-posture.ts","../src/validate-org-axis-red-lines.ts","../src/validate-dashboard-action-refs.ts","../src/validate-filter-tokens.ts","../src/validate-object-references.ts","../src/validate-action-name-refs.ts","../src/validate-chart-bindings.ts","../src/validate-nav-access.ts","../src/build-access-matrix.ts","../src/validate-translation-references.ts","../src/validate-ai-surface-affinity.ts","../src/validate-ai-tool-references.ts","../src/validate-ai-agent-authoring.ts","../src/validate-hook-body-writes.ts","../src/validate-action-body-writes.ts","../src/validate-flow-node-writes.ts","../src/reference-integrity-suite.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { isIncoherentAggregate } from '@objectstack/spec/data';\nimport { ChartTypeSchema } from '@objectstack/spec/ui';\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\n/**\n * Build-time dashboard widget binding diagnostics (issues #1719, #1721).\n *\n * Runs at `objectstack validate`/`compile`/`build` AFTER the stack has been\n * schema-parsed, so every widget's `dataset` reference can be linked to its\n * `defineDataset` and each entry in `dimensions`/`values` resolved to a\n * declared dimension/measure. This is the semantic/cross-reference phase —\n * the rules here cannot run during plain Zod parsing of the raw widget\n * literal (the dataset may even live in another package of the stack).\n *\n * Reference-integrity rules (#1721) — severity `error`, the page is broken:\n *\n * - `widget-dataset-unknown` — `dataset` does not resolve to a declared\n * `Dataset`.\n * - `widget-dimension-unknown` — a `dimensions[]` entry is not a dimension\n * name on the bound dataset.\n * - `widget-measure-unknown` — a `values[]` entry is not a measure name on\n * the bound dataset.\n * - `chart-field-unknown` — a `chartConfig` binding names a field the query\n * result will not contain: `xAxis.field` must be one of the widget's\n * dimensions (or a dataset dimension), and each `yAxis[].field` /\n * `series[].name` must be one of the widget's selected measures\n * (`values`). Post-cutover (ADR-0021) the result rows are keyed by\n * measure NAME (e.g. `sum_amount`), not the base column (`amount`) — a\n * stale base-column reference renders the axis but an empty series.\n * - `widget-legacy-analytics-unrenderable` (#1878/#1894) — a widget uses the\n * removed pre-ADR-0021 inline-analytics shape (`categoryField`/`rowField`/…)\n * as its ONLY data wiring: no `dataset`, no `object`, no inline `data`. The\n * renderer reads only the dataset path, so the widget has no data at all and\n * renders nothing. Errored (not warned) so this class of authoring mistake —\n * very often an AI emitting a removed shape — fails the build instead of\n * shipping a blank widget past human review.\n * - `dashboard-filter-field-unknown` (#3365) — a dashboard-level filter\n * (`dateRange` or a `globalFilters[]` entry) is wired into EVERY widget's\n * analytics query (#2501), but its EFFECTIVE field (after any `filterBindings`\n * re-target) does not exist on a bound widget's dataset object. The widget's\n * query then references a non-existent column and crashes at render time\n * (`no such column …`) — a build-decidable invariant that previously escaped\n * the static gate and failed only when a user opened the dashboard. A widget\n * opts out with `filterBindings: { <name>: false }` or re-targets to a real\n * field. This is the same field-existence invariant ADR-0032 enforces for\n * CEL formula / sharing-rule references, applied to dashboard filter fields.\n *\n * Advisory rules — severity `warning`, build stays green:\n *\n * - `chart-config-missing` — a chart-type widget (bar/line/pie/…) has no\n * `chartConfig`, so the renderer cannot tell which measure to plot.\n * - `table-count-only` (#1719) — a `table`/`pivot` widget whose selected\n * measures are ALL `aggregate: 'count'` and which declares no\n * `dimensions` asks the analytics service for a single summary row. That\n * is the shape a `metric` widget wants — for a table it almost always\n * means the author wanted a per-record listing, which is not an\n * analytics dataset at all (model it as an object-bound ListView,\n * ADR-0017). Evaluated on the WIDGET's binding, not the dataset.\n * - `measure-aggregate-incoherent` — a dataset measure aggregates its field\n * in a way that produces a meaningless number: today, SUM (or\n * `count_distinct`) of a `percent`/rate field, whose total routinely\n * exceeds 100%. Rates must AVG. Checked once per dataset (independent of\n * any widget) when the bound object's field types are known.\n * - `widget-legacy-analytics-shape` (#1878/#1894) — a widget sets a\n * pre-ADR-0021 inline key (`categoryField`/`valueField`/`xAxisField`/\n * `yAxisFields`/`aggregate`/`aggregation`/`rowField`/`columnField`) that the\n * single-form cutover removed. The dashboard renderer routes dataset-bound\n * widgets through `DatasetWidget` and never reads these, so they are a\n * silent no-op. Steers the author onto `dataset`+`dimensions`+`values`.\n *\n * Warnings can be deliberately suppressed per widget via\n * `suppressWarnings: ['<rule-id>']`; errors cannot — they describe a\n * binding the analytics service cannot satisfy.\n */\n\nexport const WIDGET_DATASET_UNKNOWN = 'widget-dataset-unknown';\nexport const WIDGET_DIMENSION_UNKNOWN = 'widget-dimension-unknown';\nexport const WIDGET_MEASURE_UNKNOWN = 'widget-measure-unknown';\nexport const CHART_FIELD_UNKNOWN = 'chart-field-unknown';\nexport const CHART_CONFIG_MISSING = 'chart-config-missing';\nexport const TABLE_COUNT_ONLY = 'table-count-only';\nexport const MEASURE_AGGREGATE_INCOHERENT = 'measure-aggregate-incoherent';\nexport const WIDGET_LEGACY_ANALYTICS_SHAPE = 'widget-legacy-analytics-shape';\nexport const WIDGET_LEGACY_ANALYTICS_UNRENDERABLE = 'widget-legacy-analytics-unrenderable';\nexport const DASHBOARD_FILTER_FIELD_UNKNOWN = 'dashboard-filter-field-unknown';\n\n/**\n * Pre-ADR-0021 inline-analytics keys. The single-form cutover replaced them\n * with the semantic-layer shape (`dataset` + `dimensions` + `values`); the\n * dashboard renderer routes dataset-bound widgets through `DatasetWidget` and\n * never reads these, so authoring one today is a silent no-op. Warned (not\n * errored) because they still parse and a legacy object-bound widget keeps\n * rendering — the author is just being steered to the governed shape.\n * (liveness audit #1878 / #1894).\n *\n * Interplay with `DashboardWidgetSchema.strict()` (framework#3251, protocol 16):\n * on the schema-parsed CLI paths (`compile`, `validate`) strict rejects these\n * keys as a hard parse error *before* binding validation runs, so these rules\n * are effectively preempted there. They remain the friendly, suppressible\n * bridge on the raw-config paths (`lint`, `doctor`) that hand\n * `validateWidgetBindings` un-parsed config — keeping the actionable\n * \"steer to the dataset shape\" message rather than a bare unknown-key error.\n */\nconst LEGACY_ANALYTICS_KEYS = [\n 'categoryField', 'valueField', 'xAxisField', 'yAxisFields',\n 'aggregate', 'aggregation', 'rowField', 'columnField',\n] as const;\n\nexport type WidgetBindingSeverity = 'error' | 'warning';\n\nexport interface WidgetBindingFinding {\n /** `error` = unresolvable binding (broken page); `warning` = advisory. */\n severity: WidgetBindingSeverity;\n /** Diagnostic rule id (registry entry), e.g. `widget-measure-unknown`. */\n rule: string;\n /** Human-readable location, e.g. `dashboard \"x\" › widget \"y\"`. */\n where: string;\n /** Config path, e.g. `dashboards[0].widgets[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix (or deliberately suppress) it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction asStrings(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((s): s is string => typeof s === 'string') : [];\n}\n\n/**\n * Chart families that plot a single value or every column, and so need no\n * `chartConfig` measure mapping: single-value types plot their lone value,\n * tabular types render each column as-is.\n */\nconst MEASURE_EXEMPT_CHART_TYPES = new Set([\n 'gauge', 'solid-gauge', 'metric', 'kpi', 'bullet',\n 'table', 'pivot',\n]);\n\n/**\n * Chart families whose renderer needs a `chartConfig` measure mapping — the\n * taxonomy minus the exemptions above.\n *\n * Derived from `ChartTypeSchema` rather than restated. As a hand-written list it\n * had no way to know when the taxonomy grew, and the omission is silent in\n * exactly the wrong direction: an unlisted family is treated as \"not a chart\",\n * so a widget missing its measure mapping passes validation instead of being\n * reported. objectui#2945.\n */\nconst CHART_TYPES = new Set<string>(\n ChartTypeSchema.options.filter(t => !MEASURE_EXEMPT_CHART_TYPES.has(t)),\n);\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length, n = b.length;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const cur = [i];\n for (let j = 1; j <= n; j++) {\n cur[j] = Math.min(\n prev[j] + 1,\n cur[j - 1] + 1,\n prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),\n );\n }\n prev = cur;\n }\n return prev[n];\n}\n\n/**\n * Nearest declared name for a typo'd/stale reference, or undefined when\n * nothing is close. Containment is checked first because the cutover's\n * canonical drift is base column → prefixed measure name (`amount` →\n * `sum_amount`), which is far in edit distance but obvious to a human.\n */\nfunction didYouMean(input: string, candidates: Iterable<string>): string | undefined {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const c of candidates) {\n let score: number;\n if (input.length >= 3 && (c.includes(input) || input.includes(c))) {\n score = Math.abs(c.length - input.length);\n } else {\n const d = levenshtein(input, c);\n if (d > Math.max(2, Math.floor(input.length / 3))) continue;\n score = 100 + d;\n }\n if (score < bestScore) { bestScore = score; best = c; }\n }\n return best;\n}\n\nfunction suggest(input: string, candidates: Iterable<string>): string {\n const s = didYouMean(input, candidates);\n return s ? ` Did you mean \"${s}\"?` : '';\n}\n\nfunction list(names: Iterable<string>): string {\n const arr = [...names];\n return arr.length > 0 ? arr.join(', ') : '(none)';\n}\n\n// ── dashboard-filter field-existence (#3365) ─────────────────────────────────\n\n/** Reserved filter name for the dashboard's built-in date range (#2501). */\nconst DATE_RANGE_FILTER_NAME = 'dateRange';\n/**\n * Default field of the built-in date range when `dateRange.field` is omitted.\n * MUST track objectui `dashboard-filters.ts` `DATE_RANGE_DEFAULT_FIELD` — the\n * runtime this check shadows. `created_at` is a registry-injected system field\n * (the package-shared `SYSTEM_FIELDS`, `system-fields.ts`), so a bare\n * `dateRange` never false-positives.\n */\nconst DATE_RANGE_DEFAULT_FIELD = 'created_at';\n\ninterface DashFilterDef {\n /** Stable filter name — the key widgets bind against in `filterBindings`. */\n name: string;\n /** Default target field when a widget declares no explicit binding. */\n field: string;\n /** Legacy widget-id allow-list; gates the DEFAULT binding only. */\n targetWidgets?: string[];\n}\n\n/**\n * Normalize a dashboard's declared filters into `{ name, field, targetWidgets }`\n * defs — the built-in `dateRange` (reserved name) first, then every\n * `globalFilters[]` entry named by its `name` (defaulting to `field`). Later\n * duplicates win. Mirrors objectui `resolveDashboardFilterDefs`.\n */\nfunction dashboardFilterDefs(dash: AnyRec): DashFilterDef[] {\n const byName = new Map<string, DashFilterDef>();\n\n const dateRange = dash.dateRange;\n if (dateRange && typeof dateRange === 'object') {\n const declared = (dateRange as AnyRec).field;\n const field = typeof declared === 'string' && declared ? declared : DATE_RANGE_DEFAULT_FIELD;\n byName.set(DATE_RANGE_FILTER_NAME, { name: DATE_RANGE_FILTER_NAME, field });\n }\n\n for (const f of asArray(dash.globalFilters)) {\n if (typeof f.field !== 'string' || !f.field) continue;\n const name = typeof f.name === 'string' && f.name ? f.name : f.field;\n const targetWidgets = Array.isArray(f.targetWidgets)\n ? f.targetWidgets.filter((w): w is string => typeof w === 'string')\n : undefined;\n byName.set(name, { name, field: f.field, targetWidgets });\n }\n\n return [...byName.values()];\n}\n\n/**\n * Resolve which field of `widget` a filter binds to, or `undefined` when the\n * widget is not bound (opted out / not targeted). Precedence mirrors objectui\n * `resolveBoundField`: explicit `filterBindings` entry (string re-targets,\n * `false` opts out — both win) → legacy `targetWidgets` allow-list → the\n * filter's own default `field`. `explicit` distinguishes an author-chosen field\n * (a typo they must fix) from the inherited default (which they may opt out of).\n */\nfunction effectiveFilterField(\n widget: AnyRec,\n def: DashFilterDef,\n): { field: string; explicit: boolean } | undefined {\n const bindings = widget.filterBindings;\n const binding = bindings && typeof bindings === 'object'\n ? (bindings as AnyRec)[def.name]\n : undefined;\n if (binding === false) return undefined;\n if (typeof binding === 'string' && binding) return { field: binding, explicit: true };\n if (def.targetWidgets && def.targetWidgets.length > 0) {\n const id = typeof widget.id === 'string' ? widget.id : undefined;\n if (!id || !def.targetWidgets.includes(id)) return undefined;\n }\n return { field: def.field, explicit: false };\n}\n\n/**\n * Validate every dashboard widget's dataset binding. Returns the list of\n * findings (empty = clean). Caller decides how to surface them: `error`\n * findings describe bindings the analytics service cannot satisfy and\n * should fail validate/build; `warning` findings are advisory and must\n * never fail the build on their own.\n */\nexport function validateWidgetBindings(stack: AnyRec): WidgetBindingFinding[] {\n const findings: WidgetBindingFinding[] = [];\n\n const datasets = new Map<string, AnyRec>();\n for (const ds of asArray(stack.datasets)) {\n if (typeof ds.name === 'string') datasets.set(ds.name, ds);\n }\n\n // ── (0) dataset measures aggregate their field coherently ──\n // A measure that SUMs a percentage/rate field produces a meaningless total\n // (it can exceed 100%); rates must AVG. This is a dataset-level defect (it\n // does not depend on any widget), so it is checked once over every dataset\n // whose object's field types are known. Advisory — the page still renders.\n const objectFieldTypes = new Map<string, Map<string, string>>();\n for (const o of asArray(stack.objects)) {\n if (typeof o.name !== 'string') continue;\n const fm = new Map<string, string>();\n for (const f of asArray(o.fields)) {\n if (typeof f.name === 'string' && typeof f.type === 'string') fm.set(f.name, f.type);\n }\n objectFieldTypes.set(o.name, fm);\n }\n const datasetList = asArray(stack.datasets);\n for (let i = 0; i < datasetList.length; i++) {\n const ds = datasetList[i];\n const fieldTypes = typeof ds.object === 'string' ? objectFieldTypes.get(ds.object) : undefined;\n if (!fieldTypes) continue; // cannot judge without the object's field types\n const dsMeasures = asArray(ds.measures);\n for (let k = 0; k < dsMeasures.length; k++) {\n const m = dsMeasures[k];\n const field = typeof m.field === 'string' ? m.field : undefined;\n const aggregate = typeof m.aggregate === 'string' ? m.aggregate : undefined;\n if (!field || !aggregate) continue; // count(*) and underivable measures are fine\n const ftype = fieldTypes.get(field);\n if (ftype && isIncoherentAggregate(aggregate, ftype)) {\n findings.push({\n severity: 'warning',\n rule: MEASURE_AGGREGATE_INCOHERENT,\n where: `dataset \"${typeof ds.name === 'string' ? ds.name : `(dataset ${i})`}\" › measure \"${typeof m.name === 'string' ? m.name : `(measure ${k})`}\"`,\n path: `datasets[${i}].measures[${k}]`,\n message:\n `measure \"${m.name}\" applies ${aggregate} to ${ftype} field \"${field}\" — ` +\n `summed percentages are meaningless (they can exceed 100%).`,\n hint:\n `Use aggregate \"avg\" for percentage/rate fields (or \"count\" of records). ` +\n `If a running total is genuinely intended, suppress with: ` +\n `suppressWarnings: ['${MEASURE_AGGREGATE_INCOHERENT}'] on the measure.`,\n });\n }\n }\n }\n\n const dashboards = asArray(stack.dashboards);\n for (let i = 0; i < dashboards.length; i++) {\n const dash = dashboards[i];\n const dashName = typeof dash.name === 'string' ? dash.name : `(dashboard ${i})`;\n const widgets = Array.isArray(dash.widgets) ? (dash.widgets as AnyRec[]) : [];\n // Dashboard-level filters (`dateRange` + `globalFilters`) are broadcast into\n // every widget's query (#2501) — resolved once here, checked per widget below.\n const dashFilterDefs = dashboardFilterDefs(dash);\n\n for (let j = 0; j < widgets.length; j++) {\n const w = widgets[j];\n const widgetId = typeof w.id === 'string' ? w.id : `(widget ${j})`;\n const where = `dashboard \"${dashName}\" › widget \"${widgetId}\"`;\n const path = `dashboards[${i}].widgets[${j}]`;\n const suppressed = (rule: string): boolean =>\n Array.isArray(w.suppressWarnings) && w.suppressWarnings.includes(rule);\n const push = (f: Omit<WidgetBindingFinding, 'where' | 'path'>): void => {\n if (f.severity === 'warning' && suppressed(f.rule)) return;\n findings.push({ ...f, where, path });\n };\n\n // ── (a0) legacy pre-ADR-0021 analytics shape ──\n // Steer authors (very often an AI) off the removed inline shape and onto\n // the semantic-layer `dataset`+`dimensions`+`values`. The renderer reads\n // ONLY the dataset path, so these keys are dead. Two severities:\n // • ERROR — the legacy keys are the widget's only (dead) data wiring\n // (no dataset / object / inline data): it renders nothing.\n // • warning — a data source is present, so the widget still renders and\n // the legacy keys are merely ignored noise (suppressible).\n const legacyUsed = LEGACY_ANALYTICS_KEYS.filter((k) => w[k] !== undefined);\n if (legacyUsed.length > 0) {\n const optionsData =\n typeof w.options === 'object' && w.options !== null &&\n (w.options as AnyRec).data !== undefined;\n const hasDataSource =\n w.dataset !== undefined || w.object !== undefined ||\n w.data !== undefined || optionsData;\n const keyList = legacyUsed.map((k) => `\\`${k}\\``).join(', ');\n const plural = legacyUsed.length > 1;\n const datasetHint =\n `Bind a semantic dataset and select fields BY NAME: ` +\n `\\`dataset: '<name>', dimensions: [...], values: [...]\\`. ` +\n `Dataset-bound widgets render through DatasetWidget (pivot rows/cols come from ` +\n `\\`dimensions\\`, cell values from \\`values\\`).`;\n if (!hasDataSource) {\n push({\n severity: 'error',\n rule: WIDGET_LEGACY_ANALYTICS_UNRENDERABLE,\n message:\n `sets legacy analytics key${plural ? 's' : ''} ${keyList} ` +\n `(removed by the ADR-0021 single-form cutover) and binds no data source ` +\n `(no \\`dataset\\`, \\`object\\`, or inline \\`data\\`) — it renders nothing.`,\n hint:\n `${datasetHint} The renderer ignores the legacy keys, so without a data ` +\n `source this widget has no data at all.`,\n });\n } else {\n push({\n severity: 'warning',\n rule: WIDGET_LEGACY_ANALYTICS_SHAPE,\n message:\n `sets legacy analytics key${plural ? 's' : ''} ${keyList} that the ADR-0021 ` +\n `single-form cutover removed — the dashboard renderer ignores ${plural ? 'them' : 'it'}.`,\n hint:\n `${datasetHint} These inline keys are a no-op. ` +\n `Suppress with suppressWarnings: ['${WIDGET_LEGACY_ANALYTICS_SHAPE}'] if intentional.`,\n });\n }\n }\n\n // ── (a) dataset reference resolves ──\n const dsName = typeof w.dataset === 'string' ? w.dataset : undefined;\n const dataset = dsName ? datasets.get(dsName) : undefined;\n if (dsName && !dataset) {\n push({\n severity: 'error',\n rule: WIDGET_DATASET_UNKNOWN,\n message: `dataset \"${dsName}\" does not resolve to a declared dataset.`,\n hint:\n `Declared datasets: ${list(datasets.keys())}.${suggest(dsName, datasets.keys())} ` +\n `Define the dataset with defineDataset() or fix the reference (ADR-0021).`,\n });\n }\n // A widget with NO `dataset` key at all. `DashboardWidgetSchema.dataset`\n // is REQUIRED, so the schema-parsed paths (`compile`, `validate`) reject\n // this before we run — but `lint`/`doctor` hand us raw, un-parsed config,\n // where it previously fell into the `continue` below and silently\n // bypassed EVERY binding and chart check (issue #3583). Report it rather\n // than skip: an unbound widget resolves no data and renders empty.\n if (!dsName) {\n push({\n severity: 'error',\n rule: WIDGET_DATASET_UNKNOWN,\n message:\n `binds no \\`dataset\\` — the ADR-0021 widget shape requires one, so this ` +\n `widget resolves no data and renders empty.`,\n hint:\n `Set \\`dataset: '<name>'\\` (plus \\`values\\`, and \\`dimensions\\` where the chart ` +\n `family needs them). Declared datasets: ${list(datasets.keys())}.`,\n });\n continue;\n }\n // A named-but-unresolvable dataset was already reported above; either way\n // there is nothing left to check names against.\n if (!dataset) continue;\n\n // ── (a1) dashboard filter fields exist on the widget's object (#3365) ──\n // Each dashboard-level filter is ANDed into this widget's analytics query\n // (#2501); a filter whose EFFECTIVE field (after `filterBindings`) is not a\n // column on the bound dataset object emits SQL like `WHERE close_date …`\n // against a table without that column and the widget crashes at query time.\n // Errored (not warned): a broken query, not advice. The opt-out is the\n // author's own `filterBindings: { <name>: false }`, so no suppression needed.\n if (dashFilterDefs.length > 0) {\n const datasetObject = typeof dataset.object === 'string' ? dataset.object : undefined;\n // Only judge when the bound object's fields are known in THIS stack; an\n // object from another installed package is unknowable here — skip rather\n // than false-positive (mirrors the measure-aggregate check above).\n const objectFields = datasetObject ? objectFieldTypes.get(datasetObject) : undefined;\n if (objectFields) {\n for (const def of dashFilterDefs) {\n const eff = effectiveFilterField(w, def);\n if (!eff) continue; // opted out / not targeted → filter never applies\n const field = eff.field;\n // A relationship path (`account.region`) is resolved by the query\n // engine, not a base column, so it can't be checked here — skip it.\n if (field.includes('.')) continue;\n if (objectFields.has(field) || SYSTEM_FIELDS.has(field)) continue;\n push({\n severity: 'error',\n rule: DASHBOARD_FILTER_FIELD_UNKNOWN,\n message: eff.explicit\n ? `binds dashboard filter \\`${def.name}\\` to field \\`${field}\\` ` +\n `(via filterBindings), but object \\`${datasetObject}\\` (dataset \"${dsName}\") ` +\n `has no field \\`${field}\\`.`\n : `inherits dashboard filter \\`${def.name}(${field})\\`, but object ` +\n `\\`${datasetObject}\\` (dataset \"${dsName}\") has no field \\`${field}\\`.`,\n hint: eff.explicit\n ? `Point filterBindings: { ${def.name}: '<field>' } at a field that exists on ` +\n `\\`${datasetObject}\\`, or opt out with filterBindings: { ${def.name}: false }.` +\n `${suggest(field, objectFields.keys())} Object fields: ${list(objectFields.keys())}.`\n : `Set filterBindings: { ${def.name}: false } on this widget to opt out, or ` +\n `re-target to an existing field with filterBindings: { ${def.name}: '<field>' }.` +\n `${suggest(field, objectFields.keys())} Object fields: ${list(objectFields.keys())}.`,\n });\n }\n }\n }\n\n const dimensionNames = new Set<string>();\n for (const d of asArray(dataset.dimensions)) {\n if (typeof d.name === 'string') dimensionNames.add(d.name);\n }\n const measures = new Map<string, AnyRec>();\n for (const m of asArray(dataset.measures)) {\n if (typeof m.name === 'string') measures.set(m.name, m);\n }\n\n // ── (b) every dimensions[] entry is a dataset dimension ──\n const dims = asStrings(w.dimensions);\n for (let k = 0; k < dims.length; k++) {\n if (dimensionNames.has(dims[k])) continue;\n push({\n severity: 'error',\n rule: WIDGET_DIMENSION_UNKNOWN,\n message:\n `dimensions[${k}] \"${dims[k]}\" is not a dimension of dataset ` +\n `\"${dsName}\" (declared dimensions: ${list(dimensionNames)}).`,\n hint:\n `Widgets select dataset dimensions BY NAME.${suggest(dims[k], dimensionNames)} ` +\n `Add the dimension to the dataset or fix the reference.`,\n });\n }\n\n // ── (c) every values[] entry is a dataset measure ──\n const values = asStrings(w.values);\n for (let k = 0; k < values.length; k++) {\n if (measures.has(values[k])) continue;\n push({\n severity: 'error',\n rule: WIDGET_MEASURE_UNKNOWN,\n message:\n `values[${k}] \"${values[k]}\" is not a measure of dataset ` +\n `\"${dsName}\" (declared measures: ${list(measures.keys())}).`,\n hint:\n `Widgets select dataset measures BY NAME, not by base column.` +\n `${suggest(values[k], measures.keys())} ` +\n `Add the measure to the dataset or fix the reference.`,\n });\n }\n\n // ── (d) chartConfig bindings resolve against the widget's selection ──\n const chartConfig = (w.chartConfig && typeof w.chartConfig === 'object')\n ? (w.chartConfig as AnyRec)\n : undefined;\n const isChartType = typeof w.type === 'string' && CHART_TYPES.has(w.type);\n\n if (chartConfig) {\n // The query result carries the widget's selected dimensions and\n // measures; resolve every chartConfig field against that shape.\n const selectedValues = new Set(values.filter((v) => measures.has(v)));\n\n const xAxis = (chartConfig.xAxis && typeof chartConfig.xAxis === 'object')\n ? (chartConfig.xAxis as AnyRec)\n : undefined;\n // A field naming an entry of the widget's own (already-validated)\n // selection is not re-reported here — rules (b)/(c) own that error.\n if (xAxis && typeof xAxis.field === 'string'\n && !dimensionNames.has(xAxis.field) && !dims.includes(xAxis.field)) {\n push({\n severity: 'error',\n rule: CHART_FIELD_UNKNOWN,\n message:\n `chartConfig.xAxis.field \"${xAxis.field}\" does not resolve to a ` +\n `dimension of dataset \"${dsName}\" (declared dimensions: ${list(dimensionNames)}).`,\n hint: `Point xAxis.field at a dataset dimension name.${suggest(xAxis.field, dimensionNames)}`,\n });\n }\n\n const measureField = (label: string, field: string): void => {\n if (values.includes(field)) return; // resolvable, or already errored via rule (c)\n const declaredButUnselected = measures.has(field);\n push({\n severity: 'error',\n rule: CHART_FIELD_UNKNOWN,\n message: declaredButUnselected\n ? `chartConfig.${label} \"${field}\" is a measure of dataset \"${dsName}\" ` +\n `but is not selected in the widget's values (${list(values)}), so the ` +\n `query result will not contain it.`\n : `chartConfig.${label} \"${field}\" does not resolve to a measure of ` +\n `dataset \"${dsName}\" (declared measures: ${list(measures.keys())}).`,\n hint: declaredButUnselected\n ? `Add \"${field}\" to the widget's values, or bind the chart to a selected measure.`\n : `Post-cutover data is keyed by the dataset's measure NAME, not the ` +\n `base column.${suggest(field, selectedValues.size > 0 ? selectedValues : measures.keys())}`,\n });\n };\n\n const yAxes = Array.isArray(chartConfig.yAxis) ? (chartConfig.yAxis as AnyRec[]) : [];\n for (let k = 0; k < yAxes.length; k++) {\n const field = yAxes[k]?.field;\n if (typeof field === 'string') measureField(`yAxis[${k}].field`, field);\n }\n const series = Array.isArray(chartConfig.series) ? (chartConfig.series as AnyRec[]) : [];\n for (let k = 0; k < series.length; k++) {\n const name = series[k]?.name;\n if (typeof name === 'string') measureField(`series[${k}].name`, name);\n }\n } else if (isChartType) {\n push({\n severity: 'warning',\n rule: CHART_CONFIG_MISSING,\n message:\n `chart-type widget ('${w.type}') has no chartConfig — the renderer ` +\n `cannot determine which measure to plot, so the series renders empty.`,\n hint:\n `Add chartConfig with xAxis.field set to a dimension (${list(dims)}) and ` +\n `yAxis[].field set to a measure name (${list(values)}). If the default ` +\n `rendering is intentional, suppress with: suppressWarnings: ['${CHART_CONFIG_MISSING}']`,\n });\n }\n\n // ── (e) table/pivot bound to a count-only, dimensionless selection ──\n if (w.type !== 'table' && w.type !== 'pivot') continue;\n // Grouped by at least one dimension → genuinely aggregated rows.\n if (dims.length > 0) continue;\n if (values.length === 0) continue;\n const resolved = values.map((v) => measures.get(v));\n // An unresolvable measure name already errored above — don't guess here.\n if (resolved.some((m) => !m)) continue;\n\n // Derived measures combine other measures; treat them as non-count even\n // when their (ignored) `aggregate` says otherwise.\n const countOnly = resolved.every((m) => m!.aggregate === 'count' && !m!.derived);\n if (!countOnly) continue;\n\n push({\n severity: 'warning',\n rule: TABLE_COUNT_ONLY,\n message:\n `a '${w.type}' widget bound to dataset \"${dsName}\" selects only count ` +\n `measure(s) (${values.join(', ')}) and no dimensions, so it renders a ` +\n `single summary row — not a per-record list.`,\n hint:\n `A flat record listing is not an analytics dataset. Model it as an ` +\n `object-bound ListView (ADR-0017) surfaced through app navigation, and ` +\n `use a 'metric' widget here if you only need the count. If a single-row ` +\n `table is intentional, add an explicit dimension or suppress with: ` +\n `suppressWarnings: ['${TABLE_COUNT_ONLY}']`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The ONE answer to \"which columns does the registry inject on (almost) every\n * object without them appearing in authored `fields`?\" (#4330).\n *\n * Every field-resolving rule in this package needs that answer: a reference to\n * `created_at` or `owner_id` is authored against a real, addressable column\n * even though no object declares it, so flagging it would be the false finding\n * that makes authors stop trusting the linter (ADR-0072 D1). Before this\n * module, five rules each carried their own hand-copied list and had already\n * drifted from one another — the exact shape #3786 removed from the\n * audit-provenance family, rebuilt one package over.\n *\n * DERIVED from the spec's two declarations, so it cannot drift from them:\n *\n * - {@link FIELD_GROUP_SYSTEM_FIELDS} (`@objectstack/spec/data`) — audit\n * provenance plus `organization_id` / `tenant_id` / `is_deleted` /\n * `deleted_at`;\n * - {@link SystemFieldName} (`@objectstack/spec/system`) — the protocol-level\n * ids: `id`, `owner_id`, `user_id` and the timestamp/tenant columns.\n *\n * The union is deliberately generous, because the cost asymmetry is the same\n * in every consumer: over-inclusion costs at worst a missed finding on a\n * `systemFields: false` object (rare); under-inclusion costs a false one.\n *\n * What does NOT belong here: names that are ordinary AUTHORED fields on most\n * objects (`name`, `owner`, `record_type`) or legacy physical spellings\n * (`_id`, `space`). A rule that deliberately exempts those keeps them in a\n * rule-local extension next to its reason — adding them here would silently\n * stop every other rule from catching a reference to a field the object\n * genuinely does not have.\n */\n\nimport { FIELD_GROUP_SYSTEM_FIELDS } from '@objectstack/spec/data';\nimport { SystemFieldName } from '@objectstack/spec/system';\n\n/**\n * Registry-injected columns addressable at runtime without being authored in\n * `fields` — the union of the spec's two system-field declarations.\n */\nexport const SYSTEM_FIELDS: ReadonlySet<string> = new Set<string>([\n ...FIELD_GROUP_SYSTEM_FIELDS,\n ...Object.values(SystemFieldName),\n]);\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time expression validation (ADR-0032 §Decision 1a + 1b).\n *\n * Runs at `objectstack compile`, where the whole normalized stack is in hand —\n * so flow conditions can be checked against the *resolved* object schema\n * (field existence) in addition to CEL syntax. Uses the one shared validator\n * from `@objectstack/formula`, so the verdict matches `registerFlow` and the\n * agent `validate_expression` tool exactly.\n *\n * Scope: flow predicates (start/decision `config.condition` + edge `condition`),\n * every **descriptor-declared** expression slot named by\n * `FLOW_NODE_EXPRESSION_PATHS` (#4027 — e.g. a screen field's `visibleWhen`),\n * object validation-rule / formula predicates, and UI action `visible` /\n * `disabled` predicates. Each error is located (flow/object/action +\n * node/edge/field) with a corrective message.\n */\n\nimport { validateExpression } from '@objectstack/formula';\nimport { collectFlowGraphs, resolveFlowNodeExpressions } from '@objectstack/spec/automation';\nimport type { FlowNodeParsed } from '@objectstack/spec/automation';\n\nexport interface ExprIssue {\n where: string;\n message: string;\n source: string;\n /**\n * `error` fails the build (e.g. a bare ref in a record-scoped formula). `warning`\n * is advisory and never fails it (e.g. a possible field typo in a flattened flow\n * condition, which might be a flow variable). Absent ⇒ treat as `error`.\n */\n severity?: 'error' | 'warning';\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an `objects` collection (array or name-keyed map) to an array. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** object name → set of its field names, for schema-aware field checks. */\nfunction buildFieldIndex(objects: AnyRec[]): Map<string, string[]> {\n const idx = new Map<string, string[]>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fields = obj.fields;\n let names: string[] = [];\n if (Array.isArray(fields)) names = fields.map(f => (f as AnyRec).name).filter((n): n is string => typeof n === 'string');\n else if (fields && typeof fields === 'object') names = Object.keys(fields as AnyRec);\n idx.set(name, names);\n }\n return idx;\n}\n\n/**\n * object name → (field name → field type), for the #1928 tier-4 type-soundness\n * check. Handles both `fields` shapes (array of `{name, type}` and name-keyed\n * map). Fields with a non-string `type` are simply omitted (treated as `dyn`).\n */\nfunction buildFieldTypeIndex(objects: AnyRec[]): Map<string, Record<string, string>> {\n const idx = new Map<string, Record<string, string>>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fields = obj.fields;\n const types: Record<string, string> = {};\n if (Array.isArray(fields)) {\n for (const f of fields as AnyRec[]) {\n const fn = (f as AnyRec)?.name;\n const ft = (f as AnyRec)?.type;\n if (typeof fn === 'string' && typeof ft === 'string') types[fn] = ft;\n }\n } else if (fields && typeof fields === 'object') {\n for (const [fn, def] of Object.entries(fields as AnyRec)) {\n const ft = (def as AnyRec)?.type;\n if (typeof ft === 'string') types[fn] = ft;\n }\n }\n idx.set(name, types);\n }\n return idx;\n}\n\n/**\n * Validate every predicate in the stack. Returns the list of issues (empty =\n * clean). Caller decides how to surface / whether to fail the build.\n */\nexport function validateStackExpressions(stack: AnyRec): ExprIssue[] {\n const issues: ExprIssue[] = [];\n const objects = asArray(stack.objects);\n const fieldIndex = buildFieldIndex(objects);\n const fieldTypeIndex = buildFieldTypeIndex(objects);\n\n const check = (\n where: string,\n raw: unknown,\n objectName?: string,\n scope: 'record' | 'flattened' = 'flattened',\n ): void => {\n if (raw == null) return;\n const fields = objectName ? fieldIndex.get(objectName) : undefined;\n // Field types feed the #1928 tier-4 soundness warning; only consulted for\n // `record`-scoped sites, so it is harmless to pass for flattened ones too.\n const fieldTypes = objectName ? fieldTypeIndex.get(objectName) : undefined;\n const res = validateExpression('predicate', raw as string | { dialect?: string; source?: string },\n objectName ? { objectName, fields, fieldTypes, scope } : { scope });\n for (const e of res.errors) issues.push({ where, message: e.message, source: e.source, severity: 'error' });\n for (const w of res.warnings) issues.push({ where, message: w.message, source: w.source, severity: 'warning' });\n };\n\n /**\n * A declared bare-CEL slot (#4027). No object schema is passed: these slots\n * bind the *screen's own* collected values, not the trigger record's fields, so\n * a field-existence pass would report every field name as unknown.\n */\n const checkDeclaredPredicate = (where: string, raw: unknown): void => {\n if (raw == null) return;\n const res = validateExpression('predicate', raw as string | { dialect?: string; source?: string });\n for (const e of res.errors) issues.push({ where, message: e.message, source: e.source, severity: 'error' });\n for (const w of res.warnings) issues.push({ where, message: w.message, source: w.source, severity: 'warning' });\n };\n\n // ── Flows ──────────────────────────────────────────────────────────\n for (const flow of asArray(stack.flows)) {\n const flowName = typeof flow.name === 'string' ? flow.name : '(unnamed flow)';\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n // The record-change target object — `record.*` refs resolve against it.\n const startNode = nodes.find(n => n.type === 'start');\n const startCfg = (startNode?.config ?? {}) as AnyRec;\n const objectName = typeof startCfg.objectName === 'string' ? startCfg.objectName : undefined;\n\n // #4347 — every graph in the flow, not just `flow.nodes`/`flow.edges`. An\n // ADR-0031 container keeps a whole sub-graph in its `config`, so the\n // top-level walk validated PART of the flow while reporting on all of it: a\n // predicate written in the wrong dialect inside a `loop` body passed\n // `objectstack validate` and shipped. This is the author-time half of the\n // same traversal the engine's registration pass now does; `scope` names the\n // region so the located message still points at one edge.\n for (const graph of collectFlowGraphs(flow as { nodes?: FlowNodeParsed[] })) {\n const at = graph.scope ? `flow '${flowName}' · ${graph.scope}` : `flow '${flowName}'`;\n for (const node of graph.nodes as unknown as AnyRec[]) {\n const cfg = (node.config ?? {}) as AnyRec;\n check(`${at} · node '${node.id}' (${node.type}) condition`, cfg.condition, objectName);\n\n // Descriptor-declared expression slots (#4027). Before this, the traversal\n // hardcoded `condition` and assumed every other node string was a `{var}`\n // template — so `screen.fields[].visibleWhen`, declared bare CEL since\n // #3304, was validated by nobody and #3528 shipped a template-dialect\n // predicate through compile, validate and run time in silence.\n // Only `predicate` slots are checkable: `flow-template` slots take the\n // single-brace `{var}` dialect `interpolate()` implements, which no\n // validator covers (the `template` role enforces ADR-0032 §3's\n // double-brace text template and would reject every correct\n // `loop.collection`). The ledger records them regardless, so the\n // reconciliation ratchet still sees the marker.\n const nodeType = typeof node.type === 'string' ? node.type : '';\n for (const found of resolveFlowNodeExpressions(nodeType, cfg)) {\n if (found.entry.role !== 'predicate') continue;\n checkDeclaredPredicate(\n `${at} · node '${node.id}' (${nodeType}) ${found.entry.label} at config.${found.path}`,\n found.value,\n );\n }\n // #1870 — a `script` node must declare a callable target (`actionType` or\n // `function`). A node with neither is a silent no-op that otherwise passes\n // build. (Function *existence* isn't checkable here — functions are code,\n // not serialized into the artifact — so this is a structural check; the\n // runtime verifies the named function is actually registered.)\n if (node.type === 'script') {\n // `function` is canonical; a pre-parse source may still carry the\n // `functionName` alias during the protocol-17 window, until the\n // 'flow-node-script-config-aliases' conversion (#3796) canonicalizes it.\n const fn =\n (typeof cfg.function === 'string' ? cfg.function.trim() : '') ||\n (typeof cfg.functionName === 'string' ? cfg.functionName.trim() : '');\n const action = typeof cfg.actionType === 'string' ? cfg.actionType.trim() : '';\n // Inline `config.script` (a JS body) is also a declared form — the\n // built-in runtime doesn't execute it (warned at run time), but the node\n // is not the empty no-op this check targets, so don't flag it.\n const inline = typeof cfg.script === 'string' ? cfg.script.trim() : '';\n if (!fn && !action && !inline) {\n issues.push({\n where: `${at} · node '${node.id}' (script) callable`,\n message:\n `script node declares neither \\`actionType\\` nor \\`function\\` — it would do nothing at runtime. ` +\n `Name a built-in action (e.g. \\`actionType: 'email'\\`) or a registered function ` +\n `(\\`function: 'my_fn'\\`, registered via \\`defineStack({ functions })\\`).`,\n source: JSON.stringify({ id: node.id, type: node.type, config: cfg }),\n });\n } else if (action === 'invoke_function' && !fn) {\n // `actionType: 'invoke_function'` is a marker that names no callable on\n // its own — the function name must be in `function`/`functionName`.\n issues.push({\n where: `${at} · node '${node.id}' (script) callable`,\n message:\n `script node uses \\`actionType: 'invoke_function'\\` but no \\`function\\` (or \\`functionName\\`) — ` +\n `it names no callable. Set \\`function: 'my_fn'\\` and register it via \\`defineStack({ functions })\\`.`,\n source: JSON.stringify({ id: node.id, type: node.type, config: cfg }),\n });\n }\n }\n }\n for (const edge of graph.edges as unknown as AnyRec[]) {\n check(`${at} · edge '${edge.id}' (${edge.source}→${edge.target}) condition`, edge.condition, objectName);\n }\n }\n }\n\n // ── Object validation-rule + formula predicates ────────────────────\n for (const obj of objects) {\n const objectName = typeof obj.name === 'string' ? obj.name : undefined;\n const validations = obj.validations ?? obj.validationRules;\n for (const rule of asArray(validations)) {\n const where = `object '${objectName}' · validation '${(rule.name as string) ?? '?'}'`;\n // Common predicate keys across rule shapes. Validation predicates are\n // `record`-scoped — no field flattening — so bare refs are flagged (#1928).\n check(where, rule.expression ?? rule.predicate ?? rule.condition ?? rule.formula, objectName, 'record');\n // `conditional` rules carry a nested `when` predicate (record-scoped).\n check(`${where} when`, (rule as AnyRec).when, objectName, 'record');\n }\n // Field-level formulas (computed fields) reference the same object.\n const fields = obj.fields;\n const fieldList = Array.isArray(fields)\n ? (fields as AnyRec[])\n : (fields && typeof fields === 'object' ? Object.values(fields as AnyRec) as AnyRec[] : []);\n\n // (ADR-0062 D7's `field.columnName`-on-external-objects rejection was removed\n // with `field.columnName` itself in #2377: the field no longer exists, so there\n // is no dual-source ambiguity to guard — external column mapping is `external.columnMap`.)\n\n for (const f of fieldList) {\n // Field-level conditional rules are server-enforced (rule-validator) and\n // record-scoped — a bare ref silently fails the rule (required/readonly\n // not enforced = data-integrity hole). #1928 class, same as actions.\n if (f && typeof f === 'object') {\n const fname = (f.name as string) ?? '?';\n for (const key of ['requiredWhen', 'readonlyWhen', 'conditionalRequired', 'visibleWhen'] as const) {\n check(`object '${objectName}' · field '${fname}' ${key}`, (f as AnyRec)[key], objectName, 'record');\n }\n }\n if (f && typeof f === 'object' && f.formula) {\n // formulas are `value` role (any return type), still CEL. They are\n // `record`-scoped — `record.<field>`, never bare — so flag bare refs (#1928).\n const res = validateExpression('value', f.formula as string | { dialect?: string; source?: string },\n objectName ? { objectName, fields: fieldIndex.get(objectName), fieldTypes: fieldTypeIndex.get(objectName), scope: 'record' } : { scope: 'record' });\n const fieldWhere = `object '${objectName}' · field '${(f.name as string) ?? '?'}' formula`;\n for (const e of res.errors) issues.push({ where: fieldWhere, message: e.message, source: e.source, severity: 'error' });\n for (const w of res.warnings) issues.push({ where: fieldWhere, message: w.message, source: w.source, severity: 'warning' });\n }\n }\n }\n\n // ── Action `visible` / `disabled` predicates ───────────────────────\n // Record-scoped, same as validation rules: a record-header / row action's\n // `visible` is evaluated by ActionEngine against `{ record, recordId,\n // objectName, user, … }` with fail-closed semantics, so a BARE field ref\n // (`done` instead of `record.done`) throws and the action is silently hidden\n // on every record (the trap behind the #2183 \"Mark Done never hides\" hunt).\n // Flagging it here turns that into a build error with a corrective message.\n // `disabled` may be a boolean (skip) or a predicate (check).\n const seenActions = new Set<string>();\n const checkAction = (where: string, action: AnyRec, objectName?: string): void => {\n const obj = objectName\n ?? (typeof action.objectName === 'string' ? action.objectName : undefined)\n ?? (typeof action.object === 'string' ? action.object : undefined);\n const name = typeof action.name === 'string' ? action.name : '?';\n const key = `${obj ?? ''}:${name}`;\n if (seenActions.has(key)) return; // de-dup (actions are merged onto objects AND kept top-level)\n seenActions.add(key);\n check(`${where} · action '${name}' visible`, action.visible, obj, 'record');\n if (typeof action.disabled !== 'boolean') {\n check(`${where} · action '${name}' disabled`, action.disabled, obj, 'record');\n }\n };\n for (const action of asArray(stack.actions)) {\n checkAction('stack', action);\n }\n for (const obj of objects) {\n const objectName = typeof obj.name === 'string' ? obj.name : undefined;\n for (const action of asArray(obj.actions)) {\n checkAction(`object '${objectName}'`, action, objectName);\n }\n }\n\n // ── Sharing-rule predicates (security-critical, record-scoped) ─────\n // A criteria sharing rule's `condition` decides which rows a principal sees.\n // It is evaluated against the record, so a bare ref silently changes access.\n for (const rule of asArray(stack.sharingRules)) {\n const ruleObj = typeof rule.object === 'string' ? rule.object : undefined;\n const where = `sharingRule '${(rule.name as string) ?? '?'}'${ruleObj ? ` (${ruleObj})` : ''} condition`;\n check(where, rule.condition ?? rule.criteria ?? rule.predicate, ruleObj, 'record');\n }\n\n // ── Hook `condition` predicates (record-scoped gate) ───────────────\n // A lifecycle hook's `condition` skips the handler when false; it is\n // evaluated against the record, so a bare ref silently makes the hook\n // run on every record (or never) instead of the intended subset.\n for (const hook of asArray(stack.hooks)) {\n const hookName = (hook.name as string) ?? '?';\n if (typeof hook.object === 'string') {\n check(`hook '${hookName}' (${hook.object}) condition`, hook.condition, hook.object, 'record');\n continue;\n }\n\n // A hook may target MANY objects (`object: ['a','b']`). Previously any\n // non-string target dropped to `undefined`, so the condition got NO\n // field-awareness at all — a hook filtering on a field that exists on none\n // of its targets passed clean (issue #3583). The hook body runs against\n // each target in turn, so a ref missing from ANY of them silently\n // misbehaves there; validate per target and de-duplicate the\n // object-independent diagnostics (syntax/shape) that every pass repeats.\n const targets = Array.isArray(hook.object)\n ? (hook.object as unknown[]).filter((o): o is string => typeof o === 'string' && o !== '*')\n : [];\n if (targets.length === 0) {\n // `'*'` (or an unusable shape) — no single field set to judge against;\n // syntax/shape is still validated.\n check(`hook '${hookName}' condition`, hook.condition, undefined, 'record');\n continue;\n }\n\n const before = issues.length;\n const seen = new Set<string>();\n const kept: ExprIssue[] = [];\n for (const target of targets) {\n const mark = issues.length;\n check(`hook '${hookName}' (${target}) condition`, hook.condition, target, 'record');\n for (let i = mark; i < issues.length; i++) {\n const issue = issues[i];\n const key = `${issue.message}\\u0000${issue.source ?? ''}`;\n // Keep the first occurrence of each distinct diagnostic. A field-unknown\n // finding differs per target (it names the object), so each survives;\n // a syntax error is identical across targets and collapses to one.\n if (!seen.has(key)) {\n seen.add(key);\n kept.push(issue);\n }\n }\n }\n issues.length = before;\n issues.push(...kept);\n }\n\n return issues;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for ADR-0047 list-view navigation modes.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring. It catches the \"wrong context\" authoring mistake\n// the type system alone cannot surface at author time on an object list view\n// (\"views\" mode — where the ViewTabBar owns the tab-bar role):\n// - `quickFilters` — never valid on an object list view;\n// - `userFilters` with `element: 'tabs'` (or carrying `tabs`) — the tab-bar\n// preset style is page-only; it would collide with the ViewTabBar.\n// A `dropdown` (value-chip) `userFilters` IS allowed on object views since the\n// ADR-0047 amendment (framework #2679 / objectui #2338) and is NOT flagged.\n//\n// Runs PRE-parse (on the normalizeStackInput output, before the\n// ObjectStackDefinition parse): the object-list schema (ObjectListViewSchema)\n// narrows `userFilters` to ObjectUserFiltersSchema (dropdown/toggle only), so a\n// post-parse stack has already had a `tabs` user-filter stripped and this rule\n// would never see it. The layering is deliberate — tsc rejects it at author\n// time, the schema strips it at runtime (no throw, back-compat), and this rule\n// reports it at `os validate` with a fix hint. See objectui #2338 and ADR-0047.\n\nexport type ListViewModeSeverity = 'error' | 'warning';\n\nexport interface ListViewModeFinding {\n severity: ListViewModeSeverity;\n rule: string;\n /** Human-readable location, e.g. `object \"task\" › listViews.my_pending`. */\n where: string;\n /** Config path, e.g. `objects[0].listViews.my_pending.userFilters`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const LIST_VIEW_FILTERS_IN_VIEWS_MODE = 'list-view-filters-in-views-mode';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\n/** Emit a finding for each wrong-context filter control on a single list-view def. */\nfunction scanView(\n view: unknown,\n where: string,\n path: string,\n out: ListViewModeFinding[],\n): void {\n if (!view || typeof view !== 'object') return;\n const rec = view as AnyRec;\n\n // `quickFilters` is never valid on an object list view.\n if (rec.quickFilters != null) {\n out.push({\n severity: 'error',\n rule: LIST_VIEW_FILTERS_IN_VIEWS_MODE,\n where,\n path: `${path}.quickFilters`,\n message:\n '`quickFilters` is a page filters-mode control and is ignored on an object ' +\n 'list view (\"views\" mode) — the ViewTabBar owns nav here.',\n hint:\n 'Move `quickFilters` to a page list (InterfaceListPage, \"filters\" mode), or ' +\n 'remove it. See ADR-0047.',\n });\n }\n\n // `userFilters` is allowed on object views ONLY as `dropdown` (value chips).\n // The `tabs` preset style — or any `userFilters` carrying `tabs` — collides\n // with the ViewTabBar and stays page-only.\n const uf = rec.userFilters;\n if (uf && typeof uf === 'object') {\n const ufRec = uf as AnyRec;\n if (ufRec.element === 'tabs' || ufRec.tabs != null) {\n out.push({\n severity: 'error',\n rule: LIST_VIEW_FILTERS_IN_VIEWS_MODE,\n where,\n path: `${path}.userFilters`,\n message:\n '`userFilters` with `element: \"tabs\"` is page-only and is ignored on an ' +\n 'object list view (\"views\" mode) — it would collide with the ViewTabBar.',\n hint:\n 'Use `listViews` for named presets on an object (each becomes a segmented ' +\n 'tab), switch to `element: \"dropdown\"` for value chips, or move the `tabs` ' +\n 'filter to a page list (InterfaceListPage, \"filters\" mode). See ADR-0047.',\n });\n }\n }\n}\n\n/** Scan a `listViews` record (name → list-view def). */\nfunction scanListViews(\n listViews: unknown,\n wherePrefix: string,\n pathPrefix: string,\n out: ListViewModeFinding[],\n): void {\n if (!listViews || typeof listViews !== 'object') return;\n for (const [name, view] of Object.entries(listViews as AnyRec)) {\n scanView(\n view,\n `${wherePrefix} › listViews.${name}`,\n `${pathPrefix}.listViews.${name}`,\n out,\n );\n }\n}\n\n/**\n * Flag ADR-0047 \"views\" mode violations on an object's built-in named views or a\n * `defineView` default `list` / named `listViews`: `quickFilters`, or a `tabs`\n * `userFilters`. A `dropdown` `userFilters` is allowed and not flagged. Returns\n * the list of findings (empty = clean). Caller decides how to surface / whether\n * to fail the build.\n *\n * Feed the PRE-parse stack (normalizeStackInput output) — see file header.\n */\nexport function validateListViewMode(stack: AnyRec): ListViewModeFinding[] {\n const out: ListViewModeFinding[] = [];\n\n // Object built-in named views (object.zod.ts `listViews`).\n asArray(stack.objects).forEach((obj, i) => {\n const label = typeof obj.name === 'string' ? `object \"${obj.name}\"` : `objects[${i}]`;\n scanListViews(obj.listViews, label, `objects[${i}]`, out);\n });\n\n // `defineView` aggregates (stack `views`: default `list` + named `listViews`).\n asArray(stack.views).forEach((view, i) => {\n const named =\n typeof view.objectName === 'string'\n ? view.objectName\n : typeof view.name === 'string'\n ? view.name\n : undefined;\n const label = named ? `view \"${named}\"` : `views[${i}]`;\n scanView(view.list, `${label} › list`, `views[${i}].list`, out);\n scanListViews(view.listViews, label, `views[${i}]`, out);\n });\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for auto-launched flow trigger wiring (2026-07-17\n// third-party eval: a record-change flow that silently never fires).\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring. It catches the two authoring mistakes that produce\n// a flow which LOOKS armed but never launches — with zero runtime output:\n//\n// 1. `objectName` mismatch — the start node targets an object name that is\n// not defined in this stack. The runtime binds an ObjectQL hook filtered\n// to that exact name; if nobody writes it, the flow never fires. Names\n// match exactly (`eval_app_candidate`, not `candidate`). Objects owned by\n// other packages (`sys_*`, dependency packages) are legitimate targets,\n// so this is a warning with the cross-package caveat, not an error.\n//\n// 2. `status: 'draft'` on an auto-triggered flow — the schema default when\n// no status is authored (defineFlow parses at definition time, so by the\n// time this rule runs an unauthored status is indistinguishable from an\n// explicit 'draft'). Either way the intent is ambiguous: the engine still\n// binds and fires draft flows (only `obsolete`/`invalid` disable), which\n// surprises authors in both directions. Declare `'active'` to arm\n// deliberately or `'obsolete'` to disable. Only auto-triggered flows are\n// flagged (manual/screen flows have no arming semantics to be unclear\n// about).\n\nexport type FlowTriggerReadinessSeverity = 'error' | 'warning';\n\nexport interface FlowTriggerReadinessFinding {\n severity: FlowTriggerReadinessSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"notify_on_done\" › start node`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[0].config.objectName`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const FLOW_TRIGGER_UNKNOWN_OBJECT = 'flow-trigger-unknown-object';\nexport const FLOW_DRAFT_STATUS_AMBIGUOUS = 'flow-draft-status-ambiguous';\nexport const FLOW_TRIGGER_UNKNOWN_EVENT = 'flow-trigger-unknown-event';\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * The record-change trigger fires only for a `triggerType` matching this exact\n * grammar — the same set its `triggerTypeToHookEvents` maps to ObjectQL hooks.\n * `insert` is a synonym for `create`; `write` is the create-OR-update union\n * (#3427). Any OTHER `record-`-prefixed token — a typo (`record-after-updated`),\n * a phase-less bare noun (`record-change`), or a bad phase (`record-during-update`)\n * — binds to the trigger but maps to NO hook and never fires. Kept in sync with\n * that trigger (one small, stable contract).\n */\nconst VALID_RECORD_TRIGGER = /^record-(?:before|after)-(?:create|insert|update|delete|write)$/;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\n/** The start node of a flow definition, if any. */\nfunction startNodeOf(flow: AnyRec): { node: AnyRec; index: number } | undefined {\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const index = nodes.findIndex((n) => n?.type === 'start');\n return index >= 0 ? { node: nodes[index], index } : undefined;\n}\n\n/**\n * Validate auto-launched flow trigger wiring against the stack definition.\n * Pure and dependency-free; safe on pre- or post-parse stacks.\n */\nexport function validateFlowTriggerReadiness(stack: AnyRec): FlowTriggerReadinessFinding[] {\n const findings: FlowTriggerReadinessFinding[] = [];\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n const objectNames = new Set(\n asArray(stack.objects)\n .map((o) => (typeof o.name === 'string' ? o.name : undefined))\n .filter((n): n is string => !!n),\n );\n\n flows.forEach((flow, flowIndex) => {\n const flowName = typeof flow.name === 'string' ? flow.name : `#${flowIndex}`;\n const start = startNodeOf(flow);\n const config = (start?.node.config ?? {}) as AnyRec;\n const triggerType = typeof config.triggerType === 'string' ? config.triggerType : undefined;\n const isRecordTriggered = !!triggerType && triggerType.startsWith('record-');\n // Array-form triggerType (e.g. ['record-after-create', 'record-after-delete'])\n // is NOT supported — multi-event unions are deferred (#3457). It needs its own\n // detection because a non-string triggerType folds to `undefined` above, so the\n // runtime misclassifies the flow as manual and it never fires with zero output\n // (#3481). Any record-* element is enough to recognize the (unsupported) intent.\n const isArrayRecordTriggered =\n Array.isArray(config.triggerType) &&\n (config.triggerType as unknown[]).some((t) => typeof t === 'string' && t.startsWith('record-'));\n const isTimeRelative = config.timeRelative != null && typeof config.timeRelative === 'object';\n const isAutoTriggered =\n isRecordTriggered || triggerType === 'api' || config.schedule != null ||\n isTimeRelative || flow.type === 'schedule' || flow.type === 'api';\n\n // 1. Record-triggered flow targeting an object this stack does not define.\n if (isRecordTriggered && start) {\n const objectName = typeof config.objectName === 'string' ? config.objectName : undefined;\n if (objectName && !objectNames.has(objectName) && !objectName.startsWith('sys_')) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_OBJECT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.objectName`,\n message:\n `targets object '${objectName}', which this stack does not define — if the name is wrong, ` +\n `the flow will never fire (and the runtime stays silent about it).`,\n hint:\n `Object names match exactly. Check config.objectName against the object's registered name ` +\n `(e.g. 'app_candidate', not 'candidate'). If the object comes from another installed package, ` +\n `this warning can be ignored.`,\n });\n }\n }\n\n // 1b. Time-relative flow sweeping an object this stack does not define. Like\n // the record-change case, a wrong object name makes the sweep match\n // nothing forever with no runtime output.\n if (isTimeRelative && start) {\n const tr = config.timeRelative as AnyRec;\n const objectName = typeof tr.object === 'string' ? tr.object : undefined;\n if (objectName && !objectNames.has(objectName) && !objectName.startsWith('sys_')) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_OBJECT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.timeRelative.object`,\n message:\n `sweeps object '${objectName}', which this stack does not define — if the name is wrong, ` +\n `the sweep will match nothing (and the runtime stays quiet about it).`,\n hint:\n `Object names match exactly. Check config.timeRelative.object against the object's registered name. ` +\n `If the object comes from another installed package, this warning can be ignored.`,\n });\n }\n }\n\n // 1c. A `record-`-prefixed triggerType the trigger cannot map to any hook —\n // a typo (`record-after-updated`), a phase-less bare noun (`record-change`,\n // which the Studio picker once offered as \"Record changed (any)\"), or a bad\n // phase (`record-during-update`). The engine routes any `record-` token to\n // the record-change trigger, which then binds to NO hook and never fires\n // (only a runtime warn). Surface the never-fire defect at authoring time.\n if (start && isRecordTriggered && !VALID_RECORD_TRIGGER.test((triggerType ?? '').trim())) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_EVENT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.triggerType`,\n message:\n `triggerType '${triggerType}' is not a recognized record trigger — the flow binds to the ` +\n `record-change trigger but never fires (the runtime stays silent about it).`,\n hint:\n `Use record-{before,after}-{create,update,delete,write}. 'write' fires on create OR update in one ` +\n `flow (#3427); create/insert are synonyms. There is no \"any change\" token — pick the specific event(s).`,\n });\n }\n\n // 1d. Array-form triggerType — an unsupported multi-event shape (#3457). The\n // runtime folds a non-string triggerType to \"no trigger\" and treats the\n // flow as manual, so it binds to nothing and never fires, with zero output\n // at any layer (#3481). Surface it at authoring time like the unmappable\n // single tokens above (same rule id — both are \"this token never fires\").\n if (start && isArrayRecordTriggered) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_EVENT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.triggerType`,\n message:\n `triggerType is an array (${JSON.stringify(config.triggerType)}), which is not supported — a start ` +\n `node takes a single trigger event, so the flow binds to nothing and never fires (the runtime stays silent about it).`,\n hint:\n `Use one triggerType string. For \"created or updated\" use record-after-write (one flow, both events, #3427). ` +\n `For any other combination, author one flow per event — multi-event arrays are deferred (#3457).`,\n });\n }\n\n // 2. Auto-triggered flow whose status is 'draft' — authored or defaulted\n // (defineFlow parses at definition time, so the two are the same here).\n if (isAutoTriggered && (flow.status == null || flow.status === 'draft')) {\n findings.push({\n severity: 'warning',\n rule: FLOW_DRAFT_STATUS_AMBIGUOUS,\n where: `flow \"${flowName}\"`,\n path: `flows[${flowIndex}].status`,\n message:\n `has status 'draft' (the default when none is authored). Draft flows DO still fire their ` +\n `triggers (only 'obsolete'/'invalid' disable), so the intent is ambiguous.`,\n hint: `Declare status: 'active' to arm it deliberately, or status: 'obsolete' to disable it.`,\n });\n }\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared flow-node traversal for the rules that inspect `FlowNode.config`\n * (issue #4380) — the flow-side counterpart of `page-walk.ts`, and here for the\n * same reason: every rule had hand-written the same one-liner,\n *\n * ```ts\n * const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n * ```\n *\n * …and every one of them was therefore blind to the same thing.\n *\n * ## What the one-liner misses\n *\n * `FlowRegionSchema` (`@objectstack/spec/automation`) holds a FULL\n * `nodes: z.array(FlowNodeSchema)`, and four config slots carry one:\n *\n * | node type | slot(s) |\n * |-------------|----------------------------------|\n * | `try_catch` | `config.try`, `config.catch` |\n * | `loop` | `config.body` |\n * | `parallel` | `config.branches[].nodes` |\n *\n * Regions nest arbitrarily (a region node may itself be a `try_catch`). Before\n * this walk, a node moved into any of them left the checking behind:\n * `flow-node-write-unknown-field` and `flow-update-readonly-field` — both\n * GATING errors — reported nothing, and `approval-approver-*` went quiet too.\n *\n * `validate-flow-template-paths` failed a third way, worth naming because it is\n * the one a reader would not predict: it scans a node's whole `config` for\n * string leaves, so it still SAW tokens inside a region — but its `filter`\n * position split only looks at the top level of the node it was handed, so a\n * nested filter token lost its position and the #3810 finding silently\n * downgraded from `error` to `warning`, reported against the wrapping\n * `try_catch` instead of the `get_record` that cannot run. Being visible is not\n * the same as being judged correctly, which is why {@link WalkedFlowNode}\n * carries a real per-node `path` rather than only the node object.\n *\n * ## The double-count trap\n *\n * A container node is yielded too — it has its own config worth checking (a\n * `loop`'s `collection`, a `try_catch`'s `retry`). But its `config` physically\n * CONTAINS every descendant, so any rule that walks config recursively would\n * report each nested finding twice: once at the inner node, once at the\n * container. {@link WalkedFlowNode.localConfig} is the container's config with\n * the region slots removed — the view a recursive scan must use. Rules that\n * read named keys (`config.fields`, `config.objectName`) can use either.\n */\n\nimport { FLOW_REGION_SLOTS_BY_TYPE, FLOW_REGION_CONFIG_KEYS } from '@objectstack/spec/automation';\n\nexport type AnyRec = Record<string, unknown>;\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * Config keys that hold a nested region, by owning node type.\n *\n * Projected from `@objectstack/spec/automation` rather than declared here\n * (#4401). This used to be a local copy with its own reconciliation test — as\n * did the ADR-0087 conversion walk's copy and the spec-side control-flow walk's.\n * Three tables, three tests each pinning its own copy, and nothing that would\n * fail if they drifted from ONE ANOTHER: every copy was individually protected\n * and the set was not. A fourth construct is now a single entry in\n * `spec/src/automation/region-slots.ts`.\n *\n * The **walk** below stays here. It takes raw authored records (not\n * `FlowNodeParsed`), and it yields per-node diagnostic paths and label trails —\n * formatting that is lint's business, not the protocol's (Prime Directive #2).\n * Only the table is shared; the three traversals stay separate because they walk\n * different units for different consumers.\n */\nexport const REGION_SLOTS: ReadonlyMap<string, readonly string[]> = new Map(\n [...FLOW_REGION_SLOTS_BY_TYPE].map(([type, slots]) => [type, slots.map(s => s.key)]),\n);\n\n/** Every config key that may hold region nodes, across all node types. */\nexport const REGION_CONFIG_KEYS: ReadonlySet<string> = FLOW_REGION_CONFIG_KEYS;\n\n/**\n * Depth cap. Regions are a tree in parsed metadata, so this is not a cycle\n * guard — it is a cheap promise that a hand-authored (pre-parse) stack cannot\n * make a lint hang. Well past anything reviewable: five levels of nested\n * try/loop/parallel is already an unreadable flow.\n */\nexport const MAX_REGION_DEPTH = 16;\n\n/** A visited flow node plus everything needed to locate and describe it. */\nexport interface WalkedFlowNode {\n /** The node record itself. */\n node: AnyRec;\n /** Config path, e.g. `flows[0].nodes[1].config.catch.nodes[0]`. */\n path: string;\n /**\n * The node's config with region slots stripped — what a rule that scans\n * config RECURSIVELY must read, or it reports every descendant's finding a\n * second time against this node. `undefined` when the node has no config.\n */\n localConfig?: AnyRec;\n /**\n * Region breadcrumb from the flow root, e.g. `try_catch \"Guard\" › catch`.\n * Empty string for a top-level node, so a caller can append it unconditionally.\n */\n regionTrail: string;\n /** 0 for a top-level node; 1 inside one region; and so on. */\n depth: number;\n}\n\n/** A node's label for diagnostics: `label` → `id` → `#index`. */\nexport function flowNodeLabel(node: AnyRec, index: number): string {\n return strName(node.label) ?? strName(node.id) ?? `#${index}`;\n}\n\n/** `config` minus the region slots, or `undefined` when there is no config. */\nfunction stripRegions(config: unknown): AnyRec | undefined {\n if (!isRec(config)) return undefined;\n let out: AnyRec | undefined;\n for (const key of Object.keys(config)) {\n if (!REGION_CONFIG_KEYS.has(key)) continue;\n out ??= { ...config };\n delete out[key];\n }\n return out ?? config;\n}\n\n/**\n * Walk every node of a flow, depth-first, including those nested in\n * `try_catch` / `loop` / `parallel` regions. Yields each with its own config\n * path, so a finding lands on the node that is actually wrong.\n *\n * `flowPath` is the caller's path prefix for the flow (e.g. `flows[3]`).\n */\nexport function walkFlowNodes(flow: AnyRec, flowPath: string): WalkedFlowNode[] {\n const out: WalkedFlowNode[] = [];\n if (!isRec(flow)) return out;\n\n const visitList = (nodes: unknown, basePath: string, trail: string, depth: number): void => {\n if (!Array.isArray(nodes) || depth > MAX_REGION_DEPTH) return;\n nodes.forEach((raw, index) => {\n if (!isRec(raw)) return;\n const path = `${basePath}[${index}]`;\n out.push({\n node: raw,\n path,\n localConfig: stripRegions(raw.config),\n regionTrail: trail,\n depth,\n });\n\n const type = strName(raw.type);\n const slots = type ? REGION_SLOTS.get(type) : undefined;\n if (!slots || !isRec(raw.config)) return;\n const config = raw.config;\n const here = `${type} \"${flowNodeLabel(raw, index)}\"`;\n\n for (const slot of slots) {\n const value = config[slot];\n if (slot === 'branches') {\n // parallel: an array of regions, each with its own nodes.\n if (!Array.isArray(value)) continue;\n value.forEach((branch, b) => {\n if (!isRec(branch)) return;\n const branchName = strName(branch.name) ?? `#${b}`;\n visitList(\n branch.nodes,\n `${path}.config.branches[${b}].nodes`,\n joinTrail(trail, `${here} › branch ${branchName}`),\n depth + 1,\n );\n });\n continue;\n }\n if (!isRec(value)) continue;\n visitList(\n value.nodes,\n `${path}.config.${slot}.nodes`,\n joinTrail(trail, `${here} › ${slot}`),\n depth + 1,\n );\n }\n });\n };\n\n visitList(flow.nodes, `${flowPath}.nodes`, '', 0);\n return out;\n}\n\nfunction joinTrail(trail: string, segment: string): string {\n return trail ? `${trail} › ${segment}` : segment;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for `{record.<path>}` template references in a\n// record-change flow's node config (#3426).\n//\n// A `notify` / `update_record` / `http` / ... node interpolates\n// `{record.<field>}` tokens against the triggering record. Two authoring\n// mistakes render a SILENT empty string at runtime, with no design-time\n// signal — exactly the failure #3426 reported:\n//\n// 1. `{record.<unknown>}` — the path head is neither a declared field nor a\n// system column. Almost always a typo (`{record.full_naem}`). The template\n// engine resolves it to `undefined` -> '' with no warning.\n//\n// 2. `{record.<lookup>.<subfield>}` — a cross-object hop through a lookup /\n// master_detail / user / tree relation. The seeded flow record carries the\n// relation as a SCALAR foreign-key id, not an expanded object (a default\n// data-API read does not expand relations either — see #3426's hydration\n// note and #1872). So `record.account.name` walks `.name` on a string id\n// and yields '' silently. Not resolved today; tracked on #3426.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring.\n//\n// SEVERITY FOLLOWS THE RUNTIME CONSEQUENCE, which differs by POSITION:\n//\n// - Everywhere else (a message body, an http url, a write payload) an\n// unresolved token renders a blank. The output is wrong but the run\n// completes, and the head object may legitimately come from another\n// installed package (skipped — see below). Advisory: WARNING.\n//\n// - Inside a filter-guarded CRUD node's `filter`, an unresolved token used to\n// DELETE the condition from the query, and a removed condition matches MORE\n// rows — `delete_record` with its only condition gone matched every row.\n// Since framework#3810 those nodes REFUSE TO EXECUTE. So a finding here is\n// not \"the output will be blank\", it is \"this node cannot run\": the build\n// is shipping a flow whose runtime is already decided. Gating: ERROR.\n//\n// The split is the same shift-left `validateReadonlyFlowWrites` makes — a\n// certain runtime failure gates the build, a state-dependent one advises — and\n// it keeps this rule honest about what it found. Catching the typo at build\n// time beats a failed run at 3am; catching it and calling it advisory, when the\n// runtime has already committed to refusing, understates it.\n//\n// Deliberately conservative to keep false positives near zero:\n// - Only `record.`-prefixed tokens are checked. Other `{var}` tokens address\n// flow variables / node outputs the rule cannot resolve statically.\n// - Only flows bound to an object THIS stack defines are checked; when the\n// object is unknown here (another package, `sys_*`) the rule has no schema\n// to compare against and skips the whole flow.\n// - `formula` / `summary` fields are VALID heads (formula is hydrated onto the\n// record since #3445; summary is stored on write) — never flagged.\n// - A trailing NUMERIC segment (`{record.target_channels.0}`) is an array\n// index into a `multiple` lookup (#1872), not a cross-object hop — allowed.\n// - Structured scalar heads (`json` / `composite` / `repeater` / `record`) may\n// carry legitimate sub-paths — their `.<sub>` access is left alone.\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\nimport { walkFlowNodes } from './flow-walk.js';\n\nexport type FlowTemplatePathSeverity = 'error' | 'warning';\n\nexport interface FlowTemplatePathFinding {\n severity: FlowTemplatePathSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"notify_lead\" node \"notify\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[2]`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const FLOW_TEMPLATE_UNKNOWN_FIELD = 'flow-template-unknown-field';\nexport const FLOW_TEMPLATE_LOOKUP_TRAVERSAL = 'flow-template-lookup-traversal';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\n// Path heads addressable in a `{record.<col>}` template without being authored\n// fields: the package-shared registry-injected columns (`system-fields.ts`,\n// #4330) plus three heads this rule has always exempted. `name`, `owner` and\n// `record_type` are NOT registry-injected system columns (`name` in particular\n// is an ordinary authored field on most objects), so they stay rule-local —\n// see the shared module's note — instead of widening every field-existence\n// rule in the package.\nconst IMPLICIT_HEADS: ReadonlySet<string> = new Set([\n ...SYSTEM_FIELDS,\n 'name', 'owner', 'record_type',\n]);\n\n// Field types that address ANOTHER object — a `.<subfield>` hop through one is\n// a cross-object traversal the seeded flow record does not expand.\nconst RELATION_TYPES: ReadonlySet<string> = new Set([\n 'lookup',\n 'master_detail',\n 'user',\n 'tree',\n]);\n\n// The CRUD nodes whose `filter` the runtime guards (framework#3810): each calls\n// `resolveNodeFilter`, which refuses the node when interpolation erased any\n// authored condition. `create_record` is deliberately absent — it writes a\n// payload and has no filter, so an unresolved token there is a blank value on\n// the new row, not a widened query.\nconst FILTER_GUARDED_NODE_TYPES: ReadonlySet<string> = new Set([\n 'get_record',\n 'update_record',\n 'delete_record',\n]);\n\n/** Build a `fieldName -> type` map for an object (declared fields only). */\nfunction fieldTypesOf(obj: AnyRec): Map<string, string> {\n const types = new Map<string, string>();\n for (const f of asArray(obj.fields)) {\n if (typeof f.name === 'string') {\n types.set(f.name, typeof f.type === 'string' ? f.type : '');\n }\n }\n return types;\n}\n\n/**\n * Extract the `record.<path>` references from a template string. Mirrors the\n * runtime interpolator's token grammar (service-automation builtin/template.ts):\n * a `{...}` token whose body is a dotted path whose HEAD is `record`. Arithmetic\n * / function tokens (`{NOW()}`, `{a + b}`) and non-`record` heads are ignored.\n *\n * Returns each reference's segment list AFTER the `record` head, e.g.\n * `{record.account.name}` -> `[['account', 'name']]`.\n */\nfunction recordRefsIn(text: string): string[][] {\n const refs: string[][] = [];\n const tokenRe = /\\{([^{}]+)\\}/g;\n let m: RegExpExecArray | null;\n while ((m = tokenRe.exec(text)) !== null) {\n const body = m[1].trim();\n // Pure dotted path only (same shape the interpolator's fast path accepts):\n // identifier head, then identifier-or-numeric segments. Anything with\n // operators / spaces / quotes is an arithmetic token — not a bare field ref.\n if (!/^[A-Za-z_$][\\w$]*(?:\\.(?:[A-Za-z_$][\\w$]*|\\d+))*$/.test(body)) continue;\n const segments = body.split('.');\n if (segments[0] !== 'record') continue;\n const rest = segments.slice(1);\n if (rest.length > 0) refs.push(rest);\n }\n return refs;\n}\n\n/** Recursively collect templated string leaves from a config-bearing block. */\nfunction stringLeaves(value: unknown, out: string[]): void {\n if (typeof value === 'string') {\n if (value.includes('{')) out.push(value);\n return;\n }\n if (Array.isArray(value)) {\n for (const v of value) stringLeaves(v, out);\n return;\n }\n if (value && typeof value === 'object') {\n for (const v of Object.values(value as AnyRec)) stringLeaves(v, out);\n }\n}\n\n// The typed config blocks + freeform `config` a node interpolates at runtime.\n// We scan every string leaf under these (the runtime `interpolate()` walks the\n// whole config recursively), NOT `id` / `type` / `label` / `position`, which are\n// never templated.\nconst NODE_CONFIG_KEYS = [\n 'config',\n 'notify',\n 'update_record',\n 'create_record',\n 'http',\n 'script',\n 'screen',\n 'wait',\n 'approval',\n 'connector_action',\n 'subflow',\n 'decision',\n 'start',\n];\n\n/** A templated string leaf plus the one thing severity depends on: where it sits. */\ninterface TemplateLeaf {\n text: string;\n /** Inside a filter-guarded CRUD node's `filter` — an unresolved token there is refused at runtime. */\n inFilter: boolean;\n}\n\n/**\n * Collect a node's templated string leaves, tagging those that sit under a\n * `filter` key when the node type is one the runtime guards.\n *\n * `guarded` leaves are returned FIRST so the per-node dedupe below resolves a\n * reference that appears in both positions at its higher severity: one typo\n * used in a filter and echoed in a message is an error, not a warning.\n */\nfunction collectNodeLeaves(node: AnyRec, guarded: boolean): TemplateLeaf[] {\n const filterLeaves: TemplateLeaf[] = [];\n const otherLeaves: TemplateLeaf[] = [];\n\n for (const key of NODE_CONFIG_KEYS) {\n if (!(key in node)) continue;\n const block = node[key];\n const splitFilter = guarded && !!block && typeof block === 'object' && !Array.isArray(block);\n\n if (splitFilter) {\n const { filter, ...rest } = block as AnyRec;\n const inFilter: string[] = [];\n stringLeaves(filter, inFilter);\n for (const text of inFilter) filterLeaves.push({ text, inFilter: true });\n const outside: string[] = [];\n stringLeaves(rest, outside);\n for (const text of outside) otherLeaves.push({ text, inFilter: false });\n continue;\n }\n\n const plain: string[] = [];\n stringLeaves(block, plain);\n for (const text of plain) otherLeaves.push({ text, inFilter: false });\n }\n\n return [...filterLeaves, ...otherLeaves];\n}\n\n/** True when the flow is armed by a record lifecycle event. */\nfunction isRecordTriggered(flow: AnyRec, startConfig: AnyRec): boolean {\n if (flow.type === 'record_change') return true;\n const triggerType = typeof startConfig.triggerType === 'string' ? startConfig.triggerType : undefined;\n return !!triggerType && triggerType.startsWith('record-');\n}\n\n/** Resolve the object a record-change flow binds to, from its start node. */\nfunction boundObjectOf(flow: AnyRec): string | undefined {\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const start = nodes.find((n) => n?.type === 'start');\n if (!start) return undefined;\n const config = (start.config ?? {}) as AnyRec;\n const typed = (start.start ?? {}) as AnyRec;\n const fromConfig = typeof config.objectName === 'string' ? config.objectName : undefined;\n const fromTyped = typeof typed.objectName === 'string' ? typed.objectName : undefined;\n return fromConfig ?? fromTyped;\n}\n\n/**\n * The lookup relations a record-change flow opted IN to expand, from the start\n * node's `config.expand` (#3475). A `{record.<rel>.<field>}` hop through one of\n * these IS resolved at run time — the engine re-reads it as the run's identity —\n * so the traversal warning is suppressed for those relations. Accepts a `string`\n * or `string[]`; anything else yields the empty set.\n */\nfunction declaredExpandOf(flow: AnyRec): Set<string> {\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const start = nodes.find((n) => n?.type === 'start');\n const raw = ((start?.config ?? {}) as AnyRec).expand;\n if (typeof raw === 'string') return new Set(raw ? [raw] : []);\n if (Array.isArray(raw)) return new Set(raw.filter((r): r is string => typeof r === 'string' && r.length > 0));\n return new Set();\n}\n\n/**\n * Validate `{record.<path>}` template references across every record-change\n * flow. Pure and dependency-free; safe on pre- or post-parse stacks.\n */\nexport function validateFlowTemplatePaths(stack: AnyRec): FlowTemplatePathFinding[] {\n const findings: FlowTemplatePathFinding[] = [];\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n const objectsByName = new Map<string, AnyRec>();\n for (const obj of asArray(stack.objects)) {\n if (typeof obj.name === 'string') objectsByName.set(obj.name, obj);\n }\n\n flows.forEach((flow, flowIndex) => {\n const flowName = typeof flow.name === 'string' ? flow.name : `#${flowIndex}`;\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const start = (nodes.find((n) => n?.type === 'start')?.config ?? {}) as AnyRec;\n if (!isRecordTriggered(flow, start)) return;\n\n const objectName = boundObjectOf(flow);\n if (!objectName) return;\n const obj = objectsByName.get(objectName);\n // Unknown object here -> no schema to compare against (another package /\n // `sys_*`). The trigger-readiness rule already flags a wrong name; we can't\n // meaningfully classify field paths, so skip the whole flow.\n if (!obj) return;\n\n const fieldTypes = fieldTypesOf(obj);\n const expandSet = declaredExpandOf(flow);\n\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions\n // (#4380). This rule was not merely blind to them — it was WORSE than\n // blind: the recursive string-leaf scan already saw a nested node's tokens\n // through its container's `config`, but `collectNodeLeaves` splits `filter`\n // only at the top level of the node it is handed, so a nested filter token\n // lost its position and the gating #3810 finding silently degraded to a\n // warning reported against the wrapping `try_catch`. Walking to the real\n // node restores both the severity and the location.\n walkFlowNodes(flow, `flows[${flowIndex}]`).forEach(({ node, path: nodePath, regionTrail, localConfig }, walkIndex) => {\n const nodeLabel =\n typeof node.type === 'string' ? node.type : typeof node.id === 'string' ? node.id : `#${walkIndex}`;\n const where = regionTrail\n ? `flow \"${flowName}\" ${regionTrail} node \"${nodeLabel}\"`\n : `flow \"${flowName}\" node \"${nodeLabel}\"`;\n\n // Collect templated string leaves from the config-bearing blocks only,\n // tagging filter positions when this node type guards its filter (#3810).\n const nodeType = typeof node.type === 'string' ? node.type : '';\n const guarded = FILTER_GUARDED_NODE_TYPES.has(nodeType);\n // Scan the container's config WITHOUT its region slots: their nodes are\n // walked in their own right, and leaving them in would report every\n // nested finding a second time against the container.\n const scanNode =\n localConfig !== undefined && localConfig !== node.config\n ? ({ ...node, config: localConfig } as AnyRec)\n : (node as AnyRec);\n const leaves = collectNodeLeaves(scanNode, guarded);\n if (leaves.length === 0) return;\n\n // Dedupe references so one repeated typo yields one finding per node.\n const seenUnknown = new Set<string>();\n const seenTraversal = new Set<string>();\n\n for (const leaf of leaves) {\n const inFilter = leaf.inFilter;\n for (const rest of recordRefsIn(leaf.text)) {\n const head = rest[0];\n const hasSubPath = rest.length > 1;\n // A trailing numeric segment is an array index (#1872), not a hop.\n const nextIsIdentifier = hasSubPath && !/^\\d+$/.test(rest[1]);\n\n const isKnown = fieldTypes.has(head) || IMPLICIT_HEADS.has(head);\n\n if (!isKnown) {\n if (seenUnknown.has(head)) continue;\n seenUnknown.add(head);\n findings.push({\n severity: inFilter ? 'error' : 'warning',\n rule: FLOW_TEMPLATE_UNKNOWN_FIELD,\n where,\n path: nodePath,\n message: inFilter\n ? `${nodeType} filter references '{record.${rest.join('.')}}', but '${head}' is not a field on ` +\n `object '${objectName}' — the token resolves to nothing, which DROPS the condition from the ` +\n `query instead of narrowing it. The node refuses to run at execution time (#3810).`\n : `template references '{record.${rest.join('.')}}', but '${head}' is not a field on ` +\n `object '${objectName}' — it resolves to an empty string at runtime (silently).`,\n hint: inFilter\n ? `Check the field name against the object's field definitions (e.g. '{record.full_name}', ` +\n `not '{record.full_naem}'); system columns like id/created_at/owner are also addressable. ` +\n `This gates the build rather than warning: an absent condition WIDENS the query, so the ` +\n `runtime has already decided to refuse this node.`\n : `Check the field name against the object's field definitions (e.g. '{record.full_name}', ` +\n `not '{record.full_naem}'). System columns like id/created_at/owner are also addressable.`,\n });\n continue;\n }\n\n if (nextIsIdentifier) {\n const headType = fieldTypes.get(head) ?? '';\n if (RELATION_TYPES.has(headType) && !expandSet.has(head)) {\n const key = rest.join('.');\n if (seenTraversal.has(key)) continue;\n seenTraversal.add(key);\n findings.push({\n severity: inFilter ? 'error' : 'warning',\n rule: FLOW_TEMPLATE_LOOKUP_TRAVERSAL,\n where,\n path: nodePath,\n message: inFilter\n ? `${nodeType} filter references '{record.${key}}', a cross-object hop through the ` +\n `${headType} field '${head}' — the flow record carries '${head}' as a scalar id, not an ` +\n `expanded object, so the token resolves to nothing and the condition is DROPPED from the ` +\n `query instead of narrowing it. The node refuses to run at execution time (#3810).`\n : `template references '{record.${key}}', a cross-object hop through the ${headType} field ` +\n `'${head}' — the flow record carries '${head}' as a scalar id, not an expanded object, so ` +\n `this resolves to an empty string at runtime (silently).`,\n hint: inFilter\n ? `Opt in to resolve it: add '${head}' to the start node's config.expand (#3475) and the ` +\n `engine re-reads it as the run's identity. Otherwise filter on the foreign-key id directly ` +\n `('{record.${head}}'), or project the value via a formula field on '${objectName}'. This ` +\n `gates the build rather than warning: an absent condition WIDENS the query.`\n : `Opt in to resolve it: add '${head}' to the start node's config.expand (#3475) and the ` +\n `engine re-reads it as the run's identity. Otherwise reference the foreign-key id directly ` +\n `('{record.${head}}'), or project the value via a formula field on '${objectName}'.`,\n });\n }\n // STRUCTURED_TYPES + any other scalar `.sub` access is left alone:\n // json/composite/record sub-paths are legitimate in-row reads, and\n // a plain scalar `.sub` is rare enough that flagging it would risk\n // more false positives than it prevents.\n }\n }\n }\n });\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail: a flow `update_record` node that writes a field the\n// target object declares `readonly: true`, under a non-system run identity, is\n// a SILENT NO-OP. The objectql engine strips static-`readonly` fields from a\n// non-system UPDATE payload (#2948), so the intended write never lands — yet\n// the step still reports `success`. #3407/#3413 made that strip observable at\n// RUN time (a step warning + `droppedFields`); this rule shifts the discovery\n// LEFT to `os validate` / `os build`, so an author finds the mismatch at design\n// time instead of by reading server WARN logs days later (#3425).\n//\n// Scope — deliberately narrow to keep it false-positive-free:\n//\n// • Only `update_record`. INSERT is engine-exempt from the readonly strip (a\n// `create_record` may legitimately seed readonly columns; the ingress strip\n// added in #3043 lives in metadata-protocol, which the flow engine bypasses\n// by calling the data engine directly), so a create writing a readonly\n// field is NOT a no-op and is never flagged.\n//\n// • Only `runAs !== 'system'`. A `runAs:'system'` run is elevated and the\n// engine skips the strip entirely, so a system flow legitimately MAINTAINS\n// readonly fields (\"users can't edit this, but automation does\"). That is\n// the intended channel, so it is never flagged.\n//\n// • Static `readonly:true` + a LITERAL field name is a 100%-certain no-op →\n// ERROR (gates the build). `readonlyWhen` is per-record-state — it strips\n// only on records whose predicate is TRUE at run time, so it MAY silently\n// not land → WARNING (advisory). A templated object name or a non-literal\n// `fields` map is not statically knowable → skipped, no guess.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019): no I/O, no runtime. Shared by\n// the CLI and any other consumer (AI authoring), so hand-authored and generated\n// flows are held to the same bar.\n\nimport { walkFlowNodes, flowNodeLabel } from './flow-walk.js';\n\nexport type ReadonlyFlowWriteSeverity = 'error' | 'warning';\n\nexport interface ReadonlyFlowWriteFinding {\n severity: ReadonlyFlowWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"approve_deal\" › node \"Mark approved\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[3].config.fields.approval_status`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const FLOW_UPDATE_READONLY_FIELD = 'flow-update-readonly-field';\nexport const FLOW_UPDATE_READONLY_WHEN_FIELD = 'flow-update-readonly-when-field';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\ninterface FieldReadonlyMeta {\n /** Static `readonly: true`. */\n readonly: boolean;\n /** A non-empty `readonlyWhen` predicate is declared. */\n readonlyWhen: boolean;\n}\n\n/**\n * object name → (field name → readonly metadata). Handles both `fields` shapes\n * (array of `{name, readonly, readonlyWhen}` and name-keyed map). A field with\n * neither flag is recorded as `{false, false}` so callers can distinguish a\n * \"known-writable field\" from an \"unknown field\" (absent from the map).\n */\nfunction buildReadonlyIndex(objects: AnyRec[]): Map<string, Map<string, FieldReadonlyMeta>> {\n const idx = new Map<string, Map<string, FieldReadonlyMeta>>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fieldMap = new Map<string, FieldReadonlyMeta>();\n const collect = (fieldName: string, def: AnyRec): void => {\n const rw = def?.readonlyWhen;\n const readonlyWhen = rw != null && !(typeof rw === 'string' && rw.trim() === '');\n fieldMap.set(fieldName, { readonly: def?.readonly === true, readonlyWhen });\n };\n const fields = obj.fields;\n if (Array.isArray(fields)) {\n for (const f of fields as AnyRec[]) {\n const fn = (f as AnyRec)?.name;\n if (typeof fn === 'string') collect(fn, f as AnyRec);\n }\n } else if (fields && typeof fields === 'object') {\n for (const [fn, def] of Object.entries(fields as AnyRec)) collect(fn, def as AnyRec);\n }\n idx.set(name, fieldMap);\n }\n return idx;\n}\n\n/**\n * The target object of an `update_record` node, when statically knowable. Reads\n * the canonical `objectName` and its historical `object` alias — a pre-parse\n * source may still carry the alias during the protocol-17 window, until the\n * 'flow-node-crud-object-alias' conversion (#3796) canonicalizes it at load. A\n * templated value (contains `{`) is dynamic — return undefined so the node is\n * skipped rather than guessed.\n */\nfunction readLiteralObjectName(config: AnyRec): string | undefined {\n const raw = config.objectName ?? config.object;\n if (typeof raw !== 'string' || raw.includes('{')) return undefined;\n return raw || undefined;\n}\n\n/**\n * Validate flow `update_record` writes against target-object readonly\n * declarations. Pure and dependency-free; safe on pre- or post-parse stacks.\n */\nexport function validateReadonlyFlowWrites(stack: AnyRec): ReadonlyFlowWriteFinding[] {\n const findings: ReadonlyFlowWriteFinding[] = [];\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n const roIndex = buildReadonlyIndex(asArray(stack.objects));\n\n flows.forEach((flow, flowIndex) => {\n // `runAs` defaults to 'user' (schema default). Only an explicit 'system'\n // run bypasses the strip, so treat anything else — including an unauthored\n // (undefined) runAs — as strip-subject.\n if (flow.runAs === 'system') return;\n const runAs = flow.runAs === 'user' || flow.runAs === 'system' ? flow.runAs : 'user';\n\n const flowName = typeof flow.name === 'string' ? flow.name : `#${flowIndex}`;\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions.\n // A readonly write inside a `catch` branch is the same certain no-op as one\n // at the top level, and this rule gates on it (#4380).\n const walked = walkFlowNodes(flow, `flows[${flowIndex}]`);\n\n walked.forEach(({ node, path: nodePath, regionTrail }, walkIndex) => {\n if (node?.type !== 'update_record') return;\n const config = (node.config ?? {}) as AnyRec;\n\n const objectName = readLiteralObjectName(config);\n if (!objectName) return; // templated / dynamic object — not statically knowable\n const fieldMap = roIndex.get(objectName);\n if (!fieldMap) return; // object defined by another package — cannot judge its fields\n\n const fields = config.fields;\n // A non-literal write map (templated string, spread, array) is not\n // statically knowable — skip rather than guess.\n if (!fields || typeof fields !== 'object' || Array.isArray(fields)) return;\n\n const nodeName = flowNodeLabel(node, walkIndex);\n const where = regionTrail\n ? `flow \"${flowName}\" › ${regionTrail} › node \"${nodeName}\"`\n : `flow \"${flowName}\" › node \"${nodeName}\"`;\n\n for (const fieldName of Object.keys(fields as AnyRec)) {\n const meta = fieldMap.get(fieldName);\n // Unknown field — `validate-flow-node-writes.ts` owns that question\n // (`flow-node-write-unknown-field`, also gating). This rule is about a\n // field the object DOES declare and the engine then strips; a name that\n // resolves to no column is a different failure with a different fix, so\n // the two never double-report the same key.\n if (!meta) continue;\n\n if (meta.readonly) {\n findings.push({\n severity: 'error',\n rule: FLOW_UPDATE_READONLY_FIELD,\n where,\n path: `${nodePath}.config.fields.${fieldName}`,\n message:\n `writes field '${fieldName}', which object '${objectName}' declares readonly:true. Under ` +\n `runAs:'${runAs}' the engine silently strips readonly fields from the UPDATE payload (#2948), ` +\n `so this write never lands — while the step still reports success.`,\n hint:\n `If automation is meant to maintain this field, declare the flow runAs:'system' (the intended ` +\n `channel — readonly governs the end-user/API surface, not trusted system writers). Otherwise ` +\n `remove '${fieldName}' from this update_record node.`,\n });\n } else if (meta.readonlyWhen) {\n findings.push({\n severity: 'warning',\n rule: FLOW_UPDATE_READONLY_WHEN_FIELD,\n where,\n path: `${nodePath}.config.fields.${fieldName}`,\n message:\n `writes field '${fieldName}', which object '${objectName}' declares readonlyWhen. On records ` +\n `where that predicate is TRUE, a runAs:'${runAs}' UPDATE strips the field (#3042), so this ` +\n `write may silently not land depending on the record's state.`,\n hint:\n `If automation must maintain this field regardless of record state, run the flow runAs:'system'. ` +\n `Otherwise confirm this node only targets records whose readonlyWhen predicate is FALSE.`,\n });\n }\n }\n });\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for the `defineView` container shape.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate`. It\n// catches the \"flat view object\" authoring mistake the schema alone cannot\n// surface: `ViewSchema` is a container (`{ list, form, listViews, formViews }`)\n// whose slots are all optional, and Zod strips unknown keys — so a flat list\n// view (`{ name: 'all_tasks', label, type: 'grid', columns: [...] }`) parses\n// to an EMPTY container. The stack validates, the loader finds nothing to\n// expand, and the Console silently renders no view (no switcher entry). The\n// third-party 15.1 evaluation hit exactly this via the old docs.\n//\n// Runs PRE-parse (on the normalizeStackInput output, before the\n// ObjectStackDefinition parse): post-parse the flat keys are already stripped\n// and the mistake is indistinguishable from an intentionally empty container.\n//\n// Independent ViewItems (`viewKind` + `config`) are legal `views: []` entries\n// (the loader registers them as-is) and are not flagged.\n\nexport type ViewContainerSeverity = 'error' | 'warning';\n\nexport interface ViewContainerFinding {\n severity: ViewContainerSeverity;\n rule: string;\n /** Human-readable location, e.g. `views[0] (\"all_tasks\")`. */\n where: string;\n /** Config path, e.g. `views[0]`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const VIEW_CONTAINER_SHAPE = 'view-container-shape';\n\ntype AnyRec = Record<string, unknown>;\n\nconst CONTAINER_SLOT_KEYS = ['list', 'form', 'listViews', 'formViews'] as const;\n\n/** Coerce an array-or-name-keyed-map collection to indexed entries. */\nfunction asEntries(v: unknown): Array<{ key: string; value: unknown }> {\n if (Array.isArray(v)) return v.map((value, i) => ({ key: `[${i}]`, value }));\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, value]) => ({ key: `.${name}`, value }));\n }\n return [];\n}\n\n/** Number of views a parsed-or-raw container actually carries. */\nfunction containerViewCount(rec: AnyRec): number {\n const named = (slot: unknown): number =>\n slot && typeof slot === 'object' && !Array.isArray(slot) ? Object.keys(slot as AnyRec).length : 0;\n return (rec.list ? 1 : 0) + (rec.form ? 1 : 0) + named(rec.listViews) + named(rec.formViews);\n}\n\n/**\n * Validate that every stack-level `views` entry is a real view container (or\n * an independent ViewItem). Flat list-view objects and view-less containers\n * are reported as errors with a wrap-it fix hint.\n */\nexport function validateViewContainers(stack: Record<string, unknown>): ViewContainerFinding[] {\n const out: ViewContainerFinding[] = [];\n if (!stack || typeof stack !== 'object') return out;\n\n for (const { key, value } of asEntries((stack as AnyRec).views)) {\n // Non-object entries are the schema step's problem, not this rule's.\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue;\n const rec = value as AnyRec;\n\n // Independent ViewItem (`viewKind` discriminator) — registered as-is.\n if (rec.viewKind != null) continue;\n\n if (containerViewCount(rec) > 0) continue;\n\n const label = typeof rec.name === 'string' ? ` (\"${rec.name}\")` : '';\n const hasContainerSlot = CONTAINER_SLOT_KEYS.some((k) => k in rec);\n // Flat list-view fingerprint: view-ish keys at the top level where the\n // container slots should be.\n const looksFlat = !hasContainerSlot\n && ['type', 'columns', 'data', 'filter', 'sort'].some((k) => k in rec);\n\n out.push({\n severity: 'error',\n rule: VIEW_CONTAINER_SHAPE,\n where: `views${key}${label}`,\n path: `views${key}`,\n message: looksFlat\n ? 'Flat list-view object is not a view container: `ViewSchema` strips its keys, '\n + 'so it parses to an EMPTY container — zero views register and the Console '\n + 'renders no view for it.'\n : 'View container defines no views — all of `list` / `form` / `listViews` / '\n + '`formViews` are absent or empty, so nothing registers.',\n hint: 'Wrap every view in a defineView container: defineView({ list: { type, data, '\n + 'columns, ... }, listViews: { ... }, formViews: { ... } }). See '\n + 'examples/app-showcase/src/ui/views/task.view.ts.',\n });\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time diagnostics for the SDUI scoped-styling model (ADR-0065).\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019): the same bar holds for\n// hand-authored and AI-generated pages, run from `os validate`/`compile` and\n// reusable by AI authoring. It catches the deterministic ways a `responsiveStyles`\n// block silently fails or drifts — the class of mistake an AI author is most\n// likely to make — *before* render, with actionable hints it can self-correct on.\n//\n// What it does NOT catch: whether the result looks good. Visual/semantic quality\n// (contrast, balance, \"is it ugly\") is only catchable by rendering + a VLM gate,\n// which is a separate, render-time concern (ADR-0065 §Decision-5).\n\nexport type StyleSeverity = 'error' | 'warning';\n\nexport interface StyleFinding {\n severity: StyleSeverity;\n rule: string;\n /** Human-readable location, e.g. `page \"pricing\" › node \"plan_solo\"`. */\n where: string;\n /** Config path, e.g. `pages[0].regions[0].components[1]`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const STYLE_NODE_MISSING_ID = 'style-node-missing-id';\nexport const STYLE_CLASSNAME_TAILWIND = 'style-classname-tailwind';\nexport const STYLE_RESPONSIVE_NO_BASE = 'style-responsive-no-base';\nexport const STYLE_UNKNOWN_CSS_PROPERTY = 'style-unknown-css-property';\nexport const STYLE_UNKNOWN_TOKEN = 'style-unknown-token';\n\ntype AnyRec = Record<string, unknown>;\n\nconst BREAKPOINTS = ['large', 'medium', 'small', 'xsmall'] as const;\n\n/** SDUI design-token palette (ADR-0065) + base theme tokens, referenced as\n * `var(--name)`. Authors should resolve values against these. Kept in sync with\n * `apps/console/src/index.css` / `@object-ui/components` `:root`. */\nconst KNOWN_TOKENS = new Set<string>([\n // SDUI tokens\n 'space-1', 'space-2', 'space-3', 'space-4', 'space-5', 'space-6', 'space-8', 'space-10', 'space-12',\n 'radius', 'radius-sm', 'radius-md', 'radius-lg', 'radius-xl',\n 'shadow-sm', 'shadow-md', 'shadow-lg',\n 'surface', 'surface-sunken', 'text-strong', 'text-muted', 'brand', 'brand-foreground', 'hairline',\n // Base theme tokens (shadcn) — usually wrapped as hsl(var(--x)).\n 'background', 'foreground', 'card', 'card-foreground', 'popover', 'popover-foreground',\n 'primary', 'primary-foreground', 'secondary', 'secondary-foreground',\n 'muted', 'muted-foreground', 'accent', 'accent-foreground',\n 'destructive', 'destructive-foreground', 'border', 'input', 'ring',\n 'success', 'success-foreground', 'warning', 'warning-foreground',\n 'chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5',\n]);\n\n/** Common CSS properties (camelCase) an SDUI block realistically sets. Generous\n * on purpose: an unknown property is only a *warning* (typo catcher), never a\n * blocker. Custom properties (`--x`) are always allowed. */\nconst KNOWN_CSS_PROPERTIES = new Set<string>([\n 'display', 'position', 'top', 'right', 'bottom', 'left', 'inset', 'zIndex', 'overflow', 'overflowX', 'overflowY', 'visibility', 'boxSizing', 'float', 'clear',\n 'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight', 'aspectRatio',\n 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'marginInline', 'marginBlock',\n 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'paddingInline', 'paddingBlock',\n 'flex', 'flexDirection', 'flexWrap', 'flexGrow', 'flexShrink', 'flexBasis', 'alignItems', 'alignContent', 'alignSelf', 'justifyContent', 'justifyItems', 'justifySelf', 'gap', 'rowGap', 'columnGap', 'order', 'placeItems', 'placeContent',\n 'grid', 'gridTemplate', 'gridTemplateColumns', 'gridTemplateRows', 'gridTemplateAreas', 'gridColumn', 'gridRow', 'gridArea', 'gridAutoFlow', 'gridAutoColumns', 'gridAutoRows',\n 'color', 'backgroundColor', 'background', 'backgroundImage', 'backgroundSize', 'backgroundPosition', 'backgroundRepeat', 'backgroundClip', 'opacity', 'mixBlendMode',\n 'fontSize', 'fontWeight', 'fontFamily', 'fontStyle', 'lineHeight', 'letterSpacing', 'textAlign', 'textTransform', 'textDecoration', 'textOverflow', 'whiteSpace', 'wordBreak', 'overflowWrap', 'fontVariantNumeric', 'verticalAlign', 'textShadow',\n 'border', 'borderTop', 'borderRight', 'borderBottom', 'borderLeft', 'borderColor', 'borderWidth', 'borderStyle', 'borderRadius', 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomLeftRadius', 'borderBottomRightRadius', 'outline', 'outlineOffset',\n 'boxShadow', 'transform', 'transformOrigin', 'transition', 'transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay', 'animation', 'filter', 'backdropFilter', 'willChange',\n 'cursor', 'pointerEvents', 'userSelect', 'objectFit', 'objectPosition', 'content',\n]);\n\nconst VAR_RE = /var\\(\\s*--([a-zA-Z0-9-]+)\\s*[,)]/g;\n\n// High-precision Tailwind-utility detection. A `className` in page metadata is\n// \"Tailwind-looking\" if any token is a responsive/state variant, an arbitrary\n// `[…]` value, a known utility stem followed by a Tailwind *value* (number /\n// fraction / size keyword), or a bare layout utility. Tuned to NOT trip on\n// ordinary custom class names (e.g. `my-custom-scope`, `os-s-plan_solo`).\nconst TW_VARIANT = /^(sm|md|lg|xl|2xl|hover|focus|active|disabled|dark|group-hover|peer-[a-z]+|first|last|odd|even):/;\nconst TW_STEM_VALUE = /^-?(p|m|px|py|pt|pb|pl|pr|mx|my|mt|mb|ml|mr|gap|gap-x|gap-y|space-x|space-y|w|h|min-w|max-w|min-h|max-h|size|text|leading|tracking|bg|border|rounded|shadow|ring|opacity|inset|top|bottom|left|right|z|order|col|row|grid-cols|grid-rows|basis)-(\\d+(\\.\\d+)?|\\d+\\/\\d+|px|full|auto|none|screen|min|max|fit|xs|sm|md|lg|xl|2xl|3xl|4xl|5xl|6xl)$/;\nconst TW_BARE = /^(flex|grid|block|inline|inline-block|inline-flex|hidden|contents|table|flow-root|grow|shrink|truncate|italic|underline|uppercase|lowercase|capitalize|antialiased|absolute|relative|fixed|sticky|static|isolate|flex-col|flex-row|flex-wrap|flex-nowrap|items-center|items-start|items-end|items-stretch|justify-center|justify-between|justify-around|justify-start|justify-end|text-center|text-left|text-right|font-bold|font-semibold|font-medium|font-normal|tabular-nums)$/;\n\nfunction looksLikeTailwind(className: string): boolean {\n return className.split(/\\s+/).some((tok) => {\n if (!tok) return false;\n if (TW_VARIANT.test(tok)) return true;\n if (/\\[[^\\]]+\\]/.test(tok)) return true; // arbitrary value, e.g. p-[13px]\n if (TW_STEM_VALUE.test(tok)) return true;\n if (TW_BARE.test(tok)) return true;\n return false;\n });\n}\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** Child nodes can hang off `children`, `properties.children`, `body`, or\n * `properties.body` depending on block type — collect them all. */\nfunction childrenOf(node: AnyRec): AnyRec[] {\n const props = (node.properties as AnyRec) ?? {};\n const out: AnyRec[] = [];\n for (const c of [node.children, props.children, node.body, props.body]) {\n if (Array.isArray(c)) out.push(...(c.filter((x) => x && typeof x === 'object') as AnyRec[]));\n }\n return out;\n}\n\nfunction checkNode(node: AnyRec, pageName: string, path: string, findings: StyleFinding[]): void {\n const id = typeof node.id === 'string' ? node.id : undefined;\n const type = typeof node.type === 'string' ? node.type : 'node';\n const where = `page \"${pageName}\" › ${id ? `node \"${id}\"` : `<${type}>`}`;\n const rs = node.responsiveStyles as AnyRec | undefined;\n const hasRs = !!rs && typeof rs === 'object' && BREAKPOINTS.some((b) => rs[b]);\n\n // (1) responsiveStyles needs an id to scope to — else the CSS is dropped.\n if (hasRs && !id) {\n findings.push({\n severity: 'error', rule: STYLE_NODE_MISSING_ID, where, path,\n message: `Node has responsiveStyles but no \\`id\\`; scoped CSS cannot be generated and the styles are silently dropped.`,\n hint: `Add a stable \\`id\\` to this node.`,\n });\n }\n\n // (2) responsive breakpoint without a `large` base → unstyled at desktop.\n if (hasRs && !rs!.large && BREAKPOINTS.slice(1).some((b) => rs![b])) {\n findings.push({\n severity: 'warning', rule: STYLE_RESPONSIVE_NO_BASE, where, path,\n message: `responsiveStyles sets a smaller breakpoint but no \\`large\\` base; the node is unstyled at desktop width.`,\n hint: `Put the unconditional/base styles under \\`responsiveStyles.large\\` (desktop-first).`,\n });\n }\n\n // (3) className that looks like Tailwind → won't render from metadata.\n if (typeof node.className === 'string' && node.className.trim() && looksLikeTailwind(node.className)) {\n findings.push({\n severity: 'warning', rule: STYLE_CLASSNAME_TAILWIND, where, path,\n message: `\\`className\\` contains Tailwind-looking utilities (\"${node.className.trim().slice(0, 60)}\"); these are not compiled from metadata and will silently do nothing.`,\n hint: `Style this node with \\`responsiveStyles\\` + design tokens instead of \\`className\\` (ADR-0065).`,\n });\n }\n\n // (4)+(5) unknown CSS property / unknown token inside each breakpoint map.\n if (rs && typeof rs === 'object') {\n for (const bp of BREAKPOINTS) {\n const map = rs[bp] as AnyRec | undefined;\n if (!map || typeof map !== 'object') continue;\n for (const [prop, value] of Object.entries(map)) {\n if (!prop.startsWith('--') && !KNOWN_CSS_PROPERTIES.has(prop)) {\n findings.push({\n severity: 'warning', rule: STYLE_UNKNOWN_CSS_PROPERTY, where, path: `${path}.responsiveStyles.${bp}`,\n message: `Unknown CSS property \"${prop}\" (typo?); if unintended it will not apply.`,\n hint: `Use a camelCase CSS property name (e.g. \\`flexDirection\\`, \\`backgroundColor\\`).`,\n });\n }\n if (typeof value === 'string') {\n let m: RegExpExecArray | null;\n VAR_RE.lastIndex = 0;\n while ((m = VAR_RE.exec(value))) {\n const token = m[1];\n if (!KNOWN_TOKENS.has(token) && !token.startsWith('tw-')) {\n findings.push({\n severity: 'warning', rule: STYLE_UNKNOWN_TOKEN, where, path: `${path}.responsiveStyles.${bp}.${prop}`,\n message: `References unknown design token \\`var(--${token})\\` (typo?); it will not resolve.`,\n hint: `Use a token from the ADR-0065 palette (e.g. \\`var(--space-6)\\`, \\`var(--surface)\\`, \\`hsl(var(--primary))\\`).`,\n });\n }\n }\n }\n }\n }\n }\n\n // Recurse.\n const kids = childrenOf(node);\n for (let i = 0; i < kids.length; i++) {\n checkNode(kids[i], pageName, `${path}.children[${i}]`, findings);\n }\n}\n\n/**\n * Validate every page's component tree for SDUI styling correctness (ADR-0065).\n * Returns findings (empty = clean). `error` findings describe styles that are\n * silently dropped and should fail validate/build; `warning` findings are\n * advisory (typos, drift, footguns).\n */\nexport function validateResponsiveStyles(stack: AnyRec): StyleFinding[] {\n const findings: StyleFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n const pageName = typeof page.name === 'string' ? page.name : `pages[${p}]`;\n const regions = asArray(page.regions);\n for (let r = 0; r < regions.length; r++) {\n const components = asArray(regions[r].components);\n for (let c = 0; c < components.length; c++) {\n checkNode(components[c], pageName, `pages[${p}].regions[${r}].components[${c}]`, findings);\n }\n }\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time diagnostics for AI-authored HTML-source pages (ADR-0080).\n//\n// Applies to `kind:'html'` (and its deprecated alias `kind:'jsx'`) — the tier\n// whose `source` is constrained JSX/HTML parsed (never executed) to the tree.\n// `kind:'react'` (ADR-0081) is intentionally NOT linted here: its source is\n// real JavaScript, not constrained JSX, so the constrained parser would\n// false-error on hooks / expressions.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` / `os\n// build`. An html page's `source` is a constrained JSX/Tailwind string\n// compiled (parsed, never executed) to the SDUI tree at save time. This gate\n// parses it at author time so malformed source fails loudly (ADR-0078) instead\n// of being stored and breaking only at render.\n//\n// Scope: parse-level — syntax, tag matching, and forbidden constructs (event\n// handlers, dangerouslySetInnerHTML). Full component/prop whitelist validation\n// needs the registry manifest (a cross-repo artifact); when that is wired,\n// thread it through `compile()` here. Until then this catches the structural\n// class of error an AI author is most likely to emit.\n\nimport { parseJsx, compile, type Manifest } from '@objectstack/sdui-parser';\n\nexport type JsxPageSeverity = 'error' | 'warning';\n\nexport interface JsxPageFinding {\n severity: JsxPageSeverity;\n rule: string;\n /** Human-readable location, e.g. `page \"command_center\" › <flex>`. */\n where: string;\n /** Config path, e.g. `pages[3].source`. */\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\nexport function validateJsxPages(stack: AnyRec, opts: { manifest?: Manifest } = {}): JsxPageFinding[] {\n const findings: JsxPageFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n // html tier (+ deprecated 'jsx' alias). react pages are not constrained JSX.\n if (!page || (page.kind !== 'html' && page.kind !== 'jsx')) continue;\n const name = String(page.name ?? `#${p}`);\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') {\n // (PageSchema's superRefine also covers this; keep it for the build path.)\n findings.push({\n severity: 'error',\n rule: 'jsx-page-empty-source',\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: `kind:'${page.kind}' page has no \\`source\\`.`,\n hint: 'Author the page as a constrained JSX/Tailwind string in `source`.',\n });\n continue;\n }\n // With a component manifest, do full validation (unknown component, missing/\n // wrong prop, bad enum, bindings); without it, parse-level (syntax/structure).\n const { diagnostics } = opts.manifest ? compile(source, opts.manifest) : parseJsx(source);\n for (const d of diagnostics) {\n findings.push({\n severity: d.severity,\n rule: `jsx-${d.code}`,\n where: d.tag ? `page \"${name}\" › <${d.tag}>` : `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: d.message,\n hint: 'The source is parsed (never executed) and compiled to the SDUI tree at save time — fix the JSX.',\n });\n }\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time syntax gate for `kind:'react'` pages (ADR-0081).\n//\n// A react page's `source` is REAL JavaScript/JSX executed at render by the\n// runtime — so the constrained JSX parser (validate-jsx-pages) cannot check it.\n// We instead transpile it with Sucrase (the same transpiler the runtime uses),\n// transpile-ONLY — never executed — to surface syntax errors at `os build`\n// instead of at render (ADR-0078: fail loudly at author time). It does NOT\n// validate runtime behaviour (a transpiling page can still throw at render);\n// the render-time error boundary owns that.\n\nimport { createRequire } from 'node:module';\nimport type { transform as sucraseTransform } from 'sucrase';\n\n// Sucrase must NOT be imported at module top level: it is ~1.5 MB of CJS\n// (~16 ms cold require), and @objectstack/lint sits on the kernel boot path —\n// while this gate only runs when a `kind:'react'` page is actually validated\n// (rare, trusted tier). Same boot-path contract as the TypeScript compiler in\n// validate-react-page-props.ts: loaded lazily, on first use, staying a regular\n// dependency in package.json. Guarded by lazy-deps.test.ts.\n//\n// `node:module` is a Node builtin, untouched by esbuild/tsup, so the static\n// `createRequire` import survives bundling; the `createRequire(...)` call is\n// deferred because `import.meta.url` is rewritten to an empty stub in the CJS\n// build (same pattern as driver-sqlite-wasm's knex-wasm-dialect).\nlet cachedTransform: typeof sucraseTransform | null = null;\nfunction loadSucraseTransform(): typeof sucraseTransform {\n if (cachedTransform) return cachedTransform;\n const anchor =\n typeof import.meta !== 'undefined' && import.meta.url\n ? import.meta.url\n : typeof __filename !== 'undefined'\n ? __filename\n : process.cwd() + '/';\n try {\n cachedTransform = (createRequire(anchor)('sucrase') as { transform: typeof sucraseTransform }).transform;\n } catch (err) {\n throw new Error(\n `@objectstack/lint: validating a kind:'react' page requires the \"sucrase\" package, which could not be loaded ` +\n `(${err instanceof Error ? err.message : String(err)}). It is a declared dependency of @objectstack/lint — ` +\n `if this deployment prunes packages, keep \"sucrase\" in the image; it is only loaded when a react-source page is validated.`,\n );\n }\n return cachedTransform;\n}\n\nexport type ReactPageSeverity = 'error' | 'warning';\n\nexport interface ReactPageFinding {\n severity: ReactPageSeverity;\n rule: string;\n where: string;\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\nexport function validateReactPages(stack: AnyRec): ReactPageFinding[] {\n const findings: ReactPageFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n if (!page || page.kind !== 'react') continue;\n const name = String(page.name ?? `#${p}`);\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') {\n findings.push({\n severity: 'error',\n rule: 'react-page-empty-source',\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: \"kind:'react' page has no `source`.\",\n hint: 'Author the page as a real React component string in `source`.',\n });\n continue;\n }\n // Outside the try below on purpose: a missing transpiler must surface as\n // an error, not be swallowed as a syntax finding.\n const transform = loadSucraseTransform();\n try {\n // transpile-only (no eval) — catches syntax errors, unterminated JSX, etc.\n transform(source, { transforms: ['jsx', 'typescript'], production: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n findings.push({\n severity: 'error',\n rule: 'react-page-syntax',\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: `kind:'react' source has a syntax error: ${message.split('\\n')[0]}`,\n hint: 'The source is transpiled (never executed) at build to catch syntax errors early — fix the JS/JSX.',\n });\n }\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time prop check for `kind:'react'` pages (ADR-0081 Phase 2). The syntax\n// gate (validate-react-pages) confirms the source parses; this confirms the\n// AUTHOR USED THE COMPONENT CONTRACT correctly — it parses the real JSX with the\n// TypeScript compiler, finds usages of the injected blocks (<ObjectForm>,\n// <ListView>, …), and checks each against the react-tier contract\n// (REACT_BLOCKS in @objectstack/spec):\n//\n// - missing a required binding prop (e.g. <ObjectForm> with no objectName)\n// → error. (Only the React-enforceable overlay props are required-checked;\n// a spread `{...props}` escapes the check since props may come from it.)\n// - a prop that is a near-miss (edit distance ≤ 2) of a known prop\n// (e.g. `onSucces` → `onSuccess`) → warning. We do NOT flag arbitrary\n// unknown props (the contract's data props are a curated subset) — only the\n// likely typos, to keep false positives near zero.\n// - <ObjectChart>'s data BINDINGS, by reading the attribute VALUES (#3701,\n// retargeted to the spec shape in #3729). See the block comment above\n// `checkObjectChart`.\n// - <ListView>'s `searchableFields` entries, resolved against the bound\n// object's declared fields (#4329) — the react-surface twin of the\n// metadata rule `searchable-field-unknown`, sharing its core.\n// - EVERY OTHER field-bearing prop a react block can author (#4340) — see\n// `REACT_FIELD_SPECS` below and the ledger beside it.\n//\n// Reading values is opt-in per block and per prop: everything below evaluates\n// only STATIC literals (`objectName=\"invoice\"`, an `aggregate={{…}}` object\n// literal). A value that comes from a variable, a call, or a spread is not\n// knowable at build time and is skipped silently — an unresolvable binding is\n// not a wrong one (ADR-0072 D1).\n\nimport { createRequire } from 'node:module';\nimport type ts from 'typescript';\nimport { REACT_BLOCKS, chartAggregateResultKeys } from '@objectstack/spec/ui';\nimport { VALID_AST_OPERATORS } from '@objectstack/spec/data';\nimport {\n checkSearchableFieldList,\n indexObjectSearchTargets,\n} from './validate-searchable-fields.js';\nimport {\n COMPONENT_FIELD_SPECS,\n RELATED_LIST_TYPE,\n checkFieldRefs,\n componentFieldRefs,\n fieldRefsFrom,\n indexObjectFields,\n relatedListFieldRefs,\n sortFieldRefs,\n type FieldRef,\n type PageFieldFinding,\n} from './validate-page-field-bindings.js';\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\n// The TypeScript compiler must NOT be imported at module top level: it is\n// ~9 MB of CJS (~70 ms+ to parse, worse on container cold starts), and\n// @objectstack/lint sits on the kernel boot path — while this gate only runs\n// when a `kind:'react'` page is actually validated (rare, trusted tier). An\n// eager import also hard-crashes boot in deployments that prune the package\n// from the image (cloud's Docker pruner did exactly that). So the compiler is\n// loaded lazily, on first use, and stays a regular dependency in package.json.\n// Guarded by lazy-deps.test.ts.\n//\n// `node:module` is a Node builtin, untouched by esbuild/tsup, so the static\n// `createRequire` import survives bundling; the `createRequire(...)` call is\n// deferred because `import.meta.url` is rewritten to an empty stub in the CJS\n// build (same pattern as driver-sqlite-wasm's knex-wasm-dialect).\nlet cachedTs: typeof ts | null = null;\nfunction loadTypeScript(): typeof ts {\n if (cachedTs) return cachedTs;\n const anchor =\n typeof import.meta !== 'undefined' && import.meta.url\n ? import.meta.url\n : typeof __filename !== 'undefined'\n ? __filename\n : process.cwd() + '/';\n try {\n cachedTs = createRequire(anchor)('typescript') as typeof ts;\n } catch (err) {\n throw new Error(\n `@objectstack/lint: validating a kind:'react' page requires the \"typescript\" package, which could not be loaded ` +\n `(${err instanceof Error ? err.message : String(err)}). It is a declared dependency of @objectstack/lint — ` +\n `if this deployment prunes packages, keep \"typescript\" in the image; it is only loaded when a react-source page is validated.`,\n );\n }\n return cachedTs;\n}\n\nexport type ReactPropSeverity = 'error' | 'warning';\n\nexport interface ReactPropFinding {\n severity: ReactPropSeverity;\n rule: string;\n where: string;\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\ninterface BlockSpec {\n requiredBindings: string[];\n knownProps: Set<string>;\n}\nconst BLOCKS: Map<string, BlockSpec> = new Map(\n (REACT_BLOCKS as Array<{ tag: string; interactions: Array<{ name: string; required?: boolean }> }>).map((b) => [\n b.tag,\n {\n requiredBindings: b.interactions.filter((i) => i.required).map((i) => i.name),\n knownProps: new Set(b.interactions.map((i) => i.name)),\n },\n ]),\n);\n\nfunction editDistance(a: string, b: string, cap = 2): number {\n if (Math.abs(a.length - b.length) > cap) return cap + 1;\n const dp = Array.from({ length: a.length + 1 }, (_, i) => i);\n for (let j = 1; j <= b.length; j++) {\n let prev = dp[0];\n dp[0] = j;\n for (let i = 1; i <= a.length; i++) {\n const tmp = dp[i];\n dp[i] = Math.min(dp[i] + 1, dp[i - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));\n prev = tmp;\n }\n }\n return dp[a.length];\n}\n\nfunction nearestKnown(prop: string, known: Set<string>): string | null {\n if (known.has(prop)) return null;\n let best: string | null = null;\n let bestD = 3;\n for (const k of known) {\n const d = editDistance(prop, k);\n if (d < bestD) { bestD = d; best = k; }\n }\n return bestD <= 2 ? best : null;\n}\n\n// ─── Static attribute values ──────────────────────────────────────────────\n//\n// The gate above only needed prop NAMES. Checking a binding needs its VALUE,\n// and a JSX attribute's value is an arbitrary expression. `NOT_STATIC` is the\n// sentinel for \"this expression is not knowable at build time\" — distinct from\n// a literal `undefined`, which IS knowable.\n\nconst NOT_STATIC = Symbol('not-static');\n\n/** Evaluate a JSX expression to a plain JS value, or `NOT_STATIC`. */\nfunction staticValue(tsc: typeof ts, sf: ts.SourceFile, node: ts.Node | undefined): unknown {\n if (!node) return NOT_STATIC;\n if (tsc.isParenthesizedExpression(node)) return staticValue(tsc, sf, node.expression);\n if (tsc.isStringLiteral(node) || tsc.isNoSubstitutionTemplateLiteral(node)) return node.text;\n if (tsc.isNumericLiteral(node)) return Number(node.text);\n if (node.kind === tsc.SyntaxKind.TrueKeyword) return true;\n if (node.kind === tsc.SyntaxKind.FalseKeyword) return false;\n if (node.kind === tsc.SyntaxKind.NullKeyword) return null;\n if (tsc.isArrayLiteralExpression(node)) {\n const out: unknown[] = [];\n for (const el of node.elements) {\n const v = staticValue(tsc, sf, el);\n if (v === NOT_STATIC) return NOT_STATIC;\n out.push(v);\n }\n return out;\n }\n if (tsc.isObjectLiteralExpression(node)) {\n const out: Record<string, unknown> = {};\n for (const p of node.properties) {\n // A shorthand (`{ field }`) or spread (`{ ...cfg }`) hides its value.\n if (!tsc.isPropertyAssignment(p)) return NOT_STATIC;\n const key = tsc.isIdentifier(p.name) || tsc.isStringLiteral(p.name) ? p.name.text : null;\n if (key === null) return NOT_STATIC;\n const v = staticValue(tsc, sf, p.initializer);\n if (v === NOT_STATIC) return NOT_STATIC;\n out[key] = v;\n }\n return out;\n }\n return NOT_STATIC;\n}\n\n/** The static value of one JSX attribute (`x=\"s\"` or `x={…}`), or `NOT_STATIC`. */\nfunction attrValue(tsc: typeof ts, sf: ts.SourceFile, attr: ts.JsxAttribute): unknown {\n const init = attr.initializer;\n if (!init) return true; // bare `showLegend` — JSX shorthand for `={true}`\n if (tsc.isStringLiteral(init)) return init.text;\n if (tsc.isJsxExpression(init)) return staticValue(tsc, sf, init.expression);\n return NOT_STATIC;\n}\n\n/**\n * The value of a FILTER attribute, resolved position by position: an array\n * literal survives even when some of its elements do not, each unknowable\n * element left as `NOT_STATIC` in place.\n *\n * `staticValue` collapses such an array to `NOT_STATIC` whole, which is correct\n * where the value only means something entire (an `aggregate={{…}}`) and wrong\n * for a filter, whose positions are independent: `['status', '=', stage]` has a\n * knowable FIELD beside an unknowable VALUE, and that is the shape a react page\n * writes when it drives a list from React state. See `filterFieldRefs`.\n */\nfunction filterAttrValue(tsc: typeof ts, sf: ts.SourceFile, attr: ts.JsxAttribute): unknown {\n const init = attr.initializer;\n if (!init || !tsc.isJsxExpression(init)) return NOT_STATIC;\n const perPosition = (node: ts.Node | undefined): unknown => {\n if (!node) return NOT_STATIC;\n if (tsc.isParenthesizedExpression(node)) return perPosition(node.expression);\n if (tsc.isArrayLiteralExpression(node)) return node.elements.map((el) => perPosition(el));\n return staticValue(tsc, sf, node);\n };\n return perPosition(init.expression);\n}\n\n// ─── <ObjectChart> binding integrity (#3701) ──────────────────────────────\n//\n// `validate-chart-bindings` covers every DATASET-bound chart surface: a\n// dataset declares its dimensions/measures by NAME, result rows are keyed by\n// those names, and an axis is checked against them. The react `<ObjectChart>`\n// block was excluded because it is OBJECT-bound — `objectName` + an inline\n// `aggregate` — and nothing said what the aggregated result columns were\n// called, so `xAxisKey`/`series[].dataKey` had nothing to resolve against.\n//\n// #3701 closed that: `chartAggregateResultKeys` in @objectstack/spec/ui now\n// records the convention every renderer already implements (rows keyed by the\n// RAW FIELD NAMES — `groupBy` for the category, `field` for the value, the\n// literal `'count'` for a fieldless count). With the columns pinned, both\n// halves of the binding are checkable:\n//\n// * `aggregate.field` / `aggregate.groupBy` are RAW FIELD names → check them\n// against the object's declared fields.\n// * the axes are RESULT COLUMN names → check them against what this\n// aggregate produces.\n//\n// The axes are read in the SPEC spelling — `xAxis.field`, `yAxis[].field`,\n// `series[].name` (#3729). #3701 had to read `xAxisKey`/`series[].dataKey`\n// because those were the only spellings the renderer honored; objectui#2880\n// made it honor ChartConfig, so the gate follows the protocol again. The\n// internal spellings are still accepted, silently: dashboards and the console's\n// own chart-view wiring emit them, and they remain a valid (if unpublished)\n// way to write the same binding.\n\nexport const REACT_CHART_FIELD_UNKNOWN = 'react-chart-field-unknown';\nexport const REACT_CHART_AGGREGATE_INVALID = 'react-chart-aggregate-invalid';\nexport const REACT_CHART_AXIS_UNKNOWN = 'react-chart-axis-unknown';\n\nconst CHART_FUNCTIONS = ['count', 'sum', 'avg', 'min', 'max'] as const;\n\nconst isRec = (v: unknown): v is Record<string, unknown> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\nconst strOf = (v: unknown): string | undefined =>\n typeof v === 'string' && v.length > 0 ? v : undefined;\n\ninterface ChartAttrs {\n /** Statically resolved attribute values, keyed by prop name. */\n values: Map<string, unknown>;\n where: string;\n path: string;\n}\n\nfunction checkObjectChart(\n attrs: ChartAttrs,\n objectFields: Map<string, Set<string>>,\n findings: ReactPropFinding[],\n): void {\n const { values, where, path } = attrs;\n const push = (severity: ReactPropSeverity, rule: string, message: string, hint: string) =>\n findings.push({ severity, rule, where, path, message, hint });\n\n // Inline `data` wins over the aggregate query: the columns then come from\n // the author's own rows, which this rule cannot see. Nothing further to say.\n if (values.has('data')) return;\n\n const aggregate = values.get('aggregate');\n if (aggregate === undefined || aggregate === NOT_STATIC) return;\n if (!isRec(aggregate)) return;\n\n const fn = strOf(aggregate.function);\n const field = strOf(aggregate.field);\n const groupBy = aggregate.groupBy;\n const groupByField = strOf(groupBy) ?? (isRec(groupBy) ? strOf(groupBy.field) : undefined);\n\n // 1. The aggregate declaration itself.\n if (fn && !(CHART_FUNCTIONS as readonly string[]).includes(fn)) {\n push(\n 'error',\n REACT_CHART_AGGREGATE_INVALID,\n `aggregate.function \"${fn}\" is not an aggregation this chart can run.`,\n `Use one of: ${CHART_FUNCTIONS.join(', ')}.`,\n );\n } else if (fn && fn !== 'count' && !field) {\n push(\n 'error',\n REACT_CHART_AGGREGATE_INVALID,\n `aggregate.function \"${fn}\" has no \"field\" to aggregate.`,\n 'Add aggregate.field, or use function \"count\" (the only one that may omit it).',\n );\n }\n\n // 2. `field` / `groupBy` are RAW field names on the bound object.\n const objectName = strOf(values.get('objectName'));\n const known = objectName ? objectFields.get(objectName) : undefined;\n // No object name, or an object declared in another package: unknowable here\n // — the same skip the widget/flow/page rules take.\n if (objectName && known) {\n const fieldRef = (name: string | undefined, prop: string) => {\n if (!name) return;\n // A relationship path (`account.name`) is resolved by the query engine.\n if (name.includes('.')) return;\n if (known.has(name) || SYSTEM_FIELDS.has(name)) return;\n push(\n 'error',\n REACT_CHART_FIELD_UNKNOWN,\n `aggregate.${prop} \"${name}\" is not a field on object \"${objectName}\" — ` +\n `the aggregate query has nothing to ${prop === 'groupBy' ? 'group by' : 'aggregate'}, so the chart comes back empty.`,\n `Fix the field name, or add \"${name}\" to ${objectName}.` +\n (known.size > 0 ? ` Object fields: ${[...known].sort().join(', ')}.` : ''),\n );\n };\n fieldRef(field, 'field');\n fieldRef(groupByField, 'groupBy');\n }\n\n // 3. The axes name RESULT COLUMNS, not fields — the #3701 convention.\n const keys = chartAggregateResultKeys({ field, function: fn, groupBy });\n const columns = [keys.category, keys.value].filter((k): k is string => !!k);\n if (columns.length === 0) return; // an aggregate too incomplete to judge against\n\n const axisRef = (name: string | undefined, prop: string) => {\n if (!name) return;\n if (columns.includes(name)) return;\n // The comparison overlay's column only exists when `compareTo` is on, but\n // binding it is legitimate — never flag it as unknown.\n if (keys.comparison && name === keys.comparison) return;\n push(\n 'error',\n REACT_CHART_AXIS_UNKNOWN,\n `\"${name}\" is not a column this aggregate returns, so the axis plots nothing. ` +\n `Object-bound aggregate rows are keyed by the RAW FIELD NAMES ` +\n `(unlike a dataset, whose rows are keyed by measure name).`,\n `Result columns: ${columns.join(', ')}` +\n (keys.comparison ? ` (plus \"${keys.comparison}\" with a comparison overlay)` : '') +\n `. Bind ${prop} to one of them.`,\n );\n };\n\n // The category axis: spec `xAxis: { field }`, the report surface's bare\n // string, or the internal `xAxisKey`. All three name the same column.\n const xAxisRaw = values.get('xAxis');\n const categoryAxis =\n strOf(values.get('xAxisKey')) ??\n strOf(xAxisRaw) ??\n (isRec(xAxisRaw) ? strOf(xAxisRaw.field) : undefined);\n const categoryProp = values.has('xAxisKey') ? 'xAxisKey' : 'xAxis.field';\n axisRef(categoryAxis, categoryProp);\n\n // The value axes: spec `yAxis: [{ field }]` (or a single object / bare\n // string) and spec `series: [{ name }]` / internal `series: [{ dataKey }]`.\n const yAxisRaw = values.get('yAxis');\n const yAxisList = Array.isArray(yAxisRaw) ? yAxisRaw : yAxisRaw !== undefined ? [yAxisRaw] : [];\n for (const a of yAxisList) {\n axisRef(strOf(a) ?? (isRec(a) ? strOf(a.field) : undefined), 'yAxis[].field');\n }\n\n const series = values.get('series');\n if (Array.isArray(series)) {\n for (const s of series) {\n if (!isRec(s)) continue;\n const dataKey = strOf(s.dataKey);\n axisRef(dataKey ?? strOf(s.name), dataKey ? 'series[].dataKey' : 'series[].name');\n }\n }\n\n // A category axis bound to anything but the groupBy is always wrong, and the\n // check above lets it through when it happens to equal the VALUE column.\n if (categoryAxis && keys.category && categoryAxis !== keys.category && categoryAxis === keys.value) {\n push(\n 'error',\n REACT_CHART_AXIS_UNKNOWN,\n `${categoryProp} \"${categoryAxis}\" is the aggregate's VALUE column, not its category column.`,\n `The category axis is keyed by groupBy — bind it to \"${keys.category}\".`,\n );\n }\n}\n\n// ─── Field-bearing block props (#4340) ────────────────────────────────────\n//\n// #4329 closed ONE prop — `<ListView searchableFields>` — by running the\n// metadata rule's core from here. `searchableFields` was an instance, not the\n// class: every other prop a react block binds BY FIELD NAME shipped exactly as\n// typed, the same silent drift `validate-page-field-bindings` closes for the\n// page-component `properties` bag one surface over. This section closes the\n// class.\n//\n// ## Where the answers come from\n//\n// The `record:*` blocks ARE the components that rule already walks — one\n// registry component, two authoring surfaces — so they are not re-described\n// here at all: `componentFieldRefs` / `relatedListFieldRefs` read the SAME\n// `COMPONENT_FIELD_SPECS` table, keyed by the block's own `schemaType`. A prop\n// added there is checked on both surfaces at once, which is the point.\n//\n// `REACT_FIELD_SPECS` below covers only what the shared table cannot: the two\n// blocks whose metadata twin lives under different prop names —\n// `<ListView>` (twin: a list page's `interfaceConfig`) and `<ObjectForm>`\n// (twin: `element:form` + the form-layout rule).\n//\n// ## What is deliberately NOT checked, and why\n//\n// - Anything non-static (a variable, a call, a value behind a spread) —\n// ADR-0072 D1: unresolvable is not wrong. `filters` is the one place this\n// is resolved PER POSITION rather than all-or-nothing; see below.\n// - `<RecordRelatedList relationshipValueField>`: it names a field on the\n// PARENT object, and the react surface binds the parent by `recordId`\n// only — there is no parent OBJECT to resolve against. The metadata twin\n// has the page's object and checks it there. This is the ONE field-bearing\n// prop in the index that stays unresolved, and the reason is a missing\n// binding rather than a missing rule.\n// - `<ObjectChart>`'s axes: they name the aggregate's RESULT COLUMNS, not\n// fields, and `checkObjectChart` above already owns them.\n//\n// `<ObjectForm subforms>` does not ride the table either, but IS checked:\n// each entry names its own `childObject`, so its refs are split per entry\n// rather than pooled against the block's object (`subformFieldRefs`).\n\n/**\n * Which props of a block carry field names, and in what shape. Each bucket is\n * a different SHAPE, not a different meaning — every entry resolves against the\n * block's own `objectName`.\n */\ninterface ReactFieldSpec {\n /** Bare names, `{field}`/`{name}` records, or arrays of either. */\n fields?: readonly string[];\n /** A `sort`: structured `{field,order}[]` or the legacy `\"field desc\"` string. */\n sorts?: readonly string[];\n /** A `{ fields: […] }` wrapper — one level of nesting. */\n nestedFields?: readonly string[];\n /** `{…}[]` sections whose `fields[]` name fields. */\n sections?: readonly string[];\n /** An object literal whose KEYS name fields. */\n keyedByField?: readonly string[];\n /** An ObjectQL FilterArray — its field POSITIONS gate (see `filterFieldRefs`). */\n filterArrays?: readonly string[];\n}\n\nconst REACT_FIELD_SPECS: Readonly<Record<string, ReactFieldSpec>> = {\n ListView: {\n // `fields` is the React overlay's \"limit/order the columns\"; `columns` the\n // spec ListView prop. Both name columns on the bound object, and a page may\n // write either. `hiddenFields`/`fieldOrder`/`filterableFields` are schema\n // props outside the curated contract — unadvertised but honored by the\n // renderer, so a stale name there is drift just the same.\n fields: ['fields', 'columns', 'hiddenFields', 'fieldOrder', 'filterableFields'],\n sorts: ['sort'],\n nestedFields: ['userFilters', 'grouping'],\n filterArrays: ['filters'],\n },\n ObjectForm: {\n fields: ['fields'],\n keyedByField: ['initialValues'],\n // `groups` is FormViewSchema's legacy alias for `sections`.\n sections: ['sections', 'groups'],\n },\n ObjectChart: {\n // The axes are result columns (checkObjectChart owns them); `filter` is an\n // ordinary ObjectQL predicate over the bound object, like ListView's.\n filterArrays: ['filter'],\n },\n};\n\n/**\n * How a prop name joins onto a react page's `path`. The props live inside one\n * opaque `source` string, so there is no config path to extend — #4329\n * established `pages[0].source › searchableFields[1]` and every prop below\n * follows it.\n */\nconst PATH_SEP = ' › ';\n\n/** tag → `schemaType`, read from the contract rather than restated. */\nconst SCHEMA_TYPE_BY_TAG: ReadonlyMap<string, string> = new Map(\n (REACT_BLOCKS as Array<{ tag: string; schemaType: string }>).map((b) => [b.tag, b.schemaType]),\n);\n\n/**\n * Attribute names read POSITION BY POSITION rather than all-or-nothing (see\n * `filterAttrValue`). Derived from the specs so a new `filterArrays` entry\n * cannot forget to opt in — the failure mode would be silent, since the\n * all-or-nothing reader simply finds nothing.\n *\n * `<RecordRelatedList filter>` rides along under the same name while holding a\n * different shape (`{field, operator, value}[]`, not a FilterArray). That is\n * harmless: a fully-static array reads identically either way, and the only\n * difference — surviving with `NOT_STATIC` holes — is dropped by\n * `fieldRefsFrom`, which ignores a non-string, non-record entry.\n */\nconst FILTER_PROPS: ReadonlySet<string> = new Set(\n Object.values(REACT_FIELD_SPECS).flatMap((s) => s.filterArrays ?? []),\n);\n\n/** Strip the `NOT_STATIC` sentinel so a shared extractor sees plain data. */\nfunction readableProps(values: ReadonlyMap<string, unknown>): AnyRec {\n const out: AnyRec = {};\n for (const [k, v] of values) if (v !== NOT_STATIC) out[k] = v;\n return out;\n}\n\n/**\n * `<ObjectForm subforms>` — inline master-detail child collections. Each entry\n * names the object its own refs resolve against (`childObject`), so unlike\n * every bucket in {@link ReactFieldSpec} these cannot be pooled into one batch\n * checked against the block's `objectName`. `totalField` is the exception\n * inside the exception: it names the PARENT field the child sum rolls up into.\n */\nfunction subformFieldRefs(\n value: unknown,\n basePath: string,\n): { child: Array<{ objectName: string | undefined; refs: FieldRef[] }>; parent: FieldRef[] } {\n const child: Array<{ objectName: string | undefined; refs: FieldRef[] }> = [];\n const parent: FieldRef[] = [];\n if (!Array.isArray(value)) return { child, parent };\n for (let i = 0; i < value.length; i++) {\n const sub = value[i];\n if (!isRec(sub)) continue;\n const at = (key: string) => `${basePath}[${i}].${key}`;\n child.push({\n objectName: strOf(sub.childObject),\n refs: [\n ...fieldRefsFrom(sub.columns, at('columns')),\n ...fieldRefsFrom(sub.relationshipField, at('relationshipField')),\n ...fieldRefsFrom(sub.amountField, at('amountField')),\n ],\n });\n parent.push(...fieldRefsFrom(sub.totalField, at('totalField')));\n }\n return { child, parent };\n}\n\n/**\n * Field references in an ObjectQL FilterArray, resolved PER POSITION.\n *\n * `staticValue` is all-or-nothing by design: an array containing one unknowable\n * element is not a knowable array. That is right for an `aggregate={{…}}`, and\n * wrong here, because the common react filter is exactly the mixed case —\n * `filters={['status', '=', stage]}` pairs a STATIC field position with a\n * React-state value. Bailing on the whole array would skip the only position\n * this rule can judge, on the shape authors actually write.\n *\n * So the reader keeps each position separate (`filterAttrValue`) and this walk\n * only ever reads position 0, and only when position 1 is a recognised operator\n * — the same test `isFilterAST` makes, using the spec's own operator vocabulary\n * so the two cannot drift. A non-static field position, a non-static operator,\n * or a shape that is not a filter node yields nothing.\n */\nfunction filterFieldRefs(node: unknown, basePath: string, out: FieldRef[]): void {\n if (!Array.isArray(node) || node.length === 0) return;\n const head = node[0];\n if (typeof head === 'string' && (head.toLowerCase() === 'and' || head.toLowerCase() === 'or')) {\n for (let i = 1; i < node.length; i++) filterFieldRefs(node[i], `${basePath}[${i}]`, out);\n return;\n }\n // Legacy flat array of conditions: `[[a,'=',1], [b,'>',2]]`.\n if (Array.isArray(head)) {\n for (let i = 0; i < node.length; i++) filterFieldRefs(node[i], `${basePath}[${i}]`, out);\n return;\n }\n if (\n typeof head === 'string' && head.length > 0 &&\n node.length >= 2 && typeof node[1] === 'string' &&\n VALID_AST_OPERATORS.has(node[1].toLowerCase())\n ) {\n out.push({ name: head, path: `${basePath}[0]` });\n }\n}\n\n/** The field refs one block's statically-read attributes hold, by bucket. */\nfunction reactFieldRefs(\n spec: ReactFieldSpec,\n values: ReadonlyMap<string, unknown>,\n basePath: string,\n): { own: FieldRef[]; queried: FieldRef[] } {\n const own: FieldRef[] = [];\n const queried: FieldRef[] = [];\n const readable = (key: string): unknown => {\n const v = values.get(key);\n return v === NOT_STATIC ? undefined : v;\n };\n const at = (key: string) => `${basePath}${PATH_SEP}${key}`;\n\n for (const key of spec.fields ?? []) {\n own.push(...fieldRefsFrom(readable(key), at(key)));\n }\n for (const key of spec.sorts ?? []) {\n own.push(...sortFieldRefs(readable(key), at(key)));\n }\n for (const key of spec.nestedFields ?? []) {\n const v = readable(key);\n if (isRec(v)) own.push(...fieldRefsFrom(v.fields, at(`${key}.fields`)));\n }\n for (const key of spec.sections ?? []) {\n const v = readable(key);\n if (!Array.isArray(v)) continue;\n for (let i = 0; i < v.length; i++) {\n const section = v[i];\n if (!isRec(section)) continue;\n own.push(...fieldRefsFrom(section.fields, at(`${key}[${i}].fields`)));\n }\n }\n for (const key of spec.keyedByField ?? []) {\n const v = readable(key);\n if (!isRec(v)) continue;\n for (const k of Object.keys(v)) own.push({ name: k, path: at(`${key}.${k}`) });\n }\n for (const key of spec.filterArrays ?? []) {\n // Read the raw attribute here, NOT `readable`: a filter whose VALUE is\n // non-static still has a knowable field position, and `filterAttrValue`\n // preserved exactly that.\n filterFieldRefs(values.get(key), at(key), queried);\n }\n return { own, queried };\n}\n\n/**\n * Resolve every field-bearing prop of one block usage against its bound object.\n *\n * Three sources feed it, in the order a block can claim them:\n *\n * 1. `REACT_FIELD_SPECS` — the react-only descriptors (`ListView`,\n * `ObjectForm`, `ObjectChart`'s `filter`).\n * 2. the `record:related_list` split, when the block IS that component.\n * 3. `COMPONENT_FIELD_SPECS`, keyed by the block's `schemaType` — the shared\n * table the metadata surface already uses. `<Block type=\"…\">` reaches it\n * by the type the author wrote, which is what makes the escape hatch\n * checked rather than a hole.\n *\n * Findings come back in this rule's own shape but under the metadata rule's id\n * (`page-field-unknown`): the same question, asked of the same component, with\n * the same fix.\n */\nfunction checkBlockFieldProps(\n tag: string,\n values: ReadonlyMap<string, unknown>,\n objectFields: ReadonlyMap<string, Set<string>>,\n where: string,\n path: string,\n): ReactPropFinding[] {\n const objectName = strOf(values.get('objectName'));\n const out: PageFieldFinding[] = [];\n\n const spec = REACT_FIELD_SPECS[tag];\n if (spec) {\n const { own, queried } = reactFieldRefs(spec, values, path);\n out.push(...checkFieldRefs(own, objectName, objectFields, where));\n out.push(...checkFieldRefs(queried, objectName, objectFields, where, 'queried'));\n }\n\n if (tag === 'ObjectForm') {\n const raw = values.get('subforms');\n const subs = subformFieldRefs(raw === NOT_STATIC ? undefined : raw, `${path}${PATH_SEP}subforms`);\n for (const sub of subs.child) {\n out.push(...checkFieldRefs(sub.refs, sub.objectName, objectFields, where));\n }\n // `totalField` names the FORM object's field the child sum rolls up into.\n out.push(...checkFieldRefs(subs.parent, objectName, objectFields, where));\n }\n\n // `<Block type=\"record:highlights\">` renders the registered component the\n // author names; every other block's type is fixed by its tag.\n const schemaType = tag === 'Block' ? strOf(values.get('type')) : SCHEMA_TYPE_BY_TAG.get(tag);\n if (schemaType) {\n const props = readableProps(values);\n if (schemaType === RELATED_LIST_TYPE) {\n const split = relatedListFieldRefs(props, path, PATH_SEP);\n out.push(...checkFieldRefs(split.related, split.relatedObject, objectFields, where));\n out.push(...checkFieldRefs(split.picker, split.pickerObject, objectFields, where));\n // `split.parent` (`relationshipValueField`) is deliberately dropped: the\n // react surface binds the parent RECORD (`recordId`) but never its\n // object, so there is nothing to resolve it against. See the section note.\n } else if (COMPONENT_FIELD_SPECS[schemaType]) {\n out.push(\n ...checkFieldRefs(\n componentFieldRefs(schemaType, props, path, PATH_SEP) ?? [],\n objectName,\n objectFields,\n where,\n ),\n );\n }\n }\n\n // The two finding shapes are structurally identical; `where`/`path` are\n // already this surface's, so only the declared type differs.\n return out as ReactPropFinding[];\n}\n\nexport function validateReactPageProps(stack: AnyRec): ReactPropFinding[] {\n const findings: ReactPropFinding[] = [];\n const objectFields = indexObjectFields(stack);\n // A separate index for the searchableFields check, built by the metadata\n // rule's own indexer: it keeps `null` for an object with no authored field\n // map (external / datasource-introspected), a distinction `indexObjectFields`\n // flattens — and one this check must honor so both surfaces skip alike.\n const searchTargets = indexObjectSearchTargets(stack);\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n if (!page || page.kind !== 'react') continue;\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') continue;\n const name = String(page.name ?? `#${p}`);\n\n // Outside the try below on purpose: a missing compiler must surface as an\n // error, not be swallowed as \"unparseable source\".\n const tsc = loadTypeScript();\n\n let sf: ts.SourceFile;\n try {\n sf = tsc.createSourceFile('page.tsx', source, tsc.ScriptTarget.Latest, true, tsc.ScriptKind.TSX);\n } catch {\n continue; // the syntax gate reports unparseable sources\n }\n\n const visit = (node: ts.Node): void => {\n if (tsc.isJsxOpeningElement(node) || tsc.isJsxSelfClosingElement(node)) {\n const tag = node.tagName.getText(sf);\n const block = BLOCKS.get(tag);\n if (block) {\n let hasSpread = false;\n const used = new Set<string>();\n const values = new Map<string, unknown>();\n for (const a of node.attributes.properties) {\n if (tsc.isJsxSpreadAttribute(a)) { hasSpread = true; continue; }\n if (tsc.isJsxAttribute(a)) {\n const propName = a.name.getText(sf);\n used.add(propName);\n values.set(\n propName,\n FILTER_PROPS.has(propName)\n ? filterAttrValue(tsc, sf, a)\n : attrValue(tsc, sf, a),\n );\n }\n }\n const where = `page \"${name}\" › <${tag}>`;\n const path = `pages[${p}].source`;\n if (!hasSpread) {\n for (const req of block.requiredBindings) {\n if (!used.has(req)) {\n findings.push({\n severity: 'error',\n rule: 'react-prop-missing-required',\n where, path,\n message: `<${tag}> is missing the required prop \"${req}\".`,\n hint: `Pass ${req}={…}. See the react-tier component contract.`,\n });\n }\n }\n }\n for (const u of used) {\n const near = nearestKnown(u, block.knownProps);\n if (near) {\n findings.push({\n severity: 'warning',\n rule: 'react-prop-typo',\n where, path,\n message: `<${tag}> has prop \"${u}\" — did you mean \"${near}\"?`,\n hint: 'Likely a typo of a contract prop. Fix it or remove it.',\n });\n }\n }\n // A spread can supply any of the bindings below, so the values we\n // can see are an incomplete picture — skip rather than guess.\n if (tag === 'ObjectChart' && !hasSpread) {\n checkObjectChart({ values, where, path }, objectFields, findings);\n }\n // <ListView searchableFields> names fields on the bound object — the\n // react-surface twin of `searchable-field-unknown` (#4329). It runs\n // the metadata rule's own core, so the skips (cross-package object,\n // no authored field map, system columns) and the dotted-path\n // strictness match by construction. A non-static value — either\n // attribute — bails inside the checker: unresolvable is not wrong.\n if (tag === 'ListView' && !hasSpread) {\n findings.push(\n ...checkSearchableFieldList(\n values.get('searchableFields'),\n strOf(values.get('objectName')),\n searchTargets,\n where,\n `${path} › searchableFields`,\n 'searchableFields',\n ),\n );\n }\n // Every other field-bearing prop (#4340). Same skips as the chart\n // and searchableFields checks: a spread hides the picture, and a\n // non-static value is unresolvable rather than wrong.\n if (!hasSpread) {\n findings.push(\n ...checkBlockFieldProps(tag, values, objectFields, where, path),\n );\n }\n }\n }\n tsc.forEachChild(node, visit);\n };\n visit(sf);\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0061 — searchable set] `searchableFields` entries must name a field the\n * object actually has.\n *\n * `searchableFields` is `z.array(z.string())` in both `object.zod.ts` and the\n * list-view schema, so nothing checks that an entry resolves to anything. Rename\n * a field and the old name stays behind: Zod-valid, shipped, and pointing at a\n * column that no longer exists.\n *\n * ── Why a stale entry is not merely inert ────────────────────────────────\n *\n * The engine tolerates it. `resolveSearchFields` (`@objectstack/objectql`)\n * filters the declaration down to fields that exist —\n * `searchableFields?.filter((f) => all[f])` — so a stale name is dropped\n * without a word. That tolerance is what makes the drift invisible, and it\n * fails in the direction nobody expects:\n *\n * - Some entries stale → `$search` quietly scans a NARROWER set than the\n * object declares. Records that should match do not, and the response is\n * indistinguishable from \"no such record\".\n * - EVERY entry stale → the filtered set is empty, so resolution falls\n * through to the AUTO-DEFAULT (name/title + short-text fields). A\n * declaration whose entire purpose is to CHOOSE the searchable set ends up\n * selecting a set the author never wrote — the same \"asked narrower,\n * answered wider\" inversion #4226 closed on the projection axis.\n *\n * And it does not stay quiet downstream. objectui's list search echoes\n * `schema.searchableFields` verbatim as the `$searchFields` override, so once\n * the REST read path validates that override against the object (#4254), a\n * stale declaration the engine had been silently skipping becomes a `400\n * INVALID_FIELD` on every list search for that object — a request-time break\n * whose cause is an authoring typo made long before.\n *\n * Hence `error`, not the advisory level the other field-existence rules use\n * (`page-field-unknown`, `form-field-unknown`, `semantic-role-field-unknown`\n * are all warnings). Those describe a consumer that SKIPS an unknown name and\n * renders the rest; this one describes a declaration that either selects the\n * wrong set or refuses the request outright. It is the same call\n * `validate-flow-template-paths` makes for a filter-position token: gating when\n * the miss widens the query rather than shrinking the page.\n *\n * ── What is checked, and what is deliberately not ────────────────────────\n *\n * Existence only. A field that exists but is an odd search target (a `json`\n * column, say) is NOT flagged: an explicit `searchableFields` is authoritative\n * — the engine scans exactly what it names — so declaring one is a choice, not\n * a mistake. Only a name resolving to no field at all is drift.\n *\n * Three skips keep false positives near zero (ADR-0072 D1 — one dead finding\n * and authors stop trusting the linter):\n *\n * 1. An object this stack does not define. It may come from another package,\n * and a field map we cannot see cannot be judged (the same skip the\n * page/flow/widget rules take).\n * 2. An object that declares no field map at all — external objects and\n * datasource-introspected schemas whose columns are resolved at runtime.\n * 3. Registry-injected system columns, which are searchable at runtime but\n * never appear in authored `fields` — the package-shared `SYSTEM_FIELDS`\n * (`system-fields.ts`), derived from the spec's own declarations rather\n * than hand-copied (#4330).\n *\n * Dotted paths are NOT skipped here, unlike every sibling rule. Elsewhere\n * `owner_id.name` is left alone because the query engine resolves the traversal;\n * search does not — `resolveSearchFields` matches the field map by exact string,\n * so a dotted entry is dropped exactly like a typo. Skipping it would exempt the\n * one wrong spelling most likely to be borrowed from `select`/`sort`.\n */\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\nexport const SEARCHABLE_FIELD_UNKNOWN = 'searchable-field-unknown';\n\nexport type SearchableFieldSeverity = 'error' | 'warning';\n\nexport interface SearchableFieldFinding {\n /** Always `error` — a stale entry narrows, widens or refuses the search (see module note). */\n severity: SearchableFieldSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `object \"crm_lead\"`. */\n where: string;\n /** Config path, e.g. `objects[0].searchableFields[2]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * The declared field NAMES of an object, or `null` when the object declares no\n * readable field map (external / introspected — nothing to judge against).\n * Reads both shapes: the name-keyed map and the legacy array of `{ name }`.\n */\nfunction declaredFieldNames(obj: AnyRec): Set<string> | null {\n const fields = obj.fields;\n if (!fields || typeof fields !== 'object') return null;\n const names = new Set<string>();\n for (const f of asArray(fields)) {\n const n = strName(f.name);\n if (n) names.add(n);\n }\n return names.size > 0 ? names : null;\n}\n\n/** Levenshtein-bounded \"did you mean?\" over the object's own field names. */\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\n/**\n * object name → declared field names. `null` marks an object with no readable\n * field map, so \"declared nothing\" stays distinguishable from \"not in stack\".\n * Exported alongside `checkSearchableFieldList` so every surface that authors\n * a searchable set resolves against the identical index (#4329).\n */\nexport function indexObjectSearchTargets(\n stack: Record<string, unknown>,\n): Map<string, Set<string> | null> {\n const fieldsByObject = new Map<string, Set<string> | null>();\n if (!isRec(stack)) return fieldsByObject;\n for (const obj of asArray(stack.objects)) {\n const name = strName(obj.name);\n if (name) fieldsByObject.set(name, declaredFieldNames(obj));\n }\n return fieldsByObject;\n}\n\n/**\n * Check one `searchableFields` array against the field map `fieldsByObject`\n * holds for `objectName` — the shared core behind every surface that authors a\n * searchable set: the object/list-view metadata walked by\n * `validateSearchableFields` below, and the react page surface\n * (`<ListView searchableFields={…}>`, `validate-react-page-props`), which\n * reuses it so the two surfaces agree on what counts as a field — same three\n * skips, same dotted-path strictness (#4329).\n *\n * `subject` names the declaration for the message, since an object's own set\n * and a view's narrowing of it are fixed differently; the entry index is\n * appended to `path` so the author can go straight to the stale name.\n */\nexport function checkSearchableFieldList(\n declared: unknown,\n objectName: string | undefined,\n fieldsByObject: ReadonlyMap<string, Set<string> | null>,\n where: string,\n path: string,\n subject: string,\n): SearchableFieldFinding[] {\n const findings: SearchableFieldFinding[] = [];\n if (!Array.isArray(declared) || declared.length === 0) return findings;\n if (!objectName) return findings; // nothing to resolve against\n if (!fieldsByObject.has(objectName)) return findings; // ① object from another package\n const known = fieldsByObject.get(objectName);\n if (!known) return findings; // ② external / introspected — no authored field map\n\n for (let i = 0; i < declared.length; i++) {\n const entry = declared[i];\n // Pre-parse input may carry junk here; a non-string is a SHAPE error the\n // schema owns, not a dangling reference.\n const name = strName(entry);\n if (!name) continue;\n if (known.has(name) || SYSTEM_FIELDS.has(name)) continue; // ③ system column\n\n const dotted = name.includes('.');\n findings.push({\n severity: 'error',\n rule: SEARCHABLE_FIELD_UNKNOWN,\n where,\n path: `${path}[${i}]`,\n message:\n `${subject} entry \"${name}\" is not a field on object \"${objectName}\". ` +\n `The declaration is stale: searching it can never match, and the engine ` +\n `silently drops it — leaving a narrower search than declared, or the ` +\n `auto-default set once every entry is dropped.` +\n (dotted ? '' : suggest(name, known)),\n hint:\n (dotted\n ? `'search' scans this object's own columns, so a related record's ` +\n `column cannot be a search target — expand the relation and search ` +\n `the related object, or copy the value onto a formula field here. `\n : `Fix the name, or add \"${name}\" to ${objectName}.fields. `) +\n `Clients echo this declaration verbatim as the '$searchFields' ` +\n `override, so a stale entry becomes a 400 INVALID_FIELD on list ` +\n `search (#4254), not just a quietly narrowed one.` +\n (known.size > 0 ? ` Object fields: ${[...known].sort().join(', ')}.` : ''),\n });\n }\n return findings;\n}\n\n/**\n * Validate every `searchableFields` declaration in the stack — the object's own\n * (the canonical set, ADR-0061) and the list views that narrow it. Returns\n * findings (empty = clean).\n *\n * The react page surface (`<ListView searchableFields={…}>`) is deliberately\n * NOT walked here: its declaration lives inside JSX source, and\n * `validate-react-page-props` — the gate that already parses that source —\n * runs the same `checkSearchableFieldList` core on it (#4329).\n */\nexport function validateSearchableFields(stack: AnyRec): SearchableFieldFinding[] {\n const findings: SearchableFieldFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const objects = asArray(stack.objects);\n const fieldsByObject = indexObjectSearchTargets(stack);\n\n const check = (\n declared: unknown,\n objectName: string | undefined,\n where: string,\n path: string,\n subject: string,\n ) => {\n findings.push(\n ...checkSearchableFieldList(declared, objectName, fieldsByObject, where, path, subject),\n );\n };\n\n // ── The object's own canonical set, and its built-in named list views ──\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!isRec(obj)) continue;\n const objName = strName(obj.name);\n const label = objName ? `object \"${objName}\"` : `objects[${oi}]`;\n\n check(\n obj.searchableFields,\n objName,\n label,\n `objects[${oi}].searchableFields`,\n 'searchableFields',\n );\n\n if (isRec(obj.listViews)) {\n for (const [key, lv] of Object.entries(obj.listViews)) {\n if (!isRec(lv)) continue;\n check(\n lv.searchableFields,\n // A built-in list view belongs to its object; an inline `data.object`\n // may still retarget it (ADR-0047 allows the explicit binding).\n listViewObject(lv) ?? objName,\n `${label} › listViews.${key}`,\n `objects[${oi}].listViews.${key}.searchableFields`,\n 'list-view searchableFields',\n );\n }\n }\n }\n\n // ── `defineView` aggregates: the default `list` + named `listViews` ──\n const views = asArray(stack.views);\n for (let vi = 0; vi < views.length; vi++) {\n const view = views[vi];\n if (!isRec(view)) continue;\n const viewLabel = strName(view.name) ?? strName(view.objectName) ?? `#${vi}`;\n // The aggregate's own binding is the fallback for a list view that declares\n // none — the same resolution order `validate-list-view-mode` reads.\n const viewObject = strName(view.objectName) ?? strName(view.object);\n\n if (isRec(view.list)) {\n check(\n view.list.searchableFields,\n listViewObject(view.list) ?? viewObject,\n `view \"${viewLabel}\" › list`,\n `views[${vi}].list.searchableFields`,\n 'list-view searchableFields',\n );\n }\n\n if (isRec(view.listViews)) {\n for (const [key, lv] of Object.entries(view.listViews)) {\n if (!isRec(lv)) continue;\n check(\n lv.searchableFields,\n listViewObject(lv) ?? viewObject,\n `view \"${viewLabel}\" › listViews.${key}`,\n `views[${vi}].listViews.${key}.searchableFields`,\n 'list-view searchableFields',\n );\n }\n }\n }\n\n return findings;\n}\n\n/** A list view's own object binding: `data: { provider: 'object', object }`. */\nfunction listViewObject(listView: AnyRec): string | undefined {\n const data = listView.data;\n return isRec(data) ? strName(data.object) : undefined;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared page-component traversal for the lint rules that inspect\n * `PageComponent.properties` (issue #3583).\n *\n * Getting this walk right is subtle enough that duplicating it has already\n * produced one dead rule, so it lives here once:\n *\n * - Components hang off `page.regions[].components[]` and `page.slots.<slot>`\n * — there is NO top-level `page.components`. A walker that reads\n * `page.components` silently visits nothing on a schema-parsed stack.\n * - `slots.<slot>` is `PageComponent | PageComponent[]` — a single component\n * is legal and must be normalized.\n * - `PageComponentSchema` is `.strict()`, so a component carries no `children`\n * key of its own. Sub-trees live INSIDE the untyped `properties` bag:\n * `page:tabs` → `properties.items[].children`, `page:accordion` →\n * `properties.items[].children`, `page:card` → `properties.body` /\n * `properties.footer`. All are `z.array(z.unknown())`, so the recursion is\n * untyped and has to be done by hand.\n * - `kind: 'html' | 'react' | 'jsx'` pages are authored as `source`, which is\n * authoritative; their `regions` hold at most a DERIVED cache that the\n * source wins over. Linting that cache reports findings about metadata the\n * author never wrote, so those pages are skipped here and covered by\n * `validate-jsx-pages` / `validate-react-page-props` instead.\n */\n\nexport type AnyRec = Record<string, unknown>;\n\n/** A visited component plus everything needed to locate and bind it. */\nexport interface WalkedComponent {\n /** The component record itself. */\n component: AnyRec;\n /** Config path, e.g. `pages[0].regions[1].components[2]`. */\n path: string;\n /**\n * The object this component binds against, by precedence:\n * `dataSource.object` → `properties.object` → the page's `object`.\n * `undefined` when nothing in the chain names one.\n */\n objectName?: string;\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** Page kinds whose component tree is a derived cache, not authored metadata. */\nconst SOURCE_AUTHORED_KINDS = new Set(['html', 'react', 'jsx']);\n\n/** Is this page authored as `source` (so its `regions` must not be linted)? */\nexport function isSourceAuthoredPage(page: AnyRec): boolean {\n const kind = strName(page.kind);\n return kind !== undefined && SOURCE_AUTHORED_KINDS.has(kind);\n}\n\n/**\n * Walk every component on a page, depth-first, yielding each with its config\n * path and resolved object binding. Source-authored pages yield nothing.\n *\n * `pagePath` is the caller's path prefix for the page (e.g. `pages[3]`).\n */\nexport function walkPageComponents(page: AnyRec, pagePath: string): WalkedComponent[] {\n const out: WalkedComponent[] = [];\n if (!isRec(page) || isSourceAuthoredPage(page)) return out;\n\n const pageObject = strName(page.object);\n\n const visit = (node: unknown, path: string, inheritedObject?: string) => {\n if (!isRec(node)) return;\n\n // Per-element `dataSource` overrides the page object so one page can bind\n // several objects; an inline `properties.object` does the same for the\n // element-family components that declare one.\n const props = isRec(node.properties) ? node.properties : undefined;\n const dataSource = isRec(node.dataSource) ? node.dataSource : undefined;\n const objectName =\n strName(dataSource?.object) ?? strName(props?.object) ?? inheritedObject;\n\n out.push({ component: node, path, objectName });\n\n if (!props) return;\n\n // `page:tabs` / `page:accordion` — items[].children[]\n if (Array.isArray(props.items)) {\n for (let i = 0; i < props.items.length; i++) {\n const item = props.items[i];\n if (!isRec(item) || !Array.isArray(item.children)) continue;\n for (let c = 0; c < item.children.length; c++) {\n visit(item.children[c], `${path}.properties.items[${i}].children[${c}]`, objectName);\n }\n }\n }\n // Generic layout nesting — `properties.children[]`. Not in any props\n // schema, but it is how real pages compose layout containers (`type:\n // 'flex'` grids in the showcase command-center wrap every chart this way).\n // Omitting it hides whole sub-trees from every rule built on this walk.\n if (Array.isArray(props.children)) {\n for (let i = 0; i < props.children.length; i++) {\n visit(props.children[i], `${path}.properties.children[${i}]`, objectName);\n }\n }\n // `page:card` — body[] / footer[]\n for (const key of ['body', 'footer'] as const) {\n const slotList = props[key];\n if (!Array.isArray(slotList)) continue;\n for (let i = 0; i < slotList.length; i++) {\n visit(slotList[i], `${path}.properties.${key}[${i}]`, objectName);\n }\n }\n };\n\n const regions = Array.isArray(page.regions) ? page.regions : [];\n for (let r = 0; r < regions.length; r++) {\n const region = regions[r];\n if (!isRec(region) || !Array.isArray(region.components)) continue;\n for (let c = 0; c < region.components.length; c++) {\n visit(region.components[c], `${pagePath}.regions[${r}].components[${c}]`, pageObject);\n }\n }\n\n const slots = isRec(page.slots) ? page.slots : undefined;\n if (slots) {\n for (const [slot, value] of Object.entries(slots)) {\n // A slot holds a single component or an array of them.\n const list = Array.isArray(value) ? value : [value];\n const indexed = Array.isArray(value);\n for (let i = 0; i < list.length; i++) {\n visit(list[i], `${pagePath}.slots.${slot}${indexed ? `[${i}]` : ''}`, pageObject);\n }\n }\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0078 — completeness] Field-reference integrity for page components\n * (issue #3583, assessment R3).\n *\n * `PageComponent.properties` is `z.record(z.string(), z.unknown())` — an\n * untyped bag. The typed prop schemas exist (`ComponentPropsMap` in\n * `@objectstack/spec/ui`) but nothing validates `properties` against them, so\n * every field name a component references ships exactly as typed. The HotCRM\n * audit found KPI cards and page headers bound to fields the object does not\n * have; each renders blank or falls back, and nothing reports the miss.\n *\n * Field-existence lint already exists for forms (`FORM_FIELD_UNKNOWN`),\n * semantic roles, and flow templates. This is the same check for pages, at the\n * same advisory severity: every consumer degrades gracefully (a missing field\n * is skipped, not crashed on), so a warning is the honest level.\n *\n * ── Which object a component binds ──────────────────────────────────────\n *\n * `dataSource.object` → `properties.object` → the page's `object`. A per-element\n * `dataSource` exists precisely so one page can bind several objects, and the\n * `element:*` family declares its own `object`; both must win over the page's.\n *\n * ── Why a hand-written descriptor table ─────────────────────────────────\n *\n * `ComponentPropsMap` cannot drive this rule: a Zod schema does not say which\n * of its `z.string()` props is a FIELD NAME (`RecordPathProps.statusField` and\n * `AIChatWindowProps.agentId` are both plain strings), and the type universe is\n * open anyway — `PageComponent.type` is `z.union([PageComponentType,\n * z.string()])`, so unregistered types like `record:line_items` parse and are\n * authored in the wild. The table below names the field-bearing props\n * explicitly; an unknown component type is SKIPPED silently, never flagged.\n *\n * The table also covers shapes the props schemas do not yet describe but real\n * pages authored anyway (they pass only because `properties` is unvalidated):\n * `record:details` `sections[].fields[]` and `hideFields[]`, and the record\n * picker's `labelField`. Linting the schema's shape alone would find nothing on\n * the actual corpus.\n *\n * ── Shared with the react page surface ──────────────────────────────────\n *\n * A `kind:'react'` page authors the SAME components, one surface over, as JSX\n * props instead of a `properties` bag (`<RecordHighlights fields={…}>`). The\n * extraction and the check are therefore exported — `COMPONENT_FIELD_SPECS`,\n * {@link componentFieldRefs}, {@link relatedListFieldRefs},\n * {@link indexObjectFields}, {@link checkFieldRefs} — and\n * `validate-react-page-props` runs them on the parsed JSX (#4340). Same table,\n * same skips, same rule id: the two surfaces agree on what counts as a field by\n * construction rather than by two lists that happen to match, which is the\n * drift #4330 had just finished removing from the system-field lists.\n */\n\nexport const PAGE_FIELD_UNKNOWN = 'page-field-unknown';\n\nexport type PageFieldSeverity = 'error' | 'warning';\n\nexport interface PageFieldFinding {\n /**\n * `warning` on every surface this rule itself walks — page renderers skip an\n * unknown field rather than fail. The shared {@link checkFieldRefs} core also\n * serves the react page surface, where one batch of refs reaches a QUERY\n * rather than a renderer and gates instead; see {@link FieldRefConsequence}.\n */\n severity: PageFieldSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `page \"task_detail\" · record:highlights`. */\n where: string;\n /** Config path, e.g. `pages[0].regions[1].components[0].properties.fields[2]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\nimport { walkPageComponents, type AnyRec } from './page-walk.js';\n// Real pages DO reference registry-injected columns — e.g. `sys_user.page.ts`\n// lists `created_at` in a related-list's columns — so the shared set is load-\n// bearing here, not merely defensive.\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** A field reference found in a props bag, with the path that located it. */\nexport interface FieldRef {\n name: string;\n path: string;\n}\n\n/**\n * Pull field names out of a value that may be a bare string, a `{field}` or\n * `{name}` record, or an array of either — the three shapes the component props\n * use interchangeably (`record:highlights` keys its object form `name`, while\n * columns/sort/filter key theirs `field`).\n */\nexport function fieldRefsFrom(value: unknown, basePath: string): FieldRef[] {\n const out: FieldRef[] = [];\n const one = (v: unknown, path: string) => {\n const bare = strName(v);\n if (bare) {\n out.push({ name: bare, path });\n return;\n }\n if (!isRec(v)) return;\n const named = strName(v.field) ?? strName(v.name);\n if (named) out.push({ name: named, path: `${path}.${strName(v.field) ? 'field' : 'name'}` });\n };\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) one(value[i], `${basePath}[${i}]`);\n } else {\n one(value, basePath);\n }\n return out;\n}\n\n/**\n * Field references in a `sort` value.\n *\n * The structured form (`[{ field, order }]`) is ordinary {@link fieldRefsFrom}\n * work. The LEGACY bare-string form is not: `ListViewSchema.sort` still accepts\n * `\"created_at desc\"`, where the string names ONE field followed by a direction\n * word — so reading the whole string as a field name reports `\"created_at desc\"`\n * as unknown, a finding whose \"field\" the author never wrote. Read its head\n * instead, which is exactly what the renderer does with it.\n */\nexport function sortFieldRefs(value: unknown, basePath: string): FieldRef[] {\n if (typeof value === 'string') {\n const head = value.trim().split(/\\s+/)[0];\n return head ? [{ name: head, path: basePath }] : [];\n }\n return fieldRefsFrom(value, basePath);\n}\n\n/**\n * Per-component-type descriptor: which `properties` paths hold field names, and\n * whether they resolve against this component's own object or another one.\n *\n * `props` entries are read from `component.properties`. Entries under\n * `nestedSections` walk `properties.<key>[].fields[]` — the section shape real\n * pages author for `record:details`.\n */\nexport interface ComponentFieldSpec {\n /** Props holding field names bound to the component's resolved object. */\n props?: readonly string[];\n /** Props holding `{...}[]` section objects whose `fields[]` are field names. */\n nestedSections?: readonly string[];\n}\n\nexport const COMPONENT_FIELD_SPECS: Readonly<Record<string, ComponentFieldSpec>> = {\n 'record:highlights': { props: ['fields'] },\n // `sections`/`hideFields` are not in RecordDetailsProps, but every real page\n // authors them (they survive because `properties` is unvalidated).\n 'record:details': { props: ['fields', 'hideFields'], nestedSections: ['sections'] },\n 'record:path': { props: ['statusField'] },\n 'element:number': { props: ['field'] },\n 'element:filter': { props: ['fields'] },\n 'element:form': { props: ['fields'] },\n // The schema says `displayField`; real pages author `labelField`. Accept both.\n 'element:record_picker': { props: ['displayField', 'labelField', 'searchFields'] },\n};\n\n/**\n * `record:related_list` is special: its `columns`/`sort`/`filter` resolve\n * against the RELATED object (`properties.objectName`), not the page's object,\n * so it cannot ride the generic table.\n */\nexport const RELATED_LIST_TYPE = 'record:related_list';\n\n/**\n * The field references a component's props bag holds, per\n * {@link COMPONENT_FIELD_SPECS}. `null` for a type with no descriptor —\n * unregistered / non-field components are skipped silently, never flagged.\n *\n * `basePath` is the path of the props bag itself, and `sep` joins a prop name\n * onto it: `.` for a metadata page (`…components[0].properties.fields[2]`),\n * ` › ` for react source, whose props live inside one opaque `source` string\n * and so are addressed the way #4329 established (`pages[0].source › fields[2]`).\n */\nexport function componentFieldRefs(\n type: string,\n props: AnyRec,\n basePath: string,\n sep = '.',\n): FieldRef[] | null {\n const spec = COMPONENT_FIELD_SPECS[type];\n if (!spec) return null;\n const refs: FieldRef[] = [];\n for (const key of spec.props ?? []) {\n refs.push(...fieldRefsFrom(props[key], `${basePath}${sep}${key}`));\n }\n for (const key of spec.nestedSections ?? []) {\n const sections = Array.isArray(props[key]) ? (props[key] as unknown[]) : [];\n for (let si = 0; si < sections.length; si++) {\n const section = sections[si];\n // A `sections` that is a plain `string[]` (the shape `RecordDetailsProps`\n // actually declares — section IDs) yields nothing here, which is right:\n // those are not field names.\n if (!isRec(section)) continue;\n refs.push(...fieldRefsFrom(section.fields, `${basePath}${sep}${key}[${si}].fields`));\n }\n }\n return refs;\n}\n\n/** A `record:related_list` props bag, split by which object each batch resolves against. */\nexport interface RelatedListFieldRefs {\n /** The related (child) object this list renders — `properties.objectName`. */\n relatedObject: string | undefined;\n /** Refs resolved against {@link relatedObject}. */\n related: FieldRef[];\n /** Refs resolved against the PARENT object (the record the list hangs off). */\n parent: FieldRef[];\n /** The Add picker's own object, and the refs resolved against it. */\n pickerObject: string | undefined;\n picker: FieldRef[];\n}\n\n/**\n * Split a `record:related_list` props bag into its three object scopes. Shared\n * so the react `<RecordRelatedList>` block and the metadata component cannot\n * disagree about which object each prop addresses — the exact confusion #4340\n * found published under one prop name.\n */\nexport function relatedListFieldRefs(\n props: AnyRec,\n basePath: string,\n sep = '.',\n): RelatedListFieldRefs {\n const add = isRec(props.add) ? props.add : undefined;\n const picker = add && isRec(add.picker) ? add.picker : undefined;\n const at = (key: string) => `${basePath}${sep}${key}`;\n return {\n relatedObject: strName(props.objectName),\n related: [\n ...fieldRefsFrom(props.columns, at('columns')),\n ...sortFieldRefs(props.sort, at('sort')),\n ...fieldRefsFrom(props.filter, at('filter')),\n ...fieldRefsFrom(props.relationshipField, at('relationshipField')),\n ...(add ? fieldRefsFrom(add.linkField, at('add.linkField')) : []),\n ],\n parent: fieldRefsFrom(props.relationshipValueField, at('relationshipValueField')),\n pickerObject: picker ? strName(picker.object) : undefined,\n picker: picker\n ? [\n ...fieldRefsFrom(picker.valueField, at('add.picker.valueField')),\n ...fieldRefsFrom(picker.labelField, at('add.picker.labelField')),\n ]\n : [],\n };\n}\n\n/** object name → its declared field names. Both `fields` shapes resolve. */\nexport function indexObjectFields(stack: AnyRec): Map<string, Set<string>> {\n const objectFields = new Map<string, Set<string>>();\n if (!isRec(stack)) return objectFields;\n for (const obj of asArray(stack.objects)) {\n const name = strName(obj.name);\n if (!name) continue;\n const names = new Set<string>();\n for (const f of asArray(obj.fields)) {\n const fn = strName(f.name);\n if (fn) names.add(fn);\n }\n objectFields.set(name, names);\n }\n return objectFields;\n}\n\n/**\n * How a miss on this batch of refs fails at runtime — the two calls this\n * package makes, named so the message and the severity cannot drift apart.\n *\n * - `skipped` (default): the consumer drops the unknown name and renders the\n * rest. Advisory, like every other field-existence rule.\n * - `queried`: the name reached a QUERY. An unknown column in a predicate\n * matches no row (`SqlDriver` swallows the driver's \"no such column\" and\n * returns `[]`), so the surface renders an empty list that is\n * indistinguishable from \"there is no data\" — the silent-zero failure\n * `filter-token-unknown` and `validate-flow-template-paths`' filter-position\n * call both gate on. Gating.\n */\nexport type FieldRefConsequence = 'skipped' | 'queried';\n\n/**\n * Check one batch of refs against `objectName`'s declared fields.\n *\n * Bails out entirely when the object is not defined in this stack — it may come\n * from another installed package, and we cannot judge fields on a schema we\n * cannot see (the same skip the flow/widget rules use).\n */\nexport function checkFieldRefs(\n refs: readonly FieldRef[],\n objectName: string | undefined,\n objectFields: ReadonlyMap<string, Set<string>>,\n where: string,\n consequence: FieldRefConsequence = 'skipped',\n): PageFieldFinding[] {\n const findings: PageFieldFinding[] = [];\n if (!objectName) return findings; // nothing to resolve against\n const known = objectFields.get(objectName);\n if (!known) return findings; // cross-package object — unknowable here\n for (const ref of refs) {\n // A relationship path (`account.name`) is resolved by the query engine,\n // not a base column, so it cannot be judged here.\n if (ref.name.includes('.')) continue;\n if (known.has(ref.name) || SYSTEM_FIELDS.has(ref.name)) continue;\n findings.push({\n severity: consequence === 'queried' ? 'error' : 'warning',\n rule: PAGE_FIELD_UNKNOWN,\n where,\n path: ref.path,\n message:\n `field \"${ref.name}\" is not a field on object \"${objectName}\" — ` +\n (consequence === 'queried'\n ? 'it is used in a QUERY, so the predicate can never match: the surface ' +\n 'renders an empty result that looks exactly like \"there is no data\".'\n : 'the component silently skips it, so it never renders.'),\n hint:\n `Fix the field name, or add \"${ref.name}\" to ${objectName}. ` +\n `References must match the object's field names exactly.` +\n (known.size > 0 ? ` Object fields: ${[...known].sort().join(', ')}.` : ''),\n });\n }\n return findings;\n}\n\nexport function validatePageFieldBindings(stack: AnyRec): PageFieldFinding[] {\n const findings: PageFieldFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n // object name → its declared field names. Built with `asArray` so BOTH\n // `fields` shapes (array of `{name}` and name-keyed map) resolve.\n const objectFields = indexObjectFields(stack);\n\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n const page = pages[pi];\n if (!page || typeof page !== 'object') continue;\n const pageName = strName(page.name) ?? `#${pi}`;\n\n const checkRefs = (refs: readonly FieldRef[], objectName: string | undefined, where: string) => {\n findings.push(...checkFieldRefs(refs, objectName, objectFields, where));\n };\n\n for (const { component, path, objectName } of walkPageComponents(page, `pages[${pi}]`)) {\n const type = strName(component.type);\n const props = isRec(component.properties) ? component.properties : undefined;\n if (!type || !props) continue;\n const where = `page \"${pageName}\" · ${type}`;\n const base = `${path}.properties`;\n\n if (type === RELATED_LIST_TYPE) {\n const split = relatedListFieldRefs(props, base);\n checkRefs(split.related, split.relatedObject, where);\n // `relationshipValueField` names a field on the PARENT (page) object.\n checkRefs(split.parent, objectName, where);\n // The add-picker resolves against its own object.\n checkRefs(split.picker, split.pickerObject, where);\n continue;\n }\n\n const refs = componentFieldRefs(type, props, base);\n if (!refs) continue; // unregistered / non-field component — skip silently\n checkRefs(refs, objectName, where);\n }\n\n // ── interfaceConfig (list pages) ──\n // Bound by `interfaceConfig.source`, falling back to the page's object.\n const cfg = isRec(page.interfaceConfig) ? page.interfaceConfig : undefined;\n if (cfg) {\n const cfgObject = strName(cfg.source) ?? strName(page.object);\n const base = `pages[${pi}].interfaceConfig`;\n const refs: FieldRef[] = [\n ...fieldRefsFrom(cfg.columns, `${base}.columns`),\n ...sortFieldRefs(cfg.sort, `${base}.sort`),\n ...fieldRefsFrom(cfg.filterBy, `${base}.filterBy`),\n ];\n const userFilters = isRec(cfg.userFilters) ? cfg.userFilters : undefined;\n if (userFilters) {\n refs.push(...fieldRefsFrom(userFilters.fields, `${base}.userFilters.fields`));\n }\n checkRefs(refs, cfgObject, `page \"${pageName}\" · interfaceConfig`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for SDUI source-tier page styling (ADR-0065 / ADR-0080 /\n// ADR-0081). A `kind:'html'` or `kind:'react'` page's `source` is RUNTIME\n// metadata — the console's build-time Tailwind only scans the renderer's own\n// source, never an authored page string. So a Tailwind `className` in page\n// source silently produces NO CSS (the exact ADR-0065 failure: styling that\n// \"works only by coincidence\" when the class happens to be one objectui already\n// ships). This rule flags authored `className` attributes in source-tier pages\n// before render, with the actionable fix.\n//\n// It is the styling counterpart to the react-prop gate: a pure\n// `(stack) => Finding[]` rule (ADR-0019), run from `os validate`/`compile` and\n// reusable by AI authoring so the agent self-corrects.\n\nexport type SourceStyleSeverity = 'error' | 'warning';\n\nexport interface SourceStyleFinding {\n severity: SourceStyleSeverity;\n rule: string;\n where: string;\n path: string;\n message: string;\n hint: string;\n}\n\nexport const PAGE_SOURCE_CLASSNAME = 'page-source-className-tailwind';\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\n// `className=` as a JSX attribute: name, optional ws, `=`, then `\"`/`'`/`{`.\nconst CLASSNAME_ATTR = /\\bclassName\\s*=\\s*[\"'{]/g;\n\nexport function validatePageSourceStyling(stack: AnyRec): SourceStyleFinding[] {\n const findings: SourceStyleFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n if (!page) continue;\n const kind = page.kind;\n if (kind !== 'html' && kind !== 'react' && kind !== 'jsx') continue;\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') continue;\n const name = String(page.name ?? `#${p}`);\n\n CLASSNAME_ATTR.lastIndex = 0;\n let count = 0;\n while (CLASSNAME_ATTR.exec(source) !== null) count++;\n if (count === 0) continue;\n\n findings.push({\n severity: 'warning',\n rule: PAGE_SOURCE_CLASSNAME,\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: `${count} \\`className\\` attribute${count > 1 ? 's' : ''} in ${String(kind)}-source page — Tailwind utilities in page source silently produce no CSS (the build never scans authored metadata; ADR-0065).`,\n hint:\n kind === 'react'\n ? \"Style with inline style={{}} using hsl(var(--token)) theme colors (e.g. color:'hsl(var(--foreground))', background:'hsl(var(--card))'); render drawer/modal via <ObjectForm formType=\\\"drawer\\\"|\\\"modal\\\"> instead of hand-rolled overlays.\"\n : \"Lay out with the components' structured props (<flex direction gap>, <grid columns>) and add CSS via a JSON style object style={{\\\"color\\\":\\\"hsl(var(--foreground))\\\"}}; do not use Tailwind className.\",\n });\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { objectTitleCompleteness } from '@objectstack/spec/data';\nimport type { DisplayNameObjectMeta } from '@objectstack/spec/data';\n\n/**\n * Build-time record-title diagnostics (ADR-0079).\n *\n * A record's human title is a structural invariant: every object resolves a\n * primary title from a real STORED field via `nameField` (the canonical\n * pointer; `displayNameField` is the deprecated alias) or a deterministic\n * derivation. Two authoring smells are flagged here so `os build`/`os lint`,\n * the MCP authoring surface, and hand-authoring all get the coverage cloud\n * graph-lint already has (the ADR-0078 \"not cloud-only\" principle):\n *\n * - `title-format-retired` — the object declares a `titleFormat`. That field\n * is a RENDER-ONLY template the server can neither return nor query; ADR-0079\n * retires it in favour of `nameField`. The schema still parses it (existing\n * metadata keeps loading), so this is advisory, not an error.\n * - `title-unresolvable` — `objectTitleCompleteness` reports `status: 'none'`:\n * no `nameField`/`displayNameField` pointer and no title-eligible field to\n * derive one from. Records will have no meaningful title (the runtime falls\n * back to the auto-provisioned primary / `Record #<id>` floor), so this is a\n * warning, not an error — nothing is fully broken.\n *\n * Both are warnings: the auto-provision transform and the id floor mean a\n * green build never ships a fully title-less object. Reuses the shared spec\n * predicate (`@objectstack/spec/data` → display-name) so cloud and framework\n * classify titles identically.\n */\n\nexport const TITLE_FORMAT_RETIRED = 'title-format-retired';\nexport const TITLE_UNRESOLVABLE = 'title-unresolvable';\n\nexport type RecordTitleSeverity = 'error' | 'warning';\n\nexport interface RecordTitleFinding {\n /** Always `warning` today — both rules are advisory (see module note). */\n severity: RecordTitleSeverity;\n /** Diagnostic rule id (registry entry), e.g. `title-format-retired`. */\n rule: string;\n /** Human-readable location, e.g. `object \"invoice\"`. */\n where: string;\n /** Config path, e.g. `objects[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Validate every object's record-title declaration. Returns the list of\n * findings (empty = clean). Both rules are advisory (`warning`): the caller\n * must never fail the build on them alone — auto-provision + the `Record #<id>`\n * floor guarantee a resolvable title at runtime.\n */\nexport function validateRecordTitle(stack: AnyRec): RecordTitleFinding[] {\n const findings: RecordTitleFinding[] = [];\n\n const objects = asArray(stack.objects);\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const where = `object \"${objName}\"`;\n const path = `objects[${i}]`;\n\n // ── (a) titleFormat is retired (ADR-0079) ──\n // Render-only template the server cannot return or query. Still parsed by\n // the schema for back-compat, so advisory.\n if (obj.titleFormat !== undefined && obj.titleFormat !== null && obj.titleFormat !== '') {\n findings.push({\n severity: 'warning',\n rule: TITLE_FORMAT_RETIRED,\n where,\n path,\n message:\n `${objName}: titleFormat is retired (ADR-0079) — migrate to nameField ` +\n `(single field) or a formula field designated nameField`,\n hint:\n `titleFormat is a render-only template the server cannot return or ` +\n `query, and an explicit nameField now takes precedence. For a ` +\n `single-field title set nameField: '<field>'. For a composite title, ` +\n `add a formula field (returnType: 'text') and designate it via ` +\n `nameField.`,\n });\n }\n\n // ── (b) no resolvable title (status: 'none') ──\n // Reuse the shared spec predicate so cloud graph-lint and framework lint\n // classify titles identically. `none` = no pointer AND nothing derivable.\n const completeness = objectTitleCompleteness(obj as DisplayNameObjectMeta);\n if (completeness.status === 'none') {\n findings.push({\n severity: 'warning',\n rule: TITLE_UNRESOLVABLE,\n where,\n path,\n message:\n `${objName}: no resolvable record title — records will have no ` +\n `meaningful name (no nameField and no title-eligible field to derive one)`,\n hint:\n `Set nameField to a text/email field (or a formula field with ` +\n `returnType: 'text'), or add a text field named \"name\"/\"title\". The ` +\n `runtime auto-provisions a primary and falls back to \"Record #<id>\", ` +\n `but an explicit title is far more useful.`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time semantic-role diagnostics (ADR-0085).\n *\n * The object-level semantic roles (`stageField`, `highlightFields` /\n * deprecated `compactLayout`, `fieldGroups` + `Field.group`) are pointers\n * into the object's own field map. A dangling pointer is Zod-valid but\n * silently inert at render time — the exact \"parsed, unmarked, silently\n * inert\" shape ADR-0078 prohibits — so the completeness lint flags it here,\n * uniformly for `os build`/`os validate`, MCP authoring and hand authors.\n *\n * All three rules are warnings, not errors: every consumer degrades\n * gracefully (an unknown `Field.group` renders in the ungrouped bucket, an\n * unknown highlight name is skipped, an unknown `stageField` falls back to\n * heuristics), so nothing is fully broken — but the author almost certainly\n * typo'd a name and should be told at author time, not discover it by\n * staring at an unchanged page.\n */\n\nexport const FIELD_GROUP_UNDECLARED = 'field-group-undeclared';\nexport const FIELD_GROUP_EMPTY = 'field-group-empty';\nexport const FIELD_GROUP_SHADOWED = 'field-group-shadowed';\nexport const SEMANTIC_ROLE_FIELD_UNKNOWN = 'semantic-role-field-unknown';\n\nexport type SemanticRoleSeverity = 'error' | 'warning';\n\nexport interface SemanticRoleFinding {\n /** Always `warning` today — all three rules are advisory (see module note). */\n severity: SemanticRoleSeverity;\n /** Diagnostic rule id, e.g. `field-group-undeclared`. */\n rule: string;\n /** Human-readable location, e.g. `object \"invoice\"`. */\n where: string;\n /** Config path, e.g. `objects[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Validate every object's semantic-role pointers. Returns the list of\n * findings (empty = clean). Advisory only — the caller must never fail the\n * build on these alone.\n */\nexport function validateSemanticRoles(stack: AnyRec): SemanticRoleFinding[] {\n const findings: SemanticRoleFinding[] = [];\n\n const objects = asArray(stack.objects);\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object') continue; // tolerate junk entries\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const where = `object \"${objName}\"`;\n const path = `objects[${i}]`;\n\n const fields = (obj.fields && typeof obj.fields === 'object' && !Array.isArray(obj.fields))\n ? (obj.fields as Record<string, AnyRec | undefined>)\n : {};\n const fieldNames = new Set(Object.keys(fields));\n\n // ── (a) Field.group → declared fieldGroups[].key ──\n const declaredGroups = new Set(\n (Array.isArray(obj.fieldGroups) ? obj.fieldGroups : [])\n .filter((g): g is AnyRec => !!g && typeof g === 'object')\n .map((g) => g.key)\n .filter((k): k is string => typeof k === 'string' && k.length > 0),\n );\n const referencedGroups = new Set<string>();\n for (const [fname, f] of Object.entries(fields)) {\n const g = f?.group;\n if (typeof g !== 'string' || g.length === 0) continue;\n referencedGroups.add(g);\n if (!declaredGroups.has(g)) {\n findings.push({\n severity: 'warning',\n rule: FIELD_GROUP_UNDECLARED,\n where,\n path: `${path}.fields.${fname}.group`,\n message:\n `${objName}.${fname}: group \"${g}\" is not declared in fieldGroups — ` +\n `the field renders in the ungrouped bucket, not under \"${g}\"`,\n hint:\n `Declare { key: '${g}', label: '…' } in ${objName}.fieldGroups, or fix ` +\n `the field's group reference. Group keys are snake_case and must match exactly.`,\n });\n }\n }\n\n // ── (b) declared group no field references ──\n for (const key of declaredGroups) {\n if (!referencedGroups.has(key)) {\n findings.push({\n severity: 'warning',\n rule: FIELD_GROUP_EMPTY,\n where,\n path: `${path}.fieldGroups`,\n message:\n `${objName}: fieldGroups declares \"${key}\" but no field references it — ` +\n `the group never renders`,\n hint:\n `Assign at least one field via group: '${key}', or remove the unused ` +\n `group declaration.`,\n });\n }\n }\n\n // ── (c) semantic-role pointers name real fields ──\n const stage = obj.stageField;\n if (typeof stage === 'string' && stage.length > 0 && !fieldNames.has(stage)) {\n findings.push({\n severity: 'warning',\n rule: SEMANTIC_ROLE_FIELD_UNKNOWN,\n where,\n path: `${path}.stageField`,\n message:\n `${objName}: stageField \"${stage}\" is not a field on this object — ` +\n `consumers fall back to heuristic stage detection`,\n hint:\n `Point stageField at an existing select/status field, or set ` +\n `stageField: false to declare the object has no linear lifecycle.`,\n });\n }\n\n const highlights = Array.isArray(obj.highlightFields)\n ? obj.highlightFields\n : Array.isArray(obj.compactLayout) // deprecated alias (pre-normalization input)\n ? obj.compactLayout\n : [];\n for (const entry of highlights) {\n if (typeof entry !== 'string' || entry.length === 0 || fieldNames.has(entry)) continue;\n findings.push({\n severity: 'warning',\n rule: SEMANTIC_ROLE_FIELD_UNKNOWN,\n where,\n path: `${path}.highlightFields`,\n message:\n `${objName}: highlightFields entry \"${entry}\" is not a field on this ` +\n `object — it is silently skipped by every consumer`,\n hint:\n `Fix the field name (highlightFields drives default columns, cards, ` +\n `previews and the detail highlight strip, in order).`,\n });\n }\n\n // ── (d) declared group fully shadowed by the detail highlight strip ──\n // Detail pages render the first 4 highlightFields as the top strip and\n // HIDE those fields from the details body; the record's title field is\n // the page H1 and never renders in the body either. A group whose every\n // visible member is covered by strip ∪ title therefore renders on FORMS\n // but silently never on detail pages — legal, but almost never what the\n // author pictured when they declared the group.\n const declaredStrings = highlights.filter(\n (h): h is string => typeof h === 'string' && h.length > 0,\n );\n if (declaredStrings.length > 0 && declaredGroups.size > 0) {\n // Mirror the renderer's title resolution: declared role first\n // (nameField / primaryField / deprecated displayNameField), else the\n // first conventional display-field name present on the object.\n const declaredTitle = [obj.nameField, obj.primaryField, obj.displayNameField]\n .find((v): v is string => typeof v === 'string' && v.length > 0 && fieldNames.has(v));\n const titleField = declaredTitle\n ?? ['name', 'full_name', 'title', 'subject', 'display_name'].find((c) => fieldNames.has(c));\n const stripSet = new Set(\n declaredStrings.filter((h) => h !== titleField).slice(0, 4),\n );\n const hiddenFromBody = new Set(stripSet);\n if (titleField) hiddenFromBody.add(titleField);\n\n for (const key of declaredGroups) {\n const members = Object.entries(fields)\n .filter(([, f]) => f?.group === key && f?.hidden !== true)\n .map(([fname]) => fname);\n if (members.length === 0) continue; // rule (b) already covers empty groups\n if (!members.every((m) => hiddenFromBody.has(m))) continue;\n findings.push({\n severity: 'warning',\n rule: FIELD_GROUP_SHADOWED,\n where,\n path: `${path}.fieldGroups`,\n message:\n `${objName}: every field in group \"${key}\" (${members.join(', ')}) is ` +\n `hoisted into the detail highlight strip (or is the record title) — ` +\n `the group renders on forms but never on detail pages`,\n hint:\n `Keep at least one non-highlighted field in \"${key}\", or remove the ` +\n `group if the strip already covers it. (Detail pages show the first ` +\n `4 highlightFields as the top strip and hide them from the body.)`,\n });\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time form-layout diagnostics (#2578).\n *\n * Authored form views carry field references and column-layout hints that are\n * Zod-valid but can be silently wrong at render time — the \"parsed, unmarked,\n * silently inert\" shape ADR-0078 prohibits. This lint catches the two that\n * matter for multi-column, AI-authored forms, uniformly for `os build` /\n * `os validate`, MCP authoring and hand authors (ADR-0019).\n *\n * Both rules are warnings, not errors — nothing is fully broken (an unknown\n * field name is skipped; an over-wide colSpan is clamped) — but each is almost\n * certainly an authoring mistake worth surfacing at author time:\n *\n * - `form-field-unknown` — a section references a field that is not on the\n * form's bound object, so the field silently does not render.\n * - `absolute-colspan-discouraged` — a field uses the absolute `colSpan`. Under\n * a per-surface DERIVED column count (mobile 1 / modal 2 / page 3-4) a fixed\n * span only lines up at the one width the author imagined; the renderer\n * clamps it. The robust primitive is the relative `span: 'full'`.\n *\n * Scope: top-level form `views` (a `sections` array). Forms embedded inside\n * page component trees are a follow-up — the walker deliberately stays shallow\n * so it never guesses at an arbitrary component's object binding.\n */\n\nexport const FORM_FIELD_UNKNOWN = 'form-field-unknown';\nexport const FORM_COLSPAN_ABSOLUTE = 'absolute-colspan-discouraged';\n\nexport type FormLayoutSeverity = 'error' | 'warning';\n\nexport interface FormLayoutFinding {\n /** Always `warning` today — both rules are advisory (see module note). */\n severity: FormLayoutSeverity;\n /** Diagnostic rule id, e.g. `form-field-unknown`. */\n rule: string;\n /** Human-readable location, e.g. `view \"contract_form\"`. */\n where: string;\n /** Config path, e.g. `views[2].sections[0].fields[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** A section field entry is either a bare field name or `{ field, colSpan, … }`. */\nfunction fieldNameOf(entry: unknown): string | null {\n if (typeof entry === 'string') return entry.length > 0 ? entry : null;\n if (entry && typeof entry === 'object' && !Array.isArray(entry)) {\n const f = (entry as AnyRec).field;\n return typeof f === 'string' && f.length > 0 ? f : null;\n }\n return null;\n}\n\n/** The object a form view binds to: `data.object` (canonical) or `objectName`. */\nfunction boundObject(view: AnyRec): string | undefined {\n const data = view.data;\n if (data && typeof data === 'object' && typeof (data as AnyRec).object === 'string') {\n return (data as AnyRec).object as string;\n }\n return typeof view.objectName === 'string' ? (view.objectName as string) : undefined;\n}\n\n/**\n * Validate authored form-view layout. Returns findings (empty = clean).\n * Advisory only — the caller must never fail the build on these alone.\n */\nexport function validateFormLayout(stack: AnyRec): FormLayoutFinding[] {\n const findings: FormLayoutFinding[] = [];\n\n // object name → its field-name set, for reference checking.\n const objectFields = new Map<string, Set<string>>();\n for (const obj of asArray(stack.objects)) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fields = (obj.fields && typeof obj.fields === 'object' && !Array.isArray(obj.fields))\n ? Object.keys(obj.fields as AnyRec)\n : [];\n objectFields.set(name, new Set(fields));\n }\n\n const views = asArray(stack.views);\n for (let i = 0; i < views.length; i++) {\n const view = views[i];\n if (!view || typeof view !== 'object') continue;\n const sections = Array.isArray(view.sections) ? view.sections : null;\n if (!sections) continue; // only form views carry a sections array\n\n const viewName = typeof view.name === 'string' ? view.name : `(view ${i})`;\n const objName = boundObject(view);\n // Only reference-check when the bound object resolves; otherwise we can't.\n const known = objName ? objectFields.get(objName) : undefined;\n const where = `view \"${viewName}\"`;\n const base = `views[${i}]`;\n\n for (let s = 0; s < sections.length; s++) {\n const sec = sections[s];\n const secFields = sec && typeof sec === 'object' && Array.isArray((sec as AnyRec).fields)\n ? ((sec as AnyRec).fields as unknown[])\n : [];\n for (let f = 0; f < secFields.length; f++) {\n const entry = secFields[f];\n const fname = fieldNameOf(entry);\n const fpath = `${base}.sections[${s}].fields[${f}]`;\n\n // ── (a) section field references a real field on the bound object ──\n if (fname && known && !known.has(fname)) {\n findings.push({\n severity: 'warning',\n rule: FORM_FIELD_UNKNOWN,\n where,\n path: fpath,\n message:\n `${viewName}: field \"${fname}\" is not a field on object \"${objName}\" — ` +\n `it is silently skipped and never renders on the form`,\n hint:\n `Fix the field name, or add \"${fname}\" to ${objName}. Section field ` +\n `references must match the object's field names exactly.`,\n });\n }\n\n // ── (b) absolute colSpan → steer to the surface-independent span ──\n const colSpan = entry && typeof entry === 'object' && !Array.isArray(entry)\n ? (entry as AnyRec).colSpan\n : undefined;\n if (colSpan != null) {\n findings.push({\n severity: 'warning',\n rule: FORM_COLSPAN_ABSOLUTE,\n where,\n path: `${fpath}.colSpan`,\n message:\n `${viewName}: field \"${fname ?? '?'}\" sets absolute colSpan ${String(colSpan)} — ` +\n `the form's column count is derived per surface (mobile 1 / modal 2 / page 3-4), ` +\n `so a fixed span only aligns at one width`,\n hint:\n `Prefer span: 'full' (whole row at any column count), or omit for auto ` +\n `width. The renderer clamps colSpan to the current column count.`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time conditional-visibility diagnostics (ADR-0089 D3b).\n *\n * ADR-0089 unifies the conditional-visibility predicate under the single\n * canonical key **`visibleWhen`** across data fields, view form sections/fields,\n * and page components. The deprecated spellings — `visibleOn` (view form) and\n * `visibility` (page component) — stay accepted and are folded into `visibleWhen`\n * at the schema boundary (a zod `.transform()`). Because that fold happens during\n * `parse()`, the aliases are gone from the *parsed* stack — so this rule runs on\n * the **pre-parse** (normalized) stack, exactly like `validate-list-view-mode`,\n * to see what the author actually wrote.\n *\n * Two advisory rules (both `warning` — nothing is broken, the alias still works\n * and a mis-rooted predicate just never matches):\n *\n * - `visibility-alias-deprecated` — a `visibleOn` / `visibility` key in authored\n * source. Autofix intent: rename the key to `visibleWhen` (same value).\n * - `visibility-root-mislayered` — a visibility predicate whose binding root does\n * not match its layer (ADR-0089 D3, §Context). The check is **bidirectional**:\n * - **runtime** view/page surfaces (`*.view.ts` / `*.page.ts`) bind\n * `record` + `current_user` (pages also expose `page.<var>`), so a `data.`-rooted\n * predicate here is a wrong-layer paste that silently never matches; and\n * - **metadata-editing** forms (`*.form.ts` — the row under edit) bind `data`, so\n * a `record.`-rooted predicate there is the same bug in the other direction.\n * The layer is supplied by the caller (`opts.layer`, default `'runtime'`): the\n * app-lint path (`os validate` / `compile`) always lints runtime surfaces, while a\n * file-aware caller linting a `*.form.ts` passes `layer: 'metadata'`.\n *\n * Scope: `views` (form `sections` / legacy `groups`, and their `fields`) and\n * `pages` (`regions[].components[]`). Data-field `visibleWhen` is already covered\n * by `validate-expressions` and is not re-checked here.\n */\n\nexport const VISIBILITY_ALIAS_DEPRECATED = 'visibility-alias-deprecated';\nexport const VISIBILITY_ROOT_MISLAYERED = 'visibility-root-mislayered';\n\nexport type VisibilitySeverity = 'error' | 'warning';\n\n/**\n * Which binding environment the linted surface belongs to (ADR-0089 §Context):\n * - `runtime` — `*.view.ts` / `*.page.ts`; binds `record` + `current_user` (+ `page`).\n * - `metadata` — `*.form.ts` metadata-editing forms; binds `data` (the row under edit).\n */\nexport type VisibilityLayer = 'runtime' | 'metadata';\n\n/** Options for {@link validateVisibilityPredicates}. */\nexport interface VisibilityOptions {\n /** Binding layer of the surface being linted. Defaults to `'runtime'`. */\n layer?: VisibilityLayer;\n}\n\nexport interface VisibilityFinding {\n /** Always `warning` today — both rules are advisory (see module note). */\n severity: VisibilitySeverity;\n /** Diagnostic rule id, e.g. `visibility-alias-deprecated`. */\n rule: string;\n /** Human-readable location, e.g. `view \"contact_form\"`. */\n where: string;\n /** Config path, e.g. `views[2].sections[0].fields[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** The canonical key and its two deprecated aliases (ADR-0089). */\nconst CANONICAL = 'visibleWhen';\nconst ALIASES = ['visibleOn', 'visibility'] as const;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** Extract the CEL source from a predicate value (string, or `{ source }` envelope). */\nfunction predicateSource(v: unknown): string | undefined {\n if (typeof v === 'string') return v;\n if (v && typeof v === 'object' && typeof (v as AnyRec).source === 'string') {\n return (v as AnyRec).source as string;\n }\n return undefined;\n}\n\n/** Does the predicate reference `<root>.<x>` as a leading binding root? */\nfunction usesRoot(source: string, root: string): boolean {\n // `<root>` as a leading identifier followed by a property access. The leading\n // `(^|[^.\\w$])` guard excludes a nested access like `foo.data` (a field named\n // `data`) or `my_record.x` (an identifier that merely ends in `record`).\n return new RegExp(`(^|[^.\\\\w$])${root}\\\\.\\\\w`).test(source);\n}\n\n/**\n * Per-layer mis-rooted-predicate description. The `runtime` layer forbids the\n * metadata-editing-form root (`data.`); the `metadata` layer forbids the runtime\n * record-surface root (`record.`) — ADR-0089 D3 spells out both directions.\n */\nconst MISLAYER_BY_LAYER: Record<\n VisibilityLayer,\n { forbiddenRoot: string; message: string; hint: string }\n> = {\n runtime: {\n forbiddenRoot: 'data',\n message:\n 'visibility predicate is rooted at `data.` — that is the ' +\n 'metadata-editing-form root (a `*.form.ts` row under edit), not a runtime ' +\n 'surface. A runtime view/page predicate that binds `data.` never matches ' +\n 'and the element renders unconditionally (ADR-0089).',\n hint:\n 'Runtime record surfaces bind `record` + `current_user` (pages also ' +\n \"expose `page.<var>`). Use e.g. `record.status == 'open'` instead of \" +\n \"`data.status == 'open'`.\",\n },\n metadata: {\n forbiddenRoot: 'record',\n message:\n 'visibility predicate is rooted at `record.` — that is the runtime ' +\n 'record-surface root (a `*.view.ts` / `*.page.ts` live record), not a ' +\n 'metadata-editing form. A `*.form.ts` predicate that binds `record.` never ' +\n 'matches and the element renders unconditionally (ADR-0089).',\n hint:\n 'Metadata-editing forms bind `data` (the row under edit). Use e.g. ' +\n \"`data.type == 'grid'` instead of `record.type == 'grid'`.\",\n },\n};\n\n/**\n * Inspect one element carrying a visibility predicate. Emits the alias-deprecated\n * finding (when an alias key is present) and the mis-layered-root finding (when\n * the effective predicate's binding root does not match `layer`).\n */\nfunction checkElement(\n el: AnyRec,\n where: string,\n path: string,\n layer: VisibilityLayer,\n findings: VisibilityFinding[],\n): void {\n // (1) deprecated alias key present → steer to `visibleWhen`.\n for (const alias of ALIASES) {\n if (el[alias] !== undefined) {\n findings.push({\n severity: 'warning',\n rule: VISIBILITY_ALIAS_DEPRECATED,\n where,\n path: `${path}.${alias}`,\n message:\n `\\`${alias}\\` is the deprecated spelling of the conditional-visibility ` +\n `predicate (ADR-0089). It still works — it is normalized to \\`visibleWhen\\` ` +\n `at parse — but the canonical key is \\`visibleWhen\\`.`,\n hint: `Rename the key \\`${alias}\\` → \\`visibleWhen\\` (same CEL value).`,\n });\n }\n }\n\n // (2) mis-layered binding root — check the effective predicate (canonical wins)\n // against the root expected for this layer.\n const raw = el[CANONICAL] ?? el.visibleOn ?? el.visibility;\n const source = predicateSource(raw);\n const rule = MISLAYER_BY_LAYER[layer];\n if (source && usesRoot(source, rule.forbiddenRoot)) {\n findings.push({\n severity: 'warning',\n rule: VISIBILITY_ROOT_MISLAYERED,\n where,\n path,\n message: rule.message,\n hint: rule.hint,\n });\n }\n}\n\n/** A section field entry is either a bare field name or `{ field, visibleWhen, … }`. */\nfunction isFieldObject(entry: unknown): entry is AnyRec {\n return !!entry && typeof entry === 'object' && !Array.isArray(entry);\n}\n\n/**\n * Validate conditional-visibility keys across authored views and pages.\n *\n * Runs on the **pre-parse** (normalized) stack so it can see the deprecated\n * `visibleOn` / `visibility` aliases before the schema folds them into\n * `visibleWhen`. Returns findings (empty = clean); all advisory (`warning`) —\n * the caller must never fail the build on these alone.\n *\n * The binding-root check is layer-directional (ADR-0089 D3): pass\n * `opts.layer = 'metadata'` when linting a `*.form.ts` metadata-editing form (so a\n * `record.`-rooted predicate is flagged), or leave it at the `'runtime'` default for\n * `*.view.ts` / `*.page.ts` surfaces (so a `data.`-rooted predicate is flagged). The\n * alias-deprecated check is layer-agnostic.\n */\nexport function validateVisibilityPredicates(\n stack: AnyRec,\n opts: VisibilityOptions = {},\n): VisibilityFinding[] {\n const layer: VisibilityLayer = opts.layer ?? 'runtime';\n const findings: VisibilityFinding[] = [];\n\n // ── Views: form sections / legacy groups, and their fields ──────────\n const views = asArray(stack.views);\n for (let i = 0; i < views.length; i++) {\n const view = views[i];\n if (!view || typeof view !== 'object') continue;\n const viewName = typeof view.name === 'string' ? view.name : `(view ${i})`;\n const where = `view \"${viewName}\"`;\n\n // `sections` (canonical) and `groups` (legacy alias → sections) both hold\n // FormSection objects with an optional visibility predicate + `fields`.\n for (const bucket of ['sections', 'groups'] as const) {\n const sections = Array.isArray(view[bucket]) ? (view[bucket] as unknown[]) : [];\n for (let s = 0; s < sections.length; s++) {\n const sec = sections[s];\n if (!sec || typeof sec !== 'object') continue;\n const secPath = `views[${i}].${bucket}[${s}]`;\n checkElement(sec as AnyRec, where, secPath, layer, findings);\n\n const secFields = Array.isArray((sec as AnyRec).fields) ? ((sec as AnyRec).fields as unknown[]) : [];\n for (let f = 0; f < secFields.length; f++) {\n const entry = secFields[f];\n if (isFieldObject(entry)) {\n checkElement(entry, where, `${secPath}.fields[${f}]`, layer, findings);\n }\n }\n }\n }\n }\n\n // ── Pages: regions[].components[] ───────────────────────────────────\n const pages = asArray(stack.pages);\n for (let i = 0; i < pages.length; i++) {\n const page = pages[i];\n if (!page || typeof page !== 'object') continue;\n const pageName = typeof page.name === 'string' ? page.name : `(page ${i})`;\n const where = `page \"${pageName}\"`;\n const regions = Array.isArray(page.regions) ? (page.regions as unknown[]) : [];\n for (let r = 0; r < regions.length; r++) {\n const region = regions[r];\n const components = region && typeof region === 'object' && Array.isArray((region as AnyRec).components)\n ? ((region as AnyRec).components as unknown[])\n : [];\n for (let c = 0; c < components.length; c++) {\n const comp = components[c];\n if (comp && typeof comp === 'object') {\n checkElement(comp as AnyRec, where, `pages[${i}].regions[${r}].components[${c}]`, layer, findings);\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0066 ⑨] Authoring-time validation for capability references.\n *\n * `requiredPermissions` (on objects, fields, apps, and actions) and\n * `systemPermissions` (on permission sets) are free capability strings. A typo\n * — `mange_users` for `manage_users` — is Zod-valid and fails CLOSED at runtime\n * (the caller is denied), which is the safe direction but UNDISCOVERABLE: nothing\n * tells the author the referenced capability exists nowhere. This rule closes\n * that gap by resolving every `requiredPermissions` reference against the set of\n * capabilities known at author time and warning on the unresolved ones —\n * \"reject at the producer\" (Prime Directive / ADR-0049 honesty).\n *\n * The author-time \"known\" set is:\n * 1. the built-in platform capabilities (`PLATFORM_CAPABILITY_NAMES`),\n * 2. every capability the stack DECLARES via `defineCapability`\n * (`stack.capabilities`) — the explicit, package-provenanced declaration\n * (ADR-0066 D1), materialized at boot by `bootstrapDeclaredCapabilities`,\n * 3. every capability a permission set in this stack GRANTS via\n * `systemPermissions` (granting a capability also declares it — mirrors\n * the runtime `bootstrapSystemCapabilities` derived-defaults rule), and\n * 4. any `sys_capability` row shipped as seed data.\n *\n * WARNING, not error: a single package's lint cannot see capabilities declared\n * by OTHER installed packages, and the reference fails closed at runtime anyway,\n * so a dangling reference is \"almost certainly a typo\" — surface it, don't break\n * the build. Assignment (`systemPermissions`) is NOT flagged: it is the\n * declaration side, and a package legitimately introduces new capabilities there.\n */\n\nimport { PLATFORM_CAPABILITY_NAMES } from '@objectstack/spec/security';\n\nexport const CAPABILITY_REFERENCE_UNKNOWN = 'capability-reference-unknown';\n\nexport type CapabilityRefSeverity = 'error' | 'warning';\n\nexport interface CapabilityRefFinding {\n /** Always `warning` — the reference fails closed at runtime (see module note). */\n severity: CapabilityRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `object \"sys_license\"`. */\n where: string;\n /** Config path, e.g. `objects[3].requiredPermissions`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** The capability strings in a `string[]` value. */\nfunction asCapArray(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((s): s is string => typeof s === 'string' && s.length > 0) : [];\n}\n\n/**\n * Flatten an object-level `requiredPermissions` — either a `string[]` (all\n * operations) or a per-operation `{ read, create, update, delete }` map (ADR-0066\n * ⑤) — into `[{ cap, key }]`, where `key` is the map key (or `undefined` for the\n * array form) so a finding can point at the exact operation slice.\n */\nfunction flattenObjectRequired(v: unknown): Array<{ cap: string; key?: string }> {\n if (Array.isArray(v)) return asCapArray(v).map((cap) => ({ cap }));\n if (v && typeof v === 'object') {\n const out: Array<{ cap: string; key?: string }> = [];\n for (const [key, val] of Object.entries(v as AnyRec)) {\n for (const cap of asCapArray(val)) out.push({ cap, key });\n }\n return out;\n }\n return [];\n}\n\n/**\n * Validate every capability reference in a stack. Returns findings (empty =\n * clean). Advisory only — callers must not fail the build on these alone.\n */\nexport function validateCapabilityReferences(stack: AnyRec): CapabilityRefFinding[] {\n const findings: CapabilityRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n // ── Build the author-time \"known capability\" set ──\n const known = new Set<string>(PLATFORM_CAPABILITY_NAMES);\n // [ADR-0066 D1] Capabilities the stack explicitly DECLARES via defineCapability.\n for (const cap of asArray(stack.capabilities)) {\n if (typeof cap.name === 'string' && cap.name.length > 0) known.add(cap.name);\n }\n for (const ps of asArray(stack.permissions)) {\n for (const cap of asCapArray(ps.systemPermissions)) known.add(cap);\n }\n for (const seed of asArray(stack.data)) {\n if (seed.object !== 'sys_capability') continue;\n for (const rec of Array.isArray(seed.records) ? seed.records : []) {\n const name = (rec as AnyRec | null)?.name;\n if (typeof name === 'string' && name.length > 0) known.add(name);\n }\n }\n\n const hint =\n 'Fix the capability name, define it with defineCapability (stack.capabilities), ' +\n 'declare it on a permission set’s systemPermissions, ship a sys_capability seed row, ' +\n 'or ignore this if the capability is provided by another installed package ' +\n '(references fail closed at runtime).';\n\n const flag = (cap: string, where: string, path: string) => {\n if (known.has(cap)) return;\n findings.push({\n severity: 'warning',\n rule: CAPABILITY_REFERENCE_UNKNOWN,\n where,\n path,\n message:\n `requiredPermissions references capability \"${cap}\" which is registered ` +\n `nowhere — no built-in capability, no permission set in this package grants ` +\n `it via systemPermissions, and no sys_capability seed declares it`,\n hint,\n });\n };\n\n // ── Objects (D3) + their fields (D3) + embedded actions (D4) ──\n const objects = asArray(stack.objects);\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object') continue;\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const objPath = `objects[${i}]`;\n\n for (const { cap, key } of flattenObjectRequired(obj.requiredPermissions)) {\n flag(cap, `object \"${objName}\"`, `${objPath}.requiredPermissions${key ? `.${key}` : ''}`);\n }\n\n const fields = asArray(obj.fields);\n for (const f of fields) {\n const fname = typeof f.name === 'string' ? f.name : '(field)';\n for (const cap of asCapArray(f.requiredPermissions)) {\n flag(cap, `field \"${objName}.${fname}\"`, `${objPath}.fields.${fname}.requiredPermissions`);\n }\n }\n\n for (const [ai, action] of asArray(obj.actions).entries()) {\n const aName = typeof action.name === 'string' ? action.name : `(action ${ai})`;\n for (const cap of asCapArray(action.requiredPermissions)) {\n flag(cap, `action \"${objName}.${aName}\"`, `${objPath}.actions[${ai}].requiredPermissions`);\n }\n }\n }\n\n // ── Top-level actions (D4) ──\n for (const [i, action] of asArray(stack.actions).entries()) {\n const aName = typeof action.name === 'string' ? action.name : `(action ${i})`;\n for (const cap of asCapArray(action.requiredPermissions)) {\n flag(cap, `action \"${aName}\"`, `actions[${i}].requiredPermissions`);\n }\n }\n\n // ── Apps: requiredPermissions can appear at the app, area/tab, and nav-item\n // (recursively through groups) levels. Walk each app subtree. ──\n const apps = asArray(stack.apps);\n for (let i = 0; i < apps.length; i++) {\n const app = apps[i];\n if (!app || typeof app !== 'object') continue;\n const appName = typeof app.name === 'string' ? app.name : `(app ${i})`;\n const walk = (node: unknown, path: string) => {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n node.forEach((child, ci) => walk(child, `${path}[${ci}]`));\n return;\n }\n const rec = node as AnyRec;\n for (const cap of asCapArray(rec.requiredPermissions)) {\n flag(cap, `app \"${appName}\"`, `${path}.requiredPermissions`);\n }\n // Recurse only into the sub-structures that carry requiredPermissions.\n if (rec.navigation) walk(rec.navigation, `${path}.navigation`);\n if (rec.areas) walk(rec.areas, `${path}.areas`);\n if (rec.tabs) walk(rec.tabs, `${path}.tabs`);\n if (rec.children) walk(rec.children, `${path}.children`);\n if (rec.items) walk(rec.items, `${path}.items`);\n };\n walk(app, `apps[${i}]`);\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Approval-node approver authoring lint (ADR-0090 D3 fallout).\n *\n * `org_membership_level` (and `role`, its deprecated spelling) resolves against\n * better-auth's org-membership tier (`sys_member.role`: owner / admin / member)\n * — it is NOT a position. After ADR-0090 D3 renamed `sys_role` → `sys_position`,\n * downstream apps that authored `{ type: 'role', value: 'sales_manager' }`\n * silently route the approval to nobody: the expansion finds no member row,\n * falls back to the `role:sales_manager` literal, and the request waits on an\n * approver that can never act. These rules move that failure from a stuck\n * request at runtime to a located fix-it at author time.\n *\n * Rules:\n *\n * | Rule | Severity | Origin |\n * |--------------------------------------------|----------|----------------------------|\n * | approval-approver-not-membership-tier | warning | ADR-0090 D3 (hotcrm class) |\n * | approval-approver-type-deprecated | warning | ADR-0090 D3 (#3133) |\n * | approval-approver-type-unknown | warning | contract-first (PD #12) |\n * | approval-escalation-reassign-no-target | warning | silent notify degradation |\n * | approval-approvers-may-resolve-empty | info | empty-position dead-end (#3424) |\n * | approval-expression-invalid | error/info | #3447 P2 closed-root expressions |\n * | approval-expression-no-empty-policy | info | #3447 P2 empty-slate policy |\n * | approval-decision-outputs-reserved | error | #3447 P2 resume envelope |\n * | approval-approver-cross-org-unsupported | error | ADR-0105 D9 targeting |\n *\n * The first two are mutually exclusive by construction — a bad *value* wins,\n * because its fix (`position`) differs from the deprecation's fix\n * (`org_membership_level`), and prescribing the latter for a position name\n * would be wrong advice.\n *\n * Warnings (not errors): a custom better-auth membership tier is legal, and\n * the runtime keeps its literal fallback — but both shapes are near-certainly\n * authoring mistakes, so say it out loud.\n *\n * Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input.\n */\n\nimport {\n ApproverType,\n APPROVAL_NODE_TYPE,\n DEPRECATED_APPROVER_TYPES,\n APPROVER_VALUE_BINDINGS,\n approverTypeIsOrgScoped,\n canonicalApproverType,\n normalizeDecisionOutputs,\n} from '@objectstack/spec/automation';\nimport { BUILTIN_MEMBERSHIP_ROLES } from '@objectstack/spec';\nimport { collectCelRootIdentifiers } from '@objectstack/formula';\nimport { walkFlowNodes } from './flow-walk.js';\n\nexport const APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER = 'approval-approver-not-membership-tier';\nexport const APPROVAL_APPROVER_TYPE_DEPRECATED = 'approval-approver-type-deprecated';\nexport const APPROVAL_APPROVER_TYPE_UNKNOWN = 'approval-approver-type-unknown';\nexport const APPROVAL_APPROVER_TYPE_UNSUPPORTED = 'approval-approver-type-unsupported';\nexport const APPROVAL_ESCALATION_REASSIGN_NO_TARGET = 'approval-escalation-reassign-no-target';\nexport const APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY = 'approval-approvers-may-resolve-empty';\nexport const APPROVAL_EXPRESSION_INVALID = 'approval-expression-invalid';\nexport const APPROVAL_EXPRESSION_NO_EMPTY_POLICY = 'approval-expression-no-empty-policy';\nexport const APPROVAL_DECISION_OUTPUTS_RESERVED = 'approval-decision-outputs-reserved';\nexport const APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED = 'approval-approver-cross-org-unsupported';\n\n/**\n * The CLOSED root set an `expression` approver may reference (#3447 P2) —\n * `current` (live record at node entry), `trigger` (submit-time snapshot),\n * `vars` (flow variables). Mirrors APPROVER_EXPRESSION_ROOTS in\n * plugin-approvals; both sides extract roots via the same\n * {@link collectCelRootIdentifiers}, so what lints clean is what runs.\n */\nconst EXPRESSION_ROOTS = new Set(['current', 'trigger', 'vars']);\n\n/** Resume-envelope keys a decision output may never use (#3447 P2). */\nconst RESERVED_OUTPUT_KEYS = new Set(['decision', 'requestId']);\n\n/**\n * Approver types that route to a GROUP whose membership is runtime data and can\n * be empty (an unstaffed position, an empty team/department). When EVERY\n * approver on a node is one of these, the node can resolve to an empty slate at\n * runtime — the framework#3424 dead-end. Individually-routed types\n * (`user`/`field`/`manager`), the guaranteed-staffed `org_membership_level`\n * tiers, and the opaque `queue` are deliberately excluded: any of them present\n * signals the author has a non-group route, so the node isn't purely\n * group-gated.\n */\nconst GROUP_ROUTED_TYPES = new Set(['position', 'team', 'department']);\n\nexport type ApprovalApproverSeverity = 'error' | 'warning' | 'info';\n\nexport interface ApprovalApproverFinding {\n severity: ApprovalApproverSeverity;\n /** Diagnostic rule id (`approval-*`). */\n rule: string;\n /** Human-readable location, e.g. `flow \"expense_approval\" · node \"step1\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[2].config.approvers[0]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * The org-membership tiers `sys_member.role` actually stores — DERIVED, not\n * transcribed (ADR-0108 / #3723).\n *\n * The vocabulary is closed and framework-owned, so the one source in\n * `@objectstack/spec` is also the only correct list here. A hand-kept copy is\n * how this list came to carry `guest`, which the `sys_member.role` select has\n * never offered: an approver naming it resolved to nobody, and the lint that\n * exists to catch exactly that stayed silent.\n *\n * Anything outside this set authored as `{ type: 'org_membership_level' }` (or\n * its deprecated `role` spelling) is almost certainly a position name.\n */\nconst MEMBERSHIP_TIERS: ReadonlySet<string> = new Set<string>(BUILTIN_MEMBERSHIP_ROLES);\n\n/** The same list, rendered for diagnostics — so no message can contradict it. */\nconst MEMBERSHIP_TIER_LIST = BUILTIN_MEMBERSHIP_ROLES.join('/');\n\n/** Off-spec dialect spellings we can name a canonical fix for. */\nconst TYPE_FIX: Record<string, string> = {\n business_unit: 'department',\n bu: 'department',\n};\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Validate the approvers of every Approval node in the stack's flows.\n * Returns findings (empty = clean).\n */\nexport function validateApprovalApprovers(stack: AnyRec): ApprovalApproverFinding[] {\n const findings: ApprovalApproverFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const flows = asArray(stack.flows);\n const validTypes = new Set<string>(ApproverType.options);\n\n for (let fi = 0; fi < flows.length; fi++) {\n const flow = flows[fi];\n if (!flow || typeof flow !== 'object') continue;\n const flowName = typeof flow.name === 'string' ? flow.name : `(flow ${fi})`;\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions\n // — an approval inside a loop body is still an approval (#4380).\n const walked = walkFlowNodes(flow, `flows[${fi}]`);\n\n for (let ni = 0; ni < walked.length; ni++) {\n const { node, path: nodePath } = walked[ni];\n if (!node || node.type !== APPROVAL_NODE_TYPE) continue;\n const nodeId = typeof node.id === 'string' ? node.id : `(node ${ni})`;\n const cfg = (node.config ?? {}) as AnyRec;\n const approvers = Array.isArray(cfg.approvers) ? (cfg.approvers as AnyRec[]) : [];\n const where = `flow \"${flowName}\" · node \"${nodeId}\"`;\n\n for (let ai = 0; ai < approvers.length; ai++) {\n const a = approvers[ai];\n if (!a || typeof a !== 'object') continue;\n const type = typeof a.type === 'string' ? a.type : '';\n const value = typeof a.value === 'string' ? a.value : '';\n const path = `${nodePath}.config.approvers[${ai}]`;\n\n if (type && !validTypes.has(type)) {\n const fix = TYPE_FIX[type];\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_TYPE_UNKNOWN,\n where,\n path: `${path}.type`,\n message:\n `approver type '${type}' is not an ApproverType (${ApproverType.options.join(' | ')}).`,\n hint: fix\n ? `Use the spec value: { type: '${fix}', value: '${value}' }.`\n : `Pick one of the spec values; unmapped types degrade to an inert '${type}:${value}' literal at runtime.`,\n });\n continue;\n }\n\n const canonical = canonicalApproverType(type);\n\n // Expression approvers (#3447 P2): the runtime REJECTS an expression\n // that doesn't parse or references a root outside `current`/`trigger`/\n // `vars` (the CEL env would resolve an unknown root as dyn → null → a\n // silently-empty slate, so the pre-check fails the node loudly). Catch\n // both at author time — `error`, because the node cannot run.\n if (canonical === 'expression') {\n const source = value.trim();\n if (!source) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.value`,\n message: `expression approver has an empty expression — the node fails at entry.`,\n hint:\n `Write a CEL expression over current.* (the record's live state at node entry), ` +\n `trigger.* (the submit-time snapshot) or vars.* (flow variables), ` +\n `e.g. current.approvers_dynamic or vars.approval_lead.picked_departments.`,\n });\n } else {\n const parsed = collectCelRootIdentifiers(source);\n if (!parsed.ok) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.value`,\n message: `expression approver does not parse as CEL: ${parsed.error}.`,\n hint:\n `Approver expressions are bare CEL (no {…} template braces), e.g. ` +\n `current.approvers_dynamic or vars.get_reviewers.record.owner_id.`,\n });\n } else {\n const illegal = parsed.roots.filter((r) => !EXPRESSION_ROOTS.has(r));\n if (illegal.length) {\n const wantsRecord = illegal.includes('record') || illegal.includes('previous');\n findings.push({\n severity: 'error',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.value`,\n message:\n `expression approver references \\`${illegal.join('`, `')}\\` — only current.*, ` +\n `trigger.* and vars.* are available, and the node fails at entry on any other root.`,\n hint: wantsRecord\n ? `\\`record\\`/\\`previous\\` are not bound here (on this platform \\`record\\` always means ` +\n `\"the record at event time\", which is ambiguous at an approval node). Write ` +\n `current.<field> for the live value at node entry, trigger.<field> for the ` +\n `submit-time snapshot (vars.previous carries the pre-update row).`\n : `Did you mean current.<field> (live record), trigger.<field> (submit snapshot), ` +\n `or vars.<name> (flow variable)?`,\n });\n }\n }\n }\n } else if (a.resolveAs != null) {\n // resolveAs only means something on an expression approver; on any\n // other type it silently does nothing — surface the dead config.\n findings.push({\n severity: 'info',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.resolveAs`,\n message: `resolveAs has no effect on a '${type}' approver — it only applies to type 'expression'.`,\n hint: `Remove it, or switch this approver to { type: 'expression', value: '<CEL>', resolveAs: '${String(a.resolveAs)}' }.`,\n });\n }\n\n // Exactly one of the two below fires. Order matters: a bad VALUE is\n // the more serious (and differently-fixed) defect, so it wins. Telling\n // an author to rewrite { type: 'role', value: 'sales_manager' } as\n // `org_membership_level` would be actively wrong advice — the fix is\n // `position`, and the deprecation is beside the point.\n if (canonical === 'org_membership_level' && value && !MEMBERSHIP_TIERS.has(value.toLowerCase())) {\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER,\n where,\n path: `${path}.value`,\n message:\n `approver { type: '${type}', value: '${value}' } resolves against the better-auth ` +\n `org-membership tier (sys_member.role: ${MEMBERSHIP_TIER_LIST}) — '${value}' is not ` +\n `a membership tier, so this approver matches nobody and the request stalls.`,\n hint:\n `If '${value}' is an org position, author { type: 'position', value: '${value}' } ` +\n `(resolved via sys_user_position, ADR-0090 D3). Keep type 'org_membership_level' ` +\n `only for membership tiers (${MEMBERSHIP_TIER_LIST}) — the vocabulary is closed ` +\n `(ADR-0108), so a business role is always a position.`,\n });\n } else if (type in DEPRECATED_APPROVER_TYPES) {\n const fix = canonicalApproverType(type);\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_TYPE_DEPRECATED,\n where,\n path: `${path}.type`,\n message:\n `approver type '${type}' is the deprecated spelling of '${fix}' (ADR-0090 D3) and ` +\n `is removed in the next major.`,\n hint: `Author { type: '${fix}', value: '${value}' }. It resolves identically today.`,\n });\n } else if (\n (APPROVER_VALUE_BINDINGS as Record<string, { source: string }>)[canonical]?.source === 'unsupported'\n ) {\n // Declared-but-unenforced (#3508): the runtime has no resolution for\n // this type — the slot degrades to an inert `type:value` literal and\n // the request routes to nobody. Say it at authoring time instead of\n // letting the request stall silently (Prime Directive #10).\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_TYPE_UNSUPPORTED,\n where,\n path: `${path}.type`,\n message:\n `approver type '${type}' is declared but not implemented by the runtime (#3508) — ` +\n `the slot resolves to nobody and the request stalls.`,\n hint:\n `Route to people the engine can expand: { type: 'team' | 'department' | 'position', ... }. ` +\n `Queue approvers need a real ownership-queue implementation before they take effect.`,\n });\n }\n\n // [ADR-0105 D9] Cross-organization targeting on a type that has no\n // organization-scoped directory. `user` / `field` / `manager` name a\n // person outright and `team` membership carries no organization, so the\n // declaration cannot narrow anything — it is a misunderstanding of what\n // the field does, and the runtime refuses it. Error, not warning: this\n // is a certain authoring mistake with a certain fix, and letting it\n // reach the runtime turns author time into an incident.\n const declaredOrg = (a as AnyRec).organization;\n if (typeof declaredOrg === 'string' && declaredOrg.trim() !== ''\n && ApproverType.options.includes(canonical as never)\n && !approverTypeIsOrgScoped(canonical)) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED,\n where,\n path: `${path}.organization`,\n message:\n `approver type '${type}' does not resolve through an organization directory, so ` +\n `'organization: ${declaredOrg}' has no effect (ADR-0105 D9) — the runtime refuses it.`,\n hint:\n `Drop 'organization' here. Cross-organization targeting applies to ` +\n `'position', 'org_membership_level', 'department' and 'expression' approvers.`,\n });\n }\n }\n\n // Empty-slate dead-end (#3424): when EVERY approver on the node routes to\n // a group whose membership can be empty (an unstaffed position, an empty\n // team/department), the request can resolve to an empty `pending_approvers`\n // at runtime — no concrete user can act, and with `lockRecord` the record\n // stays locked with no recovery except a platform/tenant admin override.\n // Advisory (`info`): staffing is runtime data a linter can't see, so this\n // flags the risky SHAPE and prescribes a guaranteed-staffed fallback.\n const routable = approvers.filter(\n (a) => a && typeof a === 'object' && typeof (a as AnyRec).type === 'string',\n );\n if (\n routable.length > 0 &&\n routable.every((a) => GROUP_ROUTED_TYPES.has(canonicalApproverType(String((a as AnyRec).type))))\n ) {\n const locks = (cfg as AnyRec).lockRecord !== false; // default true\n findings.push({\n severity: 'info',\n rule: APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY,\n where,\n path: `${nodePath}.config.approvers`,\n message:\n `every approver on this node routes to a group (position/team/department) whose ` +\n `members are runtime data — if none is staffed, the request resolves to an empty ` +\n `slate and waits forever` +\n (locks ? `, and (lockRecord) the record stays locked with no in-product recovery.` : `.`),\n hint:\n `Make sure at least one target is always staffed, or add a guaranteed-staffed ` +\n `fallback approver, e.g. { type: 'org_membership_level', value: 'owner' }. A request ` +\n `that still lands empty is recoverable only by a platform/tenant admin override (#3424).`,\n });\n }\n\n // #3447 P2: a node with an `expression` approver resolves people from\n // runtime data — an empty result is far likelier than for static types\n // (a mid-flow field nobody wrote yet, an upstream output that came back\n // empty). Nudge the author to SAY what an empty slate should do rather\n // than inherit the default silently.\n const hasExpression = approvers.some(\n (a) => a && typeof a === 'object' && canonicalApproverType(String((a as AnyRec).type ?? '')) === 'expression',\n );\n if (hasExpression && (cfg as AnyRec).onEmptyApprovers == null) {\n findings.push({\n severity: 'info',\n rule: APPROVAL_EXPRESSION_NO_EMPTY_POLICY,\n where,\n path: `${nodePath}.config`,\n message:\n `this node resolves approvers from an expression but declares no onEmptyApprovers — ` +\n `an empty result falls back to the default ('admin_rescue': request opens, only a ` +\n `privileged admin can act).`,\n hint:\n `Declare the empty-slate policy explicitly: onEmptyApprovers: 'admin_rescue' (hold for ` +\n `admin takeover), 'fail' (fail the node — config bug), or 'auto_approve' (wave through, ` +\n `output.autoApproved = true).`,\n });\n }\n\n // #3447 P2: `decision`/`requestId` ride the resume envelope; a declared\n // decision output with either name is rejected at runtime on every\n // decide — the node can never accept the output it declares.\n // Bare keys and typed { key, … } declarations whitelist identically —\n // the spec normalizer is the one reader of the union shape.\n const declaredOutputs = normalizeDecisionOutputs((cfg as AnyRec).decisionOutputs).map((d) => d.key);\n const reserved = declaredOutputs.filter((k) => RESERVED_OUTPUT_KEYS.has(k));\n if (reserved.length) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_DECISION_OUTPUTS_RESERVED,\n where,\n path: `${nodePath}.config.decisionOutputs`,\n message:\n `decisionOutputs declares reserved key(s) \\`${reserved.join('`, `')}\\` — the resume ` +\n `envelope owns them, so every decide carrying them is rejected.`,\n hint: `Rename the output key(s); any name other than 'decision'/'requestId' works.`,\n });\n }\n\n // escalation.action 'reassign' with no escalateTo silently degrades to a\n // plain SLA-breach notification at runtime — the hand-off the author\n // asked for never happens.\n const escalation = (cfg.escalation ?? null) as AnyRec | null;\n if (escalation && typeof escalation === 'object' && escalation.action === 'reassign') {\n const target = typeof escalation.escalateTo === 'string' ? escalation.escalateTo.trim() : '';\n if (!target) {\n findings.push({\n severity: 'warning',\n rule: APPROVAL_ESCALATION_REASSIGN_NO_TARGET,\n where,\n path: `${nodePath}.config.escalation.escalateTo`,\n message:\n `escalation.action is 'reassign' but escalateTo is empty — at runtime the ` +\n `escalation degrades to a notify and the request stays with the original approvers.`,\n hint:\n `Set escalateTo to a position machine name (expanded via sys_user_position, ` +\n `ADR-0090 D3) or a specific user id, or change action to 'notify'.`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for replay-unsafe seed datasets (framework#3434).\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring. Seeds are REPLAYED — they re-load on every\n// dev-server boot and every package re-publish, not applied once — so a\n// dataset's mode has to be idempotent. `mode: 'insert'` is the one mode that\n// is not: the loader's `insert` path writes every record unconditionally, with\n// no existing-row check, so the table grows by the dataset's size on every\n// restart (the showcase `showcase_project_membership` fixture went 3 → 6 → 9).\n//\n// This is the authoring-time nudge that would have caught #3434 before boot:\n// flag `insert`, and point at the idempotent modes (`ignore` / `upsert`) plus\n// the `externalId` — single field, or a COMPOSITE list of fields for a join /\n// junction table that has no single natural key (`['team', 'project']`), the\n// support for which #3434 added.\n//\n// Advisory (warning): an `insert` seed is not a schema error — it loads and\n// \"works\" on a fresh DB; the defect only shows on the second boot. So it earns\n// a located fix-it, not a hard `os compile` gate.\n\nexport type SeedReplaySafetySeverity = 'error' | 'warning';\n\nexport interface SeedReplaySafetyFinding {\n severity: SeedReplaySafetySeverity;\n rule: string;\n /** Human-readable location, e.g. `seed \"showcase_project_membership\"`. */\n where: string;\n /** Config path, e.g. `data[12].mode`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const SEED_INSERT_MODE_DUPLICATES_ON_REPLAY = 'seed-insert-mode-duplicates-on-replay';\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * Flag every seed dataset declared with `mode: 'insert'` — the one non-idempotent\n * mode, which duplicates its rows on every replay boot (framework#3434). Returns\n * the findings (empty = clean). The caller decides how to surface them / whether\n * to fail the build; the CLI folds them in as advisory warnings.\n *\n * Reads `stack.data` (the `SeedSchema[]` fixtures). Safe on any shape — a stack\n * with no `data` array yields no findings.\n */\nexport function validateSeedReplaySafety(stack: AnyRec): SeedReplaySafetyFinding[] {\n const out: SeedReplaySafetyFinding[] = [];\n const seeds = Array.isArray(stack.data) ? (stack.data as AnyRec[]) : [];\n\n seeds.forEach((seed, i) => {\n if (!seed || typeof seed !== 'object') return;\n if (seed.mode !== 'insert') return;\n\n const object = typeof seed.object === 'string' ? seed.object : undefined;\n const where = object ? `seed \"${object}\"` : `data[${i}]`;\n\n out.push({\n severity: 'warning',\n rule: SEED_INSERT_MODE_DUPLICATES_ON_REPLAY,\n where,\n path: `data[${i}].mode`,\n message:\n \"`mode: 'insert'` re-inserts every record on each replay boot (dev-server restart, \" +\n 'package re-publish) with no existing-row check, so the dataset duplicates the table ' +\n 'on every restart — seeds are replayed, not applied once.',\n hint:\n \"Use `mode: 'ignore'` (skip rows that already exist) or `'upsert'` (create-or-update), \" +\n \"and declare an `externalId` to match on: a single natural-key field (e.g. `externalId: 'code'`), \" +\n 'or a COMPOSITE list of fields for a join / junction table with no single natural key ' +\n \"(e.g. `externalId: ['team', 'project']`).\",\n });\n });\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for seed values that fall outside an object's declared\n// state machine (framework#3433 follow-up).\n//\n// #3433 made seed writes EXEMPT from the `state_machine` validation rule — a\n// curated seed is an established fact, so it may be born mid-lifecycle\n// (a `completed` project, a `closed_won` opportunity) without the FSM entry\n// guard rejecting it. That exemption is deliberate, but it is also a SILENT\n// back door: the state machine's \"is this value even a state I know about?\"\n// check no longer runs for seed rows. A field-level `select` still rejects a\n// value outside its `options` at write time, so a plain typo is caught there —\n// but a `state_machine` on a free-text field, or a value that is a valid option\n// yet not a declared FSM state, now sails straight through.\n//\n// This author-time rule re-adds that safety net WITHOUT re-imposing the FSM: a\n// seeded value need not be an initial state (that is the whole point of the\n// exemption), but it must be a state the machine DECLARES — the union of\n// `initialStates`, the transition-map keys, and the transition targets.\n// Anything else is almost certainly a typo or an FSM that forgot to declare the\n// state; either way the author should see it before boot.\n//\n// Advisory (warning): a curated value the FSM does not know about is suspicious\n// but not necessarily wrong. Located fix-it, not a hard `os compile` gate —\n// symmetric with the replay-safety rule (framework#3434).\n\nexport type SeedStateMachineSeverity = 'warning';\n\nexport interface SeedStateMachineFinding {\n severity: SeedStateMachineSeverity;\n rule: string;\n /** Human-readable location, e.g. `seed \"showcase_project\" (\"Legacy Sunset\")`. */\n where: string;\n /** Config path, e.g. `data[4].records[3].status`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const SEED_VALUE_OUTSIDE_STATE_MACHINE = 'seed-value-outside-state-machine';\n\ntype AnyRec = Record<string, unknown>;\n\ninterface FsmRule {\n field: string;\n /** Every state the machine declares: initialStates ∪ transition keys ∪ targets. */\n states: Set<string>;\n}\n\n/**\n * Collect the `state_machine` rules (field + full declared-state set) for every\n * object, keyed by object name. An object with no such rule contributes nothing.\n * The declared-state set is derived from the rule alone so the check does not\n * depend on the field's `options` shape (and so it covers free-text state\n * fields the enum validator never sees).\n */\nfunction fsmRulesByObject(objects: AnyRec[]): Map<string, FsmRule[]> {\n const map = new Map<string, FsmRule[]>();\n for (const obj of objects) {\n if (!obj || typeof obj !== 'object') continue;\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const validations = Array.isArray(obj.validations) ? (obj.validations as AnyRec[]) : [];\n const rules: FsmRule[] = [];\n for (const v of validations) {\n if (!v || typeof v !== 'object' || v.type !== 'state_machine') continue;\n const field = typeof v.field === 'string' ? v.field : undefined;\n if (!field) continue;\n const transitions =\n v.transitions && typeof v.transitions === 'object' ? (v.transitions as Record<string, unknown>) : {};\n const states = new Set<string>();\n for (const s of Array.isArray(v.initialStates) ? v.initialStates : []) states.add(String(s));\n for (const from of Object.keys(transitions)) {\n states.add(String(from));\n const targets = transitions[from];\n for (const to of Array.isArray(targets) ? targets : []) states.add(String(to));\n }\n // A state_machine with neither transitions nor initialStates declares no\n // states — nothing to check against, so skip it (never flag every value).\n if (states.size > 0) rules.push({ field, states });\n }\n if (rules.length > 0) map.set(name, rules);\n }\n return map;\n}\n\n/** Best-effort label for a seed record — its externalId value(s), else its index. */\nfunction recordLabel(record: AnyRec, externalId: unknown, index: number): string {\n const keys = Array.isArray(externalId)\n ? (externalId as unknown[]).map(String)\n : typeof externalId === 'string'\n ? [externalId]\n : ['name'];\n const parts = keys.map((k) => record[k]).filter((v) => v != null && v !== '');\n return parts.length > 0 ? parts.map(String).join(' · ') : `#${index}`;\n}\n\n/**\n * Flag every seed record whose `state_machine`-governed field carries a value\n * the machine does not declare (framework#3433 follow-up). Returns the findings\n * (empty = clean). The caller decides how to surface them; the CLI folds them in\n * as advisory warnings.\n *\n * Reads `stack.objects` (for the state-machine rules) and `stack.data` (the\n * `SeedSchema[]` fixtures). Safe on any shape — a stack with no objects or no\n * `data` array yields no findings. A value that is not a plain string (an\n * unresolved `cel` Expression envelope, a number) is skipped: it cannot be\n * statically compared to the declared-state set.\n */\nexport function validateSeedStateMachine(stack: AnyRec): SeedStateMachineFinding[] {\n const out: SeedStateMachineFinding[] = [];\n const objects = Array.isArray(stack.objects) ? (stack.objects as AnyRec[]) : [];\n const seeds = Array.isArray(stack.data) ? (stack.data as AnyRec[]) : [];\n if (objects.length === 0 || seeds.length === 0) return out;\n\n const rulesByObject = fsmRulesByObject(objects);\n if (rulesByObject.size === 0) return out;\n\n seeds.forEach((seed, i) => {\n if (!seed || typeof seed !== 'object') return;\n const objectName = typeof seed.object === 'string' ? seed.object : undefined;\n if (!objectName) return;\n const rules = rulesByObject.get(objectName);\n if (!rules) return;\n const records = Array.isArray(seed.records) ? (seed.records as AnyRec[]) : [];\n\n records.forEach((record, j) => {\n if (!record || typeof record !== 'object') return;\n for (const rule of rules) {\n const value = record[rule.field];\n // Absent / cleared → nothing to check. A non-string (Expression\n // envelope, number) can't be compared statically → skip.\n if (value == null || value === '') continue;\n if (typeof value !== 'string') continue;\n if (rule.states.has(value)) continue;\n\n out.push({\n severity: 'warning',\n rule: SEED_VALUE_OUTSIDE_STATE_MACHINE,\n where: `seed \"${objectName}\" (${recordLabel(record, seed.externalId, j)})`,\n path: `data[${i}].records[${j}].${rule.field}`,\n message:\n `seeds '${rule.field}=${value}', which the '${objectName}' state machine does not declare ` +\n `(known states: ${[...rule.states].sort().join(', ')}). Seed writes are exempt from the ` +\n 'state_machine rule (#3433), so this is NOT rejected at write time — a typo lands silently.',\n hint:\n `If '${value}' is a real state, add it to the state machine (as an initial state or a ` +\n `transition endpoint). If it is a typo, correct it to a declared state. The exemption lets ` +\n 'a seed be born mid-lifecycle; it is not a licence to write an unknown state.',\n });\n }\n });\n });\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0090 D7] Security-domain publish linter.\n *\n * Every rule here is traceable to an observed failure class (the taxonomy\n * grows by incident, per the ADR):\n *\n * | Rule | Origin |\n * |-----------------------------------------|---------------------------------|\n * | security-owd-unset (error) | objectui#2348 leave_request 事故 |\n * | security-owd-alias (error) | ADR-0090 D4 canonical enum |\n * | security-external-wider (error) | ADR-0090 D11 external ≤ internal|\n * | security-wildcard-vama (error) | ADR-0066 superuser wildcard |\n * | security-anchor-high-privilege(error) | ADR-0090 D5/D9 anchors |\n * | security-role-word (error) | ADR-0090 D3 vocabulary freeze |\n * | security-book-audience-unknown-set(warn)| ADR-0046 §6.7 { permissionSet } |\n * | security-private-no-readscope (info) | admin-intent mismatch class |\n * | security-master-detail-ungranted(warn) | framework#2700 os-tianshun-mtc#43|\n * | security-grant-expired-at-authoring(err)| ADR-0091 D2 resolution filtering|\n * | security-delegation-missing-reason(err) | ADR-0091 D3 dual audit |\n *\n * Per ADR-0049 discipline these are NOT advisory security: every `error` rule\n * mirrors a runtime enforcement point (D1 fail-closed OWD default, D4 zod\n * enum + fail-closed evaluator, D5/D9 anchor binding gate, D3 rename wave) —\n * the lint moves the failure from runtime-deny to author-time fix-it. The lone\n * `warning` (master-detail-ungranted) likewise mirrors a runtime gate — the\n * object-level CRUD check (ADR-0055) — but stays advisory: it flags a *likely*\n * misconfiguration whose per-permission-set nuance it cannot fully adjudicate.\n *\n * Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input (works both\n * pre- and post-zod-parse, so `os lint` catches what the zod gate would\n * reject in `os compile` — with a better message).\n */\n\nimport { describeAnchorForbiddenBits } from '@objectstack/spec/security';\n\nexport const SECURITY_OWD_UNSET = 'security-owd-unset';\nexport const SECURITY_OWD_ALIAS = 'security-owd-alias';\nexport const SECURITY_EXTERNAL_WIDER = 'security-external-wider-than-internal';\nexport const SECURITY_WILDCARD_VAMA = 'security-wildcard-vama';\nexport const SECURITY_ANCHOR_HIGH_PRIVILEGE = 'security-anchor-high-privilege';\nexport const SECURITY_ROLE_WORD = 'security-role-word';\nexport const SECURITY_BOOK_AUDIENCE_UNKNOWN_SET = 'security-book-audience-unknown-set';\nexport const SECURITY_PRIVATE_NO_READSCOPE = 'security-private-no-readscope';\nexport const SECURITY_MASTER_DETAIL_UNGRANTED = 'security-master-detail-ungranted';\nexport const SECURITY_FLS_UNQUALIFIED_KEY = 'security-fls-unqualified-key';\nexport const SECURITY_GRANT_EXPIRED_AT_AUTHORING = 'security-grant-expired-at-authoring';\nexport const SECURITY_DELEGATION_MISSING_REASON = 'security-delegation-missing-reason';\n\nexport type SecuritySeverity = 'error' | 'warning' | 'info';\n\nexport interface SecurityFinding {\n severity: SecuritySeverity;\n /** Diagnostic rule id (`security-*`). */\n rule: string;\n /** Human-readable location, e.g. `object \"leave_request\"`. */\n where: string;\n /** Config path, e.g. `objects[3].sharingModel`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nconst CANONICAL_OWD = ['private', 'public_read', 'public_read_write', 'controlled_by_parent'] as const;\n/** [ADR-0090 D4] Legacy alias → canonical fix-it mapping. */\nconst OWD_ALIAS_FIX: Record<string, string> = {\n read: 'public_read',\n read_write: 'public_read_write',\n full: 'public_read_write',\n public: 'public_read_write',\n};\n/** D11 ordering for external ≤ internal (controlled_by_parent excluded). */\nconst OWD_WIDTH: Record<string, number> = {\n private: 0,\n public_read: 1,\n public_read_write: 2,\n};\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction owdOf(obj: AnyRec): unknown {\n return obj.sharingModel ?? (obj.security as AnyRec | undefined)?.sharingModel;\n}\n\nfunction isSystemObject(obj: AnyRec): boolean {\n return obj.isSystem === true || String(obj.name ?? '').startsWith('sys_');\n}\n\n/** snake_case identifier contains the reserved token `role`/`roles`. */\nfunction identifierHasRoleToken(name: unknown): boolean {\n if (typeof name !== 'string') return false;\n return name\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .some((tok) => tok === 'role' || tok === 'roles');\n}\n\n/** Free-text label contains the whole word `role(s)` (case-insensitive). */\nfunction labelHasRoleWord(label: unknown): boolean {\n if (typeof label !== 'string') return false;\n return /\\brole(s)?\\b/i.test(label);\n}\n\n/** The `reference`/`reference_to` target a relationship field points at. */\nfunction refOf(def: AnyRec): string | undefined {\n const r = (def.reference ?? def.reference_to) as unknown;\n return typeof r === 'string' && r ? r : undefined;\n}\n\n/**\n * The first `master_detail` field on an object, if any — its presence is what\n * makes the object a DETAIL (the child side of a master-detail; ADR-0055).\n * Works for both the array and name-keyed-map field forms (`asArray` folds the\n * map key into `name`).\n */\nfunction firstMasterDetailField(obj: AnyRec): { name: string; parent?: string } | undefined {\n for (const f of asArray(obj.fields)) {\n if (f.type === 'master_detail') {\n return { name: String(f.name ?? '?'), parent: refOf(f) };\n }\n }\n return undefined;\n}\n\n/**\n * Does a per-object permission entry open the object-level CRUD gate at all?\n * Any of the four CRUD bits, or a super-user bypass (View/Modify All Data),\n * counts — this mirrors the runtime `checkObjectPermission` gate (ADR-0066 D2):\n * that gate returns true if ANY set contributes one of these for the object.\n */\nfunction grantsObjectAccess(p: AnyRec): boolean {\n return (\n p.allowRead === true ||\n p.allowCreate === true ||\n p.allowEdit === true ||\n p.allowDelete === true ||\n p.viewAllRecords === true ||\n p.modifyAllRecords === true\n );\n}\n\n/**\n * Validate the security posture of a stack. Returns findings (empty = clean).\n * `error` findings gate the build in `os compile`; `info` is advisory.\n *\n * `opts.nowMs` injects the clock for the ADR-0091 authoring-time expiry rule\n * (tests); production callers omit it.\n */\nexport function validateSecurityPosture(stack: AnyRec, opts?: { nowMs?: number }): SecurityFinding[] {\n const findings: SecurityFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const objects = asArray(stack.objects);\n const permissionSets = asArray(stack.permissions);\n\n // ── D1/D4/D11: per-object OWD posture ────────────────────────────────\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object') continue;\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const objPath = `objects[${i}]`;\n const owd = owdOf(obj);\n const external = obj.externalSharingModel;\n\n if (!isSystemObject(obj)) {\n if (owd == null) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_UNSET,\n where: `object \"${objName}\"`,\n path: `${objPath}.sharingModel`,\n message:\n `custom object \"${objName}\" declares no sharingModel (OWD). The runtime fails ` +\n `CLOSED to 'private' (ADR-0090 D1), but the baseline must be an authored decision, ` +\n `not an accident — this is the exact shape of the leave_request incident (objectui#2348).`,\n hint:\n `Declare sharingModel explicitly: 'private' (owner + shares; recommended default), ` +\n `'public_read', 'public_read_write', or 'controlled_by_parent' (master-detail children).`,\n });\n } else if (typeof owd === 'string' && OWD_ALIAS_FIX[owd]) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_ALIAS,\n where: `object \"${objName}\"`,\n path: `${objPath}.sharingModel`,\n message:\n `sharingModel '${owd}' is a retired alias (ADR-0090 D4). The runtime fails CLOSED ` +\n `to 'private' on unknown values, so this object is NOT ${owd === 'read' ? 'readable' : 'writable'} org-wide.`,\n hint: `Replace with the canonical value: sharingModel: '${OWD_ALIAS_FIX[owd]}'.`,\n });\n } else if (typeof owd === 'string' && !(CANONICAL_OWD as readonly string[]).includes(owd)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_ALIAS,\n where: `object \"${objName}\"`,\n path: `${objPath}.sharingModel`,\n message:\n `sharingModel '${owd}' is not a canonical OWD value; the runtime fails CLOSED to 'private'.`,\n hint: `Use one of: ${CANONICAL_OWD.join(', ')}.`,\n });\n }\n }\n\n // D11: external dial present on any object (system included) must obey\n // external ≤ internal. controlled_by_parent inherits the master's pair.\n if (typeof external === 'string') {\n if (OWD_ALIAS_FIX[external]) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_ALIAS,\n where: `object \"${objName}\"`,\n path: `${objPath}.externalSharingModel`,\n message: `externalSharingModel '${external}' is a retired alias (ADR-0090 D4).`,\n hint: `Replace with the canonical value: externalSharingModel: '${OWD_ALIAS_FIX[external]}'.`,\n });\n } else if (\n typeof owd === 'string' &&\n external in OWD_WIDTH &&\n owd in OWD_WIDTH &&\n OWD_WIDTH[external] > OWD_WIDTH[owd]\n ) {\n findings.push({\n severity: 'error',\n rule: SECURITY_EXTERNAL_WIDER,\n where: `object \"${objName}\"`,\n path: `${objPath}.externalSharingModel`,\n message:\n `externalSharingModel '${external}' is WIDER than the internal sharingModel '${owd}' — ` +\n `the external baseline must never exceed the internal one (ADR-0090 D11).`,\n hint: `Narrow externalSharingModel to '${owd}' or below (ordering: private < public_read < public_read_write).`,\n });\n }\n }\n }\n\n // ── ADR-0066 / D5/D9: permission-set posture ─────────────────────────\n for (let i = 0; i < permissionSets.length; i++) {\n const ps = permissionSets[i];\n if (!ps || typeof ps !== 'object') continue;\n const psName = typeof ps.name === 'string' ? ps.name : `(permission set ${i})`;\n const psPath = `permissions[${i}]`;\n const objectsMap = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n\n // [#19 / permission zoo audit] FLS keys MUST be `<object>.<field>`\n // qualified. The runtime evaluator matches keys by object prefix\n // (`getFieldPermissions`: `key.startsWith(objectName + '.')`), so a bare\n // `budget` key matches NOTHING — the declared masking silently never\n // enforces (the worst declared-≠-enforced class, ADR-0049). The showcase\n // itself shipped this bug for months.\n const flsMap = (ps.fields && typeof ps.fields === 'object' ? ps.fields : {}) as AnyRec;\n for (const flsKey of Object.keys(flsMap)) {\n if (flsKey.includes('.')) continue;\n findings.push({\n severity: 'error',\n rule: SECURITY_FLS_UNQUALIFIED_KEY,\n where: `permission set \"${psName}\"`,\n path: `${psPath}.fields[\"${flsKey}\"]`,\n message:\n `field-permission key '${flsKey}' is not object-qualified — the runtime matches FLS keys ` +\n `by '<object>.<field>' prefix, so a bare key is silently IGNORED and the declared masking never enforces.`,\n hint: `Qualify the key with its object, e.g. 'crm_opportunity.${flsKey}': { readable: true, editable: false }.`,\n });\n }\n\n const wildcard = objectsMap['*'] as AnyRec | undefined;\n if (wildcard && (wildcard.viewAllRecords === true || wildcard.modifyAllRecords === true)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_WILDCARD_VAMA,\n where: `permission set \"${psName}\"`,\n path: `${psPath}.objects.*`,\n message:\n `'*' wildcard carrying View All / Modify All Data — a package-authored superuser. ` +\n `Only the platform's own admin set may combine the wildcard with VAMA (ADR-0066).`,\n hint:\n `Enumerate the objects this set really needs, or drop viewAllRecords/modifyAllRecords ` +\n `from the wildcard entry. App-level admins belong in an ordinary set the customer binds ` +\n `to a position of their choosing (ADR-0090 D9).`,\n });\n }\n\n // D5: an isDefault set is a SUGGESTED binding to the `everyone` anchor —\n // hold it to the anchor tier at author time (the runtime gate enforces the\n // same predicate at bind time; this moves the failure to the author).\n if (ps.isDefault === true) {\n const offending = describeAnchorForbiddenBits(ps, 'everyone');\n if (offending) {\n findings.push({\n severity: 'error',\n rule: SECURITY_ANCHOR_HIGH_PRIVILEGE,\n where: `permission set \"${psName}\"`,\n path: `${psPath}.isDefault`,\n message:\n `isDefault:true suggests binding this set to the 'everyone' audience anchor, but it ` +\n `carries ${offending} — the runtime will refuse the binding (ADR-0090 D5/D9).`,\n hint:\n `Split the powerful bits into a separate set granted through ordinary positions, and ` +\n `keep the everyone-suggested set low-privilege.`,\n });\n }\n }\n }\n\n // ── D3: the word \"role\" is reserved-forbidden ────────────────────────\n // Scope: security-relevant identifiers/labels (objects, fields, actions,\n // permission sets, positions, apps). Pages/views/components are NOT\n // scanned — `role` there is HTML/ARIA semantics, not permission vocabulary.\n // The sole platform exception (better-auth `sys_member.role`) is a system\n // object, which app stacks never author.\n const flagRole = (kind: string, name: unknown, label: unknown, where: string, path: string) => {\n if (identifierHasRoleToken(name)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_ROLE_WORD,\n where,\n path,\n message:\n `${kind} name \"${String(name)}\" uses the reserved word \"role\" — the platform vocabulary ` +\n `is permission_set (capability), position (distribution), business_unit (hierarchy) (ADR-0090 D3).`,\n hint: `Rename using 'position' for distribution groups or a domain word (e.g. 'function', 'duty').`,\n });\n } else if (labelHasRoleWord(label)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_ROLE_WORD,\n where,\n path: `${path.replace(/\\.name$/, '')}.label`,\n message: `${kind} label \"${String(label)}\" uses the reserved word \"role\" (ADR-0090 D3).`,\n hint: `Relabel with 'Position' (distribution) or a domain word — admins must meet ONE vocabulary.`,\n });\n }\n };\n\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object' || isSystemObject(obj)) continue;\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n flagRole('object', obj.name, obj.label, `object \"${objName}\"`, `objects[${i}].name`);\n for (const f of asArray(obj.fields)) {\n flagRole('field', f.name, f.label, `field \"${objName}.${String(f.name ?? '?')}\"`, `objects[${i}].fields.${String(f.name ?? '?')}.name`);\n }\n for (const [ai, action] of asArray(obj.actions).entries()) {\n flagRole('action', action.name, action.label, `action \"${objName}.${String(action.name ?? '?')}\"`, `objects[${i}].actions[${ai}].name`);\n }\n }\n for (let i = 0; i < permissionSets.length; i++) {\n const ps = permissionSets[i];\n if (!ps || typeof ps !== 'object') continue;\n flagRole('permission set', ps.name, ps.label, `permission set \"${String(ps.name ?? i)}\"`, `permissions[${i}].name`);\n }\n for (const [i, pos] of asArray(stack.positions).entries()) {\n flagRole('position', pos.name, pos.label, `position \"${String(pos.name ?? i)}\"`, `positions[${i}].name`);\n }\n for (const [i, app] of asArray(stack.apps).entries()) {\n flagRole('app', app.name, app.label, `app \"${String(app.name ?? i)}\"`, `apps[${i}].name`);\n }\n for (const [i, book] of asArray(stack.books).entries()) {\n // Books entered the security-relevant set when `book.audience` became a\n // permission-model reference (ADR-0046 §6.7 / ADR-0090): their names and\n // labels are access-adjacent UI copy.\n flagRole('book', book.name, book.label, `book \"${String(book.name ?? i)}\"`, `books[${i}].name`);\n }\n\n // ── Book audience → permission-set reference must resolve ────────────\n // A `{ permissionSet }` book audience names a set the reader must hold\n // (ADR-0046 §6.7). The runtime fails CLOSED on an unknown name (nobody\n // holds it → nobody reads the book), so a typo is not a leak — but it IS\n // the \"why can nobody see the Admin Guide\" support class, and packages\n // should gate their books on their own sets (ADR-0090 D9 / ADR-0086\n // provenance). Advisory: an environment-authored book may legitimately\n // reference an installed package's set that is not in THIS stack.\n const stackSetNames = new Set(\n permissionSets\n .map((ps) => (typeof ps.name === 'string' ? ps.name : undefined))\n .filter((n): n is string => !!n),\n );\n for (const [i, book] of asArray(stack.books).entries()) {\n const audience = (book as AnyRec).audience;\n if (!audience || typeof audience !== 'object') continue;\n const setName = (audience as AnyRec).permissionSet;\n if (typeof setName !== 'string' || setName.length === 0) continue;\n if (!stackSetNames.has(setName)) {\n findings.push({\n severity: 'warning',\n rule: SECURITY_BOOK_AUDIENCE_UNKNOWN_SET,\n where: `book \"${String(book.name ?? i)}\"`,\n path: `books[${i}].audience.permissionSet`,\n message:\n `book audience references permission set \"${setName}\", which this stack does not declare. ` +\n `The runtime fails closed — no holder means NO reader can open the book.`,\n hint:\n `Gate the book on one of this package's own permission sets (ADR-0090 D9, e.g. its admin set), ` +\n `or fix the typo. Ignore if the set is intentionally provided by another installed package.`,\n });\n }\n }\n\n // ── Admin-intent mismatch: private object, plain read, no depth ──────\n // An object whose baseline is private (explicit or D1-defaulted) where a set\n // grants allowRead with neither readScope nor viewAllRecords: every reader\n // sees ONLY their own records. Legitimate (personal to-dos) often enough\n // that this stays `info` — but it is the #1 \"why can't 李四 see the data\"\n // support class, so say it out loud at author time.\n const privateObjects = new Set(\n objects\n .filter((o) => o && typeof o === 'object' && !isSystemObject(o))\n .filter((o) => {\n const owd = owdOf(o);\n return owd == null || owd === 'private';\n })\n .map((o) => String(o.name ?? '')),\n );\n if (privateObjects.size > 0) {\n for (let i = 0; i < permissionSets.length; i++) {\n const ps = permissionSets[i];\n if (!ps || typeof ps !== 'object') continue;\n const psName = typeof ps.name === 'string' ? ps.name : `(permission set ${i})`;\n const objectsMap = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n for (const [objName, rawPerm] of Object.entries(objectsMap)) {\n if (!privateObjects.has(objName)) continue;\n const p = (rawPerm ?? {}) as AnyRec;\n if (p.allowRead === true && p.readScope == null && p.viewAllRecords !== true) {\n findings.push({\n severity: 'info',\n rule: SECURITY_PRIVATE_NO_READSCOPE,\n where: `permission set \"${psName}\"`,\n path: `permissions[${i}].objects.${objName}.readScope`,\n message:\n `\"${objName}\" is private (OWD) and this set grants allowRead without a readScope — ` +\n `holders see ONLY records they own (plus explicit shares).`,\n hint:\n `If that is intended (personal data), ignore this. Otherwise add readScope: ` +\n `'own_and_reports' | 'unit' | 'unit_and_below' | 'org', or widen the object's sharingModel.`,\n });\n }\n }\n }\n }\n\n // ── ADR-0055: master-detail DETAIL object with no object-level CRUD ───\n // A master-detail CHILD derives its RECORD-level scope from the master\n // (`controlled_by_parent`) — but that is gate ②. Object-level CRUD is a\n // SEPARATE gate ① (`checkObjectPermission`) that is NEVER derived: a set that\n // lists the parent but forgets the child denies role-bound non-admin users a\n // 403 *before* the parent-derived access is ever consulted, surfacing as the\n // silent \"can't fill in / can't submit the subtable\" trap (framework#2700,\n // downstream os-tianshun-mtc#43). Statically detectable: a detail (has a\n // master_detail field) that NO authored permission set grants.\n //\n // Advisory `warning` — it does not gate the build. Two deliberate silences\n // keep the false-positive rate near zero: (a) if the package authors no\n // permission sets there is nothing to compare against, and (b) a package-\n // declared `'*'` wildcard grant is treated as covering every object (a broad\n // grant is an explicit choice — suppress rather than cry wolf). The residual\n // per-set gap (one role grants it, another forgets it) is intentionally out\n // of scope (issue #2700); the platform's own default admin set lives outside\n // the linted stack, so it never masks a package that forgot the child here.\n if (permissionSets.length > 0) {\n const wildcardGrantsAll = permissionSets.some((ps) =>\n grantsObjectAccess(((ps.objects as AnyRec | undefined)?.['*'] ?? {}) as AnyRec),\n );\n if (!wildcardGrantsAll) {\n const grantedObjects = new Set<string>();\n for (const ps of permissionSets) {\n const objectsMap = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n for (const [objName, rawPerm] of Object.entries(objectsMap)) {\n if (objName === '*') continue;\n if (grantsObjectAccess((rawPerm ?? {}) as AnyRec)) grantedObjects.add(objName);\n }\n }\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object' || isSystemObject(obj)) continue;\n const objName = typeof obj.name === 'string' ? obj.name : '';\n if (!objName || grantedObjects.has(objName)) continue;\n const md = firstMasterDetailField(obj);\n if (!md) continue;\n const parentText = md.parent ? ` → \"${md.parent}\"` : '';\n findings.push({\n severity: 'warning',\n rule: SECURITY_MASTER_DETAIL_UNGRANTED,\n where: `object \"${objName}\"`,\n path: `objects[${i}].fields.${md.name}`,\n message:\n `detail object \"${objName}\" (master_detail \"${md.name}\"${parentText}) has no object-level ` +\n `CRUD grant in any permission set. A master-detail child derives its RECORD-level access ` +\n `from the master (ADR-0055 controlled_by_parent), but object-level CRUD is a SEPARATE gate ` +\n `that is never derived — role-bound non-admin users are denied (403) before the ` +\n `parent-derived access is ever consulted (the silent \"can't submit the subtable\" trap).`,\n hint:\n `Grant \"${objName}\" in at least one permission set that already grants its master` +\n `${md.parent ? ` \"${md.parent}\"` : ''} — e.g. permissions[i].objects.${objName} = ` +\n `{ allowRead: true, allowCreate: true, allowEdit: true }. If no role should ever touch ` +\n `it (a pure system/internal table), name it sys_* or set isSystem: true.`,\n });\n }\n }\n }\n\n // ── ADR-0091: authored grant rows (seed data) — lifecycle sanity ──────\n // Grant assignments authored as seed data on the two user-grant tables.\n // Both rules mirror runtime enforcement (D2 resolution-time filtering; the\n // D3 delegation gate), per the ADR-0049 \"no advisory security\" discipline:\n // the lint moves the failure from silent-dead-grant to author-time fix-it.\n const GRANT_SEED_OBJECTS = new Set(['sys_user_position', 'sys_user_permission_set']);\n const nowMs = opts?.nowMs ?? Date.now();\n for (const [i, seed] of asArray(stack.data).entries()) {\n const seedObject = typeof seed.object === 'string' ? seed.object : '';\n if (!GRANT_SEED_OBJECTS.has(seedObject)) continue;\n const records = Array.isArray(seed.records) ? (seed.records as AnyRec[]) : [];\n for (let j = 0; j < records.length; j++) {\n const rec = (records[j] ?? {}) as AnyRec;\n const where = `seed \"${seedObject}\" record #${j}`;\n\n // D2: a valid_until already in the past (or unparseable) at authoring\n // time is a grant that will NEVER resolve — dead on arrival, fail-closed.\n const until = rec.valid_until;\n if (until != null && until !== '') {\n const ms =\n typeof until === 'number'\n ? (until < 1e12 ? until * 1000 : until)\n : until instanceof Date\n ? until.getTime()\n : typeof until === 'string'\n ? Date.parse(until)\n : Number.NaN;\n if (Number.isNaN(ms) || ms <= nowMs) {\n findings.push({\n severity: 'error',\n rule: SECURITY_GRANT_EXPIRED_AT_AUTHORING,\n where,\n path: `data[${i}].records[${j}].valid_until`,\n message: Number.isNaN(ms)\n ? `valid_until ${JSON.stringify(until)} is not a parseable timestamp — the resolver fails ` +\n `closed (ADR-0091 D2), so this grant will NEVER be active.`\n : `valid_until ${JSON.stringify(until)} is already in the past — this grant is expired at ` +\n `authoring time and will never resolve (ADR-0091 D2 filters it fail-closed).`,\n hint:\n `Set valid_until to a future instant (ISO-8601 UTC), or drop the column for an unbounded ` +\n `grant. If the row is a historical record, it belongs in audit history, not seed data.`,\n });\n }\n }\n\n // D3: delegation rows (delegated_from set) MUST carry a reason — the\n // dual-audit half the runtime gate also rejects.\n const delegatedFrom = rec.delegated_from;\n if (delegatedFrom != null && delegatedFrom !== '') {\n const reason = rec.reason;\n if (typeof reason !== 'string' || reason.trim().length === 0) {\n findings.push({\n severity: 'error',\n rule: SECURITY_DELEGATION_MISSING_REASON,\n where,\n path: `data[${i}].records[${j}].reason`,\n message:\n `delegation row (delegated_from = ${JSON.stringify(delegatedFrom)}) has no reason. ` +\n `ADR-0091 D3 requires a mandatory reason on every delegation for the dual audit trail ` +\n `(granted_by = writer, delegated_from = authority source, reason = why).`,\n hint: `Add reason: 'vacation stand-in for 张三, 2026-08-01..15' (free text, required).`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0105 D6] The two organization-axis red lines, enforced at authoring time.\n *\n * ADR-0105 gives organizations a reporting/grouping dimension\n * (`sys_organization.parent_organization_id`, sibling ordering). That dimension\n * is load-bearing for consolidated reporting — and dangerous, because it LOOKS\n * like a permission hierarchy. Two lines keep it from becoming one:\n *\n * | Rule | Red line |\n * |-----------------------------------------|-----------------------------------|\n * | org-axis-permission-inheritance (error) | D6 ①: no inheritance along the org tree |\n * | org-axis-cross-org-bu-grant (error) | D6 ②: business-unit trees stay org-internal |\n *\n * **① No permission inheritance along the org axis.** Cross-organization\n * visibility comes from membership union (`accessible_org_ids`, ADR-0105 D2) —\n * the engine's own Layer 0 wall — never from walking a parent reference. An RLS\n * policy or sharing rule that reads `parent_organization_id` builds a SECOND\n * permission hierarchy beside the business-unit tree: exactly the dual-hierarchy\n * mistake ADR-0057 D5 retired and ADR-0090 D3 finalized for positions. It also\n * silently outranks the wall it sits behind, since a Layer-1 policy cannot widen\n * Layer 0 (W1) — so the author gets a rule that appears to grant access and\n * does not. Fail at authoring, not in a support ticket.\n *\n * **② Business-unit trees remain org-internal.** `sys_business_unit` is\n * org-scoped and every BU mechanism (`unit_and_subordinates` sharing,\n * `adminScope` delegation, depth scopes) resolves within ONE organization. A\n * business-unit sharing rule on a PLATFORM-GLOBAL object (`tenancy.enabled:\n * false`) has no organization column to scope against, so the grant spans every\n * organization in the database — a cross-org BU grant by construction, and the\n * \"cross-org BU mega-tree\" the ADR rejected, arrived at by accident.\n *\n * Both are `error`, per ADR-0049 discipline: each mirrors a real enforcement\n * property (the Layer 0 wall's independence; the org-predicated BU resolver),\n * so the lint moves the failure from silent-wrong-answer to author-time fix-it.\n *\n * Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input.\n */\n\nexport const ORG_AXIS_PERMISSION_INHERITANCE = 'org-axis-permission-inheritance';\nexport const ORG_AXIS_CROSS_ORG_BU_GRANT = 'org-axis-cross-org-bu-grant';\n\nexport type OrgAxisSeverity = 'error' | 'warning';\n\nexport interface OrgAxisFinding {\n severity: OrgAxisSeverity;\n /** Diagnostic rule id (`org-axis-*`). */\n rule: string;\n /** Human-readable location, e.g. `permission set \"plant_reader\"`. */\n where: string;\n /** Config path, e.g. `permissions[2].rowLevelSecurity[0].using`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** The org-axis grouping reference. Reporting only — never an authorization input. */\nconst ORG_PARENT_FIELD = 'parent_organization_id';\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction str(v: unknown): string {\n return typeof v === 'string' ? v : '';\n}\n\n/** True iff the object opted out of tenancy — platform-global, no org column. */\nfunction isTenancyDisabled(object: AnyRec): boolean {\n const tenancy = object.tenancy as AnyRec | undefined;\n if (tenancy && typeof tenancy === 'object' && tenancy.enabled === false) return true;\n const systemFields = object.systemFields as AnyRec | undefined;\n if (systemFields && typeof systemFields === 'object' && systemFields.tenant === false) return true;\n return false;\n}\n\nconst INHERITANCE_HINT =\n `Remove the ${ORG_PARENT_FIELD} reference. Cross-organization visibility comes from MEMBERSHIP: ` +\n `under the \\`group\\` tenancy posture the engine's Layer 0 wall is ` +\n `\\`organization_id IN accessible_org_ids\\`, so a user who should see several organizations is made ` +\n `a member of them (ADR-0105 D2). A Layer-1 policy cannot widen Layer 0 anyway, so this rule would ` +\n `not grant the access it appears to. For a hierarchy INSIDE one organization, use the business-unit ` +\n `tree (\\`unit_and_subordinates\\` sharing, or a depth scope anchored on \\`sys_user_position\\`).`;\n\n/**\n * Lint an ObjectStack config for the ADR-0105 D6 organization-axis red lines.\n */\nexport function validateOrgAxisRedLines(stack: unknown): OrgAxisFinding[] {\n const findings: OrgAxisFinding[] = [];\n const cfg = (stack ?? {}) as AnyRec;\n\n // ── ① No permission inheritance along the org axis ────────────────────────\n //\n // RLS policies may live on a permission set (`rowLevelSecurity`) or be\n // authored per object; both reach the same compiler, so both are checked.\n const permissionSets = asArray(cfg.permissions ?? cfg.permissionSets);\n permissionSets.forEach((ps, psIndex) => {\n asArray(ps.rowLevelSecurity).forEach((policy, pIndex) => {\n for (const clause of ['using', 'check'] as const) {\n if (!str(policy[clause]).includes(ORG_PARENT_FIELD)) continue;\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_PERMISSION_INHERITANCE,\n where: `permission set \"${str(ps.name) || psIndex}\" policy \"${str(policy.name) || pIndex}\"`,\n path: `permissions[${psIndex}].rowLevelSecurity[${pIndex}].${clause}`,\n message:\n `RLS ${clause} reads \\`${ORG_PARENT_FIELD}\\`, which builds a permission hierarchy along the ` +\n `organization axis. ADR-0105 D6 forbids it: the org tree is a REPORTING dimension only.`,\n hint: INHERITANCE_HINT,\n });\n }\n });\n });\n\n const objects = asArray(cfg.objects);\n objects.forEach((object, oIndex) => {\n const objectName = str(object.name) || String(oIndex);\n\n asArray(object.rowLevelSecurity ?? object.rls).forEach((policy, pIndex) => {\n for (const clause of ['using', 'check'] as const) {\n if (!str(policy[clause]).includes(ORG_PARENT_FIELD)) continue;\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_PERMISSION_INHERITANCE,\n where: `object \"${objectName}\" policy \"${str(policy.name) || pIndex}\"`,\n path: `objects[${oIndex}].rowLevelSecurity[${pIndex}].${clause}`,\n message:\n `RLS ${clause} reads \\`${ORG_PARENT_FIELD}\\`, which builds a permission hierarchy along the ` +\n `organization axis. ADR-0105 D6 forbids it: the org tree is a REPORTING dimension only.`,\n hint: INHERITANCE_HINT,\n });\n }\n });\n });\n\n // Sharing rules — criteria and recipient may both reach for the org parent.\n asArray(cfg.sharingRules ?? cfg.sharing).forEach((rule, rIndex) => {\n const criteria = JSON.stringify(rule.criteria ?? rule.filter ?? '');\n const sharedTo = JSON.stringify(rule.sharedTo ?? rule.recipient ?? '');\n if (criteria.includes(ORG_PARENT_FIELD) || sharedTo.includes(ORG_PARENT_FIELD)) {\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_PERMISSION_INHERITANCE,\n where: `sharing rule \"${str(rule.name) || rIndex}\"`,\n path: `sharingRules[${rIndex}]`,\n message:\n `Sharing rule reads \\`${ORG_PARENT_FIELD}\\`, granting access by walking the organization ` +\n `tree. ADR-0105 D6 forbids permission inheritance along the org axis.`,\n hint: INHERITANCE_HINT,\n });\n }\n });\n\n // ── ② Business-unit trees remain org-internal ─────────────────────────────\n //\n // A `business_unit` recipient on a platform-global object has no organization\n // column to scope against, so the grant reaches every organization's rows.\n const tenancyDisabledObjects = new Set(\n objects.filter((o) => isTenancyDisabled(o)).map((o) => str(o.name)).filter(Boolean),\n );\n asArray(cfg.sharingRules ?? cfg.sharing).forEach((rule, rIndex) => {\n const target = str(rule.object ?? rule.objectName);\n if (!target || !tenancyDisabledObjects.has(target)) return;\n const sharedTo = (rule.sharedTo ?? rule.recipient) as AnyRec | undefined;\n const recipientType = str(sharedTo?.type);\n if (recipientType !== 'business_unit') return;\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_CROSS_ORG_BU_GRANT,\n where: `sharing rule \"${str(rule.name) || rIndex}\" on object \"${target}\"`,\n path: `sharingRules[${rIndex}].sharedTo`,\n message:\n `A business-unit sharing rule targets \"${target}\", which opted out of tenancy ` +\n `(\\`tenancy.enabled: false\\`). Platform-global objects carry no organization column, so this ` +\n `grant spans EVERY organization — a cross-organization business-unit grant, which ADR-0105 D6 ` +\n `forbids (BU trees are org-internal).`,\n hint:\n `Either scope the object to organizations (drop \\`tenancy.enabled: false\\` so Layer 0 walls it), ` +\n `or share it to a position / permission-set audience instead of a business unit. A ` +\n `platform-global catalog that everyone should read wants an OWD of \\`public_read\\`, not a BU grant.`,\n });\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0049 — references] Reference-integrity for dashboard header & widget\n * action targets (issue #3367).\n *\n * ADR-0049 established the \"enforce-or-remove\" gate for spec *properties*: a\n * declared property the runtime does not honour is a false promise and must be\n * enforced, marked experimental, or removed. This rule applies the SAME honesty\n * principle to *references*. A dashboard header action (or a widget's header\n * action button) names a target — a `script`/`modal` action, or a `url` route —\n * that must actually resolve. A dangling target ships a button that renders and,\n * on click, silently does nothing: a false affordance, exactly the failure\n * ADR-0049 exists to prevent, just for a reference rather than a property.\n *\n * Nothing in the protocol schema can express this: `actionUrl` is a free string,\n * so `{ actionType: 'script', actionUrl: 'export_dashboard_pdf' }` parses and\n * ships even when no such action is defined anywhere in the stack.\n *\n * Surfaces checked:\n * - dashboard `header.actions[]` — each `{ actionType, actionUrl }`\n * - dashboard `widgets[].actionUrl` (+ `actionType`) — the per-widget button\n *\n * Resolution mirrors the objectui runtime dispatch (`DashboardRenderer` +\n * `DashboardView`) so the lint flags exactly what would fail to resolve at\n * runtime:\n *\n * actionType 'script' → `actionUrl` must name a DEFINED action (`stack.actions`\n * or any `object.actions`, by `name`). A script target that names no\n * defined action fails open at runtime (\"action not found\"). → ERROR.\n *\n * actionType 'modal' → `actionUrl` resolves if it names a defined action, OR\n * matches the runtime `<verb>_<object>` convention the modalHandler\n * implements (create_/new_/add_/edit_/update_ + a defined object), OR is a\n * bare defined object name (the handler falls back to that object's create\n * form). Otherwise → ERROR.\n *\n * actionType 'url' → a relative in-app path. WARN when a recognizable\n * `<collection>/<name>` segment (objects/reports/dashboards/pages/views)\n * names an entity that does not exist in this stack. External URLs\n * (`http(s)://`, `//`), interpolated targets (`${…}`), and opaque routes\n * (no recognized collection segment) are skipped — they cannot be resolved\n * statically and may be host/app/plugin routes. → WARNING.\n *\n * actionType 'flow' | 'api' — not checked: flow targets resolve against the\n * automation engine / other packages, and api targets are opaque endpoints.\n * Out of scope for #3367.\n *\n * Severity split follows the issue's acceptance criteria: an undefined\n * `script`/`modal` target FAILS validation (a genuine dead reference that fails\n * open at runtime as a dead button); an unresolved `url` route is advisory\n * (route resolution is app-context-dependent, and a path may be served by\n * another installed package or a host/console route). External, interpolated,\n * convention, and opaque targets are exempted to keep false positives near zero\n * — the same conservative posture as the sibling `lint-view-refs` and\n * `validate-capability-references` rules.\n */\n\nexport const DASHBOARD_ACTION_TARGET_UNDEFINED = 'dashboard-action-target-undefined';\nexport const DASHBOARD_ACTION_ROUTE_UNRESOLVED = 'dashboard-action-route-unresolved';\n\nexport type DashboardActionRefSeverity = 'error' | 'warning';\n\nexport interface DashboardActionRefFinding {\n /** `error` for a dangling script/modal action; `warning` for an unresolved url route. */\n severity: DashboardActionRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `dashboard \"sales_overview\" · header action \"Export PDF\"`. */\n where: string;\n /** Config path, e.g. `dashboards[2].header.actions[0].actionUrl`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records, injecting\n * `name` from the map key — mirrors the helper in the sibling authoring lints so\n * the rule works on both the parsed (array) and normalized (map) stack shapes. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** The runtime modal `<verb>_<object>` convention (objectui `DashboardView`\n * modalHandler): `create_/new_/add_/edit_/update_` + an object name opens that\n * object's create/edit form. */\nconst MODAL_VERB_RE = /^(?:create|new|add|edit|update)_(.+)$/;\n\n/** URL path segments that name a metadata collection, mapped to the stack key\n * whose members can appear after them in an in-app route\n * (`/…/objects/crm_lead`, `/reports/forecast`, `/dashboards/exec`, …). Both the\n * singular and plural spellings are accepted. */\nconst URL_COLLECTION_TO_STACK_KEY: Record<string, 'objects' | 'reports' | 'dashboards' | 'pages' | 'views'> = {\n object: 'objects',\n objects: 'objects',\n report: 'reports',\n reports: 'reports',\n dashboard: 'dashboards',\n dashboards: 'dashboards',\n page: 'pages',\n pages: 'pages',\n view: 'views',\n views: 'views',\n};\n\n/** Derive the name a top-level `views` container registers under (mirrors the\n * runtime loader's `resolveMetadataItemName('views', …)` fallbacks). */\nfunction viewContainerName(item: AnyRec): string | undefined {\n return (\n strName(item.name) ??\n strName(item.id) ??\n strName(item.object) ??\n strName((item.list as AnyRec | undefined)?.data && ((item.list as AnyRec).data as AnyRec).object) ??\n strName((item.form as AnyRec | undefined)?.data && ((item.form as AnyRec).data as AnyRec).object)\n );\n}\n\ninterface KnownTargets {\n /** Every action name defined in the stack (global + object-embedded). */\n actions: Set<string>;\n /** Object names (also valid as bare modal targets and `objects/<name>` routes). */\n objects: Set<string>;\n reports: Set<string>;\n dashboards: Set<string>;\n pages: Set<string>;\n /** View names routable as `views/<name>` — container names plus object names. */\n views: Set<string>;\n}\n\n/** Build the author-time \"known target\" sets from a stack. */\nfunction collectKnownTargets(stack: AnyRec): KnownTargets {\n const actions = new Set<string>();\n const objects = new Set<string>();\n const reports = new Set<string>();\n const dashboards = new Set<string>();\n const pages = new Set<string>();\n const views = new Set<string>();\n\n const collectNames = (v: unknown, into: Set<string>, name: (rec: AnyRec) => string | undefined) => {\n for (const item of asArray(v)) {\n if (!item || typeof item !== 'object') continue;\n const n = name(item);\n if (n) into.add(n);\n }\n };\n\n collectNames(stack.actions, actions, (a) => strName(a.name));\n for (const obj of asArray(stack.objects)) {\n if (!obj || typeof obj !== 'object') continue;\n const n = strName(obj.name);\n if (n) objects.add(n);\n collectNames(obj.actions, actions, (a) => strName(a.name));\n }\n collectNames(stack.reports, reports, (r) => strName(r.name));\n collectNames(stack.dashboards, dashboards, (d) => strName(d.name));\n collectNames(stack.pages, pages, (p) => strName(p.name));\n collectNames(stack.views, views, viewContainerName);\n // An object's default view is routable by the object's own name too.\n for (const o of objects) views.add(o);\n\n return { actions, objects, reports, dashboards, pages, views };\n}\n\n/** Does a `script`/`modal` `actionUrl` resolve? */\nfunction resolveActionTarget(\n actionType: 'script' | 'modal',\n target: string,\n known: KnownTargets,\n): boolean {\n if (known.actions.has(target)) return true;\n if (actionType === 'modal') {\n // Runtime modalHandler convention: `<verb>_<object>` or a bare object name\n // opens that object's create/edit form.\n if (known.objects.has(target)) return true;\n const m = MODAL_VERB_RE.exec(target);\n if (m && known.objects.has(m[1])) return true;\n }\n return false;\n}\n\n/**\n * Resolve a relative `url` in-app route. Returns:\n * - `null` when the target is not statically resolvable (external, interpolated,\n * or carries no recognized `<collection>/<name>` segment) — SKIP, no finding.\n * - `{ collection, name }` for a recognized `<collection>/<name>` pair that does\n * NOT exist in the stack — WARN.\n * - `undefined` when a recognized pair DID resolve — OK, no finding.\n */\nfunction resolveUrlRoute(\n target: string,\n known: KnownTargets,\n): { collection: string; name: string } | null | undefined {\n // External / protocol-relative — leaves the app; not an in-app route.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(target) || target.startsWith('//')) return null;\n // Interpolated — resolved by the renderer at click time, not statically known.\n if (target.includes('${')) return null;\n // Only relative in-app paths are considered.\n if (!target.startsWith('/')) return null;\n\n // Strip query + hash, then split into non-empty segments.\n const pathPart = target.split(/[?#]/, 1)[0];\n const segments = pathPart.split('/').filter(Boolean);\n\n for (let i = 0; i < segments.length - 1; i++) {\n const stackKey = URL_COLLECTION_TO_STACK_KEY[segments[i]];\n if (!stackKey) continue;\n const name = segments[i + 1];\n if (known[stackKey].has(name)) return undefined; // resolved\n return { collection: segments[i], name }; // recognized shape, unknown name\n }\n return null; // no recognized collection segment — opaque route, skip\n}\n\ninterface HeaderAction {\n actionType?: string;\n actionUrl?: string;\n label?: string;\n}\n\n/**\n * Validate every dashboard header / widget action reference in a stack. Returns\n * findings (empty = clean). `script`/`modal` dead targets are errors; `url`\n * unresolved routes are warnings.\n */\nexport function validateDashboardActionRefs(stack: AnyRec): DashboardActionRefFinding[] {\n const findings: DashboardActionRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const dashboards = asArray(stack.dashboards);\n if (dashboards.length === 0) return findings;\n\n const known = collectKnownTargets(stack);\n\n const checkOne = (\n action: HeaderAction,\n where: string,\n path: string,\n ) => {\n const target = strName(action.actionUrl);\n if (!target) return; // nothing referenced (widget with no action button)\n if (target.includes('${')) return; // dynamic target — not statically resolvable\n\n // Renderer default: a missing actionType is treated as a 'url' navigation\n // (DashboardRenderer builds header ActionDefs with `type: actionType || 'url'`).\n const actionType = strName(action.actionType) ?? 'url';\n\n if (actionType === 'script' || actionType === 'modal') {\n if (resolveActionTarget(actionType, target, known)) return;\n const kindWord = actionType === 'script' ? 'script' : 'modal';\n findings.push({\n severity: 'error',\n rule: DASHBOARD_ACTION_TARGET_UNDEFINED,\n where,\n path,\n message:\n `${kindWord} action target \"${target}\" resolves to no defined action` +\n (actionType === 'modal' ? ' or object' : '') +\n `. The button renders but does nothing when clicked — a dangling reference ` +\n `the runtime cannot dispatch (ADR-0049: a declared reference must resolve).`,\n hint:\n actionType === 'modal'\n ? `Define an action named \"${target}\" (stack.actions or the object's actions), ` +\n `use the \"<verb>_<object>\" convention against a real object ` +\n `(e.g. \"create_<object>\"), point actionUrl at an existing object, or remove the button.`\n : `Define a script action named \"${target}\" (stack.actions or the object's actions) ` +\n `with an inline body or a registered handler, or remove the button.`,\n });\n return;\n }\n\n if (actionType === 'url') {\n const route = resolveUrlRoute(target, known);\n if (!route) return; // skip (external/interpolated/opaque) or resolved\n findings.push({\n severity: 'warning',\n rule: DASHBOARD_ACTION_ROUTE_UNRESOLVED,\n where,\n path,\n message:\n `url action target \"${target}\" points at ${route.collection}/${route.name}, ` +\n `but no ${route.collection.replace(/s$/, '')} named \"${route.name}\" is registered ` +\n `in this stack — the button likely navigates to a dead route.`,\n hint:\n `Check the path for a typo, define the referenced ${route.collection.replace(/s$/, '')}, ` +\n `or ignore this if the route is served by another installed package or a host/console route.`,\n });\n return;\n }\n // 'flow' | 'api' | custom types are out of scope (see module header).\n };\n\n for (let di = 0; di < dashboards.length; di++) {\n const dash = dashboards[di];\n if (!dash || typeof dash !== 'object') continue;\n const dashName = strName(dash.name) ?? `(dashboard ${di})`;\n const dashPath = `dashboards[${di}]`;\n\n // Header actions.\n const headerActions = asArray((dash.header as AnyRec | undefined)?.actions);\n for (let ai = 0; ai < headerActions.length; ai++) {\n const action = headerActions[ai] as HeaderAction | null;\n if (!action || typeof action !== 'object') continue;\n const label = strName(action.label) ?? strName(action.actionUrl) ?? `#${ai}`;\n checkOne(\n action,\n `dashboard \"${dashName}\" · header action \"${label}\"`,\n `${dashPath}.header.actions[${ai}].actionUrl`,\n );\n }\n\n // Per-widget action buttons.\n const widgets = asArray(dash.widgets);\n for (let wi = 0; wi < widgets.length; wi++) {\n const widget = widgets[wi];\n if (!widget || typeof widget !== 'object') continue;\n if (!strName(widget.actionUrl)) continue;\n const widgetId = strName(widget.id) ?? `#${wi}`;\n checkOne(\n { actionType: widget.actionType as string | undefined, actionUrl: widget.actionUrl as string | undefined },\n `dashboard \"${dashName}\" · widget \"${widgetId}\" action`,\n `${dashPath}.widgets[${wi}].actionUrl`,\n );\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { classifyFilterToken, CONTEXT_TOKENS } from '@objectstack/spec/data';\n\n/**\n * Build-time filter-placeholder diagnostics (issue #3574).\n *\n * Filter values travel as JSON, so a user-scoped or time-scoped slice cannot\n * call code inline — it writes a placeholder that the client resolves just\n * before querying:\n *\n * { owner_id: '{current_user_id}', created_at: { $gte: '{week_start}' } }\n *\n * Exactly two vocabularies resolve inside a filter value: context tokens\n * (`{current_user_id}`, `{current_org_id}` — see `context-tokens.zod.ts`) and\n * date macros (`{today}`, `{30_days_ago}` — see `date-macros.zod.ts`).\n * Anything else is passed to the data engine **verbatim**, matches no row, and\n * the surface renders an empty result.\n *\n * ## Why this is an error, not a warning\n *\n * The runtime failure mode is silent and indistinguishable from success. A\n * metric widget filtered on an unresolved `{current_user}` renders `0`, which\n * looks exactly like a metric that is legitimately zero — no console error, no\n * server log, nothing for a human reviewer to notice. Issue #3574 found a\n * dashboard that had been broken this way since the day it was written.\n *\n * That failure mode is worse for AI authors than for humans. An AI reads a\n * successful query returning `0` as a correct answer and builds on it — it has\n * no instinct that the number looks wrong. Its correction loop is\n * \"author → validate → fix\", so a diagnostic only reaches it if the diagnostic\n * can fail the build. A runtime warning in a server log is invisible to it.\n * Hence: authoring-time error.\n *\n * The near-miss spellings this catches are not hypothetical. Each is a correct\n * spelling *somewhere else* in the platform, which is precisely why authors\n * reach for them:\n *\n * - `{current_user}` — `current_user.id` is the RLS expression root\n * - `{user_id}` — `{user_id}` is valid `titleFormat` field interpolation\n * - `{current_organization_id}` — `organization_id` is the real column name\n *\n * `CONTEXT_TOKEN_SUGGESTIONS` maps each to what the author meant, so the\n * diagnostic names the fix instead of only reporting the symptom.\n *\n * ## Scope — filter subtrees only\n *\n * The walk descends into `filter` / `filters` / `runtimeFilter` subtrees and\n * classifies string values inside them. It deliberately does NOT check\n * navigation `recordId` / `params`, which resolve an additional vocabulary —\n * `AppContextSelector` ids such as `{active_package}` — that is meaningless in\n * a filter because filters are not evaluated with the sidebar's selector\n * state. Restricting the walk keeps that legitimate usage out of the rule and\n * holds false positives at zero.\n *\n * Only whole-string placeholders are considered (`'{token}'` / `'${token}'`,\n * anchored). A value that merely contains braces is left alone.\n */\n\nexport const FILTER_TOKEN_UNKNOWN = 'filter-token-unknown';\n\nexport type FilterTokenSeverity = 'error' | 'warning';\n\nexport interface FilterTokenFinding {\n /** Always `error` today — an unresolved placeholder silently matches nothing. */\n severity: FilterTokenSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `dashboard \"sales\" · widget \"my_deals\"`. */\n where: string;\n /** Config path, e.g. `dashboards[0].widgets[2].filter.owner_id`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Keys whose subtree is a filter — the only place placeholders resolve. */\nconst FILTER_KEYS = new Set(['filter', 'filters', 'runtimeFilter']);\n\n/**\n * Coerce a collection (array or name-keyed map) to an array of records,\n * injecting `name` from the map key — mirrors the helper in the sibling\n * authoring lints so the rule works on both the parsed (array) and normalized\n * (map) stack shapes.\n */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction label(v: unknown, fallback: string): string {\n return typeof v === 'string' && v.length > 0 ? v : fallback;\n}\n\nconst KNOWN_LIST = CONTEXT_TOKENS.join('}, {');\n\n/**\n * Classify every string inside an already-identified filter subtree.\n *\n * Walks arrays and plain objects uniformly, which is what makes this work\n * across the platform's two filter shapes: the MongoDB-style object a\n * dashboard widget carries (`{ owner_id: '{current_user_id}' }`) and the\n * condition/triple arrays a list view carries\n * (`[{ field, operator, value }]` / `[['owner','=','…']]`). The bug this rule\n * exists for was caused by a resolver that handled only one of those shapes.\n */\nfunction walkFilterValues(\n node: unknown,\n path: string,\n where: string,\n out: FilterTokenFinding[],\n seen: Set<unknown>,\n): void {\n if (node === null || node === undefined) return;\n\n if (typeof node === 'string') {\n const cls = classifyFilterToken(node);\n if (cls?.kind === 'unknown') {\n const suggestion = cls.suggestion;\n out.push({\n severity: 'error',\n rule: FILTER_TOKEN_UNKNOWN,\n where,\n path,\n message:\n `Filter value \"${node}\" is not a resolvable placeholder. It is sent to the ` +\n `data engine as a literal string, matches no record, and the surface renders empty.`,\n hint: suggestion\n ? `Did you mean \"{${suggestion}}\"? Context tokens are {${KNOWN_LIST}}; ` +\n `time-based values use date macros such as {today} or {30_days_ago}.`\n : `Resolvable placeholders are the context tokens {${KNOWN_LIST}} and the ` +\n `date macros (e.g. {today}, {week_start}, {30_days_ago}). To filter on a ` +\n `literal value that happens to look like a placeholder, this is not supported — ` +\n `rename the value.`,\n });\n }\n return;\n }\n\n if (typeof node !== 'object') return;\n // Metadata graphs can be cyclic once normalized; guard the walk.\n if (seen.has(node)) return;\n seen.add(node);\n\n if (Array.isArray(node)) {\n node.forEach((v, i) => walkFilterValues(v, `${path}[${i}]`, where, out, seen));\n return;\n }\n\n for (const [k, v] of Object.entries(node as AnyRec)) {\n walkFilterValues(v, `${path}.${k}`, where, out, seen);\n }\n}\n\n/**\n * Find `filter` / `filters` / `runtimeFilter` subtrees anywhere beneath\n * `node`, then classify the values inside them.\n *\n * Scanning for filter KEYS rather than enumerating known surfaces is\n * deliberate: widget filters, list-view filters, dataset and measure filters,\n * report runtime filters, and SDUI component filters all spell the key the\n * same way, and a new surface that follows the convention is covered the day\n * it ships. Enumerating surfaces is how #3574 happened — the dashboard was\n * simply never added to the list.\n */\nfunction scanForFilters(\n node: unknown,\n path: string,\n where: string,\n out: FilterTokenFinding[],\n seen: Set<unknown>,\n): void {\n if (!node || typeof node !== 'object') return;\n if (seen.has(node)) return;\n seen.add(node);\n\n if (Array.isArray(node)) {\n node.forEach((v, i) => scanForFilters(v, `${path}[${i}]`, where, out, seen));\n return;\n }\n\n for (const [k, v] of Object.entries(node as AnyRec)) {\n const childPath = `${path}.${k}`;\n if (FILTER_KEYS.has(k)) {\n walkFilterValues(v, childPath, where, out, new Set());\n continue;\n }\n scanForFilters(v, childPath, where, out, seen);\n }\n}\n\n/**\n * Validate filter placeholders across a schema-parsed stack.\n *\n * Pure `(stack) => Finding[]`; no I/O. Covers dashboards (widget + global\n * filters), objects (list views), top-level view containers, reports,\n * datasets, and pages.\n */\nexport function validateFilterTokens(stack: Record<string, unknown> | undefined | null): FilterTokenFinding[] {\n if (!stack || typeof stack !== 'object') return [];\n const out: FilterTokenFinding[] = [];\n\n const surfaces: Array<[key: string, kind: string]> = [\n ['dashboards', 'dashboard'],\n ['objects', 'object'],\n ['views', 'view'],\n ['reports', 'report'],\n ['datasets', 'dataset'],\n ['pages', 'page'],\n ['apps', 'app'],\n ];\n\n for (const [key, kind] of surfaces) {\n const items = asArray((stack as AnyRec)[key]);\n items.forEach((item, i) => {\n const name = label(item.name ?? item.id, `#${i}`);\n // Dashboards are the surface #3574 was filed against; name the widget in\n // `where` so the author can jump straight to it.\n if (kind === 'dashboard') {\n const widgets = Array.isArray(item.widgets) ? (item.widgets as AnyRec[]) : [];\n widgets.forEach((w, wi) => {\n const wName = label(w.id ?? w.title, `#${wi}`);\n scanForFilters(\n w,\n `${key}[${i}].widgets[${wi}]`,\n `dashboard \"${name}\" · widget \"${wName}\"`,\n out,\n new Set(),\n );\n });\n // …and everything else on the dashboard (globalFilters, header, etc.)\n // minus the widgets already covered above.\n const { widgets: _skip, ...rest } = item;\n scanForFilters(rest, `${key}[${i}]`, `dashboard \"${name}\"`, out, new Set());\n return;\n }\n scanForFilters(item, `${key}[${i}]`, `${kind} \"${name}\"`, out, new Set());\n });\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0072 — reference resolvability] Object-name reference integrity for the\n * surfaces no other rule owns (issue #3583).\n *\n * A HotCRM audit (~18k lines of shipped metadata) found ~20 instances of ONE\n * bug class: metadata naming an object that does not exist. Every instance\n * passed `objectstack validate` and `objectstack lint` cleanly and failed\n * SILENTLY at runtime — `object: 'user'` where the platform object is\n * `sys_user`, navigation targeting `sys_approval_process` (which\n * `@objectstack/plugin-approvals` never registers; ADR-0019 removed it).\n *\n * `defineStack`'s `validateCrossReferences` already hard-fails on the object\n * references it knows about (hooks, view data, seeds, mappings, permission\n * grants, nav `objectName`, action targets). This rule covers the REST — the\n * reference sites that are plain `z.string()` in the schema and therefore ship\n * whatever the author typed:\n *\n * - action params — `reference` (inline lookup/master_detail target) and\n * `objectOverride` (the object owning a field-backed param). Both are the\n * record picker's search target; a dead one degrades the dialog to a raw\n * id text input.\n * - dashboard `globalFilters[].optionsFrom.object` — the object a filter\n * dropdown fetches its options from. Dead → an always-empty dropdown.\n * - navigation `requiresObject` / `requiresService` capability gates. This is\n * the escape hatch `stack.zod.ts` honours to SKIP nav validation, so a typo\n * here is doubly silent: the entry is hidden forever (the runtime never\n * finds the object in its SchemaRegistry) AND the skip suppresses the\n * cross-reference error that would have caught the nav target.\n * - the nav `objectName` of an item that carries `requiresObject` — exempted\n * from the `defineStack` throw for good reason (it may come from another\n * package), but still worth an advisory when NO known package provides it.\n *\n * ── Severity ladder (the point of the rule) ──────────────────────────────\n *\n * Prior rules answered \"might this object come from another package?\" with a\n * PREFIX GUESS (`name.startsWith('sys_')` → skip). That guess cannot tell\n * `sys_user` (real) from `sys_approval_process` (fictional), so every fictional\n * platform-prefixed reference shipped. This rule resolves against the curated\n * `PLATFORM_PROVIDED_OBJECT_NAMES` registry instead:\n *\n * 1. resolves in the stack's own objects → OK\n * 2. unresolved, NOT platform-prefixed → ERROR\n * (`user`, `total_revenue` — no cross-package story exists for an\n * unprefixed name, since a stack's objects are namespace-prefixed;\n * this is the pure typo class and the bulk of the HotCRM findings)\n * 3. unresolved, prefixed, IN the registry → OK\n * 4. unresolved, prefixed, NOT in the registry → WARNING\n * (`sys_approval_process` — no package we know of registers it, but a\n * third-party package still might, so advisory is the honest ceiling)\n *\n * Interpolated targets (`${…}`, `{…}`) are skipped — they resolve at render\n * time, the same conservative exemption `validate-dashboard-action-refs` uses\n * to keep false positives near zero (ADR-0072 D1: one dead finding and authors\n * stop trusting the linter).\n */\n\nimport {\n hasPlatformObjectPrefix,\n isPlatformProvidedObjectName,\n PLATFORM_PROVIDED_OBJECT_NAMES,\n} from '@objectstack/spec/system';\n\n/** Materialized once for the repeated edit-distance scans in `suggest`. */\nconst PLATFORM_NAMES: readonly string[] = [...PLATFORM_PROVIDED_OBJECT_NAMES];\n\nexport const OBJECT_REFERENCE_UNKNOWN = 'object-reference-unknown';\nexport const OBJECT_REFERENCE_UNREGISTERED_PLATFORM = 'object-reference-unregistered-platform';\n\nexport type ObjectRefSeverity = 'error' | 'warning';\n\nexport interface ObjectRefFinding {\n /** `error` for an unresolvable own-stack name; `warning` for an unknown platform name. */\n severity: ObjectRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `action \"mass_update\" · param \"owner\"`. */\n where: string;\n /** Config path, e.g. `actions[2].params[0].reference`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records,\n * injecting `name` from the map key — mirrors the sibling authoring lints so\n * the rule works on both the parsed (array) and normalized (map) stack shapes. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * A target the author cannot have meant literally — `${…}` or `{…}` resolved at\n * render time.\n *\n * Scanned rather than matched with `/\\{[^}]+\\}/`: that pattern backtracks\n * quadratically on a long run of `{` with no closing brace (CodeQL\n * polynomial-ReDoS), and the question here is simply \"is there a `{` with a\n * later `}` and something in between\", which one pass answers.\n */\nfunction isInterpolated(target: string): boolean {\n if (target.includes('${')) return true;\n const open = target.indexOf('{');\n // `+ 2` keeps the original \"at least one character between the braces\"\n // semantics, so a literal `{}` is not treated as a placeholder.\n return open !== -1 && target.indexOf('}', open + 2) !== -1;\n}\n\n/** Levenshtein-bounded \"did you mean?\" over the known names. */\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n // Only offer a suggestion that is plausibly the same identifier mistyped.\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\n/**\n * Validate every object-name reference on the surfaces listed in the module\n * header. Returns findings (empty = clean).\n */\nexport function validateObjectReferences(stack: AnyRec): ObjectRefFinding[] {\n const findings: ObjectRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const objects = asArray(stack.objects);\n const ownObjects = new Set<string>();\n for (const obj of objects) {\n const n = strName(obj.name);\n if (n) ownObjects.add(n);\n }\n\n /**\n * Resolve one reference through the ladder and record a finding if it fails.\n * `subject` describes the reference for the message (\"record-picker target\").\n */\n const check = (\n target: string | undefined,\n where: string,\n path: string,\n subject: string,\n fix: string,\n ) => {\n const name = strName(target);\n if (!name) return;\n if (isInterpolated(name)) return; // resolved at render time\n if (ownObjects.has(name)) return; // ① own object\n if (isPlatformProvidedObjectName(name)) return; // ③ known platform object\n\n if (hasPlatformObjectPrefix(name)) {\n // ④ Platform-shaped, but no package we know of registers it.\n findings.push({\n severity: 'warning',\n rule: OBJECT_REFERENCE_UNREGISTERED_PLATFORM,\n where,\n path,\n message:\n `${subject} \"${name}\" carries a platform namespace prefix, but no platform ` +\n `package, official plugin, or cloud runtime object registers that name — ` +\n `and this stack does not define it either. If nothing provides it at runtime ` +\n `the reference resolves to nothing and fails silently.` +\n suggest(name, PLATFORM_NAMES),\n hint:\n `Check the spelling against the object the providing package actually registers ` +\n `(e.g. \"sys_approval_request\", not \"sys_approval_process\" — the process object ` +\n `was removed when approval became a flow node, ADR-0019). If a third-party ` +\n `package genuinely provides it, this warning is expected. ${fix}`,\n });\n return;\n }\n\n // ② Unprefixed and unresolved — a stack's own objects are namespace-\n // prefixed and present here, so there is no legitimate elsewhere.\n findings.push({\n severity: 'error',\n rule: OBJECT_REFERENCE_UNKNOWN,\n where,\n path,\n message:\n `${subject} \"${name}\" resolves to no object defined in this stack. ` +\n `The reference is inert at runtime — nothing reports the miss.` +\n suggest(name, ownObjects),\n hint:\n `Point it at one of this stack's objects, or at a platform object by its full ` +\n `name (the platform user object is \"sys_user\", not \"user\"). ${fix}` +\n (ownObjects.size > 0 ? ` Defined objects: ${[...ownObjects].sort().join(', ')}.` : ''),\n });\n };\n\n // ── Actions (global + object-embedded) → param object targets ──\n const checkActionParams = (action: AnyRec, actionPath: string, actionLabel: string) => {\n const params = asArray(action.params);\n for (let pi = 0; pi < params.length; pi++) {\n const param = params[pi];\n if (!param || typeof param !== 'object') continue;\n const paramLabel = strName(param.name) ?? strName(param.field) ?? `#${pi}`;\n const where = `${actionLabel} · param \"${paramLabel}\"`;\n check(\n strName(param.reference),\n where,\n `${actionPath}.params[${pi}].reference`,\n 'record-picker target',\n 'Without a resolvable target the picker degrades to a raw record-id text input.',\n );\n check(\n strName(param.objectOverride),\n where,\n `${actionPath}.params[${pi}].objectOverride`,\n 'field-backed param object',\n 'The param inherits type/options from a field on this object, so an unknown object leaves it untyped.',\n );\n }\n };\n\n const globalActions = asArray(stack.actions);\n for (let ai = 0; ai < globalActions.length; ai++) {\n const action = globalActions[ai];\n if (!action || typeof action !== 'object') continue;\n checkActionParams(action, `actions[${ai}]`, `action \"${strName(action.name) ?? `#${ai}`}\"`);\n }\n\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!obj || typeof obj !== 'object') continue;\n const objName = strName(obj.name) ?? `#${oi}`;\n const objActions = asArray(obj.actions);\n for (let ai = 0; ai < objActions.length; ai++) {\n const action = objActions[ai];\n if (!action || typeof action !== 'object') continue;\n checkActionParams(\n action,\n `objects[${oi}].actions[${ai}]`,\n `object \"${objName}\" · action \"${strName(action.name) ?? `#${ai}`}\"`,\n );\n }\n }\n\n // ── Dashboard global filters → optionsFrom.object ──\n const dashboards = asArray(stack.dashboards);\n for (let di = 0; di < dashboards.length; di++) {\n const dash = dashboards[di];\n if (!dash || typeof dash !== 'object') continue;\n const dashName = strName(dash.name) ?? `#${di}`;\n const filters = asArray(dash.globalFilters);\n for (let fi = 0; fi < filters.length; fi++) {\n const filter = filters[fi];\n if (!filter || typeof filter !== 'object') continue;\n const optionsFrom = filter.optionsFrom as AnyRec | undefined;\n if (!optionsFrom || typeof optionsFrom !== 'object') continue;\n check(\n strName(optionsFrom.object),\n `dashboard \"${dashName}\" · filter \"${strName(filter.name) ?? `#${fi}`}\"`,\n `dashboards[${di}].globalFilters[${fi}].optionsFrom.object`,\n 'filter options source',\n 'The dropdown fetches its options from this object; an unknown one renders an always-empty filter.',\n );\n }\n }\n\n // ── App navigation → requiresObject gates (and gated objectName) ──\n const apps = asArray(stack.apps);\n for (let ai = 0; ai < apps.length; ai++) {\n const app = apps[ai];\n if (!app || typeof app !== 'object') continue;\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walkNav = (items: unknown, basePath: string) => {\n const navItems = asArray(items);\n for (let ni = 0; ni < navItems.length; ni++) {\n const nav = navItems[ni];\n if (!nav || typeof nav !== 'object') continue;\n const navId = strName(nav.id) ?? `#${ni}`;\n const where = `app \"${appName}\" · nav \"${navId}\"`;\n const navPath = `${basePath}[${ni}]`;\n\n check(\n strName(nav.requiresObject),\n where,\n `${navPath}.requiresObject`,\n 'capability gate object',\n 'The entry is hidden unless this object is registered, so a typo hides it permanently — ' +\n 'and it suppresses the nav cross-reference check that would have caught the target.',\n );\n\n // A nav target exempted from the `defineStack` throw by `requiresObject`\n // still deserves an advisory when nothing known provides it.\n if (nav.requiresObject && strName(nav.objectName)) {\n check(\n strName(nav.objectName),\n where,\n `${navPath}.objectName`,\n 'navigation target',\n 'Declaring `requiresObject` exempts this target from the build-time check, so it is only verified here.',\n );\n }\n\n if (Array.isArray(nav.children)) walkNav(nav.children, `${navPath}.children`);\n }\n };\n\n walkNav(app.navigation, `apps[${ai}].navigation`);\n const areas = asArray(app.areas);\n for (let ri = 0; ri < areas.length; ri++) {\n walkNav(areas[ri]?.navigation, `apps[${ai}].areas[${ri}].navigation`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0049 — references] Action-NAME reference integrity for the surfaces that\n * bind an action by name (issue #3583).\n *\n * `locations` is an action's primary binding, but several surfaces reference\n * actions **by name** instead (see `content/docs/ui/actions.mdx` — \"Surfaces can\n * also reference actions by name\"). Every one of those fields is a plain\n * `z.array(z.string())` / `z.string()`, so a name that matches no defined action\n * parses and ships:\n *\n * - list views — `rowActions[]` / `bulkActions[]` (both the default `list`\n * container and each `listViews.<key>` entry)\n * - page components — `record:quick_actions` → `properties.actionNames[]`\n * - app navigation — `{ type: 'action', actionDef: { actionName } }`\n *\n * The HotCRM audit shipped `bulkActions: ['mass_update', 'mass_delete',\n * 'assign_owner']` with none of the three defined anywhere: the toolbar renders\n * the buttons, selecting rows enables them, and clicking does nothing.\n *\n * This is the same failure `validate-dashboard-action-refs` catches for\n * dashboard header/widget buttons (`DASHBOARD_ACTION_TARGET_UNDEFINED`), so it\n * carries the same severity: **error**. It is a genuine dead reference, and —\n * unlike an object name — there is no cross-package escape hatch to soften it\n * with. The runtime ships NO built-in action names (there is no\n * `BUILTIN_ACTIONS` registry; `list_toolbar`/`list_item` are *locations*, not\n * actions), so a name resolving nowhere is dead, full stop.\n *\n * Scope note: this rule asks only \"is this action defined ANYWHERE in the\n * stack?\". It deliberately does NOT check that a view's action belongs to the\n * view's own object, nor that the action declares the matching `location` —\n * both are real but distinct classes, and folding them in here would trade the\n * zero-false-positive posture (ADR-0072 D1) for coverage this issue did not ask\n * for. An action defined by another installed package is the one legitimate\n * miss; it is called out in the hint rather than guessed at.\n */\n\nimport { walkPageComponents } from './page-walk.js';\n\nexport const ACTION_NAME_UNDEFINED = 'action-name-undefined';\n\nexport type ActionNameRefSeverity = 'error' | 'warning';\n\nexport interface ActionNameRefFinding {\n /** Always `error` — a name-bound action that resolves nowhere is a dead button. */\n severity: ActionNameRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `view \"crm_lead\" · list \"all\" · bulkActions`. */\n where: string;\n /** Config path, e.g. `views[0].list.bulkActions[1]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction strList(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && x.length > 0) : [];\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\n/** Every action name defined in the stack (global + object-embedded). */\nfunction collectActionNames(stack: AnyRec): Set<string> {\n const names = new Set<string>();\n for (const action of asArray(stack.actions)) {\n const n = strName(action?.name);\n if (n) names.add(n);\n }\n for (const obj of asArray(stack.objects)) {\n if (!obj || typeof obj !== 'object') continue;\n for (const action of asArray(obj.actions)) {\n const n = strName(action?.name);\n if (n) names.add(n);\n }\n }\n return names;\n}\n\n/**\n * Validate every name-bound action reference in a stack. Returns findings\n * (empty = clean).\n */\nexport function validateActionNameRefs(stack: AnyRec): ActionNameRefFinding[] {\n const findings: ActionNameRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const known = collectActionNames(stack);\n\n const check = (name: string, where: string, path: string, surface: string) => {\n if (known.has(name)) return;\n findings.push({\n severity: 'error',\n rule: ACTION_NAME_UNDEFINED,\n where,\n path,\n message:\n `${surface} names action \"${name}\", which is defined by no action in this stack ` +\n `(neither \\`stack.actions\\` nor any object's \\`actions\\`). The button renders and ` +\n `does nothing when clicked — a dead affordance the runtime cannot dispatch.` +\n suggest(name, known),\n hint:\n `Define an action named \"${name}\" (in \\`stack.actions\\` or the object's \\`actions\\`) ` +\n `with the location this surface needs, remove the reference, or ignore this if the ` +\n `action is contributed by another installed package.` +\n (known.size > 0 ? ` Defined actions: ${[...known].sort().join(', ')}.` : ''),\n });\n };\n\n // ── List views: rowActions / bulkActions on `list` + each `listViews.<key>` ──\n const views = asArray(stack.views);\n for (let vi = 0; vi < views.length; vi++) {\n const view = views[vi];\n if (!view || typeof view !== 'object') continue;\n const viewName = strName(view.name) ?? strName(view.object) ?? `#${vi}`;\n\n const checkListContainer = (container: unknown, label: string, path: string) => {\n if (!container || typeof container !== 'object') return;\n const list = container as AnyRec;\n for (const key of ['rowActions', 'bulkActions'] as const) {\n const names = strList(list[key]);\n for (let ai = 0; ai < names.length; ai++) {\n check(\n names[ai],\n `view \"${viewName}\" · ${label} · ${key}`,\n `${path}.${key}[${ai}]`,\n key === 'bulkActions' ? 'Bulk-action menu' : 'Row-action menu',\n );\n }\n }\n };\n\n checkListContainer(view.list, 'list', `views[${vi}].list`);\n const listViews = view.listViews;\n if (listViews && typeof listViews === 'object' && !Array.isArray(listViews)) {\n for (const [key, lv] of Object.entries(listViews as AnyRec)) {\n checkListContainer(lv, `listViews.${key}`, `views[${vi}].listViews.${key}`);\n }\n }\n }\n\n // ── Page components: record:quick_actions → properties.actionNames ──\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n const page = pages[pi];\n if (!page || typeof page !== 'object') continue;\n const pageName = strName(page.name) ?? `#${pi}`;\n\n // Traversal is shared (`page-walk.ts`): the component tree is NOT where a\n // first reading suggests. Components hang off `regions[].components[]` and\n // `slots`, never a top-level `page.components`, and sub-trees nest inside\n // the untyped `properties` bag rather than under a `children` key.\n for (const { component, path } of walkPageComponents(page, `pages[${pi}]`)) {\n const props = component.properties as AnyRec | undefined;\n if (!props || typeof props !== 'object') continue;\n const names = strList(props.actionNames);\n for (let ai = 0; ai < names.length; ai++) {\n check(\n names[ai],\n `page \"${pageName}\" · component \"${strName(component.type) ?? '?'}\"`,\n `${path}.properties.actionNames[${ai}]`,\n 'Quick-actions bar',\n );\n }\n }\n }\n\n // ── App navigation: { type: 'action', actionDef: { actionName } } ──\n const apps = asArray(stack.apps);\n for (let ai = 0; ai < apps.length; ai++) {\n const app = apps[ai];\n if (!app || typeof app !== 'object') continue;\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walkNav = (items: unknown, basePath: string) => {\n const navItems = asArray(items);\n for (let ni = 0; ni < navItems.length; ni++) {\n const nav = navItems[ni];\n if (!nav || typeof nav !== 'object') continue;\n const navPath = `${basePath}[${ni}]`;\n const actionDef = nav.actionDef as AnyRec | undefined;\n const actionName = strName(actionDef?.actionName);\n if (nav.type === 'action' && actionName) {\n check(\n actionName,\n `app \"${appName}\" · nav \"${strName(nav.id) ?? `#${ni}`}\"`,\n `${navPath}.actionDef.actionName`,\n 'Navigation action item',\n );\n }\n if (Array.isArray(nav.children)) walkNav(nav.children, `${navPath}.children`);\n }\n };\n\n walkNav(app.navigation, `apps[${ai}].navigation`);\n const areas = asArray(app.areas);\n for (let ri = 0; ri < areas.length; ri++) {\n walkNav(areas[ri]?.navigation, `apps[${ai}].areas[${ri}].navigation`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0021 — semantic layer] Chart-binding integrity for the surfaces the\n * dashboard rule does not reach (issue #3583, assessment R4).\n *\n * `validate-widget-bindings` already resolves a dashboard widget's\n * `chartConfig` axes against its dataset's declared dimensions and measures\n * (`chart-field-unknown`). It is scoped to `stack.dashboards`, so three other\n * chart surfaces ship unchecked — and the HotCRM audit found exactly the bug\n * that scoping allows: an axis naming a RAW FIELD instead of a dataset measure.\n * Post-ADR-0021 the result rows are keyed by measure NAME (`sum_amount`), not\n * the base column (`amount`), so the axis renders and the series is empty.\n *\n * Surfaces covered here:\n *\n * 1. **Report charts** — `report.chart` and `report.blocks[].chart`.\n * `ReportChartSchema` narrows `xAxis`/`yAxis` from ChartConfig's\n * object/array shapes to bare STRINGS, which is why simply pointing the\n * dashboard rule at reports would find nothing: its `Array.isArray(yAxis)`\n * guard skips a string silently. `series[].name` keeps the array shape.\n * 2. **List-view charts** — `ListChartConfigSchema` (`dataset` +\n * `dimensions` + `values`), reachable through `views[].list`,\n * `views[].listViews.<key>`, and `objects[].listViews.<key>`.\n * 3. **Dataset-bound page chart components** — a `PageComponent` whose\n * `properties` carry a `dataset` (the `object-chart` component). Same\n * binding shape as a list chart, but it arrives through the untyped\n * `properties` bag.\n *\n * Not covered HERE, and deliberately so: the react `<ObjectChart>` block. It is\n * OBJECT-bound (`objectName` + an inline `aggregate`), so its result rows are\n * keyed by the RAW FIELD NAMES rather than by a measure name — the opposite\n * convention, which would make `chart-measure-unknown`'s message a lie. It also\n * arrives as JSX rather than config, so it needs the TypeScript compiler this\n * rule has no business loading. It is checked by `validate-react-page-props`\n * instead, against the naming convention `chartAggregateResultKeys`\n * (`@objectstack/spec/ui`) now pins down (#3701).\n */\n\nexport const CHART_DIMENSION_UNKNOWN = 'chart-dimension-unknown';\nexport const CHART_MEASURE_UNKNOWN = 'chart-measure-unknown';\nexport const CHART_DATASET_UNKNOWN = 'chart-dataset-unknown';\nexport const CHART_AXIS_NOT_SELECTED = 'chart-axis-not-selected';\n\nexport type ChartBindingSeverity = 'error' | 'warning';\n\nexport interface ChartBindingFinding {\n severity: ChartBindingSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `report \"hours_by_status\" · chart`. */\n where: string;\n /** Config path, e.g. `reports[2].chart.yAxis`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\nimport { walkPageComponents, type AnyRec } from './page-walk.js';\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction strList(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && x.length > 0) : [];\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const c of known) {\n const d = distance(target, c);\n if (d < bestScore) {\n bestScore = d;\n best = c;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\nfunction list(names: Iterable<string>): string {\n const all = [...names].sort();\n return all.length ? all.join(', ') : '(none)';\n}\n\n/** A dataset's declared dimension and measure names. */\ninterface DatasetNames {\n dimensions: Set<string>;\n measures: Set<string>;\n}\n\nfunction indexDatasets(stack: AnyRec): Map<string, DatasetNames> {\n const out = new Map<string, DatasetNames>();\n for (const ds of asArray(stack.datasets)) {\n const name = strName(ds.name);\n if (!name) continue;\n const dimensions = new Set<string>();\n for (const d of asArray(ds.dimensions)) {\n const n = strName(d.name);\n if (n) dimensions.add(n);\n }\n const measures = new Set<string>();\n for (const m of asArray(ds.measures)) {\n const n = strName(m.name);\n if (n) measures.add(n);\n }\n out.set(name, { dimensions, measures });\n }\n return out;\n}\n\n/**\n * One dataset-bound chart to check: the binding, the selection, and where it\n * came from. `xAxis`/`yAxis`/`series` are the ChartConfig-style axis refs;\n * `dimensions`/`values` are the list-chart-style selection.\n */\ninterface ChartBinding {\n dataset?: string;\n /** Selected dimension names (list-chart shape). */\n dimensions?: { names: string[]; path: string };\n /** Selected measure names (list-chart / report shape). */\n values?: { names: string[]; path: string };\n /** Single dimension ref (report `xAxis`). */\n xAxis?: { name: string; path: string };\n /** Single measure ref (report `yAxis`). */\n yAxis?: { name: string; path: string };\n /** Series names — measure refs (ChartConfig shape). */\n series?: Array<{ name: string; path: string }>;\n where: string;\n /** Path of the chart container, for the dataset-level finding. */\n path: string;\n}\n\nexport function validateChartBindings(stack: AnyRec): ChartBindingFinding[] {\n const findings: ChartBindingFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const datasets = indexDatasets(stack);\n if (datasets.size === 0 && !stack.reports && !stack.views && !stack.pages) return findings;\n\n const check = (binding: ChartBinding) => {\n const dsName = binding.dataset;\n if (!dsName) return; // nothing bound — the shape rules own that case\n const ds = datasets.get(dsName);\n if (!ds) {\n findings.push({\n severity: 'error',\n rule: CHART_DATASET_UNKNOWN,\n where: binding.where,\n path: `${binding.path}.dataset`,\n message:\n `binds dataset \"${dsName}\", which resolves to no declared dataset — ` +\n `the chart has no data to render.`,\n hint:\n `Declared datasets: ${list(datasets.keys())}.${suggest(dsName, datasets.keys())} ` +\n `Define it with defineDataset() or fix the reference (ADR-0021).`,\n });\n return;\n }\n\n const dimensionRef = (name: string, path: string) => {\n if (ds.dimensions.has(name)) return;\n findings.push({\n severity: 'error',\n rule: CHART_DIMENSION_UNKNOWN,\n where: binding.where,\n path,\n message:\n `\"${name}\" is not a dimension declared by dataset \"${dsName}\". ` +\n `Post-ADR-0021 result rows are keyed by DIMENSION NAME, not the base ` +\n `field, so this axis renders with no categories.`,\n hint:\n `Dataset dimensions: ${list(ds.dimensions)}.${suggest(name, ds.dimensions)} ` +\n `Declare the dimension on the dataset, or bind an existing one.`,\n });\n };\n\n const measureRef = (name: string, path: string, selected?: Set<string>) => {\n if (!ds.measures.has(name)) {\n findings.push({\n severity: 'error',\n rule: CHART_MEASURE_UNKNOWN,\n where: binding.where,\n path,\n message:\n `\"${name}\" is not a measure declared by dataset \"${dsName}\". ` +\n `Post-ADR-0021 result rows are keyed by MEASURE NAME (e.g. \"sum_amount\"), ` +\n `not the base field (e.g. \"amount\"), so this series comes back empty.`,\n hint:\n `Dataset measures: ${list(ds.measures)}.${suggest(name, ds.measures)} ` +\n `Declare the measure on the dataset, or bind an existing one.`,\n });\n return;\n }\n // Declared but not part of this chart's selection: the query never asks\n // for it, so the axis still plots nothing. Advisory — the selection may\n // legitimately be widened at runtime.\n if (selected && selected.size > 0 && !selected.has(name)) {\n findings.push({\n severity: 'warning',\n rule: CHART_AXIS_NOT_SELECTED,\n where: binding.where,\n path,\n message:\n `\"${name}\" is a declared measure of \"${dsName}\" but is not in this chart's ` +\n `selected values (${list(selected)}) — the query does not return it, ` +\n `so the series plots nothing.`,\n hint: `Add \"${name}\" to \\`values\\`, or point the axis at a selected measure.`,\n });\n }\n };\n\n const dimSel = binding.dimensions;\n if (dimSel) {\n for (let i = 0; i < dimSel.names.length; i++) {\n dimensionRef(dimSel.names[i], `${dimSel.path}[${i}]`);\n }\n }\n const valSel = binding.values;\n const selected = new Set(valSel?.names ?? []);\n if (valSel) {\n for (let i = 0; i < valSel.names.length; i++) {\n measureRef(valSel.names[i], `${valSel.path}[${i}]`);\n }\n }\n if (binding.xAxis) dimensionRef(binding.xAxis.name, binding.xAxis.path);\n if (binding.yAxis) measureRef(binding.yAxis.name, binding.yAxis.path, selected);\n for (const s of binding.series ?? []) measureRef(s.name, s.path, selected);\n };\n\n // ── 1. Report charts (report.chart + report.blocks[].chart) ──\n const reports = asArray(stack.reports);\n for (let ri = 0; ri < reports.length; ri++) {\n const report = reports[ri];\n if (!isRec(report)) continue;\n const reportName = strName(report.name) ?? `#${ri}`;\n\n const checkReportChart = (\n chart: unknown,\n dataset: string | undefined,\n values: string[],\n where: string,\n path: string,\n ) => {\n if (!isRec(chart)) return;\n check({\n dataset,\n // `values` is the report's measure SELECTION, not a chart ref; feeding\n // it in lets the yAxis \"declared but not selected\" check work without\n // reporting the selection itself twice.\n values: { names: values, path: `${path}.values` },\n xAxis: strName(chart.xAxis) ? { name: strName(chart.xAxis)!, path: `${path}.chart.xAxis` } : undefined,\n yAxis: strName(chart.yAxis) ? { name: strName(chart.yAxis)!, path: `${path}.chart.yAxis` } : undefined,\n series: asArray(chart.series)\n .map((s, si) => ({ name: strName(s.name), path: `${path}.chart.series[${si}].name` }))\n .filter((s): s is { name: string; path: string } => !!s.name),\n where,\n path: `${path}.chart`,\n });\n };\n\n checkReportChart(\n report.chart,\n strName(report.dataset),\n strList(report.values),\n `report \"${reportName}\" · chart`,\n `reports[${ri}]`,\n );\n\n const blocks = Array.isArray(report.blocks) ? report.blocks : [];\n for (let bi = 0; bi < blocks.length; bi++) {\n const block = blocks[bi];\n if (!isRec(block)) continue;\n checkReportChart(\n block.chart,\n strName(block.dataset),\n strList(block.values),\n `report \"${reportName}\" · block \"${strName(block.name) ?? `#${bi}`}\" chart`,\n `reports[${ri}].blocks[${bi}]`,\n );\n }\n }\n\n // ── 2. List-view charts ──\n const checkListChart = (container: unknown, where: string, path: string) => {\n if (!isRec(container)) return;\n const chart = container.chart;\n if (!isRec(chart)) return;\n check({\n dataset: strName(chart.dataset),\n dimensions: { names: strList(chart.dimensions), path: `${path}.chart.dimensions` },\n values: { names: strList(chart.values), path: `${path}.chart.values` },\n where,\n path: `${path}.chart`,\n });\n };\n\n const views = asArray(stack.views);\n for (let vi = 0; vi < views.length; vi++) {\n const view = views[vi];\n if (!isRec(view)) continue;\n const viewName = strName(view.name) ?? strName(view.objectName) ?? `#${vi}`;\n checkListChart(view.list, `view \"${viewName}\" · list chart`, `views[${vi}].list`);\n if (isRec(view.listViews)) {\n for (const [key, lv] of Object.entries(view.listViews)) {\n checkListChart(lv, `view \"${viewName}\" · listViews.${key} chart`, `views[${vi}].listViews.${key}`);\n }\n }\n }\n\n const objects = asArray(stack.objects);\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!isRec(obj) || !isRec(obj.listViews)) continue;\n const objName = strName(obj.name) ?? `#${oi}`;\n for (const [key, lv] of Object.entries(obj.listViews)) {\n checkListChart(\n lv,\n `object \"${objName}\" · listViews.${key} chart`,\n `objects[${oi}].listViews.${key}`,\n );\n }\n }\n\n // ── 3. Dataset-bound page chart components ──\n // A chart component arrives through the untyped `properties` bag. The\n // presence of a `dataset` key is what marks it dataset-bound (and so\n // checkable); an object-bound chart has none and is left alone.\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n const page = pages[pi];\n if (!isRec(page)) continue;\n const pageName = strName(page.name) ?? `#${pi}`;\n for (const { component, path } of walkPageComponents(page, `pages[${pi}]`)) {\n const props = isRec(component.properties) ? component.properties : undefined;\n if (!props || !strName(props.dataset)) continue;\n // A page chart mixes the list-chart selection (`dataset`/`dimensions`/\n // `values`) with ChartConfig-style axes (`yAxis: [{ field }]`), so both\n // shapes are read here.\n const axisRefs = asArray(props.yAxis)\n .map((a, ai) => ({ name: strName(a.field), path: `${path}.properties.yAxis[${ai}].field` }))\n .filter((a): a is { name: string; path: string } => !!a.name);\n const seriesRefs = asArray(props.series)\n .map((s, si) => ({ name: strName(s.name), path: `${path}.properties.series[${si}].name` }))\n .filter((s): s is { name: string; path: string } => !!s.name);\n check({\n dataset: strName(props.dataset),\n dimensions: { names: strList(props.dimensions), path: `${path}.properties.dimensions` },\n values: { names: strList(props.values), path: `${path}.properties.values` },\n series: [...axisRefs, ...seriesRefs],\n where: `page \"${pageName}\" · ${strName(component.type) ?? 'chart'}`,\n path: `${path}.properties`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0090 D6] Navigation reachability vs. granted access (issue #3583,\n * assessment R5).\n *\n * An app can put an object in its navigation without any permission set\n * granting read on it. Nothing rejects that: navigation and permissions are\n * separate metadata, each valid on its own. At runtime the entry renders, the\n * user clicks it, and the list view comes back permission-denied — for EVERY\n * user, including the admin, because a permission nobody was granted is a\n * permission nobody has. The HotCRM audit shipped two such objects\n * (`crm_forecast`, `crm_knowledge_article`).\n *\n * This is the first lint consumer of `buildAccessMatrix` (ADR-0090 D6), which\n * already derives one row per (permission set × object) with `read` folded\n * across `allowRead` / `viewAllRecords` / `modifyAllRecords`. The rule is that\n * matrix joined against what navigation exposes.\n *\n * ── Advisory, deliberately ──────────────────────────────────────────────\n *\n * A grant can legitimately live outside this stack: a permission set shipped by\n * another installed package, a platform default, or an org-level assignment\n * made after install. A stack is therefore not *wrong* to ship an ungranted nav\n * entry — it is only *suspicious*, and the ceiling for a static check is a\n * warning (the same posture `validate-capability-references` takes).\n *\n * Two exemptions keep it quiet:\n * - **Platform-provided objects** (`sys_user`, `sys_approval_request`, …) are\n * skipped: the packages that register them ship their own permission sets,\n * which this stack never sees.\n * - **A stack that declares no permission sets at all** is skipped entirely.\n * Flagging every nav entry there says nothing useful — it means permissions\n * are managed elsewhere, not that each entry is broken (the same\n * \"empty collection ⇒ don't judge\" gate `defineStack` applies to nav\n * dashboard/page/report references).\n */\n\nimport { isPlatformProvidedObjectName } from '@objectstack/spec/system';\nimport { buildAccessMatrix } from './build-access-matrix.js';\n\nexport const NAV_OBJECT_UNGRANTED = 'nav-object-ungranted';\n\nexport type NavAccessSeverity = 'error' | 'warning';\n\nexport interface NavAccessFinding {\n /** Always `warning` — a grant may come from a package this stack cannot see. */\n severity: NavAccessSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `app \"crm\" · nav \"nav_forecast\"`. */\n where: string;\n /** Config path, e.g. `apps[0].navigation[3].objectName`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** One navigation entry that exposes an object. */\ninterface NavExposure {\n objectName: string;\n where: string;\n path: string;\n}\n\n/** Collect every object a stack's navigation exposes, across areas and children. */\nfunction collectNavExposures(stack: AnyRec): NavExposure[] {\n const out: NavExposure[] = [];\n const apps = asArray(stack.apps);\n\n for (let ai = 0; ai < apps.length; ai++) {\n const app = apps[ai];\n if (!app || typeof app !== 'object') continue;\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walk = (items: unknown, basePath: string) => {\n const navItems = asArray(items);\n for (let ni = 0; ni < navItems.length; ni++) {\n const nav = navItems[ni];\n if (!nav || typeof nav !== 'object') continue;\n const navPath = `${basePath}[${ni}]`;\n const objectName = strName(nav.objectName);\n if (nav.type === 'object' && objectName) {\n out.push({\n objectName,\n where: `app \"${appName}\" · nav \"${strName(nav.id) ?? `#${ni}`}\"`,\n path: `${navPath}.objectName`,\n });\n }\n if (Array.isArray(nav.children)) walk(nav.children, `${navPath}.children`);\n }\n };\n\n walk(app.navigation, `apps[${ai}].navigation`);\n const areas = asArray(app.areas);\n for (let ri = 0; ri < areas.length; ri++) {\n walk(areas[ri]?.navigation, `apps[${ai}].areas[${ri}].navigation`);\n }\n }\n\n return out;\n}\n\n/**\n * Validate that every object a stack's navigation exposes is readable by at\n * least one permission set the stack declares. Returns findings (empty = clean).\n */\nexport function validateNavAccess(stack: AnyRec): NavAccessFinding[] {\n const findings: NavAccessFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n // No permission sets in this stack ⇒ permissions are managed elsewhere.\n const permissionSets = asArray(stack.permissions);\n if (permissionSets.length === 0) return findings;\n\n const exposures = collectNavExposures(stack);\n if (exposures.length === 0) return findings;\n\n // Objects this stack actually defines — the only ones whose grants must be\n // present here. A nav target that resolves nowhere is a different bug, owned\n // by `validate-object-references` / `defineStack`.\n const ownObjects = new Set<string>();\n for (const obj of asArray(stack.objects)) {\n const n = strName(obj.name);\n if (n) ownObjects.add(n);\n }\n\n // `read` is already folded across allowRead / viewAllRecords / modifyAllRecords.\n const readable = new Set<string>();\n for (const entry of buildAccessMatrix(stack).entries) {\n if (entry.read) readable.add(entry.object);\n }\n // A wildcard grant (`objects: { '*': { allowRead: true } }`) covers every\n // object — the shape the platform's own `admin_full_access` uses. Without\n // this the matrix records the literal key `*` and every object looks\n // ungranted, which would make the rule fire on exactly the stacks that\n // granted the most.\n if (readable.has('*')) return findings;\n\n // De-duplicate: one finding per object, not per nav entry that exposes it.\n const reported = new Set<string>();\n\n for (const exposure of exposures) {\n const { objectName } = exposure;\n if (reported.has(objectName)) continue;\n if (isPlatformProvidedObjectName(objectName)) continue; // granted by its own package\n if (!ownObjects.has(objectName)) continue; // not ours to grant\n if (readable.has(objectName)) continue;\n\n reported.add(objectName);\n findings.push({\n severity: 'warning',\n rule: NAV_OBJECT_UNGRANTED,\n where: exposure.where,\n path: exposure.path,\n message:\n `navigation exposes object \"${objectName}\", but no permission set this stack ` +\n `declares grants read on it — the entry renders, and opening it fails ` +\n `permission-denied for every principal except one holding the platform's ` +\n `built-in wildcard admin set. It works when you browse as an administrator ` +\n `and breaks for the users the app ships permission sets for.`,\n hint:\n `Add \"${objectName}\" to a permission set's \\`objects\\` with \\`allowRead: true\\` ` +\n `(or \\`viewAllRecords\\`), gate the entry with \\`requiredPermissions\\`/\\`visible\\` ` +\n `if it is meant for admins only, or drop it. Ignore this if a permission set ` +\n `from another installed package grants it.`,\n });\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0090 D6] Access-matrix snapshot — authoring-time companion to the\n * runtime explain engine.\n *\n * `buildAccessMatrix(stack)` derives, PURELY from metadata, one row per\n * (permission set × object) the stack declares: the CRUD/VAMA bits, the\n * depth axes, and the object's OWD for context. The matrix is snapshotted to\n * `access-matrix.json` and diffed on every compile: an unchanged matrix\n * auto-passes; a changed one fails the build until the snapshot is updated —\n * so every capability change becomes a REVIEWABLE, semantic diff\n * (\"`crm_admin` gains delete on `crm_lead`\") instead of a buried JSON hunk.\n * This is the publish-gate substrate the AI-authoring safety story needs:\n * AI may draft grants freely; it cannot silently change who can do what.\n */\n\nimport type { AccessMatrix, AccessMatrixEntry } from '@objectstack/spec/security';\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** Build the sorted access matrix for a normalized stack. */\nexport function buildAccessMatrix(stack: AnyRec): AccessMatrix {\n const entries: AccessMatrixEntry[] = [];\n if (!stack || typeof stack !== 'object') return { version: 1, entries };\n\n const owdByObject = new Map<string, string>();\n for (const obj of asArray(stack.objects)) {\n const name = typeof obj.name === 'string' ? obj.name : '';\n if (!name) continue;\n const owd = (obj.sharingModel ?? (obj.security as AnyRec | undefined)?.sharingModel) as string | undefined;\n if (typeof owd === 'string') owdByObject.set(name, owd);\n }\n\n for (const ps of asArray(stack.permissions)) {\n const psName = typeof ps.name === 'string' ? ps.name : '';\n if (!psName) continue;\n const objects = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n for (const [objName, rawPerm] of Object.entries(objects)) {\n const p = (rawPerm ?? {}) as AnyRec;\n const entry: AccessMatrixEntry = {\n permissionSet: psName,\n object: objName,\n create: p.allowCreate === true,\n read: p.allowRead === true || p.viewAllRecords === true || p.modifyAllRecords === true,\n edit: p.allowEdit === true || p.modifyAllRecords === true,\n delete: p.allowDelete === true || p.modifyAllRecords === true,\n viewAllRecords: p.viewAllRecords === true,\n modifyAllRecords: p.modifyAllRecords === true,\n };\n if (typeof p.readScope === 'string') entry.readScope = p.readScope;\n if (typeof p.writeScope === 'string') entry.writeScope = p.writeScope;\n const owd = owdByObject.get(objName);\n if (owd) entry.sharingModel = owd;\n entries.push(entry);\n }\n }\n\n entries.sort((a, b) =>\n a.permissionSet === b.permissionSet\n ? a.object.localeCompare(b.object)\n : a.permissionSet.localeCompare(b.permissionSet),\n );\n return { version: 1, entries };\n}\n\nconst BIT_LABELS: Array<[keyof AccessMatrixEntry, string]> = [\n ['create', 'create'],\n ['read', 'read'],\n ['edit', 'edit'],\n ['delete', 'delete'],\n ['viewAllRecords', 'View All Data'],\n ['modifyAllRecords', 'Modify All Data'],\n];\n\n/**\n * Semantic diff between two matrices — human-review lines, empty = identical.\n * Ordered: removals, additions, then per-entry bit/scope changes.\n */\nexport function diffAccessMatrix(before: AccessMatrix, after: AccessMatrix): string[] {\n const lines: string[] = [];\n const key = (e: AccessMatrixEntry) => `${e.permissionSet}\\u0000${e.object}`;\n const beforeMap = new Map((before?.entries ?? []).map((e) => [key(e), e]));\n const afterMap = new Map((after?.entries ?? []).map((e) => [key(e), e]));\n\n for (const [k, b] of beforeMap) {\n if (!afterMap.has(k)) {\n lines.push(`'${b.permissionSet}' loses ALL access to '${b.object}' (entry removed)`);\n }\n }\n for (const [k, a] of afterMap) {\n const b = beforeMap.get(k);\n if (!b) {\n const grants = BIT_LABELS.filter(([bit]) => a[bit] === true).map(([, label]) => label);\n lines.push(`'${a.permissionSet}' gains access to '${a.object}' (${grants.join(', ') || 'no bits set'})`);\n continue;\n }\n for (const [bit, label] of BIT_LABELS) {\n if (b[bit] !== a[bit]) {\n lines.push(`'${a.permissionSet}' ${a[bit] ? 'gains' : 'loses'} ${label} on '${a.object}'`);\n }\n }\n if ((b.readScope ?? 'own') !== (a.readScope ?? 'own')) {\n lines.push(`'${a.permissionSet}' read depth on '${a.object}': ${b.readScope ?? 'own'} → ${a.readScope ?? 'own'}`);\n }\n if ((b.writeScope ?? 'own') !== (a.writeScope ?? 'own')) {\n lines.push(`'${a.permissionSet}' write depth on '${a.object}': ${b.writeScope ?? 'own'} → ${a.writeScope ?? 'own'}`);\n }\n if ((b.sharingModel ?? '') !== (a.sharingModel ?? '')) {\n lines.push(`'${a.object}' record baseline (OWD): ${b.sharingModel ?? '(unset)'} → ${a.sharingModel ?? '(unset)'} (affects every principal)`);\n }\n }\n return lines;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0072 — reference resolvability] Translation-bundle reference integrity\n * and option-key validation (issue #3583, assessment R6).\n *\n * The i18n gate has always run in ONE direction: `computeI18nCoverage` asks\n * \"which keys does the metadata expect that no bundle carries?\" Nothing asks the\n * reverse — \"which keys does a bundle carry that no metadata claims?\" — even\n * though the spec already names the answer: `TranslationDiffStatus 'redundant'`\n * and `TranslationCoverageResult.redundantKeys` are declared and have no\n * producer.\n *\n * The HotCRM audit found that direction shipping broken metadata:\n *\n * - bundles keyed to fields the object never declares (`assigned_to`,\n * `budget`, `image_url`) — usually a rename that moved the field and left\n * the translation behind;\n * - select-option translations keyed by the option's DISPLAY LABEL instead of\n * its stored value, or by a near-miss of the value (`direct-mail` for\n * `direct_mail`, `planned` for `planning`).\n *\n * Both fail the same way: the resolver looks up the key it derives from the\n * metadata, finds nothing, and renders the untranslated source string. The app\n * looks *translated* — every other label on the screen resolves — so the hole is\n * invisible until a reader in that locale hits the one field or one picklist\n * value that stayed English.\n *\n * ── Severity ─────────────────────────────────────────────────────────────\n *\n * All findings are **warnings**. An orphan key is inert, not broken: it costs a\n * few bytes and one untranslated string, and nothing crashes. That is a weaker\n * failure than the dead references `validate-object-references` /\n * `validate-action-name-refs` report as errors, and the severity should say so\n * (ADR-0072 D1 — a linter that over-states is a linter authors stop reading).\n *\n * ── What this rule deliberately does NOT check ───────────────────────────\n *\n * - `messages`, `validationMessages`, `settings`, `settingsCommon` — keyed by\n * free-form message ids / namespaces owned by code and plugins, not by\n * stack metadata. There is no enumerable universe to resolve against, so a\n * rule here would be guessing.\n * - `metadataForms` — keyed by the platform's own metadata-type registry, not\n * by this stack. Owned by the platform packages; a stack translating them is\n * correct, not orphaned.\n * - leaf attribute names (`labl:` instead of `label:`) — Zod strips unknown\n * keys at parse, so they never reach a consumer with a value; that is a\n * schema-shape concern, not a reference.\n * - the object-first `AppTranslationBundle` (`o.<object>` …) — that shape is\n * the `translation` METADATA TYPE (records persisted through the metadata\n * store), not `stack.translations`, which is `TranslationBundle[]`\n * (locale → `TranslationData`). Its keys are simply not visited here: an\n * unrecognised top-level namespace is skipped, never reported.\n *\n * ── Cross-package objects ────────────────────────────────────────────────\n *\n * A stack legitimately translates objects it does not define — `sys_user`'s\n * labels are exactly the kind of thing an app localizes. Resolution follows the\n * §4 ladder of the assessment, with the field-level S3 rule intact:\n *\n * 1. own object → check its fields/views/actions/sections\n * 2. platform object in the registry → skip WHOLLY (we cannot see its\n * fields, so we cannot judge them)\n * 3. platform-prefixed, not in registry → warn on the object key only\n * 4. unresolved, unprefixed → warn on the object key only\n */\n\nimport { hasPlatformObjectPrefix, isPlatformProvidedObjectName } from '@objectstack/spec/system';\nimport { walkPageComponents } from './page-walk.js';\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\nexport const TRANSLATION_TARGET_UNKNOWN = 'translation-target-unknown';\nexport const TRANSLATION_OPTION_KEY_UNKNOWN = 'translation-option-key-unknown';\n\nexport type TranslationRefSeverity = 'warning';\n\nexport interface TranslationRefFinding {\n /** Always `warning` — an orphan translation key is inert, not broken. */\n severity: TranslationRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `locale \"zh-CN\" · object \"crm_lead\"`. */\n where: string;\n /** Config path, e.g. `translations[0][\"zh-CN\"].objects.crm_lead.fields.campaign`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** Coerce a collection (array or name-keyed map) to an array of records,\n * injecting `name` from the map key — mirrors the sibling authoring lints so\n * the rule works on both the parsed (array) and normalized (map) stack shapes. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (isRec(v)) return Object.entries(v).map(([name, def]) => ({ name, ...(isRec(def) ? def : {}) }));\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\n/**\n * \"Did you mean?\" over the known names — Levenshtein-bounded, plus a namespace\n * pass the distance metric cannot see.\n *\n * A stack prefixes its object names (`todo_task`, `crm_lead`), and the orphan\n * key is routinely the bare noun the author had in mind (`task`). That is 5\n * edits from `todo_task` — far outside the typo bound, and exactly the case\n * where the suggestion is most useful — so a candidate that differs only by a\n * snake_case namespace segment is offered before falling back to edit distance.\n */\nfunction suggest(target: string, known: Iterable<string>): string {\n const names = [...known];\n const segmentMatch = names.find(\n (candidate) => candidate.endsWith(`_${target}`) || candidate.startsWith(`${target}_`),\n );\n if (segmentMatch) return ` Did you mean \"${segmentMatch}\"?`;\n\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of names) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\n/** At most `max` names, sorted, for the \"known values are …\" tail of a hint. */\nfunction listNames(names: Iterable<string>, max = 12): string {\n const all = [...names].sort();\n if (all.length === 0) return '';\n const shown = all.slice(0, max).join(', ');\n return all.length > max ? `${shown}, … (${all.length} total)` : shown;\n}\n\n/**\n * Fields a bundle may translate without reading as orphans: the package-shared\n * registry-injected columns (`system-fields.ts`, #4330) plus three exemptions\n * this rule has carried since it landed — `_id` and `space` are legacy\n * physical spellings older bundles still key, and `name` is an ordinary\n * authored field on most objects. None of the three is a system column in the\n * spec's sense, so they stay rule-local (see the shared module's note) instead\n * of widening every field-existence rule in the package.\n */\nconst IMPLICIT_FIELDS: ReadonlySet<string> = new Set([\n ...SYSTEM_FIELDS,\n '_id', 'name', 'space',\n]);\n\n/** Everything a bundle may legally name under one object. */\ninterface ObjectFacts {\n fields: Map<string, AnyRec>;\n views: Set<string>;\n actions: Map<string, AnyRec>;\n sections: Set<string>;\n}\n\ninterface Universe {\n objects: Map<string, ObjectFacts>;\n /** App name → every navigation item id declared by that app. */\n apps: Map<string, Set<string>>;\n dashboards: Map<string, { widgets: Set<string>; actions: Set<string> }>;\n /** Object-less actions — the ones `globalActions.*` may name. */\n globalActions: Map<string, AnyRec>;\n /** Action name → owning object, so a misfiled `globalActions` key can say where it belongs. */\n actionOwners: Map<string, string>;\n}\n\nfunction emptyFacts(): ObjectFacts {\n return { fields: new Map(), views: new Set(), actions: new Map(), sections: new Set() };\n}\n\n/**\n * Register everything ONE view record contributes: the `_views` names it makes\n * legal, and the `_sections` names its form views declare — each under the\n * object that container actually binds.\n *\n * Two things about the real shape make this more than \"read `view.name`\", and\n * both were learned from the HotCRM corpus (~18k lines of shipped metadata),\n * where a first pass reported ~40 correct keys as orphans:\n *\n * 1. A view record is a CONTAINER, not a view. The default list sits at\n * `list`; the named tabs at `listViews.<key>` and `formViews.<key>`, each\n * of which may also carry its own `name`. Both the map key and the inner\n * `name` are accepted — authors write either, and the key is what the\n * console renders the tab from.\n * 2. The object binding lives INSIDE the container (`list.data.object`), not\n * at the record root. A record-level lookup alone resolves to nothing on\n * the canonical shape, which silently drops the whole record — a rule that\n * then reports every view key the app ships.\n */\nfunction collectViewRecord(view: AnyRec, factsFor: (objectName: string) => ObjectFacts): void {\n const recordObject = viewObjectName(view);\n const bindingOf = (container: AnyRec): string | undefined =>\n viewObjectName(container) ?? recordObject;\n\n const addView = (objectName: string | undefined, name: string | undefined) => {\n if (objectName && name) factsFor(objectName).views.add(name);\n };\n\n const listBinding = isRec(view.list) ? bindingOf(view.list) : undefined;\n if (isRec(view.list)) addView(listBinding, strName(view.list.name));\n addView(recordObject ?? listBinding, strName(view.name));\n\n for (const key of ['listViews', 'formViews'] as const) {\n const container = view[key];\n if (!isRec(container)) continue;\n for (const [subKey, sub] of Object.entries(container)) {\n if (!isRec(sub)) continue;\n const binding = bindingOf(sub) ?? listBinding;\n addView(binding, subKey);\n addView(binding, strName(sub.name));\n\n // Form sections carry an OPTIONAL `name` that exists purely for the\n // `_sections` lookup (`ui/view.zod.ts`: \"Stable section identifier for\n // i18n lookup\"). A section without one cannot be translated at all, so\n // it contributes nothing here.\n if (binding) {\n for (const section of asArray(sub.sections)) {\n const sectionName = strName(section.name);\n if (sectionName) factsFor(binding).sections.add(sectionName);\n }\n }\n }\n }\n\n const sectionBinding = recordObject ?? listBinding;\n if (sectionBinding) {\n for (const section of asArray(view.sections)) {\n const sectionName = strName(section.name);\n if (sectionName) factsFor(sectionBinding).sections.add(sectionName);\n }\n }\n}\n\n/** The object a view (or one of its containers) binds to, across the shapes it is authored in. */\nfunction viewObjectName(view: AnyRec): string | undefined {\n return (\n strName(view.objectName) ??\n strName(view.object) ??\n (isRec(view.data) ? strName(view.data.object) : undefined)\n );\n}\n\n/**\n * Declared select options for a field, or `undefined` when the field declares\n * none at all. Handles the canonical `{value,label}[]` shape plus the two\n * legacy shapes the extractor also tolerates (bare `string[]`, and a\n * `value → label` record).\n */\nfunction readOptions(field: AnyRec): { values: Set<string>; byLabel: Map<string, string> } | undefined {\n const raw = field.options;\n const values = new Set<string>();\n const byLabel = new Map<string, string>();\n if (Array.isArray(raw)) {\n for (const opt of raw) {\n if (typeof opt === 'string') {\n values.add(opt);\n continue;\n }\n if (!isRec(opt)) continue;\n const value = strName(opt.value);\n if (!value) continue;\n values.add(value);\n const label = strName(opt.label);\n if (label) byLabel.set(label.toLowerCase(), value);\n }\n } else if (isRec(raw)) {\n for (const [value, label] of Object.entries(raw)) {\n values.add(value);\n if (typeof label === 'string' && label.length > 0) byLabel.set(label.toLowerCase(), value);\n }\n } else {\n return undefined;\n }\n return values.size > 0 ? { values, byLabel } : undefined;\n}\n\n/**\n * Collect every name a translation bundle may resolve against. Built once per\n * run: the same universe answers all bundles and all locales.\n */\nfunction buildUniverse(stack: AnyRec): Universe {\n const objects = new Map<string, ObjectFacts>();\n const factsFor = (name: string): ObjectFacts => {\n let facts = objects.get(name);\n if (!facts) {\n facts = emptyFacts();\n objects.set(name, facts);\n }\n return facts;\n };\n\n // ── Objects: fields, embedded actions/views, fieldGroups (the `_sections` anchor) ──\n for (const obj of asArray(stack.objects)) {\n const objectName = strName(obj.name);\n if (!objectName) continue;\n const facts = factsFor(objectName);\n\n for (const field of asArray(obj.fields)) {\n const fieldName = strName(field.name);\n if (fieldName) facts.fields.set(fieldName, field);\n }\n for (const action of asArray(obj.actions)) {\n const actionName = strName(action.name);\n if (actionName) facts.actions.set(actionName, action);\n }\n // An object can carry views directly, including the `objects[].listViews`\n // container the chart rule also walks. `{ ...view, object: objectName }`\n // pins the binding: an embedded view inherits its owner, and nothing here\n // depends on the container repeating it.\n for (const view of asArray(obj.views)) {\n collectViewRecord({ ...view, object: strName(view.object) ?? objectName }, factsFor);\n }\n collectViewRecord({ object: objectName, listViews: obj.listViews }, factsFor);\n // ADR-0085: `fieldGroups[].key` is the i18n anchor for `_sections`.\n for (const group of asArray(obj.fieldGroups)) {\n const key = strName(group.key) ?? strName(group.name);\n if (key) facts.sections.add(key);\n }\n }\n\n // ── Stack-level views: `_views` names + form-section names ──\n for (const view of asArray(stack.views)) {\n collectViewRecord(view, factsFor);\n }\n\n // ── Pages: `record:details` sections are the other `_sections` anchor ──\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n for (const walked of walkPageComponents(pages[pi], `pages[${pi}]`)) {\n if (!walked.objectName) continue;\n const props = isRec(walked.component.properties) ? walked.component.properties : undefined;\n if (!props) continue;\n for (const section of asArray(props.sections)) {\n const sectionName = strName(section.name);\n if (sectionName) factsFor(walked.objectName).sections.add(sectionName);\n }\n }\n }\n\n // ── Actions: object-bound ones join their object; the rest are global ──\n const globalActions = new Map<string, AnyRec>();\n const actionOwners = new Map<string, string>();\n for (const action of asArray(stack.actions)) {\n const actionName = strName(action.name);\n if (!actionName) continue;\n const owner = strName(action.objectName) ?? strName(action.object);\n if (owner) {\n factsFor(owner).actions.set(actionName, action);\n actionOwners.set(actionName, owner);\n } else {\n globalActions.set(actionName, action);\n }\n }\n for (const [objectName, facts] of objects) {\n for (const actionName of facts.actions.keys()) {\n if (!actionOwners.has(actionName)) actionOwners.set(actionName, objectName);\n }\n }\n\n // ── Apps: navigation item ids (`apps.<app>.navigation.<id>.label`) ──\n const apps = new Map<string, Set<string>>();\n for (const app of asArray(stack.apps)) {\n const appName = strName(app.name);\n if (!appName) continue;\n const navIds = apps.get(appName) ?? new Set<string>();\n const walkNav = (items: unknown) => {\n for (const item of asArray(items)) {\n const id = strName(item.id);\n if (id) navIds.add(id);\n if (item.children) walkNav(item.children);\n }\n };\n walkNav(app.navigation);\n for (const area of asArray(app.areas)) {\n const areaId = strName(area.id);\n if (areaId) navIds.add(areaId);\n walkNav(area.navigation);\n }\n apps.set(appName, navIds);\n }\n\n // ── Dashboards: widget ids + header action urls ──\n const dashboards = new Map<string, { widgets: Set<string>; actions: Set<string> }>();\n for (const dash of asArray(stack.dashboards)) {\n const dashName = strName(dash.name);\n if (!dashName) continue;\n const widgets = new Set<string>();\n for (const widget of asArray(dash.widgets)) {\n const id = strName(widget.id) ?? strName(widget.name);\n if (id) widgets.add(id);\n }\n const actions = new Set<string>();\n const headerActions = [\n ...asArray(isRec(dash.header) ? dash.header.actions : undefined),\n ...asArray(dash.actions),\n ];\n for (const action of headerActions) {\n const key = strName(action.actionUrl) ?? strName(action.url) ?? strName(action.name);\n if (key) actions.add(key);\n }\n dashboards.set(dashName, { widgets, actions });\n }\n\n return { objects, apps, dashboards, globalActions, actionOwners };\n}\n\n/** Quote a locale for the config path — BCP-47 tags carry `-`. */\nfunction localePath(bundleIndex: number, locale: string): string {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(locale)\n ? `translations[${bundleIndex}].${locale}`\n : `translations[${bundleIndex}][\"${locale}\"]`;\n}\n\n/**\n * Validate every reference a translation bundle makes against the metadata it\n * claims to translate. Returns findings (empty = clean).\n */\nexport function validateTranslationReferences(stack: AnyRec): TranslationRefFinding[] {\n const findings: TranslationRefFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const bundles = Array.isArray(stack.translations) ? stack.translations : [];\n if (bundles.length === 0) return findings;\n\n const universe = buildUniverse(stack);\n\n const orphan = (where: string, path: string, message: string, hint: string) => {\n findings.push({ severity: 'warning', rule: TRANSLATION_TARGET_UNKNOWN, where, path, message, hint });\n };\n\n for (let bi = 0; bi < bundles.length; bi++) {\n const bundle = bundles[bi];\n if (!isRec(bundle)) continue;\n\n for (const [locale, rawData] of Object.entries(bundle)) {\n if (!isRec(rawData)) continue;\n const base = localePath(bi, locale);\n const inLocale = `locale \"${locale}\"`;\n\n // ── objects.<name>.… ──────────────────────────────────────────────\n for (const [objectName, rawNode] of Object.entries(asRecord(rawData.objects))) {\n if (!isRec(rawNode)) continue;\n const objPath = `${base}.objects.${objectName}`;\n const facts = universe.objects.get(objectName);\n\n if (!facts) {\n // Not this stack's object. Platform objects are translated by their\n // owning package but a stack may override them, so a registered\n // platform name is legitimate — and unreadable from here, which is\n // why the whole subtree is skipped rather than half-checked.\n if (isPlatformProvidedObjectName(objectName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\"`,\n objPath,\n hasPlatformObjectPrefix(objectName)\n ? `Translations are keyed to \"${objectName}\", which carries a platform namespace ` +\n `prefix but is registered by no platform package, official plugin, or cloud ` +\n `runtime object — and this stack does not define it either. Nothing resolves ` +\n `these keys.` + suggest(objectName, universe.objects.keys())\n : `Translations are keyed to \"${objectName}\", which no object in this stack ` +\n `defines. The resolver looks up keys derived from the metadata, so this whole ` +\n `subtree is dead weight — every label it carries renders untranslated.` +\n suggest(objectName, universe.objects.keys()),\n `Rename the key to the object it was written for, drop it, or ignore this if the ` +\n `object is contributed by another installed package.` +\n (universe.objects.size > 0 ? ` Defined objects: ${listNames(universe.objects.keys())}.` : ''),\n );\n continue;\n }\n\n // fields.<name>[.options.<value>]\n for (const [fieldName, rawField] of Object.entries(asRecord(rawNode.fields))) {\n const fieldPath = `${objPath}.fields.${fieldName}`;\n const field = facts.fields.get(fieldName);\n if (!field) {\n if (IMPLICIT_FIELDS.has(fieldName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\" · field \"${fieldName}\"`,\n fieldPath,\n `Translations are keyed to field \"${fieldName}\", which object \"${objectName}\" ` +\n `does not declare. The label renders untranslated in this locale — and because ` +\n `every neighbouring field DOES resolve, the hole reads as a styling quirk ` +\n `rather than a missing translation.` + suggest(fieldName, facts.fields.keys()),\n `Point the key at a declared field, or drop it if the field was removed or renamed.` +\n (facts.fields.size > 0 ? ` Declared fields: ${listNames(facts.fields.keys())}.` : ''),\n );\n continue;\n }\n if (!isRec(rawField)) continue;\n checkOptionKeys(findings, {\n optionMap: rawField.options,\n field,\n fieldName,\n objectName,\n path: `${fieldPath}.options`,\n where: `${inLocale} · object \"${objectName}\" · field \"${fieldName}\"`,\n });\n }\n\n // _views.<name>\n for (const viewName of Object.keys(asRecord(rawNode._views))) {\n if (facts.views.has(viewName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\" · view \"${viewName}\"`,\n `${objPath}._views.${viewName}`,\n `Translations are keyed to view \"${viewName}\", which no view of object ` +\n `\"${objectName}\" declares. The view tab keeps its source-locale label.` +\n suggest(viewName, facts.views),\n `Match the key to the view's \\`name\\` (not its label), or drop it.` +\n (facts.views.size > 0 ? ` Declared views: ${listNames(facts.views)}.` : ''),\n );\n }\n\n // _sections.<key>\n for (const sectionName of Object.keys(asRecord(rawNode._sections))) {\n if (facts.sections.has(sectionName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\" · section \"${sectionName}\"`,\n `${objPath}._sections.${sectionName}`,\n `Translations are keyed to section \"${sectionName}\", which nothing on object ` +\n `\"${objectName}\" declares — no \\`fieldGroups[].key\\`, no named form-view section, ` +\n `no named \\`record:details\\` section. The section heading stays in the source locale.` +\n suggest(sectionName, facts.sections),\n `Sections are translatable only through a STABLE NAME: give the group/section a ` +\n `\\`key\\`/\\`name\\` and use it here, or drop the translation.` +\n (facts.sections.size > 0\n ? ` Declared sections: ${listNames(facts.sections)}.`\n : ` Object \"${objectName}\" declares no named section at all.`),\n );\n }\n\n // _actions.<name>[.params.<name>]\n for (const [actionName, rawAction] of Object.entries(asRecord(rawNode._actions))) {\n const actionPath = `${objPath}._actions.${actionName}`;\n const action = facts.actions.get(actionName);\n if (!action) {\n orphan(\n `${inLocale} · object \"${objectName}\" · action \"${actionName}\"`,\n actionPath,\n `Translations are keyed to action \"${actionName}\", which is defined by neither ` +\n `object \"${objectName}\"'s \\`actions\\` nor a \\`stack.actions\\` entry bound to it. ` +\n `The button keeps its source-locale label.` + suggest(actionName, facts.actions.keys()),\n `Match the key to a defined action name, move it under the object that owns the ` +\n `action, or drop it.` +\n (facts.actions.size > 0 ? ` Actions on this object: ${listNames(facts.actions.keys())}.` : ''),\n );\n continue;\n }\n checkActionParams(findings, {\n rawAction,\n action,\n path: actionPath,\n where: `${inLocale} · object \"${objectName}\" · action \"${actionName}\"`,\n subject: `action \"${actionName}\"`,\n });\n }\n }\n\n // ── globalActions.<name> ──────────────────────────────────────────\n for (const [actionName, rawAction] of Object.entries(asRecord(rawData.globalActions))) {\n const actionPath = `${base}.globalActions.${actionName}`;\n const action = universe.globalActions.get(actionName);\n if (!action) {\n const owner = universe.actionOwners.get(actionName);\n orphan(\n `${inLocale} · global action \"${actionName}\"`,\n actionPath,\n owner\n ? `Action \"${actionName}\" is bound to object \"${owner}\", so the resolver looks it ` +\n `up under \\`objects.${owner}._actions.${actionName}\\` — never under ` +\n `\\`globalActions\\`, which is only consulted for object-less actions. This key ` +\n `is never read.`\n : `Translations are keyed to global action \"${actionName}\", which no object-less ` +\n `action in this stack defines. The button keeps its source-locale label.` +\n suggest(actionName, universe.globalActions.keys()),\n owner\n ? `Move these keys under \\`objects.${owner}._actions.${actionName}\\`.`\n : `Match the key to an object-less action's name, or drop it.` +\n (universe.globalActions.size > 0\n ? ` Object-less actions: ${listNames(universe.globalActions.keys())}.`\n : ''),\n );\n continue;\n }\n checkActionParams(findings, {\n rawAction,\n action,\n path: actionPath,\n where: `${inLocale} · global action \"${actionName}\"`,\n subject: `action \"${actionName}\"`,\n });\n }\n\n // ── apps.<name>[.navigation.<id>] ─────────────────────────────────\n for (const [appName, rawApp] of Object.entries(asRecord(rawData.apps))) {\n const appPath = `${base}.apps.${appName}`;\n const navIds = universe.apps.get(appName);\n if (!navIds) {\n orphan(\n `${inLocale} · app \"${appName}\"`,\n appPath,\n `Translations are keyed to app \"${appName}\", which this stack does not define. ` +\n `The app launcher shows the source-locale label.` + suggest(appName, universe.apps.keys()),\n `Match the key to an app's \\`name\\`, or drop it.` +\n (universe.apps.size > 0 ? ` Defined apps: ${listNames(universe.apps.keys())}.` : ''),\n );\n continue;\n }\n if (!isRec(rawApp)) continue;\n for (const navId of Object.keys(asRecord(rawApp.navigation))) {\n if (navIds.has(navId)) continue;\n orphan(\n `${inLocale} · app \"${appName}\" · navigation \"${navId}\"`,\n `${appPath}.navigation.${navId}`,\n `Translations are keyed to navigation item \"${navId}\", which app \"${appName}\" ` +\n `does not declare. The menu entry keeps its source-locale label.` +\n suggest(navId, navIds),\n `Match the key to the navigation item's \\`id\\`, or drop it.` +\n (navIds.size > 0 ? ` Declared navigation ids: ${listNames(navIds)}.` : ''),\n );\n }\n }\n\n // ── dashboards.<name>[.widgets.<id> | .actions.<url>] ─────────────\n for (const [dashName, rawDash] of Object.entries(asRecord(rawData.dashboards))) {\n const dashPath = `${base}.dashboards.${dashName}`;\n const dash = universe.dashboards.get(dashName);\n if (!dash) {\n orphan(\n `${inLocale} · dashboard \"${dashName}\"`,\n dashPath,\n `Translations are keyed to dashboard \"${dashName}\", which this stack does not ` +\n `define. The dashboard title stays in the source locale.` +\n suggest(dashName, universe.dashboards.keys()),\n `Match the key to a dashboard's \\`name\\`, or drop it.` +\n (universe.dashboards.size > 0 ? ` Defined dashboards: ${listNames(universe.dashboards.keys())}.` : ''),\n );\n continue;\n }\n if (!isRec(rawDash)) continue;\n for (const widgetId of Object.keys(asRecord(rawDash.widgets))) {\n if (dash.widgets.has(widgetId)) continue;\n orphan(\n `${inLocale} · dashboard \"${dashName}\" · widget \"${widgetId}\"`,\n `${dashPath}.widgets.${widgetId}`,\n `Translations are keyed to widget \"${widgetId}\", which dashboard \"${dashName}\" ` +\n `does not declare. The widget title stays in the source locale.` +\n suggest(widgetId, dash.widgets),\n `Match the key to the widget's \\`id\\`, or drop it.` +\n (dash.widgets.size > 0 ? ` Declared widget ids: ${listNames(dash.widgets)}.` : ''),\n );\n }\n for (const actionKey of Object.keys(asRecord(rawDash.actions))) {\n if (dash.actions.has(actionKey)) continue;\n orphan(\n `${inLocale} · dashboard \"${dashName}\" · action \"${actionKey}\"`,\n `${dashPath}.actions.${actionKey}`,\n `Translations are keyed to header action \"${actionKey}\", which dashboard ` +\n `\"${dashName}\" does not declare. The button keeps its source-locale label.` +\n suggest(actionKey, dash.actions),\n `Header-action translations are keyed by the action's \\`actionUrl\\`, not its label.` +\n (dash.actions.size > 0 ? ` Declared header actions: ${listNames(dash.actions)}.` : ''),\n );\n }\n }\n }\n }\n\n return findings;\n}\n\n/** `v` as a record of sub-nodes, or an empty record when absent/malformed. */\nfunction asRecord(v: unknown): Record<string, unknown> {\n return isRec(v) ? v : {};\n}\n\n/**\n * Option translations are keyed by the option's STORED VALUE. Keying them by\n * the display label — or by a near-miss of the value — is the second half of\n * issue #3583's option-key class: the map parses, ships, and never resolves.\n */\nfunction checkOptionKeys(\n findings: TranslationRefFinding[],\n ctx: {\n optionMap: unknown;\n field: AnyRec;\n fieldName: string;\n objectName: string;\n path: string;\n where: string;\n },\n): void {\n const optionKeys = Object.keys(asRecord(ctx.optionMap));\n if (optionKeys.length === 0) return;\n\n const declared = readOptions(ctx.field);\n if (!declared) {\n findings.push({\n severity: 'warning',\n rule: TRANSLATION_OPTION_KEY_UNKNOWN,\n where: ctx.where,\n path: ctx.path,\n message:\n `Option translations are keyed under field \"${ctx.fieldName}\" of object ` +\n `\"${ctx.objectName}\", which declares no \\`options\\` at all (field type ` +\n `\"${strName(ctx.field.type) ?? 'unknown'}\"). Nothing reads this map.`,\n hint:\n `Declare the options on the field, move the translations to the field that owns ` +\n `them, or drop them.`,\n });\n return;\n }\n\n for (const key of optionKeys) {\n if (declared.values.has(key)) continue;\n const byLabel = declared.byLabel.get(key.toLowerCase());\n findings.push({\n severity: 'warning',\n rule: TRANSLATION_OPTION_KEY_UNKNOWN,\n where: ctx.where,\n path: `${ctx.path}.${key}`,\n message: byLabel\n ? `Option translation is keyed by the DISPLAY LABEL \"${key}\" instead of the stored ` +\n `value \"${byLabel}\". The resolver looks the option up by value, so this entry is ` +\n `never found and the option renders with its source-locale label.`\n : `Option translation is keyed by \"${key}\", which is not one of the values declared ` +\n `by field \"${ctx.objectName}.${ctx.fieldName}\". The option renders untranslated.` +\n suggest(key, declared.values),\n hint: byLabel\n ? `Rename the key to \"${byLabel}\".`\n : `Option keys are the stored \\`value\\`, not the label and not a variant spelling ` +\n `(\\`direct_mail\\`, not \\`direct-mail\\`). Declared values: ${listNames(declared.values)}.`,\n });\n }\n}\n\n/** Action-parameter translations are keyed by the param's `name`. */\nfunction checkActionParams(\n findings: TranslationRefFinding[],\n ctx: { rawAction: unknown; action: AnyRec; path: string; where: string; subject: string },\n): void {\n const rawParams = Object.keys(asRecord(isRec(ctx.rawAction) ? ctx.rawAction.params : undefined));\n if (rawParams.length === 0) return;\n\n const declared = new Set<string>();\n for (const param of asArray(ctx.action.params)) {\n const name = strName(param.name) ?? strName(param.field);\n if (name) declared.add(name);\n }\n\n for (const paramName of rawParams) {\n if (declared.has(paramName)) continue;\n findings.push({\n severity: 'warning',\n rule: TRANSLATION_TARGET_UNKNOWN,\n where: `${ctx.where} · param \"${paramName}\"`,\n path: `${ctx.path}.params.${paramName}`,\n message:\n `Translations are keyed to parameter \"${paramName}\", which ${ctx.subject} does not ` +\n `declare. The parameter's label and help text render untranslated in the action dialog.` +\n suggest(paramName, declared),\n hint:\n `Match the key to a declared param \\`name\\`, or drop it.` +\n (declared.size > 0 ? ` Declared params: ${listNames(declared)}.` : ''),\n });\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0064 §3] Skill ↔ agent surface affinity (issue #3820).\n *\n * An agent binds a product surface (`'ask'` | `'build'`, ADR-0063 §1) and a\n * skill declares which surface it belongs to (`'ask'` | `'build'` | `'both'`,\n * ADR-0063 §3). A skill may only attach to an agent whose surface it matches —\n * `'both'` attaches to either. The runtime treats a violation as a FAST LOAD\n * ERROR: `resolveActiveSkills()` throws on the first incompatible binding, so\n * an agent shipping one mismatched skill reference fails at **chat time** with\n * a 500 — after parse, after validate, after deploy.\n *\n * Both sides of the check are declared in the same stack, so the contradiction\n * is statically provable and this rule carries severity **error** with zero\n * false positives by construction. Both sides default to `'ask'` when the\n * `surface` field is absent (mirroring the runtime's defaults), so the rule is\n * safe on the raw/normalized config the `lint` path carries as well as the\n * schema-parsed stack.\n *\n * Scope note: this rule deliberately does NOT check that `agent.skills[]`\n * names resolve at all. Kernel skills (`schema_reader`, the `ask`/`build`\n * bundles) are runtime-registered and statically invisible, and whether\n * app-stack tool/skill namespaces get a platform-name registry is an open\n * decision (#3820 D0/D2) — resolving names against `stack.skills` alone would\n * flag every kernel-skill reference. An unresolved name is therefore skipped\n * here; only a reference that resolves in-stack AND contradicts the affinity\n * contract is reported.\n */\n\nexport const AI_SKILL_SURFACE_MISMATCH = 'ai-skill-surface-mismatch';\n\nexport type AiSurfaceAffinitySeverity = 'error' | 'warning';\n\nexport interface AiSurfaceAffinityFinding {\n /** Always `error` — the runtime throws on this binding at chat time. */\n severity: AiSurfaceAffinitySeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `agent \"sales_copilot\" · skills`. */\n where: string;\n /** Config path, e.g. `agents[0].skills[2]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => !!x && typeof x === 'object');\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** The runtime defaults an absent `surface` to `'ask'` on both sides. */\nfunction surfaceOf(v: unknown): string {\n return typeof v === 'string' && v.length > 0 ? v : 'ask';\n}\n\n/**\n * Validate every in-stack agent→skill binding against the ADR-0064 §3 surface\n * affinity contract. Returns findings (empty = clean).\n */\nexport function validateAiSurfaceAffinity(stack: AnyRec): AiSurfaceAffinityFinding[] {\n const findings: AiSurfaceAffinityFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const skillsByName = new Map<string, AnyRec>();\n for (const skill of asArray(stack.skills)) {\n const n = strName(skill.name);\n if (n) skillsByName.set(n, skill);\n }\n\n const agents = asArray(stack.agents);\n for (let ai = 0; ai < agents.length; ai++) {\n const agent = agents[ai];\n const agentName = strName(agent.name) ?? `#${ai}`;\n const agentSurface = surfaceOf(agent.surface);\n const skillRefs = Array.isArray(agent.skills) ? agent.skills : [];\n\n for (let si = 0; si < skillRefs.length; si++) {\n const ref = strName(skillRefs[si]);\n if (!ref) continue;\n const skill = skillsByName.get(ref);\n // Unresolved in-stack → runtime-registered kernel skill or another\n // package's — out of this rule's scope (see header; #3820 D0/D2).\n if (!skill) continue;\n\n const skillSurface = surfaceOf(skill.surface);\n if (skillSurface === 'both' || skillSurface === agentSurface) continue;\n\n findings.push({\n severity: 'error',\n rule: AI_SKILL_SURFACE_MISMATCH,\n where: `agent \"${agentName}\" · skills`,\n path: `agents[${ai}].skills[${si}]`,\n message:\n `Agent \"${agentName}\" (surface: '${agentSurface}') references skill \"${ref}\" ` +\n `(surface: '${skillSurface}') — incompatible affinity (ADR-0064 §3). The runtime ` +\n `refuses this binding with a load error, so chatting with this agent fails at ` +\n `request time even though the stack parses and validates cleanly.`,\n hint:\n `A skill may only attach to an agent whose surface it matches. Move \"${ref}\" to a ` +\n `'${skillSurface}'-surface agent, change its \\`surface\\` to '${agentSurface}', or — ` +\n `only if it is a genuinely shared, read-only capability — declare \\`surface: 'both'\\`.`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0109 / issue #3820 R7] `skill.tools[]` reference integrity — the tool\n * branch of the AI reference rules.\n *\n * Under ADR-0109's authoring model the DEFAULT third-party path declares no\n * tool records at all: a skill names either a platform-registered tool or an\n * auto-materialised action tool. A `skill.tools[]` entry therefore resolves\n * against, in order:\n *\n * 1. the stack's own `stack.tools[]` names (the optional refinement layer);\n * 2. `PLATFORM_PROVIDED_TOOL_NAMES` — the curated registry of tools the\n * cloud AI runtime registers at boot (spec owns the list; the owning\n * cloud packages carry the conformance tests);\n * 3. the materialised `action_<name>` family — one tool per declarative\n * action (`stack.actions` ∪ every object's `actions`), the mechanism the\n * built-in `actions_executor` subscribes to with `action_*`.\n *\n * Trailing-wildcard entries (`action_*`, `foo_*`) resolve when ANY member of\n * that universe matches the prefix.\n *\n * Severity is **warning** (ADR-0078 advisory-first ratchet), not error,\n * because the universe has a known blind spot: a runtime plugin outside the\n * registry can legitimately register tools no static analysis can see, and\n * the runtime deliberately tolerates unresolved names (skills may be authored\n * before their tools exist — `skill-registry.ts`). What the warning buys: the\n * HotCRM failure — 10 fictional tools across 6 skills, every one shipping\n * through `validate`/`lint` clean and surfacing as a copilot that claims\n * abilities it does not have — now surfaces at authoring time. On that same\n * corpus the resolution ladder above yields exactly 10 findings and 0 false\n * positives (6 references resolve via the registry).\n */\n\nimport { PLATFORM_PROVIDED_TOOL_NAMES, PLATFORM_TOOL_FAMILY_PREFIXES } from '@objectstack/spec/system';\n\nexport const AI_SKILL_TOOL_UNRESOLVED = 'ai-skill-tool-unresolved';\n\nexport type AiToolRefSeverity = 'error' | 'warning';\n\nexport interface AiToolRefFinding {\n /** Always `warning` — see the header for why this rule starts advisory. */\n severity: AiToolRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `skill \"revenue_forecasting\" · tools`. */\n where: string;\n /** Config path, e.g. `skills[3].tools[1]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => !!x && typeof x === 'object');\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\nfunction suggest(target: string, known: Set<string>): string {\n // The high-frequency near-miss first: naming the raw ACTION where the\n // materialised TOOL (`action_<name>`) is meant. Edit distance cannot catch\n // it (the prefix alone is 7 edits), and it is exactly the mistake the\n // ADR-0109 default path invites from authors who know their action names.\n for (const prefix of PLATFORM_TOOL_FAMILY_PREFIXES) {\n if (known.has(`${prefix}${target}`)) return ` Did you mean \"${prefix}${target}\"?`;\n }\n\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\n/**\n * Action types with a headless invocation path. `url`/`modal`/`form` are\n * Studio-only UI types — the runtime never materialises a tool for them\n * because there is nothing to call without the UI collecting input first.\n */\nconst HEADLESS_ACTION_TYPES = new Set(['script', 'api', 'flow']);\n\n/**\n * Would the runtime materialise an `action_<name>` tool for this action?\n *\n * Mirrors the STATIC half of the runtime's `actionSkipReason` (ADR-0011\n * opt-in + the headless-path checks). The runtime additionally checks\n * service wiring (is the automation service up?), which is not knowable at\n * authoring time and is deliberately not modelled here — this predicate is\n * about \"did the author wire it\", not \"is the server configured\".\n *\n * Getting this wrong in the permissive direction is worse than having no\n * rule: an author who names `action_foo` for a `type:'modal'` action would\n * be told the reference resolves, and would ship a skill whose instructions\n * promise a capability the agent can never call — the exact failure this\n * rule exists to catch.\n */\nfunction materialisesAsTool(action: AnyRec): boolean {\n const ai = action.ai;\n if (!ai || typeof ai !== 'object') return false;\n const aiRec = ai as AnyRec;\n // ADR-0011 — opt-in, and `description` is the LLM-facing contract the\n // spec requires whenever `exposed` is true.\n if (aiRec.exposed !== true) return false;\n if (!strName(aiRec.description)) return false;\n\n const type = strName(action.type);\n if (!type || !HEADLESS_ACTION_TYPES.has(type)) return false;\n // `script` can carry either a named handler or an inline body; `api` and\n // `flow` are dispatched by target.\n if (type === 'script') return Boolean(action.target || action.body);\n return Boolean(action.target);\n}\n\n/**\n * The full set of tool names resolvable from this stack: declared tool\n * records ∪ the platform registry ∪ the materialised action family.\n */\nfunction collectToolUniverse(stack: AnyRec): Set<string> {\n const universe = new Set<string>(PLATFORM_PROVIDED_TOOL_NAMES);\n\n for (const tool of asArray(stack.tools)) {\n const n = strName(tool.name);\n if (n) universe.add(n);\n }\n\n const addActionFamily = (actions: unknown) => {\n for (const action of asArray(actions)) {\n const n = strName(action.name);\n if (n && materialisesAsTool(action)) universe.add(`action_${n}`);\n }\n };\n addActionFamily(stack.actions);\n for (const obj of asArray(stack.objects)) {\n addActionFamily(obj.actions);\n }\n\n return universe;\n}\n\n/**\n * Actions that exist but are NOT AI-exposed, for the near-miss hint: naming\n * `action_foo` when `foo` exists but never materialises is a different\n * mistake from naming something fictional, and deserves a different fix.\n */\nfunction collectUnexposedActionNames(stack: AnyRec): Set<string> {\n const names = new Set<string>();\n const scan = (actions: unknown) => {\n for (const action of asArray(actions)) {\n const n = strName(action.name);\n if (n && !materialisesAsTool(action)) names.add(n);\n }\n };\n scan(stack.actions);\n for (const obj of asArray(stack.objects)) scan(obj.actions);\n return names;\n}\n\n/**\n * Validate every `skill.tools[]` reference in a stack. Returns findings\n * (empty = clean).\n */\nexport function validateAiToolReferences(stack: AnyRec): AiToolRefFinding[] {\n const findings: AiToolRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const universe = collectToolUniverse(stack);\n const unexposedActions = collectUnexposedActionNames(stack);\n\n const resolves = (ref: string): boolean => {\n if (ref.endsWith('*')) {\n const prefix = ref.slice(0, -1);\n for (const name of universe) {\n if (name.startsWith(prefix)) return true;\n }\n return false;\n }\n return universe.has(ref);\n };\n\n const skills = asArray(stack.skills);\n for (let si = 0; si < skills.length; si++) {\n const skill = skills[si];\n const skillName = strName(skill.name) ?? `#${si}`;\n const refs = Array.isArray(skill.tools) ? skill.tools : [];\n\n for (let ti = 0; ti < refs.length; ti++) {\n const ref = strName(refs[ti]);\n if (!ref || resolves(ref)) continue;\n\n const isPattern = ref.endsWith('*');\n // The distinct, high-frequency case: the action EXISTS but never\n // materialises. \"Fictional name\" and \"real action that isn't exposed\"\n // need different fixes, so they get different messages.\n const unexposed =\n !isPattern && ref.startsWith('action_') && unexposedActions.has(ref.slice('action_'.length))\n ? ref.slice('action_'.length)\n : undefined;\n\n findings.push({\n severity: 'warning',\n rule: AI_SKILL_TOOL_UNRESOLVED,\n where: `skill \"${skillName}\" · tools`,\n path: `skills[${si}].tools[${ti}]`,\n message: isPattern\n ? `Skill \"${skillName}\" subscribes to tool family \"${ref}\", which matches nothing this ` +\n `stack can resolve (no declared tool, no platform tool, and no AI-exposed declarative ` +\n `action materialises into it). The subscription contributes zero tools at runtime.`\n : unexposed\n ? `Skill \"${skillName}\" references tool \"${ref}\", but the action \"${unexposed}\" does ` +\n `not become an AI tool: the runtime materialises \\`action_<name>\\` only for an ` +\n `action that opts in with \\`ai.exposed: true\\` + \\`ai.description\\` (ADR-0011) AND ` +\n `has a headless path (type \\`script\\`/\\`api\\`/\\`flow\\` with a target or body — ` +\n `\\`url\\`/\\`modal\\`/\\`form\\` are UI-only). The reference is dropped at runtime, so ` +\n `the skill promises a capability the agent cannot call.`\n : `Skill \"${skillName}\" references tool \"${ref}\", which resolves to nothing this stack ` +\n `can see: not a \\`stack.tools\\` record, not a platform-registered tool, and not a ` +\n `materialised action tool (\\`action_<name>\\`). The runtime silently drops the ` +\n `reference, so the skill's instructions claim a capability the agent does not have — ` +\n `the assistant will improvise or fail when asked to use it.` +\n suggest(ref, universe),\n hint: unexposed\n ? `Either opt \"${unexposed}\" in — set \\`ai: { exposed: true, description: '…' }\\` (≥40 ` +\n `chars, LLM-facing) and give it a headless type — or drop the reference and have the ` +\n `skill's instructions recommend the UI action instead. A \\`modal\\`/\\`form\\`/\\`url\\` ` +\n `action stays human-driven by design; that is a legitimate answer, not a gap.`\n : `Back \"${ref}\" with a real executable: declare a declarative action (or flow), opt it ` +\n `in with \\`ai.exposed: true\\` + \\`ai.description\\`, and reference its materialised ` +\n `tool (\\`action_<name>\\` — the ADR-0109 default path, no tool record needed); or ` +\n `reference a platform tool by its registered name; or remove the reference and the ` +\n `instructions that mention it. Ignore this only if a runtime plugin outside the ` +\n `platform registry provides \"${ref}\". Family prefixes materialised by the runtime: ` +\n `${PLATFORM_TOOL_FAMILY_PREFIXES.join(', ')}.`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0063 §2] `stack.agents` is a platform-internal slot (issue #3820).\n *\n * ADR-0063 §2 withdrew tenant/app-package custom agents: the kernel ships\n * exactly two agents (`ask`, `build`), the surface the user is in binds one,\n * and third parties extend the platform by authoring **skills**, never\n * `*.agent.ts`. The `agent` metadata type carries the decision\n * (`allowRuntimeCreate: false, allowOrgOverride: false`), and the runtime\n * enforces it on both paths — `listAgents()` filters non-platform records out\n * of the catalog, and `loadAgent()` refuses them outright (cloud#904), so a\n * stack-authored agent 404s on chat and cannot be pinned via\n * `app.defaultAgent`.\n *\n * What was missing is the AUTHORING-time signal. `defineStack` still accepts\n * an `agents` array, so an app package could declare agents that parse,\n * validate, and build into the artifact — and then do nothing at runtime.\n * HotCRM shipped two of them for months. That is the ADR-0078 shape this rule\n * closes: loud at the producer, tolerant at the consumer (Prime Directive\n * #12).\n *\n * Severity is **warning**, not error, for one reason: the platform's own\n * packages legitimately author agent records, and this rule cannot tell a\n * platform package from an app package by reading the stack alone. A warning\n * that names the runtime consequence is honest for both readers; the runtime\n * is what actually gates. Deliberately NOT a Zod refine — an existing stack\n * must keep parsing (ADR-0078 non-goal #1).\n */\n\nexport const AGENT_AUTHORING_WITHDRAWN = 'agent-authoring-withdrawn';\n\nexport type AiAgentAuthoringSeverity = 'error' | 'warning';\n\nexport interface AiAgentAuthoringFinding {\n /** Always `warning` — the runtime is the gate; this is the authoring-time signal. */\n severity: AiAgentAuthoringSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `agent \"sales_copilot\"`. */\n where: string;\n /** Config path, e.g. `agents[0]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => !!x && typeof x === 'object');\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * The two platform agent ids. A stack that re-declares one of these is doing\n * something different from inventing a custom persona (it is shadowing a\n * platform record), so it gets its own wording.\n */\nconst PLATFORM_AGENT_NAMES = new Set(['ask', 'build', 'data_chat', 'metadata_assistant']);\n\n/**\n * Flag every agent declared in a stack. Returns findings (empty = clean,\n * which is what every app package should be).\n */\nexport function validateAiAgentAuthoring(stack: AnyRec): AiAgentAuthoringFinding[] {\n const findings: AiAgentAuthoringFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const agents = asArray(stack.agents);\n for (let ai = 0; ai < agents.length; ai++) {\n const agent = agents[ai];\n const name = strName(agent.name) ?? `#${ai}`;\n const isPlatformName = PLATFORM_AGENT_NAMES.has(name);\n const skillCount = Array.isArray(agent.skills) ? agent.skills.length : 0;\n\n findings.push({\n severity: 'warning',\n rule: AGENT_AUTHORING_WITHDRAWN,\n where: `agent \"${name}\"`,\n path: `agents[${ai}]`,\n message: isPlatformName\n ? `This stack declares an agent named \"${name}\", which is a PLATFORM agent id. The ` +\n `runtime serves its own record for that name and ignores this one — the declaration ` +\n `has no effect and will drift from the platform's definition.`\n : `This stack declares the agent \"${name}\", but tenant/app-package agents were withdrawn ` +\n `(ADR-0063 §2): the kernel ships exactly two agents (\\`ask\\`, \\`build\\`) and the surface ` +\n `the user is in binds one. The runtime filters this record out of the agent catalog and ` +\n `refuses to load it, so it never runs — it parses, validates, and ships as inert ` +\n `metadata.`,\n hint: isPlatformName\n ? `Remove the declaration; the platform owns \"${name}\". Extend it with skills instead.`\n : `Delete the agent and express its capability as skills. Everything an agent carried ` +\n `that a skill does not is persona text: move the useful parts of \\`instructions\\` into ` +\n `the skills' own instructions.` +\n (skillCount > 0\n ? ` The ${skillCount} skill${skillCount === 1 ? '' : 's'} this agent references ` +\n `already carry the capability — they attach to the platform agent by \\`surface\\` ` +\n `affinity, so nothing is lost by dropping the persona.`\n : ``),\n });\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Author-time write-set check for L2 (`language:'js'`) hook bodies (#4271).\n//\n// An L2 body that writes a field the target object never declares —\n// `ctx.input.amout = 0`, `ctx.api.object('deal').update({ stag: 'won' })` —\n// runs clean in the QuickJS sandbox and reaches the driver UNFILTERED:\n// `applyMutationsToInput` (runtime/src/sandbox/body-runner.ts) is a plain\n// `Object.assign`, and `validateRecord` walks declared fields on insert and\n// `continue`s past a key with no field def on update. What happens after that\n// is DRIVER-DEPENDENT, and neither half is acceptable:\n//\n// • SQL — the stray column enters the knex statement and the WHOLE write\n// fails with a driver-level error (`table deal has no column named\n// stagee`). The write is lost, and the error surfaces far from the\n// authoring mistake that caused it.\n// • Schemaless (memory, MongoDB) — the driver spreads the payload, so the\n// stray key IS persisted: an undeclared column nothing downstream reads.\n//\n// Either way the mistake is invisible where it is MADE — the #4001 family, if\n// not literally its silent-no-op shape. Both runtime outcomes are pinned by\n// `runtime/src/sandbox/undeclared-field-write-driver-split.integration.test.ts`\n// so this rule's wording cannot drift from what the runtime does; the same\n// split is documented in `content/docs/automation/hook-bodies.mdx`.\n//\n// The read side (`hook.condition`, ADR-0032) and the capability surface are\n// statically checked; until this rule, the write side was the one blind face\n// (the gap `hook-body.zod.ts` used to carry as \"accepted\").\n//\n// Scope — the literal write patterns in {@link HOOK_BODY_WRITE_PATTERNS}, and\n// nothing else. The body is PARSED (TypeScript parser, never executed, never\n// type-checked); each declared pattern is reconciliation-tested against the\n// extractor, so a pattern cannot be declared-but-unverified (#3528's death).\n// Everything statically unknowable is skipped SILENTLY, asymmetrically\n// favouring missed findings over false ones — a false positive kills an\n// advisory lint, a miss just leaves the gap open a little longer:\n//\n// • computed keys (`ctx.input[k] = …`), spreads, non-literal payloads;\n// • dynamic object names (`ctx.api.object(name)`);\n// • `object:'*'` wildcard hooks' `ctx.input` writes (no single target);\n// • multi-target hooks where the field exists on SOME target — the body may\n// legitimately branch per object (`if (ctx.object === '…')`), so only a\n// field missing on EVERY named target is flagged;\n// • targets declared by another package (not in this stack);\n// • one-level aliasing (`const doc = ctx.input; doc.x = 1`) — known miss,\n// deliberately: v1 does no data-flow analysis.\n//\n// Severity: always `warning` (advisory, never gates). Same posture as\n// `lintUnknownAuthoringKeys` (#3786) — ratchet only with field data.\n//\n// Wired via REFERENCE_INTEGRITY_RULES (it resolves field NAMES written in\n// metadata against what the stack declares — the suite's exact membership\n// test), so it runs on `os validate`, `os lint` and `os compile` at once.\n// Deliberately NOT in the `defineStack` runtime path: the TypeScript parser\n// has no place on kernel boot (see the lazy-load contract below).\n//\n// ACTION bodies run through the same `HookBodySchema` and the same sandbox, so\n// they get the same treatment from a sibling rule — `validate-action-body-\n// writes.ts`, which reuses this module's extractor, ledger, field index and\n// implicit-field set. It carries only the pattern subset that survives the\n// context change (an action's `ctx.input` is its params bag, not a record);\n// the reasoning is declared as data there, not restated here.\n\nimport { createRequire } from 'node:module';\nimport type ts from 'typescript';\nimport { findClosestMatches, formatSuggestion } from '@objectstack/spec/shared';\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\n// The TypeScript compiler must NOT be imported at module top level: it is\n// ~9 MB of CJS, and @objectstack/lint sits on the kernel boot path — while\n// this gate only parses when a hook actually carries an L2 JS body. Same\n// lazy-load contract as validate-react-page-props.ts (which see for the\n// history, including production images pruning the package). Guarded by\n// lazy-deps.test.ts.\n//\n// `node:module` is a Node builtin, untouched by esbuild/tsup, so the static\n// `createRequire` import survives bundling; the `createRequire(...)` call is\n// deferred because `import.meta.url` is rewritten to an empty stub in the CJS\n// build (same pattern as driver-sqlite-wasm's knex-wasm-dialect).\nlet cachedTs: typeof ts | null = null;\nfunction loadTypeScript(): typeof ts {\n if (cachedTs) return cachedTs;\n const anchor =\n typeof import.meta !== 'undefined' && import.meta.url\n ? import.meta.url\n : typeof __filename !== 'undefined'\n ? __filename\n : process.cwd() + '/';\n try {\n cachedTs = createRequire(anchor)('typescript') as typeof ts;\n } catch (err) {\n throw new Error(\n `@objectstack/lint: checking an L2 (language:'js') hook body requires the \"typescript\" package, which could not ` +\n `be loaded (${err instanceof Error ? err.message : String(err)}). It is a declared dependency of ` +\n `@objectstack/lint — if this deployment prunes packages, keep \"typescript\" in the image; it is only loaded ` +\n `when a hook with a JS body is validated.`,\n );\n }\n return cachedTs;\n}\n\nexport type HookBodyWriteSeverity = 'warning';\n\nexport interface HookBodyWriteFinding {\n /** v1 is advisory-only by contract — the type says so. */\n severity: HookBodyWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `hook \"normalize_lead\" › body`. */\n where: string;\n /** Config path, e.g. `hooks[0].body.source`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const HOOK_BODY_WRITE_UNKNOWN_FIELD = 'hook-body-write-unknown-field';\n\n// ─── The write-pattern ledger ───────────────────────────────────────────────\n//\n// Every syntactic write shape the extractor recognizes, declared as data. The\n// reconciliation test (validate-hook-body-writes.test.ts) runs the extractor\n// over each entry's example and asserts it yields EXACTLY the declared writes,\n// each tagged with this entry's id — so \"the docs say this pattern is covered\n// but nothing extracts it\" cannot happen (#3528), and the answer to \"which\n// writes does the lint see?\" is this list, not the extractor's code.\n\n/** One syntactic write shape the extractor recognizes. */\nexport interface HookBodyWritePattern {\n /** Stable pattern id, carried on every extracted write. */\n readonly id: string;\n /** Author-facing syntax summary (for docs/diagnostics, not matching). */\n readonly syntax: string;\n /** Reconciliation fixture: extracting `source` must yield exactly `writes`. */\n readonly example: {\n readonly source: string;\n readonly writes: ReadonlyArray<{ field: string; object?: string }>;\n };\n}\n\nexport const HOOK_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[] = [\n {\n id: 'input-property-assign',\n syntax: \"ctx.input.<field> = … | ctx.input['<field>'] ⟨op⟩= …\",\n example: {\n // Compound (`+=`) and logical (`??=`) assignment operators write their\n // LHS exactly like `=` does — the example pins the whole operator range.\n source: \"ctx.input.total = 0; ctx.input['status'] ??= 'open'; ctx.input.retries += 1;\",\n writes: [{ field: 'total' }, { field: 'status' }, { field: 'retries' }],\n },\n },\n {\n id: 'input-object-assign',\n syntax: 'Object.assign(ctx.input, { <field>: … })',\n example: {\n source: \"Object.assign(ctx.input, { total: 5, 'status': 'open', discount });\",\n writes: [{ field: 'total' }, { field: 'status' }, { field: 'discount' }],\n },\n },\n {\n // ACTION-only shape (the hook sandbox context has no `ctx.record` at all).\n // Declared here because this ledger is the extractor's shape inventory, not\n // any one rule's; every consumer declares which shapes it consumes.\n id: 'record-property-assign',\n syntax: \"ctx.record.<field> = … | ctx.record['<field>'] ⟨op⟩= …\",\n example: {\n source: \"ctx.record.stage = 'won'; ctx.record['amount'] += 1;\",\n writes: [{ field: 'stage' }, { field: 'amount' }],\n },\n },\n {\n id: 'api-crud-literal',\n syntax:\n \"ctx.api.object('<object>').insert({…}) | .create({…}) | .update({…}) | .updateById(id, {…})\",\n example: {\n // Real ObjectRepository signatures: the record payload is argument 0 for\n // insert/create/update and argument 1 for updateById. (`update(data)` —\n // NOT `update(id, data)`; the id travels inside the payload/options.)\n source:\n \"await ctx.api.object('audit_log').insert({ event: 'won' }); \" +\n \"await ctx.api.object('crm_deal').updateById(id, { stage: 'won' });\",\n writes: [\n { field: 'event', object: 'audit_log' },\n { field: 'stage', object: 'crm_deal' },\n ],\n },\n },\n];\n\n/** A ledger pattern a given rule does NOT consume, and why. */\nexport interface BodyWritePatternExclusion {\n /** The {@link HOOK_BODY_WRITE_PATTERNS} entry id being excluded. */\n readonly id: string;\n /** Why the shape does not mean the same thing on this rule's surface. */\n readonly reason: string;\n}\n\n/**\n * The ledger shapes THIS rule consumes.\n *\n * Declared rather than implied: before the ledger carried a shape the hook\n * surface does not have, every write with no `object` was necessarily a\n * `ctx.input` write, and the rule could branch on that alone. It no longer can\n * — a `record-property-assign` write also carries no object, and would have\n * been reported as \"the hook writes 'stage' to its input\", which is false.\n * Each consumer declaring its own subset is what stops the next added shape\n * from silently landing in a branch that was never written for it.\n */\nexport const HOOK_BODY_WRITE_PATTERN_IDS: readonly string[] = [\n 'input-property-assign',\n 'input-object-assign',\n 'api-crud-literal',\n];\n\n/** Ledger shapes this rule leaves alone, each with its reason. */\nexport const HOOK_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[] = [\n {\n id: 'record-property-assign',\n reason:\n 'a hook sandbox context has no `ctx.record` at all — `buildSandboxContext` never sets it (a hook’s ' +\n 'record IS `ctx.input`), so the expression throws at run time rather than silently no-op’ing. A loud ' +\n 'failure the author sees on the first run is not this advisory rule’s business',\n },\n];\n\nconst HOOK_APPLICABLE_IDS: ReadonlySet<string> = new Set(HOOK_BODY_WRITE_PATTERN_IDS);\n\n/**\n * `ctx.api.object(name)` write methods → index of the record-payload argument.\n * Mirrors `ObjectRepository` in packages/objectql (the surface hooks actually\n * receive): `upsert` exists only on the last-resort engine facade actions may\n * fall back to, never on the hook path, so it is deliberately absent.\n */\nconst API_WRITE_METHODS: ReadonlyMap<string, number> = new Map([\n ['insert', 0],\n ['create', 0],\n ['update', 0],\n ['updateById', 1],\n]);\n\n/**\n * Wrapper keys of the flat-input proxy (`installFlatInput` in\n * packages/objectql/src/hook-wrappers.ts). `ctx.input.data = …` replaces the\n * whole record payload and `id`/`options`/`ast` address the operation\n * envelope — none is a record-FIELD write, so none is ever flagged.\n */\nconst INPUT_ENVELOPE_KEYS: ReadonlySet<string> = new Set(['id', 'options', 'ast', 'data']);\n\n/**\n * Columns always legitimately writable by automation without appearing in\n * `object.fields`: the package-shared registry-injected columns\n * (`system-fields.ts`, #4330) plus the UNION of its sibling rules' local\n * exemptions (`_id`/`name`/`space` from validate-translation-references,\n * `name`/`owner`/`record_type` from validate-flow-template-paths) — because\n * the cost asymmetry is the same everywhere: over-inclusion is at worst a\n * missed finding, under-inclusion is a false one.\n *\n * Exported for `validate-action-body-writes.ts` only (not re-exported from the\n * package barrel). The action rule is this same check on the other surface that\n * carries a `HookBodySchema` body, so the two must agree on what is implicitly\n * writable — a second copy of this extension would drift exactly the way the\n * five hand-copied lists #4330 collapsed did.\n */\nexport const IMPLICIT_FIELDS: ReadonlySet<string> = new Set([\n ...SYSTEM_FIELDS,\n '_id', 'name', 'space', 'owner', 'record_type',\n]);\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => isRec(x));\n if (isRec(v)) {\n return Object.entries(v).map(([name, def]) => ({\n name,\n ...(isRec(def) ? def : {}),\n }));\n }\n return [];\n}\n\n/**\n * object name → its declared field names (both `fields` authoring shapes).\n *\n * Exported for `validate-action-body-writes.ts` only (see\n * {@link IMPLICIT_FIELDS} for why the two rules share rather than copy).\n */\nexport function indexObjectFields(stack: AnyRec): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const obj of asArray(stack.objects)) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const names = new Set<string>();\n for (const f of asArray(obj.fields)) {\n if (typeof f.name === 'string' && f.name) names.add(f.name);\n }\n out.set(name, names);\n }\n return out;\n}\n\n/**\n * The declared field names of `objectName` — but ONLY when they are a sound\n * basis for judging \"this name resolves to nothing\". Otherwise `undefined`.\n *\n * Two different unknowns collapse to one answer on purpose, because every\n * caller in this family owes them the same silence:\n *\n * • the object is not in this stack — another package declares it, and a\n * field map we cannot see cannot be judged;\n * • the object is here but declares NO fields at all — an external object or\n * a datasource-introspected schema whose columns are resolved at runtime.\n * Its field map is not empty, it is *unknown*, and an empty Set answers\n * `has(anything) === false`, which reads as \"no such field\" for EVERY write\n * to it. That is a false-positive generator, and a false positive kills an\n * advisory lint (#4383).\n *\n * The distinction is unused today — no rule in the family wants to act on one\n * and not the other — so collapsing it here is what stops the guard from being\n * hand-copied per call site and forgotten at one of them, which is exactly how\n * it went missing from the hook and action rules while\n * `validate-searchable-fields` (skip #2) and `validate-flow-node-writes` both\n * had it. A future caller that genuinely needs to tell them apart should read\n * the index directly and say why.\n */\nexport function judgeableFieldsOf(\n index: ReadonlyMap<string, Set<string>>,\n objectName: string,\n): Set<string> | undefined {\n const declared = index.get(objectName);\n if (!declared || declared.size === 0) return undefined;\n return declared;\n}\n\n/** One statically-extracted field write found in an L2 body. */\nexport interface ExtractedHookBodyWrite {\n /** Which {@link HOOK_BODY_WRITE_PATTERNS} entry matched. */\n patternId: string;\n /** Target object name; `undefined` = the hook's own target object(s). */\n object?: string;\n /** The `ctx.api` method for diagnostics (`insert`/`create`/`update`/`updateById`). */\n method?: string;\n field: string;\n}\n\n/** Everything one parse of an L2 body yields. */\nexport interface ExtractedHookBodyWriteSet {\n /** Every literal write the {@link HOOK_BODY_WRITE_PATTERNS} ledger declares. */\n writes: ExtractedHookBodyWrite[];\n /**\n * `ctx.record` is handed to something as a VALUE somewhere in the body — an\n * argument, an assignment RHS, a spread, a return — rather than only having\n * its properties read and written, or being truthiness/type tested.\n *\n * The action rule needs this to tell a dead snapshot write from a live one:\n * `ctx.record.stage = 'won'; await ctx.api.object('d').update(ctx.record)`\n * builds a payload and persists it, so the assignment is not a no-op. When\n * this is true, no record write in the body can be judged, and none is\n * reported. (One-level aliasing — `const r = ctx.record` — reads as an\n * escape too, which is the safe direction: it suppresses findings.)\n */\n ctxRecordEscapes: boolean;\n}\n\n/**\n * Extract every literal field write the pattern ledger declares from an L2\n * body's source. Parse-only (the source is never executed), error-tolerant\n * (a body with syntax errors simply yields fewer matches), and lazy: the\n * TypeScript compiler is not loaded when no pattern can possibly match.\n *\n * Thin projection of {@link extractHookBodyWriteSet} — use that one when the\n * `ctx.record` liveness signal matters, so the body is parsed once, not twice.\n */\nexport function extractHookBodyWrites(source: string): ExtractedHookBodyWrite[] {\n return extractHookBodyWriteSet(source).writes;\n}\n\n/** {@link extractHookBodyWrites} plus the `ctx.record` liveness signal, one parse. */\nexport function extractHookBodyWriteSet(source: string): ExtractedHookBodyWriteSet {\n // Every recognizable pattern begins at a `ctx` or `Object` identifier — a\n // body containing neither cannot match, and must not pay the compiler load.\n if (!/\\bctx\\b/.test(source) && !/\\bObject\\b/.test(source)) {\n return { writes: [], ctxRecordEscapes: false };\n }\n\n const tsc = loadTypeScript();\n // The runtime wraps a hook body as `new AsyncFunction('ctx', source)` — a\n // FUNCTION BODY, not a module. Parse it in the same context so bare\n // `return` / `await` mean what they mean at run time.\n const sf = tsc.createSourceFile(\n 'hook-body.ts',\n `async function __body(ctx) {\\n${source}\\n}`,\n tsc.ScriptTarget.Latest,\n /* setParentNodes */ false,\n tsc.ScriptKind.TS,\n );\n\n const writes: ExtractedHookBodyWrite[] = [];\n /** Every `ctx.record` reference, and the subset that is only an access base. */\n const recordRefs: ts.Node[] = [];\n const consumedRecordRefs = new Set<ts.Node>();\n\n /** `node` is exactly `ctx.<prop>`. */\n const isCtxDot = (node: ts.Node, prop: string): boolean =>\n tsc.isPropertyAccessExpression(node) &&\n tsc.isIdentifier(node.expression) &&\n node.expression.text === 'ctx' &&\n node.name.text === prop;\n\n /** The literal field name of an LHS rooted at `ctx.<prop>`, if any. */\n const fieldOfCtxLhs = (lhs: ts.Expression, prop: string): string | undefined => {\n if (tsc.isPropertyAccessExpression(lhs) && tsc.isIdentifier(lhs.name) && isCtxDot(lhs.expression, prop)) {\n return lhs.name.text;\n }\n if (tsc.isElementAccessExpression(lhs) && isCtxDot(lhs.expression, prop)) {\n const arg = lhs.argumentExpression;\n if (tsc.isStringLiteral(arg) || tsc.isNoSubstitutionTemplateLiteral(arg)) return arg.text;\n }\n return undefined; // computed key / nested path — statically opaque\n };\n\n /** Literal keys of an object-literal expression (spreads/computed skipped). */\n const literalObjectKeys = (node: ts.Expression): string[] => {\n if (!tsc.isObjectLiteralExpression(node)) return [];\n const keys: string[] = [];\n for (const p of node.properties) {\n if (tsc.isPropertyAssignment(p)) {\n if (tsc.isIdentifier(p.name) || tsc.isStringLiteral(p.name)) keys.push(p.name.text);\n } else if (tsc.isShorthandPropertyAssignment(p)) {\n keys.push(p.name.text);\n }\n // spread / computed / method members are statically opaque — skipped\n }\n return keys;\n };\n\n const visit = (node: ts.Node): void => {\n // Pattern: input-property-assign. FirstAssignment..LastAssignment spans\n // `=` and every compound/logical assignment operator (`+=`, `??=`, …) —\n // each writes its LHS.\n if (\n tsc.isBinaryExpression(node) &&\n node.operatorToken.kind >= tsc.SyntaxKind.FirstAssignment &&\n node.operatorToken.kind <= tsc.SyntaxKind.LastAssignment\n ) {\n const inputField = fieldOfCtxLhs(node.left, 'input');\n if (inputField !== undefined && !INPUT_ENVELOPE_KEYS.has(inputField)) {\n writes.push({ patternId: 'input-property-assign', field: inputField });\n }\n // Pattern: record-property-assign. No envelope-key filter — `ctx.record`\n // is a plain snapshot of the record, not the flat-input proxy, so it\n // carries no operation envelope to exclude.\n const recordField = fieldOfCtxLhs(node.left, 'record');\n if (recordField !== undefined) {\n writes.push({ patternId: 'record-property-assign', field: recordField });\n }\n }\n\n // `ctx.record` liveness, for the rule that judges whether a record write\n // can possibly matter. A reference is CONSUMED when the position it sits in\n // cannot hand the object to anything that might persist it; every other\n // position — an argument, an assignment RHS, a spread, a return — can.\n //\n // 1. the base of a property/element access: `ctx.record.id`,\n // `ctx.record.x = 1`, `ctx.record['k']`;\n // 2. a truthiness or type test. `ctx.record && ctx.record.id` is the\n // defensive idiom real action bodies are written with (the showcase's\n // own `mark_done` opens with it), and reading a test as an escape\n // would suppress the finding on most bodies that have one. A test\n // reads the reference and yields a boolean — or, for `&&`/`||`/`??`,\n // yields the LEFT operand only when it is falsy, which is null or\n // undefined and persists nothing either way. Only the left operand is\n // a test: `x || ctx.record` really does evaluate to the object.\n if (tsc.isPropertyAccessExpression(node) || tsc.isElementAccessExpression(node)) {\n if (isCtxDot(node.expression, 'record')) consumedRecordRefs.add(node.expression);\n }\n if (tsc.isBinaryExpression(node)) {\n const op = node.operatorToken.kind;\n if (\n (op === tsc.SyntaxKind.AmpersandAmpersandToken ||\n op === tsc.SyntaxKind.BarBarToken ||\n op === tsc.SyntaxKind.QuestionQuestionToken) &&\n isCtxDot(node.left, 'record')\n ) {\n consumedRecordRefs.add(node.left);\n }\n }\n if (tsc.isPrefixUnaryExpression(node) && node.operator === tsc.SyntaxKind.ExclamationToken) {\n if (isCtxDot(node.operand, 'record')) consumedRecordRefs.add(node.operand);\n }\n if (tsc.isTypeOfExpression(node) && isCtxDot(node.expression, 'record')) {\n consumedRecordRefs.add(node.expression);\n }\n if (\n (tsc.isIfStatement(node) || tsc.isWhileStatement(node) || tsc.isDoStatement(node)) &&\n isCtxDot(node.expression, 'record')\n ) {\n consumedRecordRefs.add(node.expression);\n }\n if (tsc.isConditionalExpression(node) && isCtxDot(node.condition, 'record')) {\n consumedRecordRefs.add(node.condition);\n }\n if (isCtxDot(node, 'record')) recordRefs.push(node);\n\n if (tsc.isCallExpression(node)) {\n const callee = node.expression;\n\n // Pattern: input-object-assign.\n if (\n tsc.isPropertyAccessExpression(callee) &&\n tsc.isIdentifier(callee.expression) &&\n callee.expression.text === 'Object' &&\n callee.name.text === 'assign' &&\n node.arguments.length >= 2 &&\n isCtxDot(node.arguments[0], 'input')\n ) {\n // Later Object.assign sources overwrite earlier ones but never remove\n // a key, so every literal key is genuinely written regardless of the\n // non-literal arguments around it.\n for (const arg of node.arguments.slice(1)) {\n for (const field of literalObjectKeys(arg)) {\n if (!INPUT_ENVELOPE_KEYS.has(field)) {\n writes.push({ patternId: 'input-object-assign', field });\n }\n }\n }\n }\n\n // Pattern: api-crud-literal — ctx.api.object('<lit>').<method>(payload…).\n if (tsc.isPropertyAccessExpression(callee) && tsc.isIdentifier(callee.name)) {\n const payloadIndex = API_WRITE_METHODS.get(callee.name.text);\n const recv = callee.expression;\n if (\n payloadIndex !== undefined &&\n tsc.isCallExpression(recv) &&\n tsc.isPropertyAccessExpression(recv.expression) &&\n recv.expression.name.text === 'object' &&\n isCtxDot(recv.expression.expression, 'api') &&\n recv.arguments.length === 1\n ) {\n const objArg = recv.arguments[0];\n const objectName =\n tsc.isStringLiteral(objArg) || tsc.isNoSubstitutionTemplateLiteral(objArg)\n ? objArg.text\n : undefined; // dynamic object name — statically opaque\n const payload = node.arguments[payloadIndex];\n if (objectName && payload !== undefined) {\n for (const field of literalObjectKeys(payload)) {\n writes.push({\n patternId: 'api-crud-literal',\n object: objectName,\n method: callee.name.text,\n field,\n });\n }\n }\n }\n }\n }\n\n tsc.forEachChild(node, visit);\n };\n visit(sf);\n return {\n writes,\n ctxRecordEscapes: recordRefs.some((ref) => !consumedRecordRefs.has(ref)),\n };\n}\n\n/**\n * Validate L2 hook-body writes against target-object field declarations.\n * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.\n */\nexport function validateHookBodyWrites(stack: AnyRec): HookBodyWriteFinding[] {\n const findings: HookBodyWriteFinding[] = [];\n const hooks = asArray(stack.hooks);\n if (hooks.length === 0) return findings;\n\n // Built lazily: a stack whose hooks are all L1/handler-based never pays it.\n let objectFields: Map<string, Set<string>> | null = null;\n\n hooks.forEach((hook, hookIndex) => {\n const body = hook.body;\n if (!isRec(body) || body.language !== 'js') return;\n const source = body.source;\n if (typeof source !== 'string' || source.trim() === '') return;\n\n const writes = extractHookBodyWrites(source).filter((w) => HOOK_APPLICABLE_IDS.has(w.patternId));\n if (writes.length === 0) return;\n\n objectFields ??= indexObjectFields(stack);\n const hookName = typeof hook.name === 'string' && hook.name ? hook.name : `#${hookIndex}`;\n\n // The hook's own target set, for `ctx.input` writes. A wildcard target has\n // no single object to check against; a target whose fields cannot be judged\n // ({@link judgeableFieldsOf} — cross-package, or declaring no fields at all)\n // gives nothing to resolve against — either way `ctx.input` writes are\n // skipped, not guessed.\n const targets = (Array.isArray(hook.object) ? hook.object : [hook.object]).filter(\n (o): o is string => typeof o === 'string' && o.trim() !== '',\n );\n const targetSets = targets.map((t) => judgeableFieldsOf(objectFields!, t));\n // ALL targets must be judgeable, not just one: the finding below fires only\n // when a field is missing from EVERY target, and an unjudgeable target is\n // one the field might well exist on. One opaque target therefore makes the\n // whole \"missing everywhere\" claim unsound, not merely narrower (#4383).\n const inputJudgeable =\n targets.length > 0 && !targets.includes('*') && targetSets.every((s) => s !== undefined);\n\n const where = `hook \"${hookName}\" › body`;\n const path = `hooks[${hookIndex}].body.source`;\n const reported = new Set<string>();\n\n for (const w of writes) {\n const dedupeKey = `${w.object ?? ''}\\u0000${w.field}`;\n if (reported.has(dedupeKey)) continue;\n\n if (w.object === undefined) {\n // ctx.input write → the hook's own object(s). Flag only a field\n // missing on EVERY named target (a multi-target body may branch per\n // object, so a partial miss is not statically wrong).\n if (!inputJudgeable) continue;\n if (IMPLICIT_FIELDS.has(w.field)) continue;\n if (targetSets.some((s) => s!.has(w.field))) continue;\n\n reported.add(dedupeKey);\n const objDesc =\n targets.length === 1\n ? `object '${targets[0]}'`\n : `none of its target objects (${targets.join(', ')})`;\n const declares = targets.length === 1 ? 'declares no such field' : 'declare that field';\n findings.push({\n severity: 'warning',\n rule: HOOK_BODY_WRITE_UNKNOWN_FIELD,\n where,\n path,\n message:\n `body writes '${w.field}' to its input, but ${objDesc} ${declares}. The sandboxed script runs ` +\n `clean and the value is copied back onto the record payload unfiltered — on a SQL driver the ` +\n `stray column then fails the WHOLE write with a driver-level error far from here; on a ` +\n `schemaless driver (memory, MongoDB) it is persisted as an undeclared key (#4271).`,\n hint: fixHint(w.field, unionCandidates(targetSets)),\n });\n } else {\n // ctx.api write → the named object.\n const known = judgeableFieldsOf(objectFields!, w.object);\n if (!known) continue; // cross-package, or no declared fields — cannot judge\n if (IMPLICIT_FIELDS.has(w.field) || known.has(w.field)) continue;\n\n reported.add(dedupeKey);\n findings.push({\n severity: 'warning',\n rule: HOOK_BODY_WRITE_UNKNOWN_FIELD,\n where,\n path,\n message:\n `body calls ctx.api.object('${w.object}').${w.method ?? 'update'}(…) writing '${w.field}', but ` +\n `object '${w.object}' declares no such field. The write-path validator skips the unknown key — ` +\n `on a SQL driver the whole call then fails with a driver-level error far from here; on a ` +\n `schemaless driver (memory, MongoDB) the stray key is persisted (#4271).`,\n hint: fixHint(w.field, [...known]),\n });\n }\n }\n });\n\n return findings;\n}\n\n/** Every field name declared across the (all-known) target sets, deduplicated. */\nfunction unionCandidates(targetSets: ReadonlyArray<Set<string> | undefined>): string[] {\n const out = new Set<string>();\n for (const s of targetSets) for (const f of s ?? []) out.add(f);\n return [...out];\n}\n\n/** Did-you-mean (declared + system columns as candidates) plus the fix. */\nfunction fixHint(field: string, declared: string[]): string {\n const suggestion = formatSuggestion(findClosestMatches(field, [...declared, ...IMPLICIT_FIELDS]));\n return (\n (suggestion ? `${suggestion} ` : '') +\n `Fix the field name, or declare '${field}' on the object. Only the literal write patterns in ` +\n `HOOK_BODY_WRITE_PATTERNS are checked — computed keys, spreads and aliased input are not — and this ` +\n `warning never blocks a build.`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Author-time write-set check for L2 (`language:'js'`) ACTION bodies — the\n// sibling of `validate-hook-body-writes.ts` (#4271 follow-up).\n//\n// An action body is the same artefact as a hook body: the same\n// `HookBodySchema` union, parsed by the same `HookBodySchema.safeParse` in\n// `actionBodyRunnerFactory` (packages/runtime/src/sandbox/body-runner.ts), run\n// in the same QuickJS sandbox. So it fails the same way — an action body that\n// writes a field the target object never declares reaches the driver\n// unfiltered, and the outcome is DRIVER-DEPENDENT: on SQL the stray column\n// fails the whole call with a driver-level error far from the authoring\n// mistake, on a schemaless driver the stray key is persisted. Same #4271\n// split as the hook side (see that file's header for the measured chain, and\n// `undeclared-field-write-driver-split.integration.test.ts` for the pin); the\n// hook rule alone left half the surface uncovered.\n//\n// ─── What does NOT carry over ───────────────────────────────────────────────\n//\n// The context the body receives is NOT the hook context, so the hook ledger\n// cannot be adopted wholesale. `buildActionSandboxContext` binds\n// `input: unwrapProxyToPlain(actionCtx?.params ?? {})` — an action's\n// `ctx.input` is its PARAMS BAG, validated upstream against the action's own\n// `params` declaration (ADR-0104 D2), not a record. Resolving those names\n// against object fields would flag every correctly-named parameter: a pure\n// false-positive machine, and a false positive kills an advisory lint. Hence\n// {@link ACTION_BODY_WRITE_EXCLUSIONS} — declared as data, with reasons, and\n// partition-tested against the shared ledger so a pattern added to the hook\n// side later cannot be silently assumed to apply here.\n//\n// So the unknown-field check keeps exactly one shape: `api-crud-literal`\n// (`ctx.api.object('<literal>').insert|.create|.update|.updateById({…})`). That\n// is the one path through which an action body actually persists anything, it\n// addresses its target object explicitly (so the action's own `objectName`\n// binding is irrelevant to the check), and the hook rule's extractor already\n// recognizes it verbatim.\n//\n// ─── The second finding: a record write that reaches nothing (#4345) ────────\n//\n// `ctx.record` is not a write surface either, but it fails DIFFERENTLY, so it\n// gets its own rule id rather than being folded in or dropped. The runner hands\n// the body a plain snapshot (`record: unwrapProxyToPlain(actionCtx?.record)`)\n// and `boundActionHandler` returns `result.value` without writing anything back\n// — no `applyMutationsToInput`, which is the hook path's alone. So\n// `ctx.record.x = …` is discarded for DECLARED and undeclared fields alike.\n// Reporting that through the unknown-field rule would be actively wrong:\n// flagging only the undeclared half implies the declared half persists —\n// precisely the false completion this rule family exists to stop manufacturing.\n//\n// It is NOT enough to flag every `ctx.record.<field> = …`, because mutating the\n// snapshot to build a payload is a legitimate idiom:\n//\n// ctx.record.stage = 'won';\n// await ctx.api.object('crm_deal').update(ctx.record); // the write is LIVE\n//\n// So the finding requires the write to be PROVABLY dead: reported only when\n// `ctx.record` never escapes as a value anywhere in the body (see\n// `ctxRecordEscapes`). Property reads (`ctx.record.id`) do not rescue a write\n// and do not suppress the finding; handing the object to anything — an\n// argument, an assignment RHS, a spread, a return — does. Aliasing\n// (`const r = ctx.record`) reads as an escape, which is the safe direction.\n//\n// ─── Shared posture ─────────────────────────────────────────────────────────\n//\n// Both findings keep the hook rule's posture: advisory `warning`, silent bail\n// on anything statically unknowable (dynamic object names, non-literal\n// payloads, cross-package targets), did-you-mean on a miss.\n//\n// ONE suite entry, TWO rule ids. Both findings fall out of one parse of one\n// source on one surface, so splitting them into two `REFERENCE_INTEGRITY_RULES`\n// members would parse every action body twice to say two things about the same\n// walk; hand-wiring the second into the CLI instead is the drift that suite\n// exists to end — `validateReadonlyFlowWrites` is the standing proof, wired\n// into `validate` and `compile` but never into `lint`.\n//\n// Lazy like its sibling: a body mentioning neither `api` nor `record` cannot\n// match either shape and never pays the TypeScript load.\n\nimport { findClosestMatches, formatSuggestion } from '@objectstack/spec/shared';\nimport {\n extractHookBodyWriteSet,\n indexObjectFields,\n judgeableFieldsOf,\n IMPLICIT_FIELDS,\n HOOK_BODY_WRITE_PATTERNS,\n type BodyWritePatternExclusion,\n type HookBodyWritePattern,\n} from './validate-hook-body-writes.js';\n\nexport type ActionBodyWriteSeverity = 'warning';\n\nexport interface ActionBodyWriteFinding {\n /** Advisory-only by contract, exactly like the hook rule — the type says so. */\n severity: ActionBodyWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `action \"close_deal\" › body`. */\n where: string;\n /** Config path, e.g. `actions[0].body.source`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries). Two, from one walk — see the header.\nexport const ACTION_BODY_WRITE_UNKNOWN_FIELD = 'action-body-write-unknown-field';\nexport const ACTION_RECORD_WRITE_DISCARDED = 'action-record-write-discarded';\n\n// ─── The applicable-pattern ledger ──────────────────────────────────────────\n//\n// Not a second pattern list: a declared PARTITION of the shared\n// `HOOK_BODY_WRITE_PATTERNS` into the shapes each of this module's two checks\n// consumes, plus the shapes neither does. Every part is data, and\n// validate-action-body-writes.test.ts asserts they cover the shared ledger\n// exactly — so a pattern added on the hook side FAILS this rule's test until\n// someone decides which part it belongs in. Silence is not a decision.\n// (`record-property-assign` landing here is that ratchet's first live catch.)\n\n/** @deprecated alias — the exclusion shape is shared with the hook rule. */\nexport type ActionBodyWriteExclusion = BodyWritePatternExclusion;\n\n/**\n * Ledger shapes the UNKNOWN-FIELD check consumes — resolved against the named\n * object's declared fields.\n *\n * Include-list, not exclude-list, on purpose: an unclassified new pattern is\n * then inert here (a missed finding) rather than live against a context it was\n * never reasoned about (a false one) — the same asymmetry the extractor's\n * silent bails follow.\n */\nexport const ACTION_BODY_WRITE_PATTERN_IDS: readonly string[] = ['api-crud-literal'];\n\n/**\n * Ledger shapes the DISCARDED-RECORD-WRITE check consumes — never resolved\n * against anything, because no field name can make a discarded write land.\n */\nexport const ACTION_RECORD_WRITE_PATTERN_IDS: readonly string[] = ['record-property-assign'];\n\n/** Shared-ledger patterns neither check consumes, each with its reason. */\nexport const ACTION_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[] = [\n {\n id: 'input-property-assign',\n reason:\n \"an action's ctx.input is its params bag (`input: unwrapProxyToPlain(actionCtx?.params)`), not a \" +\n 'record — `ctx.input.<name>` writes a declared PARAMETER, which object fields cannot judge',\n },\n {\n id: 'input-object-assign',\n reason: 'same surface as input-property-assign — Object.assign(ctx.input, …) targets the params bag',\n },\n];\n\n/**\n * The subset of the shared ledger the unknown-field check sees — the published\n * answer to \"which writes does the action lint resolve against fields?\".\n */\nexport const ACTION_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[] =\n HOOK_BODY_WRITE_PATTERNS.filter((p) => ACTION_BODY_WRITE_PATTERN_IDS.includes(p.id));\n\n/** The subset the discarded-record-write check sees. */\nexport const ACTION_RECORD_WRITE_PATTERNS: readonly HookBodyWritePattern[] =\n HOOK_BODY_WRITE_PATTERNS.filter((p) => ACTION_RECORD_WRITE_PATTERN_IDS.includes(p.id));\n\nconst APPLICABLE_IDS: ReadonlySet<string> = new Set(ACTION_BODY_WRITE_PATTERN_IDS);\nconst RECORD_WRITE_IDS: ReadonlySet<string> = new Set(ACTION_RECORD_WRITE_PATTERN_IDS);\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => isRec(x));\n if (isRec(v)) {\n return Object.entries(v).map(([name, def]) => ({\n name,\n ...(isRec(def) ? def : {}),\n }));\n }\n return [];\n}\n\n/** One L2 action body found in the stack, with the location to report it at. */\ninterface ActionBodySite {\n name: string;\n source: string;\n path: string;\n}\n\n/** The object an action binds to, by the same rule `collectBundleActions` uses. */\nfunction actionObjectBinding(action: AnyRec, parentObject?: string): string | undefined {\n if (typeof action.object === 'string' && action.object) return action.object;\n if (typeof action.objectName === 'string' && action.objectName) return action.objectName;\n return parentObject;\n}\n\n/**\n * Every L2 action body in the stack, from both places the runtime reads them.\n *\n * `collectBundleActions` (packages/runtime/src/app-plugin.ts) registers\n * `bundle.actions` AND `objects[].actions` — and `defineStack`'s\n * `mergeObjectActions` appends an action carrying `objectName` to its object's\n * array while PRESERVING the top-level entry, so a merged action is genuinely\n * reachable twice. Walk both and collapse the duplicate, or every merged\n * action's findings are reported twice.\n *\n * Deduplicated by VALUE — bound object, name and body source — not by object\n * identity the way the runtime can afford to. The suite runs on the\n * schema-PARSED stack (`validateReferenceIntegrity(result.data)` in `os\n * validate` / `os compile`), and parsing rebuilds every node, so the two copies\n * of a merged action arrive as distinct objects that are merely equal. An\n * identity check silently degrades to no check at all there — which is how the\n * showcase app reported its one action-body warning twice.\n *\n * Two same-named actions on DIFFERENT objects stay separate (the binding is in\n * the key). Two on the SAME binding with byte-identical bodies collapse to one\n * — they would emit the same sentence twice, so the second is noise.\n *\n * The top-level entry is walked first, so a merged action reports at\n * `actions[i]` — the authored location, not the derived copy.\n *\n * `type` is deliberately not consulted: the runtime binds a handler from\n * `action.body` alone (`actionBodyRunnerFactory` never reads `type`), so a body\n * on a non-`script` action still runs and still fails silently. Checking what\n * executes beats checking what the schema says should.\n */\nfunction collectActionBodies(stack: AnyRec): ActionBodySite[] {\n const sites: ActionBodySite[] = [];\n const seen = new Set<string>();\n\n const collect = (actions: unknown, pathPrefix: string, parentObject?: string): void => {\n asArray(actions).forEach((action, index) => {\n const body = action.body;\n if (!isRec(body) || body.language !== 'js') return;\n const source = body.source;\n if (typeof source !== 'string' || source.trim() === '') return;\n const name = typeof action.name === 'string' && action.name ? action.name : `#${index}`;\n const key = `${actionObjectBinding(action, parentObject) ?? ''}\\u0000${name}\\u0000${source}`;\n if (seen.has(key)) return;\n seen.add(key);\n sites.push({ name, source, path: `${pathPrefix}[${index}].body.source` });\n });\n };\n\n collect(stack.actions, 'actions');\n asArray(stack.objects).forEach((obj, objIndex) => {\n const parentObject = typeof obj.name === 'string' && obj.name ? obj.name : undefined;\n collect(obj.actions, `objects[${objIndex}].actions`, parentObject);\n });\n\n return sites;\n}\n\n/**\n * Validate L2 action-body writes against target-object field declarations.\n * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.\n */\nexport function validateActionBodyWrites(stack: AnyRec): ActionBodyWriteFinding[] {\n const findings: ActionBodyWriteFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const sites = collectActionBodies(stack);\n if (sites.length === 0) return findings;\n\n // Built lazily: only the unknown-field check needs it, so a stack whose\n // action bodies never reach `ctx.api` never pays it.\n let objectFields: Map<string, Set<string>> | null = null;\n\n for (const site of sites) {\n // Cheap prefilter, narrower than the extractor's own: every consumed\n // pattern is rooted at `ctx.api` or `ctx.record`, so a body carrying\n // neither identifier cannot match and must not pay the ~9 MB TypeScript\n // load. Pinned by the ledger test — a consumed pattern whose example fails\n // this filter fails there, rather than going quietly unchecked here.\n if (!/\\bapi\\b/.test(site.source) && !/\\brecord\\b/.test(site.source)) continue;\n\n // ONE parse per body, both checks read from it.\n const { writes: allWrites, ctxRecordEscapes } = extractHookBodyWriteSet(site.source);\n const writes = allWrites.filter((w) => APPLICABLE_IDS.has(w.patternId));\n const recordWrites = allWrites.filter((w) => RECORD_WRITE_IDS.has(w.patternId));\n if (writes.length === 0 && recordWrites.length === 0) continue;\n\n const where = `action \"${site.name}\" › body`;\n\n // ── Discarded record writes (#4345) ──────────────────────────────────\n // Reported only when the write is PROVABLY dead: `ctx.record` never\n // leaves the body as a value, so nothing can persist the mutation. When\n // it does escape, the snapshot may be a payload under construction, and\n // every one of its writes is skipped — a missed finding, never a false one.\n if (recordWrites.length > 0 && !ctxRecordEscapes) {\n const reportedFields = new Set<string>();\n for (const w of recordWrites) {\n if (reportedFields.has(w.field)) continue;\n reportedFields.add(w.field);\n findings.push({\n severity: 'warning',\n rule: ACTION_RECORD_WRITE_DISCARDED,\n where,\n path: site.path,\n message:\n `body assigns ctx.record.${w.field}, but an action's ctx.record is a plain snapshot the runtime ` +\n `never writes back — the action returns success and the assignment is discarded, whether or not ` +\n `'${w.field}' is a declared field (#4345).`,\n hint:\n `To persist it, write through the API: ctx.api.object('<object>').updateById(ctx.recordId, ` +\n `{ ${w.field}: … }). Reported only because ctx.record is never passed anywhere in this body — ` +\n `mutating the snapshot and then handing it to an API write is a live payload and is not flagged. ` +\n `This warning never blocks a build.`,\n });\n }\n }\n\n if (writes.length === 0) continue;\n objectFields ??= indexObjectFields(stack);\n const reported = new Set<string>();\n\n for (const w of writes) {\n // Defensive: today every applicable pattern addresses its object\n // explicitly. A future applicable pattern that does not (a `ctx.input`-\n // shaped one) has no target to resolve against in an action, so it stays\n // silent rather than being guessed at the action's `objectName`.\n if (w.object === undefined) continue;\n\n const dedupeKey = `${w.object}\\u0000${w.field}`;\n if (reported.has(dedupeKey)) continue;\n\n const known = judgeableFieldsOf(objectFields, w.object);\n if (!known) continue; // cross-package, or no declared fields — cannot judge\n if (IMPLICIT_FIELDS.has(w.field) || known.has(w.field)) continue;\n\n reported.add(dedupeKey);\n findings.push({\n severity: 'warning',\n rule: ACTION_BODY_WRITE_UNKNOWN_FIELD,\n where,\n path: site.path,\n message:\n `body calls ctx.api.object('${w.object}').${w.method ?? 'update'}(…) writing '${w.field}', but ` +\n `object '${w.object}' declares no such field. The write-path validator skips the unknown key — ` +\n `on a SQL driver the whole action then fails with a driver-level error far from here; on a ` +\n `schemaless driver (memory, MongoDB) the stray key is persisted (#4271).`,\n hint: fixHint(w.field, [...known]),\n });\n }\n }\n\n return findings;\n}\n\n/** Did-you-mean (declared + system columns as candidates) plus the fix. */\nfunction fixHint(field: string, declared: string[]): string {\n const suggestion = formatSuggestion(findClosestMatches(field, [...declared, ...IMPLICIT_FIELDS]));\n return (\n (suggestion ? `${suggestion} ` : '') +\n `Fix the field name, or declare '${field}' on the object. Only the literal write patterns in ` +\n `ACTION_BODY_WRITE_PATTERNS are checked — an action's ctx.input is its params bag, so it is not a ` +\n `record-write surface and is never resolved against fields — and this warning never blocks a build.`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Author-time write-set check for a flow CRUD node's `fields` write map — the\n// THIRD surface in the family #4271 opened, and the one the docs spent the\n// longest recommending as the safe alternative to the other two.\n//\n// A flow node that writes a field the target object never declares\n// (`config.fields.stagee` against an object whose column is `stage`) was caught\n// by nothing. `validate-readonly-flow-writes.ts` walks this exact map and\n// explicitly stepped over the unknown key (\"a form/field-layout lint concern,\n// not this rule's\" — a referral to a rule that does not check writes);\n// `validate-flow-template-paths.ts` checks the `{record.<path>}` READ tokens\n// interpolated into node config, never the write-side key. So the surface the\n// hook-body docs pointed authors at — \"prefer a flow update_record node, whose\n// structural `fields` config is checked\" — was the least checked of the three.\n//\n// ─── Why this one GATES where its two siblings advise ───────────────────────\n//\n// `hook-body-write-unknown-field` (#4305) and `action-body-write-unknown-field`\n// (#4344) are `warning` because they PARSE JavaScript: the finding is only as\n// good as the extractor, and a false positive kills an advisory lint. Nothing\n// here is parsed. `config.fields` is a literal, structural map next to a\n// literal `objectName` — when this rule speaks, the key provably resolves to no\n// column, at the same certainty `flow-update-readonly-field` already gates on\n// one config key over.\n//\n// And the runtime consequence is not the benign \"consumer skips the unknown\n// name and does the rest\" that keeps `page-field-unknown` / `form-field-unknown`\n// advisory. Nothing between the node and storage removes the key: the flow\n// executor calls the data engine directly (bypassing the metadata-protocol\n// ingress, which strips `readonly` — not unknown — keys anyway), the engine's\n// write paths strip only readonly/readonlyWhen, and the SQL driver's\n// `formatInput` / `applyWriteColumnMap` pass an unrecognized key straight\n// through (`m[k] ?? k`). Every branch below was measured, not inferred:\n//\n// • Through the engine, an undeclared key reaches `driver.update` /\n// `driver.create` verbatim, alongside the audit stamps.\n// • On SQLite/knex an UPDATE becomes `update \"deal\" set \"name\" = 'n2',\n// \"stagee\" = 'won' … → no such column: stagee`. The statement is rejected\n// WHOLE: `name` — spelled correctly, in the same payload — does not land\n// either, and the step fails with a driver error naming a column, far from\n// the authoring mistake.\n// • An INSERT fails the same way (`table deal has no column named stagee`),\n// and one notch harder: the row is never created at all, so every later\n// node that expected `{<node>.id}` is working from a record that does not\n// exist.\n// • On a schemaless datasource (memory, MongoDB) nothing rejects it, so the\n// stray key is persisted into a column the object never declares — where no\n// schema-driven read surface will return it.\n//\n// No outcome is \"the rest still works\". That is the same call\n// `validate-searchable-fields` makes for a stale entry and\n// `validate-flow-template-paths` makes for a filter-position token: gate when\n// the miss breaks or corrupts the operation, advise when it merely narrows the\n// output. Every skip below exists so that gate only ever fires on a certainty.\n//\n// ─── Scope ──────────────────────────────────────────────────────────────────\n//\n// {@link FLOW_WRITE_NODE_TYPES} — every CRUD node type that carries a `fields`\n// WRITE map: `update_record` (#4369) and `create_record` (#4371). The deferred\n// half, {@link FLOW_WRITE_NODE_TYPES_DEFERRED}, is now empty, and the partition\n// test still derives the full set behaviourally from the spec's\n// executor-written config schemas — so a node type that grows a write map later\n// lands on neither side and fails that test until someone classifies it.\n//\n// `get_record.fields` is NOT a member and never will be: it is a projection\n// (`z.array(z.string())`), a READ, and an unknown entry there narrows the\n// selection rather than breaking the statement. `screen.defaults` is not one\n// either — an object-form screen forwards it into the `ScreenSpec` the client\n// renders, so an unknown key is a form prefill the renderer ignores: inert, the\n// \"skips it and renders the rest\" case this rule's severity is defined against.\n// Both are excluded on the shape of their failure, not by omission.\n//\n// `runAs` is deliberately NOT consulted, unlike its readonly sibling. A\n// `runAs:'system'` flow is elevated past the readonly strip, which is why that\n// rule skips it — but no run identity conjures a column, so an unknown field is\n// unknown at every privilege level.\n//\n// Wired via REFERENCE_INTEGRITY_RULES (it resolves a field NAME written in\n// metadata against what the stack declares — the suite's exact membership\n// test), so `os validate`, `os lint` and `os compile` report it at once. The\n// readonly rule next door is still hand-wired into two of those three; this one\n// does not repeat that.\n\nimport { findClosestMatches, formatSuggestion } from '@objectstack/spec/shared';\n\nimport { indexObjectFields, judgeableFieldsOf, IMPLICIT_FIELDS } from './validate-hook-body-writes.js';\nimport { walkFlowNodes, flowNodeLabel } from './flow-walk.js';\n\nexport type FlowNodeWriteSeverity = 'error';\n\nexport interface FlowNodeWriteFinding {\n /** Always `error` — a literal key against a literal object is a certainty (see module note). */\n severity: FlowNodeWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"close_deal\" › node \"Mark won\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[3].config.fields.stagee`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const FLOW_NODE_WRITE_UNKNOWN_FIELD = 'flow-node-write-unknown-field';\n\n// ─── The covered-node ledger ────────────────────────────────────────────────\n//\n// Which flow node types have their `config.fields` write map resolved against\n// the target object, declared as data — and, next to it, which `fields`-bearing\n// node type deliberately does not yet, with its reason. Both halves are\n// partition-tested against the CRUD schemas in\n// `@objectstack/spec/automation/builtin-node-config`, so a node type that grows\n// a write map later cannot land on the uncovered side by nobody noticing.\n\n/** Flow node types whose `config.fields` keys this rule resolves. */\nexport const FLOW_WRITE_NODE_TYPES: readonly string[] = ['update_record', 'create_record'];\n\n/** A `fields`-bearing node type this rule does NOT cover yet, and why. */\nexport interface FlowWriteNodeDeferral {\n /** The `FlowNode.type` left uncovered. */\n readonly type: string;\n /** Why it is not covered, in terms a reviewer can act on. */\n readonly reason: string;\n}\n\n/**\n * `fields`-bearing CRUD node types deliberately left uncovered.\n *\n * **Empty, and that is the point.** #4369 shipped `update_record` alone and\n * parked `create_record` here with its reason — a gating rule earning its\n * severity one measured surface at a time — rather than leaving the other half\n * as silence. #4371 measured the INSERT path (`table deal has no column named\n * stagee`, and the row never created at all), found it strictly worse than the\n * UPDATE one, and moved it across.\n *\n * The slot stays because the partition test derives the full `fields`-write-map\n * set from the spec's own config schemas: a node type that grows one later\n * belongs to neither list and fails that test until someone puts it in one.\n * Deleting this array would turn that forced decision back into a default.\n */\nexport const FLOW_WRITE_NODE_TYPES_DEFERRED: readonly FlowWriteNodeDeferral[] = [];\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => isRec(x));\n if (isRec(v)) {\n return Object.entries(v).map(([name, def]) => ({\n name,\n ...(isRec(def) ? def : {}),\n }));\n }\n return [];\n}\n\n/**\n * The target object of a CRUD node, when statically knowable. Reads the\n * canonical `objectName` and its historical `object` alias — a pre-parse source\n * may still carry the alias until the 'flow-node-crud-object-alias' conversion\n * (#3796) canonicalizes it at load. A templated value (contains `{`) is\n * resolved from flow variables at run time, so it is skipped rather than\n * guessed. Same read as `validate-readonly-flow-writes.ts`, which walks the\n * same nodes for the other question.\n */\nfunction readLiteralObjectName(config: AnyRec): string | undefined {\n const raw = config.objectName ?? config.object;\n if (typeof raw !== 'string' || raw.includes('{')) return undefined;\n return raw || undefined;\n}\n\nconst COVERED_TYPES: ReadonlySet<string> = new Set(FLOW_WRITE_NODE_TYPES);\n\n/**\n * Validate flow write-node `fields` keys against the target object's declared\n * fields. Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse\n * stacks.\n */\nexport function validateFlowNodeWrites(stack: AnyRec): FlowNodeWriteFinding[] {\n const findings: FlowNodeWriteFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n // Built lazily: a stack whose flows carry no write node never pays it.\n let objectFields: Map<string, Set<string>> | null = null;\n\n flows.forEach((flow, flowIndex) => {\n const flowName = typeof flow.name === 'string' && flow.name ? flow.name : `#${flowIndex}`;\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions\n // — a gating rule that stops at the top level simply stops gating the\n // moment an author wraps the write in error handling (#4380).\n const walked = walkFlowNodes(flow, `flows[${flowIndex}]`);\n\n walked.forEach(({ node, path: nodePath, regionTrail }, walkIndex) => {\n if (typeof node.type !== 'string' || !COVERED_TYPES.has(node.type)) return;\n\n const config = isRec(node.config) ? node.config : undefined;\n if (!config) return;\n\n // A non-literal write map (templated string, spread result, array) is not\n // statically knowable — skip rather than guess.\n const fields = config.fields;\n if (!isRec(fields)) return;\n const written = Object.keys(fields);\n if (written.length === 0) return;\n\n const objectName = readLiteralObjectName(config);\n if (!objectName) return; // templated / dynamic object — resolved at run time\n\n objectFields ??= indexObjectFields(stack);\n // Cross-package objects and objects declaring no fields at all (external /\n // datasource-introspected schemas) are both unjudgeable, and this rule\n // gates — see {@link judgeableFieldsOf}, which is where that guard now\n // lives for the whole family rather than once per rule (#4383).\n const known = judgeableFieldsOf(objectFields, objectName);\n if (!known) return;\n\n const nodeName = flowNodeLabel(node, walkIndex);\n // A nested node names the region that holds it, or \"node X\" is ambiguous\n // in a flow where the same label appears in a try and a catch branch.\n const nodeWhere = regionTrail ? `${regionTrail} › node \"${nodeName}\"` : `node \"${nodeName}\"`;\n\n for (const fieldName of written) {\n if (known.has(fieldName) || IMPLICIT_FIELDS.has(fieldName)) continue;\n // A dotted key addresses a nested path, not a top-level column — the\n // document drivers forward it verbatim. Not statically a missing field.\n if (fieldName.includes('.')) continue;\n\n findings.push({\n severity: 'error',\n rule: FLOW_NODE_WRITE_UNKNOWN_FIELD,\n where: `flow \"${flowName}\" › ${nodeWhere}`,\n path: `${nodePath}.config.fields.${fieldName}`,\n message:\n `${node.type} writes '${fieldName}', but object '${objectName}' declares no such field. Nothing ` +\n `between the node and storage removes the key: on a SQL datasource the driver rejects the whole ` +\n `statement ('no such column'), so the correctly named fields in this same payload never land ` +\n `either${\n node.type === 'create_record' ? ' and the record is never created at all' : ''\n }; on a schemaless one the stray key is persisted into a column no read surface returns.`,\n hint: fixHint(fieldName, [...known]),\n });\n }\n });\n });\n\n return findings;\n}\n\n/** Did-you-mean (declared + system columns as candidates) plus the fix. */\nfunction fixHint(field: string, declared: string[]): string {\n const suggestion = formatSuggestion(findClosestMatches(field, [...declared, ...IMPLICIT_FIELDS]));\n return (\n (suggestion ? `${suggestion} ` : '') +\n `Fix the field name, or declare '${field}' on the object. This gates the build rather than warning: ` +\n `the key is literal and so is the object, so unlike the hook/action body rules there is nothing here ` +\n `that could have been mis-extracted.`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The reference-integrity suite — one entry point for the rules that answer\n * \"does this name resolve to anything?\" (issue #3583, assessment §5 D5).\n *\n * ## Why this exists\n *\n * These rules were wired **by hand** into each CLI entry point that runs them.\n * `os validate`, `os lint` and `os compile` each grew their own import list and\n * their own call site, so landing a rule meant remembering three places — and\n * the assessment's §2.2 already named the resulting drift as the enemy: the\n * same stack, checked by a different rule subset depending on which command\n * the author happened to run.\n *\n * The suite makes the next rule's wiring a ONE-LINE edit here, and makes the\n * question \"which rules run on this path?\" answerable by reading one list.\n *\n * ## What belongs in it\n *\n * A rule belongs here when it resolves a NAME written in metadata against the\n * things a stack actually declares — objects, actions, fields, measures,\n * permissions, translation keys. That is the family the HotCRM audit found\n * shipping broken: every instance parsed, validated, and failed silently at\n * runtime because nothing checked that the name pointed at anything.\n *\n * `validateFlowTemplatePaths` is a member for exactly that reason: a\n * `{record.<field>}` token is a field name written in metadata, resolved\n * against the bound object's declared fields. It was wired by hand into\n * `os validate` alone — the drift this suite exists to end — so `os lint` and\n * `os compile` accepted a flow the runtime refuses. Its findings carry BOTH\n * severities (see that module: a filter-position miss gates, every other\n * position advises), which is why the suite's contract is severity-agnostic.\n *\n * `validateSearchableFields` is a member on the same reading, one layer in: an\n * ADR-0061 `searchableFields` entry is a field name written in metadata,\n * resolved against the object's own declared fields. It gates (`error`) because\n * the engine's tolerance for a stale entry — silently filtering it out — either\n * narrows the searched set below what the object declares or, once every entry\n * is stale, falls through to the auto-default and searches a set the author\n * never wrote. See that module for why the other field-existence rules stay\n * advisory and this one does not.\n *\n * Rules that check SHAPE rather than reference (view containers, responsive\n * styles, seed replay safety, seed state machines, seed/security posture) stay\n * out — they answer a different question and have their own call sites.\n *\n * ## Known remaining asymmetry\n *\n * `os doctor` runs only `validateWidgetBindings` and is NOT converted here: it\n * is an environment health check (node version, config presence, circular\n * lookups), not an authoring gate, so adopting the suite there is a product\n * decision about what `doctor` is for — not a wiring cleanup. It is named here\n * so the gap stays visible instead of being rediscovered.\n */\n\nimport { validateObjectReferences } from './validate-object-references.js';\nimport { validateSearchableFields } from './validate-searchable-fields.js';\nimport { validateActionNameRefs } from './validate-action-name-refs.js';\nimport { validatePageFieldBindings } from './validate-page-field-bindings.js';\nimport { validateChartBindings } from './validate-chart-bindings.js';\nimport { validateNavAccess } from './validate-nav-access.js';\nimport { validateTranslationReferences } from './validate-translation-references.js';\nimport { validateFlowTemplatePaths } from './validate-flow-template-paths.js';\nimport { validateAiSurfaceAffinity } from './validate-ai-surface-affinity.js';\nimport { validateAiToolReferences } from './validate-ai-tool-references.js';\nimport { validateAiAgentAuthoring } from './validate-ai-agent-authoring.js';\nimport { validateHookBodyWrites } from './validate-hook-body-writes.js';\nimport { validateActionBodyWrites } from './validate-action-body-writes.js';\nimport { validateFlowNodeWrites } from './validate-flow-node-writes.js';\nimport { validateReadonlyFlowWrites } from './validate-readonly-flow-writes.js';\nimport { validateReactPageProps } from './validate-react-page-props.js';\n\nexport type ReferenceIntegritySeverity = 'error' | 'warning';\n\n/**\n * The shape every rule in the suite already returns. Declared here so callers\n * can hold one type instead of a six-way union.\n */\nexport interface ReferenceIntegrityFinding {\n /** `error` = the reference is dead; `warning` = it may resolve elsewhere, or the miss is inert. */\n severity: ReferenceIntegritySeverity;\n /** Diagnostic rule id (stable; used by allowlists and docs). */\n rule: string;\n /** Human-readable location. */\n where: string;\n /** Config path. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\n/** One member of the suite. `name` is the exported function's name — the id a wiring test can assert on. */\nexport interface ReferenceIntegrityRule {\n name: string;\n run: (stack: Record<string, unknown>) => ReferenceIntegrityFinding[];\n}\n\n/**\n * Every reference-integrity rule, in the order their findings are reported.\n *\n * ADDING A RULE: append it here and it runs on `validate`, `lint` and\n * `compile` at once. Do not re-wire the commands.\n */\nexport const REFERENCE_INTEGRITY_RULES: readonly ReferenceIntegrityRule[] = [\n { name: 'validateObjectReferences', run: validateObjectReferences },\n { name: 'validateSearchableFields', run: validateSearchableFields },\n { name: 'validateActionNameRefs', run: validateActionNameRefs },\n { name: 'validatePageFieldBindings', run: validatePageFieldBindings },\n { name: 'validateChartBindings', run: validateChartBindings },\n { name: 'validateNavAccess', run: validateNavAccess },\n { name: 'validateTranslationReferences', run: validateTranslationReferences },\n { name: 'validateFlowTemplatePaths', run: validateFlowTemplatePaths },\n { name: 'validateAiSurfaceAffinity', run: validateAiSurfaceAffinity },\n { name: 'validateAiToolReferences', run: validateAiToolReferences },\n { name: 'validateAiAgentAuthoring', run: validateAiAgentAuthoring },\n // Field names WRITTEN by an L2 hook body (`ctx.input.x = …`,\n // `ctx.api.object('y').update({ x })`), resolved against the target object's\n // declared fields — the write-side counterpart of validateFlowTemplatePaths'\n // read-side membership (#4271). Lazy: only a hook that actually carries a\n // `language:'js'` body loads the TypeScript parser.\n { name: 'validateHookBodyWrites', run: validateHookBodyWrites },\n // The same check on the other surface that carries a `HookBodySchema` body:\n // action bodies, run by the same sandbox. Only the `ctx.api` write family\n // carries over — an action's `ctx.input` is its params bag, not a record\n // (see that module's ledger). Lazy on the same terms.\n //\n // The first member here to emit more than one rule id (`validateReactPageProps`\n // below is the other, and carries the most). Besides resolving `ctx.api`\n // writes against declared fields (`action-body-write-unknown-field`), it\n // reports a `ctx.record` write that can reach nothing\n // (`action-record-write-discarded`, #4345) — not a resolution question, so\n // by the charter above it does not belong in the suite. It rides along\n // anyway because it falls out of the SAME parse of the SAME source: a\n // separate member would parse every action body twice to say two things\n // about one walk, and hand-wiring it into the CLI instead is exactly the\n // drift this suite exists to end — which `validateReadonlyFlowWrites` was\n // the standing proof of, until it joined the suite below.\n { name: 'validateActionBodyWrites', run: validateActionBodyWrites },\n // The third surface that writes a record field set: a flow `update_record`\n // node's `config.fields`. Same question as the two rules above, but the map\n // is structural metadata rather than parsed JS, so a finding is a certainty\n // and gates (`error`) — see that module for why, and why the docs' long-\n // standing \"prefer a flow node, it's checked\" advice was the least true of\n // the three until it landed.\n { name: 'validateFlowNodeWrites', run: validateFlowNodeWrites },\n // The OTHER question about that same `config.fields` map: not \"does this\n // field exist?\" but \"is it writable?\" — a `runAs:'user'` update_record\n // writing a static-`readonly` field is stripped by the engine and the step\n // still reports success (#2948/#3425). It walks the identical map the rule\n // above walks, so the two splitting call sites was never defensible: hand-\n // wired into `validate` and `compile` only, it left `os lint` PASSING a flow\n // `os validate` refuses — and this one gates, so the divergence shipped a\n // build the other command would have stopped. Joining the suite is the whole\n // fix; the two hand-wired call sites are deleted with it (#4345 follow-up).\n { name: 'validateReadonlyFlowWrites', run: validateReadonlyFlowWrites },\n // The `kind:'react'` page surface. Every prop a react block binds BY FIELD\n // NAME is resolved against the object it names (#4340) — `<ListView columns>`,\n // `<ObjectForm fields>`, the `record:*` family through the SAME\n // `COMPONENT_FIELD_SPECS` table `validatePageFieldBindings` walks one surface\n // over, plus `<ObjectChart>`'s aggregate/axes (#3701/#3729) and\n // `searchableFields` (#4329). Squarely the charter's question, on the surface\n // where it had no answer at all.\n //\n // It was hand-wired into `os validate` ALONE, so `os lint` and `os compile`\n // accepted a react page whose every field binding was stale — including the\n // gating ones (a missing required binding, a filter position naming no field:\n // the predicate can never match and the list comes back empty). That is\n // `validateReadonlyFlowWrites`' divergence again, one surface over, and it is\n // the reason this entry exists rather than a fourth hand-wiring.\n //\n // Like `validateActionBodyWrites` above, it emits ids that are not resolution\n // questions — `react-prop-missing-required` and `react-prop-typo` are shape,\n // and by the charter belong outside. They ride along for the same reason: they\n // fall out of the SAME TypeScript parse of the SAME page source, and splitting\n // them into a second member would parse every react page twice to say two\n // things about one walk. Lazy on the same terms as the hook/action body rules\n // — only a page that is actually `kind:'react'` loads the compiler.\n { name: 'validateReactPageProps', run: validateReactPageProps },\n];\n\n/**\n * Run every reference-integrity rule over a stack. Returns the concatenated\n * findings (empty = clean). Pure: no I/O, safe on both the schema-parsed stack\n * and the raw/normalized config the `lint` path carries.\n */\nexport function validateReferenceIntegrity(stack: Record<string, unknown>): ReferenceIntegrityFinding[] {\n const findings: ReferenceIntegrityFinding[] = [];\n for (const rule of REFERENCE_INTEGRITY_RULES) {\n findings.push(...rule.run(stack));\n }\n return findings;\n}\n"],"mappings":"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","names":["label","asArray","asArray","label","asArray","isRecordTriggered","asArray","asArray","label","asArray","asArray","asArray","createRequire","asArray","isRec","strName","suggest","label","isRec","strName","list","asArray","strName","isRec","createRequire","asArray","isRec","asArray","asArray","asArray","asArray","asArray","asArray","asArray","asArray","label","asArray","asArray","strName","label","asArray","asArray","strName","suggest","distance","checkActionParams","asArray","strName","distance","suggest","label","list","asArray","strName","strList","isRec","distance","suggest","list","selected","isPlatformProvidedObjectName","asArray","label","asArray","strName","isPlatformProvidedObjectName","hasPlatformObjectPrefix","isPlatformProvidedObjectName","isRec","asArray","strName","distance","suggest","label","isPlatformProvidedObjectName","hasPlatformObjectPrefix","asArray","strName","asArray","strName","distance","suggest","asArray","strName","createRequire","cachedTs","loadTypeScript","createRequire","IMPLICIT_FIELDS","isRec","asArray","indexObjectFields","findClosestMatches","formatSuggestion","isRec","asArray","indexObjectFields","IMPLICIT_FIELDS","fixHint","formatSuggestion","findClosestMatches","findClosestMatches","formatSuggestion","isRec","asArray","readLiteralObjectName","indexObjectFields","IMPLICIT_FIELDS","fixHint","formatSuggestion","findClosestMatches"]}
1
+ {"version":3,"sources":["../src/validate-widget-bindings.ts","../src/system-fields.ts","../src/validate-expressions.ts","../src/validate-null-guards.ts","../src/validate-list-view-mode.ts","../src/validate-functional-completeness.ts","../src/validate-flow-trigger-readiness.ts","../src/flow-walk.ts","../src/validate-flow-template-paths.ts","../src/validate-readonly-flow-writes.ts","../src/validate-view-containers.ts","../src/validate-responsive-styles.ts","../src/validate-jsx-pages.ts","../src/validate-react-pages.ts","../src/validate-react-page-props.ts","../src/validate-searchable-fields.ts","../src/page-walk.ts","../src/validate-page-field-bindings.ts","../src/validate-page-source-styling.ts","../src/validate-record-title.ts","../src/validate-semantic-roles.ts","../src/validate-form-layout.ts","../src/validate-visibility-predicates.ts","../src/validate-capability-references.ts","../src/validate-approval-approvers.ts","../src/validate-seed-replay-safety.ts","../src/validate-seed-state-machine.ts","../src/validate-security-posture.ts","../src/validate-org-axis-red-lines.ts","../src/validate-dashboard-action-refs.ts","../src/validate-filter-tokens.ts","../src/validate-object-references.ts","../src/validate-nav-target-refs.ts","../src/validate-action-name-refs.ts","../src/validate-action-locations.ts","../src/validate-chart-bindings.ts","../src/validate-nav-access.ts","../src/build-access-matrix.ts","../src/validate-translation-references.ts","../src/validate-ai-surface-affinity.ts","../src/validate-ai-tool-references.ts","../src/validate-ai-agent-authoring.ts","../src/validate-hook-body-writes.ts","../src/validate-action-body-writes.ts","../src/validate-flow-node-writes.ts","../src/reference-integrity-suite.ts","../src/lint-flow-patterns.ts","../src/lint-liveness-properties.ts","../src/lint-autonumber-formats.ts","../src/lint-view-refs.ts","../src/data-model-rules.ts","../src/authoring-rules.ts","../src/runtime-gate.ts"],"sourcesContent":["// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { isIncoherentAggregate } from '@objectstack/spec/data';\nimport { ChartTypeSchema } from '@objectstack/spec/ui';\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\n/**\n * Build-time dashboard widget binding diagnostics (issues #1719, #1721).\n *\n * Runs at `objectstack validate`/`compile`/`build` AFTER the stack has been\n * schema-parsed, so every widget's `dataset` reference can be linked to its\n * `defineDataset` and each entry in `dimensions`/`values` resolved to a\n * declared dimension/measure. This is the semantic/cross-reference phase —\n * the rules here cannot run during plain Zod parsing of the raw widget\n * literal (the dataset may even live in another package of the stack).\n *\n * Reference-integrity rules (#1721) — severity `error`, the page is broken:\n *\n * - `widget-dataset-unknown` — `dataset` does not resolve to a declared\n * `Dataset`.\n * - `widget-dimension-unknown` — a `dimensions[]` entry is not a dimension\n * name on the bound dataset.\n * - `widget-measure-unknown` — a `values[]` entry is not a measure name on\n * the bound dataset.\n * - `chart-field-unknown` — a `chartConfig` binding names a field the query\n * result will not contain: `xAxis.field` must be one of the widget's\n * dimensions (or a dataset dimension), and each `yAxis[].field` /\n * `series[].name` must be one of the widget's selected measures\n * (`values`). Post-cutover (ADR-0021) the result rows are keyed by\n * measure NAME (e.g. `sum_amount`), not the base column (`amount`) — a\n * stale base-column reference renders the axis but an empty series.\n * - `widget-legacy-analytics-unrenderable` (#1878/#1894) — a widget uses the\n * removed pre-ADR-0021 inline-analytics shape (`categoryField`/`rowField`/…)\n * as its ONLY data wiring: no `dataset`, no `object`, no inline `data`. The\n * renderer reads only the dataset path, so the widget has no data at all and\n * renders nothing. Errored (not warned) so this class of authoring mistake —\n * very often an AI emitting a removed shape — fails the build instead of\n * shipping a blank widget past human review.\n * - `dashboard-filter-field-unknown` (#3365) — a dashboard-level filter\n * (`dateRange` or a `globalFilters[]` entry) is wired into EVERY widget's\n * analytics query (#2501), but its EFFECTIVE field (after any `filterBindings`\n * re-target) does not exist on a bound widget's dataset object. The widget's\n * query then references a non-existent column and crashes at render time\n * (`no such column …`) — a build-decidable invariant that previously escaped\n * the static gate and failed only when a user opened the dashboard. A widget\n * opts out with `filterBindings: { <name>: false }` or re-targets to a real\n * field. This is the same field-existence invariant ADR-0032 enforces for\n * CEL formula / sharing-rule references, applied to dashboard filter fields.\n *\n * Advisory rules — severity `warning`, build stays green:\n *\n * - `chart-config-missing` — a chart-type widget (bar/line/pie/…) has no\n * `chartConfig`, so the renderer cannot tell which measure to plot.\n * - `table-count-only` (#1719) — a `table`/`pivot` widget whose selected\n * measures are ALL `aggregate: 'count'` and which declares no\n * `dimensions` asks the analytics service for a single summary row. That\n * is the shape a `metric` widget wants — for a table it almost always\n * means the author wanted a per-record listing, which is not an\n * analytics dataset at all (model it as an object-bound ListView,\n * ADR-0017). Evaluated on the WIDGET's binding, not the dataset.\n * - `measure-aggregate-incoherent` — a dataset measure aggregates its field\n * in a way that produces a meaningless number: today, SUM (or\n * `count_distinct`) of a `percent`/rate field, whose total routinely\n * exceeds 100%. Rates must AVG. Checked once per dataset (independent of\n * any widget) when the bound object's field types are known.\n * - `widget-legacy-analytics-shape` (#1878/#1894) — a widget sets a\n * pre-ADR-0021 inline key (`categoryField`/`valueField`/`xAxisField`/\n * `yAxisFields`/`aggregate`/`aggregation`/`rowField`/`columnField`) that the\n * single-form cutover removed. The dashboard renderer routes dataset-bound\n * widgets through `DatasetWidget` and never reads these, so they are a\n * silent no-op. Steers the author onto `dataset`+`dimensions`+`values`.\n *\n * Warnings can be deliberately suppressed per widget via\n * `suppressWarnings: ['<rule-id>']`; errors cannot — they describe a\n * binding the analytics service cannot satisfy.\n */\n\nexport const WIDGET_DATASET_UNKNOWN = 'widget-dataset-unknown';\nexport const WIDGET_DIMENSION_UNKNOWN = 'widget-dimension-unknown';\nexport const WIDGET_MEASURE_UNKNOWN = 'widget-measure-unknown';\nexport const CHART_FIELD_UNKNOWN = 'chart-field-unknown';\nexport const CHART_CONFIG_MISSING = 'chart-config-missing';\nexport const TABLE_COUNT_ONLY = 'table-count-only';\nexport const MEASURE_AGGREGATE_INCOHERENT = 'measure-aggregate-incoherent';\nexport const WIDGET_LEGACY_ANALYTICS_SHAPE = 'widget-legacy-analytics-shape';\nexport const WIDGET_LEGACY_ANALYTICS_UNRENDERABLE = 'widget-legacy-analytics-unrenderable';\nexport const DASHBOARD_FILTER_FIELD_UNKNOWN = 'dashboard-filter-field-unknown';\n\n/**\n * Pre-ADR-0021 inline-analytics keys. The single-form cutover replaced them\n * with the semantic-layer shape (`dataset` + `dimensions` + `values`); the\n * dashboard renderer routes dataset-bound widgets through `DatasetWidget` and\n * never reads these, so authoring one today is a silent no-op. Warned (not\n * errored) because they still parse and a legacy object-bound widget keeps\n * rendering — the author is just being steered to the governed shape.\n * (liveness audit #1878 / #1894).\n *\n * Interplay with `DashboardWidgetSchema.strict()` (framework#3251, protocol 16):\n * on the schema-parsed CLI paths (`compile`, `validate`) strict rejects these\n * keys as a hard parse error *before* binding validation runs, so these rules\n * are effectively preempted there. They remain the friendly, suppressible\n * bridge on the raw-config paths (`lint`, `doctor`) that hand\n * `validateWidgetBindings` un-parsed config — keeping the actionable\n * \"steer to the dataset shape\" message rather than a bare unknown-key error.\n */\nconst LEGACY_ANALYTICS_KEYS = [\n 'categoryField', 'valueField', 'xAxisField', 'yAxisFields',\n 'aggregate', 'aggregation', 'rowField', 'columnField',\n] as const;\n\nexport type WidgetBindingSeverity = 'error' | 'warning';\n\nexport interface WidgetBindingFinding {\n /** `error` = unresolvable binding (broken page); `warning` = advisory. */\n severity: WidgetBindingSeverity;\n /** Diagnostic rule id (registry entry), e.g. `widget-measure-unknown`. */\n rule: string;\n /** Human-readable location, e.g. `dashboard \"x\" › widget \"y\"`. */\n where: string;\n /** Config path, e.g. `dashboards[0].widgets[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix (or deliberately suppress) it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction asStrings(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((s): s is string => typeof s === 'string') : [];\n}\n\n/**\n * Chart families that plot a single value or every column, and so need no\n * `chartConfig` measure mapping: single-value types plot their lone value,\n * tabular types render each column as-is.\n */\nconst MEASURE_EXEMPT_CHART_TYPES = new Set([\n 'gauge', 'solid-gauge', 'metric', 'kpi', 'bullet',\n 'table', 'pivot',\n]);\n\n/**\n * Chart families whose renderer needs a `chartConfig` measure mapping — the\n * taxonomy minus the exemptions above.\n *\n * Derived from `ChartTypeSchema` rather than restated. As a hand-written list it\n * had no way to know when the taxonomy grew, and the omission is silent in\n * exactly the wrong direction: an unlisted family is treated as \"not a chart\",\n * so a widget missing its measure mapping passes validation instead of being\n * reported. objectui#2945.\n */\nconst CHART_TYPES = new Set<string>(\n ChartTypeSchema.options.filter(t => !MEASURE_EXEMPT_CHART_TYPES.has(t)),\n);\n\nfunction levenshtein(a: string, b: string): number {\n const m = a.length, n = b.length;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const cur = [i];\n for (let j = 1; j <= n; j++) {\n cur[j] = Math.min(\n prev[j] + 1,\n cur[j - 1] + 1,\n prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1),\n );\n }\n prev = cur;\n }\n return prev[n];\n}\n\n/**\n * Nearest declared name for a typo'd/stale reference, or undefined when\n * nothing is close. Containment is checked first because the cutover's\n * canonical drift is base column → prefixed measure name (`amount` →\n * `sum_amount`), which is far in edit distance but obvious to a human.\n */\nfunction didYouMean(input: string, candidates: Iterable<string>): string | undefined {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const c of candidates) {\n let score: number;\n if (input.length >= 3 && (c.includes(input) || input.includes(c))) {\n score = Math.abs(c.length - input.length);\n } else {\n const d = levenshtein(input, c);\n if (d > Math.max(2, Math.floor(input.length / 3))) continue;\n score = 100 + d;\n }\n if (score < bestScore) { bestScore = score; best = c; }\n }\n return best;\n}\n\nfunction suggest(input: string, candidates: Iterable<string>): string {\n const s = didYouMean(input, candidates);\n return s ? ` Did you mean \"${s}\"?` : '';\n}\n\nfunction list(names: Iterable<string>): string {\n const arr = [...names];\n return arr.length > 0 ? arr.join(', ') : '(none)';\n}\n\n// ── dashboard-filter field-existence (#3365) ─────────────────────────────────\n\n/** Reserved filter name for the dashboard's built-in date range (#2501). */\nconst DATE_RANGE_FILTER_NAME = 'dateRange';\n/**\n * Default field of the built-in date range when `dateRange.field` is omitted.\n * MUST track objectui `dashboard-filters.ts` `DATE_RANGE_DEFAULT_FIELD` — the\n * runtime this check shadows. `created_at` is a registry-injected system field\n * (the package-shared `SYSTEM_FIELDS`, `system-fields.ts`), so a bare\n * `dateRange` never false-positives.\n */\nconst DATE_RANGE_DEFAULT_FIELD = 'created_at';\n\ninterface DashFilterDef {\n /** Stable filter name — the key widgets bind against in `filterBindings`. */\n name: string;\n /** Default target field when a widget declares no explicit binding. */\n field: string;\n /** Legacy widget-id allow-list; gates the DEFAULT binding only. */\n targetWidgets?: string[];\n}\n\n/**\n * Normalize a dashboard's declared filters into `{ name, field, targetWidgets }`\n * defs — the built-in `dateRange` (reserved name) first, then every\n * `globalFilters[]` entry named by its `name` (defaulting to `field`). Later\n * duplicates win. Mirrors objectui `resolveDashboardFilterDefs`.\n */\nfunction dashboardFilterDefs(dash: AnyRec): DashFilterDef[] {\n const byName = new Map<string, DashFilterDef>();\n\n const dateRange = dash.dateRange;\n if (dateRange && typeof dateRange === 'object') {\n const declared = (dateRange as AnyRec).field;\n const field = typeof declared === 'string' && declared ? declared : DATE_RANGE_DEFAULT_FIELD;\n byName.set(DATE_RANGE_FILTER_NAME, { name: DATE_RANGE_FILTER_NAME, field });\n }\n\n for (const f of asArray(dash.globalFilters)) {\n if (typeof f.field !== 'string' || !f.field) continue;\n const name = typeof f.name === 'string' && f.name ? f.name : f.field;\n const targetWidgets = Array.isArray(f.targetWidgets)\n ? f.targetWidgets.filter((w): w is string => typeof w === 'string')\n : undefined;\n byName.set(name, { name, field: f.field, targetWidgets });\n }\n\n return [...byName.values()];\n}\n\n/**\n * Resolve which field of `widget` a filter binds to, or `undefined` when the\n * widget is not bound (opted out / not targeted). Precedence mirrors objectui\n * `resolveBoundField`: explicit `filterBindings` entry (string re-targets,\n * `false` opts out — both win) → legacy `targetWidgets` allow-list → the\n * filter's own default `field`. `explicit` distinguishes an author-chosen field\n * (a typo they must fix) from the inherited default (which they may opt out of).\n */\nfunction effectiveFilterField(\n widget: AnyRec,\n def: DashFilterDef,\n): { field: string; explicit: boolean } | undefined {\n const bindings = widget.filterBindings;\n const binding = bindings && typeof bindings === 'object'\n ? (bindings as AnyRec)[def.name]\n : undefined;\n if (binding === false) return undefined;\n if (typeof binding === 'string' && binding) return { field: binding, explicit: true };\n if (def.targetWidgets && def.targetWidgets.length > 0) {\n const id = typeof widget.id === 'string' ? widget.id : undefined;\n if (!id || !def.targetWidgets.includes(id)) return undefined;\n }\n return { field: def.field, explicit: false };\n}\n\n/**\n * Validate every dashboard widget's dataset binding. Returns the list of\n * findings (empty = clean). Caller decides how to surface them: `error`\n * findings describe bindings the analytics service cannot satisfy and\n * should fail validate/build; `warning` findings are advisory and must\n * never fail the build on their own.\n */\nexport function validateWidgetBindings(stack: AnyRec): WidgetBindingFinding[] {\n const findings: WidgetBindingFinding[] = [];\n\n const datasets = new Map<string, AnyRec>();\n for (const ds of asArray(stack.datasets)) {\n if (typeof ds.name === 'string') datasets.set(ds.name, ds);\n }\n\n // ── (0) dataset measures aggregate their field coherently ──\n // A measure that SUMs a percentage/rate field produces a meaningless total\n // (it can exceed 100%); rates must AVG. This is a dataset-level defect (it\n // does not depend on any widget), so it is checked once over every dataset\n // whose object's field types are known. Advisory — the page still renders.\n const objectFieldTypes = new Map<string, Map<string, string>>();\n for (const o of asArray(stack.objects)) {\n if (typeof o.name !== 'string') continue;\n const fm = new Map<string, string>();\n for (const f of asArray(o.fields)) {\n if (typeof f.name === 'string' && typeof f.type === 'string') fm.set(f.name, f.type);\n }\n objectFieldTypes.set(o.name, fm);\n }\n const datasetList = asArray(stack.datasets);\n for (let i = 0; i < datasetList.length; i++) {\n const ds = datasetList[i];\n const fieldTypes = typeof ds.object === 'string' ? objectFieldTypes.get(ds.object) : undefined;\n if (!fieldTypes) continue; // cannot judge without the object's field types\n const dsMeasures = asArray(ds.measures);\n for (let k = 0; k < dsMeasures.length; k++) {\n const m = dsMeasures[k];\n const field = typeof m.field === 'string' ? m.field : undefined;\n const aggregate = typeof m.aggregate === 'string' ? m.aggregate : undefined;\n if (!field || !aggregate) continue; // count(*) and underivable measures are fine\n const ftype = fieldTypes.get(field);\n if (ftype && isIncoherentAggregate(aggregate, ftype)) {\n findings.push({\n severity: 'warning',\n rule: MEASURE_AGGREGATE_INCOHERENT,\n where: `dataset \"${typeof ds.name === 'string' ? ds.name : `(dataset ${i})`}\" › measure \"${typeof m.name === 'string' ? m.name : `(measure ${k})`}\"`,\n path: `datasets[${i}].measures[${k}]`,\n message:\n `measure \"${m.name}\" applies ${aggregate} to ${ftype} field \"${field}\" — ` +\n `summed percentages are meaningless (they can exceed 100%).`,\n hint:\n `Use aggregate \"avg\" for percentage/rate fields (or \"count\" of records). ` +\n `If a running total is genuinely intended, suppress with: ` +\n `suppressWarnings: ['${MEASURE_AGGREGATE_INCOHERENT}'] on the measure.`,\n });\n }\n }\n }\n\n const dashboards = asArray(stack.dashboards);\n for (let i = 0; i < dashboards.length; i++) {\n const dash = dashboards[i];\n const dashName = typeof dash.name === 'string' ? dash.name : `(dashboard ${i})`;\n const widgets = Array.isArray(dash.widgets) ? (dash.widgets as AnyRec[]) : [];\n // Dashboard-level filters (`dateRange` + `globalFilters`) are broadcast into\n // every widget's query (#2501) — resolved once here, checked per widget below.\n const dashFilterDefs = dashboardFilterDefs(dash);\n\n for (let j = 0; j < widgets.length; j++) {\n const w = widgets[j];\n const widgetId = typeof w.id === 'string' ? w.id : `(widget ${j})`;\n const where = `dashboard \"${dashName}\" › widget \"${widgetId}\"`;\n const path = `dashboards[${i}].widgets[${j}]`;\n const suppressed = (rule: string): boolean =>\n Array.isArray(w.suppressWarnings) && w.suppressWarnings.includes(rule);\n const push = (f: Omit<WidgetBindingFinding, 'where' | 'path'>): void => {\n if (f.severity === 'warning' && suppressed(f.rule)) return;\n findings.push({ ...f, where, path });\n };\n\n // ── (a0) legacy pre-ADR-0021 analytics shape ──\n // Steer authors (very often an AI) off the removed inline shape and onto\n // the semantic-layer `dataset`+`dimensions`+`values`. The renderer reads\n // ONLY the dataset path, so these keys are dead. Two severities:\n // • ERROR — the legacy keys are the widget's only (dead) data wiring\n // (no dataset / object / inline data): it renders nothing.\n // • warning — a data source is present, so the widget still renders and\n // the legacy keys are merely ignored noise (suppressible).\n const legacyUsed = LEGACY_ANALYTICS_KEYS.filter((k) => w[k] !== undefined);\n if (legacyUsed.length > 0) {\n const optionsData =\n typeof w.options === 'object' && w.options !== null &&\n (w.options as AnyRec).data !== undefined;\n const hasDataSource =\n w.dataset !== undefined || w.object !== undefined ||\n w.data !== undefined || optionsData;\n const keyList = legacyUsed.map((k) => `\\`${k}\\``).join(', ');\n const plural = legacyUsed.length > 1;\n const datasetHint =\n `Bind a semantic dataset and select fields BY NAME: ` +\n `\\`dataset: '<name>', dimensions: [...], values: [...]\\`. ` +\n `Dataset-bound widgets render through DatasetWidget (pivot rows/cols come from ` +\n `\\`dimensions\\`, cell values from \\`values\\`).`;\n if (!hasDataSource) {\n push({\n severity: 'error',\n rule: WIDGET_LEGACY_ANALYTICS_UNRENDERABLE,\n message:\n `sets legacy analytics key${plural ? 's' : ''} ${keyList} ` +\n `(removed by the ADR-0021 single-form cutover) and binds no data source ` +\n `(no \\`dataset\\`, \\`object\\`, or inline \\`data\\`) — it renders nothing.`,\n hint:\n `${datasetHint} The renderer ignores the legacy keys, so without a data ` +\n `source this widget has no data at all.`,\n });\n } else {\n push({\n severity: 'warning',\n rule: WIDGET_LEGACY_ANALYTICS_SHAPE,\n message:\n `sets legacy analytics key${plural ? 's' : ''} ${keyList} that the ADR-0021 ` +\n `single-form cutover removed — the dashboard renderer ignores ${plural ? 'them' : 'it'}.`,\n hint:\n `${datasetHint} These inline keys are a no-op. ` +\n `Suppress with suppressWarnings: ['${WIDGET_LEGACY_ANALYTICS_SHAPE}'] if intentional.`,\n });\n }\n }\n\n // ── (a) dataset reference resolves ──\n const dsName = typeof w.dataset === 'string' ? w.dataset : undefined;\n const dataset = dsName ? datasets.get(dsName) : undefined;\n if (dsName && !dataset) {\n push({\n severity: 'error',\n rule: WIDGET_DATASET_UNKNOWN,\n message: `dataset \"${dsName}\" does not resolve to a declared dataset.`,\n hint:\n `Declared datasets: ${list(datasets.keys())}.${suggest(dsName, datasets.keys())} ` +\n `Define the dataset with defineDataset() or fix the reference (ADR-0021).`,\n });\n }\n // A widget with NO `dataset` key at all. `DashboardWidgetSchema.dataset`\n // is REQUIRED, so the schema-parsed paths (`compile`, `validate`) reject\n // this before we run — but `lint`/`doctor` hand us raw, un-parsed config,\n // where it previously fell into the `continue` below and silently\n // bypassed EVERY binding and chart check (issue #3583). Report it rather\n // than skip: an unbound widget resolves no data and renders empty.\n if (!dsName) {\n push({\n severity: 'error',\n rule: WIDGET_DATASET_UNKNOWN,\n message:\n `binds no \\`dataset\\` — the ADR-0021 widget shape requires one, so this ` +\n `widget resolves no data and renders empty.`,\n hint:\n `Set \\`dataset: '<name>'\\` (plus \\`values\\`, and \\`dimensions\\` where the chart ` +\n `family needs them). Declared datasets: ${list(datasets.keys())}.`,\n });\n continue;\n }\n // A named-but-unresolvable dataset was already reported above; either way\n // there is nothing left to check names against.\n if (!dataset) continue;\n\n // ── (a1) dashboard filter fields exist on the widget's object (#3365) ──\n // Each dashboard-level filter is ANDed into this widget's analytics query\n // (#2501); a filter whose EFFECTIVE field (after `filterBindings`) is not a\n // column on the bound dataset object emits SQL like `WHERE close_date …`\n // against a table without that column and the widget crashes at query time.\n // Errored (not warned): a broken query, not advice. The opt-out is the\n // author's own `filterBindings: { <name>: false }`, so no suppression needed.\n if (dashFilterDefs.length > 0) {\n const datasetObject = typeof dataset.object === 'string' ? dataset.object : undefined;\n // Only judge when the bound object's fields are known in THIS stack; an\n // object from another installed package is unknowable here — skip rather\n // than false-positive (mirrors the measure-aggregate check above).\n const objectFields = datasetObject ? objectFieldTypes.get(datasetObject) : undefined;\n if (objectFields) {\n for (const def of dashFilterDefs) {\n const eff = effectiveFilterField(w, def);\n if (!eff) continue; // opted out / not targeted → filter never applies\n const field = eff.field;\n // A relationship path (`account.region`) is resolved by the query\n // engine, not a base column, so it can't be checked here — skip it.\n if (field.includes('.')) continue;\n if (objectFields.has(field) || SYSTEM_FIELDS.has(field)) continue;\n push({\n severity: 'error',\n rule: DASHBOARD_FILTER_FIELD_UNKNOWN,\n message: eff.explicit\n ? `binds dashboard filter \\`${def.name}\\` to field \\`${field}\\` ` +\n `(via filterBindings), but object \\`${datasetObject}\\` (dataset \"${dsName}\") ` +\n `has no field \\`${field}\\`.`\n : `inherits dashboard filter \\`${def.name}(${field})\\`, but object ` +\n `\\`${datasetObject}\\` (dataset \"${dsName}\") has no field \\`${field}\\`.`,\n hint: eff.explicit\n ? `Point filterBindings: { ${def.name}: '<field>' } at a field that exists on ` +\n `\\`${datasetObject}\\`, or opt out with filterBindings: { ${def.name}: false }.` +\n `${suggest(field, objectFields.keys())} Object fields: ${list(objectFields.keys())}.`\n : `Set filterBindings: { ${def.name}: false } on this widget to opt out, or ` +\n `re-target to an existing field with filterBindings: { ${def.name}: '<field>' }.` +\n `${suggest(field, objectFields.keys())} Object fields: ${list(objectFields.keys())}.`,\n });\n }\n }\n }\n\n const dimensionNames = new Set<string>();\n for (const d of asArray(dataset.dimensions)) {\n if (typeof d.name === 'string') dimensionNames.add(d.name);\n }\n const measures = new Map<string, AnyRec>();\n for (const m of asArray(dataset.measures)) {\n if (typeof m.name === 'string') measures.set(m.name, m);\n }\n\n // ── (b) every dimensions[] entry is a dataset dimension ──\n const dims = asStrings(w.dimensions);\n for (let k = 0; k < dims.length; k++) {\n if (dimensionNames.has(dims[k])) continue;\n push({\n severity: 'error',\n rule: WIDGET_DIMENSION_UNKNOWN,\n message:\n `dimensions[${k}] \"${dims[k]}\" is not a dimension of dataset ` +\n `\"${dsName}\" (declared dimensions: ${list(dimensionNames)}).`,\n hint:\n `Widgets select dataset dimensions BY NAME.${suggest(dims[k], dimensionNames)} ` +\n `Add the dimension to the dataset or fix the reference.`,\n });\n }\n\n // ── (c) every values[] entry is a dataset measure ──\n const values = asStrings(w.values);\n for (let k = 0; k < values.length; k++) {\n if (measures.has(values[k])) continue;\n push({\n severity: 'error',\n rule: WIDGET_MEASURE_UNKNOWN,\n message:\n `values[${k}] \"${values[k]}\" is not a measure of dataset ` +\n `\"${dsName}\" (declared measures: ${list(measures.keys())}).`,\n hint:\n `Widgets select dataset measures BY NAME, not by base column.` +\n `${suggest(values[k], measures.keys())} ` +\n `Add the measure to the dataset or fix the reference.`,\n });\n }\n\n // ── (d) chartConfig bindings resolve against the widget's selection ──\n const chartConfig = (w.chartConfig && typeof w.chartConfig === 'object')\n ? (w.chartConfig as AnyRec)\n : undefined;\n const isChartType = typeof w.type === 'string' && CHART_TYPES.has(w.type);\n\n if (chartConfig) {\n // The query result carries the widget's selected dimensions and\n // measures; resolve every chartConfig field against that shape.\n const selectedValues = new Set(values.filter((v) => measures.has(v)));\n\n const xAxis = (chartConfig.xAxis && typeof chartConfig.xAxis === 'object')\n ? (chartConfig.xAxis as AnyRec)\n : undefined;\n // A field naming an entry of the widget's own (already-validated)\n // selection is not re-reported here — rules (b)/(c) own that error.\n if (xAxis && typeof xAxis.field === 'string'\n && !dimensionNames.has(xAxis.field) && !dims.includes(xAxis.field)) {\n push({\n severity: 'error',\n rule: CHART_FIELD_UNKNOWN,\n message:\n `chartConfig.xAxis.field \"${xAxis.field}\" does not resolve to a ` +\n `dimension of dataset \"${dsName}\" (declared dimensions: ${list(dimensionNames)}).`,\n hint: `Point xAxis.field at a dataset dimension name.${suggest(xAxis.field, dimensionNames)}`,\n });\n }\n\n const measureField = (label: string, field: string): void => {\n if (values.includes(field)) return; // resolvable, or already errored via rule (c)\n const declaredButUnselected = measures.has(field);\n push({\n severity: 'error',\n rule: CHART_FIELD_UNKNOWN,\n message: declaredButUnselected\n ? `chartConfig.${label} \"${field}\" is a measure of dataset \"${dsName}\" ` +\n `but is not selected in the widget's values (${list(values)}), so the ` +\n `query result will not contain it.`\n : `chartConfig.${label} \"${field}\" does not resolve to a measure of ` +\n `dataset \"${dsName}\" (declared measures: ${list(measures.keys())}).`,\n hint: declaredButUnselected\n ? `Add \"${field}\" to the widget's values, or bind the chart to a selected measure.`\n : `Post-cutover data is keyed by the dataset's measure NAME, not the ` +\n `base column.${suggest(field, selectedValues.size > 0 ? selectedValues : measures.keys())}`,\n });\n };\n\n const yAxes = Array.isArray(chartConfig.yAxis) ? (chartConfig.yAxis as AnyRec[]) : [];\n for (let k = 0; k < yAxes.length; k++) {\n const field = yAxes[k]?.field;\n if (typeof field === 'string') measureField(`yAxis[${k}].field`, field);\n }\n const series = Array.isArray(chartConfig.series) ? (chartConfig.series as AnyRec[]) : [];\n for (let k = 0; k < series.length; k++) {\n const name = series[k]?.name;\n if (typeof name === 'string') measureField(`series[${k}].name`, name);\n }\n } else if (isChartType) {\n push({\n severity: 'warning',\n rule: CHART_CONFIG_MISSING,\n message:\n `chart-type widget ('${w.type}') has no chartConfig — the renderer ` +\n `cannot determine which measure to plot, so the series renders empty.`,\n hint:\n `Add chartConfig with xAxis.field set to a dimension (${list(dims)}) and ` +\n `yAxis[].field set to a measure name (${list(values)}). If the default ` +\n `rendering is intentional, suppress with: suppressWarnings: ['${CHART_CONFIG_MISSING}']`,\n });\n }\n\n // ── (e) table/pivot bound to a count-only, dimensionless selection ──\n if (w.type !== 'table' && w.type !== 'pivot') continue;\n // Grouped by at least one dimension → genuinely aggregated rows.\n if (dims.length > 0) continue;\n if (values.length === 0) continue;\n const resolved = values.map((v) => measures.get(v));\n // An unresolvable measure name already errored above — don't guess here.\n if (resolved.some((m) => !m)) continue;\n\n // Derived measures combine other measures; treat them as non-count even\n // when their (ignored) `aggregate` says otherwise.\n const countOnly = resolved.every((m) => m!.aggregate === 'count' && !m!.derived);\n if (!countOnly) continue;\n\n push({\n severity: 'warning',\n rule: TABLE_COUNT_ONLY,\n message:\n `a '${w.type}' widget bound to dataset \"${dsName}\" selects only count ` +\n `measure(s) (${values.join(', ')}) and no dimensions, so it renders a ` +\n `single summary row — not a per-record list.`,\n hint:\n `A flat record listing is not an analytics dataset. Model it as an ` +\n `object-bound ListView (ADR-0017) surfaced through app navigation, and ` +\n `use a 'metric' widget here if you only need the count. If a single-row ` +\n `table is intentional, add an explicit dimension or suppress with: ` +\n `suppressWarnings: ['${TABLE_COUNT_ONLY}']`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The ONE answer to \"which columns does the registry inject on (almost) every\n * object without them appearing in authored `fields`?\" (#4330).\n *\n * Every field-resolving rule in this package needs that answer: a reference to\n * `created_at` or `owner_id` is authored against a real, addressable column\n * even though no object declares it, so flagging it would be the false finding\n * that makes authors stop trusting the linter (ADR-0072 D1). Before this\n * module, five rules each carried their own hand-copied list and had already\n * drifted from one another — the exact shape #3786 removed from the\n * audit-provenance family, rebuilt one package over.\n *\n * DERIVED from the spec's two declarations, so it cannot drift from them:\n *\n * - {@link FIELD_GROUP_SYSTEM_FIELDS} (`@objectstack/spec/data`) — audit\n * provenance plus `organization_id` / `tenant_id` / `is_deleted` /\n * `deleted_at`;\n * - {@link SystemFieldName} (`@objectstack/spec/system`) — the protocol-level\n * ids: `id`, `owner_id`, `user_id` and the timestamp/tenant columns.\n *\n * The union is deliberately generous, because the cost asymmetry is the same\n * in every consumer: over-inclusion costs at worst a missed finding on a\n * `systemFields: false` object (rare); under-inclusion costs a false one.\n *\n * What does NOT belong here: names that are ordinary AUTHORED fields on most\n * objects (`name`, `owner`, `record_type`) or legacy physical spellings\n * (`_id`, `space`). A rule that deliberately exempts those keeps them in a\n * rule-local extension next to its reason — adding them here would silently\n * stop every other rule from catching a reference to a field the object\n * genuinely does not have.\n */\n\nimport { FIELD_GROUP_SYSTEM_FIELDS } from '@objectstack/spec/data';\nimport { SystemFieldName } from '@objectstack/spec/system';\n\n/**\n * Registry-injected columns addressable at runtime without being authored in\n * `fields` — the union of the spec's two system-field declarations.\n */\nexport const SYSTEM_FIELDS: ReadonlySet<string> = new Set<string>([\n ...FIELD_GROUP_SYSTEM_FIELDS,\n ...Object.values(SystemFieldName),\n]);\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time expression validation (ADR-0032 §Decision 1a + 1b).\n *\n * Runs at `objectstack compile`, where the whole normalized stack is in hand —\n * so flow conditions can be checked against the *resolved* object schema\n * (field existence) in addition to CEL syntax. Uses the one shared validator\n * from `@objectstack/formula`, so the verdict matches `registerFlow` and the\n * agent `validate_expression` tool exactly.\n *\n * Scope: flow predicates (start/decision `config.condition` + edge `condition`),\n * every **descriptor-declared** expression slot named by\n * `FLOW_NODE_EXPRESSION_PATHS` (#4027 — e.g. a screen field's `visibleWhen`),\n * object validation-rule / formula predicates, and UI action `visible` /\n * `disabled` predicates. Each error is located (flow/object/action +\n * node/edge/field) with a corrective message.\n *\n * Since #4763 it also carries the **null-guard** verdict: an ordering /\n * arithmetic operator applied to a nullable declared field that no `!= null`\n * test dominates is rejected here, so the `has(a) && has(b) && a < b` trap\n * (which reads as a guard and is not one) never reaches a production write.\n * See `validate-null-guards.ts` for the decision procedure and its scope.\n */\n\nimport { validateExpression } from '@objectstack/formula';\nimport { collectFlowGraphs, resolveFlowNodeExpressions } from '@objectstack/spec/automation';\nimport type { FlowNodeParsed } from '@objectstack/spec/automation';\n\nimport { findUnguardedNullableOperands, nullGuardMessage } from './validate-null-guards.js';\n\nexport interface ExprIssue {\n where: string;\n message: string;\n source: string;\n /**\n * `error` fails the build (e.g. a bare ref in a record-scoped formula). `warning`\n * is advisory and never fails it (e.g. a possible field typo in a flattened flow\n * condition, which might be a flow variable). Absent ⇒ treat as `error`.\n */\n severity?: 'error' | 'warning';\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an `objects` collection (array or name-keyed map) to an array. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** object name → set of its field names, for schema-aware field checks. */\nfunction buildFieldIndex(objects: AnyRec[]): Map<string, string[]> {\n const idx = new Map<string, string[]>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fields = obj.fields;\n let names: string[] = [];\n if (Array.isArray(fields)) names = fields.map(f => (f as AnyRec).name).filter((n): n is string => typeof n === 'string');\n else if (fields && typeof fields === 'object') names = Object.keys(fields as AnyRec);\n idx.set(name, names);\n }\n return idx;\n}\n\n/**\n * object name → (field name → field type), for the #1928 tier-4 type-soundness\n * check. Handles both `fields` shapes (array of `{name, type}` and name-keyed\n * map). Fields with a non-string `type` are simply omitted (treated as `dyn`).\n */\nfunction buildFieldTypeIndex(objects: AnyRec[]): Map<string, Record<string, string>> {\n const idx = new Map<string, Record<string, string>>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fields = obj.fields;\n const types: Record<string, string> = {};\n if (Array.isArray(fields)) {\n for (const f of fields as AnyRec[]) {\n const fn = (f as AnyRec)?.name;\n const ft = (f as AnyRec)?.type;\n if (typeof fn === 'string' && typeof ft === 'string') types[fn] = ft;\n }\n } else if (fields && typeof fields === 'object') {\n for (const [fn, def] of Object.entries(fields as AnyRec)) {\n const ft = (def as AnyRec)?.type;\n if (typeof ft === 'string') types[fn] = ft;\n }\n }\n idx.set(name, types);\n }\n return idx;\n}\n\n/** The field list of an object, whichever of the two `fields` shapes it uses. */\nfunction fieldEntries(obj: AnyRec): Array<[string, AnyRec]> {\n const fields = obj.fields;\n if (Array.isArray(fields)) {\n return (fields as AnyRec[])\n .filter((f) => f && typeof f === 'object' && typeof f.name === 'string')\n .map((f) => [f.name as string, f] as [string, AnyRec]);\n }\n if (fields && typeof fields === 'object') {\n return Object.entries(fields as AnyRec)\n .filter(([, def]) => !!def && typeof def === 'object')\n .map(([n, def]) => [n, def as AnyRec] as [string, AnyRec]);\n }\n return [];\n}\n\n/**\n * Can this declared field hold `null` when a predicate reads it? (#4763)\n *\n * Deliberately conservative — this feeds a **build-breaking** verdict, so every\n * uncertainty resolves to \"not nullable\" (no finding). A field is treated as\n * always-valued when it is `required`, carries a `defaultValue`, declares a\n * default option (`options: [{ …, default: true }]` — the select idiom), or is\n * an autonumber the platform populates.\n */\nfunction isNullableField(def: AnyRec): boolean {\n if (def.required === true) return false;\n if (def.defaultValue !== undefined && def.defaultValue !== null) return false;\n if (def.type === 'autonumber') return false;\n const options = def.options;\n if (Array.isArray(options) && options.some((o) => !!o && typeof o === 'object' && (o as AnyRec).default === true)) {\n return false;\n }\n return true;\n}\n\n/** object name → set of field names that may hold `null` (#4763). */\nfunction buildNullableFieldIndex(objects: AnyRec[]): Map<string, Set<string>> {\n const idx = new Map<string, Set<string>>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const nullable = new Set<string>();\n for (const [fname, def] of fieldEntries(obj)) {\n if (isNullableField(def)) nullable.add(fname);\n }\n idx.set(name, nullable);\n }\n return idx;\n}\n\n/** The raw CEL source behind a predicate slot (string or `{ dialect, source }`). */\nfunction celSourceOf(raw: unknown): string | undefined {\n if (typeof raw === 'string') return raw;\n if (raw && typeof raw === 'object') {\n const rec = raw as AnyRec;\n // A non-CEL dialect (`js`) has its own null semantics — not ours to judge.\n if (typeof rec.dialect === 'string' && rec.dialect !== 'cel') return undefined;\n if (typeof rec.source === 'string') return rec.source;\n }\n return undefined;\n}\n\n/**\n * Every predicate a validation rule carries, including the ones nested inside a\n * `conditional` rule's `then` / `otherwise` — the trap hides there just as\n * happily as at the top level.\n */\nfunction rulePredicates(rule: AnyRec, path: string): Array<{ label: string; raw: unknown }> {\n const out: Array<{ label: string; raw: unknown }> = [];\n const name = typeof rule.name === 'string' ? rule.name : '?';\n const here = path ? `${path} → '${name}'` : `'${name}'`;\n const main = rule.expression ?? rule.predicate ?? rule.condition ?? rule.formula;\n if (main != null) out.push({ label: `validation rule ${here}`, raw: main });\n if (rule.when != null) out.push({ label: `validation rule ${here} when-predicate`, raw: rule.when });\n for (const branch of ['then', 'otherwise'] as const) {\n const nested = rule[branch];\n if (nested && typeof nested === 'object' && !Array.isArray(nested)) {\n out.push(...rulePredicates(nested as AnyRec, `${here} ${branch}`));\n }\n }\n return out;\n}\n\n/**\n * Validate every predicate in the stack. Returns the list of issues (empty =\n * clean). Caller decides how to surface / whether to fail the build.\n */\nexport function validateStackExpressions(stack: AnyRec): ExprIssue[] {\n const issues: ExprIssue[] = [];\n const objects = asArray(stack.objects);\n const fieldIndex = buildFieldIndex(objects);\n const fieldTypeIndex = buildFieldTypeIndex(objects);\n const nullableIndex = buildNullableFieldIndex(objects);\n\n /**\n * The #4763 null-guard gate. Scoped to the surfaces whose predicates are\n * EVALUATED by CEL over a record made total for every declared field —\n * validation rules (`rule-validator.ts`, fail-closed since #4649/#4761) and\n * lifecycle hook `condition`s. Deliberately NOT applied to sharing-rule\n * conditions (compiled to a SQL filter, where `NULL > x` is three-valued and\n * never faults), flow conditions (flattened scope: a bare identifier may be a\n * flow variable, not a field), or `Field.formula` expressions (whose blessed\n * `guard ? value : null` shape has its own #3306 handling). Those surfaces are\n * tracked separately rather than half-covered.\n */\n const checkNullGuards = (\n where: string,\n subject: string,\n raw: unknown,\n objectName: string | undefined,\n ): void => {\n if (!objectName) return;\n const nullableFields = nullableIndex.get(objectName);\n if (!nullableFields || nullableFields.size === 0) return;\n const source = celSourceOf(raw);\n if (!source) return;\n for (const finding of findUnguardedNullableOperands(source, { nullableFields })) {\n issues.push({\n where,\n message: nullGuardMessage(subject, objectName, finding),\n source,\n severity: 'error',\n });\n }\n };\n\n const check = (\n where: string,\n raw: unknown,\n objectName?: string,\n scope: 'record' | 'flattened' = 'flattened',\n ): void => {\n if (raw == null) return;\n const fields = objectName ? fieldIndex.get(objectName) : undefined;\n // Field types feed the #1928 tier-4 soundness warning; only consulted for\n // `record`-scoped sites, so it is harmless to pass for flattened ones too.\n const fieldTypes = objectName ? fieldTypeIndex.get(objectName) : undefined;\n const res = validateExpression('predicate', raw as string | { dialect?: string; source?: string },\n objectName ? { objectName, fields, fieldTypes, scope } : { scope });\n for (const e of res.errors) issues.push({ where, message: e.message, source: e.source, severity: 'error' });\n for (const w of res.warnings) issues.push({ where, message: w.message, source: w.source, severity: 'warning' });\n };\n\n /**\n * A declared bare-CEL slot (#4027). No object schema is passed: these slots\n * bind the *screen's own* collected values, not the trigger record's fields, so\n * a field-existence pass would report every field name as unknown.\n */\n const checkDeclaredPredicate = (where: string, raw: unknown): void => {\n if (raw == null) return;\n const res = validateExpression('predicate', raw as string | { dialect?: string; source?: string });\n for (const e of res.errors) issues.push({ where, message: e.message, source: e.source, severity: 'error' });\n for (const w of res.warnings) issues.push({ where, message: w.message, source: w.source, severity: 'warning' });\n };\n\n // ── Flows ──────────────────────────────────────────────────────────\n for (const flow of asArray(stack.flows)) {\n const flowName = typeof flow.name === 'string' ? flow.name : '(unnamed flow)';\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n // The record-change target object — `record.*` refs resolve against it.\n const startNode = nodes.find(n => n.type === 'start');\n const startCfg = (startNode?.config ?? {}) as AnyRec;\n const objectName = typeof startCfg.objectName === 'string' ? startCfg.objectName : undefined;\n\n // #4347 — every graph in the flow, not just `flow.nodes`/`flow.edges`. An\n // ADR-0031 container keeps a whole sub-graph in its `config`, so the\n // top-level walk validated PART of the flow while reporting on all of it: a\n // predicate written in the wrong dialect inside a `loop` body passed\n // `objectstack validate` and shipped. This is the author-time half of the\n // same traversal the engine's registration pass now does; `scope` names the\n // region so the located message still points at one edge.\n for (const graph of collectFlowGraphs(flow as { nodes?: FlowNodeParsed[] })) {\n const at = graph.scope ? `flow '${flowName}' · ${graph.scope}` : `flow '${flowName}'`;\n for (const node of graph.nodes as unknown as AnyRec[]) {\n const cfg = (node.config ?? {}) as AnyRec;\n check(`${at} · node '${node.id}' (${node.type}) condition`, cfg.condition, objectName);\n\n // Descriptor-declared expression slots (#4027). Before this, the traversal\n // hardcoded `condition` and assumed every other node string was a `{var}`\n // template — so `screen.fields[].visibleWhen`, declared bare CEL since\n // #3304, was validated by nobody and #3528 shipped a template-dialect\n // predicate through compile, validate and run time in silence.\n // Only `predicate` slots are checkable: `flow-template` slots take the\n // single-brace `{var}` dialect `interpolate()` implements, which no\n // validator covers (the `template` role enforces ADR-0032 §3's\n // double-brace text template and would reject every correct\n // `loop.collection`). The ledger records them regardless, so the\n // reconciliation ratchet still sees the marker.\n const nodeType = typeof node.type === 'string' ? node.type : '';\n for (const found of resolveFlowNodeExpressions(nodeType, cfg)) {\n if (found.entry.role !== 'predicate') continue;\n checkDeclaredPredicate(\n `${at} · node '${node.id}' (${nodeType}) ${found.entry.label} at config.${found.path}`,\n found.value,\n );\n }\n // #1870 — a `script` node must name a callable, and since #4343 that is\n // the whole of what the node does: `config.function`. A node without one\n // is a silent no-op that otherwise passes build. (Function *existence*\n // isn't checkable here — functions are code, not serialized into the\n // artifact — so this is a structural check; the runtime verifies the\n // named function is actually registered.)\n if (node.type === 'script') {\n // `function` is canonical; a pre-parse source may still carry the\n // `functionName` alias during the protocol-17 window, until the\n // 'flow-node-script-config-aliases' conversion (#3796) canonicalizes it.\n const fn =\n (typeof cfg.function === 'string' ? cfg.function.trim() : '') ||\n (typeof cfg.functionName === 'string' ? cfg.functionName.trim() : '');\n // A source that predates #4343 may still carry a retired dispatch key.\n // Naming it beats the generic \"no callable\": these ARE what the author\n // wrote, and each has a different replacement. The schema tombstones\n // carry the full prescription; this is the one-line version at lint.\n const action = typeof cfg.actionType === 'string' ? cfg.actionType.trim() : '';\n const retired = ['actionType', 'template', 'recipients', 'variables', 'script']\n .filter((k) => cfg[k] != null);\n if (retired.length > 0) {\n issues.push({\n where: `${at} · node '${node.id}' (script) callable`,\n message:\n `script node carries \\`${retired.map((k) => `config.${k}`).join('`, `')}\\` — retired in ` +\n `@objectstack/spec 17 (#4343). The built-in 'email'/'slack' actions were logger-backed ` +\n `stubs that delivered nothing, and inline \\`config.script\\` was never executed. ` +\n (action && action !== 'invoke_function' && !['email', 'slack'].includes(action)\n ? `\\`actionType: '${action}'\\` named a registered function — move it to \\`function: '${action}'\\`. `\n : `Use a \\`notify\\` node for mail, a \\`connector_action\\` (Slack connector) or \\`http\\` node ` +\n `for Slack, and a registered function for logic. `) +\n `Run \\`os migrate meta --from 16\\` to rewrite it automatically.`,\n source: JSON.stringify({ id: node.id, type: node.type, config: cfg }),\n });\n } else if (!fn) {\n issues.push({\n where: `${at} · node '${node.id}' (script) callable`,\n message:\n `script node declares no \\`function\\` — it would do nothing at runtime. ` +\n `Name a registered function (\\`function: 'my_fn'\\`, registered via ` +\n `\\`defineStack({ functions })\\`).`,\n source: JSON.stringify({ id: node.id, type: node.type, config: cfg }),\n });\n }\n }\n }\n for (const edge of graph.edges as unknown as AnyRec[]) {\n check(`${at} · edge '${edge.id}' (${edge.source}→${edge.target}) condition`, edge.condition, objectName);\n }\n }\n }\n\n // ── Object validation-rule + formula predicates ────────────────────\n for (const obj of objects) {\n const objectName = typeof obj.name === 'string' ? obj.name : undefined;\n const validations = obj.validations ?? obj.validationRules;\n for (const rule of asArray(validations)) {\n const where = `object '${objectName}' · validation '${(rule.name as string) ?? '?'}'`;\n // Common predicate keys across rule shapes. Validation predicates are\n // `record`-scoped — no field flattening — so bare refs are flagged (#1928).\n check(where, rule.expression ?? rule.predicate ?? rule.condition ?? rule.formula, objectName, 'record');\n // `conditional` rules carry a nested `when` predicate (record-scoped).\n check(`${where} when`, (rule as AnyRec).when, objectName, 'record');\n // #4763 — null-guard gate over every predicate the rule carries, nested\n // `then`/`otherwise` branches included.\n for (const p of rulePredicates(rule, '')) {\n checkNullGuards(`object '${objectName}' · ${p.label}`, p.label, p.raw, objectName);\n }\n }\n // Field-level formulas (computed fields) reference the same object.\n const fields = obj.fields;\n const fieldList = Array.isArray(fields)\n ? (fields as AnyRec[])\n : (fields && typeof fields === 'object' ? Object.values(fields as AnyRec) as AnyRec[] : []);\n\n // (ADR-0062 D7's `field.columnName`-on-external-objects rejection was removed\n // with `field.columnName` itself in #2377: the field no longer exists, so there\n // is no dual-source ambiguity to guard — external column mapping is `external.columnMap`.)\n\n for (const f of fieldList) {\n // Field-level conditional rules are server-enforced (rule-validator) and\n // record-scoped — a bare ref silently fails the rule (required/readonly\n // not enforced = data-integrity hole). #1928 class, same as actions.\n if (f && typeof f === 'object') {\n const fname = (f.name as string) ?? '?';\n for (const key of ['requiredWhen', 'readonlyWhen', 'conditionalRequired', 'visibleWhen'] as const) {\n check(`object '${objectName}' · field '${fname}' ${key}`, (f as AnyRec)[key], objectName, 'record');\n }\n }\n if (f && typeof f === 'object' && f.formula) {\n // formulas are `value` role (any return type), still CEL. They are\n // `record`-scoped — `record.<field>`, never bare — so flag bare refs (#1928).\n const res = validateExpression('value', f.formula as string | { dialect?: string; source?: string },\n objectName ? { objectName, fields: fieldIndex.get(objectName), fieldTypes: fieldTypeIndex.get(objectName), scope: 'record' } : { scope: 'record' });\n const fieldWhere = `object '${objectName}' · field '${(f.name as string) ?? '?'}' formula`;\n for (const e of res.errors) issues.push({ where: fieldWhere, message: e.message, source: e.source, severity: 'error' });\n for (const w of res.warnings) issues.push({ where: fieldWhere, message: w.message, source: w.source, severity: 'warning' });\n }\n }\n }\n\n // ── Action `visible` / `disabled` predicates ───────────────────────\n // Record-scoped, same as validation rules: a record-header / row action's\n // `visible` is evaluated by ActionEngine against `{ record, recordId,\n // objectName, user, … }` with fail-closed semantics, so a BARE field ref\n // (`done` instead of `record.done`) throws and the action is silently hidden\n // on every record (the trap behind the #2183 \"Mark Done never hides\" hunt).\n // Flagging it here turns that into a build error with a corrective message.\n // `disabled` may be a boolean (skip) or a predicate (check).\n const seenActions = new Set<string>();\n const checkAction = (where: string, action: AnyRec, objectName?: string): void => {\n const obj = objectName\n ?? (typeof action.objectName === 'string' ? action.objectName : undefined)\n ?? (typeof action.object === 'string' ? action.object : undefined);\n const name = typeof action.name === 'string' ? action.name : '?';\n const key = `${obj ?? ''}:${name}`;\n if (seenActions.has(key)) return; // de-dup (actions are merged onto objects AND kept top-level)\n seenActions.add(key);\n check(`${where} · action '${name}' visible`, action.visible, obj, 'record');\n if (typeof action.disabled !== 'boolean') {\n check(`${where} · action '${name}' disabled`, action.disabled, obj, 'record');\n }\n };\n for (const action of asArray(stack.actions)) {\n checkAction('stack', action);\n }\n for (const obj of objects) {\n const objectName = typeof obj.name === 'string' ? obj.name : undefined;\n for (const action of asArray(obj.actions)) {\n checkAction(`object '${objectName}'`, action, objectName);\n }\n }\n\n // ── Sharing-rule predicates (security-critical, record-scoped) ─────\n // A criteria sharing rule's `condition` decides which rows a principal sees.\n // It is evaluated against the record, so a bare ref silently changes access.\n for (const rule of asArray(stack.sharingRules)) {\n const ruleObj = typeof rule.object === 'string' ? rule.object : undefined;\n const where = `sharingRule '${(rule.name as string) ?? '?'}'${ruleObj ? ` (${ruleObj})` : ''} condition`;\n check(where, rule.condition ?? rule.criteria ?? rule.predicate, ruleObj, 'record');\n }\n\n // ── Hook `condition` predicates (record-scoped gate) ───────────────\n // A lifecycle hook's `condition` skips the handler when false; it is\n // evaluated against the record, so a bare ref silently makes the hook\n // run on every record (or never) instead of the intended subset.\n for (const hook of asArray(stack.hooks)) {\n const hookName = (hook.name as string) ?? '?';\n if (typeof hook.object === 'string') {\n check(`hook '${hookName}' (${hook.object}) condition`, hook.condition, hook.object, 'record');\n // #4763 — the third instance the issue found lived on exactly this path.\n checkNullGuards(\n `hook '${hookName}' (${hook.object}) condition`,\n `hook '${hookName}' condition`,\n hook.condition,\n hook.object,\n );\n continue;\n }\n\n // A hook may target MANY objects (`object: ['a','b']`). Previously any\n // non-string target dropped to `undefined`, so the condition got NO\n // field-awareness at all — a hook filtering on a field that exists on none\n // of its targets passed clean (issue #3583). The hook body runs against\n // each target in turn, so a ref missing from ANY of them silently\n // misbehaves there; validate per target and de-duplicate the\n // object-independent diagnostics (syntax/shape) that every pass repeats.\n const targets = Array.isArray(hook.object)\n ? (hook.object as unknown[]).filter((o): o is string => typeof o === 'string' && o !== '*')\n : [];\n if (targets.length === 0) {\n // `'*'` (or an unusable shape) — no single field set to judge against;\n // syntax/shape is still validated.\n check(`hook '${hookName}' condition`, hook.condition, undefined, 'record');\n continue;\n }\n\n const before = issues.length;\n const seen = new Set<string>();\n const kept: ExprIssue[] = [];\n for (const target of targets) {\n const mark = issues.length;\n check(`hook '${hookName}' (${target}) condition`, hook.condition, target, 'record');\n checkNullGuards(\n `hook '${hookName}' (${target}) condition`,\n `hook '${hookName}' condition`,\n hook.condition,\n target,\n );\n for (let i = mark; i < issues.length; i++) {\n const issue = issues[i];\n const key = `${issue.message}\\u0000${issue.source ?? ''}`;\n // Keep the first occurrence of each distinct diagnostic. A field-unknown\n // finding differs per target (it names the object), so each survives;\n // a syntax error is identical across targets and collapses to one.\n if (!seen.has(key)) {\n seen.add(key);\n kept.push(issue);\n }\n }\n }\n issues.length = before;\n issues.push(...kept);\n }\n\n return issues;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `has(x)` is not a null guard — publish-time rejection (#4763).\n *\n * CEL's `has(x)` asks whether the key is **present**. A declared column holding\n * `NULL` is present, and since #4649 every predicate sees a record that is\n * TOTAL over the object's declared fields — so `has(record.end_date)` is\n * uniformly `true` and tells the author nothing about the value. The idiom that\n * reads like a null guard\n *\n * ```text\n * has(record.start_date) && has(record.end_date) && record.end_date < record.start_date\n * ```\n *\n * therefore reaches `null < null`, CEL has no overload for it, the predicate\n * aborts, and (post-#4761) the write is rejected fail-closed. Before #4761 the\n * abort was swallowed: the rule was declared, listed in the metadata, and\n * enforced **nothing** on exactly the rows it was written to catch.\n *\n * The fault is fully decidable from the metadata alone — the predicate's AST\n * plus the object's declared field types say whether an operand can be null —\n * so it belongs at authoring/publish, not at a 400 on production data\n * (AGENTS.md PD #12: reject at authoring, do not tolerate at the consumer).\n * This module is that decision procedure; `validate-expressions.ts` wires it\n * into the gating `validateStackExpressions` rule, which `os build`,\n * `os validate`, `os lint` and the runtime publish gate all run.\n *\n * ## What is rejected\n *\n * An **ordering** (`< <= > >=`) or **arithmetic** (`+ - * / %`, unary `-`)\n * operator applied to an operand that\n *\n * 1. resolves to a declared field of the object (`record.<f>` / `previous.<f>`),\n * 2. that field is nullable — no `required: true`, no `defaultValue`, no\n * default option, not an autonumber, and\n * 3. is not dominated by an explicit `!= null` / `== null` / `isBlank()` test\n * in the same boolean branch.\n *\n * `has(x)` deliberately does **not** satisfy (3) — that is the entire point.\n * `has()` over an **undeclared** key is untouched: it never resolves to a\n * declared field, so (1) fails and the legitimate \"was this key in the PATCH\"\n * use stays legal. Equality (`==` / `!=`) is never flagged: CEL evaluates a\n * heterogeneous equality cleanly to `false` rather than faulting.\n */\n\nimport { Environment } from '@marcbachmann/cel-js';\nimport type { ASTNode } from '@marcbachmann/cel-js';\n\n/**\n * The corrective sentence, lifted **verbatim** from `unevaluableRuleError` in\n * `packages/objectql/src/validation/rule-validator.ts` so the publish-time and\n * runtime messages read identically — an author who hits one and then the other\n * must not have to reconcile two phrasings of one rule (#4763).\n */\nexport const NULL_GUARD_HINT =\n `Guard it with '!= null'` +\n ` — 'has(x)' does NOT do that: a declared field holding null is still PRESENT, so has(x) is true.`;\n\n/** Ordering + arithmetic operators — the ones with no `null` overload. */\nconst FAULTING_BINARY_OPS = new Set(['<', '<=', '>', '>=', '+', '-', '*', '/', '%']);\n\n/** Roots that bind the object's declared record shape (total since #4649). */\nconst DEFAULT_RECORD_ROOTS = ['record', 'previous'] as const;\n\nexport interface NullGuardOptions {\n /** Field names of the object that may hold `null` at evaluation time. */\n nullableFields: ReadonlySet<string>;\n /** Roots bound to the object's record shape. Defaults to `record`/`previous`. */\n roots?: readonly string[];\n}\n\nexport interface NullGuardFinding {\n /** The operand as written, e.g. `record.end_date`. */\n operand: string;\n /** The declared field the operand resolves to, e.g. `end_date`. */\n field: string;\n /** The operator with no null overload, e.g. `<`. */\n operator: string;\n /**\n * True when the predicate \"guards\" this operand with `has()` and nothing\n * else — the exact trap #4763 exists to reject. Used only to sharpen the\n * message; the verdict is the same either way.\n */\n hasOnlyGuard: boolean;\n}\n\n// A check-only parse environment: no evaluation, no stdlib needed, every\n// identifier stays `dyn` so any authored predicate parses. Built once.\nlet parseEnv: Environment | undefined;\nfunction getParseEnv(): Environment {\n if (!parseEnv) {\n parseEnv = new Environment({ unlistedVariablesAreDyn: true, enableOptionalTypes: true });\n }\n return parseEnv;\n}\n\ntype AnyNode = { op?: string; args?: unknown };\n\nfunction isNode(v: unknown): v is AnyNode & ASTNode {\n return !!v && typeof v === 'object' && typeof (v as AnyNode).op === 'string';\n}\n\nfunction isNullLiteral(node: unknown): boolean {\n return isNode(node) && node.op === 'value' && (node as AnyNode).args === null;\n}\n\n/**\n * `record.<field>` (or `previous.<field>`) → the field name; anything else —\n * a bare id, a nested traversal `record.account.region`, an index — → null.\n * Deliberately single-segment: a nested path is a lookup traversal whose\n * nullability this object's field list cannot decide.\n */\nfunction fieldOf(node: unknown, roots: readonly string[]): { operand: string; field: string } | null {\n if (!isNode(node)) return null;\n if (node.op !== '.' && node.op !== '.?') return null;\n const args = node.args as unknown;\n if (!Array.isArray(args) || args.length < 2) return null;\n const [recv, seg] = args as [unknown, unknown];\n if (typeof seg !== 'string') return null;\n if (!isNode(recv) || recv.op !== 'id') return null;\n const root = (recv as AnyNode).args;\n if (typeof root !== 'string' || !roots.includes(root)) return null;\n return { operand: `${root}.${seg}`, field: seg };\n}\n\n/** Children of a node, whatever its arg shape. */\nfunction childNodes(node: AnyNode): unknown[] {\n const args = node.args;\n if (isNode(args)) return [args];\n if (!Array.isArray(args)) return [];\n const out: unknown[] = [];\n for (const a of args) {\n if (isNode(a)) out.push(a);\n else if (Array.isArray(a)) for (const b of a) if (isNode(b)) out.push(b);\n }\n return out;\n}\n\nfunction callName(node: AnyNode): string | null {\n if (node.op !== 'call') return null;\n const args = node.args;\n if (!Array.isArray(args) || typeof args[0] !== 'string') return null;\n return args[0];\n}\n\nfunction callArgs(node: AnyNode): unknown[] {\n const args = node.args;\n if (!Array.isArray(args) || !Array.isArray(args[1])) return [];\n return args[1] as unknown[];\n}\n\nfunction union(a: ReadonlySet<string>, b: ReadonlySet<string>): Set<string> {\n return new Set([...a, ...b]);\n}\n\nfunction intersect(a: ReadonlySet<string>, b: ReadonlySet<string>): Set<string> {\n const out = new Set<string>();\n for (const v of a) if (b.has(v)) out.add(v);\n return out;\n}\n\n/**\n * Operands proven non-null **when `node` evaluates true**.\n *\n * `has(x)` is conspicuously absent, and its absence is the rule: a present key\n * is not a non-null value. `isBlank(x)` is absent for the mirrored reason —\n * `isBlank` being *true* says nothing about non-nullness (it is true FOR null);\n * it appears in {@link falseGuards} instead.\n */\nfunction truthGuards(node: unknown, roots: readonly string[]): Set<string> {\n if (!isNode(node)) return new Set();\n switch (node.op) {\n case '!=': {\n const [l, r] = (node.args as [unknown, unknown]) ?? [];\n if (isNullLiteral(r)) {\n const f = fieldOf(l, roots);\n return f ? new Set([f.operand]) : new Set();\n }\n if (isNullLiteral(l)) {\n const f = fieldOf(r, roots);\n return f ? new Set([f.operand]) : new Set();\n }\n return new Set();\n }\n case '&&': {\n const [l, r] = node.args as [unknown, unknown];\n return union(truthGuards(l, roots), truthGuards(r, roots));\n }\n case '||': {\n const [l, r] = node.args as [unknown, unknown];\n // Only what BOTH arms prove survives the disjunction.\n return intersect(truthGuards(l, roots), truthGuards(r, roots));\n }\n case '!_':\n return falseGuards(node.args, roots);\n case '?:': {\n const [, t, f] = node.args as [unknown, unknown, unknown];\n return intersect(truthGuards(t, roots), truthGuards(f, roots));\n }\n default:\n return new Set();\n }\n}\n\n/** Operands proven non-null **when `node` evaluates false** (the `!`/`||` side). */\nfunction falseGuards(node: unknown, roots: readonly string[]): Set<string> {\n if (!isNode(node)) return new Set();\n switch (node.op) {\n case '==': {\n const [l, r] = (node.args as [unknown, unknown]) ?? [];\n if (isNullLiteral(r)) {\n const f = fieldOf(l, roots);\n return f ? new Set([f.operand]) : new Set();\n }\n if (isNullLiteral(l)) {\n const f = fieldOf(r, roots);\n return f ? new Set([f.operand]) : new Set();\n }\n return new Set();\n }\n case '||': {\n const [l, r] = node.args as [unknown, unknown];\n return union(falseGuards(l, roots), falseGuards(r, roots));\n }\n case '&&': {\n const [l, r] = node.args as [unknown, unknown];\n return intersect(falseGuards(l, roots), falseGuards(r, roots));\n }\n case '!_':\n return truthGuards(node.args, roots);\n case 'call': {\n // `!isBlank(record.x)` / `isBlank(record.x) ? … : <here>` — a false\n // `isBlank` DOES prove non-null (it is the stdlib's blank-or-null test).\n if (callName(node) !== 'isBlank') return new Set();\n const [only] = callArgs(node);\n const f = fieldOf(only, roots);\n return f ? new Set([f.operand]) : new Set();\n }\n default:\n return new Set();\n }\n}\n\n/** Collect every `has(record.<f>)` operand appearing anywhere in the tree. */\nfunction collectHasOperands(node: unknown, roots: readonly string[], out: Set<string>): void {\n if (!isNode(node)) return;\n if (callName(node) === 'has') {\n for (const a of callArgs(node)) {\n const f = fieldOf(a, roots);\n if (f) out.add(f.operand);\n }\n }\n for (const child of childNodes(node)) collectHasOperands(child, roots, out);\n}\n\n/**\n * Find every ordering/arithmetic operand that resolves to a nullable declared\n * field and is not dominated by a real null guard. Returns `[]` for anything\n * that does not parse (syntax is reported by `validateExpression`, not here) —\n * this pass never invents a second syntax verdict.\n */\nexport function findUnguardedNullableOperands(\n source: string,\n opts: NullGuardOptions,\n): NullGuardFinding[] {\n if (typeof source !== 'string' || !source.trim()) return [];\n if (opts.nullableFields.size === 0) return [];\n const roots = opts.roots ?? DEFAULT_RECORD_ROOTS;\n\n let ast: ASTNode;\n try {\n ast = getParseEnv().parse(source).ast;\n } catch {\n return [];\n }\n\n const hasOperands = new Set<string>();\n collectHasOperands(ast, roots, hasOperands);\n\n const findings: NullGuardFinding[] = [];\n const seen = new Set<string>();\n\n const report = (operandNode: unknown, operator: string, guards: ReadonlySet<string>): void => {\n const f = fieldOf(operandNode, roots);\n if (!f) return;\n if (!opts.nullableFields.has(f.field)) return;\n if (guards.has(f.operand)) return;\n // NUL separates the composite key's two halves (it can appear in neither a\n // field path nor an operator). Written as the `\\u0000` ESCAPE, never as a raw\n // byte: a raw NUL makes grep/ripgrep treat the whole file as binary and\n // silently return ZERO matches, so the file drops out of code search and out\n // of every grep-based lint - and git will not warn you, because it only\n // inspects the first 8000 bytes to decide binary-ness. Same convention as\n // `packages/rest/src/rest-server.ts`. `\\u0000` rather than `\\0`, which turns\n // into a legacy-octal-escape error the moment a digit follows it.\n const key = `${f.operand}\\u0000${operator}`;\n if (seen.has(key)) return;\n seen.add(key);\n findings.push({\n operand: f.operand,\n field: f.field,\n operator,\n hasOnlyGuard: hasOperands.has(f.operand),\n });\n };\n\n const visit = (node: unknown, guards: ReadonlySet<string>): void => {\n if (!isNode(node)) return;\n const op = node.op as string;\n\n if (op === '&&') {\n const [l, r] = node.args as [unknown, unknown];\n visit(l, guards);\n // Left-to-right: only the LEFT conjunct's proofs reach the right one.\n visit(r, union(guards, truthGuards(l, roots)));\n return;\n }\n if (op === '||') {\n const [l, r] = node.args as [unknown, unknown];\n visit(l, guards);\n // Reaching the right arm means the left one was FALSE.\n visit(r, union(guards, falseGuards(l, roots)));\n return;\n }\n if (op === '?:') {\n const [c, t, f] = node.args as [unknown, unknown, unknown];\n visit(c, guards);\n visit(t, union(guards, truthGuards(c, roots)));\n visit(f, union(guards, falseGuards(c, roots)));\n return;\n }\n if (op === 'call' && callName(node) === 'has') {\n // `has(x)` itself never faults — and it never counts as a guard either.\n return;\n }\n if (FAULTING_BINARY_OPS.has(op)) {\n const [l, r] = node.args as [unknown, unknown];\n report(l, op, guards);\n report(r, op, guards);\n visit(l, guards);\n visit(r, guards);\n return;\n }\n if (op === '-_') {\n report(node.args, '-', guards);\n visit(node.args, guards);\n return;\n }\n for (const child of childNodes(node)) visit(child, guards);\n };\n\n visit(ast, new Set<string>());\n return findings;\n}\n\n/**\n * The publish-time message for one finding. Names the rule, the operand and the\n * `!= null` fix (the three things the author needs), then closes with the\n * verbatim runtime sentence so the two gates speak with one voice.\n *\n * @param subject How the site names itself, e.g. `validation rule 'end_after_start'`.\n * @param objectName The object whose field list decided nullability.\n */\nexport function nullGuardMessage(\n subject: string,\n objectName: string | undefined,\n finding: NullGuardFinding,\n): string {\n const owner = objectName ? `'${objectName}'` : 'this object';\n const hasNote = finding.hasOnlyGuard\n ? ` \\`has(${finding.operand})\\` does not guard it.`\n : '';\n return (\n `${subject} applies \\`${finding.operator}\\` to \\`${finding.operand}\\`, which ${owner} declares ` +\n `as nullable (no \\`required: true\\`, no \\`defaultValue\\`).${hasNote} At runtime the operand is ` +\n `null, CEL has no \\`${finding.operator}\\` overload for null, and the whole predicate aborts — ` +\n `so the rule enforces nothing and the write is rejected fail-closed (#4649/#4763). ` +\n `The predicate compares a value that is null. ${NULL_GUARD_HINT}`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for ADR-0047 list-view navigation modes.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring. It catches the \"wrong context\" authoring mistake\n// the type system alone cannot surface at author time on an object list view\n// (\"views\" mode — where the ViewTabBar owns the tab-bar role):\n// - `quickFilters` — never valid on an object list view;\n// - `userFilters` with `element: 'tabs'` (or carrying `tabs`) — the tab-bar\n// preset style is page-only; it would collide with the ViewTabBar.\n// A `dropdown` (value-chip) `userFilters` IS allowed on object views since the\n// ADR-0047 amendment (framework #2679 / objectui #2338) and is NOT flagged.\n//\n// Runs PRE-parse (on the normalizeStackInput output, before the\n// ObjectStackDefinition parse): the object-list schema (ObjectListViewSchema)\n// narrows `userFilters` to ObjectUserFiltersSchema (dropdown/toggle only), so a\n// post-parse stack has already had a `tabs` user-filter stripped and this rule\n// would never see it. The layering is deliberate — tsc rejects it at author\n// time, the schema strips it at runtime (no throw, back-compat), and this rule\n// reports it at `os validate` with a fix hint. See objectui #2338 and ADR-0047.\n\nexport type ListViewModeSeverity = 'error' | 'warning';\n\nexport interface ListViewModeFinding {\n severity: ListViewModeSeverity;\n rule: string;\n /** Human-readable location, e.g. `object \"task\" › listViews.my_pending`. */\n where: string;\n /** Config path, e.g. `objects[0].listViews.my_pending.userFilters`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const LIST_VIEW_FILTERS_IN_VIEWS_MODE = 'list-view-filters-in-views-mode';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\n/** Emit a finding for each wrong-context filter control on a single list-view def. */\nfunction scanView(\n view: unknown,\n where: string,\n path: string,\n out: ListViewModeFinding[],\n): void {\n if (!view || typeof view !== 'object') return;\n const rec = view as AnyRec;\n\n // `quickFilters` is never valid on an object list view.\n if (rec.quickFilters != null) {\n out.push({\n severity: 'error',\n rule: LIST_VIEW_FILTERS_IN_VIEWS_MODE,\n where,\n path: `${path}.quickFilters`,\n message:\n '`quickFilters` is a page filters-mode control and is ignored on an object ' +\n 'list view (\"views\" mode) — the ViewTabBar owns nav here.',\n hint:\n 'Move `quickFilters` to a page list (InterfaceListPage, \"filters\" mode), or ' +\n 'remove it. See ADR-0047.',\n });\n }\n\n // `userFilters` is allowed on object views ONLY as `dropdown` (value chips).\n // The `tabs` preset style — or any `userFilters` carrying `tabs` — collides\n // with the ViewTabBar and stays page-only.\n const uf = rec.userFilters;\n if (uf && typeof uf === 'object') {\n const ufRec = uf as AnyRec;\n if (ufRec.element === 'tabs' || ufRec.tabs != null) {\n out.push({\n severity: 'error',\n rule: LIST_VIEW_FILTERS_IN_VIEWS_MODE,\n where,\n path: `${path}.userFilters`,\n message:\n '`userFilters` with `element: \"tabs\"` is page-only and is ignored on an ' +\n 'object list view (\"views\" mode) — it would collide with the ViewTabBar.',\n hint:\n 'Use `listViews` for named presets on an object (each becomes a segmented ' +\n 'tab), switch to `element: \"dropdown\"` for value chips, or move the `tabs` ' +\n 'filter to a page list (InterfaceListPage, \"filters\" mode). See ADR-0047.',\n });\n }\n }\n}\n\n/** Scan a `listViews` record (name → list-view def). */\nfunction scanListViews(\n listViews: unknown,\n wherePrefix: string,\n pathPrefix: string,\n out: ListViewModeFinding[],\n): void {\n if (!listViews || typeof listViews !== 'object') return;\n for (const [name, view] of Object.entries(listViews as AnyRec)) {\n scanView(\n view,\n `${wherePrefix} › listViews.${name}`,\n `${pathPrefix}.listViews.${name}`,\n out,\n );\n }\n}\n\n/**\n * Flag ADR-0047 \"views\" mode violations on an object's built-in named views or a\n * `defineView` default `list` / named `listViews`: `quickFilters`, or a `tabs`\n * `userFilters`. A `dropdown` `userFilters` is allowed and not flagged. Returns\n * the list of findings (empty = clean). Caller decides how to surface / whether\n * to fail the build.\n *\n * Feed the PRE-parse stack (normalizeStackInput output) — see file header.\n */\nexport function validateListViewMode(stack: AnyRec): ListViewModeFinding[] {\n const out: ListViewModeFinding[] = [];\n\n // Object built-in named views (object.zod.ts `listViews`).\n asArray(stack.objects).forEach((obj, i) => {\n const label = typeof obj.name === 'string' ? `object \"${obj.name}\"` : `objects[${i}]`;\n scanListViews(obj.listViews, label, `objects[${i}]`, out);\n });\n\n // `defineView` aggregates (stack `views`: default `list` + named `listViews`).\n asArray(stack.views).forEach((view, i) => {\n const named =\n typeof view.objectName === 'string'\n ? view.objectName\n : typeof view.name === 'string'\n ? view.name\n : undefined;\n const label = named ? `view \"${named}\"` : `views[${i}]`;\n scanView(view.list, `${label} › list`, `views[${i}].list`, out);\n scanListViews(view.listViews, label, `views[${i}]`, out);\n });\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// [ADR-0078 Phase 1] The functional-completeness gate — `validate-functional-\n// completeness`, the validator the ADR names and, until now, the one it said\n// did not exist.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019). All judgement lives in the\n// SHARED predicate — `@objectstack/spec/kernel`'s `checkFieldCompleteness` /\n// `checkViewCompleteness`, the sibling of `isIncoherentAggregate` that cloud\n// graph-lint is meant to re-home onto — so this file is only the walk: where\n// fields and list views live in a stack, and how a predicate finding becomes a\n// lint finding with a location. If a rule seems wrong, fix the predicate (and\n// its runtime citation), never this walk.\n//\n// Why this closes a real hole: instance-completeness checks existed only in\n// cloud's AI-build graph-lint, so a stack authored via `os` + a coding\n// assistant, an MCP agent, `os validate` in CI, or a hand author got NONE of\n// them (`formula_without_expression` existed nowhere in the framework). One\n// predicate, every surface — the ADR's core decision.\n//\n// Runs on the NORMALIZED (pre-parse) stack like validate-list-view-mode: the\n// findings must reach the author even when an unrelated schema error would\n// stop the parse, and nothing here depends on parse-time defaults.\n\nimport {\n checkFieldCompleteness,\n checkViewCompleteness,\n checkWebhookCompleteness,\n type CompletenessFinding,\n} from '@objectstack/spec/kernel';\n\nexport type FunctionalCompletenessSeverity = 'error' | 'warning';\n\nexport interface FunctionalCompletenessFinding {\n severity: FunctionalCompletenessSeverity;\n /** Stable rule id from the shared predicate (e.g. `field/summary-without-operations`). */\n rule: string;\n /** Human-readable location, e.g. `object \"order\" › fields.total`. */\n where: string;\n /** Config path, e.g. `objects[2].fields.total.summaryOperations`. */\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Array-or-name-keyed-map collection → entries with a name and an index label. */\nfunction entriesOf(v: unknown): Array<{ name: string; def: AnyRec; key: string }> {\n if (Array.isArray(v)) {\n return v.flatMap((def, i) =>\n isRec(def) ? [{ name: String(def.name ?? i), def, key: `[${i}]` }] : [],\n );\n }\n if (isRec(v)) {\n return Object.entries(v).flatMap(([name, def]) =>\n isRec(def) ? [{ name, def: { name, ...def }, key: `.${name}` }] : [],\n );\n }\n return [];\n}\n\nfunction push(\n out: FunctionalCompletenessFinding[],\n found: CompletenessFinding[],\n where: string,\n basePath: string,\n): void {\n for (const f of found) {\n out.push({\n severity: f.severity,\n rule: f.rule,\n where,\n path: `${basePath}.${f.path}`,\n message: f.message,\n hint: f.fix,\n });\n }\n}\n\n/**\n * Walk every field definition and every list-view definition in the stack\n * through the shared completeness predicate.\n */\nexport function validateFunctionalCompleteness(stack: unknown): FunctionalCompletenessFinding[] {\n const out: FunctionalCompletenessFinding[] = [];\n if (!isRec(stack)) return out;\n\n // ── Fields: objects[].fields (map or array) ─────────────────────────────\n for (const [oi, obj] of entriesOf(stack.objects).entries()) {\n for (const field of entriesOf(obj.def.fields)) {\n push(\n out,\n checkFieldCompleteness(field.def),\n `object \"${obj.name}\" › fields.${field.name}`,\n `objects[${oi}].fields${field.key}`,\n );\n }\n }\n\n // ── List views: views[] containers → list / listViews.* ────────────────\n // (Form views carry no layout-binding contract; field completeness inside\n // objects is already covered above.)\n for (const [vi, container] of entriesOf(stack.views).entries()) {\n const where = container.def.object ? `view container \"${container.name}\"` : `view container [${vi}]`;\n if (isRec(container.def.list)) {\n push(out, checkViewCompleteness(container.def.list), `${where} › list`, `views[${vi}].list`);\n }\n for (const lv of entriesOf(container.def.listViews)) {\n push(\n out,\n checkViewCompleteness(lv.def),\n `${where} › listViews.${lv.name}`,\n `views[${vi}].listViews${lv.key}`,\n );\n }\n }\n\n // ── Webhooks: stack.webhooks[] ─────────────────────────────────────────\n // [ADR-0078 Phase 3] The one Tier-B candidate that survived its verification\n // pass. A webhook materializes into `sys_webhook` and looks armed in Setup\n // whether or not it declares a trigger, so the omission is invisible on every\n // surface an author can see.\n for (const hook of entriesOf(stack.webhooks)) {\n push(\n out,\n checkWebhookCompleteness(hook.def),\n `webhook \"${hook.name}\"`,\n `webhooks${hook.key}`,\n );\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for auto-launched flow trigger wiring (2026-07-17\n// third-party eval: a record-change flow that silently never fires).\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring. It catches the two authoring mistakes that produce\n// a flow which LOOKS armed but never launches — with zero runtime output:\n//\n// 1. `objectName` mismatch — the start node targets an object name that is\n// not defined in this stack. The runtime binds an ObjectQL hook filtered\n// to that exact name; if nobody writes it, the flow never fires. Names\n// match exactly (`eval_app_candidate`, not `candidate`). Objects owned by\n// other packages (`sys_*`, dependency packages) are legitimate targets,\n// so this is a warning with the cross-package caveat, not an error.\n//\n// 2. `status: 'draft'` on an auto-triggered flow — the schema default when\n// no status is authored (defineFlow parses at definition time, so by the\n// time this rule runs an unauthored status is indistinguishable from an\n// explicit 'draft'). Either way the intent is ambiguous: the engine still\n// binds and fires draft flows (only `obsolete`/`invalid` disable), which\n// surprises authors in both directions. Declare `'active'` to arm\n// deliberately or `'obsolete'` to disable. Only auto-triggered flows are\n// flagged (manual/screen flows have no arming semantics to be unclear\n// about).\n\nexport type FlowTriggerReadinessSeverity = 'error' | 'warning';\n\nexport interface FlowTriggerReadinessFinding {\n severity: FlowTriggerReadinessSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"notify_on_done\" › start node`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[0].config.objectName`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const FLOW_TRIGGER_UNKNOWN_OBJECT = 'flow-trigger-unknown-object';\nexport const FLOW_DRAFT_STATUS_AMBIGUOUS = 'flow-draft-status-ambiguous';\nexport const FLOW_TRIGGER_UNKNOWN_EVENT = 'flow-trigger-unknown-event';\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * The record-change trigger fires only for a `triggerType` matching this exact\n * grammar — the same set its `triggerTypeToHookEvents` maps to ObjectQL hooks.\n * `insert` is a synonym for `create`; `write` is the create-OR-update union\n * (#3427). Any OTHER `record-`-prefixed token — a typo (`record-after-updated`),\n * a phase-less bare noun (`record-change`), or a bad phase (`record-during-update`)\n * — binds to the trigger but maps to NO hook and never fires. Kept in sync with\n * that trigger (one small, stable contract).\n */\nconst VALID_RECORD_TRIGGER = /^record-(?:before|after)-(?:create|insert|update|delete|write)$/;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\n/** The start node of a flow definition, if any. */\nfunction startNodeOf(flow: AnyRec): { node: AnyRec; index: number } | undefined {\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const index = nodes.findIndex((n) => n?.type === 'start');\n return index >= 0 ? { node: nodes[index], index } : undefined;\n}\n\n/**\n * Validate auto-launched flow trigger wiring against the stack definition.\n * Pure and dependency-free; safe on pre- or post-parse stacks.\n */\nexport function validateFlowTriggerReadiness(stack: AnyRec): FlowTriggerReadinessFinding[] {\n const findings: FlowTriggerReadinessFinding[] = [];\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n const objectNames = new Set(\n asArray(stack.objects)\n .map((o) => (typeof o.name === 'string' ? o.name : undefined))\n .filter((n): n is string => !!n),\n );\n\n flows.forEach((flow, flowIndex) => {\n const flowName = typeof flow.name === 'string' ? flow.name : `#${flowIndex}`;\n const start = startNodeOf(flow);\n const config = (start?.node.config ?? {}) as AnyRec;\n const triggerType = typeof config.triggerType === 'string' ? config.triggerType : undefined;\n const isRecordTriggered = !!triggerType && triggerType.startsWith('record-');\n // Array-form triggerType (e.g. ['record-after-create', 'record-after-delete'])\n // is NOT supported — multi-event unions are deferred (#3457). It needs its own\n // detection because a non-string triggerType folds to `undefined` above, so the\n // runtime misclassifies the flow as manual and it never fires with zero output\n // (#3481). Any record-* element is enough to recognize the (unsupported) intent.\n const isArrayRecordTriggered =\n Array.isArray(config.triggerType) &&\n (config.triggerType as unknown[]).some((t) => typeof t === 'string' && t.startsWith('record-'));\n const isTimeRelative = config.timeRelative != null && typeof config.timeRelative === 'object';\n const isAutoTriggered =\n isRecordTriggered || triggerType === 'api' || config.schedule != null ||\n isTimeRelative || flow.type === 'schedule' || flow.type === 'api';\n\n // 1. Record-triggered flow targeting an object this stack does not define.\n if (isRecordTriggered && start) {\n const objectName = typeof config.objectName === 'string' ? config.objectName : undefined;\n if (objectName && !objectNames.has(objectName) && !objectName.startsWith('sys_')) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_OBJECT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.objectName`,\n message:\n `targets object '${objectName}', which this stack does not define — if the name is wrong, ` +\n `the flow will never fire (and the runtime stays silent about it).`,\n hint:\n `Object names match exactly. Check config.objectName against the object's registered name ` +\n `(e.g. 'app_candidate', not 'candidate'). If the object comes from another installed package, ` +\n `this warning can be ignored.`,\n });\n }\n }\n\n // 1b. Time-relative flow sweeping an object this stack does not define. Like\n // the record-change case, a wrong object name makes the sweep match\n // nothing forever with no runtime output.\n if (isTimeRelative && start) {\n const tr = config.timeRelative as AnyRec;\n const objectName = typeof tr.object === 'string' ? tr.object : undefined;\n if (objectName && !objectNames.has(objectName) && !objectName.startsWith('sys_')) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_OBJECT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.timeRelative.object`,\n message:\n `sweeps object '${objectName}', which this stack does not define — if the name is wrong, ` +\n `the sweep will match nothing (and the runtime stays quiet about it).`,\n hint:\n `Object names match exactly. Check config.timeRelative.object against the object's registered name. ` +\n `If the object comes from another installed package, this warning can be ignored.`,\n });\n }\n }\n\n // 1c. A `record-`-prefixed triggerType the trigger cannot map to any hook —\n // a typo (`record-after-updated`), a phase-less bare noun (`record-change`,\n // which the Studio picker once offered as \"Record changed (any)\"), or a bad\n // phase (`record-during-update`). The engine routes any `record-` token to\n // the record-change trigger, which then binds to NO hook and never fires\n // (only a runtime warn). Surface the never-fire defect at authoring time.\n if (start && isRecordTriggered && !VALID_RECORD_TRIGGER.test((triggerType ?? '').trim())) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_EVENT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.triggerType`,\n message:\n `triggerType '${triggerType}' is not a recognized record trigger — the flow binds to the ` +\n `record-change trigger but never fires (the runtime stays silent about it).`,\n hint:\n `Use record-{before,after}-{create,update,delete,write}. 'write' fires on create OR update in one ` +\n `flow (#3427); create/insert are synonyms. There is no \"any change\" token — pick the specific event(s).`,\n });\n }\n\n // 1d. Array-form triggerType — an unsupported multi-event shape (#3457). The\n // runtime folds a non-string triggerType to \"no trigger\" and treats the\n // flow as manual, so it binds to nothing and never fires, with zero output\n // at any layer (#3481). Surface it at authoring time like the unmappable\n // single tokens above (same rule id — both are \"this token never fires\").\n if (start && isArrayRecordTriggered) {\n findings.push({\n severity: 'warning',\n rule: FLOW_TRIGGER_UNKNOWN_EVENT,\n where: `flow \"${flowName}\" › start node`,\n path: `flows[${flowIndex}].nodes[${start.index}].config.triggerType`,\n message:\n `triggerType is an array (${JSON.stringify(config.triggerType)}), which is not supported — a start ` +\n `node takes a single trigger event, so the flow binds to nothing and never fires (the runtime stays silent about it).`,\n hint:\n `Use one triggerType string. For \"created or updated\" use record-after-write (one flow, both events, #3427). ` +\n `For any other combination, author one flow per event — multi-event arrays are deferred (#3457).`,\n });\n }\n\n // 2. Auto-triggered flow whose status is 'draft' — authored or defaulted\n // (defineFlow parses at definition time, so the two are the same here).\n if (isAutoTriggered && (flow.status == null || flow.status === 'draft')) {\n findings.push({\n severity: 'warning',\n rule: FLOW_DRAFT_STATUS_AMBIGUOUS,\n where: `flow \"${flowName}\"`,\n path: `flows[${flowIndex}].status`,\n message:\n `has status 'draft' (the default when none is authored). Draft flows DO still fire their ` +\n `triggers (only 'obsolete'/'invalid' disable), so the intent is ambiguous.`,\n hint: `Declare status: 'active' to arm it deliberately, or status: 'obsolete' to disable it.`,\n });\n }\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared flow-node traversal for the rules that inspect `FlowNode.config`\n * (issue #4380) — the flow-side counterpart of `page-walk.ts`, and here for the\n * same reason: every rule had hand-written the same one-liner,\n *\n * ```ts\n * const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n * ```\n *\n * …and every one of them was therefore blind to the same thing.\n *\n * ## What the one-liner misses\n *\n * `FlowRegionSchema` (`@objectstack/spec/automation`) holds a FULL\n * `nodes: z.array(FlowNodeSchema)`, and four config slots carry one:\n *\n * | node type | slot(s) |\n * |-------------|----------------------------------|\n * | `try_catch` | `config.try`, `config.catch` |\n * | `loop` | `config.body` |\n * | `parallel` | `config.branches[].nodes` |\n *\n * Regions nest arbitrarily (a region node may itself be a `try_catch`). Before\n * this walk, a node moved into any of them left the checking behind:\n * `flow-node-write-unknown-field` and `flow-update-readonly-field` — both\n * GATING errors — reported nothing, and `approval-approver-*` went quiet too.\n *\n * `validate-flow-template-paths` failed a third way, worth naming because it is\n * the one a reader would not predict: it scans a node's whole `config` for\n * string leaves, so it still SAW tokens inside a region — but its `filter`\n * position split only looks at the top level of the node it was handed, so a\n * nested filter token lost its position and the #3810 finding silently\n * downgraded from `error` to `warning`, reported against the wrapping\n * `try_catch` instead of the `get_record` that cannot run. Being visible is not\n * the same as being judged correctly, which is why {@link WalkedFlowNode}\n * carries a real per-node `path` rather than only the node object.\n *\n * ## The double-count trap\n *\n * A container node is yielded too — it has its own config worth checking (a\n * `loop`'s `collection`, a `try_catch`'s `retry`). But its `config` physically\n * CONTAINS every descendant, so any rule that walks config recursively would\n * report each nested finding twice: once at the inner node, once at the\n * container. {@link WalkedFlowNode.localConfig} is the container's config with\n * the region slots removed — the view a recursive scan must use. Rules that\n * read named keys (`config.fields`, `config.objectName`) can use either.\n */\n\nimport { FLOW_REGION_SLOTS_BY_TYPE, FLOW_REGION_CONFIG_KEYS } from '@objectstack/spec/automation';\n\nexport type AnyRec = Record<string, unknown>;\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * Config keys that hold a nested region, by owning node type.\n *\n * Projected from `@objectstack/spec/automation` rather than declared here\n * (#4401). This used to be a local copy with its own reconciliation test — as\n * did the ADR-0087 conversion walk's copy and the spec-side control-flow walk's.\n * Three tables, three tests each pinning its own copy, and nothing that would\n * fail if they drifted from ONE ANOTHER: every copy was individually protected\n * and the set was not. A fourth construct is now a single entry in\n * `spec/src/automation/region-slots.ts`.\n *\n * The **walk** below stays here. It takes raw authored records (not\n * `FlowNodeParsed`), and it yields per-node diagnostic paths and label trails —\n * formatting that is lint's business, not the protocol's (Prime Directive #2).\n * Only the table is shared; the three traversals stay separate because they walk\n * different units for different consumers.\n */\nexport const REGION_SLOTS: ReadonlyMap<string, readonly string[]> = new Map(\n [...FLOW_REGION_SLOTS_BY_TYPE].map(([type, slots]) => [type, slots.map(s => s.key)]),\n);\n\n/** Every config key that may hold region nodes, across all node types. */\nexport const REGION_CONFIG_KEYS: ReadonlySet<string> = FLOW_REGION_CONFIG_KEYS;\n\n/**\n * Depth cap. Regions are a tree in parsed metadata, so this is not a cycle\n * guard — it is a cheap promise that a hand-authored (pre-parse) stack cannot\n * make a lint hang. Well past anything reviewable: five levels of nested\n * try/loop/parallel is already an unreadable flow.\n */\nexport const MAX_REGION_DEPTH = 16;\n\n/** A visited flow node plus everything needed to locate and describe it. */\nexport interface WalkedFlowNode {\n /** The node record itself. */\n node: AnyRec;\n /** Config path, e.g. `flows[0].nodes[1].config.catch.nodes[0]`. */\n path: string;\n /**\n * The node's config with region slots stripped — what a rule that scans\n * config RECURSIVELY must read, or it reports every descendant's finding a\n * second time against this node. `undefined` when the node has no config.\n */\n localConfig?: AnyRec;\n /**\n * Region breadcrumb from the flow root, e.g. `try_catch \"Guard\" › catch`.\n * Empty string for a top-level node, so a caller can append it unconditionally.\n */\n regionTrail: string;\n /** 0 for a top-level node; 1 inside one region; and so on. */\n depth: number;\n}\n\n/** A node's label for diagnostics: `label` → `id` → `#index`. */\nexport function flowNodeLabel(node: AnyRec, index: number): string {\n return strName(node.label) ?? strName(node.id) ?? `#${index}`;\n}\n\n/** `config` minus the region slots, or `undefined` when there is no config. */\nfunction stripRegions(config: unknown): AnyRec | undefined {\n if (!isRec(config)) return undefined;\n let out: AnyRec | undefined;\n for (const key of Object.keys(config)) {\n if (!REGION_CONFIG_KEYS.has(key)) continue;\n out ??= { ...config };\n delete out[key];\n }\n return out ?? config;\n}\n\n/**\n * Walk every node of a flow, depth-first, including those nested in\n * `try_catch` / `loop` / `parallel` regions. Yields each with its own config\n * path, so a finding lands on the node that is actually wrong.\n *\n * `flowPath` is the caller's path prefix for the flow (e.g. `flows[3]`).\n */\nexport function walkFlowNodes(flow: AnyRec, flowPath: string): WalkedFlowNode[] {\n const out: WalkedFlowNode[] = [];\n if (!isRec(flow)) return out;\n\n const visitList = (nodes: unknown, basePath: string, trail: string, depth: number): void => {\n if (!Array.isArray(nodes) || depth > MAX_REGION_DEPTH) return;\n nodes.forEach((raw, index) => {\n if (!isRec(raw)) return;\n const path = `${basePath}[${index}]`;\n out.push({\n node: raw,\n path,\n localConfig: stripRegions(raw.config),\n regionTrail: trail,\n depth,\n });\n\n const type = strName(raw.type);\n const slots = type ? REGION_SLOTS.get(type) : undefined;\n if (!slots || !isRec(raw.config)) return;\n const config = raw.config;\n const here = `${type} \"${flowNodeLabel(raw, index)}\"`;\n\n for (const slot of slots) {\n const value = config[slot];\n if (slot === 'branches') {\n // parallel: an array of regions, each with its own nodes.\n if (!Array.isArray(value)) continue;\n value.forEach((branch, b) => {\n if (!isRec(branch)) return;\n const branchName = strName(branch.name) ?? `#${b}`;\n visitList(\n branch.nodes,\n `${path}.config.branches[${b}].nodes`,\n joinTrail(trail, `${here} › branch ${branchName}`),\n depth + 1,\n );\n });\n continue;\n }\n if (!isRec(value)) continue;\n visitList(\n value.nodes,\n `${path}.config.${slot}.nodes`,\n joinTrail(trail, `${here} › ${slot}`),\n depth + 1,\n );\n }\n });\n };\n\n visitList(flow.nodes, `${flowPath}.nodes`, '', 0);\n return out;\n}\n\nfunction joinTrail(trail: string, segment: string): string {\n return trail ? `${trail} › ${segment}` : segment;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for `{record.<path>}` template references in a\n// record-change flow's node config (#3426).\n//\n// A `notify` / `update_record` / `http` / ... node interpolates\n// `{record.<field>}` tokens against the triggering record. Two authoring\n// mistakes render a SILENT empty string at runtime, with no design-time\n// signal — exactly the failure #3426 reported:\n//\n// 1. `{record.<unknown>}` — the path head is neither a declared field nor a\n// system column. Almost always a typo (`{record.full_naem}`). The template\n// engine resolves it to `undefined` -> '' with no warning.\n//\n// 2. `{record.<lookup>.<subfield>}` — a cross-object hop through a lookup /\n// master_detail / user / tree relation. The seeded flow record carries the\n// relation as a SCALAR foreign-key id, not an expanded object (a default\n// data-API read does not expand relations either — see #3426's hydration\n// note and #1872). So `record.account.name` walks `.name` on a string id\n// and yields '' silently. Not resolved today; tracked on #3426.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring.\n//\n// SEVERITY FOLLOWS THE RUNTIME CONSEQUENCE, which differs by POSITION:\n//\n// - Everywhere else (a message body, an http url, a write payload) an\n// unresolved token renders a blank. The output is wrong but the run\n// completes, and the head object may legitimately come from another\n// installed package (skipped — see below). Advisory: WARNING.\n//\n// - Inside a filter-guarded CRUD node's `filter`, an unresolved token used to\n// DELETE the condition from the query, and a removed condition matches MORE\n// rows — `delete_record` with its only condition gone matched every row.\n// Since framework#3810 those nodes REFUSE TO EXECUTE. So a finding here is\n// not \"the output will be blank\", it is \"this node cannot run\": the build\n// is shipping a flow whose runtime is already decided. Gating: ERROR.\n//\n// The split is the same shift-left `validateReadonlyFlowWrites` makes — a\n// certain runtime failure gates the build, a state-dependent one advises — and\n// it keeps this rule honest about what it found. Catching the typo at build\n// time beats a failed run at 3am; catching it and calling it advisory, when the\n// runtime has already committed to refusing, understates it.\n//\n// Deliberately conservative to keep false positives near zero:\n// - Only `record.`-prefixed tokens are checked. Other `{var}` tokens address\n// flow variables / node outputs the rule cannot resolve statically.\n// - Only flows bound to an object THIS stack defines are checked; when the\n// object is unknown here (another package, `sys_*`) the rule has no schema\n// to compare against and skips the whole flow.\n// - `formula` / `summary` fields are VALID heads (formula is hydrated onto the\n// record since #3445; summary is stored on write) — never flagged.\n// - A trailing NUMERIC segment (`{record.target_channels.0}`) is an array\n// index into a `multiple` lookup (#1872), not a cross-object hop — allowed.\n// - Structured scalar heads (`json` / `composite` / `repeater` / `record`) may\n// carry legitimate sub-paths — their `.<sub>` access is left alone.\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\nimport { walkFlowNodes } from './flow-walk.js';\n\nexport type FlowTemplatePathSeverity = 'error' | 'warning';\n\nexport interface FlowTemplatePathFinding {\n severity: FlowTemplatePathSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"notify_lead\" node \"notify\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[2]`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const FLOW_TEMPLATE_UNKNOWN_FIELD = 'flow-template-unknown-field';\nexport const FLOW_TEMPLATE_LOOKUP_TRAVERSAL = 'flow-template-lookup-traversal';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\n// Path heads addressable in a `{record.<col>}` template without being authored\n// fields: the package-shared registry-injected columns (`system-fields.ts`,\n// #4330) plus three heads this rule has always exempted. `name`, `owner` and\n// `record_type` are NOT registry-injected system columns (`name` in particular\n// is an ordinary authored field on most objects), so they stay rule-local —\n// see the shared module's note — instead of widening every field-existence\n// rule in the package.\nconst IMPLICIT_HEADS: ReadonlySet<string> = new Set([\n ...SYSTEM_FIELDS,\n 'name', 'owner', 'record_type',\n]);\n\n// Field types that address ANOTHER object — a `.<subfield>` hop through one is\n// a cross-object traversal the seeded flow record does not expand.\nconst RELATION_TYPES: ReadonlySet<string> = new Set([\n 'lookup',\n 'master_detail',\n 'user',\n 'tree',\n]);\n\n// The CRUD nodes whose `filter` the runtime guards (framework#3810): each calls\n// `resolveNodeFilter`, which refuses the node when interpolation erased any\n// authored condition. `create_record` is deliberately absent — it writes a\n// payload and has no filter, so an unresolved token there is a blank value on\n// the new row, not a widened query.\nconst FILTER_GUARDED_NODE_TYPES: ReadonlySet<string> = new Set([\n 'get_record',\n 'update_record',\n 'delete_record',\n]);\n\n/** Build a `fieldName -> type` map for an object (declared fields only). */\nfunction fieldTypesOf(obj: AnyRec): Map<string, string> {\n const types = new Map<string, string>();\n for (const f of asArray(obj.fields)) {\n if (typeof f.name === 'string') {\n types.set(f.name, typeof f.type === 'string' ? f.type : '');\n }\n }\n return types;\n}\n\n/**\n * Extract the `record.<path>` references from a template string. Mirrors the\n * runtime interpolator's token grammar (service-automation builtin/template.ts):\n * a `{...}` token whose body is a dotted path whose HEAD is `record`. Arithmetic\n * / function tokens (`{NOW()}`, `{a + b}`) and non-`record` heads are ignored.\n *\n * Returns each reference's segment list AFTER the `record` head, e.g.\n * `{record.account.name}` -> `[['account', 'name']]`.\n */\nfunction recordRefsIn(text: string): string[][] {\n const refs: string[][] = [];\n const tokenRe = /\\{([^{}]+)\\}/g;\n let m: RegExpExecArray | null;\n while ((m = tokenRe.exec(text)) !== null) {\n const body = m[1].trim();\n // Pure dotted path only (same shape the interpolator's fast path accepts):\n // identifier head, then identifier-or-numeric segments. Anything with\n // operators / spaces / quotes is an arithmetic token — not a bare field ref.\n if (!/^[A-Za-z_$][\\w$]*(?:\\.(?:[A-Za-z_$][\\w$]*|\\d+))*$/.test(body)) continue;\n const segments = body.split('.');\n if (segments[0] !== 'record') continue;\n const rest = segments.slice(1);\n if (rest.length > 0) refs.push(rest);\n }\n return refs;\n}\n\n/** Recursively collect templated string leaves from a config-bearing block. */\nfunction stringLeaves(value: unknown, out: string[]): void {\n if (typeof value === 'string') {\n if (value.includes('{')) out.push(value);\n return;\n }\n if (Array.isArray(value)) {\n for (const v of value) stringLeaves(v, out);\n return;\n }\n if (value && typeof value === 'object') {\n for (const v of Object.values(value as AnyRec)) stringLeaves(v, out);\n }\n}\n\n// The typed config blocks + freeform `config` a node interpolates at runtime.\n// We scan every string leaf under these (the runtime `interpolate()` walks the\n// whole config recursively), NOT `id` / `type` / `label` / `position`, which are\n// never templated.\nconst NODE_CONFIG_KEYS = [\n 'config',\n 'notify',\n 'update_record',\n 'create_record',\n 'http',\n 'script',\n 'screen',\n 'wait',\n 'approval',\n 'connector_action',\n 'subflow',\n 'decision',\n 'start',\n];\n\n/** A templated string leaf plus the one thing severity depends on: where it sits. */\ninterface TemplateLeaf {\n text: string;\n /** Inside a filter-guarded CRUD node's `filter` — an unresolved token there is refused at runtime. */\n inFilter: boolean;\n}\n\n/**\n * Collect a node's templated string leaves, tagging those that sit under a\n * `filter` key when the node type is one the runtime guards.\n *\n * `guarded` leaves are returned FIRST so the per-node dedupe below resolves a\n * reference that appears in both positions at its higher severity: one typo\n * used in a filter and echoed in a message is an error, not a warning.\n */\nfunction collectNodeLeaves(node: AnyRec, guarded: boolean): TemplateLeaf[] {\n const filterLeaves: TemplateLeaf[] = [];\n const otherLeaves: TemplateLeaf[] = [];\n\n for (const key of NODE_CONFIG_KEYS) {\n if (!(key in node)) continue;\n const block = node[key];\n const splitFilter = guarded && !!block && typeof block === 'object' && !Array.isArray(block);\n\n if (splitFilter) {\n const { filter, ...rest } = block as AnyRec;\n const inFilter: string[] = [];\n stringLeaves(filter, inFilter);\n for (const text of inFilter) filterLeaves.push({ text, inFilter: true });\n const outside: string[] = [];\n stringLeaves(rest, outside);\n for (const text of outside) otherLeaves.push({ text, inFilter: false });\n continue;\n }\n\n const plain: string[] = [];\n stringLeaves(block, plain);\n for (const text of plain) otherLeaves.push({ text, inFilter: false });\n }\n\n return [...filterLeaves, ...otherLeaves];\n}\n\n/** True when the flow is armed by a record lifecycle event. */\nfunction isRecordTriggered(flow: AnyRec, startConfig: AnyRec): boolean {\n if (flow.type === 'record_change') return true;\n const triggerType = typeof startConfig.triggerType === 'string' ? startConfig.triggerType : undefined;\n return !!triggerType && triggerType.startsWith('record-');\n}\n\n/** Resolve the object a record-change flow binds to, from its start node. */\nfunction boundObjectOf(flow: AnyRec): string | undefined {\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const start = nodes.find((n) => n?.type === 'start');\n if (!start) return undefined;\n const config = (start.config ?? {}) as AnyRec;\n const typed = (start.start ?? {}) as AnyRec;\n const fromConfig = typeof config.objectName === 'string' ? config.objectName : undefined;\n const fromTyped = typeof typed.objectName === 'string' ? typed.objectName : undefined;\n return fromConfig ?? fromTyped;\n}\n\n/**\n * The lookup relations a record-change flow opted IN to expand, from the start\n * node's `config.expand` (#3475). A `{record.<rel>.<field>}` hop through one of\n * these IS resolved at run time — the engine re-reads it as the run's identity —\n * so the traversal warning is suppressed for those relations. Accepts a `string`\n * or `string[]`; anything else yields the empty set.\n */\nfunction declaredExpandOf(flow: AnyRec): Set<string> {\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const start = nodes.find((n) => n?.type === 'start');\n const raw = ((start?.config ?? {}) as AnyRec).expand;\n if (typeof raw === 'string') return new Set(raw ? [raw] : []);\n if (Array.isArray(raw)) return new Set(raw.filter((r): r is string => typeof r === 'string' && r.length > 0));\n return new Set();\n}\n\n/**\n * Validate `{record.<path>}` template references across every record-change\n * flow. Pure and dependency-free; safe on pre- or post-parse stacks.\n */\nexport function validateFlowTemplatePaths(stack: AnyRec): FlowTemplatePathFinding[] {\n const findings: FlowTemplatePathFinding[] = [];\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n const objectsByName = new Map<string, AnyRec>();\n for (const obj of asArray(stack.objects)) {\n if (typeof obj.name === 'string') objectsByName.set(obj.name, obj);\n }\n\n flows.forEach((flow, flowIndex) => {\n const flowName = typeof flow.name === 'string' ? flow.name : `#${flowIndex}`;\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const start = (nodes.find((n) => n?.type === 'start')?.config ?? {}) as AnyRec;\n if (!isRecordTriggered(flow, start)) return;\n\n const objectName = boundObjectOf(flow);\n if (!objectName) return;\n const obj = objectsByName.get(objectName);\n // Unknown object here -> no schema to compare against (another package /\n // `sys_*`). The trigger-readiness rule already flags a wrong name; we can't\n // meaningfully classify field paths, so skip the whole flow.\n if (!obj) return;\n\n const fieldTypes = fieldTypesOf(obj);\n const expandSet = declaredExpandOf(flow);\n\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions\n // (#4380). This rule was not merely blind to them — it was WORSE than\n // blind: the recursive string-leaf scan already saw a nested node's tokens\n // through its container's `config`, but `collectNodeLeaves` splits `filter`\n // only at the top level of the node it is handed, so a nested filter token\n // lost its position and the gating #3810 finding silently degraded to a\n // warning reported against the wrapping `try_catch`. Walking to the real\n // node restores both the severity and the location.\n walkFlowNodes(flow, `flows[${flowIndex}]`).forEach(({ node, path: nodePath, regionTrail, localConfig }, walkIndex) => {\n const nodeLabel =\n typeof node.type === 'string' ? node.type : typeof node.id === 'string' ? node.id : `#${walkIndex}`;\n const where = regionTrail\n ? `flow \"${flowName}\" ${regionTrail} node \"${nodeLabel}\"`\n : `flow \"${flowName}\" node \"${nodeLabel}\"`;\n\n // Collect templated string leaves from the config-bearing blocks only,\n // tagging filter positions when this node type guards its filter (#3810).\n const nodeType = typeof node.type === 'string' ? node.type : '';\n const guarded = FILTER_GUARDED_NODE_TYPES.has(nodeType);\n // Scan the container's config WITHOUT its region slots: their nodes are\n // walked in their own right, and leaving them in would report every\n // nested finding a second time against the container.\n const scanNode =\n localConfig !== undefined && localConfig !== node.config\n ? ({ ...node, config: localConfig } as AnyRec)\n : (node as AnyRec);\n const leaves = collectNodeLeaves(scanNode, guarded);\n if (leaves.length === 0) return;\n\n // Dedupe references so one repeated typo yields one finding per node.\n const seenUnknown = new Set<string>();\n const seenTraversal = new Set<string>();\n\n for (const leaf of leaves) {\n const inFilter = leaf.inFilter;\n for (const rest of recordRefsIn(leaf.text)) {\n const head = rest[0];\n const hasSubPath = rest.length > 1;\n // A trailing numeric segment is an array index (#1872), not a hop.\n const nextIsIdentifier = hasSubPath && !/^\\d+$/.test(rest[1]);\n\n const isKnown = fieldTypes.has(head) || IMPLICIT_HEADS.has(head);\n\n if (!isKnown) {\n if (seenUnknown.has(head)) continue;\n seenUnknown.add(head);\n findings.push({\n severity: inFilter ? 'error' : 'warning',\n rule: FLOW_TEMPLATE_UNKNOWN_FIELD,\n where,\n path: nodePath,\n message: inFilter\n ? `${nodeType} filter references '{record.${rest.join('.')}}', but '${head}' is not a field on ` +\n `object '${objectName}' — the token resolves to nothing, which DROPS the condition from the ` +\n `query instead of narrowing it. The node refuses to run at execution time (#3810).`\n : `template references '{record.${rest.join('.')}}', but '${head}' is not a field on ` +\n `object '${objectName}' — it resolves to an empty string at runtime (silently).`,\n hint: inFilter\n ? `Check the field name against the object's field definitions (e.g. '{record.full_name}', ` +\n `not '{record.full_naem}'); system columns like id/created_at/owner are also addressable. ` +\n `This gates the build rather than warning: an absent condition WIDENS the query, so the ` +\n `runtime has already decided to refuse this node.`\n : `Check the field name against the object's field definitions (e.g. '{record.full_name}', ` +\n `not '{record.full_naem}'). System columns like id/created_at/owner are also addressable.`,\n });\n continue;\n }\n\n if (nextIsIdentifier) {\n const headType = fieldTypes.get(head) ?? '';\n if (RELATION_TYPES.has(headType) && !expandSet.has(head)) {\n const key = rest.join('.');\n if (seenTraversal.has(key)) continue;\n seenTraversal.add(key);\n findings.push({\n severity: inFilter ? 'error' : 'warning',\n rule: FLOW_TEMPLATE_LOOKUP_TRAVERSAL,\n where,\n path: nodePath,\n message: inFilter\n ? `${nodeType} filter references '{record.${key}}', a cross-object hop through the ` +\n `${headType} field '${head}' — the flow record carries '${head}' as a scalar id, not an ` +\n `expanded object, so the token resolves to nothing and the condition is DROPPED from the ` +\n `query instead of narrowing it. The node refuses to run at execution time (#3810).`\n : `template references '{record.${key}}', a cross-object hop through the ${headType} field ` +\n `'${head}' — the flow record carries '${head}' as a scalar id, not an expanded object, so ` +\n `this resolves to an empty string at runtime (silently).`,\n hint: inFilter\n ? `Opt in to resolve it: add '${head}' to the start node's config.expand (#3475) and the ` +\n `engine re-reads it as the run's identity. Otherwise filter on the foreign-key id directly ` +\n `('{record.${head}}'), or project the value via a formula field on '${objectName}'. This ` +\n `gates the build rather than warning: an absent condition WIDENS the query.`\n : `Opt in to resolve it: add '${head}' to the start node's config.expand (#3475) and the ` +\n `engine re-reads it as the run's identity. Otherwise reference the foreign-key id directly ` +\n `('{record.${head}}'), or project the value via a formula field on '${objectName}'.`,\n });\n }\n // STRUCTURED_TYPES + any other scalar `.sub` access is left alone:\n // json/composite/record sub-paths are legitimate in-row reads, and\n // a plain scalar `.sub` is rare enough that flagging it would risk\n // more false positives than it prevents.\n }\n }\n }\n });\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail: a flow `update_record` node that writes a field the\n// target object declares `readonly: true`, under a non-system run identity, is\n// a SILENT NO-OP. The objectql engine strips static-`readonly` fields from a\n// non-system UPDATE payload (#2948), so the intended write never lands — yet\n// the step still reports `success`. #3407/#3413 made that strip observable at\n// RUN time (a step warning + `droppedFields`); this rule shifts the discovery\n// LEFT to `os validate` / `os build`, so an author finds the mismatch at design\n// time instead of by reading server WARN logs days later (#3425).\n//\n// Scope — deliberately narrow to keep it false-positive-free:\n//\n// • Only `update_record`. INSERT is engine-exempt from the readonly strip (a\n// `create_record` may legitimately seed readonly columns; the ingress strip\n// added in #3043 lives in metadata-protocol, which the flow engine bypasses\n// by calling the data engine directly), so a create writing a readonly\n// field is NOT a no-op and is never flagged.\n//\n// • Only `runAs !== 'system'`. A `runAs:'system'` run is elevated and the\n// engine skips the strip entirely, so a system flow legitimately MAINTAINS\n// readonly fields (\"users can't edit this, but automation does\"). That is\n// the intended channel, so it is never flagged.\n//\n// • Static `readonly:true` + a LITERAL field name is a 100%-certain no-op →\n// ERROR (gates the build). `readonlyWhen` is per-record-state — it strips\n// only on records whose predicate is TRUE at run time, so it MAY silently\n// not land → WARNING (advisory). A templated object name or a non-literal\n// `fields` map is not statically knowable → skipped, no guess.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019): no I/O, no runtime. Shared by\n// the CLI and any other consumer (AI authoring), so hand-authored and generated\n// flows are held to the same bar.\n\nimport { walkFlowNodes, flowNodeLabel } from './flow-walk.js';\n\nexport type ReadonlyFlowWriteSeverity = 'error' | 'warning';\n\nexport interface ReadonlyFlowWriteFinding {\n severity: ReadonlyFlowWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"approve_deal\" › node \"Mark approved\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[3].config.fields.approval_status`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const FLOW_UPDATE_READONLY_FIELD = 'flow-update-readonly-field';\nexport const FLOW_UPDATE_READONLY_WHEN_FIELD = 'flow-update-readonly-when-field';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({\n name,\n ...(def as AnyRec),\n }));\n }\n return [];\n}\n\ninterface FieldReadonlyMeta {\n /** Static `readonly: true`. */\n readonly: boolean;\n /** A non-empty `readonlyWhen` predicate is declared. */\n readonlyWhen: boolean;\n}\n\n/**\n * object name → (field name → readonly metadata). Handles both `fields` shapes\n * (array of `{name, readonly, readonlyWhen}` and name-keyed map). A field with\n * neither flag is recorded as `{false, false}` so callers can distinguish a\n * \"known-writable field\" from an \"unknown field\" (absent from the map).\n */\nfunction buildReadonlyIndex(objects: AnyRec[]): Map<string, Map<string, FieldReadonlyMeta>> {\n const idx = new Map<string, Map<string, FieldReadonlyMeta>>();\n for (const obj of objects) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fieldMap = new Map<string, FieldReadonlyMeta>();\n const collect = (fieldName: string, def: AnyRec): void => {\n const rw = def?.readonlyWhen;\n const readonlyWhen = rw != null && !(typeof rw === 'string' && rw.trim() === '');\n fieldMap.set(fieldName, { readonly: def?.readonly === true, readonlyWhen });\n };\n const fields = obj.fields;\n if (Array.isArray(fields)) {\n for (const f of fields as AnyRec[]) {\n const fn = (f as AnyRec)?.name;\n if (typeof fn === 'string') collect(fn, f as AnyRec);\n }\n } else if (fields && typeof fields === 'object') {\n for (const [fn, def] of Object.entries(fields as AnyRec)) collect(fn, def as AnyRec);\n }\n idx.set(name, fieldMap);\n }\n return idx;\n}\n\n/**\n * The target object of an `update_record` node, when statically knowable. Reads\n * the canonical `objectName` and its historical `object` alias — a pre-parse\n * source may still carry the alias during the protocol-17 window, until the\n * 'flow-node-crud-object-alias' conversion (#3796) canonicalizes it at load. A\n * templated value (contains `{`) is dynamic — return undefined so the node is\n * skipped rather than guessed.\n */\nfunction readLiteralObjectName(config: AnyRec): string | undefined {\n const raw = config.objectName ?? config.object;\n if (typeof raw !== 'string' || raw.includes('{')) return undefined;\n return raw || undefined;\n}\n\n/**\n * Validate flow `update_record` writes against target-object readonly\n * declarations. Pure and dependency-free; safe on pre- or post-parse stacks.\n */\nexport function validateReadonlyFlowWrites(stack: AnyRec): ReadonlyFlowWriteFinding[] {\n const findings: ReadonlyFlowWriteFinding[] = [];\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n const roIndex = buildReadonlyIndex(asArray(stack.objects));\n\n flows.forEach((flow, flowIndex) => {\n // `runAs` defaults to 'user' (schema default). Only an explicit 'system'\n // run bypasses the strip, so treat anything else — including an unauthored\n // (undefined) runAs — as strip-subject.\n if (flow.runAs === 'system') return;\n const runAs = flow.runAs === 'user' || flow.runAs === 'system' ? flow.runAs : 'user';\n\n const flowName = typeof flow.name === 'string' ? flow.name : `#${flowIndex}`;\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions.\n // A readonly write inside a `catch` branch is the same certain no-op as one\n // at the top level, and this rule gates on it (#4380).\n const walked = walkFlowNodes(flow, `flows[${flowIndex}]`);\n\n walked.forEach(({ node, path: nodePath, regionTrail }, walkIndex) => {\n if (node?.type !== 'update_record') return;\n const config = (node.config ?? {}) as AnyRec;\n\n const objectName = readLiteralObjectName(config);\n if (!objectName) return; // templated / dynamic object — not statically knowable\n const fieldMap = roIndex.get(objectName);\n if (!fieldMap) return; // object defined by another package — cannot judge its fields\n\n const fields = config.fields;\n // A non-literal write map (templated string, spread, array) is not\n // statically knowable — skip rather than guess.\n if (!fields || typeof fields !== 'object' || Array.isArray(fields)) return;\n\n const nodeName = flowNodeLabel(node, walkIndex);\n const where = regionTrail\n ? `flow \"${flowName}\" › ${regionTrail} › node \"${nodeName}\"`\n : `flow \"${flowName}\" › node \"${nodeName}\"`;\n\n for (const fieldName of Object.keys(fields as AnyRec)) {\n const meta = fieldMap.get(fieldName);\n // Unknown field — `validate-flow-node-writes.ts` owns that question\n // (`flow-node-write-unknown-field`, also gating). This rule is about a\n // field the object DOES declare and the engine then strips; a name that\n // resolves to no column is a different failure with a different fix, so\n // the two never double-report the same key.\n if (!meta) continue;\n\n if (meta.readonly) {\n findings.push({\n severity: 'error',\n rule: FLOW_UPDATE_READONLY_FIELD,\n where,\n path: `${nodePath}.config.fields.${fieldName}`,\n message:\n `writes field '${fieldName}', which object '${objectName}' declares readonly:true. Under ` +\n `runAs:'${runAs}' the engine silently strips readonly fields from the UPDATE payload (#2948), ` +\n `so this write never lands — while the step still reports success.`,\n hint:\n `If automation is meant to maintain this field, declare the flow runAs:'system' (the intended ` +\n `channel — readonly governs the end-user/API surface, not trusted system writers). Otherwise ` +\n `remove '${fieldName}' from this update_record node.`,\n });\n } else if (meta.readonlyWhen) {\n findings.push({\n severity: 'warning',\n rule: FLOW_UPDATE_READONLY_WHEN_FIELD,\n where,\n path: `${nodePath}.config.fields.${fieldName}`,\n message:\n `writes field '${fieldName}', which object '${objectName}' declares readonlyWhen. On records ` +\n `where that predicate is TRUE, a runAs:'${runAs}' UPDATE strips the field (#3042), so this ` +\n `write may silently not land depending on the record's state.`,\n hint:\n `If automation must maintain this field regardless of record state, run the flow runAs:'system'. ` +\n `Otherwise confirm this node only targets records whose readonlyWhen predicate is FALSE.`,\n });\n }\n }\n });\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for the `defineView` container shape.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate`. It\n// catches the \"flat view object\" authoring mistake the schema alone cannot\n// surface: `ViewSchema` is a container (`{ list, form, listViews, formViews }`)\n// whose slots are all optional, and Zod strips unknown keys — so a flat list\n// view (`{ name: 'all_tasks', label, type: 'grid', columns: [...] }`) parses\n// to an EMPTY container. The stack validates, the loader finds nothing to\n// expand, and the Console silently renders no view (no switcher entry). The\n// third-party 15.1 evaluation hit exactly this via the old docs.\n//\n// Runs PRE-parse (on the normalizeStackInput output, before the\n// ObjectStackDefinition parse): post-parse the flat keys are already stripped\n// and the mistake is indistinguishable from an intentionally empty container.\n//\n// Independent ViewItems (`viewKind` + `config`) are legal `views: []` entries\n// (the loader registers them as-is) and are not flagged.\n\nexport type ViewContainerSeverity = 'error' | 'warning';\n\nexport interface ViewContainerFinding {\n severity: ViewContainerSeverity;\n rule: string;\n /** Human-readable location, e.g. `views[0] (\"all_tasks\")`. */\n where: string;\n /** Config path, e.g. `views[0]`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const VIEW_CONTAINER_SHAPE = 'view-container-shape';\n\ntype AnyRec = Record<string, unknown>;\n\nconst CONTAINER_SLOT_KEYS = ['list', 'form', 'listViews', 'formViews'] as const;\n\n/** Coerce an array-or-name-keyed-map collection to indexed entries. */\nfunction asEntries(v: unknown): Array<{ key: string; value: unknown }> {\n if (Array.isArray(v)) return v.map((value, i) => ({ key: `[${i}]`, value }));\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, value]) => ({ key: `.${name}`, value }));\n }\n return [];\n}\n\n/** Number of views a parsed-or-raw container actually carries. */\nfunction containerViewCount(rec: AnyRec): number {\n const named = (slot: unknown): number =>\n slot && typeof slot === 'object' && !Array.isArray(slot) ? Object.keys(slot as AnyRec).length : 0;\n return (rec.list ? 1 : 0) + (rec.form ? 1 : 0) + named(rec.listViews) + named(rec.formViews);\n}\n\n/**\n * Validate that every stack-level `views` entry is a real view container (or\n * an independent ViewItem). Flat list-view objects and view-less containers\n * are reported as errors with a wrap-it fix hint.\n */\nexport function validateViewContainers(stack: Record<string, unknown>): ViewContainerFinding[] {\n const out: ViewContainerFinding[] = [];\n if (!stack || typeof stack !== 'object') return out;\n\n for (const { key, value } of asEntries((stack as AnyRec).views)) {\n // Non-object entries are the schema step's problem, not this rule's.\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue;\n const rec = value as AnyRec;\n\n // Independent ViewItem (`viewKind` discriminator) — registered as-is.\n if (rec.viewKind != null) continue;\n\n if (containerViewCount(rec) > 0) continue;\n\n const label = typeof rec.name === 'string' ? ` (\"${rec.name}\")` : '';\n const hasContainerSlot = CONTAINER_SLOT_KEYS.some((k) => k in rec);\n // Flat list-view fingerprint: view-ish keys at the top level where the\n // container slots should be.\n const looksFlat = !hasContainerSlot\n && ['type', 'columns', 'data', 'filter', 'sort'].some((k) => k in rec);\n\n out.push({\n severity: 'error',\n rule: VIEW_CONTAINER_SHAPE,\n where: `views${key}${label}`,\n path: `views${key}`,\n message: looksFlat\n ? 'Flat list-view object is not a view container: `ViewSchema` strips its keys, '\n + 'so it parses to an EMPTY container — zero views register and the Console '\n + 'renders no view for it.'\n : 'View container defines no views — all of `list` / `form` / `listViews` / '\n + '`formViews` are absent or empty, so nothing registers.',\n hint: 'Wrap every view in a defineView container: defineView({ list: { type, data, '\n + 'columns, ... }, listViews: { ... }, formViews: { ... } }). See '\n + 'examples/app-showcase/src/ui/views/task.view.ts.',\n });\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time diagnostics for the SDUI scoped-styling model (ADR-0065).\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019): the same bar holds for\n// hand-authored and AI-generated pages, run from `os validate`/`compile` and\n// reusable by AI authoring. It catches the deterministic ways a `responsiveStyles`\n// block silently fails or drifts — the class of mistake an AI author is most\n// likely to make — *before* render, with actionable hints it can self-correct on.\n//\n// What it does NOT catch: whether the result looks good. Visual/semantic quality\n// (contrast, balance, \"is it ugly\") is only catchable by rendering + a VLM gate,\n// which is a separate, render-time concern (ADR-0065 §Decision-5).\n\nexport type StyleSeverity = 'error' | 'warning';\n\nexport interface StyleFinding {\n severity: StyleSeverity;\n rule: string;\n /** Human-readable location, e.g. `page \"pricing\" › node \"plan_solo\"`. */\n where: string;\n /** Config path, e.g. `pages[0].regions[0].components[1]`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries).\nexport const STYLE_NODE_MISSING_ID = 'style-node-missing-id';\nexport const STYLE_CLASSNAME_TAILWIND = 'style-classname-tailwind';\nexport const STYLE_RESPONSIVE_NO_BASE = 'style-responsive-no-base';\nexport const STYLE_UNKNOWN_CSS_PROPERTY = 'style-unknown-css-property';\nexport const STYLE_UNKNOWN_TOKEN = 'style-unknown-token';\n\ntype AnyRec = Record<string, unknown>;\n\nconst BREAKPOINTS = ['large', 'medium', 'small', 'xsmall'] as const;\n\n/** SDUI design-token palette (ADR-0065) + base theme tokens, referenced as\n * `var(--name)`. Authors should resolve values against these. Kept in sync with\n * `apps/console/src/index.css` / `@object-ui/components` `:root`. */\nconst KNOWN_TOKENS = new Set<string>([\n // SDUI tokens\n 'space-1', 'space-2', 'space-3', 'space-4', 'space-5', 'space-6', 'space-8', 'space-10', 'space-12',\n 'radius', 'radius-sm', 'radius-md', 'radius-lg', 'radius-xl',\n 'shadow-sm', 'shadow-md', 'shadow-lg',\n 'surface', 'surface-sunken', 'text-strong', 'text-muted', 'brand', 'brand-foreground', 'hairline',\n // Base theme tokens (shadcn) — usually wrapped as hsl(var(--x)).\n 'background', 'foreground', 'card', 'card-foreground', 'popover', 'popover-foreground',\n 'primary', 'primary-foreground', 'secondary', 'secondary-foreground',\n 'muted', 'muted-foreground', 'accent', 'accent-foreground',\n 'destructive', 'destructive-foreground', 'border', 'input', 'ring',\n 'success', 'success-foreground', 'warning', 'warning-foreground',\n 'chart-1', 'chart-2', 'chart-3', 'chart-4', 'chart-5',\n]);\n\n/** Common CSS properties (camelCase) an SDUI block realistically sets. Generous\n * on purpose: an unknown property is only a *warning* (typo catcher), never a\n * blocker. Custom properties (`--x`) are always allowed. */\nconst KNOWN_CSS_PROPERTIES = new Set<string>([\n 'display', 'position', 'top', 'right', 'bottom', 'left', 'inset', 'zIndex', 'overflow', 'overflowX', 'overflowY', 'visibility', 'boxSizing', 'float', 'clear',\n 'width', 'height', 'minWidth', 'minHeight', 'maxWidth', 'maxHeight', 'aspectRatio',\n 'margin', 'marginTop', 'marginRight', 'marginBottom', 'marginLeft', 'marginInline', 'marginBlock',\n 'padding', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft', 'paddingInline', 'paddingBlock',\n 'flex', 'flexDirection', 'flexWrap', 'flexGrow', 'flexShrink', 'flexBasis', 'alignItems', 'alignContent', 'alignSelf', 'justifyContent', 'justifyItems', 'justifySelf', 'gap', 'rowGap', 'columnGap', 'order', 'placeItems', 'placeContent',\n 'grid', 'gridTemplate', 'gridTemplateColumns', 'gridTemplateRows', 'gridTemplateAreas', 'gridColumn', 'gridRow', 'gridArea', 'gridAutoFlow', 'gridAutoColumns', 'gridAutoRows',\n 'color', 'backgroundColor', 'background', 'backgroundImage', 'backgroundSize', 'backgroundPosition', 'backgroundRepeat', 'backgroundClip', 'opacity', 'mixBlendMode',\n 'fontSize', 'fontWeight', 'fontFamily', 'fontStyle', 'lineHeight', 'letterSpacing', 'textAlign', 'textTransform', 'textDecoration', 'textOverflow', 'whiteSpace', 'wordBreak', 'overflowWrap', 'fontVariantNumeric', 'verticalAlign', 'textShadow',\n 'border', 'borderTop', 'borderRight', 'borderBottom', 'borderLeft', 'borderColor', 'borderWidth', 'borderStyle', 'borderRadius', 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomLeftRadius', 'borderBottomRightRadius', 'outline', 'outlineOffset',\n 'boxShadow', 'transform', 'transformOrigin', 'transition', 'transitionProperty', 'transitionDuration', 'transitionTimingFunction', 'transitionDelay', 'animation', 'filter', 'backdropFilter', 'willChange',\n 'cursor', 'pointerEvents', 'userSelect', 'objectFit', 'objectPosition', 'content',\n]);\n\nconst VAR_RE = /var\\(\\s*--([a-zA-Z0-9-]+)\\s*[,)]/g;\n\n// High-precision Tailwind-utility detection. A `className` in page metadata is\n// \"Tailwind-looking\" if any token is a responsive/state variant, an arbitrary\n// `[…]` value, a known utility stem followed by a Tailwind *value* (number /\n// fraction / size keyword), or a bare layout utility. Tuned to NOT trip on\n// ordinary custom class names (e.g. `my-custom-scope`, `os-s-plan_solo`).\nconst TW_VARIANT = /^(sm|md|lg|xl|2xl|hover|focus|active|disabled|dark|group-hover|peer-[a-z]+|first|last|odd|even):/;\nconst TW_STEM_VALUE = /^-?(p|m|px|py|pt|pb|pl|pr|mx|my|mt|mb|ml|mr|gap|gap-x|gap-y|space-x|space-y|w|h|min-w|max-w|min-h|max-h|size|text|leading|tracking|bg|border|rounded|shadow|ring|opacity|inset|top|bottom|left|right|z|order|col|row|grid-cols|grid-rows|basis)-(\\d+(\\.\\d+)?|\\d+\\/\\d+|px|full|auto|none|screen|min|max|fit|xs|sm|md|lg|xl|2xl|3xl|4xl|5xl|6xl)$/;\nconst TW_BARE = /^(flex|grid|block|inline|inline-block|inline-flex|hidden|contents|table|flow-root|grow|shrink|truncate|italic|underline|uppercase|lowercase|capitalize|antialiased|absolute|relative|fixed|sticky|static|isolate|flex-col|flex-row|flex-wrap|flex-nowrap|items-center|items-start|items-end|items-stretch|justify-center|justify-between|justify-around|justify-start|justify-end|text-center|text-left|text-right|font-bold|font-semibold|font-medium|font-normal|tabular-nums)$/;\n\nfunction looksLikeTailwind(className: string): boolean {\n return className.split(/\\s+/).some((tok) => {\n if (!tok) return false;\n if (TW_VARIANT.test(tok)) return true;\n if (/\\[[^\\]]+\\]/.test(tok)) return true; // arbitrary value, e.g. p-[13px]\n if (TW_STEM_VALUE.test(tok)) return true;\n if (TW_BARE.test(tok)) return true;\n return false;\n });\n}\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** Child nodes can hang off `children`, `properties.children`, `body`, or\n * `properties.body` depending on block type — collect them all. */\nfunction childrenOf(node: AnyRec): AnyRec[] {\n const props = (node.properties as AnyRec) ?? {};\n const out: AnyRec[] = [];\n for (const c of [node.children, props.children, node.body, props.body]) {\n if (Array.isArray(c)) out.push(...(c.filter((x) => x && typeof x === 'object') as AnyRec[]));\n }\n return out;\n}\n\nfunction checkNode(node: AnyRec, pageName: string, path: string, findings: StyleFinding[]): void {\n const id = typeof node.id === 'string' ? node.id : undefined;\n const type = typeof node.type === 'string' ? node.type : 'node';\n const where = `page \"${pageName}\" › ${id ? `node \"${id}\"` : `<${type}>`}`;\n const rs = node.responsiveStyles as AnyRec | undefined;\n const hasRs = !!rs && typeof rs === 'object' && BREAKPOINTS.some((b) => rs[b]);\n\n // (1) responsiveStyles needs an id to scope to — else the CSS is dropped.\n if (hasRs && !id) {\n findings.push({\n severity: 'error', rule: STYLE_NODE_MISSING_ID, where, path,\n message: `Node has responsiveStyles but no \\`id\\`; scoped CSS cannot be generated and the styles are silently dropped.`,\n hint: `Add a stable \\`id\\` to this node.`,\n });\n }\n\n // (2) responsive breakpoint without a `large` base → unstyled at desktop.\n if (hasRs && !rs!.large && BREAKPOINTS.slice(1).some((b) => rs![b])) {\n findings.push({\n severity: 'warning', rule: STYLE_RESPONSIVE_NO_BASE, where, path,\n message: `responsiveStyles sets a smaller breakpoint but no \\`large\\` base; the node is unstyled at desktop width.`,\n hint: `Put the unconditional/base styles under \\`responsiveStyles.large\\` (desktop-first).`,\n });\n }\n\n // (3) className that looks like Tailwind → won't render from metadata.\n if (typeof node.className === 'string' && node.className.trim() && looksLikeTailwind(node.className)) {\n findings.push({\n severity: 'warning', rule: STYLE_CLASSNAME_TAILWIND, where, path,\n message: `\\`className\\` contains Tailwind-looking utilities (\"${node.className.trim().slice(0, 60)}\"); these are not compiled from metadata and will silently do nothing.`,\n hint: `Style this node with \\`responsiveStyles\\` + design tokens instead of \\`className\\` (ADR-0065).`,\n });\n }\n\n // (4)+(5) unknown CSS property / unknown token inside each breakpoint map.\n if (rs && typeof rs === 'object') {\n for (const bp of BREAKPOINTS) {\n const map = rs[bp] as AnyRec | undefined;\n if (!map || typeof map !== 'object') continue;\n for (const [prop, value] of Object.entries(map)) {\n if (!prop.startsWith('--') && !KNOWN_CSS_PROPERTIES.has(prop)) {\n findings.push({\n severity: 'warning', rule: STYLE_UNKNOWN_CSS_PROPERTY, where, path: `${path}.responsiveStyles.${bp}`,\n message: `Unknown CSS property \"${prop}\" (typo?); if unintended it will not apply.`,\n hint: `Use a camelCase CSS property name (e.g. \\`flexDirection\\`, \\`backgroundColor\\`).`,\n });\n }\n if (typeof value === 'string') {\n let m: RegExpExecArray | null;\n VAR_RE.lastIndex = 0;\n while ((m = VAR_RE.exec(value))) {\n const token = m[1];\n if (!KNOWN_TOKENS.has(token) && !token.startsWith('tw-')) {\n findings.push({\n severity: 'warning', rule: STYLE_UNKNOWN_TOKEN, where, path: `${path}.responsiveStyles.${bp}.${prop}`,\n message: `References unknown design token \\`var(--${token})\\` (typo?); it will not resolve.`,\n hint: `Use a token from the ADR-0065 palette (e.g. \\`var(--space-6)\\`, \\`var(--surface)\\`, \\`hsl(var(--primary))\\`).`,\n });\n }\n }\n }\n }\n }\n }\n\n // Recurse.\n const kids = childrenOf(node);\n for (let i = 0; i < kids.length; i++) {\n checkNode(kids[i], pageName, `${path}.children[${i}]`, findings);\n }\n}\n\n/**\n * Validate every page's component tree for SDUI styling correctness (ADR-0065).\n * Returns findings (empty = clean). `error` findings describe styles that are\n * silently dropped and should fail validate/build; `warning` findings are\n * advisory (typos, drift, footguns).\n */\nexport function validateResponsiveStyles(stack: AnyRec): StyleFinding[] {\n const findings: StyleFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n const pageName = typeof page.name === 'string' ? page.name : `pages[${p}]`;\n const regions = asArray(page.regions);\n for (let r = 0; r < regions.length; r++) {\n const components = asArray(regions[r].components);\n for (let c = 0; c < components.length; c++) {\n checkNode(components[c], pageName, `pages[${p}].regions[${r}].components[${c}]`, findings);\n }\n }\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time diagnostics for AI-authored HTML-source pages (ADR-0080).\n//\n// Applies to `kind:'html'` (and its deprecated alias `kind:'jsx'`) — the tier\n// whose `source` is constrained JSX/HTML parsed (never executed) to the tree.\n// `kind:'react'` (ADR-0081) is intentionally NOT linted here: its source is\n// real JavaScript, not constrained JSX, so the constrained parser would\n// false-error on hooks / expressions.\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` / `os\n// build`. An html page's `source` is a constrained JSX/Tailwind string\n// compiled (parsed, never executed) to the SDUI tree at save time. This gate\n// parses it at author time so malformed source fails loudly (ADR-0078) instead\n// of being stored and breaking only at render.\n//\n// Scope: parse-level — syntax, tag matching, and forbidden constructs (event\n// handlers, dangerouslySetInnerHTML). Full component/prop whitelist validation\n// needs the registry manifest (a cross-repo artifact); when that is wired,\n// thread it through `compile()` here. Until then this catches the structural\n// class of error an AI author is most likely to emit.\n\nimport { parseJsx, compile, type Manifest } from '@objectstack/sdui-parser';\n\nexport type JsxPageSeverity = 'error' | 'warning';\n\nexport interface JsxPageFinding {\n severity: JsxPageSeverity;\n rule: string;\n /** Human-readable location, e.g. `page \"command_center\" › <flex>`. */\n where: string;\n /** Config path, e.g. `pages[3].source`. */\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\nexport function validateJsxPages(stack: AnyRec, opts: { manifest?: Manifest } = {}): JsxPageFinding[] {\n const findings: JsxPageFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n // html tier (+ deprecated 'jsx' alias). react pages are not constrained JSX.\n if (!page || (page.kind !== 'html' && page.kind !== 'jsx')) continue;\n const name = String(page.name ?? `#${p}`);\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') {\n // (PageSchema's superRefine also covers this; keep it for the build path.)\n findings.push({\n severity: 'error',\n rule: 'jsx-page-empty-source',\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: `kind:'${page.kind}' page has no \\`source\\`.`,\n hint: 'Author the page as a constrained JSX/Tailwind string in `source`.',\n });\n continue;\n }\n // With a component manifest, do full validation (unknown component, missing/\n // wrong prop, bad enum, bindings); without it, parse-level (syntax/structure).\n const { diagnostics } = opts.manifest ? compile(source, opts.manifest) : parseJsx(source);\n for (const d of diagnostics) {\n findings.push({\n severity: d.severity,\n rule: `jsx-${d.code}`,\n where: d.tag ? `page \"${name}\" › <${d.tag}>` : `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: d.message,\n hint: 'The source is parsed (never executed) and compiled to the SDUI tree at save time — fix the JSX.',\n });\n }\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time syntax gate for `kind:'react'` pages (ADR-0081).\n//\n// A react page's `source` is REAL JavaScript/JSX executed at render by the\n// runtime — so the constrained JSX parser (validate-jsx-pages) cannot check it.\n// We instead transpile it with Sucrase (the same transpiler the runtime uses),\n// transpile-ONLY — never executed — to surface syntax errors at `os build`\n// instead of at render (ADR-0078: fail loudly at author time). It does NOT\n// validate runtime behaviour (a transpiling page can still throw at render);\n// the render-time error boundary owns that.\n\nimport { createRequire } from 'node:module';\nimport type { transform as sucraseTransform } from 'sucrase';\n\n// Sucrase must NOT be imported at module top level: it is ~1.5 MB of CJS\n// (~16 ms cold require), and @objectstack/lint sits on the kernel boot path —\n// while this gate only runs when a `kind:'react'` page is actually validated\n// (rare, trusted tier). Same boot-path contract as the TypeScript compiler in\n// validate-react-page-props.ts: loaded lazily, on first use, staying a regular\n// dependency in package.json. Guarded by lazy-deps.test.ts.\n//\n// `node:module` is a Node builtin, untouched by esbuild/tsup, so the static\n// `createRequire` import survives bundling; the `createRequire(...)` call is\n// deferred because `import.meta.url` is rewritten to an empty stub in the CJS\n// build (same pattern as driver-sqlite-wasm's knex-wasm-dialect).\nlet cachedTransform: typeof sucraseTransform | null = null;\nfunction loadSucraseTransform(): typeof sucraseTransform {\n if (cachedTransform) return cachedTransform;\n const anchor =\n typeof import.meta !== 'undefined' && import.meta.url\n ? import.meta.url\n : typeof __filename !== 'undefined'\n ? __filename\n : process.cwd() + '/';\n try {\n cachedTransform = (createRequire(anchor)('sucrase') as { transform: typeof sucraseTransform }).transform;\n } catch (err) {\n throw new Error(\n `@objectstack/lint: validating a kind:'react' page requires the \"sucrase\" package, which could not be loaded ` +\n `(${err instanceof Error ? err.message : String(err)}). It is a declared dependency of @objectstack/lint — ` +\n `if this deployment prunes packages, keep \"sucrase\" in the image; it is only loaded when a react-source page is validated.`,\n );\n }\n return cachedTransform;\n}\n\nexport type ReactPageSeverity = 'error' | 'warning';\n\nexport interface ReactPageFinding {\n severity: ReactPageSeverity;\n rule: string;\n where: string;\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\nexport function validateReactPages(stack: AnyRec): ReactPageFinding[] {\n const findings: ReactPageFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n if (!page || page.kind !== 'react') continue;\n const name = String(page.name ?? `#${p}`);\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') {\n findings.push({\n severity: 'error',\n rule: 'react-page-empty-source',\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: \"kind:'react' page has no `source`.\",\n hint: 'Author the page as a real React component string in `source`.',\n });\n continue;\n }\n // Outside the try below on purpose: a missing transpiler must surface as\n // an error, not be swallowed as a syntax finding.\n const transform = loadSucraseTransform();\n try {\n // transpile-only (no eval) — catches syntax errors, unterminated JSX, etc.\n transform(source, { transforms: ['jsx', 'typescript'], production: true });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n findings.push({\n severity: 'error',\n rule: 'react-page-syntax',\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: `kind:'react' source has a syntax error: ${message.split('\\n')[0]}`,\n hint: 'The source is transpiled (never executed) at build to catch syntax errors early — fix the JS/JSX.',\n });\n }\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time prop check for `kind:'react'` pages (ADR-0081 Phase 2). The syntax\n// gate (validate-react-pages) confirms the source parses; this confirms the\n// AUTHOR USED THE COMPONENT CONTRACT correctly — it parses the real JSX with the\n// TypeScript compiler, finds usages of the injected blocks (<ObjectForm>,\n// <ListView>, …), and checks each against the react-tier contract\n// (REACT_BLOCKS in @objectstack/spec):\n//\n// - missing a required binding prop (e.g. <ObjectForm> with no objectName)\n// → error. (Only the React-enforceable overlay props are required-checked;\n// a spread `{...props}` escapes the check since props may come from it.)\n// - a prop that is a near-miss (edit distance ≤ 2) of a known prop\n// (e.g. `onSucces` → `onSuccess`) → warning. We do NOT flag arbitrary\n// unknown props (the contract's data props are a curated subset) — only the\n// likely typos, to keep false positives near zero.\n// - <ObjectChart>'s data BINDINGS, by reading the attribute VALUES (#3701,\n// retargeted to the spec shape in #3729). See the block comment above\n// `checkObjectChart`.\n// - <ListView>'s `searchableFields` entries, resolved against the bound\n// object's declared fields (#4329) — the react-surface twin of the\n// metadata rule `searchable-field-unknown`, sharing its core.\n// - EVERY OTHER field-bearing prop a react block can author (#4340) — see\n// `REACT_FIELD_SPECS` below and the ledger beside it.\n// - a `record:*` block on this surface at all (#4413) → error. They render\n// from a record page's shared record context, which a react page does not\n// mount, so they come back empty however they are bound. See\n// `recordContextFinding`.\n//\n// Reading values is opt-in per block and per prop: everything below evaluates\n// only STATIC literals (`objectName=\"invoice\"`, an `aggregate={{…}}` object\n// literal). A value that comes from a variable, a call, or a spread is not\n// knowable at build time and is skipped silently — an unresolvable binding is\n// not a wrong one (ADR-0072 D1).\n\nimport { createRequire } from 'node:module';\nimport type ts from 'typescript';\nimport {\n REACT_BLOCKS,\n RECORD_CONTEXT_BLOCK_TAGS,\n REACT_RECORD_BLOCK_ALTERNATIVES,\n chartAggregateResultKeys,\n isRecordContextBlockType,\n} from '@objectstack/spec/ui';\nimport { VALID_AST_OPERATORS } from '@objectstack/spec/data';\nimport {\n checkSearchableFieldList,\n indexObjectSearchTargets,\n} from './validate-searchable-fields.js';\nimport {\n COMPONENT_FIELD_SPECS,\n checkFieldRefs,\n componentFieldRefs,\n fieldRefsFrom,\n indexObjectFields,\n sortFieldRefs,\n type FieldRef,\n type PageFieldFinding,\n} from './validate-page-field-bindings.js';\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\n// The TypeScript compiler must NOT be imported at module top level: it is\n// ~9 MB of CJS (~70 ms+ to parse, worse on container cold starts), and\n// @objectstack/lint sits on the kernel boot path — while this gate only runs\n// when a `kind:'react'` page is actually validated (rare, trusted tier). An\n// eager import also hard-crashes boot in deployments that prune the package\n// from the image (cloud's Docker pruner did exactly that). So the compiler is\n// loaded lazily, on first use, and stays a regular dependency in package.json.\n// Guarded by lazy-deps.test.ts.\n//\n// `node:module` is a Node builtin, untouched by esbuild/tsup, so the static\n// `createRequire` import survives bundling; the `createRequire(...)` call is\n// deferred because `import.meta.url` is rewritten to an empty stub in the CJS\n// build (same pattern as driver-sqlite-wasm's knex-wasm-dialect).\nlet cachedTs: typeof ts | null = null;\nfunction loadTypeScript(): typeof ts {\n if (cachedTs) return cachedTs;\n const anchor =\n typeof import.meta !== 'undefined' && import.meta.url\n ? import.meta.url\n : typeof __filename !== 'undefined'\n ? __filename\n : process.cwd() + '/';\n try {\n cachedTs = createRequire(anchor)('typescript') as typeof ts;\n } catch (err) {\n throw new Error(\n `@objectstack/lint: validating a kind:'react' page requires the \"typescript\" package, which could not be loaded ` +\n `(${err instanceof Error ? err.message : String(err)}). It is a declared dependency of @objectstack/lint — ` +\n `if this deployment prunes packages, keep \"typescript\" in the image; it is only loaded when a react-source page is validated.`,\n );\n }\n return cachedTs;\n}\n\nexport type ReactPropSeverity = 'error' | 'warning';\n\nexport interface ReactPropFinding {\n severity: ReactPropSeverity;\n rule: string;\n where: string;\n path: string;\n message: string;\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\ninterface BlockSpec {\n requiredBindings: string[];\n knownProps: Set<string>;\n}\nconst BLOCKS: Map<string, BlockSpec> = new Map(\n (REACT_BLOCKS as Array<{ tag: string; interactions: Array<{ name: string; required?: boolean }> }>).map((b) => [\n b.tag,\n {\n requiredBindings: b.interactions.filter((i) => i.required).map((i) => i.name),\n knownProps: new Set(b.interactions.map((i) => i.name)),\n },\n ]),\n);\n\nfunction editDistance(a: string, b: string, cap = 2): number {\n if (Math.abs(a.length - b.length) > cap) return cap + 1;\n const dp = Array.from({ length: a.length + 1 }, (_, i) => i);\n for (let j = 1; j <= b.length; j++) {\n let prev = dp[0];\n dp[0] = j;\n for (let i = 1; i <= a.length; i++) {\n const tmp = dp[i];\n dp[i] = Math.min(dp[i] + 1, dp[i - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1));\n prev = tmp;\n }\n }\n return dp[a.length];\n}\n\nfunction nearestKnown(prop: string, known: Set<string>): string | null {\n if (known.has(prop)) return null;\n let best: string | null = null;\n let bestD = 3;\n for (const k of known) {\n const d = editDistance(prop, k);\n if (d < bestD) { bestD = d; best = k; }\n }\n return bestD <= 2 ? best : null;\n}\n\n// ─── Static attribute values ──────────────────────────────────────────────\n//\n// The gate above only needed prop NAMES. Checking a binding needs its VALUE,\n// and a JSX attribute's value is an arbitrary expression. `NOT_STATIC` is the\n// sentinel for \"this expression is not knowable at build time\" — distinct from\n// a literal `undefined`, which IS knowable.\n\nconst NOT_STATIC = Symbol('not-static');\n\n/** Evaluate a JSX expression to a plain JS value, or `NOT_STATIC`. */\nfunction staticValue(tsc: typeof ts, sf: ts.SourceFile, node: ts.Node | undefined): unknown {\n if (!node) return NOT_STATIC;\n if (tsc.isParenthesizedExpression(node)) return staticValue(tsc, sf, node.expression);\n if (tsc.isStringLiteral(node) || tsc.isNoSubstitutionTemplateLiteral(node)) return node.text;\n if (tsc.isNumericLiteral(node)) return Number(node.text);\n if (node.kind === tsc.SyntaxKind.TrueKeyword) return true;\n if (node.kind === tsc.SyntaxKind.FalseKeyword) return false;\n if (node.kind === tsc.SyntaxKind.NullKeyword) return null;\n if (tsc.isArrayLiteralExpression(node)) {\n const out: unknown[] = [];\n for (const el of node.elements) {\n const v = staticValue(tsc, sf, el);\n if (v === NOT_STATIC) return NOT_STATIC;\n out.push(v);\n }\n return out;\n }\n if (tsc.isObjectLiteralExpression(node)) {\n const out: Record<string, unknown> = {};\n for (const p of node.properties) {\n // A shorthand (`{ field }`) or spread (`{ ...cfg }`) hides its value.\n if (!tsc.isPropertyAssignment(p)) return NOT_STATIC;\n const key = tsc.isIdentifier(p.name) || tsc.isStringLiteral(p.name) ? p.name.text : null;\n if (key === null) return NOT_STATIC;\n const v = staticValue(tsc, sf, p.initializer);\n if (v === NOT_STATIC) return NOT_STATIC;\n out[key] = v;\n }\n return out;\n }\n return NOT_STATIC;\n}\n\n/** The static value of one JSX attribute (`x=\"s\"` or `x={…}`), or `NOT_STATIC`. */\nfunction attrValue(tsc: typeof ts, sf: ts.SourceFile, attr: ts.JsxAttribute): unknown {\n const init = attr.initializer;\n if (!init) return true; // bare `showLegend` — JSX shorthand for `={true}`\n if (tsc.isStringLiteral(init)) return init.text;\n if (tsc.isJsxExpression(init)) return staticValue(tsc, sf, init.expression);\n return NOT_STATIC;\n}\n\n/**\n * The value of a FILTER attribute, resolved position by position: an array\n * literal survives even when some of its elements do not, each unknowable\n * element left as `NOT_STATIC` in place.\n *\n * `staticValue` collapses such an array to `NOT_STATIC` whole, which is correct\n * where the value only means something entire (an `aggregate={{…}}`) and wrong\n * for a filter, whose positions are independent: `['status', '=', stage]` has a\n * knowable FIELD beside an unknowable VALUE, and that is the shape a react page\n * writes when it drives a list from React state. See `filterFieldRefs`.\n */\nfunction filterAttrValue(tsc: typeof ts, sf: ts.SourceFile, attr: ts.JsxAttribute): unknown {\n const init = attr.initializer;\n if (!init || !tsc.isJsxExpression(init)) return NOT_STATIC;\n const perPosition = (node: ts.Node | undefined): unknown => {\n if (!node) return NOT_STATIC;\n if (tsc.isParenthesizedExpression(node)) return perPosition(node.expression);\n if (tsc.isArrayLiteralExpression(node)) return node.elements.map((el) => perPosition(el));\n return staticValue(tsc, sf, node);\n };\n return perPosition(init.expression);\n}\n\n// ─── <ObjectChart> binding integrity (#3701) ──────────────────────────────\n//\n// `validate-chart-bindings` covers every DATASET-bound chart surface: a\n// dataset declares its dimensions/measures by NAME, result rows are keyed by\n// those names, and an axis is checked against them. The react `<ObjectChart>`\n// block was excluded because it is OBJECT-bound — `objectName` + an inline\n// `aggregate` — and nothing said what the aggregated result columns were\n// called, so `xAxisKey`/`series[].dataKey` had nothing to resolve against.\n//\n// #3701 closed that: `chartAggregateResultKeys` in @objectstack/spec/ui now\n// records the convention every renderer already implements (rows keyed by the\n// RAW FIELD NAMES — `groupBy` for the category, `field` for the value, the\n// literal `'count'` for a fieldless count). With the columns pinned, both\n// halves of the binding are checkable:\n//\n// * `aggregate.field` / `aggregate.groupBy` are RAW FIELD names → check them\n// against the object's declared fields.\n// * the axes are RESULT COLUMN names → check them against what this\n// aggregate produces.\n//\n// The axes are read in the SPEC spelling — `xAxis.field`, `yAxis[].field`,\n// `series[].name` (#3729). #3701 had to read `xAxisKey`/`series[].dataKey`\n// because those were the only spellings the renderer honored; objectui#2880\n// made it honor ChartConfig, so the gate follows the protocol again. The\n// internal spellings are still accepted, silently: dashboards and the console's\n// own chart-view wiring emit them, and they remain a valid (if unpublished)\n// way to write the same binding.\n\nexport const REACT_CHART_FIELD_UNKNOWN = 'react-chart-field-unknown';\nexport const REACT_CHART_AGGREGATE_INVALID = 'react-chart-aggregate-invalid';\nexport const REACT_CHART_AXIS_UNKNOWN = 'react-chart-axis-unknown';\n\nconst CHART_FUNCTIONS = ['count', 'sum', 'avg', 'min', 'max'] as const;\n\nconst isRec = (v: unknown): v is Record<string, unknown> =>\n !!v && typeof v === 'object' && !Array.isArray(v);\n\nconst strOf = (v: unknown): string | undefined =>\n typeof v === 'string' && v.length > 0 ? v : undefined;\n\ninterface ChartAttrs {\n /** Statically resolved attribute values, keyed by prop name. */\n values: Map<string, unknown>;\n where: string;\n path: string;\n}\n\nfunction checkObjectChart(\n attrs: ChartAttrs,\n objectFields: Map<string, Set<string>>,\n findings: ReactPropFinding[],\n): void {\n const { values, where, path } = attrs;\n const push = (severity: ReactPropSeverity, rule: string, message: string, hint: string) =>\n findings.push({ severity, rule, where, path, message, hint });\n\n // Inline `data` wins over the aggregate query: the columns then come from\n // the author's own rows, which this rule cannot see. Nothing further to say.\n if (values.has('data')) return;\n\n const aggregate = values.get('aggregate');\n if (aggregate === undefined || aggregate === NOT_STATIC) return;\n if (!isRec(aggregate)) return;\n\n const fn = strOf(aggregate.function);\n const field = strOf(aggregate.field);\n const groupBy = aggregate.groupBy;\n const groupByField = strOf(groupBy) ?? (isRec(groupBy) ? strOf(groupBy.field) : undefined);\n\n // 1. The aggregate declaration itself.\n if (fn && !(CHART_FUNCTIONS as readonly string[]).includes(fn)) {\n push(\n 'error',\n REACT_CHART_AGGREGATE_INVALID,\n `aggregate.function \"${fn}\" is not an aggregation this chart can run.`,\n `Use one of: ${CHART_FUNCTIONS.join(', ')}.`,\n );\n } else if (fn && fn !== 'count' && !field) {\n push(\n 'error',\n REACT_CHART_AGGREGATE_INVALID,\n `aggregate.function \"${fn}\" has no \"field\" to aggregate.`,\n 'Add aggregate.field, or use function \"count\" (the only one that may omit it).',\n );\n }\n\n // 2. `field` / `groupBy` are RAW field names on the bound object.\n const objectName = strOf(values.get('objectName'));\n const known = objectName ? objectFields.get(objectName) : undefined;\n // No object name, or an object declared in another package: unknowable here\n // — the same skip the widget/flow/page rules take.\n if (objectName && known) {\n const fieldRef = (name: string | undefined, prop: string) => {\n if (!name) return;\n // A relationship path (`account.name`) is resolved by the query engine.\n if (name.includes('.')) return;\n if (known.has(name) || SYSTEM_FIELDS.has(name)) return;\n push(\n 'error',\n REACT_CHART_FIELD_UNKNOWN,\n `aggregate.${prop} \"${name}\" is not a field on object \"${objectName}\" — ` +\n `the aggregate query has nothing to ${prop === 'groupBy' ? 'group by' : 'aggregate'}, so the chart comes back empty.`,\n `Fix the field name, or add \"${name}\" to ${objectName}.` +\n (known.size > 0 ? ` Object fields: ${[...known].sort().join(', ')}.` : ''),\n );\n };\n fieldRef(field, 'field');\n fieldRef(groupByField, 'groupBy');\n }\n\n // 3. The axes name RESULT COLUMNS, not fields — the #3701 convention.\n const keys = chartAggregateResultKeys({ field, function: fn, groupBy });\n const columns = [keys.category, keys.value].filter((k): k is string => !!k);\n if (columns.length === 0) return; // an aggregate too incomplete to judge against\n\n const axisRef = (name: string | undefined, prop: string) => {\n if (!name) return;\n if (columns.includes(name)) return;\n // The comparison overlay's column only exists when `compareTo` is on, but\n // binding it is legitimate — never flag it as unknown.\n if (keys.comparison && name === keys.comparison) return;\n push(\n 'error',\n REACT_CHART_AXIS_UNKNOWN,\n `\"${name}\" is not a column this aggregate returns, so the axis plots nothing. ` +\n `Object-bound aggregate rows are keyed by the RAW FIELD NAMES ` +\n `(unlike a dataset, whose rows are keyed by measure name).`,\n `Result columns: ${columns.join(', ')}` +\n (keys.comparison ? ` (plus \"${keys.comparison}\" with a comparison overlay)` : '') +\n `. Bind ${prop} to one of them.`,\n );\n };\n\n // The category axis: spec `xAxis: { field }`, the report surface's bare\n // string, or the internal `xAxisKey`. All three name the same column.\n const xAxisRaw = values.get('xAxis');\n const categoryAxis =\n strOf(values.get('xAxisKey')) ??\n strOf(xAxisRaw) ??\n (isRec(xAxisRaw) ? strOf(xAxisRaw.field) : undefined);\n const categoryProp = values.has('xAxisKey') ? 'xAxisKey' : 'xAxis.field';\n axisRef(categoryAxis, categoryProp);\n\n // The value axes: spec `yAxis: [{ field }]` (or a single object / bare\n // string) and spec `series: [{ name }]` / internal `series: [{ dataKey }]`.\n const yAxisRaw = values.get('yAxis');\n const yAxisList = Array.isArray(yAxisRaw) ? yAxisRaw : yAxisRaw !== undefined ? [yAxisRaw] : [];\n for (const a of yAxisList) {\n axisRef(strOf(a) ?? (isRec(a) ? strOf(a.field) : undefined), 'yAxis[].field');\n }\n\n const series = values.get('series');\n if (Array.isArray(series)) {\n for (const s of series) {\n if (!isRec(s)) continue;\n const dataKey = strOf(s.dataKey);\n axisRef(dataKey ?? strOf(s.name), dataKey ? 'series[].dataKey' : 'series[].name');\n }\n }\n\n // A category axis bound to anything but the groupBy is always wrong, and the\n // check above lets it through when it happens to equal the VALUE column.\n if (categoryAxis && keys.category && categoryAxis !== keys.category && categoryAxis === keys.value) {\n push(\n 'error',\n REACT_CHART_AXIS_UNKNOWN,\n `${categoryProp} \"${categoryAxis}\" is the aggregate's VALUE column, not its category column.`,\n `The category axis is keyed by groupBy — bind it to \"${keys.category}\".`,\n );\n }\n}\n\n// ─── Field-bearing block props (#4340) ────────────────────────────────────\n//\n// #4329 closed ONE prop — `<ListView searchableFields>` — by running the\n// metadata rule's core from here. `searchableFields` was an instance, not the\n// class: every other prop a react block binds BY FIELD NAME shipped exactly as\n// typed, the same silent drift `validate-page-field-bindings` closes for the\n// page-component `properties` bag one surface over. This section closes the\n// class.\n//\n// ## Where the answers come from\n//\n// `REACT_FIELD_SPECS` below describes the blocks whose metadata twin lives\n// under different prop names — `<ListView>` (twin: a list page's\n// `interfaceConfig`) and `<ObjectForm>` (twin: `element:form` + the\n// form-layout rule) — plus `<ObjectChart>`'s `filter`.\n//\n// `COMPONENT_FIELD_SPECS`, the table `validate-page-field-bindings` walks on\n// the metadata surface, is still read from here, keyed by `schemaType`, so a\n// prop added there is checked on both surfaces at once. What reaches it is now\n// only `<Block type=\"element:…\">`: this rule read that table mainly for the\n// `record:*` blocks, and #4413 withdrew those from the tier entirely (they\n// render from a record context this surface does not mount — see\n// `recordContextFinding` below). The table's `record:*` rows are not dead,\n// they are simply the metadata surface's alone again.\n//\n// ## What is deliberately NOT checked, and why\n//\n// - Anything non-static (a variable, a call, a value behind a spread) —\n// ADR-0072 D1: unresolvable is not wrong. `filters` is the one place this\n// is resolved PER POSITION rather than all-or-nothing; see below.\n// - `<ObjectChart>`'s axes: they name the aggregate's RESULT COLUMNS, not\n// fields, and `checkObjectChart` above already owns them.\n//\n// `<ObjectForm subforms>` does not ride the table either, but IS checked:\n// each entry names its own `childObject`, so its refs are split per entry\n// rather than pooled against the block's object (`subformFieldRefs`).\n\n/**\n * Which props of a block carry field names, and in what shape. Each bucket is\n * a different SHAPE, not a different meaning — every entry resolves against the\n * block's own `objectName`.\n */\ninterface ReactFieldSpec {\n /** Bare names, `{field}`/`{name}` records, or arrays of either. */\n fields?: readonly string[];\n /** A `sort`: structured `{field,order}[]` or the legacy `\"field desc\"` string. */\n sorts?: readonly string[];\n /** A `{ fields: […] }` wrapper — one level of nesting. */\n nestedFields?: readonly string[];\n /** `{…}[]` sections whose `fields[]` name fields. */\n sections?: readonly string[];\n /** An object literal whose KEYS name fields. */\n keyedByField?: readonly string[];\n /** An ObjectQL FilterArray — its field POSITIONS gate (see `filterFieldRefs`). */\n filterArrays?: readonly string[];\n}\n\nconst REACT_FIELD_SPECS: Readonly<Record<string, ReactFieldSpec>> = {\n ListView: {\n // `fields` is the React overlay's \"limit/order the columns\"; `columns` the\n // spec ListView prop. Both name columns on the bound object, and a page may\n // write either. `hiddenFields`/`fieldOrder`/`filterableFields` are schema\n // props outside the curated contract — unadvertised but honored by the\n // renderer, so a stale name there is drift just the same.\n fields: ['fields', 'columns', 'hiddenFields', 'fieldOrder', 'filterableFields'],\n sorts: ['sort'],\n nestedFields: ['userFilters', 'grouping'],\n filterArrays: ['filters'],\n },\n ObjectForm: {\n fields: ['fields'],\n keyedByField: ['initialValues'],\n // `groups` is FormViewSchema's legacy alias for `sections`.\n sections: ['sections', 'groups'],\n },\n ObjectChart: {\n // The axes are result columns (checkObjectChart owns them); `filter` is an\n // ordinary ObjectQL predicate over the bound object, like ListView's.\n filterArrays: ['filter'],\n },\n};\n\n/**\n * How a prop name joins onto a react page's `path`. The props live inside one\n * opaque `source` string, so there is no config path to extend — #4329\n * established `pages[0].source › searchableFields[1]` and every prop below\n * follows it.\n */\nconst PATH_SEP = ' › ';\n\n/** tag → `schemaType`, read from the contract rather than restated. */\nconst SCHEMA_TYPE_BY_TAG: ReadonlyMap<string, string> = new Map(\n (REACT_BLOCKS as Array<{ tag: string; schemaType: string }>).map((b) => [b.tag, b.schemaType]),\n);\n\n/**\n * Attribute names read POSITION BY POSITION rather than all-or-nothing (see\n * `filterAttrValue`). Derived from the specs so a new `filterArrays` entry\n * cannot forget to opt in — the failure mode would be silent, since the\n * all-or-nothing reader simply finds nothing.\n *\n * `<RecordRelatedList filter>` rides along under the same name while holding a\n * different shape (`{field, operator, value}[]`, not a FilterArray). That is\n * harmless: a fully-static array reads identically either way, and the only\n * difference — surviving with `NOT_STATIC` holes — is dropped by\n * `fieldRefsFrom`, which ignores a non-string, non-record entry.\n */\nconst FILTER_PROPS: ReadonlySet<string> = new Set(\n Object.values(REACT_FIELD_SPECS).flatMap((s) => s.filterArrays ?? []),\n);\n\n/** Strip the `NOT_STATIC` sentinel so a shared extractor sees plain data. */\nfunction readableProps(values: ReadonlyMap<string, unknown>): AnyRec {\n const out: AnyRec = {};\n for (const [k, v] of values) if (v !== NOT_STATIC) out[k] = v;\n return out;\n}\n\n/**\n * `<ObjectForm subforms>` — inline master-detail child collections. Each entry\n * names the object its own refs resolve against (`childObject`), so unlike\n * every bucket in {@link ReactFieldSpec} these cannot be pooled into one batch\n * checked against the block's `objectName`. `totalField` is the exception\n * inside the exception: it names the PARENT field the child sum rolls up into.\n */\nfunction subformFieldRefs(\n value: unknown,\n basePath: string,\n): { child: Array<{ objectName: string | undefined; refs: FieldRef[] }>; parent: FieldRef[] } {\n const child: Array<{ objectName: string | undefined; refs: FieldRef[] }> = [];\n const parent: FieldRef[] = [];\n if (!Array.isArray(value)) return { child, parent };\n for (let i = 0; i < value.length; i++) {\n const sub = value[i];\n if (!isRec(sub)) continue;\n const at = (key: string) => `${basePath}[${i}].${key}`;\n child.push({\n objectName: strOf(sub.childObject),\n refs: [\n ...fieldRefsFrom(sub.columns, at('columns')),\n ...fieldRefsFrom(sub.relationshipField, at('relationshipField')),\n ...fieldRefsFrom(sub.amountField, at('amountField')),\n ],\n });\n parent.push(...fieldRefsFrom(sub.totalField, at('totalField')));\n }\n return { child, parent };\n}\n\n/**\n * Field references in an ObjectQL FilterArray, resolved PER POSITION.\n *\n * `staticValue` is all-or-nothing by design: an array containing one unknowable\n * element is not a knowable array. That is right for an `aggregate={{…}}`, and\n * wrong here, because the common react filter is exactly the mixed case —\n * `filters={['status', '=', stage]}` pairs a STATIC field position with a\n * React-state value. Bailing on the whole array would skip the only position\n * this rule can judge, on the shape authors actually write.\n *\n * So the reader keeps each position separate (`filterAttrValue`) and this walk\n * only ever reads position 0, and only when position 1 is a recognised operator\n * — the same test `isFilterAST` makes, using the spec's own operator vocabulary\n * so the two cannot drift. A non-static field position, a non-static operator,\n * or a shape that is not a filter node yields nothing.\n */\nfunction filterFieldRefs(node: unknown, basePath: string, out: FieldRef[]): void {\n if (!Array.isArray(node) || node.length === 0) return;\n const head = node[0];\n if (typeof head === 'string' && (head.toLowerCase() === 'and' || head.toLowerCase() === 'or')) {\n for (let i = 1; i < node.length; i++) filterFieldRefs(node[i], `${basePath}[${i}]`, out);\n return;\n }\n // Legacy flat array of conditions: `[[a,'=',1], [b,'>',2]]`.\n if (Array.isArray(head)) {\n for (let i = 0; i < node.length; i++) filterFieldRefs(node[i], `${basePath}[${i}]`, out);\n return;\n }\n if (\n typeof head === 'string' && head.length > 0 &&\n node.length >= 2 && typeof node[1] === 'string' &&\n VALID_AST_OPERATORS.has(node[1].toLowerCase())\n ) {\n out.push({ name: head, path: `${basePath}[0]` });\n }\n}\n\n/** The field refs one block's statically-read attributes hold, by bucket. */\nfunction reactFieldRefs(\n spec: ReactFieldSpec,\n values: ReadonlyMap<string, unknown>,\n basePath: string,\n): { own: FieldRef[]; queried: FieldRef[] } {\n const own: FieldRef[] = [];\n const queried: FieldRef[] = [];\n const readable = (key: string): unknown => {\n const v = values.get(key);\n return v === NOT_STATIC ? undefined : v;\n };\n const at = (key: string) => `${basePath}${PATH_SEP}${key}`;\n\n for (const key of spec.fields ?? []) {\n own.push(...fieldRefsFrom(readable(key), at(key)));\n }\n for (const key of spec.sorts ?? []) {\n own.push(...sortFieldRefs(readable(key), at(key)));\n }\n for (const key of spec.nestedFields ?? []) {\n const v = readable(key);\n if (isRec(v)) own.push(...fieldRefsFrom(v.fields, at(`${key}.fields`)));\n }\n for (const key of spec.sections ?? []) {\n const v = readable(key);\n if (!Array.isArray(v)) continue;\n for (let i = 0; i < v.length; i++) {\n const section = v[i];\n if (!isRec(section)) continue;\n own.push(...fieldRefsFrom(section.fields, at(`${key}[${i}].fields`)));\n }\n }\n for (const key of spec.keyedByField ?? []) {\n const v = readable(key);\n if (!isRec(v)) continue;\n for (const k of Object.keys(v)) own.push({ name: k, path: at(`${key}.${k}`) });\n }\n for (const key of spec.filterArrays ?? []) {\n // Read the raw attribute here, NOT `readable`: a filter whose VALUE is\n // non-static still has a knowable field position, and `filterAttrValue`\n // preserved exactly that.\n filterFieldRefs(values.get(key), at(key), queried);\n }\n return { own, queried };\n}\n\n/**\n * Resolve every field-bearing prop of one block usage against its bound object.\n *\n * Three sources feed it, in the order a block can claim them:\n *\n * 1. `REACT_FIELD_SPECS` — the react-only descriptors (`ListView`,\n * `ObjectForm`, `ObjectChart`'s `filter`).\n * 2. `COMPONENT_FIELD_SPECS`, keyed by the block's `schemaType` — the shared\n * table the metadata surface already uses. `<Block type=\"…\">` reaches it\n * by the type the author wrote, which is what makes the escape hatch\n * checked rather than a hole.\n *\n * Findings come back in this rule's own shape but under the metadata rule's id\n * (`page-field-unknown`): the same question, asked of the same component, with\n * the same fix.\n */\nfunction checkBlockFieldProps(\n tag: string,\n values: ReadonlyMap<string, unknown>,\n objectFields: ReadonlyMap<string, Set<string>>,\n where: string,\n path: string,\n): ReactPropFinding[] {\n const objectName = strOf(values.get('objectName'));\n const out: PageFieldFinding[] = [];\n\n const spec = REACT_FIELD_SPECS[tag];\n if (spec) {\n const { own, queried } = reactFieldRefs(spec, values, path);\n out.push(...checkFieldRefs(own, objectName, objectFields, where));\n out.push(...checkFieldRefs(queried, objectName, objectFields, where, 'queried'));\n }\n\n if (tag === 'ObjectForm') {\n const raw = values.get('subforms');\n const subs = subformFieldRefs(raw === NOT_STATIC ? undefined : raw, `${path}${PATH_SEP}subforms`);\n for (const sub of subs.child) {\n out.push(...checkFieldRefs(sub.refs, sub.objectName, objectFields, where));\n }\n // `totalField` names the FORM object's field the child sum rolls up into.\n out.push(...checkFieldRefs(subs.parent, objectName, objectFields, where));\n }\n\n // `<Block type=\"element:form\">` renders the registered component the author\n // names; every other block's type is fixed by its tag. A `record:*` type\n // never arrives here — `recordContextFinding` rejected it upstream.\n const schemaType = tag === 'Block' ? strOf(values.get('type')) : SCHEMA_TYPE_BY_TAG.get(tag);\n if (schemaType && COMPONENT_FIELD_SPECS[schemaType]) {\n out.push(\n ...checkFieldRefs(\n componentFieldRefs(schemaType, readableProps(values), path, PATH_SEP) ?? [],\n objectName,\n objectFields,\n where,\n ),\n );\n }\n\n // The two finding shapes are structurally identical; `where`/`path` are\n // already this surface's, so only the declared type differs.\n return out as ReactPropFinding[];\n}\n\n// ─── The `record:*` family is not of this surface (#4413) ─────────────────\n//\n// Every `record:*` renderer takes its record from the context a RECORD PAGE\n// mounts once (`RecordDetailView` fetches, N blocks render it, and they\n// coordinate through it — highlights dedupe out of the detail grid, one\n// inline-edit save bar commits them all under one version). A `kind:'react'`\n// page mounts no such context: `useRecordContext()` returns null, and each\n// block renders its designer placeholder — or, for `record:related_list`,\n// refuses to fetch because the parent id never arrives.\n//\n// The react tier had published `objectName`/`recordId` on four of them anyway\n// and no renderer read either, so a page authored exactly to contract rendered\n// EMPTY with nothing reported anywhere — including by this file, which\n// cheerfully resolved those props' field names against the object they named.\n// #4413 withdrew the props (see the ledger in `@objectstack/spec/ui`); this\n// turns what was a silent blank into a publish-time error, which is the half\n// that keeps an AI author from writing them again from memory.\n//\n// The check is by TYPE, not by the withdrawn tag list: the scope objectui\n// injects is built from the whole public registry, so every `record:*`\n// component — including the six that were never in the contract\n// (`record:activity`, `record:chatter`, …) — is reachable here and equally\n// empty. `<Block type=\"record:…\">` is the same reach with the type spelled out.\n\nexport const REACT_BLOCK_NEEDS_RECORD_CONTEXT = 'react-block-needs-record-context';\n\nconst RECORD_BLOCK_GENERIC_FIX =\n 'author the page as `type:\\'record\\'` — a record page mounts the record context these blocks render from.';\n\nfunction recordContextFinding(\n tag: string,\n schemaType: string,\n where: string,\n path: string,\n): ReactPropFinding {\n return {\n severity: 'error',\n rule: REACT_BLOCK_NEEDS_RECORD_CONTEXT,\n where,\n path,\n message:\n `<${tag}> renders \"${schemaType}\", which reads its record from the record context a ` +\n `record page mounts — a kind:'react' page never mounts one, so the block renders empty ` +\n `no matter how it is bound (its objectName/recordId are not read by the renderer).`,\n hint:\n `On a react page bind the record yourself: ` +\n `${REACT_RECORD_BLOCK_ALTERNATIVES[schemaType] ?? RECORD_BLOCK_GENERIC_FIX}`,\n };\n}\n\n/**\n * Names the page source binds itself — every function/variable declaration,\n * at any depth (a react page declares its helper components INSIDE `Page`).\n *\n * A local declaration SHADOWS the injected scope, so `const RecordPath = …`\n * followed by `<RecordPath/>` is the author's own component and none of this\n * rule's business. Cheap to honor and it removes the whole false-positive\n * class from a gate that BLOCKS a publish — the older prop checks above are\n * left alone deliberately: they only ever warn about a near-miss prop name or\n * a missing required one, which is survivable advice on a shadowed tag.\n */\nfunction localComponentNames(tsc: typeof ts, sf: ts.SourceFile): Set<string> {\n const names = new Set<string>();\n const walk = (node: ts.Node): void => {\n if (tsc.isFunctionDeclaration(node) && node.name) names.add(node.name.text);\n else if (tsc.isVariableDeclaration(node) && tsc.isIdentifier(node.name)) names.add(node.name.text);\n else if (tsc.isImportSpecifier(node)) names.add(node.name.text);\n tsc.forEachChild(node, walk);\n };\n walk(sf);\n return names;\n}\n\nexport function validateReactPageProps(stack: AnyRec): ReactPropFinding[] {\n const findings: ReactPropFinding[] = [];\n const objectFields = indexObjectFields(stack);\n // A separate index for the searchableFields check, built by the metadata\n // rule's own indexer: it keeps `null` for an object with no authored field\n // map (external / datasource-introspected), a distinction `indexObjectFields`\n // flattens — and one this check must honor so both surfaces skip alike.\n const searchTargets = indexObjectSearchTargets(stack);\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n if (!page || page.kind !== 'react') continue;\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') continue;\n const name = String(page.name ?? `#${p}`);\n\n // Outside the try below on purpose: a missing compiler must surface as an\n // error, not be swallowed as \"unparseable source\".\n const tsc = loadTypeScript();\n\n let sf: ts.SourceFile;\n try {\n sf = tsc.createSourceFile('page.tsx', source, tsc.ScriptTarget.Latest, true, tsc.ScriptKind.TSX);\n } catch {\n continue; // the syntax gate reports unparseable sources\n }\n\n const locals = localComponentNames(tsc, sf);\n\n const visit = (node: ts.Node): void => {\n if (tsc.isJsxOpeningElement(node) || tsc.isJsxSelfClosingElement(node)) {\n const tag = node.tagName.getText(sf);\n const where = `page \"${name}\" › <${tag}>`;\n const path = `pages[${p}].source`;\n // A withdrawn `record:*` block, reached by its injected tag. Reported\n // and then dropped: the prop checks below have nothing useful to add\n // about a block that cannot render here at all.\n const recordType = RECORD_CONTEXT_BLOCK_TAGS.get(tag);\n if (recordType && !locals.has(tag)) {\n findings.push(recordContextFinding(tag, recordType, where, path));\n tsc.forEachChild(node, visit);\n return;\n }\n const block = BLOCKS.get(tag);\n if (block) {\n let hasSpread = false;\n const used = new Set<string>();\n const values = new Map<string, unknown>();\n for (const a of node.attributes.properties) {\n if (tsc.isJsxSpreadAttribute(a)) { hasSpread = true; continue; }\n if (tsc.isJsxAttribute(a)) {\n const propName = a.name.getText(sf);\n used.add(propName);\n values.set(\n propName,\n FILTER_PROPS.has(propName)\n ? filterAttrValue(tsc, sf, a)\n : attrValue(tsc, sf, a),\n );\n }\n }\n // The escape hatch reaches the same withdrawn components by type —\n // `<Block type=\"record:highlights\">` is `<RecordHighlights>` spelled\n // out, and just as empty. Checked here rather than by tag because\n // the type is an attribute VALUE (and a non-static one is\n // unresolvable, not wrong — ADR-0072 D1).\n if (tag === 'Block') {\n const blockType = strOf(values.get('type'));\n if (blockType && isRecordContextBlockType(blockType)) {\n findings.push(recordContextFinding(tag, blockType, where, path));\n tsc.forEachChild(node, visit);\n return;\n }\n }\n if (!hasSpread) {\n for (const req of block.requiredBindings) {\n if (!used.has(req)) {\n findings.push({\n severity: 'error',\n rule: 'react-prop-missing-required',\n where, path,\n message: `<${tag}> is missing the required prop \"${req}\".`,\n hint: `Pass ${req}={…}. See the react-tier component contract.`,\n });\n }\n }\n }\n for (const u of used) {\n const near = nearestKnown(u, block.knownProps);\n if (near) {\n findings.push({\n severity: 'warning',\n rule: 'react-prop-typo',\n where, path,\n message: `<${tag}> has prop \"${u}\" — did you mean \"${near}\"?`,\n hint: 'Likely a typo of a contract prop. Fix it or remove it.',\n });\n }\n }\n // A spread can supply any of the bindings below, so the values we\n // can see are an incomplete picture — skip rather than guess.\n if (tag === 'ObjectChart' && !hasSpread) {\n checkObjectChart({ values, where, path }, objectFields, findings);\n }\n // <ListView searchableFields> names fields on the bound object — the\n // react-surface twin of `searchable-field-unknown` (#4329) and, as a\n // view-level narrowing echoed to the runtime as `$searchFields`, of\n // `searchable-field-unsearchable` too (#4830, the checker's default\n // role). It runs the metadata rule's own core, so the skips\n // (cross-package object, no authored field map, system columns) and\n // the dotted-path strictness match by construction. A non-static\n // value — either attribute — bails inside the checker: unresolvable\n // is not wrong.\n if (tag === 'ListView' && !hasSpread) {\n findings.push(\n ...checkSearchableFieldList(\n values.get('searchableFields'),\n strOf(values.get('objectName')),\n searchTargets,\n where,\n `${path} › searchableFields`,\n 'searchableFields',\n ),\n );\n }\n // Every other field-bearing prop (#4340). Same skips as the chart\n // and searchableFields checks: a spread hides the picture, and a\n // non-static value is unresolvable rather than wrong.\n if (!hasSpread) {\n findings.push(\n ...checkBlockFieldProps(tag, values, objectFields, where, path),\n );\n }\n }\n }\n tsc.forEachChild(node, visit);\n };\n visit(sf);\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0061 — searchable set] `searchableFields` entries must name a field the\n * object actually has — and, on a list view, one the runtime will agree to scan.\n *\n * `searchableFields` is `z.array(z.string())` in both `object.zod.ts` and the\n * list-view schema, so nothing checks that an entry resolves to anything. Rename\n * a field and the old name stays behind: Zod-valid, shipped, and pointing at a\n * column that no longer exists.\n *\n * ── Why a stale entry is not merely inert ────────────────────────────────\n *\n * The engine tolerates it. `resolveSearchFields` (`@objectstack/objectql`)\n * filters the declaration down to fields that exist —\n * `searchableFields?.filter((f) => all[f])` — so a stale name is dropped\n * without a word. That tolerance is what makes the drift invisible, and it\n * fails in the direction nobody expects:\n *\n * - Some entries stale → `$search` quietly scans a NARROWER set than the\n * object declares. Records that should match do not, and the response is\n * indistinguishable from \"no such record\".\n * - EVERY entry stale → the filtered set is empty, so resolution falls\n * through to the AUTO-DEFAULT (name/title + short-text fields). A\n * declaration whose entire purpose is to CHOOSE the searchable set ends up\n * selecting a set the author never wrote — the same \"asked narrower,\n * answered wider\" inversion #4226 closed on the projection axis.\n *\n * And it does not stay quiet downstream. objectui's list search echoes\n * `schema.searchableFields` verbatim as the `$searchFields` override, so once\n * the REST read path validates that override against the object (#4254), a\n * stale declaration the engine had been silently skipping becomes a `400\n * INVALID_FIELD` on every list search for that object — a request-time break\n * whose cause is an authoring typo made long before.\n *\n * Hence `error`, not the advisory level the other field-existence rules use\n * (`page-field-unknown`, `form-field-unknown`, `semantic-role-field-unknown`\n * are all warnings). Those describe a consumer that SKIPS an unknown name and\n * renders the rest; this one describes a declaration that either selects the\n * wrong set or refuses the request outright. It is the same call\n * `validate-flow-template-paths` makes for a filter-position token: gating when\n * the miss widens the query rather than shrinking the page.\n *\n * ── What is checked ──────────────────────────────────────────────────────\n *\n * 1. EXISTENCE, on every surface (`searchable-field-unknown`): an entry that\n * resolves to no field at all is drift, whatever declared it.\n *\n * 2. RUNTIME ADMISSIBILITY, on view-level narrowings only\n * (`searchable-field-unsearchable`, #4830): a list view's\n * `searchableFields` is echoed verbatim by clients as the `$searchFields`\n * override, and the #4254 ingress gate\n * (`assertSearchFieldsAreSearchable`, `@objectstack/metadata-protocol`)\n * refuses any entry outside the object's server-resolved allowed set — so\n * ONE lookup-typed entry 400s EVERY toolbar search on that list, for every\n * role, and until #4830 `compile`/`validate` passed it in silence. The\n * allowed set here is computed by the very function the runtime gate and\n * the engine consult — {@link resolveSearchFieldResolution}\n * (`@objectstack/spec/data`) — never by a second copy of its type list, so\n * linter, gate and engine cannot drift apart (the same one-source move\n * #4254 made between gate and engine).\n *\n * The OBJECT's own `searchableFields` stays existence-only: the runtime's\n * declared branch filters by existence, never by type, so a json or lookup\n * column declared THERE is a choice the engine executes (a `$contains` over\n * the raw column), not a 400. Flagging it would reject metadata the runtime\n * accepts — the false finding that makes authors stop trusting the linter\n * (ADR-0072 D1).\n *\n * Three skips keep false positives near zero (ADR-0072 D1):\n *\n * 1. An object this stack does not define. It may come from another package,\n * and a field map we cannot see cannot be judged (the same skip the\n * page/flow/widget rules take).\n * 2. An object that declares no field map at all — external objects and\n * datasource-introspected schemas whose columns are resolved at runtime.\n * 3. Registry-injected system columns, which exist at runtime but never\n * appear in authored `fields` — the package-shared `SYSTEM_FIELDS`\n * (`system-fields.ts`), derived from the spec's own declarations rather\n * than hand-copied (#4330). The admissibility check also skips them:\n * their runtime field metadata (type, hidden) is registry-owned and not\n * visible here, and judging a column we cannot see risks the false\n * positive this list exists to avoid. (Cost asymmetry: a system column\n * the runtime would refuse — `created_by` in a view's narrowing — is a\n * missed finding, not a wrong one.)\n *\n * Dotted paths are NOT skipped here, unlike every sibling rule. Elsewhere\n * `owner_id.name` is left alone because the query engine resolves the traversal;\n * search does not — `resolveSearchFields` matches the field map by exact string,\n * so a dotted entry is dropped exactly like a typo. Skipping it would exempt the\n * one wrong spelling most likely to be borrowed from `select`/`sort`.\n */\n\nimport {\n resolveSearchFieldResolution,\n SEARCHABLE_TEXTUAL_TYPES,\n SEARCHABLE_ENUM_TYPES,\n SEARCH_AUTO_EXCLUDED_FIELDS,\n type SearchFieldMeta,\n} from '@objectstack/spec/data';\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\nexport const SEARCHABLE_FIELD_UNKNOWN = 'searchable-field-unknown';\nexport const SEARCHABLE_FIELD_UNSEARCHABLE = 'searchable-field-unsearchable';\n\nexport type SearchableFieldSeverity = 'error' | 'warning';\n\nexport interface SearchableFieldFinding {\n /** Always `error` — a stale entry narrows, widens or refuses the search (see module note). */\n severity: SearchableFieldSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `object \"crm_lead\"`. */\n where: string;\n /** Config path, e.g. `objects[0].searchableFields[2]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\n/**\n * Which runtime judgment applies to the declaration being checked:\n *\n * - `'canonical'` — the object's own `searchableFields`. The runtime honors\n * any entry that exists (existence-filtered, never type-filtered), so only\n * existence is checked.\n * - `'narrowing'` — a list view's `searchableFields` (metadata or react\n * surface). Clients echo it as the `$searchFields` override, which the\n * #4254 ingress gate intersects with the object's allowed set — entries the\n * runtime would refuse are flagged (#4830).\n */\nexport type SearchableFieldRole = 'canonical' | 'narrowing';\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * The slice of an object the search checks resolve against: the authored\n * field map (existence + the `type`/`hidden` meta search resolution reads),\n * plus the object's own declarations the runtime's resolution consumes.\n * `null` when the object declares no readable field map (external /\n * introspected — nothing to judge against).\n */\nexport interface ObjectSearchTarget {\n /** Authored field names, for the existence check. */\n names: Set<string>;\n /** name → the metadata slice `resolveSearchFieldResolution` reads. */\n fields: Record<string, SearchFieldMeta>;\n /** The object's own `searchableFields`, when declared as an array. */\n searchableFields?: string[];\n /** `nameField` / `displayNameField` — ordering only, passed for parity. */\n displayField?: string;\n}\n\n/**\n * Read both field-map shapes (name-keyed map, legacy array of `{ name }`) into\n * the target slice, or `null` when the object declares no readable field map.\n */\nfunction declaredFieldTarget(obj: AnyRec): ObjectSearchTarget | null {\n const fields = obj.fields;\n if (!fields || typeof fields !== 'object') return null;\n const names = new Set<string>();\n const metas: Record<string, SearchFieldMeta> = {};\n for (const f of asArray(fields)) {\n const n = strName(f.name);\n if (!n) continue;\n names.add(n);\n metas[n] = {\n type: typeof f.type === 'string' ? f.type : undefined,\n hidden: f.hidden === true,\n };\n }\n if (names.size === 0) return null;\n const searchableFields = Array.isArray(obj.searchableFields)\n ? obj.searchableFields.filter((e): e is string => typeof e === 'string')\n : undefined;\n return {\n names,\n fields: metas,\n searchableFields,\n displayField: strName(obj.nameField) ?? strName(obj.displayNameField),\n };\n}\n\n/**\n * The object's ALLOWED search-field set, judged by the SAME function the\n * runtime ingress gate and the engine consult (`resolveSearchFieldResolution`,\n * `@objectstack/spec/data`) — the one-source-of-truth requirement of #4830.\n *\n * One seam papered over deliberately: the runtime resolves the declared branch\n * against the REGISTRY field map (authored + injected system columns), while\n * this rule only sees authored `fields`. A declared entry naming a system\n * column (`searchableFields: ['name', 'created_at']`) must therefore survive\n * the resolution's existence filter exactly as it does at runtime, so such\n * entries get a stub meta ({}). The stub cannot leak into the auto-default:\n * `autoDefaultFields` requires a readable searchable `type`, which a stub\n * never has.\n */\nfunction resolveAllowedSet(target: ObjectSearchTarget): {\n allowed: Set<string>;\n source: 'declared' | 'auto';\n declaredList: string[];\n} {\n let fields = target.fields;\n const systemDeclared = (target.searchableFields ?? []).filter(\n (f) => !target.names.has(f) && SYSTEM_FIELDS.has(f),\n );\n if (systemDeclared.length > 0) {\n fields = { ...fields };\n for (const f of systemDeclared) fields[f] = {};\n }\n const { allowed, source } = resolveSearchFieldResolution({\n fields,\n searchableFields: target.searchableFields,\n displayField: target.displayField,\n });\n return { allowed: new Set(allowed), source, declaredList: allowed };\n}\n\n/** Levenshtein-bounded \"did you mean?\" over the object's own field names. */\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\n/**\n * object name → the search-target slice. `null` marks an object with no\n * readable field map, so \"declared nothing\" stays distinguishable from \"not in\n * stack\". Exported alongside `checkSearchableFieldList` so every surface that\n * authors a searchable set resolves against the identical index (#4329).\n */\nexport function indexObjectSearchTargets(\n stack: Record<string, unknown>,\n): Map<string, ObjectSearchTarget | null> {\n const fieldsByObject = new Map<string, ObjectSearchTarget | null>();\n if (!isRec(stack)) return fieldsByObject;\n for (const obj of asArray(stack.objects)) {\n const name = strName(obj.name);\n if (name) fieldsByObject.set(name, declaredFieldTarget(obj));\n }\n return fieldsByObject;\n}\n\n/**\n * Check one `searchableFields` array against the target `fieldsByObject`\n * holds for `objectName` — the shared core behind every surface that authors a\n * searchable set: the object/list-view metadata walked by\n * `validateSearchableFields` below, and the react page surface\n * (`<ListView searchableFields={…}>`, `validate-react-page-props`), which\n * reuses it so the two surfaces agree on what counts as a field — same three\n * skips, same dotted-path strictness (#4329), same runtime-admissibility\n * judgment for a view-level narrowing (#4830).\n *\n * `subject` names the declaration for the message, since an object's own set\n * and a view's narrowing of it are fixed differently; the entry index is\n * appended to `path` so the author can go straight to the stale name. `role`\n * picks the runtime judgment to mirror — see {@link SearchableFieldRole};\n * every list-view surface is a `'narrowing'`, which is the default.\n */\nexport function checkSearchableFieldList(\n declared: unknown,\n objectName: string | undefined,\n fieldsByObject: ReadonlyMap<string, ObjectSearchTarget | null>,\n where: string,\n path: string,\n subject: string,\n role: SearchableFieldRole = 'narrowing',\n): SearchableFieldFinding[] {\n const findings: SearchableFieldFinding[] = [];\n if (!Array.isArray(declared) || declared.length === 0) return findings;\n if (!objectName) return findings; // nothing to resolve against\n if (!fieldsByObject.has(objectName)) return findings; // ① object from another package\n const target = fieldsByObject.get(objectName);\n if (!target) return findings; // ② external / introspected — no authored field map\n\n const known = target.names;\n const resolution = role === 'narrowing' ? resolveAllowedSet(target) : undefined;\n\n for (let i = 0; i < declared.length; i++) {\n const entry = declared[i];\n // Pre-parse input may carry junk here; a non-string is a SHAPE error the\n // schema owns, not a dangling reference.\n const name = strName(entry);\n if (!name) continue;\n\n if (!known.has(name) && !SYSTEM_FIELDS.has(name)) {\n const dotted = name.includes('.');\n findings.push({\n severity: 'error',\n rule: SEARCHABLE_FIELD_UNKNOWN,\n where,\n path: `${path}[${i}]`,\n message:\n `${subject} entry \"${name}\" is not a field on object \"${objectName}\". ` +\n `The declaration is stale: searching it can never match, and the engine ` +\n `silently drops it — leaving a narrower search than declared, or the ` +\n `auto-default set once every entry is dropped.` +\n (dotted ? '' : suggest(name, known)),\n hint:\n (dotted\n ? `'search' scans this object's own columns, so a related record's ` +\n `column cannot be a search target — expand the relation and search ` +\n `the related object, or copy the value onto a formula field here. `\n : `Fix the name, or add \"${name}\" to ${objectName}.fields. `) +\n `Clients echo this declaration verbatim as the '$searchFields' ` +\n `override, so a stale entry becomes a 400 INVALID_FIELD on list ` +\n `search (#4254), not just a quietly narrowed one.` +\n (known.size > 0 ? ` Object fields: ${[...known].sort().join(', ')}.` : ''),\n });\n continue;\n }\n\n // ── Runtime admissibility (#4830) — view-level narrowings only ──\n if (!resolution || resolution.allowed.has(name)) continue;\n // ③ System column outside the allowed set: its runtime metadata is\n // registry-owned and invisible here — skip rather than risk the false\n // positive (module note).\n if (!known.has(name)) continue;\n const meta = target.fields[name];\n\n if (resolution.source === 'declared') {\n findings.push({\n severity: 'error',\n rule: SEARCHABLE_FIELD_UNSEARCHABLE,\n where,\n path: `${path}[${i}]`,\n message:\n `${subject} entry \"${name}\" is outside object \"${objectName}\"'s declared ` +\n `searchableFields (${resolution.declaredList.join(', ')}) — the set 'search' ` +\n `scans. Clients echo this declaration verbatim as the '$searchFields' ` +\n `override, and the runtime refuses an entry outside the allowed set: every ` +\n `toolbar search on this list returns 400 INVALID_FIELD (#4254).`,\n hint:\n `Add \"${name}\" to ${objectName}.searchableFields, or drop it from this ` +\n `view — a view narrows the object's searchable set, never widens it ` +\n `(ADR-0061).`,\n });\n continue;\n }\n\n // Auto-default source. Mirror the gate's own \"why\" — excluded name, then\n // hidden, then type; an unreadable type is unresolvable, not wrong.\n const isReference = meta?.type === 'lookup' || meta?.type === 'master_detail';\n let why: string;\n if (SEARCH_AUTO_EXCLUDED_FIELDS.has(name)) {\n why = 'a system/audit column, which the auto-default set never includes';\n } else if (meta?.hidden) {\n why = 'hidden';\n } else if (typeof meta?.type === 'string') {\n why = `of type '${meta.type}', which 'search' cannot scan`;\n } else {\n continue; // no readable type — unresolvable, not wrong (ADR-0072 D1)\n }\n findings.push({\n severity: 'error',\n rule: SEARCHABLE_FIELD_UNSEARCHABLE,\n where,\n path: `${path}[${i}]`,\n message:\n `${subject} entry \"${name}\" on object \"${objectName}\" is ${why}. With no ` +\n `'searchableFields' declared on the object, 'search' scans its text-like ` +\n `columns (${[...SEARCHABLE_TEXTUAL_TYPES, ...SEARCHABLE_ENUM_TYPES].join(' / ')}). ` +\n `Clients echo this declaration verbatim as the '$searchFields' override, and ` +\n `the runtime refuses it: every toolbar search on this list returns ` +\n `400 INVALID_FIELD (#4254).`,\n hint:\n (isReference\n ? `A ${meta?.type} column stores only the referenced record's id, so it ` +\n `cannot be a keyword target — drop \"${name}\" from this view and, to ` +\n `search by the related record's title, mirror it onto a text/formula ` +\n `field here and declare that instead. `\n : `Drop \"${name}\" from this view, or target a text-like field instead. `) +\n `Declaring 'searchableFields' on object \"${objectName}\" chooses the ` +\n `searchable set explicitly.`,\n });\n }\n return findings;\n}\n\n/**\n * Validate every `searchableFields` declaration in the stack — the object's own\n * (the canonical set, ADR-0061) and the list views that narrow it. Returns\n * findings (empty = clean).\n *\n * The react page surface (`<ListView searchableFields={…}>`) is deliberately\n * NOT walked here: its declaration lives inside JSX source, and\n * `validate-react-page-props` — the gate that already parses that source —\n * runs the same `checkSearchableFieldList` core on it (#4329).\n */\nexport function validateSearchableFields(stack: AnyRec): SearchableFieldFinding[] {\n const findings: SearchableFieldFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const objects = asArray(stack.objects);\n const fieldsByObject = indexObjectSearchTargets(stack);\n\n const check = (\n declared: unknown,\n objectName: string | undefined,\n where: string,\n path: string,\n subject: string,\n role: SearchableFieldRole,\n ) => {\n findings.push(\n ...checkSearchableFieldList(declared, objectName, fieldsByObject, where, path, subject, role),\n );\n };\n\n // ── The object's own canonical set, and its built-in named list views ──\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!isRec(obj)) continue;\n const objName = strName(obj.name);\n const label = objName ? `object \"${objName}\"` : `objects[${oi}]`;\n\n check(\n obj.searchableFields,\n objName,\n label,\n `objects[${oi}].searchableFields`,\n 'searchableFields',\n 'canonical',\n );\n\n if (isRec(obj.listViews)) {\n for (const [key, lv] of Object.entries(obj.listViews)) {\n if (!isRec(lv)) continue;\n check(\n lv.searchableFields,\n // A built-in list view belongs to its object; an inline `data.object`\n // may still retarget it (ADR-0047 allows the explicit binding).\n listViewObject(lv) ?? objName,\n `${label} › listViews.${key}`,\n `objects[${oi}].listViews.${key}.searchableFields`,\n 'list-view searchableFields',\n 'narrowing',\n );\n }\n }\n }\n\n // ── `defineView` aggregates: the default `list` + named `listViews` ──\n const views = asArray(stack.views);\n for (let vi = 0; vi < views.length; vi++) {\n const view = views[vi];\n if (!isRec(view)) continue;\n const viewLabel = strName(view.name) ?? strName(view.objectName) ?? `#${vi}`;\n // The aggregate's own binding is the fallback for a list view that declares\n // none — the same resolution order `validate-list-view-mode` reads.\n const viewObject = strName(view.objectName) ?? strName(view.object);\n\n if (isRec(view.list)) {\n check(\n view.list.searchableFields,\n listViewObject(view.list) ?? viewObject,\n `view \"${viewLabel}\" › list`,\n `views[${vi}].list.searchableFields`,\n 'list-view searchableFields',\n 'narrowing',\n );\n }\n\n if (isRec(view.listViews)) {\n for (const [key, lv] of Object.entries(view.listViews)) {\n if (!isRec(lv)) continue;\n check(\n lv.searchableFields,\n listViewObject(lv) ?? viewObject,\n `view \"${viewLabel}\" › listViews.${key}`,\n `views[${vi}].listViews.${key}.searchableFields`,\n 'list-view searchableFields',\n 'narrowing',\n );\n }\n }\n }\n\n return findings;\n}\n\n/** A list view's own object binding: `data: { provider: 'object', object }`. */\nfunction listViewObject(listView: AnyRec): string | undefined {\n const data = listView.data;\n return isRec(data) ? strName(data.object) : undefined;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Shared page-component traversal for the lint rules that inspect\n * `PageComponent.properties` (issue #3583).\n *\n * Getting this walk right is subtle enough that duplicating it has already\n * produced one dead rule, so it lives here once:\n *\n * - Components hang off `page.regions[].components[]` and `page.slots.<slot>`\n * — there is NO top-level `page.components`. A walker that reads\n * `page.components` silently visits nothing on a schema-parsed stack.\n * - `slots.<slot>` is `PageComponent | PageComponent[]` — a single component\n * is legal and must be normalized.\n * - `PageComponentSchema` is `.strict()`, so a component carries no `children`\n * key of its own. Sub-trees live INSIDE the untyped `properties` bag:\n * `page:tabs` → `properties.items[].children`, `page:accordion` →\n * `properties.items[].children`, `page:card` → `properties.body` /\n * `properties.footer`. All are `z.array(z.unknown())`, so the recursion is\n * untyped and has to be done by hand.\n * - `kind: 'html' | 'react' | 'jsx'` pages are authored as `source`, which is\n * authoritative; their `regions` hold at most a DERIVED cache that the\n * source wins over. Linting that cache reports findings about metadata the\n * author never wrote, so those pages are skipped here and covered by\n * `validate-jsx-pages` / `validate-react-page-props` instead.\n */\n\nexport type AnyRec = Record<string, unknown>;\n\n/** A visited component plus everything needed to locate and bind it. */\nexport interface WalkedComponent {\n /** The component record itself. */\n component: AnyRec;\n /** Config path, e.g. `pages[0].regions[1].components[2]`. */\n path: string;\n /**\n * The object this component binds against, by precedence:\n * `dataSource.object` → `properties.object` → the page's `object`.\n * `undefined` when nothing in the chain names one.\n */\n objectName?: string;\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** Page kinds whose component tree is a derived cache, not authored metadata. */\nconst SOURCE_AUTHORED_KINDS = new Set(['html', 'react', 'jsx']);\n\n/** Is this page authored as `source` (so its `regions` must not be linted)? */\nexport function isSourceAuthoredPage(page: AnyRec): boolean {\n const kind = strName(page.kind);\n return kind !== undefined && SOURCE_AUTHORED_KINDS.has(kind);\n}\n\n/**\n * Walk every component on a page, depth-first, yielding each with its config\n * path and resolved object binding. Source-authored pages yield nothing.\n *\n * `pagePath` is the caller's path prefix for the page (e.g. `pages[3]`).\n */\nexport function walkPageComponents(page: AnyRec, pagePath: string): WalkedComponent[] {\n const out: WalkedComponent[] = [];\n if (!isRec(page) || isSourceAuthoredPage(page)) return out;\n\n const pageObject = strName(page.object);\n\n const visit = (node: unknown, path: string, inheritedObject?: string) => {\n if (!isRec(node)) return;\n\n // Per-element `dataSource` overrides the page object so one page can bind\n // several objects; an inline `properties.object` does the same for the\n // element-family components that declare one.\n const props = isRec(node.properties) ? node.properties : undefined;\n const dataSource = isRec(node.dataSource) ? node.dataSource : undefined;\n const objectName =\n strName(dataSource?.object) ?? strName(props?.object) ?? inheritedObject;\n\n out.push({ component: node, path, objectName });\n\n if (!props) return;\n\n // `page:tabs` / `page:accordion` — items[].children[]\n if (Array.isArray(props.items)) {\n for (let i = 0; i < props.items.length; i++) {\n const item = props.items[i];\n if (!isRec(item) || !Array.isArray(item.children)) continue;\n for (let c = 0; c < item.children.length; c++) {\n visit(item.children[c], `${path}.properties.items[${i}].children[${c}]`, objectName);\n }\n }\n }\n // Generic layout nesting — `properties.children[]`. Not in any props\n // schema, but it is how real pages compose layout containers (`type:\n // 'flex'` grids in the showcase command-center wrap every chart this way).\n // Omitting it hides whole sub-trees from every rule built on this walk.\n if (Array.isArray(props.children)) {\n for (let i = 0; i < props.children.length; i++) {\n visit(props.children[i], `${path}.properties.children[${i}]`, objectName);\n }\n }\n // `page:card` — body[] / footer[]\n for (const key of ['body', 'footer'] as const) {\n const slotList = props[key];\n if (!Array.isArray(slotList)) continue;\n for (let i = 0; i < slotList.length; i++) {\n visit(slotList[i], `${path}.properties.${key}[${i}]`, objectName);\n }\n }\n };\n\n const regions = Array.isArray(page.regions) ? page.regions : [];\n for (let r = 0; r < regions.length; r++) {\n const region = regions[r];\n if (!isRec(region) || !Array.isArray(region.components)) continue;\n for (let c = 0; c < region.components.length; c++) {\n visit(region.components[c], `${pagePath}.regions[${r}].components[${c}]`, pageObject);\n }\n }\n\n const slots = isRec(page.slots) ? page.slots : undefined;\n if (slots) {\n for (const [slot, value] of Object.entries(slots)) {\n // A slot holds a single component or an array of them.\n const list = Array.isArray(value) ? value : [value];\n const indexed = Array.isArray(value);\n for (let i = 0; i < list.length; i++) {\n visit(list[i], `${pagePath}.slots.${slot}${indexed ? `[${i}]` : ''}`, pageObject);\n }\n }\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0078 — completeness] Field-reference integrity for page components\n * (issue #3583, assessment R3).\n *\n * `PageComponent.properties` is `z.record(z.string(), z.unknown())` — an\n * untyped bag. The typed prop schemas exist (`ComponentPropsMap` in\n * `@objectstack/spec/ui`) but nothing validates `properties` against them, so\n * every field name a component references ships exactly as typed. The HotCRM\n * audit found KPI cards and page headers bound to fields the object does not\n * have; each renders blank or falls back, and nothing reports the miss.\n *\n * Field-existence lint already exists for forms (`FORM_FIELD_UNKNOWN`),\n * semantic roles, and flow templates. This is the same check for pages, at the\n * same advisory severity: every consumer degrades gracefully (a missing field\n * is skipped, not crashed on), so a warning is the honest level.\n *\n * ── Which object a component binds ──────────────────────────────────────\n *\n * `dataSource.object` → `properties.object` → the page's `object`. A per-element\n * `dataSource` exists precisely so one page can bind several objects, and the\n * `element:*` family declares its own `object`; both must win over the page's.\n *\n * ── Why a hand-written descriptor table ─────────────────────────────────\n *\n * `ComponentPropsMap` cannot drive this rule: a Zod schema does not say which\n * of its `z.string()` props is a FIELD NAME (`RecordPathProps.statusField` and\n * `AIChatWindowProps.agentId` are both plain strings), and the type universe is\n * open anyway — `PageComponent.type` is `z.union([PageComponentType,\n * z.string()])`, so unregistered types like `record:line_items` parse and are\n * authored in the wild. The table below names the field-bearing props\n * explicitly; an unknown component type is SKIPPED silently, never flagged.\n *\n * The table also covers shapes the props schemas do not yet describe but real\n * pages authored anyway (they pass only because `properties` is unvalidated):\n * `record:details` `sections[].fields[]` and `hideFields[]`, and the record\n * picker's `labelField`. Linting the schema's shape alone would find nothing on\n * the actual corpus.\n *\n * ── Shared with the react page surface ──────────────────────────────────\n *\n * A `kind:'react'` page authors the SAME components, one surface over, as JSX\n * props instead of a `properties` bag (`<RecordHighlights fields={…}>`). The\n * extraction and the check are therefore exported — `COMPONENT_FIELD_SPECS`,\n * {@link componentFieldRefs}, {@link relatedListFieldRefs},\n * {@link indexObjectFields}, {@link checkFieldRefs} — and\n * `validate-react-page-props` runs them on the parsed JSX (#4340). Same table,\n * same skips, same rule id: the two surfaces agree on what counts as a field by\n * construction rather than by two lists that happen to match, which is the\n * drift #4330 had just finished removing from the system-field lists.\n */\n\nexport const PAGE_FIELD_UNKNOWN = 'page-field-unknown';\n\nexport type PageFieldSeverity = 'error' | 'warning';\n\nexport interface PageFieldFinding {\n /**\n * `warning` on every surface this rule itself walks — page renderers skip an\n * unknown field rather than fail. The shared {@link checkFieldRefs} core also\n * serves the react page surface, where one batch of refs reaches a QUERY\n * rather than a renderer and gates instead; see {@link FieldRefConsequence}.\n */\n severity: PageFieldSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `page \"task_detail\" · record:highlights`. */\n where: string;\n /** Config path, e.g. `pages[0].regions[1].components[0].properties.fields[2]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\nimport { walkPageComponents, type AnyRec } from './page-walk.js';\n// Real pages DO reference registry-injected columns — e.g. `sys_user.page.ts`\n// lists `created_at` in a related-list's columns — so the shared set is load-\n// bearing here, not merely defensive.\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** A field reference found in a props bag, with the path that located it. */\nexport interface FieldRef {\n name: string;\n path: string;\n}\n\n/**\n * Pull field names out of a value that may be a bare string, a `{field}` or\n * `{name}` record, or an array of either — the three shapes the component props\n * use interchangeably (`record:highlights` keys its object form `name`, while\n * columns/sort/filter key theirs `field`).\n */\nexport function fieldRefsFrom(value: unknown, basePath: string): FieldRef[] {\n const out: FieldRef[] = [];\n const one = (v: unknown, path: string) => {\n const bare = strName(v);\n if (bare) {\n out.push({ name: bare, path });\n return;\n }\n if (!isRec(v)) return;\n const named = strName(v.field) ?? strName(v.name);\n if (named) out.push({ name: named, path: `${path}.${strName(v.field) ? 'field' : 'name'}` });\n };\n if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) one(value[i], `${basePath}[${i}]`);\n } else {\n one(value, basePath);\n }\n return out;\n}\n\n/**\n * Field references in a `sort` value.\n *\n * The structured form (`[{ field, order }]`) is ordinary {@link fieldRefsFrom}\n * work. The LEGACY bare-string form is not: `ListViewSchema.sort` still accepts\n * `\"created_at desc\"`, where the string names ONE field followed by a direction\n * word — so reading the whole string as a field name reports `\"created_at desc\"`\n * as unknown, a finding whose \"field\" the author never wrote. Read its head\n * instead, which is exactly what the renderer does with it.\n */\nexport function sortFieldRefs(value: unknown, basePath: string): FieldRef[] {\n if (typeof value === 'string') {\n const head = value.trim().split(/\\s+/)[0];\n return head ? [{ name: head, path: basePath }] : [];\n }\n return fieldRefsFrom(value, basePath);\n}\n\n/**\n * Per-component-type descriptor: which `properties` paths hold field names, and\n * whether they resolve against this component's own object or another one.\n *\n * `props` entries are read from `component.properties`. Entries under\n * `nestedSections` walk `properties.<key>[].fields[]` — the section shape real\n * pages author for `record:details`.\n */\nexport interface ComponentFieldSpec {\n /** Props holding field names bound to the component's resolved object. */\n props?: readonly string[];\n /** Props holding `{...}[]` section objects whose `fields[]` are field names. */\n nestedSections?: readonly string[];\n}\n\nexport const COMPONENT_FIELD_SPECS: Readonly<Record<string, ComponentFieldSpec>> = {\n 'record:highlights': { props: ['fields'] },\n // `sections`/`hideFields` are not in RecordDetailsProps, but every real page\n // authors them (they survive because `properties` is unvalidated).\n 'record:details': { props: ['fields', 'hideFields'], nestedSections: ['sections'] },\n 'record:path': { props: ['statusField'] },\n 'element:number': { props: ['field'] },\n 'element:filter': { props: ['fields'] },\n 'element:form': { props: ['fields'] },\n // The schema says `displayField`; real pages author `labelField`. Accept both.\n 'element:record_picker': { props: ['displayField', 'labelField', 'searchFields'] },\n};\n\n/**\n * `record:related_list` is special: its `columns`/`sort`/`filter` resolve\n * against the RELATED object (`properties.objectName`), not the page's object,\n * so it cannot ride the generic table.\n */\nexport const RELATED_LIST_TYPE = 'record:related_list';\n\n/**\n * The field references a component's props bag holds, per\n * {@link COMPONENT_FIELD_SPECS}. `null` for a type with no descriptor —\n * unregistered / non-field components are skipped silently, never flagged.\n *\n * `basePath` is the path of the props bag itself, and `sep` joins a prop name\n * onto it: `.` for a metadata page (`…components[0].properties.fields[2]`),\n * ` › ` for react source, whose props live inside one opaque `source` string\n * and so are addressed the way #4329 established (`pages[0].source › fields[2]`).\n */\nexport function componentFieldRefs(\n type: string,\n props: AnyRec,\n basePath: string,\n sep = '.',\n): FieldRef[] | null {\n const spec = COMPONENT_FIELD_SPECS[type];\n if (!spec) return null;\n const refs: FieldRef[] = [];\n for (const key of spec.props ?? []) {\n refs.push(...fieldRefsFrom(props[key], `${basePath}${sep}${key}`));\n }\n for (const key of spec.nestedSections ?? []) {\n const sections = Array.isArray(props[key]) ? (props[key] as unknown[]) : [];\n for (let si = 0; si < sections.length; si++) {\n const section = sections[si];\n // A `sections` that is a plain `string[]` (the shape `RecordDetailsProps`\n // actually declares — section IDs) yields nothing here, which is right:\n // those are not field names.\n if (!isRec(section)) continue;\n refs.push(...fieldRefsFrom(section.fields, `${basePath}${sep}${key}[${si}].fields`));\n }\n }\n return refs;\n}\n\n/** A `record:related_list` props bag, split by which object each batch resolves against. */\nexport interface RelatedListFieldRefs {\n /** The related (child) object this list renders — `properties.objectName`. */\n relatedObject: string | undefined;\n /** Refs resolved against {@link relatedObject}. */\n related: FieldRef[];\n /** Refs resolved against the PARENT object (the record the list hangs off). */\n parent: FieldRef[];\n /** The Add picker's own object, and the refs resolved against it. */\n pickerObject: string | undefined;\n picker: FieldRef[];\n}\n\n/**\n * Split a `record:related_list` props bag into its three object scopes. Shared\n * so the react `<RecordRelatedList>` block and the metadata component cannot\n * disagree about which object each prop addresses — the exact confusion #4340\n * found published under one prop name.\n */\nexport function relatedListFieldRefs(\n props: AnyRec,\n basePath: string,\n sep = '.',\n): RelatedListFieldRefs {\n const add = isRec(props.add) ? props.add : undefined;\n const picker = add && isRec(add.picker) ? add.picker : undefined;\n const at = (key: string) => `${basePath}${sep}${key}`;\n return {\n relatedObject: strName(props.objectName),\n related: [\n ...fieldRefsFrom(props.columns, at('columns')),\n ...sortFieldRefs(props.sort, at('sort')),\n ...fieldRefsFrom(props.filter, at('filter')),\n ...fieldRefsFrom(props.relationshipField, at('relationshipField')),\n ...(add ? fieldRefsFrom(add.linkField, at('add.linkField')) : []),\n ],\n parent: fieldRefsFrom(props.relationshipValueField, at('relationshipValueField')),\n pickerObject: picker ? strName(picker.object) : undefined,\n picker: picker\n ? [\n ...fieldRefsFrom(picker.valueField, at('add.picker.valueField')),\n ...fieldRefsFrom(picker.labelField, at('add.picker.labelField')),\n ]\n : [],\n };\n}\n\n/** object name → its declared field names. Both `fields` shapes resolve. */\nexport function indexObjectFields(stack: AnyRec): Map<string, Set<string>> {\n const objectFields = new Map<string, Set<string>>();\n if (!isRec(stack)) return objectFields;\n for (const obj of asArray(stack.objects)) {\n const name = strName(obj.name);\n if (!name) continue;\n const names = new Set<string>();\n for (const f of asArray(obj.fields)) {\n const fn = strName(f.name);\n if (fn) names.add(fn);\n }\n objectFields.set(name, names);\n }\n return objectFields;\n}\n\n/**\n * How a miss on this batch of refs fails at runtime — the two calls this\n * package makes, named so the message and the severity cannot drift apart.\n *\n * - `skipped` (default): the consumer drops the unknown name and renders the\n * rest. Advisory, like every other field-existence rule.\n * - `queried`: the name reached a QUERY. An unknown column in a predicate\n * matches no row (`SqlDriver` swallows the driver's \"no such column\" and\n * returns `[]`), so the surface renders an empty list that is\n * indistinguishable from \"there is no data\" — the silent-zero failure\n * `filter-token-unknown` and `validate-flow-template-paths`' filter-position\n * call both gate on. Gating.\n */\nexport type FieldRefConsequence = 'skipped' | 'queried';\n\n/**\n * Check one batch of refs against `objectName`'s declared fields.\n *\n * Bails out entirely when the object is not defined in this stack — it may come\n * from another installed package, and we cannot judge fields on a schema we\n * cannot see (the same skip the flow/widget rules use).\n */\nexport function checkFieldRefs(\n refs: readonly FieldRef[],\n objectName: string | undefined,\n objectFields: ReadonlyMap<string, Set<string>>,\n where: string,\n consequence: FieldRefConsequence = 'skipped',\n): PageFieldFinding[] {\n const findings: PageFieldFinding[] = [];\n if (!objectName) return findings; // nothing to resolve against\n const known = objectFields.get(objectName);\n if (!known) return findings; // cross-package object — unknowable here\n for (const ref of refs) {\n // A relationship path (`account.name`) is resolved by the query engine,\n // not a base column, so it cannot be judged here.\n if (ref.name.includes('.')) continue;\n if (known.has(ref.name) || SYSTEM_FIELDS.has(ref.name)) continue;\n findings.push({\n severity: consequence === 'queried' ? 'error' : 'warning',\n rule: PAGE_FIELD_UNKNOWN,\n where,\n path: ref.path,\n message:\n `field \"${ref.name}\" is not a field on object \"${objectName}\" — ` +\n (consequence === 'queried'\n ? 'it is used in a QUERY, so the predicate can never match: the surface ' +\n 'renders an empty result that looks exactly like \"there is no data\".'\n : 'the component silently skips it, so it never renders.'),\n hint:\n `Fix the field name, or add \"${ref.name}\" to ${objectName}. ` +\n `References must match the object's field names exactly.` +\n (known.size > 0 ? ` Object fields: ${[...known].sort().join(', ')}.` : ''),\n });\n }\n return findings;\n}\n\nexport function validatePageFieldBindings(stack: AnyRec): PageFieldFinding[] {\n const findings: PageFieldFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n // object name → its declared field names. Built with `asArray` so BOTH\n // `fields` shapes (array of `{name}` and name-keyed map) resolve.\n const objectFields = indexObjectFields(stack);\n\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n const page = pages[pi];\n if (!page || typeof page !== 'object') continue;\n const pageName = strName(page.name) ?? `#${pi}`;\n\n const checkRefs = (refs: readonly FieldRef[], objectName: string | undefined, where: string) => {\n findings.push(...checkFieldRefs(refs, objectName, objectFields, where));\n };\n\n for (const { component, path, objectName } of walkPageComponents(page, `pages[${pi}]`)) {\n const type = strName(component.type);\n const props = isRec(component.properties) ? component.properties : undefined;\n if (!type || !props) continue;\n const where = `page \"${pageName}\" · ${type}`;\n const base = `${path}.properties`;\n\n if (type === RELATED_LIST_TYPE) {\n const split = relatedListFieldRefs(props, base);\n checkRefs(split.related, split.relatedObject, where);\n // `relationshipValueField` names a field on the PARENT (page) object.\n checkRefs(split.parent, objectName, where);\n // The add-picker resolves against its own object.\n checkRefs(split.picker, split.pickerObject, where);\n continue;\n }\n\n const refs = componentFieldRefs(type, props, base);\n if (!refs) continue; // unregistered / non-field component — skip silently\n checkRefs(refs, objectName, where);\n }\n\n // ── interfaceConfig (list pages) ──\n // Bound by `interfaceConfig.source`, falling back to the page's object.\n const cfg = isRec(page.interfaceConfig) ? page.interfaceConfig : undefined;\n if (cfg) {\n const cfgObject = strName(cfg.source) ?? strName(page.object);\n const base = `pages[${pi}].interfaceConfig`;\n const refs: FieldRef[] = [\n ...fieldRefsFrom(cfg.columns, `${base}.columns`),\n ...sortFieldRefs(cfg.sort, `${base}.sort`),\n ...fieldRefsFrom(cfg.filterBy, `${base}.filterBy`),\n ];\n const userFilters = isRec(cfg.userFilters) ? cfg.userFilters : undefined;\n if (userFilters) {\n refs.push(...fieldRefsFrom(userFilters.fields, `${base}.userFilters.fields`));\n }\n checkRefs(refs, cfgObject, `page \"${pageName}\" · interfaceConfig`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for SDUI source-tier page styling (ADR-0065 / ADR-0080 /\n// ADR-0081). A `kind:'html'` or `kind:'react'` page's `source` is RUNTIME\n// metadata — the console's build-time Tailwind only scans the renderer's own\n// source, never an authored page string. So a Tailwind `className` in page\n// source silently produces NO CSS (the exact ADR-0065 failure: styling that\n// \"works only by coincidence\" when the class happens to be one objectui already\n// ships). This rule flags authored `className` attributes in source-tier pages\n// before render, with the actionable fix.\n//\n// It is the styling counterpart to the react-prop gate: a pure\n// `(stack) => Finding[]` rule (ADR-0019), run from `os validate`/`compile` and\n// reusable by AI authoring so the agent self-corrects.\n\nexport type SourceStyleSeverity = 'error' | 'warning';\n\nexport interface SourceStyleFinding {\n severity: SourceStyleSeverity;\n rule: string;\n where: string;\n path: string;\n message: string;\n hint: string;\n}\n\nexport const PAGE_SOURCE_CLASSNAME = 'page-source-className-tailwind';\n\ntype AnyRec = Record<string, unknown>;\nconst asArray = (v: unknown): AnyRec[] => (Array.isArray(v) ? (v as AnyRec[]) : []);\n\n// `className=` as a JSX attribute: name, optional ws, `=`, then `\"`/`'`/`{`.\nconst CLASSNAME_ATTR = /\\bclassName\\s*=\\s*[\"'{]/g;\n\nexport function validatePageSourceStyling(stack: AnyRec): SourceStyleFinding[] {\n const findings: SourceStyleFinding[] = [];\n const pages = asArray(stack.pages);\n for (let p = 0; p < pages.length; p++) {\n const page = pages[p];\n if (!page) continue;\n const kind = page.kind;\n if (kind !== 'html' && kind !== 'react' && kind !== 'jsx') continue;\n const source = page.source;\n if (typeof source !== 'string' || source.trim() === '') continue;\n const name = String(page.name ?? `#${p}`);\n\n CLASSNAME_ATTR.lastIndex = 0;\n let count = 0;\n while (CLASSNAME_ATTR.exec(source) !== null) count++;\n if (count === 0) continue;\n\n findings.push({\n severity: 'warning',\n rule: PAGE_SOURCE_CLASSNAME,\n where: `page \"${name}\"`,\n path: `pages[${p}].source`,\n message: `${count} \\`className\\` attribute${count > 1 ? 's' : ''} in ${String(kind)}-source page — Tailwind utilities in page source silently produce no CSS (the build never scans authored metadata; ADR-0065).`,\n hint:\n kind === 'react'\n ? \"Style with inline style={{}} using hsl(var(--token)) theme colors (e.g. color:'hsl(var(--foreground))', background:'hsl(var(--card))'); render drawer/modal via <ObjectForm formType=\\\"drawer\\\"|\\\"modal\\\"> instead of hand-rolled overlays.\"\n : \"Lay out with the components' structured props (<flex direction gap>, <grid columns>) and add CSS via a JSON style object style={{\\\"color\\\":\\\"hsl(var(--foreground))\\\"}}; do not use Tailwind className.\",\n });\n }\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { objectTitleCompleteness } from '@objectstack/spec/data';\nimport type { DisplayNameObjectMeta } from '@objectstack/spec/data';\n\n/**\n * Build-time record-title diagnostics (ADR-0079).\n *\n * A record's human title is a structural invariant: every object resolves a\n * primary title from a real STORED field via `nameField` (the canonical\n * pointer; `displayNameField` is the deprecated alias) or a deterministic\n * derivation. Two authoring smells are flagged here so `os build`/`os lint`,\n * the MCP authoring surface, and hand-authoring all get the coverage cloud\n * graph-lint already has (the ADR-0078 \"not cloud-only\" principle):\n *\n * - `title-format-retired` — the object declares a `titleFormat`. That field\n * is a RENDER-ONLY template the server can neither return nor query; ADR-0079\n * retires it in favour of `nameField`. The schema still parses it (existing\n * metadata keeps loading), so this is advisory, not an error.\n * - `title-unresolvable` — `objectTitleCompleteness` reports `status: 'none'`:\n * no `nameField`/`displayNameField` pointer and no title-eligible field to\n * derive one from. Records will have no meaningful title (the runtime falls\n * back to the auto-provisioned primary / `Record #<id>` floor), so this is a\n * warning, not an error — nothing is fully broken.\n *\n * Both are warnings: the auto-provision transform and the id floor mean a\n * green build never ships a fully title-less object. Reuses the shared spec\n * predicate (`@objectstack/spec/data` → display-name) so cloud and framework\n * classify titles identically.\n */\n\nexport const TITLE_FORMAT_RETIRED = 'title-format-retired';\nexport const TITLE_UNRESOLVABLE = 'title-unresolvable';\n\nexport type RecordTitleSeverity = 'error' | 'warning';\n\nexport interface RecordTitleFinding {\n /** Always `warning` today — both rules are advisory (see module note). */\n severity: RecordTitleSeverity;\n /** Diagnostic rule id (registry entry), e.g. `title-format-retired`. */\n rule: string;\n /** Human-readable location, e.g. `object \"invoice\"`. */\n where: string;\n /** Config path, e.g. `objects[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Validate every object's record-title declaration. Returns the list of\n * findings (empty = clean). Both rules are advisory (`warning`): the caller\n * must never fail the build on them alone — auto-provision + the `Record #<id>`\n * floor guarantee a resolvable title at runtime.\n */\nexport function validateRecordTitle(stack: AnyRec): RecordTitleFinding[] {\n const findings: RecordTitleFinding[] = [];\n\n const objects = asArray(stack.objects);\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const where = `object \"${objName}\"`;\n const path = `objects[${i}]`;\n\n // ── (a) titleFormat is retired (ADR-0079) ──\n // Render-only template the server cannot return or query. Still parsed by\n // the schema for back-compat, so advisory.\n if (obj.titleFormat !== undefined && obj.titleFormat !== null && obj.titleFormat !== '') {\n findings.push({\n severity: 'warning',\n rule: TITLE_FORMAT_RETIRED,\n where,\n path,\n message:\n `${objName}: titleFormat is retired (ADR-0079) — migrate to nameField ` +\n `(single field) or a formula field designated nameField`,\n hint:\n `titleFormat is a render-only template the server cannot return or ` +\n `query, and an explicit nameField now takes precedence. For a ` +\n `single-field title set nameField: '<field>'. For a composite title, ` +\n `add a formula field (returnType: 'text') and designate it via ` +\n `nameField.`,\n });\n }\n\n // ── (b) no resolvable title (status: 'none') ──\n // Reuse the shared spec predicate so cloud graph-lint and framework lint\n // classify titles identically. `none` = no pointer AND nothing derivable.\n const completeness = objectTitleCompleteness(obj as DisplayNameObjectMeta);\n if (completeness.status === 'none') {\n findings.push({\n severity: 'warning',\n rule: TITLE_UNRESOLVABLE,\n where,\n path,\n message:\n `${objName}: no resolvable record title — records will have no ` +\n `meaningful name (no nameField and no title-eligible field to derive one)`,\n hint:\n `Set nameField to a text/email field (or a formula field with ` +\n `returnType: 'text'), or add a text field named \"name\"/\"title\". The ` +\n `runtime auto-provisions a primary and falls back to \"Record #<id>\", ` +\n `but an explicit title is far more useful.`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time semantic-role diagnostics (ADR-0085).\n *\n * The object-level semantic roles (`stageField`, `highlightFields` /\n * deprecated `compactLayout`, `fieldGroups` + `Field.group`) are pointers\n * into the object's own field map. A dangling pointer is Zod-valid but\n * silently inert at render time — the exact \"parsed, unmarked, silently\n * inert\" shape ADR-0078 prohibits — so the completeness lint flags it here,\n * uniformly for `os build`/`os validate`, MCP authoring and hand authors.\n *\n * All three rules are warnings, not errors: every consumer degrades\n * gracefully (an unknown `Field.group` renders in the ungrouped bucket, an\n * unknown highlight name is skipped, an unknown `stageField` falls back to\n * heuristics), so nothing is fully broken — but the author almost certainly\n * typo'd a name and should be told at author time, not discover it by\n * staring at an unchanged page.\n */\n\nexport const FIELD_GROUP_UNDECLARED = 'field-group-undeclared';\nexport const FIELD_GROUP_EMPTY = 'field-group-empty';\nexport const FIELD_GROUP_SHADOWED = 'field-group-shadowed';\nexport const SEMANTIC_ROLE_FIELD_UNKNOWN = 'semantic-role-field-unknown';\n\nexport type SemanticRoleSeverity = 'error' | 'warning';\n\nexport interface SemanticRoleFinding {\n /** Always `warning` today — all three rules are advisory (see module note). */\n severity: SemanticRoleSeverity;\n /** Diagnostic rule id, e.g. `field-group-undeclared`. */\n rule: string;\n /** Human-readable location, e.g. `object \"invoice\"`. */\n where: string;\n /** Config path, e.g. `objects[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Validate every object's semantic-role pointers. Returns the list of\n * findings (empty = clean). Advisory only — the caller must never fail the\n * build on these alone.\n */\nexport function validateSemanticRoles(stack: AnyRec): SemanticRoleFinding[] {\n const findings: SemanticRoleFinding[] = [];\n\n const objects = asArray(stack.objects);\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object') continue; // tolerate junk entries\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const where = `object \"${objName}\"`;\n const path = `objects[${i}]`;\n\n const fields = (obj.fields && typeof obj.fields === 'object' && !Array.isArray(obj.fields))\n ? (obj.fields as Record<string, AnyRec | undefined>)\n : {};\n const fieldNames = new Set(Object.keys(fields));\n\n // ── (a) Field.group → declared fieldGroups[].key ──\n const declaredGroups = new Set(\n (Array.isArray(obj.fieldGroups) ? obj.fieldGroups : [])\n .filter((g): g is AnyRec => !!g && typeof g === 'object')\n .map((g) => g.key)\n .filter((k): k is string => typeof k === 'string' && k.length > 0),\n );\n const referencedGroups = new Set<string>();\n for (const [fname, f] of Object.entries(fields)) {\n const g = f?.group;\n if (typeof g !== 'string' || g.length === 0) continue;\n referencedGroups.add(g);\n if (!declaredGroups.has(g)) {\n findings.push({\n severity: 'warning',\n rule: FIELD_GROUP_UNDECLARED,\n where,\n path: `${path}.fields.${fname}.group`,\n message:\n `${objName}.${fname}: group \"${g}\" is not declared in fieldGroups — ` +\n `the field renders in the ungrouped bucket, not under \"${g}\"`,\n hint:\n `Declare { key: '${g}', label: '…' } in ${objName}.fieldGroups, or fix ` +\n `the field's group reference. Group keys are snake_case and must match exactly.`,\n });\n }\n }\n\n // ── (b) declared group no field references ──\n for (const key of declaredGroups) {\n if (!referencedGroups.has(key)) {\n findings.push({\n severity: 'warning',\n rule: FIELD_GROUP_EMPTY,\n where,\n path: `${path}.fieldGroups`,\n message:\n `${objName}: fieldGroups declares \"${key}\" but no field references it — ` +\n `the group never renders`,\n hint:\n `Assign at least one field via group: '${key}', or remove the unused ` +\n `group declaration.`,\n });\n }\n }\n\n // ── (c) semantic-role pointers name real fields ──\n const stage = obj.stageField;\n if (typeof stage === 'string' && stage.length > 0 && !fieldNames.has(stage)) {\n findings.push({\n severity: 'warning',\n rule: SEMANTIC_ROLE_FIELD_UNKNOWN,\n where,\n path: `${path}.stageField`,\n message:\n `${objName}: stageField \"${stage}\" is not a field on this object — ` +\n `consumers fall back to heuristic stage detection`,\n hint:\n `Point stageField at an existing select/status field, or set ` +\n `stageField: false to declare the object has no linear lifecycle.`,\n });\n }\n\n const highlights = Array.isArray(obj.highlightFields)\n ? obj.highlightFields\n : Array.isArray(obj.compactLayout) // deprecated alias (pre-normalization input)\n ? obj.compactLayout\n : [];\n for (const entry of highlights) {\n if (typeof entry !== 'string' || entry.length === 0 || fieldNames.has(entry)) continue;\n findings.push({\n severity: 'warning',\n rule: SEMANTIC_ROLE_FIELD_UNKNOWN,\n where,\n path: `${path}.highlightFields`,\n message:\n `${objName}: highlightFields entry \"${entry}\" is not a field on this ` +\n `object — it is silently skipped by every consumer`,\n hint:\n `Fix the field name (highlightFields drives default columns, cards, ` +\n `previews and the detail highlight strip, in order).`,\n });\n }\n\n // ── (d) declared group fully shadowed by the detail highlight strip ──\n // Detail pages render the first 4 highlightFields as the top strip and\n // HIDE those fields from the details body; the record's title field is\n // the page H1 and never renders in the body either. A group whose every\n // visible member is covered by strip ∪ title therefore renders on FORMS\n // but silently never on detail pages — legal, but almost never what the\n // author pictured when they declared the group.\n const declaredStrings = highlights.filter(\n (h): h is string => typeof h === 'string' && h.length > 0,\n );\n if (declaredStrings.length > 0 && declaredGroups.size > 0) {\n // Mirror the renderer's title resolution: declared role first\n // (nameField / primaryField / deprecated displayNameField), else the\n // first conventional display-field name present on the object.\n const declaredTitle = [obj.nameField, obj.primaryField, obj.displayNameField]\n .find((v): v is string => typeof v === 'string' && v.length > 0 && fieldNames.has(v));\n const titleField = declaredTitle\n ?? ['name', 'full_name', 'title', 'subject', 'display_name'].find((c) => fieldNames.has(c));\n const stripSet = new Set(\n declaredStrings.filter((h) => h !== titleField).slice(0, 4),\n );\n const hiddenFromBody = new Set(stripSet);\n if (titleField) hiddenFromBody.add(titleField);\n\n for (const key of declaredGroups) {\n const members = Object.entries(fields)\n .filter(([, f]) => f?.group === key && f?.hidden !== true)\n .map(([fname]) => fname);\n if (members.length === 0) continue; // rule (b) already covers empty groups\n if (!members.every((m) => hiddenFromBody.has(m))) continue;\n findings.push({\n severity: 'warning',\n rule: FIELD_GROUP_SHADOWED,\n where,\n path: `${path}.fieldGroups`,\n message:\n `${objName}: every field in group \"${key}\" (${members.join(', ')}) is ` +\n `hoisted into the detail highlight strip (or is the record title) — ` +\n `the group renders on forms but never on detail pages`,\n hint:\n `Keep at least one non-highlighted field in \"${key}\", or remove the ` +\n `group if the strip already covers it. (Detail pages show the first ` +\n `4 highlightFields as the top strip and hide them from the body.)`,\n });\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time form-layout diagnostics (#2578).\n *\n * Authored form views carry field references and column-layout hints that are\n * Zod-valid but can be silently wrong at render time — the \"parsed, unmarked,\n * silently inert\" shape ADR-0078 prohibits. This lint catches the two that\n * matter for multi-column, AI-authored forms, uniformly for `os build` /\n * `os validate`, MCP authoring and hand authors (ADR-0019).\n *\n * Both rules are warnings, not errors — nothing is fully broken (an unknown\n * field name is skipped; an over-wide colSpan is clamped) — but each is almost\n * certainly an authoring mistake worth surfacing at author time:\n *\n * - `form-field-unknown` — a section references a field that is not on the\n * form's bound object, so the field silently does not render.\n * - `absolute-colspan-discouraged` — a field uses the absolute `colSpan`. Under\n * a per-surface DERIVED column count (mobile 1 / modal 2 / page 3-4) a fixed\n * span only lines up at the one width the author imagined; the renderer\n * clamps it. The robust primitive is the relative `span: 'full'`.\n *\n * Scope: top-level form `views` (a `sections` array). Forms embedded inside\n * page component trees are a follow-up — the walker deliberately stays shallow\n * so it never guesses at an arbitrary component's object binding.\n */\n\nexport const FORM_FIELD_UNKNOWN = 'form-field-unknown';\nexport const FORM_COLSPAN_ABSOLUTE = 'absolute-colspan-discouraged';\n\nexport type FormLayoutSeverity = 'error' | 'warning';\n\nexport interface FormLayoutFinding {\n /** Always `warning` today — both rules are advisory (see module note). */\n severity: FormLayoutSeverity;\n /** Diagnostic rule id, e.g. `form-field-unknown`. */\n rule: string;\n /** Human-readable location, e.g. `view \"contract_form\"`. */\n where: string;\n /** Config path, e.g. `views[2].sections[0].fields[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** A section field entry is either a bare field name or `{ field, colSpan, … }`. */\nfunction fieldNameOf(entry: unknown): string | null {\n if (typeof entry === 'string') return entry.length > 0 ? entry : null;\n if (entry && typeof entry === 'object' && !Array.isArray(entry)) {\n const f = (entry as AnyRec).field;\n return typeof f === 'string' && f.length > 0 ? f : null;\n }\n return null;\n}\n\n/** The object a form view binds to: `data.object` (canonical) or `objectName`. */\nfunction boundObject(view: AnyRec): string | undefined {\n const data = view.data;\n if (data && typeof data === 'object' && typeof (data as AnyRec).object === 'string') {\n return (data as AnyRec).object as string;\n }\n return typeof view.objectName === 'string' ? (view.objectName as string) : undefined;\n}\n\n/**\n * Validate authored form-view layout. Returns findings (empty = clean).\n * Advisory only — the caller must never fail the build on these alone.\n */\nexport function validateFormLayout(stack: AnyRec): FormLayoutFinding[] {\n const findings: FormLayoutFinding[] = [];\n\n // object name → its field-name set, for reference checking.\n const objectFields = new Map<string, Set<string>>();\n for (const obj of asArray(stack.objects)) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const fields = (obj.fields && typeof obj.fields === 'object' && !Array.isArray(obj.fields))\n ? Object.keys(obj.fields as AnyRec)\n : [];\n objectFields.set(name, new Set(fields));\n }\n\n const views = asArray(stack.views);\n for (let i = 0; i < views.length; i++) {\n const view = views[i];\n if (!view || typeof view !== 'object') continue;\n const sections = Array.isArray(view.sections) ? view.sections : null;\n if (!sections) continue; // only form views carry a sections array\n\n const viewName = typeof view.name === 'string' ? view.name : `(view ${i})`;\n const objName = boundObject(view);\n // Only reference-check when the bound object resolves; otherwise we can't.\n const known = objName ? objectFields.get(objName) : undefined;\n const where = `view \"${viewName}\"`;\n const base = `views[${i}]`;\n\n for (let s = 0; s < sections.length; s++) {\n const sec = sections[s];\n const secFields = sec && typeof sec === 'object' && Array.isArray((sec as AnyRec).fields)\n ? ((sec as AnyRec).fields as unknown[])\n : [];\n for (let f = 0; f < secFields.length; f++) {\n const entry = secFields[f];\n const fname = fieldNameOf(entry);\n const fpath = `${base}.sections[${s}].fields[${f}]`;\n\n // ── (a) section field references a real field on the bound object ──\n if (fname && known && !known.has(fname)) {\n findings.push({\n severity: 'warning',\n rule: FORM_FIELD_UNKNOWN,\n where,\n path: fpath,\n message:\n `${viewName}: field \"${fname}\" is not a field on object \"${objName}\" — ` +\n `it is silently skipped and never renders on the form`,\n hint:\n `Fix the field name, or add \"${fname}\" to ${objName}. Section field ` +\n `references must match the object's field names exactly.`,\n });\n }\n\n // ── (b) absolute colSpan → steer to the surface-independent span ──\n const colSpan = entry && typeof entry === 'object' && !Array.isArray(entry)\n ? (entry as AnyRec).colSpan\n : undefined;\n if (colSpan != null) {\n findings.push({\n severity: 'warning',\n rule: FORM_COLSPAN_ABSOLUTE,\n where,\n path: `${fpath}.colSpan`,\n message:\n `${viewName}: field \"${fname ?? '?'}\" sets absolute colSpan ${String(colSpan)} — ` +\n `the form's column count is derived per surface (mobile 1 / modal 2 / page 3-4), ` +\n `so a fixed span only aligns at one width`,\n hint:\n `Prefer span: 'full' (whole row at any column count), or omit for auto ` +\n `width. The renderer clamps colSpan to the current column count.`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time conditional-visibility diagnostics (ADR-0089 D3b).\n *\n * ADR-0089 unifies the conditional-visibility predicate under the single\n * canonical key **`visibleWhen`** across data fields, view form sections/fields,\n * and page components. The deprecated spellings — `visibleOn` (view form) and\n * `visibility` (page component) — stay accepted and are folded into `visibleWhen`\n * at the schema boundary (a zod `.transform()`). Because that fold happens during\n * `parse()`, the aliases are gone from the *parsed* stack — so this rule runs on\n * the **pre-parse** (normalized) stack, exactly like `validate-list-view-mode`,\n * to see what the author actually wrote.\n *\n * Two advisory rules (both `warning` — nothing is broken, the alias still works\n * and a mis-rooted predicate just never matches):\n *\n * - `visibility-alias-deprecated` — a `visibleOn` / `visibility` key in authored\n * source. Autofix intent: rename the key to `visibleWhen` (same value).\n * - `visibility-root-mislayered` — a visibility predicate whose binding root does\n * not match its layer (ADR-0089 D3, §Context). The check is **bidirectional**:\n * - **runtime** view/page surfaces (`*.view.ts` / `*.page.ts`) bind\n * `record` + `current_user` (pages also expose `page.<var>`), so a `data.`-rooted\n * predicate here is a wrong-layer paste that silently never matches; and\n * - **metadata-editing** forms (`*.form.ts` — the row under edit) bind `data`, so\n * a `record.`-rooted predicate there is the same bug in the other direction.\n * The layer is supplied by the caller (`opts.layer`, default `'runtime'`): the\n * app-lint path (`os validate` / `compile`) always lints runtime surfaces, while a\n * file-aware caller linting a `*.form.ts` passes `layer: 'metadata'`.\n *\n * Scope: `views` (form `sections` / legacy `groups`, and their `fields`) and\n * `pages` (`regions[].components[]`). Data-field `visibleWhen` is already covered\n * by `validate-expressions` and is not re-checked here.\n */\n\nexport const VISIBILITY_ALIAS_DEPRECATED = 'visibility-alias-deprecated';\nexport const VISIBILITY_ROOT_MISLAYERED = 'visibility-root-mislayered';\n\nexport type VisibilitySeverity = 'error' | 'warning';\n\n/**\n * Which binding environment the linted surface belongs to (ADR-0089 §Context):\n * - `runtime` — `*.view.ts` / `*.page.ts`; binds `record` + `current_user` (+ `page`).\n * - `metadata` — `*.form.ts` metadata-editing forms; binds `data` (the row under edit).\n */\nexport type VisibilityLayer = 'runtime' | 'metadata';\n\n/** Options for {@link validateVisibilityPredicates}. */\nexport interface VisibilityOptions {\n /** Binding layer of the surface being linted. Defaults to `'runtime'`. */\n layer?: VisibilityLayer;\n}\n\nexport interface VisibilityFinding {\n /** Always `warning` today — both rules are advisory (see module note). */\n severity: VisibilitySeverity;\n /** Diagnostic rule id, e.g. `visibility-alias-deprecated`. */\n rule: string;\n /** Human-readable location, e.g. `view \"contact_form\"`. */\n where: string;\n /** Config path, e.g. `views[2].sections[0].fields[3]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** The canonical key and its two deprecated aliases (ADR-0089). */\nconst CANONICAL = 'visibleWhen';\nconst ALIASES = ['visibleOn', 'visibility'] as const;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** Extract the CEL source from a predicate value (string, or `{ source }` envelope). */\nfunction predicateSource(v: unknown): string | undefined {\n if (typeof v === 'string') return v;\n if (v && typeof v === 'object' && typeof (v as AnyRec).source === 'string') {\n return (v as AnyRec).source as string;\n }\n return undefined;\n}\n\n/** Does the predicate reference `<root>.<x>` as a leading binding root? */\nfunction usesRoot(source: string, root: string): boolean {\n // `<root>` as a leading identifier followed by a property access. The leading\n // `(^|[^.\\w$])` guard excludes a nested access like `foo.data` (a field named\n // `data`) or `my_record.x` (an identifier that merely ends in `record`).\n return new RegExp(`(^|[^.\\\\w$])${root}\\\\.\\\\w`).test(source);\n}\n\n/**\n * Per-layer mis-rooted-predicate description. The `runtime` layer forbids the\n * metadata-editing-form root (`data.`); the `metadata` layer forbids the runtime\n * record-surface root (`record.`) — ADR-0089 D3 spells out both directions.\n */\nconst MISLAYER_BY_LAYER: Record<\n VisibilityLayer,\n { forbiddenRoot: string; message: string; hint: string }\n> = {\n runtime: {\n forbiddenRoot: 'data',\n message:\n 'visibility predicate is rooted at `data.` — that is the ' +\n 'metadata-editing-form root (a `*.form.ts` row under edit), not a runtime ' +\n 'surface. A runtime view/page predicate that binds `data.` never matches ' +\n 'and the element renders unconditionally (ADR-0089).',\n hint:\n 'Runtime record surfaces bind `record` + `current_user` (pages also ' +\n \"expose `page.<var>`). Use e.g. `record.status == 'open'` instead of \" +\n \"`data.status == 'open'`.\",\n },\n metadata: {\n forbiddenRoot: 'record',\n message:\n 'visibility predicate is rooted at `record.` — that is the runtime ' +\n 'record-surface root (a `*.view.ts` / `*.page.ts` live record), not a ' +\n 'metadata-editing form. A `*.form.ts` predicate that binds `record.` never ' +\n 'matches and the element renders unconditionally (ADR-0089).',\n hint:\n 'Metadata-editing forms bind `data` (the row under edit). Use e.g. ' +\n \"`data.type == 'grid'` instead of `record.type == 'grid'`.\",\n },\n};\n\n/**\n * Inspect one element carrying a visibility predicate. Emits the alias-deprecated\n * finding (when an alias key is present) and the mis-layered-root finding (when\n * the effective predicate's binding root does not match `layer`).\n */\nfunction checkElement(\n el: AnyRec,\n where: string,\n path: string,\n layer: VisibilityLayer,\n findings: VisibilityFinding[],\n): void {\n // (1) deprecated alias key present → steer to `visibleWhen`.\n for (const alias of ALIASES) {\n if (el[alias] !== undefined) {\n findings.push({\n severity: 'warning',\n rule: VISIBILITY_ALIAS_DEPRECATED,\n where,\n path: `${path}.${alias}`,\n message:\n `\\`${alias}\\` is the deprecated spelling of the conditional-visibility ` +\n `predicate (ADR-0089). It still works — it is normalized to \\`visibleWhen\\` ` +\n `at parse — but the canonical key is \\`visibleWhen\\`.`,\n hint: `Rename the key \\`${alias}\\` → \\`visibleWhen\\` (same CEL value).`,\n });\n }\n }\n\n // (2) mis-layered binding root — check the effective predicate (canonical wins)\n // against the root expected for this layer.\n const raw = el[CANONICAL] ?? el.visibleOn ?? el.visibility;\n const source = predicateSource(raw);\n const rule = MISLAYER_BY_LAYER[layer];\n if (source && usesRoot(source, rule.forbiddenRoot)) {\n findings.push({\n severity: 'warning',\n rule: VISIBILITY_ROOT_MISLAYERED,\n where,\n path,\n message: rule.message,\n hint: rule.hint,\n });\n }\n}\n\n/** A section field entry is either a bare field name or `{ field, visibleWhen, … }`. */\nfunction isFieldObject(entry: unknown): entry is AnyRec {\n return !!entry && typeof entry === 'object' && !Array.isArray(entry);\n}\n\n/**\n * Validate conditional-visibility keys across authored views and pages.\n *\n * Runs on the **pre-parse** (normalized) stack so it can see the deprecated\n * `visibleOn` / `visibility` aliases before the schema folds them into\n * `visibleWhen`. Returns findings (empty = clean); all advisory (`warning`) —\n * the caller must never fail the build on these alone.\n *\n * The binding-root check is layer-directional (ADR-0089 D3): pass\n * `opts.layer = 'metadata'` when linting a `*.form.ts` metadata-editing form (so a\n * `record.`-rooted predicate is flagged), or leave it at the `'runtime'` default for\n * `*.view.ts` / `*.page.ts` surfaces (so a `data.`-rooted predicate is flagged). The\n * alias-deprecated check is layer-agnostic.\n */\nexport function validateVisibilityPredicates(\n stack: AnyRec,\n opts: VisibilityOptions = {},\n): VisibilityFinding[] {\n const layer: VisibilityLayer = opts.layer ?? 'runtime';\n const findings: VisibilityFinding[] = [];\n\n // ── Views: form sections / legacy groups, and their fields ──────────\n const views = asArray(stack.views);\n for (let i = 0; i < views.length; i++) {\n const view = views[i];\n if (!view || typeof view !== 'object') continue;\n const viewName = typeof view.name === 'string' ? view.name : `(view ${i})`;\n const where = `view \"${viewName}\"`;\n\n // `sections` (canonical) and `groups` (legacy alias → sections) both hold\n // FormSection objects with an optional visibility predicate + `fields`.\n for (const bucket of ['sections', 'groups'] as const) {\n const sections = Array.isArray(view[bucket]) ? (view[bucket] as unknown[]) : [];\n for (let s = 0; s < sections.length; s++) {\n const sec = sections[s];\n if (!sec || typeof sec !== 'object') continue;\n const secPath = `views[${i}].${bucket}[${s}]`;\n checkElement(sec as AnyRec, where, secPath, layer, findings);\n\n const secFields = Array.isArray((sec as AnyRec).fields) ? ((sec as AnyRec).fields as unknown[]) : [];\n for (let f = 0; f < secFields.length; f++) {\n const entry = secFields[f];\n if (isFieldObject(entry)) {\n checkElement(entry, where, `${secPath}.fields[${f}]`, layer, findings);\n }\n }\n }\n }\n }\n\n // ── Pages: regions[].components[] ───────────────────────────────────\n const pages = asArray(stack.pages);\n for (let i = 0; i < pages.length; i++) {\n const page = pages[i];\n if (!page || typeof page !== 'object') continue;\n const pageName = typeof page.name === 'string' ? page.name : `(page ${i})`;\n const where = `page \"${pageName}\"`;\n const regions = Array.isArray(page.regions) ? (page.regions as unknown[]) : [];\n for (let r = 0; r < regions.length; r++) {\n const region = regions[r];\n const components = region && typeof region === 'object' && Array.isArray((region as AnyRec).components)\n ? ((region as AnyRec).components as unknown[])\n : [];\n for (let c = 0; c < components.length; c++) {\n const comp = components[c];\n if (comp && typeof comp === 'object') {\n checkElement(comp as AnyRec, where, `pages[${i}].regions[${r}].components[${c}]`, layer, findings);\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0066 ⑨] Authoring-time validation for capability references.\n *\n * `requiredPermissions` (on objects, fields, apps, and actions) and\n * `systemPermissions` (on permission sets) are free capability strings. A typo\n * — `mange_users` for `manage_users` — is Zod-valid and fails CLOSED at runtime\n * (the caller is denied), which is the safe direction but UNDISCOVERABLE: nothing\n * tells the author the referenced capability exists nowhere. This rule closes\n * that gap by resolving every `requiredPermissions` reference against the set of\n * capabilities known at author time and warning on the unresolved ones —\n * \"reject at the producer\" (Prime Directive / ADR-0049 honesty).\n *\n * The author-time \"known\" set is:\n * 1. the built-in platform capabilities (`PLATFORM_CAPABILITY_NAMES`),\n * 2. every capability the stack DECLARES via `defineCapability`\n * (`stack.capabilities`) — the explicit, package-provenanced declaration\n * (ADR-0066 D1), materialized at boot by `bootstrapDeclaredCapabilities`,\n * 3. every capability a permission set in this stack GRANTS via\n * `systemPermissions` (granting a capability also declares it — mirrors\n * the runtime `bootstrapSystemCapabilities` derived-defaults rule), and\n * 4. any `sys_capability` row shipped as seed data.\n *\n * WARNING, not error: a single package's lint cannot see capabilities declared\n * by OTHER installed packages, and the reference fails closed at runtime anyway,\n * so a dangling reference is \"almost certainly a typo\" — surface it, don't break\n * the build. Assignment (`systemPermissions`) is NOT flagged: it is the\n * declaration side, and a package legitimately introduces new capabilities there.\n */\n\nimport { PLATFORM_CAPABILITY_NAMES } from '@objectstack/spec/security';\n\nexport const CAPABILITY_REFERENCE_UNKNOWN = 'capability-reference-unknown';\n\nexport type CapabilityRefSeverity = 'error' | 'warning';\n\nexport interface CapabilityRefFinding {\n /** Always `warning` — the reference fails closed at runtime (see module note). */\n severity: CapabilityRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `object \"sys_license\"`. */\n where: string;\n /** Config path, e.g. `objects[3].requiredPermissions`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** The capability strings in a `string[]` value. */\nfunction asCapArray(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((s): s is string => typeof s === 'string' && s.length > 0) : [];\n}\n\n/**\n * Flatten an object-level `requiredPermissions` — either a `string[]` (all\n * operations) or a per-operation `{ read, create, update, delete }` map (ADR-0066\n * ⑤) — into `[{ cap, key }]`, where `key` is the map key (or `undefined` for the\n * array form) so a finding can point at the exact operation slice.\n */\nfunction flattenObjectRequired(v: unknown): Array<{ cap: string; key?: string }> {\n if (Array.isArray(v)) return asCapArray(v).map((cap) => ({ cap }));\n if (v && typeof v === 'object') {\n const out: Array<{ cap: string; key?: string }> = [];\n for (const [key, val] of Object.entries(v as AnyRec)) {\n for (const cap of asCapArray(val)) out.push({ cap, key });\n }\n return out;\n }\n return [];\n}\n\n/**\n * Validate every capability reference in a stack. Returns findings (empty =\n * clean). Advisory only — callers must not fail the build on these alone.\n */\nexport function validateCapabilityReferences(stack: AnyRec): CapabilityRefFinding[] {\n const findings: CapabilityRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n // ── Build the author-time \"known capability\" set ──\n const known = new Set<string>(PLATFORM_CAPABILITY_NAMES);\n // [ADR-0066 D1] Capabilities the stack explicitly DECLARES via defineCapability.\n for (const cap of asArray(stack.capabilities)) {\n if (typeof cap.name === 'string' && cap.name.length > 0) known.add(cap.name);\n }\n for (const ps of asArray(stack.permissions)) {\n for (const cap of asCapArray(ps.systemPermissions)) known.add(cap);\n }\n for (const seed of asArray(stack.data)) {\n if (seed.object !== 'sys_capability') continue;\n for (const rec of Array.isArray(seed.records) ? seed.records : []) {\n const name = (rec as AnyRec | null)?.name;\n if (typeof name === 'string' && name.length > 0) known.add(name);\n }\n }\n\n const hint =\n 'Fix the capability name, define it with defineCapability (stack.capabilities), ' +\n 'declare it on a permission set’s systemPermissions, ship a sys_capability seed row, ' +\n 'or ignore this if the capability is provided by another installed package ' +\n '(references fail closed at runtime).';\n\n const flag = (cap: string, where: string, path: string) => {\n if (known.has(cap)) return;\n findings.push({\n severity: 'warning',\n rule: CAPABILITY_REFERENCE_UNKNOWN,\n where,\n path,\n message:\n `requiredPermissions references capability \"${cap}\" which is registered ` +\n `nowhere — no built-in capability, no permission set in this package grants ` +\n `it via systemPermissions, and no sys_capability seed declares it`,\n hint,\n });\n };\n\n // ── Objects (D3) + their fields (D3) + embedded actions (D4) ──\n const objects = asArray(stack.objects);\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object') continue;\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const objPath = `objects[${i}]`;\n\n for (const { cap, key } of flattenObjectRequired(obj.requiredPermissions)) {\n flag(cap, `object \"${objName}\"`, `${objPath}.requiredPermissions${key ? `.${key}` : ''}`);\n }\n\n const fields = asArray(obj.fields);\n for (const f of fields) {\n const fname = typeof f.name === 'string' ? f.name : '(field)';\n for (const cap of asCapArray(f.requiredPermissions)) {\n flag(cap, `field \"${objName}.${fname}\"`, `${objPath}.fields.${fname}.requiredPermissions`);\n }\n }\n\n for (const [ai, action] of asArray(obj.actions).entries()) {\n const aName = typeof action.name === 'string' ? action.name : `(action ${ai})`;\n for (const cap of asCapArray(action.requiredPermissions)) {\n flag(cap, `action \"${objName}.${aName}\"`, `${objPath}.actions[${ai}].requiredPermissions`);\n }\n }\n }\n\n // ── Top-level actions (D4) ──\n for (const [i, action] of asArray(stack.actions).entries()) {\n const aName = typeof action.name === 'string' ? action.name : `(action ${i})`;\n for (const cap of asCapArray(action.requiredPermissions)) {\n flag(cap, `action \"${aName}\"`, `actions[${i}].requiredPermissions`);\n }\n }\n\n // ── Apps: requiredPermissions can appear at the app and nav-item\n // (recursively through groups) levels. Walk each app subtree. `areas` is\n // still traversed, but only to REACH the nav items nested inside it: the\n // area itself stopped carrying `requiredPermissions` in 17.0.0 (#4651 — it\n // was a fail-open gate nothing enforced), so the generic check below no\n // longer fires on an area node. Dropping the traversal would strand every\n // area-nested item. ──\n const apps = asArray(stack.apps);\n for (let i = 0; i < apps.length; i++) {\n const app = apps[i];\n if (!app || typeof app !== 'object') continue;\n const appName = typeof app.name === 'string' ? app.name : `(app ${i})`;\n const walk = (node: unknown, path: string) => {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n node.forEach((child, ci) => walk(child, `${path}[${ci}]`));\n return;\n }\n const rec = node as AnyRec;\n for (const cap of asCapArray(rec.requiredPermissions)) {\n flag(cap, `app \"${appName}\"`, `${path}.requiredPermissions`);\n }\n // Recurse only into the sub-structures that carry requiredPermissions.\n if (rec.navigation) walk(rec.navigation, `${path}.navigation`);\n if (rec.areas) walk(rec.areas, `${path}.areas`);\n if (rec.tabs) walk(rec.tabs, `${path}.tabs`);\n if (rec.children) walk(rec.children, `${path}.children`);\n if (rec.items) walk(rec.items, `${path}.items`);\n };\n walk(app, `apps[${i}]`);\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Approval-node approver authoring lint (ADR-0090 D3 fallout).\n *\n * `org_membership_level` (and `role`, its deprecated spelling) resolves against\n * better-auth's org-membership tier (`sys_member.role`: owner / admin / member)\n * — it is NOT a position. After ADR-0090 D3 renamed `sys_role` → `sys_position`,\n * downstream apps that authored `{ type: 'role', value: 'sales_manager' }`\n * silently route the approval to nobody: the expansion finds no member row,\n * falls back to the `role:sales_manager` literal, and the request waits on an\n * approver that can never act. These rules move that failure from a stuck\n * request at runtime to a located fix-it at author time.\n *\n * Rules:\n *\n * | Rule | Severity | Origin |\n * |--------------------------------------------|----------|----------------------------|\n * | approval-approver-not-membership-tier | warning | ADR-0090 D3 (hotcrm class) |\n * | approval-approver-type-deprecated | warning | ADR-0090 D3 (#3133) |\n * | approval-approver-type-unknown | warning | contract-first (PD #12) |\n * | approval-escalation-reassign-no-target | warning | silent notify degradation |\n * | approval-approvers-may-resolve-empty | info | empty-position dead-end (#3424) |\n * | approval-expression-invalid | error/info | #3447 P2 closed-root expressions |\n * | approval-expression-no-empty-policy | info | #3447 P2 empty-slate policy |\n * | approval-decision-outputs-reserved | error | #3447 P2 resume envelope |\n * | approval-approver-cross-org-unsupported | error | ADR-0105 D9 targeting |\n *\n * The first two are mutually exclusive by construction — a bad *value* wins,\n * because its fix (`position`) differs from the deprecation's fix\n * (`org_membership_level`), and prescribing the latter for a position name\n * would be wrong advice.\n *\n * Warnings (not errors): a custom better-auth membership tier is legal, and\n * the runtime keeps its literal fallback — but both shapes are near-certainly\n * authoring mistakes, so say it out loud.\n *\n * Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input.\n */\n\nimport {\n ApproverType,\n APPROVAL_NODE_TYPE,\n DEPRECATED_APPROVER_TYPES,\n APPROVER_VALUE_BINDINGS,\n approverTypeIsOrgScoped,\n canonicalApproverType,\n normalizeDecisionOutputs,\n} from '@objectstack/spec/automation';\nimport { BUILTIN_MEMBERSHIP_ROLES } from '@objectstack/spec';\nimport { collectCelRootIdentifiers } from '@objectstack/formula';\nimport { walkFlowNodes } from './flow-walk.js';\n\nexport const APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER = 'approval-approver-not-membership-tier';\nexport const APPROVAL_APPROVER_TYPE_DEPRECATED = 'approval-approver-type-deprecated';\nexport const APPROVAL_APPROVER_TYPE_UNKNOWN = 'approval-approver-type-unknown';\nexport const APPROVAL_APPROVER_TYPE_UNSUPPORTED = 'approval-approver-type-unsupported';\nexport const APPROVAL_ESCALATION_REASSIGN_NO_TARGET = 'approval-escalation-reassign-no-target';\nexport const APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY = 'approval-approvers-may-resolve-empty';\nexport const APPROVAL_EXPRESSION_INVALID = 'approval-expression-invalid';\nexport const APPROVAL_EXPRESSION_NO_EMPTY_POLICY = 'approval-expression-no-empty-policy';\nexport const APPROVAL_DECISION_OUTPUTS_RESERVED = 'approval-decision-outputs-reserved';\nexport const APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED = 'approval-approver-cross-org-unsupported';\n\n/**\n * The CLOSED root set an `expression` approver may reference (#3447 P2) —\n * `current` (live record at node entry), `trigger` (submit-time snapshot),\n * `vars` (flow variables). Mirrors APPROVER_EXPRESSION_ROOTS in\n * plugin-approvals; both sides extract roots via the same\n * {@link collectCelRootIdentifiers}, so what lints clean is what runs.\n */\nconst EXPRESSION_ROOTS = new Set(['current', 'trigger', 'vars']);\n\n/** Resume-envelope keys a decision output may never use (#3447 P2). */\nconst RESERVED_OUTPUT_KEYS = new Set(['decision', 'requestId']);\n\n/**\n * Approver types that route to a GROUP whose membership is runtime data and can\n * be empty (an unstaffed position, an empty team/department). When EVERY\n * approver on a node is one of these, the node can resolve to an empty slate at\n * runtime — the framework#3424 dead-end. Individually-routed types\n * (`user`/`field`/`manager`), the guaranteed-staffed `org_membership_level`\n * tiers, and the opaque `queue` are deliberately excluded: any of them present\n * signals the author has a non-group route, so the node isn't purely\n * group-gated.\n */\nconst GROUP_ROUTED_TYPES = new Set(['position', 'team', 'department']);\n\nexport type ApprovalApproverSeverity = 'error' | 'warning' | 'info';\n\nexport interface ApprovalApproverFinding {\n severity: ApprovalApproverSeverity;\n /** Diagnostic rule id (`approval-*`). */\n rule: string;\n /** Human-readable location, e.g. `flow \"expense_approval\" · node \"step1\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[2].config.approvers[0]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * The org-membership tiers `sys_member.role` actually stores — DERIVED, not\n * transcribed (ADR-0108 / #3723).\n *\n * The vocabulary is closed and framework-owned, so the one source in\n * `@objectstack/spec` is also the only correct list here. A hand-kept copy is\n * how this list came to carry `guest`, which the `sys_member.role` select has\n * never offered: an approver naming it resolved to nobody, and the lint that\n * exists to catch exactly that stayed silent.\n *\n * Anything outside this set authored as `{ type: 'org_membership_level' }` (or\n * its deprecated `role` spelling) is almost certainly a position name.\n */\nconst MEMBERSHIP_TIERS: ReadonlySet<string> = new Set<string>(BUILTIN_MEMBERSHIP_ROLES);\n\n/** The same list, rendered for diagnostics — so no message can contradict it. */\nconst MEMBERSHIP_TIER_LIST = BUILTIN_MEMBERSHIP_ROLES.join('/');\n\n/** Off-spec dialect spellings we can name a canonical fix for. */\nconst TYPE_FIX: Record<string, string> = {\n business_unit: 'department',\n bu: 'department',\n};\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Validate the approvers of every Approval node in the stack's flows.\n * Returns findings (empty = clean).\n */\nexport function validateApprovalApprovers(stack: AnyRec): ApprovalApproverFinding[] {\n const findings: ApprovalApproverFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const flows = asArray(stack.flows);\n const validTypes = new Set<string>(ApproverType.options);\n\n for (let fi = 0; fi < flows.length; fi++) {\n const flow = flows[fi];\n if (!flow || typeof flow !== 'object') continue;\n const flowName = typeof flow.name === 'string' ? flow.name : `(flow ${fi})`;\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions\n // — an approval inside a loop body is still an approval (#4380).\n const walked = walkFlowNodes(flow, `flows[${fi}]`);\n\n for (let ni = 0; ni < walked.length; ni++) {\n const { node, path: nodePath } = walked[ni];\n if (!node || node.type !== APPROVAL_NODE_TYPE) continue;\n const nodeId = typeof node.id === 'string' ? node.id : `(node ${ni})`;\n const cfg = (node.config ?? {}) as AnyRec;\n const approvers = Array.isArray(cfg.approvers) ? (cfg.approvers as AnyRec[]) : [];\n const where = `flow \"${flowName}\" · node \"${nodeId}\"`;\n\n for (let ai = 0; ai < approvers.length; ai++) {\n const a = approvers[ai];\n if (!a || typeof a !== 'object') continue;\n const type = typeof a.type === 'string' ? a.type : '';\n const value = typeof a.value === 'string' ? a.value : '';\n const path = `${nodePath}.config.approvers[${ai}]`;\n\n if (type && !validTypes.has(type)) {\n const fix = TYPE_FIX[type];\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_TYPE_UNKNOWN,\n where,\n path: `${path}.type`,\n message:\n `approver type '${type}' is not an ApproverType (${ApproverType.options.join(' | ')}).`,\n hint: fix\n ? `Use the spec value: { type: '${fix}', value: '${value}' }.`\n : `Pick one of the spec values; unmapped types degrade to an inert '${type}:${value}' literal at runtime.`,\n });\n continue;\n }\n\n const canonical = canonicalApproverType(type);\n\n // Expression approvers (#3447 P2): the runtime REJECTS an expression\n // that doesn't parse or references a root outside `current`/`trigger`/\n // `vars` (the CEL env would resolve an unknown root as dyn → null → a\n // silently-empty slate, so the pre-check fails the node loudly). Catch\n // both at author time — `error`, because the node cannot run.\n if (canonical === 'expression') {\n const source = value.trim();\n if (!source) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.value`,\n message: `expression approver has an empty expression — the node fails at entry.`,\n hint:\n `Write a CEL expression over current.* (the record's live state at node entry), ` +\n `trigger.* (the submit-time snapshot) or vars.* (flow variables), ` +\n `e.g. current.approvers_dynamic or vars.approval_lead.picked_departments.`,\n });\n } else {\n const parsed = collectCelRootIdentifiers(source);\n if (!parsed.ok) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.value`,\n message: `expression approver does not parse as CEL: ${parsed.error}.`,\n hint:\n `Approver expressions are bare CEL (no {…} template braces), e.g. ` +\n `current.approvers_dynamic or vars.get_reviewers.record.owner_id.`,\n });\n } else {\n const illegal = parsed.roots.filter((r) => !EXPRESSION_ROOTS.has(r));\n if (illegal.length) {\n const wantsRecord = illegal.includes('record') || illegal.includes('previous');\n findings.push({\n severity: 'error',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.value`,\n message:\n `expression approver references \\`${illegal.join('`, `')}\\` — only current.*, ` +\n `trigger.* and vars.* are available, and the node fails at entry on any other root.`,\n hint: wantsRecord\n ? `\\`record\\`/\\`previous\\` are not bound here (on this platform \\`record\\` always means ` +\n `\"the record at event time\", which is ambiguous at an approval node). Write ` +\n `current.<field> for the live value at node entry, trigger.<field> for the ` +\n `submit-time snapshot (vars.previous carries the pre-update row).`\n : `Did you mean current.<field> (live record), trigger.<field> (submit snapshot), ` +\n `or vars.<name> (flow variable)?`,\n });\n }\n }\n }\n } else if (a.resolveAs != null) {\n // resolveAs only means something on an expression approver; on any\n // other type it silently does nothing — surface the dead config.\n findings.push({\n severity: 'info',\n rule: APPROVAL_EXPRESSION_INVALID,\n where,\n path: `${path}.resolveAs`,\n message: `resolveAs has no effect on a '${type}' approver — it only applies to type 'expression'.`,\n hint: `Remove it, or switch this approver to { type: 'expression', value: '<CEL>', resolveAs: '${String(a.resolveAs)}' }.`,\n });\n }\n\n // Exactly one of the two below fires. Order matters: a bad VALUE is\n // the more serious (and differently-fixed) defect, so it wins. Telling\n // an author to rewrite { type: 'role', value: 'sales_manager' } as\n // `org_membership_level` would be actively wrong advice — the fix is\n // `position`, and the deprecation is beside the point.\n if (canonical === 'org_membership_level' && value && !MEMBERSHIP_TIERS.has(value.toLowerCase())) {\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_NOT_MEMBERSHIP_TIER,\n where,\n path: `${path}.value`,\n message:\n `approver { type: '${type}', value: '${value}' } resolves against the better-auth ` +\n `org-membership tier (sys_member.role: ${MEMBERSHIP_TIER_LIST}) — '${value}' is not ` +\n `a membership tier, so this approver matches nobody and the request stalls.`,\n hint:\n `If '${value}' is an org position, author { type: 'position', value: '${value}' } ` +\n `(resolved via sys_user_position, ADR-0090 D3). Keep type 'org_membership_level' ` +\n `only for membership tiers (${MEMBERSHIP_TIER_LIST}) — the vocabulary is closed ` +\n `(ADR-0108), so a business role is always a position.`,\n });\n } else if (type in DEPRECATED_APPROVER_TYPES) {\n const fix = canonicalApproverType(type);\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_TYPE_DEPRECATED,\n where,\n path: `${path}.type`,\n message:\n `approver type '${type}' is the deprecated spelling of '${fix}' (ADR-0090 D3) and ` +\n `is removed in the next major.`,\n hint: `Author { type: '${fix}', value: '${value}' }. It resolves identically today.`,\n });\n } else if (\n (APPROVER_VALUE_BINDINGS as Record<string, { source: string }>)[canonical]?.source === 'unsupported'\n ) {\n // Declared-but-unenforced (#3508): the runtime has no resolution for\n // this type — the slot degrades to an inert `type:value` literal and\n // the request routes to nobody. Say it at authoring time instead of\n // letting the request stall silently (Prime Directive #10).\n findings.push({\n severity: 'warning',\n rule: APPROVAL_APPROVER_TYPE_UNSUPPORTED,\n where,\n path: `${path}.type`,\n message:\n `approver type '${type}' is declared but not implemented by the runtime (#3508) — ` +\n `the slot resolves to nobody and the request stalls.`,\n hint:\n `Route to people the engine can expand: { type: 'team' | 'department' | 'position', ... }. ` +\n `Queue approvers need a real ownership-queue implementation before they take effect.`,\n });\n }\n\n // [ADR-0105 D9] Cross-organization targeting on a type that has no\n // organization-scoped directory. `user` / `field` / `manager` name a\n // person outright and `team` membership carries no organization, so the\n // declaration cannot narrow anything — it is a misunderstanding of what\n // the field does, and the runtime refuses it. Error, not warning: this\n // is a certain authoring mistake with a certain fix, and letting it\n // reach the runtime turns author time into an incident.\n const declaredOrg = (a as AnyRec).organization;\n if (typeof declaredOrg === 'string' && declaredOrg.trim() !== ''\n && ApproverType.options.includes(canonical as never)\n && !approverTypeIsOrgScoped(canonical)) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_APPROVER_CROSS_ORG_UNSUPPORTED,\n where,\n path: `${path}.organization`,\n message:\n `approver type '${type}' does not resolve through an organization directory, so ` +\n `'organization: ${declaredOrg}' has no effect (ADR-0105 D9) — the runtime refuses it.`,\n hint:\n `Drop 'organization' here. Cross-organization targeting applies to ` +\n `'position', 'org_membership_level', 'department' and 'expression' approvers.`,\n });\n }\n }\n\n // Empty-slate dead-end (#3424): when EVERY approver on the node routes to\n // a group whose membership can be empty (an unstaffed position, an empty\n // team/department), the request can resolve to an empty `pending_approvers`\n // at runtime — no concrete user can act, and with `lockRecord` the record\n // stays locked with no recovery except a platform/tenant admin override.\n // Advisory (`info`): staffing is runtime data a linter can't see, so this\n // flags the risky SHAPE and prescribes a guaranteed-staffed fallback.\n const routable = approvers.filter(\n (a) => a && typeof a === 'object' && typeof (a as AnyRec).type === 'string',\n );\n if (\n routable.length > 0 &&\n routable.every((a) => GROUP_ROUTED_TYPES.has(canonicalApproverType(String((a as AnyRec).type))))\n ) {\n const locks = (cfg as AnyRec).lockRecord !== false; // default true\n findings.push({\n severity: 'info',\n rule: APPROVAL_APPROVERS_MAY_RESOLVE_EMPTY,\n where,\n path: `${nodePath}.config.approvers`,\n message:\n `every approver on this node routes to a group (position/team/department) whose ` +\n `members are runtime data — if none is staffed, the request resolves to an empty ` +\n `slate and waits forever` +\n (locks ? `, and (lockRecord) the record stays locked with no in-product recovery.` : `.`),\n hint:\n `Make sure at least one target is always staffed, or add a guaranteed-staffed ` +\n `fallback approver, e.g. { type: 'org_membership_level', value: 'owner' }. A request ` +\n `that still lands empty is recoverable only by a platform/tenant admin override (#3424).`,\n });\n }\n\n // #3447 P2: a node with an `expression` approver resolves people from\n // runtime data — an empty result is far likelier than for static types\n // (a mid-flow field nobody wrote yet, an upstream output that came back\n // empty). Nudge the author to SAY what an empty slate should do rather\n // than inherit the default silently.\n const hasExpression = approvers.some(\n (a) => a && typeof a === 'object' && canonicalApproverType(String((a as AnyRec).type ?? '')) === 'expression',\n );\n if (hasExpression && (cfg as AnyRec).onEmptyApprovers == null) {\n findings.push({\n severity: 'info',\n rule: APPROVAL_EXPRESSION_NO_EMPTY_POLICY,\n where,\n path: `${nodePath}.config`,\n message:\n `this node resolves approvers from an expression but declares no onEmptyApprovers — ` +\n `an empty result falls back to the default ('admin_rescue': request opens, only a ` +\n `privileged admin can act).`,\n hint:\n `Declare the empty-slate policy explicitly: onEmptyApprovers: 'admin_rescue' (hold for ` +\n `admin takeover), 'fail' (fail the node — config bug), or 'auto_approve' (wave through, ` +\n `output.autoApproved = true).`,\n });\n }\n\n // #3447 P2: `decision`/`requestId` ride the resume envelope; a declared\n // decision output with either name is rejected at runtime on every\n // decide — the node can never accept the output it declares.\n // Bare keys and typed { key, … } declarations whitelist identically —\n // the spec normalizer is the one reader of the union shape.\n const declaredOutputs = normalizeDecisionOutputs((cfg as AnyRec).decisionOutputs).map((d) => d.key);\n const reserved = declaredOutputs.filter((k) => RESERVED_OUTPUT_KEYS.has(k));\n if (reserved.length) {\n findings.push({\n severity: 'error',\n rule: APPROVAL_DECISION_OUTPUTS_RESERVED,\n where,\n path: `${nodePath}.config.decisionOutputs`,\n message:\n `decisionOutputs declares reserved key(s) \\`${reserved.join('`, `')}\\` — the resume ` +\n `envelope owns them, so every decide carrying them is rejected.`,\n hint: `Rename the output key(s); any name other than 'decision'/'requestId' works.`,\n });\n }\n\n // escalation.action 'reassign' with no escalateTo silently degrades to a\n // plain SLA-breach notification at runtime — the hand-off the author\n // asked for never happens.\n const escalation = (cfg.escalation ?? null) as AnyRec | null;\n if (escalation && typeof escalation === 'object' && escalation.action === 'reassign') {\n const target = typeof escalation.escalateTo === 'string' ? escalation.escalateTo.trim() : '';\n if (!target) {\n findings.push({\n severity: 'warning',\n rule: APPROVAL_ESCALATION_REASSIGN_NO_TARGET,\n where,\n path: `${nodePath}.config.escalation.escalateTo`,\n message:\n `escalation.action is 'reassign' but escalateTo is empty — at runtime the ` +\n `escalation degrades to a notify and the request stays with the original approvers.`,\n hint:\n `Set escalateTo to a position machine name (expanded via sys_user_position, ` +\n `ADR-0090 D3) or a specific user id, or change action to 'notify'.`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for replay-unsafe seed datasets (framework#3434).\n//\n// A pure `(stack) => Finding[]` rule (ADR-0019), run from `os validate` and\n// reusable by AI authoring. Seeds are REPLAYED — they re-load on every\n// dev-server boot and every package re-publish, not applied once — so a\n// dataset's mode has to be idempotent. `mode: 'insert'` is the one mode that\n// is not: the loader's `insert` path writes every record unconditionally, with\n// no existing-row check, so the table grows by the dataset's size on every\n// restart (the showcase `showcase_project_membership` fixture went 3 → 6 → 9).\n//\n// This is the authoring-time nudge that would have caught #3434 before boot:\n// flag `insert`, and point at the idempotent modes (`ignore` / `upsert`) plus\n// the `externalId` — single field, or a COMPOSITE list of fields for a join /\n// junction table that has no single natural key (`['team', 'project']`), the\n// support for which #3434 added.\n//\n// Advisory (warning): an `insert` seed is not a schema error — it loads and\n// \"works\" on a fresh DB; the defect only shows on the second boot. So it earns\n// a located fix-it, not a hard `os compile` gate.\n\nexport type SeedReplaySafetySeverity = 'error' | 'warning';\n\nexport interface SeedReplaySafetyFinding {\n severity: SeedReplaySafetySeverity;\n rule: string;\n /** Human-readable location, e.g. `seed \"showcase_project_membership\"`. */\n where: string;\n /** Config path, e.g. `data[12].mode`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const SEED_INSERT_MODE_DUPLICATES_ON_REPLAY = 'seed-insert-mode-duplicates-on-replay';\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * Flag every seed dataset declared with `mode: 'insert'` — the one non-idempotent\n * mode, which duplicates its rows on every replay boot (framework#3434). Returns\n * the findings (empty = clean). The caller decides how to surface them / whether\n * to fail the build; the CLI folds them in as advisory warnings.\n *\n * Reads `stack.data` (the `SeedSchema[]` fixtures). Safe on any shape — a stack\n * with no `data` array yields no findings.\n */\nexport function validateSeedReplaySafety(stack: AnyRec): SeedReplaySafetyFinding[] {\n const out: SeedReplaySafetyFinding[] = [];\n const seeds = Array.isArray(stack.data) ? (stack.data as AnyRec[]) : [];\n\n seeds.forEach((seed, i) => {\n if (!seed || typeof seed !== 'object') return;\n if (seed.mode !== 'insert') return;\n\n const object = typeof seed.object === 'string' ? seed.object : undefined;\n const where = object ? `seed \"${object}\"` : `data[${i}]`;\n\n out.push({\n severity: 'warning',\n rule: SEED_INSERT_MODE_DUPLICATES_ON_REPLAY,\n where,\n path: `data[${i}].mode`,\n message:\n \"`mode: 'insert'` re-inserts every record on each replay boot (dev-server restart, \" +\n 'package re-publish) with no existing-row check, so the dataset duplicates the table ' +\n 'on every restart — seeds are replayed, not applied once.',\n hint:\n \"Use `mode: 'ignore'` (skip rows that already exist) or `'upsert'` (create-or-update), \" +\n \"and declare an `externalId` to match on: a single natural-key field (e.g. `externalId: 'code'`), \" +\n 'or a COMPOSITE list of fields for a join / junction table with no single natural key ' +\n \"(e.g. `externalId: ['team', 'project']`).\",\n });\n });\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Build-time guardrail for seed values that fall outside an object's declared\n// state machine (framework#3433 follow-up).\n//\n// #3433 made seed writes EXEMPT from the `state_machine` validation rule — a\n// curated seed is an established fact, so it may be born mid-lifecycle\n// (a `completed` project, a `closed_won` opportunity) without the FSM entry\n// guard rejecting it. That exemption is deliberate, but it is also a SILENT\n// back door: the state machine's \"is this value even a state I know about?\"\n// check no longer runs for seed rows. A field-level `select` still rejects a\n// value outside its `options` at write time, so a plain typo is caught there —\n// but a `state_machine` on a free-text field, or a value that is a valid option\n// yet not a declared FSM state, now sails straight through.\n//\n// This author-time rule re-adds that safety net WITHOUT re-imposing the FSM: a\n// seeded value need not be an initial state (that is the whole point of the\n// exemption), but it must be a state the machine DECLARES — the union of\n// `initialStates`, the transition-map keys, and the transition targets.\n// Anything else is almost certainly a typo or an FSM that forgot to declare the\n// state; either way the author should see it before boot.\n//\n// Advisory (warning): a curated value the FSM does not know about is suspicious\n// but not necessarily wrong. Located fix-it, not a hard `os compile` gate —\n// symmetric with the replay-safety rule (framework#3434).\n\nexport type SeedStateMachineSeverity = 'warning';\n\nexport interface SeedStateMachineFinding {\n severity: SeedStateMachineSeverity;\n rule: string;\n /** Human-readable location, e.g. `seed \"showcase_project\" (\"Legacy Sunset\")`. */\n where: string;\n /** Config path, e.g. `data[4].records[3].status`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const SEED_VALUE_OUTSIDE_STATE_MACHINE = 'seed-value-outside-state-machine';\n\ntype AnyRec = Record<string, unknown>;\n\ninterface FsmRule {\n field: string;\n /** Every state the machine declares: initialStates ∪ transition keys ∪ targets. */\n states: Set<string>;\n}\n\n/**\n * Collect the `state_machine` rules (field + full declared-state set) for every\n * object, keyed by object name. An object with no such rule contributes nothing.\n * The declared-state set is derived from the rule alone so the check does not\n * depend on the field's `options` shape (and so it covers free-text state\n * fields the enum validator never sees).\n */\nfunction fsmRulesByObject(objects: AnyRec[]): Map<string, FsmRule[]> {\n const map = new Map<string, FsmRule[]>();\n for (const obj of objects) {\n if (!obj || typeof obj !== 'object') continue;\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const validations = Array.isArray(obj.validations) ? (obj.validations as AnyRec[]) : [];\n const rules: FsmRule[] = [];\n for (const v of validations) {\n if (!v || typeof v !== 'object' || v.type !== 'state_machine') continue;\n const field = typeof v.field === 'string' ? v.field : undefined;\n if (!field) continue;\n const transitions =\n v.transitions && typeof v.transitions === 'object' ? (v.transitions as Record<string, unknown>) : {};\n const states = new Set<string>();\n for (const s of Array.isArray(v.initialStates) ? v.initialStates : []) states.add(String(s));\n for (const from of Object.keys(transitions)) {\n states.add(String(from));\n const targets = transitions[from];\n for (const to of Array.isArray(targets) ? targets : []) states.add(String(to));\n }\n // A state_machine with neither transitions nor initialStates declares no\n // states — nothing to check against, so skip it (never flag every value).\n if (states.size > 0) rules.push({ field, states });\n }\n if (rules.length > 0) map.set(name, rules);\n }\n return map;\n}\n\n/** Best-effort label for a seed record — its externalId value(s), else its index. */\nfunction recordLabel(record: AnyRec, externalId: unknown, index: number): string {\n const keys = Array.isArray(externalId)\n ? (externalId as unknown[]).map(String)\n : typeof externalId === 'string'\n ? [externalId]\n : ['name'];\n const parts = keys.map((k) => record[k]).filter((v) => v != null && v !== '');\n return parts.length > 0 ? parts.map(String).join(' · ') : `#${index}`;\n}\n\n/**\n * Flag every seed record whose `state_machine`-governed field carries a value\n * the machine does not declare (framework#3433 follow-up). Returns the findings\n * (empty = clean). The caller decides how to surface them; the CLI folds them in\n * as advisory warnings.\n *\n * Reads `stack.objects` (for the state-machine rules) and `stack.data` (the\n * `SeedSchema[]` fixtures). Safe on any shape — a stack with no objects or no\n * `data` array yields no findings. A value that is not a plain string (an\n * unresolved `cel` Expression envelope, a number) is skipped: it cannot be\n * statically compared to the declared-state set.\n */\nexport function validateSeedStateMachine(stack: AnyRec): SeedStateMachineFinding[] {\n const out: SeedStateMachineFinding[] = [];\n const objects = Array.isArray(stack.objects) ? (stack.objects as AnyRec[]) : [];\n const seeds = Array.isArray(stack.data) ? (stack.data as AnyRec[]) : [];\n if (objects.length === 0 || seeds.length === 0) return out;\n\n const rulesByObject = fsmRulesByObject(objects);\n if (rulesByObject.size === 0) return out;\n\n seeds.forEach((seed, i) => {\n if (!seed || typeof seed !== 'object') return;\n const objectName = typeof seed.object === 'string' ? seed.object : undefined;\n if (!objectName) return;\n const rules = rulesByObject.get(objectName);\n if (!rules) return;\n const records = Array.isArray(seed.records) ? (seed.records as AnyRec[]) : [];\n\n records.forEach((record, j) => {\n if (!record || typeof record !== 'object') return;\n for (const rule of rules) {\n const value = record[rule.field];\n // Absent / cleared → nothing to check. A non-string (Expression\n // envelope, number) can't be compared statically → skip.\n if (value == null || value === '') continue;\n if (typeof value !== 'string') continue;\n if (rule.states.has(value)) continue;\n\n out.push({\n severity: 'warning',\n rule: SEED_VALUE_OUTSIDE_STATE_MACHINE,\n where: `seed \"${objectName}\" (${recordLabel(record, seed.externalId, j)})`,\n path: `data[${i}].records[${j}].${rule.field}`,\n message:\n `seeds '${rule.field}=${value}', which the '${objectName}' state machine does not declare ` +\n `(known states: ${[...rule.states].sort().join(', ')}). Seed writes are exempt from the ` +\n 'state_machine rule (#3433), so this is NOT rejected at write time — a typo lands silently.',\n hint:\n `If '${value}' is a real state, add it to the state machine (as an initial state or a ` +\n `transition endpoint). If it is a typo, correct it to a declared state. The exemption lets ` +\n 'a seed be born mid-lifecycle; it is not a licence to write an unknown state.',\n });\n }\n });\n });\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0090 D7] Security-domain publish linter.\n *\n * Every rule here is traceable to an observed failure class (the taxonomy\n * grows by incident, per the ADR):\n *\n * | Rule | Origin |\n * |-----------------------------------------|---------------------------------|\n * | security-owd-unset (error) | objectui#2348 leave_request 事故 |\n * | security-owd-alias (error) | ADR-0090 D4 canonical enum |\n * | security-external-wider (error) | ADR-0090 D11 external ≤ internal|\n * | security-wildcard-vama (error) | ADR-0066 superuser wildcard |\n * | security-anchor-high-privilege(error) | ADR-0090 D5/D9 anchors |\n * | security-role-word (error) | ADR-0090 D3 vocabulary freeze |\n * | security-book-audience-unknown-set(warn)| ADR-0046 §6.7 { permissionSet } |\n * | security-private-no-readscope (info) | admin-intent mismatch class |\n * | security-master-detail-ungranted(warn) | framework#2700 os-tianshun-mtc#43|\n * | security-grant-expired-at-authoring(err)| ADR-0091 D2 resolution filtering|\n * | security-delegation-missing-reason(err) | ADR-0091 D3 dual audit |\n *\n * Per ADR-0049 discipline these are NOT advisory security: every `error` rule\n * mirrors a runtime enforcement point (D1 fail-closed OWD default, D4 zod\n * enum + fail-closed evaluator, D5/D9 anchor binding gate, D3 rename wave) —\n * the lint moves the failure from runtime-deny to author-time fix-it. The lone\n * `warning` (master-detail-ungranted) likewise mirrors a runtime gate — the\n * object-level CRUD check (ADR-0055) — but stays advisory: it flags a *likely*\n * misconfiguration whose per-permission-set nuance it cannot fully adjudicate.\n *\n * Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input (works both\n * pre- and post-zod-parse, so `os lint` catches what the zod gate would\n * reject in `os compile` — with a better message).\n */\n\nimport { describeAnchorForbiddenBits } from '@objectstack/spec/security';\n\nexport const SECURITY_OWD_UNSET = 'security-owd-unset';\nexport const SECURITY_OWD_ALIAS = 'security-owd-alias';\nexport const SECURITY_EXTERNAL_WIDER = 'security-external-wider-than-internal';\nexport const SECURITY_WILDCARD_VAMA = 'security-wildcard-vama';\nexport const SECURITY_ANCHOR_HIGH_PRIVILEGE = 'security-anchor-high-privilege';\nexport const SECURITY_ROLE_WORD = 'security-role-word';\nexport const SECURITY_BOOK_AUDIENCE_UNKNOWN_SET = 'security-book-audience-unknown-set';\nexport const SECURITY_PRIVATE_NO_READSCOPE = 'security-private-no-readscope';\nexport const SECURITY_MASTER_DETAIL_UNGRANTED = 'security-master-detail-ungranted';\nexport const SECURITY_FLS_UNQUALIFIED_KEY = 'security-fls-unqualified-key';\nexport const SECURITY_GRANT_EXPIRED_AT_AUTHORING = 'security-grant-expired-at-authoring';\nexport const SECURITY_DELEGATION_MISSING_REASON = 'security-delegation-missing-reason';\n\nexport type SecuritySeverity = 'error' | 'warning' | 'info';\n\nexport interface SecurityFinding {\n severity: SecuritySeverity;\n /** Diagnostic rule id (`security-*`). */\n rule: string;\n /** Human-readable location, e.g. `object \"leave_request\"`. */\n where: string;\n /** Config path, e.g. `objects[3].sharingModel`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nconst CANONICAL_OWD = ['private', 'public_read', 'public_read_write', 'controlled_by_parent'] as const;\n/** [ADR-0090 D4] Legacy alias → canonical fix-it mapping. */\nconst OWD_ALIAS_FIX: Record<string, string> = {\n read: 'public_read',\n read_write: 'public_read_write',\n full: 'public_read_write',\n public: 'public_read_write',\n};\n/** D11 ordering for external ≤ internal (controlled_by_parent excluded). */\nconst OWD_WIDTH: Record<string, number> = {\n private: 0,\n public_read: 1,\n public_read_write: 2,\n};\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction owdOf(obj: AnyRec): unknown {\n return obj.sharingModel ?? (obj.security as AnyRec | undefined)?.sharingModel;\n}\n\nfunction isSystemObject(obj: AnyRec): boolean {\n return obj.isSystem === true || String(obj.name ?? '').startsWith('sys_');\n}\n\n/** snake_case identifier contains the reserved token `role`/`roles`. */\nfunction identifierHasRoleToken(name: unknown): boolean {\n if (typeof name !== 'string') return false;\n return name\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .some((tok) => tok === 'role' || tok === 'roles');\n}\n\n/** Free-text label contains the whole word `role(s)` (case-insensitive). */\nfunction labelHasRoleWord(label: unknown): boolean {\n if (typeof label !== 'string') return false;\n return /\\brole(s)?\\b/i.test(label);\n}\n\n/** The `reference`/`reference_to` target a relationship field points at. */\nfunction refOf(def: AnyRec): string | undefined {\n const r = (def.reference ?? def.reference_to) as unknown;\n return typeof r === 'string' && r ? r : undefined;\n}\n\n/**\n * The first `master_detail` field on an object, if any — its presence is what\n * makes the object a DETAIL (the child side of a master-detail; ADR-0055).\n * Works for both the array and name-keyed-map field forms (`asArray` folds the\n * map key into `name`).\n */\nfunction firstMasterDetailField(obj: AnyRec): { name: string; parent?: string } | undefined {\n for (const f of asArray(obj.fields)) {\n if (f.type === 'master_detail') {\n return { name: String(f.name ?? '?'), parent: refOf(f) };\n }\n }\n return undefined;\n}\n\n/**\n * Does a per-object permission entry open the object-level CRUD gate at all?\n * Any of the four CRUD bits, or a super-user bypass (View/Modify All Data),\n * counts — this mirrors the runtime `checkObjectPermission` gate (ADR-0066 D2):\n * that gate returns true if ANY set contributes one of these for the object.\n */\nfunction grantsObjectAccess(p: AnyRec): boolean {\n return (\n p.allowRead === true ||\n p.allowCreate === true ||\n p.allowEdit === true ||\n p.allowDelete === true ||\n p.viewAllRecords === true ||\n p.modifyAllRecords === true\n );\n}\n\n/**\n * Validate the security posture of a stack. Returns findings (empty = clean).\n * `error` findings gate the build in `os compile`; `info` is advisory.\n *\n * `opts.nowMs` injects the clock for the ADR-0091 authoring-time expiry rule\n * (tests); production callers omit it.\n */\nexport function validateSecurityPosture(stack: AnyRec, opts?: { nowMs?: number }): SecurityFinding[] {\n const findings: SecurityFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const objects = asArray(stack.objects);\n const permissionSets = asArray(stack.permissions);\n\n // ── D1/D4/D11: per-object OWD posture ────────────────────────────────\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object') continue;\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n const objPath = `objects[${i}]`;\n const owd = owdOf(obj);\n const external = obj.externalSharingModel;\n\n if (!isSystemObject(obj)) {\n if (owd == null) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_UNSET,\n where: `object \"${objName}\"`,\n path: `${objPath}.sharingModel`,\n message:\n `custom object \"${objName}\" declares no sharingModel (OWD). The runtime fails ` +\n `CLOSED to 'private' (ADR-0090 D1), but the baseline must be an authored decision, ` +\n `not an accident — this is the exact shape of the leave_request incident (objectui#2348).`,\n hint:\n `Declare sharingModel explicitly: 'private' (owner + shares; recommended default), ` +\n `'public_read', 'public_read_write', or 'controlled_by_parent' (master-detail children).`,\n });\n } else if (typeof owd === 'string' && OWD_ALIAS_FIX[owd]) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_ALIAS,\n where: `object \"${objName}\"`,\n path: `${objPath}.sharingModel`,\n message:\n `sharingModel '${owd}' is a retired alias (ADR-0090 D4). The runtime fails CLOSED ` +\n `to 'private' on unknown values, so this object is NOT ${owd === 'read' ? 'readable' : 'writable'} org-wide.`,\n hint: `Replace with the canonical value: sharingModel: '${OWD_ALIAS_FIX[owd]}'.`,\n });\n } else if (typeof owd === 'string' && !(CANONICAL_OWD as readonly string[]).includes(owd)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_ALIAS,\n where: `object \"${objName}\"`,\n path: `${objPath}.sharingModel`,\n message:\n `sharingModel '${owd}' is not a canonical OWD value; the runtime fails CLOSED to 'private'.`,\n hint: `Use one of: ${CANONICAL_OWD.join(', ')}.`,\n });\n }\n }\n\n // D11: external dial present on any object (system included) must obey\n // external ≤ internal. controlled_by_parent inherits the master's pair.\n if (typeof external === 'string') {\n if (OWD_ALIAS_FIX[external]) {\n findings.push({\n severity: 'error',\n rule: SECURITY_OWD_ALIAS,\n where: `object \"${objName}\"`,\n path: `${objPath}.externalSharingModel`,\n message: `externalSharingModel '${external}' is a retired alias (ADR-0090 D4).`,\n hint: `Replace with the canonical value: externalSharingModel: '${OWD_ALIAS_FIX[external]}'.`,\n });\n } else if (\n typeof owd === 'string' &&\n external in OWD_WIDTH &&\n owd in OWD_WIDTH &&\n OWD_WIDTH[external] > OWD_WIDTH[owd]\n ) {\n findings.push({\n severity: 'error',\n rule: SECURITY_EXTERNAL_WIDER,\n where: `object \"${objName}\"`,\n path: `${objPath}.externalSharingModel`,\n message:\n `externalSharingModel '${external}' is WIDER than the internal sharingModel '${owd}' — ` +\n `the external baseline must never exceed the internal one (ADR-0090 D11).`,\n hint: `Narrow externalSharingModel to '${owd}' or below (ordering: private < public_read < public_read_write).`,\n });\n }\n }\n }\n\n // ── ADR-0066 / D5/D9: permission-set posture ─────────────────────────\n for (let i = 0; i < permissionSets.length; i++) {\n const ps = permissionSets[i];\n if (!ps || typeof ps !== 'object') continue;\n const psName = typeof ps.name === 'string' ? ps.name : `(permission set ${i})`;\n const psPath = `permissions[${i}]`;\n const objectsMap = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n\n // [#19 / permission zoo audit] FLS keys MUST be `<object>.<field>`\n // qualified. The runtime evaluator matches keys by object prefix\n // (`getFieldPermissions`: `key.startsWith(objectName + '.')`), so a bare\n // `budget` key matches NOTHING — the declared masking silently never\n // enforces (the worst declared-≠-enforced class, ADR-0049). The showcase\n // itself shipped this bug for months.\n const flsMap = (ps.fields && typeof ps.fields === 'object' ? ps.fields : {}) as AnyRec;\n for (const flsKey of Object.keys(flsMap)) {\n if (flsKey.includes('.')) continue;\n findings.push({\n severity: 'error',\n rule: SECURITY_FLS_UNQUALIFIED_KEY,\n where: `permission set \"${psName}\"`,\n path: `${psPath}.fields[\"${flsKey}\"]`,\n message:\n `field-permission key '${flsKey}' is not object-qualified — the runtime matches FLS keys ` +\n `by '<object>.<field>' prefix, so a bare key is silently IGNORED and the declared masking never enforces.`,\n hint: `Qualify the key with its object, e.g. 'crm_opportunity.${flsKey}': { readable: true, editable: false }.`,\n });\n }\n\n const wildcard = objectsMap['*'] as AnyRec | undefined;\n if (wildcard && (wildcard.viewAllRecords === true || wildcard.modifyAllRecords === true)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_WILDCARD_VAMA,\n where: `permission set \"${psName}\"`,\n path: `${psPath}.objects.*`,\n message:\n `'*' wildcard carrying View All / Modify All Data — a package-authored superuser. ` +\n `Only the platform's own admin set may combine the wildcard with VAMA (ADR-0066).`,\n hint:\n `Enumerate the objects this set really needs, or drop viewAllRecords/modifyAllRecords ` +\n `from the wildcard entry. App-level admins belong in an ordinary set the customer binds ` +\n `to a position of their choosing (ADR-0090 D9).`,\n });\n }\n\n // D5: an isDefault set is a SUGGESTED binding to the `everyone` anchor —\n // hold it to the anchor tier at author time (the runtime gate enforces the\n // same predicate at bind time; this moves the failure to the author).\n if (ps.isDefault === true) {\n const offending = describeAnchorForbiddenBits(ps, 'everyone');\n if (offending) {\n findings.push({\n severity: 'error',\n rule: SECURITY_ANCHOR_HIGH_PRIVILEGE,\n where: `permission set \"${psName}\"`,\n path: `${psPath}.isDefault`,\n message:\n `isDefault:true suggests binding this set to the 'everyone' audience anchor, but it ` +\n `carries ${offending} — the runtime will refuse the binding (ADR-0090 D5/D9).`,\n hint:\n `Split the powerful bits into a separate set granted through ordinary positions, and ` +\n `keep the everyone-suggested set low-privilege.`,\n });\n }\n }\n }\n\n // ── D3: the word \"role\" is reserved-forbidden ────────────────────────\n // Scope: security-relevant identifiers/labels (objects, fields, actions,\n // permission sets, positions, apps). Pages/views/components are NOT\n // scanned — `role` there is HTML/ARIA semantics, not permission vocabulary.\n // The sole platform exception (better-auth `sys_member.role`) is a system\n // object, which app stacks never author.\n const flagRole = (kind: string, name: unknown, label: unknown, where: string, path: string) => {\n if (identifierHasRoleToken(name)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_ROLE_WORD,\n where,\n path,\n message:\n `${kind} name \"${String(name)}\" uses the reserved word \"role\" — the platform vocabulary ` +\n `is permission_set (capability), position (distribution), business_unit (hierarchy) (ADR-0090 D3).`,\n hint: `Rename using 'position' for distribution groups or a domain word (e.g. 'function', 'duty').`,\n });\n } else if (labelHasRoleWord(label)) {\n findings.push({\n severity: 'error',\n rule: SECURITY_ROLE_WORD,\n where,\n path: `${path.replace(/\\.name$/, '')}.label`,\n message: `${kind} label \"${String(label)}\" uses the reserved word \"role\" (ADR-0090 D3).`,\n hint: `Relabel with 'Position' (distribution) or a domain word — admins must meet ONE vocabulary.`,\n });\n }\n };\n\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object' || isSystemObject(obj)) continue;\n const objName = typeof obj.name === 'string' ? obj.name : `(object ${i})`;\n flagRole('object', obj.name, obj.label, `object \"${objName}\"`, `objects[${i}].name`);\n for (const f of asArray(obj.fields)) {\n flagRole('field', f.name, f.label, `field \"${objName}.${String(f.name ?? '?')}\"`, `objects[${i}].fields.${String(f.name ?? '?')}.name`);\n }\n for (const [ai, action] of asArray(obj.actions).entries()) {\n flagRole('action', action.name, action.label, `action \"${objName}.${String(action.name ?? '?')}\"`, `objects[${i}].actions[${ai}].name`);\n }\n }\n for (let i = 0; i < permissionSets.length; i++) {\n const ps = permissionSets[i];\n if (!ps || typeof ps !== 'object') continue;\n flagRole('permission set', ps.name, ps.label, `permission set \"${String(ps.name ?? i)}\"`, `permissions[${i}].name`);\n }\n for (const [i, pos] of asArray(stack.positions).entries()) {\n flagRole('position', pos.name, pos.label, `position \"${String(pos.name ?? i)}\"`, `positions[${i}].name`);\n }\n for (const [i, app] of asArray(stack.apps).entries()) {\n flagRole('app', app.name, app.label, `app \"${String(app.name ?? i)}\"`, `apps[${i}].name`);\n }\n for (const [i, book] of asArray(stack.books).entries()) {\n // Books entered the security-relevant set when `book.audience` became a\n // permission-model reference (ADR-0046 §6.7 / ADR-0090): their names and\n // labels are access-adjacent UI copy.\n flagRole('book', book.name, book.label, `book \"${String(book.name ?? i)}\"`, `books[${i}].name`);\n }\n\n // ── Book audience → permission-set reference must resolve ────────────\n // A `{ permissionSet }` book audience names a set the reader must hold\n // (ADR-0046 §6.7). The runtime fails CLOSED on an unknown name (nobody\n // holds it → nobody reads the book), so a typo is not a leak — but it IS\n // the \"why can nobody see the Admin Guide\" support class, and packages\n // should gate their books on their own sets (ADR-0090 D9 / ADR-0086\n // provenance). Advisory: an environment-authored book may legitimately\n // reference an installed package's set that is not in THIS stack.\n const stackSetNames = new Set(\n permissionSets\n .map((ps) => (typeof ps.name === 'string' ? ps.name : undefined))\n .filter((n): n is string => !!n),\n );\n for (const [i, book] of asArray(stack.books).entries()) {\n const audience = (book as AnyRec).audience;\n if (!audience || typeof audience !== 'object') continue;\n const setName = (audience as AnyRec).permissionSet;\n if (typeof setName !== 'string' || setName.length === 0) continue;\n if (!stackSetNames.has(setName)) {\n findings.push({\n severity: 'warning',\n rule: SECURITY_BOOK_AUDIENCE_UNKNOWN_SET,\n where: `book \"${String(book.name ?? i)}\"`,\n path: `books[${i}].audience.permissionSet`,\n message:\n `book audience references permission set \"${setName}\", which this stack does not declare. ` +\n `The runtime fails closed — no holder means NO reader can open the book.`,\n hint:\n `Gate the book on one of this package's own permission sets (ADR-0090 D9, e.g. its admin set), ` +\n `or fix the typo. Ignore if the set is intentionally provided by another installed package.`,\n });\n }\n }\n\n // ── Admin-intent mismatch: private object, plain read, no depth ──────\n // An object whose baseline is private (explicit or D1-defaulted) where a set\n // grants allowRead with neither readScope nor viewAllRecords: every reader\n // sees ONLY their own records. Legitimate (personal to-dos) often enough\n // that this stays `info` — but it is the #1 \"why can't 李四 see the data\"\n // support class, so say it out loud at author time.\n const privateObjects = new Set(\n objects\n .filter((o) => o && typeof o === 'object' && !isSystemObject(o))\n .filter((o) => {\n const owd = owdOf(o);\n return owd == null || owd === 'private';\n })\n .map((o) => String(o.name ?? '')),\n );\n if (privateObjects.size > 0) {\n for (let i = 0; i < permissionSets.length; i++) {\n const ps = permissionSets[i];\n if (!ps || typeof ps !== 'object') continue;\n const psName = typeof ps.name === 'string' ? ps.name : `(permission set ${i})`;\n const objectsMap = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n for (const [objName, rawPerm] of Object.entries(objectsMap)) {\n if (!privateObjects.has(objName)) continue;\n const p = (rawPerm ?? {}) as AnyRec;\n if (p.allowRead === true && p.readScope == null && p.viewAllRecords !== true) {\n findings.push({\n severity: 'info',\n rule: SECURITY_PRIVATE_NO_READSCOPE,\n where: `permission set \"${psName}\"`,\n path: `permissions[${i}].objects.${objName}.readScope`,\n message:\n `\"${objName}\" is private (OWD) and this set grants allowRead without a readScope — ` +\n `holders see ONLY records they own (plus explicit shares).`,\n hint:\n `If that is intended (personal data), ignore this. Otherwise add readScope: ` +\n `'own_and_reports' | 'unit' | 'unit_and_below' | 'org', or widen the object's sharingModel.`,\n });\n }\n }\n }\n }\n\n // ── ADR-0055: master-detail DETAIL object with no object-level CRUD ───\n // A master-detail CHILD derives its RECORD-level scope from the master\n // (`controlled_by_parent`) — but that is gate ②. Object-level CRUD is a\n // SEPARATE gate ① (`checkObjectPermission`) that is NEVER derived: a set that\n // lists the parent but forgets the child denies role-bound non-admin users a\n // 403 *before* the parent-derived access is ever consulted, surfacing as the\n // silent \"can't fill in / can't submit the subtable\" trap (framework#2700,\n // downstream os-tianshun-mtc#43). Statically detectable: a detail (has a\n // master_detail field) that NO authored permission set grants.\n //\n // Advisory `warning` — it does not gate the build. Two deliberate silences\n // keep the false-positive rate near zero: (a) if the package authors no\n // permission sets there is nothing to compare against, and (b) a package-\n // declared `'*'` wildcard grant is treated as covering every object (a broad\n // grant is an explicit choice — suppress rather than cry wolf). The residual\n // per-set gap (one role grants it, another forgets it) is intentionally out\n // of scope (issue #2700); the platform's own default admin set lives outside\n // the linted stack, so it never masks a package that forgot the child here.\n if (permissionSets.length > 0) {\n const wildcardGrantsAll = permissionSets.some((ps) =>\n grantsObjectAccess(((ps.objects as AnyRec | undefined)?.['*'] ?? {}) as AnyRec),\n );\n if (!wildcardGrantsAll) {\n const grantedObjects = new Set<string>();\n for (const ps of permissionSets) {\n const objectsMap = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n for (const [objName, rawPerm] of Object.entries(objectsMap)) {\n if (objName === '*') continue;\n if (grantsObjectAccess((rawPerm ?? {}) as AnyRec)) grantedObjects.add(objName);\n }\n }\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj || typeof obj !== 'object' || isSystemObject(obj)) continue;\n const objName = typeof obj.name === 'string' ? obj.name : '';\n if (!objName || grantedObjects.has(objName)) continue;\n const md = firstMasterDetailField(obj);\n if (!md) continue;\n const parentText = md.parent ? ` → \"${md.parent}\"` : '';\n findings.push({\n severity: 'warning',\n rule: SECURITY_MASTER_DETAIL_UNGRANTED,\n where: `object \"${objName}\"`,\n path: `objects[${i}].fields.${md.name}`,\n message:\n `detail object \"${objName}\" (master_detail \"${md.name}\"${parentText}) has no object-level ` +\n `CRUD grant in any permission set. A master-detail child derives its RECORD-level access ` +\n `from the master (ADR-0055 controlled_by_parent), but object-level CRUD is a SEPARATE gate ` +\n `that is never derived — role-bound non-admin users are denied (403) before the ` +\n `parent-derived access is ever consulted (the silent \"can't submit the subtable\" trap).`,\n hint:\n `Grant \"${objName}\" in at least one permission set that already grants its master` +\n `${md.parent ? ` \"${md.parent}\"` : ''} — e.g. permissions[i].objects.${objName} = ` +\n `{ allowRead: true, allowCreate: true, allowEdit: true }. If no role should ever touch ` +\n `it (a pure system/internal table), name it sys_* or set isSystem: true.`,\n });\n }\n }\n }\n\n // ── ADR-0091: authored grant rows (seed data) — lifecycle sanity ──────\n // Grant assignments authored as seed data on the two user-grant tables.\n // Both rules mirror runtime enforcement (D2 resolution-time filtering; the\n // D3 delegation gate), per the ADR-0049 \"no advisory security\" discipline:\n // the lint moves the failure from silent-dead-grant to author-time fix-it.\n const GRANT_SEED_OBJECTS = new Set(['sys_user_position', 'sys_user_permission_set']);\n const nowMs = opts?.nowMs ?? Date.now();\n for (const [i, seed] of asArray(stack.data).entries()) {\n const seedObject = typeof seed.object === 'string' ? seed.object : '';\n if (!GRANT_SEED_OBJECTS.has(seedObject)) continue;\n const records = Array.isArray(seed.records) ? (seed.records as AnyRec[]) : [];\n for (let j = 0; j < records.length; j++) {\n const rec = (records[j] ?? {}) as AnyRec;\n const where = `seed \"${seedObject}\" record #${j}`;\n\n // D2: a valid_until already in the past (or unparseable) at authoring\n // time is a grant that will NEVER resolve — dead on arrival, fail-closed.\n const until = rec.valid_until;\n if (until != null && until !== '') {\n const ms =\n typeof until === 'number'\n ? (until < 1e12 ? until * 1000 : until)\n : until instanceof Date\n ? until.getTime()\n : typeof until === 'string'\n ? Date.parse(until)\n : Number.NaN;\n if (Number.isNaN(ms) || ms <= nowMs) {\n findings.push({\n severity: 'error',\n rule: SECURITY_GRANT_EXPIRED_AT_AUTHORING,\n where,\n path: `data[${i}].records[${j}].valid_until`,\n message: Number.isNaN(ms)\n ? `valid_until ${JSON.stringify(until)} is not a parseable timestamp — the resolver fails ` +\n `closed (ADR-0091 D2), so this grant will NEVER be active.`\n : `valid_until ${JSON.stringify(until)} is already in the past — this grant is expired at ` +\n `authoring time and will never resolve (ADR-0091 D2 filters it fail-closed).`,\n hint:\n `Set valid_until to a future instant (ISO-8601 UTC), or drop the column for an unbounded ` +\n `grant. If the row is a historical record, it belongs in audit history, not seed data.`,\n });\n }\n }\n\n // D3: delegation rows (delegated_from set) MUST carry a reason — the\n // dual-audit half the runtime gate also rejects.\n const delegatedFrom = rec.delegated_from;\n if (delegatedFrom != null && delegatedFrom !== '') {\n const reason = rec.reason;\n if (typeof reason !== 'string' || reason.trim().length === 0) {\n findings.push({\n severity: 'error',\n rule: SECURITY_DELEGATION_MISSING_REASON,\n where,\n path: `data[${i}].records[${j}].reason`,\n message:\n `delegation row (delegated_from = ${JSON.stringify(delegatedFrom)}) has no reason. ` +\n `ADR-0091 D3 requires a mandatory reason on every delegation for the dual audit trail ` +\n `(granted_by = writer, delegated_from = authority source, reason = why).`,\n hint: `Add reason: 'vacation stand-in for 张三, 2026-08-01..15' (free text, required).`,\n });\n }\n }\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0105 D6] The two organization-axis red lines, enforced at authoring time.\n *\n * ADR-0105 gives organizations a reporting/grouping dimension\n * (`sys_organization.parent_organization_id`, sibling ordering). That dimension\n * is load-bearing for consolidated reporting — and dangerous, because it LOOKS\n * like a permission hierarchy. Two lines keep it from becoming one:\n *\n * | Rule | Red line |\n * |-----------------------------------------|-----------------------------------|\n * | org-axis-permission-inheritance (error) | D6 ①: no inheritance along the org tree |\n * | org-axis-cross-org-bu-grant (error) | D6 ②: business-unit trees stay org-internal |\n *\n * **① No permission inheritance along the org axis.** Cross-organization\n * visibility comes from membership union (`accessible_org_ids`, ADR-0105 D2) —\n * the engine's own Layer 0 wall — never from walking a parent reference. An RLS\n * policy or sharing rule that reads `parent_organization_id` builds a SECOND\n * permission hierarchy beside the business-unit tree: exactly the dual-hierarchy\n * mistake ADR-0057 D5 retired and ADR-0090 D3 finalized for positions. It also\n * silently outranks the wall it sits behind, since a Layer-1 policy cannot widen\n * Layer 0 (W1) — so the author gets a rule that appears to grant access and\n * does not. Fail at authoring, not in a support ticket.\n *\n * **② Business-unit trees remain org-internal.** `sys_business_unit` is\n * org-scoped and every BU mechanism (`unit_and_subordinates` sharing,\n * `adminScope` delegation, depth scopes) resolves within ONE organization. A\n * business-unit sharing rule on a PLATFORM-GLOBAL object (`tenancy.enabled:\n * false`) has no organization column to scope against, so the grant spans every\n * organization in the database — a cross-org BU grant by construction, and the\n * \"cross-org BU mega-tree\" the ADR rejected, arrived at by accident.\n *\n * Both are `error`, per ADR-0049 discipline: each mirrors a real enforcement\n * property (the Layer 0 wall's independence; the org-predicated BU resolver),\n * so the lint moves the failure from silent-wrong-answer to author-time fix-it.\n *\n * Pure `(stack) => Finding[]`; accepts the NORMALIZED stack input.\n */\n\nexport const ORG_AXIS_PERMISSION_INHERITANCE = 'org-axis-permission-inheritance';\nexport const ORG_AXIS_CROSS_ORG_BU_GRANT = 'org-axis-cross-org-bu-grant';\n\nexport type OrgAxisSeverity = 'error' | 'warning';\n\nexport interface OrgAxisFinding {\n severity: OrgAxisSeverity;\n /** Diagnostic rule id (`org-axis-*`). */\n rule: string;\n /** Human-readable location, e.g. `permission set \"plant_reader\"`. */\n where: string;\n /** Config path, e.g. `permissions[2].rowLevelSecurity[0].using`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** The org-axis grouping reference. Reporting only — never an authorization input. */\nconst ORG_PARENT_FIELD = 'parent_organization_id';\n\n/** Coerce a collection (array or name-keyed map) to an array of records. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction str(v: unknown): string {\n return typeof v === 'string' ? v : '';\n}\n\n/** True iff the object opted out of tenancy — platform-global, no org column. */\nfunction isTenancyDisabled(object: AnyRec): boolean {\n const tenancy = object.tenancy as AnyRec | undefined;\n if (tenancy && typeof tenancy === 'object' && tenancy.enabled === false) return true;\n const systemFields = object.systemFields as AnyRec | undefined;\n if (systemFields && typeof systemFields === 'object' && systemFields.tenant === false) return true;\n return false;\n}\n\nconst INHERITANCE_HINT =\n `Remove the ${ORG_PARENT_FIELD} reference. Cross-organization visibility comes from MEMBERSHIP: ` +\n `under the \\`group\\` tenancy posture the engine's Layer 0 wall is ` +\n `\\`organization_id IN accessible_org_ids\\`, so a user who should see several organizations is made ` +\n `a member of them (ADR-0105 D2). A Layer-1 policy cannot widen Layer 0 anyway, so this rule would ` +\n `not grant the access it appears to. For a hierarchy INSIDE one organization, use the business-unit ` +\n `tree (\\`unit_and_subordinates\\` sharing, or a depth scope anchored on \\`sys_user_position\\`).`;\n\n/**\n * Lint an ObjectStack config for the ADR-0105 D6 organization-axis red lines.\n */\nexport function validateOrgAxisRedLines(stack: unknown): OrgAxisFinding[] {\n const findings: OrgAxisFinding[] = [];\n const cfg = (stack ?? {}) as AnyRec;\n\n // ── ① No permission inheritance along the org axis ────────────────────────\n //\n // RLS policies may live on a permission set (`rowLevelSecurity`) or be\n // authored per object; both reach the same compiler, so both are checked.\n const permissionSets = asArray(cfg.permissions ?? cfg.permissionSets);\n permissionSets.forEach((ps, psIndex) => {\n asArray(ps.rowLevelSecurity).forEach((policy, pIndex) => {\n for (const clause of ['using', 'check'] as const) {\n if (!str(policy[clause]).includes(ORG_PARENT_FIELD)) continue;\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_PERMISSION_INHERITANCE,\n where: `permission set \"${str(ps.name) || psIndex}\" policy \"${str(policy.name) || pIndex}\"`,\n path: `permissions[${psIndex}].rowLevelSecurity[${pIndex}].${clause}`,\n message:\n `RLS ${clause} reads \\`${ORG_PARENT_FIELD}\\`, which builds a permission hierarchy along the ` +\n `organization axis. ADR-0105 D6 forbids it: the org tree is a REPORTING dimension only.`,\n hint: INHERITANCE_HINT,\n });\n }\n });\n });\n\n const objects = asArray(cfg.objects);\n objects.forEach((object, oIndex) => {\n const objectName = str(object.name) || String(oIndex);\n\n asArray(object.rowLevelSecurity ?? object.rls).forEach((policy, pIndex) => {\n for (const clause of ['using', 'check'] as const) {\n if (!str(policy[clause]).includes(ORG_PARENT_FIELD)) continue;\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_PERMISSION_INHERITANCE,\n where: `object \"${objectName}\" policy \"${str(policy.name) || pIndex}\"`,\n path: `objects[${oIndex}].rowLevelSecurity[${pIndex}].${clause}`,\n message:\n `RLS ${clause} reads \\`${ORG_PARENT_FIELD}\\`, which builds a permission hierarchy along the ` +\n `organization axis. ADR-0105 D6 forbids it: the org tree is a REPORTING dimension only.`,\n hint: INHERITANCE_HINT,\n });\n }\n });\n });\n\n // Sharing rules — criteria and recipient may both reach for the org parent.\n asArray(cfg.sharingRules ?? cfg.sharing).forEach((rule, rIndex) => {\n const criteria = JSON.stringify(rule.criteria ?? rule.filter ?? '');\n const sharedTo = JSON.stringify(rule.sharedTo ?? rule.recipient ?? '');\n if (criteria.includes(ORG_PARENT_FIELD) || sharedTo.includes(ORG_PARENT_FIELD)) {\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_PERMISSION_INHERITANCE,\n where: `sharing rule \"${str(rule.name) || rIndex}\"`,\n path: `sharingRules[${rIndex}]`,\n message:\n `Sharing rule reads \\`${ORG_PARENT_FIELD}\\`, granting access by walking the organization ` +\n `tree. ADR-0105 D6 forbids permission inheritance along the org axis.`,\n hint: INHERITANCE_HINT,\n });\n }\n });\n\n // ── ② Business-unit trees remain org-internal ─────────────────────────────\n //\n // A `business_unit` recipient on a platform-global object has no organization\n // column to scope against, so the grant reaches every organization's rows.\n const tenancyDisabledObjects = new Set(\n objects.filter((o) => isTenancyDisabled(o)).map((o) => str(o.name)).filter(Boolean),\n );\n asArray(cfg.sharingRules ?? cfg.sharing).forEach((rule, rIndex) => {\n const target = str(rule.object ?? rule.objectName);\n if (!target || !tenancyDisabledObjects.has(target)) return;\n const sharedTo = (rule.sharedTo ?? rule.recipient) as AnyRec | undefined;\n const recipientType = str(sharedTo?.type);\n if (recipientType !== 'business_unit') return;\n findings.push({\n severity: 'error',\n rule: ORG_AXIS_CROSS_ORG_BU_GRANT,\n where: `sharing rule \"${str(rule.name) || rIndex}\" on object \"${target}\"`,\n path: `sharingRules[${rIndex}].sharedTo`,\n message:\n `A business-unit sharing rule targets \"${target}\", which opted out of tenancy ` +\n `(\\`tenancy.enabled: false\\`). Platform-global objects carry no organization column, so this ` +\n `grant spans EVERY organization — a cross-organization business-unit grant, which ADR-0105 D6 ` +\n `forbids (BU trees are org-internal).`,\n hint:\n `Either scope the object to organizations (drop \\`tenancy.enabled: false\\` so Layer 0 walls it), ` +\n `or share it to a position / permission-set audience instead of a business unit. A ` +\n `platform-global catalog that everyone should read wants an OWD of \\`public_read\\`, not a BU grant.`,\n });\n });\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0049 — references] Reference-integrity for dashboard header & widget\n * action targets (issue #3367).\n *\n * ADR-0049 established the \"enforce-or-remove\" gate for spec *properties*: a\n * declared property the runtime does not honour is a false promise and must be\n * enforced, marked experimental, or removed. This rule applies the SAME honesty\n * principle to *references*. A dashboard header action (or a widget's header\n * action button) names a target — a `script`/`modal` action, or a `url` route —\n * that must actually resolve. A dangling target ships a button that renders and,\n * on click, silently does nothing: a false affordance, exactly the failure\n * ADR-0049 exists to prevent, just for a reference rather than a property.\n *\n * Nothing in the protocol schema can express this: `actionUrl` is a free string,\n * so `{ actionType: 'script', actionUrl: 'export_dashboard_pdf' }` parses and\n * ships even when no such action is defined anywhere in the stack.\n *\n * Surfaces checked:\n * - dashboard `header.actions[]` — each `{ actionType, actionUrl }`\n * - dashboard `widgets[].actionUrl` (+ `actionType`) — the per-widget button\n *\n * Resolution mirrors the objectui runtime dispatch (`DashboardRenderer` +\n * `DashboardView`) so the lint flags exactly what would fail to resolve at\n * runtime:\n *\n * actionType 'script' → `actionUrl` must name a DEFINED action (`stack.actions`\n * or any `object.actions`, by `name`). A script target that names no\n * defined action fails open at runtime (\"action not found\"). → ERROR.\n *\n * actionType 'modal' → `actionUrl` resolves if it names a defined action, OR\n * matches the runtime `<verb>_<object>` convention the modalHandler\n * implements (create_/new_/add_/edit_/update_ + a defined object), OR is a\n * bare defined object name (the handler falls back to that object's create\n * form). Otherwise → ERROR.\n *\n * actionType 'url' → a relative in-app path. WARN when a recognizable\n * `<collection>/<name>` segment (objects/reports/dashboards/pages/views)\n * names an entity that does not exist in this stack. External URLs\n * (`http(s)://`, `//`), interpolated targets (`${…}`), and opaque routes\n * (no recognized collection segment) are skipped — they cannot be resolved\n * statically and may be host/app/plugin routes. → WARNING.\n *\n * actionType 'flow' | 'api' — not checked: flow targets resolve against the\n * automation engine / other packages, and api targets are opaque endpoints.\n * Out of scope for #3367.\n *\n * Severity split follows the issue's acceptance criteria: an undefined\n * `script`/`modal` target FAILS validation (a genuine dead reference that fails\n * open at runtime as a dead button); an unresolved `url` route is advisory\n * (route resolution is app-context-dependent, and a path may be served by\n * another installed package or a host/console route). External, interpolated,\n * convention, and opaque targets are exempted to keep false positives near zero\n * — the same conservative posture as the sibling `lint-view-refs` and\n * `validate-capability-references` rules.\n */\n\nexport const DASHBOARD_ACTION_TARGET_UNDEFINED = 'dashboard-action-target-undefined';\nexport const DASHBOARD_ACTION_ROUTE_UNRESOLVED = 'dashboard-action-route-unresolved';\n\nexport type DashboardActionRefSeverity = 'error' | 'warning';\n\nexport interface DashboardActionRefFinding {\n /** `error` for a dangling script/modal action; `warning` for an unresolved url route. */\n severity: DashboardActionRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `dashboard \"sales_overview\" · header action \"Export PDF\"`. */\n where: string;\n /** Config path, e.g. `dashboards[2].header.actions[0].actionUrl`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records, injecting\n * `name` from the map key — mirrors the helper in the sibling authoring lints so\n * the rule works on both the parsed (array) and normalized (map) stack shapes. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** The runtime modal `<verb>_<object>` convention (objectui `DashboardView`\n * modalHandler): `create_/new_/add_/edit_/update_` + an object name opens that\n * object's create/edit form. */\nconst MODAL_VERB_RE = /^(?:create|new|add|edit|update)_(.+)$/;\n\n/** URL path segments that name a metadata collection, mapped to the stack key\n * whose members can appear after them in an in-app route\n * (`/…/objects/crm_lead`, `/reports/forecast`, `/dashboards/exec`, …). Both the\n * singular and plural spellings are accepted. */\nconst URL_COLLECTION_TO_STACK_KEY: Record<string, 'objects' | 'reports' | 'dashboards' | 'pages' | 'views'> = {\n object: 'objects',\n objects: 'objects',\n report: 'reports',\n reports: 'reports',\n dashboard: 'dashboards',\n dashboards: 'dashboards',\n page: 'pages',\n pages: 'pages',\n view: 'views',\n views: 'views',\n};\n\n/** Derive the name a top-level `views` container registers under (mirrors the\n * runtime loader's `resolveMetadataItemName('views', …)` fallbacks). */\nfunction viewContainerName(item: AnyRec): string | undefined {\n return (\n strName(item.name) ??\n strName(item.id) ??\n strName(item.object) ??\n strName((item.list as AnyRec | undefined)?.data && ((item.list as AnyRec).data as AnyRec).object) ??\n strName((item.form as AnyRec | undefined)?.data && ((item.form as AnyRec).data as AnyRec).object)\n );\n}\n\ninterface KnownTargets {\n /** Every action name defined in the stack (global + object-embedded). */\n actions: Set<string>;\n /** Object names (also valid as bare modal targets and `objects/<name>` routes). */\n objects: Set<string>;\n reports: Set<string>;\n dashboards: Set<string>;\n pages: Set<string>;\n /** View names routable as `views/<name>` — container names plus object names. */\n views: Set<string>;\n}\n\n/** Build the author-time \"known target\" sets from a stack. */\nfunction collectKnownTargets(stack: AnyRec): KnownTargets {\n const actions = new Set<string>();\n const objects = new Set<string>();\n const reports = new Set<string>();\n const dashboards = new Set<string>();\n const pages = new Set<string>();\n const views = new Set<string>();\n\n const collectNames = (v: unknown, into: Set<string>, name: (rec: AnyRec) => string | undefined) => {\n for (const item of asArray(v)) {\n if (!item || typeof item !== 'object') continue;\n const n = name(item);\n if (n) into.add(n);\n }\n };\n\n collectNames(stack.actions, actions, (a) => strName(a.name));\n for (const obj of asArray(stack.objects)) {\n if (!obj || typeof obj !== 'object') continue;\n const n = strName(obj.name);\n if (n) objects.add(n);\n collectNames(obj.actions, actions, (a) => strName(a.name));\n }\n collectNames(stack.reports, reports, (r) => strName(r.name));\n collectNames(stack.dashboards, dashboards, (d) => strName(d.name));\n collectNames(stack.pages, pages, (p) => strName(p.name));\n collectNames(stack.views, views, viewContainerName);\n // An object's default view is routable by the object's own name too.\n for (const o of objects) views.add(o);\n\n return { actions, objects, reports, dashboards, pages, views };\n}\n\n/** Does a `script`/`modal` `actionUrl` resolve? */\nfunction resolveActionTarget(\n actionType: 'script' | 'modal',\n target: string,\n known: KnownTargets,\n): boolean {\n if (known.actions.has(target)) return true;\n if (actionType === 'modal') {\n // Runtime modalHandler convention: `<verb>_<object>` or a bare object name\n // opens that object's create/edit form.\n if (known.objects.has(target)) return true;\n const m = MODAL_VERB_RE.exec(target);\n if (m && known.objects.has(m[1])) return true;\n }\n return false;\n}\n\n/**\n * Resolve a relative `url` in-app route. Returns:\n * - `null` when the target is not statically resolvable (external, interpolated,\n * or carries no recognized `<collection>/<name>` segment) — SKIP, no finding.\n * - `{ collection, name }` for a recognized `<collection>/<name>` pair that does\n * NOT exist in the stack — WARN.\n * - `undefined` when a recognized pair DID resolve — OK, no finding.\n */\nfunction resolveUrlRoute(\n target: string,\n known: KnownTargets,\n): { collection: string; name: string } | null | undefined {\n // External / protocol-relative — leaves the app; not an in-app route.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(target) || target.startsWith('//')) return null;\n // Interpolated — resolved by the renderer at click time, not statically known.\n if (target.includes('${')) return null;\n // Only relative in-app paths are considered.\n if (!target.startsWith('/')) return null;\n\n // Strip query + hash, then split into non-empty segments.\n const pathPart = target.split(/[?#]/, 1)[0];\n const segments = pathPart.split('/').filter(Boolean);\n\n for (let i = 0; i < segments.length - 1; i++) {\n const stackKey = URL_COLLECTION_TO_STACK_KEY[segments[i]];\n if (!stackKey) continue;\n const name = segments[i + 1];\n if (known[stackKey].has(name)) return undefined; // resolved\n return { collection: segments[i], name }; // recognized shape, unknown name\n }\n return null; // no recognized collection segment — opaque route, skip\n}\n\ninterface HeaderAction {\n actionType?: string;\n actionUrl?: string;\n label?: string;\n}\n\n/**\n * Validate every dashboard header / widget action reference in a stack. Returns\n * findings (empty = clean). `script`/`modal` dead targets are errors; `url`\n * unresolved routes are warnings.\n */\nexport function validateDashboardActionRefs(stack: AnyRec): DashboardActionRefFinding[] {\n const findings: DashboardActionRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const dashboards = asArray(stack.dashboards);\n if (dashboards.length === 0) return findings;\n\n const known = collectKnownTargets(stack);\n\n const checkOne = (\n action: HeaderAction,\n where: string,\n path: string,\n ) => {\n const target = strName(action.actionUrl);\n if (!target) return; // nothing referenced (widget with no action button)\n if (target.includes('${')) return; // dynamic target — not statically resolvable\n\n // Renderer default: a missing actionType is treated as a 'url' navigation\n // (DashboardRenderer builds header ActionDefs with `type: actionType || 'url'`).\n const actionType = strName(action.actionType) ?? 'url';\n\n if (actionType === 'script' || actionType === 'modal') {\n if (resolveActionTarget(actionType, target, known)) return;\n const kindWord = actionType === 'script' ? 'script' : 'modal';\n findings.push({\n severity: 'error',\n rule: DASHBOARD_ACTION_TARGET_UNDEFINED,\n where,\n path,\n message:\n `${kindWord} action target \"${target}\" resolves to no defined action` +\n (actionType === 'modal' ? ' or object' : '') +\n `. The button renders but does nothing when clicked — a dangling reference ` +\n `the runtime cannot dispatch (ADR-0049: a declared reference must resolve).`,\n hint:\n actionType === 'modal'\n ? `Define an action named \"${target}\" (stack.actions or the object's actions), ` +\n `use the \"<verb>_<object>\" convention against a real object ` +\n `(e.g. \"create_<object>\"), point actionUrl at an existing object, or remove the button.`\n : `Define a script action named \"${target}\" (stack.actions or the object's actions) ` +\n `with an inline body or a registered handler, or remove the button.`,\n });\n return;\n }\n\n if (actionType === 'url') {\n const route = resolveUrlRoute(target, known);\n if (!route) return; // skip (external/interpolated/opaque) or resolved\n findings.push({\n severity: 'warning',\n rule: DASHBOARD_ACTION_ROUTE_UNRESOLVED,\n where,\n path,\n message:\n `url action target \"${target}\" points at ${route.collection}/${route.name}, ` +\n `but no ${route.collection.replace(/s$/, '')} named \"${route.name}\" is registered ` +\n `in this stack — the button likely navigates to a dead route.`,\n hint:\n `Check the path for a typo, define the referenced ${route.collection.replace(/s$/, '')}, ` +\n `or ignore this if the route is served by another installed package or a host/console route.`,\n });\n return;\n }\n // 'flow' | 'api' | custom types are out of scope (see module header).\n };\n\n for (let di = 0; di < dashboards.length; di++) {\n const dash = dashboards[di];\n if (!dash || typeof dash !== 'object') continue;\n const dashName = strName(dash.name) ?? `(dashboard ${di})`;\n const dashPath = `dashboards[${di}]`;\n\n // Header actions.\n const headerActions = asArray((dash.header as AnyRec | undefined)?.actions);\n for (let ai = 0; ai < headerActions.length; ai++) {\n const action = headerActions[ai] as HeaderAction | null;\n if (!action || typeof action !== 'object') continue;\n const label = strName(action.label) ?? strName(action.actionUrl) ?? `#${ai}`;\n checkOne(\n action,\n `dashboard \"${dashName}\" · header action \"${label}\"`,\n `${dashPath}.header.actions[${ai}].actionUrl`,\n );\n }\n\n // Per-widget action buttons.\n const widgets = asArray(dash.widgets);\n for (let wi = 0; wi < widgets.length; wi++) {\n const widget = widgets[wi];\n if (!widget || typeof widget !== 'object') continue;\n if (!strName(widget.actionUrl)) continue;\n const widgetId = strName(widget.id) ?? `#${wi}`;\n checkOne(\n { actionType: widget.actionType as string | undefined, actionUrl: widget.actionUrl as string | undefined },\n `dashboard \"${dashName}\" · widget \"${widgetId}\" action`,\n `${dashPath}.widgets[${wi}].actionUrl`,\n );\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { classifyFilterToken, CONTEXT_TOKENS } from '@objectstack/spec/data';\n\n/**\n * Build-time filter-placeholder diagnostics (issue #3574).\n *\n * Filter values travel as JSON, so a user-scoped or time-scoped slice cannot\n * call code inline — it writes a placeholder that the client resolves just\n * before querying:\n *\n * { owner_id: '{current_user_id}', created_at: { $gte: '{week_start}' } }\n *\n * Exactly two vocabularies resolve inside a filter value: context tokens\n * (`{current_user_id}`, `{current_org_id}` — see `context-tokens.zod.ts`) and\n * date macros (`{today}`, `{30_days_ago}` — see `date-macros.zod.ts`).\n * Anything else is passed to the data engine **verbatim**, matches no row, and\n * the surface renders an empty result.\n *\n * ## Why this is an error, not a warning\n *\n * The runtime failure mode is silent and indistinguishable from success. A\n * metric widget filtered on an unresolved `{current_user}` renders `0`, which\n * looks exactly like a metric that is legitimately zero — no console error, no\n * server log, nothing for a human reviewer to notice. Issue #3574 found a\n * dashboard that had been broken this way since the day it was written.\n *\n * That failure mode is worse for AI authors than for humans. An AI reads a\n * successful query returning `0` as a correct answer and builds on it — it has\n * no instinct that the number looks wrong. Its correction loop is\n * \"author → validate → fix\", so a diagnostic only reaches it if the diagnostic\n * can fail the build. A runtime warning in a server log is invisible to it.\n * Hence: authoring-time error.\n *\n * The near-miss spellings this catches are not hypothetical. Each is a correct\n * spelling *somewhere else* in the platform, which is precisely why authors\n * reach for them:\n *\n * - `{current_user}` — `current_user.id` is the RLS expression root\n * - `{user_id}` — `{user_id}` is valid `titleFormat` field interpolation\n * - `{current_organization_id}` — `organization_id` is the real column name\n *\n * `CONTEXT_TOKEN_SUGGESTIONS` maps each to what the author meant, so the\n * diagnostic names the fix instead of only reporting the symptom.\n *\n * ## Scope — filter subtrees only\n *\n * The walk descends into `filter` / `filters` / `runtimeFilter` subtrees and\n * classifies string values inside them. It deliberately does NOT check\n * navigation `recordId` / `params`, which resolve an additional vocabulary —\n * `AppContextSelector` ids such as `{active_package}` — that is meaningless in\n * a filter because filters are not evaluated with the sidebar's selector\n * state. Restricting the walk keeps that legitimate usage out of the rule and\n * holds false positives at zero.\n *\n * Only whole-string placeholders are considered (`'{token}'` / `'${token}'`,\n * anchored). A value that merely contains braces is left alone.\n */\n\nexport const FILTER_TOKEN_UNKNOWN = 'filter-token-unknown';\n\nexport type FilterTokenSeverity = 'error' | 'warning';\n\nexport interface FilterTokenFinding {\n /** Always `error` today — an unresolved placeholder silently matches nothing. */\n severity: FilterTokenSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `dashboard \"sales\" · widget \"my_deals\"`. */\n where: string;\n /** Config path, e.g. `dashboards[0].widgets[2].filter.owner_id`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Keys whose subtree is a filter — the only place placeholders resolve. */\nconst FILTER_KEYS = new Set(['filter', 'filters', 'runtimeFilter']);\n\n/**\n * Coerce a collection (array or name-keyed map) to an array of records,\n * injecting `name` from the map key — mirrors the helper in the sibling\n * authoring lints so the rule works on both the parsed (array) and normalized\n * (map) stack shapes.\n */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction label(v: unknown, fallback: string): string {\n return typeof v === 'string' && v.length > 0 ? v : fallback;\n}\n\nconst KNOWN_LIST = CONTEXT_TOKENS.join('}, {');\n\n/**\n * Classify every string inside an already-identified filter subtree.\n *\n * Walks arrays and plain objects uniformly, which is what makes this work\n * across the platform's two filter shapes: the MongoDB-style object a\n * dashboard widget carries (`{ owner_id: '{current_user_id}' }`) and the\n * condition/triple arrays a list view carries\n * (`[{ field, operator, value }]` / `[['owner','=','…']]`). The bug this rule\n * exists for was caused by a resolver that handled only one of those shapes.\n */\nfunction walkFilterValues(\n node: unknown,\n path: string,\n where: string,\n out: FilterTokenFinding[],\n seen: Set<unknown>,\n): void {\n if (node === null || node === undefined) return;\n\n if (typeof node === 'string') {\n const cls = classifyFilterToken(node);\n if (cls?.kind === 'unknown') {\n const suggestion = cls.suggestion;\n out.push({\n severity: 'error',\n rule: FILTER_TOKEN_UNKNOWN,\n where,\n path,\n message:\n `Filter value \"${node}\" is not a resolvable placeholder. It is sent to the ` +\n `data engine as a literal string, matches no record, and the surface renders empty.`,\n hint: suggestion\n ? `Did you mean \"{${suggestion}}\"? Context tokens are {${KNOWN_LIST}}; ` +\n `time-based values use date macros such as {today} or {30_days_ago}.`\n : `Resolvable placeholders are the context tokens {${KNOWN_LIST}} and the ` +\n `date macros (e.g. {today}, {week_start}, {30_days_ago}). To filter on a ` +\n `literal value that happens to look like a placeholder, this is not supported — ` +\n `rename the value.`,\n });\n }\n return;\n }\n\n if (typeof node !== 'object') return;\n // Metadata graphs can be cyclic once normalized; guard the walk.\n if (seen.has(node)) return;\n seen.add(node);\n\n if (Array.isArray(node)) {\n node.forEach((v, i) => walkFilterValues(v, `${path}[${i}]`, where, out, seen));\n return;\n }\n\n for (const [k, v] of Object.entries(node as AnyRec)) {\n walkFilterValues(v, `${path}.${k}`, where, out, seen);\n }\n}\n\n/**\n * Find `filter` / `filters` / `runtimeFilter` subtrees anywhere beneath\n * `node`, then classify the values inside them.\n *\n * Scanning for filter KEYS rather than enumerating known surfaces is\n * deliberate: widget filters, list-view filters, dataset and measure filters,\n * report runtime filters, and SDUI component filters all spell the key the\n * same way, and a new surface that follows the convention is covered the day\n * it ships. Enumerating surfaces is how #3574 happened — the dashboard was\n * simply never added to the list.\n */\nfunction scanForFilters(\n node: unknown,\n path: string,\n where: string,\n out: FilterTokenFinding[],\n seen: Set<unknown>,\n): void {\n if (!node || typeof node !== 'object') return;\n if (seen.has(node)) return;\n seen.add(node);\n\n if (Array.isArray(node)) {\n node.forEach((v, i) => scanForFilters(v, `${path}[${i}]`, where, out, seen));\n return;\n }\n\n for (const [k, v] of Object.entries(node as AnyRec)) {\n const childPath = `${path}.${k}`;\n if (FILTER_KEYS.has(k)) {\n walkFilterValues(v, childPath, where, out, new Set());\n continue;\n }\n scanForFilters(v, childPath, where, out, seen);\n }\n}\n\n/**\n * Validate filter placeholders across a schema-parsed stack.\n *\n * Pure `(stack) => Finding[]`; no I/O. Covers dashboards (widget + global\n * filters), objects (list views), top-level view containers, reports,\n * datasets, and pages.\n */\nexport function validateFilterTokens(stack: Record<string, unknown> | undefined | null): FilterTokenFinding[] {\n if (!stack || typeof stack !== 'object') return [];\n const out: FilterTokenFinding[] = [];\n\n const surfaces: Array<[key: string, kind: string]> = [\n ['dashboards', 'dashboard'],\n ['objects', 'object'],\n ['views', 'view'],\n ['reports', 'report'],\n ['datasets', 'dataset'],\n ['pages', 'page'],\n ['apps', 'app'],\n ];\n\n for (const [key, kind] of surfaces) {\n const items = asArray((stack as AnyRec)[key]);\n items.forEach((item, i) => {\n const name = label(item.name ?? item.id, `#${i}`);\n // Dashboards are the surface #3574 was filed against; name the widget in\n // `where` so the author can jump straight to it.\n if (kind === 'dashboard') {\n const widgets = Array.isArray(item.widgets) ? (item.widgets as AnyRec[]) : [];\n widgets.forEach((w, wi) => {\n const wName = label(w.id ?? w.title, `#${wi}`);\n scanForFilters(\n w,\n `${key}[${i}].widgets[${wi}]`,\n `dashboard \"${name}\" · widget \"${wName}\"`,\n out,\n new Set(),\n );\n });\n // …and everything else on the dashboard (globalFilters, header, etc.)\n // minus the widgets already covered above.\n const { widgets: _skip, ...rest } = item;\n scanForFilters(rest, `${key}[${i}]`, `dashboard \"${name}\"`, out, new Set());\n return;\n }\n scanForFilters(item, `${key}[${i}]`, `${kind} \"${name}\"`, out, new Set());\n });\n }\n\n return out;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0072 — reference resolvability] Object-name reference integrity for the\n * surfaces no other rule owns (issue #3583).\n *\n * A HotCRM audit (~18k lines of shipped metadata) found ~20 instances of ONE\n * bug class: metadata naming an object that does not exist. Every instance\n * passed `objectstack validate` and `objectstack lint` cleanly and failed\n * SILENTLY at runtime — `object: 'user'` where the platform object is\n * `sys_user`, navigation targeting `sys_approval_process` (which\n * `@objectstack/plugin-approvals` never registers; ADR-0019 removed it).\n *\n * `defineStack`'s `validateCrossReferences` already hard-fails on the object\n * references it knows about (hooks, view data, seeds, mappings, permission\n * grants, nav `objectName`, action targets). This rule covers the REST — the\n * reference sites that are plain `z.string()` in the schema and therefore ship\n * whatever the author typed:\n *\n * - action params — `reference` (inline lookup/master_detail target) and\n * `objectOverride` (the object owning a field-backed param). Both are the\n * record picker's search target; a dead one degrades the dialog to a raw\n * id text input.\n * - dashboard `globalFilters[].optionsFrom.object` — the object a filter\n * dropdown fetches its options from. Dead → an always-empty dropdown.\n * - navigation `requiresObject` / `requiresService` capability gates. This is\n * the escape hatch `stack.zod.ts` honours to SKIP nav validation, so a typo\n * here is doubly silent: the entry is hidden forever (the runtime never\n * finds the object in its SchemaRegistry) AND the skip suppresses the\n * cross-reference error that would have caught the nav target.\n * - the nav `objectName` of an item that carries `requiresObject` — exempted\n * from the `defineStack` throw for good reason (it may come from another\n * package), but still worth an advisory when NO known package provides it.\n *\n * ── Severity ladder (the point of the rule) ──────────────────────────────\n *\n * Prior rules answered \"might this object come from another package?\" with a\n * PREFIX GUESS (`name.startsWith('sys_')` → skip). That guess cannot tell\n * `sys_user` (real) from `sys_approval_process` (fictional), so every fictional\n * platform-prefixed reference shipped. This rule resolves against the curated\n * `PLATFORM_PROVIDED_OBJECT_NAMES` registry instead:\n *\n * 1. resolves in the stack's own objects → OK\n * 2. unresolved, NOT platform-prefixed → ERROR\n * (`user`, `total_revenue` — no cross-package story exists for an\n * unprefixed name, since a stack's objects are namespace-prefixed;\n * this is the pure typo class and the bulk of the HotCRM findings)\n * 3. unresolved, prefixed, IN the registry → OK\n * 4. unresolved, prefixed, NOT in the registry → WARNING\n * (`sys_approval_process` — no package we know of registers it, but a\n * third-party package still might, so advisory is the honest ceiling)\n *\n * Interpolated targets (`${…}`, `{…}`) are skipped — they resolve at render\n * time, the same conservative exemption `validate-dashboard-action-refs` uses\n * to keep false positives near zero (ADR-0072 D1: one dead finding and authors\n * stop trusting the linter).\n */\n\nimport {\n hasPlatformObjectPrefix,\n isPlatformProvidedObjectName,\n PLATFORM_PROVIDED_OBJECT_NAMES,\n} from '@objectstack/spec/system';\n\n/** Materialized once for the repeated edit-distance scans in `suggest`. */\nconst PLATFORM_NAMES: readonly string[] = [...PLATFORM_PROVIDED_OBJECT_NAMES];\n\nexport const OBJECT_REFERENCE_UNKNOWN = 'object-reference-unknown';\nexport const OBJECT_REFERENCE_UNREGISTERED_PLATFORM = 'object-reference-unregistered-platform';\n\nexport type ObjectRefSeverity = 'error' | 'warning';\n\nexport interface ObjectRefFinding {\n /** `error` for an unresolvable own-stack name; `warning` for an unknown platform name. */\n severity: ObjectRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `action \"mass_update\" · param \"owner\"`. */\n where: string;\n /** Config path, e.g. `actions[2].params[0].reference`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\n/** Coerce a collection (array or name-keyed map) to an array of records,\n * injecting `name` from the map key — mirrors the sibling authoring lints so\n * the rule works on both the parsed (array) and normalized (map) stack shapes. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * A target the author cannot have meant literally — `${…}` or `{…}` resolved at\n * render time.\n *\n * Scanned rather than matched with `/\\{[^}]+\\}/`: that pattern backtracks\n * quadratically on a long run of `{` with no closing brace (CodeQL\n * polynomial-ReDoS), and the question here is simply \"is there a `{` with a\n * later `}` and something in between\", which one pass answers.\n */\nfunction isInterpolated(target: string): boolean {\n if (target.includes('${')) return true;\n const open = target.indexOf('{');\n // `+ 2` keeps the original \"at least one character between the braces\"\n // semantics, so a literal `{}` is not treated as a placeholder.\n return open !== -1 && target.indexOf('}', open + 2) !== -1;\n}\n\n/** Levenshtein-bounded \"did you mean?\" over the known names. */\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n // Only offer a suggestion that is plausibly the same identifier mistyped.\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\n/**\n * Validate every object-name reference on the surfaces listed in the module\n * header. Returns findings (empty = clean).\n */\nexport function validateObjectReferences(stack: AnyRec): ObjectRefFinding[] {\n const findings: ObjectRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const objects = asArray(stack.objects);\n const ownObjects = new Set<string>();\n for (const obj of objects) {\n const n = strName(obj.name);\n if (n) ownObjects.add(n);\n }\n\n /**\n * Resolve one reference through the ladder and record a finding if it fails.\n * `subject` describes the reference for the message (\"record-picker target\").\n */\n const check = (\n target: string | undefined,\n where: string,\n path: string,\n subject: string,\n fix: string,\n ) => {\n const name = strName(target);\n if (!name) return;\n if (isInterpolated(name)) return; // resolved at render time\n if (ownObjects.has(name)) return; // ① own object\n if (isPlatformProvidedObjectName(name)) return; // ③ known platform object\n\n if (hasPlatformObjectPrefix(name)) {\n // ④ Platform-shaped, but no package we know of registers it.\n findings.push({\n severity: 'warning',\n rule: OBJECT_REFERENCE_UNREGISTERED_PLATFORM,\n where,\n path,\n message:\n `${subject} \"${name}\" carries a platform namespace prefix, but no platform ` +\n `package, official plugin, or cloud runtime object registers that name — ` +\n `and this stack does not define it either. If nothing provides it at runtime ` +\n `the reference resolves to nothing and fails silently.` +\n suggest(name, PLATFORM_NAMES),\n hint:\n `Check the spelling against the object the providing package actually registers ` +\n `(e.g. \"sys_approval_request\", not \"sys_approval_process\" — the process object ` +\n `was removed when approval became a flow node, ADR-0019). If a third-party ` +\n `package genuinely provides it, this warning is expected. ${fix}`,\n });\n return;\n }\n\n // ② Unprefixed and unresolved — a stack's own objects are namespace-\n // prefixed and present here, so there is no legitimate elsewhere.\n findings.push({\n severity: 'error',\n rule: OBJECT_REFERENCE_UNKNOWN,\n where,\n path,\n message:\n `${subject} \"${name}\" resolves to no object defined in this stack. ` +\n `The reference is inert at runtime — nothing reports the miss.` +\n suggest(name, ownObjects),\n hint:\n `Point it at one of this stack's objects, or at a platform object by its full ` +\n `name (the platform user object is \"sys_user\", not \"user\"). ${fix}` +\n (ownObjects.size > 0 ? ` Defined objects: ${[...ownObjects].sort().join(', ')}.` : ''),\n });\n };\n\n // ── Actions (global + object-embedded) → param object targets ──\n const checkActionParams = (action: AnyRec, actionPath: string, actionLabel: string) => {\n const params = asArray(action.params);\n for (let pi = 0; pi < params.length; pi++) {\n const param = params[pi];\n if (!param || typeof param !== 'object') continue;\n const paramLabel = strName(param.name) ?? strName(param.field) ?? `#${pi}`;\n const where = `${actionLabel} · param \"${paramLabel}\"`;\n check(\n strName(param.reference),\n where,\n `${actionPath}.params[${pi}].reference`,\n 'record-picker target',\n 'Without a resolvable target the picker degrades to a raw record-id text input.',\n );\n check(\n strName(param.objectOverride),\n where,\n `${actionPath}.params[${pi}].objectOverride`,\n 'field-backed param object',\n 'The param inherits type/options from a field on this object, so an unknown object leaves it untyped.',\n );\n }\n };\n\n const globalActions = asArray(stack.actions);\n for (let ai = 0; ai < globalActions.length; ai++) {\n const action = globalActions[ai];\n if (!action || typeof action !== 'object') continue;\n checkActionParams(action, `actions[${ai}]`, `action \"${strName(action.name) ?? `#${ai}`}\"`);\n }\n\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!obj || typeof obj !== 'object') continue;\n const objName = strName(obj.name) ?? `#${oi}`;\n const objActions = asArray(obj.actions);\n for (let ai = 0; ai < objActions.length; ai++) {\n const action = objActions[ai];\n if (!action || typeof action !== 'object') continue;\n checkActionParams(\n action,\n `objects[${oi}].actions[${ai}]`,\n `object \"${objName}\" · action \"${strName(action.name) ?? `#${ai}`}\"`,\n );\n }\n }\n\n // ── Dashboard global filters → optionsFrom.object ──\n const dashboards = asArray(stack.dashboards);\n for (let di = 0; di < dashboards.length; di++) {\n const dash = dashboards[di];\n if (!dash || typeof dash !== 'object') continue;\n const dashName = strName(dash.name) ?? `#${di}`;\n const filters = asArray(dash.globalFilters);\n for (let fi = 0; fi < filters.length; fi++) {\n const filter = filters[fi];\n if (!filter || typeof filter !== 'object') continue;\n const optionsFrom = filter.optionsFrom as AnyRec | undefined;\n if (!optionsFrom || typeof optionsFrom !== 'object') continue;\n check(\n strName(optionsFrom.object),\n `dashboard \"${dashName}\" · filter \"${strName(filter.name) ?? `#${fi}`}\"`,\n `dashboards[${di}].globalFilters[${fi}].optionsFrom.object`,\n 'filter options source',\n 'The dropdown fetches its options from this object; an unknown one renders an always-empty filter.',\n );\n }\n }\n\n // ── App navigation → requiresObject gates (and gated objectName) ──\n const apps = asArray(stack.apps);\n for (let ai = 0; ai < apps.length; ai++) {\n const app = apps[ai];\n if (!app || typeof app !== 'object') continue;\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walkNav = (items: unknown, basePath: string) => {\n const navItems = asArray(items);\n for (let ni = 0; ni < navItems.length; ni++) {\n const nav = navItems[ni];\n if (!nav || typeof nav !== 'object') continue;\n const navId = strName(nav.id) ?? `#${ni}`;\n const where = `app \"${appName}\" · nav \"${navId}\"`;\n const navPath = `${basePath}[${ni}]`;\n\n check(\n strName(nav.requiresObject),\n where,\n `${navPath}.requiresObject`,\n 'capability gate object',\n 'The entry is hidden unless this object is registered, so a typo hides it permanently — ' +\n 'and it suppresses the nav cross-reference check that would have caught the target.',\n );\n\n // A nav target exempted from the `defineStack` throw by `requiresObject`\n // still deserves an advisory when nothing known provides it.\n if (nav.requiresObject && strName(nav.objectName)) {\n check(\n strName(nav.objectName),\n where,\n `${navPath}.objectName`,\n 'navigation target',\n 'Declaring `requiresObject` exempts this target from the build-time check, so it is only verified here.',\n );\n }\n\n if (Array.isArray(nav.children)) walkNav(nav.children, `${navPath}.children`);\n }\n };\n\n walkNav(app.navigation, `apps[${ai}].navigation`);\n const areas = asArray(app.areas);\n for (let ri = 0; ri < areas.length; ri++) {\n walkNav(areas[ri]?.navigation, `apps[${ai}].areas[${ri}].navigation`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0072 — reference resolvability] App-navigation targets that are not\n * object names: `page`, `report`, `dashboard`.\n *\n * ## The hole this closes is inside an EXISTING check, not a missing one\n *\n * `defineStack`'s `validateCrossReferences` already validates these three\n * (`stack.zod.ts`, the \"Validate app navigation → object/dashboard/page/report\n * references\" block). But each of the three is guarded on the collection being\n * non-empty:\n *\n * ```ts\n * if (nav.type === 'page' && typeof nav.pageName === 'string'\n * && pageNames.size > 0 && !pageNames.has(nav.pageName)) { … }\n * ```\n *\n * So a stack that declares **no `pages` at all** has its page-nav check\n * silently switched off, and `{ type: 'page', pageName: 'anything' }` sails\n * through. That is precisely the state a stack is in when the target was never\n * written — the most likely way to get here, not the least.\n *\n * Note the asymmetry the guard creates. The `object` arm of the same block has\n * no size gate: it errors unless the item carries `requiresObject`, an\n * EXPLICIT opt-in to \"another package provides this\". Objects therefore say so\n * out loud; pages, reports and dashboards get an implicit exemption that\n * depends on an unrelated property of the stack.\n *\n * This rule restores the coverage with the ADR-0072 severity posture rather\n * than by tightening the parse-time throw — a throw has no escape hatch for a\n * legitimately cross-package page, and ADR-0072 D1's rule is that one dead\n * finding costs more than a missed one.\n *\n * ## Severity: warning, and why it is not error\n *\n * `validate-object-references` can say ERROR for an unresolved *object*\n * because it resolves against a curated `PLATFORM_PROVIDED_OBJECT_NAMES`\n * registry — it knows which cross-package names are real. No such registry\n * exists for pages, reports or dashboards, so \"unresolved\" genuinely cannot be\n * distinguished from \"provided by a package we cannot see from here\".\n * Advisory is the honest ceiling. When `defineStack`'s own check is live (the\n * collection is non-empty) it still hard-fails first; this rule is what speaks\n * when that check has switched itself off.\n *\n * ## Deliberately NOT covered — each verified, not assumed\n *\n * - **`action`** (`actionDef.actionName`) — already owned by\n * `validate-action-name-refs`, which walks app navigation explicitly. Adding\n * it here would double-report the same finding.\n * - **`component`** (`componentRef`) — verified a NON-rule. An unregistered ref\n * does NOT fail silently: `ComponentNavView` renders a named diagnostic\n * (\"Component not registered … Ensure the plugin that provides this surface\n * is installed and has called `registerAppComponent()`\"), and the registry\n * exists precisely so plugin-provided surfaces may legitimately be absent.\n * Flagging it would break valid plugin nav and prescribe a fix for something\n * already reported better at runtime.\n * - **`url`** — external by definition; nothing to resolve against.\n */\n\nimport type { ReferenceIntegrityFinding } from './reference-integrity-suite.js';\n\nexport type NavTargetRefSeverity = 'error' | 'warning';\nexport type NavTargetRefFinding = ReferenceIntegrityFinding;\n\n/** Emitted when a nav item targets a page/report/dashboard the stack cannot resolve. */\nexport const NAV_TARGET_UNRESOLVED = 'nav-target-unresolved';\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter(isRec);\n if (isRec(v)) return Object.entries(v).map(([name, def]) => (isRec(def) ? { name, ...def } : { name }));\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * An interpolated target resolves at render time — the same conservative\n * exemption `validate-object-references` and `validate-dashboard-action-refs`\n * use to keep false positives near zero (ADR-0072 D1).\n */\nconst isInterpolated = (s: string): boolean => s.includes('${') || s.includes('{');\n\n/** nav `type` → [target property, stack collection, human noun]. */\nconst NAV_TARGETS: ReadonlyArray<readonly [string, string, string, string]> = [\n ['page', 'pageName', 'pages', 'page'],\n ['report', 'reportName', 'reports', 'report'],\n ['dashboard', 'dashboardName', 'dashboards', 'dashboard'],\n];\n\nfunction namesOf(collection: unknown): Set<string> {\n const out = new Set<string>();\n for (const entry of asArray(collection)) {\n const n = strName(entry.name);\n if (n) out.add(n);\n }\n return out;\n}\n\nexport function validateNavTargetRefs(stack: unknown): NavTargetRefFinding[] {\n const findings: NavTargetRefFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const apps = asArray(stack.apps);\n if (apps.length === 0) return findings;\n\n const declared = new Map<string, Set<string>>();\n for (const [, , collection] of NAV_TARGETS) {\n declared.set(collection, namesOf((stack as AnyRec)[collection]));\n }\n\n for (const [ai, app] of apps.entries()) {\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walk = (items: unknown, basePath: string): void => {\n if (!Array.isArray(items)) return;\n for (const [ni, raw] of items.entries()) {\n if (!isRec(raw)) continue;\n const nav = raw;\n const navPath = `${basePath}[${ni}]`;\n\n for (const [type, prop, collection, noun] of NAV_TARGETS) {\n if (nav.type !== type) continue;\n const target = strName(nav[prop]);\n if (!target || isInterpolated(target)) continue;\n const known = declared.get(collection)!;\n if (known.has(target)) continue;\n\n const emptyCollection = known.size === 0;\n findings.push({\n severity: 'warning',\n rule: NAV_TARGET_UNRESOLVED,\n where: `app \"${appName}\" · nav \"${strName(nav.id) ?? strName(nav.label) ?? `#${ni}`}\"`,\n path: `${navPath}.${prop}`,\n message:\n `Navigation targets ${noun} '${target}', which this stack does not declare in `\n + `\\`${collection}\\`. `\n + (emptyCollection\n ? `The stack declares NO ${collection} at all, so \\`defineStack\\`'s own `\n + `cross-reference check skipped this entry entirely (it is gated on `\n + `\\`${collection === 'pages' ? 'pageNames' : collection === 'reports' ? 'reportNames' : 'dashboardNames'}.size > 0\\`) — `\n + `nothing else will report it. `\n : '')\n + `The entry renders in the sidebar and resolves to nothing when clicked. If another `\n + `package provides this ${noun}, this is expected and advisory only.`,\n hint:\n `Declare the ${noun} in \\`${collection}\\`, correct the name, or remove the nav entry `\n + `if the ${noun} is gone.`,\n });\n }\n\n // Recurse: an `object` nav item carries `children` too, not just a\n // `group` — the same reason `stack.zod.ts` does not gate its recursion\n // on the item type.\n if (Array.isArray(nav.children)) walk(nav.children, `${navPath}.children`);\n }\n };\n\n walk(app.navigation, `apps[${ai}].navigation`);\n // `areas[]` is the other nav container; it was once skipped wholesale in\n // `stack.zod.ts`, so an areas-based app got no nav validation at all.\n for (const [ari, area] of asArray(app.areas).entries()) {\n walk(area.items, `apps[${ai}].areas[${ari}].items`);\n walk(area.navigation, `apps[${ai}].areas[${ari}].navigation`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0049 — references] Action-NAME reference integrity for the surfaces that\n * bind an action by name (issue #3583).\n *\n * `locations` is an action's primary binding, but several surfaces reference\n * actions **by name** instead (see `content/docs/ui/actions.mdx` — \"Surfaces can\n * also reference actions by name\"). Every one of those fields is a plain\n * `z.array(z.string())` / `z.string()`, so a name that matches no defined action\n * parses and ships:\n *\n * - list views — `rowActions[]` / `bulkActions[]`, plus each\n * `bulkActionDefs[]` entry that is a reference rather than a button id\n * (`execution: 'aggregate'` — see the walk). Across all three tiers: the\n * default `list` container, each `listViews.<key>` entry, and an OBJECT's\n * own `listViews.<key>` (added in #4457; an object has no top-level `list`,\n * so that tier had simply never been walked)\n * - page components — `record:quick_actions` → `properties.actionNames[]`\n * - app navigation — `{ type: 'action', actionDef: { actionName } }`\n *\n * The HotCRM audit shipped `bulkActions: ['mass_update', 'mass_delete',\n * 'assign_owner']` with none of the three defined anywhere: the toolbar renders\n * the buttons, selecting rows enables them, and clicking does nothing.\n *\n * This is the same failure `validate-dashboard-action-refs` catches for\n * dashboard header/widget buttons (`DASHBOARD_ACTION_TARGET_UNDEFINED`), so it\n * carries the same severity: **error**. It is a genuine dead reference, and —\n * unlike an object name — there is no cross-package escape hatch to soften it\n * with. The runtime ships NO built-in action names (there is no\n * `BUILTIN_ACTIONS` registry; `list_toolbar`/`list_item` are *locations*, not\n * actions), so a name resolving nowhere is dead, full stop.\n *\n * Scope note: this rule asks only \"is this action defined ANYWHERE in the\n * stack?\". It deliberately does NOT check that a view's action belongs to the\n * view's own object, nor that the action declares the matching `location` —\n * both are real but distinct classes, and folding them in here would trade the\n * zero-false-positive posture (ADR-0072 D1) for coverage this issue did not ask\n * for. An action defined by another installed package is the one legitimate\n * miss; it is called out in the hint rather than guessed at.\n */\n\nimport { walkPageComponents } from './page-walk.js';\n\nexport const ACTION_NAME_UNDEFINED = 'action-name-undefined';\n\nexport type ActionNameRefSeverity = 'error' | 'warning';\n\nexport interface ActionNameRefFinding {\n /** Always `error` — a name-bound action that resolves nowhere is a dead button. */\n severity: ActionNameRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `view \"crm_lead\" · list \"all\" · bulkActions`. */\n where: string;\n /** Config path, e.g. `views[0].list.bulkActions[1]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction strList(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && x.length > 0) : [];\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\n/** Every action name defined in the stack (global + object-embedded). */\nfunction collectActionNames(stack: AnyRec): Set<string> {\n const names = new Set<string>();\n for (const action of asArray(stack.actions)) {\n const n = strName(action?.name);\n if (n) names.add(n);\n }\n for (const obj of asArray(stack.objects)) {\n if (!obj || typeof obj !== 'object') continue;\n for (const action of asArray(obj.actions)) {\n const n = strName(action?.name);\n if (n) names.add(n);\n }\n }\n return names;\n}\n\n/**\n * Validate every name-bound action reference in a stack. Returns findings\n * (empty = clean).\n */\nexport function validateActionNameRefs(stack: AnyRec): ActionNameRefFinding[] {\n const findings: ActionNameRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const known = collectActionNames(stack);\n\n const check = (\n name: string,\n where: string,\n path: string,\n surface: string,\n /**\n * What the newly-defined action still needs to be reachable from THIS\n * surface. A row/quick-action menu filters on `locations`; the selection\n * bar does not — naming the action in the view is its whole declaration\n * (the `action.bulkEnabled` tombstone says so, and `content/docs/ui/\n * actions.mdx` names it as the one exception to location filtering). One\n * hint for both would have to be wrong for one of them.\n */\n placement = 'with the location this surface needs',\n ) => {\n if (known.has(name)) return;\n findings.push({\n severity: 'error',\n rule: ACTION_NAME_UNDEFINED,\n where,\n path,\n message:\n `${surface} names action \"${name}\", which is defined by no action in this stack ` +\n `(neither \\`stack.actions\\` nor any object's \\`actions\\`). The button renders and ` +\n `does nothing when clicked — a dead affordance the runtime cannot dispatch.` +\n suggest(name, known),\n hint:\n `Define an action named \"${name}\" (in \\`stack.actions\\` or the object's \\`actions\\`) ` +\n `${placement}, remove the reference, or ignore this if the ` +\n `action is contributed by another installed package.` +\n (known.size > 0 ? ` Defined actions: ${[...known].sort().join(', ')}.` : ''),\n });\n };\n\n /** Naming an action in the selection bar IS its placement — see `check`. */\n const SELECTION_BAR_PLACEMENT =\n '(no `locations` entry needed — the selection bar places it by name)';\n\n /**\n * One list container: the default `list`, a `listViews.<key>` entry, or an\n * object-embedded one. Shared so the three tiers cannot drift into checking\n * different keys — an object has no top-level `list`, and its `listViews`\n * went unchecked until #4457 while the view-level ones were covered.\n */\n const checkListContainer = (\n container: unknown,\n owner: string,\n label: string,\n path: string,\n ) => {\n if (!container || typeof container !== 'object') return;\n const list = container as AnyRec;\n for (const key of ['rowActions', 'bulkActions'] as const) {\n const names = strList(list[key]);\n for (let ai = 0; ai < names.length; ai++) {\n check(\n names[ai],\n `${owner} · ${label} · ${key}`,\n `${path}.${key}[${ai}]`,\n key === 'bulkActions' ? 'Bulk-action menu' : 'Row-action menu',\n key === 'bulkActions' ? SELECTION_BAR_PLACEMENT : undefined,\n );\n }\n }\n\n // `bulkActionDefs` — only SOME entries are name references (#4457).\n //\n // An `update`/`delete` def is a data-plane mass mutation: its `name` is a\n // button id and resolving it against `stack.actions` would be nonsense.\n // The one entry that IS a reference is `execution: 'aggregate'`, which is\n // exactly what objectui's `resolveBulkActions` looks up by name to attach\n // the action it dispatches — a name that hits nothing leaves the def with\n // no dispatcher, so the button opens its dialog and the run resolves to\n // \"no dispatcher wired\". Same dead affordance, same severity.\n //\n // (Spec's `BulkActionDefSchema` rejects a hand-written `actionDef`, but a\n // stack can reach lint through paths that never parsed — a raw JSON fixture,\n // an older package — so an inlined definition is skipped rather than\n // assumed impossible: it carries its own dispatcher and resolves nothing.)\n const defs = Array.isArray(list.bulkActionDefs) ? (list.bulkActionDefs as AnyRec[]) : [];\n for (let di = 0; di < defs.length; di++) {\n const def = defs[di];\n if (!def || typeof def !== 'object') continue;\n if (def.execution !== 'aggregate') continue;\n if (def.actionDef !== undefined) continue;\n const name = strName(def.name);\n if (!name) continue;\n check(\n name,\n `${owner} · ${label} · bulkActionDefs[${di}]`,\n `${path}.bulkActionDefs[${di}].name`,\n 'Aggregate bulk action',\n SELECTION_BAR_PLACEMENT,\n );\n }\n };\n\n // ── List views: `list` + each `listViews.<key>`, on views AND on objects ──\n const views = asArray(stack.views);\n for (let vi = 0; vi < views.length; vi++) {\n const view = views[vi];\n if (!view || typeof view !== 'object') continue;\n const viewName = strName(view.name) ?? strName(view.object) ?? `#${vi}`;\n const owner = `view \"${viewName}\"`;\n\n checkListContainer(view.list, owner, 'list', `views[${vi}].list`);\n const listViews = view.listViews;\n if (listViews && typeof listViews === 'object' && !Array.isArray(listViews)) {\n for (const [key, lv] of Object.entries(listViews as AnyRec)) {\n checkListContainer(lv, owner, `listViews.${key}`, `views[${vi}].listViews.${key}`);\n }\n }\n }\n\n // An object carries its own `listViews` (it has no top-level `list`), and a\n // reference there is as dead as one in a standalone view — it was simply\n // never walked. Object-EMBEDDED actions were already collected as\n // definitions above; this is the consuming half.\n const objects = asArray(stack.objects);\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!obj || typeof obj !== 'object') continue;\n const objListViews = obj.listViews;\n if (!objListViews || typeof objListViews !== 'object' || Array.isArray(objListViews)) continue;\n const owner = `object \"${strName(obj.name) ?? `#${oi}`}\"`;\n for (const [key, lv] of Object.entries(objListViews as AnyRec)) {\n checkListContainer(lv, owner, `listViews.${key}`, `objects[${oi}].listViews.${key}`);\n }\n }\n\n // ── Page components: record:quick_actions → properties.actionNames ──\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n const page = pages[pi];\n if (!page || typeof page !== 'object') continue;\n const pageName = strName(page.name) ?? `#${pi}`;\n\n // Traversal is shared (`page-walk.ts`): the component tree is NOT where a\n // first reading suggests. Components hang off `regions[].components[]` and\n // `slots`, never a top-level `page.components`, and sub-trees nest inside\n // the untyped `properties` bag rather than under a `children` key.\n for (const { component, path } of walkPageComponents(page, `pages[${pi}]`)) {\n const props = component.properties as AnyRec | undefined;\n if (!props || typeof props !== 'object') continue;\n const names = strList(props.actionNames);\n for (let ai = 0; ai < names.length; ai++) {\n check(\n names[ai],\n `page \"${pageName}\" · component \"${strName(component.type) ?? '?'}\"`,\n `${path}.properties.actionNames[${ai}]`,\n 'Quick-actions bar',\n );\n }\n }\n }\n\n // ── App navigation: { type: 'action', actionDef: { actionName } } ──\n const apps = asArray(stack.apps);\n for (let ai = 0; ai < apps.length; ai++) {\n const app = apps[ai];\n if (!app || typeof app !== 'object') continue;\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walkNav = (items: unknown, basePath: string) => {\n const navItems = asArray(items);\n for (let ni = 0; ni < navItems.length; ni++) {\n const nav = navItems[ni];\n if (!nav || typeof nav !== 'object') continue;\n const navPath = `${basePath}[${ni}]`;\n const actionDef = nav.actionDef as AnyRec | undefined;\n const actionName = strName(actionDef?.actionName);\n if (nav.type === 'action' && actionName) {\n check(\n actionName,\n `app \"${appName}\" · nav \"${strName(nav.id) ?? `#${ni}`}\"`,\n `${navPath}.actionDef.actionName`,\n 'Navigation action item',\n );\n }\n if (Array.isArray(nav.children)) walkNav(nav.children, `${navPath}.children`);\n }\n };\n\n walkNav(app.navigation, `apps[${ai}].navigation`);\n const areas = asArray(app.areas);\n for (let ri = 0; ri < areas.length; ri++) {\n walkNav(areas[ri]?.navigation, `apps[${ai}].areas[${ri}].navigation`);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0078 Phase 3 — Tier-A `action-locations`] An action nobody placed.\n *\n * `locations` is an action's placement declaration. An action that omits it —\n * and that no view names in `bulkActions` / `bulkActionDefs` / `rowActions` —\n * has no surface at all: it parses, it publishes, Setup lists it, and no user\n * can ever click it. ADR-0078 names this shape in its opening paragraph (\"a\n * `summary` with no `summaryOperations`; **an `action` with no `locations`**;\n * … Each parses, 'renders', reports success — and does nothing\") and Phase 3\n * asks for exactly this rule, one verified shape at a time.\n *\n * The renderer half is now unambiguous: objectui#3142 collapsed four\n * disagreeing renderers onto one predicate — an action renders at a location\n * only if it DECLARES that location. Before that, `action:bar` and the record\n * header showed an undeclared action *everywhere*, which is what made this\n * shape look alive; it is measurably inert as of objectui 17.1.\n *\n * ## What is NOT flagged, and why\n *\n * **`locations: []` — a deliberate headless action.** `content/docs/ui/\n * actions.mdx` (\"Headless actions: declare it, then hide it\") documents the\n * empty array as a first-class shape: the action stays callable over REST /\n * MCP / AI and keeps its capability gate, param contract and audit trail,\n * while claiming no UI surface. ADR-0110 D3 refuses an *undeclared* handler,\n * so a headless declaration is the only legal way to expose such an action —\n * flagging it would fight that ADR. The distinction this rule draws is\n * therefore between an author who said \"nowhere, deliberately\" (`[]`) and one\n * who never said anything at all (key absent).\n *\n * **Actions a view places by NAME.** Naming an action in a list view's\n * `bulkActions` or `bulkActionDefs` IS its placement — the selection bar is\n * driven by the view, never by `locations` (that is what the retired\n * `action.bulkEnabled` tombstone prescribes, and what objectui#3139's\n * aggregate bulk mode relies on: an action that only makes sense over a\n * selection has no single-record location by construction). `rowActions` is\n * exempted on the same zero-false-positive posture (ADR-0072 D1): it is the\n * same field pair on the same container, and an author who named an action\n * there has stated an intent — a name that resolves to nothing is already\n * `action-name-undefined`'s job, not this rule's.\n *\n * Scope note: this rule asks only \"did anyone place this action?\". It\n * deliberately does NOT check that a declared location is one a renderer\n * actually serves, nor that a view's named action belongs to that view's\n * object — distinct classes with their own rules. Cross-package placement (a\n * view in another installed package naming this action) is the one legitimate\n * miss, which is why this is a **warning**: like every other \"declared but\n * does nothing\" finding in this package (`validateSemanticRoles`,\n * `lintLivenessProperties`), it is high-signal and never fatal.\n */\n\nexport const ACTION_NO_PLACEMENT = 'action-no-placement';\n\nexport type ActionLocationsSeverity = 'error' | 'warning';\n\nexport interface ActionLocationsFinding {\n /** Always `warning` — cross-package placement is a legitimate miss. */\n severity: ActionLocationsSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `action \"crm_convert_lead\"`. */\n where: string;\n /** Config path, e.g. `actions[2]` or `objects[0].actions[1]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction strList(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && x.length > 0) : [];\n}\n\n/**\n * Every action name a view places by NAME, across all three list-view tiers:\n * `views[i].list`, `views[i].listViews.<key>`, and the object-embedded\n * `objects[i].listViews.<key>` (an object has no top-level `list`). Missing\n * the object-embedded tier would flag actions that an object's own view\n * places — the trap `validate-list-view-mode.ts` already walks around.\n */\nfunction collectNamePlacedActions(stack: AnyRec): Set<string> {\n const placed = new Set<string>();\n\n const harvest = (container: unknown): void => {\n if (!container || typeof container !== 'object') return;\n const list = container as AnyRec;\n for (const key of ['rowActions', 'bulkActions'] as const) {\n for (const n of strList(list[key])) placed.add(n);\n }\n // A `bulkActionDefs` entry is a loose record; its `name` is the action it\n // dispatches. Inline field-patch defs (`operation: 'update'`) carry a name\n // that matches no action — harmless here, since an unmatched name simply\n // never exempts anything.\n for (const def of asArray(list.bulkActionDefs)) {\n const n = strName(def?.name);\n if (n) placed.add(n);\n }\n };\n\n const harvestListViews = (listViews: unknown): void => {\n if (!listViews || typeof listViews !== 'object' || Array.isArray(listViews)) return;\n for (const lv of Object.values(listViews as AnyRec)) harvest(lv);\n };\n\n for (const view of asArray(stack.views)) {\n if (!view || typeof view !== 'object') continue;\n harvest(view.list);\n harvestListViews(view.listViews);\n }\n for (const obj of asArray(stack.objects)) {\n if (!obj || typeof obj !== 'object') continue;\n harvestListViews(obj.listViews);\n }\n\n return placed;\n}\n\n/**\n * Flag every action that declares no placement and that no view places by\n * name. Returns findings (empty = clean).\n */\nexport function validateActionLocations(stack: AnyRec): ActionLocationsFinding[] {\n const findings: ActionLocationsFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const namePlaced = collectNamePlacedActions(stack);\n\n const check = (action: AnyRec | undefined, path: string): void => {\n if (!action || typeof action !== 'object') return;\n // `[]` is the documented headless shape — the author said \"nowhere\" on\n // purpose. Only a MISSING key is unstated placement.\n if ('locations' in action) return;\n const name = strName(action.name);\n if (!name) return; // nameless actions are `action-name-*`'s problem\n if (namePlaced.has(name)) return;\n\n findings.push({\n severity: 'warning',\n rule: ACTION_NO_PLACEMENT,\n where: `action \"${name}\"`,\n path,\n message:\n `Action \"${name}\" declares no \\`locations\\` and no view places it by name, ` +\n 'so it renders on no surface — the button exists in metadata and nowhere in the UI.',\n hint:\n 'Add the surface it belongs on, e.g. `locations: [\\'record_header\\']` (or `list_item`, ' +\n '`list_toolbar`, `record_more`, `record_section`, `record_related`, `global_nav`); or ' +\n \"place it from a list view's `bulkActions` / `bulkActionDefs` if it acts on a selection. \" +\n 'If it is meant to be callable over REST / MCP / AI with no UI surface, say so explicitly ' +\n 'with `locations: []` — an empty array is the documented headless shape and is never flagged.',\n });\n };\n\n const actions = asArray(stack.actions);\n for (let i = 0; i < actions.length; i++) check(actions[i], `actions[${i}]`);\n\n const objects = asArray(stack.objects);\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!obj || typeof obj !== 'object') continue;\n const own = asArray(obj.actions);\n for (let ai = 0; ai < own.length; ai++) check(own[ai], `objects[${oi}].actions[${ai}]`);\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0021 — semantic layer] Chart-binding integrity for the surfaces the\n * dashboard rule does not reach (issue #3583, assessment R4).\n *\n * `validate-widget-bindings` already resolves a dashboard widget's\n * `chartConfig` axes against its dataset's declared dimensions and measures\n * (`chart-field-unknown`). It is scoped to `stack.dashboards`, so three other\n * chart surfaces ship unchecked — and the HotCRM audit found exactly the bug\n * that scoping allows: an axis naming a RAW FIELD instead of a dataset measure.\n * Post-ADR-0021 the result rows are keyed by measure NAME (`sum_amount`), not\n * the base column (`amount`), so the axis renders and the series is empty.\n *\n * Surfaces covered here:\n *\n * 1. **Report charts** — `report.chart` and `report.blocks[].chart`.\n * `ReportChartSchema` narrows `xAxis`/`yAxis` from ChartConfig's\n * object/array shapes to bare STRINGS, which is why simply pointing the\n * dashboard rule at reports would find nothing: its `Array.isArray(yAxis)`\n * guard skips a string silently. `series[].name` keeps the array shape.\n * 2. **List-view charts** — `ListChartConfigSchema` (`dataset` +\n * `dimensions` + `values`), reachable through `views[].list`,\n * `views[].listViews.<key>`, and `objects[].listViews.<key>`.\n * 3. **Dataset-bound page chart components** — a `PageComponent` whose\n * `properties` carry a `dataset` (the `object-chart` component). Same\n * binding shape as a list chart, but it arrives through the untyped\n * `properties` bag.\n *\n * Not covered HERE, and deliberately so: the react `<ObjectChart>` block. It is\n * OBJECT-bound (`objectName` + an inline `aggregate`), so its result rows are\n * keyed by the RAW FIELD NAMES rather than by a measure name — the opposite\n * convention, which would make `chart-measure-unknown`'s message a lie. It also\n * arrives as JSX rather than config, so it needs the TypeScript compiler this\n * rule has no business loading. It is checked by `validate-react-page-props`\n * instead, against the naming convention `chartAggregateResultKeys`\n * (`@objectstack/spec/ui`) now pins down (#3701).\n */\n\nexport const CHART_DIMENSION_UNKNOWN = 'chart-dimension-unknown';\nexport const CHART_MEASURE_UNKNOWN = 'chart-measure-unknown';\nexport const CHART_DATASET_UNKNOWN = 'chart-dataset-unknown';\nexport const CHART_AXIS_NOT_SELECTED = 'chart-axis-not-selected';\n\nexport type ChartBindingSeverity = 'error' | 'warning';\n\nexport interface ChartBindingFinding {\n severity: ChartBindingSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `report \"hours_by_status\" · chart`. */\n where: string;\n /** Config path, e.g. `reports[2].chart.yAxis`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\nimport { walkPageComponents, type AnyRec } from './page-walk.js';\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction strList(v: unknown): string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string' && x.length > 0) : [];\n}\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\nfunction suggest(target: string, known: Iterable<string>): string {\n let best: string | undefined;\n let bestScore = Infinity;\n for (const c of known) {\n const d = distance(target, c);\n if (d < bestScore) {\n bestScore = d;\n best = c;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\nfunction list(names: Iterable<string>): string {\n const all = [...names].sort();\n return all.length ? all.join(', ') : '(none)';\n}\n\n/** A dataset's declared dimension and measure names. */\ninterface DatasetNames {\n dimensions: Set<string>;\n measures: Set<string>;\n}\n\nfunction indexDatasets(stack: AnyRec): Map<string, DatasetNames> {\n const out = new Map<string, DatasetNames>();\n for (const ds of asArray(stack.datasets)) {\n const name = strName(ds.name);\n if (!name) continue;\n const dimensions = new Set<string>();\n for (const d of asArray(ds.dimensions)) {\n const n = strName(d.name);\n if (n) dimensions.add(n);\n }\n const measures = new Set<string>();\n for (const m of asArray(ds.measures)) {\n const n = strName(m.name);\n if (n) measures.add(n);\n }\n out.set(name, { dimensions, measures });\n }\n return out;\n}\n\n/**\n * One dataset-bound chart to check: the binding, the selection, and where it\n * came from. `xAxis`/`yAxis`/`series` are the ChartConfig-style axis refs;\n * `dimensions`/`values` are the list-chart-style selection.\n */\ninterface ChartBinding {\n dataset?: string;\n /** Selected dimension names (list-chart shape). */\n dimensions?: { names: string[]; path: string };\n /** Selected measure names (list-chart / report shape). */\n values?: { names: string[]; path: string };\n /** Single dimension ref (report `xAxis`). */\n xAxis?: { name: string; path: string };\n /** Single measure ref (report `yAxis`). */\n yAxis?: { name: string; path: string };\n /** Series names — measure refs (ChartConfig shape). */\n series?: Array<{ name: string; path: string }>;\n where: string;\n /** Path of the chart container, for the dataset-level finding. */\n path: string;\n}\n\nexport function validateChartBindings(stack: AnyRec): ChartBindingFinding[] {\n const findings: ChartBindingFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const datasets = indexDatasets(stack);\n if (datasets.size === 0 && !stack.reports && !stack.views && !stack.pages) return findings;\n\n const check = (binding: ChartBinding) => {\n const dsName = binding.dataset;\n if (!dsName) return; // nothing bound — the shape rules own that case\n const ds = datasets.get(dsName);\n if (!ds) {\n findings.push({\n severity: 'error',\n rule: CHART_DATASET_UNKNOWN,\n where: binding.where,\n path: `${binding.path}.dataset`,\n message:\n `binds dataset \"${dsName}\", which resolves to no declared dataset — ` +\n `the chart has no data to render.`,\n hint:\n `Declared datasets: ${list(datasets.keys())}.${suggest(dsName, datasets.keys())} ` +\n `Define it with defineDataset() or fix the reference (ADR-0021).`,\n });\n return;\n }\n\n const dimensionRef = (name: string, path: string) => {\n if (ds.dimensions.has(name)) return;\n findings.push({\n severity: 'error',\n rule: CHART_DIMENSION_UNKNOWN,\n where: binding.where,\n path,\n message:\n `\"${name}\" is not a dimension declared by dataset \"${dsName}\". ` +\n `Post-ADR-0021 result rows are keyed by DIMENSION NAME, not the base ` +\n `field, so this axis renders with no categories.`,\n hint:\n `Dataset dimensions: ${list(ds.dimensions)}.${suggest(name, ds.dimensions)} ` +\n `Declare the dimension on the dataset, or bind an existing one.`,\n });\n };\n\n const measureRef = (name: string, path: string, selected?: Set<string>) => {\n if (!ds.measures.has(name)) {\n findings.push({\n severity: 'error',\n rule: CHART_MEASURE_UNKNOWN,\n where: binding.where,\n path,\n message:\n `\"${name}\" is not a measure declared by dataset \"${dsName}\". ` +\n `Post-ADR-0021 result rows are keyed by MEASURE NAME (e.g. \"sum_amount\"), ` +\n `not the base field (e.g. \"amount\"), so this series comes back empty.`,\n hint:\n `Dataset measures: ${list(ds.measures)}.${suggest(name, ds.measures)} ` +\n `Declare the measure on the dataset, or bind an existing one.`,\n });\n return;\n }\n // Declared but not part of this chart's selection: the query never asks\n // for it, so the axis still plots nothing. Advisory — the selection may\n // legitimately be widened at runtime.\n if (selected && selected.size > 0 && !selected.has(name)) {\n findings.push({\n severity: 'warning',\n rule: CHART_AXIS_NOT_SELECTED,\n where: binding.where,\n path,\n message:\n `\"${name}\" is a declared measure of \"${dsName}\" but is not in this chart's ` +\n `selected values (${list(selected)}) — the query does not return it, ` +\n `so the series plots nothing.`,\n hint: `Add \"${name}\" to \\`values\\`, or point the axis at a selected measure.`,\n });\n }\n };\n\n const dimSel = binding.dimensions;\n if (dimSel) {\n for (let i = 0; i < dimSel.names.length; i++) {\n dimensionRef(dimSel.names[i], `${dimSel.path}[${i}]`);\n }\n }\n const valSel = binding.values;\n const selected = new Set(valSel?.names ?? []);\n if (valSel) {\n for (let i = 0; i < valSel.names.length; i++) {\n measureRef(valSel.names[i], `${valSel.path}[${i}]`);\n }\n }\n if (binding.xAxis) dimensionRef(binding.xAxis.name, binding.xAxis.path);\n if (binding.yAxis) measureRef(binding.yAxis.name, binding.yAxis.path, selected);\n for (const s of binding.series ?? []) measureRef(s.name, s.path, selected);\n };\n\n // ── 1. Report charts (report.chart + report.blocks[].chart) ──\n const reports = asArray(stack.reports);\n for (let ri = 0; ri < reports.length; ri++) {\n const report = reports[ri];\n if (!isRec(report)) continue;\n const reportName = strName(report.name) ?? `#${ri}`;\n\n const checkReportChart = (\n chart: unknown,\n dataset: string | undefined,\n values: string[],\n where: string,\n path: string,\n ) => {\n if (!isRec(chart)) return;\n check({\n dataset,\n // `values` is the report's measure SELECTION, not a chart ref; feeding\n // it in lets the yAxis \"declared but not selected\" check work without\n // reporting the selection itself twice.\n values: { names: values, path: `${path}.values` },\n xAxis: strName(chart.xAxis) ? { name: strName(chart.xAxis)!, path: `${path}.chart.xAxis` } : undefined,\n yAxis: strName(chart.yAxis) ? { name: strName(chart.yAxis)!, path: `${path}.chart.yAxis` } : undefined,\n series: asArray(chart.series)\n .map((s, si) => ({ name: strName(s.name), path: `${path}.chart.series[${si}].name` }))\n .filter((s): s is { name: string; path: string } => !!s.name),\n where,\n path: `${path}.chart`,\n });\n };\n\n checkReportChart(\n report.chart,\n strName(report.dataset),\n strList(report.values),\n `report \"${reportName}\" · chart`,\n `reports[${ri}]`,\n );\n\n const blocks = Array.isArray(report.blocks) ? report.blocks : [];\n for (let bi = 0; bi < blocks.length; bi++) {\n const block = blocks[bi];\n if (!isRec(block)) continue;\n checkReportChart(\n block.chart,\n strName(block.dataset),\n strList(block.values),\n `report \"${reportName}\" · block \"${strName(block.name) ?? `#${bi}`}\" chart`,\n `reports[${ri}].blocks[${bi}]`,\n );\n }\n }\n\n // ── 2. List-view charts ──\n const checkListChart = (container: unknown, where: string, path: string) => {\n if (!isRec(container)) return;\n const chart = container.chart;\n if (!isRec(chart)) return;\n check({\n dataset: strName(chart.dataset),\n dimensions: { names: strList(chart.dimensions), path: `${path}.chart.dimensions` },\n values: { names: strList(chart.values), path: `${path}.chart.values` },\n where,\n path: `${path}.chart`,\n });\n };\n\n const views = asArray(stack.views);\n for (let vi = 0; vi < views.length; vi++) {\n const view = views[vi];\n if (!isRec(view)) continue;\n const viewName = strName(view.name) ?? strName(view.objectName) ?? `#${vi}`;\n checkListChart(view.list, `view \"${viewName}\" · list chart`, `views[${vi}].list`);\n if (isRec(view.listViews)) {\n for (const [key, lv] of Object.entries(view.listViews)) {\n checkListChart(lv, `view \"${viewName}\" · listViews.${key} chart`, `views[${vi}].listViews.${key}`);\n }\n }\n }\n\n const objects = asArray(stack.objects);\n for (let oi = 0; oi < objects.length; oi++) {\n const obj = objects[oi];\n if (!isRec(obj) || !isRec(obj.listViews)) continue;\n const objName = strName(obj.name) ?? `#${oi}`;\n for (const [key, lv] of Object.entries(obj.listViews)) {\n checkListChart(\n lv,\n `object \"${objName}\" · listViews.${key} chart`,\n `objects[${oi}].listViews.${key}`,\n );\n }\n }\n\n // ── 3. Dataset-bound page chart components ──\n // A chart component arrives through the untyped `properties` bag. The\n // presence of a `dataset` key is what marks it dataset-bound (and so\n // checkable); an object-bound chart has none and is left alone.\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n const page = pages[pi];\n if (!isRec(page)) continue;\n const pageName = strName(page.name) ?? `#${pi}`;\n for (const { component, path } of walkPageComponents(page, `pages[${pi}]`)) {\n const props = isRec(component.properties) ? component.properties : undefined;\n if (!props || !strName(props.dataset)) continue;\n // A page chart mixes the list-chart selection (`dataset`/`dimensions`/\n // `values`) with ChartConfig-style axes (`yAxis: [{ field }]`), so both\n // shapes are read here.\n const axisRefs = asArray(props.yAxis)\n .map((a, ai) => ({ name: strName(a.field), path: `${path}.properties.yAxis[${ai}].field` }))\n .filter((a): a is { name: string; path: string } => !!a.name);\n const seriesRefs = asArray(props.series)\n .map((s, si) => ({ name: strName(s.name), path: `${path}.properties.series[${si}].name` }))\n .filter((s): s is { name: string; path: string } => !!s.name);\n check({\n dataset: strName(props.dataset),\n dimensions: { names: strList(props.dimensions), path: `${path}.properties.dimensions` },\n values: { names: strList(props.values), path: `${path}.properties.values` },\n series: [...axisRefs, ...seriesRefs],\n where: `page \"${pageName}\" · ${strName(component.type) ?? 'chart'}`,\n path: `${path}.properties`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0090 D6] Navigation reachability vs. granted access (issue #3583,\n * assessment R5).\n *\n * An app can put an object in its navigation without any permission set\n * granting read on it. Nothing rejects that: navigation and permissions are\n * separate metadata, each valid on its own. At runtime the entry renders, the\n * user clicks it, and the list view comes back permission-denied — for EVERY\n * user, including the admin, because a permission nobody was granted is a\n * permission nobody has. The HotCRM audit shipped two such objects\n * (`crm_forecast`, `crm_knowledge_article`).\n *\n * This is the first lint consumer of `buildAccessMatrix` (ADR-0090 D6), which\n * already derives one row per (permission set × object) with `read` folded\n * across `allowRead` / `viewAllRecords` / `modifyAllRecords`. The rule is that\n * matrix joined against what navigation exposes.\n *\n * ── Advisory, deliberately ──────────────────────────────────────────────\n *\n * A grant can legitimately live outside this stack: a permission set shipped by\n * another installed package, a platform default, or an org-level assignment\n * made after install. A stack is therefore not *wrong* to ship an ungranted nav\n * entry — it is only *suspicious*, and the ceiling for a static check is a\n * warning (the same posture `validate-capability-references` takes).\n *\n * Two exemptions keep it quiet:\n * - **Platform-provided objects** (`sys_user`, `sys_approval_request`, …) are\n * skipped: the packages that register them ship their own permission sets,\n * which this stack never sees.\n * - **A stack that declares no permission sets at all** is skipped entirely.\n * Flagging every nav entry there says nothing useful — it means permissions\n * are managed elsewhere, not that each entry is broken (the same\n * \"empty collection ⇒ don't judge\" gate `defineStack` applies to nav\n * dashboard/page/report references).\n */\n\nimport { isPlatformProvidedObjectName } from '@objectstack/spec/system';\nimport { buildAccessMatrix } from './build-access-matrix.js';\n\nexport const NAV_OBJECT_UNGRANTED = 'nav-object-ungranted';\n\nexport type NavAccessSeverity = 'error' | 'warning';\n\nexport interface NavAccessFinding {\n /** Always `warning` — a grant may come from a package this stack cannot see. */\n severity: NavAccessSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `app \"crm\" · nav \"nav_forecast\"`. */\n where: string;\n /** Config path, e.g. `apps[0].navigation[3].objectName`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** One navigation entry that exposes an object. */\ninterface NavExposure {\n objectName: string;\n where: string;\n path: string;\n}\n\n/** Collect every object a stack's navigation exposes, across areas and children. */\nfunction collectNavExposures(stack: AnyRec): NavExposure[] {\n const out: NavExposure[] = [];\n const apps = asArray(stack.apps);\n\n for (let ai = 0; ai < apps.length; ai++) {\n const app = apps[ai];\n if (!app || typeof app !== 'object') continue;\n const appName = strName(app.name) ?? `#${ai}`;\n\n const walk = (items: unknown, basePath: string) => {\n const navItems = asArray(items);\n for (let ni = 0; ni < navItems.length; ni++) {\n const nav = navItems[ni];\n if (!nav || typeof nav !== 'object') continue;\n const navPath = `${basePath}[${ni}]`;\n const objectName = strName(nav.objectName);\n if (nav.type === 'object' && objectName) {\n out.push({\n objectName,\n where: `app \"${appName}\" · nav \"${strName(nav.id) ?? `#${ni}`}\"`,\n path: `${navPath}.objectName`,\n });\n }\n if (Array.isArray(nav.children)) walk(nav.children, `${navPath}.children`);\n }\n };\n\n walk(app.navigation, `apps[${ai}].navigation`);\n const areas = asArray(app.areas);\n for (let ri = 0; ri < areas.length; ri++) {\n walk(areas[ri]?.navigation, `apps[${ai}].areas[${ri}].navigation`);\n }\n }\n\n return out;\n}\n\n/**\n * Validate that every object a stack's navigation exposes is readable by at\n * least one permission set the stack declares. Returns findings (empty = clean).\n */\nexport function validateNavAccess(stack: AnyRec): NavAccessFinding[] {\n const findings: NavAccessFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n // No permission sets in this stack ⇒ permissions are managed elsewhere.\n const permissionSets = asArray(stack.permissions);\n if (permissionSets.length === 0) return findings;\n\n const exposures = collectNavExposures(stack);\n if (exposures.length === 0) return findings;\n\n // Objects this stack actually defines — the only ones whose grants must be\n // present here. A nav target that resolves nowhere is a different bug, owned\n // by `validate-object-references` / `defineStack`.\n const ownObjects = new Set<string>();\n for (const obj of asArray(stack.objects)) {\n const n = strName(obj.name);\n if (n) ownObjects.add(n);\n }\n\n // `read` is already folded across allowRead / viewAllRecords / modifyAllRecords.\n const readable = new Set<string>();\n for (const entry of buildAccessMatrix(stack).entries) {\n if (entry.read) readable.add(entry.object);\n }\n // A wildcard grant (`objects: { '*': { allowRead: true } }`) covers every\n // object — the shape the platform's own `admin_full_access` uses. Without\n // this the matrix records the literal key `*` and every object looks\n // ungranted, which would make the rule fire on exactly the stacks that\n // granted the most.\n if (readable.has('*')) return findings;\n\n // De-duplicate: one finding per object, not per nav entry that exposes it.\n const reported = new Set<string>();\n\n for (const exposure of exposures) {\n const { objectName } = exposure;\n if (reported.has(objectName)) continue;\n if (isPlatformProvidedObjectName(objectName)) continue; // granted by its own package\n if (!ownObjects.has(objectName)) continue; // not ours to grant\n if (readable.has(objectName)) continue;\n\n reported.add(objectName);\n findings.push({\n severity: 'warning',\n rule: NAV_OBJECT_UNGRANTED,\n where: exposure.where,\n path: exposure.path,\n message:\n `navigation exposes object \"${objectName}\", but no permission set this stack ` +\n `declares grants read on it — the entry renders, and opening it fails ` +\n `permission-denied for every principal except one holding the platform's ` +\n `built-in wildcard admin set. It works when you browse as an administrator ` +\n `and breaks for the users the app ships permission sets for.`,\n hint:\n `Add \"${objectName}\" to a permission set's \\`objects\\` with \\`allowRead: true\\` ` +\n `(or \\`viewAllRecords\\`), gate the entry with \\`requiredPermissions\\`/\\`visible\\` ` +\n `if it is meant for admins only, or drop it. Ignore this if a permission set ` +\n `from another installed package grants it.`,\n });\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0090 D6] Access-matrix snapshot — authoring-time companion to the\n * runtime explain engine.\n *\n * `buildAccessMatrix(stack)` derives, PURELY from metadata, one row per\n * (permission set × object) the stack declares: the CRUD/VAMA bits, the\n * depth axes, and the object's OWD for context. The matrix is snapshotted to\n * `access-matrix.json` and diffed on every compile: an unchanged matrix\n * auto-passes; a changed one fails the build until the snapshot is updated —\n * so every capability change becomes a REVIEWABLE, semantic diff\n * (\"`crm_admin` gains delete on `crm_lead`\") instead of a buried JSON hunk.\n * This is the publish-gate substrate the AI-authoring safety story needs:\n * AI may draft grants freely; it cannot silently change who can do what.\n */\n\nimport type { AccessMatrix, AccessMatrixEntry } from '@objectstack/spec/security';\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/** Build the sorted access matrix for a normalized stack. */\nexport function buildAccessMatrix(stack: AnyRec): AccessMatrix {\n const entries: AccessMatrixEntry[] = [];\n if (!stack || typeof stack !== 'object') return { version: 1, entries };\n\n const owdByObject = new Map<string, string>();\n for (const obj of asArray(stack.objects)) {\n const name = typeof obj.name === 'string' ? obj.name : '';\n if (!name) continue;\n const owd = (obj.sharingModel ?? (obj.security as AnyRec | undefined)?.sharingModel) as string | undefined;\n if (typeof owd === 'string') owdByObject.set(name, owd);\n }\n\n for (const ps of asArray(stack.permissions)) {\n const psName = typeof ps.name === 'string' ? ps.name : '';\n if (!psName) continue;\n const objects = (ps.objects && typeof ps.objects === 'object' ? ps.objects : {}) as AnyRec;\n for (const [objName, rawPerm] of Object.entries(objects)) {\n const p = (rawPerm ?? {}) as AnyRec;\n const entry: AccessMatrixEntry = {\n permissionSet: psName,\n object: objName,\n create: p.allowCreate === true,\n read: p.allowRead === true || p.viewAllRecords === true || p.modifyAllRecords === true,\n edit: p.allowEdit === true || p.modifyAllRecords === true,\n delete: p.allowDelete === true || p.modifyAllRecords === true,\n viewAllRecords: p.viewAllRecords === true,\n modifyAllRecords: p.modifyAllRecords === true,\n };\n if (typeof p.readScope === 'string') entry.readScope = p.readScope;\n if (typeof p.writeScope === 'string') entry.writeScope = p.writeScope;\n const owd = owdByObject.get(objName);\n if (owd) entry.sharingModel = owd;\n entries.push(entry);\n }\n }\n\n entries.sort((a, b) =>\n a.permissionSet === b.permissionSet\n ? a.object.localeCompare(b.object)\n : a.permissionSet.localeCompare(b.permissionSet),\n );\n return { version: 1, entries };\n}\n\nconst BIT_LABELS: Array<[keyof AccessMatrixEntry, string]> = [\n ['create', 'create'],\n ['read', 'read'],\n ['edit', 'edit'],\n ['delete', 'delete'],\n ['viewAllRecords', 'View All Data'],\n ['modifyAllRecords', 'Modify All Data'],\n];\n\n/**\n * Semantic diff between two matrices — human-review lines, empty = identical.\n * Ordered: removals, additions, then per-entry bit/scope changes.\n */\nexport function diffAccessMatrix(before: AccessMatrix, after: AccessMatrix): string[] {\n const lines: string[] = [];\n const key = (e: AccessMatrixEntry) => `${e.permissionSet}\\u0000${e.object}`;\n const beforeMap = new Map((before?.entries ?? []).map((e) => [key(e), e]));\n const afterMap = new Map((after?.entries ?? []).map((e) => [key(e), e]));\n\n for (const [k, b] of beforeMap) {\n if (!afterMap.has(k)) {\n lines.push(`'${b.permissionSet}' loses ALL access to '${b.object}' (entry removed)`);\n }\n }\n for (const [k, a] of afterMap) {\n const b = beforeMap.get(k);\n if (!b) {\n const grants = BIT_LABELS.filter(([bit]) => a[bit] === true).map(([, label]) => label);\n lines.push(`'${a.permissionSet}' gains access to '${a.object}' (${grants.join(', ') || 'no bits set'})`);\n continue;\n }\n for (const [bit, label] of BIT_LABELS) {\n if (b[bit] !== a[bit]) {\n lines.push(`'${a.permissionSet}' ${a[bit] ? 'gains' : 'loses'} ${label} on '${a.object}'`);\n }\n }\n if ((b.readScope ?? 'own') !== (a.readScope ?? 'own')) {\n lines.push(`'${a.permissionSet}' read depth on '${a.object}': ${b.readScope ?? 'own'} → ${a.readScope ?? 'own'}`);\n }\n if ((b.writeScope ?? 'own') !== (a.writeScope ?? 'own')) {\n lines.push(`'${a.permissionSet}' write depth on '${a.object}': ${b.writeScope ?? 'own'} → ${a.writeScope ?? 'own'}`);\n }\n if ((b.sharingModel ?? '') !== (a.sharingModel ?? '')) {\n lines.push(`'${a.object}' record baseline (OWD): ${b.sharingModel ?? '(unset)'} → ${a.sharingModel ?? '(unset)'} (affects every principal)`);\n }\n }\n return lines;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0072 — reference resolvability] Translation-bundle reference integrity\n * and option-key validation (issue #3583, assessment R6).\n *\n * The i18n gate has always run in ONE direction: `computeI18nCoverage` asks\n * \"which keys does the metadata expect that no bundle carries?\" Nothing asks the\n * reverse — \"which keys does a bundle carry that no metadata claims?\" — even\n * though the spec already names the answer: `TranslationDiffStatus 'redundant'`\n * and `TranslationCoverageResult.redundantKeys` are declared and have no\n * producer.\n *\n * The HotCRM audit found that direction shipping broken metadata:\n *\n * - bundles keyed to fields the object never declares (`assigned_to`,\n * `budget`, `image_url`) — usually a rename that moved the field and left\n * the translation behind;\n * - select-option translations keyed by the option's DISPLAY LABEL instead of\n * its stored value, or by a near-miss of the value (`direct-mail` for\n * `direct_mail`, `planned` for `planning`).\n *\n * Both fail the same way: the resolver looks up the key it derives from the\n * metadata, finds nothing, and renders the untranslated source string. The app\n * looks *translated* — every other label on the screen resolves — so the hole is\n * invisible until a reader in that locale hits the one field or one picklist\n * value that stayed English.\n *\n * ── Severity ─────────────────────────────────────────────────────────────\n *\n * All findings are **warnings**. An orphan key is inert, not broken: it costs a\n * few bytes and one untranslated string, and nothing crashes. That is a weaker\n * failure than the dead references `validate-object-references` /\n * `validate-action-name-refs` report as errors, and the severity should say so\n * (ADR-0072 D1 — a linter that over-states is a linter authors stop reading).\n *\n * ── What this rule deliberately does NOT check ───────────────────────────\n *\n * - `messages`, `validationMessages`, `settings`, `settingsCommon` — keyed by\n * free-form message ids / namespaces owned by code and plugins, not by\n * stack metadata. There is no enumerable universe to resolve against, so a\n * rule here would be guessing.\n * - `metadataForms` — keyed by the platform's own metadata-type registry, not\n * by this stack. Owned by the platform packages; a stack translating them is\n * correct, not orphaned.\n * - leaf attribute names (`labl:` instead of `label:`) — Zod strips unknown\n * keys at parse, so they never reach a consumer with a value; that is a\n * schema-shape concern, not a reference.\n * - the object-first `AppTranslationBundle` (`o.<object>` …) — that shape is\n * the `translation` METADATA TYPE (records persisted through the metadata\n * store), not `stack.translations`, which is `TranslationBundle[]`\n * (locale → `TranslationData`). Its keys are simply not visited here: an\n * unrecognised top-level namespace is skipped, never reported.\n *\n * ── Cross-package objects ────────────────────────────────────────────────\n *\n * A stack legitimately translates objects it does not define — `sys_user`'s\n * labels are exactly the kind of thing an app localizes. Resolution follows the\n * §4 ladder of the assessment, with the field-level S3 rule intact:\n *\n * 1. own object → check its fields/views/actions/sections\n * 2. platform object in the registry → skip WHOLLY (we cannot see its\n * fields, so we cannot judge them)\n * 3. platform-prefixed, not in registry → warn on the object key only\n * 4. unresolved, unprefixed → warn on the object key only\n */\n\nimport { hasPlatformObjectPrefix, isPlatformProvidedObjectName } from '@objectstack/spec/system';\nimport { walkPageComponents } from './page-walk.js';\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\nexport const TRANSLATION_TARGET_UNKNOWN = 'translation-target-unknown';\nexport const TRANSLATION_OPTION_KEY_UNKNOWN = 'translation-option-key-unknown';\n\nexport type TranslationRefSeverity = 'warning';\n\nexport interface TranslationRefFinding {\n /** Always `warning` — an orphan translation key is inert, not broken. */\n severity: TranslationRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `locale \"zh-CN\" · object \"crm_lead\"`. */\n where: string;\n /** Config path, e.g. `translations[0][\"zh-CN\"].objects.crm_lead.fields.campaign`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction isRec(v: unknown): v is AnyRec {\n return !!v && typeof v === 'object' && !Array.isArray(v);\n}\n\n/** Coerce a collection (array or name-keyed map) to an array of records,\n * injecting `name` from the map key — mirrors the sibling authoring lints so\n * the rule works on both the parsed (array) and normalized (map) stack shapes. */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (isRec(v)) return Object.entries(v).map(([name, def]) => ({ name, ...(isRec(def) ? def : {}) }));\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\n/**\n * \"Did you mean?\" over the known names — Levenshtein-bounded, plus a namespace\n * pass the distance metric cannot see.\n *\n * A stack prefixes its object names (`todo_task`, `crm_lead`), and the orphan\n * key is routinely the bare noun the author had in mind (`task`). That is 5\n * edits from `todo_task` — far outside the typo bound, and exactly the case\n * where the suggestion is most useful — so a candidate that differs only by a\n * snake_case namespace segment is offered before falling back to edit distance.\n */\nfunction suggest(target: string, known: Iterable<string>): string {\n const names = [...known];\n const segmentMatch = names.find(\n (candidate) => candidate.endsWith(`_${target}`) || candidate.startsWith(`${target}_`),\n );\n if (segmentMatch) return ` Did you mean \"${segmentMatch}\"?`;\n\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of names) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\n/** At most `max` names, sorted, for the \"known values are …\" tail of a hint. */\nfunction listNames(names: Iterable<string>, max = 12): string {\n const all = [...names].sort();\n if (all.length === 0) return '';\n const shown = all.slice(0, max).join(', ');\n return all.length > max ? `${shown}, … (${all.length} total)` : shown;\n}\n\n/**\n * Fields a bundle may translate without reading as orphans: the package-shared\n * registry-injected columns (`system-fields.ts`, #4330) plus three exemptions\n * this rule has carried since it landed — `_id` and `space` are legacy\n * physical spellings older bundles still key, and `name` is an ordinary\n * authored field on most objects. None of the three is a system column in the\n * spec's sense, so they stay rule-local (see the shared module's note) instead\n * of widening every field-existence rule in the package.\n */\nconst IMPLICIT_FIELDS: ReadonlySet<string> = new Set([\n ...SYSTEM_FIELDS,\n '_id', 'name', 'space',\n]);\n\n/** Everything a bundle may legally name under one object. */\ninterface ObjectFacts {\n fields: Map<string, AnyRec>;\n views: Set<string>;\n actions: Map<string, AnyRec>;\n sections: Set<string>;\n}\n\ninterface Universe {\n objects: Map<string, ObjectFacts>;\n /** App name → every navigation item id declared by that app. */\n apps: Map<string, Set<string>>;\n dashboards: Map<string, { widgets: Set<string>; actions: Set<string> }>;\n /** Object-less actions — the ones `globalActions.*` may name. */\n globalActions: Map<string, AnyRec>;\n /** Action name → owning object, so a misfiled `globalActions` key can say where it belongs. */\n actionOwners: Map<string, string>;\n}\n\nfunction emptyFacts(): ObjectFacts {\n return { fields: new Map(), views: new Set(), actions: new Map(), sections: new Set() };\n}\n\n/**\n * Register everything ONE view record contributes: the `_views` names it makes\n * legal, and the `_sections` names its form views declare — each under the\n * object that container actually binds.\n *\n * Two things about the real shape make this more than \"read `view.name`\", and\n * both were learned from the HotCRM corpus (~18k lines of shipped metadata),\n * where a first pass reported ~40 correct keys as orphans:\n *\n * 1. A view record is a CONTAINER, not a view. The default list sits at\n * `list`; the named tabs at `listViews.<key>` and `formViews.<key>`, each\n * of which may also carry its own `name`. Both the map key and the inner\n * `name` are accepted — authors write either, and the key is what the\n * console renders the tab from.\n * 2. The object binding lives INSIDE the container (`list.data.object`), not\n * at the record root. A record-level lookup alone resolves to nothing on\n * the canonical shape, which silently drops the whole record — a rule that\n * then reports every view key the app ships.\n */\nfunction collectViewRecord(view: AnyRec, factsFor: (objectName: string) => ObjectFacts): void {\n const recordObject = viewObjectName(view);\n const bindingOf = (container: AnyRec): string | undefined =>\n viewObjectName(container) ?? recordObject;\n\n const addView = (objectName: string | undefined, name: string | undefined) => {\n if (objectName && name) factsFor(objectName).views.add(name);\n };\n\n const listBinding = isRec(view.list) ? bindingOf(view.list) : undefined;\n if (isRec(view.list)) addView(listBinding, strName(view.list.name));\n addView(recordObject ?? listBinding, strName(view.name));\n\n for (const key of ['listViews', 'formViews'] as const) {\n const container = view[key];\n if (!isRec(container)) continue;\n for (const [subKey, sub] of Object.entries(container)) {\n if (!isRec(sub)) continue;\n const binding = bindingOf(sub) ?? listBinding;\n addView(binding, subKey);\n addView(binding, strName(sub.name));\n\n // Form sections carry an OPTIONAL `name` that exists purely for the\n // `_sections` lookup (`ui/view.zod.ts`: \"Stable section identifier for\n // i18n lookup\"). A section without one cannot be translated at all, so\n // it contributes nothing here.\n if (binding) {\n for (const section of asArray(sub.sections)) {\n const sectionName = strName(section.name);\n if (sectionName) factsFor(binding).sections.add(sectionName);\n }\n }\n }\n }\n\n const sectionBinding = recordObject ?? listBinding;\n if (sectionBinding) {\n for (const section of asArray(view.sections)) {\n const sectionName = strName(section.name);\n if (sectionName) factsFor(sectionBinding).sections.add(sectionName);\n }\n }\n}\n\n/** The object a view (or one of its containers) binds to, across the shapes it is authored in. */\nfunction viewObjectName(view: AnyRec): string | undefined {\n return (\n strName(view.objectName) ??\n strName(view.object) ??\n (isRec(view.data) ? strName(view.data.object) : undefined)\n );\n}\n\n/**\n * Declared select options for a field, or `undefined` when the field declares\n * none at all. Handles the canonical `{value,label}[]` shape plus the two\n * legacy shapes the extractor also tolerates (bare `string[]`, and a\n * `value → label` record).\n */\nfunction readOptions(field: AnyRec): { values: Set<string>; byLabel: Map<string, string> } | undefined {\n const raw = field.options;\n const values = new Set<string>();\n const byLabel = new Map<string, string>();\n if (Array.isArray(raw)) {\n for (const opt of raw) {\n if (typeof opt === 'string') {\n values.add(opt);\n continue;\n }\n if (!isRec(opt)) continue;\n const value = strName(opt.value);\n if (!value) continue;\n values.add(value);\n const label = strName(opt.label);\n if (label) byLabel.set(label.toLowerCase(), value);\n }\n } else if (isRec(raw)) {\n for (const [value, label] of Object.entries(raw)) {\n values.add(value);\n if (typeof label === 'string' && label.length > 0) byLabel.set(label.toLowerCase(), value);\n }\n } else {\n return undefined;\n }\n return values.size > 0 ? { values, byLabel } : undefined;\n}\n\n/**\n * Collect every name a translation bundle may resolve against. Built once per\n * run: the same universe answers all bundles and all locales.\n */\nfunction buildUniverse(stack: AnyRec): Universe {\n const objects = new Map<string, ObjectFacts>();\n const factsFor = (name: string): ObjectFacts => {\n let facts = objects.get(name);\n if (!facts) {\n facts = emptyFacts();\n objects.set(name, facts);\n }\n return facts;\n };\n\n // ── Objects: fields, embedded actions/views, fieldGroups (the `_sections` anchor) ──\n for (const obj of asArray(stack.objects)) {\n const objectName = strName(obj.name);\n if (!objectName) continue;\n const facts = factsFor(objectName);\n\n for (const field of asArray(obj.fields)) {\n const fieldName = strName(field.name);\n if (fieldName) facts.fields.set(fieldName, field);\n }\n for (const action of asArray(obj.actions)) {\n const actionName = strName(action.name);\n if (actionName) facts.actions.set(actionName, action);\n }\n // An object can carry views directly, including the `objects[].listViews`\n // container the chart rule also walks. `{ ...view, object: objectName }`\n // pins the binding: an embedded view inherits its owner, and nothing here\n // depends on the container repeating it.\n for (const view of asArray(obj.views)) {\n collectViewRecord({ ...view, object: strName(view.object) ?? objectName }, factsFor);\n }\n collectViewRecord({ object: objectName, listViews: obj.listViews }, factsFor);\n // ADR-0085: `fieldGroups[].key` is the i18n anchor for `_sections`.\n for (const group of asArray(obj.fieldGroups)) {\n const key = strName(group.key) ?? strName(group.name);\n if (key) facts.sections.add(key);\n }\n }\n\n // ── Stack-level views: `_views` names + form-section names ──\n for (const view of asArray(stack.views)) {\n collectViewRecord(view, factsFor);\n }\n\n // ── Pages: `record:details` sections are the other `_sections` anchor ──\n const pages = asArray(stack.pages);\n for (let pi = 0; pi < pages.length; pi++) {\n for (const walked of walkPageComponents(pages[pi], `pages[${pi}]`)) {\n if (!walked.objectName) continue;\n const props = isRec(walked.component.properties) ? walked.component.properties : undefined;\n if (!props) continue;\n for (const section of asArray(props.sections)) {\n const sectionName = strName(section.name);\n if (sectionName) factsFor(walked.objectName).sections.add(sectionName);\n }\n }\n }\n\n // ── Actions: object-bound ones join their object; the rest are global ──\n const globalActions = new Map<string, AnyRec>();\n const actionOwners = new Map<string, string>();\n for (const action of asArray(stack.actions)) {\n const actionName = strName(action.name);\n if (!actionName) continue;\n const owner = strName(action.objectName) ?? strName(action.object);\n if (owner) {\n factsFor(owner).actions.set(actionName, action);\n actionOwners.set(actionName, owner);\n } else {\n globalActions.set(actionName, action);\n }\n }\n for (const [objectName, facts] of objects) {\n for (const actionName of facts.actions.keys()) {\n if (!actionOwners.has(actionName)) actionOwners.set(actionName, objectName);\n }\n }\n\n // ── Apps: navigation item ids (`apps.<app>.navigation.<id>.label`) ──\n const apps = new Map<string, Set<string>>();\n for (const app of asArray(stack.apps)) {\n const appName = strName(app.name);\n if (!appName) continue;\n const navIds = apps.get(appName) ?? new Set<string>();\n const walkNav = (items: unknown) => {\n for (const item of asArray(items)) {\n const id = strName(item.id);\n if (id) navIds.add(id);\n if (item.children) walkNav(item.children);\n }\n };\n walkNav(app.navigation);\n for (const area of asArray(app.areas)) {\n const areaId = strName(area.id);\n if (areaId) navIds.add(areaId);\n walkNav(area.navigation);\n }\n apps.set(appName, navIds);\n }\n\n // ── Dashboards: widget ids + header action urls ──\n const dashboards = new Map<string, { widgets: Set<string>; actions: Set<string> }>();\n for (const dash of asArray(stack.dashboards)) {\n const dashName = strName(dash.name);\n if (!dashName) continue;\n const widgets = new Set<string>();\n for (const widget of asArray(dash.widgets)) {\n const id = strName(widget.id) ?? strName(widget.name);\n if (id) widgets.add(id);\n }\n const actions = new Set<string>();\n const headerActions = [\n ...asArray(isRec(dash.header) ? dash.header.actions : undefined),\n ...asArray(dash.actions),\n ];\n for (const action of headerActions) {\n const key = strName(action.actionUrl) ?? strName(action.url) ?? strName(action.name);\n if (key) actions.add(key);\n }\n dashboards.set(dashName, { widgets, actions });\n }\n\n return { objects, apps, dashboards, globalActions, actionOwners };\n}\n\n/** Quote a locale for the config path — BCP-47 tags carry `-`. */\nfunction localePath(bundleIndex: number, locale: string): string {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(locale)\n ? `translations[${bundleIndex}].${locale}`\n : `translations[${bundleIndex}][\"${locale}\"]`;\n}\n\n/**\n * Validate every reference a translation bundle makes against the metadata it\n * claims to translate. Returns findings (empty = clean).\n */\nexport function validateTranslationReferences(stack: AnyRec): TranslationRefFinding[] {\n const findings: TranslationRefFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const bundles = Array.isArray(stack.translations) ? stack.translations : [];\n if (bundles.length === 0) return findings;\n\n const universe = buildUniverse(stack);\n\n const orphan = (where: string, path: string, message: string, hint: string) => {\n findings.push({ severity: 'warning', rule: TRANSLATION_TARGET_UNKNOWN, where, path, message, hint });\n };\n\n for (let bi = 0; bi < bundles.length; bi++) {\n const bundle = bundles[bi];\n if (!isRec(bundle)) continue;\n\n for (const [locale, rawData] of Object.entries(bundle)) {\n if (!isRec(rawData)) continue;\n const base = localePath(bi, locale);\n const inLocale = `locale \"${locale}\"`;\n\n // ── objects.<name>.… ──────────────────────────────────────────────\n for (const [objectName, rawNode] of Object.entries(asRecord(rawData.objects))) {\n if (!isRec(rawNode)) continue;\n const objPath = `${base}.objects.${objectName}`;\n const facts = universe.objects.get(objectName);\n\n if (!facts) {\n // Not this stack's object. Platform objects are translated by their\n // owning package but a stack may override them, so a registered\n // platform name is legitimate — and unreadable from here, which is\n // why the whole subtree is skipped rather than half-checked.\n if (isPlatformProvidedObjectName(objectName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\"`,\n objPath,\n hasPlatformObjectPrefix(objectName)\n ? `Translations are keyed to \"${objectName}\", which carries a platform namespace ` +\n `prefix but is registered by no platform package, official plugin, or cloud ` +\n `runtime object — and this stack does not define it either. Nothing resolves ` +\n `these keys.` + suggest(objectName, universe.objects.keys())\n : `Translations are keyed to \"${objectName}\", which no object in this stack ` +\n `defines. The resolver looks up keys derived from the metadata, so this whole ` +\n `subtree is dead weight — every label it carries renders untranslated.` +\n suggest(objectName, universe.objects.keys()),\n `Rename the key to the object it was written for, drop it, or ignore this if the ` +\n `object is contributed by another installed package.` +\n (universe.objects.size > 0 ? ` Defined objects: ${listNames(universe.objects.keys())}.` : ''),\n );\n continue;\n }\n\n // fields.<name>[.options.<value>]\n for (const [fieldName, rawField] of Object.entries(asRecord(rawNode.fields))) {\n const fieldPath = `${objPath}.fields.${fieldName}`;\n const field = facts.fields.get(fieldName);\n if (!field) {\n if (IMPLICIT_FIELDS.has(fieldName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\" · field \"${fieldName}\"`,\n fieldPath,\n `Translations are keyed to field \"${fieldName}\", which object \"${objectName}\" ` +\n `does not declare. The label renders untranslated in this locale — and because ` +\n `every neighbouring field DOES resolve, the hole reads as a styling quirk ` +\n `rather than a missing translation.` + suggest(fieldName, facts.fields.keys()),\n `Point the key at a declared field, or drop it if the field was removed or renamed.` +\n (facts.fields.size > 0 ? ` Declared fields: ${listNames(facts.fields.keys())}.` : ''),\n );\n continue;\n }\n if (!isRec(rawField)) continue;\n checkOptionKeys(findings, {\n optionMap: rawField.options,\n field,\n fieldName,\n objectName,\n path: `${fieldPath}.options`,\n where: `${inLocale} · object \"${objectName}\" · field \"${fieldName}\"`,\n });\n }\n\n // _views.<name>\n for (const viewName of Object.keys(asRecord(rawNode._views))) {\n if (facts.views.has(viewName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\" · view \"${viewName}\"`,\n `${objPath}._views.${viewName}`,\n `Translations are keyed to view \"${viewName}\", which no view of object ` +\n `\"${objectName}\" declares. The view tab keeps its source-locale label.` +\n suggest(viewName, facts.views),\n `Match the key to the view's \\`name\\` (not its label), or drop it.` +\n (facts.views.size > 0 ? ` Declared views: ${listNames(facts.views)}.` : ''),\n );\n }\n\n // _sections.<key>\n for (const sectionName of Object.keys(asRecord(rawNode._sections))) {\n if (facts.sections.has(sectionName)) continue;\n orphan(\n `${inLocale} · object \"${objectName}\" · section \"${sectionName}\"`,\n `${objPath}._sections.${sectionName}`,\n `Translations are keyed to section \"${sectionName}\", which nothing on object ` +\n `\"${objectName}\" declares — no \\`fieldGroups[].key\\`, no named form-view section, ` +\n `no named \\`record:details\\` section. The section heading stays in the source locale.` +\n suggest(sectionName, facts.sections),\n `Sections are translatable only through a STABLE NAME: give the group/section a ` +\n `\\`key\\`/\\`name\\` and use it here, or drop the translation.` +\n (facts.sections.size > 0\n ? ` Declared sections: ${listNames(facts.sections)}.`\n : ` Object \"${objectName}\" declares no named section at all.`),\n );\n }\n\n // _actions.<name>[.params.<name>]\n for (const [actionName, rawAction] of Object.entries(asRecord(rawNode._actions))) {\n const actionPath = `${objPath}._actions.${actionName}`;\n const action = facts.actions.get(actionName);\n if (!action) {\n orphan(\n `${inLocale} · object \"${objectName}\" · action \"${actionName}\"`,\n actionPath,\n `Translations are keyed to action \"${actionName}\", which is defined by neither ` +\n `object \"${objectName}\"'s \\`actions\\` nor a \\`stack.actions\\` entry bound to it. ` +\n `The button keeps its source-locale label.` + suggest(actionName, facts.actions.keys()),\n `Match the key to a defined action name, move it under the object that owns the ` +\n `action, or drop it.` +\n (facts.actions.size > 0 ? ` Actions on this object: ${listNames(facts.actions.keys())}.` : ''),\n );\n continue;\n }\n checkActionParams(findings, {\n rawAction,\n action,\n path: actionPath,\n where: `${inLocale} · object \"${objectName}\" · action \"${actionName}\"`,\n subject: `action \"${actionName}\"`,\n });\n }\n }\n\n // ── globalActions.<name> ──────────────────────────────────────────\n for (const [actionName, rawAction] of Object.entries(asRecord(rawData.globalActions))) {\n const actionPath = `${base}.globalActions.${actionName}`;\n const action = universe.globalActions.get(actionName);\n if (!action) {\n const owner = universe.actionOwners.get(actionName);\n orphan(\n `${inLocale} · global action \"${actionName}\"`,\n actionPath,\n owner\n ? `Action \"${actionName}\" is bound to object \"${owner}\", so the resolver looks it ` +\n `up under \\`objects.${owner}._actions.${actionName}\\` — never under ` +\n `\\`globalActions\\`, which is only consulted for object-less actions. This key ` +\n `is never read.`\n : `Translations are keyed to global action \"${actionName}\", which no object-less ` +\n `action in this stack defines. The button keeps its source-locale label.` +\n suggest(actionName, universe.globalActions.keys()),\n owner\n ? `Move these keys under \\`objects.${owner}._actions.${actionName}\\`.`\n : `Match the key to an object-less action's name, or drop it.` +\n (universe.globalActions.size > 0\n ? ` Object-less actions: ${listNames(universe.globalActions.keys())}.`\n : ''),\n );\n continue;\n }\n checkActionParams(findings, {\n rawAction,\n action,\n path: actionPath,\n where: `${inLocale} · global action \"${actionName}\"`,\n subject: `action \"${actionName}\"`,\n });\n }\n\n // ── apps.<name>[.navigation.<id>] ─────────────────────────────────\n for (const [appName, rawApp] of Object.entries(asRecord(rawData.apps))) {\n const appPath = `${base}.apps.${appName}`;\n const navIds = universe.apps.get(appName);\n if (!navIds) {\n orphan(\n `${inLocale} · app \"${appName}\"`,\n appPath,\n `Translations are keyed to app \"${appName}\", which this stack does not define. ` +\n `The app launcher shows the source-locale label.` + suggest(appName, universe.apps.keys()),\n `Match the key to an app's \\`name\\`, or drop it.` +\n (universe.apps.size > 0 ? ` Defined apps: ${listNames(universe.apps.keys())}.` : ''),\n );\n continue;\n }\n if (!isRec(rawApp)) continue;\n for (const navId of Object.keys(asRecord(rawApp.navigation))) {\n if (navIds.has(navId)) continue;\n orphan(\n `${inLocale} · app \"${appName}\" · navigation \"${navId}\"`,\n `${appPath}.navigation.${navId}`,\n `Translations are keyed to navigation item \"${navId}\", which app \"${appName}\" ` +\n `does not declare. The menu entry keeps its source-locale label.` +\n suggest(navId, navIds),\n `Match the key to the navigation item's \\`id\\`, or drop it.` +\n (navIds.size > 0 ? ` Declared navigation ids: ${listNames(navIds)}.` : ''),\n );\n }\n }\n\n // ── dashboards.<name>[.widgets.<id> | .actions.<url>] ─────────────\n for (const [dashName, rawDash] of Object.entries(asRecord(rawData.dashboards))) {\n const dashPath = `${base}.dashboards.${dashName}`;\n const dash = universe.dashboards.get(dashName);\n if (!dash) {\n orphan(\n `${inLocale} · dashboard \"${dashName}\"`,\n dashPath,\n `Translations are keyed to dashboard \"${dashName}\", which this stack does not ` +\n `define. The dashboard title stays in the source locale.` +\n suggest(dashName, universe.dashboards.keys()),\n `Match the key to a dashboard's \\`name\\`, or drop it.` +\n (universe.dashboards.size > 0 ? ` Defined dashboards: ${listNames(universe.dashboards.keys())}.` : ''),\n );\n continue;\n }\n if (!isRec(rawDash)) continue;\n for (const widgetId of Object.keys(asRecord(rawDash.widgets))) {\n if (dash.widgets.has(widgetId)) continue;\n orphan(\n `${inLocale} · dashboard \"${dashName}\" · widget \"${widgetId}\"`,\n `${dashPath}.widgets.${widgetId}`,\n `Translations are keyed to widget \"${widgetId}\", which dashboard \"${dashName}\" ` +\n `does not declare. The widget title stays in the source locale.` +\n suggest(widgetId, dash.widgets),\n `Match the key to the widget's \\`id\\`, or drop it.` +\n (dash.widgets.size > 0 ? ` Declared widget ids: ${listNames(dash.widgets)}.` : ''),\n );\n }\n for (const actionKey of Object.keys(asRecord(rawDash.actions))) {\n if (dash.actions.has(actionKey)) continue;\n orphan(\n `${inLocale} · dashboard \"${dashName}\" · action \"${actionKey}\"`,\n `${dashPath}.actions.${actionKey}`,\n `Translations are keyed to header action \"${actionKey}\", which dashboard ` +\n `\"${dashName}\" does not declare. The button keeps its source-locale label.` +\n suggest(actionKey, dash.actions),\n `Header-action translations are keyed by the action's \\`actionUrl\\`, not its label.` +\n (dash.actions.size > 0 ? ` Declared header actions: ${listNames(dash.actions)}.` : ''),\n );\n }\n }\n }\n }\n\n return findings;\n}\n\n/** `v` as a record of sub-nodes, or an empty record when absent/malformed. */\nfunction asRecord(v: unknown): Record<string, unknown> {\n return isRec(v) ? v : {};\n}\n\n/**\n * Option translations are keyed by the option's STORED VALUE. Keying them by\n * the display label — or by a near-miss of the value — is the second half of\n * issue #3583's option-key class: the map parses, ships, and never resolves.\n */\nfunction checkOptionKeys(\n findings: TranslationRefFinding[],\n ctx: {\n optionMap: unknown;\n field: AnyRec;\n fieldName: string;\n objectName: string;\n path: string;\n where: string;\n },\n): void {\n const optionKeys = Object.keys(asRecord(ctx.optionMap));\n if (optionKeys.length === 0) return;\n\n const declared = readOptions(ctx.field);\n if (!declared) {\n findings.push({\n severity: 'warning',\n rule: TRANSLATION_OPTION_KEY_UNKNOWN,\n where: ctx.where,\n path: ctx.path,\n message:\n `Option translations are keyed under field \"${ctx.fieldName}\" of object ` +\n `\"${ctx.objectName}\", which declares no \\`options\\` at all (field type ` +\n `\"${strName(ctx.field.type) ?? 'unknown'}\"). Nothing reads this map.`,\n hint:\n `Declare the options on the field, move the translations to the field that owns ` +\n `them, or drop them.`,\n });\n return;\n }\n\n for (const key of optionKeys) {\n if (declared.values.has(key)) continue;\n const byLabel = declared.byLabel.get(key.toLowerCase());\n findings.push({\n severity: 'warning',\n rule: TRANSLATION_OPTION_KEY_UNKNOWN,\n where: ctx.where,\n path: `${ctx.path}.${key}`,\n message: byLabel\n ? `Option translation is keyed by the DISPLAY LABEL \"${key}\" instead of the stored ` +\n `value \"${byLabel}\". The resolver looks the option up by value, so this entry is ` +\n `never found and the option renders with its source-locale label.`\n : `Option translation is keyed by \"${key}\", which is not one of the values declared ` +\n `by field \"${ctx.objectName}.${ctx.fieldName}\". The option renders untranslated.` +\n suggest(key, declared.values),\n hint: byLabel\n ? `Rename the key to \"${byLabel}\".`\n : `Option keys are the stored \\`value\\`, not the label and not a variant spelling ` +\n `(\\`direct_mail\\`, not \\`direct-mail\\`). Declared values: ${listNames(declared.values)}.`,\n });\n }\n}\n\n/** Action-parameter translations are keyed by the param's `name`. */\nfunction checkActionParams(\n findings: TranslationRefFinding[],\n ctx: { rawAction: unknown; action: AnyRec; path: string; where: string; subject: string },\n): void {\n const rawParams = Object.keys(asRecord(isRec(ctx.rawAction) ? ctx.rawAction.params : undefined));\n if (rawParams.length === 0) return;\n\n const declared = new Set<string>();\n for (const param of asArray(ctx.action.params)) {\n const name = strName(param.name) ?? strName(param.field);\n if (name) declared.add(name);\n }\n\n for (const paramName of rawParams) {\n if (declared.has(paramName)) continue;\n findings.push({\n severity: 'warning',\n rule: TRANSLATION_TARGET_UNKNOWN,\n where: `${ctx.where} · param \"${paramName}\"`,\n path: `${ctx.path}.params.${paramName}`,\n message:\n `Translations are keyed to parameter \"${paramName}\", which ${ctx.subject} does not ` +\n `declare. The parameter's label and help text render untranslated in the action dialog.` +\n suggest(paramName, declared),\n hint:\n `Match the key to a declared param \\`name\\`, or drop it.` +\n (declared.size > 0 ? ` Declared params: ${listNames(declared)}.` : ''),\n });\n }\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0064 §3] Skill ↔ agent surface affinity (issue #3820).\n *\n * An agent binds a product surface (`'ask'` | `'build'`, ADR-0063 §1) and a\n * skill declares which surface it belongs to (`'ask'` | `'build'` | `'both'`,\n * ADR-0063 §3). A skill may only attach to an agent whose surface it matches —\n * `'both'` attaches to either. The runtime treats a violation as a FAST LOAD\n * ERROR: `resolveActiveSkills()` throws on the first incompatible binding, so\n * an agent shipping one mismatched skill reference fails at **chat time** with\n * a 500 — after parse, after validate, after deploy.\n *\n * Both sides of the check are declared in the same stack, so the contradiction\n * is statically provable and this rule carries severity **error** with zero\n * false positives by construction. Both sides default to `'ask'` when the\n * `surface` field is absent (mirroring the runtime's defaults), so the rule is\n * safe on the raw/normalized config the `lint` path carries as well as the\n * schema-parsed stack.\n *\n * Scope note: this rule deliberately does NOT check that `agent.skills[]`\n * names resolve at all. Kernel skills (`schema_reader`, the `ask`/`build`\n * bundles) are runtime-registered and statically invisible, and whether\n * app-stack tool/skill namespaces get a platform-name registry is an open\n * decision (#3820 D0/D2) — resolving names against `stack.skills` alone would\n * flag every kernel-skill reference. An unresolved name is therefore skipped\n * here; only a reference that resolves in-stack AND contradicts the affinity\n * contract is reported.\n */\n\nexport const AI_SKILL_SURFACE_MISMATCH = 'ai-skill-surface-mismatch';\n\nexport type AiSurfaceAffinitySeverity = 'error' | 'warning';\n\nexport interface AiSurfaceAffinityFinding {\n /** Always `error` — the runtime throws on this binding at chat time. */\n severity: AiSurfaceAffinitySeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `agent \"sales_copilot\" · skills`. */\n where: string;\n /** Config path, e.g. `agents[0].skills[2]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => !!x && typeof x === 'object');\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/** The runtime defaults an absent `surface` to `'ask'` on both sides. */\nfunction surfaceOf(v: unknown): string {\n return typeof v === 'string' && v.length > 0 ? v : 'ask';\n}\n\n/**\n * Validate every in-stack agent→skill binding against the ADR-0064 §3 surface\n * affinity contract. Returns findings (empty = clean).\n */\nexport function validateAiSurfaceAffinity(stack: AnyRec): AiSurfaceAffinityFinding[] {\n const findings: AiSurfaceAffinityFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const skillsByName = new Map<string, AnyRec>();\n for (const skill of asArray(stack.skills)) {\n const n = strName(skill.name);\n if (n) skillsByName.set(n, skill);\n }\n\n const agents = asArray(stack.agents);\n for (let ai = 0; ai < agents.length; ai++) {\n const agent = agents[ai];\n const agentName = strName(agent.name) ?? `#${ai}`;\n const agentSurface = surfaceOf(agent.surface);\n const skillRefs = Array.isArray(agent.skills) ? agent.skills : [];\n\n for (let si = 0; si < skillRefs.length; si++) {\n const ref = strName(skillRefs[si]);\n if (!ref) continue;\n const skill = skillsByName.get(ref);\n // Unresolved in-stack → runtime-registered kernel skill or another\n // package's — out of this rule's scope (see header; #3820 D0/D2).\n if (!skill) continue;\n\n const skillSurface = surfaceOf(skill.surface);\n if (skillSurface === 'both' || skillSurface === agentSurface) continue;\n\n findings.push({\n severity: 'error',\n rule: AI_SKILL_SURFACE_MISMATCH,\n where: `agent \"${agentName}\" · skills`,\n path: `agents[${ai}].skills[${si}]`,\n message:\n `Agent \"${agentName}\" (surface: '${agentSurface}') references skill \"${ref}\" ` +\n `(surface: '${skillSurface}') — incompatible affinity (ADR-0064 §3). The runtime ` +\n `refuses this binding with a load error, so chatting with this agent fails at ` +\n `request time even though the stack parses and validates cleanly.`,\n hint:\n `A skill may only attach to an agent whose surface it matches. Move \"${ref}\" to a ` +\n `'${skillSurface}'-surface agent, change its \\`surface\\` to '${agentSurface}', or — ` +\n `only if it is a genuinely shared, read-only capability — declare \\`surface: 'both'\\`.`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0109 / issue #3820 R7] `skill.tools[]` reference integrity — the tool\n * branch of the AI reference rules.\n *\n * Under ADR-0109's authoring model the DEFAULT third-party path declares no\n * tool records at all: a skill names either a platform-registered tool or an\n * auto-materialised action tool. A `skill.tools[]` entry therefore resolves\n * against, in order:\n *\n * 1. the stack's own `stack.tools[]` names (the optional refinement layer);\n * 2. `PLATFORM_PROVIDED_TOOL_NAMES` — the curated registry of tools the\n * cloud AI runtime registers at boot (spec owns the list; the owning\n * cloud packages carry the conformance tests);\n * 3. the materialised `action_<name>` family — one tool per declarative\n * action (`stack.actions` ∪ every object's `actions`), the mechanism the\n * built-in `actions_executor` subscribes to with `action_*`.\n *\n * Trailing-wildcard entries (`action_*`, `foo_*`) resolve when ANY member of\n * that universe matches the prefix.\n *\n * Severity is **warning** (ADR-0078 advisory-first ratchet), not error,\n * because the universe has a known blind spot: a runtime plugin outside the\n * registry can legitimately register tools no static analysis can see, and\n * the runtime deliberately tolerates unresolved names (skills may be authored\n * before their tools exist — `skill-registry.ts`). What the warning buys: the\n * HotCRM failure — 10 fictional tools across 6 skills, every one shipping\n * through `validate`/`lint` clean and surfacing as a copilot that claims\n * abilities it does not have — now surfaces at authoring time. On that same\n * corpus the resolution ladder above yields exactly 10 findings and 0 false\n * positives (6 references resolve via the registry).\n */\n\nimport { PLATFORM_PROVIDED_TOOL_NAMES, PLATFORM_TOOL_FAMILY_PREFIXES } from '@objectstack/spec/system';\n\nexport const AI_SKILL_TOOL_UNRESOLVED = 'ai-skill-tool-unresolved';\n\nexport type AiToolRefSeverity = 'error' | 'warning';\n\nexport interface AiToolRefFinding {\n /** Always `warning` — see the header for why this rule starts advisory. */\n severity: AiToolRefSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `skill \"revenue_forecasting\" · tools`. */\n where: string;\n /** Config path, e.g. `skills[3].tools[1]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => !!x && typeof x === 'object');\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\nfunction distance(a: string, b: string): number {\n const m = a.length;\n const n = b.length;\n if (m === 0) return n;\n if (n === 0) return m;\n let prev = Array.from({ length: n + 1 }, (_, j) => j);\n for (let i = 1; i <= m; i++) {\n const curr = [i, ...new Array<number>(n).fill(0)];\n for (let j = 1; j <= n; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n curr[j] = Math.min(curr[j - 1] + 1, prev[j] + 1, prev[j - 1] + cost);\n }\n prev = curr;\n }\n return prev[n];\n}\n\nfunction suggest(target: string, known: Set<string>): string {\n // The high-frequency near-miss first: naming the raw ACTION where the\n // materialised TOOL (`action_<name>`) is meant. Edit distance cannot catch\n // it (the prefix alone is 7 edits), and it is exactly the mistake the\n // ADR-0109 default path invites from authors who know their action names.\n for (const prefix of PLATFORM_TOOL_FAMILY_PREFIXES) {\n if (known.has(`${prefix}${target}`)) return ` Did you mean \"${prefix}${target}\"?`;\n }\n\n let best: string | undefined;\n let bestScore = Infinity;\n for (const candidate of known) {\n const d = distance(target, candidate);\n if (d < bestScore) {\n bestScore = d;\n best = candidate;\n }\n }\n const limit = Math.max(2, Math.floor(target.length / 3));\n return best && bestScore <= limit ? ` Did you mean \"${best}\"?` : '';\n}\n\n/**\n * Action types with a headless invocation path. `url`/`modal`/`form` are\n * Studio-only UI types — the runtime never materialises a tool for them\n * because there is nothing to call without the UI collecting input first.\n */\nconst HEADLESS_ACTION_TYPES = new Set(['script', 'api', 'flow']);\n\n/**\n * Would the runtime materialise an `action_<name>` tool for this action?\n *\n * Mirrors the STATIC half of the runtime's `actionSkipReason` (ADR-0011\n * opt-in + the headless-path checks). The runtime additionally checks\n * service wiring (is the automation service up?), which is not knowable at\n * authoring time and is deliberately not modelled here — this predicate is\n * about \"did the author wire it\", not \"is the server configured\".\n *\n * Getting this wrong in the permissive direction is worse than having no\n * rule: an author who names `action_foo` for a `type:'modal'` action would\n * be told the reference resolves, and would ship a skill whose instructions\n * promise a capability the agent can never call — the exact failure this\n * rule exists to catch.\n */\nfunction materialisesAsTool(action: AnyRec): boolean {\n const ai = action.ai;\n if (!ai || typeof ai !== 'object') return false;\n const aiRec = ai as AnyRec;\n // ADR-0011 — opt-in, and `description` is the LLM-facing contract the\n // spec requires whenever `exposed` is true.\n if (aiRec.exposed !== true) return false;\n if (!strName(aiRec.description)) return false;\n\n const type = strName(action.type);\n if (!type || !HEADLESS_ACTION_TYPES.has(type)) return false;\n // `script` can carry either a named handler or an inline body; `api` and\n // `flow` are dispatched by target.\n if (type === 'script') return Boolean(action.target || action.body);\n return Boolean(action.target);\n}\n\n/**\n * The full set of tool names resolvable from this stack: declared tool\n * records ∪ the platform registry ∪ the materialised action family.\n */\nfunction collectToolUniverse(stack: AnyRec): Set<string> {\n const universe = new Set<string>(PLATFORM_PROVIDED_TOOL_NAMES);\n\n for (const tool of asArray(stack.tools)) {\n const n = strName(tool.name);\n if (n) universe.add(n);\n }\n\n const addActionFamily = (actions: unknown) => {\n for (const action of asArray(actions)) {\n const n = strName(action.name);\n if (n && materialisesAsTool(action)) universe.add(`action_${n}`);\n }\n };\n addActionFamily(stack.actions);\n for (const obj of asArray(stack.objects)) {\n addActionFamily(obj.actions);\n }\n\n return universe;\n}\n\n/**\n * Actions that exist but are NOT AI-exposed, for the near-miss hint: naming\n * `action_foo` when `foo` exists but never materialises is a different\n * mistake from naming something fictional, and deserves a different fix.\n */\nfunction collectUnexposedActionNames(stack: AnyRec): Set<string> {\n const names = new Set<string>();\n const scan = (actions: unknown) => {\n for (const action of asArray(actions)) {\n const n = strName(action.name);\n if (n && !materialisesAsTool(action)) names.add(n);\n }\n };\n scan(stack.actions);\n for (const obj of asArray(stack.objects)) scan(obj.actions);\n return names;\n}\n\n/**\n * Validate every `skill.tools[]` reference in a stack. Returns findings\n * (empty = clean).\n */\nexport function validateAiToolReferences(stack: AnyRec): AiToolRefFinding[] {\n const findings: AiToolRefFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const universe = collectToolUniverse(stack);\n const unexposedActions = collectUnexposedActionNames(stack);\n\n const resolves = (ref: string): boolean => {\n if (ref.endsWith('*')) {\n const prefix = ref.slice(0, -1);\n for (const name of universe) {\n if (name.startsWith(prefix)) return true;\n }\n return false;\n }\n return universe.has(ref);\n };\n\n const skills = asArray(stack.skills);\n for (let si = 0; si < skills.length; si++) {\n const skill = skills[si];\n const skillName = strName(skill.name) ?? `#${si}`;\n const refs = Array.isArray(skill.tools) ? skill.tools : [];\n\n for (let ti = 0; ti < refs.length; ti++) {\n const ref = strName(refs[ti]);\n if (!ref || resolves(ref)) continue;\n\n const isPattern = ref.endsWith('*');\n // The distinct, high-frequency case: the action EXISTS but never\n // materialises. \"Fictional name\" and \"real action that isn't exposed\"\n // need different fixes, so they get different messages.\n const unexposed =\n !isPattern && ref.startsWith('action_') && unexposedActions.has(ref.slice('action_'.length))\n ? ref.slice('action_'.length)\n : undefined;\n\n findings.push({\n severity: 'warning',\n rule: AI_SKILL_TOOL_UNRESOLVED,\n where: `skill \"${skillName}\" · tools`,\n path: `skills[${si}].tools[${ti}]`,\n message: isPattern\n ? `Skill \"${skillName}\" subscribes to tool family \"${ref}\", which matches nothing this ` +\n `stack can resolve (no declared tool, no platform tool, and no AI-exposed declarative ` +\n `action materialises into it). The subscription contributes zero tools at runtime.`\n : unexposed\n ? `Skill \"${skillName}\" references tool \"${ref}\", but the action \"${unexposed}\" does ` +\n `not become an AI tool: the runtime materialises \\`action_<name>\\` only for an ` +\n `action that opts in with \\`ai.exposed: true\\` + \\`ai.description\\` (ADR-0011) AND ` +\n `has a headless path (type \\`script\\`/\\`api\\`/\\`flow\\` with a target or body — ` +\n `\\`url\\`/\\`modal\\`/\\`form\\` are UI-only). The reference is dropped at runtime, so ` +\n `the skill promises a capability the agent cannot call.`\n : `Skill \"${skillName}\" references tool \"${ref}\", which resolves to nothing this stack ` +\n `can see: not a \\`stack.tools\\` record, not a platform-registered tool, and not a ` +\n `materialised action tool (\\`action_<name>\\`). The runtime silently drops the ` +\n `reference, so the skill's instructions claim a capability the agent does not have — ` +\n `the assistant will improvise or fail when asked to use it.` +\n suggest(ref, universe),\n hint: unexposed\n ? `Either opt \"${unexposed}\" in — set \\`ai: { exposed: true, description: '…' }\\` (≥40 ` +\n `chars, LLM-facing) and give it a headless type — or drop the reference and have the ` +\n `skill's instructions recommend the UI action instead. A \\`modal\\`/\\`form\\`/\\`url\\` ` +\n `action stays human-driven by design; that is a legitimate answer, not a gap.`\n : `Back \"${ref}\" with a real executable: declare a declarative action (or flow), opt it ` +\n `in with \\`ai.exposed: true\\` + \\`ai.description\\`, and reference its materialised ` +\n `tool (\\`action_<name>\\` — the ADR-0109 default path, no tool record needed); or ` +\n `reference a platform tool by its registered name; or remove the reference and the ` +\n `instructions that mention it. Ignore this only if a runtime plugin outside the ` +\n `platform registry provides \"${ref}\". Family prefixes materialised by the runtime: ` +\n `${PLATFORM_TOOL_FAMILY_PREFIXES.join(', ')}.`,\n });\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * [ADR-0063 §2] `stack.agents` is a platform-internal slot (issue #3820).\n *\n * ADR-0063 §2 withdrew tenant/app-package custom agents: the kernel ships\n * exactly two agents (`ask`, `build`), the surface the user is in binds one,\n * and third parties extend the platform by authoring **skills**, never\n * `*.agent.ts`. The `agent` metadata type carries the decision\n * (`allowRuntimeCreate: false, allowOrgOverride: false`), and the runtime\n * enforces it on both paths — `listAgents()` filters non-platform records out\n * of the catalog, and `loadAgent()` refuses them outright (cloud#904), so a\n * stack-authored agent 404s on chat and cannot be pinned via\n * `app.defaultAgent`.\n *\n * What was missing is the AUTHORING-time signal. `defineStack` still accepts\n * an `agents` array, so an app package could declare agents that parse,\n * validate, and build into the artifact — and then do nothing at runtime.\n * HotCRM shipped two of them for months. That is the ADR-0078 shape this rule\n * closes: loud at the producer, tolerant at the consumer (Prime Directive\n * #12).\n *\n * Severity is **warning**, not error, for one reason: the platform's own\n * packages legitimately author agent records, and this rule cannot tell a\n * platform package from an app package by reading the stack alone. A warning\n * that names the runtime consequence is honest for both readers; the runtime\n * is what actually gates. Deliberately NOT a Zod refine — an existing stack\n * must keep parsing (ADR-0078 non-goal #1).\n */\n\nexport const AGENT_AUTHORING_WITHDRAWN = 'agent-authoring-withdrawn';\n\nexport type AiAgentAuthoringSeverity = 'error' | 'warning';\n\nexport interface AiAgentAuthoringFinding {\n /** Always `warning` — the runtime is the gate; this is the authoring-time signal. */\n severity: AiAgentAuthoringSeverity;\n /** Diagnostic rule id. */\n rule: string;\n /** Human-readable location, e.g. `agent \"sales_copilot\"`. */\n where: string;\n /** Config path, e.g. `agents[0]`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => !!x && typeof x === 'object');\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\nfunction strName(v: unknown): string | undefined {\n return typeof v === 'string' && v.length > 0 ? v : undefined;\n}\n\n/**\n * The two platform agent ids. A stack that re-declares one of these is doing\n * something different from inventing a custom persona (it is shadowing a\n * platform record), so it gets its own wording.\n */\nconst PLATFORM_AGENT_NAMES = new Set(['ask', 'build', 'data_chat', 'metadata_assistant']);\n\n/**\n * Flag every agent declared in a stack. Returns findings (empty = clean,\n * which is what every app package should be).\n */\nexport function validateAiAgentAuthoring(stack: AnyRec): AiAgentAuthoringFinding[] {\n const findings: AiAgentAuthoringFinding[] = [];\n if (!stack || typeof stack !== 'object') return findings;\n\n const agents = asArray(stack.agents);\n for (let ai = 0; ai < agents.length; ai++) {\n const agent = agents[ai];\n const name = strName(agent.name) ?? `#${ai}`;\n const isPlatformName = PLATFORM_AGENT_NAMES.has(name);\n const skillCount = Array.isArray(agent.skills) ? agent.skills.length : 0;\n\n findings.push({\n severity: 'warning',\n rule: AGENT_AUTHORING_WITHDRAWN,\n where: `agent \"${name}\"`,\n path: `agents[${ai}]`,\n message: isPlatformName\n ? `This stack declares an agent named \"${name}\", which is a PLATFORM agent id. The ` +\n `runtime serves its own record for that name and ignores this one — the declaration ` +\n `has no effect and will drift from the platform's definition.`\n : `This stack declares the agent \"${name}\", but tenant/app-package agents were withdrawn ` +\n `(ADR-0063 §2): the kernel ships exactly two agents (\\`ask\\`, \\`build\\`) and the surface ` +\n `the user is in binds one. The runtime filters this record out of the agent catalog and ` +\n `refuses to load it, so it never runs — it parses, validates, and ships as inert ` +\n `metadata.`,\n hint: isPlatformName\n ? `Remove the declaration; the platform owns \"${name}\". Extend it with skills instead.`\n : `Delete the agent and express its capability as skills. Everything an agent carried ` +\n `that a skill does not is persona text: move the useful parts of \\`instructions\\` into ` +\n `the skills' own instructions.` +\n (skillCount > 0\n ? ` The ${skillCount} skill${skillCount === 1 ? '' : 's'} this agent references ` +\n `already carry the capability — they attach to the platform agent by \\`surface\\` ` +\n `affinity, so nothing is lost by dropping the persona.`\n : ``),\n });\n }\n\n return findings;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Author-time write-set check for L2 (`language:'js'`) hook bodies (#4271).\n//\n// An L2 body that writes a field the target object never declares —\n// `ctx.input.amout = 0`, `ctx.api.object('deal').update({ stag: 'won' })` —\n// runs clean in the QuickJS sandbox and reaches the driver UNFILTERED:\n// `applyMutationsToInput` (runtime/src/sandbox/body-runner.ts) is a plain\n// `Object.assign`, and `validateRecord` walks declared fields on insert and\n// `continue`s past a key with no field def on update. What happens after that\n// is DRIVER-DEPENDENT, and neither half is acceptable:\n//\n// • SQL — the stray column enters the knex statement and the WHOLE write\n// fails with a driver-level error (`table deal has no column named\n// stagee`). The write is lost, and the error surfaces far from the\n// authoring mistake that caused it.\n// • Schemaless (memory, MongoDB) — the driver spreads the payload, so the\n// stray key IS persisted: an undeclared column nothing downstream reads.\n//\n// Either way the mistake is invisible where it is MADE — the #4001 family, if\n// not literally its silent-no-op shape. Both runtime outcomes are pinned by\n// `runtime/src/sandbox/undeclared-field-write-driver-split.integration.test.ts`\n// so this rule's wording cannot drift from what the runtime does; the same\n// split is documented in `content/docs/automation/hook-bodies.mdx`.\n//\n// The read side (`hook.condition`, ADR-0032) and the capability surface are\n// statically checked; until this rule, the write side was the one blind face\n// (the gap `hook-body.zod.ts` used to carry as \"accepted\").\n//\n// Scope — the literal write patterns in {@link HOOK_BODY_WRITE_PATTERNS}, and\n// nothing else. The body is PARSED (TypeScript parser, never executed, never\n// type-checked); each declared pattern is reconciliation-tested against the\n// extractor, so a pattern cannot be declared-but-unverified (#3528's death).\n// Everything statically unknowable is skipped SILENTLY, asymmetrically\n// favouring missed findings over false ones — a false positive kills an\n// advisory lint, a miss just leaves the gap open a little longer:\n//\n// • computed keys (`ctx.input[k] = …`), spreads, non-literal payloads;\n// • dynamic object names (`ctx.api.object(name)`);\n// • `object:'*'` wildcard hooks' `ctx.input` writes (no single target);\n// • multi-target hooks where the field exists on SOME target — the body may\n// legitimately branch per object (`if (ctx.object === '…')`), so only a\n// field missing on EVERY named target is flagged;\n// • targets declared by another package (not in this stack);\n// • one-level aliasing (`const doc = ctx.input; doc.x = 1`) — known miss,\n// deliberately: v1 does no data-flow analysis.\n//\n// Severity: always `warning` (advisory, never gates). Same posture as\n// `lintUnknownAuthoringKeys` (#3786) — ratchet only with field data.\n//\n// Wired via REFERENCE_INTEGRITY_RULES (it resolves field NAMES written in\n// metadata against what the stack declares — the suite's exact membership\n// test), so it runs on `os validate`, `os lint` and `os compile` at once.\n// Deliberately NOT in the `defineStack` runtime path: the TypeScript parser\n// has no place on kernel boot (see the lazy-load contract below).\n//\n// ACTION bodies run through the same `HookBodySchema` and the same sandbox, so\n// they get the same treatment from a sibling rule — `validate-action-body-\n// writes.ts`, which reuses this module's extractor, ledger, field index and\n// implicit-field set. It carries only the pattern subset that survives the\n// context change (an action's `ctx.input` is its params bag, not a record);\n// the reasoning is declared as data there, not restated here.\n\nimport { createRequire } from 'node:module';\nimport type ts from 'typescript';\nimport { findClosestMatches, formatSuggestion } from '@objectstack/spec/shared';\n\nimport { SYSTEM_FIELDS } from './system-fields.js';\n\n// The TypeScript compiler must NOT be imported at module top level: it is\n// ~9 MB of CJS, and @objectstack/lint sits on the kernel boot path — while\n// this gate only parses when a hook actually carries an L2 JS body. Same\n// lazy-load contract as validate-react-page-props.ts (which see for the\n// history, including production images pruning the package). Guarded by\n// lazy-deps.test.ts.\n//\n// `node:module` is a Node builtin, untouched by esbuild/tsup, so the static\n// `createRequire` import survives bundling; the `createRequire(...)` call is\n// deferred because `import.meta.url` is rewritten to an empty stub in the CJS\n// build (same pattern as driver-sqlite-wasm's knex-wasm-dialect).\nlet cachedTs: typeof ts | null = null;\nfunction loadTypeScript(): typeof ts {\n if (cachedTs) return cachedTs;\n const anchor =\n typeof import.meta !== 'undefined' && import.meta.url\n ? import.meta.url\n : typeof __filename !== 'undefined'\n ? __filename\n : process.cwd() + '/';\n try {\n cachedTs = createRequire(anchor)('typescript') as typeof ts;\n } catch (err) {\n throw new Error(\n `@objectstack/lint: checking an L2 (language:'js') hook body requires the \"typescript\" package, which could not ` +\n `be loaded (${err instanceof Error ? err.message : String(err)}). It is a declared dependency of ` +\n `@objectstack/lint — if this deployment prunes packages, keep \"typescript\" in the image; it is only loaded ` +\n `when a hook with a JS body is validated.`,\n );\n }\n return cachedTs;\n}\n\nexport type HookBodyWriteSeverity = 'warning';\n\nexport interface HookBodyWriteFinding {\n /** v1 is advisory-only by contract — the type says so. */\n severity: HookBodyWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `hook \"normalize_lead\" › body`. */\n where: string;\n /** Config path, e.g. `hooks[0].body.source`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const HOOK_BODY_WRITE_UNKNOWN_FIELD = 'hook-body-write-unknown-field';\n\n// ─── The write-pattern ledger ───────────────────────────────────────────────\n//\n// Every syntactic write shape the extractor recognizes, declared as data. The\n// reconciliation test (validate-hook-body-writes.test.ts) runs the extractor\n// over each entry's example and asserts it yields EXACTLY the declared writes,\n// each tagged with this entry's id — so \"the docs say this pattern is covered\n// but nothing extracts it\" cannot happen (#3528), and the answer to \"which\n// writes does the lint see?\" is this list, not the extractor's code.\n\n/** One syntactic write shape the extractor recognizes. */\nexport interface HookBodyWritePattern {\n /** Stable pattern id, carried on every extracted write. */\n readonly id: string;\n /** Author-facing syntax summary (for docs/diagnostics, not matching). */\n readonly syntax: string;\n /** Reconciliation fixture: extracting `source` must yield exactly `writes`. */\n readonly example: {\n readonly source: string;\n readonly writes: ReadonlyArray<{ field: string; object?: string }>;\n };\n}\n\nexport const HOOK_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[] = [\n {\n id: 'input-property-assign',\n syntax: \"ctx.input.<field> = … | ctx.input['<field>'] ⟨op⟩= …\",\n example: {\n // Compound (`+=`) and logical (`??=`) assignment operators write their\n // LHS exactly like `=` does — the example pins the whole operator range.\n source: \"ctx.input.total = 0; ctx.input['status'] ??= 'open'; ctx.input.retries += 1;\",\n writes: [{ field: 'total' }, { field: 'status' }, { field: 'retries' }],\n },\n },\n {\n id: 'input-object-assign',\n syntax: 'Object.assign(ctx.input, { <field>: … })',\n example: {\n source: \"Object.assign(ctx.input, { total: 5, 'status': 'open', discount });\",\n writes: [{ field: 'total' }, { field: 'status' }, { field: 'discount' }],\n },\n },\n {\n // ACTION-only shape (the hook sandbox context has no `ctx.record` at all).\n // Declared here because this ledger is the extractor's shape inventory, not\n // any one rule's; every consumer declares which shapes it consumes.\n id: 'record-property-assign',\n syntax: \"ctx.record.<field> = … | ctx.record['<field>'] ⟨op⟩= …\",\n example: {\n source: \"ctx.record.stage = 'won'; ctx.record['amount'] += 1;\",\n writes: [{ field: 'stage' }, { field: 'amount' }],\n },\n },\n {\n id: 'api-crud-literal',\n syntax:\n \"ctx.api.object('<object>').insert({…}) | .create({…}) | .update({…}) | .updateById(id, {…})\",\n example: {\n // Real ObjectRepository signatures: the record payload is argument 0 for\n // insert/create/update and argument 1 for updateById. (`update(data)` —\n // NOT `update(id, data)`; the id travels inside the payload/options.)\n source:\n \"await ctx.api.object('audit_log').insert({ event: 'won' }); \" +\n \"await ctx.api.object('crm_deal').updateById(id, { stage: 'won' });\",\n writes: [\n { field: 'event', object: 'audit_log' },\n { field: 'stage', object: 'crm_deal' },\n ],\n },\n },\n];\n\n/** A ledger pattern a given rule does NOT consume, and why. */\nexport interface BodyWritePatternExclusion {\n /** The {@link HOOK_BODY_WRITE_PATTERNS} entry id being excluded. */\n readonly id: string;\n /** Why the shape does not mean the same thing on this rule's surface. */\n readonly reason: string;\n}\n\n/**\n * The ledger shapes THIS rule consumes.\n *\n * Declared rather than implied: before the ledger carried a shape the hook\n * surface does not have, every write with no `object` was necessarily a\n * `ctx.input` write, and the rule could branch on that alone. It no longer can\n * — a `record-property-assign` write also carries no object, and would have\n * been reported as \"the hook writes 'stage' to its input\", which is false.\n * Each consumer declaring its own subset is what stops the next added shape\n * from silently landing in a branch that was never written for it.\n */\nexport const HOOK_BODY_WRITE_PATTERN_IDS: readonly string[] = [\n 'input-property-assign',\n 'input-object-assign',\n 'api-crud-literal',\n];\n\n/** Ledger shapes this rule leaves alone, each with its reason. */\nexport const HOOK_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[] = [\n {\n id: 'record-property-assign',\n reason:\n 'a hook sandbox context has no `ctx.record` at all — `buildSandboxContext` never sets it (a hook’s ' +\n 'record IS `ctx.input`), so the expression throws at run time rather than silently no-op’ing. A loud ' +\n 'failure the author sees on the first run is not this advisory rule’s business',\n },\n];\n\nconst HOOK_APPLICABLE_IDS: ReadonlySet<string> = new Set(HOOK_BODY_WRITE_PATTERN_IDS);\n\n/**\n * `ctx.api.object(name)` write methods → index of the record-payload argument.\n * Mirrors `ObjectRepository` in packages/objectql (the surface hooks actually\n * receive): `upsert` exists only on the last-resort engine facade actions may\n * fall back to, never on the hook path, so it is deliberately absent.\n */\nconst API_WRITE_METHODS: ReadonlyMap<string, number> = new Map([\n ['insert', 0],\n ['create', 0],\n ['update', 0],\n ['updateById', 1],\n]);\n\n/**\n * Wrapper keys of the flat-input proxy (`installFlatInput` in\n * packages/objectql/src/hook-wrappers.ts). `ctx.input.data = …` replaces the\n * whole record payload and `id`/`options`/`ast` address the operation\n * envelope — none is a record-FIELD write, so none is ever flagged.\n */\nconst INPUT_ENVELOPE_KEYS: ReadonlySet<string> = new Set(['id', 'options', 'ast', 'data']);\n\n/**\n * Columns always legitimately writable by automation without appearing in\n * `object.fields`: the package-shared registry-injected columns\n * (`system-fields.ts`, #4330) plus the UNION of its sibling rules' local\n * exemptions (`_id`/`name`/`space` from validate-translation-references,\n * `name`/`owner`/`record_type` from validate-flow-template-paths) — because\n * the cost asymmetry is the same everywhere: over-inclusion is at worst a\n * missed finding, under-inclusion is a false one.\n *\n * Exported for `validate-action-body-writes.ts` only (not re-exported from the\n * package barrel). The action rule is this same check on the other surface that\n * carries a `HookBodySchema` body, so the two must agree on what is implicitly\n * writable — a second copy of this extension would drift exactly the way the\n * five hand-copied lists #4330 collapsed did.\n */\nexport const IMPLICIT_FIELDS: ReadonlySet<string> = new Set([\n ...SYSTEM_FIELDS,\n '_id', 'name', 'space', 'owner', 'record_type',\n]);\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => isRec(x));\n if (isRec(v)) {\n return Object.entries(v).map(([name, def]) => ({\n name,\n ...(isRec(def) ? def : {}),\n }));\n }\n return [];\n}\n\n/**\n * object name → its declared field names (both `fields` authoring shapes).\n *\n * Exported for `validate-action-body-writes.ts` only (see\n * {@link IMPLICIT_FIELDS} for why the two rules share rather than copy).\n */\nexport function indexObjectFields(stack: AnyRec): Map<string, Set<string>> {\n const out = new Map<string, Set<string>>();\n for (const obj of asArray(stack.objects)) {\n const name = typeof obj.name === 'string' ? obj.name : undefined;\n if (!name) continue;\n const names = new Set<string>();\n for (const f of asArray(obj.fields)) {\n if (typeof f.name === 'string' && f.name) names.add(f.name);\n }\n out.set(name, names);\n }\n return out;\n}\n\n/**\n * The declared field names of `objectName` — but ONLY when they are a sound\n * basis for judging \"this name resolves to nothing\". Otherwise `undefined`.\n *\n * Two different unknowns collapse to one answer on purpose, because every\n * caller in this family owes them the same silence:\n *\n * • the object is not in this stack — another package declares it, and a\n * field map we cannot see cannot be judged;\n * • the object is here but declares NO fields at all — an external object or\n * a datasource-introspected schema whose columns are resolved at runtime.\n * Its field map is not empty, it is *unknown*, and an empty Set answers\n * `has(anything) === false`, which reads as \"no such field\" for EVERY write\n * to it. That is a false-positive generator, and a false positive kills an\n * advisory lint (#4383).\n *\n * The distinction is unused today — no rule in the family wants to act on one\n * and not the other — so collapsing it here is what stops the guard from being\n * hand-copied per call site and forgotten at one of them, which is exactly how\n * it went missing from the hook and action rules while\n * `validate-searchable-fields` (skip #2) and `validate-flow-node-writes` both\n * had it. A future caller that genuinely needs to tell them apart should read\n * the index directly and say why.\n */\nexport function judgeableFieldsOf(\n index: ReadonlyMap<string, Set<string>>,\n objectName: string,\n): Set<string> | undefined {\n const declared = index.get(objectName);\n if (!declared || declared.size === 0) return undefined;\n return declared;\n}\n\n/** One statically-extracted field write found in an L2 body. */\nexport interface ExtractedHookBodyWrite {\n /** Which {@link HOOK_BODY_WRITE_PATTERNS} entry matched. */\n patternId: string;\n /** Target object name; `undefined` = the hook's own target object(s). */\n object?: string;\n /** The `ctx.api` method for diagnostics (`insert`/`create`/`update`/`updateById`). */\n method?: string;\n field: string;\n}\n\n/** Everything one parse of an L2 body yields. */\nexport interface ExtractedHookBodyWriteSet {\n /** Every literal write the {@link HOOK_BODY_WRITE_PATTERNS} ledger declares. */\n writes: ExtractedHookBodyWrite[];\n /**\n * `ctx.record` is handed to something as a VALUE somewhere in the body — an\n * argument, an assignment RHS, a spread, a return — rather than only having\n * its properties read and written, or being truthiness/type tested.\n *\n * The action rule needs this to tell a dead snapshot write from a live one:\n * `ctx.record.stage = 'won'; await ctx.api.object('d').update(ctx.record)`\n * builds a payload and persists it, so the assignment is not a no-op. When\n * this is true, no record write in the body can be judged, and none is\n * reported. (One-level aliasing — `const r = ctx.record` — reads as an\n * escape too, which is the safe direction: it suppresses findings.)\n */\n ctxRecordEscapes: boolean;\n}\n\n/**\n * Extract every literal field write the pattern ledger declares from an L2\n * body's source. Parse-only (the source is never executed), error-tolerant\n * (a body with syntax errors simply yields fewer matches), and lazy: the\n * TypeScript compiler is not loaded when no pattern can possibly match.\n *\n * Thin projection of {@link extractHookBodyWriteSet} — use that one when the\n * `ctx.record` liveness signal matters, so the body is parsed once, not twice.\n */\nexport function extractHookBodyWrites(source: string): ExtractedHookBodyWrite[] {\n return extractHookBodyWriteSet(source).writes;\n}\n\n/** {@link extractHookBodyWrites} plus the `ctx.record` liveness signal, one parse. */\nexport function extractHookBodyWriteSet(source: string): ExtractedHookBodyWriteSet {\n // Every recognizable pattern begins at a `ctx` or `Object` identifier — a\n // body containing neither cannot match, and must not pay the compiler load.\n if (!/\\bctx\\b/.test(source) && !/\\bObject\\b/.test(source)) {\n return { writes: [], ctxRecordEscapes: false };\n }\n\n const tsc = loadTypeScript();\n // The runtime wraps a hook body as `new AsyncFunction('ctx', source)` — a\n // FUNCTION BODY, not a module. Parse it in the same context so bare\n // `return` / `await` mean what they mean at run time.\n const sf = tsc.createSourceFile(\n 'hook-body.ts',\n `async function __body(ctx) {\\n${source}\\n}`,\n tsc.ScriptTarget.Latest,\n /* setParentNodes */ false,\n tsc.ScriptKind.TS,\n );\n\n const writes: ExtractedHookBodyWrite[] = [];\n /** Every `ctx.record` reference, and the subset that is only an access base. */\n const recordRefs: ts.Node[] = [];\n const consumedRecordRefs = new Set<ts.Node>();\n\n /** `node` is exactly `ctx.<prop>`. */\n const isCtxDot = (node: ts.Node, prop: string): boolean =>\n tsc.isPropertyAccessExpression(node) &&\n tsc.isIdentifier(node.expression) &&\n node.expression.text === 'ctx' &&\n node.name.text === prop;\n\n /** The literal field name of an LHS rooted at `ctx.<prop>`, if any. */\n const fieldOfCtxLhs = (lhs: ts.Expression, prop: string): string | undefined => {\n if (tsc.isPropertyAccessExpression(lhs) && tsc.isIdentifier(lhs.name) && isCtxDot(lhs.expression, prop)) {\n return lhs.name.text;\n }\n if (tsc.isElementAccessExpression(lhs) && isCtxDot(lhs.expression, prop)) {\n const arg = lhs.argumentExpression;\n if (tsc.isStringLiteral(arg) || tsc.isNoSubstitutionTemplateLiteral(arg)) return arg.text;\n }\n return undefined; // computed key / nested path — statically opaque\n };\n\n /** Literal keys of an object-literal expression (spreads/computed skipped). */\n const literalObjectKeys = (node: ts.Expression): string[] => {\n if (!tsc.isObjectLiteralExpression(node)) return [];\n const keys: string[] = [];\n for (const p of node.properties) {\n if (tsc.isPropertyAssignment(p)) {\n if (tsc.isIdentifier(p.name) || tsc.isStringLiteral(p.name)) keys.push(p.name.text);\n } else if (tsc.isShorthandPropertyAssignment(p)) {\n keys.push(p.name.text);\n }\n // spread / computed / method members are statically opaque — skipped\n }\n return keys;\n };\n\n const visit = (node: ts.Node): void => {\n // Pattern: input-property-assign. FirstAssignment..LastAssignment spans\n // `=` and every compound/logical assignment operator (`+=`, `??=`, …) —\n // each writes its LHS.\n if (\n tsc.isBinaryExpression(node) &&\n node.operatorToken.kind >= tsc.SyntaxKind.FirstAssignment &&\n node.operatorToken.kind <= tsc.SyntaxKind.LastAssignment\n ) {\n const inputField = fieldOfCtxLhs(node.left, 'input');\n if (inputField !== undefined && !INPUT_ENVELOPE_KEYS.has(inputField)) {\n writes.push({ patternId: 'input-property-assign', field: inputField });\n }\n // Pattern: record-property-assign. No envelope-key filter — `ctx.record`\n // is a plain snapshot of the record, not the flat-input proxy, so it\n // carries no operation envelope to exclude.\n const recordField = fieldOfCtxLhs(node.left, 'record');\n if (recordField !== undefined) {\n writes.push({ patternId: 'record-property-assign', field: recordField });\n }\n }\n\n // `ctx.record` liveness, for the rule that judges whether a record write\n // can possibly matter. A reference is CONSUMED when the position it sits in\n // cannot hand the object to anything that might persist it; every other\n // position — an argument, an assignment RHS, a spread, a return — can.\n //\n // 1. the base of a property/element access: `ctx.record.id`,\n // `ctx.record.x = 1`, `ctx.record['k']`;\n // 2. a truthiness or type test. `ctx.record && ctx.record.id` is the\n // defensive idiom real action bodies are written with (the showcase's\n // own `mark_done` opens with it), and reading a test as an escape\n // would suppress the finding on most bodies that have one. A test\n // reads the reference and yields a boolean — or, for `&&`/`||`/`??`,\n // yields the LEFT operand only when it is falsy, which is null or\n // undefined and persists nothing either way. Only the left operand is\n // a test: `x || ctx.record` really does evaluate to the object.\n if (tsc.isPropertyAccessExpression(node) || tsc.isElementAccessExpression(node)) {\n if (isCtxDot(node.expression, 'record')) consumedRecordRefs.add(node.expression);\n }\n if (tsc.isBinaryExpression(node)) {\n const op = node.operatorToken.kind;\n if (\n (op === tsc.SyntaxKind.AmpersandAmpersandToken ||\n op === tsc.SyntaxKind.BarBarToken ||\n op === tsc.SyntaxKind.QuestionQuestionToken) &&\n isCtxDot(node.left, 'record')\n ) {\n consumedRecordRefs.add(node.left);\n }\n }\n if (tsc.isPrefixUnaryExpression(node) && node.operator === tsc.SyntaxKind.ExclamationToken) {\n if (isCtxDot(node.operand, 'record')) consumedRecordRefs.add(node.operand);\n }\n if (tsc.isTypeOfExpression(node) && isCtxDot(node.expression, 'record')) {\n consumedRecordRefs.add(node.expression);\n }\n if (\n (tsc.isIfStatement(node) || tsc.isWhileStatement(node) || tsc.isDoStatement(node)) &&\n isCtxDot(node.expression, 'record')\n ) {\n consumedRecordRefs.add(node.expression);\n }\n if (tsc.isConditionalExpression(node) && isCtxDot(node.condition, 'record')) {\n consumedRecordRefs.add(node.condition);\n }\n if (isCtxDot(node, 'record')) recordRefs.push(node);\n\n if (tsc.isCallExpression(node)) {\n const callee = node.expression;\n\n // Pattern: input-object-assign.\n if (\n tsc.isPropertyAccessExpression(callee) &&\n tsc.isIdentifier(callee.expression) &&\n callee.expression.text === 'Object' &&\n callee.name.text === 'assign' &&\n node.arguments.length >= 2 &&\n isCtxDot(node.arguments[0], 'input')\n ) {\n // Later Object.assign sources overwrite earlier ones but never remove\n // a key, so every literal key is genuinely written regardless of the\n // non-literal arguments around it.\n for (const arg of node.arguments.slice(1)) {\n for (const field of literalObjectKeys(arg)) {\n if (!INPUT_ENVELOPE_KEYS.has(field)) {\n writes.push({ patternId: 'input-object-assign', field });\n }\n }\n }\n }\n\n // Pattern: api-crud-literal — ctx.api.object('<lit>').<method>(payload…).\n if (tsc.isPropertyAccessExpression(callee) && tsc.isIdentifier(callee.name)) {\n const payloadIndex = API_WRITE_METHODS.get(callee.name.text);\n const recv = callee.expression;\n if (\n payloadIndex !== undefined &&\n tsc.isCallExpression(recv) &&\n tsc.isPropertyAccessExpression(recv.expression) &&\n recv.expression.name.text === 'object' &&\n isCtxDot(recv.expression.expression, 'api') &&\n recv.arguments.length === 1\n ) {\n const objArg = recv.arguments[0];\n const objectName =\n tsc.isStringLiteral(objArg) || tsc.isNoSubstitutionTemplateLiteral(objArg)\n ? objArg.text\n : undefined; // dynamic object name — statically opaque\n const payload = node.arguments[payloadIndex];\n if (objectName && payload !== undefined) {\n for (const field of literalObjectKeys(payload)) {\n writes.push({\n patternId: 'api-crud-literal',\n object: objectName,\n method: callee.name.text,\n field,\n });\n }\n }\n }\n }\n }\n\n tsc.forEachChild(node, visit);\n };\n visit(sf);\n return {\n writes,\n ctxRecordEscapes: recordRefs.some((ref) => !consumedRecordRefs.has(ref)),\n };\n}\n\n/**\n * Validate L2 hook-body writes against target-object field declarations.\n * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.\n */\nexport function validateHookBodyWrites(stack: AnyRec): HookBodyWriteFinding[] {\n const findings: HookBodyWriteFinding[] = [];\n const hooks = asArray(stack.hooks);\n if (hooks.length === 0) return findings;\n\n // Built lazily: a stack whose hooks are all L1/handler-based never pays it.\n let objectFields: Map<string, Set<string>> | null = null;\n\n hooks.forEach((hook, hookIndex) => {\n const body = hook.body;\n if (!isRec(body) || body.language !== 'js') return;\n const source = body.source;\n if (typeof source !== 'string' || source.trim() === '') return;\n\n const writes = extractHookBodyWrites(source).filter((w) => HOOK_APPLICABLE_IDS.has(w.patternId));\n if (writes.length === 0) return;\n\n objectFields ??= indexObjectFields(stack);\n const hookName = typeof hook.name === 'string' && hook.name ? hook.name : `#${hookIndex}`;\n\n // The hook's own target set, for `ctx.input` writes. A wildcard target has\n // no single object to check against; a target whose fields cannot be judged\n // ({@link judgeableFieldsOf} — cross-package, or declaring no fields at all)\n // gives nothing to resolve against — either way `ctx.input` writes are\n // skipped, not guessed.\n const targets = (Array.isArray(hook.object) ? hook.object : [hook.object]).filter(\n (o): o is string => typeof o === 'string' && o.trim() !== '',\n );\n const targetSets = targets.map((t) => judgeableFieldsOf(objectFields!, t));\n // ALL targets must be judgeable, not just one: the finding below fires only\n // when a field is missing from EVERY target, and an unjudgeable target is\n // one the field might well exist on. One opaque target therefore makes the\n // whole \"missing everywhere\" claim unsound, not merely narrower (#4383).\n const inputJudgeable =\n targets.length > 0 && !targets.includes('*') && targetSets.every((s) => s !== undefined);\n\n const where = `hook \"${hookName}\" › body`;\n const path = `hooks[${hookIndex}].body.source`;\n const reported = new Set<string>();\n\n for (const w of writes) {\n const dedupeKey = `${w.object ?? ''}\\u0000${w.field}`;\n if (reported.has(dedupeKey)) continue;\n\n if (w.object === undefined) {\n // ctx.input write → the hook's own object(s). Flag only a field\n // missing on EVERY named target (a multi-target body may branch per\n // object, so a partial miss is not statically wrong).\n if (!inputJudgeable) continue;\n if (IMPLICIT_FIELDS.has(w.field)) continue;\n if (targetSets.some((s) => s!.has(w.field))) continue;\n\n reported.add(dedupeKey);\n const objDesc =\n targets.length === 1\n ? `object '${targets[0]}'`\n : `none of its target objects (${targets.join(', ')})`;\n const declares = targets.length === 1 ? 'declares no such field' : 'declare that field';\n findings.push({\n severity: 'warning',\n rule: HOOK_BODY_WRITE_UNKNOWN_FIELD,\n where,\n path,\n message:\n `body writes '${w.field}' to its input, but ${objDesc} ${declares}. The sandboxed script runs ` +\n `clean and the value is copied back onto the record payload unfiltered — on a SQL driver the ` +\n `stray column then fails the WHOLE write with a driver-level error far from here; on a ` +\n `schemaless driver (memory, MongoDB) it is persisted as an undeclared key (#4271).`,\n hint: fixHint(w.field, unionCandidates(targetSets)),\n });\n } else {\n // ctx.api write → the named object.\n const known = judgeableFieldsOf(objectFields!, w.object);\n if (!known) continue; // cross-package, or no declared fields — cannot judge\n if (IMPLICIT_FIELDS.has(w.field) || known.has(w.field)) continue;\n\n reported.add(dedupeKey);\n findings.push({\n severity: 'warning',\n rule: HOOK_BODY_WRITE_UNKNOWN_FIELD,\n where,\n path,\n message:\n `body calls ctx.api.object('${w.object}').${w.method ?? 'update'}(…) writing '${w.field}', but ` +\n `object '${w.object}' declares no such field. The write-path validator skips the unknown key — ` +\n `on a SQL driver the whole call then fails with a driver-level error far from here; on a ` +\n `schemaless driver (memory, MongoDB) the stray key is persisted (#4271).`,\n hint: fixHint(w.field, [...known]),\n });\n }\n }\n });\n\n return findings;\n}\n\n/** Every field name declared across the (all-known) target sets, deduplicated. */\nfunction unionCandidates(targetSets: ReadonlyArray<Set<string> | undefined>): string[] {\n const out = new Set<string>();\n for (const s of targetSets) for (const f of s ?? []) out.add(f);\n return [...out];\n}\n\n/** Did-you-mean (declared + system columns as candidates) plus the fix. */\nfunction fixHint(field: string, declared: string[]): string {\n const suggestion = formatSuggestion(findClosestMatches(field, [...declared, ...IMPLICIT_FIELDS]));\n return (\n (suggestion ? `${suggestion} ` : '') +\n `Fix the field name, or declare '${field}' on the object. Only the literal write patterns in ` +\n `HOOK_BODY_WRITE_PATTERNS are checked — computed keys, spreads and aliased input are not — and this ` +\n `warning never blocks a build.`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Author-time write-set check for L2 (`language:'js'`) ACTION bodies — the\n// sibling of `validate-hook-body-writes.ts` (#4271 follow-up).\n//\n// An action body is the same artefact as a hook body: the same\n// `HookBodySchema` union, parsed by the same `HookBodySchema.safeParse` in\n// `actionBodyRunnerFactory` (packages/runtime/src/sandbox/body-runner.ts), run\n// in the same QuickJS sandbox. So it fails the same way — an action body that\n// writes a field the target object never declares reaches the driver\n// unfiltered, and the outcome is DRIVER-DEPENDENT: on SQL the stray column\n// fails the whole call with a driver-level error far from the authoring\n// mistake, on a schemaless driver the stray key is persisted. Same #4271\n// split as the hook side (see that file's header for the measured chain, and\n// `undeclared-field-write-driver-split.integration.test.ts` for the pin); the\n// hook rule alone left half the surface uncovered.\n//\n// ─── What does NOT carry over ───────────────────────────────────────────────\n//\n// The context the body receives is NOT the hook context, so the hook ledger\n// cannot be adopted wholesale. `buildActionSandboxContext` binds\n// `input: unwrapProxyToPlain(actionCtx?.params ?? {})` — an action's\n// `ctx.input` is its PARAMS BAG, validated upstream against the action's own\n// `params` declaration (ADR-0104 D2), not a record. Resolving those names\n// against object fields would flag every correctly-named parameter: a pure\n// false-positive machine, and a false positive kills an advisory lint. Hence\n// {@link ACTION_BODY_WRITE_EXCLUSIONS} — declared as data, with reasons, and\n// partition-tested against the shared ledger so a pattern added to the hook\n// side later cannot be silently assumed to apply here.\n//\n// So the unknown-field check keeps exactly one shape: `api-crud-literal`\n// (`ctx.api.object('<literal>').insert|.create|.update|.updateById({…})`). That\n// is the one path through which an action body actually persists anything, it\n// addresses its target object explicitly (so the action's own `objectName`\n// binding is irrelevant to the check), and the hook rule's extractor already\n// recognizes it verbatim.\n//\n// ─── The second finding: a record write that reaches nothing (#4345) ────────\n//\n// `ctx.record` is not a write surface either, but it fails DIFFERENTLY, so it\n// gets its own rule id rather than being folded in or dropped. The runner hands\n// the body a plain snapshot (`record: unwrapProxyToPlain(actionCtx?.record)`)\n// and `boundActionHandler` returns `result.value` without writing anything back\n// — no `applyMutationsToInput`, which is the hook path's alone. So\n// `ctx.record.x = …` is discarded for DECLARED and undeclared fields alike.\n// Reporting that through the unknown-field rule would be actively wrong:\n// flagging only the undeclared half implies the declared half persists —\n// precisely the false completion this rule family exists to stop manufacturing.\n//\n// It is NOT enough to flag every `ctx.record.<field> = …`, because mutating the\n// snapshot to build a payload is a legitimate idiom:\n//\n// ctx.record.stage = 'won';\n// await ctx.api.object('crm_deal').update(ctx.record); // the write is LIVE\n//\n// So the finding requires the write to be PROVABLY dead: reported only when\n// `ctx.record` never escapes as a value anywhere in the body (see\n// `ctxRecordEscapes`). Property reads (`ctx.record.id`) do not rescue a write\n// and do not suppress the finding; handing the object to anything — an\n// argument, an assignment RHS, a spread, a return — does. Aliasing\n// (`const r = ctx.record`) reads as an escape, which is the safe direction.\n//\n// ─── Shared posture ─────────────────────────────────────────────────────────\n//\n// Both findings keep the hook rule's posture: advisory `warning`, silent bail\n// on anything statically unknowable (dynamic object names, non-literal\n// payloads, cross-package targets), did-you-mean on a miss.\n//\n// ONE suite entry, TWO rule ids. Both findings fall out of one parse of one\n// source on one surface, so splitting them into two `REFERENCE_INTEGRITY_RULES`\n// members would parse every action body twice to say two things about the same\n// walk; hand-wiring the second into the CLI instead is the drift that suite\n// exists to end — `validateReadonlyFlowWrites` is the standing proof, wired\n// into `validate` and `compile` but never into `lint`.\n//\n// Lazy like its sibling: a body mentioning neither `api` nor `record` cannot\n// match either shape and never pays the TypeScript load.\n\nimport { findClosestMatches, formatSuggestion } from '@objectstack/spec/shared';\nimport {\n extractHookBodyWriteSet,\n indexObjectFields,\n judgeableFieldsOf,\n IMPLICIT_FIELDS,\n HOOK_BODY_WRITE_PATTERNS,\n type BodyWritePatternExclusion,\n type HookBodyWritePattern,\n} from './validate-hook-body-writes.js';\n\nexport type ActionBodyWriteSeverity = 'warning';\n\nexport interface ActionBodyWriteFinding {\n /** Advisory-only by contract, exactly like the hook rule — the type says so. */\n severity: ActionBodyWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `action \"close_deal\" › body`. */\n where: string;\n /** Config path, e.g. `actions[0].body.source`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule ids (registry entries). Two, from one walk — see the header.\nexport const ACTION_BODY_WRITE_UNKNOWN_FIELD = 'action-body-write-unknown-field';\nexport const ACTION_RECORD_WRITE_DISCARDED = 'action-record-write-discarded';\n\n// ─── The applicable-pattern ledger ──────────────────────────────────────────\n//\n// Not a second pattern list: a declared PARTITION of the shared\n// `HOOK_BODY_WRITE_PATTERNS` into the shapes each of this module's two checks\n// consumes, plus the shapes neither does. Every part is data, and\n// validate-action-body-writes.test.ts asserts they cover the shared ledger\n// exactly — so a pattern added on the hook side FAILS this rule's test until\n// someone decides which part it belongs in. Silence is not a decision.\n// (`record-property-assign` landing here is that ratchet's first live catch.)\n\n/** @deprecated alias — the exclusion shape is shared with the hook rule. */\nexport type ActionBodyWriteExclusion = BodyWritePatternExclusion;\n\n/**\n * Ledger shapes the UNKNOWN-FIELD check consumes — resolved against the named\n * object's declared fields.\n *\n * Include-list, not exclude-list, on purpose: an unclassified new pattern is\n * then inert here (a missed finding) rather than live against a context it was\n * never reasoned about (a false one) — the same asymmetry the extractor's\n * silent bails follow.\n */\nexport const ACTION_BODY_WRITE_PATTERN_IDS: readonly string[] = ['api-crud-literal'];\n\n/**\n * Ledger shapes the DISCARDED-RECORD-WRITE check consumes — never resolved\n * against anything, because no field name can make a discarded write land.\n */\nexport const ACTION_RECORD_WRITE_PATTERN_IDS: readonly string[] = ['record-property-assign'];\n\n/** Shared-ledger patterns neither check consumes, each with its reason. */\nexport const ACTION_BODY_WRITE_EXCLUSIONS: readonly BodyWritePatternExclusion[] = [\n {\n id: 'input-property-assign',\n reason:\n \"an action's ctx.input is its params bag (`input: unwrapProxyToPlain(actionCtx?.params)`), not a \" +\n 'record — `ctx.input.<name>` writes a declared PARAMETER, which object fields cannot judge',\n },\n {\n id: 'input-object-assign',\n reason: 'same surface as input-property-assign — Object.assign(ctx.input, …) targets the params bag',\n },\n];\n\n/**\n * The subset of the shared ledger the unknown-field check sees — the published\n * answer to \"which writes does the action lint resolve against fields?\".\n */\nexport const ACTION_BODY_WRITE_PATTERNS: readonly HookBodyWritePattern[] =\n HOOK_BODY_WRITE_PATTERNS.filter((p) => ACTION_BODY_WRITE_PATTERN_IDS.includes(p.id));\n\n/** The subset the discarded-record-write check sees. */\nexport const ACTION_RECORD_WRITE_PATTERNS: readonly HookBodyWritePattern[] =\n HOOK_BODY_WRITE_PATTERNS.filter((p) => ACTION_RECORD_WRITE_PATTERN_IDS.includes(p.id));\n\nconst APPLICABLE_IDS: ReadonlySet<string> = new Set(ACTION_BODY_WRITE_PATTERN_IDS);\nconst RECORD_WRITE_IDS: ReadonlySet<string> = new Set(ACTION_RECORD_WRITE_PATTERN_IDS);\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => isRec(x));\n if (isRec(v)) {\n return Object.entries(v).map(([name, def]) => ({\n name,\n ...(isRec(def) ? def : {}),\n }));\n }\n return [];\n}\n\n/** One L2 action body found in the stack, with the location to report it at. */\ninterface ActionBodySite {\n name: string;\n source: string;\n path: string;\n}\n\n/** The object an action binds to, by the same rule `collectBundleActions` uses. */\nfunction actionObjectBinding(action: AnyRec, parentObject?: string): string | undefined {\n if (typeof action.object === 'string' && action.object) return action.object;\n if (typeof action.objectName === 'string' && action.objectName) return action.objectName;\n return parentObject;\n}\n\n/**\n * Every L2 action body in the stack, from both places the runtime reads them.\n *\n * `collectBundleActions` (packages/runtime/src/app-plugin.ts) registers\n * `bundle.actions` AND `objects[].actions` — and `defineStack`'s\n * `mergeObjectActions` appends an action carrying `objectName` to its object's\n * array while PRESERVING the top-level entry, so a merged action is genuinely\n * reachable twice. Walk both and collapse the duplicate, or every merged\n * action's findings are reported twice.\n *\n * Deduplicated by VALUE — bound object, name and body source — not by object\n * identity the way the runtime can afford to. The suite runs on the\n * schema-PARSED stack (`validateReferenceIntegrity(result.data)` in `os\n * validate` / `os compile`), and parsing rebuilds every node, so the two copies\n * of a merged action arrive as distinct objects that are merely equal. An\n * identity check silently degrades to no check at all there — which is how the\n * showcase app reported its one action-body warning twice.\n *\n * Two same-named actions on DIFFERENT objects stay separate (the binding is in\n * the key). Two on the SAME binding with byte-identical bodies collapse to one\n * — they would emit the same sentence twice, so the second is noise.\n *\n * The top-level entry is walked first, so a merged action reports at\n * `actions[i]` — the authored location, not the derived copy.\n *\n * Only `type: 'script'` bodies are walked (`type` omitted counts, since\n * `ActionType.default('script')` makes that the same declaration).\n *\n * This rule USED to be deliberately type-blind, on the grounds that the\n * runtime bound a handler from `action.body` alone and so a body on a\n * non-`script` action still ran and still failed silently — checking what\n * executes beat checking what the schema said should. That comment predicted\n * its own revision (\"定了之后 lint 那边要跟着调\"), and #4352 is the ruling:\n * `actionBodyRunnerFactory` now refuses to bind a handler unless the type is\n * `script`, and `ActionSchema` rejects the contradictory pair at publish. So\n * what executes and what the schema says are the same set again, and walking\n * a non-`script` body here would produce advice about writes that provably\n * never happen — noise pointing at metadata whose real defect is the `type`,\n * which the publish gate already names with its own prescription.\n */\nfunction collectActionBodies(stack: AnyRec): ActionBodySite[] {\n const sites: ActionBodySite[] = [];\n const seen = new Set<string>();\n\n const collect = (actions: unknown, pathPrefix: string, parentObject?: string): void => {\n asArray(actions).forEach((action, index) => {\n // Same default the spec declares, and the same one the runtime gate\n // applies — a stack may reach lint unparsed, so an omitted `type` is\n // `'script'`, not \"unknown\".\n const type = typeof action.type === 'string' ? action.type : 'script';\n if (type !== 'script') return;\n const body = action.body;\n if (!isRec(body) || body.language !== 'js') return;\n const source = body.source;\n if (typeof source !== 'string' || source.trim() === '') return;\n const name = typeof action.name === 'string' && action.name ? action.name : `#${index}`;\n const key = `${actionObjectBinding(action, parentObject) ?? ''}\\u0000${name}\\u0000${source}`;\n if (seen.has(key)) return;\n seen.add(key);\n sites.push({ name, source, path: `${pathPrefix}[${index}].body.source` });\n });\n };\n\n collect(stack.actions, 'actions');\n asArray(stack.objects).forEach((obj, objIndex) => {\n const parentObject = typeof obj.name === 'string' && obj.name ? obj.name : undefined;\n collect(obj.actions, `objects[${objIndex}].actions`, parentObject);\n });\n\n return sites;\n}\n\n/**\n * Validate L2 action-body writes against target-object field declarations.\n * Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse stacks.\n */\nexport function validateActionBodyWrites(stack: AnyRec): ActionBodyWriteFinding[] {\n const findings: ActionBodyWriteFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const sites = collectActionBodies(stack);\n if (sites.length === 0) return findings;\n\n // Built lazily: only the unknown-field check needs it, so a stack whose\n // action bodies never reach `ctx.api` never pays it.\n let objectFields: Map<string, Set<string>> | null = null;\n\n for (const site of sites) {\n // Cheap prefilter, narrower than the extractor's own: every consumed\n // pattern is rooted at `ctx.api` or `ctx.record`, so a body carrying\n // neither identifier cannot match and must not pay the ~9 MB TypeScript\n // load. Pinned by the ledger test — a consumed pattern whose example fails\n // this filter fails there, rather than going quietly unchecked here.\n if (!/\\bapi\\b/.test(site.source) && !/\\brecord\\b/.test(site.source)) continue;\n\n // ONE parse per body, both checks read from it.\n const { writes: allWrites, ctxRecordEscapes } = extractHookBodyWriteSet(site.source);\n const writes = allWrites.filter((w) => APPLICABLE_IDS.has(w.patternId));\n const recordWrites = allWrites.filter((w) => RECORD_WRITE_IDS.has(w.patternId));\n if (writes.length === 0 && recordWrites.length === 0) continue;\n\n const where = `action \"${site.name}\" › body`;\n\n // ── Discarded record writes (#4345) ──────────────────────────────────\n // Reported only when the write is PROVABLY dead: `ctx.record` never\n // leaves the body as a value, so nothing can persist the mutation. When\n // it does escape, the snapshot may be a payload under construction, and\n // every one of its writes is skipped — a missed finding, never a false one.\n if (recordWrites.length > 0 && !ctxRecordEscapes) {\n const reportedFields = new Set<string>();\n for (const w of recordWrites) {\n if (reportedFields.has(w.field)) continue;\n reportedFields.add(w.field);\n findings.push({\n severity: 'warning',\n rule: ACTION_RECORD_WRITE_DISCARDED,\n where,\n path: site.path,\n message:\n `body assigns ctx.record.${w.field}, but an action's ctx.record is a plain snapshot the runtime ` +\n `never writes back — the action returns success and the assignment is discarded, whether or not ` +\n `'${w.field}' is a declared field (#4345).`,\n hint:\n `To persist it, write through the API: ctx.api.object('<object>').updateById(ctx.recordId, ` +\n `{ ${w.field}: … }). Reported only because ctx.record is never passed anywhere in this body — ` +\n `mutating the snapshot and then handing it to an API write is a live payload and is not flagged. ` +\n `This warning never blocks a build.`,\n });\n }\n }\n\n if (writes.length === 0) continue;\n objectFields ??= indexObjectFields(stack);\n const reported = new Set<string>();\n\n for (const w of writes) {\n // Defensive: today every applicable pattern addresses its object\n // explicitly. A future applicable pattern that does not (a `ctx.input`-\n // shaped one) has no target to resolve against in an action, so it stays\n // silent rather than being guessed at the action's `objectName`.\n if (w.object === undefined) continue;\n\n const dedupeKey = `${w.object}\\u0000${w.field}`;\n if (reported.has(dedupeKey)) continue;\n\n const known = judgeableFieldsOf(objectFields, w.object);\n if (!known) continue; // cross-package, or no declared fields — cannot judge\n if (IMPLICIT_FIELDS.has(w.field) || known.has(w.field)) continue;\n\n reported.add(dedupeKey);\n findings.push({\n severity: 'warning',\n rule: ACTION_BODY_WRITE_UNKNOWN_FIELD,\n where,\n path: site.path,\n message:\n `body calls ctx.api.object('${w.object}').${w.method ?? 'update'}(…) writing '${w.field}', but ` +\n `object '${w.object}' declares no such field. The write-path validator skips the unknown key — ` +\n `on a SQL driver the whole action then fails with a driver-level error far from here; on a ` +\n `schemaless driver (memory, MongoDB) the stray key is persisted (#4271).`,\n hint: fixHint(w.field, [...known]),\n });\n }\n }\n\n return findings;\n}\n\n/** Did-you-mean (declared + system columns as candidates) plus the fix. */\nfunction fixHint(field: string, declared: string[]): string {\n const suggestion = formatSuggestion(findClosestMatches(field, [...declared, ...IMPLICIT_FIELDS]));\n return (\n (suggestion ? `${suggestion} ` : '') +\n `Fix the field name, or declare '${field}' on the object. Only the literal write patterns in ` +\n `ACTION_BODY_WRITE_PATTERNS are checked — an action's ctx.input is its params bag, so it is not a ` +\n `record-write surface and is never resolved against fields — and this warning never blocks a build.`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n//\n// Author-time write-set check for a flow CRUD node's `fields` write map — the\n// THIRD surface in the family #4271 opened, and the one the docs spent the\n// longest recommending as the safe alternative to the other two.\n//\n// A flow node that writes a field the target object never declares\n// (`config.fields.stagee` against an object whose column is `stage`) was caught\n// by nothing. `validate-readonly-flow-writes.ts` walks this exact map and\n// explicitly stepped over the unknown key (\"a form/field-layout lint concern,\n// not this rule's\" — a referral to a rule that does not check writes);\n// `validate-flow-template-paths.ts` checks the `{record.<path>}` READ tokens\n// interpolated into node config, never the write-side key. So the surface the\n// hook-body docs pointed authors at — \"prefer a flow update_record node, whose\n// structural `fields` config is checked\" — was the least checked of the three.\n//\n// ─── Why this one GATES where its two siblings advise ───────────────────────\n//\n// `hook-body-write-unknown-field` (#4305) and `action-body-write-unknown-field`\n// (#4344) are `warning` because they PARSE JavaScript: the finding is only as\n// good as the extractor, and a false positive kills an advisory lint. Nothing\n// here is parsed. `config.fields` is a literal, structural map next to a\n// literal `objectName` — when this rule speaks, the key provably resolves to no\n// column, at the same certainty `flow-update-readonly-field` already gates on\n// one config key over.\n//\n// And the runtime consequence is not the benign \"consumer skips the unknown\n// name and does the rest\" that keeps `page-field-unknown` / `form-field-unknown`\n// advisory. Nothing between the node and storage removes the key: the flow\n// executor calls the data engine directly (bypassing the metadata-protocol\n// ingress, which strips `readonly` — not unknown — keys anyway), the engine's\n// write paths strip only readonly/readonlyWhen, and the SQL driver's\n// `formatInput` / `applyWriteColumnMap` pass an unrecognized key straight\n// through (`m[k] ?? k`). Every branch below was measured, not inferred:\n//\n// • Through the engine, an undeclared key reaches `driver.update` /\n// `driver.create` verbatim, alongside the audit stamps.\n// • On SQLite/knex an UPDATE becomes `update \"deal\" set \"name\" = 'n2',\n// \"stagee\" = 'won' … → no such column: stagee`. The statement is rejected\n// WHOLE: `name` — spelled correctly, in the same payload — does not land\n// either, and the step fails with a driver error naming a column, far from\n// the authoring mistake.\n// • An INSERT fails the same way (`table deal has no column named stagee`),\n// and one notch harder: the row is never created at all, so every later\n// node that expected `{<node>.id}` is working from a record that does not\n// exist.\n// • On a schemaless datasource (memory, MongoDB) nothing rejects it, so the\n// stray key is persisted into a column the object never declares — where no\n// schema-driven read surface will return it.\n//\n// No outcome is \"the rest still works\". That is the same call\n// `validate-searchable-fields` makes for a stale entry and\n// `validate-flow-template-paths` makes for a filter-position token: gate when\n// the miss breaks or corrupts the operation, advise when it merely narrows the\n// output. Every skip below exists so that gate only ever fires on a certainty.\n//\n// ─── Scope ──────────────────────────────────────────────────────────────────\n//\n// {@link FLOW_WRITE_NODE_TYPES} — every CRUD node type that carries a `fields`\n// WRITE map: `update_record` (#4369) and `create_record` (#4371). The deferred\n// half, {@link FLOW_WRITE_NODE_TYPES_DEFERRED}, is now empty, and the partition\n// test still derives the full set behaviourally from the spec's\n// executor-written config schemas — so a node type that grows a write map later\n// lands on neither side and fails that test until someone classifies it.\n//\n// `get_record.fields` is NOT a member and never will be: it is a projection\n// (`z.array(z.string())`), a READ, and an unknown entry there narrows the\n// selection rather than breaking the statement. `screen.defaults` is not one\n// either — an object-form screen forwards it into the `ScreenSpec` the client\n// renders, so an unknown key is a form prefill the renderer ignores: inert, the\n// \"skips it and renders the rest\" case this rule's severity is defined against.\n// Both are excluded on the shape of their failure, not by omission.\n//\n// `runAs` is deliberately NOT consulted, unlike its readonly sibling. A\n// `runAs:'system'` flow is elevated past the readonly strip, which is why that\n// rule skips it — but no run identity conjures a column, so an unknown field is\n// unknown at every privilege level.\n//\n// Wired via REFERENCE_INTEGRITY_RULES (it resolves a field NAME written in\n// metadata against what the stack declares — the suite's exact membership\n// test), so `os validate`, `os lint` and `os compile` report it at once. The\n// readonly rule next door is still hand-wired into two of those three; this one\n// does not repeat that.\n\nimport { findClosestMatches, formatSuggestion } from '@objectstack/spec/shared';\n\nimport { indexObjectFields, judgeableFieldsOf, IMPLICIT_FIELDS } from './validate-hook-body-writes.js';\nimport { walkFlowNodes, flowNodeLabel } from './flow-walk.js';\n\nexport type FlowNodeWriteSeverity = 'error';\n\nexport interface FlowNodeWriteFinding {\n /** Always `error` — a literal key against a literal object is a certainty (see module note). */\n severity: FlowNodeWriteSeverity;\n rule: string;\n /** Human-readable location, e.g. `flow \"close_deal\" › node \"Mark won\"`. */\n where: string;\n /** Config path, e.g. `flows[0].nodes[3].config.fields.stagee`. */\n path: string;\n message: string;\n hint: string;\n}\n\n// Rule id (registry entry).\nexport const FLOW_NODE_WRITE_UNKNOWN_FIELD = 'flow-node-write-unknown-field';\n\n// ─── The covered-node ledger ────────────────────────────────────────────────\n//\n// Which flow node types have their `config.fields` write map resolved against\n// the target object, declared as data — and, next to it, which `fields`-bearing\n// node type deliberately does not yet, with its reason. Both halves are\n// partition-tested against the CRUD schemas in\n// `@objectstack/spec/automation/builtin-node-config`, so a node type that grows\n// a write map later cannot land on the uncovered side by nobody noticing.\n\n/** Flow node types whose `config.fields` keys this rule resolves. */\nexport const FLOW_WRITE_NODE_TYPES: readonly string[] = ['update_record', 'create_record'];\n\n/** A `fields`-bearing node type this rule does NOT cover yet, and why. */\nexport interface FlowWriteNodeDeferral {\n /** The `FlowNode.type` left uncovered. */\n readonly type: string;\n /** Why it is not covered, in terms a reviewer can act on. */\n readonly reason: string;\n}\n\n/**\n * `fields`-bearing CRUD node types deliberately left uncovered.\n *\n * **Empty, and that is the point.** #4369 shipped `update_record` alone and\n * parked `create_record` here with its reason — a gating rule earning its\n * severity one measured surface at a time — rather than leaving the other half\n * as silence. #4371 measured the INSERT path (`table deal has no column named\n * stagee`, and the row never created at all), found it strictly worse than the\n * UPDATE one, and moved it across.\n *\n * The slot stays because the partition test derives the full `fields`-write-map\n * set from the spec's own config schemas: a node type that grows one later\n * belongs to neither list and fails that test until someone puts it in one.\n * Deleting this array would turn that forced decision back into a default.\n */\nexport const FLOW_WRITE_NODE_TYPES_DEFERRED: readonly FlowWriteNodeDeferral[] = [];\n\ntype AnyRec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is AnyRec => !!v && typeof v === 'object' && !Array.isArray(v);\n\n/** Coerce an array-or-name-keyed-map collection to an array (name injected). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v.filter((x): x is AnyRec => isRec(x));\n if (isRec(v)) {\n return Object.entries(v).map(([name, def]) => ({\n name,\n ...(isRec(def) ? def : {}),\n }));\n }\n return [];\n}\n\n/**\n * The target object of a CRUD node, when statically knowable. Reads the\n * canonical `objectName` and its historical `object` alias — a pre-parse source\n * may still carry the alias until the 'flow-node-crud-object-alias' conversion\n * (#3796) canonicalizes it at load. A templated value (contains `{`) is\n * resolved from flow variables at run time, so it is skipped rather than\n * guessed. Same read as `validate-readonly-flow-writes.ts`, which walks the\n * same nodes for the other question.\n */\nfunction readLiteralObjectName(config: AnyRec): string | undefined {\n const raw = config.objectName ?? config.object;\n if (typeof raw !== 'string' || raw.includes('{')) return undefined;\n return raw || undefined;\n}\n\nconst COVERED_TYPES: ReadonlySet<string> = new Set(FLOW_WRITE_NODE_TYPES);\n\n/**\n * Validate flow write-node `fields` keys against the target object's declared\n * fields. Pure `(stack) => Finding[]` (ADR-0019); safe on pre- or post-parse\n * stacks.\n */\nexport function validateFlowNodeWrites(stack: AnyRec): FlowNodeWriteFinding[] {\n const findings: FlowNodeWriteFinding[] = [];\n if (!isRec(stack)) return findings;\n\n const flows = asArray(stack.flows);\n if (flows.length === 0) return findings;\n\n // Built lazily: a stack whose flows carry no write node never pays it.\n let objectFields: Map<string, Set<string>> | null = null;\n\n flows.forEach((flow, flowIndex) => {\n const flowName = typeof flow.name === 'string' && flow.name ? flow.name : `#${flowIndex}`;\n // Every node, INCLUDING those nested in try_catch / loop / parallel regions\n // — a gating rule that stops at the top level simply stops gating the\n // moment an author wraps the write in error handling (#4380).\n const walked = walkFlowNodes(flow, `flows[${flowIndex}]`);\n\n walked.forEach(({ node, path: nodePath, regionTrail }, walkIndex) => {\n if (typeof node.type !== 'string' || !COVERED_TYPES.has(node.type)) return;\n\n const config = isRec(node.config) ? node.config : undefined;\n if (!config) return;\n\n // A non-literal write map (templated string, spread result, array) is not\n // statically knowable — skip rather than guess.\n const fields = config.fields;\n if (!isRec(fields)) return;\n const written = Object.keys(fields);\n if (written.length === 0) return;\n\n const objectName = readLiteralObjectName(config);\n if (!objectName) return; // templated / dynamic object — resolved at run time\n\n objectFields ??= indexObjectFields(stack);\n // Cross-package objects and objects declaring no fields at all (external /\n // datasource-introspected schemas) are both unjudgeable, and this rule\n // gates — see {@link judgeableFieldsOf}, which is where that guard now\n // lives for the whole family rather than once per rule (#4383).\n const known = judgeableFieldsOf(objectFields, objectName);\n if (!known) return;\n\n const nodeName = flowNodeLabel(node, walkIndex);\n // A nested node names the region that holds it, or \"node X\" is ambiguous\n // in a flow where the same label appears in a try and a catch branch.\n const nodeWhere = regionTrail ? `${regionTrail} › node \"${nodeName}\"` : `node \"${nodeName}\"`;\n\n for (const fieldName of written) {\n if (known.has(fieldName) || IMPLICIT_FIELDS.has(fieldName)) continue;\n // A dotted key addresses a nested path, not a top-level column — the\n // document drivers forward it verbatim. Not statically a missing field.\n if (fieldName.includes('.')) continue;\n\n findings.push({\n severity: 'error',\n rule: FLOW_NODE_WRITE_UNKNOWN_FIELD,\n where: `flow \"${flowName}\" › ${nodeWhere}`,\n path: `${nodePath}.config.fields.${fieldName}`,\n message:\n `${node.type} writes '${fieldName}', but object '${objectName}' declares no such field. Nothing ` +\n `between the node and storage removes the key: on a SQL datasource the driver rejects the whole ` +\n `statement ('no such column'), so the correctly named fields in this same payload never land ` +\n `either${\n node.type === 'create_record' ? ' and the record is never created at all' : ''\n }; on a schemaless one the stray key is persisted into a column no read surface returns.`,\n hint: fixHint(fieldName, [...known]),\n });\n }\n });\n });\n\n return findings;\n}\n\n/** Did-you-mean (declared + system columns as candidates) plus the fix. */\nfunction fixHint(field: string, declared: string[]): string {\n const suggestion = formatSuggestion(findClosestMatches(field, [...declared, ...IMPLICIT_FIELDS]));\n return (\n (suggestion ? `${suggestion} ` : '') +\n `Fix the field name, or declare '${field}' on the object. This gates the build rather than warning: ` +\n `the key is literal and so is the object, so unlike the hook/action body rules there is nothing here ` +\n `that could have been mis-extracted.`\n );\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The reference-integrity suite — one entry point for the rules that answer\n * \"does this name resolve to anything?\" (issue #3583, assessment §5 D5).\n *\n * ## Why this exists\n *\n * These rules were wired **by hand** into each CLI entry point that runs them.\n * `os validate`, `os lint` and `os compile` each grew their own import list and\n * their own call site, so landing a rule meant remembering three places — and\n * the assessment's §2.2 already named the resulting drift as the enemy: the\n * same stack, checked by a different rule subset depending on which command\n * the author happened to run.\n *\n * The suite makes the next rule's wiring a ONE-LINE edit here, and makes the\n * question \"which rules run on this path?\" answerable by reading one list.\n *\n * ## What belongs in it\n *\n * A rule belongs here when it resolves a NAME written in metadata against the\n * things a stack actually declares — objects, actions, fields, measures,\n * permissions, translation keys. That is the family the HotCRM audit found\n * shipping broken: every instance parsed, validated, and failed silently at\n * runtime because nothing checked that the name pointed at anything.\n *\n * `validateFlowTemplatePaths` is a member for exactly that reason: a\n * `{record.<field>}` token is a field name written in metadata, resolved\n * against the bound object's declared fields. It was wired by hand into\n * `os validate` alone — the drift this suite exists to end — so `os lint` and\n * `os compile` accepted a flow the runtime refuses. Its findings carry BOTH\n * severities (see that module: a filter-position miss gates, every other\n * position advises), which is why the suite's contract is severity-agnostic.\n *\n * `validateSearchableFields` is a member on the same reading, one layer in: an\n * ADR-0061 `searchableFields` entry is a field name written in metadata,\n * resolved against the object's own declared fields. It gates (`error`) because\n * the engine's tolerance for a stale entry — silently filtering it out — either\n * narrows the searched set below what the object declares or, once every entry\n * is stale, falls through to the auto-default and searches a set the author\n * never wrote. See that module for why the other field-existence rules stay\n * advisory and this one does not.\n *\n * Rules that check SHAPE rather than reference (view containers, responsive\n * styles, seed replay safety, seed state machines, seed/security posture) stay\n * out — they answer a different question and have their own call sites.\n *\n * ## Known remaining asymmetry\n *\n * `os doctor` runs only `validateWidgetBindings` and is NOT converted here: it\n * is an environment health check (node version, config presence, circular\n * lookups), not an authoring gate, so adopting the suite there is a product\n * decision about what `doctor` is for — not a wiring cleanup. It is named here\n * so the gap stays visible instead of being rediscovered.\n */\n\nimport { validateObjectReferences } from './validate-object-references.js';\nimport { validateSearchableFields } from './validate-searchable-fields.js';\nimport { validateActionNameRefs } from './validate-action-name-refs.js';\nimport { validatePageFieldBindings } from './validate-page-field-bindings.js';\nimport { validateChartBindings } from './validate-chart-bindings.js';\nimport { validateNavAccess } from './validate-nav-access.js';\nimport { validateNavTargetRefs } from './validate-nav-target-refs.js';\nimport { validateTranslationReferences } from './validate-translation-references.js';\nimport { validateFlowTemplatePaths } from './validate-flow-template-paths.js';\nimport { validateAiSurfaceAffinity } from './validate-ai-surface-affinity.js';\nimport { validateAiToolReferences } from './validate-ai-tool-references.js';\nimport { validateAiAgentAuthoring } from './validate-ai-agent-authoring.js';\nimport { validateHookBodyWrites } from './validate-hook-body-writes.js';\nimport { validateActionBodyWrites } from './validate-action-body-writes.js';\nimport { validateFlowNodeWrites } from './validate-flow-node-writes.js';\nimport { validateReadonlyFlowWrites } from './validate-readonly-flow-writes.js';\nimport { validateReactPageProps } from './validate-react-page-props.js';\n\nexport type ReferenceIntegritySeverity = 'error' | 'warning';\n\n/**\n * The shape every rule in the suite already returns. Declared here so callers\n * can hold one type instead of a six-way union.\n */\nexport interface ReferenceIntegrityFinding {\n /** `error` = the reference is dead; `warning` = it may resolve elsewhere, or the miss is inert. */\n severity: ReferenceIntegritySeverity;\n /** Diagnostic rule id (stable; used by allowlists and docs). */\n rule: string;\n /** Human-readable location. */\n where: string;\n /** Config path. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\n/** One member of the suite. `name` is the exported function's name — the id a wiring test can assert on. */\nexport interface ReferenceIntegrityRule {\n name: string;\n run: (stack: Record<string, unknown>) => ReferenceIntegrityFinding[];\n}\n\n/**\n * Every reference-integrity rule, in the order their findings are reported.\n *\n * ADDING A RULE: append it here and it runs on `validate`, `lint` and\n * `compile` at once. Do not re-wire the commands.\n */\nexport const REFERENCE_INTEGRITY_RULES: readonly ReferenceIntegrityRule[] = [\n { name: 'validateObjectReferences', run: validateObjectReferences },\n { name: 'validateSearchableFields', run: validateSearchableFields },\n { name: 'validateActionNameRefs', run: validateActionNameRefs },\n { name: 'validatePageFieldBindings', run: validatePageFieldBindings },\n { name: 'validateChartBindings', run: validateChartBindings },\n { name: 'validateNavAccess', run: validateNavAccess },\n // Nav targets that are NOT object names — page/report/dashboard. Restores the\n // coverage `defineStack`'s own cross-reference block switches off whenever the\n // stack declares none of that collection (`pageNames.size > 0 && …`), which is\n // exactly the state a stack is in when the target was never written.\n // `action` is deliberately absent (validateActionNameRefs owns it) and so is\n // `component` (an unregistered ref renders a named diagnostic, not silence).\n { name: 'validateNavTargetRefs', run: validateNavTargetRefs },\n { name: 'validateTranslationReferences', run: validateTranslationReferences },\n { name: 'validateFlowTemplatePaths', run: validateFlowTemplatePaths },\n { name: 'validateAiSurfaceAffinity', run: validateAiSurfaceAffinity },\n { name: 'validateAiToolReferences', run: validateAiToolReferences },\n { name: 'validateAiAgentAuthoring', run: validateAiAgentAuthoring },\n // Field names WRITTEN by an L2 hook body (`ctx.input.x = …`,\n // `ctx.api.object('y').update({ x })`), resolved against the target object's\n // declared fields — the write-side counterpart of validateFlowTemplatePaths'\n // read-side membership (#4271). Lazy: only a hook that actually carries a\n // `language:'js'` body loads the TypeScript parser.\n { name: 'validateHookBodyWrites', run: validateHookBodyWrites },\n // The same check on the other surface that carries a `HookBodySchema` body:\n // action bodies, run by the same sandbox. Only the `ctx.api` write family\n // carries over — an action's `ctx.input` is its params bag, not a record\n // (see that module's ledger). Lazy on the same terms.\n //\n // The first member here to emit more than one rule id (`validateReactPageProps`\n // below is the other, and carries the most). Besides resolving `ctx.api`\n // writes against declared fields (`action-body-write-unknown-field`), it\n // reports a `ctx.record` write that can reach nothing\n // (`action-record-write-discarded`, #4345) — not a resolution question, so\n // by the charter above it does not belong in the suite. It rides along\n // anyway because it falls out of the SAME parse of the SAME source: a\n // separate member would parse every action body twice to say two things\n // about one walk, and hand-wiring it into the CLI instead is exactly the\n // drift this suite exists to end — which `validateReadonlyFlowWrites` was\n // the standing proof of, until it joined the suite below.\n { name: 'validateActionBodyWrites', run: validateActionBodyWrites },\n // The third surface that writes a record field set: a flow `update_record`\n // node's `config.fields`. Same question as the two rules above, but the map\n // is structural metadata rather than parsed JS, so a finding is a certainty\n // and gates (`error`) — see that module for why, and why the docs' long-\n // standing \"prefer a flow node, it's checked\" advice was the least true of\n // the three until it landed.\n { name: 'validateFlowNodeWrites', run: validateFlowNodeWrites },\n // The OTHER question about that same `config.fields` map: not \"does this\n // field exist?\" but \"is it writable?\" — a `runAs:'user'` update_record\n // writing a static-`readonly` field is stripped by the engine and the step\n // still reports success (#2948/#3425). It walks the identical map the rule\n // above walks, so the two splitting call sites was never defensible: hand-\n // wired into `validate` and `compile` only, it left `os lint` PASSING a flow\n // `os validate` refuses — and this one gates, so the divergence shipped a\n // build the other command would have stopped. Joining the suite is the whole\n // fix; the two hand-wired call sites are deleted with it (#4345 follow-up).\n { name: 'validateReadonlyFlowWrites', run: validateReadonlyFlowWrites },\n // The `kind:'react'` page surface. Every prop a react block binds BY FIELD\n // NAME is resolved against the object it names (#4340) — `<ListView columns>`,\n // `<ObjectForm fields>`, `<Block type=\"element:…\">` through the SAME\n // `COMPONENT_FIELD_SPECS` table `validatePageFieldBindings` walks one surface\n // over, plus `<ObjectChart>`'s aggregate/axes (#3701/#3729) and\n // `searchableFields` (#4329). Squarely the charter's question, on the surface\n // where it had no answer at all.\n //\n // It also carries `react-block-needs-record-context` (#4413) — a BINDING\n // question rather than a resolution one: the `record:*` family reads its\n // record from a record page's context, so on THIS surface the binding does\n // not exist at all and the props the contract published for it were read by\n // no renderer. This rule used to resolve those props' field names against\n // the object they named — lint standing guard over a binding that never ran.\n // It rejects the blocks now, out of the same parse.\n //\n // It was hand-wired into `os validate` ALONE, so `os lint` and `os compile`\n // accepted a react page whose every field binding was stale — including the\n // gating ones (a missing required binding, a filter position naming no field:\n // the predicate can never match and the list comes back empty). That is\n // `validateReadonlyFlowWrites`' divergence again, one surface over, and it is\n // the reason this entry exists rather than a fourth hand-wiring.\n //\n // Like `validateActionBodyWrites` above, it emits ids that are not resolution\n // questions — `react-prop-missing-required` and `react-prop-typo` are shape,\n // and by the charter belong outside. They ride along for the same reason: they\n // fall out of the SAME TypeScript parse of the SAME page source, and splitting\n // them into a second member would parse every react page twice to say two\n // things about one walk. Lazy on the same terms as the hook/action body rules\n // — only a page that is actually `kind:'react'` loads the compiler.\n { name: 'validateReactPageProps', run: validateReactPageProps },\n];\n\n/**\n * Run every reference-integrity rule over a stack. Returns the concatenated\n * findings (empty = clean). Pure: no I/O, safe on both the schema-parsed stack\n * and the raw/normalized config the `lint` path carries.\n */\nexport function validateReferenceIntegrity(stack: Record<string, unknown>): ReferenceIntegrityFinding[] {\n const findings: ReferenceIntegrityFinding[] = [];\n for (const rule of REFERENCE_INTEGRITY_RULES) {\n findings.push(...rule.run(stack));\n }\n return findings;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time lint for flow authoring ANTI-PATTERNS — metadata that is valid\n * (passes schema + expression checks) but is semantically a footgun at runtime.\n * Most are emitted as WARNINGS: they guide the author (very often an AI\n * generating templates) toward the robust pattern without failing the build on\n * a technically-legal construct.\n *\n * A finding carrying `severity: 'error'` FAILS the build. The bar is: **no\n * reading of the author's metadata does what it says, deterministically, on\n * every run.** Warning about such a shape is just a slower way of finding out.\n * That covers two kinds, and only these:\n *\n * - **The runtime refuses.** {@link FLOW_RUNAS_UNSCOPED} — a user-less trigger\n * with `runAs:'user'` has no identity to scope to, so the data operation is\n * refused outright (#3760).\n * - **The declaration is inert and the route silently differs from what is\n * written.** {@link FLOW_BRANCH_LABEL_UNMATCHED} — a decision computes a\n * branch no out-edge carries, so the branch is discarded and every out-edge\n * is considered instead. {@link FLOW_DEFAULT_EDGE_WITH_CONDITION} — an edge\n * that is both the default and conditional; the condition wins and the\n * marker routes nothing. Neither *fails*; both are wrong every time, and\n * silently, which is worse (#4414).\n *\n * The bar is deliberately about *provability*, not severity of consequence. A\n * shape with a legitimate reading stays a warning even when it is usually a\n * mistake — {@link FLOW_DECISION_UNCONDITIONAL_BRANCH} is normally a guard that\n * does not guard, but a decision with one guarded and one unconditional out-edge\n * is a legal \"maybe notify, always continue\" fan-out, and\n * {@link FLOW_MULTIPLE_DEFAULT_EDGES} can genuinely mean \"when nothing matched,\n * do both\". Failing a customer's build on a shape we cannot prove wrong is a\n * worse trade than letting the warning be ignored.\n *\n * #1874 — time-relative rules via record-change date-EQUALITY. A start-node\n * trigger condition like `end_date == daysFromNow(60)` on a `record-*` trigger\n * only fires if the record happens to be written on that exact day; the robust\n * shape is a daily SCHEDULE trigger + a range query. We flag the equality form\n * specifically (range operators `>=`/`<=` are not flagged — they're the building\n * block of the correct pattern), keeping false positives near zero.\n */\n\nexport interface FlowLintFinding {\n where: string;\n message: string;\n hint: string;\n rule: string;\n /**\n * `'error'` FAILS the build; `'warning'` (the default when absent) prints and\n * continues. Most rules here flag a technically-legal footgun and stay\n * advisory. A rule is only promoted to `'error'` when the shape it flags is a\n * *guaranteed* runtime failure — then a warning would just be a slower way of\n * finding out (#3760).\n *\n * `os build` and `os validate` both filter on this field, so promoting a rule\n * gates both surfaces at once — neither can report clean while the other\n * rejects the same stack (#3782).\n */\n severity?: 'error' | 'warning';\n}\n\ntype AnyRec = Record<string, unknown>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n return [];\n}\n\n/** Extract the raw predicate source from a `condition` (string or Expression envelope). */\nfunction conditionSource(raw: unknown): string {\n if (typeof raw === 'string') return raw;\n if (raw && typeof raw === 'object' && typeof (raw as AnyRec).source === 'string') return (raw as AnyRec).source as string;\n return '';\n}\n\nconst TIME_FNS = 'daysFromNow|daysAgo|today|now|date|datetime';\nconst TIME_FN_RE = new RegExp(`\\\\b(?:${TIME_FNS})\\\\s*\\\\(`);\n// A time function adjacent to an equality operator, either side:\n// `end_date == daysFromNow(60)` / `today() != record.start`\nconst DATE_EQ = new RegExp(\n `(?:(?:${TIME_FNS})\\\\s*\\\\([^)]*\\\\)\\\\s*(?:==|!=))|(?:(?:==|!=)\\\\s*(?:${TIME_FNS})\\\\s*\\\\()`,\n);\n\nexport const FLOW_TIME_RELATIVE_ANTIPATTERN = 'flow-time-relative-antipattern';\nexport const FLOW_DATE_EQUALITY_FILTER = 'flow-date-equality-filter';\nexport const FLOW_PHANTOM_AGGREGATION = 'flow-phantom-aggregation';\nexport const FLOW_DOUBLE_BRACE_INTERP = 'flow-double-brace-interpolation';\nexport const FLOW_BARE_DOLLAR_REF = 'flow-bare-dollar-reference';\nexport const FLOW_APPROVAL_REVISE_DEAD_END = 'flow-approval-revise-dead-end';\nexport const FLOW_APPROVAL_REVISE_UNMARKED_BACKEDGE = 'flow-approval-revise-unmarked-backedge';\nexport const FLOW_APPROVAL_REVISE_DISABLED = 'flow-approval-revise-disabled';\n/**\n * #3760 — renamed from `flow-schedule-runas-unscoped`. The old id named the\n * *schedule*, which was never the boundary: the rule is about a trigger that\n * resolves NO USER, and a schedule is only the most obvious such trigger.\n */\nexport const FLOW_RUNAS_UNSCOPED = 'flow-runas-unscoped';\nexport const FLOW_ERROR_LABEL_NOT_FAULT = 'flow-error-label-not-fault';\n/** #4414 — the four ways a decision's declared branching fails to route. */\nexport const FLOW_BRANCH_LABEL_UNMATCHED = 'flow-branch-label-unmatched';\nexport const FLOW_DECISION_UNCONDITIONAL_BRANCH = 'flow-decision-unconditional-branch';\nexport const FLOW_DEFAULT_EDGE_WITH_CONDITION = 'flow-default-edge-with-condition';\nexport const FLOW_MULTIPLE_DEFAULT_EDGES = 'flow-multiple-default-edges';\n/** #4414 — `config.condition` on a node whose executor never reads it. */\nexport const FLOW_INERT_NODE_CONDITION = 'flow-inert-node-condition';\n\n/**\n * Node types that ship in the box. `config.condition` is only ever READ on the\n * `start` node (the trigger gate — `AutomationEngine.execute` and the trigger\n * bindings); every other builtin ignores it, so a predicate written there is a\n * guard that does not guard.\n *\n * Deliberately a closed list rather than \"any node type\": ADR-0018 keeps\n * `node.type` open so plugins can register their own, and a plugin executor is\n * free to declare and read `config.condition` from its own `configSchema`. We\n * can only prove the key is inert for the types we ship.\n *\n * Kept as a literal rather than imported from `FLOW_BUILTIN_NODE_TYPES` because\n * membership here means \"we have read this executor and it ignores the key\",\n * which is a stronger claim than \"this id is built in\" — a new builtin must be\n * checked, not silently inherited.\n */\nconst INERT_CONDITION_NODE_TYPES = new Set([\n 'decision', 'assignment', 'loop', 'parallel', 'try_catch',\n 'create_record', 'update_record', 'delete_record', 'get_record',\n 'http', 'notify', 'script', 'screen', 'wait', 'subflow', 'map',\n 'connector_action', 'approval', 'end',\n]);\n\n/** Node types that perform a data operation — the ones `flow.runAs` governs (#1888). */\nconst DATA_NODE_TYPES = new Set(['get_record', 'create_record', 'update_record', 'delete_record']);\n\n/**\n * #3863 — an edge LABELLED like an error path but not TYPED as one.\n *\n * Error routing is `type: 'fault'`. `label` is cosmetic on an ordinary edge, so\n * `{ source, target, label: 'error' }` without the type does not mean \"go here\n * on failure\" — it is an unconditional out-edge, and `traverseNext` runs every\n * unconditional out-edge in parallel. The handler therefore fires on every\n * SUCCESSFUL run of the source node, concurrently with the real success path,\n * and never on a failure.\n *\n * Silent in both directions: the author believes errors are handled (they are\n * not — the run still aborts) and never notices the handler running when\n * nothing went wrong. The reading is especially natural for an AI author, since\n * `label: 'error'` is exactly what the intent sounds like.\n *\n * Deliberately narrow, because a label IS meaningful on a branching node: a\n * `decision`/`approval` executor returns a `branchLabel` and traversal then\n * prefers the edge with that label, so `label: 'error'` there is a real branch\n * selector. Conditional edges are likewise legitimate. Both are excluded.\n */\nconst ERROR_LABELS = new Set(['error', 'fault', 'failure', 'failed', 'catch', 'on_error', 'onerror', 'on error']);\n\n/** Node types whose executor selects an out-edge BY LABEL (`branchLabel`). */\nconst BRANCH_LABEL_NODE_TYPES = new Set(['decision', 'approval', 'screen', 'try_catch']);\n\n/**\n * Does this flow auto-launch on a SCHEDULE (so a run carries no trigger user)?\n * Accepts the three author-time signals: `flow.type === 'schedule'`, a start-node\n * `config.triggerType === 'schedule'`, or a start-node `config.schedule` descriptor.\n */\nfunction isScheduleTriggered(flow: AnyRec, startCfg: AnyRec): boolean {\n if (flow.type === 'schedule') return true;\n if (typeof startCfg.triggerType === 'string' && startCfg.triggerType === 'schedule') return true;\n return startCfg.schedule != null;\n}\n\n/**\n * The trigger shapes that PROVABLY resolve no trigger user, with a human label\n * for the diagnostic (ADR-0073 D5, #3760). `null` when the flow's trigger either\n * supplies a user (`screen`) or may or may not, depending on who made the\n * triggering write (`record_change`, `autolaunched`) — those are not decidable\n * here and are caught at run time instead.\n *\n * Each entry is grounded in the trigger's own dispatch code, all of which build\n * an `AutomationContext` with no `userId` field at all:\n * - schedule — `trigger-schedule/src/schedule-trigger.ts`\n * - time_relative — `trigger-schedule/src/time-relative-trigger.ts`\n * - api — `trigger-api/src/api-trigger.ts` (webhook / queue)\n */\nfunction userLessTriggerKind(flow: AnyRec, startCfg: AnyRec): string | null {\n if (isScheduleTriggered(flow, startCfg)) return 'schedule';\n if (startCfg.timeRelative != null) return 'time-relative';\n if (typeof startCfg.triggerType === 'string' && startCfg.triggerType === 'time_relative') return 'time-relative';\n if (flow.type === 'api') return 'api';\n if (typeof startCfg.triggerType === 'string' && startCfg.triggerType === 'api') return 'api';\n return null;\n}\n\n/**\n * Node-config keys that name a capability the automation engine does NOT have.\n * There is no aggregate node, so a `script`/`loop`/… node carrying these keys is\n * silently ignored — the node runs and computes nothing (templates #1870,\n * `publication_rollup`). Aggregation belongs in the data layer, not a flow.\n */\nconst PHANTOM_AGG_KEYS = new Set(['aggregations', 'aggregate', 'groupBy', 'rollup', 'having']);\n\n/** If `v` is a CEL expression whose source calls a time function, return that source. */\nfunction celTimeSource(v: unknown): string | null {\n if (v && typeof v === 'object' && (v as AnyRec).dialect === 'cel') {\n const src = (v as AnyRec).source;\n if (typeof src === 'string' && TIME_FN_RE.test(src)) return src;\n }\n return null;\n}\n\n/** Range operators — the building block of the CORRECT time-window pattern, never flagged. */\nconst RANGE_OPS = new Set(['$gte', '$gt', '$lte', '$lt', '$ne']);\n\n/**\n * Walk a get_record/query `filter` for the date-EQUALITY footgun: a field bound\n * directly (`field: daysFromNow(N)`) or via `$eq` / `$in` to a time-function value.\n * A `Field.date` is stored with a time component, so two independently-computed\n * timestamps never compare equal — the query silently returns nothing (#1928 /\n * templates #1874). Range operators (`$gte`/`$lt` day windows) are the correct\n * shape and are never flagged.\n */\nfunction scanFilterForDateEquality(\n filter: unknown,\n where: string,\n findings: FlowLintFinding[],\n): void {\n if (!filter || typeof filter !== 'object' || Array.isArray(filter)) return;\n for (const [key, val] of Object.entries(filter as AnyRec)) {\n if (key === '$or' || key === '$and') {\n if (Array.isArray(val)) for (const sub of val) scanFilterForDateEquality(sub, where, findings);\n continue;\n }\n // `key` is a field name; `val` is its constraint.\n const direct = celTimeSource(val); // `field: daysFromNow(N)` → implicit equality\n let hit: { op: string; src: string } | null = direct ? { op: '==', src: direct } : null;\n if (!hit && val && typeof val === 'object' && (val as AnyRec).dialect !== 'cel') {\n for (const [op, operand] of Object.entries(val as AnyRec)) {\n if (RANGE_OPS.has(op)) continue; // correct pattern — leave it\n if (op === '$eq') {\n const s = celTimeSource(operand);\n if (s) { hit = { op: '$eq', src: s }; break; }\n } else if (op === '$in' && Array.isArray(operand)) {\n for (const item of operand) {\n const s = celTimeSource(item);\n if (s) { hit = { op: '$in', src: s }; break; }\n }\n if (hit) break;\n }\n }\n }\n if (hit) {\n findings.push({\n where,\n message:\n `filter matches \\`${key}\\` by ${hit.op} against a time value (\\`${hit.src}\\`) — a date field carries a ` +\n `time component, so exact equality against \\`${hit.src}\\` (re-computed each run) silently matches nothing.`,\n hint:\n `Use a one-day window instead: \\`${key}: { $gte: daysFromNow(N), $lt: daysFromNow(N+1) }\\` ` +\n `(wrap multiple tiers in \\`$or\\`). The abutting windows tile the timeline so each row matches exactly once. (#1874)`,\n rule: FLOW_DATE_EQUALITY_FILTER,\n });\n }\n }\n}\n\n// Flow node VALUES interpolate with SINGLE braces (`{var}` / `{rec.field}` /\n// `{$User.Id}`). Two wrong-syntax mistakes AI/human authors carry over from the\n// *formula* template dialect (`{{ path }}`) or other platforms:\n// - `{{ai_reply}}` — double-brace (verified: no flow node uses `{{ }}`).\n// - `$source.id` — a `$`-prefixed reference written bare (resolves as a\n// literal string), instead of `{source.id}`.\nconst DOUBLE_BRACE = /\\{\\{\\s*[\\w$][\\w$.\\s]*\\}\\}/;\n// A `$Ident.field` not immediately inside a `{` (so `{$User.Id}` is NOT flagged).\n// Require a letter/_ after `$` so currency like `$5.00` is never matched.\nconst BARE_DOLLAR_REF = /(?:^|[^{])\\$[A-Za-z_]\\w*\\.[A-Za-z_]/;\n\n/** Config keys whose string values are CEL predicates, not interpolated templates. */\nconst CEL_KEYS = new Set(['condition', 'expression', 'conditions']);\n\n/** Collect every interpolated-template string value in a node config (skips CEL keys). */\nfunction collectTemplateStrings(value: unknown, key: string | undefined, out: string[]): void {\n if (key && CEL_KEYS.has(key)) return;\n if (typeof value === 'string') { out.push(value); return; }\n if (Array.isArray(value)) { for (const v of value) collectTemplateStrings(v, key, out); return; }\n if (value && typeof value === 'object') {\n for (const [k, v] of Object.entries(value as AnyRec)) collectTemplateStrings(v, k, out);\n }\n}\n\n/** Edge `label`, normalized (trimmed, lowercased) for branch matching. */\nfunction edgeLabelOf(e: AnyRec): string {\n return typeof e.label === 'string' ? e.label.trim().toLowerCase() : '';\n}\n\n/**\n * ADR-0044 send-back-for-revision footguns on an approval node that declares a\n * `revise` out-edge — the two shapes an AI authoring an approval flow gets wrong:\n * - the revise branch never loops back to the approval (the submitter reworks\n * the record with nowhere to resubmit). This is a VALID DAG, so `registerFlow`\n * ACCEPTS it — the linter is the only place that catches the dead end.\n * - the loop DOES return to the approval, but the closing edge isn't declared\n * `type: 'back'`, so `registerFlow` rejects it as an un-declared cycle. The\n * lint fires at compile time with the specific fix (mark the resubmit edge).\n */\n/**\n * #3863 — flag edges labelled like an error path but left at the default type.\n * See {@link ERROR_LABELS} for why this is a footgun and what is excluded.\n */\nfunction scanErrorLabelledEdges(\n flowName: string,\n nodes: AnyRec[],\n edges: AnyRec[],\n findings: FlowLintFinding[],\n): void {\n const typeById = new Map<string, string>();\n for (const n of nodes) {\n if (typeof n.id === 'string') typeById.set(n.id, typeof n.type === 'string' ? n.type : '');\n }\n\n for (const e of edges) {\n const label = typeof e.label === 'string' ? e.label.trim().toLowerCase() : '';\n if (!ERROR_LABELS.has(label)) continue;\n if (e.type === 'fault') continue; // already an error path — nothing to say\n if (e.condition) continue; // a guarded edge is not the unconditional footgun\n const src = typeof e.source === 'string' ? e.source : '';\n // A branching node picks its out-edge BY label, so the label is load-bearing.\n if (BRANCH_LABEL_NODE_TYPES.has(typeById.get(src) ?? '')) continue;\n\n findings.push({\n where: `flow '${flowName}' · edge '${src}' → '${String(e.target)}'`,\n message:\n `edge is labelled '${String(e.label)}' but its type is '${String(e.type ?? 'default')}', not 'fault' — ` +\n `so it is an ORDINARY out-edge. Unconditional out-edges all run in parallel, so '${String(e.target)}' ` +\n `executes on every SUCCESSFUL run of '${src}' and never on a failure. The error path the label ` +\n `describes does not exist, and the run still aborts when '${src}' fails.`,\n hint:\n `Add \\`type: 'fault'\\` to this edge. Only runtime failures route — a guard refusal (a filter token ` +\n `that resolved to nothing, a missing required config key, an unscoped run) stays fatal by design and ` +\n `must be fixed in the metadata, not handled. (#3863)`,\n rule: FLOW_ERROR_LABEL_NOT_FAULT,\n });\n }\n}\n\n/**\n * #4414 — a decision node that DECLARES a branch it cannot route.\n *\n * A decision has three declared ways to pick a branch, and until #4414 only one\n * of them worked. They now compose (`branchLabel` narrows the edge set →\n * `condition` gates → `isDefault` catches the rest), but composing them still\n * leaves four authorable shapes where what the author wrote does not route what\n * they meant. All four are silent at run time — the flow completes green, having\n * taken the wrong path — so they are caught here, at authoring time:\n *\n * (1) `flow-branch-label-unmatched` — the decision's `conditions[].label` names\n * a branch no out-edge carries. Traversal cannot honour a label nothing\n * claims, so it falls back to considering EVERY out-edge. This is the\n * shipped defect: app-crm's convert-lead guard computed `'No — proceed'`\n * against out-edges labelled `'Yes'` / `'No'`, matched nothing, and ran\n * both branches.\n * (2) `flow-decision-unconditional-branch` — an out-edge of a decision that has\n * no `condition`, no `isDefault`, and no label the decision can select. It\n * is traversed on EVERY pass, in parallel with whichever branch did match,\n * so the guard next to it does not guard.\n * (3) `flow-default-edge-with-condition` — `isDefault` means \"when nothing else\n * matched\"; a condition on the same edge contradicts it (BPMN forbids a\n * conditional default flow). The condition wins and the marker is inert.\n * (4) `flow-multiple-default-edges` — two fallbacks out of one node. Both are\n * traversed when nothing matched, which is a parallel fan-out, not the\n * exclusive \"otherwise\" the marker promises.\n * (5) `flow-inert-node-condition` — `config.condition` on a node that never\n * reads it. The key is the trigger gate on `start` and dead on every other\n * builtin, so the predicate reads like a guard and gates nothing.\n *\n * (1) and (3) GATE — neither has a reading under which the author's metadata\n * routes what it says, on any run, so a warning would just be a slower way of\n * finding out. (2) and (4) stay advisory: an unconditional sibling is a legal\n * \"maybe notify, always continue\" fan-out, and two defaults can mean \"when\n * nothing matched, do both\". See the severity policy at the top of this file.\n *\n * The engine also warns when it hits (1) live — a stored flow authored before\n * this rule existed still reaches run time.\n */\nfunction scanBranchRouting(\n flowName: string,\n nodes: AnyRec[],\n edges: AnyRec[],\n findings: FlowLintFinding[],\n): void {\n const outEdgesBySource = new Map<string, AnyRec[]>();\n for (const e of edges) {\n if (e.type === 'fault') continue; // error routing, not branch selection\n const src = typeof e.source === 'string' ? e.source : '';\n if (!src) continue;\n if (!outEdgesBySource.has(src)) outEdgesBySource.set(src, []);\n outEdgesBySource.get(src)!.push(e);\n }\n\n // (3) + (4) apply to every node's out-edges, not just decisions — `isDefault`\n // is meaningful wherever conditional siblings exist.\n for (const [src, outs] of outEdgesBySource) {\n for (const e of outs) {\n if (e.isDefault === true && e.condition) {\n findings.push({\n where: `flow '${flowName}' · edge '${src}' → '${String(e.target)}'`,\n message:\n `edge sets \\`isDefault: true\\` AND a \\`condition\\` — contradictory. \\`isDefault\\` means ` +\n `\"take this edge when NO sibling condition matched\"; a condition makes it an ordinary ` +\n `guarded branch. The condition wins and the default marker routes nothing.`,\n hint:\n `Drop one: keep \\`condition\\` for a guarded branch, or drop it and keep \\`isDefault: true\\` ` +\n `for the \"otherwise\" path. (#4414)`,\n rule: FLOW_DEFAULT_EDGE_WITH_CONDITION,\n // Gating: the two keys contradict, the condition always wins, and the\n // marker never routes. No reading makes it do what it says.\n severity: 'error',\n });\n }\n }\n const defaults = outs.filter((e) => e.isDefault === true && !e.condition);\n if (defaults.length > 1) {\n findings.push({\n where: `flow '${flowName}' · node '${src}'`,\n message:\n `${defaults.length} out-edges are marked \\`isDefault: true\\` (${defaults\n .map((e) => `'${String(e.target)}'`)\n .join(', ')}) — a node has at most ONE default path. All of them are traversed together ` +\n `when no condition matches, which is a parallel fan-out, not an \"otherwise\".`,\n hint:\n `Keep \\`isDefault: true\\` on the single fallback edge and give the others a \\`condition\\` ` +\n `(or leave them unconditional if the fan-out really is intended). (#4414)`,\n rule: FLOW_MULTIPLE_DEFAULT_EDGES,\n });\n }\n }\n\n // (5) #4414 — `config.condition` on a node that never reads it.\n //\n // The key is LIVE on `start`, where it is the trigger gate, and dead\n // everywhere else: the engine parse-validates it on every node at\n // registration (so a malformed one is caught), and then no executor but the\n // start path looks at it. On a `decision` the name makes it read as the\n // branch predicate — app-todo's `check_recurring` carried one for exactly\n // that reason, a third copy of a predicate its out-edges were already\n // enforcing. Where the out-edges are NOT already deciding, the same shape is\n // a guard that does nothing and every out-edge runs.\n //\n // Advisory: the surrounding edges usually still route correctly, so this is\n // dead weight rather than a provable misroute (the gating bar is at the top\n // of this file).\n for (const node of nodes) {\n const nodeType = typeof node.type === 'string' ? node.type : '';\n if (!INERT_CONDITION_NODE_TYPES.has(nodeType)) continue;\n const cfg = (node.config ?? {}) as AnyRec;\n if (cfg.condition == null || conditionSource(cfg.condition).trim() === '') continue;\n findings.push({\n where: `flow '${flowName}' · node '${String(node.id)}' (${nodeType})`,\n message:\n `\\`config.condition\\` is set but nothing reads it — the key is the trigger gate on a \\`start\\` ` +\n `node and is ignored on every other node type, so this predicate never gates anything. ` +\n `(It is still parse-validated at registration, which is why a malformed one is caught and an ` +\n `inert one is not.)`,\n hint:\n nodeType === 'decision'\n ? `Branching lives on the OUT-EDGES: give each branch its own \\`condition\\` and mark the ` +\n `fallback \\`isDefault: true\\`. If the edges already carry the predicate, delete this copy. (#4414)`\n : `Delete it, or move the predicate to the incoming edge's \\`condition\\` if this step was ` +\n `meant to be conditional. (#4414)`,\n rule: FLOW_INERT_NODE_CONDITION,\n });\n }\n\n // (1) + (2) are about a DECISION's own declared branching.\n for (const node of nodes) {\n if (node.type !== 'decision') continue;\n const nid = typeof node.id === 'string' ? node.id : '';\n if (!nid) continue;\n const outs = outEdgesBySource.get(nid) ?? [];\n if (outs.length === 0) continue;\n\n const cfg = (node.config ?? {}) as AnyRec;\n const declaredLabels = new Set(\n (Array.isArray(cfg.conditions) ? (cfg.conditions as AnyRec[]) : [])\n .map((c) => (typeof c?.label === 'string' ? c.label.trim().toLowerCase() : ''))\n .filter(Boolean),\n );\n const edgeLabels = new Set(outs.map(edgeLabelOf).filter(Boolean));\n\n // (1) a declared branch label nothing claims. `default` is the engine's own\n // sentinel for \"no declared condition matched\" and is additionally\n // claimed by the BPMN default edge, so it is never counted as unclaimed.\n const unclaimed = [...declaredLabels].filter((l) => !edgeLabels.has(l));\n if (unclaimed.length > 0) {\n findings.push({\n where: `flow '${flowName}' · decision '${nid}'`,\n message:\n `declares branch label(s) ${unclaimed.map((l) => `'${l}'`).join(', ')} that no out-edge ` +\n `carries — out-edge labels are [${[...edgeLabels].map((l) => `'${l}'`).join(', ') || 'none'}]. ` +\n `Traversal cannot honour a label nothing claims, so it falls back to considering EVERY ` +\n `out-edge and the branch the decision computed is ignored.`,\n hint:\n `Make an out-edge's \\`label\\` match the declared branch exactly, or drop \\`config.conditions\\` ` +\n `and branch on the edges instead (\\`condition\\` per branch + \\`isDefault: true\\` on the ` +\n `fallback) — one mechanism per decision, never both. (#4414)`,\n rule: FLOW_BRANCH_LABEL_UNMATCHED,\n // Gating: a label nothing claims cannot route under ANY reading, on\n // every run. See the severity policy at the top of this file.\n severity: 'error',\n });\n }\n\n // (2) an out-edge nothing can gate: no condition, not the default, and not\n // selectable by a label the decision declares.\n const gated = outs.filter((e) => e.condition || e.isDefault === true);\n if (gated.length === 0) continue; // no branching declared at all — nothing to undercut\n const ungated = outs.filter(\n (e) => !e.condition && e.isDefault !== true && !declaredLabels.has(edgeLabelOf(e)),\n );\n if (ungated.length > 0) {\n findings.push({\n where: `flow '${flowName}' · decision '${nid}'`,\n message:\n `has guarded out-edge(s) alongside unconditional one(s) ` +\n `(${ungated.map((e) => `'${String(e.target)}'`).join(', ')}) — an unconditional out-edge is ` +\n `traversed on EVERY pass, in parallel with whichever guarded branch matched, so the ` +\n `decision does not actually exclude it. A \\`label\\` alone does not select a path unless the ` +\n `decision declares a matching \\`conditions[].label\\`.`,\n hint:\n `Mark the fallback \\`isDefault: true\\` so it is taken only when no sibling condition matched ` +\n `(BPMN default flow), or give it its own \\`condition\\`. (#4414)`,\n rule: FLOW_DECISION_UNCONDITIONAL_BRANCH,\n });\n }\n }\n}\n\nfunction scanApprovalReviseLoops(\n flowName: string,\n nodes: AnyRec[],\n edges: AnyRec[],\n findings: FlowLintFinding[],\n): void {\n const approvals = nodes.filter((n) => n.type === 'approval');\n if (approvals.length === 0) return;\n const nodeIds = new Set(nodes.map((n) => (typeof n.id === 'string' ? n.id : '')).filter(Boolean));\n const outEdges = new Map<string, AnyRec[]>();\n for (const e of edges) {\n const src = typeof e.source === 'string' ? e.source : '';\n if (!src) continue;\n if (!outEdges.has(src)) outEdges.set(src, []);\n outEdges.get(src)!.push(e);\n }\n\n for (const a of approvals) {\n const aid = typeof a.id === 'string' ? a.id : '';\n if (!aid) continue;\n const reviseTargets = edges\n .filter((e) => e.source === aid && edgeLabelOf(e) === 'revise')\n .map((e) => (typeof e.target === 'string' ? e.target : ''))\n .filter((t) => t && nodeIds.has(t));\n if (reviseTargets.length === 0) continue; // only approvals that declare a revise branch\n const where = `flow '${flowName}' \\u00b7 approval '${aid}'`;\n\n // maxRevisions:0 alongside a revise edge is self-contradictory — send-back is\n // disabled, so the branch always auto-rejects and never actually runs.\n const cfg = (a.config ?? {}) as AnyRec;\n if (cfg.maxRevisions === 0) {\n findings.push({\n where,\n message:\n `declares a 'revise' out-edge but \\`maxRevisions: 0\\` disables send-back — every revise ` +\n `auto-rejects, so the revise branch never runs.`,\n hint:\n `Set \\`maxRevisions\\` >= 1 to allow N send-backs before auto-reject, or drop the 'revise' edge ` +\n `if send-back isn't intended (ADR-0044).`,\n rule: FLOW_APPROVAL_REVISE_DISABLED,\n });\n }\n\n // BFS from the revise target(s) over ALL edges; collect edges returning to\n // the approval (target === aid). A declared loop has >=1 such edge typed `back`.\n const seen = new Set<string>(reviseTargets);\n const queue = [...reviseTargets];\n const returnEdges: AnyRec[] = [];\n while (queue.length) {\n const cur = queue.shift()!;\n for (const e of outEdges.get(cur) ?? []) {\n if (e.target === aid) returnEdges.push(e);\n const t = typeof e.target === 'string' ? e.target : '';\n if (t && nodeIds.has(t) && !seen.has(t)) {\n seen.add(t);\n queue.push(t);\n }\n }\n }\n\n if (returnEdges.length === 0) {\n findings.push({\n where,\n message:\n `has a 'revise' out-edge but no path loops back to it — the submitter reworks the record with ` +\n `nowhere to resubmit, so the revise branch dead-ends. (registerFlow accepts this — it's a valid DAG.)`,\n hint:\n `Close the loop: the 'revise' edge should reach a wait node whose resubmit edge returns to ` +\n `'${aid}' marked \\`type: 'back'\\` (ADR-0044). See examples/app-showcase showcase_budget_approval.`,\n rule: FLOW_APPROVAL_REVISE_DEAD_END,\n });\n } else if (!returnEdges.some((e) => e.type === 'back')) {\n findings.push({\n where,\n message:\n `has a 'revise' loop that returns to it, but the closing edge isn't declared \\`type: 'back'\\` — ` +\n `registerFlow rejects this as an un-declared cycle.`,\n hint:\n `Mark the resubmit edge (whose target is '${aid}') \\`type: 'back'\\` so cycle validation skips it ` +\n `while it still traverses at runtime; \\`maxRevisions\\` guards the loop (ADR-0044).`,\n rule: FLOW_APPROVAL_REVISE_UNMARKED_BACKEDGE,\n });\n }\n }\n}\n\n/**\n * Lint every flow's start node for known authoring anti-patterns. Returns a\n * (possibly empty) list of advisory findings — never throws, never fails a build.\n */\nexport function lintFlowPatterns(stack: AnyRec): FlowLintFinding[] {\n const findings: FlowLintFinding[] = [];\n for (const flow of asArray(stack.flows)) {\n const flowName = typeof flow.name === 'string' ? flow.name : '(unnamed flow)';\n const nodes = Array.isArray(flow.nodes) ? (flow.nodes as AnyRec[]) : [];\n const edges = Array.isArray(flow.edges) ? (flow.edges as AnyRec[]) : [];\n\n // (a) #1874 — date-equality time condition on a record-change start node.\n const start = nodes.find((n) => n.type === 'start');\n const startCfg = (start?.config ?? {}) as AnyRec;\n const triggerType = typeof startCfg.triggerType === 'string' ? startCfg.triggerType : '';\n if (triggerType.startsWith('record-')) {\n const src = conditionSource(startCfg.condition).trim();\n if (src && DATE_EQ.test(src)) {\n findings.push({\n where: `flow '${flowName}' · start condition`,\n message:\n `record-change trigger uses a date-EQUALITY time condition (\\`${src}\\`) — it only fires if the ` +\n `record happens to be written on that exact day, so unattended \"N days before\" rules never run.`,\n hint:\n `Use a SCHEDULE trigger (daily cron) + a range query instead — e.g. a scheduled flow whose ` +\n `get_record filters \\`end_date\\` BETWEEN {TODAY()} and {TODAY()+N}. (#1874)`,\n rule: FLOW_TIME_RELATIVE_ANTIPATTERN,\n });\n }\n }\n\n // (a4) #1888 / ADR-0049 / ADR-0073 D5 — a trigger that resolves NO USER at\n // runtime (schedule, time-relative, api/webhook/queue) combined with an\n // effective `runAs:'user'` (explicit, or unset → the spec default) is a\n // CONFIGURATION ERROR: there is no user to scope to. Since #3760 the\n // runtime REFUSES the data operation rather than running it unscoped, so\n // this shape is a guaranteed run-time failure — which is why it fails the\n // build instead of warning. Only flagged when the flow actually performs\n // a data operation (otherwise `runAs` is moot and the run is fine).\n //\n // This rule is necessary but NOT sufficient, and deliberately so: a\n // record-change flow fired by a write that carried no user hits exactly\n // the same refusal, but whether a given write carries a user is not\n // knowable at authoring time. That case is caught at run time only\n // (#3760) — do not try to approximate it here.\n const runAs = typeof flow.runAs === 'string' ? flow.runAs : 'user';\n const userLessKind = userLessTriggerKind(flow, startCfg);\n if (userLessKind && runAs !== 'system') {\n const dataNode = nodes.find((n) => DATA_NODE_TYPES.has(typeof n.type === 'string' ? (n.type as string) : ''));\n if (dataNode) {\n const declared = typeof flow.runAs === 'string' ? `\\`runAs:'${runAs}'\\`` : `the default \\`runAs:'user'\\``;\n findings.push({\n where: `flow '${flowName}' · runAs`,\n message:\n `${userLessKind}-triggered flow runs as ${declared}, but a ${userLessKind} run has no trigger ` +\n `user — so its data node '${dataNode.id}' (${dataNode.type}) has no identity to scope to and ` +\n `will be REFUSED at run time.`,\n hint:\n `Declare \\`runAs:'system'\\` to make the elevation explicit and intended (the run reads/writes ` +\n `every record). A ${userLessKind} flow cannot scope to a user — there is none. ` +\n `(ADR-0049, ADR-0073 D5, #1888, #3760)`,\n rule: FLOW_RUNAS_UNSCOPED,\n severity: 'error',\n });\n }\n }\n\n // (b) #1315 — wrong interpolation syntax in any node's template values. Flow\n // node values use SINGLE braces; double-brace `{{ }}` and bare `$ref.x`\n // are carried over from the formula template dialect / other platforms.\n for (const node of nodes) {\n const nodeWhere = `flow '${flowName}' · node '${node.id}' (${node.type})`;\n\n // (a2) #1874 — date-EQUALITY (`==`/`$eq`/`$in`) against a time value in a\n // query filter. A scheduled flow that filters this way silently matches\n // nothing; the robust shape is a `$gte`/`$lt` day window.\n const cfg = (node.config ?? {}) as AnyRec;\n if (cfg.filter) scanFilterForDateEquality(cfg.filter, `${nodeWhere} filter`, findings);\n\n // (a3) #1870 — a node-config key naming a non-existent capability (there is\n // no aggregate node) is silently ignored at runtime, so the node\n // computes nothing. Point the author at the data-layer equivalent.\n for (const key of Object.keys(cfg)) {\n if (PHANTOM_AGG_KEYS.has(key)) {\n findings.push({\n where: nodeWhere,\n message:\n `node config has \\`${key}\\` — the automation engine has no aggregate node, so \\`${key}\\` is ` +\n `silently ignored and this node computes nothing at runtime.`,\n hint:\n `Aggregation belongs in the data layer: use \\`Field.summary\\` for a cross-object rollup ` +\n `(sum/count of children), or \\`Field.formula\\` for a per-record computed value. (#1870)`,\n rule: FLOW_PHANTOM_AGGREGATION,\n });\n }\n }\n\n const strings: string[] = [];\n collectTemplateStrings(node.config, undefined, strings);\n for (const str of strings) {\n if (DOUBLE_BRACE.test(str)) {\n findings.push({\n where: nodeWhere,\n message: `double-brace interpolation \\`${str.trim().slice(0, 80)}\\` — flow node values use SINGLE braces.`,\n hint: `Use \\`{var}\\` (e.g. \\`{record.title}\\`). Double-brace \\`{{ }}\\` is the formula/template-field dialect, not flow node values. (#1315)`,\n rule: FLOW_DOUBLE_BRACE_INTERP,\n });\n }\n if (BARE_DOLLAR_REF.test(str)) {\n findings.push({\n where: nodeWhere,\n message: `\\`${str.trim().slice(0, 80)}\\` looks like a reference written as a literal — a bare \\`$ref.field\\` is NOT interpolated.`,\n hint: `Wrap it and bind a variable: \\`{source.id}\\` (or \\`{$User.Id}\\` for the current user). (#1315)`,\n rule: FLOW_BARE_DOLLAR_REF,\n });\n }\n }\n }\n\n // (c) ADR-0044 — approval send-back-for-revision loop footguns.\n scanApprovalReviseLoops(flowName, nodes, edges, findings);\n\n // (d) #3863 — an edge labelled like an error path but typed 'default' is an\n // unconditional out-edge: the handler runs on every SUCCESS, in parallel\n // with the real path, and never on a failure.\n scanErrorLabelledEdges(flowName, nodes, edges, findings);\n\n // (e) #4414 — a decision that declares a branch it cannot route: an\n // unclaimable branch label, an unconditional sibling that runs anyway,\n // or a self-contradictory / duplicated `isDefault` marker.\n scanBranchRouting(flowName, nodes, edges, findings);\n }\n return findings;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time lint that closes the spec-liveness loop on the AUTHOR side.\n *\n * The liveness ledgers (`@objectstack/spec/liveness/<type>.json`) classify every\n * authorable metadata property as live / experimental / dead with evidence. The\n * CI gate enforces that classification is *complete*, but the ledger's knowledge\n * never reached the person (very often an AI) writing the metadata. This lint\n * surfaces it: when an authored object/field sets a property the ledger marks as\n * dead-and-misleading (or experimental), it emits an advisory WARNING — \"you set\n * this expecting it to do something; at runtime it does nothing\" — with a hint\n * toward the supported alternative. It NEVER fails the build.\n *\n * Signal over noise is the whole point, so the ledger opts in per entry via\n * `\"authorWarn\": true` (+ an optional `\"authorHint\"`). A property being merely\n * `dead` is NOT enough — plenty of dead props are benign display/doc metadata.\n * Only entries an author would be *misled* by are marked. Booleans warn only when\n * set truthy (so schema defaults like `enable.searchable` never trip it); object/\n * string/array props warn when present at all.\n */\n\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\n\nexport interface LivenessLintFinding {\n where: string;\n message: string;\n hint: string;\n rule: string;\n}\n\nexport const LIVENESS_DEAD_PROPERTY = 'liveness-dead-property';\nexport const LIVENESS_EXPERIMENTAL_PROPERTY = 'liveness-experimental-property';\n\ntype AnyRec = Record<string, unknown>;\n\ninterface LedgerEntry {\n status?: string;\n authorWarn?: boolean;\n authorHint?: string;\n note?: string;\n children?: Record<string, LedgerEntry>;\n}\n\n/** Flattened, warn-only view of a type's ledger: propPath → entry (incl. `a.b` children). */\ntype WarnMap = Map<string, LedgerEntry>;\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n return [];\n}\n\n/** Locate `@objectstack/spec`'s shipped `liveness/` dir (workspace src or published files). */\nfunction resolveLivenessDir(): string | null {\n try {\n const require = createRequire(import.meta.url);\n const pkgJson = require.resolve('@objectstack/spec/package.json');\n const dir = join(dirname(pkgJson), 'liveness');\n return existsSync(dir) ? dir : null;\n } catch {\n return null;\n }\n}\n\n/** Build the warn-only lookup for one type, flattening one level of `children`. */\nfunction loadWarnMap(dir: string, type: string): WarnMap {\n const map: WarnMap = new Map();\n const file = join(dir, `${type}.json`);\n if (!existsSync(file)) return map;\n let ledger: { props?: Record<string, LedgerEntry> };\n try {\n ledger = JSON.parse(readFileSync(file, 'utf8'));\n } catch {\n return map;\n }\n const props = ledger.props || {};\n for (const [key, entry] of Object.entries(props)) {\n if (entry?.children) {\n for (const [ck, centry] of Object.entries(entry.children)) {\n if (shouldWarn(centry)) map.set(`${key}.${ck}`, centry);\n }\n }\n if (shouldWarn(entry)) map.set(key, entry);\n }\n return map;\n}\n\n/** An entry warns when explicitly opted in, OR when it's experimental (a declared-but-unenforced guarantee). */\nfunction shouldWarn(entry: LedgerEntry | undefined): boolean {\n if (!entry) return false;\n return entry.authorWarn === true || entry.status === 'experimental';\n}\n\n/** A value that signals authoring intent: booleans only when truthy; everything else when present. */\nfunction isAuthored(value: unknown): boolean {\n if (value === undefined || value === null) return false;\n if (typeof value === 'boolean') return value === true;\n return true;\n}\n\nfunction describe(entry: LedgerEntry): { kind: string; rule: string } {\n if (entry.status === 'experimental') {\n return { kind: 'is experimental — declared but NOT enforced at runtime', rule: LIVENESS_EXPERIMENTAL_PROPERTY };\n }\n return { kind: 'has no runtime effect (liveness: dead)', rule: LIVENESS_DEAD_PROPERTY };\n}\n\n/** Check one metadata item's set properties against its type's warn-map. */\nfunction checkItem(\n type: string,\n item: AnyRec,\n whereBase: string,\n warnMap: WarnMap,\n findings: LivenessLintFinding[],\n): void {\n for (const [path, entry] of warnMap) {\n const values = path.includes('.')\n ? getNested(item, path)\n : [item[path]];\n for (const value of values instanceof Array ? values : [values]) {\n if (!isAuthored(value)) continue;\n const { kind, rule } = describe(entry);\n const hint = entry.authorHint\n ?? entry.note\n ?? 'Remove it — it is declared in the spec but not consumed at runtime.';\n findings.push({\n where: whereBase,\n message: `sets \\`${path}\\` but this ${type} property ${kind}.`,\n hint,\n rule,\n });\n break; // one finding per (item, path) even when the container is an array\n }\n }\n}\n\n/**\n * Resolve a dotted path one or more levels, treating a missing parent as\n * absent. A container level that is an ARRAY fans out over its elements\n * (e.g. `nodes.outputSchema` on a flow checks every node), returning the\n * list of resolved values.\n */\nfunction getNested(obj: AnyRec, path: string): unknown[] {\n let cur: unknown[] = [obj];\n for (const seg of path.split('.')) {\n const next: unknown[] = [];\n for (const c of cur) {\n if (c === null || typeof c !== 'object') continue;\n const v = Array.isArray(c) ? undefined : (c as AnyRec)[seg];\n if (Array.isArray(c)) {\n for (const el of c) {\n if (el && typeof el === 'object') next.push((el as AnyRec)[seg]);\n }\n } else {\n next.push(v);\n }\n }\n cur = next;\n }\n // Final level may itself contain arrays-of-values; flatten one step so a\n // trailing array container (e.g. `measures` → each measure) fans out too.\n return cur.flatMap((v) => (Array.isArray(v) ? v : [v]));\n}\n\n/**\n * The compiled-stack collection each governed metadata type lives in.\n * `object`/`field` keep their bespoke walk (fields nest under objects);\n * everything else is a flat top-level array on the stack definition.\n */\nconst TYPE_COLLECTIONS: Array<{ type: string; key: string }> = [\n { type: 'flow', key: 'flows' },\n { type: 'action', key: 'actions' },\n { type: 'agent', key: 'agents' },\n { type: 'tool', key: 'tools' },\n { type: 'skill', key: 'skills' },\n { type: 'dataset', key: 'datasets' },\n { type: 'permission', key: 'permissions' },\n { type: 'hook', key: 'hooks' },\n { type: 'page', key: 'pages' },\n { type: 'view', key: 'views' },\n { type: 'webhook', key: 'webhooks' },\n // #4487. Note what adding a TYPE costs versus adding a warned property: the\n // doc below is right that coverage grows by marking entries `authorWarn` —\n // but only WITHIN a type already listed here. A newly governed type needs its\n // collection registered or its ledger warns nobody, which would leave the\n // ledger correct and silent: the exact shape this lint exists to prevent.\n { type: 'datasource', key: 'datasources' },\n // #4488 — the six newly governed types that carry `authorWarn` entries.\n // (doc / seed / validation are governed too but warn on nothing today, so\n // they are not listed; add them here the day one of their entries warns.)\n { type: 'app', key: 'apps' },\n { type: 'book', key: 'books' },\n { type: 'job', key: 'jobs' },\n { type: 'email_template', key: 'emailTemplates' },\n { type: 'mapping', key: 'mappings' },\n { type: 'translation', key: 'translations' },\n];\n\n/**\n * Lint the compiled stack for authored properties the liveness ledger flags as\n * misleading. Advisory only — returns findings, never throws. Covers every\n * governed metadata type: objects (incl. `enable.*`) and their fields walk\n * bespoke nesting; the remaining types are flat stack collections. Container\n * properties fan out over arrays (each flow node, each dataset measure). The\n * mechanism stays ledger-driven — coverage grows by marking more entries\n * `authorWarn` rather than touching this code.\n */\nexport function lintLivenessProperties(stack: AnyRec): LivenessLintFinding[] {\n const dir = resolveLivenessDir();\n if (!dir) return [];\n\n const findings: LivenessLintFinding[] = [];\n\n const objectWarn = loadWarnMap(dir, 'object');\n const fieldWarn = loadWarnMap(dir, 'field');\n for (const obj of asArray(stack.objects)) {\n const objName = typeof obj.name === 'string' ? obj.name : '(unnamed object)';\n if (objectWarn.size > 0) checkItem('object', obj, `object '${objName}'`, objectWarn, findings);\n if (fieldWarn.size > 0) {\n for (const field of asArray(obj.fields)) {\n const fieldName = typeof field.name === 'string' ? field.name : '(unnamed field)';\n checkItem('field', field, `object '${objName}' · field '${fieldName}'`, fieldWarn, findings);\n }\n }\n }\n\n for (const { type, key } of TYPE_COLLECTIONS) {\n const warnMap = loadWarnMap(dir, type);\n if (warnMap.size === 0) continue;\n for (const item of asArray(stack[key])) {\n // view containers bind via `object`, not `name`\n const name = typeof item.name === 'string' ? item.name\n : typeof item.object === 'string' ? item.object\n : `(unnamed ${type})`;\n checkItem(type, item, `${type} '${name}'`, warnMap, findings);\n }\n }\n\n return findings;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time lint for `autonumber` field formats. An `autonumberFormat` may\n * interpolate other fields of the same record (`{plan_no}{000}`,\n * `{section}{island_zone}{000}`). That field value forms the counter SCOPE, so\n * if it is missing at create time the record number silently collapses into the\n * wrong scope — and the runtime now throws rather than emit a wrong number\n * (see sql-driver / engine `missingFieldValues`). This lint catches the two\n * ways an author (very often an AI generating templates) gets that wrong,\n * BEFORE it ships:\n *\n * - ERROR: `{field}` names a field that does not exist on the object — the\n * generation will always throw. This is broken, so it fails the build.\n * - WARNING: `{field}` names an OPTIONAL field — generation throws on any\n * record left blank. The robust shape marks the referenced field\n * `required: true` (mirroring ERPNext/Odoo, where a field that drives the\n * naming series must be mandatory). Advisory; does not fail the build.\n *\n * A self-reference (`{self}` on the autonumber field itself) is always an\n * ERROR — the value does not exist yet when the format renders.\n */\n\nimport { parseAutonumberFormat, referencedFields } from '@objectstack/spec/data';\n\nexport interface AutonumberLintFinding {\n where: string;\n message: string;\n hint: string;\n rule: string;\n severity: 'error' | 'warning';\n}\n\ntype AnyRec = Record<string, unknown>;\n\nexport const AUTONUMBER_UNKNOWN_FIELD = 'autonumber-references-unknown-field';\nexport const AUTONUMBER_OPTIONAL_FIELD = 'autonumber-references-optional-field';\nexport const AUTONUMBER_SELF_REFERENCE = 'autonumber-references-self';\nexport const AUTONUMBER_LITERAL_TOKEN = 'autonumber-unrecognized-token';\n\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') {\n return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n }\n return [];\n}\n\n/**\n * Lint every `autonumber` field's format for unresolvable / fragile `{field}`\n * interpolation. Returns a (possibly empty) list of findings; never throws.\n */\nexport function lintAutonumberFormats(stack: AnyRec): AutonumberLintFinding[] {\n const findings: AutonumberLintFinding[] = [];\n for (const obj of asArray(stack.objects)) {\n const objectName = typeof obj.name === 'string' ? obj.name : '(unnamed object)';\n const fields = asArray(obj.fields);\n // name → required?, for schema-aware reference checks.\n const fieldMeta = new Map<string, { required: boolean }>();\n for (const f of fields) {\n if (typeof f.name === 'string') fieldMeta.set(f.name, { required: f.required === true });\n }\n\n for (const f of fields) {\n if (f.type !== 'autonumber') continue;\n const name = typeof f.name === 'string' ? f.name : '(unnamed field)';\n const fmt = typeof f.autonumberFormat === 'string'\n ? f.autonumberFormat\n : (typeof f.format === 'string' ? f.format : '');\n if (!fmt) continue;\n const tokens = parseAutonumberFormat(fmt);\n const refs = referencedFields(tokens);\n const where = `object '${objectName}' · field '${name}' (autonumber \"${fmt}\")`;\n\n // An unrecognized `{...}` group is kept as literal text by the parser, so\n // it ships VERBATIM in the record number. This catches case/spacing/typo\n // mistakes the field-reference checks miss — date tokens are exact\n // (`{YYYY}`, not `{yyyy}` or `{ YYYY }`), and only one `{0..0}` slot counts.\n for (const t of tokens) {\n if (t.kind !== 'literal') continue;\n const braced = t.text.match(/\\{[^{}]*\\}/g);\n if (!braced) continue;\n for (const tok of braced) {\n const body = tok.slice(1, -1);\n const isExtraSeq = /^0+$/.test(body);\n findings.push({\n where,\n message: isExtraSeq\n ? `format has a second sequence slot \\`${tok}\\` — only the first \\`{0..0}\\` counts; this one renders literally as \"${tok}\".`\n : `format has an unrecognized token \\`${tok}\\` — it is not a counter/date/{field} token, so it renders literally as \"${tok}\" in every record number.`,\n hint: isExtraSeq\n ? `Use a single \\`{0000}\\` slot; fold any second number into a literal or a {field} token.`\n : `Date tokens are case-sensitive and exact: {YYYY} {YY} {MM} {DD} {YYYYMMDD} (no spaces/punctuation inside). For a field value use {field_name}.`,\n rule: AUTONUMBER_LITERAL_TOKEN,\n severity: 'warning',\n });\n }\n }\n for (const ref of refs) {\n if (ref === name) {\n findings.push({\n where,\n message: `format interpolates \\`{${ref}}\\` — its own value, which does not exist yet when the number is generated.`,\n hint: `Reference a DIFFERENT field that is set before create (e.g. \\`{plan_no}{000}\\`), or drop the token.`,\n rule: AUTONUMBER_SELF_REFERENCE,\n severity: 'error',\n });\n continue;\n }\n const meta = fieldMeta.get(ref);\n if (!meta) {\n findings.push({\n where,\n message: `format interpolates \\`{${ref}}\\`, but object '${objectName}' has no field named '${ref}' — generation will always throw.`,\n hint: `Reference an existing field, or remove the \\`{${ref}}\\` token from the format.`,\n rule: AUTONUMBER_UNKNOWN_FIELD,\n severity: 'error',\n });\n } else if (!meta.required) {\n findings.push({\n where,\n message: `format interpolates \\`{${ref}}\\`, but '${ref}' is optional — any record left blank fails autonumber generation at create time.`,\n hint: `Mark '${ref}' as \\`required: true\\` so it is always set before the record number is rendered.`,\n rule: AUTONUMBER_OPTIONAL_FIELD,\n severity: 'warning',\n });\n }\n }\n }\n }\n return findings;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Build-time lint for VIEW REFERENCES — closes the gap where a form action can\n * point at the wrong view and only fail at runtime (a blank/broken form, or a\n * silent no-op submit). This shifts objectui's runtime `viewKind` guard left to\n * the build, where the author — very often an AI generating templates —\n * discovers the mistake on `os compile` instead of when an end user clicks.\n *\n * Severity follows the broken/fragile split, tuned so upgrading does NOT break\n * existing apps that merely have a colliding key — only a high-confidence,\n * genuinely-broken reference fails the build:\n *\n * view-ref-form-target-kind — ERROR (fails the build)\n * A `type:'form'` action whose `target` resolves to an existing LIST view\n * opens a blank form (and a submit can silently no-op) at runtime. This is\n * the concrete #2554 breakage and is high-confidence, so it fails the build.\n *\n * view-key-collision (#2554) — WARNING\n * List and form views share one `<object>.<key>` namespace during expansion,\n * and the default `list` implicitly claims `<object>.default`. A colliding\n * key is renamed (`<object>.<key>` → `<object>.<key>_2`) so the registry key\n * stays unique. The rename alone is only *fragile* — it breaks something only\n * if that name is referenced — so it warns rather than failing the build.\n *\n * view-ref-form-target-missing — WARNING\n * A `type:'form'` target that resolves to no view is probably a typo, but it\n * may also be a view this lint failed to collect (a non-standard container\n * shape), so it warns rather than risk a false-positive build failure.\n *\n * Deliberately conservative to keep false positives near zero: only `type:'form'`\n * targets are checked (the one type that unambiguously names a form view),\n * interpolated targets (`${…}`) are skipped as non-static, and non-qualified\n * targets (no `.`) are treated as opaque handler/modal refs rather than view\n * references.\n */\n\nimport { expandViewContainerWithDiagnostics, isAggregatedViewContainer } from '@objectstack/spec';\n\nexport interface ViewRefFinding {\n where: string;\n message: string;\n hint: string;\n rule: string;\n severity: 'error' | 'warning';\n}\n\ntype AnyRec = Record<string, any>;\n\n/** Normalise a record-or-map metadata slot into an array, injecting `name` from\n * the map key (mirrors the helper in the sibling authoring lints). */\nfunction asArray(v: unknown): AnyRec[] {\n if (Array.isArray(v)) return v as AnyRec[];\n if (v && typeof v === 'object') return Object.entries(v as AnyRec).map(([name, def]) => ({ name, ...(def as AnyRec) }));\n return [];\n}\n\nexport const VIEW_KEY_COLLISION = 'view-key-collision';\nexport const VIEW_REF_FORM_TARGET_MISSING = 'view-ref-form-target-missing';\nexport const VIEW_REF_FORM_TARGET_KIND = 'view-ref-form-target-kind';\n\n/** Pull the view-container slots out of an object definition (ADR-0017 nested\n * \"Object has-many View\"). Absent slots stay undefined — the expander ignores\n * them. */\nfunction containerFromObject(obj: AnyRec): AnyRec {\n return { list: obj.list, form: obj.form, listViews: obj.listViews, formViews: obj.formViews };\n}\n\n/** Derive the object a top-level `defineView` container binds to. A container\n * has no top-level `name`/`object`, so — exactly like the runtime loader's\n * `resolveMetadataItemName('views', …)` — fall back to its inner default\n * `list`/`form` data source. Kept in lock-step with that resolver so the lint's\n * expansion keys match the ones the engine actually registers. */\nfunction viewContainerObjectName(item: AnyRec): string | undefined {\n if (typeof item.name === 'string' && item.name) return item.name;\n if (typeof item.id === 'string' && item.id) return item.id;\n if (typeof item.object === 'string' && item.object) return item.object;\n if (typeof item.list?.data?.object === 'string') return item.list.data.object;\n if (typeof item.form?.data?.object === 'string') return item.form.data.object;\n return undefined;\n}\n\nexport function lintViewRefs(stack: AnyRec): ViewRefFinding[] {\n const findings: ViewRefFinding[] = [];\n\n // Expanded view name -> the kind(s) registered under it. A Set (not a scalar)\n // so the target check stays correct even if a list and a form ever share a\n // name — a form target is satisfied iff `form` is among the kinds.\n const viewKinds = new Map<string, Set<'list' | 'form'>>();\n const indexKind = (name: string, kind: 'list' | 'form') => {\n let s = viewKinds.get(name);\n if (!s) viewKinds.set(name, (s = new Set()));\n s.add(kind);\n };\n\n // 1) Gather every aggregated container: top-level `views` + object-nested.\n const containers: Array<{ object: string; container: AnyRec }> = [];\n for (const v of asArray(stack.views)) {\n if (v.viewKind) {\n // Already an independent, expanded ViewItem — index it directly.\n if (typeof v.name === 'string') indexKind(v.name, v.viewKind === 'form' ? 'form' : 'list');\n continue;\n }\n if (!isAggregatedViewContainer(v)) continue;\n const object = viewContainerObjectName(v);\n if (object) containers.push({ object, container: v });\n }\n for (const obj of asArray(stack.objects)) {\n const object = typeof obj.name === 'string' ? obj.name : undefined;\n if (!object) continue;\n if (obj.list || obj.form || obj.listViews || obj.formViews) {\n containers.push({ object, container: containerFromObject(obj) });\n }\n }\n\n // 2) Expand each container: index names + report every collision as an error.\n for (const { object, container } of containers) {\n const { items, collisions } = expandViewContainerWithDiagnostics(object, container);\n for (const it of items) indexKind(it.name, it.viewKind);\n for (const col of collisions) {\n findings.push({\n where: `object '${object}' · view key '${col.key}'`,\n message:\n `View key collision: the ${col.viewKind} view '${col.requested}' clashes with another view ` +\n `in the same container and was renamed to '${col.renamedTo}'. Anything referencing ` +\n `'${col.requested}' (a form action target, a navigation viewName) resolves to the OTHER view, not this one.`,\n hint:\n `Give the ${col.viewKind} view a unique key — the default list implicitly claims '<object>.default'. ` +\n `Renaming key '${col.key}' fixes both this collision and any reference that targets it.`,\n rule: VIEW_KEY_COLLISION,\n severity: 'warning',\n });\n }\n }\n\n // 3) Validate every `type:'form'` action target against the expanded view set.\n // An action often appears BOTH top-level and nested under its object, so\n // dedupe by (name, target): a shared action is reported once, not twice.\n const seenFormTargets = new Set<string>();\n const checkAction = (action: AnyRec, ownerObject?: string) => {\n if (!action || action.type !== 'form') return;\n const target = action.target;\n if (typeof target !== 'string' || !target) return;\n if (target.includes('${')) return; // dynamic interpolation — not statically resolvable\n if (!target.includes('.')) return; // non-qualified — treated as an opaque ref, not a view\n\n const actionName = typeof action.name === 'string' ? action.name : '(unnamed)';\n const dedupeKey = `${actionName}\\u0000${target}`;\n if (seenFormTargets.has(dedupeKey)) return;\n seenFormTargets.add(dedupeKey);\n const where = ownerObject ? `action '${actionName}' on object '${ownerObject}'` : `action '${actionName}'`;\n\n const kinds = viewKinds.get(target);\n if (!kinds) {\n findings.push({\n where,\n message:\n `Form action target '${target}' does not resolve to any view. A type:'form' action must point at ` +\n `an existing form view; at runtime this opens a blank/broken form.`,\n hint: `Check for a typo, or a form view renamed by a key collision. Expected a form view named '<object>.<formViewKey>'.`,\n rule: VIEW_REF_FORM_TARGET_MISSING,\n severity: 'warning',\n });\n return;\n }\n if (!kinds.has('form')) {\n const actual = [...kinds].join('/');\n findings.push({\n where,\n message:\n `Form action target '${target}' resolves to a ${actual} view, not a form view. Opening a ${actual} ` +\n `view through a form action renders an empty form (and a submit can silently no-op) at runtime.`,\n hint:\n `Point target at a form view (viewKind 'form'). If the form view was renamed by a key collision, ` +\n `fix the colliding key so '${target}' names the form again.`,\n rule: VIEW_REF_FORM_TARGET_KIND,\n severity: 'error',\n });\n }\n };\n\n // Object-nested first so the retained (deduped) finding keeps object context.\n for (const obj of asArray(stack.objects)) {\n const object = typeof obj.name === 'string' ? obj.name : undefined;\n for (const action of asArray(obj.actions)) checkAction(action, object);\n }\n for (const action of asArray(stack.actions)) checkAction(action);\n\n return findings;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Data-model best-practice lint rules.\n *\n * These rules encode the relationship / master-detail / roll-up conventions the\n * platform ships (see the objectstack-data and objectstack-ui skills, ADR-0035).\n * They run over the normalized object set and flag anti-patterns that an\n * author — human OR an AI generator — commonly produces. They are intentionally\n * heuristic: structural problems are `error`, likely-wrong choices are\n * `warning`, and \"you probably want this\" nudges are `suggestion`. None of them\n * block on a judgement call.\n *\n * The same rules double as the automated rubric for the metadata-generation\n * eval (see `score.ts`): a generated stack scores well exactly when it is\n * schema-valid AND lint-clean here.\n */\n\nexport type Severity = 'error' | 'warning' | 'suggestion';\n\nexport interface LintIssue {\n severity: Severity;\n rule: string;\n message: string;\n path: string;\n fix?: string;\n}\n\n// ─── Heuristics ─────────────────────────────────────────────────────\n\nconst RELATIONSHIP_TYPES = new Set(['lookup', 'master_detail']);\nconst NUMERIC_TYPES = new Set([\n 'number', 'currency', 'integer', 'decimal', 'percent', 'float', 'double',\n]);\nconst OPTION_FIELD_TYPES = new Set(['select', 'multiselect', 'radio', 'enum']);\nconst NAME_LIKE_FIELDS = ['name', 'title', 'subject', 'label', 'full_name', 'display_name', 'code'];\n\n/** Child object names that read as line-items / composition (entered with the parent). */\nconst LINE_ITEM_RE = /_(line|lines|line_item|line_items|item|items|detail|details|entry|entries)$/;\n/** Child object names that read as associations (comments/audit/activity — NOT line items). */\nconst ASSOCIATION_TOKENS = [\n 'comment', 'attachment', 'note', 'log', 'audit', 'activity', 'activities',\n 'history', 'event', 'reaction', 'like', 'mention', 'notification', 'message',\n];\n\nfunction isLineItemName(name: string): boolean {\n return LINE_ITEM_RE.test(name);\n}\n\nfunction isAssociationName(name: string): boolean {\n const lc = name.toLowerCase();\n return ASSOCIATION_TOKENS.some((t) => lc === t || lc.endsWith(`_${t}`) || lc.endsWith(`_${t}s`));\n}\n\ninterface FieldEntry {\n name: string;\n def: any;\n}\n\nfunction fieldEntries(fields: any): FieldEntry[] {\n if (!fields) return [];\n if (Array.isArray(fields)) {\n return fields.filter((f) => f && f.name != null).map((f) => ({ name: String(f.name), def: f }));\n }\n return Object.entries<any>(fields).map(([name, def]) => ({ name, def }));\n}\n\nfunction refOf(def: any): string | undefined {\n return def?.reference || def?.reference_to;\n}\n\n// ─── Uniqueness declarations ────────────────────────────────────────\n\nexport const UNIQUE_DOUBLE_DECLARATION = 'unique/double-declaration';\n\n/** Is `unique` declared at all? Mirrors `isUniqueDeclared` in @objectstack/spec/data. */\nfunction uniqueDeclared(u: unknown): boolean {\n return u === true || u === 'global';\n}\n\n/**\n * R10 — the same column carries BOTH a field-level `unique: true` and an\n * object-level single-column unique index (#3991).\n *\n * The two spellings are deliberately different (see `IndexSchema`): field-level\n * `unique: true` is tenant-scoped since #3696 — it materializes as\n * `(organization_id, col)`, unique *within* the tenant — while a declared index\n * is materialized over exactly the columns listed, i.e. platform-wide. Both are\n * legitimate on their own; together on one column they are never right:\n *\n * - On a tenant-scoped object they CONTRADICT. The stricter one wins\n * physically, so the global index enforces uniqueness and the tenant\n * composite becomes a constraint nothing can ever trip. One of the two\n * intents the author wrote is silently discarded.\n * - On a tenancy-less object they are exactly REDUNDANT — both describe the\n * same single-column unique index, under the same generated name.\n *\n * Tenancy is deliberately NOT inferred here: `organization_id` is injected by\n * the kernel at registration rather than authored, so an authoring-time guess\n * would be wrong half the time. The combination is worth flagging either way,\n * and the message names both readings so the author picks the one they meant.\n *\n * A field declared `unique: 'global'` is exempt: it already says\n * platform-wide, so the declared index restates the same intent rather than\n * contradicting it (still redundant, but not a silent loss of meaning).\n *\n * Advisory. The resulting stack is well-defined — the cost is an intent that\n * never takes effect, not a broken artifact — so this never fails a build.\n */\nexport function lintUniqueDeclarations(objects: any[]): LintIssue[] {\n const issues: LintIssue[] = [];\n if (!Array.isArray(objects) || objects.length === 0) return issues;\n\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj?.name) continue;\n const declaredIndexes = Array.isArray(obj.indexes) ? obj.indexes : [];\n if (declaredIndexes.length === 0) continue;\n\n // Columns covered by a declared SINGLE-column unique index. A composite\n // (`['organization_id', 'email']`) is the explicit tenant-scoped spelling —\n // it agrees with the field-level default rather than fighting it.\n const singleColumnUniqueIndexes = new Map<string, any>();\n for (const idx of declaredIndexes) {\n if (!uniqueDeclared(idx?.unique)) continue;\n const cols = Array.isArray(idx?.fields) ? idx.fields.filter((f: unknown) => typeof f === 'string') : [];\n if (cols.length !== 1) continue;\n if (!singleColumnUniqueIndexes.has(cols[0])) singleColumnUniqueIndexes.set(cols[0], idx);\n }\n if (singleColumnUniqueIndexes.size === 0) continue;\n\n for (const { name, def } of fieldEntries(obj.fields)) {\n if (!uniqueDeclared(def?.unique)) continue;\n if (def.unique === 'global') continue; // already says platform-wide — no lost intent\n const idx = singleColumnUniqueIndexes.get(name);\n if (!idx) continue;\n const indexLabel = typeof idx?.name === 'string' && idx.name.trim() ? ` '${idx.name.trim()}'` : '';\n issues.push({\n severity: 'warning',\n rule: UNIQUE_DOUBLE_DECLARATION,\n message:\n `\"${obj.name}.${name}\" declares field-level \\`unique: true\\` AND a single-column unique index${indexLabel} on the same column. ` +\n `Since #3696 the field-level form is scoped per tenant — \\`(tenant, ${name})\\` — while a declared index is materialized ` +\n `over exactly its \\`fields\\`, i.e. platform-wide. On a tenant-scoped object the global index wins and the per-tenant ` +\n `constraint can never be reached; on a tenancy-less object the two are the same index declared twice. Either way one of ` +\n `the two declarations has no effect.`,\n path: `objects[${i}]`,\n fix:\n `Pick the intent: for platform-wide uniqueness set \\`unique: 'global'\\` on '${name}' and drop the duplicate index; ` +\n `for per-tenant uniqueness drop the index (the field-level declaration already builds the tenant composite), ` +\n `or spell the index out as \\`fields: ['organization_id', '${name}']\\` if you want it explicit.`,\n });\n }\n }\n return issues;\n}\n\n// ─── Rule engine ────────────────────────────────────────────────────\n\n/**\n * Lint the relationship / data-modeling conventions across the full object set.\n * Pure and deterministic — safe to call from both the `lint` command and the\n * metadata-generation scorer.\n */\nexport function lintDataModel(objects: any[]): LintIssue[] {\n // R10 lives in its own exported function so `os build` can run that ONE rule\n // without pulling in the whole best-practice sweep (#3991).\n const issues: LintIssue[] = lintUniqueDeclarations(objects);\n if (!Array.isArray(objects) || objects.length === 0) return issues;\n\n // Index: parent object name → child relationships pointing at it.\n const childrenByParent: Record<string, Array<{ child: any; fieldName: string; def: any }>> = {};\n for (const child of objects) {\n if (!child?.name) continue;\n for (const { name: fieldName, def } of fieldEntries(child.fields)) {\n if (!RELATIONSHIP_TYPES.has(def?.type)) continue;\n const parent = refOf(def);\n if (!parent) continue;\n (childrenByParent[parent] ||= []).push({ child, fieldName, def });\n }\n }\n\n for (let i = 0; i < objects.length; i++) {\n const obj = objects[i];\n if (!obj?.name) continue;\n const objPath = `objects[${i}]`;\n const fields = fieldEntries(obj.fields);\n\n // R9 — object should have a derivable display/primary field.\n const hasNameField =\n !!obj.primaryField ||\n !!obj.titleFormat ||\n fields.some((f) => NAME_LIKE_FIELDS.includes(f.name));\n if (fields.length > 0 && !hasNameField) {\n issues.push({\n severity: 'suggestion',\n rule: 'object/missing-name-field',\n message: `Object \"${obj.name}\" has no name/title field or primaryField — records will display as raw IDs`,\n path: `${objPath}.fields`,\n });\n }\n\n for (const { name: fieldName, def } of fields) {\n if (!def || typeof def !== 'object') continue;\n const fieldPath = `${objPath}.fields.${fieldName}`;\n const type = def.type;\n\n // R8 — option fields need options (or an options source).\n if (OPTION_FIELD_TYPES.has(type)) {\n const hasOptions =\n (Array.isArray(def.options) && def.options.length > 0) ||\n !!def.optionsFrom || !!def.dataSource || !!def.reference;\n if (!hasOptions) {\n issues.push({\n severity: 'warning',\n rule: 'field/select-missing-options',\n message: `${type} field \"${obj.name}.${fieldName}\" has no options`,\n path: `${fieldPath}.options`,\n });\n }\n }\n\n if (!RELATIONSHIP_TYPES.has(type)) continue;\n const parent = refOf(def);\n\n // R1 — relationship fields must declare a reference target.\n if (!parent) {\n issues.push({\n severity: 'error',\n rule: 'relationship/missing-reference',\n message: `${type} field \"${obj.name}.${fieldName}\" is missing a reference target`,\n path: `${fieldPath}.reference`,\n });\n continue;\n }\n\n if (type === 'master_detail') {\n // R2 — master-detail children should require their parent.\n if (def.required !== true) {\n issues.push({\n severity: 'warning',\n rule: 'relationship/master-detail-required',\n message: `master_detail \"${obj.name}.${fieldName}\" → ${parent} should be required (a detail record cannot exist without its master)`,\n path: `${fieldPath}.required`,\n fix: 'required: true',\n });\n }\n // R3 — be explicit about cascade behaviour.\n if (def.deleteBehavior === undefined) {\n issues.push({\n severity: 'suggestion',\n rule: 'relationship/delete-behavior',\n message: `master_detail \"${obj.name}.${fieldName}\" → ${parent} should declare deleteBehavior (cascade/restrict/set_null)`,\n path: `${fieldPath}.deleteBehavior`,\n fix: \"deleteBehavior: 'cascade'\",\n });\n }\n // R5 — line-item children are usually entered inline with the parent.\n if (isLineItemName(obj.name) && def.inlineEdit !== true) {\n issues.push({\n severity: 'suggestion',\n rule: 'relationship/line-items-inline-edit',\n message: `\"${obj.name}\" looks like line items of ${parent}; consider inlineEdit: true on \"${fieldName}\" so it is entered inline within the ${parent} form`,\n path: `${fieldPath}.inlineEdit`,\n fix: 'inlineEdit: true',\n });\n }\n }\n\n // R4 — a line-item-shaped child should usually be master_detail, not lookup.\n if (type === 'lookup' && isLineItemName(obj.name)) {\n issues.push({\n severity: 'suggestion',\n rule: 'relationship/line-item-should-be-master-detail',\n message: `\"${obj.name}\" looks like line items of ${parent} but uses lookup; master_detail gives ownership + cascade + roll-ups`,\n path: `${fieldPath}.type`,\n fix: \"type: 'master_detail'\",\n });\n }\n\n // R6 — associations should NOT be inlined into the parent's entry form.\n if (def.inlineEdit === true && isAssociationName(obj.name)) {\n issues.push({\n severity: 'warning',\n rule: 'relationship/association-inline-edit',\n message: `\"${obj.name}\" is an association (comments/audit/activity), not line items — inlineEdit clutters the ${parent} entry form; surface it as a detail-page related list instead`,\n path: `${fieldPath}.inlineEdit`,\n fix: 'remove inlineEdit (use relatedList on the detail page)',\n });\n }\n }\n\n // R7 — a parent of master_detail children with numeric fields should roll one up.\n const children = childrenByParent[obj.name] || [];\n const summaryChildObjects = new Set(\n fields\n .filter((f) => f.def?.type === 'summary')\n .map((f) => f.def?.summaryOperations?.object || f.def?.reference)\n .filter(Boolean),\n );\n const seenSuggestedChild = new Set<string>();\n for (const { child, def } of children) {\n if (def?.type !== 'master_detail') continue;\n if (!child?.name || seenSuggestedChild.has(child.name)) continue;\n if (summaryChildObjects.has(child.name)) continue;\n // Only nudge when the child actually has something worth aggregating.\n const numericChildField = fieldEntries(child.fields).find((f) => NUMERIC_TYPES.has(f.def?.type));\n if (!numericChildField) continue;\n seenSuggestedChild.add(child.name);\n issues.push({\n severity: 'suggestion',\n rule: 'rollup/missing-summary',\n message: `\"${obj.name}\" owns \"${child.name}\" (master_detail) with numeric field \"${numericChildField.name}\" but has no roll-up summary; consider a summary field (count/sum) on ${obj.name}`,\n path: `${objPath}.fields`,\n fix: `summary field aggregating ${child.name}.${numericChildField.name}`,\n });\n }\n }\n\n return issues;\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The author-time rule registry — WHICH rules each authoring surface runs,\n * declared as data, with a written reason for every narrowing (#4409, #4463).\n *\n * Lives in `@objectstack/lint` (not the CLI) since #4463: the CLI is one\n * CONSUMER of this table, not its home. The runtime metadata write path\n * (`saveMetaItem` / `publishMetaItem` — the Studio, REST `/meta` and MCP door)\n * is the other, and it reads the same array through `./runtime.js`. That is the\n * whole point: a shared core plus N gates, never N rule engines.\n *\n * ## Why this exists\n *\n * Each of the three authoring commands grew its own import list and its own\n * call site per rule. Nothing connected them, so \"which rules run here?\" was\n * answerable only by reading three 800-line files and diffing them by eye — and\n * the answer drifted every time a rule landed. At the point this registry was\n * written, 23 of the 26 hand-wired rules ran on some strict subset of the three,\n * and nine of those could emit `severity: 'error'`. The worst direction was not\n * the obvious one: `os build` was the WEAKEST of the three gates, so it emitted\n * an artifact for stacks `os validate` or `os lint` refuses. A flow whose\n * expression approver does not parse (`approval-expression-invalid`, an `error`)\n * built and published green — only `os lint` stopped it, and CI usually runs the\n * other two.\n *\n * That failure mode had already been fixed four times, one instance at a time:\n * the reference-integrity suite (#3583 §5 D5), the four CLI-local authoring\n * lints that ran on `build` alone (#3782), `validateReadonlyFlowWrites` missing\n * from `lint` (#4384/#4394), and the wiring guard that followed (#4402). Each\n * repair removed an instance and left the MODE — a rule's command coverage was\n * whatever its author remembered to type, and forgetting was silent. This file\n * replaces \"remembering\" with a table, and the guard in\n * `authoring-rule-wiring.test.ts` makes a narrowing an explicit,\n * reasoned edit instead of an omission.\n *\n * ## The invariant\n *\n * **Any rule that can emit `error` runs on all three commands.** A gate is only\n * as strong as the weakest command an author or CI happens to run, so a gating\n * rule with partial coverage is not a stricter check — it is a coin flip.\n *\n * #4463 is the same sentence one layer out: the three commands are three doors\n * in ONE wall, and the runtime metadata write path is a fourth wall with no\n * door at all. A tenant editing in Studio, a REST `/meta` PUT and an MCP/AI\n * author all reach `saveMetaItem`, which ran a per-type Zod `safeParse` and\n * nothing else — so the very stack `os build` refuses walked in through the\n * only door most tenants have. {@link AuthoringRule.surfaces} is what makes\n * \"which rules run at that door?\" a table entry rather than a second wiring\n * list, and `authoring-rule-wiring.test.ts` ratchets it exactly as it ratchets\n * the three commands.\n *\n * An `advisory` rule (never emits `error`) MAY be scoped to fewer commands, but\n * only with a `scopeReason` recorded here. The distinction that matters is not\n * cost, it is consequence: a missing advisory costs the author a hint, a missing\n * gate ships broken metadata.\n *\n * Cost, as it turns out, argues for almost nothing. The heavy dependencies\n * (`typescript` ~9 MB, `sucrase` ~1.5 MB) are already lazy and load only when a\n * stack actually carries the metadata that needs them — a contract pinned by\n * `@objectstack/lint`'s `lazy-deps.test.ts`. `validateReactPageProps`, the\n * heaviest rule of the set, has run on all three commands as a suite member\n * since #4340 without anyone noticing a cost. So `os lint` stays light on the\n * stacks that do not use those surfaces, whether or not the rules are wired.\n *\n * ## Adding a rule\n *\n * Append one entry here. It reaches all three commands at once and nothing else\n * needs editing. Do NOT import the rule into a command file — the wiring guard\n * fails on a direct import, because that is precisely how a rule ends up running\n * on two commands out of three.\n *\n * Then answer the fourth-door question in the same edit: does it belong on\n * `surfaces: ['cli', 'runtime-publish']`? Say yes (with `runtimeTypes`) or say\n * why not (with `surfaceReason`). There is no third option — the guard rejects\n * an entry that answers neither, which is the whole mechanism: #4409 was\n * \"nobody wrote down which commands\", #4463 was \"nobody wrote down which\n * doors\", and both were invisible until someone measured.\n *\n * ## What is NOT in here\n *\n * This registry covers the rules the three commands SHARE. Two neighbouring\n * families are deliberately outside it, and the guard's ratchet lists them by\n * name so the boundary stays a decision rather than an oversight:\n *\n * - **`os lint`'s own style rubric** (snake_case names, missing labels, the\n * data-model best-practice sweep, docs, i18n coverage). Its `error` severity\n * is a LINT verdict, not a publish gate — `os build` has never rejected a\n * camelCase object name and making it do so is a product decision, not a\n * wiring fix.\n * - **Gates that need more than the stack** — the capability-provider preflight\n * (reads `node_modules`), package docs (reads `src/docs/`), the access-matrix\n * snapshot (reads/writes a file next to the config). They are I/O, not pure\n * metadata rules, and each is wired where its input exists.\n */\n\n// Imported per-module, never through `./index.js`: the barrel would make this\n// file a cycle partner of its own package entry, and the runtime surface\n// (`./runtime.js`) needs a graph it can reason about rule by rule.\nimport { validateStackExpressions } from './validate-expressions.js';\nimport { validateListViewMode } from './validate-list-view-mode.js';\nimport { validateFunctionalCompleteness } from './validate-functional-completeness.js';\nimport { validateViewContainers } from './validate-view-containers.js';\nimport { validateWidgetBindings } from './validate-widget-bindings.js';\nimport { validateDashboardActionRefs } from './validate-dashboard-action-refs.js';\nimport { validateFilterTokens } from './validate-filter-tokens.js';\nimport { validateReferenceIntegrity } from './reference-integrity-suite.js';\nimport { validateResponsiveStyles } from './validate-responsive-styles.js';\nimport { validateJsxPages } from './validate-jsx-pages.js';\nimport { validateReactPages } from './validate-react-pages.js';\nimport { validatePageSourceStyling } from './validate-page-source-styling.js';\nimport { validateCapabilityReferences } from './validate-capability-references.js';\nimport { validateFlowTriggerReadiness } from './validate-flow-trigger-readiness.js';\nimport { validateApprovalApprovers } from './validate-approval-approvers.js';\nimport { validateRecordTitle } from './validate-record-title.js';\nimport { validateSemanticRoles } from './validate-semantic-roles.js';\nimport { validateFormLayout } from './validate-form-layout.js';\nimport { validateSeedReplaySafety } from './validate-seed-replay-safety.js';\nimport { validateSeedStateMachine } from './validate-seed-state-machine.js';\nimport { validateVisibilityPredicates } from './validate-visibility-predicates.js';\nimport { validateSecurityPosture } from './validate-security-posture.js';\nimport { validateOrgAxisRedLines } from './validate-org-axis-red-lines.js';\nimport { validateActionLocations } from './validate-action-locations.js';\nimport { lintFlowPatterns } from './lint-flow-patterns.js';\nimport { lintLivenessProperties } from './lint-liveness-properties.js';\nimport { lintAutonumberFormats } from './lint-autonumber-formats.js';\nimport { lintViewRefs } from './lint-view-refs.js';\nimport { lintUniqueDeclarations } from './data-model-rules.js';\n\ntype AnyRec = Record<string, unknown>;\n\n// ─── Types ──────────────────────────────────────────────────────────\n\n/** The three commands that hold a stack to the same author-time bar. */\nexport const AUTHORING_COMMANDS = ['validate', 'build', 'lint'] as const;\nexport type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];\n\n/**\n * The authoring SURFACES this registry serves (#4463).\n *\n * - `cli` — `os validate` / `os build` / `os lint`. Which of the three is a\n * separate axis ({@link AuthoringRule.commands}); every rule here runs on the\n * `cli` surface, because that is where the registry was born.\n * - `runtime-publish` — the metadata write path's PUBLISH gate: a\n * `state: 'active'` `saveMetaItem`, and the draft→active promotion in\n * `publishMetaItem`. Studio saves, REST `/meta` item CRUD and MCP/AI\n * authoring all funnel through those two, so wiring the surface once covers\n * all three doors (the maintainer ruling on #4463: one shared core, one\n * runtime gate, not a gate per surface).\n *\n * Draft saves are deliberately NOT a surface: a draft is allowed to be a\n * half-finished thing, and gating one would break the Studio editing loop for\n * no safety gain — the draft cannot execute until it is published, and\n * publishing runs this table (#4463 D1).\n */\nexport const AUTHORING_SURFACES = ['cli', 'runtime-publish'] as const;\nexport type AuthoringSurface = (typeof AUTHORING_SURFACES)[number];\n\n/** `error` gates. `warning` advises. `info` is a suggestion (`os lint` grades it as one). */\nexport type AuthoringSeverity = 'error' | 'warning' | 'info';\n\n/**\n * The one finding shape all three commands render. Rules whose own return type\n * predates it are adapted at their registry entry, so the commands hold one\n * type instead of a twenty-way union.\n */\nexport interface AuthoringFinding {\n severity: AuthoringSeverity;\n /** Stable diagnostic rule id (used by docs, allowlists and `--json` consumers). */\n rule: string;\n /** Human-readable location, e.g. `object \"leave_request\"`. */\n where: string;\n /** Config path, e.g. `objects[3].sharingModel`. */\n path: string;\n /** What is wrong. */\n message: string;\n /** How to fix it. */\n hint: string;\n}\n\n/**\n * `gating` = the rule can emit `severity: 'error'`, so it MUST run on all three\n * commands. `advisory` = it never does, and may be scoped with a reason.\n *\n * The claim is not taken on trust: the wiring guard reads each `advisory` rule's\n * own source and fails if it emits an `error`. That check is the reason the tier\n * is worth declaring — #3760 promoted a `lintFlowPatterns` rule from advisory to\n * gating, and nothing anywhere asked whether its command coverage should follow.\n */\nexport type AuthoringRuleTier = 'gating' | 'advisory';\n\n/**\n * Which tier of the stack a rule reads.\n *\n * - `normalized` — the `normalizeStackInput` output, BEFORE the Zod parse. The\n * rules that need it check keys the parse strips (a flat list view in\n * `views: []`, `userFilters` on an object list view, a `visibleOn` alias): by\n * the time `result.data` exists the evidence is gone.\n * - `parsed` — the post-parse stack, where defaults are filled and shapes are\n * settled.\n *\n * `os lint` never parses (it is the cheap pre-flight; a schema error is\n * `os validate`'s verdict to give), so it runs BOTH tiers on the normalized\n * stack. Every rule here is written to tolerate that — it is what `os lint`\n * already did for the reference-integrity suite and the security linter.\n */\nexport type AuthoringRuleInputTier = 'normalized' | 'parsed';\n\n/** Per-run inputs a rule may need beyond the stack itself. */\nexport interface AuthoringRuleContext {\n /** ADR-0080 SDUI component manifest, when the project ships one. */\n sduiManifest?: unknown;\n}\n\nexport interface AuthoringRule {\n /** The exported function's name — the id the wiring guard asserts on. */\n name: string;\n tier: AuthoringRuleTier;\n input: AuthoringRuleInputTier;\n /** Which commands run it. Must be all three when `tier` is `gating`. */\n commands: readonly AuthoringCommand[];\n /** Repo-relative path to the rule's implementation (the guard verifies the tier claim against it). */\n source: string;\n /** REQUIRED when `commands` is not all three: why this rule is scoped. */\n scopeReason?: string;\n /**\n * Which authoring surfaces run this rule (#4463). Always contains `'cli'`.\n *\n * Adding `'runtime-publish'` is what puts the rule on the metadata write\n * path's publish gate; {@link runtimeTypes} then says which metadata types'\n * writes it inspects. Leaving it off REQUIRES {@link surfaceReason} — the\n * same discipline `scopeReason` applies to the command axis, for the same\n * reason: the whole defect class #4409 and #4463 describe is coverage that\n * narrowed silently.\n */\n surfaces: readonly AuthoringSurface[];\n /**\n * REQUIRED when `surfaces` includes `'runtime-publish'`: the SINGULAR\n * metadata type names whose runtime write this rule inspects (e.g. `flow`).\n *\n * The runtime gate builds a per-write stack snapshot and only runs the rules\n * that declare the written type, which is #4463 D2 option (c) expressed as\n * data. Widening a rule to another type is a one-line edit here, not new\n * wiring at the gate.\n */\n runtimeTypes?: readonly string[];\n /** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */\n surfaceReason?: string;\n run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];\n}\n\n/** Every command runs every rule unless an entry says otherwise. */\nconst ALL: readonly AuthoringCommand[] = AUTHORING_COMMANDS;\n\n/** The CLI-only surface set — the default for a rule the runtime gate does not (yet) run. */\nconst CLI_ONLY: readonly AuthoringSurface[] = ['cli'];\n/** Runs on both the three CLI commands and the runtime publish gate. */\nconst CLI_AND_RUNTIME: readonly AuthoringSurface[] = ['cli', 'runtime-publish'];\n\n// ─── Why a rule is not on the runtime publish gate ──────────────────\n//\n// #4463's P1 slice deliberately wires ONE metadata type (`flow`) and the four\n// rule families the issue named (flow / approval / expression / reference).\n// Every other rule carries one of the reasons below rather than silence. They\n// are shared constants because the reason really is the same for a group of\n// rules — writing twenty near-identical sentences would hide which ones differ.\n\n/**\n * The rule reads a stack-wide COLLECTION the per-write snapshot does not carry\n * (pages, dashboards, navigation, translations, seeds, permission sets). The\n * runtime universe can answer these — it is strictly more complete than the\n * CLI's single-package view — but building that snapshot is #4463 P2, and\n * shipping the rule against a partial snapshot would invent findings for\n * metadata the tenant simply did not include in THIS write. A false 422 on the\n * only door a Studio tenant has is worse than the gap it would close.\n */\nconst RUNTIME_NEEDS_FULL_SNAPSHOT =\n 'P2 (#4463): reads a stack-wide collection the per-write snapshot does not carry, so running it ' +\n 'now would report the rest of the tenant\\'s metadata as missing rather than judging this write.';\n\n/**\n * The rule parses authored SOURCE (react/jsx page bodies, L2 JS hook/action\n * bodies) through `typescript` / `sucrase`. Those are exactly the dependencies\n * `lazy-deps.test.ts` keeps off the kernel boot path, and `@objectstack/lint`'s\n * runtime entry is guarded to load neither. Studio's page editor has its own\n * save-time compile path; this gate is not where that check belongs.\n */\nconst RUNTIME_HEAVY_SOURCE_PARSE =\n 'Not runtime-safe: parses authored source through typescript/sucrase, the two dependencies the ' +\n 'kernel boot path must never load (lazy-deps.test.ts). Studio compiles page source on its own path.';\n\n/**\n * The rule judges an OBJECT/field declaration. Object writes are the hottest\n * metadata path there is (every Studio field edit) and the blast radius of a\n * wrong 422 there is the whole product, so P1 does not gate them — the issue's\n * own worked example, and every acceptance criterion on it, is a flow.\n */\nconst RUNTIME_OBJECT_WRITES_P2 =\n 'P2 (#4463): judges an object/field declaration. Object writes are the hottest metadata path in ' +\n 'the product, so P1 gates `flow` first and widens once the gate has real traffic behind it.';\n\n/**\n * `ExprIssue` is the one rule finding that carries no rule id of its own — it\n * predates the `{ rule, path, hint }` shape every other rule settled on. Given\n * one here so `os lint --json` and the docs can name it like any other.\n */\nexport const EXPRESSION_INVALID = 'expression-invalid';\n\n// ─── The registry ───────────────────────────────────────────────────\n\n/**\n * Every author-time rule the three commands share, in the order their findings\n * are reported.\n */\nexport const AUTHORING_RULES: readonly AuthoringRule[] = [\n // ADR-0032 §1a/1b — CEL predicates in actions/validations/flows/sharing/hooks\n // are parsed for syntax AND checked that each `record.<field>` resolves. This\n // is what catches a BARE field ref (`done` instead of `record.done`) that\n // would otherwise silently hide an action on every record (#2183/#2185).\n {\n name: 'validateStackExpressions',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-expressions.ts',\n // Runtime publish gate (#4463): the EXPRESSION family. On a flow write it\n // checks every script-node callable and every declared predicate the flow\n // carries, against the live object universe — the same parse `os build`\n // runs, now at the door Studio/REST/MCP authors actually use.\n surfaces: CLI_AND_RUNTIME,\n runtimeTypes: ['flow'],\n run: (stack) =>\n validateStackExpressions(stack).map((i) => ({\n severity: i.severity ?? 'error',\n rule: EXPRESSION_INVALID,\n where: i.where,\n path: i.where,\n message: i.message,\n hint: `source: \\`${i.source}\\``,\n })),\n },\n // ADR-0053 — `userFilters`/`quickFilters` on an object list view (\"views\"\n // mode) are silently dropped: `ObjectListViewSchema` omits them, so this must\n // read the pre-parse tier or the evidence is already gone.\n {\n name: 'validateListViewMode',\n tier: 'gating',\n input: 'normalized',\n commands: ALL,\n source: 'packages/lint/src/validate-list-view-mode.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateListViewMode(stack),\n },\n // [ADR-0078] A Zod-VALID instance that silently does nothing: a `summary`\n // with no `summaryOperations`, a `lookup` with no `reference`, a `select`\n // with no `options`. Every key is one we know, so #4001's unknown-key\n // rejection cannot see it, and the liveness ledger cannot either (it is\n // per-property; the properties ARE live). This is the gate between them.\n //\n // `gating` because the error-severity shapes are fully inert — the field\n // reads 0 forever while authoring reports success, which is the failure the\n // ADR was written for (cloud#687). Pre-parse so the findings survive an\n // unrelated schema error elsewhere in the stack.\n {\n name: 'validateFunctionalCompleteness',\n tier: 'gating',\n input: 'normalized',\n commands: ALL,\n source: 'packages/lint/src/validate-functional-completeness.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_OBJECT_WRITES_P2,\n run: (stack) => validateFunctionalCompleteness(stack),\n },\n // A flat list-view object in `views: []` parses to an EMPTY container\n // (ViewSchema strips unknown keys): the schema step passes, zero views\n // register, and the Console renders nothing. Pre-parse for the same reason.\n {\n name: 'validateViewContainers',\n tier: 'gating',\n input: 'normalized',\n commands: ALL,\n source: 'packages/lint/src/validate-view-containers.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateViewContainers(stack),\n },\n // ADR-0021 (#1719/#1721) — a widget's `dataset`/`dimensions`/`values` and its\n // chartConfig axis/series must resolve against the declared datasets.\n {\n name: 'validateWidgetBindings',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-widget-bindings.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateWidgetBindings(stack),\n },\n // ADR-0049 / #3367 — a header or widget action naming a `script`/`modal`\n // target that resolves to no defined action ships a button that renders and\n // silently does nothing on click. Unresolved `url` routes stay advisory.\n {\n name: 'validateDashboardActionRefs',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-dashboard-action-refs.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateDashboardActionRefs(stack),\n },\n // #3574 — a filter value like `{current_user}` resolves in no vocabulary,\n // reaches the data engine as a literal and matches nothing. The surface\n // renders empty with no error, and a silent zero is indistinguishable from a\n // genuine one at review time.\n {\n name: 'validateFilterTokens',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-filter-tokens.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateFilterTokens(stack),\n },\n // The reference-integrity suite (#3583 §5 D5) — itself a registry, of the\n // rules that answer \"does this name resolve to anything?\". It reached all\n // three commands before this file existed; it is an entry here so the two\n // registries compose instead of competing, and so its members are covered by\n // the same guard as everything else.\n {\n name: 'validateReferenceIntegrity',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/reference-integrity-suite.ts',\n // Runtime publish gate (#4463): the REFERENCE family. On a flow snapshot the\n // members that answer a flow question run (`validateFlowTemplatePaths`,\n // `validateFlowNodeWrites`, `validateReadonlyFlowWrites`,\n // `validateObjectReferences`); the page/nav/AI members see no such\n // collection in the snapshot and return nothing, and the two that would\n // load `typescript` need a hook/action/react body the snapshot never\n // carries — which is what `runtime-lazy-deps.test.ts` pins.\n surfaces: CLI_AND_RUNTIME,\n runtimeTypes: ['flow'],\n run: (stack) => validateReferenceIntegrity(stack),\n },\n // ADR-0065 — a styled node's responsiveStyles must be scopable (needs an\n // `id`), name real CSS properties + design tokens, and carry a `large` base.\n {\n name: 'validateResponsiveStyles',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-responsive-styles.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateResponsiveStyles(stack),\n },\n // ADR-0080 — a `kind:'jsx'` page's `source` is parsed (never executed) and\n // compiled to the SDUI tree at save time, so malformed source must fail loudly\n // here (ADR-0078) instead of being stored and breaking only at render.\n {\n name: 'validateJsxPages',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-jsx-pages.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_HEAVY_SOURCE_PARSE,\n run: (stack, ctx) =>\n validateJsxPages(stack, ctx.sduiManifest ? { manifest: ctx.sduiManifest as never } : {}),\n },\n // ADR-0081 — a `kind:'react'` page's `source` is real React executed at\n // render. Transpiled here (Sucrase, never executed) so a syntax error fails at\n // author time, not at render. Lazy: only a stack with such a page pays.\n {\n name: 'validateReactPages',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-react-pages.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_HEAVY_SOURCE_PARSE,\n run: (stack) => validateReactPages(stack),\n },\n // ADR-0065, source tier — Tailwind `className` in a `kind:'html'`/`'react'`\n // page silently no-ops (the build never scans authored metadata).\n {\n name: 'validatePageSourceStyling',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-page-source-styling.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validatePageSourceStyling(stack),\n },\n // ADR-0066 ⑨ — a `requiredPermissions` entry naming a capability registered\n // nowhere fails closed at runtime. Advisory: another installed package may\n // legitimately provide it.\n {\n name: 'validateCapabilityReferences',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-capability-references.ts',\n surfaces: CLI_ONLY,\n surfaceReason: 'P2 (#4463): the ONE rule the runtime universe makes strictly stronger — the advisory hedge (\"another '\n + 'installed package may provide it\") is decidable against the live capability registry, so it '\n + 'graduates from advisory to gating there rather than merely being ported. That promotion is a '\n + 'severity change on a published rule id and belongs in its own PR, not riding a wiring change.',\n run: (stack) => validateCapabilityReferences(stack),\n },\n // A record-change flow whose start-node objectName matches nothing never\n // fires — silently. Reads the pre-parse tier so an author sees what they\n // wrote. Advisory: the object may come from another installed package.\n {\n name: 'validateFlowTriggerReadiness',\n tier: 'advisory',\n input: 'normalized',\n commands: ALL,\n source: 'packages/lint/src/validate-flow-trigger-readiness.ts',\n // Runtime publish gate (#4463): the FLOW family. Advisory at this surface\n // too — its findings are logged, not thrown (P1 gates on `error` only; P2\n // puts advisories on the response for Studio to render).\n surfaces: CLI_AND_RUNTIME,\n runtimeTypes: ['flow'],\n run: (stack) => validateFlowTriggerReadiness(stack),\n },\n // ADR-0090 D3 fallout — an approval `{ type: 'role' }` resolves against the\n // better-auth org-membership tier, not positions, so a position name authored\n // there routes the approval to nobody; and an expression approver that does\n // not parse can never resolve. The rule whose absence from `os build` and\n // `os validate` was #4409's worked example: it gates, and it ran on `os lint`\n // alone, so a broken approval flow built and published green.\n {\n name: 'validateApprovalApprovers',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-approval-approvers.ts',\n // Runtime publish gate (#4463): the APPROVAL family, and the issue's worked\n // example both times. #4409 fixed it for `os build`; a tenant saving the\n // same broken expression approver from Studio still sailed through, because\n // `approver.value` is just a string to Zod. It is not just a string here.\n surfaces: CLI_AND_RUNTIME,\n runtimeTypes: ['flow'],\n run: (stack) => validateApprovalApprovers(stack),\n },\n // ADR-0079 — `titleFormat` is retired in favour of `nameField`, and an object\n // with no resolvable title ships records with no meaningful name. Advisory:\n // auto-provision and the `Record #<id>` floor keep it from ever being fatal.\n {\n name: 'validateRecordTitle',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-record-title.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_OBJECT_WRITES_P2,\n run: (stack) => validateRecordTitle(stack),\n },\n // ADR-0085 — `stageField` / `highlightFields` / `Field.group` are pointers\n // into the object's field map; a dangling one is Zod-valid and silently inert\n // at render. Advisory: every consumer degrades gracefully.\n {\n name: 'validateSemanticRoles',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-semantic-roles.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_OBJECT_WRITES_P2,\n run: (stack) => validateSemanticRoles(stack),\n },\n // #2578 / #4449 — a form section's field reference that resolves to nothing\n // (silently not rendered) and an absolute `colSpan` under a per-surface\n // derived column count. Advisory: the renderer skips the unknown field and\n // clamps the span, so nothing is broken — but each is almost certainly an\n // authoring mistake, and until #4449 this rule ran on no command at all.\n // Pure structured-metadata walk (no lazy dependency), so wiring it to all\n // three costs nothing measurable.\n {\n name: 'validateFormLayout',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-form-layout.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateFormLayout(stack),\n },\n // ADR-0078 Phase 3 (Tier-A `action-locations`) — an action that declares no\n // `locations` and that no view places by name renders on no surface at all.\n // objectui#3142 made that measurable: four renderers used to show an\n // undeclared action anyway, and now none does. Advisory: a view in another\n // installed package may be the one placing it, and `locations: []` (the\n // documented headless shape) is deliberately never flagged.\n {\n name: 'validateActionLocations',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-action-locations.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateActionLocations(stack),\n },\n // framework#3434 — seeds replay on every boot, so a `mode: 'insert'` dataset\n // duplicates its table on every restart.\n {\n name: 'validateSeedReplaySafety',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-seed-replay-safety.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateSeedReplaySafety(stack),\n },\n // framework#3433 follow-up — #3433 exempts seed writes from the\n // `state_machine` rule, so a seeded status the FSM does not declare is no\n // longer rejected at write time. Re-added at author time; advisory, because\n // the exemption itself is legitimate.\n {\n name: 'validateSeedStateMachine',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-seed-state-machine.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateSeedStateMachine(stack),\n },\n // ADR-0089 D3b — deprecated visibility aliases and a mis-layered binding root.\n // Pre-parse: the schema folds `visibleOn`/`visibility` into `visibleWhen`\n // during parse, so the alias the author wrote is gone from `result.data`.\n {\n name: 'validateVisibilityPredicates',\n tier: 'advisory',\n input: 'normalized',\n commands: ALL,\n source: 'packages/lint/src/validate-visibility-predicates.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateVisibilityPredicates(stack),\n },\n // #1874 — flow authoring anti-patterns. Advisory by default; a finding marked\n // `error` gates. Three do today: `flow-runas-unscoped` (#3760 — metadata the\n // runtime REFUSES to execute), plus `flow-branch-label-unmatched` and\n // `flow-default-edge-with-condition` (#4414 — a declaration that is inert, so\n // the route silently differs from what the author wrote). The bar for\n // promoting one is stated at the top of `lint-flow-patterns.ts`.\n {\n name: 'lintFlowPatterns',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/lint-flow-patterns.ts',\n // Runtime publish gate (#4463): the FLOW family's anti-pattern half. Its\n // three `error` rules are the sharpest fit for a publish gate that exists —\n // `flow-runas-unscoped` is metadata the automation engine REFUSES to\n // execute, so publishing it can only ever produce a broken flow.\n surfaces: CLI_AND_RUNTIME,\n runtimeTypes: ['flow'],\n run: (stack) =>\n lintFlowPatterns(stack).map((f) => ({\n severity: f.severity ?? 'warning',\n rule: f.rule,\n where: f.where,\n path: f.where,\n message: f.message,\n hint: f.hint,\n })),\n },\n // The spec-liveness loop on the author side: a property the ledger marks\n // dead-and-misleading or experimental is set hopefully and does nothing.\n // Ledger-driven (entries opt in via `authorWarn`), so it is high-signal and\n // never fatal.\n {\n name: 'lintLivenessProperties',\n tier: 'advisory',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/lint-liveness-properties.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_OBJECT_WRITES_P2,\n run: (stack) =>\n lintLivenessProperties(stack).map((f) => ({\n severity: 'warning' as const,\n rule: f.rule,\n where: f.where,\n path: f.where,\n message: f.message,\n hint: f.hint,\n })),\n },\n // A format like `{plan_no}{000}` makes the referenced field part of the\n // counter scope, so it must exist and be set at create time. Unknown field →\n // broken (error); optional field → fragile (warning).\n {\n name: 'lintAutonumberFormats',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/lint-autonumber-formats.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_OBJECT_WRITES_P2,\n run: (stack) =>\n lintAutonumberFormats(stack).map((f) => ({\n severity: f.severity,\n rule: f.rule,\n where: f.where,\n path: f.where,\n message: f.message,\n hint: f.hint,\n })),\n },\n // #2554 — a `type:'form'` action target naming a missing or LIST view opens a\n // broken form at runtime; a list/form view-key collision silently renames one\n // view so references resolve to the OTHER. Both are broken.\n {\n name: 'lintViewRefs',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/lint-view-refs.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) =>\n lintViewRefs(stack).map((f) => ({\n severity: f.severity,\n rule: f.rule,\n where: f.where,\n path: f.where,\n message: f.message,\n hint: f.hint,\n })),\n },\n // #3991 — a column carrying BOTH a field-level `unique: true` and a\n // single-column declared unique index has two intents, of which exactly one\n // takes effect (the global index wins; the tenant composite is unreachable).\n {\n name: 'lintUniqueDeclarations',\n tier: 'advisory',\n input: 'parsed',\n commands: ['validate', 'build'],\n source: 'packages/lint/src/data-model-rules.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_OBJECT_WRITES_P2,\n scopeReason:\n \"`os lint` already reports this rule through `lintDataModel`, which calls it directly as R10 of \" +\n 'its best-practice sweep — registering it for `lint` as well would report every finding twice. ' +\n 'This is coverage recorded, not coverage missing: all three commands report the rule.',\n run: (stack) =>\n lintUniqueDeclarations(Array.isArray(stack.objects) ? (stack.objects as unknown[]) : []).map((f) => ({\n severity: f.severity === 'suggestion' ? ('info' as const) : f.severity,\n rule: f.rule,\n where: f.path,\n path: f.path,\n message: f.message,\n hint: f.fix ?? '',\n })),\n },\n // ADR-0090 D7 — the security-domain publish linter. Every `error` rule mirrors\n // a runtime enforcement point (fail-closed OWD default, canonical enum, anchor\n // binding gate, vocabulary freeze), moving the failure from a runtime deny to\n // an author-time fix-it. Per ADR-0049 this is not advisory security.\n {\n name: 'validateSecurityPosture',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-security-posture.ts',\n surfaces: CLI_ONLY,\n surfaceReason: 'Already gated at this surface by a DIFFERENT mechanism: plugin-security registers an ADR-0094 '\n + 'authoring gate on `object` (`registerAuthoringGate`) that enforces the same OWD posture rules on '\n + 'every runtime write. Running the linter here as well would double-report one refusal in two '\n + 'vocabularies. Consolidating the two onto this table is P2 (#4463), and is a merge, not a hole.',\n run: (stack) => validateSecurityPosture(stack),\n },\n // ADR-0105 D6 — the org tree is a REPORTING dimension. An RLS policy or\n // sharing rule that walks it builds a second permission hierarchy (the\n // dual-hierarchy mistake ADR-0057 D5 retired) and cannot widen Layer 0 anyway,\n // so it grants nothing it appears to.\n {\n name: 'validateOrgAxisRedLines',\n tier: 'gating',\n input: 'parsed',\n commands: ALL,\n source: 'packages/lint/src/validate-org-axis-red-lines.ts',\n surfaces: CLI_ONLY,\n surfaceReason: RUNTIME_NEEDS_FULL_SNAPSHOT,\n run: (stack) => validateOrgAxisRedLines(stack),\n },\n];\n\n// ─── Runner ─────────────────────────────────────────────────────────\n\n/** The stack tiers a command has in hand when it runs the registry. */\nexport interface AuthoringRuleRun extends AuthoringRuleContext {\n /** `normalizeStackInput` output — pre-Zod-parse. Always required. */\n normalized: AnyRec;\n /**\n * Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`\n * rules then read `normalized` (see `AuthoringRuleInputTier`).\n */\n parsed?: AnyRec;\n}\n\n/** The rules `command` runs, in registry order. */\nexport function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[] {\n return AUTHORING_RULES.filter((r) => r.commands.includes(command));\n}\n\n/**\n * Run every rule registered for `command` and return the concatenated findings\n * (empty = clean).\n *\n * Findings are collected across ALL rules rather than short-circuiting at the\n * first failing one. The commands used to exit at the first failing gate, which\n * meant an author with three unrelated problems fixed them in three round trips\n * and could not tell how deep the hole went. One report per run is also what\n * makes the three commands comparable: same rules, same order, same output.\n */\nexport function runAuthoringRules(command: AuthoringCommand, run: AuthoringRuleRun): AuthoringFinding[] {\n const findings: AuthoringFinding[] = [];\n const ctx: AuthoringRuleContext = { sduiManifest: run.sduiManifest };\n for (const rule of authoringRulesFor(command)) {\n const stack = rule.input === 'normalized' ? run.normalized : (run.parsed ?? run.normalized);\n findings.push(...rule.run(stack, ctx));\n }\n return findings;\n}\n\n/** Split findings into the gating set and the advisory set (`warning` + `info`). */\nexport function splitBySeverity(findings: readonly AuthoringFinding[]): {\n errors: AuthoringFinding[];\n advisories: AuthoringFinding[];\n} {\n return {\n errors: findings.filter((f) => f.severity === 'error'),\n advisories: findings.filter((f) => f.severity !== 'error'),\n };\n}\n","// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * The RUNTIME publish gate over the author-time rule registry (#4463).\n *\n * ## The hole this closes\n *\n * #4409/#4445 put 26 author-time rules behind one table and made `os validate`,\n * `os build` and `os lint` run it by construction. All three are CLI commands.\n * The metadata WRITE path — Studio's designer, REST `/meta` item CRUD, an\n * MCP/AI author — reaches `saveMetaItem`, which ran a per-type Zod `safeParse`\n * and nothing else. Zero of the 26 rules ran there. For a tenant that is not\n * one weak door among four, it is the ONLY door: `os lint` cannot see a\n * `sys_metadata` overlay row at all, so there was no command they could run\n * instead.\n *\n * It also happens to be the door AI authors use. That is the axis the #4463\n * ruling weighted highest: metadata written by a model is exactly the metadata\n * most likely to be subtly wrong, and it was arriving through the one entrance\n * with no checks on it.\n *\n * ## Shape\n *\n * This module is a GATE, not a rule engine. It owns no judgement: every finding\n * it returns came from {@link AUTHORING_RULES}, the same array the three CLI\n * commands run. Delete a rule from that table and this gate stops enforcing it\n * in the same commit — which is the property the issue asked for, and the\n * reason a second \"runtime rule list\" was never on the table.\n *\n * ## Why it evaluates DIFFERENTIALLY\n *\n * The rules are `(stack) => findings`. A runtime write is one ITEM. The gate\n * therefore builds a per-write snapshot — the written item, plus the live\n * registry's objects as resolution context — and runs the rules TWICE: once on\n * the context alone, once with the item grafted in. Only findings the item\n * ADDED are attributable to this write.\n *\n * That is not defensive padding, it is the D4 requirement made structural:\n *\n * - a tenant's existing `sys_metadata` rows may already violate a rule that did\n * not exist when they were written, and the read path must keep serving them\n * (ADR-0087's asymmetry). Gating on the absolute finding set would make an\n * unrelated legacy row block every future save;\n * - the context is resolution material, not subject matter. Without the\n * subtraction, saving flow A would 422 because object B — untouched, already\n * stored, possibly shipped in a package — has a bad predicate.\n *\n * The cost is two passes over a small in-memory snapshot, on a PUBLISH (not on\n * a draft autosave). That is the correct place to spend it.\n */\n\nimport {\n AUTHORING_RULES,\n type AuthoringFinding,\n type AuthoringRule,\n type AuthoringRuleContext,\n} from './authoring-rules.js';\n\ntype AnyRec = Record<string, unknown>;\n\n/**\n * The stack-key each gated metadata type occupies in a stack view.\n *\n * Only the types some rule declares in `runtimeTypes` need an entry; the guard\n * in `authoring-rule-wiring.test.ts` fails if a declared type is missing one,\n * so widening the gate cannot half-land.\n */\nconst TYPE_TO_STACK_KEY: Readonly<Record<string, string>> = {\n flow: 'flows',\n object: 'objects',\n view: 'views',\n action: 'actions',\n page: 'pages',\n dashboard: 'dashboards',\n agent: 'agents',\n hook: 'hooks',\n seed: 'seeds',\n};\n\n/** Everything the gate needs from the host runtime to build a snapshot. */\nexport interface RuntimeStackContext {\n /**\n * The live object declarations (registry + tenant overlay), as authored.\n *\n * Objects are the resolution universe almost every rule needs: `record.<field>`\n * in a CEL predicate, a flow node's target object, a template path. This is\n * where the runtime is strictly BETTER informed than `os lint`, which sees one\n * package's config file and has to hedge.\n */\n objects?: readonly unknown[];\n}\n\n/** One rule's verdict at the runtime surface, carrying which rule produced it. */\nexport interface RuntimeGateResult {\n /** Findings the written item ADDED, severity `error` — the reason to refuse the write. */\n errors: AuthoringFinding[];\n /** Findings the written item added at `warning` / `info`. Never blocks (#4463 P1). */\n advisories: AuthoringFinding[];\n /** Names of the registry rules that actually ran, in registry order. */\n rulesRun: string[];\n}\n\n/**\n * The rules the runtime publish gate runs for a write of `type` — read off\n * {@link AUTHORING_RULES}, never a list of its own.\n *\n * @param type Singular metadata type name (`flow`, `object`, …).\n */\nexport function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[] {\n return AUTHORING_RULES.filter(\n (r) => r.surfaces.includes('runtime-publish') && (r.runtimeTypes ?? []).includes(type),\n );\n}\n\n/** Every singular metadata type at least one rule gates at the runtime surface. */\nexport function runtimeGatedTypes(): string[] {\n const types = new Set<string>();\n for (const rule of AUTHORING_RULES) {\n if (!rule.surfaces.includes('runtime-publish')) continue;\n for (const t of rule.runtimeTypes ?? []) types.add(t);\n }\n return [...types].sort();\n}\n\n/** The stack key a metadata type occupies in a stack view, or null when unmapped. */\nexport function stackKeyForType(type: string): string | null {\n return TYPE_TO_STACK_KEY[type] ?? null;\n}\n\n/** Stable identity of a finding, so two rule passes can be set-differenced. */\nconst fingerprint = (f: AuthoringFinding) => `${f.rule}\\u0000${f.where}\\u0000${f.path}\\u0000${f.message}`;\n\nfunction runRules(\n rules: readonly AuthoringRule[],\n stack: AnyRec,\n ctx: AuthoringRuleContext,\n): AuthoringFinding[] {\n const findings: AuthoringFinding[] = [];\n for (const rule of rules) {\n // A rule that throws on an unexpected runtime body must not take the write\n // down with it: the gate's job is to REFUSE bad metadata, and an internal\n // error is not a verdict about the author's document. Surfaced as a\n // warning-tier finding so it is neither silent nor fatal.\n try {\n findings.push(...rule.run(stack, ctx));\n } catch (err) {\n findings.push({\n severity: 'warning',\n rule: 'authoring-rule-threw',\n where: rule.name,\n path: rule.source,\n message: `rule ${rule.name} threw while judging this write: ${err instanceof Error ? err.message : String(err)}`,\n hint:\n 'This is a bug in the rule, not in the metadata — the write was not blocked by it. '\n + 'Please report it with the body that triggered it.',\n });\n }\n }\n return findings;\n}\n\n/**\n * Judge one about-to-be-published metadata item against the shared registry.\n *\n * Returns an empty `errors` array when the item is clean OR when no rule gates\n * its type — callers must not treat \"no rules ran\" as a failure, and\n * `rulesRun` is there so a caller can tell the two apart.\n *\n * Pure: no I/O, no `process.env`, no logging. The escape hatch and the HTTP\n * status live at the call site, where the request context is.\n */\nexport function runRuntimeAuthoringRules(args: {\n /** Singular metadata type of the item being written. */\n type: string;\n /** The item body as it will be persisted. */\n item: unknown;\n /** Live resolution context from the host runtime. */\n context?: RuntimeStackContext;\n /** ADR-0080 SDUI manifest, when the host has one. */\n sduiManifest?: unknown;\n}): RuntimeGateResult {\n const rules = runtimeAuthoringRulesFor(args.type);\n const empty: RuntimeGateResult = { errors: [], advisories: [], rulesRun: [] };\n if (rules.length === 0) return empty;\n\n const stackKey = stackKeyForType(args.type);\n if (!stackKey) return empty;\n if (!args.item || typeof args.item !== 'object') return empty;\n\n const item = args.item as AnyRec;\n const itemName = typeof item.name === 'string' ? item.name : undefined;\n const contextObjects = (args.context?.objects ?? []) as AnyRec[];\n const ctx: AuthoringRuleContext = { sduiManifest: args.sduiManifest };\n\n // When the written type IS the context collection (an `object` write), the\n // item must REPLACE its stored self rather than erase the other objects —\n // otherwise every lookup in the tenant's model reads as dangling. Written\n // generally so widening `runtimeTypes` to `object` is a data edit, not a\n // rewrite of this function.\n const writesIntoContext = stackKey === 'objects';\n const baselineObjects = writesIntoContext\n ? contextObjects.filter((o) => !itemName || o?.name !== itemName)\n : contextObjects;\n\n // Baseline: the resolution context WITHOUT the written item. Anything found\n // here is somebody else's pre-existing condition and is not this write's to\n // answer for (#4463 D4 — the gate blocks new writes, never stored rows).\n const baseline: AnyRec = { objects: baselineObjects };\n // Candidate: the same context with this write's item added. For a non-object\n // type it is the SOLE member of its own collection, so index-0 paths in the\n // findings are unambiguously this write.\n const candidate: AnyRec = writesIntoContext\n ? { objects: [...baselineObjects, item] }\n : { objects: baselineObjects, [stackKey]: [item] };\n\n const before = new Set(runRules(rules, baseline, ctx).map(fingerprint));\n const added = runRules(rules, candidate, ctx).filter((f) => !before.has(fingerprint(f)));\n\n return {\n errors: added.filter((f) => f.severity === 'error'),\n advisories: added.filter((f) => f.severity !== 'error'),\n rulesRun: rules.map((r) => r.name),\n };\n}\n"],"mappings":"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","names":["push","label","asArray","asArray","label","asArray","isRecordTriggered","isRec","asArray","asArray","label","asArray","asArray","asArray","createRequire","asArray","isRec","strName","suggest","label","isRec","strName","list","asArray","strName","isRec","createRequire","asArray","isRec","push","asArray","asArray","asArray","asArray","asArray","asArray","asArray","asArray","label","asArray","asArray","strName","label","asArray","asArray","strName","suggest","distance","checkActionParams","isRec","asArray","strName","isInterpolated","asArray","strName","distance","suggest","label","list","asArray","strName","strList","list","asArray","strName","strList","isRec","distance","suggest","list","selected","isPlatformProvidedObjectName","asArray","label","asArray","strName","isPlatformProvidedObjectName","hasPlatformObjectPrefix","isPlatformProvidedObjectName","isRec","asArray","strName","distance","suggest","label","isPlatformProvidedObjectName","hasPlatformObjectPrefix","asArray","strName","asArray","strName","distance","suggest","asArray","strName","createRequire","cachedTs","loadTypeScript","createRequire","IMPLICIT_FIELDS","isRec","asArray","indexObjectFields","findClosestMatches","formatSuggestion","isRec","asArray","indexObjectFields","IMPLICIT_FIELDS","fixHint","formatSuggestion","findClosestMatches","findClosestMatches","formatSuggestion","isRec","asArray","readLiteralObjectName","indexObjectFields","IMPLICIT_FIELDS","fixHint","formatSuggestion","findClosestMatches","asArray","label","str","createRequire","asArray","require","asArray","asArray","fieldEntries","refOf"]}