[GH-ISSUE #5992] [Bug] inspect_asset_catalog loses the primary failure when directory cleanup fails #3660

Closed
opened 2026-10-31 00:42:48 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 21, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5992

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

When catalog inspection fails and directory cleanup also rejects, inspect_asset_catalog returns only the cleanup failure. A catalog changed during inspection ordinarily produces reason integrity, category integrity_mismatch, and the affected catalog path. Adding a controlled directory-close failure changes that result to reason io, category execution_failure, with generic file-access advice. The changed-catalog diagnosis disappears from the projected result and the complete Error/AggregateError cause graph.

The top-level cleanup_incomplete code correctly reports unresolved cleanup. The defect is that the original catalog failure is discarded rather than retained alongside it.

Reproduction

From a macOS source checkout with locked dependencies installed, save the following as src/artifacts/apple/assetCatalogCleanup.test.ts and run:

node node_modules/vitest/vitest.mjs run src/artifacts/apple/assetCatalogCleanup.test.ts --maxWorkers=1

This five-case characterization test was executed against the production artifact-provider boundary. It supplies successful utility stdout rather than running native assetutil. It uses real file identity checks and native directory handles, injecting EIO before native directory close. Test finalization calls the captured native closes to release every handle. The final case injects a typed reader-cleanup failure after normal reader cleanup to isolate the outer finalizer. The exact test below ran with REA_ASSET_TEST_ROOT selecting a disposable directory; all five cases passed on Node 26.12.0 and again on the repository-pinned Node 24.18.0. Its assertions describe the current faulty behavior, not a repair.

import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { describe, expect, it, vi } from "vitest";
import { ArtifactProvider } from "../ArtifactProvider.js";
import { ArtifactReaderFailure } from "../ArtifactReader.js";
import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js";
import { projectAnalysisError } from "../../domain/analysisErrorProjection.js";
import { execFileOutput } from "../../process/ExecFileOutput.js";

const state = vi.hoisted(() => ({
  denyDirectoryClose: false,
  resourcePath: "",
  releases: [] as (() => Promise<void>)[],
  closeFailures: 0,
}));
vi.mock("node:fs/promises", async (importOriginal) => {
  const actual = await importOriginal<typeof import("node:fs/promises")>();
  return {
    ...actual,
    opendir: async (...args: Parameters<typeof actual.opendir>) => {
      const handle = await actual.opendir(...args);
      if (String(args[0]) === state.resourcePath) {
        const close = handle.close.bind(handle);
        state.releases.push(async () => { try { await close(); } catch (e) {
          if ((e as NodeJS.ErrnoException).code !== "ERR_DIR_CLOSED") throw e;
        }});
        vi.spyOn(handle, "close").mockImplementation(async () => {
          if (state.denyDirectoryClose) {
            state.closeFailures += 1;
            throw Object.assign(new Error("controlled directory close rejection"), {code: "EIO"});
          }
          await close();
        });
      }
      return handle;
    },
  };
});
vi.mock("../../process/ExecFileOutput.js", async (importOriginal) => {
  const actual = await importOriginal<typeof import("../../process/ExecFileOutput.js")>();
  return {...actual, execFileOutput: vi.fn(actual.execFileOutput)};
});

function causes(value: unknown): string[] {
  const seen = new Set<unknown>();
  const result: string[] = [];
  const walk = (v: unknown) => {
    if (!(v instanceof Error) || seen.has(v)) return;
    seen.add(v); result.push(v.message);
    walk(v.cause);
    if (v instanceof AggregateError) for (const e of v.errors) walk(e);
  };
  walk(value); return result;
}

describe("asset catalog primary failure and cleanup", () => {
  it.each([
    {primary: false, cleanup: false},
    {primary: true, cleanup: false},
    {primary: false, cleanup: true},
    {primary: true, cleanup: true},
    {primary: true, cleanup: "reader" as const},
  ])("observes $primary primary / $cleanup cleanup failure", async ({primary, cleanup}) => {
    const bundle = await mkdtemp(join(process.env.REA_ASSET_TEST_ROOT ?? tmpdir(), "fixture-"));
    const resources = join(bundle, "Contents", "Resources");
    await mkdir(resources, {recursive: true});
    const catalog = join(resources, "Assets.car");
    await writeFile(catalog, "inert catalog bytes");
    state.resourcePath = await import("node:fs/promises").then(m => m.realpath(resources));
    state.denyDirectoryClose = cleanup === true; state.closeFailures = 0; state.releases = [];
    if (cleanup === "reader") {
      const close = DirectoryArtifactReader.prototype.close;
      vi.spyOn(DirectoryArtifactReader.prototype, "close").mockImplementation(async function () {
        await close.call(this);
        state.closeFailures += 1;
        throw new ArtifactReaderFailure("io", "controlled reader cleanup rejection", {
          cleanup: {reason: "controlled reader cleanup rejection", resources: [resources]},
        });
      });
    }
    vi.mocked(execFileOutput).mockImplementationOnce(async (command, args) => {
      expect(command).toBe("/usr/bin/assetutil"); expect(args[0]).toBe("--info");
      if (primary) await writeFile(catalog, "changed catalog bytes with a different length");
      return {stdout: "[]", stderr: "", stdoutBytes: 2, stderrBytes: 0};
    });
    const client = new ArtifactProvider({}, "darwin").createBundleResourceClient({
      callerPath: bundle, canonicalPath: bundle,
      infoPlistPath: join(bundle, "Contents", "Info.plist"),
      layout: "contents", executableSha256: null,
    });
    try {
      const result = await client.execute("inspect_asset_catalog", {});
      if (!primary && !cleanup) {expect(result.ok).toBe(true);return;}
      expect(result.ok).toBe(false);
      if (result.ok) throw new Error("expected failure");
      const projected = projectAnalysisError(result.error);
      const chain = causes(result.error);
      console.log(JSON.stringify({primary, cleanup, code: projected.code, category: projected.category, reason: projected.details?.reason, primaryRetained: chain.some(m => m.includes("Asset catalog changed during inspection")), closeFailures: state.closeFailures}));
      if (!cleanup) {
        expect(projected).toMatchObject({code: "artifact_operation_failed", details: {reason: "integrity"}});
        expect(chain.some(m => m.includes("Asset catalog changed during inspection"))).toBe(true);
      } else {
        expect(state.closeFailures).toBeGreaterThan(0);
        expect(projected).toMatchObject({code: "cleanup_incomplete", details: {reason: "io", cleanup: "incomplete"}});
        expect(chain.some(m => m.includes("Asset catalog changed during inspection"))).toBe(false);
        expect(JSON.stringify(projected)).not.toContain("Asset catalog changed during inspection");
      }
    } finally {
      state.denyDirectoryClose = false;
      await client.close();
      for (const release of state.releases) await release();
      vi.restoreAllMocks();
      await rm(bundle, {recursive:true, force:true});
    }
  });
});

Expected behavior

Retain the primary catalog failure's reason, diagnostic, category and cause while also reporting cleanup uncertainty and remaining resources. The one-shot lifecycle contract requires preserving the primary diagnostic; the cancellation/lifecycle rule explicitly separates the primary failure from cleanup classification. ArtifactReaderFailure.withCleanup already models that composition.

For this integrity case, the final code can remain cleanup_incomplete while category integrity_mismatch, reason integrity, the changed-catalog detail/cause, and cleanup resources all survive.

Impact and scope

P2: coincident catalog and cleanup failures erase the actionable diagnosis and alter machine-readable classification and recovery advice. The primary-only, cleanup-only, and combined controls establish the difference. No data corruption or security-boundary crossing was observed. Naturally occurring close-failure frequency and real-app prevalence are unknown. No general caller-side way to recover the discarded primary failure was established.

REA version

rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e. Latest npm version checked: 6.7.0. Fetched origin/main at 25e1a24a3ed3671a0148757555f18d16c29d936a leaves the producing paths unchanged. That newer revision was source-compared, not executed; its unrelated projection helper change does not alter this path.

Runtime and platform

macOS 27.0.1 (26A434), arm64; Node.js v24.18.0 (repository pin) and v26.12.0; locked Vitest 5.0.3. Native assetutil and deep-analysis engines were not run.

Target and artifact context

A synthetic macOS-layout resource directory containing Contents/Resources/Assets.car with inert fixture bytes. The test changes those bytes during the supplied utility call to trigger the production integrity check. No target code runs, and native catalog validity is not asserted.

Interface

Other

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

Observed summaries from the focused test:

{"primary":true,"cleanup":false,"code":"artifact_operation_failed","category":"integrity_mismatch","reason":"integrity","primaryRetained":true,"closeFailures":0}
{"primary":false,"cleanup":true,"code":"cleanup_incomplete","category":"execution_failure","reason":"io","primaryRetained":false,"closeFailures":2}
{"primary":true,"cleanup":true,"code":"cleanup_incomplete","category":"execution_failure","reason":"io","primaryRetained":false,"closeFailures":2}
{"primary":true,"cleanup":"reader","code":"cleanup_incomplete","category":"execution_failure","reason":"io","primaryRetained":false,"closeFailures":2}

The ordinary success control also passes. primaryRetained checks the complete Error/AggregateError cause graph for Asset catalog changed during inspection. The combined result's projection also lacks that diagnosis. With cleanup permitted, the diagnostic is Asset catalog changed during inspection: Contents/Resources/Assets.car. With failed cleanup, the projected message is Artifact could not be read or written. Check file access and try again., and details.detail describes only directory cleanup.

Typed outcome or recovery expected

Preserve ArtifactReaderFailure("integrity", ...) as the primary outcome and attach the cleanup observation without replacing it. Preserve exact cleanup ownership and do not clear cleanup uncertainty merely to restore the primary classification. The canonical projection already keeps primary details alongside cleanup_incomplete when the producing error retains them.

Analysis or suspected cause

Source-derived: collectAssetCatalogs unconditionally awaits reader.close() in finally. The identity check throws the primary integrity failure, but DirectoryArtifactReader.close can throw an independent cleanup failure. JavaScript propagates that finalizer exception instead. The provider translation then receives only the cleanup error.

Additional context

The runtime evidence comes from the production artifact-provider boundary, not a CLI subprocess or MCP transport. Public reachability is source-derived: the CLI registration dispatches this operation through the same provider. No software fix, restoration check, or last known good revision was established.

Related #5698 concerns damaged catalog bytes reported as I/O, #5976 concerns utility deadlines, and #5977 concerns malformed successful utility stdout. This case uses successful [] stdout and an independently detected catalog identity change, then loses that already correct primary error during cleanup. #3935/#4240 concern CLI finalizers and resource-field composition; #4743 concerns Apple dispatch descriptor cleanup; #4522 concerns compiler-root removal; #5963 concerns Rizin cancellation. Their fixes do not alter this catalog-reader finalizer.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5992 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior When catalog inspection fails and directory cleanup also rejects, `inspect_asset_catalog` returns only the cleanup failure. A catalog changed during inspection ordinarily produces reason `integrity`, category `integrity_mismatch`, and the affected catalog path. Adding a controlled directory-close failure changes that result to reason `io`, category `execution_failure`, with generic file-access advice. The changed-catalog diagnosis disappears from the projected result and the complete Error/AggregateError cause graph. The top-level `cleanup_incomplete` code correctly reports unresolved cleanup. The defect is that the original catalog failure is discarded rather than retained alongside it. ### Reproduction From a macOS source checkout with locked dependencies installed, save the following as `src/artifacts/apple/assetCatalogCleanup.test.ts` and run: ```sh node node_modules/vitest/vitest.mjs run src/artifacts/apple/assetCatalogCleanup.test.ts --maxWorkers=1 ``` This five-case characterization test was executed against the production artifact-provider boundary. It supplies successful utility stdout rather than running native assetutil. It uses real file identity checks and native directory handles, injecting EIO before native directory close. Test finalization calls the captured native closes to release every handle. The final case injects a typed reader-cleanup failure after normal reader cleanup to isolate the outer finalizer. The exact test below ran with REA_ASSET_TEST_ROOT selecting a disposable directory; all five cases passed on Node 26.12.0 and again on the repository-pinned Node 24.18.0. Its assertions describe the current faulty behavior, not a repair. ```ts import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { describe, expect, it, vi } from "vitest"; import { ArtifactProvider } from "../ArtifactProvider.js"; import { ArtifactReaderFailure } from "../ArtifactReader.js"; import { DirectoryArtifactReader } from "../DirectoryArtifactReader.js"; import { projectAnalysisError } from "../../domain/analysisErrorProjection.js"; import { execFileOutput } from "../../process/ExecFileOutput.js"; const state = vi.hoisted(() => ({ denyDirectoryClose: false, resourcePath: "", releases: [] as (() => Promise<void>)[], closeFailures: 0, })); vi.mock("node:fs/promises", async (importOriginal) => { const actual = await importOriginal<typeof import("node:fs/promises")>(); return { ...actual, opendir: async (...args: Parameters<typeof actual.opendir>) => { const handle = await actual.opendir(...args); if (String(args[0]) === state.resourcePath) { const close = handle.close.bind(handle); state.releases.push(async () => { try { await close(); } catch (e) { if ((e as NodeJS.ErrnoException).code !== "ERR_DIR_CLOSED") throw e; }}); vi.spyOn(handle, "close").mockImplementation(async () => { if (state.denyDirectoryClose) { state.closeFailures += 1; throw Object.assign(new Error("controlled directory close rejection"), {code: "EIO"}); } await close(); }); } return handle; }, }; }); vi.mock("../../process/ExecFileOutput.js", async (importOriginal) => { const actual = await importOriginal<typeof import("../../process/ExecFileOutput.js")>(); return {...actual, execFileOutput: vi.fn(actual.execFileOutput)}; }); function causes(value: unknown): string[] { const seen = new Set<unknown>(); const result: string[] = []; const walk = (v: unknown) => { if (!(v instanceof Error) || seen.has(v)) return; seen.add(v); result.push(v.message); walk(v.cause); if (v instanceof AggregateError) for (const e of v.errors) walk(e); }; walk(value); return result; } describe("asset catalog primary failure and cleanup", () => { it.each([ {primary: false, cleanup: false}, {primary: true, cleanup: false}, {primary: false, cleanup: true}, {primary: true, cleanup: true}, {primary: true, cleanup: "reader" as const}, ])("observes $primary primary / $cleanup cleanup failure", async ({primary, cleanup}) => { const bundle = await mkdtemp(join(process.env.REA_ASSET_TEST_ROOT ?? tmpdir(), "fixture-")); const resources = join(bundle, "Contents", "Resources"); await mkdir(resources, {recursive: true}); const catalog = join(resources, "Assets.car"); await writeFile(catalog, "inert catalog bytes"); state.resourcePath = await import("node:fs/promises").then(m => m.realpath(resources)); state.denyDirectoryClose = cleanup === true; state.closeFailures = 0; state.releases = []; if (cleanup === "reader") { const close = DirectoryArtifactReader.prototype.close; vi.spyOn(DirectoryArtifactReader.prototype, "close").mockImplementation(async function () { await close.call(this); state.closeFailures += 1; throw new ArtifactReaderFailure("io", "controlled reader cleanup rejection", { cleanup: {reason: "controlled reader cleanup rejection", resources: [resources]}, }); }); } vi.mocked(execFileOutput).mockImplementationOnce(async (command, args) => { expect(command).toBe("/usr/bin/assetutil"); expect(args[0]).toBe("--info"); if (primary) await writeFile(catalog, "changed catalog bytes with a different length"); return {stdout: "[]", stderr: "", stdoutBytes: 2, stderrBytes: 0}; }); const client = new ArtifactProvider({}, "darwin").createBundleResourceClient({ callerPath: bundle, canonicalPath: bundle, infoPlistPath: join(bundle, "Contents", "Info.plist"), layout: "contents", executableSha256: null, }); try { const result = await client.execute("inspect_asset_catalog", {}); if (!primary && !cleanup) {expect(result.ok).toBe(true);return;} expect(result.ok).toBe(false); if (result.ok) throw new Error("expected failure"); const projected = projectAnalysisError(result.error); const chain = causes(result.error); console.log(JSON.stringify({primary, cleanup, code: projected.code, category: projected.category, reason: projected.details?.reason, primaryRetained: chain.some(m => m.includes("Asset catalog changed during inspection")), closeFailures: state.closeFailures})); if (!cleanup) { expect(projected).toMatchObject({code: "artifact_operation_failed", details: {reason: "integrity"}}); expect(chain.some(m => m.includes("Asset catalog changed during inspection"))).toBe(true); } else { expect(state.closeFailures).toBeGreaterThan(0); expect(projected).toMatchObject({code: "cleanup_incomplete", details: {reason: "io", cleanup: "incomplete"}}); expect(chain.some(m => m.includes("Asset catalog changed during inspection"))).toBe(false); expect(JSON.stringify(projected)).not.toContain("Asset catalog changed during inspection"); } } finally { state.denyDirectoryClose = false; await client.close(); for (const release of state.releases) await release(); vi.restoreAllMocks(); await rm(bundle, {recursive:true, force:true}); } }); }); ``` ### Expected behavior Retain the primary catalog failure's reason, diagnostic, category and cause while also reporting cleanup uncertainty and remaining resources. The [one-shot lifecycle contract](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/tool-design.md#L132-L134) requires preserving the primary diagnostic; [the cancellation/lifecycle rule](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/tool-design.md#L101-L105) explicitly separates the primary failure from cleanup classification. [ArtifactReaderFailure.withCleanup](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/ArtifactReader.ts#L222-L266) already models that composition. For this integrity case, the final code can remain `cleanup_incomplete` while category `integrity_mismatch`, reason `integrity`, the changed-catalog detail/cause, and cleanup resources all survive. ### Impact and scope P2: coincident catalog and cleanup failures erase the actionable diagnosis and alter machine-readable classification and recovery advice. The primary-only, cleanup-only, and combined controls establish the difference. No data corruption or security-boundary crossing was observed. Naturally occurring close-failure frequency and real-app prevalence are unknown. No general caller-side way to recover the discarded primary failure was established. ### REA version `rea-agents` 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. Latest npm version checked: 6.7.0. Fetched `origin/main` at `25e1a24a3ed3671a0148757555f18d16c29d936a` leaves the producing paths unchanged. That newer revision was source-compared, not executed; its unrelated projection helper change does not alter this path. ### Runtime and platform macOS 27.0.1 (26A434), arm64; Node.js v24.18.0 (repository pin) and v26.12.0; locked Vitest 5.0.3. Native assetutil and deep-analysis engines were not run. ### Target and artifact context A synthetic macOS-layout resource directory containing `Contents/Resources/Assets.car` with inert fixture bytes. The test changes those bytes during the supplied utility call to trigger the production integrity check. No target code runs, and native catalog validity is not asserted. ### Interface Other ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence Observed summaries from the focused test: ```json {"primary":true,"cleanup":false,"code":"artifact_operation_failed","category":"integrity_mismatch","reason":"integrity","primaryRetained":true,"closeFailures":0} {"primary":false,"cleanup":true,"code":"cleanup_incomplete","category":"execution_failure","reason":"io","primaryRetained":false,"closeFailures":2} {"primary":true,"cleanup":true,"code":"cleanup_incomplete","category":"execution_failure","reason":"io","primaryRetained":false,"closeFailures":2} {"primary":true,"cleanup":"reader","code":"cleanup_incomplete","category":"execution_failure","reason":"io","primaryRetained":false,"closeFailures":2} ``` The ordinary success control also passes. `primaryRetained` checks the complete Error/AggregateError cause graph for `Asset catalog changed during inspection`. The combined result's projection also lacks that diagnosis. With cleanup permitted, the diagnostic is `Asset catalog changed during inspection: Contents/Resources/Assets.car`. With failed cleanup, the projected message is `Artifact could not be read or written. Check file access and try again.`, and `details.detail` describes only directory cleanup. ### Typed outcome or recovery expected Preserve `ArtifactReaderFailure("integrity", ...)` as the primary outcome and attach the cleanup observation without replacing it. Preserve exact cleanup ownership and do not clear cleanup uncertainty merely to restore the primary classification. The [canonical projection](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorProjection.ts#L69-L88) already keeps primary details alongside `cleanup_incomplete` when the producing error retains them. ### Analysis or suspected cause Source-derived: [collectAssetCatalogs](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/apple/AppleAssetCatalogAnalysis.ts#L130-L156) unconditionally awaits `reader.close()` in `finally`. [The identity check](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/apple/AppleAssetCatalogAnalysis.ts#L178-L198) throws the primary integrity failure, but [DirectoryArtifactReader.close](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/DirectoryArtifactReader.ts#L389-L426) can throw an independent cleanup failure. JavaScript propagates that finalizer exception instead. The [provider translation](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/artifacts/ArtifactProvider.ts#L145-L170) then receives only the cleanup error. ### Additional context The runtime evidence comes from the production artifact-provider boundary, not a CLI subprocess or MCP transport. Public reachability is source-derived: [the CLI registration](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/cli/artifactCommands.ts#L115-L145) dispatches this operation through the same provider. No software fix, restoration check, or last known good revision was established. Related #5698 concerns damaged catalog bytes reported as I/O, #5976 concerns utility deadlines, and #5977 concerns malformed successful utility stdout. This case uses successful `[]` stdout and an independently detected catalog identity change, then loses that already correct primary error during cleanup. #3935/#4240 concern CLI finalizers and resource-field composition; #4743 concerns Apple dispatch descriptor cleanup; #4522 concerns compiler-root removal; #5963 concerns Rizin cancellation. Their fixes do not alter this catalog-reader finalizer.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

The original failure was a catalog integrity refusal being replaced by reader.close() rejection. collectAssetCatalogs now records its completed/failed outcome, releases the exact reader through ArtifactResourceScope, and composes failed release with that original outcome. ArtifactReaderFailure.withCleanup preserves the primary reason, message, details, and primary cause, while cleanup leases retain ownership.

Implementation: AppleAssetCatalogAnalysis.ts, AppleAssetCatalogAnalysis.ts.

The relevant regression checks in assetCatalogFinalization.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380716679 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. The original failure was a catalog integrity refusal being replaced by reader.close() rejection. collectAssetCatalogs now records its completed/failed outcome, releases the exact reader through ArtifactResourceScope, and composes failed release with that original outcome. ArtifactReaderFailure.withCleanup preserves the primary reason, message, details, and primary cause, while cleanup leases retain ownership. Implementation: [AppleAssetCatalogAnalysis.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/artifacts/apple/AppleAssetCatalogAnalysis.ts#L202), [AppleAssetCatalogAnalysis.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/artifacts/apple/AppleAssetCatalogAnalysis.ts#L328). The relevant regression checks in [assetCatalogFinalization.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/boundary/filesystem/assetCatalogFinalization.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3660
No description provided.