@objectstack/lint 17.0.0-rc.6 → 17.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- type AnyRec = Record<string, unknown>;
1
+ type AnyRec$1 = Record<string, unknown>;
2
2
  /** The three commands that hold a stack to the same author-time bar. */
3
3
  declare const AUTHORING_COMMANDS: readonly ["validate", "build", "lint"];
4
4
  type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];
@@ -152,7 +152,7 @@ interface AuthoringRule {
152
152
  runtimeTypes?: readonly string[];
153
153
  /** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */
154
154
  surfaceReason?: string;
155
- run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
155
+ run: (stack: AnyRec$1, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
156
156
  }
157
157
  /**
158
158
  * `ExprIssue` is the one rule finding that carries no rule id of its own — it
@@ -168,12 +168,12 @@ declare const AUTHORING_RULES: readonly AuthoringRule[];
168
168
  /** The stack tiers a command has in hand when it runs the registry. */
169
169
  interface AuthoringRuleRun extends AuthoringRuleContext {
170
170
  /** `normalizeStackInput` output — pre-Zod-parse. Always required. */
171
- normalized: AnyRec;
171
+ normalized: AnyRec$1;
172
172
  /**
173
173
  * Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`
174
174
  * rules then read `normalized` (see `AuthoringRuleInputTier`).
175
175
  */
176
- parsed?: AnyRec;
176
+ parsed?: AnyRec$1;
177
177
  }
178
178
  /** The rules `command` runs, in registry order. */
179
179
  declare function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[];
@@ -243,7 +243,20 @@ declare function splitBySeverity(findings: readonly AuthoringFinding[]): {
243
243
  * a draft autosave). That is the correct place to spend it.
244
244
  */
245
245
 
246
- /** Everything the gate needs from the host runtime to build a snapshot. */
246
+ type AnyRec = Record<string, unknown>;
247
+ /**
248
+ * Everything the gate needs from the host runtime to build a snapshot.
249
+ *
250
+ * [#8309] Each key here doubles as the stack key the collection occupies in
251
+ * the per-write snapshot — see {@link CONTEXT_STACK_KEYS}, which is derived
252
+ * from this shape and keeps the two from drifting. The set is deliberately
253
+ * BOUNDED to what the runtime-wired rules actually read (measured, not
254
+ * projected): the three cross-collection security rules compare
255
+ * objects × permissions × books, and nothing on the runtime surface reads
256
+ * `positions` / `apps` — so those are NOT carried. Widening the snapshot is a
257
+ * one-key edit here plus a `CONTEXT_STACK_KEYS` entry, made when a rule that
258
+ * reads the collection actually crosses the wall, never in advance.
259
+ */
247
260
  interface RuntimeStackContext {
248
261
  /**
249
262
  * The live object declarations (registry + tenant overlay), as authored.
@@ -254,6 +267,27 @@ interface RuntimeStackContext {
254
267
  * package's config file and has to hedge.
255
268
  */
256
269
  objects?: readonly unknown[];
270
+ /**
271
+ * The live permission-set declarations (stack key `permissions`).
272
+ *
273
+ * [#8309] The collection the three cross-collection security rules compare
274
+ * against. Without it a per-write snapshot holds exactly ONE permission set
275
+ * (the written item), so `security-master-detail-ungranted` reads every
276
+ * detail object the tenant's OTHER sets grant as ungranted — measured as 38
277
+ * phantom findings per-write vs 4 whole-stack over the shipped corpus
278
+ * (PR #7886). `security-book-audience-unknown-set` needs the set NAMES to
279
+ * resolve a book's `audience.permissionSet` for the same reason.
280
+ */
281
+ permissions?: readonly unknown[];
282
+ /**
283
+ * The live documentation-book declarations (stack key `books`).
284
+ *
285
+ * [#8309] Carried so a `book` write is judged with its siblings present and
286
+ * replace-not-erase semantics apply to it (an updated book must not read as
287
+ * a second book of the same name), and so book-derived findings cancel in
288
+ * the differential for every other write type.
289
+ */
290
+ books?: readonly unknown[];
257
291
  }
258
292
  /** One rule's verdict at the runtime surface, carrying which rule produced it. */
259
293
  interface RuntimeGateResult {
@@ -275,6 +309,52 @@ declare function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[
275
309
  declare function runtimeGatedTypes(): string[];
276
310
  /** The stack key a metadata type occupies in a stack view, or null when unmapped. */
277
311
  declare function stackKeyForType(type: string): string | null;
312
+ /**
313
+ * The baseline/candidate stack pair the gate judges one write against, or
314
+ * `null` when no snapshot can be built (unmapped type, non-object body).
315
+ *
316
+ * Exported (#8309) so the tests that measure per-write vs whole-stack
317
+ * agreement exercise the REAL construction instead of a hand-kept mirror —
318
+ * `validate-security-posture.runtime-surface.test.ts` used to mirror this
319
+ * logic in a local `wouldGateAdd`, which is exactly the drift surface a
320
+ * snapshot change here would have missed.
321
+ *
322
+ * Shape:
323
+ * - The baseline carries every context collection ({@link CONTEXT_STACK_KEYS})
324
+ * WITHOUT the written item. Anything found there is somebody else's
325
+ * pre-existing condition and is not this write's to answer for (#4463 D4 —
326
+ * the gate blocks new writes, never stored rows). Carrying the sibling
327
+ * collections in BOTH passes is what makes their findings cancel in the
328
+ * diff — and what gives the cross-collection rules the sibling collection
329
+ * they compare against, so the per-write verdict agrees with the
330
+ * whole-stack one instead of inventing findings (the 38-vs-4 measurement,
331
+ * PR #7886).
332
+ * - The candidate is the same context with this write's item added. When the
333
+ * written type IS one of the context collections (an `object`, `permission`
334
+ * or `book` write), the item REPLACES its stored self rather than appearing
335
+ * beside it — otherwise an update reads as a duplicate name, and for
336
+ * `objects` every lookup in the tenant's model would read as dangling. For
337
+ * any other type the item is the sole member of its own collection, so
338
+ * index-0 paths in the findings are unambiguously this write.
339
+ *
340
+ * Cost (the #4463 D2 question, measured rather than assumed): built per
341
+ * write, never cached. The construction is one filter + one spread over the
342
+ * written type's collection; the sibling collections are passed by reference.
343
+ * Over the shipped corpus (30 objects, 10 permission sets, 1 book) that is
344
+ * microseconds on a PUBLISH (never a draft autosave, D1) — a cache would buy
345
+ * nothing and would need cross-org invalidation the gate has no seam for.
346
+ */
347
+ declare function buildRuntimeWriteSnapshots(args: {
348
+ /** Singular metadata type of the item being written. */
349
+ type: string;
350
+ /** The item body as it will be persisted. */
351
+ item: unknown;
352
+ /** Live resolution context from the host runtime. */
353
+ context?: RuntimeStackContext;
354
+ }): {
355
+ baseline: AnyRec;
356
+ candidate: AnyRec;
357
+ } | null;
278
358
  /**
279
359
  * Judge one about-to-be-published metadata item against the shared registry.
280
360
  *
@@ -296,4 +376,4 @@ declare function runRuntimeAuthoringRules(args: {
296
376
  sduiManifest?: unknown;
297
377
  }): RuntimeGateResult;
298
378
 
299
- 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 };
379
+ 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, buildRuntimeWriteSnapshots as n, runRuntimeAuthoringRules as o, runtimeAuthoringRulesFor as p, runtimeGatedTypes as q, runAuthoringRules as r, splitBySeverity as s, stackKeyForType as t };
@@ -1,4 +1,4 @@
1
- type AnyRec = Record<string, unknown>;
1
+ type AnyRec$1 = Record<string, unknown>;
2
2
  /** The three commands that hold a stack to the same author-time bar. */
3
3
  declare const AUTHORING_COMMANDS: readonly ["validate", "build", "lint"];
4
4
  type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];
@@ -152,7 +152,7 @@ interface AuthoringRule {
152
152
  runtimeTypes?: readonly string[];
153
153
  /** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */
154
154
  surfaceReason?: string;
155
- run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
155
+ run: (stack: AnyRec$1, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
156
156
  }
157
157
  /**
158
158
  * `ExprIssue` is the one rule finding that carries no rule id of its own — it
@@ -168,12 +168,12 @@ declare const AUTHORING_RULES: readonly AuthoringRule[];
168
168
  /** The stack tiers a command has in hand when it runs the registry. */
169
169
  interface AuthoringRuleRun extends AuthoringRuleContext {
170
170
  /** `normalizeStackInput` output — pre-Zod-parse. Always required. */
171
- normalized: AnyRec;
171
+ normalized: AnyRec$1;
172
172
  /**
173
173
  * Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`
174
174
  * rules then read `normalized` (see `AuthoringRuleInputTier`).
175
175
  */
176
- parsed?: AnyRec;
176
+ parsed?: AnyRec$1;
177
177
  }
178
178
  /** The rules `command` runs, in registry order. */
179
179
  declare function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[];
@@ -243,7 +243,20 @@ declare function splitBySeverity(findings: readonly AuthoringFinding[]): {
243
243
  * a draft autosave). That is the correct place to spend it.
244
244
  */
245
245
 
246
- /** Everything the gate needs from the host runtime to build a snapshot. */
246
+ type AnyRec = Record<string, unknown>;
247
+ /**
248
+ * Everything the gate needs from the host runtime to build a snapshot.
249
+ *
250
+ * [#8309] Each key here doubles as the stack key the collection occupies in
251
+ * the per-write snapshot — see {@link CONTEXT_STACK_KEYS}, which is derived
252
+ * from this shape and keeps the two from drifting. The set is deliberately
253
+ * BOUNDED to what the runtime-wired rules actually read (measured, not
254
+ * projected): the three cross-collection security rules compare
255
+ * objects × permissions × books, and nothing on the runtime surface reads
256
+ * `positions` / `apps` — so those are NOT carried. Widening the snapshot is a
257
+ * one-key edit here plus a `CONTEXT_STACK_KEYS` entry, made when a rule that
258
+ * reads the collection actually crosses the wall, never in advance.
259
+ */
247
260
  interface RuntimeStackContext {
248
261
  /**
249
262
  * The live object declarations (registry + tenant overlay), as authored.
@@ -254,6 +267,27 @@ interface RuntimeStackContext {
254
267
  * package's config file and has to hedge.
255
268
  */
256
269
  objects?: readonly unknown[];
270
+ /**
271
+ * The live permission-set declarations (stack key `permissions`).
272
+ *
273
+ * [#8309] The collection the three cross-collection security rules compare
274
+ * against. Without it a per-write snapshot holds exactly ONE permission set
275
+ * (the written item), so `security-master-detail-ungranted` reads every
276
+ * detail object the tenant's OTHER sets grant as ungranted — measured as 38
277
+ * phantom findings per-write vs 4 whole-stack over the shipped corpus
278
+ * (PR #7886). `security-book-audience-unknown-set` needs the set NAMES to
279
+ * resolve a book's `audience.permissionSet` for the same reason.
280
+ */
281
+ permissions?: readonly unknown[];
282
+ /**
283
+ * The live documentation-book declarations (stack key `books`).
284
+ *
285
+ * [#8309] Carried so a `book` write is judged with its siblings present and
286
+ * replace-not-erase semantics apply to it (an updated book must not read as
287
+ * a second book of the same name), and so book-derived findings cancel in
288
+ * the differential for every other write type.
289
+ */
290
+ books?: readonly unknown[];
257
291
  }
258
292
  /** One rule's verdict at the runtime surface, carrying which rule produced it. */
259
293
  interface RuntimeGateResult {
@@ -275,6 +309,52 @@ declare function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[
275
309
  declare function runtimeGatedTypes(): string[];
276
310
  /** The stack key a metadata type occupies in a stack view, or null when unmapped. */
277
311
  declare function stackKeyForType(type: string): string | null;
312
+ /**
313
+ * The baseline/candidate stack pair the gate judges one write against, or
314
+ * `null` when no snapshot can be built (unmapped type, non-object body).
315
+ *
316
+ * Exported (#8309) so the tests that measure per-write vs whole-stack
317
+ * agreement exercise the REAL construction instead of a hand-kept mirror —
318
+ * `validate-security-posture.runtime-surface.test.ts` used to mirror this
319
+ * logic in a local `wouldGateAdd`, which is exactly the drift surface a
320
+ * snapshot change here would have missed.
321
+ *
322
+ * Shape:
323
+ * - The baseline carries every context collection ({@link CONTEXT_STACK_KEYS})
324
+ * WITHOUT the written item. Anything found there is somebody else's
325
+ * pre-existing condition and is not this write's to answer for (#4463 D4 —
326
+ * the gate blocks new writes, never stored rows). Carrying the sibling
327
+ * collections in BOTH passes is what makes their findings cancel in the
328
+ * diff — and what gives the cross-collection rules the sibling collection
329
+ * they compare against, so the per-write verdict agrees with the
330
+ * whole-stack one instead of inventing findings (the 38-vs-4 measurement,
331
+ * PR #7886).
332
+ * - The candidate is the same context with this write's item added. When the
333
+ * written type IS one of the context collections (an `object`, `permission`
334
+ * or `book` write), the item REPLACES its stored self rather than appearing
335
+ * beside it — otherwise an update reads as a duplicate name, and for
336
+ * `objects` every lookup in the tenant's model would read as dangling. For
337
+ * any other type the item is the sole member of its own collection, so
338
+ * index-0 paths in the findings are unambiguously this write.
339
+ *
340
+ * Cost (the #4463 D2 question, measured rather than assumed): built per
341
+ * write, never cached. The construction is one filter + one spread over the
342
+ * written type's collection; the sibling collections are passed by reference.
343
+ * Over the shipped corpus (30 objects, 10 permission sets, 1 book) that is
344
+ * microseconds on a PUBLISH (never a draft autosave, D1) — a cache would buy
345
+ * nothing and would need cross-org invalidation the gate has no seam for.
346
+ */
347
+ declare function buildRuntimeWriteSnapshots(args: {
348
+ /** Singular metadata type of the item being written. */
349
+ type: string;
350
+ /** The item body as it will be persisted. */
351
+ item: unknown;
352
+ /** Live resolution context from the host runtime. */
353
+ context?: RuntimeStackContext;
354
+ }): {
355
+ baseline: AnyRec;
356
+ candidate: AnyRec;
357
+ } | null;
278
358
  /**
279
359
  * Judge one about-to-be-published metadata item against the shared registry.
280
360
  *
@@ -296,4 +376,4 @@ declare function runRuntimeAuthoringRules(args: {
296
376
  sduiManifest?: unknown;
297
377
  }): RuntimeGateResult;
298
378
 
299
- 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 };
379
+ 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, buildRuntimeWriteSnapshots as n, runRuntimeAuthoringRules as o, runtimeAuthoringRulesFor as p, runtimeGatedTypes as q, runAuthoringRules as r, splitBySeverity as s, stackKeyForType as t };