prompts: add dead_dn_finder (cross-server, story C from threads)
First cross-server prompt landing. Story C from
axl/agent-threads/cross-server-prompts/. Closes a concrete cucx-docs
open finding (orphaned paging DNs 1302 / 1304 at /systems/paging/
carried as "confirm with operator").
A DN is "definitively dead" when:
1. It exists in numplan but has no devicenumplanmap entry —
no device claims it as a line (mcaxl, required)
2. It is not referenced as a UCCX trigger entry point
(mcuccx, strongly recommended)
3. It has no recent CDR activity (mcsiphon, optional)
Each layer narrows the candidate set; the intersection is "safe to
retire — verified across CUCM dial plan + UCCX contact center + CDR
activity."
The prompt embodies the architectural decisions confirmed in
cross-server-prompts/002:
- Per-primary-lens placement (Q1): lives in mcaxl because the
dial plan is its primary lens
- Graceful degradation with explicit gaps (Q2): the verdict
declares MCP availability up-front and adjusts confidence per
sibling; distinguishes connected-but-broken (include error) from
not-connected (note "unavailable")
- Normal naming, no cross_* prefix (Q3): just "dead_dn_finder"
Tier output: "definitively dead" / "likely dead, partial coverage" /
"structurally orphan, unconfirmed" / "active". The cucx-docs paging
DNs (1302, 1304) get explicit name-callouts in the verdict if they
appear, closing the loop back to /systems/paging/.
Tests: registration sentinel updated to 13 prompts. 238/238 passing.
Live-cluster smoke test pending — cucx-docs will run against the
Bingham cluster once they consume this thread direction.
This commit is contained in:
parent
a07f8c7291
commit
ee1e058559
@ -20,6 +20,7 @@ shim is where the parameter contract lives.
|
||||
from . import (
|
||||
audit_routing,
|
||||
cucm_sql_help,
|
||||
dead_dn_finder,
|
||||
did_block_overlap,
|
||||
hunt_pilot_audit,
|
||||
inbound_did_audit,
|
||||
@ -35,6 +36,7 @@ from . import (
|
||||
__all__ = [
|
||||
"audit_routing",
|
||||
"cucm_sql_help",
|
||||
"dead_dn_finder",
|
||||
"did_block_overlap",
|
||||
"hunt_pilot_audit",
|
||||
"inbound_did_audit",
|
||||
|
||||
230
src/mcaxl/prompts/dead_dn_finder.py
Normal file
230
src/mcaxl/prompts/dead_dn_finder.py
Normal file
@ -0,0 +1,230 @@
|
||||
"""Cross-server prompt: find DNs that are definitively dead.
|
||||
|
||||
A DN is "definitively dead" when:
|
||||
|
||||
1. It exists in `numplan` but has no `devicenumplanmap` entry — no
|
||||
device claims it as a line (mcaxl)
|
||||
2. It is not referenced as a UCCX trigger entry point (mcuccx)
|
||||
3. It has no recent CDR activity (mcsiphon)
|
||||
|
||||
Each layer narrows the candidate set; the final list is "safe to
|
||||
retire — verified across CUCM dial plan + UCCX contact center + CDR
|
||||
activity." Closes findings of the shape "DN exists in numplan but
|
||||
nobody knows whose it is" (e.g. orphaned paging DNs that were created
|
||||
for a use-case that's since been retired).
|
||||
|
||||
This is a **cross-server** prompt — see
|
||||
`axl/agent-threads/cross-server-prompts/` for the architectural
|
||||
discussion that motivated the per-primary-lens placement (this prompt
|
||||
lives in mcaxl because the dial plan is its primary lens) and the
|
||||
graceful-degradation behavior when sibling MCPs are unavailable.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ._common import render_schema_block
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..docs_loader import DocsIndex
|
||||
|
||||
|
||||
_KEYWORDS = [
|
||||
"directory number", "numplan", "device numplan map",
|
||||
"orphan DN", "paging", "CTI route point",
|
||||
]
|
||||
|
||||
|
||||
def render(docs: "DocsIndex | None", days_inactive: int = 30) -> str:
|
||||
"""Find DNs with no device, no UCCX trigger, and no recent CDR
|
||||
activity — the intersection is "definitively dead."
|
||||
|
||||
Args:
|
||||
days_inactive: window for "no recent CDR activity" check, in days.
|
||||
Default 30. Operator may widen for low-volume DNs (paging,
|
||||
emergency lines) where 30 days is genuinely quiet.
|
||||
|
||||
Required MCP servers:
|
||||
- mcaxl (always — this prompt lives in mcaxl, primary data source)
|
||||
|
||||
Strongly recommended:
|
||||
- mcuccx — cross-checks UCCX trigger references. Without it, the
|
||||
prompt cannot rule out "this DN is a UCCX trigger entry point
|
||||
even though no CUCM device claims it."
|
||||
|
||||
Optional:
|
||||
- mcsiphon — confirms zero CDR activity in the time window.
|
||||
Without it, the prompt produces a "structurally orphan" verdict
|
||||
but cannot confirm operational quietness.
|
||||
"""
|
||||
schema_block = render_schema_block(
|
||||
docs, _KEYWORDS, max_chunks=3, max_chars_per_chunk=800
|
||||
)
|
||||
|
||||
return f"""# Dead-DN Finder (cross-server audit)
|
||||
|
||||
Find directory numbers that are **definitively dead** — exist in the
|
||||
dial plan but have no claiming device, no UCCX trigger reference, and
|
||||
no recent CDR activity. The intersection of those three conditions is
|
||||
"safe to retire" with high confidence.
|
||||
|
||||
## MCP availability — declare up-front in the verdict
|
||||
|
||||
Before producing the final list, **state which sibling MCP servers are
|
||||
connected** in your output. This determines the verdict-confidence
|
||||
level:
|
||||
|
||||
- **All three (mcaxl + mcuccx + mcsiphon) connected** → "definitively
|
||||
dead" verdict possible
|
||||
- **mcaxl + mcuccx, no mcsiphon** → "structurally orphan, CDR activity
|
||||
unconfirmed" — operator may want to widen the time window or check
|
||||
CDRs manually before retiring
|
||||
- **mcaxl + mcsiphon, no mcuccx** → "no device + no recent activity,
|
||||
UCCX trigger status unconfirmed" — risk: a paging DN driven by a
|
||||
UCCX self-service script would falsely qualify
|
||||
- **mcaxl only** → "structurally orphan from CUCM perspective only" —
|
||||
weakest verdict; treat as candidates-for-investigation, not
|
||||
candidates-for-retirement
|
||||
|
||||
If a sibling MCP is **connected but errors mid-call** (different from
|
||||
not-connected), include the error message in the verdict's confidence
|
||||
notes — a connected-but-broken sibling is its own state.
|
||||
|
||||
## Step 1 — Structurally-orphan DNs (mcaxl, required)
|
||||
|
||||
Find every DN that exists in `numplan` but has no `devicenumplanmap`
|
||||
entry claiming it:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
np.dnorpattern AS dn,
|
||||
rp.name AS partition,
|
||||
np.description,
|
||||
tpu.name AS pattern_type,
|
||||
np.alertingname,
|
||||
np.fkroutepartition AS partition_pkid
|
||||
FROM numplan np
|
||||
LEFT OUTER JOIN routepartition rp ON np.fkroutepartition = rp.pkid
|
||||
LEFT OUTER JOIN typepatternusage tpu ON np.tkpatternusage = tpu.enum
|
||||
LEFT OUTER JOIN devicenumplanmap m ON m.fknumplan = np.pkid
|
||||
WHERE m.fknumplan IS NULL
|
||||
AND tpu.name = 'Device' -- only true DNs, not route patterns
|
||||
ORDER BY np.dnorpattern;
|
||||
```
|
||||
|
||||
The `LEFT OUTER JOIN ... WHERE m.fknumplan IS NULL` is the
|
||||
"orphan-detection" idiom — DNs that are NOT in the join table.
|
||||
|
||||
Filter to `tpu.name = 'Device'` because numplan also holds route
|
||||
patterns, translation patterns, etc. — those are not DNs and aren't
|
||||
relevant here. Verify the enum name matches by running
|
||||
`axl_describe_table("typepatternusage")` first if you're unsure.
|
||||
|
||||
## Step 2 — UCCX trigger cross-check (mcuccx, strongly recommended)
|
||||
|
||||
A DN can be a UCCX trigger entry point even with no CUCM device
|
||||
claiming it — UCCX scripts can answer calls directed at a CTI route
|
||||
point that fronts the trigger.
|
||||
|
||||
If `mcuccx` is connected, call `list_triggers()` and cross-reference
|
||||
the result against the orphan candidates from Step 1:
|
||||
|
||||
- Each UCCX trigger has a `cti_route_point_dn` (or similar field —
|
||||
consult mcuccx's tool output schema) — that's a DN the trigger
|
||||
answers on
|
||||
- Remove from the candidate list any DN that appears as a trigger entry
|
||||
point
|
||||
|
||||
If `mcuccx` is **not connected**, note this explicitly in your verdict:
|
||||
*"UCCX trigger cross-check unavailable (mcuccx not connected); orphan
|
||||
DNs in the result MAY include trigger entry points — verify before
|
||||
retiring any DN that resembles a contact-center entry point."*
|
||||
|
||||
## Step 3 — CDR activity confirmation (mcsiphon, optional)
|
||||
|
||||
If `mcsiphon` is connected, for each remaining candidate DN, check
|
||||
recent CDR activity via `cdr_query_calls(start, end, called_number=DN)`:
|
||||
|
||||
- Time window: `{days_inactive}` days back from today
|
||||
- Field to check: count of returned records; if > 0, the DN had
|
||||
activity (calls placed to it) in the window
|
||||
- Note: mcsiphon's CDR records use `dateTimeOrigination_iso_local` —
|
||||
the values are local-cluster-time, NOT UTC (see
|
||||
`cucm-schema-cheatsheet.md` for the localization convention). Don't
|
||||
do timezone conversion on the values.
|
||||
|
||||
DNs with zero CDR activity in the window pass this check.
|
||||
|
||||
If `mcsiphon` is **not connected**, note this explicitly:
|
||||
*"CDR activity confirmation unavailable (mcsiphon not connected);
|
||||
orphan DNs in the result are structurally orphan but operational
|
||||
quietness is unverified."*
|
||||
|
||||
## Step 4 — Verdict per candidate DN
|
||||
|
||||
For each remaining DN, produce a structured entry:
|
||||
|
||||
```
|
||||
DN: 1302
|
||||
Partition: Internal-PT
|
||||
Description: SNRC Paging
|
||||
Verdict: definitively dead
|
||||
Evidence:
|
||||
- mcaxl: no device claims this DN (no devicenumplanmap entry)
|
||||
- mcuccx: not referenced as a UCCX trigger
|
||||
- mcsiphon: zero CDR activity in the last {days_inactive} days
|
||||
Confidence: high (all three sibling MCPs reported)
|
||||
Recommended action: safe to retire
|
||||
```
|
||||
|
||||
For partial-coverage cases (some MCPs unavailable), the `Evidence`
|
||||
block lists what was checked and what was skipped, and `Confidence`
|
||||
adjusts down accordingly.
|
||||
|
||||
## Findings to surface
|
||||
|
||||
Group the output by verdict tier:
|
||||
|
||||
- **Definitively dead** (passed all three available checks) — primary
|
||||
retirement candidates
|
||||
- **Likely dead, partial coverage** (passed mcaxl + 1 sibling) —
|
||||
retirement candidates with a verification step required
|
||||
- **Structurally orphan, CDR/UCCX unconfirmed** (mcaxl-only) —
|
||||
candidates for investigation, not retirement
|
||||
- **Active** (failed any check) — not orphan; do not retire. List
|
||||
briefly with reason ("appears as UCCX trigger 'PatientIntakeQueue'",
|
||||
"had 47 calls in the last 30 days", etc.)
|
||||
|
||||
## Common-cause cluster of findings
|
||||
|
||||
Patterns to watch for in the result:
|
||||
|
||||
- **Paging DNs** with descriptions like "All Campus Paging", "<unit>
|
||||
Paging", "Code <X> Page" — frequently created during initial
|
||||
deployment and never connected to a real device after the paging
|
||||
product changed (Algo, InformaCast, native CUCM). Often genuinely
|
||||
dead; sometimes a forgotten endpoint
|
||||
- **Conference / IVR DNs** — may LOOK orphan from CUCM's perspective
|
||||
but be UCCX entry points. The mcuccx check is critical here
|
||||
- **Test / staging DNs** with descriptions naming a tester or a date
|
||||
— almost always safe to retire if the date is > 1 year old
|
||||
- **Single-DN range patterns** that look like wildcard escapes — verify
|
||||
the pattern is actually a DN (`tpu.name = 'Device'`) and not a route
|
||||
pattern that happened to match the orphan-detection query
|
||||
|
||||
## Reference: DN-vs-pattern semantics, devicenumplanmap join
|
||||
|
||||
""" + schema_block + """
|
||||
|
||||
Produce a structured findings report grouped by verdict tier. Lead
|
||||
with the MCP-availability declaration so the operator immediately
|
||||
sees the confidence level. For each definitively-dead DN, include the
|
||||
recommended action. For active DNs, list briefly to confirm they were
|
||||
checked but ruled out — don't enumerate every active line.
|
||||
|
||||
If `1302` (SNRC Paging) or `1304` (All Campus Paging) appear in the
|
||||
candidate set, those are explicitly tracked findings on the cucx-docs
|
||||
side — flag them by name in the verdict so the loop closes back to the
|
||||
`/systems/paging/` page.
|
||||
"""
|
||||
@ -560,6 +560,24 @@ def did_block_overlap(block_pattern: str) -> str:
|
||||
return _prompts.did_block_overlap.render(_docs, block_pattern)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def dead_dn_finder(days_inactive: int = 30) -> str:
|
||||
"""Find DNs that are definitively dead — exist in numplan but have
|
||||
no claiming device, no UCCX trigger reference, and no recent CDR
|
||||
activity. Cross-server prompt: composes mcaxl (required) + mcuccx
|
||||
(strongly recommended) + mcsiphon (optional). Closes findings of
|
||||
the shape "DN exists but nobody knows whose it is" — the canonical
|
||||
Bingham example is paging DNs `1302` / `1304` carried as "confirm
|
||||
with operator" on `/systems/paging/`.
|
||||
|
||||
Args:
|
||||
days_inactive: window for "no recent CDR activity" check, in
|
||||
days (default 30). Widen for low-volume DNs (paging,
|
||||
emergency lines) where 30 days is genuinely quiet.
|
||||
"""
|
||||
return _prompts.dead_dn_finder.render(_docs, days_inactive)
|
||||
|
||||
|
||||
@mcp.prompt
|
||||
def partition_summary(partition_name: str | None = None) -> str:
|
||||
"""Compose a "what is this partition for?" report from existing
|
||||
|
||||
@ -171,6 +171,7 @@ def test_all_prompts_registered_in_server():
|
||||
"whoami",
|
||||
"did_block_overlap",
|
||||
"partition_summary",
|
||||
"dead_dn_finder",
|
||||
}, f"unexpected prompt set: {names}"
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user