flightcheck: add TOPIC-020 — verify handoff agent id is not the shipped placeholder - #218
Conversation
…ed placeholder Adds a conditional Agent Handoff check (TOPIC-020, High priority). For each auto-handoff template topic that is ENABLED (component status == "Active"), it locates the SetVariable node assigning Topic.HandoffAgentId inside the topic dialog and verifies the resolved value is a concrete GPT id, not the shipped placeholder "AgentIdentifier" (and not blank). A placeholder or blank value means the handoff fails silently at runtime, which is the customer scenario from the issue. The check reads Power Virtual Agents DialogComponents via a new pva_client.get_dialog_components(bot_id), mirroring get_knowledge_sources: it iterates botComponentChanges and unwraps each change["component"], returning inner DialogComponent dicts. The Island Gateway botcomponents API is in the validated tier; grounding traces to tests/fixtures/cassettes/island_gateway_botcomponents.yaml (interaction 2, the POST botcomponents response). The captured handoff component ships status="Inactive" and expressionText "\"AgentIdentifier\"", so the check only fires on enabled topics and flags the unreplaced placeholder. Gating is conservative: no PVA client / not configured / missing bot id / fetch error / no enabled handoff topic all return no results. Emits per-topic checkpoints TOPIC-020-001, -002, ... (Passed / Failed / Warning when the SetVariable node is absent). Wired into cli.py FULL_SCOPE and a standalone "handoff" scope. Tests build DialogComponent dicts inline from the cassette shape (no separate mock module, matching the graph_connector_kb PVA pattern) and cover gating, good config, placeholder-still-set, blank value, and missing SetVariable. Fixes #152 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
daeunJe0ng
left a comment
There was a problem hiding this comment.
Code review — TOPIC-020 handoff agent id check
Verdict: Approve with nits
Reviewed the new check, the get_dialog_components client method, the CLI wiring, and the test module against the cardinal rules in flightcheck/AGENTS.md and tests/AGENTS.md.
What holds up well:
get_dialog_components(bot_id)mirrorsget_knowledge_sourcesexactly (same endpoint/headers/extraction) — reuses an already-registered API tier, no new grounding surface.- Gating is correct and fails safe: returns
[]when PVA is absent/unconfigured, bot id is missing, no enabled (Active) auto-handoff topic exists, or the client raises. CheckResultfields and theStatus/Priority/Roleenum values all matchrunner.py.- Tests are cassette-grounded (validated tier, not the banned placeholder tier): every mock field traces to
island_gateway_botcomponents.yaml, and only the two real captured values (status,expressionText) are varied. Gating / good / placeholder / blank / missing-SetVariable / multi-topic indexing are all covered.
One nit inline (non-blocking) on the FAILED-branch message. No blocking or should-fix bugs found.
|
Addressed the code-review nit in 8135710: the FAILED result text now branches on the failure mode. A blank |
The FAILED result text previously always claimed the variable held the shipped placeholder 'AgentIdentifier', which is inaccurate for the blank-value case. Branch the wording so a blank Topic.HandoffAgentId is reported as blank and the placeholder case still names the placeholder. Strengthen the blank-value test to assert the accurate wording. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…older-agent-identifiers-in-enable
…ccelerator placeholders (catch renamed handoff clones/accelerators per #152) Identify a handoff topic by the presence of a SetVariable assigning Topic.HandoffAgentId, not by a schemaName containing "agenthandoff". Makers clone/rename the sample template or enable an out-of-the-box Handoff Accelerator, so the enabled topics do not carry the shipped "Agenthandoff-scenarioname" name. Keying on the name skipped exactly the enabled, misconfigured topics #152 exists to catch (confirmed live: 2 of 4 enabled handoff topics had schemaNames without "agenthandoff"). Detect the full placeholder family: the sample template ships "AgentIdentifier"; accelerators ship system-prefixed variants such as "ServiceNowAgentIdentifier" (observed on live OOTB accelerators). Match a case-insensitive "...AgentIdentifier" suffix so an enabled accelerator left on its own placeholder fails instead of passing. Real target ids are prefixed GUIDs (T_<guid>, P_<guid>) and never end that way. Give two-path remediation on failure: set a valid target agent id, or disable the topic if the handoff was unintended. Either reaches a correct end state. Drop the unreachable WARNING branch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 06a09025-d97f-4f25-97af-cf2e37ae1d6d
|
Reviewed PR #218 — checked out the branch, read the check, the 1. The 2. Non-literal 3. Minor:
CC: @daeunJe0ng |
Implements the FlightCheck check requested in #152.
Checkpoint:
TOPIC-020Category: Agent Handoff
Priority: High
Conditional? Yes — fires only for ENABLED handoff topics (
status == "Active"). Not-configured PVA, missing bot id, fetch errors, and no-enabled-handoff-topic all return no results.What identifies a handoff topic: a topic contains a
SetVariablenode assigningTopic.HandoffAgentId. Identity is that variable, NOT the schemaName. Makers clone and rename the sample template (agent-handoff.md Step 1) or enable an out-of-the-box Handoff Accelerator, so real enabled topics do not carry theAgenthandoff-scenarionamename. Keying on the name would skip exactly the topics the maker enabled and misconfigured — the case #152 exists to catch.What it validates: For each enabled handoff topic, resolve
Topic.HandoffAgentIdand confirm it is a concrete GPT id, not a shipped placeholder and not blank. Placeholder detection covers the whole family: the sample template ships"AgentIdentifier"; the OOTB accelerators (Workday / ServiceNow) ship system-prefixed variants such as"ServiceNowAgentIdentifier". Match is a case-insensitive...AgentIdentifiersuffix — real target ids are prefixed GUIDs (T_<guid>,P_<guid>) and never end that way. A placeholder or blank value makes the handoff fail at runtime.Remediation (two paths): On failure the check offers both correct end states — set a valid target agent id (agent-handoff.md Step 4), OR disable the topic if the handoff was unintended (returns it to the OOTB default). Either resolves the failure.
API tier: validated
Mock source:
tests/fixtures/cassettes/island_gateway_botcomponents.yaml(interaction 2, the POST botcomponents response). The captured handoffDialogComponentshipsstatus="Inactive"andexpressionText: "\"AgentIdentifier\"". Read viapva_client.get_dialog_components(bot_id), mirroringget_knowledge_sources.status == "Active"meaning "enabled" is inferred; the captured cassette only holds the disabled template. Enabled shape is consistent with other live components but unproven pending a captured enabled cassette. Validated live instead (below).Tests:
tests/flightcheck/checks/test_agent_handoff.py— gating (no client / not configured / no bot id / no handoff topic), identity by SetVariable (a handoff-named topic without the SetVariable is ignored; a renamed clone with the placeholder fails), GOOD (concrete id → Passed), BAD (sample placeholder → Failed; accelerator-prefixedServiceNowAgentIdentifier→ Failed; blank → Failed), and two-path remediation text. DialogComponent dicts are built inline from the cassette shape.Live E2E (EmployeeHub tenant): ran the real check across the accessible ServiceNow environments. Of the handoff topics found, 4 were enabled: a blank-target accelerator → Failed; an
AgentIdentifiertemplate → Failed; anAgentIdentifiertemplate on a second env → Failed; a renamed accelerator with a concreteT_<guid>id → Passed. 2 of the 4 enabled topics had schemaNames withoutagenthandoff, which the previous schemaName gate would have skipped. No regression.Local runs: 15 targeted (
test_agent_handoff.py), 1011 full flightcheck suite — all passing; ruff clean on both changed files.Fixes #152