Conversation
📝 WalkthroughWalkthroughIntroduces 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
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}
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Reviewer's GuideImplements 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 flowsequenceDiagram
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
ER diagram for CRM view metadata and userSavedViewserDiagram
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
Updated class diagram for CRM normalized field and view contractsclassDiagram
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
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
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>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
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
titleFieldNameandstatusFieldNameby 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 inisVisibleByDefaulthandling.In
updateField,isVisibleByDefaultis only patched when explicitly provided in args, while other derived fields (normalizedFieldKind,rendererHint, etc.) are always recomputed and patched.This differs from
createFieldwhereisVisibleByDefaultis 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
📒 Files selected for processing (15)
AGENTS.mdCLAUDE.mdconvex/crm/__tests__/metadataCompiler.test.tsconvex/crm/__tests__/viewEngine.test.tsconvex/crm/fieldDefs.tsconvex/crm/metadataCompiler.tsconvex/crm/systemAdapters/bootstrap.tsconvex/crm/types.tsconvex/crm/validators.tsconvex/crm/viewQueries.tsconvex/schema.tssrc/components/admin/shell/AdminDetailSheet.tsxsrc/components/admin/shell/RecordSidebarProvider.tsxsrc/lib/admin-detail-route-state.tssrc/routes/admin/route.tsx
There was a problem hiding this comment.
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
getViewSchemato return full normalized field definitions, a structured system view definition (incl. filters/layout messages), and an adapter contract. - Add
userSavedViewsschema 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.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
convex/crm/fieldDefs.ts (1)
278-286: Redundant assignment ofisVisibleByDefault.Lines 278-280 conditionally set
patch.isVisibleByDefault, but line 286 unconditionally overwrites it witheffectiveFieldContract.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
📒 Files selected for processing (4)
convex/crm/__tests__/viewEngine.test.tsconvex/crm/fieldDefs.tsconvex/crm/types.tsconvex/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
Merge activity
|
## 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 -->

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:
Bug Fixes:
Enhancements:
Tests:
Summary by CodeRabbit
New Features
Improvements