mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 16:26:36 +00:00
fix(artifacts): compare leaf inventories using explicit coverage
Classify coverage once at inventory production and require it in current outputs. Normalize older retained Evidence at the inventory parser, reject inconsistent page statuses, and remove comparison heuristics over limitation prose. Validation: public CLI-to-MCP plist comparison, changed-behavior and unknowns workflow; boundary checks for legacy Evidence, partial coverage, page disagreement, content and executable changes; generated docs checks. Fixes #2173.
This commit is contained in:
1 parent
8d4d631606
commit
ed485a3dc8
11 files changed
+379
-59
No files matched your search
@@ -1,8 +1,8 @@
|
||||
import { realpath } from "node:fs/promises";
|
||||
|
||||
import {
|
||||
artifactInventoryResultSchema,
|
||||
type ArtifactInventoryResult,
|
||||
artifactInventoryOutputSchema,
|
||||
type ArtifactInventoryOutput,
|
||||
} from "../../domain/artifactGraph.js";
|
||||
import { abortIfNeeded } from "../ArtifactHash.js";
|
||||
import { scanCanonicalArtifactInventoryInScope } from "./scanCanonical.js";
|
||||
@@ -13,9 +13,9 @@ import type { ArtifactInventorySnapshot } from "../../domain/artifactInventorySn
|
||||
export const inventoryArtifact = async (
|
||||
inputPath: string,
|
||||
options: ArtifactInventoryOptions,
|
||||
): Promise<ArtifactInventoryResult> => {
|
||||
): Promise<ArtifactInventoryOutput> => {
|
||||
const snapshot = await scanArtifactInventory(inputPath, options);
|
||||
return artifactInventoryResultSchema.parse(snapshot);
|
||||
return artifactInventoryOutputSchema.parse(snapshot);
|
||||
};
|
||||
|
||||
/** Scan an artifact once and retain the complete immutable graph for projection. */
|
||||
|
||||
@@ -4,7 +4,11 @@ import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
|
||||
import { MachOSliceArtifactReader } from "../MachOSliceArtifactReader.js";
|
||||
import { NativeDmgArtifactReader } from "../NativeDmgArtifactReader.js";
|
||||
import { ZipArtifactReader } from "../ZipArtifactReader.js";
|
||||
import type { ArtifactOccurrence } from "../../domain/artifactGraph.js";
|
||||
import type {
|
||||
ArtifactInventoryCoverage,
|
||||
ArtifactOccurrence,
|
||||
IntegrityContradiction,
|
||||
} from "../../domain/artifactGraph.js";
|
||||
import {
|
||||
isZipPackageFormat,
|
||||
ZIP_NON_ENTRY_TAIL_LIMITATION,
|
||||
@@ -36,20 +40,42 @@ export const createReader = (
|
||||
}
|
||||
};
|
||||
|
||||
export const inventoryLimitations = (
|
||||
/** Classify source coverage and preserve its user-facing explanation once. */
|
||||
export const inventoryAssessment = (
|
||||
format: ArtifactOccurrence["artifact_format"],
|
||||
reader: ArtifactReader | undefined,
|
||||
): string[] => {
|
||||
if (reader !== undefined)
|
||||
return isZipPackageFormat(reader.format)
|
||||
? [ZIP_NON_ENTRY_TAIL_LIMITATION]
|
||||
: [];
|
||||
// dmg and pkg reach here only after classifyRootFormat matched their bytes.
|
||||
if (format === "dmg" || format === "pkg")
|
||||
return [
|
||||
integrityContradictions: readonly IntegrityContradiction[],
|
||||
caseCollisionLimitation: string | undefined,
|
||||
): {
|
||||
readonly coverage_status: ArtifactInventoryCoverage;
|
||||
readonly limitations: string[];
|
||||
} => {
|
||||
let coverageStatus: ArtifactInventoryCoverage = "complete";
|
||||
const limitations: string[] = [];
|
||||
if (reader !== undefined) {
|
||||
if (isZipPackageFormat(reader.format))
|
||||
limitations.push(ZIP_NON_ENTRY_TAIL_LIMITATION);
|
||||
} else if (format === "dmg" || format === "pkg") {
|
||||
coverageStatus = "partial";
|
||||
limitations.push(
|
||||
`${format.toUpperCase()} root hash is observed; child inventory requires a native macOS adapter.`,
|
||||
];
|
||||
if (format === "mach-o-universal" && process.platform !== "darwin")
|
||||
return ["Universal Mach-O slices require the native macOS lipo adapter."];
|
||||
return ["Artifact has no child container entries."];
|
||||
);
|
||||
} else if (format === "mach-o-universal" && process.platform !== "darwin") {
|
||||
coverageStatus = "partial";
|
||||
limitations.push(
|
||||
"Universal Mach-O slices require the native macOS lipo adapter.",
|
||||
);
|
||||
} else {
|
||||
// The graph's modeled scope is the root itself for recognized leaf formats.
|
||||
limitations.push("Artifact has no child container entries.");
|
||||
}
|
||||
if (caseCollisionLimitation !== undefined) {
|
||||
coverageStatus = "partial";
|
||||
limitations.push(caseCollisionLimitation);
|
||||
}
|
||||
if (integrityContradictions.length > 0)
|
||||
limitations.push(
|
||||
`${String(integrityContradictions.length)} integrity contradiction(s) were recorded; mismatched content is observed-untrusted.`,
|
||||
);
|
||||
return { coverage_status: coverageStatus, limitations };
|
||||
};
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
hashStableRootArtifact,
|
||||
hashStableRootArtifactHandle,
|
||||
} from "./hashStableRootArtifact.js";
|
||||
import { createReader, inventoryLimitations } from "./reader.js";
|
||||
import { createReader, inventoryAssessment } from "./reader.js";
|
||||
import {
|
||||
scanReader,
|
||||
type PendingIntegrityContradiction,
|
||||
@@ -268,6 +268,12 @@ const buildInventorySnapshot = async (
|
||||
orderedOccurrences,
|
||||
orderedEdges,
|
||||
});
|
||||
const assessment = inventoryAssessment(
|
||||
rootFormat,
|
||||
reader,
|
||||
integrityContradictions,
|
||||
input.caseCollisionLimitation,
|
||||
);
|
||||
return {
|
||||
manifest,
|
||||
nodes: orderedNodes,
|
||||
@@ -275,12 +281,8 @@ const buildInventorySnapshot = async (
|
||||
edges: orderedEdges,
|
||||
provenance: reader?.provenance() ?? [],
|
||||
integrity_contradictions: integrityContradictions,
|
||||
limitations: buildLimitations(
|
||||
rootFormat,
|
||||
reader,
|
||||
integrityContradictions,
|
||||
input.caseCollisionLimitation,
|
||||
),
|
||||
coverage_status: assessment.coverage_status,
|
||||
limitations: assessment.limitations,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -393,18 +395,3 @@ const buildManifest = ({
|
||||
occurrence_count: orderedOccurrences.length,
|
||||
edge_count: orderedEdges.length,
|
||||
});
|
||||
|
||||
const buildLimitations = (
|
||||
rootFormat: ArtifactOccurrence["artifact_format"],
|
||||
reader: ArtifactReader | undefined,
|
||||
integrityContradictions: readonly IntegrityContradiction[],
|
||||
caseCollisionLimitation: string | undefined,
|
||||
): string[] => [
|
||||
...inventoryLimitations(rootFormat, reader),
|
||||
...(caseCollisionLimitation === undefined ? [] : [caseCollisionLimitation]),
|
||||
...(integrityContradictions.length === 0
|
||||
? []
|
||||
: [
|
||||
`${String(integrityContradictions.length)} integrity contradiction(s) were recorded; mismatched content is observed-untrusted.`,
|
||||
]),
|
||||
];
|
||||
Reference in new issue
Block a user