[PR #4133] feat(evidence): cluster inspected artifacts into families by root digest #6077

Open
opened 2026-10-31 01:11:41 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4133
Author: @0xLLVEL
Created: 10/15/2026
Status: 🔄 Open

Base: main ← Head: feat/cluster-artifacts


📝 Commits (10+)

  • dfce61a feat(evidence): cluster inspected artifacts into families by root digest
  • fc51e3b fix(evidence): keep cluster member identity and byte claims exact
  • 8ca824d fix(evidence): count scope exclusions on the scope axis only
  • b5b1352 fix(evidence): bound the cluster member index and make its rows exact
  • ca971eb fix(server): measure the clustering response before delivering it
  • 158e1da fix(evidence): keep clustering coverage, identity, and provenance exact
  • 0e40918 fix(evidence): count clustering omissions and withhold the index without reparsing
  • acadcfa fix(evidence): remove the unused clustering input type alias
  • d7d00b4 Merge remote-tracking branch 'upstream/main' into feat/cluster-artifacts
  • e418b75 test(mcp): assert the canonical input-contract error

📊 Changes

21 files changed (+2848 additions, -4 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/evidence-workflows.md (+9 -0)
📝 docs/cli.md (+13 -0)
📝 docs/mcp-contracts.md (+56 -0)
➕ src/application/artifacts/ArtifactFamilyClusteringCommand.ts (+66 -0)
➕ src/application/artifacts/ArtifactFamilyClusteringEvidence.ts (+61 -0)
📝 src/cliCommandNames.ts (+1 -0)
📝 src/cliEvidenceCommands.ts (+46 -0)
📝 src/contracts/sessionToolContracts.ts (+6 -0)
📝 src/contracts/toolContractExamples.ts (+8 -0)
📝 src/contracts/toolEffects.ts (+1 -0)
📝 src/contracts/toolOutputSchemaGroups.ts (+2 -0)
➕ src/domain/artifactFamilyClustering.test.ts (+753 -0)
➕ src/domain/artifactFamilyClustering.ts (+563 -0)
➕ src/domain/artifactFamilyClusteringIndex.test.ts (+137 -0)
➕ src/domain/artifactFamilyClusteringIndex.ts (+196 -0)
📝 src/domain/artifactInventoryEvidence.test.ts (+14 -1)
📝 src/domain/artifactInventoryEvidence.ts (+12 -3)
➕ src/server/registerArtifactFamilyClusteringTool.ts (+180 -0)
📝 src/server/registerSessionTools.ts (+7 -0)
➕ tests/boundary/cli/clusterArtifactsCli.test.ts (+80 -0)

...and 1 more files

📄 Description

Summary

Closes #4135.

Analysts can already compare two inspected artifacts, but nothing answers the
wider question: among the artifacts I have inspected, which are the same
build, which merely share members, and which only look alike?
Today that
answer has to be reconstructed by hand from pairwise comparisons and
occurrence lists.

This adds cluster_artifacts (MCP session tool) and rea cluster-artifacts
(CLI). It partitions two or more complete inspect_artifact inventories by
exact root content digest and indexes every graph member as shared, partially
shared or unique. It reuses the existing artifact inventory contract instead of
introducing a parallel one: every family and member row cites the member
Evidence that established it, and a member that cannot be compared is reported
unplaced with its reason, never as different.

Related follow-up issue: #4135. Its full scope (retained-reference
and summary members) stays listed under Remaining proof gaps.

Problem and expected behavior

  • Trigger: inspect three builds of one artifact, then ask which two are
    byte-identical at the root and which members they share.
  • Today: only pairwise compare_artifacts output exists, so the grouping and
    the sharing threshold are reconstructed manually.
  • Invariant this change must not break: incomplete or unobserved data never
    becomes a claim. A member whose inventory is incomplete, contradictory or
    declared under a different coverage scope is reported unplaced with its
    reason; a re-packed container with identical members has no family and appears
    only in the member index; a member with no partner is counted without being
    evidence of difference.
  • Observable outcome: status (partitioned / partial / unknown), family
    rows whose members each carry their own Evidence identity, a shared / partially shared / unique
    member index that is never paged away, and explicit coverage fields for the
    scope, the shared-member threshold actually used and whether the member index
    was withheld.

Change and scope

  • src/domain/artifactFamilyClustering.ts owns placement: the single
    root-sha256 family tier, the strict-majority coverage scope, the
    shared/partially-shared/unique index, unplaced rows with reasons, code-point
    ordering, and explicit resource bounds (ARTIFACT_FAMILY_MEMBER_LIMIT = 64
    members, 250,000 examined nodes before the index is withheld rather than
    reported partially).
  • src/domain/artifactInventoryEvidence.ts extracts
    declaresCompleteInventory so "graph members present" and "producer declares
    complete child coverage" are one decision instead of two callers with
    different definitions. Behavior-preserving.
  • Application, CLI and MCP: ArtifactFamilyClusteringCommand.ts,
    ArtifactFamilyClusteringEvidence.ts, registerArtifactFamilyClusteringTool.ts,
    the session tool contracts (sessionToolContracts, toolEffects =
    sessionEvidence, toolOutputSchemaGroups, toolContractExamples),
    cliEvidenceCommands.ts and cliCommandNames.ts.
  • Docs and skill: docs/mcp-contracts.md and the authored skill reference
    .agents/skills/reverse-engineer-anything/references/evidence-workflows.md;
    skills/ is regenerated by npm run artifacts:skills and not committed.
  • Intentionally not included: members as retained Evidence references or
    inspect_analysis_view summaries (inline Evidence only), cost measurements,
    and CI coverage for a truncation shape the public producer cannot currently
    emit (see Remaining proof gaps).
  • Contribution scope: read-only analysis over Evidence the caller already has.
    No target execution, injection, mutation or protection recovery, and no
    provider is launched — a boundary test asserts the clustering path refuses to
    start one. Maintainer scope decision: not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: the artifact inventory parser
    remains the owner of inventory facts (src/domain/artifactInventoryEvidence.ts);
    the new domain function decides placement from those facts only. No producer
    representation was added downstream and no consumer reinterprets raw source.
  • Contribution scope fit and linked maintainer decision: fits the existing
    read-only analysis boundary; no maintainer scope decision required, and none
    is claimed.
  • CLI and MCP/tool-catalog contract: one new session tool cluster_artifacts
    (effect sessionEvidence) with a strict input schema and output schema bound
    through the existing toolOutputSchemaGroups map, plus the CLI command
    rea cluster-artifacts. Advertised JSON Schemas are validated by
    docs:check and the output-schema profile tests.
  • Provider, bridge, target-format, or platform compatibility: no provider is
    started and no format reader changed. One member shape is host-dependent — a
    universal Mach-O is only fully readable where a native slice adapter exists —
    and the boundary test reads the producer's own coverage declaration instead of
    assuming which host it runs on.
  • Evidence, artifact, provenance, or reconstruction contract: families and
    member rows carry the member evidence_ids that established them; unplaced
    members carry a reason; the producer's own per-member limitations travel into
    the result prefixed with the caller's label.
  • Target selection, authority, upstream behavior, and cleanup details: not
    applicable — nothing is selected, launched, attached to or cleaned up. The
    clustering handler returns a validation error if an Evidence reference cannot
    be resolved by the caller's session instead of launching anything.
  • Generated metadata (docs/public/product-catalog.json), package, or
    installation impact: none. Authored instructions were edited in
    .agents/skills/reverse-engineer-anything/ and regenerated locally; no build
    outputs are committed.

Expected effects:

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration.
  • Contact network services.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • src/domain/artifactFamilyClustering.test.ts — 14 tests over every
      placement decision: family formation, shared/partially shared/unique
      thresholding, repeated labels, per-row source Evidence, graph-derived root
      digests, truncated inventories, members that declare unknown child coverage,
      scope ties, strict-majority scope, coverage accounting, paging, determinism
      and result-shape validation.
    • tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts — 3 tests driving
      the real MCP session (createServer({ kind: "session" }), linked in-memory
      transports, ArtifactProvider) through open_binary → inspect_artifact →
      cluster_artifacts, with Evidence read back from the session and passed
      inline. Fixtures are fixture code: archives written per run with the
      existing @zip.js/zip.js writer, and a universal Mach-O built with the
      repo's own fatImage/machoImage fixtures.
  • Base reproduction: the producing-boundary defect below was observed by driving
    that real session over a universal Mach-O plus a ZIP before the fix — the
    result reported status: "partitioned" while
    summary.members_with_unknown_inventory was already 1 and the member
    appeared in no family, index or unplaced row. After the fix the same input
    reports partial with the member in unknown_members as
    incomplete-inventory. The regression is covered deterministically by the
    domain test using the new coverageStatus fixture option, which fails on the
    base revision.
  • User-visible CLI/MCP output: the fields asserted by the boundary test for that
    real call are status: "partial", coverage.member_scope: "container-entries",
    families: [{ family_id: "unplaced", tier: null, members: [], unknown_members: [{ label: "universal", reason: "incomplete-inventory" }] }],
    summary.members_with_complete_inventory: 1,
    summary.members_with_unknown_inventory: 1,
    summary.members_without_family: 1, and a limitation beginning
    universal: Universal Mach-O slices.
  • Remaining proof gaps: (1) the universal Mach-O member is only unreadable on a
    host without a native slice adapter, so that boundary test reads the
    producer's declaration and skips where children are readable; (2) a
    count-truncated inventory is covered by the domain test only, because a
    3,000-entry ZIP still reports complete coverage and a CRC-contradicted
    archive is refused by the producer even under
    integrity_policy record-and-continue; (3) no cost measurement was taken, so
    no performance claim is made.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npx tsc --noEmit — exit 0.
  • npm run lint (oxlint plus module-boundary verification over 1,941 source
    files) — exit 0; repository-wide warnings unchanged.
  • npm run check:fast — exit 0.
  • npx vitest run src/domain/artifactFamilyClustering.test.ts tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts tests/boundary/filesystem/artifactComparison.test.ts tests/boundary/filesystem/setupSkill.test.ts
    — 45 passed, 4 skipped (49), exit 0.
  • npx vitest run src/domain tests/boundary/filesystem/artifactComparison.test.ts tests/boundary/filesystem/setupSkill.test.ts tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts
    — 3,235 passed, 4 skipped (3,239) across 186 files, exit 0.
  • npx vitest run tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts -t "unknown child coverage"
    — the new test runs on this host (1 passed), it does not silently skip.
  • npx oxfmt --check on every changed file — all correctly formatted.
  • npm run docs:generate then npm run docs:check — exit 0, including
    generate-skill-metadata.mjs --check, the product catalog, doc facts and
    tests/conformance/readmeConsistency.test.ts (5 passed).
  • Real-provider verification: not applicable. The capability consumes Evidence
    the caller already holds and starts no provider; the boundary test asserts a
    provider launch would be a bug. No Hopper, Ghidra or IDA run is involved.

After merging main (905cbaf23)

  • Conflicts: sessionToolContracts.ts and toolContractExamples.ts needed both new tools (this PR's cluster_artifacts and main's match_native_functions), and artifactInventoryEvidence.test.ts needed both import sets. Resolved as a union, with main's bundleComparisonMcpInputSchema replacing the older bundleComparisonInputSchema.
  • npm ci, npm run build:cached — exit 0.
  • npm run check:fast — exit 0.
  • npm run test:focused over the six clustering/encoding files — 47 passed, exit 0.
  • npm run test:focused over toolEffects, toolInputReferences, toolOutputReferences, compactInputSchema, mcpResponseBudget and targetContentProvenance — 101 passed, exit 0.
  • npm run docs:check — exit 0.
  • One test change was required by upstream: SDK schema diagnostics are now normalized into the invalid_request / inspect_input_contract envelope, so the "not artifact inventory Evidence" case asserts that structured contract instead of the old "Input validation" sentence.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. The completeness predicate
    extraction is behavior-preserving (verified by the 19 artifactComparison
    boundary tests and the full domain suite); no schema field was removed or
    renamed, and the CLI/MCP surfaces are additive.
  • Real Hopper/Ghidra, browser, or OS coverage: not applicable, no provider is
    launched. The one platform-dependent member shape (universal Mach-O) is
    handled by reading the producer's declaration.
  • Package or release metadata impact: none; ignored catalogs and skills/ are
    generated and validated by CI rather than committed here.
  • Security, privacy, process, or containment review: read-only. No target is
    selected, launched, attached to or modified; no process is supervised or
    signalled; member labels are caller text and are never parsed as identity;
    no credentials or transport data are persisted.

Review round (fc51e3b00)

Every finding from the first review round (Greptile and Codex) was addressed,
and each thread carries a reply with its root cause, structural fix and
validation:

  • Member-index rows carry observations: [{ label, evidence_id }] and count
    members, so repeated labels stay distinct and a row keeps its provenance when
    it has no family row.
  • A family requires a producer digest of the root's own bytes. A directory root
    (graph-derived digest, unread symlink targets uncommitted) is compared and
    counted but never called byte-identical, and directory nodes leave the member
    index; members_without_byte_root_digest and nodes_without_byte_identity
    keep those omissions visible.
  • Inventory completeness and coverage scope are counted separately
    (members_with_complete_inventory, members_with_unknown_inventory,
    members_outside_member_scope).
  • The returned page is stated in coverage.families_offset and
    coverage.families_limit and in the retained Evidence parameters, and the CLI
    enforces the 64-member bound before reading any document.
  • A follow-up finding on that counter is fixed in 8ca824d70:
    members_outside_member_scope counts on the scope axis only (complete
    inventories, with the no-majority case defined explicitly), so it no longer
    counts members whose own inventory is incomplete.

Author readiness

Human owner: @0xLLVEL

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/4133 **Author:** [@0xLLVEL](https://github.com/0xLLVEL) **Created:** 10/15/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/cluster-artifacts` --- ### 📝 Commits (10+) - [`dfce61a`](https://github.com/morluto/rea/commit/dfce61a43d6d45c88e9a47b261881eb3f343cd54) feat(evidence): cluster inspected artifacts into families by root digest - [`fc51e3b`](https://github.com/morluto/rea/commit/fc51e3b002093f4690ed990573179782b55d6e22) fix(evidence): keep cluster member identity and byte claims exact - [`8ca824d`](https://github.com/morluto/rea/commit/8ca824d70a706c688d98cbebc589c2fac9462c67) fix(evidence): count scope exclusions on the scope axis only - [`b5b1352`](https://github.com/morluto/rea/commit/b5b1352f63c7437282d1473fc42a2d2b95b726ad) fix(evidence): bound the cluster member index and make its rows exact - [`ca971eb`](https://github.com/morluto/rea/commit/ca971eb68dd33153607ce8d16c500fd68a68b69f) fix(server): measure the clustering response before delivering it - [`158e1da`](https://github.com/morluto/rea/commit/158e1da519258919aa6b6e9dcc4ebb45844e85c0) fix(evidence): keep clustering coverage, identity, and provenance exact - [`0e40918`](https://github.com/morluto/rea/commit/0e40918d35abeea3fa5121d9277645b3cb1a50b4) fix(evidence): count clustering omissions and withhold the index without reparsing - [`acadcfa`](https://github.com/morluto/rea/commit/acadcfad5d560d1c8e811755799ba3ae4ebc057e) fix(evidence): remove the unused clustering input type alias - [`d7d00b4`](https://github.com/morluto/rea/commit/d7d00b4d19e9154f5b96732f308e14654c0f4f58) Merge remote-tracking branch 'upstream/main' into feat/cluster-artifacts - [`e418b75`](https://github.com/morluto/rea/commit/e418b75bd973a187ba595c2be5a367846232df7c) test(mcp): assert the canonical input-contract error ### 📊 Changes **21 files changed** (+2848 additions, -4 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/evidence-workflows.md` (+9 -0) 📝 `docs/cli.md` (+13 -0) 📝 `docs/mcp-contracts.md` (+56 -0) ➕ `src/application/artifacts/ArtifactFamilyClusteringCommand.ts` (+66 -0) ➕ `src/application/artifacts/ArtifactFamilyClusteringEvidence.ts` (+61 -0) 📝 `src/cliCommandNames.ts` (+1 -0) 📝 `src/cliEvidenceCommands.ts` (+46 -0) 📝 `src/contracts/sessionToolContracts.ts` (+6 -0) 📝 `src/contracts/toolContractExamples.ts` (+8 -0) 📝 `src/contracts/toolEffects.ts` (+1 -0) 📝 `src/contracts/toolOutputSchemaGroups.ts` (+2 -0) ➕ `src/domain/artifactFamilyClustering.test.ts` (+753 -0) ➕ `src/domain/artifactFamilyClustering.ts` (+563 -0) ➕ `src/domain/artifactFamilyClusteringIndex.test.ts` (+137 -0) ➕ `src/domain/artifactFamilyClusteringIndex.ts` (+196 -0) 📝 `src/domain/artifactInventoryEvidence.test.ts` (+14 -1) 📝 `src/domain/artifactInventoryEvidence.ts` (+12 -3) ➕ `src/server/registerArtifactFamilyClusteringTool.ts` (+180 -0) 📝 `src/server/registerSessionTools.ts` (+7 -0) ➕ `tests/boundary/cli/clusterArtifactsCli.test.ts` (+80 -0) _...and 1 more files_ </details> ### 📄 Description ## Summary Closes #4135. Analysts can already compare two inspected artifacts, but nothing answers the wider question: *among the artifacts I have inspected, which are the same build, which merely share members, and which only look alike?* Today that answer has to be reconstructed by hand from pairwise comparisons and occurrence lists. This adds `cluster_artifacts` (MCP session tool) and `rea cluster-artifacts` (CLI). It partitions two or more complete `inspect_artifact` inventories by exact root content digest and indexes every graph member as shared, partially shared or unique. It reuses the existing artifact inventory contract instead of introducing a parallel one: every family and member row cites the member Evidence that established it, and a member that cannot be compared is reported unplaced with its reason, never as different. Related follow-up issue: `#4135`. Its full scope (retained-reference and summary members) stays listed under *Remaining proof gaps*. ## Problem and expected behavior - Trigger: inspect three builds of one artifact, then ask which two are byte-identical at the root and which members they share. - Today: only pairwise `compare_artifacts` output exists, so the grouping and the sharing threshold are reconstructed manually. - Invariant this change must not break: incomplete or unobserved data never becomes a claim. A member whose inventory is incomplete, contradictory or declared under a different coverage scope is reported unplaced with its reason; a re-packed container with identical members has no family and appears only in the member index; a member with no partner is counted without being evidence of difference. - Observable outcome: `status` (`partitioned` / `partial` / `unknown`), family rows whose members each carry their own Evidence identity, a shared / partially shared / unique member index that is never paged away, and explicit `coverage` fields for the scope, the shared-member threshold actually used and whether the member index was withheld. ## Change and scope - `src/domain/artifactFamilyClustering.ts` owns placement: the single `root-sha256` family tier, the strict-majority coverage scope, the shared/partially-shared/unique index, unplaced rows with reasons, code-point ordering, and explicit resource bounds (`ARTIFACT_FAMILY_MEMBER_LIMIT` = 64 members, 250,000 examined nodes before the index is withheld rather than reported partially). - `src/domain/artifactInventoryEvidence.ts` extracts `declaresCompleteInventory` so "graph members present" and "producer declares complete child coverage" are one decision instead of two callers with different definitions. Behavior-preserving. - Application, CLI and MCP: `ArtifactFamilyClusteringCommand.ts`, `ArtifactFamilyClusteringEvidence.ts`, `registerArtifactFamilyClusteringTool.ts`, the session tool contracts (`sessionToolContracts`, `toolEffects` = `sessionEvidence`, `toolOutputSchemaGroups`, `toolContractExamples`), `cliEvidenceCommands.ts` and `cliCommandNames.ts`. - Docs and skill: `docs/mcp-contracts.md` and the authored skill reference `.agents/skills/reverse-engineer-anything/references/evidence-workflows.md`; `skills/` is regenerated by `npm run artifacts:skills` and not committed. - Intentionally not included: members as retained Evidence references or `inspect_analysis_view` summaries (inline Evidence only), cost measurements, and CI coverage for a truncation shape the public producer cannot currently emit (see *Remaining proof gaps*). - Contribution scope: read-only analysis over Evidence the caller already has. No target execution, injection, mutation or protection recovery, and no provider is launched — a boundary test asserts the clustering path refuses to start one. Maintainer scope decision: **not applicable**. ## Contract and boundary impact - Semantic owner and earliest changed stage: the artifact inventory parser remains the owner of inventory facts (`src/domain/artifactInventoryEvidence.ts`); the new domain function decides placement from those facts only. No producer representation was added downstream and no consumer reinterprets raw source. - Contribution scope fit and linked maintainer decision: fits the existing read-only analysis boundary; no maintainer scope decision required, and none is claimed. - CLI and MCP/tool-catalog contract: one new session tool `cluster_artifacts` (effect `sessionEvidence`) with a strict input schema and output schema bound through the existing `toolOutputSchemaGroups` map, plus the CLI command `rea cluster-artifacts`. Advertised JSON Schemas are validated by `docs:check` and the output-schema profile tests. - Provider, bridge, target-format, or platform compatibility: no provider is started and no format reader changed. One member shape is host-dependent — a universal Mach-O is only fully readable where a native slice adapter exists — and the boundary test reads the producer's own coverage declaration instead of assuming which host it runs on. - Evidence, artifact, provenance, or reconstruction contract: families and member rows carry the member `evidence_id`s that established them; unplaced members carry a reason; the producer's own per-member limitations travel into the result prefixed with the caller's label. - Target selection, authority, upstream behavior, and cleanup details: not applicable — nothing is selected, launched, attached to or cleaned up. The clustering handler returns a validation error if an Evidence reference cannot be resolved by the caller's session instead of launching anything. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. Authored instructions were edited in `.agents/skills/reverse-engineer-anything/` and regenerated locally; no build outputs are committed. Expected effects: - [x] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [ ] Change process memory, registers, files, or configuration. - [ ] Contact network services. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: - `src/domain/artifactFamilyClustering.test.ts` — 14 tests over every placement decision: family formation, shared/partially shared/unique thresholding, repeated labels, per-row source Evidence, graph-derived root digests, truncated inventories, members that declare unknown child coverage, scope ties, strict-majority scope, coverage accounting, paging, determinism and result-shape validation. - `tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts` — 3 tests driving the real MCP session (`createServer({ kind: "session" })`, linked in-memory transports, `ArtifactProvider`) through `open_binary` → `inspect_artifact` → `cluster_artifacts`, with Evidence read back from the session and passed inline. Fixtures are fixture *code*: archives written per run with the existing `@zip.js/zip.js` writer, and a universal Mach-O built with the repo's own `fatImage`/`machoImage` fixtures. - Base reproduction: the producing-boundary defect below was observed by driving that real session over a universal Mach-O plus a ZIP **before** the fix — the result reported `status: "partitioned"` while `summary.members_with_unknown_inventory` was already `1` and the member appeared in no family, index or unplaced row. After the fix the same input reports `partial` with the member in `unknown_members` as `incomplete-inventory`. The regression is covered deterministically by the domain test using the new `coverageStatus` fixture option, which fails on the base revision. - User-visible CLI/MCP output: the fields asserted by the boundary test for that real call are `status: "partial"`, `coverage.member_scope: "container-entries"`, `families: [{ family_id: "unplaced", tier: null, members: [], unknown_members: [{ label: "universal", reason: "incomplete-inventory" }] }]`, `summary.members_with_complete_inventory: 1`, `summary.members_with_unknown_inventory: 1`, `summary.members_without_family: 1`, and a limitation beginning `universal: Universal Mach-O slices`. - Remaining proof gaps: (1) the universal Mach-O member is only unreadable on a host without a native slice adapter, so that boundary test reads the producer's declaration and skips where children *are* readable; (2) a count-truncated inventory is covered by the domain test only, because a 3,000-entry ZIP still reports complete coverage and a CRC-contradicted archive is refused by the producer even under `integrity_policy record-and-continue`; (3) no cost measurement was taken, so no performance claim is made. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npx tsc --noEmit` — exit 0. - `npm run lint` (oxlint plus module-boundary verification over 1,941 source files) — exit 0; repository-wide warnings unchanged. - `npm run check:fast` — exit 0. - `npx vitest run src/domain/artifactFamilyClustering.test.ts tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts tests/boundary/filesystem/artifactComparison.test.ts tests/boundary/filesystem/setupSkill.test.ts` — 45 passed, 4 skipped (49), exit 0. - `npx vitest run src/domain tests/boundary/filesystem/artifactComparison.test.ts tests/boundary/filesystem/setupSkill.test.ts tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts` — 3,235 passed, 4 skipped (3,239) across 186 files, exit 0. - `npx vitest run tests/boundary/mcp/artifactFamilyClusteringMcp.test.ts -t "unknown child coverage"` — the new test runs on this host (1 passed), it does not silently skip. - `npx oxfmt --check` on every changed file — all correctly formatted. - `npm run docs:generate` then `npm run docs:check` — exit 0, including `generate-skill-metadata.mjs --check`, the product catalog, doc facts and `tests/conformance/readmeConsistency.test.ts` (5 passed). - Real-provider verification: not applicable. The capability consumes Evidence the caller already holds and starts no provider; the boundary test asserts a provider launch would be a bug. No Hopper, Ghidra or IDA run is involved. ### After merging main (`905cbaf23`) - Conflicts: `sessionToolContracts.ts` and `toolContractExamples.ts` needed both new tools (this PR's `cluster_artifacts` and main's `match_native_functions`), and `artifactInventoryEvidence.test.ts` needed both import sets. Resolved as a union, with main's `bundleComparisonMcpInputSchema` replacing the older `bundleComparisonInputSchema`. - `npm ci`, `npm run build:cached` — exit 0. - `npm run check:fast` — exit 0. - `npm run test:focused` over the six clustering/encoding files — 47 passed, exit 0. - `npm run test:focused` over `toolEffects`, `toolInputReferences`, `toolOutputReferences`, `compactInputSchema`, `mcpResponseBudget` and `targetContentProvenance` — 101 passed, exit 0. - `npm run docs:check` — exit 0. - One test change was required by upstream: SDK schema diagnostics are now normalized into the `invalid_request` / `inspect_input_contract` envelope, so the "not artifact inventory Evidence" case asserts that structured contract instead of the old "Input validation" sentence. ## Compatibility, safety, and release - Breaking changes or migration steps: none. The completeness predicate extraction is behavior-preserving (verified by the 19 `artifactComparison` boundary tests and the full domain suite); no schema field was removed or renamed, and the CLI/MCP surfaces are additive. - Real Hopper/Ghidra, browser, or OS coverage: not applicable, no provider is launched. The one platform-dependent member shape (universal Mach-O) is handled by reading the producer's declaration. - Package or release metadata impact: none; ignored catalogs and `skills/` are generated and validated by CI rather than committed here. - Security, privacy, process, or containment review: read-only. No target is selected, launched, attached to or modified; no process is supervised or signalled; member labels are caller text and are never parsed as identity; no credentials or transport data are persisted. ## Review round (fc51e3b00) Every finding from the first review round (Greptile and Codex) was addressed, and each thread carries a reply with its root cause, structural fix and validation: - Member-index rows carry `observations: [{ label, evidence_id }]` and count members, so repeated labels stay distinct and a row keeps its provenance when it has no family row. - A family requires a producer digest of the root's own bytes. A directory root (graph-derived digest, unread symlink targets uncommitted) is compared and counted but never called byte-identical, and directory nodes leave the member index; `members_without_byte_root_digest` and `nodes_without_byte_identity` keep those omissions visible. - Inventory completeness and coverage scope are counted separately (`members_with_complete_inventory`, `members_with_unknown_inventory`, `members_outside_member_scope`). - The returned page is stated in `coverage.families_offset` and `coverage.families_limit` and in the retained Evidence parameters, and the CLI enforces the 64-member bound before reading any document. - A follow-up finding on that counter is fixed in 8ca824d70: `members_outside_member_scope` counts on the scope axis only (complete inventories, with the no-majority case defined explicitly), so it no longer counts members whose own inventory is incomplete. ## Author readiness Human owner: @0xLLVEL - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#6077
No description provided.