Skip to content

fix: deduplicate leaf record common types when objects_as_records is enabled#134

Open
tomjwxf wants to merge 2 commits into
cedar-policy:mainfrom
tomjwxf:dedup-record-types-objects-as-records
Open

fix: deduplicate leaf record common types when objects_as_records is enabled#134
tomjwxf wants to merge 2 commits into
cedar-policy:mainfrom
tomjwxf:dedup-record-types-objects-as-records

Conversation

@tomjwxf

@tomjwxf tomjwxf commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Extends deduplicate_entity_types to cover leaf records encoded as common types when objects_as_records is enabled. No new config flag — the existing dedup pipeline gains a second emission target.

Closes #126

Problem

When both deduplicate_entity_types and objects_as_records are true, identical leaf record objects across tools produced duplicate common type definitions instead of being consolidated:

// Before (bug): per-tool copies
namespace MyMcpServer::tool_a::Input {
  type metadata = { author: String, version?: Long };
}
namespace MyMcpServer::tool_b::Input {
  type metadata = { author: String, version?: Long };
}

Solution

// After (fix): shared LCA common type
namespace MyMcpServer {
  type metadata = { author: String, version?: Long };
  type tool_aInput = { metadata?: MyMcpServer::metadata, query: String };
  type tool_bInput = { metadata?: MyMcpServer::metadata, data: String };
}

Changes

Change Detail
Remove blanket LeafRecord filter resolved.retain() was stripping all record fingerprints when objects_as_records=true
Collision detection for common types Check existing common types for matching record shapes (not just name) — parallel to entity path
Placement via add_commontype() Deduplicated LeafRecords emit as common types when objects_as_records=true
Pass 2 returns EntityOrCommon cedar_type_from_property_type() no longer skips dedup lookup with objects_as_records

Tests

Test What it covers
dedup_leaf_record_objects_as_records Happy path: shared records consolidated as common type
dedup_leaf_record_objects_as_records_flat Flat namespace rendering with dedup
dedup_leaf_record_different_required_objects_as_records Different required fields → correctly NOT deduplicated
dedup_enum_record_name_conflict_objects_as_records Enum/record name collision → correctly skipped

All 92 tests pass (62 unit + 30 integration), 0 warnings.

…enabled

When both deduplicate_entity_types and objects_as_records are true,
identical leaf record objects across tools are now consolidated into a
shared common type at the LCA namespace, instead of each tool getting
its own duplicate common type definition.

Previously, LeafRecord fingerprints were explicitly stripped from the
deduplication map when objects_as_records was enabled. This made sense
when objects_as_records implied records shouldn't participate in
entity-type dedup, but left record-type dedup unsupported.

Changes:
- Remove the blanket LeafRecord filter in deduplicate_entities()
- Place deduplicated LeafRecords as common types (via add_commontype)
  when objects_as_records is true, entity types otherwise
- Update collision detection to check existing common types for matching
  record shapes and reuse them when identical
- Enable dedup lookup in cedar_type_from_property_type() regardless of
  the objects_as_records setting, returning EntityOrCommon references
  for records

Before (bug): duplicate per-tool common types
  namespace MyMcpServer::tool_a::Input {
    type metadata = { author: String, version?: Long };
  }
  namespace MyMcpServer::tool_b::Input {
    type metadata = { author: String, version?: Long };
  }

After (fix): shared LCA common type
  namespace MyMcpServer {
    type metadata = { author: String, version?: Long };
    type tool_aInput = { metadata?: MyMcpServer::metadata, ... };
    type tool_bInput = { metadata?: MyMcpServer::metadata, ... };
  }

Closes cedar-policy#126
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Coverage Report

Head Commit: 92e828ddeb64c5ef886d344c350182e65b505090

Base Commit: b70f91034bba7a4af2ba02a5900878a2284e68aa

Download the full coverage report.

Coverage of Added or Modified Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 76.47%

Status: FAILED ❌

Details
File Status Covered Coverage Missed Lines
cedar-policy-mcp-schema-generator/src/generator/schema.rs 🟡 52/68 76.47% 937-938, 940-949, 3106-3109

Coverage of All Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 90.66%

Status: PASSED ✅

Details
Package Status Covered Coverage Base Coverage
cedar-policy-mcp-schema-generator 🟢 2542/2772 91.70% --
cedar-policy-mcp-schema-generator-wasm 🟢 147/161 91.30% --
mcp-tools-sdk 🟢 1601/1799 88.99% --

Add unit tests for reusing or skipping dedup when the LCA already
has a common type with the same name. Also keep tool_a::Input alive in
the objects_as_records dedup test so the local-namespace assertion runs.
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

Coverage Report

Head Commit: 0dd610a78f20fbda1b4763716660e1fb90c1514a

Base Commit: b70f91034bba7a4af2ba02a5900878a2284e68aa

Download the full coverage report.

Coverage of Added or Modified Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 98.31%

Status: PASSED ✅

Details
File Status Covered Coverage Missed Lines
cedar-policy-mcp-schema-generator/src/generator/schema.rs 🟢 233/237 98.31% 946-949

Coverage of All Lines of Rust Code

Required coverage: 80.00%

Actual coverage: 91.77%

Status: PASSED ✅

Details
Package Status Covered Coverage Base Coverage
cedar-policy-mcp-schema-generator 🟢 2757/2949 93.49% --
cedar-policy-mcp-schema-generator-wasm 🟢 147/161 91.30% --
mcp-tools-sdk 🟢 1601/1799 88.99% --

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deduplicate record types when objects_as_records is enabled.

2 participants