From 3595241bcd9d8e8bcff899a22e631be6afb58b3f Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 9 Oct 2025 12:41:22 -0400 Subject: [PATCH 01/46] committing bevs changes from previous branch in private space --- kubernetes-monitoring/unstyled.html | 459 ++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 kubernetes-monitoring/unstyled.html diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html new file mode 100644 index 000000000..e45f50b43 --- /dev/null +++ b/kubernetes-monitoring/unstyled.html @@ -0,0 +1,459 @@ + + + + + Explore Kubernetes Monitoring + + +

Explore your infrastructure with Kubernetes Monitoring

+ +

Section: Exploring your infrastructure

+ +

What you will learn

+

Travel through your Kubernetes cluster and search for any Kubernetes object.

+ + + + + +

What you have learned

+

You can now navigate to the various objects in your infrastructure, from clusters to containers.

+ +

Section: Search and browsing

+ +

What you will learn

+

Use various means to search for and find any Kubernetes object.

+ + + + + +

What you have learned

+

You can now:

+ + +

Section: Namespaces and workloads-list

+ +

What you will learn

+

Use tabs on detail pages to view CPU, memory, and network performance for namespaces and workloads.

+ + + + + +

What you have learned

+

You can now use the CPU, Memory, and Network tabs to view performance in namespaces and workloads.

+ +

Section: Predictive capabilities

+ +

What you will learn

+

Use machine learning features to predict future resource usage and detect anomalies.

+ + + + + +

What you have learned

+

You can view predictions to understand potential future resource usage.

+ +

Done!

+

You've learned how to use Grafana Cloud's Kubernetes Monitoring application! You can now:

+ + + +

These workflows help you monitor, troubleshoot, and optimize any Kubernetes cluster connected to Grafana Cloud.

+ + + \ No newline at end of file From 9501f3dda6c739d42777cd4162b4d404144c13ac Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Thu, 9 Oct 2025 11:49:05 -0500 Subject: [PATCH 02/46] add period --- kubernetes-monitoring/unstyled.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html index e45f50b43..29e23fe15 100644 --- a/kubernetes-monitoring/unstyled.html +++ b/kubernetes-monitoring/unstyled.html @@ -7,7 +7,7 @@

Explore your infrastructure with Kubernetes Monitoring

-

Section: Exploring your infrastructure

+

Section: Exploring your infrastructure.

What you will learn

Travel through your Kubernetes cluster and search for any Kubernetes object.

From 94e2631b279b3d6e006cd8f5cae31f6579be2b85 Mon Sep 17 00:00:00 2001 From: Beverly Buchanan <131809838+BeverlyJaneJ@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:45:14 -0500 Subject: [PATCH 03/46] Enhance navigation instructions in unstyled.html Added instructions for navigating to Kubernetes Monitoring and exploring the Infrastructure section. --- kubernetes-monitoring/unstyled.html | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html index 29e23fe15..95134aece 100644 --- a/kubernetes-monitoring/unstyled.html +++ b/kubernetes-monitoring/unstyled.html @@ -6,6 +6,26 @@

Explore your infrastructure with Kubernetes Monitoring

+ +

Step 1: Navigate to Kubernetes Monitoring

+ +

Grafana provides specialized views for monitoring Kubernetes. Let's start by exploring the Infrastructure section + where you can view your Kubernetes clusters and their resources.

+ + + + highlight|button[aria-label="Expand section: Observability"]| +
    +
  • + Click Observability in the left navigation menu.
  • +
+

Section: Exploring your infrastructure.

@@ -456,4 +476,4 @@

Done!

These workflows help you monitor, troubleshoot, and optimize any Kubernetes cluster connected to Grafana Cloud.

- \ No newline at end of file + From e51a61433ac6ea44b41dbb98e15e4223664f132e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 14 Nov 2025 16:30:34 +0000 Subject: [PATCH 04/46] Add interactive link to return to Kubernetes Overview Co-authored-by: beverly.buchanan --- kubernetes-monitoring/unstyled.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html index 95134aece..93ad3a099 100644 --- a/kubernetes-monitoring/unstyled.html +++ b/kubernetes-monitoring/unstyled.html @@ -200,6 +200,14 @@

What you will learn

Click the namespace name. Click the namespace name in the workload information section to navigate to that namespace's detail page. + +
  • + Return to the Kubernetes Overview page. + Click Kubernetes in the main navigation to return to the Kubernetes Overview page so you can start exploring another path. +
  • From fd99a93ebc50e9631997b62385a51feb98b76444 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 14 Nov 2025 19:39:27 +0000 Subject: [PATCH 05/46] Add steps to navigate to Kubernetes Overview and Namespaces Co-authored-by: beverly.buchanan --- kubernetes-monitoring/unstyled.html | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html index 93ad3a099..8ec62bcdb 100644 --- a/kubernetes-monitoring/unstyled.html +++ b/kubernetes-monitoring/unstyled.html @@ -20,10 +20,25 @@

    Step 1: Navigate to Kubernetes Monitoring

    highlight|button[aria-label="Expand section: Observability"]|
      -
    • - Click Observability in the left navigation menu.
    • +
    • + Click Observability in the left navigation menu. +
    • +
    • + Click the Kubernetes tile to open the Kubernetes Overview page. + Click Kubernetes. +
    • +
    • + Click Namespaces in the menu. + Click Namespaces in the main menu to open the list of all namespaces in your environment. +
    From f28ce914c961738cbd1037f8fc2a7017c900e6f9 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 6 Dec 2025 12:41:09 -0600 Subject: [PATCH 06/46] all remaining edits --- k8s-cpu/unstyled.html | 226 +++++++++++++ kubernetes-monitoring/unstyled.html | 502 ---------------------------- 2 files changed, 226 insertions(+), 502 deletions(-) create mode 100644 k8s-cpu/unstyled.html diff --git a/k8s-cpu/unstyled.html b/k8s-cpu/unstyled.html new file mode 100644 index 000000000..ca28db960 --- /dev/null +++ b/k8s-cpu/unstyled.html @@ -0,0 +1,226 @@ + + + + + + Explore Kubernetes Monitoring + + +

    Explore CPU usage in Kubernetes Monitoring

    + +

    + Travel through the Kubernetes Monitoring application to explore CPU usage and any associated issues, + starting at the namespace level. +

    + +

    What you will learn

    +
      +
    • + Navigate from namespaces through the Kubernetes hierarchy to individual + containers. +
    • +
    • Review key panels to understand CPU usage.
    • +
    • Review throttling, restarts, and termination trends at the container level.
    • +
    + + +
      +
    • + Click Namespaces in the main menu to view the list of + namespaces in your environment.

      +
    • + +
    • + Select the first namespace in the list to open its detail view. +
    • + +
    • + This is an overview of the CPU and memory usage in the namespace.

      + Click the CPU tab to view more detail about the CPU + usage in the namespace. +
    • + +
    • + Notice how many containers in the + Scheduling: Containers with CPU requests set panel + have requests configured. Pods without CPU requests get + BestEffort QoS and are evicted first when nodes + reclaim CPU.

      + Review how requests compare to actual CPU usage for workloads in the + namespace. Ideally, each workload should have approximately 60-90% CPU + usage. +
    • + +
    • + Open the first workload in the list to see details about the workload. +
    • + +
    • + Review the Alignment graph for Pod CPU usage compared to requests.

      + Determine if Pods are running over or too close to their CPU requests. +
    • + +
    • + Select the first Pod in the Pods list. +
    • +
    • + Notice the Alignment: Container Usage/Requests (%) panel, compare actual container CPU usage against the CPU requests.

      + If usage exceeds 100%, containers in the Pod are using more CPU than requested. This can cause CPU throttling, leading to performance degradation. Consider increasing the CPU requests in the Pod specification.

      + If usage is between 80-100%, containers are using a healthy amount of CPU. + If usage is less than 50%, containers are over-provisioned. +
    • + +
    • + Click the first container in the list to view container details. +
    • + +
    • + At the Container detail page, scroll to the "CPU requests sizing section.

      + Notice on either side of the gauge, there are two numbers. The number on the left is the currently set CPU requests. The number on the right is the recommended CPU requests. You can use the recommendation to adjust your CPU requests for this container. +
    • + +
    • + Scroll further to view the CPU throttling panel.

      + This panel shows how much time your container is being artificially slowed down because it's trying to use more CPU than its configured limits allow. + + Why it matters: + +
        +
      • Performance impact: High throttling means your application is running slower than it could, directly affecting user experience.
      • +
      • Right-sizing: Helps you identify if CPU limits are too restrictive. You might need to increase them.
      • +
      • Cost optimization: If throttling is zero or very low, you might have over-provisioned CPU limits and can reduce costs.
      • +
      • Application behavior: Sudden throttling spikes can correlate with slow response times or timeout errors.
      • +
      +
    • + +
    • + Notice the Last terminated reason panel.

      + This view tells you exactly why Kubernetes last stopped the container, giving you clues to resolve instability. +
        +
      • Root cause: Last termination reason points to the underlying trigger (OOMKilled, CrashLoopBackOff, and so on).
      • +
      • Resilience: Helps you fix recurring issues before they cascade into bigger outages.
      • +
      • Alerting: Use these reasons to fine-tune alerts and spot regressions faster.
      • +
      +
    • + +
    • + Review the Container restart history panel.

      + This timeline shows how often the container has restarted, helping you detect flapping workloads. +
        +
      • Stability check: Frequent restarts indicate crashing code, resource starvation, or readiness issues.
      • +
      • Trend insight: Recent spikes can pinpoint deployments or config changes that destabilized the service.
      • +
      • Mitigation planning: Correlate restarts with logs and metrics to prioritize fixes and improve uptime.
      • +
      +
    • + +
    • + Examine the Container restarts panel for a point-in-time view of restart counts.

      +
        +
      • Health indicator: High restart counts signal unstable workloads that need investigation.
      • +
      • Operational visibility: Provides a quick glance at which containers are repeatedly crashing.
      • +
      • Actionable data: Use the restart tally to correlate with logs and metrics for faster remediation.
      • +
      +
    • + +
    +
    + +

    What you have learned

    +

    You have learned how to:

    +
      +
    • + Navigate from namespaces to individual containers to look at CPU usage. +
    • +
    • + Interpret container CPU usage, throttling, restart history, and termination reasons. +
    • +
    • + Turn insights and CPU request recommendations into improved resource sizing and workload reliability. +
    • +
    + + \ No newline at end of file diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html index 8ec62bcdb..e69de29bb 100644 --- a/kubernetes-monitoring/unstyled.html +++ b/kubernetes-monitoring/unstyled.html @@ -1,502 +0,0 @@ - - - - - Explore Kubernetes Monitoring - - -

    Explore your infrastructure with Kubernetes Monitoring

    - -

    Step 1: Navigate to Kubernetes Monitoring

    - -

    Grafana provides specialized views for monitoring Kubernetes. Let's start by exploring the Infrastructure section - where you can view your Kubernetes clusters and their resources.

    - - - - highlight|button[aria-label="Expand section: Observability"]| -
      -
    • - Click Observability in the left navigation menu. -
    • -
    • - Click the Kubernetes tile to open the Kubernetes Overview page. - Click Kubernetes. -
    • -
    • - Click Namespaces in the menu. - Click Namespaces in the main menu to open the list of all namespaces in your environment. -
    • -
    -
    - -

    Section: Exploring your infrastructure.

    - -

    What you will learn

    -

    Travel through your Kubernetes cluster and search for any Kubernetes object.

    - - -
      - -
    • - Click the cluster count. - Click the cluster count on the Kubernetes Overview page to open the list of all clusters in your environment. -
    • - -
    • - Click the name of any cluster. - Click the name of any cluster to open its detail page. -
    • - -
    • - Notice the Cluster information section. - The Cluster information section contains cluster identification information as well as memory and disk size. -
    • - -
    • - View Cluster optimization. - In the Cluster optimization section, you can view CPU and memory usage graphs to help identify potential over-provisioning. -
    • - -
    • - Notice CPU requests and limits. - The CPU requests and limits show the number of containers in the cluster that have requests and limits set. When CPU requests are not set for a container, you can see issues with quality of service and stability. -
    • - -
    • - Notice memory requests and limits. - Memory requests and limits show the number of containers in the cluster that have memory requests and limits set. When these are not set, you can see issues with crash looping and stability. -
    • - -
    • - Review the list of nodes. - The Nodes list displays a list of nodes in the cluster and: -
        -
      • Their CPU and memory usage data
      • -
      • The number of alerts firing for each node
      • -
      -
    • - -
    • - Click any node name. - Click any node name in the list to open its detail page. -
    • - -
    • - View Node optimization. - Just like the cluster detail page, the Node optimization section is below the Node information section. It shows CPU and memory usage graphs for the node. -
    • - -
    • - View the Pods list. - The Pods list shows all pods running on this node, including the workload and namespace each pod belongs to. -
    • - -
    • - Click any pod name. - Click any pod name in the list to navigate to that pod's detail page. As on other detail pages, this page includes: -
        -
      • Information about the pod
      • -
      • CPU and memory usage graphs
      • -
      • A list of objects that are in the pod
      • -
      -
    • - -
    • - Click any container name. - Click any container name in the Containers list to navigate to view the container's detail page. -
    • - -
    • - View Container information. - The Container information section shows the cluster, node, pod, workload, and namespace that the container belongs to. -
    • - -
    • - View Container optimization. - Notice in the Container optimization section, there are recommendations for sizing CPU and memory requests and limits. -
    • - -
    • - Click the namespace name. - Click the namespace name in the container information section to open the namespace's detail page. -
    • - -
    • - View the list of workloads. - The namespace detail page shows a list of all workloads running in this namespace. Each workload has a link to its detail page. -
    • - -
    • - Click any workload name. - Click any workload name to open that workload's detail page. -
    • - -
    • - View the Statefulset information. - The Statefulsete information includes any outliers in CPU or memory usage, as well as the cluster and namespace the workload belongs to. In the Workloads optimization section following, you can view CPU and memory usage graphs for the workload. -
    • - -
    • - Click the namespace name. - Click the namespace name in the workload information section to navigate to that namespace's detail page. -
    • - -
    • - Return to the Kubernetes Overview page. - Click Kubernetes in the main navigation to return to the Kubernetes Overview page so you can start exploring another path. -
    • -
    -
    - -

    What you have learned

    -

    You can now navigate to the various objects in your infrastructure, from clusters to containers.

    - -

    Section: Search and browsing

    - -

    What you will learn

    -

    Use various means to search for and find any Kubernetes object.

    - - -
      -
    • - Click Kubernetes in the menu. - Click Kubernetes in the main menu to open the Kubernetes Overview page. -
    • - -
    • - Click Search in the menu. - Click Search in the main menu to open the search page where you can also enter a name of any Kubernetes object. -
    • - -
    • - Enter any object's name. - Use the search bar to enter the name of a namespace, workload, node, pod, or container that you know exists in your cluster. -
    • - -
    • - Click any search result. - Click any search result to open the page for that object. -
    • - -
    • - Click Nodes on the menu. - Click Nodes on the main menu to go to the list of nodes. You can navigate to the main pages in Kubernetes Monitoring using the main menu. -
    • - -
    • - Click the caret beside the Nodes page title. - Click the arrow icon beside the page title to open another navigation menu. -
    • - -
    • - Click Alerts in the menu. - Click Alerts in the menu to open the Alerts. Here you can view and manage Kubernetes-specific alerts. -
    • - -
    -
    - -

    What you have learned

    -

    You can now:

    -
      -
    • Use the main menu to navigate to different pages
    • -
    • Use the top title menu to access navigation options
    • -
    • Use search to find any Kubernetes object
    • -
    - -

    Section: Namespaces and workloads-list

    - -

    What you will learn

    -

    Use tabs on detail pages to view CPU, memory, and network performance for namespaces and workloads.

    - - -
      -
    • - Click Namespaces in the menu. - Click Namespaces in the main menu to open the list of all namespaces in your environment. -
    • - -
    • - Click any namespace name. - Click any namespace name in the list to open its detail page. -
    • - -
    • - Click the CPU tab. - Click the CPU tab to see detailed CPU analysis. -
    • - -
    • - Hover over the i icon in the Usage panel. - Hover over the information icon in the Overview: Usage (vCPU cores) panel to read how CPU usage is calculated. -
    • - -
    • - Hover over i icons in other panels. - Optionally, hover over the information icons in other panels to understand the data shown in the graphs. -
    • - -
    • - Click the Memory tab. - Click the Memory tab to see memory utilization patterns. -
    • - -
    • - Click the Network tab. - Click the Network tab to understand bandwidth usage and potential network saturation. -
    • - -
    • - Click Workloads in the menu. - Click Workloads in the main menu to open the list of all workloads in your environment. -
    • - -
    • - Click any workload name. - Click any workload name in the list to open its detail page. -
    • - -
    • - Click the CPU tab. - Click the CPU tab to view the workload's CPU performance metrics. -
    • - -
    • - Click the Memory tab. - Click the Memory tab to view the workload's memory usage and allocation patterns. -
    • - -
    • - Click the Network tab. - Click the Network tab to view the workload's network performance and bandwidth usage. -
    • - -
    -
    - -

    What you have learned

    -

    You can now use the CPU, Memory, and Network tabs to view performance in namespaces and workloads.

    - -

    Section: Predictive capabilities

    - -

    What you will learn

    -

    Use machine learning features to predict future resource usage and detect anomalies.

    - - -
      - -
    • - Click Workloads in the menu. - Click Workloads in the main menu to open the list of all workloads. -
    • - -
    • - Click any workload name. - Click any workload name in the list to open its detail page. -
    • - -
    • - Change time range to last 7 days. - Click the time range filter and select Last 7 days. This allows the prediction to use more data for greater accuracy. -
    • - -
    • - Click Predict CPU usage. - In the CPU usage panel of the Workload optimization section, click the Predict CPU usage button to see future CPU usage predictions. To close the popup, click the X in the upper right corner. -
    • - -
    • - Forecast memory requirementsClick Predict Mem Usage. - To forecast memory usage, click Predict Mem Usage in the Workload memory panel. To close the popup, click the X in the upper right corner. -
    • - -
    -
    - -

    What you have learned

    -

    You can view predictions to understand potential future resource usage.

    - -

    Done!

    -

    You've learned how to use Grafana Cloud's Kubernetes Monitoring application! You can now:

    - -
      -
    • Navigate the Overview page with time range and cluster filtering
    • -
    • Search for and drill down through Kubernetes objects
    • -
    • Analyze performance using specialized CPU, Memory, and Network tabs
    • -
    • Investigate alerts and use troubleshooting tools
    • -
    • Optimize costs using efficiency and savings analysis
    • -
    • Use ML predictions for capacity planning and outlier detection
    • -
    - -

    These workflows help you monitor, troubleshoot, and optimize any Kubernetes cluster connected to Grafana Cloud.

    - - - From d2b8faef21050688f134f4340a8ddcf1f1a75efc Mon Sep 17 00:00:00 2001 From: David Allen Date: Sat, 6 Dec 2025 14:01:03 -0500 Subject: [PATCH 07/46] convert agent guidance HTML -> JSON --- .cursor/action-types-reference.mdc | 817 +++----- .cursor/best-practices.mdc | 88 +- .cursor/common-workflows.mdc | 2244 +++++++++------------- .cursor/complete-example-tutorial.mdc | 1707 +++++----------- .cursor/complex-selectors-and-hover.mdc | 834 ++++---- .cursor/requirements-quick-reference.mdc | 167 +- .cursor/selector-library.mdc | 124 +- .cursor/tutorial-patterns.mdc | 1760 ++++++----------- 8 files changed, 2897 insertions(+), 4844 deletions(-) diff --git a/.cursor/action-types-reference.mdc b/.cursor/action-types-reference.mdc index d44b5d1ca..b6e3f655b 100644 --- a/.cursor/action-types-reference.mdc +++ b/.cursor/action-types-reference.mdc @@ -12,13 +12,14 @@ alwaysApply: true **Do**: Scrolls to element, then clicks **Requirements**: Always include `exists-reftarget` -```html -
  • - Click Dashboards in navigation -
  • +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='Nav menu item'][href='/dashboards']", + "requirements": ["navmenu-open", "exists-reftarget"], + "content": "Click Dashboards in navigation" +} ``` ### button @@ -28,13 +29,14 @@ alwaysApply: true **Do**: Finds and clicks button by text **Advantages**: More stable than CSS selectors -```html -
  • - Save the data source -
  • +```json +{ + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "requirements": ["exists-reftarget"], + "content": "Save the data source" +} ``` ### formfill @@ -45,14 +47,15 @@ alwaysApply: true **Show**: Highlights the form field **Do**: Sets value and fires appropriate events -```html -
  • - Set Prometheus URL -
  • +```json +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "http://prometheus:9090", + "requirements": ["exists-reftarget"], + "content": "Set Prometheus URL" +} ``` ### navigate @@ -63,49 +66,70 @@ alwaysApply: true **Internal**: `/dashboard/new`, `/admin/plugins` **External**: `https://grafana.com/docs/` -```html -
  • - Create new dashboard -
  • +```json +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboard/new", + "requirements": ["has-datasources"], + "verify": "on-page:/dashboard/new", + "content": "Create new dashboard" +} ``` -### sequence +### section (previously sequence) **Purpose**: Group multiple steps with coordinated execution -**Target**: Container selector (references self) -**Structure**: Container with child interactive elements +**Target**: Not used (defined by structure) +**Structure**: JSON object with `type: "section"` and `blocks` array **Features**: Progress tracking, resumable execution, state persistence **Button**: Single "Do Section" button executes all child steps -```html - -
      -
    • Step 1
    • -
    • Step 2
    • -
    -
    +```json +{ + "type": "section", + "id": "setup-datasource", + "title": "Setup Data Source", + "objectives": ["has-datasource:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "button", + "reftarget": "New", + "content": "Step 1" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='name']", + "targetvalue": "prometheus", + "content": "Step 2" + } + ] +} ``` ### multistep **Purpose**: Execute multiple actions as single atomic step -**Target**: Not used (child spans define actions) -**Structure**: Container with `` child actions +**Target**: Not used (child steps define actions) +**Structure**: JSON object with `type: "multistep"` and `steps` array **Execution**: Shows each action, then executes, in sequence **Button**: Single "Do it" button, no "Show me" button -```html -
  • - - - Add visualization and select data source -
  • +```json +{ + "type": "multistep", + "content": "Add visualization and select data source", + "steps": [ + { + "action": "button", + "reftarget": "Add visualization" + }, + { + "action": "button", + "reftarget": "prometheus-datasource" + } + ] +} ``` ### hover @@ -115,37 +139,53 @@ alwaysApply: true **Do**: Dispatches mouse events (`mouseenter`, `mouseover`, `mousemove`) and maintains hover state for 2 seconds **Use when**: UI elements are hidden until hover, or CSS hover states need to be triggered -```html -
  • - - - Hover over service row and click Dashboard button -
  • +```json +{ + "type": "multistep", + "content": "Hover over service row and click Dashboard button", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('checkoutservice'))" + }, + { + "action": "button", + "reftarget": "Dashboard" + } + ] +} ``` ### guided **Purpose**: Provides a middle ground between fully automated "Do it" actions and manual user execution. The system highlights elements and displays instructions, then **waits for the user to manually perform the action** before proceeding. -**Target**: Not used (child spans define actions) -**Structure**: Container with `` child actions +**Target**: Not used (child steps define actions) +**Structure**: JSON object with `type: "guided"` and `steps` array **Execution**: Highlights each step, waits for user to manually perform action, then moves to next **Supported Actions**: `hover`, `button`, `highlight` (formfill and navigate not yet supported) **Use when**: Actions depend on CSS `:hover` states that can't be programmatically triggered, or you want users to learn by doing -```html -
    - - Hover over the service row to reveal action buttons - - - Click the revealed button to proceed - - Inspect service details -
    +```json +{ + "type": "guided", + "content": "Inspect service details", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='wb-list-item']:contains('adaptive-logs-api')", + "tooltip": "Hover over the service row to reveal action buttons" + }, + { + "action": "button", + "reftarget": "button.css-8mjxyo", + "tooltip": "Click the revealed button to proceed" + } + ] +} ``` **Configuration Options**: -- `data-step-timeout`: Control timeout per step (default: 30 seconds, range: 5000-120000ms) -- `data-skippable`: Allow users to skip if they can't complete it +- `stepTimeout`: Control timeout per step (default: 30 seconds, range: 5000-120000ms) +- `skippable`: Allow users to skip if they can't complete it **Section Integration**: When "Do Section" encounters a guided step, the section pauses and waits for the user to manually complete the guided interaction before resuming. @@ -178,7 +218,7 @@ User wants to click something: User wants to fill form: - Single field → use "formfill" action - - Multiple fields → use "sequence" with formfill steps + - Multiple fields → use "section" with formfill steps - Complex workflow → use "multistep" action User wants to navigate: @@ -186,10 +226,10 @@ User wants to navigate: - Navigate then act → use "multistep" with navigate + action User wants to teach workflow: - - Linear steps with checkpoints → use "sequence" action + - Linear steps with checkpoints → use "section" action - Atomic multi-action → use "multistep" action - User performs manually → use "guided" action - - Just explanation → use "highlight" with data-doit="false" + - Just explanation → use "highlight" with doIt: false User needs to reveal hover-hidden UI: - Hover then click → use "multistep" with hover + button/highlight @@ -235,79 +275,79 @@ Common objective patterns: ## Content Structure Patterns ### Basic Step Pattern -```html -
  • - Human-readable step description -
  • +```json +{ + "type": "interactive", + "action": "ACTION_TYPE", + "reftarget": "TARGET_REFERENCE", + "targetvalue": "VALUE_FOR_FORMFILL", + "requirements": ["REQ1", "REQ2"], + "objectives": ["OBJ1"], + "verify": "POST_ACTION_VERIFICATION", + "tooltip": "USER_FRIENDLY_TOOLTIP", + "skippable": true, + "doIt": false, + "content": "Human-readable step description" +} ``` ### Section Pattern -```html - - -

    Section Title

    -

    Section description explaining what will be accomplished.

    - -
      - -
    -
    +```json +{ + "type": "section", + "id": "unique-section-id", + "title": "Section Title", + "requirements": ["SECTION_REQUIREMENTS"], + "objectives": ["SECTION_OBJECTIVES"], + "tooltip": "SECTION_DESCRIPTION", + "blocks": [ + { + "type": "markdown", + "content": "Section description explaining what will be accomplished." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "..." + } + ] +} ``` ### Multi-step Pattern -```html -
  • - - - - - - - Human description of what these actions accomplish together -
  • +```json +{ + "type": "multistep", + "requirements": ["OVERALL_REQUIREMENTS"], + "objectives": ["OVERALL_OBJECTIVES"], + "tooltip": "WHAT_THIS_ACCOMPLISHES", + "content": "Human description of what these actions accomplish together", + "steps": [ + { + "action": "ACTION_TYPE_1", + "reftarget": "TARGET_1", + "requirements": ["ACTION_1_REQUIREMENTS"] + }, + { + "action": "ACTION_TYPE_2", + "reftarget": "TARGET_2", + "requirements": ["ACTION_2_REQUIREMENTS"] + } + ] +} ``` ### Interactive Comment Pattern -```html -
  • - - - The UI_ELEMENT_NAME [PURPOSE/FUNCTION]. - [CONTEXT_EXPLANATION]. Use it for TECHNICAL_FEATURES - to achieve USER_BENEFITS. - - - Human-readable description for the step -
  • +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "SELECTOR", + "doIt": false, + "requirements": ["exists-reftarget"], + "tooltip": "The UI_ELEMENT_NAME [PURPOSE/FUNCTION]. [CONTEXT_EXPLANATION]. Use it for TECHNICAL_FEATURES to achieve USER_BENEFITS.", + "content": "Human-readable description for the step" +} ``` ## Common Selector Patterns @@ -338,8 +378,8 @@ Common objective patterns: 1. **Always include `exists-reftarget`** for DOM-dependent actions 2. **Use `navmenu-open`** for navigation menu interactions 3. **Include appropriate page requirements** (`on-page:/path`) -4. **Add verification** (`data-verify`) for state-changing actions -5. **Provide helpful hints** (`data-hint`) for user guidance +4. **Add verification** (`verify`) for state-changing actions +5. **Provide helpful hints** (`tooltip`) for user guidance ### Selector Preferences 1. **Prefer `button` action** for buttons with stable text @@ -365,434 +405,81 @@ Common objective patterns: ## Common Guide Patterns ### Onboarding Tour -```html - -
      -
    • - Home page explanation - Tour the home page -
    • - -
    -
    +```json +{ + "type": "section", + "id": "grafana-tour", + "title": "Grafana Tour", + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/']", + "doIt": false, + "tooltip": "Home page explanation", + "content": "Tour the home page" + } + ] +} ``` ### Setup Workflow -```html - -
      -
    • Navigate to connections
    • -
    • Add data source
    • -
    • Name it
    • -
    • Save and test
    • -
    -
    +```json +{ + "type": "section", + "id": "setup-monitoring", + "title": "Setup Monitoring", + "objectives": ["has-datasource:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections", + "content": "Navigate to connections" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "content": "Add data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='name']", + "targetvalue": "prometheus", + "content": "Name it" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "content": "Save and test" + } + ] +} ``` ### Complex Interaction -```html -
  • - - - - Create a panel with Prometheus data source and basic query -
  • -```# Interactive Action Types Reference for AI - -## Action Type Definitions - -### highlight -**Purpose**: Click DOM elements using CSS selectors -**Target**: CSS selector (prefer data-testid attributes) -**Show**: Scrolls to element, highlights with visual feedback -**Do**: Scrolls to element, then clicks -**Requirements**: Always include `exists-reftarget` - -```html -
  • - Click Dashboards in navigation -
  • -``` - -### button -**Purpose**: Click buttons using visible text -**Target**: Button text (exact or partial match) -**Show**: Finds and highlights button by text -**Do**: Finds and clicks button by text -**Advantages**: More stable than CSS selectors - -```html -
  • - Save the data source -
  • -``` - -### formfill -**Purpose**: Fill form inputs with values -**Target**: CSS selector for form element -**Value**: String value to set (required) -**Supports**: text inputs, textareas, selects, checkboxes, Monaco editors, ARIA comboboxes -**Show**: Highlights the form field -**Do**: Sets value and fires appropriate events - -```html -
  • - Set Prometheus URL -
  • -``` - -### navigate -**Purpose**: Navigate to internal routes or external URLs -**Target**: URL path or external URL -**Show**: Indicates navigation intent -**Do**: Uses locationService.push() for internal routes, opens external URLs in new tab -**Internal**: `/dashboard/new`, `/admin/plugins` -**External**: `https://grafana.com/docs/` - -```html -
  • - Create new dashboard -
  • -``` - -### sequence -**Purpose**: Group multiple steps with coordinated execution -**Target**: Container selector (references self) -**Structure**: Container with child interactive elements -**Features**: Progress tracking, resumable execution, state persistence -**Button**: Single "Do Section" button executes all child steps - -```html - -
      -
    • Step 1
    • -
    • Step 2
    • -
    -
    -``` - -### multistep -**Purpose**: Execute multiple actions as single atomic step -**Target**: Not used (child spans define actions) -**Structure**: Container with `` child actions -**Execution**: Shows each action, then executes, in sequence -**Button**: Single "Do it" button, no "Show me" button - -```html -
  • - - - Add visualization and select data source -
  • -``` - -### hover -**Purpose**: Trigger hover states on elements to reveal UI that appears only on hover (e.g., Tailwind `group-hover:` classes, CSS `:hover` states) -**Target**: CSS selector for element to hover over -**Show**: Highlights the element without triggering hover -**Do**: Dispatches mouse events (`mouseenter`, `mouseover`, `mousemove`) and maintains hover state for 2 seconds -**Use when**: UI elements are hidden until hover, or CSS hover states need to be triggered - -```html -
  • - - - Hover over service row and click Dashboard button -
  • +```json +{ + "type": "multistep", + "requirements": ["on-page:/dashboard/new"], + "content": "Create a panel with Prometheus data source and basic query", + "steps": [ + { + "action": "button", + "reftarget": "Add visualization" + }, + { + "action": "button", + "reftarget": "prometheus-datasource" + }, + { + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up" + } + ] +} ``` - -### guided -**Purpose**: Provides a middle ground between fully automated "Do it" actions and manual user execution. The system highlights elements and displays instructions, then **waits for the user to manually perform the action** before proceeding. -**Target**: Not used (child spans define actions) -**Structure**: Container with `` child actions -**Execution**: Highlights each step, waits for user to manually perform action, then moves to next -**Supported Actions**: `hover`, `button`, `highlight` (formfill and navigate not yet supported) -**Use when**: Actions depend on CSS `:hover` states that can't be programmatically triggered, or you want users to learn by doing - -```html -
    - - Hover over the service row to reveal action buttons - - - Click the revealed button to proceed - - Inspect service details -
    -``` - -**Configuration Options**: -- `data-step-timeout`: Control timeout per step (default: 30 seconds, range: 5000-120000ms) -- `data-skippable`: Allow users to skip if they can't complete it - -**Section Integration**: When "Do Section" encounters a guided step, the section pauses and waits for the user to manually complete the guided interaction before resuming. - -## Requirements System - -### Common Requirements -- `exists-reftarget` - Target element exists in DOM -- `navmenu-open` - Navigation menu is open (auto-fixable) -- `is-admin` - User has admin privileges -- `has-datasources` - At least one data source configured -- `has-datasource:name` - Specific data source exists -- `has-plugin:id` - Plugin installed and enabled -- `on-page:/path` - User is on specific page -- `section-completed:id` - Previous section completed - -### Objectives System -- Same syntax as requirements -- Auto-completes steps when conditions already met -- Always takes priority over requirements -- For sections: marks ALL child steps complete - -## AI Generation Guidelines - -### Action Selection Logic -``` -User wants to click something: - - Button with stable text → use "button" action - - Element with stable selector → use "highlight" action - - Multiple related clicks → use "multistep" action - -User wants to fill form: - - Single field → use "formfill" action - - Multiple fields → use "sequence" with formfill steps - - Complex workflow → use "multistep" action - -User wants to navigate: - - Change pages → use "navigate" action - - Navigate then act → use "multistep" with navigate + action - -User wants to teach workflow: - - Linear steps with checkpoints → use "sequence" action - - Atomic multi-action → use "multistep" action - - User performs manually → use "guided" action - - Just explanation → use "highlight" with data-doit="false" - -User needs to reveal hover-hidden UI: - - Hover then click → use "multistep" with hover + button/highlight - - User performs hover manually → use "guided" with hover action -``` - -### Requirements Selection Logic -``` -For DOM interactions: - - Always include: exists-reftarget - - Navigation elements: add navmenu-open - - Page-specific elements: add on-page:/path - -For admin actions: - - Add: is-admin or has-role:admin - - Or: has-permission:specific-permission - -For data source actions: - - General: has-datasources - - Specific: has-datasource:name or has-datasource:type:prometheus - -For plugin actions: - - Add: has-plugin:plugin-id - -For sequential guides: - - Add: section-completed:previous-section-id -``` - -### Objectives Usage Logic -``` -Use objectives when: - - Step outcome might already be achieved - - Want to skip entire sections if setup already done - - Checking expensive conditions that auto-complete - -Common objective patterns: - - has-datasource:type:prometheus (skip data source setup if exists) - - on-page:/target (skip navigation if already there) - - has-plugin:plugin-id (skip installation if already installed) - - has-dashboard-named:title (skip creation if already exists) -``` - -## Content Structure Patterns - -### Basic Step Pattern -```html -
  • - Human-readable step description -
  • -``` - -### Section Pattern -```html - - -

    Section Title

    -

    Section description explaining what will be accomplished.

    - -
      - -
    -
    -``` - -### Multi-step Pattern -```html -
  • - - - - - - - Human description of what these actions accomplish together -
  • -``` - -### Interactive Comment Pattern -```html -
  • - - - The UI_ELEMENT_NAME [PURPOSE/FUNCTION]. - [CONTEXT_EXPLANATION]. Use it for TECHNICAL_FEATURES - to achieve USER_BENEFITS. - - - Human-readable description for the step -
  • -``` - -## Common Selector Patterns - -### Navigation Elements -- `a[data-testid='data-testid Nav menu item'][href='/PATH']` - Navigation menu items -- `#mega-menu-toggle` - Navigation menu toggle -- `nav[data-testid='navigation-mega-menu']` - Navigation container - -### Dashboard Elements -- `button[data-testid='add-panel']` - Add panel button -- `input[data-testid='dashboard-title-input']` - Dashboard title -- `button[data-testid='save-dashboard']` - Save dashboard - -### Query Elements -- `textarea[data-testid='query-editor']` - Query editor -- `textarea.inputarea.monaco-mouse-cursor-text` - Monaco editor -- `button[data-testid='run-query']` - Execute query - -### Data Source Elements -- `input[id='basic-settings-name']` - Data source name -- `input[id='connection-url']` - Connection URL -- `button[data-testid='data-source-save-test']` - Save & test - -## AI Generation Rules - -### Mandatory Practices -1. **Always include `exists-reftarget`** for DOM-dependent actions -2. **Use `navmenu-open`** for navigation menu interactions -3. **Include appropriate page requirements** (`on-page:/path`) -4. **Add verification** (`data-verify`) for state-changing actions -5. **Provide helpful hints** (`data-hint`) for user guidance - -### Selector Preferences -1. **Prefer `button` action** for buttons with stable text -2. **Use `data-testid` attributes** when available in selectors -3. **Avoid CSS classes** unless they're semantic (like `interactive`) -4. **Include element type** in selectors for specificity -5. **Use pseudo-selectors** (`:first-of-type`) for precision - -### Content Organization -1. **Use sections** for multi-step workflows -2. **Use multisteps** for atomic multi-actions -3. **Use show-only mode** for educational explanations -4. **Include interactive comments** for complex UI elements -5. **Structure progressively** from simple to complex - -### Error Prevention -1. **Test selectors** before using in guides -2. **Include fallback requirements** for edge cases -3. **Make steps skippable** when permissions might vary -4. **Provide clear error messages** in hints -5. **Use objectives** to handle already-completed states - -## Common Guide Patterns - -### Onboarding Tour -```html - -
      -
    • - Home page explanation - Tour the home page -
    • - -
    -
    -``` - -### Setup Workflow -```html - -
      -
    • Navigate to connections
    • -
    • Add data source
    • -
    • Name it
    • -
    • Save and test
    • -
    -
    -``` - -### Complex Interaction -```html -
  • - - - - Create a panel with Prometheus data source and basic query -
  • -``` \ No newline at end of file diff --git a/.cursor/best-practices.mdc b/.cursor/best-practices.mdc index 823b3328d..6ba1f4ecd 100644 --- a/.cursor/best-practices.mdc +++ b/.cursor/best-practices.mdc @@ -3,7 +3,7 @@ alwaysApply: true description: help is available on writing good guides through best practices for authorship. --- -## Best Practices +# Best Practices The following best practices are oriented around the idea of defensive design. Interactive guides are written to make it easy for beginners @@ -21,9 +21,9 @@ language, not discourse & concepts. Rely on links for concepts. ### Include requirements wherever possible -* Every DOM interaction needs exists-reftarget -* Navigation elements need navmenu-open -* Page-specific actions need on-page:/path +* Every DOM interaction needs `exists-reftarget` +* Navigation elements need `navmenu-open` +* Page-specific actions need `on-page:/path` * Be aware of what roles a given feature needs; Admin features need `is-admin` or appropriate role requirements ### Connect Requirements and Objectives @@ -41,18 +41,18 @@ to the top of the section to block starting step 1. ### Use Stable Selectors * Prefer `data-testid` attributes over CSS classes -* Use button[data-testid='save'] not .btn-primary +* Use `button[data-testid='save']` not `.btn-primary` * Include fallback selectors for critical elements ### Handle Edge Cases Gracefully -Make admin-only steps data-skippable="true" with helpful hints -Use data-objectives to auto-complete already-done steps -Provide alternative workflows for different user permissions +Make admin-only steps `skippable: true` with helpful hints. +Use `objectives` to auto-complete already-done steps. +Provide alternative workflows for different user permissions. ### Verify State Changes -* Use data-verify for actions that change state +* Use `verify` for actions that change state * Include verification after save/create operations * Check for expected outcomes after navigation @@ -65,15 +65,15 @@ Provide alternative workflows for different user permissions ### Structure for Resilience * Break complex workflows into logical sections -* Use sequence for multi-step processes with dependencies -* Use multistep for related actions that should happen together +* Use sequence (section blocks) for multi-step processes with dependencies +* Use `multistep` for related actions that should happen together * Include section completion requirements for dependencies ### Content Quality -* Keep interactive comments under 250 characters -* Use `` for UI elements, `` for technical terms -* For content between ``, keep to one short sentence. Do not use the name of the element being highlighted by the system. For example, instead of `The Logs menu opens the Logs page
    `, use `Click to open the Logs page.` +* Keep interactive comments (`tooltip`) under 250 characters +* Use Markdown for formatting UI elements and technical terms +* For content in `tooltip`, keep to one short sentence. Do not use the name of the element being highlighted by the system. For example, instead of "The **Logs** menu opens the Logs page", use "Click to open the Logs page." * Use user-centric words like those in the following list: * Instead of visualization or card, use panel * Instead of menu item, use the name of the menu item. For example, instead of "Click the Logs menu item", write "Click Logs." @@ -81,45 +81,41 @@ Provide alternative workflows for different user permissions * Instead of dropdown, use menu. For example, instead of "the Data source dropdown", write "the Data source menu" * Instead of interface, use page, section, or panel. * Avoid using button. For example, instead of "Click the Select button", write "Click Select." - * Only bold titles or names of items on the GUI. For example, do not use "Click the `Logs tab`." Instead, write "Click the `Logs` tab." + * Only bold titles or names of items on the GUI. For example, do not use "Click the **Logs tab**." Instead, write "Click the **Logs** tab." * Do not use integration or plugin. Find another way to describe what you mean. - * Instead of "`menu button` (three dots)", use "log menu icon (three dots)". + * Instead of "**menu button** (three dots)", use "log menu icon (three dots)". * Always include "What you will learn" before each section, and "What you have learned" after each section. * If an item is to be clicked, only use "Click". Do not use obscure terms like "navigate to", "examine", or "check out". * Write user-centric descriptions that match mental models * Include context about why each step matters -```html - - - - - (TUTORIAL NAME GOES HERE)) - - - -

    (TUTORIAL NAME GOES HERE)

    - -

    What you will learn: A brief description of your guide

    - -

    Section: Heading Name

    - - -
      -
    • - Step 1: User does a thing -
    • -
    - - +```json +{ + "id": "guide-id", + "title": "TUTORIAL NAME GOES HERE", + "blocks": [ + { + "type": "markdown", + "content": "# TUTORIAL NAME GOES HERE\n\nWhat you will learn: A brief description of your guide" + }, + { + "type": "section", + "id": "section-id", + "title": "Section: Heading Name", + "requirements": ["navmenu-open"], + "blocks": [ + { + "type": "interactive", + "action": "FILL ME OUT", + "reftarget": "FILL ME OUT", + "content": "Step 1: User does a thing" + } + ] + } + ] +} ``` 5. Finally, encourage the user to go edit this file, and remind them of the -other commands that you have available. \ No newline at end of file +other commands that you have available. diff --git a/.cursor/common-workflows.mdc b/.cursor/common-workflows.mdc index 210d57665..d1943e67f 100644 --- a/.cursor/common-workflows.mdc +++ b/.cursor/common-workflows.mdc @@ -7,615 +7,925 @@ alwaysApply: true ## Standard Workflow Templates ### Data Source Setup Workflow -```html - - -

    Set Up {DataSource} Data Source

    - -
      -
    • - Navigate to data source management -
    • - -
    • - Click Add new data source -
    • - -
    • - Select {DataSource} from the list -
    • - -
    • - Name your data source -
    • - -
    • - Set the server URL -
    • - -
    • - Save and test the connection -
    • -
    -
    +```json +{ + "type": "section", + "id": "setup-{datasource-type}", + "title": "Set Up {DataSource} Data Source", + "requirements": ["is-admin"], + "objectives": ["has-datasource:type:{datasource-type}"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "requirements": ["navmenu-open"], + "verify": "on-page:/connections", + "content": "Navigate to data source management" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "requirements": ["exists-reftarget"], + "content": "Click Add new data source" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections/datasources/{datasource-type}']", + "requirements": ["exists-reftarget"], + "content": "Select {DataSource} from the list" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='basic-settings-name']", + "targetvalue": "{datasource-name}", + "requirements": ["exists-reftarget"], + "content": "Name your data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "{connection-url}", + "requirements": ["exists-reftarget"], + "content": "Set the server URL" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "requirements": ["exists-reftarget"], + "verify": "has-datasource:{datasource-name}", + "content": "Save and test the connection" + } + ] +} ``` ### Dashboard Creation Workflow -```html - - -

    Create {Purpose} Dashboard

    - -
      -
    • - Navigate to create new dashboard -
    • - -
    • - Add your first panel -
    • - -
    • - Select your data source -
    • - -
    • - Enter your query -
    • - -
    • - Set panel title -
    • - -
    • - Save your dashboard -
    • -
    -
    +```json +{ + "type": "section", + "id": "create-dashboard-{purpose}", + "title": "Create {Purpose} Dashboard", + "requirements": ["has-datasources"], + "objectives": ["has-dashboard-named:{dashboard-title}"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboard/new", + "requirements": ["navmenu-open"], + "verify": "on-page:/dashboard/new", + "content": "Navigate to create new dashboard" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add visualization", + "requirements": ["exists-reftarget"], + "content": "Add your first panel" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "{datasource-name}", + "requirements": ["has-datasource:{datasource-name}"], + "tooltip": "Select the data source you configured earlier", + "content": "Select your data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "{example-query}", + "requirements": ["exists-reftarget"], + "content": "Enter your query" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "{panel-title}", + "requirements": ["exists-reftarget"], + "content": "Set panel title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["exists-reftarget"], + "verify": "has-dashboard-named:{dashboard-title}", + "content": "Save your dashboard" + } + ] +} ``` ### Plugin Installation Workflow -```html - - -

    Install {Plugin Name} Plugin

    - -
      -
    • - Navigate to plugin management -
    • - -
    • - Search for the plugin -
    • - -
    • - Find the plugin in search results -
    • - -
    • - Install the plugin -
    • -
    -
    +```json +{ + "type": "section", + "id": "install-plugin-{plugin-id}", + "title": "Install {Plugin Name} Plugin", + "requirements": ["is-admin"], + "objectives": ["has-plugin:{plugin-id}"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/plugins", + "requirements": ["navmenu-open", "is-admin"], + "verify": "on-page:/admin/plugins", + "content": "Navigate to plugin management" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='plugin-search']", + "targetvalue": "{search-term}", + "requirements": ["exists-reftarget"], + "content": "Search for the plugin" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/admin/plugins/{plugin-id}']", + "requirements": ["exists-reftarget"], + "content": "Find the plugin in search results" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Install", + "requirements": ["exists-reftarget"], + "verify": "has-plugin:{plugin-id}", + "content": "Install the plugin" + } + ] +} ``` ### Alert Rule Creation Workflow -```html - - -

    Create {Metric} Alert Rule

    - -
      -
    • - Navigate to create alert rule -
    • - -
    • - Name your alert rule -
    • - -
    • - Select data source -
    • - -
    • - Enter alert query -
    • - -
    • - Set alert threshold -
    • - -
    • - Save the alert rule -
    • -
    -
    +```json +{ + "type": "section", + "id": "create-alert-rule-{metric}", + "title": "Create {Metric} Alert Rule", + "requirements": ["has-datasource:prometheus", "has-permission:alerting:write"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/alerting/new", + "requirements": ["navmenu-open"], + "verify": "on-page:/alerting/new", + "content": "Navigate to create alert rule" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='alert-rule-name']", + "targetvalue": "{alert-rule-name}", + "requirements": ["exists-reftarget"], + "content": "Name your alert rule" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "{datasource-name}", + "requirements": ["has-datasource:{datasource-name}"], + "content": "Select data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='alert-query-editor']", + "targetvalue": "{alert-query}", + "requirements": ["exists-reftarget"], + "content": "Enter alert query" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='alert-threshold']", + "targetvalue": "{threshold-value}", + "requirements": ["exists-reftarget"], + "content": "Set alert threshold" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save rule", + "requirements": ["exists-reftarget"], + "content": "Save the alert rule" + } + ] +} ``` ## Data Source Specific Patterns ### Prometheus Setup -```html - -
      -
    • Go to data sources
    • -
    • Add data source
    • -
    • Select Prometheus
    • -
    • Name it
    • -
    • Set URL
    • -
    • Save and test
    • -
    -
    +```json +{ + "type": "section", + "id": "setup-prometheus", + "title": "Setup Prometheus", + "objectives": ["has-datasource:type:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "content": "Go to data sources" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "content": "Add data source" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections/datasources/prometheus']", + "content": "Select Prometheus" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='basic-settings-name']", + "targetvalue": "prometheus", + "content": "Name it" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "http://prometheus:9090", + "content": "Set URL" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "verify": "has-datasource:prometheus", + "content": "Save and test" + } + ] +} ``` ### Loki Setup -```html - -
      -
    • Go to data sources
    • -
    • Add data source
    • -
    • Select Loki
    • -
    • Name it
    • -
    • Set URL
    • -
    • Save and test
    • -
    -
    +```json +{ + "type": "section", + "id": "setup-loki", + "title": "Setup Loki", + "objectives": ["has-datasource:type:loki"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "content": "Go to data sources" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "content": "Add data source" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections/datasources/loki']", + "content": "Select Loki" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='basic-settings-name']", + "targetvalue": "loki", + "content": "Name it" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "http://loki:3100", + "content": "Set URL" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "verify": "has-datasource:loki", + "content": "Save and test" + } + ] +} ``` ### InfluxDB Setup -```html - -
      -
    • Go to data sources
    • -
    • Add data source
    • -
    • Select InfluxDB
    • -
    • Name it
    • -
    • Set URL
    • -
    • Set database
    • -
    • Save and test
    • -
    -
    +```json +{ + "type": "section", + "id": "setup-influxdb", + "title": "Setup InfluxDB", + "objectives": ["has-datasource:type:influxdb"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "content": "Go to data sources" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "content": "Add data source" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections/datasources/influxdb']", + "content": "Select InfluxDB" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='basic-settings-name']", + "targetvalue": "influxdb", + "content": "Name it" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "http://influxdb:8086", + "content": "Set URL" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='influxdb-database']", + "targetvalue": "mydb", + "content": "Set database" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "verify": "has-datasource:influxdb", + "content": "Save and test" + } + ] +} ``` ## Visualization Creation Patterns ### Time Series Panel -```html - -
      -
    • Add panel
    • -
    • Select Prometheus
    • -
    • Enter rate query
    • -
    • Select time series
    • -
    • Set title
    • -
    • Apply changes
    • -
    -
    +```json +{ + "type": "section", + "id": "create-timeseries-panel", + "title": "Create Time Series Panel", + "requirements": ["on-page:/dashboard", "has-datasource:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "button", + "reftarget": "Add visualization", + "content": "Add panel" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "prometheus", + "content": "Select Prometheus" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "rate(http_requests_total[5m])", + "content": "Enter rate query" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[aria-label='Plugin visualization item Time series']", + "content": "Select time series" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "Request Rate", + "content": "Set title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Apply", + "content": "Apply changes" + } + ] +} ``` ### Stat Panel -```html - -
      -
    • Add panel
    • -
    • Select Prometheus
    • -
    • Enter up query
    • -
    • Select stat visualization
    • -
    • Set title
    • -
    • Apply changes
    • -
    -
    +```json +{ + "type": "section", + "id": "create-stat-panel", + "title": "Create Stat Panel", + "requirements": ["on-page:/dashboard", "has-datasource:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "button", + "reftarget": "Add visualization", + "content": "Add panel" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "prometheus", + "content": "Select Prometheus" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "content": "Enter up query" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[aria-label='Plugin visualization item Stat']", + "content": "Select stat visualization" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "Services Up", + "content": "Set title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Apply", + "content": "Apply changes" + } + ] +} ``` ### Table Panel -```html - -
      -
    • Add panel
    • -
    • Select Prometheus
    • -
    • Enter up query
    • -
    • Select table visualization
    • -
    • Set title
    • -
    • Apply changes
    • -
    -
    +```json +{ + "type": "section", + "id": "create-table-panel", + "title": "Create Table Panel", + "requirements": ["on-page:/dashboard", "has-datasource:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "button", + "reftarget": "Add visualization", + "content": "Add panel" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "prometheus", + "content": "Select Prometheus" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "content": "Enter up query" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[aria-label='Plugin visualization item Table']", + "content": "Select table visualization" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "Target Status", + "content": "Set title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Apply", + "content": "Apply changes" + } + ] +} ``` ## Query Pattern Library ### Prometheus Queries -```html - -
  • - Check which targets are up -
  • - - -
  • - Calculate request rate over 5 minutes -
  • - - -
  • - Calculate CPU usage percentage -
  • - - -
  • - Calculate memory usage percentage -
  • - - -
  • - Calculate disk usage percentage -
  • +```json +// Basic availability +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "content": "Check which targets are up" +} + +// Request rate +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "rate(http_requests_total[5m])", + "content": "Calculate request rate over 5 minutes" +} + +// CPU usage +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "100 - (avg(rate(node_cpu_seconds_total{mode='idle'}[5m])) * 100)", + "content": "Calculate CPU usage percentage" +} + +// Memory usage +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100", + "content": "Calculate memory usage percentage" +} + +// Disk usage +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "100 - ((node_filesystem_avail_bytes * 100) / node_filesystem_size_bytes)", + "content": "Calculate disk usage percentage" +} ``` ### Loki Queries -```html - -
  • - Query Grafana service logs -
  • - - -
  • - Filter for error messages -
  • - - -
  • - Calculate log rate -
  • - - -
  • - Parse JSON logs and filter by level -
  • +```json +// Basic log query +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "{job='grafana'}", + "content": "Query Grafana service logs" +} + +// Error logs +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "{job='grafana'} |= 'error'", + "content": "Filter for error messages" +} + +// Rate calculation +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "rate({job='grafana'}[5m])", + "content": "Calculate log rate" +} + +// JSON parsing +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "{job='grafana'} | json | level='error'", + "content": "Parse JSON logs and filter by level" +} ``` ## Educational Pattern Library ### Interface Explanation Pattern -```html -
  • - - - This {UI_ELEMENT_NAME} {what_it_does}. - {why_its_important}. {specific_capabilities_or_features}. - - - Learn about the {UI_ELEMENT_NAME} -
  • +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "{ui-element-selector}", + "requirements": ["exists-reftarget"], + "doIt": false, + "tooltip": "This {UI_ELEMENT_NAME} {what_it_does}. {why_its_important}. {specific_capabilities_or_features}.", + "content": "Learn about the {UI_ELEMENT_NAME}" +} ``` ### Feature Introduction Pattern -```html - - -

    Understanding {Feature Name}

    - -
      - -
    • - {Feature overview explanation} - Explore the main {feature} interface -
    • - - -
    • - {Control explanation} - Notice the {control name} controls -
    • - - -
    • - {Example explanation} - See how {feature} displays information -
    • -
    -
    +```json +{ + "type": "section", + "id": "learn-{feature-name}", + "title": "Understanding {Feature Name}", + "requirements": ["on-page:/{feature-page}"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "{main-interface}", + "doIt": false, + "tooltip": "{Feature overview explanation}", + "content": "Explore the main {feature} interface" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "{control-element}", + "doIt": false, + "tooltip": "{Control explanation}", + "content": "Notice the {control name} controls" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "{example-element}", + "doIt": false, + "tooltip": "{Example explanation}", + "content": "See how {feature} displays information" + } + ] +} ``` ### Concept-to-Practice Pattern -```html - -
  • - - The query editor is where you write {query language} queries... - - Learn about writing queries -
  • - - -
  • - - This {example-query} query {what it does and why}... - - See how a {query type} query looks -
  • - - -
  • - Now enter your own {query type} query -
  • - - -
  • - Run the query to see results -
  • +```json +{ + "type": "section", + "id": "concept-practice", + "title": "Concept to Practice", + "blocks": [ + // 1. Explain the concept + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='query-editor']", + "doIt": false, + "tooltip": "The query editor is where you write {query language} queries...", + "content": "Learn about writing queries" + }, + // 2. Show an example + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "{example-query}", + "doIt": false, + "tooltip": "This {example-query} query {what it does and why}...", + "content": "See how a {query type} query looks" + }, + // 3. Practice + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "{practice-query}", + "content": "Now enter your own {query type} query" + }, + // 4. Execute and observe + { + "type": "interactive", + "action": "button", + "reftarget": "Run query", + "content": "Run the query to see results" + } + ] +} ``` ## Multi-Step Patterns ### Quick Multi-Actions -```html - -
  • - - - Add visualization and select {datasource} data source -
  • - - -
  • - - - Go to plugins and search for {plugin name} -
  • - - -
  • - - - - Fill name and URL, then test connection -
  • +```json +// Add visualization with data source +{ + "type": "multistep", + "requirements": ["on-page:/dashboard/new"], + "content": "Add visualization and select {datasource} data source", + "steps": [ + { + "action": "button", + "reftarget": "Add visualization" + }, + { + "action": "button", + "reftarget": "{datasource-name}" + } + ] +} + +// Navigate and configure +{ + "type": "multistep", + "requirements": ["is-admin"], + "content": "Go to plugins and search for {plugin name}", + "steps": [ + { + "action": "navigate", + "reftarget": "/admin/plugins" + }, + { + "action": "formfill", + "reftarget": "input[type='search']", + "targetvalue": "{plugin-name}" + } + ] +} + +// Form sequence +{ + "type": "multistep", + "requirements": ["on-page:/connections/new"], + "content": "Fill name and URL, then test connection", + "steps": [ + { + "action": "formfill", + "reftarget": "input[id='name']", + "targetvalue": "{name}" + }, + { + "action": "formfill", + "reftarget": "input[id='url']", + "targetvalue": "{url}" + }, + { + "action": "button", + "reftarget": "Test connection" + } + ] +} ``` ## Permission-Aware Patterns ### Admin vs User Workflows -```html - - -

    Complete Setup (Admin)

    -
      -
    • Install plugins
    • -
    • Configure data sources
    • -
    • Manage users
    • -
    -
    - - - -

    User Setup (Non-Admin)

    -
      -
    • Browse dashboards
    • -
    • Try data exploration
    • -
    • - - Contact your administrator to request additional permissions - for creating dashboards, configuring data sources, or installing plugins. - - Know how to request additional access -
    • -
    -
    +```json +// Admin workflow +{ + "type": "section", + "id": "admin-setup", + "title": "Complete Setup (Admin)", + "requirements": ["is-admin"], + "tooltip": "Full setup process for administrators", + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/plugins", + "content": "Install plugins" + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "content": "Configure data sources" + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/users", + "content": "Manage users" + } + ] +} + +// User workflow +{ + "type": "section", + "id": "user-setup", + "title": "User Setup (Non-Admin)", + "requirements": ["has-role:viewer"], + "tooltip": "Limited setup for regular users", + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboards", + "content": "Browse dashboards" + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/explore", + "content": "Try data exploration" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='contact-admin']", + "doIt": false, + "tooltip": "Contact your administrator to request additional permissions for creating dashboards, configuring data sources, or installing plugins.", + "content": "Know how to request additional access" + } + ] +} ``` ### Skippable Feature Patterns -```html - -
  • - Use advanced query expressions (skip if not supported) -
  • - - -
  • - Open custom application (skip if plugin not installed) -
  • +```json +// Optional advanced features +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='advanced-query']", + "targetvalue": "complex_query_expression", + "requirements": ["has-feature:expressions", "min-version:9.0.0"], + "skippable": true, + "tooltip": "Advanced feature - skip if not available", + "content": "Use advanced query expressions (skip if not supported)" +} + +// Plugin-dependent features +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/a/custom-app", + "requirements": ["has-plugin:custom-app"], + "skippable": true, + "tooltip": "Requires custom app plugin", + "content": "Open custom application (skip if plugin not installed)" +} ``` ## Error Recovery Patterns ### Primary + Fallback Pattern -```html - -
  • - Use automatic setup wizard -
  • - - -
  • - Or configure data source manually -
  • +```json +// Try automated approach first +{ + "type": "interactive", + "action": "button", + "reftarget": "Auto setup", + "requirements": ["has-plugin:setup-wizard", "exists-reftarget"], + "skippable": true, + "tooltip": "Automated setup if wizard available", + "content": "Use automatic setup wizard" +} + +// Manual fallback +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources/new", + "requirements": ["is-admin"], + "content": "Or configure data source manually" +} ``` ### Validation and Retry Pattern -```html - -
  • - Test the data source connection -
  • - - -
  • - - If the connection test failed, verify your URL and - authentication settings. Common issues include - incorrect port numbers, network connectivity, - or authentication credentials. - - Review connection settings if test failed -
  • +```json +// Action with verification +{ + "type": "interactive", + "action": "button", + "reftarget": "Test connection", + "requirements": ["exists-reftarget"], + "verify": "has-datasource:{name}", + "content": "Test the data source connection" +} + +// Retry if failed +{ + "type": "interactive", + "action": "highlight", + "reftarget": "input[id='connection-url']", + "requirements": ["exists-reftarget"], + "tooltip": "If the connection test failed, verify your URL and authentication settings. Common issues include incorrect port numbers, network connectivity, or authentication credentials.", + "content": "Review connection settings if test failed" +} ``` ## AI Content Generation Guidelines @@ -663,797 +973,123 @@ alwaysApply: true #### Interactive Comments - **Educational**: Explain what elements do and why they matter - **Contextual**: Provide information that helps users understand -- **Formatted**: Use `` for UI elements, `` for technical terms +- **Formatted**: Use Markdown for UI elements and technical terms - **Concise**: Keep under 250 characters for good UX #### Error Prevention - **Comprehensive requirements**: Include all necessary preconditions - **Helpful hints**: Explain what will happen and why - **Graceful failures**: Provide skippable options for edge cases -- **Clear verification**: Use `data-verify` for state-changing actions +- **Clear verification**: Use `verify` for state-changing actions ### Common Mistakes to Avoid ❌ **Missing Requirements** -```html - -
  • +```json +// Bad: No requirements for DOM interaction +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save']" +} ``` ✅ **Good: Includes necessary requirements** -```html -
  • +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save']", + "requirements": ["exists-reftarget", "on-page:/dashboard"] +} ``` ❌ **Unclear Selectors** -```html - -
  • +```json +// Bad: Brittle CSS class selector +{ + "type": "interactive", + "action": "highlight", + "reftarget": ".btn-primary.save-button" +} ``` ✅ **Good: Stable attribute-based selector** -```html -
  • +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save-dashboard']" +} ``` ❌ **Missing Context** -```html - -
  • +```json +// Bad: No explanation of what will happen +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input", + "targetvalue": "value" +} ``` ✅ **Good: Clear context and purpose** -```html -
  • +```json +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='name']", + "targetvalue": "prometheus", + "tooltip": "Sets a descriptive name for easy identification" +} ``` ❌ **Overly Complex Steps** -```html - -
  • - - - - - - - Navigate to admin, go to users, add user, fill details, create user -
  • +```json +// Bad: Too many actions in one step +{ + "type": "multistep", + "content": "Navigate to admin, go to users, add user, fill details, create user", + "steps": [ + {"action": "navigate", "reftarget": "/admin"}, + {"action": "button", "reftarget": "Users"}, + {"action": "button", "reftarget": "Add user"}, + {"action": "formfill", "reftarget": "input[id='name']", "targetvalue": "john"}, + {"action": "formfill", "reftarget": "input[id='email']", "targetvalue": "john@example.com"}, + {"action": "button", "reftarget": "Create"} + ] +} ``` ✅ **Good: Broken into logical sections** -```html - -
  • Navigate to user management
  • -
  • Start adding new user
  • -
  • - - - Fill user details -
  • -
  • Create the user account
  • -
    -```# Common Interactive Workflow Patterns for AI - -## Standard Workflow Templates - -### Data Source Setup Workflow -```html - - -

    Set Up {DataSource} Data Source

    - -
      -
    • - Navigate to data source management -
    • - -
    • - Click Add new data source -
    • - -
    • - Select {DataSource} from the list -
    • - -
    • - Name your data source -
    • - -
    • - Set the server URL -
    • - -
    • - Save and test the connection -
    • -
    -
    -``` - -### Dashboard Creation Workflow -```html - - -

    Create {Purpose} Dashboard

    - -
      -
    • - Navigate to create new dashboard -
    • - -
    • - Add your first panel -
    • - -
    • - Select your data source -
    • - -
    • - Enter your query -
    • - -
    • - Set panel title -
    • - -
    • - Save your dashboard -
    • -
    -
    -``` - -### Plugin Installation Workflow -```html - - -

    Install {Plugin Name} Plugin

    - -
      -
    • - Navigate to plugin management -
    • - -
    • - Search for the plugin -
    • - -
    • - Find the plugin in search results -
    • - -
    • - Install the plugin -
    • -
    -
    -``` - -### Alert Rule Creation Workflow -```html - - -

    Create {Metric} Alert Rule

    - -
      -
    • - Navigate to create alert rule -
    • - -
    • - Name your alert rule -
    • - -
    • - Select data source -
    • - -
    • - Enter alert query -
    • - -
    • - Set alert threshold -
    • - -
    • - Save the alert rule -
    • -
    -
    -``` - -## Data Source Specific Patterns - -### Prometheus Setup -```html - -
      -
    • Go to data sources
    • -
    • Add data source
    • -
    • Select Prometheus
    • -
    • Name it
    • -
    • Set URL
    • -
    • Save and test
    • -
    -
    -``` - -### Loki Setup -```html - -
      -
    • Go to data sources
    • -
    • Add data source
    • -
    • Select Loki
    • -
    • Name it
    • -
    • Set URL
    • -
    • Save and test
    • -
    -
    -``` - -### InfluxDB Setup -```html - -
      -
    • Go to data sources
    • -
    • Add data source
    • -
    • Select InfluxDB
    • -
    • Name it
    • -
    • Set URL
    • -
    • Set database
    • -
    • Save and test
    • -
    -
    -``` - -## Visualization Creation Patterns - -### Time Series Panel -```html - -
      -
    • Add panel
    • -
    • Select Prometheus
    • -
    • Enter rate query
    • -
    • Select time series
    • -
    • Set title
    • -
    • Apply changes
    • -
    -
    -``` - -### Stat Panel -```html - -
      -
    • Add panel
    • -
    • Select Prometheus
    • -
    • Enter up query
    • -
    • Select stat visualization
    • -
    • Set title
    • -
    • Apply changes
    • -
    -
    -``` - -### Table Panel -```html - -
      -
    • Add panel
    • -
    • Select Prometheus
    • -
    • Enter up query
    • -
    • Select table visualization
    • -
    • Set title
    • -
    • Apply changes
    • -
    -
    -``` - -## Query Pattern Library - -### Prometheus Queries -```html - -
  • - Check which targets are up -
  • - - -
  • - Calculate request rate over 5 minutes -
  • - - -
  • - Calculate CPU usage percentage -
  • - - -
  • - Calculate memory usage percentage -
  • - - -
  • - Calculate disk usage percentage -
  • -``` - -### Loki Queries -```html - -
  • - Query Grafana service logs -
  • - - -
  • - Filter for error messages -
  • - - -
  • - Calculate log rate -
  • - - -
  • - Parse JSON logs and filter by level -
  • -``` - -## Educational Pattern Library - -### Interface Explanation Pattern -```html -
  • - - - This {UI_ELEMENT_NAME} {what_it_does}. - {why_its_important}. {specific_capabilities_or_features}. - - - Learn about the {UI_ELEMENT_NAME} -
  • -``` - -### Feature Introduction Pattern -```html - - -

    Understanding {Feature Name}

    - -
      - -
    • - {Feature overview explanation} - Explore the main {feature} interface -
    • - - -
    • - {Control explanation} - Notice the {control name} controls -
    • - - -
    • - {Example explanation} - See how {feature} displays information -
    • -
    -
    -``` - -### Concept-to-Practice Pattern -```html - -
  • - - The query editor is where you write {query language} queries... - - Learn about writing queries -
  • - - -
  • - - This {example-query} query {what it does and why}... - - See how a {query type} query looks -
  • - - -
  • - Now enter your own {query type} query -
  • - - -
  • - Run the query to see results -
  • -``` - -## Multi-Step Patterns - -### Quick Multi-Actions -```html - -
  • - - - Add visualization and select {datasource} data source -
  • - - -
  • - - - Go to plugins and search for {plugin name} -
  • - - -
  • - - - - Fill name and URL, then test connection -
  • -``` - -## Permission-Aware Patterns - -### Admin vs User Workflows -```html - - -

    Complete Setup (Admin)

    -
      -
    • Install plugins
    • -
    • Configure data sources
    • -
    • Manage users
    • -
    -
    - - - -

    User Setup (Non-Admin)

    -
      -
    • Browse dashboards
    • -
    • Try data exploration
    • -
    • - - Contact your administrator to request additional permissions - for creating dashboards, configuring data sources, or installing plugins. - - Know how to request additional access -
    • -
    -
    -``` - -### Skippable Feature Patterns -```html - -
  • - Use advanced query expressions (skip if not supported) -
  • - - -
  • - Open custom application (skip if plugin not installed) -
  • -``` - -## Error Recovery Patterns - -### Primary + Fallback Pattern -```html - -
  • - Use automatic setup wizard -
  • - - -
  • - Or configure data source manually -
  • -``` - -### Validation and Retry Pattern -```html - -
  • - Test the data source connection -
  • - - -
  • - - If the connection test failed, verify your URL and - authentication settings. Common issues include - incorrect port numbers, network connectivity, - or authentication credentials. - - Review connection settings if test failed -
  • -``` - -## AI Content Generation Guidelines - -### Step-by-Step Generation Process - -1. **Analyze User Request** - - Identify the main goal/outcome - - Determine required Grafana features - - Assess user permission requirements - - Plan logical step progression - -2. **Choose Guide Structure** - - Single workflow → one section - - Complex workflow → multiple sections - - Educational content → mix show-only and regular steps - - Quick actions → multistep elements - -3. **Generate Requirements** - - Always include `exists-reftarget` for DOM actions - - Add `navmenu-open` for navigation elements - - Include page requirements for page-specific actions - - Add permission requirements for admin features - - Include data/plugin requirements for dependent features - -4. **Add Objectives Where Appropriate** - - Use for expensive setup steps that might already be done - - Include for navigation when user might already be on target page - - Add for state checks that auto-complete workflows - -5. **Write Helpful Content** - - Clear step descriptions that match user mental model - - Interactive comments for complex UI elements - - Hints that provide context and guidance - - Error handling for edge cases - -### Content Quality Rules - -#### Step Descriptions -- **Action-focused**: "Click Save button" not "The save button can be clicked" -- **User-centric**: "Save your dashboard" not "Execute save action" -- **Context-aware**: "Enter Prometheus server URL" not "Enter URL" -- **Goal-oriented**: "Create monitoring dashboard" not "Fill out form" - -#### Interactive Comments -- **Educational**: Explain what elements do and why they matter -- **Contextual**: Provide information that helps users understand -- **Formatted**: Use `` for UI elements, `` for technical terms -- **Concise**: Keep under 250 characters for good UX - -#### Error Prevention -- **Comprehensive requirements**: Include all necessary preconditions -- **Helpful hints**: Explain what will happen and why -- **Graceful failures**: Provide skippable options for edge cases -- **Clear verification**: Use `data-verify` for state-changing actions - -### Common Mistakes to Avoid - -❌ **Missing Requirements** -```html - -
  • -``` -✅ **Good: Includes necessary requirements** -```html -
  • -``` - -❌ **Unclear Selectors** -```html - -
  • +```json +{ + "type": "section", + "id": "user-management", + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/users", + "content": "Navigate to user management" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add user", + "content": "Start adding new user" + }, + { + "type": "multistep", + "content": "Fill user details", + "steps": [ + {"action": "formfill", "reftarget": "input[id='name']", "targetvalue": "john"}, + {"action": "formfill", "reftarget": "input[id='email']", "targetvalue": "john@example.com"} + ] + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Create user", + "content": "Create the user account" + } + ] +} ``` -✅ **Good: Stable attribute-based selector** -```html -
  • -``` - -❌ **Missing Context** -```html - -
  • -``` -✅ **Good: Clear context and purpose** -```html -
  • -``` - -❌ **Overly Complex Steps** -```html - -
  • - - - - - - - Navigate to admin, go to users, add user, fill details, create user -
  • -``` -✅ **Good: Broken into logical sections** -```html - -
  • Navigate to user management
  • -
  • Start adding new user
  • -
  • - - - Fill user details -
  • -
  • Create the user account
  • -
    -``` \ No newline at end of file diff --git a/.cursor/complete-example-tutorial.mdc b/.cursor/complete-example-tutorial.mdc index 480c47c2d..6c290f041 100644 --- a/.cursor/complete-example-tutorial.mdc +++ b/.cursor/complete-example-tutorial.mdc @@ -8,1251 +8,466 @@ alwaysApply: true This complete guide demonstrates all interactive features and best practices. Use this as a template for creating comprehensive guides. -```html - - - - - Advanced Grafana Monitoring Setup - - -

    Advanced Grafana Monitoring Setup

    - -

    Welcome to the complete Grafana monitoring setup guide! This comprehensive guide will teach you:

    -
      -
    • Setting up multiple data sources (Prometheus, Loki)
    • -
    • Creating effective monitoring dashboards
    • -
    • Configuring intelligent alerting rules
    • -
    • Understanding advanced Grafana features
    • -
    - - -

    Section 1: Grafana Interface Overview

    -

    Before we start building, let's explore Grafana's interface to understand where everything is located.

    - - -
      - -
    • - - The navigation menu is your control center for accessing all Grafana features. - It organizes tools by function: Dashboards for visualizations, - Explore for ad-hoc queries, Alerting for monitoring, - and Administration for system management. - - Explore the main navigation structure -
    • - - -
    • - - The main content area is where you'll spend most of your time. - It displays dashboards, configuration pages, query interfaces, - and all the tools you need for effective observability. - - This is where your monitoring work happens -
    • - - -
    • - - The top header contains global controls available from any page: - organization switcher, user preferences, help resources, - and global search. These tools support your workflow regardless of where you are in Grafana. - - Notice the global controls in the header -
    • -
    -
    - - -

    Section 2: Configure Prometheus Data Source

    -

    Now let's set up Prometheus as our metrics data source. This will provide the foundation for all our monitoring dashboards.

    - - -
      - -
    • - Navigate to data source management -
    • - - -
    • - Click Add new data source -
    • - - -
    • - - Prometheus is a powerful monitoring system that collects - time-series metrics from your applications and infrastructure. - It's perfect for tracking performance indicators, resource usage, - and application health. - - Select Prometheus from the data source types -
    • - - -
    • - Set a descriptive name for the data source -
    • - - -
    • - - The Prometheus URL tells Grafana where to find your metrics server. - Port 9090 is the standard Prometheus port. In production, this might be - https://prometheus.company.com or an internal service address. - - Enter the Prometheus server URL -
    • - - -
    • - Save and test the connection -
    • -
    -
    - - -

    Section 3: Create Monitoring Dashboard

    -

    With our data source configured, let's create a comprehensive monitoring dashboard with multiple visualization types.

    - - -
      - -
    • - Navigate to create a new dashboard -
    • - - -
    • - - - - - - Add visualization and select Prometheus data source -
    • - - -
    • - - The up query shows how many monitoring targets are healthy. - This is a fundamental availability metric that tells you - if your monitoring infrastructure is working correctly. - - Enter the availability query -
    • - - -
    • - - - - - - Open visualization picker and select Stat -
    • - - -
    • - Set descriptive panel title -
    • - - -
    • - Apply the panel configuration -
    • - - -
    • - Add a second panel for trend analysis -
    • - - -
    • - - - - - - - - Add visualization, select Prometheus, and enter CPU usage query -
    • - - -
    • - - Time series visualizations are perfect for showing how metrics - change over time. This CPU usage percentage calculation helps you - understand resource utilization patterns and plan capacity accordingly. - - Set time series panel title -
    • - - -
    • - Apply the time series panel -
    • - - -
    • - - - - - - - - Save dashboard with title "System Monitoring" -
    • -
    -
    - - -

    Section 4: Configure Monitoring Alerts

    -

    Now let's set up intelligent alerts to notify you when system issues occur, before your users notice them.

    - - -
      - -
    • - Navigate to create new alert rule -
    • - - -
    • - - The alert rule form lets you define monitoring conditions that trigger notifications. - You'll specify query conditions, evaluation frequency, - threshold values, and notification channels to create - proactive monitoring that keeps your systems healthy. - - Examine the alert rule configuration interface -
    • - - -
    • - Name your alert rule descriptively -
    • - - -
    • - Select Prometheus as the alert data source -
    • - - -
    • - - This alert query triggers when CPU usage exceeds 80%. - The rate() function calculates per-second CPU usage, - and the condition > 80 defines our threshold - for what constitutes "high" CPU usage. - - Enter CPU usage alert condition -
    • - - -
    • - Set evaluation interval to 1 minute -
    • - - -
    • - Save the alert rule -
    • -
    -
    - - -

    Section 5: Explore Advanced Features

    -

    Let's explore some of Grafana's advanced features that can enhance your monitoring experience.

    - - -
      - -
    • - Explore the query library feature (skip if not available) -
    • - - -
    • - Navigate to Explore for ad-hoc queries -
    • - - -
    • - - Explore is your data investigation playground. Write queries, - test hypotheses, and analyze metrics without creating permanent dashboards. - It's perfect for incident troubleshooting and data discovery. - - Learn about the Explore interface -
    • - - -
    • - Access plugin management (admin only) -
    • - - -
    • - - Plugins extend Grafana's capabilities with new data sources, - panel types, and applications. While you need admin privileges - to install plugins, you can use any plugins that are already installed - in your Grafana instance. - - Learn about Grafana's plugin ecosystem -
    • -
    -
    - - -

    Section 6: Verify Your Setup

    -

    Let's verify everything is working correctly and explore your new monitoring capabilities.

    - - -
      - -
    • - Return to your dashboards list -
    • - - -
    • - - Your System Monitoring dashboard now appears in the dashboard list. - The star icon lets you favorite it for quick access, and the - menu button provides options for sharing, exporting, or managing permissions. - - Find your newly created monitoring dashboard -
    • - - -
    • - Open your System Monitoring dashboard -
    • - - -
    • - - Your Targets Up panel shows real-time availability data. - The large number indicates how many monitoring targets are healthy. - This gives you an instant overview of your infrastructure health. - - Review your availability monitoring panel -
    • - - -
    • - - The time range picker controls what time period your dashboard displays. - Try Last 1 hour for recent data, Last 24 hours for daily patterns, - or Last 7 days for weekly trend analysis. - - Notice the time range controls -
    • - - -
    • - - The refresh button updates your dashboard with the latest data. - You can also enable auto-refresh to keep your monitoring views - current without manual intervention - perfect for live monitoring scenarios. - - Learn about dashboard refresh options -
    • -
    -
    - - -

    🎉 Congratulations!

    -

    Outstanding work! You've successfully completed the advanced Grafana monitoring setup guide. You now have:

    -
      -
    • Comprehensive Interface Knowledge - You understand Grafana's layout and navigation
    • -
    • Prometheus Data Source - Connected and tested for reliable metrics collection
    • -
    • System Monitoring Dashboard - Multiple panel types showing different aspects of system health
    • -
    • Intelligent Alerting - Proactive monitoring that notifies you of issues
    • -
    • Advanced Feature Awareness - Knowledge of Explore, plugins, and additional capabilities
    • -
    - -

    What You Can Do Next

    -
      -
    • Expand Your Dashboard - Add more panels for memory, disk, and network monitoring
    • -
    • Configure More Alerts - Set up alerts for different metrics and thresholds
    • -
    • Explore Data Sources - Connect Loki for logs, Tempo for traces, or other systems
    • -
    • Team Collaboration - Share dashboards with your team and set up notification channels
    • -
    • Advanced Queries - Learn more complex PromQL for sophisticated monitoring
    • -
    - -

    Next Recommended Guide: Loki + Grafana 101 to add log monitoring to your observability stack.

    - - - -``` - -## Key Features Demonstrated - -### 1. Show-Only Educational Mode -- Interface orientation without state changes -- Rich interactive comments with formatting -- Progressive disclosure of concepts - -### 2. Requirements and Objectives System -- Auto-completion when objectives already met -- Permission-aware workflows -- Graceful degradation for missing requirements - -### 3. Multi-Step Atomic Actions -- Complex UI workflows as single user actions -- Just-in-time requirements checking -- Error handling with specific failure points - -### 4. Section-Based Organization -- Logical grouping of related steps -- Progress persistence across browser sessions -- Sequential dependencies between sections - -### 5. Advanced Error Handling -- Skippable steps for permission issues -- Alternative workflows for different user types -- Helpful error messages with context - -### 6. Performance Optimization -- Efficient selector usage -- Grouped requirement checks -- Lazy evaluation of complex conditions - -### 7. Accessibility Features -- Screen reader friendly hints -- Keyboard navigation support -- ARIA attribute integration - -## Guide Quality Characteristics - -### Educational Value -- **Progressive Learning**: Concepts introduced before practice -- **Contextual Explanations**: Rich comments explain why things matter -- **Multiple Learning Modes**: Observation, demonstration, and hands-on practice -- **Real-World Relevance**: Practical skills applicable to actual monitoring - -### Technical Excellence -- **Robust Error Handling**: Graceful failure and recovery options -- **Cross-Version Compatibility**: Works across different Grafana versions -- **Performance Conscious**: Efficient selectors and requirement checking -- **Accessibility Compliant**: Works with assistive technologies - -### User Experience -- **Clear Progress Indicators**: Users always know where they are -- **Resumable Workflows**: Can continue after interruptions -- **Helpful Guidance**: Hints and comments provide context -- **Flexible Paths**: Alternatives for different user capabilities - -This example demonstrates the full power of the interactive guide system and serves as a comprehensive template for creating high-quality, educational Grafana guides.# Complete Example Guide - All Features Demonstration - -## Guide: Advanced Grafana Monitoring Setup - -This complete guide demonstrates all interactive features and best practices. Use this as a template for creating comprehensive guides. - -```html - - - - - Advanced Grafana Monitoring Setup - - -

    Advanced Grafana Monitoring Setup

    - -

    Welcome to the complete Grafana monitoring setup guide! This comprehensive guide will teach you:

    -
      -
    • Setting up multiple data sources (Prometheus, Loki)
    • -
    • Creating effective monitoring dashboards
    • -
    • Configuring intelligent alerting rules
    • -
    • Understanding advanced Grafana features
    • -
    - - -

    Section 1: Grafana Interface Overview

    -

    Before we start building, let's explore Grafana's interface to understand where everything is located.

    - - -
      - -
    • - - The navigation menu is your control center for accessing all Grafana features. - It organizes tools by function: Dashboards for visualizations, - Explore for ad-hoc queries, Alerting for monitoring, - and Administration for system management. - - Explore the main navigation structure -
    • - - -
    • - - The main content area is where you'll spend most of your time. - It displays dashboards, configuration pages, query interfaces, - and all the tools you need for effective observability. - - This is where your monitoring work happens -
    • - - -
    • - - The top header contains global controls available from any page: - organization switcher, user preferences, help resources, - and global search. These tools support your workflow regardless of where you are in Grafana. - - Notice the global controls in the header -
    • -
    -
    - - -

    Section 2: Configure Prometheus Data Source

    -

    Now let's set up Prometheus as our metrics data source. This will provide the foundation for all our monitoring dashboards.

    - - -
      - -
    • - Navigate to data source management -
    • - - -
    • - Click Add new data source -
    • - - -
    • - - Prometheus is a powerful monitoring system that collects - time-series metrics from your applications and infrastructure. - It's perfect for tracking performance indicators, resource usage, - and application health. - - Select Prometheus from the data source types -
    • - - -
    • - Set a descriptive name for the data source -
    • - - -
    • - - The Prometheus URL tells Grafana where to find your metrics server. - Port 9090 is the standard Prometheus port. In production, this might be - https://prometheus.company.com or an internal service address. - - Enter the Prometheus server URL -
    • - - -
    • - Save and test the connection -
    • -
    -
    - - -

    Section 3: Create Monitoring Dashboard

    -

    With our data source configured, let's create a comprehensive monitoring dashboard with multiple visualization types.

    - - -
      - -
    • - Navigate to create a new dashboard -
    • - - -
    • - - - - - - Add visualization and select Prometheus data source -
    • - - -
    • - - The up query shows how many monitoring targets are healthy. - This is a fundamental availability metric that tells you - if your monitoring infrastructure is working correctly. - - Enter the availability query -
    • - - -
    • - - - - - - Open visualization picker and select Stat -
    • - - -
    • - Set descriptive panel title -
    • - - -
    • - Apply the panel configuration -
    • - - -
    • - Add a second panel for trend analysis -
    • - - -
    • - - - - - - - - Add visualization, select Prometheus, and enter CPU usage query -
    • - - -
    • - - Time series visualizations are perfect for showing how metrics - change over time. This CPU usage percentage calculation helps you - understand resource utilization patterns and plan capacity accordingly. - - Set time series panel title -
    • - - -
    • - Apply the time series panel -
    • - - -
    • - - - - - - - - Save dashboard with title "System Monitoring" -
    • -
    -
    - - -

    Section 4: Configure Monitoring Alerts

    -

    Now let's set up intelligent alerts to notify you when system issues occur, before your users notice them.

    - - -
      - -
    • - Navigate to create new alert rule -
    • - - -
    • - - The alert rule form lets you define monitoring conditions that trigger notifications. - You'll specify query conditions, evaluation frequency, - threshold values, and notification channels to create - proactive monitoring that keeps your systems healthy. - - Examine the alert rule configuration interface -
    • - - -
    • - Name your alert rule descriptively -
    • - - -
    • - Select Prometheus as the alert data source -
    • - - -
    • - - This alert query triggers when CPU usage exceeds 80%. - The rate() function calculates per-second CPU usage, - and the condition > 80 defines our threshold - for what constitutes "high" CPU usage. - - Enter CPU usage alert condition -
    • - - -
    • - Set evaluation interval to 1 minute -
    • - - -
    • - Save the alert rule -
    • -
    -
    - - -

    Section 5: Explore Advanced Features

    -

    Let's explore some of Grafana's advanced features that can enhance your monitoring experience.

    - - -
      - -
    • - Explore the query library feature (skip if not available) -
    • - - -
    • - Navigate to Explore for ad-hoc queries -
    • - - -
    • - - Explore is your data investigation playground. Write queries, - test hypotheses, and analyze metrics without creating permanent dashboards. - It's perfect for incident troubleshooting and data discovery. - - Learn about the Explore interface -
    • - - -
    • - Access plugin management (admin only) -
    • - - -
    • - - Plugins extend Grafana's capabilities with new data sources, - panel types, and applications. While you need admin privileges - to install plugins, you can use any plugins that are already installed - in your Grafana instance. - - Learn about Grafana's plugin ecosystem -
    • -
    -
    - - -

    Section 6: Verify Your Setup

    -

    Let's verify everything is working correctly and explore your new monitoring capabilities.

    - - -
      - -
    • - Return to your dashboards list -
    • - - -
    • - - Your System Monitoring dashboard now appears in the dashboard list. - The star icon lets you favorite it for quick access, and the - menu button provides options for sharing, exporting, or managing permissions. - - Find your newly created monitoring dashboard -
    • - - -
    • - Open your System Monitoring dashboard -
    • - - -
    • - - Your Targets Up panel shows real-time availability data. - The large number indicates how many monitoring targets are healthy. - This gives you an instant overview of your infrastructure health. - - Review your availability monitoring panel -
    • - - -
    • - - The time range picker controls what time period your dashboard displays. - Try Last 1 hour for recent data, Last 24 hours for daily patterns, - or Last 7 days for weekly trend analysis. - - Notice the time range controls -
    • - - -
    • - - The refresh button updates your dashboard with the latest data. - You can also enable auto-refresh to keep your monitoring views - current without manual intervention - perfect for live monitoring scenarios. - - Learn about dashboard refresh options -
    • -
    -
    - - -

    🎉 Congratulations!

    -

    Outstanding work! You've successfully completed the advanced Grafana monitoring setup guide. You now have:

    -
      -
    • Comprehensive Interface Knowledge - You understand Grafana's layout and navigation
    • -
    • Prometheus Data Source - Connected and tested for reliable metrics collection
    • -
    • System Monitoring Dashboard - Multiple panel types showing different aspects of system health
    • -
    • Intelligent Alerting - Proactive monitoring that notifies you of issues
    • -
    • Advanced Feature Awareness - Knowledge of Explore, plugins, and additional capabilities
    • -
    - -

    What You Can Do Next

    -
      -
    • Expand Your Dashboard - Add more panels for memory, disk, and network monitoring
    • -
    • Configure More Alerts - Set up alerts for different metrics and thresholds
    • -
    • Explore Data Sources - Connect Loki for logs, Tempo for traces, or other systems
    • -
    • Team Collaboration - Share dashboards with your team and set up notification channels
    • -
    • Advanced Queries - Learn more complex PromQL for sophisticated monitoring
    • -
    - -

    Next Recommended Guide: Loki + Grafana 101 to add log monitoring to your observability stack.

    - - - +```json +{ + "id": "advanced-grafana-monitoring", + "title": "Advanced Grafana Monitoring Setup", + "blocks": [ + { + "type": "markdown", + "content": "# Advanced Grafana Monitoring Setup\n\nWelcome to the complete Grafana monitoring setup guide! This comprehensive guide will teach you:\n\n- Setting up multiple data sources (Prometheus, Loki)\n- Creating effective monitoring dashboards\n- Configuring intelligent alerting rules\n- Understanding advanced Grafana features" + }, + { + "type": "section", + "id": "interface-tour", + "title": "Section 1: Grafana Interface Overview", + "tooltip": "Educational tour of Grafana's main interface elements", + "blocks": [ + { + "type": "markdown", + "content": "Before we start building, let's explore Grafana's interface to understand where everything is located." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "nav[data-testid='navigation-mega-menu']", + "requirements": ["navmenu-open"], + "doIt": false, + "tooltip": "The navigation menu is your control center for accessing all Grafana features. It organizes tools by function: Dashboards for visualizations, Explore for ad-hoc queries, Alerting for monitoring, and Administration for system management.", + "content": "Explore the main navigation structure" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "main[data-testid='main-content']", + "doIt": false, + "tooltip": "The main content area is where you'll spend most of your time. It displays dashboards, configuration pages, query interfaces, and all the tools you need for effective observability.", + "content": "This is where your monitoring work happens" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "header[data-testid='grafana-app-header']", + "doIt": false, + "tooltip": "The top header contains global controls available from any page: organization switcher, user preferences, help resources, and global search. These tools support your workflow regardless of where you are in Grafana.", + "content": "Notice the global controls in the header" + } + ] + }, + { + "type": "section", + "id": "setup-prometheus", + "title": "Section 2: Configure Prometheus Data Source", + "requirements": ["is-admin"], + "objectives": ["has-datasource:type:prometheus"], + "tooltip": "Creates Prometheus data source - skipped if already configured", + "blocks": [ + { + "type": "markdown", + "content": "Now let's set up Prometheus as our metrics data source. This will provide the foundation for all our monitoring dashboards." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "requirements": ["navmenu-open"], + "verify": "on-page:/connections", + "content": "Navigate to data source management" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "requirements": ["exists-reftarget"], + "content": "Click Add new data source" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections/datasources/prometheus']", + "requirements": ["exists-reftarget"], + "tooltip": "Prometheus is a powerful monitoring system that collects time-series metrics from your applications and infrastructure. It's perfect for tracking performance indicators, resource usage, and application health.", + "content": "Select Prometheus from the data source types" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='basic-settings-name']", + "targetvalue": "prometheus-main", + "requirements": ["exists-reftarget"], + "tooltip": "Descriptive names help identify data sources in large deployments", + "content": "Set a descriptive name for the data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "http://prometheus:9090", + "requirements": ["exists-reftarget"], + "tooltip": "The Prometheus URL tells Grafana where to find your metrics server. Port 9090 is the standard Prometheus port. In production, this might be https://prometheus.company.com or an internal service address.", + "content": "Enter the Prometheus server URL" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "requirements": ["exists-reftarget"], + "verify": "has-datasource:prometheus-main", + "content": "Save and test the connection" + } + ] + }, + { + "type": "section", + "id": "create-monitoring-dashboard", + "title": "Section 3: Create Monitoring Dashboard", + "requirements": ["section-completed:setup-prometheus"], + "objectives": ["has-dashboard-named:System Monitoring"], + "tooltip": "Creates comprehensive monitoring dashboard with multiple panels", + "blocks": [ + { + "type": "markdown", + "content": "With our data source configured, let's create a comprehensive monitoring dashboard with multiple visualization types." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboard/new", + "requirements": ["navmenu-open"], + "verify": "on-page:/dashboard/new", + "content": "Navigate to create a new dashboard" + }, + { + "type": "multistep", + "requirements": ["on-page:/dashboard/new"], + "tooltip": "Creates a stat panel showing system availability", + "content": "Add visualization and select Prometheus data source", + "steps": [ + { + "action": "button", + "reftarget": "Add visualization", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "prometheus-main", + "requirements": ["has-datasource:prometheus-main"] + } + ] + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "requirements": ["exists-reftarget"], + "tooltip": "The up query shows how many monitoring targets are healthy. This is a fundamental availability metric that tells you if your monitoring infrastructure is working correctly.", + "content": "Enter the availability query" + }, + { + "type": "multistep", + "requirements": ["exists-reftarget"], + "tooltip": "Changes to stat visualization for single-value display", + "content": "Open visualization picker and select Stat", + "steps": [ + { + "action": "button", + "reftarget": "button[data-testid='toggle-viz-picker']", + "requirements": ["exists-reftarget"] + }, + { + "action": "highlight", + "reftarget": "div[aria-label='Plugin visualization item Stat']", + "requirements": ["exists-reftarget"] + } + ] + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "Targets Up", + "requirements": ["exists-reftarget"], + "content": "Set descriptive panel title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Apply", + "requirements": ["exists-reftarget"], + "content": "Apply the panel configuration" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add panel", + "requirements": ["exists-reftarget", "on-page:/dashboard"], + "content": "Add a second panel for trend analysis" + }, + { + "type": "multistep", + "requirements": ["on-page:/dashboard"], + "tooltip": "Creates time series panel for CPU usage trends", + "content": "Add visualization, select Prometheus, and enter CPU usage query", + "steps": [ + { + "action": "button", + "reftarget": "Add visualization", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "prometheus-main", + "requirements": ["has-datasource:prometheus-main"] + }, + { + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "100 - (avg(rate(node_cpu_seconds_total{mode='idle'}[5m])) * 100)", + "requirements": ["exists-reftarget"] + } + ] + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "CPU Usage %", + "requirements": ["exists-reftarget"], + "tooltip": "Time series visualizations are perfect for showing how metrics change over time. This CPU usage percentage calculation helps you understand resource utilization patterns and plan capacity accordingly.", + "content": "Set time series panel title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Apply", + "requirements": ["exists-reftarget"], + "content": "Apply the time series panel" + }, + { + "type": "multistep", + "requirements": ["on-page:/dashboard"], + "tooltip": "Saves dashboard with descriptive title", + "content": "Save dashboard with title 'System Monitoring'", + "steps": [ + { + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["exists-reftarget"] + }, + { + "action": "formfill", + "reftarget": "input[aria-label='Save dashboard title field']", + "targetvalue": "System Monitoring", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Save", + "requirements": ["exists-reftarget"] + } + ] + } + ] + }, + { + "type": "section", + "id": "setup-alerting", + "title": "Section 4: Configure Monitoring Alerts", + "requirements": ["section-completed:create-monitoring-dashboard", "has-permission:alerting:write"], + "tooltip": "Configures CPU usage alert - requires alerting permissions", + "blocks": [ + { + "type": "markdown", + "content": "Now let's set up intelligent alerts to notify you when system issues occur, before your users notice them." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/alerting/new", + "requirements": ["navmenu-open"], + "verify": "on-page:/alerting/new", + "content": "Navigate to create new alert rule" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='alert-rule-form']", + "requirements": ["exists-reftarget"], + "doIt": false, + "tooltip": "The alert rule form lets you define monitoring conditions that trigger notifications. You'll specify query conditions, evaluation frequency, threshold values, and notification channels to create proactive monitoring that keeps your systems healthy.", + "content": "Examine the alert rule configuration interface" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='alert-rule-name']", + "targetvalue": "High CPU Usage Alert", + "requirements": ["exists-reftarget"], + "content": "Name your alert rule descriptively" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "prometheus-main", + "requirements": ["has-datasource:prometheus-main", "exists-reftarget"], + "content": "Select Prometheus as the alert data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='alert-query-editor']", + "targetvalue": "100 - (avg(rate(node_cpu_seconds_total{mode='idle'}[5m])) * 100) > 80", + "requirements": ["exists-reftarget"], + "tooltip": "This alert query triggers when CPU usage exceeds 80%. The rate() function calculates per-second CPU usage, and the condition > 80 defines our threshold for what constitutes 'high' CPU usage.", + "content": "Enter CPU usage alert condition" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='evaluation-interval']", + "targetvalue": "1m", + "requirements": ["exists-reftarget"], + "tooltip": "How often Grafana checks this condition", + "content": "Set evaluation interval to 1 minute" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save rule", + "requirements": ["exists-reftarget"], + "content": "Save the alert rule" + } + ] + }, + { + "type": "section", + "id": "advanced-features", + "title": "Section 5: Explore Advanced Features", + "requirements": ["section-completed:setup-alerting"], + "blocks": [ + { + "type": "markdown", + "content": "Let's explore some of Grafana's advanced features that can enhance your monitoring experience." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/explore", + "requirements": ["navmenu-open", "has-feature:queryLibrary", "min-version:8.5.0"], + "skippable": true, + "tooltip": "Query library requires specific Grafana version and feature toggle", + "verify": "on-page:/explore", + "content": "Explore the query library feature (skip if not available)" + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/explore", + "requirements": ["navmenu-open"], + "verify": "on-page:/explore", + "content": "Navigate to Explore for ad-hoc queries" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='query-editor-container']", + "requirements": ["on-page:/explore", "exists-reftarget"], + "doIt": false, + "tooltip": "Explore is your data investigation playground. Write queries, test hypotheses, and analyze metrics without creating permanent dashboards. It's perfect for incident troubleshooting and data discovery.", + "content": "Learn about the Explore interface" + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/plugins", + "requirements": ["is-admin", "navmenu-open"], + "skippable": true, + "tooltip": "Plugin management requires admin privileges", + "verify": "on-page:/admin/plugins", + "content": "Access plugin management (admin only)" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='plugin-info']", + "requirements": ["has-role:viewer"], + "doIt": false, + "tooltip": "Plugins extend Grafana's capabilities with new data sources, panel types, and applications. While you need admin privileges to install plugins, you can use any plugins that are already installed in your Grafana instance.", + "content": "Learn about Grafana's plugin ecosystem" + } + ] + }, + { + "type": "section", + "id": "verify-setup", + "title": "Section 6: Verify Your Setup", + "requirements": ["section-completed:create-monitoring-dashboard"], + "blocks": [ + { + "type": "markdown", + "content": "Let's verify everything is working correctly and explore your new monitoring capabilities." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboards", + "requirements": ["navmenu-open"], + "verify": "on-page:/dashboards", + "content": "Return to your dashboards list" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[title*='System Monitoring']", + "requirements": ["has-dashboard-named:System Monitoring", "exists-reftarget"], + "doIt": false, + "tooltip": "Your System Monitoring dashboard now appears in the dashboard list. The star icon lets you favorite it for quick access, and the menu button provides options for sharing, exporting, or managing permissions.", + "content": "Find your newly created monitoring dashboard" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "System Monitoring", + "requirements": ["has-dashboard-named:System Monitoring", "exists-reftarget"], + "verify": "on-page:/d/", + "content": "Open your System Monitoring dashboard" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='panel-container']:first-of-type", + "requirements": ["exists-reftarget"], + "doIt": false, + "tooltip": "Your Targets Up panel shows real-time availability data. The large number indicates how many monitoring targets are healthy. This gives you an instant overview of your infrastructure health.", + "content": "Review your availability monitoring panel" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='time-range-picker']", + "requirements": ["exists-reftarget"], + "doIt": false, + "tooltip": "The time range picker controls what time period your dashboard displays. Try Last 1 hour for recent data, Last 24 hours for daily patterns, or Last 7 days for weekly trend analysis.", + "content": "Notice the time range controls" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='refresh-button']", + "requirements": ["exists-reftarget"], + "doIt": false, + "tooltip": "The refresh button updates your dashboard with the latest data. You can also enable auto-refresh to keep your monitoring views current without manual intervention - perfect for live monitoring scenarios.", + "content": "Learn about dashboard refresh options" + } + ] + }, + { + "type": "markdown", + "content": "## 🎉 Congratulations!\n\nOutstanding work! You've successfully completed the advanced Grafana monitoring setup guide. You now have:\n\n- ✅ **Comprehensive Interface Knowledge** - You understand Grafana's layout and navigation\n- ✅ **Prometheus Data Source** - Connected and tested for reliable metrics collection\n- ✅ **System Monitoring Dashboard** - Multiple panel types showing different aspects of system health\n- ✅ **Intelligent Alerting** - Proactive monitoring that notifies you of issues\n- ✅ **Advanced Feature Awareness** - Knowledge of Explore, plugins, and additional capabilities\n\n### What You Can Do Next\n\n- **Expand Your Dashboard** - Add more panels for memory, disk, and network monitoring\n- **Configure More Alerts** - Set up alerts for different metrics and thresholds\n- **Explore Data Sources** - Connect Loki for logs, Tempo for traces, or other systems\n- **Team Collaboration** - Share dashboards with your team and set up notification channels\n- **Advanced Queries** - Learn more complex PromQL for sophisticated monitoring\n\n**Next Recommended Guide:** [Loki + Grafana 101](bundled:loki-grafana-101) to add log monitoring to your observability stack." + } + ] +} ``` ## Key Features Demonstrated @@ -1312,4 +527,4 @@ This complete guide demonstrates all interactive features and best practices. Us - **Helpful Guidance**: Hints and comments provide context - **Flexible Paths**: Alternatives for different user capabilities -This example demonstrates the full power of the interactive guide system and serves as a comprehensive template for creating high-quality, educational Grafana guides. \ No newline at end of file +This example demonstrates the full power of the interactive guide system and serves as a comprehensive template for creating high-quality, educational Grafana guides. diff --git a/.cursor/complex-selectors-and-hover.mdc b/.cursor/complex-selectors-and-hover.mdc index f391bace0..2bb9f910b 100644 --- a/.cursor/complex-selectors-and-hover.mdc +++ b/.cursor/complex-selectors-and-hover.mdc @@ -15,13 +15,14 @@ The interactive guide system now supports advanced CSS selectors including `:has Trigger hover states on elements to reveal UI that appears only on hover (e.g., Tailwind `group-hover:` classes, CSS `:hover` states). ### Syntax -```html -
  • - {Description of what to hover} -
  • +```json +{ + "type": "interactive", + "action": "hover", + "reftarget": "{CSS_SELECTOR}", + "requirements": ["exists-reftarget"], + "content": "{Description of what to hover}" +} ``` ### Behavior @@ -33,51 +34,66 @@ Trigger hover states on elements to reveal UI that appears only on hover (e.g., ### Common Use Cases #### Hover-Revealed Action Buttons -```html - -
  • - - - Edit user details -
  • +```json +// Buttons that appear on row hover +{ + "type": "multistep", + "content": "Edit user details", + "steps": [ + { + "action": "hover", + "reftarget": "tr[data-row-id='user-123']", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Edit", + "requirements": ["exists-reftarget"] + } + ] +} ``` #### Hover-Revealed Menus -```html - -
  • - - - Access preferences from settings menu -
  • +```json +// Dropdown menus on navigation hover +{ + "type": "multistep", + "content": "Access preferences from settings menu", + "steps": [ + { + "action": "hover", + "reftarget": "nav[role='navigation'] > div:contains('Settings')", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Preferences", + "requirements": ["exists-reftarget"] + } + ] +} ``` #### Service List Hover Actions -```html - -
  • - - - Open checkout service dashboard -
  • +```json +// Application Observability workload list +{ + "type": "multistep", + "content": "Open checkout service dashboard", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('checkoutservice'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Dashboard", + "requirements": ["exists-reftarget"] + } + ] +} ``` ### Best Practices for Hover @@ -95,49 +111,54 @@ Trigger hover states on elements to reveal UI that appears only on hover (e.g., Finds elements containing specific text content (jQuery-style selector). #### Basic Usage -```html - -
  • - Highlight checkout service -
  • - - -
  • - Find error messages -
  • - - -
  • - Find running status indicators -
  • +```json +// Find by text content +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div:contains('checkoutservice')", + "requirements": ["exists-reftarget"], + "content": "Highlight checkout service" +} + +// Case-sensitive matching +{ + "type": "interactive", + "action": "highlight", + "reftarget": "p:contains('ERROR')", + "requirements": ["exists-reftarget"], + "content": "Find error messages" +} + +// Partial text matching +{ + "type": "interactive", + "action": "highlight", + "reftarget": "span:contains('Running')", + "requirements": ["exists-reftarget"], + "content": "Find running status indicators" +} ``` #### Combined with Attributes -```html - -
  • - Find frontend service card -
  • - - -
  • - Find production dashboard -
  • +```json +// Most reliable: Attribute + text content +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-cy='service-card']:contains('frontend')", + "requirements": ["exists-reftarget"], + "content": "Find frontend service card" +} + +// Dashboard list item by title +{ + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='dashboard-link']:contains('Production Metrics')", + "requirements": ["exists-reftarget"], + "content": "Find production dashboard" +} ``` ### `:has()` Pseudo-Selector @@ -145,49 +166,54 @@ Finds elements containing specific text content (jQuery-style selector). Finds elements that contain specific descendant elements. #### Basic Usage -```html - -
  • - Highlight service cards with descriptions -
  • - - -
  • - Find panels with alert icons -
  • - - -
  • - Highlight forms with validation errors -
  • +```json +// Find containers with specific children +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-cy='service-card']:has(p)", + "requirements": ["exists-reftarget"], + "content": "Highlight service cards with descriptions" +} + +// Find panels with alerts +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='panel']:has(svg[data-icon='alert'])", + "requirements": ["exists-reftarget"], + "content": "Find panels with alert icons" +} + +// Find forms with errors +{ + "type": "interactive", + "action": "highlight", + "reftarget": "form:has(span[role='alert'])", + "requirements": ["exists-reftarget"], + "content": "Highlight forms with validation errors" +} ``` #### Advanced Targeting -```html - -
  • - Find deletable rows -
  • - - -
  • - Find configurable sections -
  • +```json +// Find rows with specific buttons +{ + "type": "interactive", + "action": "highlight", + "reftarget": "tr:has(button[data-action='delete'])", + "requirements": ["exists-reftarget"], + "content": "Find deletable rows" +} + +// Find sections with interactive elements +{ + "type": "interactive", + "action": "highlight", + "reftarget": "section:has(button[data-testid='configure'])", + "requirements": ["exists-reftarget"], + "content": "Find configurable sections" +} ``` ### Combined Complex Selectors @@ -195,89 +221,105 @@ Finds elements that contain specific descendant elements. The most powerful feature: combining `:has()` and `:contains()` for precise targeting. #### Service/Workload Lists -```html - -
  • - Highlight checkout service item -
  • - - -
  • - Find healthy running services -
  • +```json +// Find specific service by name +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('checkoutservice'))", + "requirements": ["exists-reftarget"], + "content": "Highlight checkout service item" +} + +// Find service with specific status +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-service]:has(span:contains('Running')):has(span:contains('Healthy'))", + "requirements": ["exists-reftarget"], + "content": "Find healthy running services" +} ``` #### Dashboard and Panel Selection -```html - -
  • - Find production dashboard -
  • - - -
  • - Find CPU usage panel -
  • +```json +// Find dashboard by title +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='dashboard-card']:has(h3:contains('Production'))", + "requirements": ["exists-reftarget"], + "content": "Find production dashboard" +} + +// Find panel by title +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='panel']:has(h2:contains('CPU Usage'))", + "requirements": ["exists-reftarget"], + "content": "Find CPU usage panel" +} ``` #### Form Error Handling -```html - -
  • - Highlight login form with error -
  • - - -
  • - Find required field errors -
  • +```json +// Find forms with specific errors +{ + "type": "interactive", + "action": "highlight", + "reftarget": "form:has(div:contains('Invalid credentials'))", + "requirements": ["exists-reftarget"], + "content": "Highlight login form with error" +} + +// Find required fields with errors +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-field]:has(span:contains('required'))", + "requirements": ["exists-reftarget"], + "content": "Find required field errors" +} ``` #### Hover + Complex Selector Patterns -```html - -
  • - - - View checkout service dashboard -
  • - - -
  • - - - Star the Kubernetes dashboard -
  • +```json +// Hover over specific service to reveal buttons +{ + "type": "multistep", + "content": "View checkout service dashboard", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('checkoutservice'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Dashboard", + "requirements": ["exists-reftarget"] + } + ] +} + +// Hover over dashboard card to reveal actions +{ + "type": "multistep", + "content": "Star the Kubernetes dashboard", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-testid='dashboard-card']:has(h3:contains('Kubernetes'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Star", + "requirements": ["exists-reftarget"] + } + ] +} ``` ## Browser Compatibility @@ -301,10 +343,13 @@ The system provides transparent JavaScript fallbacks: - Logs fallback usage for debugging (in dev mode) ### No Configuration Required -```html - -
  • +```json +// Just use complex selectors - system handles compatibility +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div:has(p:contains('text'))" +} ``` ## Performance Optimization @@ -312,20 +357,20 @@ The system provides transparent JavaScript fallbacks: ### Efficient Selector Strategies #### Good Selectors (Fast) -```html - +``` +// Specific base with complex refinement div[data-cy="service-item"]:has(p:contains("checkout")) div[data-testid="card"]:contains("Production") section[id="services"]:has(button:contains("Configure")) ``` #### Poor Selectors (Slow) -```html - +``` +// Too generic - searches entire DOM div:contains("service") div:has(button) - +// Overly complex nesting div:has(ul:has(li:has(span:contains("text")))) ``` @@ -353,74 +398,96 @@ div:has(ul:has(li:has(span:contains("text")))) ### Application Observability Workload List -```html - - -
      -
    • - - - Open checkout service dashboard -
    • -
    -
    +```json +// Navigate to specific service +{ + "type": "section", + "id": "inspect-checkout-service", + "blocks": [ + { + "type": "multistep", + "content": "Open checkout service dashboard", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('checkoutservice'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Dashboard", + "requirements": ["exists-reftarget"] + } + ] + } + ] +} ``` ### Kubernetes Workload Management -```html - -
  • - - - View nginx pod logs -
  • +```json +// Interact with specific pod +{ + "type": "multistep", + "content": "View nginx pod logs", + "steps": [ + { + "action": "hover", + "reftarget": "tr[data-resource='pod']:has(td:contains('nginx-deployment'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "View logs", + "requirements": ["exists-reftarget"] + } + ] +} ``` ### Dashboard Management -```html - -
  • - - - Favorite the production metrics dashboard -
  • +```json +// Star specific dashboard +{ + "type": "multistep", + "content": "Favorite the production metrics dashboard", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-testid='dashboard-card']:has(h3:contains('Production Metrics'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Mark \"Production Metrics\" as favorite", + "requirements": ["exists-reftarget"] + } + ] +} ``` ### Alert Rules by Name -```html - -
  • - - - Edit the high CPU alert rule -
  • +```json +// Edit specific alert rule +{ + "type": "multistep", + "content": "Edit the high CPU alert rule", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-testid='alert-rule-row']:has(span:contains('High CPU Alert'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "Edit", + "requirements": ["exists-reftarget"] + } + ] +} ``` ## Complex Selector Reference @@ -433,21 +500,21 @@ div:has(ul:has(li:has(span:contains("text")))) **Partial Match**: Yes #### Examples -```html - -
  • +``` +// Service names +div:contains("checkoutservice") - -
  • +// Status indicators +span:contains("Running") - -
  • +// Error messages +p:contains("ERROR") - -
  • +// Dashboard titles +h2:contains("Production") - -
  • +// Button labels +button:contains("Configure") ``` ### `:has()` Pseudo-Selector @@ -457,21 +524,21 @@ div:has(ul:has(li:has(span:contains("text")))) **Nesting**: Supports multiple levels (but limit to 2 for performance) #### Examples -```html - -
  • +``` +// Cards with descriptions +div[data-cy="card"]:has(p) - -
  • +// Services with alerts +div[data-service]:has(svg[data-icon="alert"]) - -
  • +// Panels with queries +div[data-panel]:has(textarea[data-testid="query-editor"]) - -
  • +// Forms with errors +form:has(span[role="alert"]) - -
  • +// Lists with checkboxes +ul:has(input[type="checkbox"]) ``` ### Combined Selectors @@ -480,36 +547,36 @@ div:has(ul:has(li:has(span:contains("text")))) **Power**: Most precise targeting for dynamic content #### Examples -```html - -
  • +``` +// Specific service item +div[data-cy="wb-list-item"]:has(p:contains("checkoutservice")) - -
  • +// Dashboard with status +div[data-testid="dashboard-card"]:has(span:contains("Active")) - -
  • +// Alert rule by name +tr[data-alert-rule]:has(td:contains("High Memory")) - -
  • +// User row by email +tr[data-user]:has(td:contains("admin@example.com")) - -
  • +// Panel by title +div[data-panel-id]:has(h2:contains("CPU Usage")) ``` ### Nested Complex Selectors **Use Sparingly**: Performance impact increases with nesting depth -```html - -
  • +``` +// Good: One level of complexity +div[data-service]:has(span:contains("Running")) - -
  • +// Acceptable: Two levels of complexity +div[data-service]:has(div[data-status]:contains("Healthy")) - -
  • +// Avoid: Three+ levels of complexity +div:has(ul:has(li:has(span:contains("text")))) ``` ## AI Generation Guidelines @@ -568,96 +635,115 @@ Need to target element: ### Service List Interactions -```html - -
  • - - - {{ACTION_DESCRIPTION}} -
  • +```json +// Pattern: Find service, hover to reveal, click action +{ + "type": "multistep", + "content": "{{ACTION_DESCRIPTION}}", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('{{SERVICE_NAME}}'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "{{ACTION_BUTTON}}", + "requirements": ["exists-reftarget"] + } + ] +} ``` ### Table Row Actions -```html - -
  • - - - {{ACTION_DESCRIPTION}} -
  • +```json +// Pattern: Find row by content, hover, click action +{ + "type": "multistep", + "content": "{{ACTION_DESCRIPTION}}", + "steps": [ + { + "action": "hover", + "reftarget": "tr:has(td:contains('{{ROW_IDENTIFIER}}'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "{{ACTION_BUTTON}}", + "requirements": ["exists-reftarget"] + } + ] +} ``` ### Card-Based UIs -```html - -
  • - - - {{ACTION_DESCRIPTION}} -
  • +```json +// Pattern: Find card by title, hover, click action +{ + "type": "multistep", + "content": "{{ACTION_DESCRIPTION}}", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-card]:has(h3:contains('{{CARD_TITLE}}'))", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "{{ACTION_BUTTON}}", + "requirements": ["exists-reftarget"] + } + ] +} ``` ## Migration Examples ### Before: Limited Targeting -```html - -
  • - Highlight first service (might not be the right one) -
  • +```json +// Old approach: Less precise +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-cy='wb-list-item']:first-of-type", + "content": "Highlight first service (might not be the right one)" +} ``` ### After: Precise Targeting -```html - -
  • - Highlight checkout service (exact match) -
  • -``` - -### Before: Manual Hover Not Possible - -```html - - +```json +// New approach: Exact targeting +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-cy='wb-list-item']:has(p:contains('checkoutservice'))", + "requirements": ["exists-reftarget"], + "content": "Highlight checkout service (exact match)" +} ``` ### After: Hover Support -```html - -
  • - - - Edit item -
  • +```json +// New: Full hover interaction support +{ + "type": "multistep", + "content": "Edit item", + "steps": [ + { + "action": "hover", + "reftarget": "div[data-cy='item']" + }, + { + "action": "button", + "reftarget": "Edit" + } + ] +} ``` ## Troubleshooting @@ -719,4 +805,4 @@ Need to target element: - ✅ Include appropriate page requirements - ❌ Don't assume elements exist without checking -This enhanced functionality makes interactive guides significantly more powerful and precise, especially for dynamic interfaces like Application Observability, Kubernetes monitoring, and dashboard management. \ No newline at end of file +This enhanced functionality makes interactive guides significantly more powerful and precise, especially for dynamic interfaces like Application Observability, Kubernetes monitoring, and dashboard management. diff --git a/.cursor/requirements-quick-reference.mdc b/.cursor/requirements-quick-reference.mdc index bac6eb679..1b218cbbd 100644 --- a/.cursor/requirements-quick-reference.mdc +++ b/.cursor/requirements-quick-reference.mdc @@ -4,10 +4,10 @@ alwaysApply: true # Requirements and Objectives Quick Reference for AI ## Syntax Rules -- **Requirements**: `data-requirements="req1,req2,req3"` (ALL must pass) -- **Objectives**: `data-objectives="obj1,obj2,obj3"` (ALL must pass, auto-completes when met) +- **Requirements**: `requirements: ["req1", "req2", "req3"]` (ALL must pass) +- **Objectives**: `objectives: ["obj1", "obj2", "obj3"]` (ALL must pass, auto-completes when met) - **Priority**: Objectives always win over requirements -- **Verification**: `data-verify="condition"` (checked after action execution) +- **Verification**: `verify: "condition"` (checked after action execution) ## Essential Requirements (Use These Frequently) @@ -35,28 +35,28 @@ alwaysApply: true ## Common Requirement Combinations ### Navigation Actions -```html -data-requirements="navmenu-open,exists-reftarget" +```json +"requirements": ["navmenu-open", "exists-reftarget"] ``` ### Admin Actions -```html -data-requirements="is-admin,navmenu-open,exists-reftarget" +```json +"requirements": ["is-admin", "navmenu-open", "exists-reftarget"] ``` ### Data Source Actions -```html -data-requirements="has-datasource:prometheus,on-page:/explore,exists-reftarget" +```json +"requirements": ["has-datasource:prometheus", "on-page:/explore", "exists-reftarget"] ``` ### Dashboard Actions -```html -data-requirements="has-datasources,on-page:/dashboard,exists-reftarget" +```json +"requirements": ["has-datasources", "on-page:/dashboard", "exists-reftarget"] ``` ### Plugin Actions -```html -data-requirements="has-plugin:plugin-id,is-admin,exists-reftarget" +```json +"requirements": ["has-plugin:plugin-id", "is-admin", "exists-reftarget"] ``` ## Objectives Patterns @@ -314,77 +314,94 @@ data-objectives="has-dashboard-named:My Dashboard" ``` ### Section-Level Auto-Completion -```html - - - - +```json +// Entire section skipped if objective met +{ + "type": "section", + "id": "setup-prometheus", + "title": "Setup Prometheus", + "objectives": ["has-datasource:type:prometheus"], + "blocks": [ + // All child steps marked complete if Prometheus exists + ] +} ``` ## AI Generation Templates ### Basic Step Template -```html -
  • - - {Interactive comment if complex UI element} - Human-readable step description -
  • +```json +{ + "type": "interactive", + "action": "ACTION_TYPE", + "reftarget": "TARGET_REFERENCE", + "targetvalue": "VALUE_FOR_FORMFILL", + "requirements": ["REQUIREMENTS_LIST"], + "objectives": ["OBJECTIVES_LIST"], + "verify": "VERIFICATION_CONDITION", + "tooltip": "USER_HELPFUL_DESCRIPTION", + "skippable": true, + "doIt": false, + "content": "Human-readable step description" +} ``` ### Section Template -```html - - -

    {Section Title}

    -

    {Section description explaining goals}

    - -
      - {Child interactive steps} -
    -
    +```json +{ + "type": "section", + "id": "unique-section-id", + "title": "Section Title", + "requirements": ["SECTION_REQUIREMENTS"], + "objectives": ["SECTION_OBJECTIVES"], + "tooltip": "SECTION_DESCRIPTION", + "blocks": [ + { + "type": "markdown", + "content": "Section description explaining goals" + }, + { + "type": "interactive", + "action": "..." + } + ] +} ``` ### Multi-step Template -```html -
  • - - {Internal action spans - not rendered visually} - - - - {User-visible description of what happens} -
  • +```json +{ + "type": "multistep", + "requirements": ["OVERALL_REQUIREMENTS"], + "objectives": ["OVERALL_OBJECTIVES"], + "tooltip": "WHAT_THIS_ACCOMPLISHES", + "content": "User-visible description of what happens", + "steps": [ + { + "action": "TYPE", + "reftarget": "TARGET", + "requirements": ["REQ"] + }, + { + "action": "TYPE", + "reftarget": "TARGET", + "requirements": ["REQ"] + } + ] +} ``` ### Interactive Comment Template -```html - - The {UI_ELEMENT_NAME} {purpose/function explanation}. - {Context about why it's important}. Use it for {technical_features} - to {user_benefit_or_goal}. - +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "SELECTOR", + "doIt": false, + "requirements": ["exists-reftarget"], + "tooltip": "The UI_ELEMENT_NAME purpose/function explanation. Context about why it's important. Use it for technical_features to user_benefit_or_goal.", + "content": "Learn about UI_ELEMENT_NAME" +} ``` ## Stable Selector Library @@ -438,7 +455,7 @@ Prometheus Type: a[href='/connections/datasources/prometheus'] - `on-page:/path` - Shows message about navigating to correct page ### Skippable Requirements -- Use `data-skippable="true"` when requirements might legitimately fail +- Use `skippable: true` when requirements might legitimately fail - Common for admin-only features, plugin-dependent features - Shows "Skip" button when requirements not met @@ -449,7 +466,7 @@ Prometheus Type: a[href='/connections/datasources/prometheus'] ❌ **Slow**: `.class1 .class2 .class3`, `div > div > button`, `[class*='partial']` ### Requirement Optimization -- **Group related requirements**: `navmenu-open,exists-reftarget` +- **Group related requirements**: `navmenu-open`, `exists-reftarget` - **Use specific requirements**: `has-datasource:prometheus` vs `has-datasources` - **Cache expensive checks**: Use objectives for expensive state checks - **Avoid redundant checks**: Don't duplicate requirements across steps @@ -459,8 +476,8 @@ Prometheus Type: a[href='/connections/datasources/prometheus'] ### For Every Interactive Element - [ ] Includes appropriate requirements for action type - [ ] Uses stable, testable selectors -- [ ] Has helpful hint text for users -- [ ] Includes verification for state-changing actions +- [ ] Has helpful hint text for users (`tooltip`) +- [ ] Includes verification for state-changing actions (`verify`) - [ ] Handles error cases gracefully ### For Sections @@ -475,4 +492,4 @@ Prometheus Type: a[href='/connections/datasources/prometheus'] - [ ] Multi-steps are truly atomic operations - [ ] Error messages are specific and actionable - [ ] Alternative paths provided for different user states -- [ ] Performance impact is reasonable \ No newline at end of file +- [ ] Performance impact is reasonable diff --git a/.cursor/selector-library.mdc b/.cursor/selector-library.mdc index c8e2d3084..91be736e5 100644 --- a/.cursor/selector-library.mdc +++ b/.cursor/selector-library.mdc @@ -31,7 +31,7 @@ alwaysApply: true ## Core Navigation Selectors ### Main Navigation Menu -```html +``` Home: a[data-testid='data-testid Nav menu item'][href='/'] Dashboards: a[data-testid='data-testid Nav menu item'][href='/dashboards'] @@ -52,7 +52,7 @@ Explore: a[href='/explore'] ``` ### Admin Subsections -```html +``` Users: a[data-testid='data-testid Nav menu item'][href='/admin/users'] Teams: a[data-testid='data-testid Nav menu item'][href='/admin/teams'] @@ -67,7 +67,7 @@ Plugins: a[href='/admin/plugins'] ``` ### Alerting Subsections -```html +``` Alert Rules: a[data-testid='data-testid Nav menu item'][href='/alerting/list'] Contact Points: a[data-testid='data-testid Nav menu item'][href='/alerting/notifications'] @@ -83,7 +83,7 @@ Contact Points: a[href='/alerting/notifications'] ## Dashboard and Panel Selectors ### Dashboard Management -```html +``` New Dashboard: button[data-testid='new-dashboard'] Save Dashboard: button[data-testid='save-dashboard'] @@ -103,7 +103,7 @@ Dashboard Settings: "Settings", "Dashboard settings" ``` ### Panel Management -```html +``` Add Panel: button[data-testid='add-panel'] Add Visualization: button[data-testid='add-visualization'] @@ -122,7 +122,7 @@ Panel Title: input[aria-label*='title'], input[placeholder*='title'] ``` ### Visualization and Query Editor -```html +``` Query Editor: textarea[data-testid='query-editor'] Monaco Editor: textarea.inputarea.monaco-mouse-cursor-text @@ -150,7 +150,7 @@ Run Query: "Run query", "Refresh", "Execute" ## Data Source Configuration Selectors ### General Data Source Fields -```html +``` Data Source Name: input[id='basic-settings-name'] Connection URL: input[id='connection-url'] @@ -180,7 +180,7 @@ Save & Test: "Save & test", "Save and test", "Test connection" ``` ### Prometheus Specific -```html +``` Prometheus Type: a[href='/connections/datasources/prometheus'] @@ -197,7 +197,7 @@ Query Timeout: "60s" ``` ### Loki Specific -```html +``` Loki Type: a[href='/connections/datasources/loki'] @@ -213,7 +213,7 @@ Timeout: "60s" ``` ### InfluxDB Specific -```html +``` InfluxDB Type: a[href='/connections/datasources/influxdb'] @@ -232,7 +232,7 @@ User: "admin" ## Form Element Patterns ### Input Field Types -```html +``` Text Input by ID: input[id='{field-id}'] Text Input by TestID: input[data-testid='{field-testid}'] @@ -264,7 +264,7 @@ Color Picker: input[type='color'] ``` ### Form Interaction Patterns -```html +```
  • Save: "Save", "Save dashboard", "Save changes" Create: "Create", "Create dashboard", "Create new" @@ -344,7 +344,7 @@ Configure: "Configure", "Configuration" ``` ### Context-Specific Buttons -```html +``` "Add panel" - when on dashboard edit page "Save dashboard" - when editing dashboard @@ -371,7 +371,7 @@ Configure: "Configure", "Configuration" ## Specialized Component Selectors ### Time and Date Controls -```html +``` Time Range Button: button[data-testid='time-range-picker'] Absolute Time: button[data-testid='absolute-time'] @@ -389,7 +389,7 @@ To Time: input[data-testid='time-to'] ``` ### Search and Filter Elements -```html +``` Global Search: input[data-testid='search-input'] Search Button: button[data-testid='search-button'] @@ -408,7 +408,7 @@ Data Source Filter: select[data-testid='data-source-filter'] ``` ### Modal and Dialog Selectors -```html +``` Modal Dialog: div[role='dialog'] Modal Header: div[data-testid='modal-header'] @@ -428,7 +428,7 @@ Confirm Save: button[data-testid='confirm-save'] ## Advanced Selector Patterns ### Pseudo-Selector Usage -```html +``` First Panel: div[data-testid='panel-container']:first-of-type Last Panel: div[data-testid='panel-container']:last-of-type @@ -447,7 +447,7 @@ Checked Checkbox: input[type='checkbox']:checked ``` ### Attribute Combination Patterns -```html +``` Active Nav Item: a[data-testid='nav-item'][aria-current='page'] Enabled Button: button[data-testid='action-btn']:not([disabled]) @@ -464,7 +464,7 @@ Delete Button: button[data-testid='delete-btn'][aria-label*='delete'] ``` ### Hierarchical Selection -```html +``` Nav Menu Link: nav[data-testid='navigation'] a[href='/dashboards'] Panel Editor Button: div[data-testid='panel-editor'] button[data-testid='save'] @@ -479,7 +479,7 @@ Alert Form: form[data-testid='alert-form'] input[data-testid='rule-name'] ## Query Editor Specific Selectors ### Query Editor Components -```html +``` Query Editor: textarea[data-testid='query-editor'] Monaco Query Editor: textarea.inputarea.monaco-mouse-cursor-text @@ -503,7 +503,7 @@ Relative Time: input[data-testid='relative-time'] ``` ### Data Source Specific Query Elements -```html +``` PromQL Editor: textarea[data-testid='prometheus-query-editor'] Prometheus Metrics Browser: button[data-testid='metrics-browser'] @@ -523,7 +523,7 @@ Field Select: select[data-testid='field-select'] ## Plugin and App Selectors ### Plugin Management -```html +``` Plugin Catalog: div[data-testid='plugin-catalog'] Plugin Search: input[data-testid='plugin-search'] @@ -544,7 +544,7 @@ Plugin Author: span[data-testid='plugin-author'] ``` ### Common App Plugin Selectors -```html +``` Synthetic Monitoring: a[href='/a/grafana-synthetic-monitoring-app/'] SM Checks: div[data-testid='synthetic-monitoring-checks'] @@ -564,7 +564,7 @@ Create Job: button[data-testid='create-ml-job'] ## Alerting System Selectors ### Alert Rule Management -```html +``` Alert Rules List: div[data-testid='alert-rules-list'] Alert Rule Row: div[data-testid='alert-rule-row'] @@ -584,7 +584,7 @@ Delete Rule: button[data-testid='delete-alert-rule'] ``` ### Contact Points and Notifications -```html +``` Contact Points List: div[data-testid='contact-points-list'] New Contact Point: button[data-testid='new-contact-point'] @@ -601,7 +601,7 @@ Webhook URL: input[data-testid='webhook-url'] ## User and Team Management Selectors ### User Management -```html +``` Users List: table[data-testid='users-table'] User Row: tr[data-testid='user-row'] @@ -621,7 +621,7 @@ Reset Password: button[data-testid='reset-password'] ``` ### Team Management -```html +``` Teams List: div[data-testid='teams-list'] Team Card: div[data-testid='team-card'] @@ -641,7 +641,7 @@ Add Member: button[data-testid='add-team-member'] ## Performance-Optimized Selectors ### Fast Selectors (Prefer These) -```html +``` #unique-element-id @@ -657,7 +657,7 @@ div[role='dialog'] ``` ### Moderate Performance Selectors -```html +``` input[type='text'][data-testid='field'] button[data-testid='action'][aria-label='Save'] @@ -670,7 +670,7 @@ input[data-testid='field']:nth-of-type(2) ``` ### Avoid These (Slow) Selectors -```html +``` .container .row .col .panel .header .title @@ -778,7 +778,7 @@ measureSelector(".complex .nested .selector"); // Avoid if > 5ms ## Core Navigation Selectors ### Main Navigation Menu -```html +``` Home: a[data-testid='data-testid Nav menu item'][href='/'] Dashboards: a[data-testid='data-testid Nav menu item'][href='/dashboards'] @@ -799,7 +799,7 @@ Explore: a[href='/explore'] ``` ### Admin Subsections -```html +``` Users: a[data-testid='data-testid Nav menu item'][href='/admin/users'] Teams: a[data-testid='data-testid Nav menu item'][href='/admin/teams'] @@ -814,7 +814,7 @@ Plugins: a[href='/admin/plugins'] ``` ### Alerting Subsections -```html +``` Alert Rules: a[data-testid='data-testid Nav menu item'][href='/alerting/list'] Contact Points: a[data-testid='data-testid Nav menu item'][href='/alerting/notifications'] @@ -830,7 +830,7 @@ Contact Points: a[href='/alerting/notifications'] ## Dashboard and Panel Selectors ### Dashboard Management -```html +``` New Dashboard: button[data-testid='new-dashboard'] Save Dashboard: button[data-testid='save-dashboard'] @@ -850,7 +850,7 @@ Dashboard Settings: "Settings", "Dashboard settings" ``` ### Panel Management -```html +``` Add Panel: button[data-testid='add-panel'] Add Visualization: button[data-testid='add-visualization'] @@ -869,7 +869,7 @@ Panel Title: input[aria-label*='title'], input[placeholder*='title'] ``` ### Visualization and Query Editor -```html +``` Query Editor: textarea[data-testid='query-editor'] Monaco Editor: textarea.inputarea.monaco-mouse-cursor-text @@ -897,7 +897,7 @@ Run Query: "Run query", "Refresh", "Execute" ## Data Source Configuration Selectors ### General Data Source Fields -```html +``` Data Source Name: input[id='basic-settings-name'] Connection URL: input[id='connection-url'] @@ -927,7 +927,7 @@ Save & Test: "Save & test", "Save and test", "Test connection" ``` ### Prometheus Specific -```html +``` Prometheus Type: a[href='/connections/datasources/prometheus'] @@ -944,7 +944,7 @@ Query Timeout: "60s" ``` ### Loki Specific -```html +``` Loki Type: a[href='/connections/datasources/loki'] @@ -960,7 +960,7 @@ Timeout: "60s" ``` ### InfluxDB Specific -```html +``` InfluxDB Type: a[href='/connections/datasources/influxdb'] @@ -979,7 +979,7 @@ User: "admin" ## Form Element Patterns ### Input Field Types -```html +``` Text Input by ID: input[id='{field-id}'] Text Input by TestID: input[data-testid='{field-testid}'] @@ -1011,7 +1011,7 @@ Color Picker: input[type='color'] ``` ### Form Interaction Patterns -```html +```
  • Save: "Save", "Save dashboard", "Save changes" Create: "Create", "Create dashboard", "Create new" @@ -1091,7 +1091,7 @@ Configure: "Configure", "Configuration" ``` ### Context-Specific Buttons -```html +``` "Add panel" - when on dashboard edit page "Save dashboard" - when editing dashboard @@ -1118,7 +1118,7 @@ Configure: "Configure", "Configuration" ## Specialized Component Selectors ### Time and Date Controls -```html +``` Time Range Button: button[data-testid='time-range-picker'] Absolute Time: button[data-testid='absolute-time'] @@ -1136,7 +1136,7 @@ To Time: input[data-testid='time-to'] ``` ### Search and Filter Elements -```html +``` Global Search: input[data-testid='search-input'] Search Button: button[data-testid='search-button'] @@ -1155,7 +1155,7 @@ Data Source Filter: select[data-testid='data-source-filter'] ``` ### Modal and Dialog Selectors -```html +``` Modal Dialog: div[role='dialog'] Modal Header: div[data-testid='modal-header'] @@ -1175,7 +1175,7 @@ Confirm Save: button[data-testid='confirm-save'] ## Advanced Selector Patterns ### Pseudo-Selector Usage -```html +``` First Panel: div[data-testid='panel-container']:first-of-type Last Panel: div[data-testid='panel-container']:last-of-type @@ -1194,7 +1194,7 @@ Checked Checkbox: input[type='checkbox']:checked ``` ### Attribute Combination Patterns -```html +``` Active Nav Item: a[data-testid='nav-item'][aria-current='page'] Enabled Button: button[data-testid='action-btn']:not([disabled]) @@ -1211,7 +1211,7 @@ Delete Button: button[data-testid='delete-btn'][aria-label*='delete'] ``` ### Hierarchical Selection -```html +``` Nav Menu Link: nav[data-testid='navigation'] a[href='/dashboards'] Panel Editor Button: div[data-testid='panel-editor'] button[data-testid='save'] @@ -1226,7 +1226,7 @@ Alert Form: form[data-testid='alert-form'] input[data-testid='rule-name'] ## Query Editor Specific Selectors ### Query Editor Components -```html +``` Query Editor: textarea[data-testid='query-editor'] Monaco Query Editor: textarea.inputarea.monaco-mouse-cursor-text @@ -1250,7 +1250,7 @@ Relative Time: input[data-testid='relative-time'] ``` ### Data Source Specific Query Elements -```html +``` PromQL Editor: textarea[data-testid='prometheus-query-editor'] Prometheus Metrics Browser: button[data-testid='metrics-browser'] @@ -1270,7 +1270,7 @@ Field Select: select[data-testid='field-select'] ## Plugin and App Selectors ### Plugin Management -```html +``` Plugin Catalog: div[data-testid='plugin-catalog'] Plugin Search: input[data-testid='plugin-search'] @@ -1291,7 +1291,7 @@ Plugin Author: span[data-testid='plugin-author'] ``` ### Common App Plugin Selectors -```html +``` Synthetic Monitoring: a[href='/a/grafana-synthetic-monitoring-app/'] SM Checks: div[data-testid='synthetic-monitoring-checks'] @@ -1311,7 +1311,7 @@ Create Job: button[data-testid='create-ml-job'] ## Alerting System Selectors ### Alert Rule Management -```html +``` Alert Rules List: div[data-testid='alert-rules-list'] Alert Rule Row: div[data-testid='alert-rule-row'] @@ -1331,7 +1331,7 @@ Delete Rule: button[data-testid='delete-alert-rule'] ``` ### Contact Points and Notifications -```html +``` Contact Points List: div[data-testid='contact-points-list'] New Contact Point: button[data-testid='new-contact-point'] @@ -1348,7 +1348,7 @@ Webhook URL: input[data-testid='webhook-url'] ## User and Team Management Selectors ### User Management -```html +``` Users List: table[data-testid='users-table'] User Row: tr[data-testid='user-row'] @@ -1368,7 +1368,7 @@ Reset Password: button[data-testid='reset-password'] ``` ### Team Management -```html +``` Teams List: div[data-testid='teams-list'] Team Card: div[data-testid='team-card'] @@ -1388,7 +1388,7 @@ Add Member: button[data-testid='add-team-member'] ## Performance-Optimized Selectors ### Fast Selectors (Prefer These) -```html +``` #unique-element-id @@ -1404,7 +1404,7 @@ div[role='dialog'] ``` ### Moderate Performance Selectors -```html +``` input[type='text'][data-testid='field'] button[data-testid='action'][aria-label='Save'] @@ -1417,7 +1417,7 @@ input[data-testid='field']:nth-of-type(2) ``` ### Avoid These (Slow) Selectors -```html +``` .container .row .col .panel .header .title diff --git a/.cursor/tutorial-patterns.mdc b/.cursor/tutorial-patterns.mdc index 23820dea0..e6dea7b2c 100644 --- a/.cursor/tutorial-patterns.mdc +++ b/.cursor/tutorial-patterns.mdc @@ -4,60 +4,65 @@ alwaysApply: true # Interactive Guide Patterns for AI Generation -The term "guide" is preferred over "guide" throughout this documentation. +The term "guide" is preferred over "tutorial" throughout this documentation. ## Guide Structure Patterns ### Complete Guide Template -```html - - - - {Guide Title} - - -

    {Guide Title}

    - -

    {Introduction explaining what user will learn}

    -
      -
    • {Learning objective 1}
    • -
    • {Learning objective 2}
    • -
    • {Learning objective 3}
    • -
    - - {Section 1} - {Section 2} - {Section N} - -

    🎉 Congratulations!

    -

    {Summary of what was accomplished}

    -
      -
    • ✅ {Achievement 1}
    • -
    • ✅ {Achievement 2}
    • -
    • ✅ {Achievement 3}
    • -
    - -

    Next: {Link to next guide or suggested actions}

    - - +```json +{ + "id": "unique-guide-id", + "title": "Guide Title", + "blocks": [ + { + "type": "markdown", + "content": "# Guide Title\n\nIntroduction explaining what user will learn.\n\n- Learning objective 1\n- Learning objective 2\n- Learning objective 3" + }, + { + "type": "section", + "id": "section-1", + "title": "Section 1 Title", + "blocks": [ + // Section 1 steps + ] + }, + { + "type": "section", + "id": "section-2", + "title": "Section 2 Title", + "blocks": [ + // Section 2 steps + ] + }, + { + "type": "markdown", + "content": "## 🎉 Congratulations!\n\nSummary of what was accomplished.\n\n- ✅ Achievement 1\n- ✅ Achievement 2\n- ✅ Achievement 3\n\n**Next:** Link to next guide or suggested actions" + } + ] +} ``` ### Section Structure Pattern -```html -

    Section {N}: {Section Title}

    -

    {Section description and context}

    - - -
      - {Interactive steps} -
    -
    +```json +{ + "type": "section", + "id": "section-id", + "title": "Section Title", + "requirements": ["section-requirements"], + "objectives": ["section-objectives"], + "tooltip": "section-description", + "blocks": [ + { + "type": "markdown", + "content": "Section description and context" + }, + { + "type": "interactive", + "action": "...", + "content": "Step 1" + } + ] +} ``` ## Common Guide Types @@ -67,35 +72,31 @@ The term "guide" is preferred over "guide" throughout this documentation. **Pattern**: Show-only highlights with rich comments **Sections**: Navigation, Dashboard basics, Key features -```html - -
      -
    • - - The Home page is your Grafana dashboard hub. - It shows recent dashboards, starred favorites, - and quick access to common tasks. - - Start with the Home page -
    • - -
    • - - Dashboards are collections of panels displaying your data. - Create time series, stats, tables - and more to visualize your monitoring data. - - Explore the Dashboards section -
    • -
    -
    +```json +{ + "type": "section", + "id": "grafana-tour", + "title": "Grafana Tour", + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='Nav menu item'][href='/']", + "requirements": ["navmenu-open"], + "doIt": false, + "tooltip": "The Home page is your Grafana dashboard hub. It shows recent dashboards, starred favorites, and quick access to common tasks.", + "content": "Start with the Home page" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='Nav menu item'][href='/dashboards']", + "doIt": false, + "tooltip": "Dashboards are collections of panels displaying your data. Create time series, stats, tables and more to visualize your monitoring data.", + "content": "Explore the Dashboards section" + } + ] +} ``` ### 2. Setup/Configuration Guide @@ -103,72 +104,86 @@ The term "guide" is preferred over "guide" throughout this documentation. **Pattern**: Sections with verification and objectives **Sections**: Prerequisites, Configuration, Testing, Usage -```html -

    Section 1: Set Up Data Source

    - -
      -
    • - Navigate to data sources -
    • - -
    • - Add new data source -
    • - -
    • - Select Prometheus -
    • - -
    • - Name the data source -
    • - -
    • - Set server URL -
    • - -
    • - Save and test connection -
    • -
    -
    - -

    Section 2: Create Dashboard

    - - - +```json +{ + "id": "setup-guide", + "title": "Setup Guide", + "blocks": [ + { + "type": "markdown", + "content": "## Section 1: Set Up Data Source" + }, + { + "type": "section", + "id": "setup-datasource", + "title": "Set Up Data Source", + "requirements": ["is-admin"], + "objectives": ["has-datasource:prometheus"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources", + "requirements": ["navmenu-open"], + "verify": "on-page:/connections", + "content": "Navigate to data sources" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add new data source", + "requirements": ["exists-reftarget"], + "content": "Add new data source" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections/datasources/prometheus']", + "requirements": ["exists-reftarget"], + "content": "Select Prometheus" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='basic-settings-name']", + "targetvalue": "prometheus-datasource", + "requirements": ["exists-reftarget"], + "content": "Name the data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='connection-url']", + "targetvalue": "http://prometheus:9090", + "requirements": ["exists-reftarget"], + "content": "Set server URL" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "requirements": ["exists-reftarget"], + "verify": "has-datasource:prometheus-datasource", + "content": "Save and test connection" + } + ] + }, + { + "type": "markdown", + "content": "## Section 2: Create Dashboard" + }, + { + "type": "section", + "id": "create-dashboard", + "title": "Create Dashboard", + "requirements": ["section-completed:setup-datasource"], + "objectives": ["has-dashboard-named:Monitoring Dashboard"], + "blocks": [ + // Dashboard creation steps + ] + } + ] +} ``` ### 3. Feature Deep-Dive Guide @@ -176,33 +191,42 @@ The term "guide" is preferred over "guide" throughout this documentation. **Pattern**: Mix of education (show-only) and practice (regular actions) **Sections**: Concept intro, Basic usage, Advanced features -```html -

    Understanding Grafana Alerting

    - - - -
      -
    • - - Alert rules continuously monitor your metrics. - When conditions like CPU > 80% are met, they trigger - notifications to keep your team informed. - - Explore the alert rules interface -
    • -
    -
    - - - - - +```json +{ + "id": "feature-guide", + "title": "Understanding Grafana Alerting", + "blocks": [ + { + "type": "markdown", + "content": "## Understanding Grafana Alerting" + }, + { + "type": "section", + "id": "learn-alerting", + "title": "Learn Alerting", + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='alert-rules-list']", + "requirements": ["on-page:/alerting"], + "doIt": false, + "tooltip": "Alert rules continuously monitor your metrics. When conditions like CPU > 80% are met, they trigger notifications to keep your team informed.", + "content": "Explore the alert rules interface" + } + ] + }, + { + "type": "section", + "id": "create-alert", + "title": "Create Alert", + "requirements": ["section-completed:learn-alerting", "has-datasource:prometheus"], + "blocks": [ + // Actual alert creation steps + ] + } + ] +} ``` ### 4. Workflow Guide @@ -210,144 +234,206 @@ The term "guide" is preferred over "guide" throughout this documentation. **Pattern**: Sequential sections building toward final goal **Sections**: Setup, Configuration, Implementation, Validation -```html -

    Complete Monitoring Setup

    - - - - - - - - - - - - - - - +```json +{ + "id": "workflow-guide", + "title": "Complete Monitoring Setup", + "blocks": [ + { + "type": "markdown", + "content": "# Complete Monitoring Setup" + }, + { + "type": "section", + "id": "setup-monitoring-data", + "title": "Setup Monitoring Data", + "objectives": ["has-datasource:type:prometheus"], + "blocks": [ + // Data source setup + ] + }, + { + "type": "section", + "id": "create-monitoring-dashboard", + "title": "Create Monitoring Dashboard", + "requirements": ["section-completed:setup-monitoring-data"], + "objectives": ["has-dashboard-named:System Monitoring"], + "blocks": [ + // Dashboard creation + ] + }, + { + "type": "section", + "id": "setup-monitoring-alerts", + "title": "Setup Monitoring Alerts", + "requirements": ["section-completed:create-monitoring-dashboard"], + "objectives": ["exists-reftarget"], // Check for alert rules + "blocks": [ + // Alert setup + ] + } + ] +} ``` ## Content Writing Patterns ### Step Description Patterns -```html - -
  • - Save your dashboard changes -
  • - - -
  • - Set the Prometheus server URL to connect to your metrics -
  • - - -
  • - - This panel displays your metrics in real-time... - - Examine how panels display your monitoring data -
  • +```json +// Action-focused descriptions +{ + "type": "interactive", + "action": "button", + "reftarget": "Save", + "content": "Save your dashboard changes" +} + +// Context-rich descriptions +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='url']", + "targetvalue": "http://prometheus:9090", + "content": "Set the Prometheus server URL to connect to your metrics" +} + +// Educational descriptions +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='panel']", + "doIt": false, + "tooltip": "This panel displays your metrics in real-time...", + "content": "Examine how panels display your monitoring data" +} ``` ### Section Introduction Patterns -```html - -

    Section 1: Configure Prometheus Data Source

    -

    In this section, we'll connect Grafana to your Prometheus server so you can query and visualize your metrics.

    - - -

    Section 2: Create Your First Dashboard

    -

    Now that your data source is configured, let's create a dashboard to display your Prometheus metrics in meaningful visualizations.

    - - -

    Section 3: Set Up Alerting

    -

    Dashboards show you what happened, but alerts tell you when something is happening. Let's create alert rules to monitor your system proactively.

    +```json +// Goal-oriented +{ + "type": "markdown", + "content": "## Section 1: Configure Prometheus Data Source\n\nIn this section, we'll connect Grafana to your Prometheus server so you can query and visualize your metrics." +} + +// Context-setting +{ + "type": "markdown", + "content": "## Section 2: Create Your First Dashboard\n\nNow that your data source is configured, let's create a dashboard to display your Prometheus metrics in meaningful visualizations." +} + +// Problem-solving +{ + "type": "markdown", + "content": "## Section 3: Set Up Alerting\n\nDashboards show you what happened, but alerts tell you when something is happening. Let's create alert rules to monitor your system proactively." +} ``` ## Advanced Patterns ### Conditional Workflows -```html - - - - - - - - +```json +// Different paths based on user state +{ + "type": "section", + "id": "admin-setup", + "title": "Admin Setup", + "requirements": ["is-admin"], + "tooltip": "Admin-specific setup process", + "blocks": [ + // Admin workflow + ] +} + +{ + "type": "section", + "id": "user-setup", + "title": "User Setup", + "requirements": ["has-role:viewer"], + "tooltip": "Non-admin workflow", + "blocks": [ + // Limited user workflow + ] +} ``` ### Progressive Complexity -```html - - -
  • - Enter basic "up" query -
  • -
    - - - -
  • - Enter rate calculation query -
  • -
    - - - -
  • - Enter aggregated rate query -
  • -
    +```json +// Basic level +{ + "type": "section", + "id": "basic-query", + "title": "Basic Query", + "blocks": [ + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea", + "targetvalue": "up", + "content": "Enter basic 'up' query" + } + ] +} + +// Intermediate level +{ + "type": "section", + "id": "intermediate-query", + "title": "Intermediate Query", + "requirements": ["section-completed:basic-query"], + "blocks": [ + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea", + "targetvalue": "rate(http_requests_total[5m])", + "content": "Enter rate calculation query" + } + ] +} + +// Advanced level +{ + "type": "section", + "id": "advanced-query", + "title": "Advanced Query", + "requirements": ["section-completed:intermediate-query", "has-feature:expressions"], + "blocks": [ + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea", + "targetvalue": "sum(rate(http_requests_total[5m])) by (status)", + "content": "Enter aggregated rate query" + } + ] +} ``` ### Error Recovery Workflows -```html - - - - - - - - - +```json +// Primary workflow +{ + "type": "section", + "id": "primary-setup", + "title": "Primary Setup", + "objectives": ["has-datasource:prometheus"], + "blocks": [ + // Normal setup steps + ] +} + +// Alternative workflow +{ + "type": "section", + "id": "alternative-setup", + "title": "Alternative Setup", + "requirements": ["section-completed:primary-setup"], + "skippable": true, + "blocks": [ + // Alternative if primary fails + ] +} ``` ## AI Generation Decision Trees @@ -359,8 +445,8 @@ User request analysis: "Click [element_description]" → highlight action "Enter [value]" → formfill action "Go to [page]" → navigate action - "Do [multiple things]" → sequence or multistep - "Show [element]" → highlight with data-doit="false" + "Do [multiple things]" → sequence (as section) or multistep + "Show [element]" → highlight with doIt: false ``` ### Choose Requirements @@ -380,225 +466,214 @@ Action context analysis: Content complexity analysis: Single action → individual step Related actions → multistep - Multiple checkpoints → sequence + Multiple checkpoints → section Educational content → show-only with comments - Complex workflow → sequence with mixed step types + Complex workflow → section with mixed step types ``` ## Common Guide Scenarios ### Scenario: "Show me how to create a dashboard" -```html - - -

    Create Your First Dashboard

    - -
      -
    • - Navigate to create new dashboard -
    • - -
    • - Add your first visualization -
    • - -
    • - Select your data source -
    • - -
    • - Enter a basic query -
    • - -
    • - Set panel title -
    • - -
    • - Save the dashboard -
    • -
    -
    +```json +{ + "type": "section", + "id": "create-dashboard-guide", + "title": "Create Your First Dashboard", + "requirements": ["has-datasources"], + "objectives": ["has-dashboard-named:My First Dashboard"], + "blocks": [ + { + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboard/new", + "requirements": ["navmenu-open"], + "verify": "on-page:/dashboard/new", + "content": "Navigate to create new dashboard" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Add visualization", + "requirements": ["exists-reftarget"], + "content": "Add your first visualization" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "prometheus-datasource", + "requirements": ["has-datasource:prometheus"], + "content": "Select your data source" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "requirements": ["exists-reftarget"], + "content": "Enter a basic query" + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='Panel editor option pane field input Title']", + "targetvalue": "System Status", + "requirements": ["exists-reftarget"], + "content": "Set panel title" + }, + { + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["exists-reftarget"], + "verify": "has-dashboard-named:My First Dashboard", + "content": "Save the dashboard" + } + ] +} ``` ### Scenario: "Set up Prometheus monitoring" -```html -

    Prometheus + Grafana Setup

    - - - - -

    Configure Prometheus Data Source

    - -
    - - - - -

    Create Prometheus Dashboard

    - -
    - - - - -

    Configure Prometheus Alerts

    - -
    +```json +{ + "id": "prometheus-setup", + "title": "Prometheus + Grafana Setup", + "blocks": [ + { + "type": "markdown", + "content": "# Prometheus + Grafana Setup" + }, + { + "type": "section", + "id": "setup-prometheus-datasource", + "title": "Configure Prometheus Data Source", + "requirements": ["is-admin"], + "objectives": ["has-datasource:type:prometheus"], + "blocks": [ + // Data source setup steps + ] + }, + { + "type": "section", + "id": "create-prometheus-dashboard", + "title": "Create Prometheus Dashboard", + "requirements": ["section-completed:setup-prometheus-datasource"], + "blocks": [ + // Dashboard creation steps + ] + }, + { + "type": "section", + "id": "setup-prometheus-alerts", + "title": "Configure Prometheus Alerts", + "requirements": ["section-completed:create-prometheus-dashboard"], + "blocks": [ + // Alert setup steps + ] + } + ] +} ``` ### Scenario: "Explain this interface element" -```html -
  • - - - The query editor is where you write queries in your data source's - native language. For Prometheus, use PromQL. For Loki, use LogQL. - The editor provides syntax highlighting and auto-completion to help you write effective queries. - - - Learn about the query editor interface -
  • +```json +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='query-editor-container']", + "requirements": ["on-page:/explore"], + "doIt": false, + "tooltip": "The query editor is where you write queries in your data source's native language. For Prometheus, use PromQL. For Loki, use LogQL. The editor provides syntax highlighting and auto-completion to help you write effective queries.", + "content": "Learn about the query editor interface" +} ``` ## Interactive Comment Guidelines ### Content Structure Template -```html - - The {UI_ELEMENT_NAME} {what_it_does}. - {why_its_important_or_context}. {specific_features_or_capabilities} - {user_benefit_or_outcome}. - +```json +{ + "tooltip": "The UI_ELEMENT_NAME purpose/function explanation. Context about why it's important. Use it for technical_features to user_benefit_or_outcome." +} ``` ### Applied Examples #### Feature Explanation -```html - - The visualization picker lets you choose how to display your data. - Different chart types tell different stories about your metrics. - Choose time series for trends, stat panels for current values, - and tables for detailed breakdowns. - +```json +{ + "tooltip": "The visualization picker lets you choose how to display your data. Different chart types tell different stories about your metrics. Choose time series for trends, stat panels for current values, and tables for detailed breakdowns." +} ``` #### Process Guidance -```html - - Save & test validates your data source configuration and saves it. - A green success message confirms everything works correctly, - while red error messages help you troubleshoot connection issues. - +```json +{ + "tooltip": "Save & test validates your data source configuration and saves it. A green success message confirms everything works correctly, while red error messages help you troubleshoot connection issues." +} ``` #### Context Building -```html - - This metrics panel shows your application's performance in real-time. - The time series chart reveals trends and patterns, helping you - identify performance bottlenecks and capacity planning needs. - +```json +{ + "tooltip": "This metrics panel shows your application's performance in real-time. The time series chart reveals trends and patterns, helping you identify performance bottlenecks and capacity planning needs." +} ``` ## Error Handling Patterns ### Graceful Degradation -```html - -
  • - Use quick setup wizard (skip if not available) -
  • - - -
  • - Or set up data source manually -
  • +```json +// Primary action with fallback +{ + "type": "interactive", + "action": "button", + "reftarget": "Quick setup", + "requirements": ["has-plugin:setup-wizard"], + "skippable": true, + "tooltip": "Uses setup wizard if available, can skip if not installed", + "content": "Use quick setup wizard (skip if not available)" +} + +// Manual fallback +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/connections/datasources/new", + "requirements": ["is-admin"], + "content": "Or set up data source manually" +} ``` ### Permission-Aware Guides -```html - - -

    Install Plugin (Admin)

    - -
    - - - -

    Request Plugin Installation

    -
      -
    • - - You can browse available plugins here, but installation requires admin privileges. - Contact your Grafana administrator to request plugin installations. - - Browse available plugins (view-only) -
    • -
    -
    +```json +// Admin path +{ + "type": "section", + "id": "admin-plugin-setup", + "title": "Install Plugin (Admin)", + "requirements": ["is-admin"], + "blocks": [ + // Admin-specific steps + ] +} + +// Non-admin alternative +{ + "type": "section", + "id": "user-plugin-info", + "title": "Request Plugin Installation", + "requirements": ["has-role:viewer"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='plugin-catalog']", + "doIt": false, + "tooltip": "You can browse available plugins here, but installation requires admin privileges. Contact your Grafana administrator to request plugin installations.", + "content": "Browse available plugins (view-only)" + } + ] +} ``` ## Quality Guidelines for AI @@ -615,7 +690,7 @@ Content complexity analysis: - [ ] All DOM interactions include `exists-reftarget` - [ ] Navigation interactions include `navmenu-open` - [ ] Page-specific actions include `on-page:/path` -- [ ] State-changing actions include `data-verify` +- [ ] State-changing actions include `verify` - [ ] Admin actions include appropriate permission requirements - [ ] Sections have unique, stable IDs @@ -629,705 +704,46 @@ Content complexity analysis: ## Maintenance Patterns ### Version Compatibility -```html - -
  • - Use new scenes feature (skip if older version) -
  • - - -
  • - Create dashboard using standard interface -
  • +```json +// Version-specific features +{ + "type": "interactive", + "action": "button", + "reftarget": "New scene", + "requirements": ["min-version:9.0.0", "exists-reftarget"], + "skippable": true, + "tooltip": "Scenes feature requires Grafana 9.0+", + "content": "Use new scenes feature (skip if older version)" +} + +// Fallback for older versions +{ + "type": "interactive", + "action": "button", + "reftarget": "Create dashboard", + "requirements": ["exists-reftarget"], + "content": "Create dashboard using standard interface" +} ``` ### Environment Adaptation -```html - -
  • - Access cloud-specific setup -
  • - - -
  • - Configure on-premise settings -
  • -```# Interactive Guide Patterns for AI Generation - -## Guide Structure Patterns - -### Complete Guide Template -```html - - - - {Guide Title} - - -

    {Guide Title}

    - -

    {Introduction explaining what user will learn}

    -
      -
    • {Learning objective 1}
    • -
    • {Learning objective 2}
    • -
    • {Learning objective 3}
    • -
    - - {Section 1} - {Section 2} - {Section N} - -

    🎉 Congratulations!

    -

    {Summary of what was accomplished}

    -
      -
    • ✅ {Achievement 1}
    • -
    • ✅ {Achievement 2}
    • -
    • ✅ {Achievement 3}
    • -
    - -

    Next: {Link to next guide or suggested actions}

    - - +```json +// Cloud-specific +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/a/cloud-setup", + "requirements": ["in-environment:cloud"], + "skippable": true, + "content": "Access cloud-specific setup" +} + +// On-premise alternative +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/settings", + "requirements": ["is-admin", "in-environment:production"], + "content": "Configure on-premise settings" +} ``` - -### Section Structure Pattern -```html -

    Section {N}: {Section Title}

    -

    {Section description and context}

    - - -
      - {Interactive steps} -
    -
    -``` - -## Common Guide Types - -### 1. Product Tour / Onboarding -**Goal**: Familiarize users with Grafana interface -**Pattern**: Show-only highlights with rich comments -**Sections**: Navigation, Dashboard basics, Key features - -```html - -
      -
    • - - The Home page is your Grafana dashboard hub. - It shows recent dashboards, starred favorites, - and quick access to common tasks. - - Start with the Home page -
    • - -
    • - - Dashboards are collections of panels displaying your data. - Create time series, stats, tables - and more to visualize your monitoring data. - - Explore the Dashboards section -
    • -
    -
    -``` - -### 2. Setup/Configuration Guide -**Goal**: Configure a specific feature or integration -**Pattern**: Sections with verification and objectives -**Sections**: Prerequisites, Configuration, Testing, Usage - -```html -

    Section 1: Set Up Data Source

    - -
      -
    • - Navigate to data sources -
    • - -
    • - Add new data source -
    • - -
    • - Select Prometheus -
    • - -
    • - Name the data source -
    • - -
    • - Set server URL -
    • - -
    • - Save and test connection -
    • -
    -
    - -

    Section 2: Create Dashboard

    - - - -``` - -### 3. Feature Deep-Dive Guide -**Goal**: Teach specific Grafana feature in depth -**Pattern**: Mix of education (show-only) and practice (regular actions) -**Sections**: Concept intro, Basic usage, Advanced features - -```html -

    Understanding Grafana Alerting

    - - - -
      -
    • - - Alert rules continuously monitor your metrics. - When conditions like CPU > 80% are met, they trigger - notifications to keep your team informed. - - Explore the alert rules interface -
    • -
    -
    - - - - - -``` - -### 4. Workflow Guide -**Goal**: Complete a real-world task end-to-end -**Pattern**: Sequential sections building toward final goal -**Sections**: Setup, Configuration, Implementation, Validation - -```html -

    Complete Monitoring Setup

    - - - - - - - - - - - - - - - -``` - -## Content Writing Patterns - -### Step Description Patterns -```html - -
  • - Save your dashboard changes -
  • - - -
  • - Set the Prometheus server URL to connect to your metrics -
  • - - -
  • - - This panel displays your metrics in real-time... - - Examine how panels display your monitoring data -
  • -``` - -### Section Introduction Patterns -```html - -

    Section 1: Configure Prometheus Data Source

    -

    In this section, we'll connect Grafana to your Prometheus server so you can query and visualize your metrics.

    - - -

    Section 2: Create Your First Dashboard

    -

    Now that your data source is configured, let's create a dashboard to display your Prometheus metrics in meaningful visualizations.

    - - -

    Section 3: Set Up Alerting

    -

    Dashboards show you what happened, but alerts tell you when something is happening. Let's create alert rules to monitor your system proactively.

    -``` - -## Advanced Patterns - -### Conditional Workflows -```html - - - - - - - - -``` - -### Progressive Complexity -```html - - -
  • - Enter basic "up" query -
  • -
    - - - -
  • - Enter rate calculation query -
  • -
    - - - -
  • - Enter aggregated rate query -
  • -
    -``` - -### Error Recovery Workflows -```html - - - - - - - - - -``` - -## AI Generation Decision Trees - -### Choose Action Type -``` -User request analysis: - "Click [button_text]" → button action - "Click [element_description]" → highlight action - "Enter [value]" → formfill action - "Go to [page]" → navigate action - "Do [multiple things]" → sequence or multistep - "Show [element]" → highlight with data-doit="false" -``` - -### Choose Requirements -``` -Action context analysis: - DOM interaction → add "exists-reftarget" - Navigation menu → add "navmenu-open" - Specific page → add "on-page:/path" - Admin feature → add "is-admin" - Data source needed → add "has-datasource:type" - Plugin needed → add "has-plugin:id" - Previous section → add "section-completed:id" -``` - -### Choose Structure -``` -Content complexity analysis: - Single action → individual step - Related actions → multistep - Multiple checkpoints → sequence - Educational content → show-only with comments - Complex workflow → sequence with mixed step types -``` - -## Common Guide Scenarios - -### Scenario: "Show me how to create a dashboard" -```html - - -

    Create Your First Dashboard

    - -
      -
    • - Navigate to create new dashboard -
    • - -
    • - Add your first visualization -
    • - -
    • - Select your data source -
    • - -
    • - Enter a basic query -
    • - -
    • - Set panel title -
    • - -
    • - Save the dashboard -
    • -
    -
    -``` - -### Scenario: "Set up Prometheus monitoring" -```html -

    Prometheus + Grafana Setup

    - - - - -

    Configure Prometheus Data Source

    - -
    - - - - -

    Create Prometheus Dashboard

    - -
    - - - - -

    Configure Prometheus Alerts

    - -
    -``` - -### Scenario: "Explain this interface element" -```html -
  • - - - The query editor is where you write queries in your data source's - native language. For Prometheus, use PromQL. For Loki, use LogQL. - The editor provides syntax highlighting and auto-completion to help you write effective queries. - - - Learn about the query editor interface -
  • -``` - -## Interactive Comment Guidelines - -### Content Structure Template -```html - - The {UI_ELEMENT_NAME} {what_it_does}. - {why_its_important_or_context}. {specific_features_or_capabilities} - {user_benefit_or_outcome}. - -``` - -### Applied Examples - -#### Feature Explanation -```html - - The visualization picker lets you choose how to display your data. - Different chart types tell different stories about your metrics. - Choose time series for trends, stat panels for current values, - and tables for detailed breakdowns. - -``` - -#### Process Guidance -```html - - Save & test validates your data source configuration and saves it. - A green success message confirms everything works correctly, - while red error messages help you troubleshoot connection issues. - -``` - -#### Context Building -```html - - This metrics panel shows your application's performance in real-time. - The time series chart reveals trends and patterns, helping you - identify performance bottlenecks and capacity planning needs. - -``` - -## Error Handling Patterns - -### Graceful Degradation -```html - -
  • - Use quick setup wizard (skip if not available) -
  • - - -
  • - Or set up data source manually -
  • -``` - -### Permission-Aware Guides -```html - - -

    Install Plugin (Admin)

    - -
    - - - -

    Request Plugin Installation

    -
      -
    • - - You can browse available plugins here, but installation requires admin privileges. - Contact your Grafana administrator to request plugin installations. - - Browse available plugins (view-only) -
    • -
    -
    -``` - -## Quality Guidelines for AI - -### Content Quality Checklist -- [ ] Guide has clear learning objectives -- [ ] Steps progress logically toward goal -- [ ] Requirements are appropriate for each action -- [ ] Error cases are handled gracefully -- [ ] Interactive comments add educational value -- [ ] Section structure supports resumable progress - -### Technical Quality Checklist -- [ ] All DOM interactions include `exists-reftarget` -- [ ] Navigation interactions include `navmenu-open` -- [ ] Page-specific actions include `on-page:/path` -- [ ] State-changing actions include `data-verify` -- [ ] Admin actions include appropriate permission requirements -- [ ] Sections have unique, stable IDs - -### User Experience Checklist -- [ ] Steps can be completed by intended user audience -- [ ] Error messages are helpful and actionable -- [ ] Guide works across different Grafana setups -- [ ] Progress can be resumed after interruption -- [ ] Alternative paths provided for different user states - -## Maintenance Patterns - -### Version Compatibility -```html - -
  • - Use new scenes feature (skip if older version) -
  • - - -
  • - Create dashboard using standard interface -
  • -``` - -### Environment Adaptation -```html - -
  • - Access cloud-specific setup -
  • - - -
  • - Configure on-premise settings -
  • -``` \ No newline at end of file From 36ed6141c17b874164a1c6a84e2174d0d593989f Mon Sep 17 00:00:00 2001 From: David Allen Date: Sat, 6 Dec 2025 14:04:08 -0500 Subject: [PATCH 08/46] remove unnecessary extra content --- AGENTS.md | 83 ------------------------------------------------------- 1 file changed, 83 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2712b50ff..bb111b400 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,47 +108,6 @@ Plugin needed → has-plugin:id Sequential dependency → section-completed:id ``` -### Common Patterns -```html - -
  • - Save changes -
  • - - - -
      {steps}
    -
    - - -
  • - {explanation} - {description} -
  • - - -
  • - - - {description} -
  • - - -
  • - {description} -
  • -``` - -This documentation provides everything needed to understand and generate high-quality interactive Grafana guides. # AI Interactive Reference Documentation This folder contains structured reference documentation specifically designed for AI systems to understand and generate interactive Grafana guides. @@ -241,45 +200,3 @@ Data source needed → has-datasource:type Plugin needed → has-plugin:id Sequential dependency → section-completed:id ``` - -### Common Patterns -```html - -
  • - Save changes -
  • - - - -
      {steps}
    -
    - - -
  • - {explanation} - {description} -
  • - - -
  • - - - {description} -
  • - - -
  • - {description} -
  • -``` - -This documentation provides everything needed to understand and generate high-quality interactive Grafana guides. From c9154d4b1538a3b7ac0a5ce4a35a66dffbeb1a96 Mon Sep 17 00:00:00 2001 From: David Allen Date: Sat, 6 Dec 2025 14:06:53 -0500 Subject: [PATCH 09/46] HTML -> JSON --- .cursor/edge-cases-and-troubleshooting.mdc | 2091 ++++++-------------- 1 file changed, 638 insertions(+), 1453 deletions(-) diff --git a/.cursor/edge-cases-and-troubleshooting.mdc b/.cursor/edge-cases-and-troubleshooting.mdc index c0e7acb60..b7ef0471f 100644 --- a/.cursor/edge-cases-and-troubleshooting.mdc +++ b/.cursor/edge-cases-and-troubleshooting.mdc @@ -12,42 +12,46 @@ alwaysApply: true **Cause**: Interactive action executes before DOM element is rendered **Solution**: Include appropriate requirements and verification -```html - -
  • - Save dashboard -
  • - - -
  • - Save dashboard -
  • +```json +// Bad: No existence check +{ + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "content": "Save dashboard" +} + +// Good: Includes existence check +{ + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["exists-reftarget", "on-page:/dashboard/edit"], + "content": "Save dashboard" +} ``` #### Problem: Navigation Menu Not Open **Cause**: Trying to interact with navigation elements when menu is closed **Solution**: Always include `navmenu-open` requirement for nav interactions -```html - -
  • - Click Connections -
  • - - -
  • - Click Connections -
  • +```json +// Bad: Navigation element without menu requirement +{ + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections']", + "content": "Click Connections" +} + +// Good: Includes navigation requirement +{ + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections']", + "requirements": ["navmenu-open", "exists-reftarget"], + "content": "Click Connections" +} ``` ### Permission and Access Issues @@ -56,101 +60,107 @@ alwaysApply: true **Cause**: Guide requires admin permissions but user doesn't have them **Solution**: Make admin steps skippable or provide alternative paths -```html - -
  • - Access plugin management (admin only) -
  • - - -
  • - - You can browse plugins here, but installation requires admin privileges. - Contact your administrator to request plugin installations. - - Browse available plugins (view-only) -
  • +```json +// Option 1: Make skippable +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/plugins", + "requirements": ["is-admin"], + "skippable": true, + "tooltip": "Admin access required - skip if you don't have permissions", + "content": "Access plugin management (admin only)" +} + +// Option 2: Provide alternative +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='plugin-catalog']", + "requirements": ["has-role:viewer"], + "doIt": false, + "tooltip": "You can browse plugins here, but installation requires admin privileges. Contact your administrator to request plugin installations.", + "content": "Browse available plugins (view-only)" +} ``` #### Problem: Data Source Dependencies **Cause**: Guide assumes specific data source exists **Solution**: Use objectives for auto-completion or include setup sections -```html - - - - - - -
  • - Enter Prometheus query -
  • +```json +// Option 1: Auto-complete if exists +{ + "type": "section", + "id": "setup-prometheus", + "title": "Setup Prometheus", + "objectives": ["has-datasource:type:prometheus"], + "tooltip": "Skips if Prometheus already configured", + "blocks": [ + // Setup steps only run if Prometheus not configured + ] +} + +// Option 2: Check before dependent actions +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "requirements": ["has-datasource:prometheus", "exists-reftarget"], + "content": "Enter Prometheus query" +} ``` ### Form Interaction Issues #### Problem: React Controlled Inputs **Cause**: React controlled inputs don't update with simple value assignment -**Solution**: System handles this automatically, but verify with `data-verify` - -```html - -
  • - Set dashboard title -
  • +**Solution**: System handles this automatically, but verify with `verify` + +```json +// Good: Includes verification +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='dashboard-title']", + "targetvalue": "My Dashboard", + "requirements": ["exists-reftarget"], + "verify": "has-dashboard-named:My Dashboard", + "content": "Set dashboard title" +} ``` #### Problem: ARIA Combobox Complexity **Cause**: ARIA comboboxes require special tokenization and event handling **Solution**: Use formfill action - system handles tokenization automatically -```html - -
  • - Filter logs by job and level -
  • +```json +// Complex query tokenization handled automatically +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[role='combobox'][aria-autocomplete='list']", + "targetvalue": "job=\"prometheus\" level=\"error\"", + "requirements": ["exists-reftarget", "on-page:/explore"], + "content": "Filter logs by job and level" +} ``` #### Problem: Monaco Editor Special Handling **Cause**: Monaco editors need special event handling for React integration **Solution**: System detects Monaco editors automatically -```html - -
  • - Enter PromQL query -
  • +```json +// Monaco editor automatically detected by CSS classes +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea.inputarea.monaco-mouse-cursor-text", + "targetvalue": "rate(http_requests_total[5m])", + "requirements": ["exists-reftarget"], + "content": "Enter PromQL query" +} ``` ### Navigation and Routing Issues @@ -159,37 +169,40 @@ alwaysApply: true **Cause**: Navigation completes but target page content not yet loaded **Solution**: Use verification to confirm page content is ready -```html - -
  • - Navigate to create dashboard -
  • - - -
  • - Add your first panel -
  • +```json +// Good: Verifies navigation completed +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboard/new", + "requirements": ["has-datasources"], + "verify": "on-page:/dashboard/new,exists-reftarget", + "content": "Navigate to create dashboard" +} + +// Follow-up action waits for page content +{ + "type": "interactive", + "action": "button", + "reftarget": "Add visualization", + "requirements": ["exists-reftarget", "on-page:/dashboard/new"], + "content": "Add your first panel" +} ``` #### Problem: External URL Handling **Cause**: External URLs might be blocked by browser security **Solution**: Use navigate action - system opens in new tab safely -```html - -
  • - View Grafana documentation -
  • +```json +// External URLs handled automatically +{ + "type": "interactive", + "action": "navigate", + "reftarget": "https://grafana.com/docs/", + "tooltip": "Opens documentation in new tab", + "content": "View Grafana documentation" +} ``` ## Troubleshooting Patterns @@ -197,37 +210,40 @@ alwaysApply: true ### Debugging Failed Requirements #### Network/API Issues -```html - -
  • - Test data source connection (skip if network issues) -
  • +```json +// Include fallback for network failures +{ + "type": "interactive", + "action": "button", + "reftarget": "Test connection", + "requirements": ["exists-reftarget"], + "skippable": true, + "tooltip": "Skip if network connectivity issues", + "content": "Test data source connection (skip if network issues)" +} ``` #### Plugin Availability Issues -```html - -
  • - Use custom plugin features -
  • - - -
  • - Use standard Grafana dashboards instead -
  • +```json +// Handle missing plugins gracefully +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/a/custom-plugin/", + "requirements": ["has-plugin:custom-plugin"], + "skippable": true, + "tooltip": "Requires custom plugin - skip if not installed", + "content": "Use custom plugin features" +} + +// Alternative without plugin +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/dashboards", + "tooltip": "Standard dashboards work without additional plugins", + "content": "Use standard Grafana dashboards instead" +} ``` ### Selector Reliability Issues @@ -236,932 +252,54 @@ alwaysApply: true **Cause**: Grafana UI updates change DOM structure or attributes **Solution**: Use multiple fallback selectors and stable attributes -```html - -
  • - Save dashboard -
  • - - -
  • - Save dashboard using stable selector -
  • +```json +// Primary selector with fallbacks +{ + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["exists-reftarget"], + "content": "Save dashboard" +} + +// If button text changes, use attribute selector +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save-dashboard']", + "requirements": ["exists-reftarget"], + "content": "Save dashboard using stable selector" +} ``` #### Problem: Multiple Element Matches **Cause**: Selector matches multiple elements when only one intended **Solution**: Use more specific selectors or pseudo-selectors -```html - -
  • - Edit panel -
  • - - -
  • - Edit the first panel -
  • - - -
  • - Edit panel 1 -
  • -``` - -### Sequential Flow Issues - -#### Problem: Steps Out of Order -**Cause**: User completes steps manually outside guide flow -**Solution**: Use objectives to handle already-completed states - -```html - -
  • - Name your data source -
  • -``` - -#### Problem: Section Dependencies -**Cause**: User tries to do section 2 before completing section 1 -**Solution**: Use section-completed requirements - -```html - - - -``` - -### Performance and Scalability Issues - -#### Problem: Too Many Requirements Checks -**Cause**: Complex requirements causing performance issues -**Solution**: Group related requirements and use caching - -```html - -
  • - Create new dashboard -
  • - - -
  • - Step 1 -
  • -
  • - Step 2 (redundant check) -
  • -``` - -#### Problem: Expensive State Checks -**Cause**: Requirements that make expensive API calls -**Solution**: Use objectives for expensive checks that auto-complete - -```html - - - - -``` - -## Error Recovery Strategies - -### Graceful Degradation Patterns - -#### Network Connectivity Issues -```html - -
  • - Test data source connection -
  • - - -
  • - - If connection testing fails, check your network connectivity - and firewall settings. You can still configure the data source - and test the connection later. - - Review connection status information -
  • -``` - -#### Missing Feature Fallbacks -```html - -
  • - Use query library (if available) -
  • - - -
  • - Or enter query manually in editor -
  • -``` - -### Error Message Patterns - -#### Helpful Error Messages -```html - -
  • - Create monitoring alert -
  • - - -
  • - Manage plugins (admin required) -
  • -``` - -#### Progressive Error Handling -```html - -
  • - - Click Connections in navigation -
  • - - -
  • - - If the navigation menu didn't open automatically, click the menu toggle - button to open it manually. - - Manually open navigation menu if needed -
  • -``` - -## Browser and Environment Issues - -### Cross-Browser Compatibility - -#### Safari-Specific Issues -```html - -
  • - Save changes -
  • - - -
  • - Save using attribute selector -
  • -``` - -#### Mobile and Tablet Considerations -```html - -
  • - Access mobile navigation menu -
  • -``` - -### Theme and Styling Issues - -#### Dark vs Light Theme -```html - -
  • - - The theme toggle switches between light and dark modes. - All interactive guides work in both themes using stable selectors - that don't depend on theme-specific styling. - - Notice the theme toggle option -
  • -``` - -## Version Compatibility Issues - -### Grafana Version Differences - -#### Version-Specific Features -```html - -
  • - Use new scenes feature -
  • - - -
  • - Create dashboard using standard interface -
  • -``` - -#### API Compatibility -```html - -
  • - Access query library -
  • -``` - -### Plugin Compatibility -```html - -
  • - Use plugin features -
  • -``` - -## Complex Workflow Edge Cases - -### Multi-Step Failures - -#### Problem: Mid-Sequence Failures -**Cause**: One action in multistep fails, leaving partial state -**Solution**: Include requirements for each internal action - -```html -
  • - - - - - Add visualization, select data source, enter query -
  • -``` - -#### Problem: Section Execution Interruption -**Cause**: User cancels section execution or browser/tab closes -**Solution**: System provides resume functionality automatically - -```html - - - - -``` - -### State Synchronization Issues - -#### Problem: External State Changes -**Cause**: User makes changes outside guide that affect requirements -**Solution**: Use live requirement monitoring and reactive checks - -```html - -
  • - Edit the panel -
  • -``` - -#### Problem: Concurrent User Actions -**Cause**: User performs actions while guide is running -**Solution**: Use section blocking and cancellation - -```html - - - - -``` - -## Data Consistency Issues - -### Data Source State Issues - -#### Problem: Data Source Configuration Drift -**Cause**: Data source settings change after guide creation -**Solution**: Use type-based requirements instead of name-based - -```html - -
  • - Select my prometheus data source -
  • - - -
  • - Select any Prometheus data source -
  • -``` - -#### Problem: Missing Test Data -**Cause**: Guide assumes specific metrics/logs exist -**Solution**: Use generic queries that work with minimal data - -```html - -
  • - Enter universal monitoring query -
  • - - -
  • - Enter application-specific query -
  • -``` - -## Accessibility and Usability Edge Cases - -### Screen Reader Compatibility -```html - -
  • - Save your dashboard -
  • - - -
  • - Set dashboard title -
  • -``` - -### Keyboard Navigation -```html - -
  • - Save dashboard (keyboard accessible) -
  • -``` - -## Testing and Validation Edge Cases - -### Automated Testing Considerations -```html - -
  • - Save dashboard -
  • - - -
  • - Save dashboard using test ID -
  • -``` - -### Load Testing Considerations -```html - -
  • - Load dashboard (may take time) -
  • - - -
  • - Save and test (with verification) -
  • -``` - -## Debugging Tools and Techniques - -### Browser Console Debugging -```javascript -// Check if selector works -document.querySelector("button[data-testid='save']"); - -// Test requirement checking -const testRequirement = async (req) => { - const checker = await import('./requirements-checker.utils'); - const result = await checker.checkRequirements({ - requirements: req, - targetAction: 'button', - refTarget: 'test' - }); - console.log(result); -}; - -// Test specific requirements -testRequirement('has-datasource:prometheus'); -testRequirement('is-admin'); - -// Check current user state -console.log('User:', window.grafanaBootData?.user); -console.log('Features:', window.grafanaBootData?.settings?.featureToggles); -``` - -### Development Mode Features -```javascript -// Enable debug logging -localStorage.setItem('grafana-docs-debug', 'true'); - -// Check interactive element state -const manager = window.SequentialRequirementsManager?.getInstance(); -if (manager) { - manager.logCurrentState(); +```json +// Bad: Might match multiple panels +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='panel']", + "content": "Edit panel" } -// Monitor requirement changes -document.addEventListener('interactive-action-completed', (e) => { - console.log('Action completed:', e.detail); -}); -``` - -### Common Debug Scenarios - -#### "Requirements never pass" -1. Check browser console for error messages -2. Verify requirement syntax exactly matches reference -3. Test individual requirements in isolation -4. Check if required data/state actually exists - -#### "Element not found" -1. Verify selector in browser console -2. Check if element exists in current page state -3. Ensure navigation requirements are met -4. Try alternative selectors - -#### "Action doesn't execute" -1. Check if requirements are met -2. Verify element is clickable/interactable -3. Check browser console for JavaScript errors -4. Test action manually to verify it's possible - -#### "Guide gets stuck" -1. Check for JavaScript errors in console -2. Verify network connectivity for API-dependent requirements -3. Clear localStorage to reset section progress -4. Check if user permissions changed - -### Recovery Procedures - -#### Reset Guide State -```javascript -// Clear all guide progress -Object.keys(localStorage) - .filter(key => key.startsWith('docsPlugin:completedSteps:')) - .forEach(key => localStorage.removeItem(key)); - -// Reload page to restart -window.location.reload(); -``` - -#### Skip Problematic Steps -```html - -
  • - Perform action (skippable if problems) -
  • -``` - -#### Provide Manual Alternatives -```html - -
  • - - - Use automatic configuration -
  • - - -
  • - Or configure manually step-by-step -
  • -```# Edge Cases and Troubleshooting for AI - -## Common Edge Cases - -### DOM Timing Issues - -#### Problem: Element Not Found -**Cause**: Interactive action executes before DOM element is rendered -**Solution**: Include appropriate requirements and verification - -```html - -
  • - Save dashboard -
  • - - -
  • - Save dashboard -
  • -``` - -#### Problem: Navigation Menu Not Open -**Cause**: Trying to interact with navigation elements when menu is closed -**Solution**: Always include `navmenu-open` requirement for nav interactions - -```html - -
  • - Click Connections -
  • - - -
  • - Click Connections -
  • -``` - -### Permission and Access Issues - -#### Problem: Admin Actions for Non-Admin Users -**Cause**: Guide requires admin permissions but user doesn't have them -**Solution**: Make admin steps skippable or provide alternative paths - -```html - -
  • - Access plugin management (admin only) -
  • - - -
  • - - You can browse plugins here, but installation requires admin privileges. - Contact your administrator to request plugin installations. - - Browse available plugins (view-only) -
  • -``` - -#### Problem: Data Source Dependencies -**Cause**: Guide assumes specific data source exists -**Solution**: Use objectives for auto-completion or include setup sections - -```html - - - - - - -
  • - Enter Prometheus query -
  • -``` - -### Form Interaction Issues - -#### Problem: React Controlled Inputs -**Cause**: React controlled inputs don't update with simple value assignment -**Solution**: System handles this automatically, but verify with `data-verify` - -```html - -
  • - Set dashboard title -
  • -``` - -#### Problem: ARIA Combobox Complexity -**Cause**: ARIA comboboxes require special tokenization and event handling -**Solution**: Use formfill action - system handles tokenization automatically - -```html - -
  • - Filter logs by job and level -
  • -``` - -#### Problem: Monaco Editor Special Handling -**Cause**: Monaco editors need special event handling for React integration -**Solution**: System detects Monaco editors automatically - -```html - -
  • - Enter PromQL query -
  • -``` - -### Navigation and Routing Issues - -#### Problem: SPA Navigation Timing -**Cause**: Navigation completes but target page content not yet loaded -**Solution**: Use verification to confirm page content is ready - -```html - -
  • - Navigate to create dashboard -
  • - - -
  • - Add your first panel -
  • -``` - -#### Problem: External URL Handling -**Cause**: External URLs might be blocked by browser security -**Solution**: Use navigate action - system opens in new tab safely - -```html - -
  • - View Grafana documentation -
  • -``` - -## Troubleshooting Patterns - -### Debugging Failed Requirements - -#### Network/API Issues -```html - -
  • - Test data source connection (skip if network issues) -
  • -``` - -#### Plugin Availability Issues -```html - -
  • - Use custom plugin features -
  • - - -
  • - Use standard Grafana dashboards instead -
  • -``` - -### Selector Reliability Issues - -#### Problem: Element Selector Changes -**Cause**: Grafana UI updates change DOM structure or attributes -**Solution**: Use multiple fallback selectors and stable attributes - -```html - -
  • - Save dashboard -
  • - - -
  • - Save dashboard using stable selector -
  • -``` - -#### Problem: Multiple Element Matches -**Cause**: Selector matches multiple elements when only one intended -**Solution**: Use more specific selectors or pseudo-selectors +// Good: Targets specific panel +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='panel']:first-of-type", + "content": "Edit the first panel" +} -```html - -
  • - Edit panel -
  • - - -
  • - Edit the first panel -
  • - - -
  • - Edit panel 1 -
  • +// Better: Use unique identifier if available +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-panel-id='panel-1']", + "content": "Edit panel 1" +} ``` ### Sequential Flow Issues @@ -1170,30 +308,34 @@ window.location.reload(); **Cause**: User completes steps manually outside guide flow **Solution**: Use objectives to handle already-completed states -```html - -
  • - Name your data source -
  • +```json +// Auto-complete if already done +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='datasource-name']", + "targetvalue": "prometheus", + "objectives": ["has-datasource:prometheus"], + "requirements": ["exists-reftarget"], + "content": "Name your data source" +} ``` #### Problem: Section Dependencies **Cause**: User tries to do section 2 before completing section 1 **Solution**: Use section-completed requirements -```html - - - +```json +{ + "type": "section", + "id": "section-2", + "title": "Section 2", + "requirements": ["section-completed:section-1"], + "tooltip": "Complete the previous section first", + "blocks": [ + // Section 2 content + ] +} ``` ### Performance and Scalability Issues @@ -1202,44 +344,52 @@ window.location.reload(); **Cause**: Complex requirements causing performance issues **Solution**: Group related requirements and use caching -```html - -
  • - Create new dashboard -
  • - - -
  • - Step 1 -
  • -
  • - Step 2 (redundant check) -
  • +```json +// Good: Grouped related requirements +{ + "type": "interactive", + "action": "button", + "reftarget": "Create dashboard", + "requirements": ["has-datasources", "has-role:editor", "on-page:/dashboards"], + "tooltip": "Groups related checks efficiently", + "content": "Create new dashboard" +} + +// Bad: Separate similar checks +// Step 1 +{ + "type": "interactive", + "action": "button", + "reftarget": "Create dashboard", + "requirements": ["has-datasource:prometheus"], + "content": "Step 1" +} +// Step 2 (redundant check) +{ + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["has-datasource:prometheus"], + "content": "Step 2" +} ``` #### Problem: Expensive State Checks **Cause**: Requirements that make expensive API calls **Solution**: Use objectives for expensive checks that auto-complete -```html - - - - +```json +// Use objective for expensive dashboard search +{ + "type": "section", + "id": "create-dashboard", + "title": "Create Dashboard", + "objectives": ["has-dashboard-named:Monitoring Dashboard"], + "tooltip": "Skips creation if dashboard already exists", + "blocks": [ + // Creation steps + ] +} ``` ## Error Recovery Strategies @@ -1247,100 +397,99 @@ window.location.reload(); ### Graceful Degradation Patterns #### Network Connectivity Issues -```html - -
  • - Test data source connection -
  • - - -
  • - - If connection testing fails, check your network connectivity - and firewall settings. You can still configure the data source - and test the connection later. - - Review connection status information -
  • +```json +// Primary action with network dependency +{ + "type": "interactive", + "action": "button", + "reftarget": "Test connection", + "requirements": ["exists-reftarget"], + "skippable": true, + "tooltip": "Skip if experiencing network connectivity issues", + "content": "Test data source connection" +} + +// Offline alternative +{ + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='connection-status']", + "doIt": false, + "tooltip": "If connection testing fails, check your network connectivity and firewall settings. You can still configure the data source and test the connection later.", + "content": "Review connection status information" +} ``` #### Missing Feature Fallbacks -```html - -
  • - Use query library (if available) -
  • - - -
  • - Or enter query manually in editor -
  • +```json +// Modern feature with fallback +{ + "type": "interactive", + "action": "button", + "reftarget": "Use query library", + "requirements": ["has-feature:queryLibrary", "exists-reftarget"], + "skippable": true, + "tooltip": "Query library feature - skip if not available", + "content": "Use query library (if available)" +} + +// Traditional approach +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "rate(http_requests_total[5m])", + "requirements": ["exists-reftarget"], + "content": "Or enter query manually in editor" +} ``` ### Error Message Patterns #### Helpful Error Messages -```html - -
  • - Create monitoring alert -
  • - - -
  • - Manage plugins (admin required) -
  • +```json +// Clear explanation with context +{ + "type": "interactive", + "action": "button", + "reftarget": "Create alert", + "requirements": ["has-datasource:prometheus", "has-permission:alerting:write"], + "tooltip": "Requires Prometheus data source and alerting permissions to create alert rules", + "content": "Create monitoring alert" +} + +// Specific guidance for resolution +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/admin/plugins", + "requirements": ["is-admin"], + "skippable": true, + "tooltip": "Plugin management requires admin role - contact your administrator if you need plugin access", + "content": "Manage plugins (admin required)" +} ``` #### Progressive Error Handling -```html - -
  • - - Click Connections in navigation -
  • - - -
  • - - If the navigation menu didn't open automatically, click the menu toggle - button to open it manually. - - Manually open navigation menu if needed -
  • +```json +// Try automatic fix first +{ + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/connections']", + "requirements": ["navmenu-open", "exists-reftarget"], + "content": "Click Connections in navigation" +} + +// Manual alternative if auto-fix fails +{ + "type": "interactive", + "action": "highlight", + "reftarget": "#mega-menu-toggle", + "requirements": ["exists-reftarget"], + "tooltip": "If the navigation menu didn't open automatically, click the menu toggle button to open it manually.", + "content": "Manually open navigation menu if needed" +} ``` ## Browser and Environment Issues @@ -1348,54 +497,54 @@ window.location.reload(); ### Cross-Browser Compatibility #### Safari-Specific Issues -```html - -
  • - Save changes -
  • - - -
  • - Save using attribute selector -
  • +```json +// Safari has different button text handling +{ + "type": "interactive", + "action": "button", + "reftarget": "Save", + "requirements": ["exists-reftarget"], + "tooltip": "Uses exact text matching for cross-browser compatibility", + "content": "Save changes" +} + +// Fallback with selector +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save-button']", + "requirements": ["exists-reftarget"], + "content": "Save using attribute selector" +} ``` #### Mobile and Tablet Considerations -```html - -
  • - Access mobile navigation menu -
  • +```json +// Touch-friendly interactions +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='mobile-menu']", + "requirements": ["exists-reftarget"], + "tooltip": "Mobile navigation might use different menu structure", + "content": "Access mobile navigation menu" +} ``` ### Theme and Styling Issues #### Dark vs Light Theme -```html - -
  • - - The theme toggle switches between light and dark modes. - All interactive guides work in both themes using stable selectors - that don't depend on theme-specific styling. - - Notice the theme toggle option -
  • +```json +// Theme-agnostic selectors +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='theme-toggle']", + "requirements": ["exists-reftarget"], + "doIt": false, + "tooltip": "The theme toggle switches between light and dark modes. All interactive guides work in both themes using stable selectors that don't depend on theme-specific styling.", + "content": "Notice the theme toggle option" +} ``` ## Version Compatibility Issues @@ -1403,50 +552,54 @@ window.location.reload(); ### Grafana Version Differences #### Version-Specific Features -```html - -
  • - Use new scenes feature -
  • - - -
  • - Create dashboard using standard interface -
  • +```json +// Modern Grafana features +{ + "type": "interactive", + "action": "button", + "reftarget": "New scene", + "requirements": ["min-version:9.0.0", "exists-reftarget"], + "skippable": true, + "tooltip": "Scenes feature requires Grafana 9.0+", + "content": "Use new scenes feature" +} + +// Fallback for older versions +{ + "type": "interactive", + "action": "button", + "reftarget": "Create dashboard", + "requirements": ["exists-reftarget"], + "content": "Create dashboard using standard interface" +} ``` #### API Compatibility -```html - -
  • - Access query library -
  • +```json +// Check for API availability +{ + "type": "interactive", + "action": "button", + "reftarget": "Query library", + "requirements": ["has-feature:queryLibrary", "min-version:8.5.0", "exists-reftarget"], + "skippable": true, + "tooltip": "Query library requires specific Grafana version and feature toggle", + "content": "Access query library" +} ``` ### Plugin Compatibility -```html - -
  • - Use plugin features -
  • +```json +// Plugin version dependencies +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/a/plugin-app/", + "requirements": ["has-plugin:plugin-id", "min-version:9.0.0"], + "skippable": true, + "tooltip": "Plugin requires compatible Grafana version", + "content": "Use plugin features" +} ``` ## Complex Workflow Edge Cases @@ -1457,39 +610,47 @@ window.location.reload(); **Cause**: One action in multistep fails, leaving partial state **Solution**: Include requirements for each internal action -```html -
  • - - - - - Add visualization, select data source, enter query -
  • +```json +{ + "type": "multistep", + "requirements": ["on-page:/dashboard/new"], + "content": "Add visualization, select data source, enter query", + "steps": [ + { + "action": "button", + "reftarget": "Add visualization", + "requirements": ["exists-reftarget"] + }, + { + "action": "button", + "reftarget": "prometheus-datasource", + "requirements": ["has-datasource:prometheus"] + }, + { + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "requirements": ["exists-reftarget"] + } + ] +} ``` #### Problem: Section Execution Interruption **Cause**: User cancels section execution or browser/tab closes **Solution**: System provides resume functionality automatically -```html - - - - +```json +// Section automatically resumes from last completed step +{ + "type": "section", + "id": "long-workflow", + "title": "Long Workflow", + "tooltip": "Progress saved automatically - can resume if interrupted", + "blocks": [ + // Steps will resume from where user left off + ] +} ``` ### State Synchronization Issues @@ -1498,30 +659,33 @@ window.location.reload(); **Cause**: User makes changes outside guide that affect requirements **Solution**: Use live requirement monitoring and reactive checks -```html - -
  • - Edit the panel -
  • +```json +// Requirements automatically recheck when DOM changes +{ + "type": "interactive", + "action": "button", + "reftarget": "Edit panel", + "requirements": ["exists-reftarget", "on-page:/dashboard"], + "tooltip": "Requirements automatically recheck if user navigates", + "content": "Edit the panel" +} ``` #### Problem: Concurrent User Actions **Cause**: User performs actions while guide is running **Solution**: Use section blocking and cancellation -```html - - - - +```json +// Section blocks other interactions during execution +{ + "type": "section", + "id": "atomic-workflow", + "title": "Atomic Workflow", + "tooltip": "Blocks other interactions during execution for consistency", + "blocks": [ + // Steps execute without interference + ] +} ``` ## Data Consistency Issues @@ -1532,127 +696,138 @@ window.location.reload(); **Cause**: Data source settings change after guide creation **Solution**: Use type-based requirements instead of name-based -```html - -
  • - Select my prometheus data source -
  • - - -
  • - Select any Prometheus data source -
  • +```json +// Bad: Relies on specific name +{ + "type": "interactive", + "action": "button", + "reftarget": "my-prometheus", + "requirements": ["has-datasource:my-prometheus"], + "content": "Select my prometheus data source" +} + +// Good: Uses type for flexibility +{ + "type": "interactive", + "action": "button", + "reftarget": "prometheus", + "requirements": ["has-datasource:type:prometheus"], + "tooltip": "Works with any Prometheus data source", + "content": "Select any Prometheus data source" +} ``` #### Problem: Missing Test Data **Cause**: Guide assumes specific metrics/logs exist **Solution**: Use generic queries that work with minimal data -```html - -
  • - Enter universal monitoring query -
  • - - -
  • - Enter application-specific query -
  • +```json +// Good: Uses universal Prometheus metric +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "up", + "requirements": ["has-datasource:prometheus", "exists-reftarget"], + "tooltip": "'up' metric exists on all Prometheus instances", + "content": "Enter universal monitoring query" +} + +// Avoid: Specific application metrics +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea[data-testid='query-editor']", + "targetvalue": "my_custom_app_requests_total", + "tooltip": "Might not exist in all environments", + "content": "Enter application-specific query" +} ``` ## Accessibility and Usability Edge Cases ### Screen Reader Compatibility -```html - -
  • - Save your dashboard -
  • - - -
  • - Set dashboard title -
  • +```json +// Include helpful hints for screen readers +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save']", + "requirements": ["exists-reftarget"], + "tooltip": "Save button - preserves your dashboard configuration", + "content": "Save your dashboard" +} + +// Use ARIA attributes in selectors when available +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[aria-label='Dashboard title']", + "targetvalue": "My Dashboard", + "requirements": ["exists-reftarget"], + "content": "Set dashboard title" +} ``` ### Keyboard Navigation -```html - -
  • - Save dashboard (keyboard accessible) -
  • +```json +// Ensure interactive elements are keyboard accessible +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save'][tabindex='0']", + "requirements": ["exists-reftarget"], + "tooltip": "Accessible via keyboard navigation", + "content": "Save dashboard (keyboard accessible)" +} ``` ## Testing and Validation Edge Cases ### Automated Testing Considerations -```html - -
  • - Save dashboard -
  • - - -
  • - Save dashboard using test ID -
  • +```json +// Include test-friendly attributes +{ + "type": "interactive", + "action": "button", + "reftarget": "Save dashboard", + "requirements": ["exists-reftarget"], + "tooltip": "Uses button text for reliable automated testing", + "content": "Save dashboard" +} + +// Alternative with test ID +{ + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='save-dashboard-btn']", + "requirements": ["exists-reftarget"], + "content": "Save dashboard using test ID" +} ``` ### Load Testing Considerations -```html - -
  • - Load dashboard (may take time) -
  • - - -
  • - Save and test (with verification) -
  • +```json +// Handle high-latency environments +{ + "type": "interactive", + "action": "button", + "reftarget": "Load dashboard", + "requirements": ["exists-reftarget"], + "tooltip": "May take longer in high-latency environments", + "content": "Load dashboard (may take time)" +} + +// Include verification for slow operations +{ + "type": "interactive", + "action": "button", + "reftarget": "Save & test", + "requirements": ["exists-reftarget"], + "verify": "has-datasource:prometheus", + "tooltip": "Verification confirms operation completed successfully", + "content": "Save and test (with verification)" +} ``` ## Debugging Tools and Techniques @@ -1739,33 +914,43 @@ window.location.reload(); ``` #### Skip Problematic Steps -```html - -
  • - Perform action (skippable if problems) -
  • +```json +// Make problematic steps skippable +{ + "type": "interactive", + "action": "button", + "reftarget": "Problematic action", + "requirements": ["complex-requirement"], + "skippable": true, + "tooltip": "Skip if experiencing issues", + "content": "Perform action (skippable if problems)" +} ``` #### Provide Manual Alternatives -```html - -
  • - - - Use automatic configuration -
  • - - -
  • - Or configure manually step-by-step -
  • -``` \ No newline at end of file +```json +// Automated approach +{ + "type": "multistep", + "skippable": true, + "content": "Use automatic configuration", + "steps": [ + { + "action": "navigate", + "reftarget": "/setup" + }, + { + "action": "button", + "reftarget": "Auto configure" + } + ] +} + +// Manual fallback +{ + "type": "interactive", + "action": "navigate", + "reftarget": "/manual-setup", + "content": "Or configure manually step-by-step" +} +``` From 73c39fa00b323eaa03e86c2b8f8d90228f998f70 Mon Sep 17 00:00:00 2001 From: David Allen Date: Sat, 6 Dec 2025 14:07:53 -0500 Subject: [PATCH 10/46] HTML -> JSON changes --- .cursor/selector-library.mdc | 170 +++++++++++++++++--------------- .cursor/system-architecture.mdc | 16 +-- 2 files changed, 98 insertions(+), 88 deletions(-) diff --git a/.cursor/selector-library.mdc b/.cursor/selector-library.mdc index 91be736e5..e73846640 100644 --- a/.cursor/selector-library.mdc +++ b/.cursor/selector-library.mdc @@ -264,46 +264,51 @@ Color Picker: input[type='color'] ``` ### Form Interaction Patterns -``` - -
  • - Set dashboard name -
  • - - -
  • - Enable alerting -
  • - - -
  • - Set refresh interval -
  • - - -
  • - Enter PromQL query -
  • - - -
  • - Filter by job label -
  • +```json +// Simple text entry +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='dashboard-name']", + "targetvalue": "My Dashboard", + "content": "Set dashboard name" +} + +// Checkbox selection +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[type='checkbox'][id='enable-alerts']", + "targetvalue": "true", + "content": "Enable alerting" +} + +// Dropdown selection +{ + "type": "interactive", + "action": "formfill", + "reftarget": "select[data-testid='refresh-interval']", + "targetvalue": "5s", + "content": "Set refresh interval" +} + +// Complex query entry +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea.inputarea", + "targetvalue": "rate(http_requests_total[5m])", + "content": "Enter PromQL query" +} + +// ARIA combobox (label filters) +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[role='combobox'][aria-autocomplete='list']", + "targetvalue": "job=\"prometheus\"", + "content": "Filter by job label" +} ``` ## Button Text Library @@ -1011,46 +1016,51 @@ Color Picker: input[type='color'] ``` ### Form Interaction Patterns -``` - -
  • - Set dashboard name -
  • - - -
  • - Enable alerting -
  • - - -
  • - Set refresh interval -
  • - - -
  • - Enter PromQL query -
  • - - -
  • - Filter by job label -
  • +```json +// Simple text entry +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[id='dashboard-name']", + "targetvalue": "My Dashboard", + "content": "Set dashboard name" +} + +// Checkbox selection +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[type='checkbox'][id='enable-alerts']", + "targetvalue": "true", + "content": "Enable alerting" +} + +// Dropdown selection +{ + "type": "interactive", + "action": "formfill", + "reftarget": "select[data-testid='refresh-interval']", + "targetvalue": "5s", + "content": "Set refresh interval" +} + +// Complex query entry +{ + "type": "interactive", + "action": "formfill", + "reftarget": "textarea.inputarea", + "targetvalue": "rate(http_requests_total[5m])", + "content": "Enter PromQL query" +} + +// ARIA combobox (label filters) +{ + "type": "interactive", + "action": "formfill", + "reftarget": "input[role='combobox'][aria-autocomplete='list']", + "targetvalue": "job=\"prometheus\"", + "content": "Filter by job label" +} ``` ## Button Text Library diff --git a/.cursor/system-architecture.mdc b/.cursor/system-architecture.mdc index 5c19cf046..8db4038e9 100644 --- a/.cursor/system-architecture.mdc +++ b/.cursor/system-architecture.mdc @@ -1,13 +1,13 @@ # Interactive Tutorial System Architecture ## Overview -The interactive tutorial system is a React-based framework for creating guided, executable documentation within Grafana. It transforms static HTML documentation into interactive experiences with real Grafana UI manipulation. +The interactive tutorial system is a React-based framework for creating guided, executable documentation within Grafana. It transforms structured JSON documentation into interactive experiences with real Grafana UI manipulation. ## Core Components -### HTML Processing Pipeline -1. **HTML Input**: Raw HTML with interactive attributes -2. **HTML Parser** (`html-parser.ts`): Converts HTML to React component data +### Content Processing Pipeline +1. **JSON Input**: Structured JSON with interactive properties +2. **Content Parser**: Converts JSON structure to React component data 3. **Content Renderer** (`content-renderer.tsx`): Renders React components 4. **Interactive Components**: Execute real actions in Grafana UI @@ -105,7 +105,7 @@ ContentRenderer ### Adding New Action Types 1. Create action handler class implementing standard interface 2. Add action type to `executeInteractiveAction` switch statement -3. Update HTML parser to recognize new action attributes +3. Update content parser to recognize new action types 4. Add React component if specialized rendering needed ### Adding New Requirements @@ -115,7 +115,7 @@ ContentRenderer 4. Test with existing interactive elements ### Adding New UI Features -1. Extend HTML parser to recognize new attributes +1. Extend content schema to support new features 2. Add React component props for new features 3. Implement rendering logic in content renderer 4. Add CSS styling in appropriate style files @@ -140,10 +140,10 @@ ContentRenderer - **Inline Styles**: Uses CSS custom properties instead of inline styles - **Script Execution**: No dynamic script execution in tutorial content - **External Resources**: External URLs opened with security attributes -- **HTML Sanitization**: Interactive comments support limited, safe HTML subset +- **Markdown Content**: Uses safe Markdown rendering for text content ### User Safety - **Action Validation**: All actions validated before execution - **Permission Checking**: User permissions verified before privileged actions - **State Isolation**: Tutorial execution doesn't interfere with user work -- **Graceful Failure**: Failed actions don't leave system in broken state \ No newline at end of file +- **Graceful Failure**: Failed actions don't leave system in broken state From de2124106194a878a6bb60cb33b3712dbee56413 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 6 Dec 2025 16:24:47 -0600 Subject: [PATCH 11/46] add all content --- k8s-mem/unstyled.html | 250 ++++++++++++++++++++++++++++ kubernetes-monitoring/unstyled.html | 0 2 files changed, 250 insertions(+) create mode 100644 k8s-mem/unstyled.html delete mode 100644 kubernetes-monitoring/unstyled.html diff --git a/k8s-mem/unstyled.html b/k8s-mem/unstyled.html new file mode 100644 index 000000000..719bb8add --- /dev/null +++ b/k8s-mem/unstyled.html @@ -0,0 +1,250 @@ + + + + + + Memory usage + + +

    Explore memory usage in Kubernetes Monitoring

    + +

    + Travel through the Kubernetes Monitoring application to explore memory usage and any associated issues, + starting at the namespace level. +

    + +

    What you will learn

    +
      +
    • + Navigate from namespaces through the Kubernetes hierarchy to individual + containers. +
    • +
    • Review key panels to understand memory usage.
    • +
    • what should be here?
    • +
    + + +

    Namespace memory details

    +
      +
    • + Click Namespaces in the main menu to view the list of + namespaces in your environment.

      +
    • + +
    • + Select the first namespace in the list to open its detail view. +
    • + +
    • + This is the namespace detail page showing an overview of the CPU and memory usage in the namespace.

      + Click the Memory tab to view more detail about + memory usage in the namespace. +
    • + +
    • + Notice the panel titled Scheduling: Containers with Memory requests set . + If all memory requests are set for all containers in this namespace, then the percentage would be 100%.

      + When memory requests are not set, this creates a significant operational risk that affects scheduling efficiency, pod stability, cluster capacity planning, and cost optimization. Always set requests based on observed usage patterns. +
    • + +
    • + Notice theAlignment: usage/requests panel.

      + If all memory requests are set for all containers in this namespace, total memory usage should fall between the 60-90% range of requests. +
    • + +
    • + The Distribution: Workload usage panel helps visualise which workloads are contributing the most to the overall namespace memory usage. +
    • +
    • + Focus on the Alignment: usage/requests panel.

      + Each workload should see approximately 60-90% memory usage. + Workloads that fall outside of this range could be considered as incorrectly sized, or they have containers with memory requests and limits that are not well configured. +
    • +
    +
    + + +

    Workload memory details

    +
      +
    • + Scroll down the list of workloads in this namespace. + Click the first workload in the list to see details about the workload. +
    • + +
    • + This is the workload detail page.

      + Review the Alignment graph for Pod memory usage compared to requests.

      + Determine if Pods are running over or too close to their CPU requests. +
    • +
    +
    + + +

    Pod memory details

    +
  • + Select the first Pod in the Pods list. +
  • +
  • + Notice the Alignment: usage/requests panel.

    + Ideally, each container should have around 60-90% memory usage. + Containers that fall outside of this range could be have memory that is incorrectly configured. +
  • + +
    + +

    Container memory details

    +
  • + Click the first container in the list of containers in this Pod to view container details. +
  • + +
  • + At the Container detail page, scroll to the "Memory sizing limits section.

    + The number on the left side of the guage is the currently set memory sizing limits. + The number on the right is the recommended memory sizing limits. + You can use the recommendation to adjust your memory limits for this container. +

    + Without memory limits, a container can consume all available memory on the node, potentially causing: +
      +
    • Node-wide OOM events, which can kill other workloads or system components.
    • +
    • Pod eviction when the node runs low on memory.
    • +
    • Resource starvation for other pods on the node, degrading cluster performance.
    • +
    +
  • +
  • + Next, focus on the "Memory requests sizing panel.

    + The number on the left side of the guage is the currently set memory requests. + The number on the right is the recommended setting for memory requests. + You can use the recommendation to adjust your memory requests for this container.

    + When requests are too high, it can cause: +
      +
    • Wasted capacity: Kubernetes reserves more memory than actually needed, leaving nodes artificially "full" even when memory sits unused.
    • +
    • Scheduling failures: Pods can't be scheduled even when nodes have available memory, because the scheduler sees insufficient requestable capacity.
    • +
    • Higher costs: You need more nodes to run the same workload due to over-reservation.
    • +
    +
    + When requests are too low, it can cause: +
      +
    • Pod evictions: Under memory pressure, Kubernetes evicts pods exceeding their requests first—your pod becomes a target even when running normally.
    • +
    • Unpredictable performance: The kubelet may throttle or kill processes on memory-pressured nodes, causing instability.
    • +
    • Wrong QoS class: Without appropriate limits, pods drop into BestEffort (first to be evicted) instead of Burstable or Guaranteed.
    • +
    +
  • + +
    + +

    What you have learned

    +

    You have learned how to:

    +
      +
    • + Navigate from namespaces to individual containers to look at memory usage, requests, and limits. +
    • +
    • + Review container memory usage, limits, requests, and recommendations. +
    • +
    • + Turn insights and memory request recommendations into improved resource sizing and reliability. +
    • +
    + + diff --git a/kubernetes-monitoring/unstyled.html b/kubernetes-monitoring/unstyled.html deleted file mode 100644 index e69de29bb..000000000 From c623c7f0c9febcd3cc9772a1e67cd3ebdc9aa272 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 6 Dec 2025 16:33:54 -0600 Subject: [PATCH 12/46] fixed issues --- k8s-mem/unstyled.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/k8s-mem/unstyled.html b/k8s-mem/unstyled.html index 719bb8add..7de9aa416 100644 --- a/k8s-mem/unstyled.html +++ b/k8s-mem/unstyled.html @@ -20,7 +20,7 @@

    What you will learn

    containers.
  • Review key panels to understand memory usage.
  • -
  • what should be here?
  • +
  • The importance of setting memory requests and limits.
  • Namespace memory details data-requirements="exists-reftarget" data-doit="false" > - Notice theAlignment: usage/requests panel.

    + Notice the Alignment: usage/requests panel.

    If all memory requests are set for all containers in this namespace, total memory usage should fall between the 60-90% range of requests. @@ -135,8 +135,8 @@

    Workload memory details

    data-doit="false" > This is the workload detail page.

    - Review the Alignment graph for Pod memory usage compared to requests.

    - Determine if Pods are running over or too close to their CPU requests. + Review the Alignment graph for Pod memory usage compared to requests.

    + The target range should be around 60-80% utilization of requests to provide headroom for bursts while avoiding waste.
    @@ -149,6 +149,7 @@

    Workload memory details

    data-requirements="navmenu-open,on-page:/a/grafana-k8s-app" >

    Pod memory details

    +
    • Date: Sat, 6 Dec 2025 16:39:35 -0600 Subject: [PATCH 13/46] last fix --- k8s-mem/unstyled.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s-mem/unstyled.html b/k8s-mem/unstyled.html index 7de9aa416..9e9220270 100644 --- a/k8s-mem/unstyled.html +++ b/k8s-mem/unstyled.html @@ -248,4 +248,4 @@

      What you have learned

    - + \ No newline at end of file From f160446205eb5b76dcca8167575b2aa624a97a2a Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 6 Dec 2025 17:24:54 -0600 Subject: [PATCH 14/46] added section headings --- k8s-cpu/unstyled.html | 47 ++++++++++++++++++++++++++++++++++++------- k8s-mem/unstyled.html | 10 ++++----- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/k8s-cpu/unstyled.html b/k8s-cpu/unstyled.html index ca28db960..4153a0499 100644 --- a/k8s-cpu/unstyled.html +++ b/k8s-cpu/unstyled.html @@ -24,12 +24,13 @@

    What you will learn

    +

    Namespace CPU details

    • What you will learn namespace. Ideally, each workload should have approximately 60-90% CPU usage.
    • +
    +
    + +

    Workload CPU details

    +
    • - Open the first workload in the list to see details about the workload. + Scroll down to the list of workloads in this namespace. + Click the first workload in the list to see details about the workload.
    • What you will learn data-requirements="exists-reftarget" data-doit="false" > - Review the Alignment graph for Pod CPU usage compared to requests.

      + This is the workload detail page. Review the Alignment graph for Pod CPU usage compared to requests.

      Determine if Pods are running over or too close to their CPU requests.
    • +
    +
    + +

    Pod CPU details

    +
    • What you will learn data-reftarget="section[data-testid*='Alignment: Container Usage/Requests']" data-targetaction="highlight" data-requirements="exists-reftarget" - data-doit="false" > - Notice the Alignment: Container Usage/Requests (%) panel, compare actual container CPU usage against the CPU requests.

      + Notice the Alignment: Container Usage/Requests panel, which compares actual container CPU usage against the CPU requests.

      If usage exceeds 100%, containers in the Pod are using more CPU than requested. This can cause CPU throttling, leading to performance degradation. Consider increasing the CPU requests in the Pod specification.

      If usage is between 80-100%, containers are using a healthy amount of CPU. If usage is less than 50%, containers are over-provisioned.
    • +
    +
    + +

    Container CPU details

    +
    • What you will learn
    • Actionable data: Use the restart tally to correlate with logs and metrics for faster remediation.
    -
    diff --git a/k8s-mem/unstyled.html b/k8s-mem/unstyled.html index 9e9220270..deedf015d 100644 --- a/k8s-mem/unstyled.html +++ b/k8s-mem/unstyled.html @@ -91,15 +91,14 @@

    Namespace memory details

    data-targetaction="highlight" data-requirements="exists-reftarget" data-doit="false" - > - The Distribution: Workload usage panel helps visualise which workloads are contributing the most to the overall namespace memory usage. - + > + The Distribution: Workload usage panel helps visualise which workloads are contributing the most to the overall namespace memory usage. +
  • Focus on the Alignment: usage/requests panel.

    Each workload should see approximately 60-90% memory usage. @@ -178,6 +177,7 @@

    Pod memory details

    data-requirements="navmenu-open,on-page:/a/grafana-k8s-app" >

    Container memory details

    +
    • Container memory details
    • Wrong QoS class: Without appropriate limits, pods drop into BestEffort (first to be evicted) instead of Burstable or Guaranteed.
  • - +

    What you have learned

    From abf0418465a520fdd83ba71087e43af5861c6bad Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sun, 7 Dec 2025 06:59:57 -0600 Subject: [PATCH 15/46] add content to intro --- k8s-cpu/unstyled.html | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/k8s-cpu/unstyled.html b/k8s-cpu/unstyled.html index 4153a0499..0342c3c9f 100644 --- a/k8s-cpu/unstyled.html +++ b/k8s-cpu/unstyled.html @@ -3,14 +3,15 @@ - Explore Kubernetes Monitoring + CPU usage in Kubernetes -

    Explore CPU usage in Kubernetes Monitoring

    +

    Investigate CPU usage in Kubernetes Monitoring

    - Travel through the Kubernetes Monitoring application to explore CPU usage and any associated issues, - starting at the namespace level. + Namespaces often map to teams, projects, environments (dev, staging, prod), or business units. + When you Monitor CPU usage by namespace, you learn the usage patterns of each group. + Then you can plan for capacity and forecast for scaling the cluster or reallocating resources.

    What you will learn

    From 52ece6050203271e72850c33fcb9598a7e53284d Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sun, 7 Dec 2025 07:00:43 -0600 Subject: [PATCH 16/46] edits --- k8s-cpu/unstyled.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s-cpu/unstyled.html b/k8s-cpu/unstyled.html index 0342c3c9f..b693cab2b 100644 --- a/k8s-cpu/unstyled.html +++ b/k8s-cpu/unstyled.html @@ -20,8 +20,8 @@

    What you will learn

    Navigate from namespaces through the Kubernetes hierarchy to individual containers. -
  • Review key panels to understand CPU usage.
  • -
  • Review throttling, restarts, and termination trends at the container level.
  • +
  • Review key panels to understand CPU usage for a namespace.
  • +
  • Locate panels that show throttling, restarts, and termination trends at the container level.
  • Date: Sun, 7 Dec 2025 07:02:02 -0600 Subject: [PATCH 17/46] revert changes --- k8s-cpu/unstyled.html | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/k8s-cpu/unstyled.html b/k8s-cpu/unstyled.html index b693cab2b..7c5524a9a 100644 --- a/k8s-cpu/unstyled.html +++ b/k8s-cpu/unstyled.html @@ -3,15 +3,14 @@ - CPU usage in Kubernetes + Explore Kubernetes Monitoring -

    Investigate CPU usage in Kubernetes Monitoring

    +

    Explore CPU usage in Kubernetes Monitoring

    - Namespaces often map to teams, projects, environments (dev, staging, prod), or business units. - When you Monitor CPU usage by namespace, you learn the usage patterns of each group. - Then you can plan for capacity and forecast for scaling the cluster or reallocating resources. + Travel through the Kubernetes Monitoring application to explore CPU usage and any associated issues, + starting at the namespace level.

    What you will learn

    @@ -20,8 +19,8 @@

    What you will learn

    Navigate from namespaces through the Kubernetes hierarchy to individual containers. -
  • Review key panels to understand CPU usage for a namespace.
  • -
  • Locate panels that show throttling, restarts, and termination trends at the container level.
  • +
  • Review key panels to understand CPU usage.
  • +
  • Review throttling, restarts, and termination trends at the container level.
  • Namespace CPU details data-reftarget="div[data-testid='uplot-main-div']:nth-match(6)" data-targetaction="highlight" data-requirements="exists-reftarget" - data-doit="false" - > - Notice how many containers in the - Scheduling: Containers with CPU requests set panel - have requests configured. Pods without CPU requests get - BestEffort QoS and are evicted first when nodes + data-doit="false + Notice how many containers in the Scheduling: Containers with CPU requests set panel + have requeget. Pods without CPU requests are assigneQoSng>Beort
    quality of service, and they are evicted first when nodes reclaim CPU.

    Review how requests compare to actual CPU usage for workloads in the namespace. Ideally, each workload should have approximately 60-90% CPU @@ -97,8 +93,7 @@

    Workload CPU details

    data-targetaction="highlight" data-requirements="exists-reftarget" > - Scroll down to the list of workloads in this namespace. - Click the first workload in the list to see details about the workload. + Open the first workload in the list to see details about the workload.
  • Workload CPU details data-requirements="exists-reftarget" data-doit="false" > - This is the workload detail page. Review the Alignment graph for Pod CPU usage compared to requests.

    + Review the Alignment graph for Pod CPU usage compared to requests.

    Determine if Pods are running over or too close to their CPU requests.
  • @@ -136,8 +131,9 @@

    Pod CPU details

    data-reftarget="section[data-testid*='Alignment: Container Usage/Requests']" data-targetaction="highlight" data-requirements="exists-reftarget" + data-doit="false" > - Notice the Alignment: Container Usage/Requests panel, which compares actual container CPU usage against the CPU requests.

    + Notice the Alignment: Container Usage/Requests (%) panel, compare actual container CPU usage against the CPU requests.

    If usage exceeds 100%, containers in the Pod are using more CPU than requested. This can cause CPU throttling, leading to performance degradation. Consider increasing the CPU requests in the Pod specification.

    If usage is between 80-100%, containers are using a healthy amount of CPU. If usage is less than 50%, containers are over-provisioned. From 5d215dadc6f854ae440441ffc1041a1c66bdbc53 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sun, 7 Dec 2025 07:06:34 -0600 Subject: [PATCH 18/46] edits --- k8s-mem/unstyled.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/k8s-mem/unstyled.html b/k8s-mem/unstyled.html index deedf015d..703990baf 100644 --- a/k8s-mem/unstyled.html +++ b/k8s-mem/unstyled.html @@ -6,10 +6,10 @@ Memory usage -

    Explore memory usage in Kubernetes Monitoring

    +

    Memory usage in Kubernetes namespaces

    - Travel through the Kubernetes Monitoring application to explore memory usage and any associated issues, + Travel through Kubernetes Monitoring to explore memory usage and any associated issues within a namespace, starting at the namespace level.

    @@ -17,7 +17,7 @@

    What you will learn

    • Navigate from namespaces through the Kubernetes hierarchy to individual - containers. + containers within a namespace.
    • Review key panels to understand memory usage.
    • The importance of setting memory requests and limits.
    • From bceaf44058ce033bdb3c0d074827b5e7020af3b6 Mon Sep 17 00:00:00 2001 From: Tom Glenn Date: Mon, 8 Dec 2025 09:28:26 +0000 Subject: [PATCH 19/46] Migrates content to json --- k8s-cpu/content.json | 153 +++++++++++++++++++++++++++++++++++++++++++ k8s-mem/content.json | 151 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 304 insertions(+) create mode 100644 k8s-cpu/content.json create mode 100644 k8s-mem/content.json diff --git a/k8s-cpu/content.json b/k8s-cpu/content.json new file mode 100644 index 000000000..846852ca7 --- /dev/null +++ b/k8s-cpu/content.json @@ -0,0 +1,153 @@ +{ + "id": "k8s-cpu", + "title": "Explore CPU usage in Kubernetes Monitoring (JSON)", + "blocks": [ + { + "type": "markdown", + "content": "# Explore CPU usage in Kubernetes Monitoring\n\nTravel through the Kubernetes Monitoring application to explore CPU usage and any associated issues, starting at the namespace level.\n\n### What you will learn\n\n- Navigate from namespaces through the Kubernetes hierarchy to individual containers.\n- Review key panels to understand CPU usage.\n- Review throttling, restarts, and termination trends at the container level." + }, + { + "type": "section", + "id": "namespace-cpu-detail", + "title": "Namespace CPU details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Nav menu item'][href='/a/grafana-k8s-app/navigation/namespace']", + "requirements": ["exists-reftarget"], + "content": "Click **Namespaces** in the main menu to view the list of namespaces in your environment." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Select the first namespace in the list to open its detail view." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Tab CPU']", + "requirements": ["exists-reftarget"], + "content": "This is an overview of the CPU and memory usage in the namespace.\n\nClick the **CPU** tab to view more detail about the CPU usage in the namespace." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='uplot-main-div']:nth-match(6)", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "Notice how many containers in the **Scheduling: Containers with CPU requests set** panel have requests set. Pods without CPU requests are assigned **BestEffort** quality of service, and they are evicted first when nodes reclaim CPU.\n\nReview how requests compare to actual CPU usage for workloads in the namespace. Ideally, each workload should have approximately 60-90% CPU usage." + } + ] + }, + { + "type": "section", + "id": "workload-cpu-detail", + "title": "Workload CPU details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Open the first workload in the list to see details about the workload." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Alignment: Pod Usage/Requests (%)'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "Review the Alignment graph for Pod CPU usage compared to requests.\n\nDetermine if Pods are running over or too close to their CPU requests." + } + ] + }, + { + "type": "section", + "id": "pod-cpu-detail", + "title": "Pod CPU details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Select the first Pod in the Pods list." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid*='Alignment: Container Usage/Requests']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "Notice the Alignment: Container Usage/Requests (%) panel, compare actual container CPU usage against the CPU requests.\n\nIf usage exceeds 100%, containers in the Pod are using more CPU than requested. This can cause CPU throttling, leading to performance degradation. Consider increasing the CPU requests in the Pod specification.\n\nIf usage is between 80-100%, containers are using a healthy amount of CPU. If usage is less than 50%, containers are over-provisioned." + } + ] + }, + { + "type": "section", + "id": "container-cpu-detail", + "title": "Container CPU details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='row'][aria-rowindex='2'] div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']", + "requirements": ["exists-reftarget"], + "content": "Click the first container in the list to view container details." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "canvas:nth-of-type(2):nth-match(3)", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "At the Container detail page, scroll to the **CPU requests sizing** section.\n\nNotice on either side of the gauge, there are two numbers. The number on the left is the currently set CPU requests. The number on the right is the recommended CPU requests. You can use the recommendation to adjust your CPU requests for this container." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header CPU throttling']", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "Scroll further to view the **CPU throttling** panel.\n\nThis panel shows how much time your container is being artificially slowed down because it's trying to use more CPU than its configured limits allow.\n\nWhy it matters:\n\n- Performance impact: High throttling means your application is running slower than it could, directly affecting user experience.\n- Right-sizing: Helps you identify if CPU limits are too restrictive. You might need to increase them.\n- Cost optimization: If throttling is zero or very low, you might have over-provisioned CPU limits and can reduce costs.\n- Application behavior: Sudden throttling spikes can correlate with slow response times or timeout errors." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Last terminated reason'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "Notice the **Last terminated reason** panel.\n\nThis view tells you exactly why Kubernetes last stopped the container, giving you clues to resolve instability.\n\n- **Root cause:** Last termination reason points to the underlying trigger (OOMKilled, CrashLoopBackOff, and so on).\n- **Resilience:** Helps you fix recurring issues before they cascade into bigger outages.\n- **Alerting:** Use these reasons to fine-tune alerts and spot regressions faster." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Container restart history'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "Review the **Container restart history** panel.\n\nThis timeline shows how often the container has restarted, helping you detect flapping workloads.\n\n- **Stability check:** Frequent restarts indicate crashing code, resource starvation, or readiness issues.\n- **Trend insight:** Recent spikes can pinpoint deployments or config changes that destabilized the service.\n- **Mitigation planning:** Correlate restarts with logs and metrics to prioritize fixes and improve uptime." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Container restarts'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "Examine the **Container restarts** panel for a point-in-time view of restart counts.\n\n- **Health indicator:** High restart counts signal unstable workloads that need investigation.\n- **Operational visibility:** Provides a quick glance at which containers are repeatedly crashing.\n- **Actionable data:** Use the restart tally to correlate with logs and metrics for faster remediation." + } + ] + }, + { + "type": "markdown", + "content": "### What you have learned\n\nYou have learned how to:\n\n- Navigate from namespaces to individual containers to look at CPU usage.\n- Interpret container CPU usage, throttling, restart history, and termination reasons.\n- Turn insights and CPU request recommendations into improved resource sizing and workload reliability." + } + ] +} + diff --git a/k8s-mem/content.json b/k8s-mem/content.json new file mode 100644 index 000000000..ebba2904e --- /dev/null +++ b/k8s-mem/content.json @@ -0,0 +1,151 @@ +{ + "id": "k8s-mem", + "title": "Memory usage in Kubernetes namespaces (JSON)", + "blocks": [ + { + "type": "markdown", + "content": "# Memory usage in Kubernetes namespaces\n\nTravel through Kubernetes Monitoring to explore memory usage and any associated issues within a namespace, starting at the namespace level.\n\n### What you will learn\n\n- Navigate from namespaces through the Kubernetes hierarchy to individual containers within a namespace.\n- Review key panels to understand memory usage.\n- The importance of setting memory requests and limits." + }, + { + "type": "section", + "id": "namespace-detail", + "title": "Namespace memory details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Nav menu item'][href='/a/grafana-k8s-app/navigation/namespace']", + "requirements": ["exists-reftarget"], + "content": "Click **Namespaces** in the main menu to view the list of namespaces in your environment." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Select the first namespace in the list to open its detail view." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Tab Memory']", + "requirements": ["exists-reftarget"], + "content": "This is the namespace detail page showing an overview of the CPU and memory usage in the namespace.\n\nClick the **Memory** tab to view more detail about memory usage in the namespace." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Scheduling: Containers with Memory requests set (p95)'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "Notice the panel titled **Scheduling: Containers with Memory requests set**. If all memory requests are set for all containers in this namespace, then the percentage would be 100%.\n\nWhen memory requests are not set, this creates a significant operational risk that affects scheduling efficiency, pod stability, cluster capacity planning, and cost optimization. Always set requests based on observed usage patterns." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Alignment: usage/requests (p95)'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "Notice the **Alignment: usage/requests** panel.\n\nIf all memory requests are set for all containers in this namespace, total memory usage should fall between the 60-90% range of requests." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='uplot-main-div']:nth-match(5)", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Distribution: Workload usage** panel helps visualise which workloads are contributing the most to the overall namespace memory usage." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[data-testid='uplot-main-div']:nth-match(6)", + "requirements": ["exists-reftarget"], + "content": "Focus on the **Alignment: usage/requests** panel.\n\nEach workload should see approximately 60-90% memory usage. Workloads that fall outside of this range could be considered as incorrectly sized, or they have containers with memory requests and limits that are not well configured." + } + ] + }, + { + "type": "section", + "id": "workload-detail", + "title": "Workload memory details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Scroll down the list of workloads in this namespace. Click the first workload in the list to see details about the workload." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Alignment: Pod Usage/Requests (%)'] > div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "This is the workload detail page.\n\nReview the Alignment graph for Pod memory usage compared to requests.\n\nThe target range should be around 60-80% utilization of requests to provide headroom for bursts while avoiding waste." + } + ] + }, + { + "type": "section", + "id": "pod-detail", + "title": "Pod memory details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Select the first Pod in the Pods list." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid*='Alignment: Container Usage/Requests']", + "requirements": ["exists-reftarget"], + "content": "Notice the Alignment: usage/requests panel.\n\nIdeally, each container should have around 60-90% memory usage. Containers that fall outside of this range could have memory that is incorrectly configured." + } + ] + }, + { + "type": "section", + "id": "container-detail", + "title": "Container memory details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='row'][aria-rowindex='2'] div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']", + "requirements": ["exists-reftarget"], + "content": "Click the first container in the list of containers in this Pod to view container details." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "canvas:nth-of-type(2):nth-match(2)", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "At the Container detail page, scroll to the **Memory sizing limits** section.\n\nThe number on the left side of the gauge is the currently set memory sizing limits. The number on the right is the recommended memory sizing limits. You can use the recommendation to adjust your memory limits for this container.\n\nWithout memory limits, a container can consume all available memory on the node, potentially causing:\n\n- Node-wide OOM events, which can kill other workloads or system components.\n- Pod eviction when the node runs low on memory.\n- Resource starvation for other pods on the node, degrading cluster performance." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "canvas:nth-of-type(2):nth-match(4)", + "requirements": ["exists-reftarget"], + "scroll": true, + "content": "Next, focus on the **Memory requests sizing** panel.\n\nThe number on the left side of the gauge is the currently set memory requests. The number on the right is the recommended setting for memory requests. You can use the recommendation to adjust your memory requests for this container.\n\nWhen requests are too high, it can cause:\n\n- Wasted capacity: Kubernetes reserves more memory than actually needed, leaving nodes artificially \"full\" even when memory sits unused.\n- Scheduling failures: Pods can't be scheduled even when nodes have available memory, because the scheduler sees insufficient requestable capacity.\n- Higher costs: You need more nodes to run the same workload due to over-reservation.\n\nWhen requests are too low, it can cause:\n\n- Pod evictions: Under memory pressure, Kubernetes evicts pods exceeding their requests first—your pod becomes a target even when running normally.\n- Unpredictable performance: The kubelet may throttle or kill processes on memory-pressured nodes, causing instability.\n- Wrong QoS class: Without appropriate limits, pods drop into BestEffort (first to be evicted) instead of Burstable or Guaranteed." + } + ] + }, + { + "type": "markdown", + "content": "### What you have learned\n\nYou have learned how to:\n\n- Navigate from namespaces to individual containers to look at memory usage, requests, and limits.\n- Review container memory usage, limits, requests, and recommendations.\n- Turn insights and memory request recommendations into improved resource sizing and reliability." + } + ] +} + From 0c0521227ff6626c53f341b6980fd3b8789d6e7f Mon Sep 17 00:00:00 2001 From: Beverly Buchanan <131809838+BeverlyJaneJ@users.noreply.github.com> Date: Mon, 8 Dec 2025 07:59:16 -0600 Subject: [PATCH 20/46] Revise title and content for CPU usage focus Updated the title and introductory text to focus on CPU usage in namespaces, emphasizing its importance for monitoring and resource planning. --- k8s-cpu/unstyled.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/k8s-cpu/unstyled.html b/k8s-cpu/unstyled.html index 7c5524a9a..1c6e0b136 100644 --- a/k8s-cpu/unstyled.html +++ b/k8s-cpu/unstyled.html @@ -3,24 +3,25 @@ - Explore Kubernetes Monitoring + CPU usage in Namespaces

      Explore CPU usage in Kubernetes Monitoring

      - Travel through the Kubernetes Monitoring application to explore CPU usage and any associated issues, - starting at the namespace level. -

      + Namespaces often map to teams, projects, environments (dev, staging, prod), or business units. + When you Monitor CPU usage by namespace, you learn the usage patterns of each group. + Then you can plan for capacity and forecast for scaling the cluster or reallocating resources. +

      What you will learn

      • Navigate from namespaces through the Kubernetes hierarchy to individual - containers. + containers within a namespace.
      • Review key panels to understand CPU usage.
      • -
      • Review throttling, restarts, and termination trends at the container level.
      • +
      • Locate panels that show throttling, restarts, and termination trends at the container level.
      What you have learned
    - \ No newline at end of file + From 34ea62f3202bfcbed10c769016c55257dfa6e13a Mon Sep 17 00:00:00 2001 From: Beverly Buchanan <131809838+BeverlyJaneJ@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:40:18 -0600 Subject: [PATCH 21/46] Update title and content for CPU usage monitoring --- k8s-cpu/content.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/k8s-cpu/content.json b/k8s-cpu/content.json index 846852ca7..1a3b69f5c 100644 --- a/k8s-cpu/content.json +++ b/k8s-cpu/content.json @@ -1,10 +1,10 @@ { "id": "k8s-cpu", - "title": "Explore CPU usage in Kubernetes Monitoring (JSON)", + "title": "CPU usage in Namespaces (JSON)", "blocks": [ { "type": "markdown", - "content": "# Explore CPU usage in Kubernetes Monitoring\n\nTravel through the Kubernetes Monitoring application to explore CPU usage and any associated issues, starting at the namespace level.\n\n### What you will learn\n\n- Navigate from namespaces through the Kubernetes hierarchy to individual containers.\n- Review key panels to understand CPU usage.\n- Review throttling, restarts, and termination trends at the container level." + "content": "# Explore CPU usage in Kubernetes Monitoring\n\Namespaces often map to teams, projects, environments (dev, staging, prod), or business units. When you Monitor CPU usage by namespace, you learn the usage patterns of each group. Then you can plan for capacity and forecast for scaling the cluster or reallocating resources.\n\n### What you will learn\n\n- Navigate from namespaces through the Kubernetes hierarchy to individual containers.\n- Review key panels to understand CPU usage.\n- Locate panels that show throttling, restarts, and termination trends at the container level." }, { "type": "section", From a820bf51c96023e09400230cea1cc29d6daa3389 Mon Sep 17 00:00:00 2001 From: Beverly Buchanan <131809838+BeverlyJaneJ@users.noreply.github.com> Date: Tue, 9 Dec 2025 13:40:54 -0600 Subject: [PATCH 22/46] Update content formatting in content.json Fix formatting of content in CPU usage documentation. --- k8s-cpu/content.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s-cpu/content.json b/k8s-cpu/content.json index 1a3b69f5c..1d68763c2 100644 --- a/k8s-cpu/content.json +++ b/k8s-cpu/content.json @@ -4,7 +4,7 @@ "blocks": [ { "type": "markdown", - "content": "# Explore CPU usage in Kubernetes Monitoring\n\Namespaces often map to teams, projects, environments (dev, staging, prod), or business units. When you Monitor CPU usage by namespace, you learn the usage patterns of each group. Then you can plan for capacity and forecast for scaling the cluster or reallocating resources.\n\n### What you will learn\n\n- Navigate from namespaces through the Kubernetes hierarchy to individual containers.\n- Review key panels to understand CPU usage.\n- Locate panels that show throttling, restarts, and termination trends at the container level." + "content": "# Explore CPU usage in Kubernetes Monitoring\n\n Namespaces often map to teams, projects, environments (dev, staging, prod), or business units. When you Monitor CPU usage by namespace, you learn the usage patterns of each group. Then you can plan for capacity and forecast for scaling the cluster or reallocating resources.\n\n### What you will learn\n\n- Navigate from namespaces through the Kubernetes hierarchy to individual containers.\n- Review key panels to understand CPU usage.\n- Locate panels that show throttling, restarts, and termination trends at the container level." }, { "type": "section", From 20e72eab6534740d5dbb107f1a2a72ad7864cb89 Mon Sep 17 00:00:00 2001 From: David Allen Date: Tue, 9 Dec 2025 15:24:48 -0500 Subject: [PATCH 23/46] recommend bev's k8s tutorials on play --- index.json | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/index.json b/index.json index 0e4944c96..3c424a93f 100644 --- a/index.json +++ b/index.json @@ -134,6 +134,38 @@ } ] } + }, + { + "title": "Interactive Guide: CPU Usage in Kubernetes", + "url": "https://interactive-learning.grafana.net/guides/k8s-cpu", + "description": "Hands-on guide: Explore CPU usage in Kubernetes Monitoring, from namespaces to containers.", + "type": "docs-page", + "match": { + "and": [ + { + "urlPrefix": "/a/grafana-k8s-app" + }, + { + "source": "play.grafana.org" + } + ] + } + }, + { + "title": "Interactive Guide: Memory Usage in Kubernetes", + "url": "https://interactive-learning.grafana.net/guides/k8s-mem", + "description": "Hands-on guide: Explore memory usage in Kubernetes Monitoring, from namespaces to containers.", + "type": "docs-page", + "match": { + "and": [ + { + "urlPrefix": "/a/grafana-k8s-app" + }, + { + "source": "play.grafana.org" + } + ] + } } ] } From 42a30fcfc288150736ffa68611e72fde79d8504f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 10 Dec 2025 22:26:15 +0000 Subject: [PATCH 24/46] Fix tutorial text: sentence case and terminology - Change "Make your First Dashboard" to "Make your first dashboard" (sentence case) - Change "Create a Datasource" to "Create a data source" (sentence case + correct term) - Change "Create a Dashboard" to "Create a dashboard" (sentence case) - Change "Install Button" to "Install button" (sentence case) - Change "datasource" to "data source" throughout prose (Grafana style: two words) --- first-dashboard/content.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/first-dashboard/content.json b/first-dashboard/content.json index 7c4d04bd4..474070ec0 100644 --- a/first-dashboard/content.json +++ b/first-dashboard/content.json @@ -1,10 +1,10 @@ { "id": "first-dashboard", - "title": "Make your First Dashboard (JSON)", + "title": "Make your first dashboard (JSON)", "blocks": [ { "type": "markdown", - "content": "# Make your First Dashboard\n\nIn this tutorial, we'll make your first dashboard, of Grafana news. To do this, we'll learn the following parts of Grafana in sequence:\n\n- Installing plugins to get access to data (in our case, RSS feeds)\n- Creating data sources, so we have something to visualize\n- Creating a dashboard that uses that datasource\n- Setting up basic Alerting" + "content": "# Make your first dashboard\n\nIn this tutorial, we'll make your first dashboard, of Grafana news. To do this, we'll learn the following parts of Grafana in sequence:\n\n- Installing plugins to get access to data (in our case, RSS feeds)\n- Creating data sources, so we have something to visualize\n- Creating a dashboard that uses that data source\n- Setting up basic Alerting" }, { "type": "markdown", @@ -49,18 +49,18 @@ "reftarget": "Install", "requirements": ["is-admin"], "hint": "You'll need to be logged in, and an administrator", - "content": "Click the Install Button" + "content": "Click the Install button" } ] }, { "type": "markdown", - "content": "## Step 2: Create a Datasource\n\nA datasource lets us specify which feed we want to use, so we can query it in a dashboard." + "content": "## Step 2: Create a data source\n\nA data source lets us specify which feed we want to use, so we can query it in a dashboard." }, { "type": "section", "id": "create-datasource", - "title": "Create a Datasource", + "title": "Create a data source", "requirements": ["navmenu-open", "has-plugin:volkovlabs-rss-datasource"], "objectives": ["has-datasource:volkovlabs-rss-datasource"], "blocks": [ @@ -113,12 +113,12 @@ }, { "type": "markdown", - "content": "## Step 3: Create a Dashboard\n\nIn this section, we'll use the datasource we created to create a visualization." + "content": "## Step 3: Create a dashboard\n\nIn this section, we'll use the data source we created to create a visualization." }, { "type": "section", "id": "create-dashboard", - "title": "Create a Dashboard", + "title": "Create a dashboard", "requirements": ["has-datasource:volkovlabs-rss-datasource"], "blocks": [ { From ce878f1ad41802a910cf72a2f02afe12da34b2e0 Mon Sep 17 00:00:00 2001 From: David Allen Date: Thu, 11 Dec 2025 10:35:29 -0500 Subject: [PATCH 25/46] play:quick tour of grafana visualizations --- .cursor/best-practices.mdc | 58 +++++++++ index.json | 13 ++ tour-of-visualizations/content.json | 192 ++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 tour-of-visualizations/content.json diff --git a/.cursor/best-practices.mdc b/.cursor/best-practices.mdc index 6ba1f4ecd..a3091ef4b 100644 --- a/.cursor/best-practices.mdc +++ b/.cursor/best-practices.mdc @@ -50,6 +50,57 @@ Make admin-only steps `skippable: true` with helpful hints. Use `objectives` to auto-complete already-done steps. Provide alternative workflows for different user permissions. +### Don't Put Markdown Titles in Guides + +Don't begin your interactive guides with a markdown block which contains +a title, such as: + +{ + "type": "markdown", + "content": "## My Cool Guide" +} + +This is because Pathfinder will already render the guide's title in an +enclosing box, and so if you start with things like this, it will end +up duplicating the title twice (once in the surrounding frame, again +in the content). The `title` of the guide as declared at the top will +always be displayed. + +### Use Interactive Sections, not just Markdown blocks + +Rather than doing this: + +```json +{ + "type": "markdown", + "content": "## My Section" +}, +{ + "type": "markdown", + "content": "I have things to say" +} +``` + +You can accomplish the same better by using guide sections like so: + +```json +{ + "type": "section", + "id": "something-unique", + "title": "My Section", + "blocks": [ + { + "type": "markdown", + "content": "I have things to say" + } + ] +} +``` + +This is better because it gives the Pathfinder app better control over +visual rendering, and allows the content to be enhanced with closeable +sections, etc. + ### Verify State Changes * Use `verify` for actions that change state @@ -69,6 +120,13 @@ Provide alternative workflows for different user permissions. * Use `multistep` for related actions that should happen together * Include section completion requirements for dependencies +### Editorial Quality + +* Always check for typos +* Always check for consistent use of title case +* Be on the lookout for excess jargon; try to stay focused to known +Grafana technical terms. + ### Content Quality * Keep interactive comments (`tooltip`) under 250 characters diff --git a/index.json b/index.json index 3c424a93f..9fef71c43 100644 --- a/index.json +++ b/index.json @@ -1,5 +1,18 @@ { "rules": [ + { + "title": "Tour of Grafana Visualizations", + "url": "https://interactive-learning.grafana.net/guides/tour-of-visualizations", + "description": "A quick tour of Grafana's visualization types—when to use each one, with live examples.", + "type": "docs-page", + "match": { + "and": [ + { + "source": "play.grafana.org" + } + ] + } + }, { "title": "Interactive Guide: Explore Drilldowns 101", "url": "https://interactive-learning.grafana.net/guides/explore-drilldowns-101", diff --git a/tour-of-visualizations/content.json b/tour-of-visualizations/content.json new file mode 100644 index 000000000..1299646fa --- /dev/null +++ b/tour-of-visualizations/content.json @@ -0,0 +1,192 @@ +{ + "id": "tour-of-visualizations", + "title": "Tour of Grafana Visualizations", + "blocks": [ + { + "type": "markdown", + "content": "**What you will learn:** Which panel type to pick for your data.\n\nEach stop links to a live example on Grafana Play—click through and explore!" + }, + { + "type": "section", + "id": "time-based-visualizations", + "title": "Time-Based Visualizations", + "blocks": [ + { + "type": "markdown", + "content": "The bread and butter of monitoring—showing how things change over time." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/000000016/time-series-graphs", + "content": "**Time Series** — The workhorse. Use this when you want to see metrics over time—CPU usage, request rates, temperatures, you name it." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/qD-rVv6Mz/state-timeline-and-status-history", + "content": "**State Timeline & Status History** — When you care about *what state* something was in, not just numbers. Perfect for tracking service status, deployments, or on/off states." + } + ] + }, + { + "type": "section", + "id": "single-value-displays", + "title": "Single Value Displays", + "blocks": [ + { + "type": "markdown", + "content": "Sometimes you just need one big number that tells you what's happening *right now*." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/Zb3f4veGk/stats", + "content": "**Stats** — Big numbers with optional sparklines. Use these when a single value tells the whole story: total users, current latency, error count." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/KIhkVD6Gk/gauges", + "content": "**Gauges** — When you need to show how close a value is to a threshold. Think: disk usage at 87%, CPU approaching danger zone." + } + ] + }, + { + "type": "section", + "id": "tables-structured-data", + "title": "Tables & Structured Data", + "blocks": [ + { + "type": "markdown", + "content": "When you need to see the actual data, not just a pretty picture." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/OhR1ID6Mk/table", + "content": "**Tables** — For when you need rows and columns. Great for top-N lists, detailed breakdowns, or any time you want to see exact values side by side." + } + ] + }, + { + "type": "section", + "id": "categorical-distribution-charts", + "title": "Categorical & Distribution Charts", + "blocks": [ + { + "type": "markdown", + "content": "Comparing things across categories or understanding how values are distributed." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/ktMs4D6Mk/bar-charts-and-pie-charts", + "content": "**Bar Charts & Pie Charts** — Comparing categories. Bar charts for \"which is bigger?\", pie charts for \"what's the proportion?\"" + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/histogram_tests/histogram-examples", + "content": "**Histograms** — When you want to understand distribution. How are response times spread out? Where do most values fall?" + } + ] + }, + { + "type": "section", + "id": "specialized-analytics", + "title": "Specialized Analytics", + "blocks": [ + { + "type": "markdown", + "content": "More advanced panels for specific analytical needs." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/heatmap-calculate-log/grafana-heatmaps", + "content": "**Heatmaps** — Visualizing density and patterns in two dimensions. Perfect for spotting \"hot\" time periods or understanding where values cluster." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/candlestick/candlestick", + "content": "**Candlestick** — The classic financial chart. Use it when you're tracking price movements, trades, or any data with open/high/low/close semantics." + } + ] + }, + { + "type": "section", + "id": "observability-specific", + "title": "Observability-Specific", + "blocks": [ + { + "type": "markdown", + "content": "Built for the unique needs of logs and performance profiling." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/6NmftOxZz/logs-panel", + "content": "**Logs Panel** — For viewing log lines directly in your dashboard. Search, filter, and correlate logs alongside your metrics." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/cdl34qv4zzg8wa/flame-graphs", + "content": "**Flame Graphs** — For performance profiling. See where your application is spending its time, function by function." + } + ] + }, + { + "type": "section", + "id": "geospatial", + "title": "Geospatial", + "blocks": [ + { + "type": "markdown", + "content": "When location matters." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/panel-geomap/geomap-examples", + "content": "**Geomaps** — Plot your data on a map. Server locations, regional metrics, delivery routes—anything with latitude and longitude." + } + ] + }, + { + "type": "section", + "id": "content-navigation-widgets", + "title": "Content & Navigation Widgets", + "blocks": [ + { + "type": "markdown", + "content": "Helpers for building better dashboards." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/adl33bxy1ih34b/text-panel", + "content": "**Text Panels** — Add context to your dashboards with markdown, HTML, or dynamic text. Great for instructions, links, or explanations." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/fdlojrg7daebka/dashboard-list-visualization", + "content": "**Dashboard List** — A panel that links to other dashboards. Build navigation hubs or group related dashboards together." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/bdodlcyou483ke/alert-list", + "content": "**Alert List** — Show active or recent alerts right in your dashboard. Keep an eye on what's firing without leaving the page." + } + ] + }, + { + "type": "markdown", + "content": "**What you have learned:** Grafana's panel library—each one designed for a specific job. Pick the right one, and your data tells its own story. Now go build something awesome!" + } + ] +} From 72eef6c1a7230247a64449ca69ae5cec41fb3146 Mon Sep 17 00:00:00 2001 From: Marie Cruz Date: Thu, 11 Dec 2025 17:48:22 +0000 Subject: [PATCH 26/46] add extra requirements to play homepage guide --- welcome-to-play/main-page/content.json | 41 +++++++++++++++++++------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/welcome-to-play/main-page/content.json b/welcome-to-play/main-page/content.json index 1a868df4a..71c3fab1f 100644 --- a/welcome-to-play/main-page/content.json +++ b/welcome-to-play/main-page/content.json @@ -38,9 +38,13 @@ "type": "interactive", "action": "highlight", "reftarget": "a[href*=\"/d/ma79mqp/visualization-examples\"]", - "completeEarly": true, + "content": "Examine the **Visualization Examples** panel showing the wide variety of chart types available.", "tooltip": "The **Visualization Examples** panel showcases Grafana's extensive visualization library. From *time series* for trend analysis to *bar charts* for comparisons, *heatmaps* for distributions, *geomaps* for location data, and *flame graphs* for performance profiling - Grafana provides the right visualization for every data story. Each link leads to a dedicated dashboard demonstrating that visualization type with real data.", - "content": "Examine the **Visualization Examples** panel showing the wide variety of chart types available." + "requirements": [ + "on-page:/d/to6j8mh/grafana-play-home", + "exists-reftarget" + ], + "completeEarly": true } ] }, @@ -57,9 +61,13 @@ "type": "interactive", "action": "highlight", "reftarget": "a[href*=\"/d/mamnq22/data-source-examples\"]", - "completeEarly": true, + "content": "Explore the **Data Source Examples** panel showcasing Grafana's extensive connectivity options.", "tooltip": "The **Data Source Examples** panel demonstrates Grafana's extensive data source ecosystem. From traditional databases like `PostgreSQL` and `MySQL` to modern observability tools like `Prometheus` and `Loki`, cloud services like `InfluxDB`, and even APIs through the `Infinity` plugin - Grafana connects to virtually any data source. Each example shows real-world use cases and query patterns.", - "content": "Explore the **Data Source Examples** panel showcasing Grafana's extensive connectivity options." + "requirements": [ + "on-page:/d/to6j8mh/grafana-play-home", + "exists-reftarget" + ], + "completeEarly": true } ] }, @@ -76,8 +84,12 @@ "type": "interactive", "action": "highlight", "reftarget": "a[href*=\"/d/mabjzp6/grafana-arcade\"]", + "content": "Click on the **Grafana Arcade** to explore Grafana's collection of interactive games.", "tooltip": "The **Grafana Arcade** section includes beloved games like **Doom** and **SuperGrot**, all built with Grafana's powerful visualization capabilities.", - "content": "Click on the **Grafana Arcade** to explore Grafana's collection of interactive games." + "requirements": [ + "on-page:/d/to6j8mh/grafana-play-home", + "exists-reftarget" + ] }, { "type": "interactive", @@ -101,8 +113,12 @@ "type": "interactive", "action": "highlight", "reftarget": "a[href*=\"a/grafana-k8s-app\"]", + "content": "Explore the **Kubernetes Monitoring App** for comprehensive cluster observability.", "tooltip": "The **Grafana Kubernetes App** provides comprehensive monitoring for Kubernetes clusters. It includes pre-built dashboards for cluster overview, node monitoring, pod performance, namespace usage, and workload analysis. The app automatically discovers your cluster topology and provides intelligent alerting rules based on Kubernetes best practices.", - "content": "Explore the **Kubernetes Monitoring App** for comprehensive cluster observability." + "requirements": [ + "on-page:/d/to6j8mh/grafana-play-home", + "exists-reftarget" + ] } ] }, @@ -119,8 +135,12 @@ "type": "interactive", "action": "highlight", "reftarget": "a[href*=\"a/grafana-app-observability-app\"]", + "content": "Visit the **Application Observability App** for end-to-end application monitoring.", "tooltip": "The **Application Observability App** brings together metrics, logs, and traces to provide complete visibility into application performance. It includes service maps, error tracking, performance monitoring, and user experience analytics. This unified view helps developers and SREs quickly identify and resolve application issues.", - "content": "Visit the **Application Observability App** for end-to-end application monitoring." + "requirements": [ + "on-page:/d/to6j8mh/grafana-play-home", + "exists-reftarget" + ] } ] }, @@ -137,7 +157,9 @@ "type": "interactive", "action": "highlight", "reftarget": "a[data-testid=\"data-testid Nav menu item\"][href=\"/drilldown\"]", - "requirements": ["navmenu-open"], + "requirements": [ + "navmenu-open" + ], "tooltip": "**Drilldowns** enable contextual navigation between dashboards and data views. When you click on a data point, graph, or table row, you can automatically navigate to related dashboards with relevant filters and time ranges applied. This creates a seamless exploration experience where you can move from high-level overviews to detailed investigations without losing context.", "content": "Navigate to the **Drilldowns** section to learn about advanced data exploration." } @@ -148,5 +170,4 @@ "content": "## 🎉 Congratulations!\n\nYou've completed the comprehensive tour of Grafana Play! You've discovered:\n\n- 📊 **Visualization variety** - From time series to geomaps\n- 🔌 **Data source flexibility** - Connect to anything\n- 🔍 **Advanced exploration** - Drilldowns and contextual navigation\n- ☸️ **Kubernetes monitoring** - Complete cluster observability\n- 📱 **Application insights** - End-to-end observability\n- 🎮 **Creative possibilities** - Even gaming in Grafana!\n\nGrafana Play is your sandbox for exploring these features with real data and no setup required. Feel free to continue exploring, create your own dashboards, and discover what makes Grafana the leading observability platform trusted by millions of users worldwide.\n\n**Next steps:** Try creating your own dashboard, explore more data sources, or dive deeper into any of the features that caught your interest. The possibilities are endless! 🚀" } ] -} - +} \ No newline at end of file From c641c9c9defb82471916db079aa8b5ab8c5766be Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 11 Dec 2025 20:56:19 +0000 Subject: [PATCH 27/46] Add JSON format for SM setting up your first check guide Convert the Synthetic Monitoring "Setting up your first check" guide from HTML-only to JSON format, matching the structure used by other guides in the repository. --- sm-setting-up-your-first-check/content.json | 197 ++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 sm-setting-up-your-first-check/content.json diff --git a/sm-setting-up-your-first-check/content.json b/sm-setting-up-your-first-check/content.json new file mode 100644 index 000000000..e7a817bea --- /dev/null +++ b/sm-setting-up-your-first-check/content.json @@ -0,0 +1,197 @@ +{ + "id": "sm-setting-up-your-first-check", + "title": "How to set up your first Synthetic Monitoring check", + "blocks": [ + { + "type": "markdown", + "content": "# How to set up your first Synthetic Monitoring check\n\nIn this interactive guide you'll learn how to set up and run an HTTP check in Grafana Cloud Synthetic Monitoring. You will:\n\n- Know how to create and configure HTTP checks\n- Understand how Synthetic Monitoring helps you track uptime and performance\n- Learn how to set up alerts to stay informed about your service status" + }, + { + "type": "markdown", + "content": "## Section 1: Navigate to adding a new HTTP check\n\nLet's start by navigating to the Testing & Synthetics section. This is where you'll create and manage your synthetic checks." + }, + { + "type": "section", + "id": "navigate-synthetics", + "title": "Navigate to adding a new HTTP check", + "objectives": ["on-page:/a/grafana-synthetic-monitoring-app/checks/new/api-endpoint"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Nav menu item'][href='/testing-and-synthetics']", + "requirements": ["navmenu-open"], + "content": "In the main menu, navigate to **Testing & synthetics**." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "[id='pageContent'] a[href='/a/grafana-synthetic-monitoring-app/home']", + "requirements": ["exists-reftarget"], + "objectives": ["on-page:/a/grafana-synthetic-monitoring-app/home"], + "content": "Next, click **Synthetics**." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='action create check']", + "requirements": ["exists-reftarget"], + "content": "Click **Create new check**." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[href='/a/grafana-synthetic-monitoring-app/checks/new/api-endpoint']", + "requirements": ["exists-reftarget"], + "content": "Choose **API Endpoint** as the check type." + } + ] + }, + { + "type": "markdown", + "content": "## Section 2: Configure the target of the Synthetic Monitoring check\n\nIn this section you will configure the Synthetic Monitoring check's parameters." + }, + { + "type": "section", + "id": "create-check", + "title": "Configure the target", + "blocks": [ + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='checkEditor form job']", + "targetvalue": "Grafana: HTTP Check", + "requirements": ["exists-reftarget"], + "comment": "All your logs and metrics generated by this check will contain a **job** label with this value.", + "content": "Enter **Grafana: HTTP Check** in the Job Name field." + }, + { + "type": "interactive", + "action": "formfill", + "reftarget": "input[data-testid='checkEditor form instance']", + "targetvalue": "https://grafana.com", + "requirements": ["exists-reftarget"], + "comment": "They will also contain an **instance** label with this value.", + "content": "Set **URL: https://grafana.com**." + } + ] + }, + { + "type": "markdown", + "content": "## Section 3: Review Optional Parameters for the check\n\nIn this section you can define uptime parameters for the check." + }, + { + "type": "section", + "id": "configure-check-uptime", + "title": "Review Optional Parameters", + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='checkEditor navigation uptime']", + "requirements": ["exists-reftarget"], + "comment": "This is the form stepper for configuring your check. You can freely move between sections whenever you want.", + "content": "Click **Uptime** to move to the next section of the check configuration." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "input[data-testid='checkEditor form validStatusCodes']", + "doIt": false, + "skippable": true, + "comment": "Status codes in the 2xx range are the default, so we can skip this step.", + "content": "Set **Valid status codes** if required." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='checkEditor navigation labels']", + "content": "Move to the **Labels** section." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "[data-testid='checkEditor genericLabelContent']", + "skippable": true, + "doIt": false, + "comment": "This is optional; but if you have many checks, this can help you organize them and route your alerts to the right team. Common labels include 'environment', 'service', and 'team' but you are free to use any label you want.", + "content": "Set label names and values if required." + } + ] + }, + { + "type": "markdown", + "content": "## Section 4: Execution\n\nSetup probe location and frequency for the check." + }, + { + "type": "section", + "id": "configure-execution", + "title": "Configure Execution", + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='checkEditor navigation execution']", + "content": "Move to the **Execution** section." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "label[data-testid='checkEditor form probeLabel']:first-of-type", + "comment": "There are multiple probe locations to choose from. It is good practice to run checks from multiple locations to ensure the check is reliable.", + "content": "Select a **probe location** to run the check from." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "input[id^='option-60000-radiogroup-']", + "skippable": true, + "doIt": false, + "comment": "The default probe frequency is 1 minute which is suitable for most checks. Less frequent means fewer executions, but longer lag times before an alert notifying you of an issue.", + "content": "Set the desired probe frequency." + } + ] + }, + { + "type": "markdown", + "content": "## Section 5: Configure Alerting\n\nConfigure alerting for the check.\n\nFinally, you'll configure alerting for the check." + }, + { + "type": "section", + "id": "configure-alerting", + "title": "Configure Alerting", + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='checkEditor navigation alerting']", + "content": "Move to the **Alerting** section." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "input[data-testid='checkEditor alerts ProbeFailedExecutionsTooHigh selectedCheckbox']", + "comment": "The failed check alert will be triggered if the check fails more than the configured number of times.", + "content": "Click the checkbox to enable the failed check alert." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='checkEditor feat-adhoc-check testButton']", + "skippable": true, + "content": "You can click **Test** to see if the check is working as expected." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "button[data-testid='checkEditor form submit']", + "content": "Click **Save** to create the check." + } + ] + }, + { + "type": "markdown", + "content": "## 🎉 Congratulations!\n\nYou have completed the HTTP check tutorial in Grafana Cloud Synthetic Monitoring. You have successfully:\n\n- Created and configured an HTTP check to monitor uptime and performance\n- Added a probe location and frequency to the check\n- Set up an alert to stay informed about service issues" + } + ] +} From f4479f014af04b49dd326e1429bcdfab684edf97 Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 06:52:29 -0500 Subject: [PATCH 28/46] validate guide JSON using zod/pathfinder --- .github/workflows/validate-json.yml | 78 ++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate-json.yml b/.github/workflows/validate-json.yml index 0a3384fd4..21a5bf453 100644 --- a/.github/workflows/validate-json.yml +++ b/.github/workflows/validate-json.yml @@ -1,4 +1,4 @@ -name: Validate JSON +name: Validate JSON Files on: push: @@ -6,7 +6,8 @@ on: workflow_dispatch: jobs: - validate-json: + validate-recommender-rules: + name: Validate Recommender Rules (index.json) runs-on: ubuntu-latest permissions: contents: read @@ -47,3 +48,76 @@ jobs: echo "❌ JSON validation failed!" exit 1 fi + + validate-guides: + name: Validate Interactive Guides (content.json) + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + persist-credentials: false + submodules: false + + - name: Checkout pathfinder-app CLI + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + repository: grafana/grafana-pathfinder-app + ref: main + path: pathfinder-app + persist-credentials: false + + - name: Setup Node.js + uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: pathfinder-app/package-lock.json + + - name: Install dependencies + working-directory: pathfinder-app + run: npm ci + + - name: Build CLI + working-directory: pathfinder-app + run: npm run build:cli + + - name: Validate guides + run: | + echo "🔍 Finding and validating content.json files..." + echo "" + + PASSED=0 + FAILED=0 + + # Find and iterate through each content.json file + while IFS= read -r file; do + if [ -n "$file" ]; then + echo "📄 Validating: $file" + + if node pathfinder-app/dist/cli/cli/index.js validate --strict "$file"; then + PASSED=$((PASSED + 1)) + else + echo "❌ Validation failed for: $file" + echo "" + FAILED=$((FAILED + 1)) + fi + fi + done < <(find . -name "content.json" -type f -not -path "./pathfinder-app/*") + + echo "" + echo "📊 Validation Summary:" + echo " ✅ Passed: $PASSED" + echo " ❌ Failed: $FAILED" + + if [ $FAILED -gt 0 ]; then + echo "" + echo "❌ $FAILED guide(s) failed validation!" + exit 1 + else + echo "" + echo "🎉 All $PASSED guide(s) validated successfully!" + fi From 9d50a2235e731566ff4785718c65ff7d297c4a2d Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 06:58:23 -0500 Subject: [PATCH 29/46] fix failing guides step 1: remove "scroll" --- k8s-cpu/content.json | 5 ----- k8s-mem/content.json | 2 -- 2 files changed, 7 deletions(-) diff --git a/k8s-cpu/content.json b/k8s-cpu/content.json index 1d68763c2..427d8a5be 100644 --- a/k8s-cpu/content.json +++ b/k8s-cpu/content.json @@ -107,7 +107,6 @@ "action": "highlight", "reftarget": "canvas:nth-of-type(2):nth-match(3)", "requirements": ["exists-reftarget"], - "scroll": true, "content": "At the Container detail page, scroll to the **CPU requests sizing** section.\n\nNotice on either side of the gauge, there are two numbers. The number on the left is the currently set CPU requests. The number on the right is the recommended CPU requests. You can use the recommendation to adjust your CPU requests for this container." }, { @@ -115,7 +114,6 @@ "action": "highlight", "reftarget": "section[data-testid='data-testid Panel header CPU throttling']", "requirements": ["exists-reftarget"], - "scroll": true, "content": "Scroll further to view the **CPU throttling** panel.\n\nThis panel shows how much time your container is being artificially slowed down because it's trying to use more CPU than its configured limits allow.\n\nWhy it matters:\n\n- Performance impact: High throttling means your application is running slower than it could, directly affecting user experience.\n- Right-sizing: Helps you identify if CPU limits are too restrictive. You might need to increase them.\n- Cost optimization: If throttling is zero or very low, you might have over-provisioned CPU limits and can reduce costs.\n- Application behavior: Sudden throttling spikes can correlate with slow response times or timeout errors." }, { @@ -123,7 +121,6 @@ "action": "highlight", "reftarget": "section[data-testid='data-testid Panel header Last terminated reason'] > div[data-testid='data-testid panel content']", "requirements": ["exists-reftarget"], - "scroll": true, "content": "Notice the **Last terminated reason** panel.\n\nThis view tells you exactly why Kubernetes last stopped the container, giving you clues to resolve instability.\n\n- **Root cause:** Last termination reason points to the underlying trigger (OOMKilled, CrashLoopBackOff, and so on).\n- **Resilience:** Helps you fix recurring issues before they cascade into bigger outages.\n- **Alerting:** Use these reasons to fine-tune alerts and spot regressions faster." }, { @@ -131,7 +128,6 @@ "action": "highlight", "reftarget": "section[data-testid='data-testid Panel header Container restart history'] > div[data-testid='data-testid panel content']", "requirements": ["exists-reftarget"], - "scroll": true, "content": "Review the **Container restart history** panel.\n\nThis timeline shows how often the container has restarted, helping you detect flapping workloads.\n\n- **Stability check:** Frequent restarts indicate crashing code, resource starvation, or readiness issues.\n- **Trend insight:** Recent spikes can pinpoint deployments or config changes that destabilized the service.\n- **Mitigation planning:** Correlate restarts with logs and metrics to prioritize fixes and improve uptime." }, { @@ -139,7 +135,6 @@ "action": "highlight", "reftarget": "section[data-testid='data-testid Panel header Container restarts'] > div[data-testid='data-testid panel content']", "requirements": ["exists-reftarget"], - "scroll": true, "content": "Examine the **Container restarts** panel for a point-in-time view of restart counts.\n\n- **Health indicator:** High restart counts signal unstable workloads that need investigation.\n- **Operational visibility:** Provides a quick glance at which containers are repeatedly crashing.\n- **Actionable data:** Use the restart tally to correlate with logs and metrics for faster remediation." } ] diff --git a/k8s-mem/content.json b/k8s-mem/content.json index ebba2904e..566f2c2fd 100644 --- a/k8s-mem/content.json +++ b/k8s-mem/content.json @@ -129,7 +129,6 @@ "action": "highlight", "reftarget": "canvas:nth-of-type(2):nth-match(2)", "requirements": ["exists-reftarget"], - "scroll": true, "content": "At the Container detail page, scroll to the **Memory sizing limits** section.\n\nThe number on the left side of the gauge is the currently set memory sizing limits. The number on the right is the recommended memory sizing limits. You can use the recommendation to adjust your memory limits for this container.\n\nWithout memory limits, a container can consume all available memory on the node, potentially causing:\n\n- Node-wide OOM events, which can kill other workloads or system components.\n- Pod eviction when the node runs low on memory.\n- Resource starvation for other pods on the node, degrading cluster performance." }, { @@ -137,7 +136,6 @@ "action": "highlight", "reftarget": "canvas:nth-of-type(2):nth-match(4)", "requirements": ["exists-reftarget"], - "scroll": true, "content": "Next, focus on the **Memory requests sizing** panel.\n\nThe number on the left side of the gauge is the currently set memory requests. The number on the right is the recommended setting for memory requests. You can use the recommendation to adjust your memory requests for this container.\n\nWhen requests are too high, it can cause:\n\n- Wasted capacity: Kubernetes reserves more memory than actually needed, leaving nodes artificially \"full\" even when memory sits unused.\n- Scheduling failures: Pods can't be scheduled even when nodes have available memory, because the scheduler sees insufficient requestable capacity.\n- Higher costs: You need more nodes to run the same workload due to over-reservation.\n\nWhen requests are too low, it can cause:\n\n- Pod evictions: Under memory pressure, Kubernetes evicts pods exceeding their requests first—your pod becomes a target even when running normally.\n- Unpredictable performance: The kubelet may throttle or kill processes on memory-pressured nodes, causing instability.\n- Wrong QoS class: Without appropriate limits, pods drop into BestEffort (first to be evicted) instead of Burstable or Guaranteed." } ] From ba8469073c9fa85a2991f5711e8d5371d8d2dfbd Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:07:01 -0500 Subject: [PATCH 30/46] fix guides step 2: remove "hint" --- logql-101/content.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/logql-101/content.json b/logql-101/content.json index 4c10c6306..1a0d690ee 100644 --- a/logql-101/content.json +++ b/logql-101/content.json @@ -50,7 +50,6 @@ "id": "first-query", "title": "Write Your First LogQL Query", "requirements": ["section-completed:select-datasource"], - "hint": "Learn to write basic LogQL queries using label selectors", "blocks": [ { "type": "interactive", @@ -139,7 +138,6 @@ "id": "json-query", "title": "Use JSON parsing for precise filtering", "requirements": ["section-completed:first-query"], - "hint": "Learn to use JSON parsing for more precise log filtering", "blocks": [ { "type": "interactive", From 9c81cf117f252e2b5fdbc82b4e7f10407e29a1e8 Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:12:10 -0500 Subject: [PATCH 31/46] fixing guides: IRM --- irm-configuration/content.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/irm-configuration/content.json b/irm-configuration/content.json index a4de01f56..c8cd25824 100644 --- a/irm-configuration/content.json +++ b/irm-configuration/content.json @@ -12,9 +12,7 @@ }, { "type": "guided", - "id": "create-schedule", "content": "Create a new, empty on-call schedule.", - "reftarget": "a[data-testid=\"data-testid Nav menu item\"][href=\"/a/grafana-irm-app/schedules\"]", "requirements": ["navmenu-open", "exists-reftarget"], "stepTimeout": 45000, "steps": [ @@ -78,9 +76,7 @@ }, { "type": "guided", - "id": "create-escalation-chain", "content": "Create a new escalation chain.", - "reftarget": "a[data-testid=\"data-testid Nav menu item\"][href=\"/a/grafana-irm-app/escalations\"]", "requirements": ["navmenu-open", "exists-reftarget"], "stepTimeout": 45000, "steps": [ @@ -158,7 +154,6 @@ { "type": "guided", "content": "Create a new Grafana Alerting integration.", - "reftarget": "a[data-testid=\"data-testid Nav menu item\"][href=\"/a/grafana-irm-app/integrations\"]", "requirements": ["navmenu-open", "exists-reftarget"], "stepTimeout": 45000, "steps": [ From 23f564e6f9df839fc09b0e56f9cc966830419a6a Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:16:37 -0500 Subject: [PATCH 32/46] no such action "click", you want "highlight" --- logql-101/content.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logql-101/content.json b/logql-101/content.json index 1a0d690ee..9c25feee2 100644 --- a/logql-101/content.json +++ b/logql-101/content.json @@ -70,7 +70,7 @@ }, { "type": "interactive", - "action": "click", + "action": "highlight", "reftarget": "button[aria-label=\"Run query\"]", "requirements": ["exists-reftarget"], "tooltip": "The **Run query** button executes your LogQL query and displays the results. This is how you see the actual log data that matches your query criteria.", @@ -100,7 +100,7 @@ }, { "type": "interactive", - "action": "click", + "action": "highlight", "reftarget": "button[data-testid=\"data-testid RefreshPicker run button\"]", "requirements": ["exists-reftarget"], "tooltip": "Now let's execute this refined query to see the filtered results.", From 6d2a1f93226b1cfeff489c2868bd23eb8317c0ff Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:16:45 -0500 Subject: [PATCH 33/46] tell agent about schema validation --- .cursor/best-practices.mdc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.cursor/best-practices.mdc b/.cursor/best-practices.mdc index 6ba1f4ecd..8e1d4ac22 100644 --- a/.cursor/best-practices.mdc +++ b/.cursor/best-practices.mdc @@ -69,6 +69,12 @@ Provide alternative workflows for different user permissions. * Use `multistep` for related actions that should happen together * Include section completion requirements for dependencies +## Schema Validity + +In our CI system, @.github/workflows/validate-json.yml we have strict schema +checking for guides. When you need, you can use this tool locally by scripting +the equivalent, to rigorously check the structure and syntax of a JSON guide. + ### Content Quality * Keep interactive comments (`tooltip`) under 250 characters From a4b885f3634d7e23dc9a528c0da528c8d4aa04ec Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:23:57 -0500 Subject: [PATCH 34/46] click -> highlight --- logql-101/content.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logql-101/content.json b/logql-101/content.json index 9c25feee2..bdfd6788d 100644 --- a/logql-101/content.json +++ b/logql-101/content.json @@ -121,7 +121,7 @@ }, { "type": "interactive", - "action": "click", + "action": "highlight", "reftarget": "button[data-testid=\"data-testid RefreshPicker run button\"]", "requirements": ["exists-reftarget"], "tooltip": "Now let's execute this combined query to see the filtered results that contain \"500\" but not \"200\".", @@ -150,7 +150,7 @@ }, { "type": "interactive", - "action": "click", + "action": "highlight", "reftarget": "button[data-testid=\"data-testid RefreshPicker run button\"]", "requirements": ["exists-reftarget"], "tooltip": "Now let's execute this JSON-based query to see the precise HTTP 500 error logs.", From 0209af27bae0021c73078d23a6c378abfd11a3eb Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:25:45 -0500 Subject: [PATCH 35/46] clarification --- .cursor/action-types-reference.mdc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.cursor/action-types-reference.mdc b/.cursor/action-types-reference.mdc index b6e3f655b..8a4e2145c 100644 --- a/.cursor/action-types-reference.mdc +++ b/.cursor/action-types-reference.mdc @@ -12,6 +12,9 @@ alwaysApply: true **Do**: Scrolls to element, then clicks **Requirements**: Always include `exists-reftarget` +For regular clicks on buttons by selectors, use this type. +For clicking on buttons _based on their inner text_ prefer `button` type. + ```json { "type": "interactive", @@ -23,12 +26,15 @@ alwaysApply: true ``` ### button -**Purpose**: Click buttons using visible text +**Purpose**: Click buttons _using visible text_ **Target**: Button text (exact or partial match) **Show**: Finds and highlights button by text **Do**: Finds and clicks button by text **Advantages**: More stable than CSS selectors +The button type is **not to be used** if clicking buttons by DOM selector; +only by text. Prefer the use of `highlight` for clicking by DOM selectors. + ```json { "type": "interactive", From 5af306fc8e5d9e7932a0ea3d41d004910e544dd9 Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:35:20 -0500 Subject: [PATCH 36/46] "tooltip" isn't valid on multistep --- logql-101/content.json | 1 - 1 file changed, 1 deletion(-) diff --git a/logql-101/content.json b/logql-101/content.json index bdfd6788d..55af4bfc3 100644 --- a/logql-101/content.json +++ b/logql-101/content.json @@ -26,7 +26,6 @@ { "type": "multistep", "requirements": ["exists-reftarget"], - "tooltip": "The **data source picker** allows you to select which Loki data source to query. In Grafana, you can have multiple Loki instances configured (e.g., production logs, development logs, different regions). The **Loki Demo Kit (loki_nginx)** data source contains sample logs that we'll use for this tutorial. Selecting this data source will connect you to the specific Loki instance where you can explore and query logs.", "content": "Click on the **data source picker**, then select the **Loki Demo Kit (loki_nginx)** data source.", "steps": [ { From 10e5d6cbc92f82e97dc24dcce33c743d922d40e1 Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:42:06 -0500 Subject: [PATCH 37/46] "comment" -> "tooltip" --- sm-setting-up-your-first-check/content.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sm-setting-up-your-first-check/content.json b/sm-setting-up-your-first-check/content.json index e7a817bea..1b4ad092d 100644 --- a/sm-setting-up-your-first-check/content.json +++ b/sm-setting-up-your-first-check/content.json @@ -62,7 +62,7 @@ "reftarget": "input[data-testid='checkEditor form job']", "targetvalue": "Grafana: HTTP Check", "requirements": ["exists-reftarget"], - "comment": "All your logs and metrics generated by this check will contain a **job** label with this value.", + "tooltip": "All your logs and metrics generated by this check will contain a **job** label with this value.", "content": "Enter **Grafana: HTTP Check** in the Job Name field." }, { @@ -71,7 +71,7 @@ "reftarget": "input[data-testid='checkEditor form instance']", "targetvalue": "https://grafana.com", "requirements": ["exists-reftarget"], - "comment": "They will also contain an **instance** label with this value.", + "tooltip": "They will also contain an **instance** label with this value.", "content": "Set **URL: https://grafana.com**." } ] @@ -90,7 +90,7 @@ "action": "highlight", "reftarget": "button[data-testid='checkEditor navigation uptime']", "requirements": ["exists-reftarget"], - "comment": "This is the form stepper for configuring your check. You can freely move between sections whenever you want.", + "tooltip": "This is the form stepper for configuring your check. You can freely move between sections whenever you want.", "content": "Click **Uptime** to move to the next section of the check configuration." }, { @@ -99,7 +99,7 @@ "reftarget": "input[data-testid='checkEditor form validStatusCodes']", "doIt": false, "skippable": true, - "comment": "Status codes in the 2xx range are the default, so we can skip this step.", + "tooltip": "Status codes in the 2xx range are the default, so we can skip this step.", "content": "Set **Valid status codes** if required." }, { @@ -114,7 +114,7 @@ "reftarget": "[data-testid='checkEditor genericLabelContent']", "skippable": true, "doIt": false, - "comment": "This is optional; but if you have many checks, this can help you organize them and route your alerts to the right team. Common labels include 'environment', 'service', and 'team' but you are free to use any label you want.", + "tooltip": "This is optional; but if you have many checks, this can help you organize them and route your alerts to the right team. Common labels include 'environment', 'service', and 'team' but you are free to use any label you want.", "content": "Set label names and values if required." } ] From 72b1042a782d661198e3272e0489b1554cf7b5d7 Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:43:31 -0500 Subject: [PATCH 38/46] comment -> tooltip --- sm-setting-up-your-first-check/content.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sm-setting-up-your-first-check/content.json b/sm-setting-up-your-first-check/content.json index 1b4ad092d..f691fabb0 100644 --- a/sm-setting-up-your-first-check/content.json +++ b/sm-setting-up-your-first-check/content.json @@ -138,7 +138,7 @@ "type": "interactive", "action": "highlight", "reftarget": "label[data-testid='checkEditor form probeLabel']:first-of-type", - "comment": "There are multiple probe locations to choose from. It is good practice to run checks from multiple locations to ensure the check is reliable.", + "tooltip": "There are multiple probe locations to choose from. It is good practice to run checks from multiple locations to ensure the check is reliable.", "content": "Select a **probe location** to run the check from." }, { @@ -147,7 +147,7 @@ "reftarget": "input[id^='option-60000-radiogroup-']", "skippable": true, "doIt": false, - "comment": "The default probe frequency is 1 minute which is suitable for most checks. Less frequent means fewer executions, but longer lag times before an alert notifying you of an issue.", + "tooltip": "The default probe frequency is 1 minute which is suitable for most checks. Less frequent means fewer executions, but longer lag times before an alert notifying you of an issue.", "content": "Set the desired probe frequency." } ] @@ -171,7 +171,7 @@ "type": "interactive", "action": "highlight", "reftarget": "input[data-testid='checkEditor alerts ProbeFailedExecutionsTooHigh selectedCheckbox']", - "comment": "The failed check alert will be triggered if the check fails more than the configured number of times.", + "tooltip": "The failed check alert will be triggered if the check fails more than the configured number of times.", "content": "Click the checkbox to enable the failed check alert." }, { From fa647ec62442127f8bf6cd88b7c095b467c9d45b Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 07:55:20 -0500 Subject: [PATCH 39/46] add canvas panel to the tour of visualizations --- tour-of-visualizations/content.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tour-of-visualizations/content.json b/tour-of-visualizations/content.json index 1299646fa..432b1653b 100644 --- a/tour-of-visualizations/content.json +++ b/tour-of-visualizations/content.json @@ -184,6 +184,23 @@ } ] }, + { + "type": "section", + "id": "custom-layouts", + "title": "Custom Layouts", + "blocks": [ + { + "type": "markdown", + "content": "When standard panels aren't enough—design your own." + }, + { + "type": "interactive", + "action": "navigate", + "reftarget": "/d/c9ea65f5-ed5a-45cf-8fb7-f82af7c3afdf/canvas-visualization", + "content": "**Canvas** — Your blank slate. Place shapes, icons, images, and metrics anywhere you want. Perfect for network diagrams, floor plans, custom status boards, or any layout that doesn't fit a traditional chart." + } + ] + }, { "type": "markdown", "content": "**What you have learned:** Grafana's panel library—each one designed for a specific job. Pick the right one, and your data tells its own story. Now go build something awesome!" From 9b576f673cde3a7154748f0233afaf874969be93 Mon Sep 17 00:00:00 2001 From: David Allen Date: Fri, 12 Dec 2025 08:26:25 -0500 Subject: [PATCH 40/46] tour of viz only appears on viz pages --- index.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.json b/index.json index 9fef71c43..4dcec690a 100644 --- a/index.json +++ b/index.json @@ -7,6 +7,9 @@ "type": "docs-page", "match": { "and": [ + { + "urlPrefix": "/d/ma79mqp/visualization-examples" + }, { "source": "play.grafana.org" } From 1cdf0534c89890c2dcbcf359acc25b5c4355b050 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 13 Dec 2025 09:26:07 -0600 Subject: [PATCH 41/46] add directory, file and image --- k8s-network/ntwkpanels.png | Bin 0 -> 286827 bytes k8s-network/unstyled.html | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 k8s-network/ntwkpanels.png create mode 100644 k8s-network/unstyled.html diff --git a/k8s-network/ntwkpanels.png b/k8s-network/ntwkpanels.png new file mode 100644 index 0000000000000000000000000000000000000000..7a1208c58897417e7b3ef6aac1abf03b4521d996 GIT binary patch literal 286827 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

    0b}$( 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%0DyB9h72?Aj`S2i~2 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

    WEBjo% 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 ivsb8zJ%k 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 b8?$zM-|ZO3SHSCqfX~9z7FBijO7y=0Cu~*v literal 0 HcmV?d00001 diff --git a/k8s-network/unstyled.html b/k8s-network/unstyled.html new file mode 100644 index 000000000..e69de29bb From 2b2308f6dd777a503da69588ff272d0e3106c6df Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 13 Dec 2025 11:26:47 -0600 Subject: [PATCH 42/46] add tutorial --- index.json | 16 +++ k8s-network/content.json | 198 ++++++++++++++++++++++++++++++++++++++ k8s-network/unstyled.html | 82 ++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 k8s-network/content.json diff --git a/index.json b/index.json index 4dcec690a..ccaeb2a51 100644 --- a/index.json +++ b/index.json @@ -182,6 +182,22 @@ } ] } + }, + { + "title": "Interactive Guide: Network Diagnostics in Kubernetes", + "url": "https://interactive-learning.grafana.net/guides/k8s-network", + "description": "Hands-on guide: Diagnose network issues in Kubernetes Monitoring using bandwidth and saturation metrics.", + "type": "docs-page", + "match": { + "and": [ + { + "urlPrefix": "/a/grafana-k8s-app" + }, + { + "source": "play.grafana.org" + } + ] + } } ] } diff --git a/k8s-network/content.json b/k8s-network/content.json new file mode 100644 index 000000000..16c135e90 --- /dev/null +++ b/k8s-network/content.json @@ -0,0 +1,198 @@ +{ + "id": "k8s-network", + "title": "Network diagnostics in Kubernetes Monitoring", + "blocks": [ + { + "type": "markdown", + "content": "# Diagnose network issues in Kubernetes Monitoring\n\nNetwork performance directly impacts application responsiveness, reliability, and user experience. When applications are slow or experience connectivity issues, network metrics can help you determine whether the network is the bottleneck or if issues lie elsewhere.\n\n### What you will learn\n\n- Navigate to the Network tab and understand network bandwidth and saturation metrics.\n- Identify network performance issues like high traffic, packet loss, and saturation.\n- Use the pod and interface breakdown views to pinpoint which workloads are consuming bandwidth or experiencing problems.\n- Apply diagnostic workflows to troubleshoot common network scenarios." + }, + { + "type": "section", + "id": "namespace-network-overview", + "title": "Namespace network overview", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Nav menu item'][href='/a/grafana-k8s-app/navigation/namespace']", + "requirements": ["exists-reftarget"], + "content": "Click **Namespaces** in the main menu to view the list of namespaces in your environment." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "Select the first namespace in the list to open its detail view." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Tab Network']", + "requirements": ["exists-reftarget"], + "content": "Click the **Network** tab to view network performance metrics for this namespace.\n\nThe Network tab provides visibility into network bandwidth usage and saturation (packet loss) across your namespace." + } + ] + }, + { + "type": "section", + "id": "network-bandwidth-panel", + "title": "Understanding Network Bandwidth", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Network Bandwidth** panel shows how much data is flowing in and out of your namespace over time.\n\n- **Rx (white/gray line)**: Receiving traffic - data coming into the namespace\n- **Tx (blue line)**: Transmitting traffic - data going out of the namespace\n\nThe table below the graph shows Min, 90th percentile, and Max values to help you understand traffic patterns." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth'] div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "**How to interpret the patterns:**\n\n- **Steady traffic**: Indicates consistent workload behavior\n- **Regular spikes**: Often normal. Spikescould be scheduled jobs, health checks, or metrics collection\n- **Sudden sustained increase**: May indicate new load, deployments (image pulls), or potential issues\n- **Unexpected drops**: Could signal pod crashes, network partitions, or configuration problems\n- **Asymmetric Rx/Tx**: Large differences are normal for certain workloads like log shipping or data ingestion" + } + ] + }, + { + "type": "section", + "id": "network-saturation-panel", + "title": "Understanding Network Saturation", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Saturation']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Network Saturation** panel shows whether your network is being overwhelmed by displaying dropped packets.\n\n- **Rx dropped packets**: Incoming packets that couldn't be processed\n- **Tx dropped packets**: Outgoing packets that couldn't be sent\n\nMeasured in packets per second (p/s)." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Saturation'] div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "**Why this matters:**\n\n- **Zero is ideal**: No dropped packets means the network has sufficient capacity\n- **Non-zero values**: Indicate network saturation - the network cannot handle current traffic volume\n- **User impact**: Dropped packets cause application timeouts, retries, slow performance, and failed requests\n- **Root cause indicator**: Helps determine if network capacity is the bottleneck\n\n**Action**: If you see dropped packets, consider increasing network limits, optimizing traffic patterns, or implementing network QoS policies." + } + ] + }, + { + "type": "section", + "id": "network-by-pod", + "title": "Network Bandwidth by pod", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth by pod']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Network Bandwidth by pod** panel breaks down network traffic by individual pods.\n\nThis stacked visualization shows:\n- Which pods are consuming the most bandwidth\n- How each pod contributes to total namespace traffic\n- Traffic patterns for specific workloads over time" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth by pod'] div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "**Use this panel to:**\n\n- **Identify noisy neighbors**: Find which pods consume excessive bandwidth\n- **Troubleshoot performance**: Correlate high traffic pods with application slowness\n- **Capacity planning**: Understand which services need network resources\n- **Detect anomalies**: Spot pods with unusual traffic patterns\n- **Resource allocation**: Determine if specific pods need network limits or QoS\n\nClick on pod names in the legend to view individual pod traffic or review the table below for Min/90th percentile/Max statistics per pod." + } + ] + }, + { + "type": "section", + "id": "saturation-by-pod", + "title": "Network Saturation by pod", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Saturation by pod']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Network Saturation by pod** panel shows packet loss for each individual pod.\n\nThis helps you pinpoint exactly which pod is experiencing network congestion rather than just knowing the namespace has issues." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Saturation by pod'] div[data-testid='data-testid panel content']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "**Diagnostic workflow:**\n\n1. If you see dropped packets in the namespace-level saturation panel, check this panel to identify which specific pod is affected\n2. Compare dropped packets against bandwidth - high bandwidth + dropped packets = capacity issue\n3. Zero dropped packets across all pods = healthy network configuration\n\n**Common scenarios:**\n- **One pod dropping packets**: That pod's network limits may be too restrictive\n- **Multiple pods dropping packets**: Namespace or node-level network saturation\n- **Intermittent drops**: May indicate temporary bursts exceeding capacity" + } + ] + }, + { + "type": "section", + "id": "pod-network-detail", + "title": "Pod-level network details", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "div[role='gridcell'][aria-colindex='1'] a[data-testid='data-testid Data link']:nth-match(1)", + "requirements": ["exists-reftarget"], + "content": "To investigate a specific pod's network performance, select the first pod in the list.\n\nThis drills down to pod-level details where you can see network metrics for individual containers and interfaces." + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "a[data-testid='data-testid Tab Network']", + "requirements": ["exists-reftarget"], + "content": "Click the **Network** tab to view the pod's network performance.\n\nAt the pod level, you'll see the same four panels but specific to this pod:\n- Network Bandwidth (pod level)\n- Network Saturation (pod level)\n- Network Bandwidth by interface (eth0, etc.)\n- Network Saturation by interface" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "Review the pod's **Network Bandwidth** to understand its traffic patterns.\n\n**Traffic scale matters:**\n- Traffic in **KiB/s** or **B/s**: Low-bandwidth workload (monitoring agents, sidecars)\n- Traffic in **MiB/s**: Moderate workload (typical application traffic)\n- Traffic in **GiB/s**: High-bandwidth workload (data processing, large file transfers)\n\n**Pattern recognition:**\n- **Minimal baseline with periodic spikes**: Often metrics reporting, log shipping, or health checks\n- **Sustained high traffic**: Data-intensive operations\n- **Regular spike pattern**: Scheduled operations or batch jobs" + } + ] + }, + { + "type": "section", + "id": "network-by-interface", + "title": "Network by interface", + "requirements": ["navmenu-open", "on-page:/a/grafana-k8s-app"], + "blocks": [ + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth by interface']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Network Bandwidth by interface** panel shows traffic broken down by network interface (typically eth0).\n\n**When this is useful:**\n- Pods with multiple network interfaces (service mesh, CNI plugins)\n- Identifying if a specific interface has issues\n- Troubleshooting network plugin or CNI problems\n- Understanding traffic routing through different interfaces" + }, + { + "type": "interactive", + "action": "highlight", + "reftarget": "section[data-testid='data-testid Panel header Network Saturation by interface']", + "requirements": ["exists-reftarget"], + "doIt": false, + "content": "The **Network Saturation by interface** panel shows dropped packets per network interface.\n\n**Diagnostic value:**\n- Identifies if packet loss is interface-specific\n- Helps isolate hardware or driver issues\n- Useful for troubleshooting CNI plugin configuration\n- Can reveal if one interface is overloaded while others are healthy" + } + ] + }, + { + "type": "markdown", + "content": "## Common diagnostic scenarios\n\n### Scenario 1: Application is slow\n\n**Workflow:**\n1. Check namespace Network Bandwidth - is traffic unusually high?\n2. Check Network Saturation - are packets being dropped?\n3. Review Network Bandwidth by pod - is one pod consuming excessive bandwidth?\n4. If saturation is high, increase network limits or optimize data transfer\n5. If saturation is zero, the issue is likely application-level, not network\n\n### Scenario 2: Intermittent connectivity issues\n\n**Workflow:**\n1. Look for periodic drops in bandwidth (indicates flapping)\n2. Check for saturation spikes (temporary overload)\n3. Review by interface view - is a specific interface failing?\n4. Investigate pod restarts, network policy changes, or infrastructure issues\n\n### Scenario 3: High network costs\n\n**Workflow:**\n1. Check total bandwidth over time to identify trends\n2. Use by pod breakdown to find data-intensive services\n3. Compare Rx vs Tx - are you sending/receiving more than expected?\n4. Optimize data transfers, implement caching, or review service mesh overhead\n\n### Scenario 4: New deployment causing issues\n\n**Workflow:**\n1. Check for bandwidth spike during deployment (image pulls, init containers)\n2. Compare traffic before/after deployment\n3. Check if saturation occurred (did deployment cause network stress?)\n4. Consider staggering deployments, pre-pulling images, or increasing capacity" + }, + { + "type": "markdown", + "content": "### What you have learned\n\nYou have learned how to:\n\n- Navigate to network metrics at the namespace and pod level.\n- Interpret Network Bandwidth panels to understand traffic patterns and identify anomalies.\n- Use Network Saturation panels to detect packet loss and network capacity issues.\n- Drill down from namespace to pod to interface to isolate network problems.\n- Apply diagnostic workflows to troubleshoot common network performance scenarios.\n- Distinguish between network-level issues and application-level problems.\n\n**Next steps:**\n- Establish baseline patterns for your namespaces and pods\n- Set up alerts for non-zero dropped packets\n- Correlate network metrics with CPU, memory, and application metrics\n- Document normal traffic patterns for capacity planning" + } + ] +} + diff --git a/k8s-network/unstyled.html b/k8s-network/unstyled.html index e69de29bb..9151979ed 100644 --- a/k8s-network/unstyled.html +++ b/k8s-network/unstyled.html @@ -0,0 +1,82 @@ + + + + + + Network diagnostics in Kubernetes + + +

    Network diagnostics in Kubernetes Monitoring

    + +

    + Network performance directly impacts application responsiveness, reliability, and user experience. + When applications are slow or experience connectivity issues, network metrics help you quickly determine + whether the network is the bottleneck or if issues lie elsewhere. +

    + +

    What you will learn

    +
      +
    • Navigate to the Network tab and understand network bandwidth and saturation metrics.
    • +
    • Identify network performance issues like high traffic, packet loss, and saturation.
    • +
    • Use the pod and interface breakdown views to pinpoint which workloads are consuming bandwidth or experiencing problems.
    • +
    • Apply diagnostic workflows to troubleshoot common network scenarios.
    • +
    + +

    Key concepts

    + +

    Network Bandwidth

    +

    + Network bandwidth shows how much data is flowing in and out of your workloads: +

    +
      +
    • Rx (Receive): Incoming traffic - data coming into the namespace/pod
    • +
    • Tx (Transmit): Outgoing traffic - data going out of the namespace/pod
    • +
    + +

    Network Saturation

    +

    + Network saturation indicates whether your network is overwhelmed by showing dropped packets. + Zero dropped packets is ideal - any non-zero value indicates network stress that can cause + application timeouts, retries, and performance degradation. +

    + +

    Diagnostic scenarios

    + +

    Scenario 1: Application is slow

    +
      +
    1. Check namespace Network Bandwidth - is traffic unusually high?
    2. +
    3. Check Network Saturation - are packets being dropped?
    4. +
    5. Review Network Bandwidth by pod - is one pod consuming excessive bandwidth?
    6. +
    7. If saturation is high, increase network limits or optimize data transfer
    8. +
    9. If saturation is zero, the issue is likely application-level, not network
    10. +
    + +

    Scenario 2: Intermittent connectivity issues

    +
      +
    1. Look for periodic drops in bandwidth (indicates flapping)
    2. +
    3. Check for saturation spikes (temporary overload)
    4. +
    5. Review by interface view - is a specific interface failing?
    6. +
    7. Investigate pod restarts, network policy changes, or infrastructure issues
    8. +
    + +

    Scenario 3: High network costs

    +
      +
    1. Check total bandwidth over time to identify trends
    2. +
    3. Use by pod breakdown to find data-intensive services
    4. +
    5. Compare Rx vs Tx - are you sending/receiving more than expected?
    6. +
    7. Optimize data transfers, implement caching, or review service mesh overhead
    8. +
    + +

    What you will learn

    +

    By the end of this guide, you will be able to:

    +
      +
    • Navigate to network metrics at the namespace and pod level
    • +
    • Interpret Network Bandwidth panels to understand traffic patterns and identify anomalies
    • +
    • Use Network Saturation panels to detect packet loss and network capacity issues
    • +
    • Drill down from namespace to pod to interface to isolate network problems
    • +
    • Apply diagnostic workflows to troubleshoot common network performance scenarios
    • +
    • Distinguish between network-level issues and application-level problems
    • +
    + + + From ae915f978e380d8bb0c5d4f6f19bdbd5efce1618 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sat, 13 Dec 2025 11:29:27 -0600 Subject: [PATCH 43/46] fix typo in content.json regarding 'spikes' for clarity --- k8s-network/content.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s-network/content.json b/k8s-network/content.json index 16c135e90..a1ed31592 100644 --- a/k8s-network/content.json +++ b/k8s-network/content.json @@ -55,7 +55,7 @@ "reftarget": "section[data-testid='data-testid Panel header Network Bandwidth'] div[data-testid='data-testid panel content']", "requirements": ["exists-reftarget"], "doIt": false, - "content": "**How to interpret the patterns:**\n\n- **Steady traffic**: Indicates consistent workload behavior\n- **Regular spikes**: Often normal. Spikescould be scheduled jobs, health checks, or metrics collection\n- **Sudden sustained increase**: May indicate new load, deployments (image pulls), or potential issues\n- **Unexpected drops**: Could signal pod crashes, network partitions, or configuration problems\n- **Asymmetric Rx/Tx**: Large differences are normal for certain workloads like log shipping or data ingestion" + "content": "**How to interpret the patterns:**\n\n- **Steady traffic**: Indicates consistent workload behavior\n- **Regular spikes**: Often normal. Spikes could be scheduled jobs, health checks, or metrics collection\n- **Sudden sustained increase**: May indicate new load, deployments (image pulls), or potential issues\n- **Unexpected drops**: Could signal pod crashes, network partitions, or configuration problems\n- **Asymmetric Rx/Tx**: Large differences are normal for certain workloads like log shipping or data ingestion" } ] }, From c69da169072c251e569cd25b5a1f85a8dc4018e5 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sun, 14 Dec 2025 10:00:12 -0600 Subject: [PATCH 44/46] changes from review --- k8s-network/unstyled.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/k8s-network/unstyled.html b/k8s-network/unstyled.html index 9151979ed..704c5cf18 100644 --- a/k8s-network/unstyled.html +++ b/k8s-network/unstyled.html @@ -67,6 +67,14 @@

    Scenario 3: High network costs

  • Optimize data transfers, implement caching, or review service mesh overhead
  • +

    Scenario 4: New deployment causing issues

    +
      +
    1. Check for bandwidth spike during deployment (image pulls, init containers)
    2. +
    3. Compare traffic before/after deployment
    4. +
    5. Check if saturation occurred (did deployment cause network stress?)
    6. +
    7. Consider staggering deployments, pre-pulling images, or increasing capacity
    8. +
    +

    What you will learn

    By the end of this guide, you will be able to:

      From 74f67b30458cc5413e50bfcd3770c213932226ab Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sun, 14 Dec 2025 10:00:48 -0600 Subject: [PATCH 45/46] more fixes --- k8s-network/unstyled.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/k8s-network/unstyled.html b/k8s-network/unstyled.html index 704c5cf18..98b5132c2 100644 --- a/k8s-network/unstyled.html +++ b/k8s-network/unstyled.html @@ -75,7 +75,7 @@

      Scenario 4: New deployment causing issues

    • Consider staggering deployments, pre-pulling images, or increasing capacity
    • -

      What you will learn

      +

      What you have learned

      By the end of this guide, you will be able to:

      • Navigate to network metrics at the namespace and pod level
      • From 210ff3cccf68da541cc88474b6c1adefb8d2d378 Mon Sep 17 00:00:00 2001 From: BeverlyJaneJ Date: Sun, 14 Dec 2025 16:38:27 -0600 Subject: [PATCH 46/46] add some content --- k8s-network/unstyled.html | 73 +++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/k8s-network/unstyled.html b/k8s-network/unstyled.html index 98b5132c2..1a9276b14 100644 --- a/k8s-network/unstyled.html +++ b/k8s-network/unstyled.html @@ -3,15 +3,16 @@ - Network diagnostics in Kubernetes + Network usage trends in Kubernetes -

        Network diagnostics in Kubernetes Monitoring

        +

        Recognizing network performance trends

        Network performance directly impacts application responsiveness, reliability, and user experience. - When applications are slow or experience connectivity issues, network metrics help you quickly determine - whether the network is the bottleneck or if issues lie elsewhere. + When applications are slow or experience connectivity issues, network metrics can help you determine + whether the network is the bottleneck or if the issues caused by something else. + Another statement goes here about planning for capacity and forecasting for scaling the cluster or reallocating resources.

        What you will learn

        @@ -21,9 +22,69 @@

        What you will learn

      • Use the pod and interface breakdown views to pinpoint which workloads are consuming bandwidth or experiencing problems.
      • Apply diagnostic workflows to troubleshoot common network scenarios.
      - -

      Key concepts

      + +

      Cluster network details

      + +
        +
      • + data-targetaction="highlight" + data-requirements="exists-reftarget" + > + Click Clusters in the main menu to view the list of + Clusters in your environment. +
      • + +
      • + data-targetaction="highlight" + data-requirements="exists-reftarget" + > + Select the first Cluster in the list to open its detail view. +
      • + +
          +
        • + Navigate to the Network tab for more detail on bandwidth and saturation. +
        • +
        +
        + +
      • +

        + Notice **Network Bandwidth** shows how much data is flowing in and out of your workloads: +

        +
          +
        • Rx (Receive): Incoming traffic, which is data coming into the namespace/pod
        • +
        • Tx (Transmit): Outgoing traffic, which is data going out of the namespace/pod
        • +
        +
      • +
      +
      +

      Network details

      + +

      Network Bandwidth

      +

      + Network bandwidth shows how much data is flowing in and out of your workloads: +

      +

      Network Bandwidth

      Network bandwidth shows how much data is flowing in and out of your workloads: