diff --git a/docs/javascript-artifact-reconstruction.md b/docs/javascript-artifact-reconstruction.md index f166814287..6bda719d9c 100644 --- a/docs/javascript-artifact-reconstruction.md +++ b/docs/javascript-artifact-reconstruction.md @@ -72,14 +72,17 @@ code-point order. Dependency manifests remain package observations but do not declare Electron main or renderer entries. If only dependency manifests are present, the inventory artifact remains the graph root. -Package observations keep the legacy `renderer` property as a derived entrypoint -candidate: the first nonempty string from `renderer`, `browser`, then `module`. -It does not assert that the source declares a literal `renderer` field. -`renderer_declarations` records the actual present fields: strings, including -empty strings, remain exact; non-string values use `{ "status": "non-string" }` +Package observations use `renderer_declarations` to record the actual present +fields: strings, including empty strings, remain exact; non-string values use `{ "status": "non-string" }` without copying nested browser mappings. An empty record means no such fields were present; `null` means package metadata was invalid or unavailable. Renderer -role mechanisms and package load edges name the field that supplied the candidate. +role mechanisms and package load edges name the field that supplied the candidate, +selected as the first nonempty string from `renderer`, `browser`, then `module`. +The package load edge retains `declared_path`, `source_field`, and resolution +facts for that selection. New analyses no longer emit the derived package +`renderer` alias; consumers must read declarations for source metadata and load +edges for the selected candidate. Previously retained Evidence keeps its original +properties and remains readable without migration. ## Select sources before analysis diff --git a/docs/verification/javascript-retained-facts-2026-10-15.md b/docs/verification/javascript-retained-facts-2026-10-15.md index 258ba54fd8..6dbde3eecf 100644 --- a/docs/verification/javascript-retained-facts-2026-10-15.md +++ b/docs/verification/javascript-retained-facts-2026-10-15.md @@ -36,6 +36,13 @@ not a claim of globally complete analysis. Full Evidence remains exportable. The domain projector and shared application service own both CLI/MCP behavior; no new provider, executor, persistent cache or live-state freshness claim exists. +The dated measurements below requested the then-emitted derived package +`renderer` property. New analyses use `renderer_declarations` for source fields +and package `loads` edges for the selected candidate; use +`"renderer_declarations"` in place of `"renderer"` when repeating the fact query +against a new analysis. Historical retained Evidence and measurements remain +unchanged. + ## Real input and reproduction Use the official [Joplin v3.7.21 Linux AppImage](https://github.com/laurent22/joplin/releases/download/v3.7.21/Joplin-3.7.21.AppImage). diff --git a/src/application/javascript/JavaScriptArtifactGraphStructure.ts b/src/application/javascript/JavaScriptArtifactGraphStructure.ts index c3ff99f226..4ce33e7e2a 100644 --- a/src/application/javascript/JavaScriptArtifactGraphStructure.ts +++ b/src/application/javascript/JavaScriptArtifactGraphStructure.ts @@ -553,8 +553,6 @@ export const addJavaScriptPackageNodes = ( version: packageValue.version, main: packageValue.main, main_status: packageValue.main_status, - // Legacy derived candidate; source fields live in renderer_declarations. - renderer: packageValue.renderer_entrypoint?.path ?? null, renderer_declarations: packageValue.renderer_declarations, parse_status: packageValue.status, }, diff --git a/src/cli/browserCommands.ts b/src/cli/browserCommands.ts index 5a967c5134..b06fbb49bf 100644 --- a/src/cli/browserCommands.ts +++ b/src/cli/browserCommands.ts @@ -1,8 +1,9 @@ +import { webBundleDeliveryOptions } from "./javascriptRouteDelivery.js"; import { - parseJavaScriptRouteDelivery, - webBundleDeliveryOptions, -} from "./javascriptRouteDelivery.js"; -import { deliverCliAnalysisEvidence } from "./analysisEvidenceDelivery.js"; + deliverCliAnalysisEvidence, + parseCliAnalysisEvidenceDelivery, +} from "./analysisEvidenceDelivery.js"; +import { webRoutesDeliverySchema } from "../domain/analysisView/javascriptRoutesView.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import { Cli, z } from "incur"; @@ -180,9 +181,10 @@ const registerBundleAnalysis = ( run: ({ args, options }) => withCommandCancellation((signal) => logCliCommand(logger, "analyze-web-bundle", async () => { - const delivery = parseJavaScriptRouteDelivery( + const delivery = parseCliAnalysisEvidenceDelivery( "analyze_web_bundle", options, + webRoutesDeliverySchema, ); if (!delivery.ok) return projectAnalysisError(delivery.error); const provider = createBrowserObservationProvider(); diff --git a/src/cli/electronCommands.ts b/src/cli/electronCommands.ts index 804d2da92e..783f87c1c4 100644 --- a/src/cli/electronCommands.ts +++ b/src/cli/electronCommands.ts @@ -1,4 +1,5 @@ -import { parseJavaScriptRouteDelivery } from "./javascriptRouteDelivery.js"; +import { parseCliAnalysisEvidenceDelivery } from "./analysisEvidenceDelivery.js"; +import { javaScriptRoutesDeliverySchema } from "../domain/analysisView/javascriptRoutesView.js"; import { isAbsolute, resolve, sep } from "node:path"; import { Cli, z } from "incur"; @@ -321,9 +322,10 @@ const registerJavaScriptApplicationCommand = ( logger, CLI_COMMANDS.analyzeJavaScriptApplication, async () => { - const delivery = parseJavaScriptRouteDelivery( + const delivery = parseCliAnalysisEvidenceDelivery( "analyze_javascript_application", options, + javaScriptRoutesDeliverySchema, ); if (!delivery.ok) return projectAnalysisError(delivery.error); const sourcePaths = await cliJavaScriptSourcePaths( diff --git a/src/cli/javascriptRouteDelivery.ts b/src/cli/javascriptRouteDelivery.ts index 05e07f5f14..03262d90e6 100644 --- a/src/cli/javascriptRouteDelivery.ts +++ b/src/cli/javascriptRouteDelivery.ts @@ -1,12 +1,5 @@ import { z } from "incur"; -import { - javaScriptRoutesDeliverySchema, - webRoutesDeliverySchema, -} from "../domain/analysisView/javascriptRoutesView.js"; -import { - analysisEvidenceDeliveryOptions, - parseCliAnalysisEvidenceDelivery, -} from "./analysisEvidenceDelivery.js"; +import { analysisEvidenceDeliveryOptions } from "./analysisEvidenceDelivery.js"; export const javascriptRouteDeliveryOptions = { ...analysisEvidenceDeliveryOptions, @@ -24,19 +17,3 @@ export const webBundleDeliveryOptions = { "JSON web-bundle (endpoints, inferences, summary) or javascript-routes view; requires --detail summary and --evidence_output", ), }; - -/** Validate delivery before opening artifacts or attaching a browser. */ -export const parseJavaScriptRouteDelivery = ( - operation: string, - options: { - detail: "complete" | "summary"; - evidence_output?: string | undefined; - view?: string | undefined; - }, -) => { - const schema = - operation === "analyze_web_bundle" - ? webRoutesDeliverySchema - : javaScriptRoutesDeliverySchema; - return parseCliAnalysisEvidenceDelivery(operation, options, schema); -}; diff --git a/tests/boundary/filesystem/javascript-applications/javascriptPackageRendererProvenance.test.ts b/tests/boundary/filesystem/javascript-applications/javascriptPackageRendererProvenance.test.ts index 467de327ca..07bb01a5e1 100644 --- a/tests/boundary/filesystem/javascript-applications/javascriptPackageRendererProvenance.test.ts +++ b/tests/boundary/filesystem/javascript-applications/javascriptPackageRendererProvenance.test.ts @@ -42,11 +42,13 @@ it.each(["renderer", "browser", "module"] as const)( expect(metadata?.observations).toContainEqual( expect.objectContaining({ properties: expect.objectContaining({ - renderer: "entry.js", renderer_declarations: { [field]: "entry.js" }, }), }), ); + expect(metadata?.observations[0]?.properties).not.toHaveProperty( + "renderer", + ); expect(graph.edges).toContainEqual( expect.objectContaining({ source_node_id: metadata?.node_id, @@ -71,7 +73,6 @@ it("keeps present browser mappings distinct from absent renderer metadata when m }); const metadata = graph.nodes.find(({ kind }) => kind === "package"); expect(metadata?.observations[0]?.properties).toMatchObject({ - renderer: "entry.js", renderer_declarations: { browser: { status: "non-string" }, module: "entry.js", @@ -91,7 +92,6 @@ it("retains simultaneous string declarations while keeping renderer priority", a }); const metadata = graph.nodes.find(({ kind }) => kind === "package"); expect(metadata?.observations[0]?.properties).toMatchObject({ - renderer: "entry.js", renderer_declarations: { renderer: "entry.js", browser: "browser.js",