CedarCopilot wants to merge 4 commits into staging from feat/field-propagation-primitive
Live on prod, no production signal yet
Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.
Since the deploy, there are 0 production OTEL spans matching config-propagate/propagate-field and 0 CloudWatch log lines matching config-propagate, propagate-field, or propagation result identifiers. Therefore no successful executions, failures, latency, volume, or.
Verification reached its budget before a verdict.
Unable to complete the telemetry verification within the available tool loop.
Since deployment at 2026-09-08T00:34:33Z, OTEL returned 0 matching config-propagate/propagate-field/propagate-mapping spans, so volume, success/error rate, latency, and distinct users are unavailable. CloudWatch showed config-propagate only in MCP tool-discovery listings.
Verification reached its budget before a verdict.
Libra has verdicts on 0 of 14 tracked behaviors on prod; 14 are still being checked. Libra checks hourly for 3 days after each deploy.
A field-propagation primitive: lets an admin see, across every rep linked to an org AOP, which fields/mappings match the org default vs. have drifted vs. are missing entirely, and push updates to many reps at once without a blind overwrite. Design doc: apps/server/docs/design/field-propagation-primitive.md.
1. Unify the CRM-mapping write path (closes a pre-existing bug).
Two paths wrote connection.metadata.fieldMappings and diverged: config-write's link-mapping action ran live CRM introspection + option reconciliation for select/list fields, but connection-write's update_crm_field_mappings never did , a simple mapping onto a select/list field written through connection-write silently skipped reconciliation. writeCrmFieldMapping() (services/integrations/crm/field-mapping-writer.ts) is now the one function both paths call; linkMapping() is a thin wrapper over it with its external contract and existing tests unchanged. Mapping validation now runs before option reconciliation commits, so a failed mapping write (e.g. op:'add' on an existing mapping) can never leave a field's options mutated.
2. Rollup read mode on list-fields.
listFields() gains targetUserIds/rollup params for viewing many reps' field configs at once instead of one call per rep, via two new pure comparison functions (field-diff.ts's diffFieldAgainstOrgDefault/rollupFieldsAcrossUsers, field-mapping-diff.ts's diffMappingAgainstReference). Wired through config-read-tool.ts's list-fields action with an authorize() gate for cross-user targeting.
3. config-propagate , new MCP tool, propagate-field/propagate-mapping.
Admin-only, service-layer functions kept free of any Mastra dependency (services/aop/propagate-field.ts, services/aop/propagate-mapping.ts) so a future UI can call them directly. Uniform/missing targets apply immediately; a target that genuinely diverges from the source is refused with the diff attached unless force: true, which routes it through the same runMutation/proposed-card mechanism every other config-write action already uses , an explicit per-target Slack Accept, never a blanket approval across reps.
Two real security/correctness issues found across two review passes, both fixed on this branch (not left as follow-ups):
aopId gap (self-review): an org-scope authorize() verdict alone only proves the caller administers some organization , it never sees the client-supplied aopId. Added a callerOrgId row-binding check in list-fields.ts's loadOrgContext that refuses when the AOP's actual owning org doesn't match the verdict's org.targetUserIds (caught by Greptile, verified independently before fixing): the AOP-definitions query was correctly org-scoped, but the connection and user-name queries inside loadPerUserFields were not , an admin naming a foreign-tenant userId in an explicit array got that user's real display name and a real slice of their CRM connection's mapping metadata back, via field-key overlap on shared built-in keys like status/dealValue. Fixed: every target is now validated against the caller's own orgOwnerId before any connection/name query runs; an invalid target is dropped entirely and reported via a separate invalidTargetUserIds, never blended into a "missing" rep.pnpm exec tsc -p tsconfig.json --noEmit , cleanpnpm deps:check , no dependency violationspnpm exec eslint on all touched source + test files , cleanfield-diff.test.ts, field-mapping-diff.test.ts, field-mapping-writer.test.ts (new) , diff/rollup bucketing, reconciliation triggering rules, mapping-validation-before-reconciliation orderinglist-fields.test.ts , byte-for-byte single-user regression, rollup fixture, cross-tenant row-binding refusal, and the newLibra has not measured any production surfaces for this change yet.