CedarCopilot wants to merge 5 commits into staging from fix/next-steps-stage-label
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.
Verification reached its 120s wall-clock budget before a tied verdict. Libra is keeping this intent verifying until the next check.
Unable to verify: the telemetry query was rejected before execution as containing a forbidden non-read keyword, so no production success or failure evidence was obtained.
Unable to complete verification: the available telemetry queries did not yield a valid OTEL result, and CloudWatch returned only partial execution logs without a bounded success/error count, latency, or distinct-user measurement. Verdict is inconclusive.
Libra has verdicts on 0 of 4 tracked behaviors on prod; 4 are still being checked. Libra checks hourly for 3 days after each deploy.
Two independent facts were missing from the next-steps agent's prompt, and each one caused a distinct class of wrongly-cancelled work. Both are fixed here; the harness shows neither is sufficient alone.
Cedar stores the external CRM's stage value. That agrees with the label on an untouched pipeline and diverges completely on one re-valued during a migration.
| conversation | shown to the agent | real HubSpot label | isClosed |
|---|---|---|---|
| Middough Inc. - 1 (Pirros) | Scoping (closedwon) | Scoping | false |
| Buchan - Cancelled (Pirros) | Deal DQ (226595252) | Deal DQ | true |
Verified live on staging's code via buildNextStepsPreview:
AssertionError: expected 'Status: Scoping (closedwon)\nNext Ste…' not to contain 'closedwon'
57 open tasks / 9 users / 5 orgs sit on conversations whose stored status reads
closedwon/closedlost while every linked deal reports isClosed: false.
formatIntegrationMetadataForAgent (conversation-context.ts:1128) has rendered one
<linked_crm_deal> block per linked opportunity for every other agent surface for a long time.
Each carries Deal State: open | closed/terminal , the authoritative hs_is_closed in English ,
and, when a conversation carries more than one deal, marks the single one that represents it via
pickActiveExternalCrmDeal. Its own comment names the failure it exists to prevent:
otherwise it sees several indistinguishable
<linked_crm_deal>blocks and can gate/reason off a terminal sibling.
updateNextStepsAndTasksTool , the one agent that cancels work , never called it. A
conversation with a dead sibling deal and a live active one was, to that agent, indistinguishable
from a dead conversation.
Wired in through a small pure helper over integrationMetadata the hydrated conversation already
carries. No extra read. Not via buildConversationContext, which would drag event formatting
and several queries onto this hot path for one block. Only the external-CRM entries are passed
through , the formatter also emits a Slack routing block, and routing is already this agent's
<output_policy>. Verified against production: the OLC conversation (4 linked HubSpot deals)
renders four blocks with correct labels, states and exactly one ACTIVE marker.
deal-close-task-cancel-eval.ts, 9 cases × 5 trials × 4 instruction variants per arm. Unit of
analysis is the CASE; trials are repeats, so no pooling and no p-values.
| arm | <current_state> | <trigger_event> | <linked_crm_deal> |
|---|---|---|---|
| A | closedwon | raw | , |
| B | Scoping | raw | , |
| D | Scoping | label | , |
| E | closedwon | raw | yes |
| F | Scoping | label | yes |
False cancellations (a must-keep task cancelled) out of 45:
| variant | A | B | D | E | F |
|---|---|---|---|---|---|
| baseline | 34 | 37 | 15 | 18 | 0 |
| blanket | 44 | 38 | 20 | 32 | 8 |
| exceptions | 22 | 23 | 13 | 11 | 0 |
| authoritative | 9 | 6 | 13 | 5 | 4 |
Negative controls, baseline variant, cancellations (0 = clean):
| case | shape | A | B | D | E | F |
|---|---|---|---|---|---|---|
| marlowe-live | single link, re-valued stage | 12/15 | 15/15 | 0/15 | 12/15 | 0/15 |
| kelvin-unlinked | no CRM link at all | 5/5 | 5/5 | 0/5 | 5/5 | 0/5 |
| fenwick-live | single link, re-valued stage | 2/5 | 2/5 | 0/5 | 1/5 | 0/5 |
| thorne-multilink | 2 open + 1 closed sibling | 5/5 | 5/5 | 5/5 | 0/5 | 0/5 |
| hallward-mixed | 1 dead + 1 live | 10/10 | 10/10 | 10/10 | 0/10 | 0/10 |
The two fixes own disjoint failure classes, and this is the whole argument for shipping both:
marlowe 12→0 and kelvin 5→0 across E→F;
the formatter alone (E) leaves them exactly where they were, becauseLibra has not measured any production surfaces for this change yet.