CedarCopilot wants to merge 2 commits into staging from fix/field-ownership-subagent-docs
Live on prod, no production signal yet
OTEL fallback found no prod spans matching getFieldOwnership / buildFieldPermissions since deploy.
OTEL fallback found no prod spans matching runCrmUpdaterTool since deploy.
Verification cited only 10 matching post-deploy events, below the 20-event floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
OTEL fallback found no prod spans matching runPlaybookSectionExecution since deploy.
OTEL fallback found no prod spans matching readSubagentFull / run-subagent / runSingleSubagent since deploy.
Verification cited only 10 matching post-deploy events, below the 20-event floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
Libra has verdicts on 0 of 8 tracked behaviors on prod; 8 are still being checked. Libra checks hourly for 3 days after each deploy.
Two commits. The first fixes the ownership read surface; the second removes the id-resolution divergence that made the first only conditionally correct.
aop_agents (a table nothing writes)getFieldOwnership resolved a field's ownerAgentId by looking it up in the legacy aop_agents table. Subagent documents replaced that table , the header of services/aop/aop-agents.ts states outright that "this file performs no aop_agents table reads" , so no doc-backed agent has a row there.
Every stamped owner therefore resolved as "deleted", and both consumers silently no-op'd: the CRM updater's exclusion set (live-editable-field.ts:25) came back empty, and write-time forbidding (updateConversationFieldsTool.ts:247) never added the field.
owner row exists in aop_agents | fields | AOPs |
|---|---|---|
| no | 1212 | 101 |
| yes, enabled | 13 | 1 |
All 1225 are owned by strategist.md. So the CRM updater has been writing the_play, how_we_win, risks, on_track and the 8 MEDDPICC fields that stampStrategistOwnership explicitly handed to the Strategist.
Fix: resolve owners from getAllSubagentsForAop, the canonical listing for the document system. It merges user- and org-level docs (user shadowing org on filename) and drops any doc whose frontmatter sets enabled: false, so what it returns is the live-and-enabled set and the auto-reversion contract is preserved rather than reimplemented. The pure resolveFieldOwnership core is untouched, so its tests still apply.
Behavior change: the CRM updater stops writing those 12 strategic fields on the 102 affected AOPs, leaving the Strategist as their sole writer. That is what stampStrategistOwnership's docstring always described; it just never took effect. Nothing is orphaned , all 1225 stamped owners match a subagent doc's metadata.agent_id for the same user and AOP, including the 13 that previously resolved via aop_agents:
owned_fields | match_metadata_agent_id | match_doc_row_id | no_doc_match
1225 | 1225 | 0 | 0
documents.metadata.agent_id is the canonical identity of a subagent doc: it is what playbook <ref> resolution matches on, what findSubagentByAgentId looks up, and what verify-subagent-doc.ts:87 requires to be present and equal to the frontmatter id. The readers disagreed anyway:
| reader | precedence |
|---|---|
subagents.ts, aop-agents.ts | metadata → frontmatter → document id |
runSubagentTool.ts | frontmatter → sha256(userId:aopId:name) |
run-single-subagent.ts | frontmatter only, hard error when absent |
orchestrator-dispatch-tools.ts | frontmatter only |
updateNextStepsAndTasksTool.ts | frontmatter only |
So the id a field's ownerAgentId was stamped with could differ from the id its owning subagent executed under. Because buildFieldPermissions forbids a field to everyone except its owner and the CRM updater excludes owned fields, such a field ends up writable by nobody. That made commit 1 correct only for docs where both ids happen to agree.
Fix: resolveSubagentAgentId(metadata, frontmatter) is now the single place the precedence lives. readSubagentFull / readSubagentByDocId return the resolved agentId and documentId through a named SubagentDocRead, and every call site reads that instead of reaching into frontmatter. Each caller keeps its own last resort for a doc carrying neither id, so no existing execution identity moves: run-subagent keeps its stable hash, the doc listings keep the document id.
Prod impact: 3 docs, all carrying a metadata id with no matching frontmatter one; 0 docs have ids that disagree.
next-steps.md be91650e-017f-4364-93e9-34d06707f78d
crm-updater.md 2b57f89d-1461-4fbd-a08b-c221963296dd
post-call-coach.md post-call-coach
be91650e… already has 402 executions reco
Libra has not measured any production surfaces for this change yet.