Skip to content

Commit 095c7f6

Browse files
fix(cli): os validate runs the per-package author-time rule pass os build already ran (#18769)
Fixes #18677 Clause-②: no — no key is added to any published payload, and no newly-refused input could be exhibited (measurement below). Verified through `readClause2Line` from `scripts/pm/check-clause2-carriers.mjs`, ⛔ not an ad-hoc regex. `os build` runs the artifact's authoring rules **twice**: once over the union-folded stack, then a second `runAuthoringRules('build', …)` pass over each `artifactPackages(…)` entry with `packageBodyAsStack(…)` as resolution context, de-duplicated against the union run. `os validate` ran the union pass and stopped — importing neither seam. By `compile.ts`' own description the survivors of that second pass are *"exactly the set the union could not see"*, so that whole set was findings `os build` reported and `os validate` **structurally could not**. False-clean, on the worse door: the fast pre-flight is what an author runs *before* shipping. ## The measurement `examples/app-multi-package` — the repo's only two-package fixture — at `origin/main` 09e16a5, both commands exiting 0: | | `os build --json` | `os validate --json` | |---|---|---| | before | warnings **4** (3 union + 1 per-package survivor) | warnings **3** | | after | warnings **4** | warnings **4** — same set, same order | `os build`'s own output is unchanged: text face byte-identical modulo timings and artifact size, `--json` payload identical. **Red/green, both legs, from the committed state.** With `validate.ts` alone restored to its pre-change blob (pinned commit `09e16a574`, ⛔ never a moving ref) and `artifact-packages.ts` left carrying the shared pass, so the reverted change is exactly the one the card is about: ``` runPerPackageAuthoringRules occurrences: committed=3 mutated=0 mutated blob bafa54b… != HEAD blob 340cec6… == proof the mutation reached disk unit seam 1 failed | 5 passed -> "both `os build` and `os validate` CALL the shared pass" integ parity 1 failed | 3 passed -> "these per-package findings ride `os build` and `os validate` cannot see them — the #18677 false-clean set: expected [ Array(1) ] to deeply equal [] + "package 'com.example.ppparity.core' — object \"pp_account\" · field \"industry\"" ``` Restored with `git checkout HEAD -- …` (⛔ not a bare `git checkout --`, which reads the poisoned index); `git diff HEAD` empty and the blob hash back to `340cec6253…`. After: **28 passed** in the unit run (the two pins + `vitest-tiers-partition`) and **4 passed** in the integration run. ## The card's declared-unmeasured limb, answered *"Does any authoring rule fire ONLY under `packageBodyAsStack` resolution today?"* — measured against the registry, ⛔ not guessed from rule names, with the instrument's positive control lit: | reading | result | |---|---| | per-package survivors on `examples/app-multi-package` | **1** | | of those, genuinely new (a finding the union could not see) | **0** | | of those, ECHOES (same rule + same message as a union finding, differing only in positional `path`) | **1** | | ⭐ positive control — the same instrument on a stack with a top-level-only consumer | **1 genuinely new** ⇒ the zero is measured, not a blind instrument | | per-package findings at `error` severity, any fixture tried | **0** | ⇒ **a measured "none today" for the gating tier**, and the mechanism is `packageBodyAsStack`'s own design, not luck: it hands each package the artifact's whole `packages[]` as resolution context, and the reference-integrity suite resolves object names through it (`artifactProvidedObjectNames`, `packages/lint/src/validate-object-references.ts`). Two constructed attempts at a per-package-only `error` — a view on a sibling's object, an app whose own navigation names a sibling's object — resolved identically in both runs and produced nothing. `defineStack` refuses the first shape outright at definition time. ⛔ **NOT MEASURED:** whether *any* of the 30 gating rules is reachable per-package-only. Two candidate shapes were measured and both were negative; the remaining rules were not swept one by one. ## Why this is not a second copy of the loop `runPerPackageAuthoringRules` lives beside `artifactPackages` / `packageBodyAsStack` in `utils/artifact-packages.ts`, whose header already forbids a second copy of that shape **by name**. What would have drifted between two hand-written loops is not the package reading but the **verdict** — the de-duplication key, the severity split, the `where` prefix. The severity mapping is `os build`'s, unchanged: a per-package `error` refuses, an advisory joins `warnings`. ## Tiers — both pins gate the merge queue - `test/validate-per-package-authoring-seam.test.ts` → **UNIT** (`tierSignals` = `none`). Source ratchet + the pass's door-independence, with a non-vacuity case. - `test/validate-per-package-authoring-parity.test.ts` → **INTEGRATION** (`childProcess`, `helperCliOrTsx`). Deliberately carries no `.e2e` segment, so by the orthogonal nightly cut it stays QUEUE-tier — the combination `vitest-tiers.ts` names as deliberate. Both were confirmed present in `testFilesOnDisk()` with `OS_TEST_TIERS` unset, and `test/vitest-tiers-partition.test.ts` passes. ⚠️ `test/build-json-advisory-parity.e2e.test.ts` — whose standing assertion *"nothing rides in build's `warnings` that validate does not also report"* stayed green through this entire defect, its fixtures declaring no `packages[]` — is NIGHTLY tier by name, so the queue run skips it. Run explicitly under `OS_TEST_TIERS=nightly`: **8 passed**, unaffected. ## Acceptance notes — out of scope, noted, not fixed here 1. **The de-duplication key is leaky, and the file's own sentence overstates what survives.** `findingKey` includes the positional `path`, and a collection index inside one package's body is not the index the flattened top level gives the same item — so what survives is *"the set the union could not see"* **plus every finding whose two coordinates differ**. On `examples/app-multi-package` the single survivor is an echo of the union's own `crm_account.industry` finding at the package-local index. ⛔ Not fixed here: changing the key changes what `os build` reports, a different decision from making the two doors agree. To file. Dedupe words: `findingKey positional path` · `per-package echo` · `de-duplication key collection index`. 2. **`os build` counts per-package advisories on its text face that it never prints.** `printAuthoringAdvisories(ruleAdvisories)` runs *before* the per-package loop appends to that list, so the closing line reads `⚠ 4 author-time warning(s) — see above` above a list of 3. Measured on `examples/app-multi-package`; `--json` carries all 4, so this is the text face only and is not #11727. After this change `os validate` prints all 4 — the direction is now build-side. To file. Dedupe words: `printAuthoringAdvisories before per-package loop` · `see above count mismatch` · `build text face advisory omitted`. 3. **`os lint` has the same structural gap and it is not fixed here.** The card's table reads `compile.ts` vs `validate.ts`; measured, the asymmetry was **2 of 3 doors**. `lint.ts` imports `artifactPackages` and `packageBodyAsStack` — but for its **own** intra-package duplicate-name advisory (#17821), not the shared table. Its `runAuthoringRules('lint', …)` is union-only, exactly as `validate.ts`' was. ⛔ A count is not a reading: opening the hits is what separates the two. Out of the dispatch fence, which named `os validate`. To file. Dedupe words: `os lint per-package authoring pass` · `lint.ts union-only runAuthoringRules` · `third door #18677`. `origin/main` merged at `4c837a58aa`; gates re-run on the merged tree. --- _Generated by [Claude Code](https://claude.ai/code/session_01DvvamiacK328idtBYJBxV3)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6de7a2d commit 095c7f6

7 files changed

Lines changed: 731 additions & 37 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
'@objectstack/cli': minor
3+
---
4+
5+
`os validate` runs the per-package author-time rule pass `os build` already ran — the false-clean residue #17069 left one layer down.
6+
7+
`os build` runs the artifact's authoring rules **twice**: once over the union-folded stack, then a second `runAuthoringRules('build', …)` pass over each `artifactPackages(…)` entry with `packageBodyAsStack(…)` as resolution context, de-duplicated against the union run. `os validate` ran the union pass and stopped — it imported neither seam. By `compile.ts`' own description the survivors of that second pass are "exactly the set the union could not see", so that whole set was findings `os build` reported and `os validate` **structurally could not**. The direction is false-clean, and on the worse door: the fast pre-flight is what an author runs *before* shipping, so its clean bill of health is the strongest false assurance the three commands can give.
8+
9+
Measured on `origin/main` 09e16a574 over `examples/app-multi-package`, both commands exiting 0:
10+
11+
```
12+
os build --json warnings: 4 <- 3 union + 1 per-package survivor
13+
os validate --json warnings: 3 <- the survivor is the defect
14+
```
15+
16+
After: both report 4, the same set, in the same order.
17+
18+
**The loop is now one seam, not two copies.** `runPerPackageAuthoringRules` lives beside `artifactPackages` / `packageBodyAsStack` in `utils/artifact-packages.ts`, whose header already forbids a second copy of that shape by name. What would have drifted between two hand-written loops is not the package reading but the **verdict** — the de-duplication key, the severity split, the `where` prefix. `os build`'s observable output is unchanged (text face byte-identical modulo timings; `--json` payload identical).
19+
20+
**Severity mapping is `os build`'s, unchanged.** A per-package `error` refuses (exit 1); an advisory joins `warnings`. So `os validate` is narrowed only to the bar the command that *ships* already holds: every input it can now refuse is one `os build` already refuses, which means **nothing that builds today stops validating**. No newly-refused input could be exhibited on any fixture — across the repo's own two-package example and three constructed variants the observable change is advisory-only, because `packageBodyAsStack` hands each package the artifact's whole `packages[]` as resolution context and the reference-integrity suite resolves object names through it. Graded `minor` rather than `patch` for the new observable step line, the new advisories and the newly reachable non-zero exit; ⛔ **not** declared breaking, because the narrowing could not be exhibited and is bounded by an existing gate.
21+
22+
Unchanged and out of scope: the ADR-0130 D4 union fold (#17069, fixed — `authoringRuleUnionStack` is in both commands), `--json` rendering (#11727), and disagreements *within* the per-package pass's verdicts (#18204). `os lint` still runs the union pass alone; its `artifactPackages` / `packageBodyAsStack` imports serve its own intra-package duplicate-name advisory, not the shared table.

‎packages/cli/src/commands/compile.ts‎

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
import { loadConfig, namedExportRejectionHints } from '../utils/config.js';
1717
import { lowerCallables } from '../utils/lower-callables.js';
1818
import { authoringRuleUnionStack } from '../utils/stack-collections.js';
19-
import { artifactPackages, packageBodyAsStack } from '../utils/artifact-packages.js';
19+
import { artifactPackages, runPerPackageAuthoringRules } from '../utils/artifact-packages.js';
2020
import { buildAccessMatrix, diffAccessMatrix } from '@objectstack/lint';
2121
import { runAuthoringRules, splitBySeverity, authoringRulesFor } from '@objectstack/lint';
2222
import { resolveSduiManifest } from '../utils/sdui-manifest.js';
@@ -59,10 +59,6 @@ import {
5959
} from '../utils/permission-set-name-collisions.js';
6060
import type { PermissionSetNameCollisionDiagnostic } from '@objectstack/plugin-security';
6161

62-
/** Identity of one finding, for the per-package de-duplication below. */
63-
const findingKey = (f: { rule: string; where: string; path: string; message: string }): string =>
64-
`${f.rule}\u0000${f.where}\u0000${f.path}\u0000${f.message}`;
65-
6662
export default class Compile extends Command {
6763
static override description = 'Compile ObjectStack configuration to JSON artifact';
6864

@@ -425,32 +421,35 @@ export default class Compile extends Command {
425421
// is the one `artifactPackages` above walked, off the same parsed
426422
// stack, so the context a package resolves against is exactly the set
427423
// of packages this artifact will register (ADR-0130 D4/D5).
428-
const artifactPackageEntries = (result.data as Record<string, unknown>).packages;
424+
//
425+
// ⛔ [#18677] The LOOP itself is not written here either — it is
426+
// `runPerPackageAuthoringRules`, beside the two seams it reads, for
427+
// the reason that module's header already gives about them: the
428+
// `os validate` door owes the identical pass, and the thing that
429+
// would have drifted between two hand-written copies is the VERDICT
430+
// (the de-duplication key, the severity split, the `where` prefix),
431+
// not the package reading. Every observable of this step — the step
432+
// line, the advisory order, the error sentence, the `--json` envelope
433+
// — is unchanged; only the loop moved.
434+
//
435+
// The count is read for the step LINE before the pass runs, so the
436+
// line still precedes the work it announces on every path — including
437+
// a rule that throws inside it.
429438
const packageEntries = artifactPackages(result.data as Record<string, unknown>);
430439
if (packageEntries.length > 0) {
431440
if (!flags.json) {
432441
printStep(`Running author-time rules per package (${packageEntries.length})...`);
433442
}
434-
const alreadyReported = new Set(findings.map(findingKey));
435-
const perPackageErrors: Array<{ package: string } & typeof ruleErrors[number]> = [];
436-
for (const pkg of packageEntries) {
437-
const asStack = packageBodyAsStack(pkg.body, artifactPackageEntries);
438-
const pkgFindings = runAuthoringRules('build', {
439-
normalized: asStack,
440-
parsed: asStack,
441-
sduiManifest: resolveSduiManifest(),
442-
loweredHookRefs: lowering.loweredHookRefs,
443-
}).filter((f) => !alreadyReported.has(findingKey(f)));
444-
for (const f of pkgFindings) alreadyReported.add(findingKey(f));
445-
const split = splitBySeverity(pkgFindings);
446-
ruleAdvisories = [
447-
...ruleAdvisories,
448-
...split.advisories.map((a) => ({ ...a, where: `package '${pkg.id}' — ${a.where}` })),
449-
];
450-
perPackageErrors.push(
451-
...split.errors.map((e) => ({ ...e, package: pkg.id, where: `package '${pkg.id}' — ${e.where}` })),
452-
);
453-
}
443+
const perPackage = runPerPackageAuthoringRules({
444+
command: 'build',
445+
parsed: result.data as Record<string, unknown>,
446+
unionFindings: findings,
447+
sduiManifest: resolveSduiManifest(),
448+
loweredHookRefs: lowering.loweredHookRefs,
449+
});
450+
const perPackageErrors: Array<{ package: string } & typeof ruleErrors[number]> =
451+
perPackage.errors;
452+
ruleAdvisories = [...ruleAdvisories, ...perPackage.advisories];
454453
if (perPackageErrors.length > 0) {
455454
if (flags.json) {
456455
await emitJson(

‎packages/cli/src/commands/validate.ts‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import {
1515
import { loadConfig, namedExportRejectionHints } from '../utils/config.js';
1616
import { lowerCallables } from '../utils/lower-callables.js';
1717
import { authoringRuleUnionStack } from '../utils/stack-collections.js';
18+
// [#18677] The per-package half of the author-time rule run, shared with
19+
// `os compile` — ⛔ the loop is not re-written here; see that module's header.
20+
import { artifactPackages, runPerPackageAuthoringRules } from '../utils/artifact-packages.js';
1821
import { runAuthoringRules, splitBySeverity, authoringRulesFor } from '@objectstack/lint';
1922
import { resolveSduiManifest } from '../utils/sdui-manifest.js';
2023
import { preflightRequiredCapabilities, renderCapabilityMessage } from '../utils/capability-preflight.js';
@@ -382,6 +385,70 @@ export default class Validate extends Command {
382385
this.exit(1);
383386
}
384387

388+
// 3a-ii. [ADR-0130 D4, #18677] The SAME rule table, once per PACKAGE —
389+
// the second half of the run above, and the half this door ran
390+
// without.
391+
//
392+
// `os build` has run it since #16611; `os validate` ran the union
393+
// fold and stopped, importing neither `artifactPackages` nor
394+
// `packageBodyAsStack`. `compile.ts` step 3b-ii says what survives
395+
// the de-duplication is "exactly the set the union could not see" ⇒
396+
// that whole set was findings `os build` reported and this command
397+
// structurally could not. Same FALSE-CLEAN direction #17069 fixed one
398+
// layer up, and the worse door for it: the fast inner-loop check is
399+
// what an author runs BEFORE shipping, so its clean bill of health is
400+
// the strongest false assurance the three commands can give.
401+
//
402+
// ⛔ Not a second copy of the loop — `runPerPackageAuthoringRules` is
403+
// the one the build door calls, so the de-duplication key, the
404+
// severity split and the `where` prefix cannot drift between the two
405+
// doors. That drift is the defect this step closes, one layer down.
406+
//
407+
// The SEVERITY MAPPING is `os build`'s, unchanged and deliberately:
408+
// an `error` refuses (exit 1), an advisory joins `ruleAdvisories` and
409+
// rides `warningsSoFar()`. The card asked for the asymmetry, ⛔ not
410+
// for a severity judgement, and a per-package `error` is one
411+
// `os build` ALREADY refuses — so this narrows `os validate` to the
412+
// bar the command that ships already holds, never past it.
413+
//
414+
// Skipped entirely for a stack with no `packages[]`: one package by
415+
// definition, already judged whole by the union run above.
416+
const packageEntries = artifactPackages(result.data as Record<string, unknown>);
417+
if (packageEntries.length > 0) {
418+
if (!flags.json) {
419+
printStep(`Running author-time rules per package (${packageEntries.length})...`);
420+
}
421+
const perPackage = runPerPackageAuthoringRules({
422+
command: 'validate',
423+
parsed: result.data as Record<string, unknown>,
424+
unionFindings: findings,
425+
sduiManifest: resolveSduiManifest(),
426+
// [#16546] The same ref set the union run above was handed, so a
427+
// per-package hook write-set finding reports at the same `path` the
428+
// other two doors report it at.
429+
loweredHookRefs: lowering.loweredHookRefs,
430+
});
431+
ruleAdvisories = [...ruleAdvisories, ...perPackage.advisories];
432+
if (perPackage.errors.length > 0) {
433+
if (flags.json) {
434+
await emitJson({
435+
valid: false,
436+
errors: perPackage.errors,
437+
warnings: warningsSoFar(),
438+
conversions: conversionNotices,
439+
duration: timer.elapsed(),
440+
});
441+
this.exit(1);
442+
}
443+
console.log('');
444+
printError(
445+
`Author-time rules failed inside the artifact's packages (${perPackage.errors.length} issue${perPackage.errors.length > 1 ? 's' : ''})`,
446+
);
447+
printAuthoringRuleErrors(perPackage.errors, { remedy: JSON_FULL_LIST_REMEDY });
448+
this.exit(1);
449+
}
450+
}
451+
385452
// 3b. [#3366] Installable-provider preflight — the shift-left of the
386453
// `serve`-time capability check. `os validate` previously only checked
387454
// the `requires` tokens against the vocabulary (ADR-0066), never

‎packages/cli/src/utils/artifact-packages.ts‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,30 @@
2424
* slightly differently is how one entry comes to judge a different set of
2525
* packages than the other while both look right — so the functions moved here
2626
* unchanged and both entries call these.
27+
*
28+
* Since #18677 the module also carries the PASS those two seams exist to feed —
29+
* {@link runPerPackageAuthoringRules} — for the same reason one layer out: the
30+
* `os build` door ran it and the `os validate` door did not, and a second copy
31+
* of the loop is how that asymmetry would come back.
2732
*/
2833

34+
import {
35+
runAuthoringRules,
36+
splitBySeverity,
37+
type AuthoringCommand,
38+
type AuthoringFinding,
39+
} from '@objectstack/lint';
40+
41+
/**
42+
* Identity of one finding, for the per-package de-duplication below.
43+
*
44+
* Moved here from `compile.ts` unchanged (#18677): the two doors must
45+
* de-duplicate identically, or "the set the union could not see" means two
46+
* different things depending on which command the author happened to run.
47+
*/
48+
const findingKey = (f: { rule: string; where: string; path: string; message: string }): string =>
49+
[f.rule, f.where, f.path, f.message].join('\u0000');
50+
2951
/**
3052
* The artifact's package entries, as `{ index, id, body }` (ADR-0130 D4).
3153
*
@@ -107,3 +129,90 @@ export function packageBodyAsStack(
107129
): Record<string, unknown> {
108130
return { ...body, manifest: body, packages: artifactPackageEntries };
109131
}
132+
133+
/**
134+
* The author-time rule table, run ONCE PER PACKAGE and de-duplicated against a
135+
* union run — the pass `os build` has run since #16611 and `os validate` did
136+
* not (#18677).
137+
*
138+
* ## Why it lives here and not in one of the two commands
139+
*
140+
* It is the THIRD entry to owe the shape the module header describes, and the
141+
* header's fence binds it: the only ways to reach `compile.ts`' loop from
142+
* `validate.ts` are to import one oclif command from another — pulling the
143+
* lowerer and the docs sweep into every `os validate` invocation — or to write
144+
* a second copy. ⛔ The second copy is what must not happen, and here it would
145+
* not be the `{index,id,body}` reading that drifted but the VERDICT: two loops
146+
* choosing their own de-duplication key, their own severity split or their own
147+
* `where` prefix is how one door comes to report a different set from the other
148+
* while both look right. That is the defect #18677 is, one layer down.
149+
*
150+
* ## What the asymmetry was, measured
151+
*
152+
* `os build` ran this pass; `os validate` ran the union fold and stopped,
153+
* importing neither seam above. `compile.ts`' own comment says what survives
154+
* the de-duplication is "exactly the set the union could not see" ⇒ that whole
155+
* set was findings `os build` reported and `os validate` structurally could
156+
* not. The direction is FALSE-CLEAN, and on the command an author runs BEFORE
157+
* shipping — the same direction and the same door #17069 fixed one layer up,
158+
* which is why `authoringRuleUnionStack` being in both commands did not settle
159+
* it. `packages/cli/test/build-json-advisory-parity.e2e.test.ts` already
160+
* asserted "nothing rides in build's `warnings` that validate does not also
161+
* report"; it stayed green because its fixture declares no `packages[]` at all,
162+
* so the pass it would have caught never ran there.
163+
*
164+
* ## The de-duplication key is the caller's, and it is not perfect
165+
*
166+
* `findingKey` below is `compile.ts`' key, moved unchanged: `rule`, `where`,
167+
* `path`, `message`. ⚠️ `path` is POSITIONAL, and a collection index in one
168+
* package's own body is not the index the flattened top level gives the same
169+
* item — so a finding on any package whose local index differs from its
170+
* flattened one survives the filter as an ECHO of a union finding rather than
171+
* as something the union could not see. Measured on `examples/app-multi-package`
172+
* (2 packages, `crm_account.industry`): 1 survivor, 0 of them new. ⛔ Not fixed
173+
* here — changing the key changes what `os build` reports, which is a separate
174+
* decision from making the two doors agree, and agreeing IMPERFECTLY at one
175+
* seam is strictly better than disagreeing at two. When it is fixed it is
176+
* fixed once, for both commands, which is the property this module buys.
177+
*/
178+
export function runPerPackageAuthoringRules(run: {
179+
/** Which door is asking — the same string its union run passed. */
180+
command: AuthoringCommand;
181+
/** The PARSED stack, as `artifactPackages` reads it. */
182+
parsed: Record<string, unknown>;
183+
/** The union run's findings, whose keys this pass de-duplicates against. */
184+
unionFindings: readonly AuthoringFinding[];
185+
sduiManifest?: unknown;
186+
loweredHookRefs?: ReadonlySet<string>;
187+
}): {
188+
/** How many package entries were walked — 0 means the pass did not run. */
189+
packageCount: number;
190+
errors: Array<{ package: string } & AuthoringFinding>;
191+
advisories: AuthoringFinding[];
192+
} {
193+
const artifactPackageEntries = run.parsed.packages;
194+
const packageEntries = artifactPackages(run.parsed);
195+
const errors: Array<{ package: string } & AuthoringFinding> = [];
196+
const advisories: AuthoringFinding[] = [];
197+
if (packageEntries.length === 0) return { packageCount: 0, errors, advisories };
198+
199+
const alreadyReported = new Set(run.unionFindings.map(findingKey));
200+
for (const pkg of packageEntries) {
201+
const asStack = packageBodyAsStack(pkg.body, artifactPackageEntries);
202+
const pkgFindings = runAuthoringRules(run.command, {
203+
normalized: asStack,
204+
parsed: asStack,
205+
sduiManifest: run.sduiManifest,
206+
loweredHookRefs: run.loweredHookRefs,
207+
}).filter((f) => !alreadyReported.has(findingKey(f)));
208+
for (const f of pkgFindings) alreadyReported.add(findingKey(f));
209+
const split = splitBySeverity(pkgFindings);
210+
advisories.push(
211+
...split.advisories.map((a) => ({ ...a, where: `package '${pkg.id}' — ${a.where}` })),
212+
);
213+
errors.push(
214+
...split.errors.map((e) => ({ ...e, package: pkg.id, where: `package '${pkg.id}' — ${e.where}` })),
215+
);
216+
}
217+
return { packageCount: packageEntries.length, errors, advisories };
218+
}

0 commit comments

Comments
 (0)