Skip to content

eng-271#381

Merged
Connorbelez merged 2 commits intomainfrom
eng-271
Apr 9, 2026
Merged

eng-271#381
Connorbelez merged 2 commits intomainfrom
eng-271

Conversation

@Connorbelez
Copy link
Copy Markdown
Owner

@Connorbelez Connorbelez commented Apr 3, 2026

Summary by Sourcery

Introduce a normalized field and view contract layer for the CRM view engine and persist this metadata on field and view definitions.

New Features:

  • Expose normalized field definitions, system view definitions, and an entity view adapter contract from the getViewSchema endpoint.
  • Add support for computed and relation-backed fields, including renderer hints, editability modes, layout eligibility, and aggregation metadata.
  • Introduce per-view disabled layout messages and user-saved views storage to support richer layout and personalization features.

Bug Fixes:

  • Ensure the admin detail sheet can resolve the active entity type from shared state when the prop is omitted, avoiding broken record links.

Enhancements:

  • Centralize derivation of field capabilities into reusable metadata compiler helpers that compute normalized kind, renderer hint, layout eligibility, aggregation, and editability.
  • Persist normalized field contract metadata on fieldDefs during creation, update, and system adapter bootstrap for consistent behavior across native and EAV-backed objects.
  • Extend validators and schema definitions to cover new field, view, and relation metadata types and constraints, including enforcement that a field cannot be both relation-backed and computed.
  • Enrich view schema columns with field contract properties so clients can drive UI behavior without additional lookups.
  • Refine admin record sidebar and routing utilities with minor type and formatting cleanups.
  • Update GitNexus documentation references to the current project index identifier and usage guidance.

Tests:

  • Add unit tests for field contract derivation and extend view engine and metadata compiler integration tests to cover normalized field contract persistence and the expanded view schema payload.

Summary by CodeRabbit

  • New Features

    • Added support for saved views with customizable field visibility, ordering, and filtering
    • Enhanced field configuration with relation, computed field, and visibility settings
    • Introduced view grouping and aggregate presets for data summarization
    • Expanded view metadata to include layout eligibility and disabled layout messaging
  • Improvements

    • Enriched view schema responses with comprehensive field and layout metadata
    • Improved admin detail sheet entity type handling for better navigation

@linear
Copy link
Copy Markdown

linear bot commented Apr 3, 2026

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 3, 2026

📝 Walkthrough

Walkthrough

Introduces a comprehensive field metadata and view normalization system for the CRM backend, including new type definitions, derivation functions for field contract metadata, schema extensions for persisting normalized field properties, and view query enhancements to compute adapter contracts. Updates GitNexus references in documentation. Refactors admin detail sheet to make entity type optional and resolve it from context.

Changes

Cohort / File(s) Summary
Documentation Updates
AGENTS.md, CLAUDE.md
Updated GitNexus index identifier from t3code-35e1a6e1 to fairlendapp and adjusted all embedded resource URLs and repository path references.
Type Definitions & Validators
convex/crm/types.ts, convex/crm/validators.ts
Added comprehensive type exports for view layouts, normalized field kinds, aggregation functions, editability modes, renderer hints, eligibility rules, relation/computed metadata, and view configuration structures. Added corresponding validators for all new types with exhaustive union definitions.
Field Metadata Derivation
convex/crm/metadataCompiler.ts
Introduced set of exported derivation functions (deriveNormalizedFieldKind, deriveRendererHint, deriveLayoutEligibility, deriveAggregationEligibility, deriveEditabilityMetadata, deriveFieldContractMetadata) that compute normalized field properties from field type, relation, computed, and editability state. Updated deriveCapabilities to consult eligibility results.
Field Definition Handlers
convex/crm/fieldDefs.ts
Extended createField and updateField input schemas with optional relation, computed, and isVisibleByDefault fields. Integrated deriveFieldContractMetadata to compute and persist normalized field contract properties (kind, renderer hint, layout/aggregation eligibility, editability, visibility) alongside core field attributes. Added validation to reject simultaneous relation and computed specifications.
Schema Extensions
convex/schema.ts
Extended fieldDefs table with normalized metadata fields (normalizedFieldKind, rendererHint, relation, computed, layoutEligibility, aggregation, editability, isVisibleByDefault). Extended viewDefs with groupByFieldId, aggregatePresets, and disabledLayoutMessages. Added new userSavedViews table with full view configuration, visibility/ordering, filtering, grouping, aggregates, and multi-index support.
View Schema Generation
convex/crm/viewQueries.ts
Significantly enhanced getViewSchema to load view filters, verify object def ownership, enrich columns with normalized metadata, construct EntityViewAdapterContract, and generate SystemViewDefinition. Updated return type to include adapterContract, fields, and view alongside existing properties. Added filter parsing with operator normalization and rejection of unsupported operators.
Bootstrap Initialization
convex/crm/systemAdapters/bootstrap.ts
Updated bootstrapForOrg to compute and persist field contract metadata for all initial field definitions via deriveFieldContractMetadata, adding normalized properties to inserted records.
Test Coverage
convex/crm/__tests__/metadataCompiler.test.ts, convex/crm/__tests__/viewEngine.test.ts
Added test suites for deriveFieldContractMetadata covering primitive and computed field classification. Expanded view engine tests with filter operator validation (equals, is), error handling for unsupported operators, and comprehensive normalized schema contract assertions (field kind, renderer hints, layout eligibility, editability, visibility).
Admin UI Adjustments
src/components/admin/shell/AdminDetailSheet.tsx, src/components/admin/shell/RecordSidebarProvider.tsx, src/lib/admin-detail-route-state.ts, src/routes/admin/route.tsx
Made AdminDetailSheetProps.entityType optional; resolved entity type from context hook when not explicitly provided. Refactored record link computation to support optional entity type and choose route based on resolved type. Minor formatting adjustments to imports and function signatures in supporting files.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/System
    participant FieldHandler as fieldDefs Handler
    participant MetaCompiler as Metadata Compiler
    participant Schema as Convex Schema
    participant ViewQuery as View Query Engine

    User->>FieldHandler: create/update field (fieldType, relation?, computed?)
    FieldHandler->>FieldHandler: validate schema (reject relation + computed)
    FieldHandler->>MetaCompiler: deriveFieldContractMetadata(fieldType, relation?, computed?, isVisibleByDefault?)
    MetaCompiler->>MetaCompiler: deriveNormalizedFieldKind()
    MetaCompiler->>MetaCompiler: deriveRendererHint()
    MetaCompiler->>MetaCompiler: deriveLayoutEligibility()
    MetaCompiler->>MetaCompiler: deriveAggregationEligibility()
    MetaCompiler->>MetaCompiler: deriveEditabilityMetadata()
    MetaCompiler-->>FieldHandler: DerivedFieldContractMetadata
    FieldHandler->>Schema: persist field + normalized metadata<br/>(normalizedFieldKind, rendererHint, layoutEligibility, etc.)
    Schema-->>FieldHandler: ack

    User->>ViewQuery: getViewSchema(viewDefId)
    ViewQuery->>Schema: load viewDef, objectDef, fieldDefs, viewFilters
    ViewQuery->>ViewQuery: enrich columns with normalized metadata
    ViewQuery->>ViewQuery: construct EntityViewAdapterContract
    ViewQuery->>ViewQuery: generate SystemViewDefinition (filters, grouping, aggregates)
    ViewQuery-->>User: {adapterContract, view, fields, columns, viewType}
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • eng-254: Canelndar View & Filter Builder #354: Both PRs enhance the CRM view/filter/field normalization stack, adding metadata derivation, field property enrichment, and view schema contract generation.
  • eng-251 #351: Both PRs extend the CRM backend schema and type system (convex/crm/types.ts, convex/schema.ts) with new field metadata and view configuration structures.
  • eng-231 #368: Both PRs modify AdminDetailSheet.tsx, adjusting how the component sources and resolves entity type from context and props.

Poem

🐰 Metadata blooms in structured fields,
Each type a hint, each layout yields,
From field to view, the contracts flow,
Aggregates dance, eligibility shines bright—
Normalized paths light the admin's night! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'eng-271' is a ticket/issue identifier with no descriptive content about the changes, making it vague and non-informative about the actual work performed. Replace with a descriptive single sentence summarizing the main change, e.g., 'Add normalized field and view contract metadata layer to CRM view engine' or similar.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eng-271

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 3, 2026

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Apr 3, 2026

Reviewer's Guide

Implements normalized field and view contracts for the CRM view engine, persisting richer field metadata on field definitions, exposing it through getViewSchema along with a new adapter contract, adding userSavedViews storage, tightening validators/schema, and making small admin UI routing and layout tweaks plus documentation updates.

Sequence diagram for enriched getViewSchema flow

sequenceDiagram
    actor Client
    participant CRMApi as crmQuery_getViewSchema
    participant DB as ConvexDB
    participant Compiler as metadataCompiler

    Client->>CRMApi: getViewSchema(viewDefId)

    CRMApi->>DB: get viewDefs by id
    DB-->>CRMApi: viewDef
    CRMApi->>DB: get objectDefs by viewDef.objectDefId
    DB-->>CRMApi: objectDef
    CRMApi->>DB: query viewFields by viewDefId
    DB-->>CRMApi: viewFields[]
    CRMApi->>DB: query viewFilters by viewDefId
    DB-->>CRMApi: viewFilters[]
    CRMApi->>DB: loadActiveFieldDefs(objectDefId)
    DB-->>CRMApi: fieldDefs[]

    CRMApi->>Compiler: deriveLayoutEligibility(fieldType) for each field
    Compiler-->>CRMApi: FieldLayoutEligibility
    CRMApi->>Compiler: deriveAggregationEligibility(fieldType)
    Compiler-->>CRMApi: AggregationEligibility

    CRMApi->>CRMApi: toColumnDef + attach normalized metadata
    CRMApi->>CRMApi: toNormalizedFieldDefinition(fieldDef) for each field

    CRMApi->>CRMApi: buildSystemViewDefinition(viewDef, viewFields, viewFilters, fieldDefsById)
    CRMApi->>CRMApi: buildAdapterContract(objectDef, viewDef, fieldDefs)

    CRMApi-->>Client: ViewSchemaResult
    note right of Client: includes columns, fields, adapterContract, view(SystemViewDefinition), viewType, needsRepair
Loading

ER diagram for CRM view metadata and userSavedViews

erDiagram
    objectDefs {
        string id
        string orgId
        string name
        boolean isActive
    }

    fieldDefs {
        string id
        string orgId
        string objectDefId
        string name
        string label
        string fieldType
        string normalizedFieldKind
        boolean isRequired
        boolean isUnique
        boolean isActive
        number displayOrder
        string rendererHint
        string relation
        string computed
        string layoutEligibility
        string aggregation
        string editability
        boolean isVisibleByDefault
        string nativeColumnPath
        boolean nativeReadOnly
    }

    viewDefs {
        string id
        string orgId
        string objectDefId
        string name
        string viewType
        string boundFieldId
        string groupByFieldId
        string aggregatePresets
        string disabledLayoutMessages
        boolean isDefault
        boolean needsRepair
    }

    viewFields {
        string id
        string viewDefId
        string fieldDefId
        boolean isVisible
        number displayOrder
    }

    viewFilters {
        string id
        string viewDefId
        string operator
        string fieldDefId
        string value
    }

    userSavedViews {
        string id
        string orgId
        string objectDefId
        string ownerAuthId
        string sourceViewDefId
        string name
        string viewType
        string visibleFieldIds
        string fieldOrder
        string filtersJson
        string groupByFieldId
        string aggregatePresets
        boolean isDefault
    }

    objectDefs ||--o{ fieldDefs : has_fields
    objectDefs ||--o{ viewDefs : has_views
    objectDefs ||--o{ userSavedViews : has_user_views

    viewDefs ||--o{ viewFields : has_view_fields
    viewDefs ||--o{ viewFilters : has_view_filters

    fieldDefs ||--o{ viewFields : appears_in_view

    viewDefs ||--o{ userSavedViews : source_for

    fieldDefs ||--o{ userSavedViews : referenced_fields
Loading

Updated class diagram for CRM normalized field and view contracts

classDiagram
    class LayoutEligibilityRule {
        +boolean enabled
        +string reason
    }

    class FieldLayoutEligibility {
        +LayoutEligibilityRule table
        +LayoutEligibilityRule kanban
        +LayoutEligibilityRule calendar
        +LayoutEligibilityRule groupBy
    }

    class AggregationEligibility {
        +boolean enabled
        +string reason
        +AggregateFn[] supportedFunctions
    }

    class RelationMetadata {
        +string cardinality
        +string relationName
        +string targetFieldName
        +Id_objectDefs targetObjectDefId
    }

    class ComputedFieldMetadata {
        +string expressionKey
        +string[] sourceFieldNames
    }

    class EditabilityMetadata {
        +EditabilityMode mode
        +string reason
    }

    class AggregatePreset {
        +Id_fieldDefs fieldDefId
        +AggregateFn fn
        +string label
    }

    class NormalizedFieldDefinition {
        +AggregationEligibility aggregation
        +ComputedFieldMetadata computed
        +string defaultValue
        +string description
        +number displayOrder
        +EditabilityMetadata editability
        +Id_fieldDefs fieldDefId
        +string fieldType
        +boolean isActive
        +boolean isRequired
        +boolean isUnique
        +boolean isVisibleByDefault
        +string label
        +FieldLayoutEligibility layoutEligibility
        +string name
        +string nativeColumnPath
        +boolean nativeReadOnly
        +NormalizedFieldKind normalizedFieldKind
        +Id_objectDefs objectDefId
        +any options
        +RelationMetadata relation
        +FieldRendererHint rendererHint
    }

    class SystemViewDefinition {
        +AggregatePreset[] aggregatePresets
        +Id_fieldDefs boundFieldId
        +string calendar
        +string kanban
        +string table
        +Id_fieldDefs[] fieldOrder
        +RecordFilter[] filters
        +Id_fieldDefs groupByFieldId
        +boolean isDefault
        +ViewLayout layout
        +string name
        +boolean needsRepair
        +Id_objectDefs objectDefId
        +Id_viewDefs viewDefId
        +Id_fieldDefs[] visibleFieldIds
    }

    class UserSavedViewDefinition {
        +AggregatePreset[] aggregatePresets
        +Id_fieldDefs[] fieldOrder
        +RecordFilter[] filters
        +Id_fieldDefs groupByFieldId
        +boolean isDefault
        +ViewLayout layout
        +string name
        +Id_objectDefs objectDefId
        +string ownerAuthId
        +Id_viewDefs sourceViewDefId
        +Id_fieldDefs[] visibleFieldIds
    }

    class EntityViewAdapterContract {
        +string detailSurfaceKey
        +string entityType
        +Id_objectDefs objectDefId
        +string statusFieldName
        +ViewLayout[] supportedLayouts
        +string titleFieldName
    }

    NormalizedFieldDefinition --> FieldLayoutEligibility : has
    NormalizedFieldDefinition --> LayoutEligibilityRule : uses
    NormalizedFieldDefinition --> AggregationEligibility : has
    NormalizedFieldDefinition --> RelationMetadata : optional
    NormalizedFieldDefinition --> ComputedFieldMetadata : optional
    NormalizedFieldDefinition --> EditabilityMetadata : has

    SystemViewDefinition --> AggregatePreset : uses
    SystemViewDefinition --> Id_fieldDefs : references

    UserSavedViewDefinition --> AggregatePreset : uses
    UserSavedViewDefinition --> Id_fieldDefs : references

    EntityViewAdapterContract --> Id_objectDefs : references

    class Id_fieldDefs
    class Id_objectDefs
    class Id_viewDefs
    class RecordFilter
    class ViewLayout
    class NormalizedFieldKind
    class AggregateFn
    class EditabilityMode
    class FieldRendererHint
Loading

File-Level Changes

Change Details Files
Add normalized field contracts and helpers, and persist them on field definitions during creation, update, and bootstrap.
  • Introduce TypeScript types for normalized field kinds, renderer hints, layout and aggregation eligibility, relation/computed/editability metadata, and view/adapter contracts.
  • Add pure helper functions to derive normalizedFieldKind, rendererHint, layoutEligibility, aggregation eligibility, editability, and combined field contract metadata from fieldType, relation, computed, and nativeReadOnly flags.
  • Update field creation, update, and system adapter bootstrap flows to compute and store normalized field contract metadata on fieldDefs, enforcing that a field cannot be both relation-backed and computed.
convex/crm/types.ts
convex/crm/metadataCompiler.ts
convex/crm/fieldDefs.ts
convex/crm/systemAdapters/bootstrap.ts
Extend view schema API to return full normalized field definitions, system view definitions, and an entity view adapter contract.
  • Expand ViewSchemaResult to include adapterContract, fields, and a SystemViewDefinition, while changing viewType to a typed ViewLayout and adding richer column metadata such as normalizedFieldKind, rendererHint, relation, layoutEligibility, aggregation, editability, and isVisibleByDefault.
  • Add helpers to map fieldDefs into NormalizedFieldDefinition objects, derive disabled layout messages based on field layout eligibility, build an EntityViewAdapterContract (including supported layouts and title/status fields), and construct a SystemViewDefinition (ordering, visible fields, filters, aggregatePresets, disabledLayoutMessages).
  • Update getViewSchema to load objectDef and viewFilters, build the richer columns array, normalized fields list, system view definition, and adapter contract, and return them in the response.
convex/crm/viewQueries.ts
convex/crm/types.ts
Strengthen Convex validators and schema to support normalized field metadata, view layout metadata, and per-user saved views.
  • Add validators for normalized field kinds, renderer hints, layout eligibility rules, aggregation eligibility, relation/computed/editability metadata, aggregate presets, and layout messages, and reuse them across CRM entities.
  • Extend fieldDefs schema to store normalizedFieldKind, rendererHint, relation, computed, layoutEligibility, aggregation, editability, and isVisibleByDefault on each field.
  • Extend viewDefs schema to persist groupByFieldId, aggregatePresets, and disabledLayoutMessages, and introduce a userSavedViews table with indexes for per-user, per-org, and source-view lookups.
convex/crm/validators.ts
convex/schema.ts
Update tests to cover normalized field contracts, computed/read-only behavior, and enriched view schema responses.
  • Add pure-unit tests for deriveFieldContractMetadata to verify read-only native fields and computed fields behavior including aggregation and editability.
  • Extend metadata compiler integration tests to assert that fieldDefs persist normalized field contract metadata (kind, rendererHint, visibility, aggregation, editability, layoutEligibility).
  • Enhance view engine tests to expect normalized field metadata in columns and fields, presence of SystemViewDefinition and adapterContract, and correct supportedLayouts and disabledLayoutMessages semantics from getViewSchema.
convex/crm/__tests__/metadataCompiler.test.ts
convex/crm/__tests__/viewEngine.test.ts
Improve admin UI detail routing and minor React component ergonomics.
  • Relax AdminDetailSheet to accept an optional entityType and fall back to the entity type from shared detail route state, and adjust link construction accordingly while keeping disabled state when no record is selected.
  • Tidy RecordSidebarProvider and admin detail route state utilities with minor signature/formatting cleanups without changing behavior.
src/components/admin/shell/AdminDetailSheet.tsx
src/components/admin/shell/RecordSidebarProvider.tsx
src/lib/admin-detail-route-state.ts
Update GitNexus documentation to reference the new fairlendapp index and resource paths.
  • Replace the old GitNexus index identifier with fairlendapp and adjust linked resource URLs for context, clusters, processes, and process traces in both AGENTS and CLAUDE documentation.
  • Tighten GitNexus workflow guidance to require impact analysis and change detection before edits and commits.
AGENTS.md
CLAUDE.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@Connorbelez Connorbelez marked this pull request as ready for review April 3, 2026 01:56
Copilot AI review requested due to automatic review settings April 3, 2026 01:56
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="convex/crm/viewQueries.ts" line_range="180-205" />
<code_context>
+	};
+}
+
+function deriveDisabledLayoutMessages(
+	fieldDefs: FieldDef[]
+): SystemViewDefinition["disabledLayoutMessages"] | undefined {
+	const messages: NonNullable<SystemViewDefinition["disabledLayoutMessages"]> =
+		{};
+
+	if (!fieldDefs.some((fieldDef) => fieldDef.layoutEligibility.table.enabled)) {
+		messages.table = "Table layout requires at least one active field.";
+	}
+
+	if (
+		!fieldDefs.some((fieldDef) => fieldDef.layoutEligibility.kanban.enabled)
+	) {
+		messages.kanban =
+			"Add a select or multi-select field to unlock kanban layouts.";
+	}
+
+	if (
+		!fieldDefs.some((fieldDef) => fieldDef.layoutEligibility.calendar.enabled)
+	) {
+		messages.calendar =
+			"Add a date or datetime field to unlock calendar layouts.";
+	}
+
+	return Object.keys(messages).length > 0 ? messages : undefined;
+}
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Disabled table layout message is effectively unreachable and the implementation doesn’t consider field `isActive` state.

Because `deriveLayoutEligibility` always sets `table: enabledRule()`, `fieldDef.layoutEligibility.table.enabled` is always true, so this condition never triggers and `messages.table` is never populated.

If this check is meant to enforce “at least one active field”, it should key off `isActive` (e.g. `fieldDefs.some(f => f.isActive)`) or otherwise operate on a list already filtered to active fields. If that’s not the intent, the table-specific message is effectively dead code and should be removed or corrected to match the actual condition being checked.

```suggestion
function deriveDisabledLayoutMessages(
	fieldDefs: FieldDef[]
): SystemViewDefinition["disabledLayoutMessages"] | undefined {
	const messages: NonNullable<SystemViewDefinition["disabledLayoutMessages"]> =
		{};

	// Table layout requires at least one active field; check the field's active state
	if (!fieldDefs.some((fieldDef) => fieldDef.isActive)) {
		messages.table = "Table layout requires at least one active field.";
	}

	if (
		!fieldDefs.some((fieldDef) => fieldDef.layoutEligibility.kanban.enabled)
	) {
		messages.kanban =
			"Add a select or multi-select field to unlock kanban layouts.";
	}

	if (
		!fieldDefs.some((fieldDef) => fieldDef.layoutEligibility.calendar.enabled)
	) {
		messages.calendar =
			"Add a date or datetime field to unlock calendar layouts.";
	}

	return Object.keys(messages).length > 0 ? messages : undefined;
}
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread convex/crm/viewQueries.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
convex/crm/viewQueries.ts (1)

226-231: Document the title/status field naming convention.

The adapter contract derives titleFieldName and statusFieldName by looking for fields named "name" and "status" respectively. This is a reasonable convention but could benefit from a brief comment explaining the heuristic.

📝 Optional: Add clarifying comment
 	supportedLayouts.add(args.viewDef.viewType);
+	// Convention: Use fields named "name" and "status" for UI title/status display.
+	// These are common patterns in CRM objects (e.g., lead.name, deal.status).
 	const titleField = args.fieldDefs.find(
 		(fieldDef) => fieldDef.name === "name"
 	);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/crm/viewQueries.ts` around lines 226 - 231, The code locates
title/status fields by searching args.fieldDefs for fieldDef.name === "name" and
"status" (variables titleField and statusField) but lacks an explanatory
comment; add a short clarifying comment next to the titleField/statusField
lookups (or above them) that documents this naming convention/heuristic (e.g.,
that the adapter expects a "name" field to be the title and a "status" field for
workflow state) so future readers understand why titleFieldName/statusFieldName
are derived this way.
convex/crm/fieldDefs.ts (1)

272-285: Clarify intentional asymmetry in isVisibleByDefault handling.

In updateField, isVisibleByDefault is only patched when explicitly provided in args, while other derived fields (normalizedFieldKind, rendererHint, etc.) are always recomputed and patched.

This differs from createField where isVisibleByDefault is set from the derived contract. The asymmetry appears intentional—preserving explicit user settings on update while allowing derivation to set defaults on create—but a brief comment would clarify this design decision.

📝 Optional: Add clarifying comment
 	if (args.isVisibleByDefault !== undefined) {
 		patch.isVisibleByDefault = args.isVisibleByDefault;
 	}
+	// Note: isVisibleByDefault is only patched when explicitly provided to preserve
+	// user-set values. Derived metadata fields below are always recomputed.
 	patch.normalizedFieldKind = effectiveFieldContract.normalizedFieldKind;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/crm/fieldDefs.ts` around lines 272 - 285, In updateField, the code
only applies args.isVisibleByDefault when provided while always overwriting
derived properties like normalizedFieldKind, rendererHint, layoutEligibility,
aggregation, and editability; add a concise comment near the updateField
function (adjacent to the patch.isVisibleByDefault conditional and the
subsequent assignments to patch.normalizedFieldKind, patch.rendererHint,
patch.layoutEligibility, patch.aggregation, patch.editability) explaining that
this asymmetry is intentional: createField uses derived contract defaults but
updateField preserves an explicit user-provided isVisibleByDefault while still
recomputing other derived fields to keep defaults in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@convex/crm/metadataCompiler.ts`:
- Around line 199-215: deriveCapabilities currently only pushes "sort" when
calendar or aggregation eligibility are true, causing many sortable scalar
fields to be excluded; add a dedicated sort eligibility check instead of
piggybacking on calendar/aggregation. Implement a function or call (e.g.,
deriveSortEligibility(fieldType) or deriveSortEligibility inside
deriveCapabilities) that returns whether the field is sortable and use its
enabled result to push "sort" into caps; keep the existing
deriveLayoutEligibility and deriveAggregationEligibility checks for their own
capabilities but ensure "sort" is added whenever the new sort eligibility
indicates true so viewQueries/fieldDefs continue to see sortable fields.

In `@convex/schema.ts`:
- Around line 1980-1994: The schema added six required fields for fieldDefs
(normalizedFieldKind, rendererHint, layoutEligibility, aggregation, editability,
isVisibleByDefault) but they may be missing on first-deploy documents; update
the bootstrap initialization to set these fields when creating system fieldDefs
and verify the fieldDefs creation path sets them too. Specifically, in
bootstrap.ts ensure the code path that seeds system field definitions assigns
values for normalizedFieldKind, rendererHint, layoutEligibility, aggregation,
editability, and isVisibleByDefault; also confirm and, if needed, update the
factory/creator in fieldDefs.ts (e.g., the function creating or upserting
fieldDef documents) to include defaults for these six fields so all generated
fieldDefs satisfy the new schema.

---

Nitpick comments:
In `@convex/crm/fieldDefs.ts`:
- Around line 272-285: In updateField, the code only applies
args.isVisibleByDefault when provided while always overwriting derived
properties like normalizedFieldKind, rendererHint, layoutEligibility,
aggregation, and editability; add a concise comment near the updateField
function (adjacent to the patch.isVisibleByDefault conditional and the
subsequent assignments to patch.normalizedFieldKind, patch.rendererHint,
patch.layoutEligibility, patch.aggregation, patch.editability) explaining that
this asymmetry is intentional: createField uses derived contract defaults but
updateField preserves an explicit user-provided isVisibleByDefault while still
recomputing other derived fields to keep defaults in sync.

In `@convex/crm/viewQueries.ts`:
- Around line 226-231: The code locates title/status fields by searching
args.fieldDefs for fieldDef.name === "name" and "status" (variables titleField
and statusField) but lacks an explanatory comment; add a short clarifying
comment next to the titleField/statusField lookups (or above them) that
documents this naming convention/heuristic (e.g., that the adapter expects a
"name" field to be the title and a "status" field for workflow state) so future
readers understand why titleFieldName/statusFieldName are derived this way.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4dfcb3ac-f33f-48a1-be51-873c4c5dc7f0

📥 Commits

Reviewing files that changed from the base of the PR and between c1d1fcc and f6d46c9.

📒 Files selected for processing (15)
  • AGENTS.md
  • CLAUDE.md
  • convex/crm/__tests__/metadataCompiler.test.ts
  • convex/crm/__tests__/viewEngine.test.ts
  • convex/crm/fieldDefs.ts
  • convex/crm/metadataCompiler.ts
  • convex/crm/systemAdapters/bootstrap.ts
  • convex/crm/types.ts
  • convex/crm/validators.ts
  • convex/crm/viewQueries.ts
  • convex/schema.ts
  • src/components/admin/shell/AdminDetailSheet.tsx
  • src/components/admin/shell/RecordSidebarProvider.tsx
  • src/lib/admin-detail-route-state.ts
  • src/routes/admin/route.tsx

Comment thread convex/crm/metadataCompiler.ts
Comment thread convex/schema.ts
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces normalized “field” and “view” contracts for the CRM view engine, persists richer derived field metadata into fieldDefs, and expands getViewSchema to return these contracts (plus an adapter contract) to consumers.

Changes:

  • Add normalized field/view contract types + derivation helpers, and persist derived metadata during field creation/update and system bootstrap.
  • Extend getViewSchema to return full normalized field definitions, a structured system view definition (incl. filters/layout messages), and an adapter contract.
  • Add userSavedViews schema storage and update admin-shell/detail-sheet plumbing plus documentation/test updates.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/routes/admin/route.tsx Import ordering cleanup for admin route shell.
src/lib/admin-detail-route-state.ts Formatting adjustment for route-state helper signature.
src/components/admin/shell/RecordSidebarProvider.tsx Minor signature/type placement tidy-up.
src/components/admin/shell/AdminDetailSheet.tsx Make entityType optional and resolve from shared detail-sheet state.
convex/schema.ts Add normalized field contract fields to fieldDefs, expand viewDefs, and introduce userSavedViews.
convex/crm/viewQueries.ts Expand getViewSchema response to include adapter + normalized field/view contracts.
convex/crm/validators.ts Add validators for normalized contracts, layout/aggregation/editability metadata.
convex/crm/types.ts Add normalized contract TypeScript types for fields/views/adapters.
convex/crm/systemAdapters/bootstrap.ts Persist derived normalized field contract metadata during system bootstrap.
convex/crm/metadataCompiler.ts Introduce reusable derivation helpers for normalized field kind/rendering/layout/editability/aggregation.
convex/crm/fieldDefs.ts Persist derived normalized field contract metadata on create/update mutations.
convex/crm/tests/viewEngine.test.ts Update schema test expectations for the new getViewSchema contract shape.
convex/crm/tests/metadataCompiler.test.ts Add unit + integration assertions for derived/persisted contract metadata.
CLAUDE.md Update GitNexus index identifier/resource paths.
AGENTS.md Update GitNexus index identifier/resource paths and strengthen workflow guidance text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread convex/schema.ts
Comment thread convex/crm/fieldDefs.ts
Comment thread convex/crm/types.ts
Comment thread convex/crm/viewQueries.ts
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
convex/crm/fieldDefs.ts (1)

278-286: Redundant assignment of isVisibleByDefault.

Lines 278-280 conditionally set patch.isVisibleByDefault, but line 286 unconditionally overwrites it with effectiveFieldContract.isVisibleByDefault. Since both derive from the same logic, lines 278-280 have no effect and can be removed.

♻️ Proposed fix to remove dead code
 		if (args.computed !== undefined) {
 			patch.computed = args.computed;
 		}
-		if (args.isVisibleByDefault !== undefined) {
-			patch.isVisibleByDefault = args.isVisibleByDefault;
-		}
 		patch.normalizedFieldKind = effectiveFieldContract.normalizedFieldKind;
 		patch.rendererHint = effectiveFieldContract.rendererHint;
 		patch.layoutEligibility = effectiveFieldContract.layoutEligibility;
 		patch.aggregation = effectiveFieldContract.aggregation;
 		patch.editability = effectiveFieldContract.editability;
 		patch.isVisibleByDefault = effectiveFieldContract.isVisibleByDefault;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@convex/crm/fieldDefs.ts` around lines 278 - 286, Remove the redundant
conditional assignment to patch.isVisibleByDefault: currently the block that
checks args.isVisibleByDefault and sets patch.isVisibleByDefault is immediately
overwritten by the later unconditional assignment patch.isVisibleByDefault =
effectiveFieldContract.isVisibleByDefault; delete the conditional lines (the
args.isVisibleByDefault check and assignment) so patch.isVisibleByDefault is
only set from effectiveFieldContract.isVisibleByDefault in the code that updates
the patch object.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@convex/crm/fieldDefs.ts`:
- Around line 278-286: Remove the redundant conditional assignment to
patch.isVisibleByDefault: currently the block that checks
args.isVisibleByDefault and sets patch.isVisibleByDefault is immediately
overwritten by the later unconditional assignment patch.isVisibleByDefault =
effectiveFieldContract.isVisibleByDefault; delete the conditional lines (the
args.isVisibleByDefault check and assignment) so patch.isVisibleByDefault is
only set from effectiveFieldContract.isVisibleByDefault in the code that updates
the patch object.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6dc01467-1dec-4b41-a02a-e887bbd6e616

📥 Commits

Reviewing files that changed from the base of the PR and between f6d46c9 and f90c34f.

📒 Files selected for processing (4)
  • convex/crm/__tests__/viewEngine.test.ts
  • convex/crm/fieldDefs.ts
  • convex/crm/types.ts
  • convex/crm/viewQueries.ts
✅ Files skipped from review due to trivial changes (1)
  • convex/crm/types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • convex/crm/viewQueries.ts

Copy link
Copy Markdown
Owner Author

Connorbelez commented Apr 9, 2026

Merge activity

  • Apr 9, 6:12 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Apr 9, 6:12 PM UTC: @Connorbelez merged this pull request with Graphite.

@Connorbelez Connorbelez merged commit 0c391b1 into main Apr 9, 2026
1 of 3 checks passed
Connorbelez added a commit that referenced this pull request Apr 20, 2026
## Summary by Sourcery

Introduce a normalized field and view contract layer for the CRM view engine and persist this metadata on field and view definitions.

New Features:
- Expose normalized field definitions, system view definitions, and an entity view adapter contract from the getViewSchema endpoint.
- Add support for computed and relation-backed fields, including renderer hints, editability modes, layout eligibility, and aggregation metadata.
- Introduce per-view disabled layout messages and user-saved views storage to support richer layout and personalization features.

Bug Fixes:
- Ensure the admin detail sheet can resolve the active entity type from shared state when the prop is omitted, avoiding broken record links.

Enhancements:
- Centralize derivation of field capabilities into reusable metadata compiler helpers that compute normalized kind, renderer hint, layout eligibility, aggregation, and editability.
- Persist normalized field contract metadata on fieldDefs during creation, update, and system adapter bootstrap for consistent behavior across native and EAV-backed objects.
- Extend validators and schema definitions to cover new field, view, and relation metadata types and constraints, including enforcement that a field cannot be both relation-backed and computed.
- Enrich view schema columns with field contract properties so clients can drive UI behavior without additional lookups.
- Refine admin record sidebar and routing utilities with minor type and formatting cleanups.
- Update GitNexus documentation references to the current project index identifier and usage guidance.

Tests:
- Add unit tests for field contract derivation and extend view engine and metadata compiler integration tests to cover normalized field contract persistence and the expanded view schema payload.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

* **New Features**
  * Added support for saved views with customizable field visibility, ordering, and filtering
  * Enhanced field configuration with relation, computed field, and visibility settings
  * Introduced view grouping and aggregate presets for data summarization
  * Expanded view metadata to include layout eligibility and disabled layout messaging

* **Improvements**
  * Enriched view schema responses with comprehensive field and layout metadata
  * Improved admin detail sheet entity type handling for better navigation
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
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.

2 participants