From d2ade249f66671ae98b3578d65d016ca87431320 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Mon, 11 May 2026 08:33:00 -0400 Subject: [PATCH] feat: salvage network diagnostics skills (#1731) --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- AGENTS.md | 4 +- README.md | 6 +- README.zh-CN.md | 2 +- docs/zh-CN/AGENTS.md | 4 +- docs/zh-CN/README.md | 6 +- manifests/install-modules.json | 3 + package.json | 3 + skills/cisco-ios-patterns/SKILL.md | 163 ++++++++++++++++++++++ skills/netmiko-ssh-automation/SKILL.md | 173 ++++++++++++++++++++++++ skills/network-bgp-diagnostics/SKILL.md | 167 +++++++++++++++++++++++ 12 files changed, 522 insertions(+), 13 deletions(-) create mode 100644 skills/cisco-ios-patterns/SKILL.md create mode 100644 skills/netmiko-ssh-automation/SKILL.md create mode 100644 skills/network-bgp-diagnostics/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 6e9b770f..b213aeb5 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "ecc", "source": "./", - "description": "The most comprehensive Claude Code plugin — 53 agents, 195 skills, 69 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning", + "description": "The most comprehensive Claude Code plugin — 53 agents, 198 skills, 69 legacy command shims, selective install profiles, and production-ready hooks for TDD, security scanning, code review, and continuous learning", "version": "2.0.0-rc.1", "author": { "name": "Affaan Mustafa", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 0673fbaf..1b9b6f32 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ecc", "version": "2.0.0-rc.1", - "description": "Battle-tested Claude Code plugin for engineering teams — 53 agents, 195 skills, 69 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use", + "description": "Battle-tested Claude Code plugin for engineering teams — 53 agents, 198 skills, 69 legacy command shims, production-ready hooks, and selective install workflows evolved through continuous real-world use", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/AGENTS.md b/AGENTS.md index f3a9852c..9ac4e149 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 53 specialized agents, 195 skills, 69 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 53 specialized agents, 198 skills, 69 commands, and automated hook workflows for software development. **Version:** 2.0.0-rc.1 @@ -146,7 +146,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 53 specialized subagents -skills/ — 195 workflow skills and domain knowledge +skills/ — 198 workflow skills and domain knowledge commands/ — 69 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index 99b0cfce..4d5037cb 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ If you stacked methods, clean up in this order: /plugin list ecc@ecc ``` -**That's it!** You now have access to 53 agents, 195 skills, and 69 legacy command shims. +**That's it!** You now have access to 53 agents, 198 skills, and 69 legacy command shims. ### Dashboard GUI @@ -1338,7 +1338,7 @@ The configuration is automatically detected from `.opencode/opencode.json`. |---------|-------------|----------|--------| | Agents | PASS: 53 agents | PASS: 12 agents | **Claude Code leads** | | Commands | PASS: 69 commands | PASS: 31 commands | **Claude Code leads** | -| Skills | PASS: 195 skills | PASS: 37 skills | **Claude Code leads** | +| Skills | PASS: 198 skills | PASS: 37 skills | **Claude Code leads** | | Hooks | PASS: 8 event types | PASS: 11 events | **OpenCode has more!** | | Rules | PASS: 29 rules | PASS: 13 instructions | **Claude Code leads** | | MCP Servers | PASS: 14 servers | PASS: Full | **Full parity** | @@ -1443,7 +1443,7 @@ ECC is the **first plugin to maximize every major AI coding tool**. Here's how e |---------|------------|------------|-----------|----------| | **Agents** | 53 | Shared (AGENTS.md) | Shared (AGENTS.md) | 12 | | **Commands** | 69 | Shared | Instruction-based | 31 | -| **Skills** | 195 | Shared | 10 (native format) | 37 | +| **Skills** | 198 | Shared | 10 (native format) | 37 | | **Hook Events** | 8 types | 15 types | None yet | 11 types | | **Hook Scripts** | 20+ scripts | 16 scripts (DRY adapter) | N/A | Plugin hooks | | **Rules** | 34 (common + lang) | 34 (YAML frontmatter) | Instruction-based | 13 instructions | diff --git a/README.zh-CN.md b/README.zh-CN.md index 0373aee5..1702fa54 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -160,7 +160,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**完成!** 你现在可以使用 53 个代理、195 个技能和 69 个命令。 +**完成!** 你现在可以使用 53 个代理、198 个技能和 69 个命令。 ### multi-* 命令需要额外配置 diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index 434bea5b..d52859fe 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 53 个专业代理、195 项技能、69 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 53 个专业代理、198 项技能、69 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 2.0.0-rc.1 @@ -147,7 +147,7 @@ ``` agents/ — 53 个专业子代理 -skills/ — 195 个工作流技能和领域知识 +skills/ — 198 个工作流技能和领域知识 commands/ — 69 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 0d8938bc..84413ffa 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -224,7 +224,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 53 个智能体、195 项技能和 69 个命令了。 +**搞定!** 你现在可以使用 53 个智能体、198 项技能和 69 个命令了。 *** @@ -1134,7 +1134,7 @@ opencode |---------|-------------|----------|--------| | 智能体 | PASS: 53 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 69 个 | PASS: 31 个 | **Claude Code 领先** | -| 技能 | PASS: 195 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 198 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1242,7 +1242,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|------------|------------|-----------|----------| | **智能体** | 53 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 69 | 共享 | 基于指令 | 31 | -| **技能** | 195 | 共享 | 10 (原生格式) | 37 | +| **技能** | 198 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | 暂无 | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | N/A | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | diff --git a/manifests/install-modules.json b/manifests/install-modules.json index e226cc0f..054312b9 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -512,9 +512,12 @@ "kind": "skills", "description": "Deployment workflows, Docker patterns, and infrastructure skills.", "paths": [ + "skills/cisco-ios-patterns", "skills/deployment-patterns", "skills/docker-patterns", "skills/homelab-network-setup", + "skills/netmiko-ssh-automation", + "skills/network-bgp-diagnostics", "skills/network-config-validation", "skills/network-interface-health" ], diff --git a/package.json b/package.json index df790d8c..088e63b1 100644 --- a/package.json +++ b/package.json @@ -104,6 +104,7 @@ "skills/brand-voice/", "skills/carrier-relationship-management/", "skills/claude-devfleet/", + "skills/cisco-ios-patterns/", "skills/clickhouse-io/", "skills/code-tour/", "skills/coding-standards/", @@ -187,6 +188,8 @@ "skills/messages-ops/", "skills/nanoclaw-repl/", "skills/nestjs-patterns/", + "skills/netmiko-ssh-automation/", + "skills/network-bgp-diagnostics/", "skills/network-config-validation/", "skills/network-interface-health/", "skills/nodejs-keccak256/", diff --git a/skills/cisco-ios-patterns/SKILL.md b/skills/cisco-ios-patterns/SKILL.md new file mode 100644 index 00000000..22f9840a --- /dev/null +++ b/skills/cisco-ios-patterns/SKILL.md @@ -0,0 +1,163 @@ +--- +name: cisco-ios-patterns +description: Cisco IOS and IOS-XE review patterns for show commands, config hierarchy, wildcard masks, ACL placement, interface hygiene, and safe change-window verification. +origin: community +--- + +# Cisco IOS Patterns + +Use this skill when reviewing Cisco IOS or IOS-XE snippets, building a +change-window checklist, or explaining how to collect evidence from a router or +switch without making the incident worse. + +## When to Use + +- Reviewing IOS or IOS-XE configuration before a planned change. +- Choosing read-only `show` commands for troubleshooting. +- Checking ACL wildcard masks and interface direction. +- Explaining global, interface, routing process, and line configuration modes. +- Verifying that a change landed in running config and was saved intentionally. + +## Operating Rules + +Treat IOS examples as patterns, not paste-ready production changes. Confirm the +platform, interface names, current config, rollback path, and out-of-band access +before making changes on a real device. + +Prefer this workflow: + +1. Capture current state with read-only commands. +2. Review the exact candidate config. +3. Confirm management access cannot be locked out. +4. Apply the smallest change in a maintenance window. +5. Re-read state, compare to the baseline, then save only after validation. + +## Mode Reference + +```text +Router> enable +Router# show running-config +Router# configure terminal +Router(config)# interface GigabitEthernet0/1 +Router(config-if)# description UPLINK-TO-CORE +Router(config-if)# no shutdown +Router(config-if)# exit +Router(config)# end +Router# show running-config interface GigabitEthernet0/1 +``` + +`running-config` is active memory. `startup-config` is what survives reload. +Do not save a change just because a command was accepted; validate behavior +first, then use `copy running-config startup-config` if the change is approved. + +## Read-Only Collection + +```text +show version +show inventory +show processes cpu sorted +show memory statistics +show logging +show running-config | section line vty +show running-config | section interface +show running-config | section router bgp +show ip interface brief +show interfaces +show interfaces status +show vlan brief +show mac address-table +show spanning-tree +show ip route +show ip protocols +show ip access-lists +show route-map +show ip prefix-list +``` + +Collect the specific section you need instead of dumping full config into a +ticket when the config may contain secrets, customer names, or private topology. + +## Wildcard Masks + +IOS ACL and many routing statements use wildcard masks, not subnet masks. + +```text +Subnet mask Wildcard mask +255.255.255.255 0.0.0.0 +255.255.255.252 0.0.0.3 +255.255.255.0 0.0.0.255 +255.255.0.0 0.0.255.255 +``` + +Review wildcard masks before deployment. A subnet mask accidentally used as a +wildcard can match far more traffic than intended. + +```text +ip access-list extended WEB-IN + 10 permit tcp 192.0.2.0 0.0.0.255 any eq 443 + 999 deny ip any any log +``` + +Every ACL has an implicit deny at the end. Add an explicit logged deny when the +operational goal includes observing misses, and confirm logging volume is safe. + +## ACL Placement Review + +Before applying an ACL to an interface, answer these questions: + +- Which traffic direction is being filtered, `in` or `out`? +- Is management traffic sourced from a known jump host or management subnet? +- Is there an explicit permit for required routing, DNS, NTP, monitoring, or + application traffic? +- Are hit counters available from a safe test source? +- Is there a rollback command and an active console or out-of-band path? + +Do not test reachability by removing firewall or ACL protections. Read counters, +logs, and route state first. + +## Interface Hygiene + +```text +interface GigabitEthernet0/1 + description UPLINK-TO-CORE + switchport mode trunk + switchport trunk allowed vlan 10,20,30 + switchport trunk native vlan 999 + no shutdown +``` + +Use clear descriptions, explicit switchport mode, and documented native VLANs. +On routed interfaces, confirm the mask, peer addressing, and routing process +before assuming link state means forwarding is correct. + +## Change-Window Verification + +Use before/after checks that match the actual change. + +```text +show running-config | section interface GigabitEthernet0/1 +show interfaces GigabitEthernet0/1 +show logging | include GigabitEthernet0/1|changed state|line protocol +show ip route +show ip access-lists +``` + +For routing changes, also capture neighbor state and route tables before and +after the change. For ACL changes, compare hit counters from a planned test +source rather than relying on a generic ping. + +## Anti-Patterns + +- Applying a generated config without a device-specific diff. +- Saving configuration before post-change checks pass. +- Using a subnet mask where IOS expects a wildcard mask. +- Applying an ACL to the wrong interface direction. +- Troubleshooting by disabling ACLs, route policies, or authentication. +- Pasting full configs into public tools without sanitizing secrets and topology. + +## See Also + +- Agent: `network-config-reviewer` +- Agent: `network-troubleshooter` +- Skill: `network-config-validation` +- Skill: `network-interface-health` diff --git a/skills/netmiko-ssh-automation/SKILL.md b/skills/netmiko-ssh-automation/SKILL.md new file mode 100644 index 00000000..1374ff53 --- /dev/null +++ b/skills/netmiko-ssh-automation/SKILL.md @@ -0,0 +1,173 @@ +--- +name: netmiko-ssh-automation +description: Safe Python Netmiko patterns for read-only collection, bounded batch SSH, TextFSM parsing, guarded config changes, timeouts, and network automation error handling. +origin: community +--- + +# Netmiko SSH Automation + +Use this skill when writing or reviewing Python automation that connects to +network devices with Netmiko. Keep the default path read-only; config changes +need a separate change window, peer review, and rollback plan. + +## When to Use + +- Collecting `show` command output across routers, switches, or firewalls. +- Building a small audit script for interface, routing, or config evidence. +- Adding timeouts and exception handling to network SSH scripts. +- Parsing command output with TextFSM when a template exists. +- Reviewing automation before it touches production devices. + +## Safety Defaults + +- Start with read-only `send_command()` collection. +- Keep inventory small and explicit; do not sweep whole address ranges. +- Use environment variables, a vault, or `getpass`; never hardcode credentials. +- Set connection and read timeouts. +- Limit concurrency so older devices are not overloaded. +- Require an explicit operator flag before `send_config_set()`. +- Do not call `save_config()` until the change has been verified and approved. + +## Read-Only Connection Pattern + +```python +import os +from getpass import getpass +from netmiko import ConnectHandler +from netmiko.exceptions import ( + NetmikoAuthenticationException, + NetmikoTimeoutException, + ReadTimeout, +) + +device = { + "device_type": "cisco_ios", + "host": "192.0.2.10", + "username": os.environ.get("NETMIKO_USERNAME") or input("Username: "), + "password": os.environ.get("NETMIKO_PASSWORD") or getpass("Password: "), + "secret": os.environ.get("NETMIKO_ENABLE_SECRET"), + "conn_timeout": 10, + "auth_timeout": 20, + "banner_timeout": 15, + "read_timeout_override": 30, +} + +try: + with ConnectHandler(**device) as conn: + if device.get("secret") and not conn.check_enable_mode(): + conn.enable() + output = conn.send_command("show ip interface brief", read_timeout=30) + print(output) +except NetmikoAuthenticationException: + print("Authentication failed") +except NetmikoTimeoutException: + print("SSH connection timed out") +except ReadTimeout: + print("Command read timed out") +``` + +Use placeholder addresses from documentation ranges in examples. Keep real +inventory in an ignored local file or a secrets-managed system. + +## Batch Collection + +```python +from concurrent.futures import ThreadPoolExecutor, as_completed +from typing import Any + +def collect_show(device: dict[str, Any], command: str) -> dict[str, Any]: + host = device["host"] + try: + with ConnectHandler(**device) as conn: + output = conn.send_command(command, read_timeout=45) + return {"host": host, "ok": True, "output": output} + except (NetmikoAuthenticationException, NetmikoTimeoutException, ReadTimeout) as exc: + return {"host": host, "ok": False, "error": type(exc).__name__} + +results = [] +with ThreadPoolExecutor(max_workers=8) as pool: + futures = [pool.submit(collect_show, device, "show version") for device in devices] + for future in as_completed(futures): + results.append(future.result()) +``` + +Keep `max_workers` low unless the device estate and AAA systems are known to +handle higher connection volume. + +## Structured Parsing + +Netmiko can ask TextFSM, TTP, or Genie to parse supported command output. Treat +parser output as an optimization, not the only evidence path. + +```python +with ConnectHandler(**device) as conn: + parsed = conn.send_command( + "show ip interface brief", + use_textfsm=True, + raise_parsing_error=False, + read_timeout=30, + ) + +if isinstance(parsed, str): + print("No parser template matched; store raw output for review") +else: + for row in parsed: + print(row) +``` + +If parsing drives a blocking decision, keep the raw command output alongside +the parsed result so an operator can inspect mismatches. + +## Guarded Config Pattern + +```python +import os + +commands = [ + "interface GigabitEthernet0/1", + "description CHANGE-1234 UPLINK-TO-CORE", +] + +apply_changes = os.environ.get("APPLY_NETWORK_CHANGES") == "1" + +if not apply_changes: + print("Dry run only. Candidate commands:") + print("\n".join(commands)) +else: + with ConnectHandler(**device) as conn: + conn.enable() + before = conn.send_command("show running-config interface GigabitEthernet0/1") + output = conn.send_config_set(commands) + after = conn.send_command("show running-config interface GigabitEthernet0/1") + print(before) + print(output) + print(after) + print("Verify behavior before saving startup config.") +``` + +Saving the config is a separate approval step. In production, include a rollback +snippet and capture before/after evidence in the change record. + +## Review Checklist + +- Does the script identify an explicit inventory source? +- Are credentials absent from source, logs, and exception messages? +- Are `conn_timeout`, `auth_timeout`, and command `read_timeout` set? +- Are failures reported per device without stopping the whole batch? +- Does the script avoid broad scans and unbounded concurrency? +- Are config changes behind a dry-run or explicit operator flag? +- Is `save_config()` separate from the initial push and tied to verification? + +## Anti-Patterns + +- Hardcoding passwords, enable secrets, or private keys in source. +- Sending config commands as the default code path. +- Running automation against a CIDR range instead of a reviewed inventory. +- Logging full running configs to shared systems without sanitization. +- Treating parser success as proof that the device state is correct. + +## See Also + +- Skill: `cisco-ios-patterns` +- Skill: `network-config-validation` +- Skill: `network-interface-health` diff --git a/skills/network-bgp-diagnostics/SKILL.md b/skills/network-bgp-diagnostics/SKILL.md new file mode 100644 index 00000000..d5c7eecf --- /dev/null +++ b/skills/network-bgp-diagnostics/SKILL.md @@ -0,0 +1,167 @@ +--- +name: network-bgp-diagnostics +description: Diagnostics-only BGP troubleshooting patterns for neighbor state, route exchange, prefix policy, AS path inspection, and safe evidence collection. +origin: community +--- + +# Network BGP Diagnostics + +Use this skill when a BGP session is down, flapping, established with missing +routes, or advertising unexpected prefixes. The default workflow is read-only +evidence collection; policy and reset actions belong in a reviewed change +window. + +## When to Use + +- BGP neighbors are stuck in Idle, Connect, Active, OpenSent, or OpenConfirm. +- A session is Established but expected prefixes are missing. +- A route-map, prefix-list, max-prefix limit, or AS path policy may be filtering + routes. +- You need before/after evidence for a BGP change. +- You are reviewing automation that parses BGP summary output. + +## Read-Only Triage Flow + +1. Identify the exact neighbor, address family, VRF, and local/remote ASNs. +2. Capture summary state and last reset reason. +3. Prove reachability to the peer source address. +4. Check route policy references before assuming transport failure. +5. Compare advertised, received, and installed routes where the platform + supports those commands. + +```text +show bgp summary +show bgp neighbors +show ip route +show tcp brief | include |:179 +show logging | include BGP| +show running-config | section router bgp +show ip prefix-list +show route-map +``` + +Use platform-specific address-family commands when the device uses VRFs, IPv6, +VPNv4, or EVPN. Do not assume global IPv4 unicast. + +## State Interpretation + +| State | First checks | +| --- | --- | +| Established with prefix count | Route exchange is up; inspect policy and table selection | +| Established with zero prefixes | Check inbound policy, max-prefix, advertised routes, and AFI/SAFI | +| Active | TCP session is not completing; check routing, source, ACLs, and peer reachability | +| Connect | TCP connection is in progress; check path and remote listener | +| OpenSent/OpenConfirm | TCP works; check ASN, authentication, timers, capabilities, and logs | +| Idle | Neighbor may be disabled, missing config, blocked by policy, or backoff timer | + +## Transport Checks + +```text +ping source +traceroute source +show ip route +show bgp neighbors | include BGP state|Last reset|Local host|Foreign host +``` + +If the peer is sourced from a loopback, confirm both directions route to the +loopback addresses and that the neighbor config uses the expected update source. + +Avoid disabling ACLs or firewall policy as a diagnostic shortcut. Read hit +counters, logs, and path state first. + +## Route Policy Checks + +```text +show bgp neighbors advertised-routes +show bgp neighbors routes +show ip prefix-list +show route-map +show bgp +``` + +Some platforms require additional configuration before `received-routes` is +available. Do not add that configuration during incident triage unless the +operator approves the change. + +## AS Path And Prefix Review + +```text +show bgp regexp _65001_ +show bgp regexp ^65001$ +show bgp +show bgp neighbors advertised-routes | include Network|Path| +``` + +Use AS-path regex carefully. `_65001_` matches AS 65001 as a token. Plain +`65001` can match longer ASNs or unrelated text. + +## Parser Pattern + +```python +import re +from typing import Any + +BGP_SUMMARY_RE = re.compile( + r"^(?P\d{1,3}(?:\.\d{1,3}){3})\s+" + r"(?P\d+)\s+" + r"(?P\d+)\s+" + r"(?P\d+)\s+" + r"(?P\d+)\s+" + r"(?P\d+)\s+" + r"(?P\d+)\s+" + r"(?P\d+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)$", + re.M, +) + +def parse_bgp_summary(raw: str) -> list[dict[str, Any]]: + rows = [] + for match in BGP_SUMMARY_RE.finditer(raw): + state_or_prefixes = match.group("state_or_prefixes") + if state_or_prefixes.isdigit(): + state = "Established" + prefixes_received = int(state_or_prefixes) + else: + state = state_or_prefixes + prefixes_received = None + rows.append({ + "neighbor": match.group("neighbor"), + "remote_as": int(match.group("remote_as")), + "state": state, + "prefixes_received": prefixes_received, + "uptime": match.group("uptime"), + }) + return rows +``` + +Prefer structured parser output when available, but store raw output with the +incident record because BGP summary formats vary by platform and address family. + +## Change-Window Only + +These actions can affect routing and should not be suggested as automatic +diagnostics: + +- Clearing a BGP session. +- Changing neighbor authentication, timers, update source, route-maps, or + prefix-lists. +- Enabling additional received-route storage. +- Relaxing firewall, ACL, or control-plane policy. + +If a reset is approved, prefer the least disruptive soft or route-refresh option +supported by the platform and document exactly why it is safe. + +## Anti-Patterns + +- Assuming `Active` always means the remote side is down. +- Ignoring VRF, address family, or update-source differences. +- Using broad AS-path regex without token boundaries. +- Hard-resetting a peer before reading last reset reason and logs. +- Treating missing `received-routes` output as proof that no routes arrived. + +## See Also + +- Skill: `cisco-ios-patterns` +- Skill: `network-config-validation` +- Skill: `network-interface-health`