@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/CHANGELOG.md +688 -0
- package/dist/index.cjs +2597 -343
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +610 -108
- package/dist/index.d.ts +610 -108
- package/dist/index.js +2556 -340
- package/dist/index.js.map +1 -1
- package/dist/runtime-Cs64ShwN.d.cts +254 -0
- package/dist/runtime-Cs64ShwN.d.ts +254 -0
- package/dist/runtime.cjs +7740 -0
- package/dist/runtime.cjs.map +1 -0
- package/dist/runtime.d.cts +1 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +7733 -0
- package/dist/runtime.js.map +1 -0
- package/package.json +12 -6
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
type AnyRec = Record<string, unknown>;
|
|
2
|
+
/** The three commands that hold a stack to the same author-time bar. */
|
|
3
|
+
declare const AUTHORING_COMMANDS: readonly ["validate", "build", "lint"];
|
|
4
|
+
type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* The authoring SURFACES this registry serves (#4463).
|
|
7
|
+
*
|
|
8
|
+
* - `cli` — `os validate` / `os build` / `os lint`. Which of the three is a
|
|
9
|
+
* separate axis ({@link AuthoringRule.commands}); every rule here runs on the
|
|
10
|
+
* `cli` surface, because that is where the registry was born.
|
|
11
|
+
* - `runtime-publish` — the metadata write path's PUBLISH gate: a
|
|
12
|
+
* `state: 'active'` `saveMetaItem`, and the draft→active promotion in
|
|
13
|
+
* `publishMetaItem`. Studio saves, REST `/meta` item CRUD and MCP/AI
|
|
14
|
+
* authoring all funnel through those two, so wiring the surface once covers
|
|
15
|
+
* all three doors (the maintainer ruling on #4463: one shared core, one
|
|
16
|
+
* runtime gate, not a gate per surface).
|
|
17
|
+
*
|
|
18
|
+
* Draft saves are deliberately NOT a surface: a draft is allowed to be a
|
|
19
|
+
* half-finished thing, and gating one would break the Studio editing loop for
|
|
20
|
+
* no safety gain — the draft cannot execute until it is published, and
|
|
21
|
+
* publishing runs this table (#4463 D1).
|
|
22
|
+
*/
|
|
23
|
+
declare const AUTHORING_SURFACES: readonly ["cli", "runtime-publish"];
|
|
24
|
+
type AuthoringSurface = (typeof AUTHORING_SURFACES)[number];
|
|
25
|
+
/** `error` gates. `warning` advises. `info` is a suggestion (`os lint` grades it as one). */
|
|
26
|
+
type AuthoringSeverity = 'error' | 'warning' | 'info';
|
|
27
|
+
/**
|
|
28
|
+
* The one finding shape all three commands render. Rules whose own return type
|
|
29
|
+
* predates it are adapted at their registry entry, so the commands hold one
|
|
30
|
+
* type instead of a twenty-way union.
|
|
31
|
+
*/
|
|
32
|
+
interface AuthoringFinding {
|
|
33
|
+
severity: AuthoringSeverity;
|
|
34
|
+
/** Stable diagnostic rule id (used by docs, allowlists and `--json` consumers). */
|
|
35
|
+
rule: string;
|
|
36
|
+
/** Human-readable location, e.g. `object "leave_request"`. */
|
|
37
|
+
where: string;
|
|
38
|
+
/** Config path, e.g. `objects[3].sharingModel`. */
|
|
39
|
+
path: string;
|
|
40
|
+
/** What is wrong. */
|
|
41
|
+
message: string;
|
|
42
|
+
/** How to fix it. */
|
|
43
|
+
hint: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `gating` = the rule can emit `severity: 'error'`, so it MUST run on all three
|
|
47
|
+
* commands. `advisory` = it never does, and may be scoped with a reason.
|
|
48
|
+
*
|
|
49
|
+
* The claim is not taken on trust: the wiring guard reads each `advisory` rule's
|
|
50
|
+
* own source and fails if it emits an `error`. That check is the reason the tier
|
|
51
|
+
* is worth declaring — #3760 promoted a `lintFlowPatterns` rule from advisory to
|
|
52
|
+
* gating, and nothing anywhere asked whether its command coverage should follow.
|
|
53
|
+
*/
|
|
54
|
+
type AuthoringRuleTier = 'gating' | 'advisory';
|
|
55
|
+
/**
|
|
56
|
+
* Which tier of the stack a rule reads.
|
|
57
|
+
*
|
|
58
|
+
* - `normalized` — the `normalizeStackInput` output, BEFORE the Zod parse. The
|
|
59
|
+
* rules that need it check keys the parse strips (a flat list view in
|
|
60
|
+
* `views: []`, `userFilters` on an object list view, a `visibleOn` alias): by
|
|
61
|
+
* the time `result.data` exists the evidence is gone.
|
|
62
|
+
* - `parsed` — the post-parse stack, where defaults are filled and shapes are
|
|
63
|
+
* settled.
|
|
64
|
+
*
|
|
65
|
+
* `os lint` never parses (it is the cheap pre-flight; a schema error is
|
|
66
|
+
* `os validate`'s verdict to give), so it runs BOTH tiers on the normalized
|
|
67
|
+
* stack. Every rule here is written to tolerate that — it is what `os lint`
|
|
68
|
+
* already did for the reference-integrity suite and the security linter.
|
|
69
|
+
*/
|
|
70
|
+
type AuthoringRuleInputTier = 'normalized' | 'parsed';
|
|
71
|
+
/** Per-run inputs a rule may need beyond the stack itself. */
|
|
72
|
+
interface AuthoringRuleContext {
|
|
73
|
+
/** ADR-0080 SDUI component manifest, when the project ships one. */
|
|
74
|
+
sduiManifest?: unknown;
|
|
75
|
+
}
|
|
76
|
+
interface AuthoringRule {
|
|
77
|
+
/** The exported function's name — the id the wiring guard asserts on. */
|
|
78
|
+
name: string;
|
|
79
|
+
tier: AuthoringRuleTier;
|
|
80
|
+
input: AuthoringRuleInputTier;
|
|
81
|
+
/** Which commands run it. Must be all three when `tier` is `gating`. */
|
|
82
|
+
commands: readonly AuthoringCommand[];
|
|
83
|
+
/** Repo-relative path to the rule's implementation (the guard verifies the tier claim against it). */
|
|
84
|
+
source: string;
|
|
85
|
+
/** REQUIRED when `commands` is not all three: why this rule is scoped. */
|
|
86
|
+
scopeReason?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Which authoring surfaces run this rule (#4463). Always contains `'cli'`.
|
|
89
|
+
*
|
|
90
|
+
* Adding `'runtime-publish'` is what puts the rule on the metadata write
|
|
91
|
+
* path's publish gate; {@link runtimeTypes} then says which metadata types'
|
|
92
|
+
* writes it inspects. Leaving it off REQUIRES {@link surfaceReason} — the
|
|
93
|
+
* same discipline `scopeReason` applies to the command axis, for the same
|
|
94
|
+
* reason: the whole defect class #4409 and #4463 describe is coverage that
|
|
95
|
+
* narrowed silently.
|
|
96
|
+
*/
|
|
97
|
+
surfaces: readonly AuthoringSurface[];
|
|
98
|
+
/**
|
|
99
|
+
* REQUIRED when `surfaces` includes `'runtime-publish'`: the SINGULAR
|
|
100
|
+
* metadata type names whose runtime write this rule inspects (e.g. `flow`).
|
|
101
|
+
*
|
|
102
|
+
* The runtime gate builds a per-write stack snapshot and only runs the rules
|
|
103
|
+
* that declare the written type, which is #4463 D2 option (c) expressed as
|
|
104
|
+
* data. Widening a rule to another type is a one-line edit here, not new
|
|
105
|
+
* wiring at the gate.
|
|
106
|
+
*/
|
|
107
|
+
runtimeTypes?: readonly string[];
|
|
108
|
+
/** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */
|
|
109
|
+
surfaceReason?: string;
|
|
110
|
+
run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `ExprIssue` is the one rule finding that carries no rule id of its own — it
|
|
114
|
+
* predates the `{ rule, path, hint }` shape every other rule settled on. Given
|
|
115
|
+
* one here so `os lint --json` and the docs can name it like any other.
|
|
116
|
+
*/
|
|
117
|
+
declare const EXPRESSION_INVALID = "expression-invalid";
|
|
118
|
+
/**
|
|
119
|
+
* Every author-time rule the three commands share, in the order their findings
|
|
120
|
+
* are reported.
|
|
121
|
+
*/
|
|
122
|
+
declare const AUTHORING_RULES: readonly AuthoringRule[];
|
|
123
|
+
/** The stack tiers a command has in hand when it runs the registry. */
|
|
124
|
+
interface AuthoringRuleRun extends AuthoringRuleContext {
|
|
125
|
+
/** `normalizeStackInput` output — pre-Zod-parse. Always required. */
|
|
126
|
+
normalized: AnyRec;
|
|
127
|
+
/**
|
|
128
|
+
* Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`
|
|
129
|
+
* rules then read `normalized` (see `AuthoringRuleInputTier`).
|
|
130
|
+
*/
|
|
131
|
+
parsed?: AnyRec;
|
|
132
|
+
}
|
|
133
|
+
/** The rules `command` runs, in registry order. */
|
|
134
|
+
declare function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[];
|
|
135
|
+
/**
|
|
136
|
+
* Run every rule registered for `command` and return the concatenated findings
|
|
137
|
+
* (empty = clean).
|
|
138
|
+
*
|
|
139
|
+
* Findings are collected across ALL rules rather than short-circuiting at the
|
|
140
|
+
* first failing one. The commands used to exit at the first failing gate, which
|
|
141
|
+
* meant an author with three unrelated problems fixed them in three round trips
|
|
142
|
+
* and could not tell how deep the hole went. One report per run is also what
|
|
143
|
+
* makes the three commands comparable: same rules, same order, same output.
|
|
144
|
+
*/
|
|
145
|
+
declare function runAuthoringRules(command: AuthoringCommand, run: AuthoringRuleRun): AuthoringFinding[];
|
|
146
|
+
/** Split findings into the gating set and the advisory set (`warning` + `info`). */
|
|
147
|
+
declare function splitBySeverity(findings: readonly AuthoringFinding[]): {
|
|
148
|
+
errors: AuthoringFinding[];
|
|
149
|
+
advisories: AuthoringFinding[];
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The RUNTIME publish gate over the author-time rule registry (#4463).
|
|
154
|
+
*
|
|
155
|
+
* ## The hole this closes
|
|
156
|
+
*
|
|
157
|
+
* #4409/#4445 put 26 author-time rules behind one table and made `os validate`,
|
|
158
|
+
* `os build` and `os lint` run it by construction. All three are CLI commands.
|
|
159
|
+
* The metadata WRITE path — Studio's designer, REST `/meta` item CRUD, an
|
|
160
|
+
* MCP/AI author — reaches `saveMetaItem`, which ran a per-type Zod `safeParse`
|
|
161
|
+
* and nothing else. Zero of the 26 rules ran there. For a tenant that is not
|
|
162
|
+
* one weak door among four, it is the ONLY door: `os lint` cannot see a
|
|
163
|
+
* `sys_metadata` overlay row at all, so there was no command they could run
|
|
164
|
+
* instead.
|
|
165
|
+
*
|
|
166
|
+
* It also happens to be the door AI authors use. That is the axis the #4463
|
|
167
|
+
* ruling weighted highest: metadata written by a model is exactly the metadata
|
|
168
|
+
* most likely to be subtly wrong, and it was arriving through the one entrance
|
|
169
|
+
* with no checks on it.
|
|
170
|
+
*
|
|
171
|
+
* ## Shape
|
|
172
|
+
*
|
|
173
|
+
* This module is a GATE, not a rule engine. It owns no judgement: every finding
|
|
174
|
+
* it returns came from {@link AUTHORING_RULES}, the same array the three CLI
|
|
175
|
+
* commands run. Delete a rule from that table and this gate stops enforcing it
|
|
176
|
+
* in the same commit — which is the property the issue asked for, and the
|
|
177
|
+
* reason a second "runtime rule list" was never on the table.
|
|
178
|
+
*
|
|
179
|
+
* ## Why it evaluates DIFFERENTIALLY
|
|
180
|
+
*
|
|
181
|
+
* The rules are `(stack) => findings`. A runtime write is one ITEM. The gate
|
|
182
|
+
* therefore builds a per-write snapshot — the written item, plus the live
|
|
183
|
+
* registry's objects as resolution context — and runs the rules TWICE: once on
|
|
184
|
+
* the context alone, once with the item grafted in. Only findings the item
|
|
185
|
+
* ADDED are attributable to this write.
|
|
186
|
+
*
|
|
187
|
+
* That is not defensive padding, it is the D4 requirement made structural:
|
|
188
|
+
*
|
|
189
|
+
* - a tenant's existing `sys_metadata` rows may already violate a rule that did
|
|
190
|
+
* not exist when they were written, and the read path must keep serving them
|
|
191
|
+
* (ADR-0087's asymmetry). Gating on the absolute finding set would make an
|
|
192
|
+
* unrelated legacy row block every future save;
|
|
193
|
+
* - the context is resolution material, not subject matter. Without the
|
|
194
|
+
* subtraction, saving flow A would 422 because object B — untouched, already
|
|
195
|
+
* stored, possibly shipped in a package — has a bad predicate.
|
|
196
|
+
*
|
|
197
|
+
* The cost is two passes over a small in-memory snapshot, on a PUBLISH (not on
|
|
198
|
+
* a draft autosave). That is the correct place to spend it.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/** Everything the gate needs from the host runtime to build a snapshot. */
|
|
202
|
+
interface RuntimeStackContext {
|
|
203
|
+
/**
|
|
204
|
+
* The live object declarations (registry + tenant overlay), as authored.
|
|
205
|
+
*
|
|
206
|
+
* Objects are the resolution universe almost every rule needs: `record.<field>`
|
|
207
|
+
* in a CEL predicate, a flow node's target object, a template path. This is
|
|
208
|
+
* where the runtime is strictly BETTER informed than `os lint`, which sees one
|
|
209
|
+
* package's config file and has to hedge.
|
|
210
|
+
*/
|
|
211
|
+
objects?: readonly unknown[];
|
|
212
|
+
}
|
|
213
|
+
/** One rule's verdict at the runtime surface, carrying which rule produced it. */
|
|
214
|
+
interface RuntimeGateResult {
|
|
215
|
+
/** Findings the written item ADDED, severity `error` — the reason to refuse the write. */
|
|
216
|
+
errors: AuthoringFinding[];
|
|
217
|
+
/** Findings the written item added at `warning` / `info`. Never blocks (#4463 P1). */
|
|
218
|
+
advisories: AuthoringFinding[];
|
|
219
|
+
/** Names of the registry rules that actually ran, in registry order. */
|
|
220
|
+
rulesRun: string[];
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The rules the runtime publish gate runs for a write of `type` — read off
|
|
224
|
+
* {@link AUTHORING_RULES}, never a list of its own.
|
|
225
|
+
*
|
|
226
|
+
* @param type Singular metadata type name (`flow`, `object`, …).
|
|
227
|
+
*/
|
|
228
|
+
declare function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[];
|
|
229
|
+
/** Every singular metadata type at least one rule gates at the runtime surface. */
|
|
230
|
+
declare function runtimeGatedTypes(): string[];
|
|
231
|
+
/** The stack key a metadata type occupies in a stack view, or null when unmapped. */
|
|
232
|
+
declare function stackKeyForType(type: string): string | null;
|
|
233
|
+
/**
|
|
234
|
+
* Judge one about-to-be-published metadata item against the shared registry.
|
|
235
|
+
*
|
|
236
|
+
* Returns an empty `errors` array when the item is clean OR when no rule gates
|
|
237
|
+
* its type — callers must not treat "no rules ran" as a failure, and
|
|
238
|
+
* `rulesRun` is there so a caller can tell the two apart.
|
|
239
|
+
*
|
|
240
|
+
* Pure: no I/O, no `process.env`, no logging. The escape hatch and the HTTP
|
|
241
|
+
* status live at the call site, where the request context is.
|
|
242
|
+
*/
|
|
243
|
+
declare function runRuntimeAuthoringRules(args: {
|
|
244
|
+
/** Singular metadata type of the item being written. */
|
|
245
|
+
type: string;
|
|
246
|
+
/** The item body as it will be persisted. */
|
|
247
|
+
item: unknown;
|
|
248
|
+
/** Live resolution context from the host runtime. */
|
|
249
|
+
context?: RuntimeStackContext;
|
|
250
|
+
/** ADR-0080 SDUI manifest, when the host has one. */
|
|
251
|
+
sduiManifest?: unknown;
|
|
252
|
+
}): RuntimeGateResult;
|
|
253
|
+
|
|
254
|
+
export { AUTHORING_COMMANDS as A, EXPRESSION_INVALID as E, type RuntimeGateResult as R, AUTHORING_RULES as a, AUTHORING_SURFACES as b, type AuthoringCommand as c, type AuthoringFinding as d, type AuthoringRule as e, type AuthoringRuleContext as f, type AuthoringRuleInputTier as g, type AuthoringRuleRun as h, type AuthoringRuleTier as i, type AuthoringSeverity as j, type AuthoringSurface as k, type RuntimeStackContext as l, authoringRulesFor as m, runRuntimeAuthoringRules as n, runtimeAuthoringRulesFor as o, runtimeGatedTypes as p, stackKeyForType as q, runAuthoringRules as r, splitBySeverity as s };
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
type AnyRec = Record<string, unknown>;
|
|
2
|
+
/** The three commands that hold a stack to the same author-time bar. */
|
|
3
|
+
declare const AUTHORING_COMMANDS: readonly ["validate", "build", "lint"];
|
|
4
|
+
type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];
|
|
5
|
+
/**
|
|
6
|
+
* The authoring SURFACES this registry serves (#4463).
|
|
7
|
+
*
|
|
8
|
+
* - `cli` — `os validate` / `os build` / `os lint`. Which of the three is a
|
|
9
|
+
* separate axis ({@link AuthoringRule.commands}); every rule here runs on the
|
|
10
|
+
* `cli` surface, because that is where the registry was born.
|
|
11
|
+
* - `runtime-publish` — the metadata write path's PUBLISH gate: a
|
|
12
|
+
* `state: 'active'` `saveMetaItem`, and the draft→active promotion in
|
|
13
|
+
* `publishMetaItem`. Studio saves, REST `/meta` item CRUD and MCP/AI
|
|
14
|
+
* authoring all funnel through those two, so wiring the surface once covers
|
|
15
|
+
* all three doors (the maintainer ruling on #4463: one shared core, one
|
|
16
|
+
* runtime gate, not a gate per surface).
|
|
17
|
+
*
|
|
18
|
+
* Draft saves are deliberately NOT a surface: a draft is allowed to be a
|
|
19
|
+
* half-finished thing, and gating one would break the Studio editing loop for
|
|
20
|
+
* no safety gain — the draft cannot execute until it is published, and
|
|
21
|
+
* publishing runs this table (#4463 D1).
|
|
22
|
+
*/
|
|
23
|
+
declare const AUTHORING_SURFACES: readonly ["cli", "runtime-publish"];
|
|
24
|
+
type AuthoringSurface = (typeof AUTHORING_SURFACES)[number];
|
|
25
|
+
/** `error` gates. `warning` advises. `info` is a suggestion (`os lint` grades it as one). */
|
|
26
|
+
type AuthoringSeverity = 'error' | 'warning' | 'info';
|
|
27
|
+
/**
|
|
28
|
+
* The one finding shape all three commands render. Rules whose own return type
|
|
29
|
+
* predates it are adapted at their registry entry, so the commands hold one
|
|
30
|
+
* type instead of a twenty-way union.
|
|
31
|
+
*/
|
|
32
|
+
interface AuthoringFinding {
|
|
33
|
+
severity: AuthoringSeverity;
|
|
34
|
+
/** Stable diagnostic rule id (used by docs, allowlists and `--json` consumers). */
|
|
35
|
+
rule: string;
|
|
36
|
+
/** Human-readable location, e.g. `object "leave_request"`. */
|
|
37
|
+
where: string;
|
|
38
|
+
/** Config path, e.g. `objects[3].sharingModel`. */
|
|
39
|
+
path: string;
|
|
40
|
+
/** What is wrong. */
|
|
41
|
+
message: string;
|
|
42
|
+
/** How to fix it. */
|
|
43
|
+
hint: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* `gating` = the rule can emit `severity: 'error'`, so it MUST run on all three
|
|
47
|
+
* commands. `advisory` = it never does, and may be scoped with a reason.
|
|
48
|
+
*
|
|
49
|
+
* The claim is not taken on trust: the wiring guard reads each `advisory` rule's
|
|
50
|
+
* own source and fails if it emits an `error`. That check is the reason the tier
|
|
51
|
+
* is worth declaring — #3760 promoted a `lintFlowPatterns` rule from advisory to
|
|
52
|
+
* gating, and nothing anywhere asked whether its command coverage should follow.
|
|
53
|
+
*/
|
|
54
|
+
type AuthoringRuleTier = 'gating' | 'advisory';
|
|
55
|
+
/**
|
|
56
|
+
* Which tier of the stack a rule reads.
|
|
57
|
+
*
|
|
58
|
+
* - `normalized` — the `normalizeStackInput` output, BEFORE the Zod parse. The
|
|
59
|
+
* rules that need it check keys the parse strips (a flat list view in
|
|
60
|
+
* `views: []`, `userFilters` on an object list view, a `visibleOn` alias): by
|
|
61
|
+
* the time `result.data` exists the evidence is gone.
|
|
62
|
+
* - `parsed` — the post-parse stack, where defaults are filled and shapes are
|
|
63
|
+
* settled.
|
|
64
|
+
*
|
|
65
|
+
* `os lint` never parses (it is the cheap pre-flight; a schema error is
|
|
66
|
+
* `os validate`'s verdict to give), so it runs BOTH tiers on the normalized
|
|
67
|
+
* stack. Every rule here is written to tolerate that — it is what `os lint`
|
|
68
|
+
* already did for the reference-integrity suite and the security linter.
|
|
69
|
+
*/
|
|
70
|
+
type AuthoringRuleInputTier = 'normalized' | 'parsed';
|
|
71
|
+
/** Per-run inputs a rule may need beyond the stack itself. */
|
|
72
|
+
interface AuthoringRuleContext {
|
|
73
|
+
/** ADR-0080 SDUI component manifest, when the project ships one. */
|
|
74
|
+
sduiManifest?: unknown;
|
|
75
|
+
}
|
|
76
|
+
interface AuthoringRule {
|
|
77
|
+
/** The exported function's name — the id the wiring guard asserts on. */
|
|
78
|
+
name: string;
|
|
79
|
+
tier: AuthoringRuleTier;
|
|
80
|
+
input: AuthoringRuleInputTier;
|
|
81
|
+
/** Which commands run it. Must be all three when `tier` is `gating`. */
|
|
82
|
+
commands: readonly AuthoringCommand[];
|
|
83
|
+
/** Repo-relative path to the rule's implementation (the guard verifies the tier claim against it). */
|
|
84
|
+
source: string;
|
|
85
|
+
/** REQUIRED when `commands` is not all three: why this rule is scoped. */
|
|
86
|
+
scopeReason?: string;
|
|
87
|
+
/**
|
|
88
|
+
* Which authoring surfaces run this rule (#4463). Always contains `'cli'`.
|
|
89
|
+
*
|
|
90
|
+
* Adding `'runtime-publish'` is what puts the rule on the metadata write
|
|
91
|
+
* path's publish gate; {@link runtimeTypes} then says which metadata types'
|
|
92
|
+
* writes it inspects. Leaving it off REQUIRES {@link surfaceReason} — the
|
|
93
|
+
* same discipline `scopeReason` applies to the command axis, for the same
|
|
94
|
+
* reason: the whole defect class #4409 and #4463 describe is coverage that
|
|
95
|
+
* narrowed silently.
|
|
96
|
+
*/
|
|
97
|
+
surfaces: readonly AuthoringSurface[];
|
|
98
|
+
/**
|
|
99
|
+
* REQUIRED when `surfaces` includes `'runtime-publish'`: the SINGULAR
|
|
100
|
+
* metadata type names whose runtime write this rule inspects (e.g. `flow`).
|
|
101
|
+
*
|
|
102
|
+
* The runtime gate builds a per-write stack snapshot and only runs the rules
|
|
103
|
+
* that declare the written type, which is #4463 D2 option (c) expressed as
|
|
104
|
+
* data. Widening a rule to another type is a one-line edit here, not new
|
|
105
|
+
* wiring at the gate.
|
|
106
|
+
*/
|
|
107
|
+
runtimeTypes?: readonly string[];
|
|
108
|
+
/** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */
|
|
109
|
+
surfaceReason?: string;
|
|
110
|
+
run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* `ExprIssue` is the one rule finding that carries no rule id of its own — it
|
|
114
|
+
* predates the `{ rule, path, hint }` shape every other rule settled on. Given
|
|
115
|
+
* one here so `os lint --json` and the docs can name it like any other.
|
|
116
|
+
*/
|
|
117
|
+
declare const EXPRESSION_INVALID = "expression-invalid";
|
|
118
|
+
/**
|
|
119
|
+
* Every author-time rule the three commands share, in the order their findings
|
|
120
|
+
* are reported.
|
|
121
|
+
*/
|
|
122
|
+
declare const AUTHORING_RULES: readonly AuthoringRule[];
|
|
123
|
+
/** The stack tiers a command has in hand when it runs the registry. */
|
|
124
|
+
interface AuthoringRuleRun extends AuthoringRuleContext {
|
|
125
|
+
/** `normalizeStackInput` output — pre-Zod-parse. Always required. */
|
|
126
|
+
normalized: AnyRec;
|
|
127
|
+
/**
|
|
128
|
+
* Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`
|
|
129
|
+
* rules then read `normalized` (see `AuthoringRuleInputTier`).
|
|
130
|
+
*/
|
|
131
|
+
parsed?: AnyRec;
|
|
132
|
+
}
|
|
133
|
+
/** The rules `command` runs, in registry order. */
|
|
134
|
+
declare function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[];
|
|
135
|
+
/**
|
|
136
|
+
* Run every rule registered for `command` and return the concatenated findings
|
|
137
|
+
* (empty = clean).
|
|
138
|
+
*
|
|
139
|
+
* Findings are collected across ALL rules rather than short-circuiting at the
|
|
140
|
+
* first failing one. The commands used to exit at the first failing gate, which
|
|
141
|
+
* meant an author with three unrelated problems fixed them in three round trips
|
|
142
|
+
* and could not tell how deep the hole went. One report per run is also what
|
|
143
|
+
* makes the three commands comparable: same rules, same order, same output.
|
|
144
|
+
*/
|
|
145
|
+
declare function runAuthoringRules(command: AuthoringCommand, run: AuthoringRuleRun): AuthoringFinding[];
|
|
146
|
+
/** Split findings into the gating set and the advisory set (`warning` + `info`). */
|
|
147
|
+
declare function splitBySeverity(findings: readonly AuthoringFinding[]): {
|
|
148
|
+
errors: AuthoringFinding[];
|
|
149
|
+
advisories: AuthoringFinding[];
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The RUNTIME publish gate over the author-time rule registry (#4463).
|
|
154
|
+
*
|
|
155
|
+
* ## The hole this closes
|
|
156
|
+
*
|
|
157
|
+
* #4409/#4445 put 26 author-time rules behind one table and made `os validate`,
|
|
158
|
+
* `os build` and `os lint` run it by construction. All three are CLI commands.
|
|
159
|
+
* The metadata WRITE path — Studio's designer, REST `/meta` item CRUD, an
|
|
160
|
+
* MCP/AI author — reaches `saveMetaItem`, which ran a per-type Zod `safeParse`
|
|
161
|
+
* and nothing else. Zero of the 26 rules ran there. For a tenant that is not
|
|
162
|
+
* one weak door among four, it is the ONLY door: `os lint` cannot see a
|
|
163
|
+
* `sys_metadata` overlay row at all, so there was no command they could run
|
|
164
|
+
* instead.
|
|
165
|
+
*
|
|
166
|
+
* It also happens to be the door AI authors use. That is the axis the #4463
|
|
167
|
+
* ruling weighted highest: metadata written by a model is exactly the metadata
|
|
168
|
+
* most likely to be subtly wrong, and it was arriving through the one entrance
|
|
169
|
+
* with no checks on it.
|
|
170
|
+
*
|
|
171
|
+
* ## Shape
|
|
172
|
+
*
|
|
173
|
+
* This module is a GATE, not a rule engine. It owns no judgement: every finding
|
|
174
|
+
* it returns came from {@link AUTHORING_RULES}, the same array the three CLI
|
|
175
|
+
* commands run. Delete a rule from that table and this gate stops enforcing it
|
|
176
|
+
* in the same commit — which is the property the issue asked for, and the
|
|
177
|
+
* reason a second "runtime rule list" was never on the table.
|
|
178
|
+
*
|
|
179
|
+
* ## Why it evaluates DIFFERENTIALLY
|
|
180
|
+
*
|
|
181
|
+
* The rules are `(stack) => findings`. A runtime write is one ITEM. The gate
|
|
182
|
+
* therefore builds a per-write snapshot — the written item, plus the live
|
|
183
|
+
* registry's objects as resolution context — and runs the rules TWICE: once on
|
|
184
|
+
* the context alone, once with the item grafted in. Only findings the item
|
|
185
|
+
* ADDED are attributable to this write.
|
|
186
|
+
*
|
|
187
|
+
* That is not defensive padding, it is the D4 requirement made structural:
|
|
188
|
+
*
|
|
189
|
+
* - a tenant's existing `sys_metadata` rows may already violate a rule that did
|
|
190
|
+
* not exist when they were written, and the read path must keep serving them
|
|
191
|
+
* (ADR-0087's asymmetry). Gating on the absolute finding set would make an
|
|
192
|
+
* unrelated legacy row block every future save;
|
|
193
|
+
* - the context is resolution material, not subject matter. Without the
|
|
194
|
+
* subtraction, saving flow A would 422 because object B — untouched, already
|
|
195
|
+
* stored, possibly shipped in a package — has a bad predicate.
|
|
196
|
+
*
|
|
197
|
+
* The cost is two passes over a small in-memory snapshot, on a PUBLISH (not on
|
|
198
|
+
* a draft autosave). That is the correct place to spend it.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/** Everything the gate needs from the host runtime to build a snapshot. */
|
|
202
|
+
interface RuntimeStackContext {
|
|
203
|
+
/**
|
|
204
|
+
* The live object declarations (registry + tenant overlay), as authored.
|
|
205
|
+
*
|
|
206
|
+
* Objects are the resolution universe almost every rule needs: `record.<field>`
|
|
207
|
+
* in a CEL predicate, a flow node's target object, a template path. This is
|
|
208
|
+
* where the runtime is strictly BETTER informed than `os lint`, which sees one
|
|
209
|
+
* package's config file and has to hedge.
|
|
210
|
+
*/
|
|
211
|
+
objects?: readonly unknown[];
|
|
212
|
+
}
|
|
213
|
+
/** One rule's verdict at the runtime surface, carrying which rule produced it. */
|
|
214
|
+
interface RuntimeGateResult {
|
|
215
|
+
/** Findings the written item ADDED, severity `error` — the reason to refuse the write. */
|
|
216
|
+
errors: AuthoringFinding[];
|
|
217
|
+
/** Findings the written item added at `warning` / `info`. Never blocks (#4463 P1). */
|
|
218
|
+
advisories: AuthoringFinding[];
|
|
219
|
+
/** Names of the registry rules that actually ran, in registry order. */
|
|
220
|
+
rulesRun: string[];
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The rules the runtime publish gate runs for a write of `type` — read off
|
|
224
|
+
* {@link AUTHORING_RULES}, never a list of its own.
|
|
225
|
+
*
|
|
226
|
+
* @param type Singular metadata type name (`flow`, `object`, …).
|
|
227
|
+
*/
|
|
228
|
+
declare function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[];
|
|
229
|
+
/** Every singular metadata type at least one rule gates at the runtime surface. */
|
|
230
|
+
declare function runtimeGatedTypes(): string[];
|
|
231
|
+
/** The stack key a metadata type occupies in a stack view, or null when unmapped. */
|
|
232
|
+
declare function stackKeyForType(type: string): string | null;
|
|
233
|
+
/**
|
|
234
|
+
* Judge one about-to-be-published metadata item against the shared registry.
|
|
235
|
+
*
|
|
236
|
+
* Returns an empty `errors` array when the item is clean OR when no rule gates
|
|
237
|
+
* its type — callers must not treat "no rules ran" as a failure, and
|
|
238
|
+
* `rulesRun` is there so a caller can tell the two apart.
|
|
239
|
+
*
|
|
240
|
+
* Pure: no I/O, no `process.env`, no logging. The escape hatch and the HTTP
|
|
241
|
+
* status live at the call site, where the request context is.
|
|
242
|
+
*/
|
|
243
|
+
declare function runRuntimeAuthoringRules(args: {
|
|
244
|
+
/** Singular metadata type of the item being written. */
|
|
245
|
+
type: string;
|
|
246
|
+
/** The item body as it will be persisted. */
|
|
247
|
+
item: unknown;
|
|
248
|
+
/** Live resolution context from the host runtime. */
|
|
249
|
+
context?: RuntimeStackContext;
|
|
250
|
+
/** ADR-0080 SDUI manifest, when the host has one. */
|
|
251
|
+
sduiManifest?: unknown;
|
|
252
|
+
}): RuntimeGateResult;
|
|
253
|
+
|
|
254
|
+
export { AUTHORING_COMMANDS as A, EXPRESSION_INVALID as E, type RuntimeGateResult as R, AUTHORING_RULES as a, AUTHORING_SURFACES as b, type AuthoringCommand as c, type AuthoringFinding as d, type AuthoringRule as e, type AuthoringRuleContext as f, type AuthoringRuleInputTier as g, type AuthoringRuleRun as h, type AuthoringRuleTier as i, type AuthoringSeverity as j, type AuthoringSurface as k, type RuntimeStackContext as l, authoringRulesFor as m, runRuntimeAuthoringRules as n, runtimeAuthoringRulesFor as o, runtimeGatedTypes as p, stackKeyForType as q, runAuthoringRules as r, splitBySeverity as s };
|