refactor(artifacts): unify stable read failure ownership

Centralize completed file observations and cleanup error projection across stable-file providers while preserving each provider’s failure classification and exact cleanup leases. Cover BEAM finalization cancellation and devicetree cleanup retries.

Validated 223 focused lifecycle and MCP tests and check:fast.
This commit is contained in:
morluto
2026-10-27 03:02:05 +08:00
parent 99ff4344c0
commit bab49aaf7c
15 changed files with 258 additions and 341 deletions
+12 -26
View File
@@ -1,15 +1,16 @@
import {
projectStableArtifactFailure,
projectArtifactCloseFailure,
} from "../artifacts/StableArtifactFailure.js";
import { isSelectedRegularFileError } from "../filesystem/RegularFile.js";
import {
artifactFileCancellationCause,
artifactFilePrimaryFacts,
type ArtifactFilePrimaryFacts,
bindArtifactFileCleanup,
} from "../artifacts/ArtifactFileOperation.js";
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
import { readStableArtifact } from "../artifacts/readStableArtifact.js";
import { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
import { analysisErrorWithCleanupFailure } from "../domain/analysisErrorCleanup.js";
import { completedStableArtifactObservation } from "../artifacts/StableArtifactObservation.js";
import type { PythonPycPort } from "../application/python/PythonPycPort.js";
import type { ExecutionOptions } from "../application/AnalysisProvider.js";
import { AnalysisError } from "../domain/analysisErrorBase.js";
@@ -26,7 +27,6 @@ import {
type InspectPythonPycInput,
} from "../domain/python/pythonPyc.js";
import { ProviderAdapterError } from "../domain/providerAdapterError.js";
import { ProviderCleanupError } from "../domain/providerCleanupError.js";
import { err, ok } from "../domain/result.js";
import {
PythonPycFormatFailure,
@@ -165,19 +165,13 @@ const failureForPrimary = (
const failureFor = (cause: unknown, input: InspectPythonPycInput) => {
const primary = failureForPrimary(artifactFilePrimaryFacts(cause), input);
const observation = completedStableArtifactObservation(cause, input.path);
if (observation !== undefined) primary.retainPartialObservation(observation);
if (!(cause instanceof ArtifactReaderFailure) || cause.cleanup === undefined)
return primary;
const cleanup = new ProviderCleanupError(
return projectStableArtifactFailure(
cause,
primary,
input.path,
PYTHON_PYC_PROVIDER_IDENTITY.id,
cause.cleanup.resources,
{ reason: cause.cleanup.reason },
{ operation: OPERATION, cause },
OPERATION,
);
const error = analysisErrorWithCleanupFailure(primary, cleanup, OPERATION);
bindArtifactFileCleanup(cause, error);
return error;
};
/** Portable parser over one bounded, identity-checked regular file. */
@@ -189,19 +183,11 @@ export class PythonPycProvider implements PythonPycPort {
try {
await this.#resources.close();
} catch (cause: unknown) {
if (
!(cause instanceof ArtifactReaderFailure) ||
cause.cleanup === undefined
)
throw cause;
const error = new ProviderCleanupError(
throw projectArtifactCloseFailure(
cause,
PYTHON_PYC_PROVIDER_IDENTITY.id,
cause.cleanup.resources,
{ reason: cause.cleanup.reason },
{ cause, operation: OPERATION },
OPERATION,
);
bindArtifactFileCleanup(cause, error);
throw error;
}
}