@happyvertical/smrt-fields 0.40.62 → 0.40.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -196,6 +196,24 @@ per-field `{defaultValue, visibility, help, label, order, locked}` for any
196
196
  - `policyToVisibleColumnIds(policy, columns)` feeds smrt-ui `DataTable`'s
197
197
  `visibleColumnIds`; it filters policy-hidden mapped fields, preserves unmapped
198
198
  computed/action columns, and cannot reveal a static `column.hidden` column.
199
+ - **Two-tier context contract.** `PolicyField` alone reads the context with
200
+ `tryGetFieldPolicyContext()`, because outside a Provider it still has the
201
+ caller's children to render verbatim (incremental adoption). The
202
+ provider-only compositions — `ModeSwitch`, `AdvancedFields`, `FormHelp` —
203
+ read it with the throwing `getFieldPolicyContext()`: their whole output is
204
+ derived from the resolved policy, so degrading would render an empty or
205
+ absent control instead of surfacing the missing Provider. Do not convert one
206
+ of them to the non-throwing accessor in isolation (#2272).
207
+ - **One provider per form.** `FieldPolicyProvider` renders only
208
+ `{@render children?.()}`, so a host wanting `FormHelp` in a page header
209
+ extends the SAME provider over the header rather than hoisting the component
210
+ out. It must not add a second: each provider constructs its own
211
+ `FieldPolicyModeStore` and `setContext` shadows for the subtree, so a
212
+ `FormHelp` above a nested provider stops tracking the `ModeSwitch` inside it.
213
+ `ObjectForm` builds its own provider, so never wrap it in another; its
214
+ `actions` snippet renders INSIDE that provider (context resolves at the render
215
+ site), which is the only way to reach `FormHelp` through it — below the
216
+ fields, since there is no header seam (#2289).
199
217
 
200
218
  ## Defaults control panel (#2050)
201
219
 
package/README.md CHANGED
@@ -148,6 +148,6 @@ for the capture, privacy, retention, and schedule contract.
148
148
 
149
149
  ## Example application
150
150
 
151
- The [SMRT SaaS starter field-policy walkthrough](https://github.com/happyvertical/smrt-saas-starter/pull/51)
152
- uses SMRT `0.40.61` and shows the owner/admin controls and the member-facing
151
+ The [s-m-r-t SaaS starter field-policy walkthrough](https://github.com/happyvertical/smrt-saas-starter/pull/51)
152
+ uses s-m-r-t `0.40.61` and shows the owner/admin controls and the member-facing
153
153
  personal form flow in a working application.
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { assertOperationPermission } from "@happyvertical/smrt-users";
5
5
  import { TenantIsolationError, assertTenantReadAllowed, getCurrentTenant, isSuperAdminBypass, tenantId } from "@happyvertical/smrt-tenancy";
6
6
  import { importWorkspaceModule } from "@happyvertical/smrt-core/utils/import-workspace-module";
7
7
  //#region src/__smrt-register__.ts
8
- ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-fields\",\"packageVersion\":\"0.40.62\",\"objects\":{\"@happyvertical/smrt-fields:FieldUsageLearningAgent\":{\"name\":\"fieldusagelearningagent\",\"className\":\"FieldUsageLearningAgent\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageLearningAgent\",\"collection\":\"fieldusagelearningagents\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/usage-learning.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"runUsageMaintenance\":{\"name\":\"runUsageMaintenance\",\"async\":true,\"parameters\":[{\"name\":\"args\",\"type\":\"Record<string>\",\"optional\":true}],\"returnType\":\"Promise<FieldUsageMaintenanceSummary>\",\"isStatic\":false,\"isPublic\":true},\"runSuggestionGeneration\":{\"name\":\"runSuggestionGeneration\",\"async\":true,\"parameters\":[{\"name\":\"args\",\"type\":\"Record<string>\",\"optional\":true}],\"returnType\":\"Promise<FieldUsageSuggestionRunSummary>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_usage_learning_agents\",\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldUsageLearningAgent\",\"collectionExportName\":\"FieldUsageLearningAgentCollection\",\"schema\":{\"tableName\":\"_smrt_field_usage_learning_agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_learning_agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_learning_agents_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_learning_agents_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true}],\"version\":\"183ee1d8\"}},\"@happyvertical/smrt-fields:FieldPolicyCollection\":{\"name\":\"fieldpolicycollection\",\"className\":\"FieldPolicyCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicyCollection\",\"collection\":\"fieldpolicies\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldPolicyCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"policyAudit\":{\"name\":\"policyAudit\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldPolicyAuditSnapshot>\",\"isStatic\":false,\"isPublic\":true},\"getAppRows\":{\"name\":\"getAppRows\",\"async\":true,\"parameters\":[{\"name\":\"objectRef\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<Map<string, FieldPolicy>>\",\"isStatic\":false,\"isPublic\":true},\"getTenantRows\":{\"name\":\"getTenantRows\",\"async\":true,\"parameters\":[{\"name\":\"objectRef\",\"type\":\"string\",\"optional\":false},{\"name\":\"tenantIds\",\"type\":\"string[]\",\"optional\":false}],\"returnType\":\"Promise<Map<string, Map<string, FieldPolicy>>>\",\"isStatic\":false,\"isPublic\":true},\"getUserRows\":{\"name\":\"getUserRows\",\"async\":true,\"parameters\":[{\"name\":\"objectRef\",\"type\":\"string\",\"optional\":false},{\"name\":\"userId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<Map<string, FieldPolicy>>\",\"isStatic\":false,\"isPublic\":true},\"resolveBatch\":{\"name\":\"resolveBatch\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldPolicyBatchResult>\",\"isStatic\":false,\"isPublic\":true},\"getEditorState\":{\"name\":\"getEditorState\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldPolicyEditorStateResult>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"api\":{\"include\":[\"create\",\"update\",\"delete\",\"resolveBatch\",\"getEditorState\",\"policyAudit\"],\"principalContext\":true,\"routes\":{\"resolveBatch\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"resolve\"},\"getEditorState\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"editor-state\"},\"policyAudit\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"policy-audit\"}}},\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_policies\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldPolicy\",\"exportName\":\"FieldPolicyCollection\",\"collectionExportName\":\"FieldPolicyCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_policies\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policies\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_policies_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policies_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"unique\":true}],\"version\":\"13aa748c\"}},\"@happyvertical/smrt-fields:FieldPolicySuggestionCollection\":{\"name\":\"fieldpolicysuggestioncollection\",\"className\":\"FieldPolicySuggestionCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicySuggestionCollection\",\"collection\":\"fieldpolicysuggestions\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldPolicySuggestionCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"pendingSuggestions\":{\"name\":\"pendingSuggestions\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<PendingFieldPolicySuggestionsResult>\",\"isStatic\":false,\"isPublic\":true},\"acceptSuggestion\":{\"name\":\"acceptSuggestion\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<AcceptFieldPolicySuggestionResult>\",\"isStatic\":false,\"isPublic\":true},\"dismissSuggestion\":{\"name\":\"dismissSuggestion\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<DismissFieldPolicySuggestionResult>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"api\":{\"include\":[\"pendingSuggestions\",\"acceptSuggestion\",\"dismissSuggestion\"],\"principalContext\":true,\"routes\":{\"pendingSuggestions\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"pending\"},\"acceptSuggestion\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"accept\"},\"dismissSuggestion\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"dismiss\"}}},\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_policy_suggestions\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldPolicySuggestion\",\"exportName\":\"FieldPolicySuggestionCollection\",\"collectionExportName\":\"FieldPolicySuggestionCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_policy_suggestions\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policy_suggestions\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_policy_suggestions_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policy_suggestions_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"unique\":true}],\"version\":\"853df86d\"}},\"@happyvertical/smrt-fields:FieldUsageCounterCollection\":{\"name\":\"fieldusagecountercollection\",\"className\":\"FieldUsageCounterCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageCounterCollection\",\"collection\":\"fieldusagecounters\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldUsageCounterCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"reportUsage\":{\"name\":\"reportUsage\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldUsageReportResult>\",\"isStatic\":false,\"isPublic\":true},\"listWindow\":{\"name\":\"listWindow\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":false}],\"returnType\":\"Promise<FieldUsageCounter[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"api\":{\"include\":[\"reportUsage\"],\"principalContext\":true,\"routes\":{\"reportUsage\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"report\"}}},\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_usage_counters\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldUsageCounter\",\"exportName\":\"FieldUsageCounterCollection\",\"collectionExportName\":\"FieldUsageCounterCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_usage_counters\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_counters\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_counters_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_counters_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"unique\":true}],\"version\":\"8bd9beae\"}},\"@happyvertical/smrt-fields:FieldUsageReportReceiptCollection\":{\"name\":\"fieldusagereportreceiptcollection\",\"className\":\"FieldUsageReportReceiptCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageReportReceiptCollection\",\"collection\":\"fieldusagereportreceipts\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldUsageCounterCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"claim\":{\"name\":\"claim\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":false}],\"returnType\":\"Promise<boolean>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"api\":false,\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_usage_report_receipts\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldUsageReportReceipt\",\"exportName\":\"FieldUsageReportReceiptCollection\",\"collectionExportName\":\"FieldUsageReportReceiptCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_usage_report_receipts\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_report_receipts\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_report_receipts_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_report_receipts_tenant_id_user_id_idx\",\"columns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"unique\":true}],\"version\":\"2e77b775\"}},\"@happyvertical/smrt-fields:FieldPolicy\":{\"name\":\"fieldpolicy\",\"className\":\"FieldPolicy\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicy\",\"collection\":\"fieldpolicies\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldPolicy.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"scopeType\":{\"type\":\"text\",\"required\":true,\"default\":\"app\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true}}},\"userId\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\",\"_meta\":{\"nullable\":true}},\"scopeKey\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"defaultValue\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"visibility\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"help\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"label\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"displayOrder\":{\"type\":\"integer\",\"required\":false,\"_meta\":{\"nullable\":true}},\"locked\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{\"nullable\":true}},\"updatedBy\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\",\"_meta\":{\"nullable\":true}}},\"methods\":{\"getDefaultValue\":{\"name\":\"getDefaultValue\",\"async\":false,\"parameters\":[],\"returnType\":\"any\",\"isStatic\":false,\"isPublic\":true},\"setDefaultValue\":{\"name\":\"setDefaultValue\",\"async\":false,\"parameters\":[{\"name\":\"value\",\"type\":\"any\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_policies\",\"conflictColumns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"api\":{\"include\":[\"create\",\"update\",\"delete\"],\"principalContext\":true},\"cli\":{\"include\":[\"create\",\"update\",\"delete\"],\"exclude\":[\"getDefaultValue\",\"setDefaultValue\"]},\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldPolicy\",\"collectionExportName\":\"FieldPolicyCollection\",\"validationRules\":[{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"scopeType\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"scopeKey\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_policies\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policies\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"scope_type\\\" TEXT NOT NULL DEFAULT 'app',\\n \\\"tenant_id\\\" UUID,\\n \\\"user_id\\\" UUID,\\n \\\"scope_key\\\" TEXT NOT NULL,\\n \\\"default_value\\\" TEXT,\\n \\\"visibility\\\" TEXT,\\n \\\"help\\\" TEXT,\\n \\\"label\\\" TEXT,\\n \\\"display_order\\\" INTEGER,\\n \\\"locked\\\" BOOLEAN,\\n \\\"updated_by\\\" UUID\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"scope_type\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"app\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"user_id\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false},\"scope_key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"default_value\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"visibility\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"help\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"label\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"display_order\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"locked\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"updated_by\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_field_policies_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policies_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"unique\":true}],\"version\":\"34b355cc\"}},\"@happyvertical/smrt-fields:FieldPolicySuggestion\":{\"name\":\"fieldpolicysuggestion\",\"className\":\"FieldPolicySuggestion\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicySuggestion\",\"collection\":\"fieldpolicysuggestions\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldPolicySuggestion.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false}}},\"kind\":{\"type\":\"text\",\"required\":true,\"default\":\"promote\",\"_meta\":{\"required\":true}},\"proposedValue\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"evidence\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"status\":{\"type\":\"text\",\"required\":true,\"default\":\"pending\",\"_meta\":{\"required\":true}},\"activeKey\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"cooldownUntil\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}},\"decidedBy\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\",\"_meta\":{\"nullable\":true}},\"decidedAt\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{\"getEvidence\":{\"name\":\"getEvidence\",\"async\":false,\"parameters\":[],\"returnType\":\"Record<string>\",\"isStatic\":false,\"isPublic\":true},\"setEvidence\":{\"name\":\"setEvidence\",\"async\":false,\"parameters\":[{\"name\":\"evidence\",\"type\":\"Record<string>\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getProposedValue\":{\"name\":\"getProposedValue\",\"async\":false,\"parameters\":[],\"returnType\":\"any\",\"isStatic\":false,\"isPublic\":true},\"toSuggestionData\":{\"name\":\"toSuggestionData\",\"async\":false,\"parameters\":[],\"returnType\":\"FieldPolicySuggestionData\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_policy_suggestions\",\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldPolicySuggestion\",\"collectionExportName\":\"FieldPolicySuggestionCollection\",\"validationRules\":[{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"tenantId\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"kind\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"status\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"activeKey\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_policy_suggestions\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policy_suggestions\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"kind\\\" TEXT NOT NULL DEFAULT 'promote',\\n \\\"proposed_value\\\" TEXT,\\n \\\"evidence\\\" TEXT,\\n \\\"status\\\" TEXT NOT NULL DEFAULT 'pending',\\n \\\"active_key\\\" TEXT NOT NULL,\\n \\\"cooldown_until\\\" TIMESTAMP,\\n \\\"decided_by\\\" UUID,\\n \\\"decided_at\\\" TIMESTAMP\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"kind\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"promote\"},\"proposed_value\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"evidence\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"pending\"},\"active_key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"cooldown_until\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"decided_by\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false},\"decided_at\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_field_policy_suggestions_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policy_suggestions_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"unique\":true}],\"version\":\"1bc8c6e5\"}},\"@happyvertical/smrt-fields:FieldUsageCounter\":{\"name\":\"fieldusagecounter\",\"className\":\"FieldUsageCounter\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageCounter\",\"collection\":\"fieldusagecounters\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldUsageCounter.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false}}},\"period\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"submissionCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"setCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"distinctUserCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"distinctUserIds\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"distinctUsersOverflowed\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{}},\"valueHistogram\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"valueHistogramOverflowed\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{}}},\"methods\":{\"getDistinctUserIds\":{\"name\":\"getDistinctUserIds\",\"async\":false,\"parameters\":[],\"returnType\":\"string[]\",\"isStatic\":false,\"isPublic\":true},\"addDistinctUser\":{\"name\":\"addDistinctUser\",\"async\":false,\"parameters\":[{\"name\":\"userId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getValueHistogram\":{\"name\":\"getValueHistogram\",\"async\":false,\"parameters\":[],\"returnType\":\"Record<string, number>\",\"isStatic\":false,\"isPublic\":true},\"recordHistogramSample\":{\"name\":\"recordHistogramSample\",\"async\":false,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"isLegacyBucket\":{\"name\":\"isLegacyBucket\",\"async\":false,\"parameters\":[],\"returnType\":\"boolean\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_usage_counters\",\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldUsageCounter\",\"collectionExportName\":\"FieldUsageCounterCollection\",\"validationRules\":[{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"tenantId\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"period\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_usage_counters\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_counters\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"period\\\" TEXT NOT NULL DEFAULT '',\\n \\\"submission_count\\\" INTEGER,\\n \\\"set_count\\\" INTEGER,\\n \\\"distinct_user_count\\\" INTEGER,\\n \\\"distinct_user_ids\\\" TEXT,\\n \\\"distinct_users_overflowed\\\" BOOLEAN,\\n \\\"value_histogram\\\" TEXT,\\n \\\"value_histogram_overflowed\\\" BOOLEAN\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"period\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"submission_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"set_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"distinct_user_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"distinct_user_ids\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"distinct_users_overflowed\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"value_histogram\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"value_histogram_overflowed\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_field_usage_counters_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_counters_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"unique\":true}],\"version\":\"46fcebc6\"}},\"@happyvertical/smrt-fields:FieldUsageReportReceipt\":{\"name\":\"fieldusagereportreceipt\",\"className\":\"FieldUsageReportReceipt\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageReportReceipt\",\"collection\":\"fieldusagereportreceipts\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldUsageReportReceipt.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false}}},\"userId\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\"},\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"period\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"_smrt_field_usage_report_receipts\",\"conflictColumns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldUsageReportReceipt\",\"collectionExportName\":\"FieldUsageReportReceiptCollection\",\"validationRules\":[{\"field\":\"tenantId\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"period\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_usage_report_receipts\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_report_receipts\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"user_id\\\" UUID,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"period\\\" TEXT NOT NULL DEFAULT ''\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"user_id\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"period\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_report_receipts_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_report_receipts_tenant_id_user_id_idx\",\"columns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"unique\":true}],\"version\":\"c3119047\"}}},\"moduleType\":\"smrt\",\"smrtDependencies\":[\"@happyvertical/smrt-core\",\"@happyvertical/smrt-tenancy\",\"@happyvertical/smrt-users\"]}"));
8
+ ObjectRegistry.registerPackageManifest(JSON.parse("{\"version\":\"1.0.0\",\"timestamp\":0,\"packageName\":\"@happyvertical/smrt-fields\",\"packageVersion\":\"0.40.63\",\"objects\":{\"@happyvertical/smrt-fields:FieldUsageLearningAgent\":{\"name\":\"fieldusagelearningagent\",\"className\":\"FieldUsageLearningAgent\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageLearningAgent\",\"collection\":\"fieldusagelearningagents\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/usage-learning.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"runUsageMaintenance\":{\"name\":\"runUsageMaintenance\",\"async\":true,\"parameters\":[{\"name\":\"args\",\"type\":\"Record<string>\",\"optional\":true}],\"returnType\":\"Promise<FieldUsageMaintenanceSummary>\",\"isStatic\":false,\"isPublic\":true},\"runSuggestionGeneration\":{\"name\":\"runSuggestionGeneration\",\"async\":true,\"parameters\":[{\"name\":\"args\",\"type\":\"Record<string>\",\"optional\":true}],\"returnType\":\"Promise<FieldUsageSuggestionRunSummary>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_usage_learning_agents\",\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldUsageLearningAgent\",\"collectionExportName\":\"FieldUsageLearningAgentCollection\",\"schema\":{\"tableName\":\"_smrt_field_usage_learning_agents\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_learning_agents\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_learning_agents_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_learning_agents_slug_context_idx\",\"columns\":[\"slug\",\"context\"],\"unique\":true}],\"version\":\"183ee1d8\"}},\"@happyvertical/smrt-fields:FieldPolicyCollection\":{\"name\":\"fieldpolicycollection\",\"className\":\"FieldPolicyCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicyCollection\",\"collection\":\"fieldpolicies\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldPolicyCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"policyAudit\":{\"name\":\"policyAudit\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldPolicyAuditSnapshot>\",\"isStatic\":false,\"isPublic\":true},\"getAppRows\":{\"name\":\"getAppRows\",\"async\":true,\"parameters\":[{\"name\":\"objectRef\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<Map<string, FieldPolicy>>\",\"isStatic\":false,\"isPublic\":true},\"getTenantRows\":{\"name\":\"getTenantRows\",\"async\":true,\"parameters\":[{\"name\":\"objectRef\",\"type\":\"string\",\"optional\":false},{\"name\":\"tenantIds\",\"type\":\"string[]\",\"optional\":false}],\"returnType\":\"Promise<Map<string, Map<string, FieldPolicy>>>\",\"isStatic\":false,\"isPublic\":true},\"getUserRows\":{\"name\":\"getUserRows\",\"async\":true,\"parameters\":[{\"name\":\"objectRef\",\"type\":\"string\",\"optional\":false},{\"name\":\"userId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"Promise<Map<string, FieldPolicy>>\",\"isStatic\":false,\"isPublic\":true},\"resolveBatch\":{\"name\":\"resolveBatch\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldPolicyBatchResult>\",\"isStatic\":false,\"isPublic\":true},\"getEditorState\":{\"name\":\"getEditorState\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldPolicyEditorStateResult>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"api\":{\"include\":[\"create\",\"update\",\"delete\",\"resolveBatch\",\"getEditorState\",\"policyAudit\"],\"principalContext\":true,\"routes\":{\"resolveBatch\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"resolve\"},\"getEditorState\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"editor-state\"},\"policyAudit\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"policy-audit\"}}},\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_policies\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldPolicy\",\"exportName\":\"FieldPolicyCollection\",\"collectionExportName\":\"FieldPolicyCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_policies\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policies\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_policies_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policies_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"unique\":true}],\"version\":\"13aa748c\"}},\"@happyvertical/smrt-fields:FieldPolicySuggestionCollection\":{\"name\":\"fieldpolicysuggestioncollection\",\"className\":\"FieldPolicySuggestionCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicySuggestionCollection\",\"collection\":\"fieldpolicysuggestions\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldPolicySuggestionCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"pendingSuggestions\":{\"name\":\"pendingSuggestions\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<PendingFieldPolicySuggestionsResult>\",\"isStatic\":false,\"isPublic\":true},\"acceptSuggestion\":{\"name\":\"acceptSuggestion\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<AcceptFieldPolicySuggestionResult>\",\"isStatic\":false,\"isPublic\":true},\"dismissSuggestion\":{\"name\":\"dismissSuggestion\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<DismissFieldPolicySuggestionResult>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"api\":{\"include\":[\"pendingSuggestions\",\"acceptSuggestion\",\"dismissSuggestion\"],\"principalContext\":true,\"routes\":{\"pendingSuggestions\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"pending\"},\"acceptSuggestion\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"accept\"},\"dismissSuggestion\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"dismiss\"}}},\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_policy_suggestions\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldPolicySuggestion\",\"exportName\":\"FieldPolicySuggestionCollection\",\"collectionExportName\":\"FieldPolicySuggestionCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_policy_suggestions\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policy_suggestions\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_policy_suggestions_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policy_suggestions_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"unique\":true}],\"version\":\"853df86d\"}},\"@happyvertical/smrt-fields:FieldUsageCounterCollection\":{\"name\":\"fieldusagecountercollection\",\"className\":\"FieldUsageCounterCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageCounterCollection\",\"collection\":\"fieldusagecounters\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldUsageCounterCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"reportUsage\":{\"name\":\"reportUsage\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":true}],\"returnType\":\"Promise<FieldUsageReportResult>\",\"isStatic\":false,\"isPublic\":true},\"listWindow\":{\"name\":\"listWindow\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":false}],\"returnType\":\"Promise<FieldUsageCounter[]>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"api\":{\"include\":[\"reportUsage\"],\"principalContext\":true,\"routes\":{\"reportUsage\":{\"scope\":\"collection\",\"method\":\"POST\",\"path\":\"report\"}}},\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_usage_counters\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldUsageCounter\",\"exportName\":\"FieldUsageCounterCollection\",\"collectionExportName\":\"FieldUsageCounterCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_usage_counters\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_counters\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_counters_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_counters_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"unique\":true}],\"version\":\"8bd9beae\"}},\"@happyvertical/smrt-fields:FieldUsageReportReceiptCollection\":{\"name\":\"fieldusagereportreceiptcollection\",\"className\":\"FieldUsageReportReceiptCollection\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageReportReceiptCollection\",\"collection\":\"fieldusagereportreceipts\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/collections/FieldUsageCounterCollection.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{},\"methods\":{\"claim\":{\"name\":\"claim\",\"async\":true,\"parameters\":[{\"name\":\"options\",\"type\":\"object\",\"optional\":false}],\"returnType\":\"Promise<boolean>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"conflictColumns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"api\":false,\"cli\":false,\"mcp\":false,\"tableName\":\"_smrt_field_usage_report_receipts\"},\"extends\":\"SmrtCollection\",\"extendsTypeArg\":\"FieldUsageReportReceipt\",\"exportName\":\"FieldUsageReportReceiptCollection\",\"collectionExportName\":\"FieldUsageReportReceiptCollectionCollection\",\"schema\":{\"tableName\":\"_smrt_field_usage_report_receipts\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_report_receipts\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_report_receipts_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_report_receipts_tenant_id_user_id_idx\",\"columns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"unique\":true}],\"version\":\"2e77b775\"}},\"@happyvertical/smrt-fields:FieldPolicy\":{\"name\":\"fieldpolicy\",\"className\":\"FieldPolicy\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicy\",\"collection\":\"fieldpolicies\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldPolicy.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"scopeType\":{\"type\":\"text\",\"required\":true,\"default\":\"app\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"sqlType\":\"UUID\",\"nullable\":true,\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":false,\"autoPopulate\":true,\"nullable\":true}}},\"userId\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\",\"_meta\":{\"nullable\":true}},\"scopeKey\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"defaultValue\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"visibility\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"help\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"label\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"displayOrder\":{\"type\":\"integer\",\"required\":false,\"_meta\":{\"nullable\":true}},\"locked\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{\"nullable\":true}},\"updatedBy\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\",\"_meta\":{\"nullable\":true}}},\"methods\":{\"getDefaultValue\":{\"name\":\"getDefaultValue\",\"async\":false,\"parameters\":[],\"returnType\":\"any\",\"isStatic\":false,\"isPublic\":true},\"setDefaultValue\":{\"name\":\"setDefaultValue\",\"async\":false,\"parameters\":[{\"name\":\"value\",\"type\":\"any\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_policies\",\"conflictColumns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"api\":{\"include\":[\"create\",\"update\",\"delete\"],\"principalContext\":true},\"cli\":{\"include\":[\"create\",\"update\",\"delete\"],\"exclude\":[\"getDefaultValue\",\"setDefaultValue\"]},\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldPolicy\",\"collectionExportName\":\"FieldPolicyCollection\",\"validationRules\":[{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"scopeType\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"scopeKey\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_policies\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policies\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"scope_type\\\" TEXT NOT NULL DEFAULT 'app',\\n \\\"tenant_id\\\" UUID,\\n \\\"user_id\\\" UUID,\\n \\\"scope_key\\\" TEXT NOT NULL,\\n \\\"default_value\\\" TEXT,\\n \\\"visibility\\\" TEXT,\\n \\\"help\\\" TEXT,\\n \\\"label\\\" TEXT,\\n \\\"display_order\\\" INTEGER,\\n \\\"locked\\\" BOOLEAN,\\n \\\"updated_by\\\" UUID\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"scope_type\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"app\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":false,\"unique\":false},\"user_id\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false},\"scope_key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"default_value\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"visibility\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"help\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"label\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"display_order\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"locked\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"updated_by\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_field_policies_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policies_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"scope_type\",\"scope_key\"],\"unique\":true}],\"version\":\"34b355cc\"}},\"@happyvertical/smrt-fields:FieldPolicySuggestion\":{\"name\":\"fieldpolicysuggestion\",\"className\":\"FieldPolicySuggestion\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldPolicySuggestion\",\"collection\":\"fieldpolicysuggestions\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldPolicySuggestion.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false}}},\"kind\":{\"type\":\"text\",\"required\":true,\"default\":\"promote\",\"_meta\":{\"required\":true}},\"proposedValue\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"evidence\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"status\":{\"type\":\"text\",\"required\":true,\"default\":\"pending\",\"_meta\":{\"required\":true}},\"activeKey\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"required\":true}},\"cooldownUntil\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}},\"decidedBy\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\",\"_meta\":{\"nullable\":true}},\"decidedAt\":{\"type\":\"datetime\",\"required\":false,\"_meta\":{\"nullable\":true}}},\"methods\":{\"getEvidence\":{\"name\":\"getEvidence\",\"async\":false,\"parameters\":[],\"returnType\":\"Record<string>\",\"isStatic\":false,\"isPublic\":true},\"setEvidence\":{\"name\":\"setEvidence\",\"async\":false,\"parameters\":[{\"name\":\"evidence\",\"type\":\"Record<string>\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getProposedValue\":{\"name\":\"getProposedValue\",\"async\":false,\"parameters\":[],\"returnType\":\"any\",\"isStatic\":false,\"isPublic\":true},\"toSuggestionData\":{\"name\":\"toSuggestionData\",\"async\":false,\"parameters\":[],\"returnType\":\"FieldPolicySuggestionData\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_policy_suggestions\",\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldPolicySuggestion\",\"collectionExportName\":\"FieldPolicySuggestionCollection\",\"validationRules\":[{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"tenantId\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"kind\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"status\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"activeKey\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_policy_suggestions\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_policy_suggestions\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"kind\\\" TEXT NOT NULL DEFAULT 'promote',\\n \\\"proposed_value\\\" TEXT,\\n \\\"evidence\\\" TEXT,\\n \\\"status\\\" TEXT NOT NULL DEFAULT 'pending',\\n \\\"active_key\\\" TEXT NOT NULL,\\n \\\"cooldown_until\\\" TIMESTAMP,\\n \\\"decided_by\\\" UUID,\\n \\\"decided_at\\\" TIMESTAMP\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"kind\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"promote\"},\"proposed_value\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"evidence\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"status\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"pending\"},\"active_key\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false},\"cooldown_until\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false},\"decided_by\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false},\"decided_at\":{\"type\":\"TIMESTAMP\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_field_policy_suggestions_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_policy_suggestions_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"kind\",\"active_key\"],\"unique\":true}],\"version\":\"1bc8c6e5\"}},\"@happyvertical/smrt-fields:FieldUsageCounter\":{\"name\":\"fieldusagecounter\",\"className\":\"FieldUsageCounter\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageCounter\",\"collection\":\"fieldusagecounters\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldUsageCounter.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false}}},\"period\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"submissionCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"setCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"distinctUserCount\":{\"type\":\"integer\",\"required\":false,\"_meta\":{}},\"distinctUserIds\":{\"type\":\"text\",\"required\":false,\"_meta\":{}},\"distinctUsersOverflowed\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{}},\"valueHistogram\":{\"type\":\"text\",\"required\":false,\"_meta\":{\"nullable\":true}},\"valueHistogramOverflowed\":{\"type\":\"boolean\",\"required\":false,\"_meta\":{}}},\"methods\":{\"getDistinctUserIds\":{\"name\":\"getDistinctUserIds\",\"async\":false,\"parameters\":[],\"returnType\":\"string[]\",\"isStatic\":false,\"isPublic\":true},\"addDistinctUser\":{\"name\":\"addDistinctUser\",\"async\":false,\"parameters\":[{\"name\":\"userId\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"getValueHistogram\":{\"name\":\"getValueHistogram\",\"async\":false,\"parameters\":[],\"returnType\":\"Record<string, number>\",\"isStatic\":false,\"isPublic\":true},\"recordHistogramSample\":{\"name\":\"recordHistogramSample\",\"async\":false,\"parameters\":[{\"name\":\"key\",\"type\":\"string\",\"optional\":false}],\"returnType\":\"void\",\"isStatic\":false,\"isPublic\":true},\"isLegacyBucket\":{\"name\":\"isLegacyBucket\",\"async\":false,\"parameters\":[],\"returnType\":\"boolean\",\"isStatic\":false,\"isPublic\":true},\"save\":{\"name\":\"save\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise\",\"isStatic\":false,\"isPublic\":true},\"delete\":{\"name\":\"delete\",\"async\":true,\"parameters\":[],\"returnType\":\"Promise<void>\",\"isStatic\":false,\"isPublic\":true}},\"decoratorConfig\":{\"tableName\":\"_smrt_field_usage_counters\",\"conflictColumns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldUsageCounter\",\"collectionExportName\":\"FieldUsageCounterCollection\",\"validationRules\":[{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"tenantId\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"period\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_usage_counters\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_counters\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"period\\\" TEXT NOT NULL DEFAULT '',\\n \\\"submission_count\\\" INTEGER,\\n \\\"set_count\\\" INTEGER,\\n \\\"distinct_user_count\\\" INTEGER,\\n \\\"distinct_user_ids\\\" TEXT,\\n \\\"distinct_users_overflowed\\\" BOOLEAN,\\n \\\"value_histogram\\\" TEXT,\\n \\\"value_histogram_overflowed\\\" BOOLEAN\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"period\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"submission_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"set_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"distinct_user_count\":{\"type\":\"INTEGER\",\"notNull\":false,\"unique\":false},\"distinct_user_ids\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"distinct_users_overflowed\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false},\"value_histogram\":{\"type\":\"TEXT\",\"notNull\":false,\"unique\":false},\"value_histogram_overflowed\":{\"type\":\"BOOLEAN\",\"notNull\":false,\"unique\":false}},\"indexes\":[{\"name\":\"_smrt_field_usage_counters_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_counters_object_ref_field_name_idx\",\"columns\":[\"object_ref\",\"field_name\",\"tenant_id\",\"period\"],\"unique\":true}],\"version\":\"46fcebc6\"}},\"@happyvertical/smrt-fields:FieldUsageReportReceipt\":{\"name\":\"fieldusagereportreceipt\",\"className\":\"FieldUsageReportReceipt\",\"qualifiedName\":\"@happyvertical/smrt-fields:FieldUsageReportReceipt\",\"collection\":\"fieldusagereportreceipts\",\"filePath\":\"/home/runner/_work/smrt/smrt/packages/fields/src/models/FieldUsageReportReceipt.ts\",\"packageName\":\"@happyvertical/smrt-fields\",\"fields\":{\"tenantId\":{\"type\":\"text\",\"required\":true,\"_meta\":{\"sqlType\":\"UUID\",\"__tenancy\":{\"isTenantIdField\":true,\"autoFilter\":true,\"required\":true,\"autoPopulate\":true,\"nullable\":false}}},\"userId\":{\"type\":\"crossPackageRef\",\"required\":false,\"related\":\"@happyvertical/smrt-users:User\"},\"objectRef\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"fieldName\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}},\"period\":{\"type\":\"text\",\"required\":true,\"default\":\"\",\"_meta\":{\"required\":true}}},\"methods\":{},\"decoratorConfig\":{\"tableName\":\"_smrt_field_usage_report_receipts\",\"conflictColumns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"api\":{\"include\":[]},\"cli\":false,\"mcp\":{\"include\":[]}},\"extends\":\"SmrtObject\",\"exportName\":\"FieldUsageReportReceipt\",\"collectionExportName\":\"FieldUsageReportReceiptCollection\",\"validationRules\":[{\"field\":\"tenantId\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"objectRef\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"fieldName\",\"rule\":\"required\",\"fieldType\":\"text\"},{\"field\":\"period\",\"rule\":\"required\",\"fieldType\":\"text\"}],\"schema\":{\"tableName\":\"_smrt_field_usage_report_receipts\",\"ddl\":\"CREATE TABLE IF NOT EXISTS \\\"_smrt_field_usage_report_receipts\\\" (\\n \\\"id\\\" UUID PRIMARY KEY NOT NULL,\\n \\\"slug\\\" TEXT NOT NULL,\\n \\\"context\\\" TEXT NOT NULL DEFAULT '',\\n \\\"created_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"updated_at\\\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\\n \\\"tenant_id\\\" UUID NOT NULL,\\n \\\"user_id\\\" UUID,\\n \\\"object_ref\\\" TEXT NOT NULL DEFAULT '',\\n \\\"field_name\\\" TEXT NOT NULL DEFAULT '',\\n \\\"period\\\" TEXT NOT NULL DEFAULT ''\\n);\",\"columns\":{\"id\":{\"type\":\"UUID\",\"primaryKey\":true,\"referenceKind\":\"id\",\"notNull\":true},\"slug\":{\"type\":\"TEXT\",\"notNull\":true},\"context\":{\"type\":\"TEXT\",\"notNull\":true,\"default\":\"\"},\"created_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"updated_at\":{\"type\":\"TIMESTAMP\",\"notNull\":true,\"default\":\"current_timestamp\"},\"tenant_id\":{\"type\":\"UUID\",\"referenceKind\":\"tenantId\",\"notNull\":true,\"unique\":false},\"user_id\":{\"type\":\"UUID\",\"referenceKind\":\"crossPackageRef\",\"notNull\":false,\"unique\":false},\"object_ref\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"field_name\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"},\"period\":{\"type\":\"TEXT\",\"notNull\":true,\"unique\":false,\"default\":\"\"}},\"indexes\":[{\"name\":\"_smrt_field_usage_report_receipts_id_idx\",\"columns\":[\"id\"]},{\"name\":\"_smrt_field_usage_report_receipts_tenant_id_user_id_idx\",\"columns\":[\"tenant_id\",\"user_id\",\"object_ref\",\"field_name\",\"period\"],\"unique\":true}],\"version\":\"c3119047\"}}},\"moduleType\":\"smrt\",\"smrtDependencies\":[\"@happyvertical/smrt-core\",\"@happyvertical/smrt-tenancy\",\"@happyvertical/smrt-users\"]}"));
9
9
  //#endregion
10
10
  //#region src/deterministic-id.ts
11
11
  async function deterministicFieldsUuid(parts) {
@@ -2,7 +2,7 @@
2
2
  "version": "1.0.0",
3
3
  "timestamp": 0,
4
4
  "packageName": "@happyvertical/smrt-fields",
5
- "packageVersion": "0.40.62",
5
+ "packageVersion": "0.40.63",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-fields:FieldUsageLearningAgent": {
8
8
  "name": "fieldusagelearningagent",
@@ -2,13 +2,13 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedAt": "1970-01-01T00:00:00.000Z",
4
4
  "packageName": "@happyvertical/smrt-fields",
5
- "packageVersion": "0.40.62",
5
+ "packageVersion": "0.40.63",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "eb2aa4ac80249c876753a5418120cdf2ee70ee832c845fb189f53493c686aff3",
10
- "packageJson": "f1419cf86a24912c88a618cb45a881a86ea346b965dc01dd8cc4bcfae7703053",
11
- "agents": "6e3f776e4a585c5d21f0d9168687bf2eafefe17508c69a1c8870e9034ac2ec72"
9
+ "manifest": "55b4b82a66a8e2900ef68f632baab0109cb922218d09397ae6eacec84cdc83c8",
10
+ "packageJson": "7cb75c639de455602d2d589902a5b031a08d9f4a27eed0e0e1a052025666d868",
11
+ "agents": "1d1e0a7d8c1fa0ff5b14aa1b3bda3aef7eb8812e6e7ecb9b5980c66baafd7429"
12
12
  },
13
13
  "exports": [
14
14
  ".",
@@ -783,5 +783,5 @@
783
783
  "polymorphicAssociations": 0,
784
784
  "uuidColumns": 17
785
785
  },
786
- "agentDoc": "# smrt-fields\n\nLayered field policy store and resolution engine (epic #2045). Personalizes\nper-field `{defaultValue, visibility, help, label, order, locked}` for any\n`@smrt()` object at app, tenant, and user scope over the code seed.\n\n## Core pieces\n\n- `FieldPolicy` (`_smrt_field_policies` table) — sparse override rows keyed\n `(objectRef, fieldName, scopeType, scopeKey)`; NULL column = inherit from\n the lower layer, reset = row DELETE (later lower-layer changes flow through)\n- `FieldPolicyCollection` — write surface plus the `resolveBatch` custom\n collection-scoped action (`POST /<collection>/resolve`). NO generated\n surface exposes read verbs: API `list`/`get` on this non-tenant-scoped\n model would enumerate every tenant's and user's rows; the model's CLI is\n writes-only (the generated CLI invokes over HTTP, and the cli↔api\n coherence gate rejects CLI entries without API routes); the runtime\n CLI/MCP surfaces are closed by the collection config (ContentContributions\n precedent). Reads go through `resolveBatch` (context-scoped) or the\n server-side resolver. Keep the api include lists in lockstep: a decorated\n collection's config is the RUNTIME registry authority for its item class,\n while build-time generation reads each manifest object's own config. Both\n transports dispatch the action: generated SvelteKit routes natively, and\n core's runtime `APIGenerator` via its decorator-route dispatch\n (single-segment collection-scoped paths; multi-segment custom paths remain\n SvelteKit-only).\n- `resolveFieldPolicy(objectRef, { tenantId, userId, db })` — merged policy\n- `resolveFieldPolicyExplained(...)` — merged policy plus ordered per-layer\n contributions (code/app/tenant/user) for gear/admin UIs (#2049/#2050)\n\n## Resolution layers (priority low → high)\n\n1. Code seed — manifest field defaults, `description` as help, `_meta.ui`\n hints (#2046: `basic`/`group`/`order`/`locked`; cold-start rule: no\n `basic` markers ⇒ everything basic, any marker ⇒ unmarked fields advanced)\n2. App rows — `scopeType: 'app'`, `tenantId`/`userId` null\n3. Tenant rows — hierarchy walk root → leaf via an injected\n `tenantHierarchyLoader` (smrt-features shape); the default loader\n uses `@happyvertical/smrt-users`' `TenantCollection`; an injected loader\n may return no provider (or no chain for a tenant), which falls back to a\n flat single-tenant chain. `smrt-users` itself is a required Fields runtime\n dependency for policy authorization, not an optional hierarchy dependency.\n A node that breaks permission\n inheritance discards every earlier tenant contribution (chain-structural),\n so only the suffix from the LAST break participates — in merging AND in\n the explained layers, which therefore replay to the merged result\n4. User rows — keyed by `userId` alone (preferences follow the user); both\n defaults AND visibility resolve through this tier\n\n## Invariants\n\n- **Scope shape**: app ⇒ `tenantId`+`userId` null; tenant ⇒ only `tenantId`;\n user ⇒ only `userId`. `scopeKey` (`userId ?? tenantId ?? '__app__'`) exists\n ONLY to keep the `conflictColumns` unique index total — never read it for\n scoping logic.\n- **Manifest as definition registry**: writes validate against the live\n `ObjectRegistry` (never checked-in manifest.json artifacts): unknown\n objectRef/fieldName rejected; defaults type-checked against the field type;\n system fields, relationship pseudo-fields, and STI meta storage fields are\n not policy-addressable (the resolver excludes them, so rows would silently\n never apply).\n- **Security rail**: defaults are refused on `transient`, `sensitive`, and\n `readPermission`-gated fields (both top-level and `_meta` flags checked).\n Reference-field (`foreignKey`/`crossPackageRef`) defaults must be UUID\n strings unless the field declares `idType: 'text'` — the columns are\n native UUID on PostgreSQL/DuckDB and a non-UUID default would fail at\n insert time. `resolveBatch` responses omit sensitive/read-permission-gated\n fields for every caller (fail closed) and transient fields\n (client-emission parity).\n- **Required-field invariant**: demoting a required field to\n advanced/hidden requires a usable resolved default (not null/empty) —\n enforced at write time AND re-enforced at resolution (a required field with\n no usable default always resolves `basic`, flagged `visibilityForced`).\n- **Org lock**: `locked` may be set on app/tenant rows only; while the\n code/app/tenant tiers resolve locked, user-scope writes are rejected and\n existing user rows are skipped at resolution.\n- **Write-time org checks reuse the resolver**: the required-demotion default\n and the user-write lock are computed by `resolveFieldPolicy` over the org\n tiers (default hierarchy loader), so cascading ancestor-tenant defaults and\n locks are honored at save time too. On updates the persisted row is excluded\n from projected lower-layer resolution, so clearing the row's only default\n while demoting a required field is rejected before mutation; that projected\n lookup bypasses the shared cache. The resolver-side safety net remains\n authoritative when a different row is later deleted.\n- **Isolation — a MISSING identity component DENIES, it never skips.** This\n is the package rule; both the write guard\n (`FieldPolicy.assertScopeOwnedByAmbientContext`) and the read guard\n (`assertResolutionAllowedInContext`) obey it. A non-bypass tenant context\n may only resolve/write its own tenant, and the user tier only for its own\n user id — a context carrying permissions but NO `userId` (no `resolveUserId`\n hook configured: API-key auth, service principals, background jobs, a bare\n `withTenant({ tenantId })`) may not touch the user tier at all. Skipping\n that check instead of denying it was a live ownership bypass: user rows are\n `tenantId: null` by design, so nothing else contains such a write. App-scope\n writes inside a tenant context require super-admin bypass. `fields.policy.manage`\n authorizes app/tenant policy mutations; `fields.policy.personalize` authorizes\n only the caller's user tier (default-seeded for built-in roles). With NO ambient\n identity at all (tenancy ALS never entered), policy writes/deletes fail closed;\n trusted system and super-admin contexts retain their explicit bypasses.\n Context-LESS *reads* stay allowed because `resolveFieldPolicy` is a trusted\n server-side API. `save()`/`delete()`\n on an existing row additionally authorize against the row's PERSISTED\n scope, looked up by primary key AND — because a generated create always\n mints a fresh UUID while the `conflictColumns` upsert still replaces the\n occupant — by NATURAL key. `resolveBatch` takes identity exclusively from\n the ambient context; the request body cannot select another tenant or user.\n- **Scope attribution**: inside an ambient context the model DERIVES a\n missing `tenantId` (tenant rows) or `userId` (user rows) from that context,\n and always stamps `updatedBy` from it. Core's mass-assignment guard treats\n `tenantId` as server-managed and strips it from every generated write body\n while `FieldPolicy` is deliberately not `@TenantScoped`, so without this the\n org tier is write-dead over REST/SvelteKit (scope-shape validation throws).\n Deriving grants nothing — the ownership guard already pinned the value to\n the ambient one. An explicit value is never overwritten, so a super-admin\n bypass caller writing ANOTHER tenant's row must go through a server-side\n model call; the generated routes still strip it.\n\n## Caching and invalidation\n\n- Resolver results cached per\n `(dbNamespace, objectRef, tenantId, userId, hierarchyLoader)` with a 30s TTL\n (`cache.ts` mirrors smrt-prompts' `getDbNamespace`). The loader identity is\n part of the key because an injected `tenantHierarchyLoader` yields a\n different ancestor chain — and so different defaults/locks — for the same\n `(db, objectRef, tenant, user)`; it goes LAST so the `(db, objectRef)`\n prefix scan still invalidates every loader's entries.\n- `FieldPolicy.save()`/`.delete()` invalidate ALL entries for the row's\n `(db, objectRef)` — coarser than prompts because tenant hierarchy makes a\n parent-tenant row affect every descendant's resolution.\n- `_smrt_field_policies` rows do NOT ride the client change feed: core's\n change-feed writer deliberately skips `_smrt_`-prefixed system tables, and\n the emit side is private. Live client invalidation is a core-side decision;\n do not add custom push/emit paths here.\n\n## Gotchas\n\n- Defaults have TWO explicit constructor channels, never sniffed:\n `defaultValue` is ALREADY JSON-encoded (the wire contract — generated write\n routes hand the request body straight to the constructor, and #2049's gear\n posts `JSON.stringify(draft.defaultValue)`), while `defaultValueRaw` is a\n plain value that is always serialized, strings included. Passing both throws.\n One option cannot carry both meanings: `'\"TBD\"'` and `'TBD'` are\n indistinguishable, so `{ defaultValue: 'Net 30' }` is a parse error that\n names `defaultValueRaw` in its message. `setDefaultValue()` is the\n method-level plain channel.\n- The sort-order column is `displayOrder` (resolved output exposes `order`):\n a column literally named `order` is an SQL keyword the runtime INSERT path\n does not quote.\n- `FieldPolicy` deliberately has NO class-level `@TenantScoped` (the\n prompts/features precedent): resolution legitimately reads app rows and\n ancestor-tenant rows, which the tenancy interceptor would block. Isolation\n is enforced at the resolver/save boundaries instead.\n- Identity changes (objectRef/fieldName/scope) on a persisted row go through\n delete-then-insert (transactional when the driver supports it) because the\n natural-key upsert would otherwise collide with the primary key.\n- `FieldPolicyCollection` MUST repeat FieldPolicy's `conflictColumns`. A\n decorated collection emits its OWN manifest schema for the item's table, and\n without the natural key that schema falls back to SmrtObject's default unique\n `(slug, context)` index; manifest-driven migrations aggregate both onto\n `_smrt_field_policies`, where the stray index rejects legitimate layered rows\n (all policy rows have NULL slug/context). The runtime registry cannot catch\n this — `getAllSchemas()` is keyed by TABLE name, so the two schemas collapse\n into one entry. Pinned against the generated manifest in\n `generated-surfaces.test.ts`.\n- `withSystemContext()` does NOT unlock tenant/user-scope writes:\n `getCurrentTenant()` returns undefined inside it, so the context-absent rule\n rejects those tiers before any bypass check. Seeds and migrations that must\n write org/user rows use a `superAdminBypass` context instead.\n- The MODEL's `cli`/`mcp` decorator config is dead at runtime: the registry\n re-registers the item slot with the COLLECTION's config wholesale, so\n `FieldPolicyCollection`'s `cli: false, mcp: false` is what actually closes\n those surfaces. Keep both in lockstep anyway — build-time generation reads\n the model's own config. Related: `ObjectRegistry.getTableName(\n 'FieldPolicyCollection')` resolves to the UNPREFIXED collection fallback\n `field_policies`; persistence uses the item class\n (`_smrt_field_policies`), and both are pinned in\n `generated-surfaces.test.ts`.\n\n## Svelte generated forms (#2049)\n\n- `ObjectForm` accepts direct generated `fields` plus a resolved `policy` for\n SSR and explicit hosts, or accepts only `objectRef` beneath an\n `ObjectFormSourceProvider`. `ObjectFormSourceRegistry` is per app and maps\n canonical refs to generated web collection definitions before calling the\n generated `resolveBatch` custom action. It uses structural types only: this\n package must never import `smrt-web`.\n- Generated custom-action clients resolve `Promise<any>`; validate their\n `policies[objectRef]` response at the registry boundary and fail closed on a\n missing/mismatched definition or policy. The component renders an accessible\n loading state and alert rather than a partial form.\n- Browser manifest types are `text`, `integer`, `decimal`, `boolean`,\n `datetime`, `json`, `foreignKey`, and `crossPackageRef`; there is no `select`\n wire type. Apps use the per-app `FieldInputRegistry.registerField` seam for\n select-like widgets. Reference fields intentionally default to identifier\n inputs unless an app supplies a chooser.\n- `policyToVisibleColumnIds(policy, columns)` feeds smrt-ui `DataTable`'s\n `visibleColumnIds`; it filters policy-hidden mapped fields, preserves unmapped\n computed/action columns, and cannot reveal a static `column.hidden` column.\n\n## Defaults control panel (#2050)\n\n- `buildFieldPolicySettingsCatalog()` is the server-side, URL/GET-driven\n catalog builder. It structurally targets `SettingsCatalog`, but this package\n must not import `smrt-svelte`; hosts inject `SettingsCatalog` into\n `FieldPolicyControlPanel` and retain their own route and transport adapters.\n- `policyAudit` is the only routed organization roll-up. It requires\n `fields.policy.manage`, returns only the caller tenant's editable rows and\n read-only app summaries, and represents other users strictly as per-field\n counts. It resolves only requested page refs; never pre-resolve the catalog.\n- Display code/app/org values by replaying the explained resolver layers, not\n by independently calculating precedence. Reset and drift prune are ordinary\n model deletes, so existing scope/permission validation remains authoritative.\n The panel asks for an explicit confirmation before either destructive action;\n SSR hosts can inject that confirmation decision instead of relying on\n `window.confirm`.\n- `fieldPolicyControlPanelNavItem()` is a structural AdminShell tenant-nav\n seam. It never imports `smrt-svelte` or `smrt-web`; the host supplies the\n returned entry and must enforce its real route permission server-side.\n\n## Related\n\n- `@happyvertical/smrt-prompts` / `smrt-languages` / `smrt-features` — the\n same architecture family (override rows + layered resolver + TTL cache)\n- Core `FieldUIHints` (#2046) — the `@field({ ui })` code seed this package\n resolves over\n"
786
+ "agentDoc": "# smrt-fields\n\nLayered field policy store and resolution engine (epic #2045). Personalizes\nper-field `{defaultValue, visibility, help, label, order, locked}` for any\n`@smrt()` object at app, tenant, and user scope over the code seed.\n\n## Core pieces\n\n- `FieldPolicy` (`_smrt_field_policies` table) — sparse override rows keyed\n `(objectRef, fieldName, scopeType, scopeKey)`; NULL column = inherit from\n the lower layer, reset = row DELETE (later lower-layer changes flow through)\n- `FieldPolicyCollection` — write surface plus the `resolveBatch` custom\n collection-scoped action (`POST /<collection>/resolve`). NO generated\n surface exposes read verbs: API `list`/`get` on this non-tenant-scoped\n model would enumerate every tenant's and user's rows; the model's CLI is\n writes-only (the generated CLI invokes over HTTP, and the cli↔api\n coherence gate rejects CLI entries without API routes); the runtime\n CLI/MCP surfaces are closed by the collection config (ContentContributions\n precedent). Reads go through `resolveBatch` (context-scoped) or the\n server-side resolver. Keep the api include lists in lockstep: a decorated\n collection's config is the RUNTIME registry authority for its item class,\n while build-time generation reads each manifest object's own config. Both\n transports dispatch the action: generated SvelteKit routes natively, and\n core's runtime `APIGenerator` via its decorator-route dispatch\n (single-segment collection-scoped paths; multi-segment custom paths remain\n SvelteKit-only).\n- `resolveFieldPolicy(objectRef, { tenantId, userId, db })` — merged policy\n- `resolveFieldPolicyExplained(...)` — merged policy plus ordered per-layer\n contributions (code/app/tenant/user) for gear/admin UIs (#2049/#2050)\n\n## Resolution layers (priority low → high)\n\n1. Code seed — manifest field defaults, `description` as help, `_meta.ui`\n hints (#2046: `basic`/`group`/`order`/`locked`; cold-start rule: no\n `basic` markers ⇒ everything basic, any marker ⇒ unmarked fields advanced)\n2. App rows — `scopeType: 'app'`, `tenantId`/`userId` null\n3. Tenant rows — hierarchy walk root → leaf via an injected\n `tenantHierarchyLoader` (smrt-features shape); the default loader\n uses `@happyvertical/smrt-users`' `TenantCollection`; an injected loader\n may return no provider (or no chain for a tenant), which falls back to a\n flat single-tenant chain. `smrt-users` itself is a required Fields runtime\n dependency for policy authorization, not an optional hierarchy dependency.\n A node that breaks permission\n inheritance discards every earlier tenant contribution (chain-structural),\n so only the suffix from the LAST break participates — in merging AND in\n the explained layers, which therefore replay to the merged result\n4. User rows — keyed by `userId` alone (preferences follow the user); both\n defaults AND visibility resolve through this tier\n\n## Invariants\n\n- **Scope shape**: app ⇒ `tenantId`+`userId` null; tenant ⇒ only `tenantId`;\n user ⇒ only `userId`. `scopeKey` (`userId ?? tenantId ?? '__app__'`) exists\n ONLY to keep the `conflictColumns` unique index total — never read it for\n scoping logic.\n- **Manifest as definition registry**: writes validate against the live\n `ObjectRegistry` (never checked-in manifest.json artifacts): unknown\n objectRef/fieldName rejected; defaults type-checked against the field type;\n system fields, relationship pseudo-fields, and STI meta storage fields are\n not policy-addressable (the resolver excludes them, so rows would silently\n never apply).\n- **Security rail**: defaults are refused on `transient`, `sensitive`, and\n `readPermission`-gated fields (both top-level and `_meta` flags checked).\n Reference-field (`foreignKey`/`crossPackageRef`) defaults must be UUID\n strings unless the field declares `idType: 'text'` — the columns are\n native UUID on PostgreSQL/DuckDB and a non-UUID default would fail at\n insert time. `resolveBatch` responses omit sensitive/read-permission-gated\n fields for every caller (fail closed) and transient fields\n (client-emission parity).\n- **Required-field invariant**: demoting a required field to\n advanced/hidden requires a usable resolved default (not null/empty) —\n enforced at write time AND re-enforced at resolution (a required field with\n no usable default always resolves `basic`, flagged `visibilityForced`).\n- **Org lock**: `locked` may be set on app/tenant rows only; while the\n code/app/tenant tiers resolve locked, user-scope writes are rejected and\n existing user rows are skipped at resolution.\n- **Write-time org checks reuse the resolver**: the required-demotion default\n and the user-write lock are computed by `resolveFieldPolicy` over the org\n tiers (default hierarchy loader), so cascading ancestor-tenant defaults and\n locks are honored at save time too. On updates the persisted row is excluded\n from projected lower-layer resolution, so clearing the row's only default\n while demoting a required field is rejected before mutation; that projected\n lookup bypasses the shared cache. The resolver-side safety net remains\n authoritative when a different row is later deleted.\n- **Isolation — a MISSING identity component DENIES, it never skips.** This\n is the package rule; both the write guard\n (`FieldPolicy.assertScopeOwnedByAmbientContext`) and the read guard\n (`assertResolutionAllowedInContext`) obey it. A non-bypass tenant context\n may only resolve/write its own tenant, and the user tier only for its own\n user id — a context carrying permissions but NO `userId` (no `resolveUserId`\n hook configured: API-key auth, service principals, background jobs, a bare\n `withTenant({ tenantId })`) may not touch the user tier at all. Skipping\n that check instead of denying it was a live ownership bypass: user rows are\n `tenantId: null` by design, so nothing else contains such a write. App-scope\n writes inside a tenant context require super-admin bypass. `fields.policy.manage`\n authorizes app/tenant policy mutations; `fields.policy.personalize` authorizes\n only the caller's user tier (default-seeded for built-in roles). With NO ambient\n identity at all (tenancy ALS never entered), policy writes/deletes fail closed;\n trusted system and super-admin contexts retain their explicit bypasses.\n Context-LESS *reads* stay allowed because `resolveFieldPolicy` is a trusted\n server-side API. `save()`/`delete()`\n on an existing row additionally authorize against the row's PERSISTED\n scope, looked up by primary key AND — because a generated create always\n mints a fresh UUID while the `conflictColumns` upsert still replaces the\n occupant — by NATURAL key. `resolveBatch` takes identity exclusively from\n the ambient context; the request body cannot select another tenant or user.\n- **Scope attribution**: inside an ambient context the model DERIVES a\n missing `tenantId` (tenant rows) or `userId` (user rows) from that context,\n and always stamps `updatedBy` from it. Core's mass-assignment guard treats\n `tenantId` as server-managed and strips it from every generated write body\n while `FieldPolicy` is deliberately not `@TenantScoped`, so without this the\n org tier is write-dead over REST/SvelteKit (scope-shape validation throws).\n Deriving grants nothing — the ownership guard already pinned the value to\n the ambient one. An explicit value is never overwritten, so a super-admin\n bypass caller writing ANOTHER tenant's row must go through a server-side\n model call; the generated routes still strip it.\n\n## Caching and invalidation\n\n- Resolver results cached per\n `(dbNamespace, objectRef, tenantId, userId, hierarchyLoader)` with a 30s TTL\n (`cache.ts` mirrors smrt-prompts' `getDbNamespace`). The loader identity is\n part of the key because an injected `tenantHierarchyLoader` yields a\n different ancestor chain — and so different defaults/locks — for the same\n `(db, objectRef, tenant, user)`; it goes LAST so the `(db, objectRef)`\n prefix scan still invalidates every loader's entries.\n- `FieldPolicy.save()`/`.delete()` invalidate ALL entries for the row's\n `(db, objectRef)` — coarser than prompts because tenant hierarchy makes a\n parent-tenant row affect every descendant's resolution.\n- `_smrt_field_policies` rows do NOT ride the client change feed: core's\n change-feed writer deliberately skips `_smrt_`-prefixed system tables, and\n the emit side is private. Live client invalidation is a core-side decision;\n do not add custom push/emit paths here.\n\n## Gotchas\n\n- Defaults have TWO explicit constructor channels, never sniffed:\n `defaultValue` is ALREADY JSON-encoded (the wire contract — generated write\n routes hand the request body straight to the constructor, and #2049's gear\n posts `JSON.stringify(draft.defaultValue)`), while `defaultValueRaw` is a\n plain value that is always serialized, strings included. Passing both throws.\n One option cannot carry both meanings: `'\"TBD\"'` and `'TBD'` are\n indistinguishable, so `{ defaultValue: 'Net 30' }` is a parse error that\n names `defaultValueRaw` in its message. `setDefaultValue()` is the\n method-level plain channel.\n- The sort-order column is `displayOrder` (resolved output exposes `order`):\n a column literally named `order` is an SQL keyword the runtime INSERT path\n does not quote.\n- `FieldPolicy` deliberately has NO class-level `@TenantScoped` (the\n prompts/features precedent): resolution legitimately reads app rows and\n ancestor-tenant rows, which the tenancy interceptor would block. Isolation\n is enforced at the resolver/save boundaries instead.\n- Identity changes (objectRef/fieldName/scope) on a persisted row go through\n delete-then-insert (transactional when the driver supports it) because the\n natural-key upsert would otherwise collide with the primary key.\n- `FieldPolicyCollection` MUST repeat FieldPolicy's `conflictColumns`. A\n decorated collection emits its OWN manifest schema for the item's table, and\n without the natural key that schema falls back to SmrtObject's default unique\n `(slug, context)` index; manifest-driven migrations aggregate both onto\n `_smrt_field_policies`, where the stray index rejects legitimate layered rows\n (all policy rows have NULL slug/context). The runtime registry cannot catch\n this — `getAllSchemas()` is keyed by TABLE name, so the two schemas collapse\n into one entry. Pinned against the generated manifest in\n `generated-surfaces.test.ts`.\n- `withSystemContext()` does NOT unlock tenant/user-scope writes:\n `getCurrentTenant()` returns undefined inside it, so the context-absent rule\n rejects those tiers before any bypass check. Seeds and migrations that must\n write org/user rows use a `superAdminBypass` context instead.\n- The MODEL's `cli`/`mcp` decorator config is dead at runtime: the registry\n re-registers the item slot with the COLLECTION's config wholesale, so\n `FieldPolicyCollection`'s `cli: false, mcp: false` is what actually closes\n those surfaces. Keep both in lockstep anyway — build-time generation reads\n the model's own config. Related: `ObjectRegistry.getTableName(\n 'FieldPolicyCollection')` resolves to the UNPREFIXED collection fallback\n `field_policies`; persistence uses the item class\n (`_smrt_field_policies`), and both are pinned in\n `generated-surfaces.test.ts`.\n\n## Svelte generated forms (#2049)\n\n- `ObjectForm` accepts direct generated `fields` plus a resolved `policy` for\n SSR and explicit hosts, or accepts only `objectRef` beneath an\n `ObjectFormSourceProvider`. `ObjectFormSourceRegistry` is per app and maps\n canonical refs to generated web collection definitions before calling the\n generated `resolveBatch` custom action. It uses structural types only: this\n package must never import `smrt-web`.\n- Generated custom-action clients resolve `Promise<any>`; validate their\n `policies[objectRef]` response at the registry boundary and fail closed on a\n missing/mismatched definition or policy. The component renders an accessible\n loading state and alert rather than a partial form.\n- Browser manifest types are `text`, `integer`, `decimal`, `boolean`,\n `datetime`, `json`, `foreignKey`, and `crossPackageRef`; there is no `select`\n wire type. Apps use the per-app `FieldInputRegistry.registerField` seam for\n select-like widgets. Reference fields intentionally default to identifier\n inputs unless an app supplies a chooser.\n- `policyToVisibleColumnIds(policy, columns)` feeds smrt-ui `DataTable`'s\n `visibleColumnIds`; it filters policy-hidden mapped fields, preserves unmapped\n computed/action columns, and cannot reveal a static `column.hidden` column.\n- **Two-tier context contract.** `PolicyField` alone reads the context with\n `tryGetFieldPolicyContext()`, because outside a Provider it still has the\n caller's children to render verbatim (incremental adoption). The\n provider-only compositions — `ModeSwitch`, `AdvancedFields`, `FormHelp` —\n read it with the throwing `getFieldPolicyContext()`: their whole output is\n derived from the resolved policy, so degrading would render an empty or\n absent control instead of surfacing the missing Provider. Do not convert one\n of them to the non-throwing accessor in isolation (#2272).\n- **One provider per form.** `FieldPolicyProvider` renders only\n `{@render children?.()}`, so a host wanting `FormHelp` in a page header\n extends the SAME provider over the header rather than hoisting the component\n out. It must not add a second: each provider constructs its own\n `FieldPolicyModeStore` and `setContext` shadows for the subtree, so a\n `FormHelp` above a nested provider stops tracking the `ModeSwitch` inside it.\n `ObjectForm` builds its own provider, so never wrap it in another; its\n `actions` snippet renders INSIDE that provider (context resolves at the render\n site), which is the only way to reach `FormHelp` through it — below the\n fields, since there is no header seam (#2289).\n\n## Defaults control panel (#2050)\n\n- `buildFieldPolicySettingsCatalog()` is the server-side, URL/GET-driven\n catalog builder. It structurally targets `SettingsCatalog`, but this package\n must not import `smrt-svelte`; hosts inject `SettingsCatalog` into\n `FieldPolicyControlPanel` and retain their own route and transport adapters.\n- `policyAudit` is the only routed organization roll-up. It requires\n `fields.policy.manage`, returns only the caller tenant's editable rows and\n read-only app summaries, and represents other users strictly as per-field\n counts. It resolves only requested page refs; never pre-resolve the catalog.\n- Display code/app/org values by replaying the explained resolver layers, not\n by independently calculating precedence. Reset and drift prune are ordinary\n model deletes, so existing scope/permission validation remains authoritative.\n The panel asks for an explicit confirmation before either destructive action;\n SSR hosts can inject that confirmation decision instead of relying on\n `window.confirm`.\n- `fieldPolicyControlPanelNavItem()` is a structural AdminShell tenant-nav\n seam. It never imports `smrt-svelte` or `smrt-web`; the host supplies the\n returned entry and must enforce its real route permission server-side.\n\n## Related\n\n- `@happyvertical/smrt-prompts` / `smrt-languages` / `smrt-features` — the\n same architecture family (override rows + layered resolver + TTL cache)\n- Core `FieldUIHints` (#2046) — the `@field({ ui })` code seed this package\n resolves over\n"
787
787
  }
@@ -122,6 +122,14 @@ describe('FormHelp', () => {
122
122
  expect(terms[0]).toHaveTextContent('SKU');
123
123
  expect(terms[1]).toHaveTextContent('Name');
124
124
  });
125
+ it('throws an actionable error outside a FieldPolicyProvider (#2272)', () => {
126
+ // Deliberate contract: FormHelp's whole output is the resolved glossary, so
127
+ // it reports a missing Provider instead of degrading to an empty panel.
128
+ // (PolicyField degrades because it has caller-owned children to preserve.)
129
+ expect(() => render(FormHelp, {
130
+ props: { objectDescription: 'A widget is a configurable thing.' },
131
+ })).toThrow(/Wrap your form in <FieldPolicyProvider>/);
132
+ });
125
133
  it('is axe-clean', async () => {
126
134
  const { container } = render(FormHelpFixture, {
127
135
  props: { policy: makePolicy() },
@@ -0,0 +1,66 @@
1
+ // @vitest-environment jsdom
2
+ /**
3
+ * Playground smoke tests (#2272).
4
+ *
5
+ * Registering `@happyvertical/smrt-fields/playground` publishes EVERY entry in
6
+ * the module, and `PlaygroundHost` mounts the selected one bare — no
7
+ * `<svelte:boundary>` around it. So a preview that throws on mount is not a
8
+ * contained failure the host can route around; a host cannot offer the sound
9
+ * entry without also offering one that breaks the page.
10
+ *
11
+ * That is exactly how #2272 shipped: the `Policy-Driven Form` preview rendered
12
+ * `<FormHelp>` outside its `<FieldPolicyProvider>`, and no unit test could see
13
+ * it, because `FormHelp`'s own tests always mount it through a provider
14
+ * fixture — they exercise the component's contract, never the preview's
15
+ * composition.
16
+ *
17
+ * These tests therefore load and MOUNT every exported entry the way
18
+ * `PlaygroundHost` does — one mount per declared mode, with the entry props and
19
+ * mode props merged (`PlaygroundHost.svelte`). Nothing wraps the component, so
20
+ * a preview that needs an ancestor provider must supply its own.
21
+ *
22
+ * Structural types below: this package must not depend on
23
+ * `@happyvertical/smrt-playground`, so the module shape is asserted, not
24
+ * imported.
25
+ */
26
+ import { cleanup, render } from '@happyvertical/smrt-vitest/svelte';
27
+ import { afterEach, describe, expect, it } from 'vitest';
28
+ import playground from '../playground.js';
29
+ const entries = playground.entries;
30
+ afterEach(() => {
31
+ cleanup();
32
+ });
33
+ describe('smrt-fields playground module', () => {
34
+ it('publishes the field-policy previews', () => {
35
+ expect(playground.packageName).toBe('@happyvertical/smrt-fields');
36
+ expect(entries.map((entry) => entry.id)).toEqual([
37
+ 'policy-form',
38
+ 'object-form',
39
+ ]);
40
+ for (const entry of entries) {
41
+ expect(entry.loadComponent).toEqual(expect.any(Function));
42
+ expect(entry.modes).toHaveProperty('mock');
43
+ }
44
+ });
45
+ // One case per entry/mode so a failure names the broken preview.
46
+ for (const entry of entries) {
47
+ for (const [mode, config] of Object.entries(entry.modes ?? {})) {
48
+ it(`mounts the "${entry.title}" preview in ${mode} mode`, async () => {
49
+ const loaded = await entry.loadComponent();
50
+ const component = typeof loaded === 'function'
51
+ ? loaded
52
+ : loaded.default;
53
+ expect(component).toEqual(expect.any(Function));
54
+ // Mirror PlaygroundHost: entry props, then mode props. Nothing else.
55
+ const props = {
56
+ ...(entry.props ?? {}),
57
+ ...(config === true ? {} : (config.props ?? {})),
58
+ };
59
+ const { container } = render(component, {
60
+ props: props,
61
+ });
62
+ expect(container.textContent?.trim()).not.toBe('');
63
+ });
64
+ }
65
+ }
66
+ });
@@ -9,7 +9,22 @@
9
9
  * plus the object description if provided. The data comes entirely from the
10
10
  * resolved policy — no separate fetch needed.
11
11
  *
12
- * Must be used inside a FieldPolicyProvider.
12
+ * Must be used inside a FieldPolicyProvider — deliberately, via the throwing
13
+ * accessor. The panel's payload IS the resolved glossary, so with no provider
14
+ * there is nothing to assemble and a `?` affordance that opens onto "No field
15
+ * help available." would hide the mistake instead of reporting it. This matches
16
+ * the other provider-only compositions (`ModeSwitch`, `AdvancedFields`);
17
+ * `PolicyField` degrades instead only because it has caller-owned children to
18
+ * render verbatim.
19
+ *
20
+ * To put form help in a page header, extend the SAME provider over the header —
21
+ * it renders no markup of its own. Do not add a second one: each provider owns
22
+ * its own mode store and shadows the outer for its subtree, so a `FormHelp`
23
+ * above a nested provider stops tracking the `ModeSwitch` inside it (#2272).
24
+ *
25
+ * Inside `ObjectForm`, which builds its own provider, use its `actions` snippet
26
+ * — it renders within that provider, so the glossary resolves. There is no
27
+ * header seam, so it lands below the fields; tracked in #2289.
13
28
  */
14
29
  import { getFieldPolicyContext } from '../context.svelte.js';
15
30
 
@@ -1 +1 @@
1
- {"version":3,"file":"FormHelp.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/FormHelp.svelte.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,KAAK;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuED,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"FormHelp.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/FormHelp.svelte.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,KAAK;IACpB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuED,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -12,6 +12,21 @@
12
12
  * outside a Provider: an unwrapped field renders its children verbatim, and a
13
13
  * consumer can adopt the primitives incrementally without wrapping the whole
14
14
  * form.
15
+ *
16
+ * The two accessors are a deliberate two-tier contract, not a style choice:
17
+ *
18
+ * - `tryGetFieldPolicyContext` is for a primitive that still has something
19
+ * meaningful to render without a policy — today only `PolicyField`, whose
20
+ * fallback is the caller's own children.
21
+ * - `getFieldPolicyContext` is for the provider-only compositions
22
+ * (`ModeSwitch`, `AdvancedFields`, `FormHelp`) whose entire output is derived
23
+ * from the resolved policy. Degrading those would render an empty or absent
24
+ * control rather than surface the missing Provider, so they throw with an
25
+ * actionable message instead (#2272).
26
+ *
27
+ * One provider per form. `setContext` shadows, so a nested `FieldPolicyProvider`
28
+ * gives its subtree a second, independent `FieldPolicyModeStore` — consumers
29
+ * above the nested provider then stop tracking the `ModeSwitch` below it.
15
30
  */
16
31
  import type { ResolvedFieldPolicy, ResolvedObjectFieldPolicy } from '../types.js';
17
32
  /** UI display mode for progressive disclosure. */
@@ -1 +1 @@
1
- {"version":3,"file":"context.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/context.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB,kDAAkD;AAClD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,UAAU,CAAC;AAEnD;;;;GAIG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,kBAAoC;gBAE/B,OAAO,GAAE,eAAyB;IAI9C,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAIhC,MAAM,IAAI,IAAI;CAGf;AAED,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAC;IAC7D,iEAAiE;IACjE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAID,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAE5E;AAED,4DAA4D;AAC5D,wBAAgB,qBAAqB,IAAI,uBAAuB,CAS/D;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IACpC,uBAAuB,GACvB,SAAS,CAIZ"}
1
+ {"version":3,"file":"context.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/context.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EACV,mBAAmB,EACnB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB,kDAAkD;AAClD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,UAAU,CAAC;AAEnD;;;;GAIG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,kBAAoC;gBAE/B,OAAO,GAAE,eAAyB;IAI9C,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,IAAI;IAIhC,MAAM,IAAI,IAAI;CAGf;AAED,+EAA+E;AAC/E,MAAM,WAAW,uBAAuB;IACtC,gDAAgD;IAChD,QAAQ,CAAC,MAAM,EAAE,yBAAyB,CAAC;IAC3C,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAAC;IAC7D,iEAAiE;IACjE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAID,2DAA2D;AAC3D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAE5E;AAED,4DAA4D;AAC5D,wBAAgB,qBAAqB,IAAI,uBAAuB,CAS/D;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IACpC,uBAAuB,GACvB,SAAS,CAIZ"}
@@ -12,6 +12,21 @@
12
12
  * outside a Provider: an unwrapped field renders its children verbatim, and a
13
13
  * consumer can adopt the primitives incrementally without wrapping the whole
14
14
  * form.
15
+ *
16
+ * The two accessors are a deliberate two-tier contract, not a style choice:
17
+ *
18
+ * - `tryGetFieldPolicyContext` is for a primitive that still has something
19
+ * meaningful to render without a policy — today only `PolicyField`, whose
20
+ * fallback is the caller's own children.
21
+ * - `getFieldPolicyContext` is for the provider-only compositions
22
+ * (`ModeSwitch`, `AdvancedFields`, `FormHelp`) whose entire output is derived
23
+ * from the resolved policy. Degrading those would render an empty or absent
24
+ * control rather than surface the missing Provider, so they throw with an
25
+ * actionable message instead (#2272).
26
+ *
27
+ * One provider per form. `setContext` shadows, so a nested `FieldPolicyProvider`
28
+ * gives its subtree a second, independent `FieldPolicyModeStore` — consumers
29
+ * above the nested provider then stop tracking the `ModeSwitch` below it.
15
30
  */
16
31
  import { getContext, setContext } from 'svelte';
17
32
  /**
@@ -97,19 +97,25 @@ function handleSubmit() {
97
97
  </script>
98
98
 
99
99
  <section class="field-policy-preview">
100
- <header class="preview-header">
101
- <h2>Hand-written form adopting PolicyField</h2>
102
- <p class="preview-desc">
103
- A developer-authored product form. Two fields (name, SKU) adopt
104
- PolicyField for help, defaults, and progressive disclosure; the rest stay
105
- hand-written. Toggle the mode switch to reveal advanced-tier fields.
106
- </p>
107
- <FormHelp
108
- objectDescription="A sellable catalog product with wholesale pricing and inventory controls."
109
- />
110
- </header>
111
-
100
+ <!--
101
+ ONE provider, extended over the header too. FormHelp assembles its glossary
102
+ from the provider context and must live inside it; the provider renders no
103
+ markup of its own, so covering the header costs no layout. Do not add a
104
+ second provider to reach it a nested one shadows this mode store — #2272.
105
+ -->
112
106
  <FieldPolicyProvider policy={resolvedPolicy} mode="basic">
107
+ <header class="preview-header">
108
+ <h2>Hand-written form adopting PolicyField</h2>
109
+ <p class="preview-desc">
110
+ A developer-authored product form. Two fields (name, SKU) adopt
111
+ PolicyField for help, defaults, and progressive disclosure; the rest stay
112
+ hand-written. Toggle the mode switch to reveal advanced-tier fields.
113
+ </p>
114
+ <FormHelp
115
+ objectDescription="A sellable catalog product with wholesale pricing and inventory controls."
116
+ />
117
+ </header>
118
+
113
119
  <div class="toolbar">
114
120
  <ModeSwitch />
115
121
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"FieldPolicyFormPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/FieldPolicyFormPreview.svelte.ts"],"names":[],"mappings":"AAqKA,QAAA,MAAM,sBAAsB,2DAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"FieldPolicyFormPreview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/playground/FieldPolicyFormPreview.svelte.ts"],"names":[],"mappings":"AAsKA,QAAA,MAAM,sBAAsB,2DAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-fields",
3
- "version": "0.40.62",
3
+ "version": "0.40.63",
4
4
  "description": "Layered field policy store and resolver (defaults, visibility tiers, help text) for SMRT objects",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,11 +36,11 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@happyvertical/sql": "^0.85.5",
40
- "@happyvertical/smrt-core": "0.40.62",
41
- "@happyvertical/smrt-tenancy": "0.40.62",
42
- "@happyvertical/smrt-ui": "0.40.62",
43
- "@happyvertical/smrt-users": "0.40.62"
39
+ "@happyvertical/sql": "^0.86.1",
40
+ "@happyvertical/smrt-core": "0.40.63",
41
+ "@happyvertical/smrt-tenancy": "0.40.63",
42
+ "@happyvertical/smrt-users": "0.40.63",
43
+ "@happyvertical/smrt-ui": "0.40.63"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@sveltejs/package": "^2.5.8",
@@ -52,9 +52,9 @@
52
52
  "typescript": "5.9.3",
53
53
  "vite": "8.1.4",
54
54
  "vitest": "4.1.10",
55
- "@happyvertical/smrt-cli": "0.40.62",
56
- "@happyvertical/smrt-users": "0.40.62",
57
- "@happyvertical/smrt-vitest": "0.40.62"
55
+ "@happyvertical/smrt-cli": "0.40.63",
56
+ "@happyvertical/smrt-users": "0.40.63",
57
+ "@happyvertical/smrt-vitest": "0.40.63"
58
58
  },
59
59
  "keywords": [
60
60
  "smrt",