[GH-ISSUE #2547] fix(apple): earlier truncation marks a retained later hierarchy incomplete #864

Closed
opened 2026-10-15 18:29:06 +00:00 by bsakaguchi · 1 comment
Owner

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

Summary

When Interface Builder analysis processes a document whose hierarchy exceeds the selected limit before a small complete document, the small document reports hierarchy_complete: false. Its hierarchy remains in the graph and its own coverage facet reports complete. The same small document reports hierarchy_complete: true when analyzed alone. This gives callers a wrong completeness answer for an individual document.

Steps to reproduce

Use the source baseline and locked dependencies listed under Runtime or environment. Save this focused test as src/domain/apple/hierarchyCompleteness.repro.test.ts and run the command below. The first document has one object referenced five times in its hierarchy. The second has one object referenced once. The four-node limit accommodates both roots and both objects.

import { expect, it } from "vitest";
import {
  buildInterfaceBuilderAnalysis,
  interfaceBuilderLimitsSchema,
} from "./interfaceBuilderGraph.js";

it("preserves a later document's complete hierarchy", () => {
  const digest = "a".repeat(64);
  const document = (path: string, objectId: string, references: number) => ({
    relativePath: path,
    archiveSha256: digest,
    documentKind: "nib" as const,
    raw: {
      "com.apple.ibtool.document.objects": {
        [objectId]: { class: "UIView" },
      },
      "com.apple.ibtool.document.hierarchy": Array.from(
        { length: references },
        () => ({ objectID: objectId }),
      ),
    },
  });
  const first = document("First.nib", "a", 5);
  const second = document("Second.nib", "view", 1);
  const analyze = (documents: ReturnType<typeof document>[]) =>
    buildInterfaceBuilderAnalysis({
      targetSha256: digest,
      toolVersion: "reproduction",
      documents,
      limits: interfaceBuilderLimitsSchema.parse({ max_objects: 4 }),
    });
  expect(analyze([second]).documents[0]?.hierarchy_complete).toBe(true);
  const result = analyze([first, second]);
  expect(result.graph.nodes).toHaveLength(4);
  expect(result.graph.coverage).toContainEqual(
    expect.objectContaining({
      facet: "hierarchy:Second.nib",
      status: "complete",
      examined: 1,
      omitted: 0,
    }),
  );
  expect(result.documents[1]?.hierarchy_complete).toBe(true);
});
npm run test:focused -- src/domain/apple/hierarchyCompleteness.repro.test.ts

The final assertion fails. Remove the reproduction test after inspection. These are supported synthetic ibtool-shaped dictionaries, not archives produced by a native ibtool invocation.

Expected behavior

Each document's hierarchy_complete describes its own retained hierarchy. The second document has its root, object and one resolved hierarchy relationship in the returned graph, so it should report true regardless of the earlier document's incomplete hierarchy. The aggregate graph should still report truncated: true because the first document's traversal reached the limit.

Actual behavior

The independently executed test returned false for both document summaries. The first hierarchy facet was partial, with four examined references; the second was complete, with one examined reference and zero omissions. Both roots and objects were present, and the second hierarchy relationship resolved normally. The second document alone returned hierarchy_complete: true and graph.truncated: false. The paired test exited 1 with AssertionError: expected false to be true.

The same fixture dictionaries were also serialized as XML property lists named First.nib and Second.nib and passed through the bundle decoder. That run returned the same false second summary and complete hierarchy facet, with archive_decode reporting complete for both documents. The compiled CLI, MCP transport and native archive producer were not executed.

Evidence

  • Expected source: src/domain/apple/interfaceBuilderGraph.ts:15-23 defines completeness per document, and src/domain/apple/interfaceBuilderGraph.ts:462-486 reports hierarchy coverage per document; AGENTS.md, Tool and Test Changes, requires completeness to derive from examined coverage.
  • Failure source: src/domain/apple/interfaceBuilderGraph.ts:425-428 derives the document summary from the aggregate truncation state. The focused test above reproduced the contradictory second-document summary and coverage. The bundle decoder calls this producer at src/artifacts/apple/InterfaceBuilderAnalysis.ts:165-171, and its finalizer at lines 261-265 preserves the false value.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The parser accepts the ibtool-shaped dictionaries, and the selected object limit is valid. The first document sets the shared truncation flag while visiting its hierarchy. The second document starts its own hierarchy traversal state but reads the existing shared flag when constructing its summary. The final bundle projection does not correct that value.

The related closed report morluto/rea#781 concerns omitted serialized AppKit parent links and false positive completeness. Its described repair restores those links. This reproduction concerns false negative completeness for an already retained later hierarchy and remains observable at the tested baseline.

Restoration check

Run the paired input and the single-document control. The paired result must report First.nib with hierarchy_complete: false and Second.nib with hierarchy_complete: true. Preserve the second document's resolved hierarchy relationship and complete coverage, and preserve the first document's partial coverage and aggregate graph.truncated: true. The second document alone must remain complete. Repeat with the serialized XML fixtures through the bundle decoder so its final result also restores the same per-document answer.

Runtime or environment

  • Failure verified at a2d8b94dd5 and still present at 8b181716ac.
  • Repository package version: 6.4.0; Node v26.10.0; npm 11.19.1; installed locked dependencies; Vitest v5.0.3, focused domain project.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The reproduced multi-document result gives an incorrect completeness answer for a fully retained hierarchy. This is bounded metadata corruption, with no observed loss of the second document's graph or demonstrated downstream consumer failure. Native archive prevalence was not measured.
  • Workaround status: available
  • Workaround basis: For the reproduced input, the returned hierarchy:Second.nib coverage facet correctly reports complete coverage, and the graph retains its resolved relationship. Callers can read that facet to recover the correct per-document completeness answer without changing the source.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2547 ## Summary When Interface Builder analysis processes a document whose hierarchy exceeds the selected limit before a small complete document, the small document reports `hierarchy_complete: false`. Its hierarchy remains in the graph and its own coverage facet reports `complete`. The same small document reports `hierarchy_complete: true` when analyzed alone. This gives callers a wrong completeness answer for an individual document. ## Steps to reproduce Use the source baseline and locked dependencies listed under Runtime or environment. Save this focused test as `src/domain/apple/hierarchyCompleteness.repro.test.ts` and run the command below. The first document has one object referenced five times in its hierarchy. The second has one object referenced once. The four-node limit accommodates both roots and both objects. ```ts import { expect, it } from "vitest"; import { buildInterfaceBuilderAnalysis, interfaceBuilderLimitsSchema, } from "./interfaceBuilderGraph.js"; it("preserves a later document's complete hierarchy", () => { const digest = "a".repeat(64); const document = (path: string, objectId: string, references: number) => ({ relativePath: path, archiveSha256: digest, documentKind: "nib" as const, raw: { "com.apple.ibtool.document.objects": { [objectId]: { class: "UIView" }, }, "com.apple.ibtool.document.hierarchy": Array.from( { length: references }, () => ({ objectID: objectId }), ), }, }); const first = document("First.nib", "a", 5); const second = document("Second.nib", "view", 1); const analyze = (documents: ReturnType<typeof document>[]) => buildInterfaceBuilderAnalysis({ targetSha256: digest, toolVersion: "reproduction", documents, limits: interfaceBuilderLimitsSchema.parse({ max_objects: 4 }), }); expect(analyze([second]).documents[0]?.hierarchy_complete).toBe(true); const result = analyze([first, second]); expect(result.graph.nodes).toHaveLength(4); expect(result.graph.coverage).toContainEqual( expect.objectContaining({ facet: "hierarchy:Second.nib", status: "complete", examined: 1, omitted: 0, }), ); expect(result.documents[1]?.hierarchy_complete).toBe(true); }); ``` ```sh npm run test:focused -- src/domain/apple/hierarchyCompleteness.repro.test.ts ``` The final assertion fails. Remove the reproduction test after inspection. These are supported synthetic ibtool-shaped dictionaries, not archives produced by a native ibtool invocation. ## Expected behavior Each document's `hierarchy_complete` describes its own retained hierarchy. The second document has its root, object and one resolved hierarchy relationship in the returned graph, so it should report `true` regardless of the earlier document's incomplete hierarchy. The aggregate graph should still report `truncated: true` because the first document's traversal reached the limit. ## Actual behavior The independently executed test returned `false` for both document summaries. The first hierarchy facet was `partial`, with four examined references; the second was `complete`, with one examined reference and zero omissions. Both roots and objects were present, and the second hierarchy relationship resolved normally. The second document alone returned `hierarchy_complete: true` and `graph.truncated: false`. The paired test exited 1 with `AssertionError: expected false to be true`. The same fixture dictionaries were also serialized as XML property lists named `First.nib` and `Second.nib` and passed through the bundle decoder. That run returned the same false second summary and complete hierarchy facet, with `archive_decode` reporting `complete` for both documents. The compiled CLI, MCP transport and native archive producer were not executed. ## Evidence - Expected source: `src/domain/apple/interfaceBuilderGraph.ts:15-23` defines completeness per document, and `src/domain/apple/interfaceBuilderGraph.ts:462-486` reports hierarchy coverage per document; `AGENTS.md`, Tool and Test Changes, requires completeness to derive from examined coverage. - Failure source: `src/domain/apple/interfaceBuilderGraph.ts:425-428` derives the document summary from the aggregate truncation state. The focused test above reproduced the contradictory second-document summary and coverage. The bundle decoder calls this producer at `src/artifacts/apple/InterfaceBuilderAnalysis.ts:165-171`, and its finalizer at lines 261-265 preserves the false value. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The parser accepts the ibtool-shaped dictionaries, and the selected object limit is valid. The first document sets the shared truncation flag while visiting its hierarchy. The second document starts its own hierarchy traversal state but reads the existing shared flag when constructing its summary. The final bundle projection does not correct that value. The related closed report [morluto/rea#781](https://github.com/morluto/rea/issues/781) concerns omitted serialized AppKit parent links and false positive completeness. Its described repair restores those links. This reproduction concerns false negative completeness for an already retained later hierarchy and remains observable at the tested baseline. ## Restoration check Run the paired input and the single-document control. The paired result must report `First.nib` with `hierarchy_complete: false` and `Second.nib` with `hierarchy_complete: true`. Preserve the second document's resolved hierarchy relationship and complete coverage, and preserve the first document's partial coverage and aggregate `graph.truncated: true`. The second document alone must remain complete. Repeat with the serialized XML fixtures through the bundle decoder so its final result also restores the same per-document answer. ## Runtime or environment - Failure verified at a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and still present at 8b181716aca2cb467be499cba226abfb5ae19141. - Repository package version: 6.4.0; Node v26.10.0; npm 11.19.1; installed locked dependencies; Vitest v5.0.3, focused domain project. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The reproduced multi-document result gives an incorrect completeness answer for a fully retained hierarchy. This is bounded metadata corruption, with no observed loss of the second document's graph or demonstrated downstream consumer failure. Native archive prevalence was not measured. - Workaround status: available - Workaround basis: For the reproduced input, the returned `hierarchy:Second.nib` coverage facet correctly reports complete coverage, and the graph retains its resolved relationship. Callers can read that facet to recover the correct per-document completeness answer without changing the source.
bsakaguchi 2026-10-15 18:29:06 +00:00
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Resolved on main by 7cfcd166e.

Hierarchy completeness now derives from each document's examined coverage, so an earlier truncated document does not taint a retained later hierarchy.

Rechecked on main b8510ee00 in the current focused run: 17 test files passed, 485 tests passed and one platform-dependent test skipped. Relevant regression coverage: src/domain/apple/interfaceBuilderGraph.test.ts.

<!-- gh-comment-id:6175075546 --> @morluto commented on GitHub (Oct 14, 2026): Resolved on main by 7cfcd166e. Hierarchy completeness now derives from each document's examined coverage, so an earlier truncated document does not taint a retained later hierarchy. Rechecked on main b8510ee00 in the current focused run: 17 test files passed, 485 tests passed and one platform-dependent test skipped. Relevant regression coverage: src/domain/apple/interfaceBuilderGraph.test.ts.
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#864
No description provided.