Skip to content

Improve evals - #709

Merged
aantn merged 23 commits into
masterfrom
improve-kubernetes-scheduling-scenarios
Jul 25, 2025
Merged

aantn merged 23 commits into
masterfrom
improve-kubernetes-scheduling-scenarios

Conversation

@aantn

@aantn aantn commented Jul 25, 2025

Copy link
Copy Markdown
Collaborator

No description provided.

@coderabbitai

coderabbitai Bot commented Jul 25, 2025 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@aantn has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 3 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 1d690d0 and 22d11d4.

📒 Files selected for processing (8)
  • .github/workflows/llm-evaluation.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/50_logs_since_specific_date/test_case.yaml (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/56_kafka_runbook_no_tool/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/60_count_less_than/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml (1 hunks)
## Walkthrough

This update introduces a large set of new and revised Kubernetes troubleshooting test cases, manifests, and log generators for an LLM evaluation framework. It adds detailed runbook documentation for pod scheduling failures and replica mismatches, expands the pytest marker set, and updates or creates numerous YAML fixtures, manifests, and Python scripts to simulate realistic Kubernetes scenarios, resource constraints, and log patterns.

## Changes

| File(s)                                                                                                 | Change Summary                                                                                                              |
|---------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|
| CLAUDE.md, pyproject.toml                                                                               | Updated test marker documentation: removed `k8s-misconfig` from docs; added `leaked-information` marker to pytest config.   |
| holmes/plugins/runbooks/catalog.json                                                                    | Added two new runbook entries for Kubernetes pod scheduling failures and replica mismatch troubleshooting.                   |
| holmes/plugins/runbooks/kubernetes/pod_scheduling_failures_instructions.md                              | Added comprehensive runbook for diagnosing and remediating Kubernetes pod scheduling failures.                              |
| holmes/plugins/runbooks/kubernetes/replica_mismatch_troubleshooting.md                                  | Added detailed runbook for troubleshooting Kubernetes replica mismatch issues.                                              |
| tests/llm/fixtures/test_ask_holmes/14_pending_resources/test_case.yaml                                  | Added "easy" and "kubernetes" tags to test metadata.                                                                        |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml                         | Simplified expected output to directly state DNS failure cause; removed step-by-step instructions.                          |
| tests/llm/fixtures/test_ask_holmes/47_truncated_logs_context_window/manifest.yaml                       | Reduced memory and CPU requests/limits for `long-logs-app` container.                                                       |
| tests/llm/fixtures/test_ask_holmes/50_logs_since_specific_date/test_case.yaml                           | Removed comments, added "easy" tag, updated correctness to 1.                                                               |
| tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/{manifest.yaml,test_case.yaml}              | Reduced resource requests/limits in manifest; added "easy" and "leaked-information" tags in test case; set correctness=1.   |
| tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/{manifest.yaml,test_case.yaml}                  | Reduced resource requests/limits; added "leaked-information" tag and comment in test case.                                   |
| tests/llm/fixtures/test_ask_holmes/53_logs_find_term/{manifest.yaml,test_case.yaml}                     | Reduced resource requests/limits; added "easy" and "leaked-information" tags, updated correctness to 1.                     |
| tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml                    | Swapped script names in setup/teardown; added "easy", "kubernetes", "context_window" tags.                                  |
| tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/manifest.yaml                                     | Increased memory requests/limits, decreased CPU request, removed CPU limit.                                                 |
| tests/llm/fixtures/test_ask_holmes/60_count_less_than/{manifests.yaml,test_case.yaml}                   | Renamed pod metadata names; added "easy" tag to test case.                                                                  |
| tests/llm/fixtures/test_ask_holmes/60_time_based_filtering/*                                            | Deleted two test fixture files containing shell scripts for pod filtering/counting.                                         |
| tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml                               | Added "easy" tag to evaluation section.                                                                                     |
| tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/{manifest.yaml,test_case.yaml}       | Increased memory, decreased CPU, removed CPU limit in manifest; added "leaked-information" tag and comment in test case.    |
| tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/{app.py,manifest.yaml,test_case.yaml}  | Added Python log generator; switched manifest to Python app via secret volume; expanded setup/teardown; added "easy" tag.   |
| tests/llm/fixtures/test_ask_holmes/64_keda_vs_hpa_confusion/manifest.yaml                               | Decreased CPU, increased memory requests/limits, removed CPU limit.                                                         |
| tests/llm/fixtures/test_ask_holmes/66_http_error_needle/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator script, manifest for web-server pod, and new test case for error diagnosis.                             |
| tests/llm/fixtures/test_ask_holmes/67_performance_degradation/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator, manifest, and test case simulating performance degradation.                                      |
| tests/llm/fixtures/test_ask_holmes/68_cascading_failures/{generate_logs.py,manifest.yaml,test_case.yaml}| Added log generator, manifest, and test case for cascading failure scenario.                                                |
| tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator, manifest, and test case for rate limit exhaustion scenario.                                      |
| tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator, manifest, and test case for memory leak detection scenario.                                      |
| tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator, manifest, and test case for connection pool exhaustion.                                         |
| tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator, manifest, and test case for time-based anomaly detection.                                        |
| tests/llm/fixtures/test_ask_holmes/74_config_change_impact/{generate_logs.py,manifest.yaml,test_case.yaml} | Added log generator, manifest, and test case for config change impact on cache hit rate.                              |
| tests/llm/fixtures/test_ask_holmes/75_network_flapping/{generate_logs.py,manifest.yaml,test_case.yaml}  | Added log generator, manifest, and test case for network flapping scenario.                                                 |
| tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/{manifest.yaml,test_case.yaml}            | Added manifest and test case for service selector mismatch scenario.                                                         |
| tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/{manifest.yaml,test_case.yaml}    | Added manifest and test case for liveness probe misconfiguration scenario.                                                  |
| tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/{manifest.yaml,test_case.yaml}            | Added manifest and test case for resource quota exceeded scenario.                                                           |
| tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/{manifest.yaml,test_case.yaml}              | Added manifest and test case for ConfigMap mount issue scenario.                                                            |
| tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/{manifest.yaml,test_case.yaml}         | Added manifest and test case for PVC requesting non-existent storage class.                                                 |
| tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/{manifest.yaml,test_case.yaml}  | Added manifest and test case for service account permission denied scenario.                                                |
| tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/{manifest.yaml,test_case.yaml}         | Added manifest and test case for pod anti-affinity scheduling conflict.                                                     |
| tests/llm/fixtures/test_ask_holmes/83_secret_not_found/{manifest.yaml,test_case.yaml}                   | Added manifest and test case for missing secret in pod environment.                                                         |
| tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/{manifest.yaml,test_case.yaml}    | Added manifest and test case for network policy blocking frontend-backend traffic.                                          |
| tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/{manifest.yaml,test_case.yaml}                    | Added manifest and test case for HPA not scaling due to missing resource requests.                                          |
| tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/{manifest.yaml,test_case.yaml}          | Added manifest and test case for missing secret mistaken as ConfigMap issue.                                                |
| tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/{manifest.yaml,test_case.yaml}           | Added manifest and test case for taint/toleration scheduling issue with zone constraints.                                   |
| tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/test_case.yaml                             | Updated pod name in prompt; added setup/teardown creating pod designed to trigger OOMKilled; no change to expected output.   |
| tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml                | Changed prompt to natural language; added setup/teardown to deploy grafana pod; refined expected output requirements.       |
| tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml                                 | Changed prompt to natural language; generalized expected output summaries; added setup/teardown applying nginx deployment.  |
| tests/llm/fixtures/test_ask_holmes/05_image_version/test_case.yaml                                     | Changed pod name and image version in prompt and expected output; added setup/teardown deploying pod with specified image.  |
| tests/llm/fixtures/test_ask_holmes/06_explain_issue/test_case.yaml                                     | Added `mock_policy: always_mock` key to test case metadata.                                                                 |
| tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml                   | Changed expected output format to numbered list; removed setup/teardown commands; replaced `tags` with `mock_policy`.        |
| tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml                                | Changed manifest paths in setup/teardown; removed `reproducible` tag; added `skip: true` with explanation.                   |
| tests/llm/fixtures/test_ask_holmes/48_logs_since_thursday/test_case.yaml                               | Added "easy" tag.                                                                                                            |
| tests/llm/fixtures/test_ask_holmes/55_kafka_runbook/test_case.yaml                                     | Replaced `reproducible` tag with `hard` tag and added `skip: true` with reason about port-forward dependency.                |
| tests/llm/fixtures/test_ask_holmes/56_kafka_runbook_no_tool/test_case.yaml                             | Replaced `reproducible` tag with `easy`.                                                                                    |
| tests/llm/fixtures/test_ask_holmes/01_how_many_pods/test_case.yaml                                    | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/07_high_latency/test_case.yaml                                     | Removed `reproducible` tag and emptied tags list.                                                                           |
| tests/llm/fixtures/test_ask_holmes/08_sock_shop_frontend/test_case.yaml                               | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/09_crashpod/test_case.yaml                                        | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/10_image_pull_backoff/test_case.yaml                              | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/11_init_containers/test_case.yaml                                 | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/12_job_crashing/test_case.yaml                                   | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/13_pending_node_selector/test_case.yaml                           | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/15_failed_readiness_probe/test_case.yaml                          | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/17_oom_kill/test_case.yaml                                       | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/18_crash_looping_v2/test_case.yaml                               | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/20_long_log_file_search/test_case.yaml                            | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/21_job_fail_curl_no_svc_account/test_case.yaml                    | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/22_high_latency_dbi_down/test_case.yaml                           | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/23_app_error_in_current_logs/test_case.yaml                        | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/24_misconfigured_pvc/test_case.yaml                              | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/25_misconfigured_ingress_class/test_case.yaml                     | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/28_permissions_error_helm_tools_enabled/test_case.yaml            | Removed trailing empty line and `tags` section containing `reproducible`.                                                   |
| tests/llm/fixtures/test_ask_holmes/38_rabbitmq_split_head/test_case.yaml                             | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_all_tools/test_case.yaml                      | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_new_tools/test_case.yaml                      | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_new_tools_no_runbook/test_case.yaml          | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_old_tools/test_case.yaml                      | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_all_tools/test_case.yaml                   | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_old_tools/test_case.yaml                       | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/43_slack_deployment_logs/test_case.yaml                            | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/test_case.yaml                           | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/45_fetch_deployment_logs_simple/test_case.yaml                     | Removed `tags` section containing `reproducible`.                                                                           |
| tests/llm/fixtures/test_ask_holmes/46_job_crashing_no_longer_exists/test_case.yaml                    | Removed `reproducible` tag; test remains skipped.                                                                           |
| tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/test_case.yaml                                 | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/58_counting_pods_by_status/test_case.yaml                          | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/59_label_based_counting/test_case.yaml                             | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/64_keda_vs_hpa_confusion/test_case.yaml                            | Removed `reproducible` tag.                                                                                                 |
| tests/llm/fixtures/test_ask_holmes/65_health_check_followup/test_case.yaml                            | Removed extra empty line and `reproducible` tag, leaving only `understanding-real-intent`.                                   |
| .github/workflows/llm-evaluation.yaml                                                                 | Removed pytest marker filter `-m 'not skip'`, running all tests without marker exclusion.                                    |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant TestRunner
    participant KubernetesCluster
    participant HolmesLLM
    participant RunbookDocs

    User->>TestRunner: Trigger test case (e.g., pod scheduling failure)
    TestRunner->>KubernetesCluster: Apply manifest/setup resources
    KubernetesCluster-->>TestRunner: Pods/services/events/logs
    TestRunner->>HolmesLLM: Provide scenario context and cluster state
    HolmesLLM->>RunbookDocs: Reference runbook for diagnosis/remediation
    RunbookDocs-->>HolmesLLM: Steps, root cause analysis, remediation
    HolmesLLM-->>TestRunner: Diagnosis and remediation suggestions
    TestRunner-->>User: Present results and evaluation

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Complexity: Large volume of new and updated YAML manifests, test fixtures, Python log generators, and two detailed runbooks.
  • Review time: Substantial due to breadth of new scenarios, resource configuration changes, and documentation additions, but changes are mostly additive and modular.

Possibly related PRs

  • test: stabilise port forward eval #608: Related to test case updates for port forwarding with setup and teardown commands; both modify the same test case but differ in scope and detail.
  • add test case #580: Related to Kubernetes test fixtures and namespace isolation; both add Kubernetes test fixtures but address different concerns.
  • add tags to failing tests #597: Related to management of k8s-misconfig pytest marker and test metadata tags; both modify test markers and documentation.

Suggested labels

enhancement

Suggested reviewers

  • Sheeproid
  • arikalon1

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEejqANiS4BJZr3xTIAa2wDK5GskQNYdbBbwGESQPmRoFPD4iAYActjM7hRcAOwADACcBgCqiJRcaJi4GEb6xuBQZPT4AGY4BMRkyjT0TKzsXLz8wqLiUjLyTEpUqupaOmUmUHCoqJj1hKTkVC0K7RickFQA7qEJzBHycgrDKmqa2rpghuWmBgDCADIAgtkAIgCiGsy0HAYARICDABiYGQZ42RrLah0PasQ78Oq+TCkGIGOAkRiwFGYigkZiOaSQXB+SAAA2cAA5EGBmEgmBgavAiGTidJcJADhRnJRIDUKCxiaTaPgGAl2LDAogObV0BJtBYVFYeLIvByuTyKIgNODaLR1FEMGgLBZZAAadCQci7f4feUWSCxfBef6hXqGlDINDcbhVWHUIWYqqIwOQEVitgbC1oWgSJBBEIkgMke3YagetXIBjzdxbbBYbbqWDkiCIZzwbhgBhWTDYbis/B8Mml8uVvK4OusmqKojIAh8tAMeCBXAwsMkATYIhEBMhklB1Pp/BYBwMaTax34fjzvgE/VM7PiZd9rfztnSzkRTXIRuQEgADyQ4mCYdF4o2S4LlExByUGiMRiguCFg0CshonqGSjVhEn43nUD7cI2qy3twbiBAwd4bAa0hGE65BGB80rwAcqxDLiKbwCQuwkDUNRIVwjz4NsAJAgYEBgEYsD4BYbCIAA9NwFhTkE/EUPmAj4Pgzj8Yexr4EQGhCIgy5/IC/wgmCEJQs0sKIPsCKysiwQ4ei2xbtaeYYBJUmYbgkREts37oHqsL9meskWPJkAAFIAMoAPKxHyw4kDqGLBVqHIWewFDmmGML0AATGkiUAKxgGkKRgIlAAsFpKD4kTuF6lnWc4fK3vqaBEBg0Szpg9B4spFhxi+ADSbgeCQXg8IooS+P4gQvjUCrYE1FqIL6Q4HsaprObQTWILOAAKVSzohtBehg9AAGIKnQvkDbQASzim7AbuFeQMvQ0UbLF+UJZAyVpRlWVpfl0gMEVRJoKVknlXRfB2fgaHSFxzqzh1SSeESeKCfA2ackgJG+J6enSBaQTVtg+ovkogn4LIkYcnD6G/SNw5jT9239ca60CgIVjMBuABCzrFjFlHIFjQlKOOhUVkeGBbfQQ3ScSp5+PAfBNVNfQ/leIrbFgIl2dgYgekyVjagBGnAaBsES5BoiKmBx4hghSGwihaEI7Z2FolAeGYgAFO52LGTeGBzTGN1UT5AVBYIIhiBB8wPk+9UUFQsgANxWlulsUMhfCoYzdtkfb4gOU5e7wEydAAJT/gYhHiCRsKZ3icb+zRgObIxzFqaUdxcTx0gCUJM7C3xYlWf9/GuND3UdxtAD6Ph+MdQ1EGPFNCU1Y+q2JGvHt8vwsepQFaUsOn0Hp8KxSGRmokYzxrLwJB+MLkiYlDXU9X3ZWQNiMgkGQKB3Yo6uuVuVU1dETEDUtjSG4k4Da/Up4nWGqNJqWIYJiEoPAAAXrCY4G1vAdgYOVIIkBVrbVnNKMct49ohVoIdKBM87xSA2BdUkT9/qQHcN3ZAhYSSfwNMadAiA8g8OJqEGg3AILwCUFhGo8g0C0V6LCDB6AaZEGoH4PgShRxkN6vQM6GxP6AxIoaHUNgOTziwEQbAIjYaSQ5NmbAeQ5HGlkEtAYPAIhLRfG0AkWBSFWHIUdaBIReKIGqkSPSqM0DICaiDCga47z3mxNYoWFpap8zyFYMQt46SIBRn4RAFpVEbD4gQKwZssAMm7AjXA2S5HiDAJIpkGB1DyDEtrC0votRPnYJACQ3FxSQGQcuH8yNqADQqWgPA0RsxUMCHScp0YabLUUG8JAYluBCxZjjUgHJGaimkiXHat4SCDmLAKZ0jARk2MPCQIgjY4pngYTZBwcYCprAONtCCTATS9D2D6JCs5sbSl5MAxJmIgg6M/JjDA2NcYhHCWNKJ1jAkJMUJiRU7gLAVNydMuR9AWkOJoFozpQk2DjlUai/RHIeY4yJMvdWQsIIqI+e5TyOMeACgebyF2ABxNqHwLQfDar5C0zw+WFz5AKZgCc+bIpIKizFkAFE7mZY4MxFAwCTVEPnO2ZixGUS1EbXeKxyJWHlFohkzUkVBHFoDEBbAqpCxLuFW55VVXTS5vwPAqEOQgtwM+EIJizFDSJJag+XIUGziQOjCp5LIWWmlCvDseJ976SPv6iao4OwVKOZY05mIVEKmGTTRswxYR4mtfAT8expzsnAqSzCgM1yYNkMqRpQT1bFlCegDWThOkIxIBNKa6qhy4HkGw4sGAEhJDza0U29TP5WroKWoWAiSBCJ1PcY83EzUYHKnqstTbkBkD0niSAYsvSHuXHNSNsJtjX3QCaEBABHUx8anFUDYKBE9QD5TDiVKFM+44hxLWXMSPEmIQa4H9WE/Ec6y16QrURQDvAoiRGzogLg8b1brT6tCyJ5EH3sgqfgZZxEQ0vnZryMy3JPIxgqQhBqobGRUBjdSqmMz6DenhoeD0J9pA6l8n2rh2ZeHzUWpyAI4hBKYiWswUTmASAg25jw7AGMTk+BjLOeeVMJ3EmIiQFV+6DTgMUDrAwO0gizWufQ8SjD7lmO8PAe8RL9oaPvGgewyowhGkiNEFAJo4lgVcSwdxA5KaHr3ESF2WGokPliXBjACKklSt6GkgZuBfA5O0HkgpzQPTpMyTMqpNSgjTpKehXAzTKA4vafi7pvTyBIwyYM2ALGQHKRhThxT0o933jXHOl8gKTncEHPU4Vw6KoUBIqsINEQXXAN4IOcQ4zZ02o9H8oRn8IWziHr0B0biGoi0YIy1oa7lTHpLoBTSIEsvmzcsKE2MEaUW3vIhZO1tU62wwuwB2pRHT+3DO+VNmsQrzToFwMkbdeKd2Ej3B1g9OoUBhvxcek9BoJjnrA6QS9hZq1XsLderIGS5Pqg5tR984cj2QBApH081No7+jZEb/9apEQwit9NFiTnWKAUaU0DiLTFqgwu31tBMBrgtJ671eYmn7ZBlillSqiXumXE1h8LnxP9XCJ5ozZdiKPSrhRWutF6KQAABLMlgJvFuHEDBg9Hl3ESvdLNSRh8PLwvcl1kzHjlhrY9gag0QODb169VJAm3pCbdE3E3yEMh7U+Bhz5tEvtfJaTgSfw6AyDRmYMLGzgdaNkB7Hfqe5S8WMN7WX5tvcB/IIwNjprloNW6zjzfqMbEFTVjPoBQHIEREcXI3CviC4aE3hGT2k7ZeUbAa2DxwEyJu0ohabml9U0Ri0mCNfpMHzBi4BvzQLNYiVEtAn7kXDnqWFCz/cbKC6JCSAUU5Dls6sTYzAdiHFMPkMrukdSXwU9TUpzyvYLRL6aZuKAabLYLlZ7AhLICrqMjMgACy3oFSvkogeIy+oCrWCgmOVAVeFSeQFAcYUSvExYS0UEziTWcytACyiASyKyay3UG4Bia2vMRIo+m04+fgk++MnkM+WiAoJooGXeaaMqoG4GrquA7qo242s4l+IhlqJqbSWiWMmqJMS63ywQYK62L4k290Ah1ivO9+WaTWfOS2gGLOJc58UE8YgGdk78noCqrK9A/Yl+i286HoAgoS1sxSAWgG6mh65yly9kKGGBTIJiRSaM7WmMBwpACqGezMC+9A5YJoFSfgxo7CE+AMaOmmvBjKZKCm3GkAvGaq/Gx4SqhsvswmA2ycpaDoPhSmAm2YUaNRmm5cOmbheQ9AJe3GZgZ+z8+O6WyAn+2a3Ujm0SKubma4HmUQ3MPmMa6YL4g63Aq+DoK+iMheqMDgMR3gLa3CdWLiIQ0BwR8BQiERgSPAAQ1RGRvO+yuM640RmIjRFo/k/ksBbUw4Xi6iOBlA+BP4YMoQZi2YfAQQIcC6HRFSjkAg4M6RwWSm0o6s5UWRs4dYQuPUx0mI/Y5BlB1BhoqytA6ywy04i0t8R6t85APCtxmm88dG6gGBuSvISJKg7hSMRARSG4Hwne7+quF6JUYuukEeehxyD+nOz+SATWCGjYBoqCjUkGxhWALOkAI2Tq/aLgnUYg22AWu2Jc/kUgVAJoOSwob4/CuB/Q2xTegiYAcgKqgisqpifMlqDOdUL4wC4SLUkMsOqeyxBeyWaxDMTMrCRY+2+yfALBOBWOca1sbqeAIpVAGsC27J2sp2eszwF2zJRsZ4UEpshssoScKcpx6c72WEyGX2sQP2+p7AZaWsQCLkvw5I1uCOtuUODu0kfEm2pOLu7pHunpsA3uN+GefuWewQuO1J6WhOKeZOaevu/u2eDZ8ctUJwQY+ZLqt4S0NU1AGmN6oEdAJcWuFch2fM1clE1EBuycXAJuRAZuzcbEdw3AsgDgQJGgBAzAFgQeW8mkoeTQ+qCah8keSI0eJk4UXGVqhIfYpI/wcMAoNe8AGeroGovI/IgoZ4V5aol43I4BUoHIwCecBcQFmI/w2IFAtAUFV4tJn0kQhGVhW4QQ+o5y545SQoAYEQmIuFrGNMBw8gtUHIA2pJuCZ45AEQfIeAzGn8fyMYIYQgcSCc7Fg+s4BAQusg1avsJUFk/wJaCQBFyFfAlqmYxIEQ6ysIlqPFyqNQ/Fh6dEYoe2HFuxp+2Fzgki1lqlmoRJtWqAiJj0/YF6lodkCoPyapRsg4wBSJqJpIFk0FfAvsBoy4Oo58ilNYPItAYAwKjYuiy4dlvIswX8Nev8KAoi4g4i1FrCSiC4xoaYC6vlShZmR6+y4s8VY2ZaSYHIvZAQN0xyuYvl64EFyo12mIjwjwsBE0WxbaZAcYAoGA/C8okQ36VoLmRIlUWyKVkRgxxKEGUgdMX+3ecEoYaoOoTo24SifF4KQsXCt4JSzIY0ZaXGrCucMYP6AE28SZhsHVf66Z92mZj2Vs1Qr2uZWcXMhZfSll8Cxk/A3s8gGFLq8Fqo7ISF14RJF4XFpIR1IRZZgOw6/BKRSCDpGAb+L1z2b1OZ6Es5n1nR557ERgmYfEJozAfETI94oZ/EaoY8oSzgY8NZfEAAjDlGPL6AQsEGPBFh3LTQJqFLIC5o+ebiHtpO+XCFyF+X9THn+T+QBUaWeB9oev8CmIVZ+K6FdPEowTjFJbDY2HiGICSd4EwHiKLqKuteDfzZyEMXSRFXqGFVzqaJjMoYJIOFfqSLse1dVJACBUumBehpBXKX6bgGZFaP7KOL2FwCraErIK6MAv8M2fDhoK6FtaRruIoP2ndZLKieDX/gjGCu6vhuGRiodcuMDNUZ5LsI5CFpdSXDdQbPdvdWmXduBA9k9tmWnLjR9gWXED9ebheZbiTWTRTXZtTfkuyHTWWIzdxODjlIlGPLQMLEvLkYgBPIIqvdaN7pJKiuPdKGPPzRoILQ+U+Qma+dCOHp+cfD+WiOFIhfzQAOSdZyxhliF4ByltqSbwwFz0DHDFqKp4xDFqJUrY5AUBh4gPrSyuSkiYCIDV1GxKDuIzE0CWhSYgTwAWlLqQBvCxC+Rjk9kTlf4Ch1oRrgq8w/LsHlgvirp4jYO4MYIWi0Nuh4HdoMM4O75CQLqwViq/QbQJLdTkblSIToRcwWjUMkCMNw0nVa3AKMMeVrj6KY5XG6lBiPbSLVDF0Jy7D6gG1gbyBz7u0BhniMNOl4Caw06oACk/0SJh0h2NiCPcQIysWTX0D/BKAjSiZgCiKyBgAXKLTJ0wCkgR3nWHrGjKTS1oINL4j/0hC+0ODgWB3x37KICx0yqhUvhKVzoJD+PhTuCAwkDe7g3AKSKgQFMXhBlWrpaQD5hcZ166z12XbCwpk3bQTJnPXt0vY412zd1fVsSbjhMQRZkdOd1dP5nyDN3JnV0Kx8ww2oD7g1D/iE13BD08Qj1U0aa724CT0M1M05QpBdnVMJRjx/6r344PibOFjbRMR8TPL5zsiH1C0n2i1h48mX1R44g330JoHYb3rtY0XAKTLqDIAaV60fhBC8hGjWrkieTBBgDHPVI+isgw13yumjmcGEz8I3M1Dg3YhOAV4ynMh1IzQbBzRoa148Z9ozQmjmbfEEhHzgO/Pv3ICV3fj0BcNPSpQABssBOg/YHLOU3LTWZ49wy02QPz4N2wbazLT6bLzNaQaQYq/YsrzAv1bAtL8gALHIEryAUEeI7hrL5tqUzNiU3LRsfLArMqQrIrRJUyoQFLq+C6Wr8tsIStUqslAT5ERCycvU6EkTBwIkVT4KP5tT1152DdrdTdt2rT8EmNHdb2+NjsfTZ1Rsgz2NwzeZ4gg6D1Ldx48cZ6kTLW3zCpM0C6qYlKWAv0I5PUmL4rucGd39m5RE25eNe5+u9cXAsBmTzA/dRNBgyz5NlNY9tN9N097c/EqUaQRz8kq9Lia4E8drDAC9MIGz+97h9zx9ItL5Ytj0WhUtZ1XROdZTDJAAms8LAY8MFMqI68DRE/LbrUCnUv3qqesKsMA/dgVF9BBXe9Es/T/VfAfohhFWHbsBHT7Ukyk4677BlWeEE1DZoO69QmrQuprR6I685asMcEoFYOLmvgFu0ioPwUbjPQGt1L4LOMcyKoKM9AAMyCU0DCWyjPSpQyonxfsm06NG0dKFV3Hm1pBGzM1gqUWzGJiBPg2oC1S7ByGFqHbRy9Dxn1PJkRstMZnRvtOptxvdMmROxbhJv9gpv8DvVd2jNZvMkNvlw66IogI1wHltuQCNzdtLN4ak0rMDvrNDtT1M2GuTu9gTyJooL5OUAChajXOYC3PShrvC3NxPNvnbsR5X3vP7u77oH0t4YyoatAu3juRl3pbgvOM+1+sYBVhZdgsUCuhIuQCVtEhovcHqghdYsXg4uYh4u/E1SZ3EuRPpV175HztmbKPW1qtisXiOtSu6Xm1pRcs8tbhmvwCCukjCuivJeDeStMQssUdiqyvyt8fysqtRNJpEaattqksjeCiGvGsTeQBTczeYhzfWtUlFv2socHdRNSAaJYR4imi/VNSjhetCOONCHyoDXSzLgjVTbfoQb5cBs1NyehsNMQSpmRvKffuvX6edPpufa9NbU6eJwxtDPqfznSD33GewS5uA2JeFvdf3ZldVcYu1c1shZ1uUTBtbnme7l67WeG4dv6gJD2eD2OfD0udNTLvDsefM1efTu+eoJjwBeNg00T0H1H0RfB6bvPMfmS1xeewJd33Hunvnvlm55njkdaEoLQbjFTZbiofcD+VWN56KhDhzHAWgXfxDiB0gcja2NAdaWR2gcx1x00z/DRV0BxWMgJWfj+Nx64daIQeVnfuKiFZ2+GOkja5REQvYWoNiZWA+PRzS+B+wtTscASDM0+8veDVA+VYg89lEk8j5FG7+QADqY8jw/k7KNgsQY8HwAASm3/5G37g9XPsg6PBd6fiHbfqPtVS716rUJKdfrb0Bx8h4Bo63/c9wAQC0aOLgPxRHJgoDJ4bTcaWyGGkHXdDwp9nYT09Sp0jzbB9Rpwmxj9fcm9j2p1f3j4gMT3NIha+jGNQL9MAhP0VR6BUZNTfRyla2+4Bngs1Yg9s+2qzQdhPSF5Ecx289Y5l5yCDL1w0wXQlnc3l6PMle0XC+qrzebq90QnzAtlEgW5/MaYaXPXiCxpJ8Bk+LjfLoV1BbLBSuWAM8BV21Zu50W7SatvVwPyNd34+LFrkS10YgIOu5LNVJSydqhhVWVyAbvtyZbLdpWo3Tlia15b8tpuFrWblazIGMsj0igo7mtzlYKstwSrbbv1w1a6DDu+rY7ka1UGTd1Bl3SANdwsEL8nuzrV7q6w+7shu83rX7sWj6IQ8g2UPfWDDyaaDElOZ/RHljWR5pt4231fpvf1U4xDceGbLmK/3zZ74JM5PD0KW25jltyuKLKtjT0G4gD1UG5AiI22Z7kQrOd4Q8psA57wAueizHnh1ic79tR6rnWAe53gF8RUoiAqdsgIwCoD2sy7OXg8w3bggz6e8CWgZG/LxciBmIf8vJUtAWQI6FoP3hVQD5VVEqsWV0PdVQpzhqoeQi2oexsQnsz2F7UKLqBH6GgeuloNxO0lSq4FVg+HJwCSDE7h1vaCEGPp/mE7x8gUc1CaoSiUqiYKw6fKXlqGz7HM8+BfGVADyGrA8xq5fQIJX18jV86+DfJvi33b6d9u+lnPvqGHWJMxfql8OMHJjmgRwiIcxb2rE39pO8rApXMHoSE66p15UwNDjFdhP6IU86DAAAqo0QTqNX6pWZHvjl4J8hK66AGun+DqZH8s6xsCIa3TaYX8DOIzVISZGdjc9iavPZzh0IF5udtmPQ1KFR1F5zwKKBTMbOgNC6aAsBEwneLgJeb4C5hhA+1F81IEkBcMHWVLnt3S5AxqB2XWgblyUrpZGBNAlgcbGnzU8MBvA3FgIOa6EtV8bXUQT/E64FFnU4yKQWeBkF0t3RDLIbvoOsFioxudg87g4M0FXdtBOY8Vkt0mYFjIA63YwXWK27wcsx6rPbroJ1ZJMDBkAE7sWIu5linBVrFwY9wJDPd7Yb3N1s8Htqj8pBv0RmJgHKj+pdBkHX9nk1DCJ8shEg+7oBn9SbUtwadBIbpwwDF9hqpfJEdrBFFFdlgQRY6qEUmaXhpRIbEIcf3lGPVFR5/aIZf0M5qib+2nO/rpwf7JCn+34gnuMyJ4A0SWrojcWmMNi4IK2hQyrpwOq6XgoxmrUofWwqFmdSIFnFtmzyPKQAGhTQiAQ51aF89dRvNLoQaNHa9DjRSA2pLQHNHk0+aq7G0ZFxwHn0HRsw/phr0toMkP+dJXQWhxvakxBsdvbCg73iYMiPeQddhG71WFHCo6YHQvj7X96xVthIfV0GH2fZLio+3wocnH0MQJ9ARyfPLqCPEwZ9AuNIIIDn0jr58lJ8IkvnijL7niURmINEbXzHhvAWYrfDvl3x75nQuEA/QQMSOuEO07hitRcEhwVwykKWsgLytv2KA3ERsv0PfrKF46uCRx5QhMrdUbon9QJkQvTp+NVFo9nY4A9SAPS1EkSdRazPURRJHbg5UobNNivs3BSHMr0ZAMeOsm9Ts1DMow5ieMNYmTCt2eAziXuwWFVNcCCqewPIKqYW8XKW4RfgFUxCXxKaPtemmABrJgBXQbLbipNUmhu0gRPaaWqGg9Tm1/ga0jacnz2lrgwADUgQHsK3D/BLpA2a6bdOyakgyQuTRsPkzVCshgEZIYppQFKa4A8capMfHeOvYOF5pbufaWeAFjLJyQaRbmm7lXzag/crIEbGSAlbqA54jYJGfnlRmwBfpNMfsIJNDBPT9p2lP7Kt1Wllh1p8AsAOTJek5Q0A90n2ozJ0wNS0Ab0zEGSF/6fhWQc/aKRINin+Z4pHHdKcyLHGeD4OiFGDneJqav5b2ok8kOJIDpWBWQIHIpvbTt7AZ3eQTEHGBzJAWgKQ8ExAEbJlRkhTmVNMeBcyVhkhfqCENRqIXEL+CwW9AapkELOxPi5RcPBUebCVEfiVRqPHuiVM1G9ttR7Q6qeRL3pwCqJqUPZtsCGqzx2ZlourtaP6mK9BpyvGYUfAIEy1iBmQuQXtkoHAtFhl4nLpC3+k+gQx/oxFqwJuwRjuBxQjkA1yYSxiIZ4g6CWP2kE7dImHotChQLbG5DgBh6LGLqzaLUz/gVHU7qzP+BTd/gv1a7joMdYdi9Wk88dswFnlKt/gFoK9IDktaishxTI0cS6zmjbTSQlAwWZABdikiog1iOaP8A3n/Bi4ATCxn+ILnoFZoooKfpgXSw0VS5g5YruoXHmzgWxcoBUCoGPyZtHS/gW3nsQrH9zd5fpGcs4LbF3d/QNQHfO7BxDBDEyYbTkS+OzaNMA5sbICWjy04HiseSQwqcHLGbw8aU6Q0nlEju4cisAw82CQUOdwITG5WiHgahLp6gDMpTPLCSzxqF1x2enbMOVAP57RzNmsc8HBywnbr4NgRzdcN7g9ipy8M4XbAVnPtEq8Rp19BLkmzxDGTMxNtL/gdLWp0QxonCh+ESHIJMKHIRYMrmSDVBgBFFrIdmZ3MzrdzoOodWRGKW7hlU6B5IbsGgGcDeNDS3aMAMzSJn0AyQYSiJbpmYbXTEov0w9CYty79gPpDWMAKuHXCNgYlcS8kG4SLx5KiGBS5VGksuJOp5Y73fIkRlNgZjAm/iwzPp2ZCmYqWB0+JYku8YCNKARSi2b0rAD9Kql6S8iCEuyV0lqkXOWQKgmVSxKLZ0yp/KaHmXZRzZssd0FIHqWsidqmPQkX1BYUZldw5i0cJKKmZXUvZeC0IYp1fH+z3xpCr8eQs3C1Y3YsNO/nm3QBSIxA6ia2qOH4mXSZ0v0RCjIsgDnCdeIUXeQgBCSHo2KUQx5RnAs7X9i4GE7XCIuqH7lahNnOzs0IqnlI2h0AzoTHO6FUTFF+9EGCou1ir0kwGAXqXkG0W2iph4tHdmr3zmLC5awkutOtRpGqz6RJAV0K70CY0jFJM6fXk+EOG9gA2wwUMCrQimGgNaUU0VaSHBXUVlMdxEKLuO2q8h2RPsi+fJDtil0NgeHKUVdXk66rwhdy4hQ8px5kKQ5fSKRRHMJU1TiVlEhRRO2aJjxWidAU0UmQTBNlTZ5KjfOoHxDTsEsYgMeN8FOVcz2ZQM9xROwagRq581iDQL0WK6+RU0HOVehoE+7d5V0G+MeMaMjX/Kv+GgZPmPHIJ3kqaOiu0exP0W5ynRbK8cFh10qA5U15AVjAHECjdAgS/MD9moAdK2KWyV+beu2EgC4Yj4tVC9uuSxRtLjg7Mn2m4sUVKS21vIHNV62UU0UqVtFLAFRz5FyNxcECQFXGG/6QAhAd6b9gSXCqQAGCF6DtTmjUR+4pUDoOGQZOTD3hRAeAf0EqXcAqknkwCfsCgXshOBZo7xGdH5C7WepmkFYKBjflPKnrz1/YLWDvmATJIPkh63aZeCLysckMSCLmfBxfX9ZQyP9dmAIhFBv0UNuAYYHwDyAACaAUgj2LQDeIQjHEaRL9hHEsSIpIyVeZ1pnxOU8JAk3MOoJl3hA0x54G5a9XUGCTENDKFgZ2mGCQCu1ZA/odGu/ThwJggW5taUFRtFzDhp1ohO8FNMjy0CziDlANObRuTSBRMFSDdVA0xCjpEgvIWUJkgwybRIyiqJQB2tvn+xZQdYTbr8sBUjZFx9m8dMx1wpzZeQaXX6qBnEK3r1qzoIohvhDBNQrNqTZlKzzdDY5YOLmyxRAVbTIB/gRCDPPC3Qawjr8IMODQVtHBFbRlMS5OjKO9k5TCFUbeFTaqeV2ryAv1TDiPFZatry51QEniCrInxQzlKGvwLegI08ikF7CGcgVKDnxtjOQiyoeiss6YrxFeEnFURJaH4rSJUcmXi6rqkdwyVHqr1fRKQ2o0iA/qrhScwpUZsQ1E8MNZsyLWf9Rwpa3ac9O+nsg41dNbaImozWoyvuuAcwGwDniip/KSAfAJSEUUi9xOY8MAGPByipQI1fEktWWo2iVrOAjKoaRxPrVcSxpXW1YLrxXUdrBtUc3PHBKu1gA3uc07iKOpCUJ1TZfEE2vyukmtoJY3EE5O8hcaJ0ycgajYNzOCik70FWKGCK+nAL9gN1g61PLIjO1Po51b2/af8EXVpAlJJpHwc0QwI3DAM1iQnGeovWtUrC2IOqg9vJxtKr0gCckBoCTX/bu8QOkgIixvAWBpVNKyAAjoy3LhWC58xYWNBMWGJtMv1UFbFt+j3q3ij68bR+3hmTqHwH6l4eSE2y/r1kvyiALNAZny6uVYALcEpGXAgyRNWKaDQ4VgCwbiwZIM9RrK3Ay64RVNKMuxT6iArnNysj9qBFLTVpswyyVvEwhI1EJtoEQQUeIR/68aBEurWImXm2hMbeNLGihmxsfAcalAXGo1Ron73+IBNKAITXrRz1BYvE+iSTerGk0BA5N+oSaIqCU1MUh0EQX4RpsFBaaAuOmpMi/QM3LIjNVoEzTIXM3ECrNFoGzfnrs1jpHNdQOvSEHoYyoG8X6rzbsB83cA/N6G3iEBgOarB+wyUXjsiCr3gF9kvgf3UNtCbkVSGFKegMjpG0SQ36sMgaAcA6TlZDQKaFcppgAGi6dUHOVlhl1JDjrZK9W65c+N9mWq1qs2lHnEN7odb4O+OltZezbTArwaoK4BPqEaochxmnODGtQrm3X9TOaKyuNhPS1raG4TEB1ZVMjkwD9tTNDliL2VzhrMkvO71HSoFoZznyui2tTnN3aGKxp/5BaVhSlmgc5VSVDLVrT3mowid9w6fjv1JI+AvpIYfjtTOg48Szh2vS4cP1Cm+KhVMTXlW1WZ2oBOVl6YOjEa97JNXQ+YJ3TEaZZPhNV+4nVY1sm3UkxR3YJiBaD2T96GNVCeiveNNWyjGtbBohRwYAk0LuDGo3FeHM0NOrZFWzA7fxA5bz0sWReSXrxpNHDoRjFkzRWFxYmZya10wllXnN/IfzvmZA4ud6KoFlymBKVeuYMR4U1cUJZeGMR/A7lddNx6Y6ln11kE6D/mQ8rjsE3vZfROxtYqeTPKNhzz1BC8pwcuC1LJILjS8ysYtw4GPG15bLR+fKy3ngmoV+8rQYfLbESyT5HgnZXuPlT7LMuWx6jdkMAyVREJkYq0SPIuW4Lsp4bXKfQrfEtbH+bWnphQv2WcHYh3dOhX7OFiMKeatrM42WnYX5D2ByEvE+DPp6LbMJSh0RatrqHttJFHR6RWRL21yKSVCiwYyR07IQixjRYCY9L1MMMqBpcx5lbF0WMfMD2maGxLgd+iZHeQZ4VxUcIFmKqr2fJq3ovy/Zkg4jGeDWd7S1lRpZJnwz3rzJjrFKyQKkwPp6kND2yQp04uKDhy0mR8PN0fPSX8Jbnd6yI8+wLtbX42oglVuxrggaUxNYBe+XCAwyIKvTJgx9QdW9KKDFAUBfqvMlwxgAtOZai2IskIGxxn6JThxzIzVUm3BmXUwhqqoo47IFHOyIyyPfcYhSoMjwtQh/BrcSaa0I9aTKQ4qX3XFOOqZFUp3o7oeNFDHfAKpigCaNqjrn+IbGDQFeWrVMqYurzBtSZEir+xloqoLiDKXD05FP41eZMbRTqrEQAgMILaOgB9BkwBcuAsjl5EX3Jmka5GlTRCNhZnQHQxzatG4zBYlQfT8kLsNUwXSTqEMtCRMwEn/N+lgV2mIhPYFS5eRDUUqMs/lzgt7UPQ5HJvEgimp1A1Jo/GVEoEnB+J1wS+0CtIA+wOkPzW4mUgDrrDv7lwwRKRiRfwCqY1Cw2+kmcmXDkBscFoZIiBGLBpFMiAJ9ihUp4R0YmEDWPIs8EwXbHOEDofJUpaEtUkJQOqeYMCj7xIpJI4B/EOoAPVIIM6GEHCgGVwDuBqALAuiHoMlVnQj4iit3WPlcoEIqKZ4NjJ+Y9CuzGmUlkkG60ug3nxpRIVxX7sQBWAl0pe34qgzHCYc0A9iNuSHVjEEYYev1AjagCj1igqtyoIQ13i73QslpAoJki5kJP4LGmtypo23WVFcHr+X2HaPBY9DLiQcbjVyy7DwuyaULgSQuHXPJASndtgvGU4dpXPyn1zm5/ANub4i7mryZIXpm1eIuAZOr5Ibq/lxdhDWZ0ri+c5KfGuurJrc8aa4qaQFbnmNC1n0HudkDLXS4S2wUxitbYSLOeXbOc10YXNHW+jfEDllNeGPnWBhl1sfVMfTnrsNTh54aTjtGl/ly5GJtk8CR2NT40zTcg446wVlstZx1iWQBJHvBmk15oe59ZFcj1TIJs/2DCH/gGvJmimL8ey45Y5CeQCMPlPBJecAxUcNAGQUIJMhQCAi+4vwy0BeZJDrW2LrCvQeclYwcgyQ117gNLduuBnLoY2p9s8iYrtFvYYLfqCRX4XkQMpL3cQOOMA5VZCUBIRLSEqrmVgyI6MoQ5WQtDG2sIA6q6CgW6Xkg2MhXJQO4qo6/TJb0t36RBhjDp7AaOoNvpBKLleipk9x5yKJT+RVkwFqx0O4CzWzjyux083saWP+P9ygTid2sRvM27MAmsqCsO/iadaddwohtwYrUgdq54zbrtu3SAixYmK4FoYB291G0kRn/hvyp1OVW2U1WblJJxk80dkPNXn+8Qmky0bm2GU1rsWfbIPnKMAkjVFAEaFEjyngRGFXJvhY4vHDl37svJwRYz0es7lnruE+oWKc214r+IO27Q9KeOv9H/ra5wG952BuTGmJ9KmYxYc1NHnHRuO2+iIbImP03QHYcAy7A+k0QvpQMskMNn6qPYGoGVJkJFEYC6tkG5Ouxd4GQLN3TbLtsiO7eGummiE3cIgJg8sXvbqZv0TyAtl15m25b40wnAtG8Z9x9slELRNFrfoLFfQkM2PcqTAzvnBId10/KgBWwt20EwDkJh+drOhhKR4uPhb9To54UmIWAQBwDI3M/TwHnWXFBGf7D8H29Mk0kGvf/Ww14HukFB8KMFUSYDHTa7qB6AJZikXwzG5fY3ZMdidjk1i7aJEeDO9d7TftR3vEadMhB4TXYkIxeENMe9NMv0OSSEE9PJNLbXoSst3dYPNN2DjVwOYPe/G4Q+6D1gU/vZW0vW8JBE96yfc6PbaqpF9pcz0L5ZjwYqaAMeBIFXqwABs5KxkNruXCg31TsxyG9jpsPzCXRJAtrClxuMF2AFK6gMZC3+BBAu010sPI2DDFw89j3JtOYXZOOpifFzS8sfN3kvtiUHIJg1ht0VYbcXYW+INszYfTOhdIdkBMIXEcEx2VnjrMeU8epnJ2zuU3a+RgbHXYBDntAF+eFHztUkfHutyBjOOJlaPe5N3aaVc/WeChbnprdQdfIOctAX5uy3kKiYbko2GHIVUZCpioSSNbxF1B8VcqJMELGjzWqc7aqpOJs/xVCpq3SaM5L2c24EjIegSOUls7jLOynjidRs8m0JYA1FU211xiKRT+E4+2VMgEHWxr+on6ykEpBIzOnY8KF+U4fDdYPNjTl+6fSx11q2nzohAP0X9hr3dekFkkpaC5PzrTb7MsAKK+KUBXps69qV07ctksB3UOmKV4lbGDwcLXn3SIKHDJmp7USW4AvGgEfBSYxUDHDbmnd+ZrGw7FoHKGkBNbnP07cdzfDTF81wGeApBv5Hii6SEpoIxEDcJOM12O0QzWDZl1ojQcLFklWpe3XA/2QvskbASQlPOpGw0d3SLJTvO6RDtKsBxorYBPc5bFNZKB7b3uZI/i0OhA37IG9pM8RccgXYNHAAOuNixUAAXkgBUd5WwqGV+/FYIHynn8WkMMlFzv9jI3vzGRKuuRmIwXYU3Rd11mXeOGwFFr2UGG+5Y8PkH/92wtxu2hQdwaLJi12QFrSQYtExwagDQCmlSU/4iEy0AI0oz5631srmQCRuu7oUAXUrozNi9quw9YnDVkha1qKk90nY/sLk7EHdfr2pDVZMkIa+Nfo9MPpsyAEHc6cABFZ52cpZN4eQc8eT9WADtczp51BH910a8pDLWMPuwLk28DzfKFOnC2/D96HQaGlKAw11j4R848s6531vFGcxzhvr3Y7PT+O9q9hAuxm3A76UBaG7f9drjg8m1i0jk/ZhC4HLqoRk8Puim3rGh/J1oaJWX2RXYrnmpK+o/SvT3dAOgGqYVdRcrDCxk82iDPPAdQjmIFVbr3DOwhjTTYUa4U/kUdxRX4rzIS5/i2S93Pcrp+2YYfLy21Xqqq2pJhfM9REHQ642uriiByl8qoGgBSJ6LdEUpnHClhQQ/2nBWgVCbzmiEH0ZhhFMRsV9654wIzFsCv1DnKnFFTwzx1INT5qEm8IoRZ1JAdaGtH7LwduzyEDRrpP9aTr4KbSmoxzSjQ/qs04YkdyHfndio5uFoScAZLs04fuv67z5S0gLubuJYo4ftwgqDeSPwa7YXzaa5+iS7Ry2jmmFjJorJQvLrBT6Yej5m2pwQGl30Xqd65r3ZmCWWA9pxrBYA6wDsis38pde556zvh+TH/b47RPzVp/MkwS8pOac+mRPtDy6jvHLjkeEM+OO8KzBy1Plyw4J+HWC9grwj5ZBQ5y+UPcvsV6hj67Z+6OLnYv/ESkBO24ASAF20oRsIEn3qKgeE7ZerEXnlfmHFX2cvz7jsC9feihBx3XqlXvO149elae2yb08xlenIv0VaK0iTe4AAAaim5ID3AY+YqF2MtBt/3BhUynhOAVzEez4CAVAKItBB4Qqtm5pb18ysJw907DXovz4zX2cX1yxO7rvfTRCgsC23fTt1xl/y9V5KJfeww3U2/ZQ6BZQUv/35iCSkMBiGnICzkHZjA18cN/kcFJzn3h2tPvxfwJI78HzYfgRI0aUCqkQD4UoTqMPXx9mXDBLlwPjR8Nb+ce3Du5v0dNTCBk1IEoogYukl6sZEb23ZKZ0IJNQa/yNX5yAOfzQAX/N2+4JUT2uRHzwhgcO6Jyh8rP+CThkmuN0rnNT5HR7Ccn9NzIrb/XEytweXxUAg8sj82K/quzwc/TpX4b4JUKXbp+0yqXZx0HIP8B8QdJDvJleCMMXgyAg4DFSKy6/GSL3ydCrUiwgrvvcA+0QAXkDZ+DAJ8bkehcsp4Ge8dpahQezFAC41GQuhsaAKywLe5/2vmrD5Lk7aimTg07gDiyIYZvvkIEBtdt+AN+p/n74nEgfqAySG+AESBwqYjnj4NGSHvi6j2iTs8pFkPHqR480kThGa4I+1p9aHWwrkzSi+7NBL5r00vqQCy+g+Ar6ZIyvpl5/AkALoCOg7riDhR+aQMtaOBUAJb4VYGwHb4EoDvk75dWmfu4SkBEnjh5sevEO9pgAovujJ+krfqQDt+PCJ348y3frgC9+tAB4FOBB/jRABAi/kEFVa7hGEGRB+0hEHSAUQTEEiB9ditBp+ZIMQE6Y4vgwD3Wwik9YWeWKobgba/LsRIC+X1kYE9CJgQ0HmBJflYHy+qxLABeeKvj57zG2pv56/oqwqz6heQ2uF50GkPrZzdU1FGpgvoVEHYxSqvIPoHdBhgbVLGBYvmYHxB+TFIE2BDWOMGZebAaCpqep/ib6lebUoehngg3pNLwy9NHlTWMdQUwxfEthHCp82ylk3jzsWvnIHuuL3tDR9g1UKQD0AI2PTpXa/Oot4v0MWlgaN4WALpKr8GYHn6pkwQTYjeB8hByB+B3SO37EQ18gQHCo9jhsgUUs4Dt4c4KAMoRRuwTmP4++WiKcFT2pJHTqpB6QWaBKSMGKiA0ULHHMSeuaIRP7i4rIVIHNIsuFGjx2yfGgaC6zflmCgyK7p67COurqR5r2wPotKqq/6gB7NqdIV8qgQErHhRTarOslC6Y10O/RUk8jpw6mgCYCj6Ic2WN1Do+shD4YJSnFHe64+8Hj3YTm+UqoHkuSTr0yZusIHMEXgoKmQ7Re9nkU5US/QScESBlgecGjBVwRYAgyTAgiQTSDgFNJpY/+N+xOyjDsKJve4Bv+oBkSsJ4Q56mmKD6cYOGmNSlSFuKfYEqPQYcF9B89OPBFA8AHTS0QfeLIC1OpSGIB2BCvK/YtOyrqyqnm7vKvYh+uvnkL6+UDAGB3B4fsUFRIBrux6UgaSqlp8eUzumKwglsgcg6YYnhQCxB7wvXIBgXivByrh+3ugrIA87sZ5tosoDwyYY+YPzamm8aEgAcAxoAsTkAyYTQI82gSDqCskaxJhjyWwbvHZ0BOBgC7aBOdi24younlcgkifUL+6u0yDJyTRoxzj8oQILYdUjthD7A0gBAAVGAw5ikDCbqsERjkegqAUqM7Y+gXANmADQ1XruHiURsLmCU4WEf1qN22/oCgAEQfNhhW8D4bhHxobwGNAJgFCMjjBANgAAjcRvEcEAfA76oVYBmwUFKgwhKRh65CM8kPIA8g8gCbLwhUQHxBcQ0oMnxZevDt1Dve6bkbCki3AqZLKgASivoSY2/ieFcCWiGyxMAOfBxgkY+Qmf7ioh0h4bFgLFOzg2I9EWi48WJWIJqhg2+LyCvwfIFRAmmHsHO68cgKHB5ZSCHh2aUuVquSaASxPjfwkeV2s4FzhHrIXJ0e5IFH5pKxHpoFpRVkUhK0ekbMDgIyW4VRFFBZQSUG5Re4cWAXhdbnESVI6DAVgYREuEpgwKgnqsak06xn06Kem4ZRE7hrIACEvgbjvvrPhFgK+F26Znsto4SbQetp8+uTuGHOqDnsYFNhigN9riAbYZvadhxWGUiJhB5kq7WGQ4QF56y39qToLByoMFFNcVPpajzAXVLAQ5eDJFTrw+IIfhGQIAkQpAyyrPqN4OQsAIMAVRO4aBrZgGAHCp0RPiG8R1ewIfOouwuUS/I3qKIVfhHCGPjCD+ERvNIzChP7L2YcgK3veF5+KEVhBoRO0e1Hk4vfCyEQxs4CiRGwIkNgDthQ4O0hRRTWPmHj03ejI5f+SodaESINMOo6FeqeOqGCOjfvqFhRepnaGT8C6K+hOhpdGLI3EukYYgmCigeOZ4uk5r6HTm7WldTNB6TnNGqGDEItGdBW2mfYFOEYcL58QlIMaJN2mzE1KOOtAL2GHRavtMGf22XmvbAmBXm9E7+53pXJR+HtjqCx++4UKAJ+GUUn54Bp/iY6m2tAAIBVg8aB9ihM74VGYqWeQHyw+M4KIijuyuBMZIzYg+ORj0AttEGbT+UglSRVcp/gmDp8jbnMjSgTJCQC+QFHuewQIkqG8Rm2XAHUEayefj+74gyyBBAgedIXuhHigPCeKOSZ4tFbLQ/kL5DQA7KG3wfAvkGPDZAvkO3zemg8cPGjx48eWrPAvkL5A18XfG8Bdg5tGRaPGS/pXJ1BZoSgSBmhHI5BakvXObGVBZAAb7HA63lnGyB/RMchiOm/uzLRgjhp8rnxl8HkAKE/RLly1B4cY8ZZUVRGbLQRtAL/YsmVAaLDrGjAc3b9gH+NriPerbvQH9cuQgrG4uygcrED2foeoGpRSDulHVRbooJ7ZRpQVdI6YpsVx7fYBUTglIEO8bh6lR+HmHERxHnlhDRxLHuEGexZCRoGuxRUfwglRLTGVG1BOITXa4IknouEe2Mnn0iXhlxHXaXxhOLVDe+IobPgmO/CQUEkB5sV2B7I3cQiKnipaD2Sc+5nlrE8u2TjZ4GxdnitGRh4OKbH3aO8WPCWxFKtbHpeTTv2FHR6vqNKa+mvGEYXCYXlbYRmkXiNaCuMXhNYi+ZsQY7WJzoDjKZGiYTpF9grPpwEEsGVFqT5w8gGeB8B/7LeBtS+Qt8EQI88BBCeskhp179gHpLpYhAXJlQmO2ocQIkHx3UFg4e0zfjdA4egDmwlvOInNDSIxJUK8HphI3opixQrHLt6ygN8evoCUMHH4RikyCF+yIUj3H/4ZU7Ak1jhw/Ikt5Ci0fAjCWAejP9jKymSeYzaseSSfwqAn8RyAlJ3UOCFG697h94lQo4Tr6A4gDhI7y8YDjKh/eyAAD5XQ7um/Cri5YVerqWWCk0nCimYiH6w+zaqw7Vg+yEj7gGitBokOScHOFAvJWABLEao7oeLouh4suhaccQkCBh1AzNKOYsG+PvFH92ZLqrFEuW1GT7ByFPub5R8aSNaa0+2Xv+SM+eoApQs+pwiF7s+GqjNEtB+ibz5NwS0X4lGxASSbEcsXYcyAHA3AEczwAPIJ6p4AliSgQ2xmOnbHHmGviOFqhY4YDh6+dIrCDauoCiZGZRrWL6S+xv0P8BxM1KPKpuxzeno6OG86tql0iQsCrq5uUzhyFDUKjgKpx+j5vqnwcsFEao0wXCbPjyhkACkCXha1Ff4fhI0cJwAiJxP8Dx4mALJTqRTqSo5587NolAaAsIqhRkAs4PBhIQkAKL6i+v1KAH2SvcRyCjU2icqCDxbfNAC2Eo6v2D/AKacrq/Urqbwo3awcdQmQBdOlV7/xUcaiiwBPtHxAkc0thUnlIcdL7axUebBNB74X7OfH/A4aVUDFaYAMpGIAC8mfBTiuceZi6yeyQJ6ZC76Ov7cBZXCamO8ZqQ6nhQd/ugEjpVXuOkKq1Cb0nXkmIP5ADYuGCwFoB8cTlCJxcZi4AkA6VndGFuyke/qBA7AHFS0ATWNWD0OaQebG/UGfsok6YDaUwlNpGWo7ZHpvoJACnpaAOeltql6SQAJxl8cnFRWcONv7pxPCJnF/pYGDSBrglRMWzSAB6WBlCax6d+pIOGgOpFYZF6XHHwZ16YhkRm0AI8C4MOGdlT2sgscpHCWJcHimVGDUFQiGq5dOKJMQthB/F4cQFLMDiB6YBhBcmLJhI7MGOLnVa92cTih4Um5PiT4cJkmZBJAmpsLCB6By0T0bGxkOjylEAfKQKlCpp3qKndQvYWSAOBTgckHEJIODqnY4mQVAAVpmwAkrWpVQFVHEJ5IPZlCwjmfkQGOIOG4RgE20KOnjpHmYQ5kg3mQGYGAngX5k7x+QSJaAZkccBlmyLCRlFeZpqVFkxZ86SDhYZVYJQDMZ5yCllCJ4QZFlZ6jKZrEqGPLh0E1heTsYmC+31sYHcpcNEZkuSwqZszmxB0RKl6Kx0TqazBNKfqZ0pHiYsFeJulLeD3RawWqAbBk1AIxaUFAOsgukaUVTztIPuPgx9kX0TMBRJF4L/5h+5MG5ncx/Hpv5rpNeBunVufpJ6mNRZvoeg5QFXgqBGwOSR14euKDGGizg86R0j2+YATwSKIYUfMC/Q+xHATegYRFcLhQAei0mWgbScN5oUZYP+6fw/QOIByopfv9GhALAOf5kwS6f8F3htoQt6zJSIW/T0seEaGCSUnenLGZUH2DlQ3I+hLSFtomYs9kvgr2UOl7ZsVHun3p2SFBTD+SNo25W618lRx8Q52RjlCW12bgj3AejtAQ0Cf2UQAfAvGqc6sm00IkmkgiITgY3aTiOwguwraSljtp5sYgBS5KGlOB8hzhGWjHAzsQmkyw7IJY7+pOxC9kGOsoaGQ3oYTAhHlM+SSqFiGcPpqH8xq4kUwQ+JwsKL/qyMX46Q5b0ZTpSoj0D/wx0KCXJnehhPirGEuKmf1kC61ubrwbWewXVn1hOhn0FNZPFrynegxmfkymZHWfYlXJLAW/4hkbeohTuYpvLnhcmS2VogrZ2sAQwKQRiXWEHBKeVGFiurUfUhZ5bWd7h/y/EJckTBbElMFSpMNtl4WQpyXiYzhoEcOgcKR2bqlj+lIJSCugR4QwTOxn3niHW+hIWwDEhYqKbaIQZcU1CkBYANYg+MoSGkHM0aAMNHyWs4OOwF+IYKyGERZIEQDcAoiWKFy+GbptblJCGJLSsgzmYWk1JMntdl1uAAXabb5uAOXEoYrNNUiTRYLDHFAK14iETJG7CBmmIiOaQGiVQAiaW4AJoTDKhgJ/4dfKQR2hJp4isjSambWRZKKDnAqBGNxCKRtrLqyHYv8lXjwMT4EVCfqoGoOACgpJDVhEgp3kRHYIJ4IUiGwd0ZtZzo4ttO51BHAE6D8R08DXbooU/iP6+KodPuLOZS6Wp7Ce1ckNEyeTHP/l+pciKRHcAMIhoDuBLAfu79y9UNQHi4uBVzH0A+BdkBNYA0X4B1RsnhoV85IQL9BjRT4VlAvhkBQYXG5RhWjQQJUyLOBmFMqJYURUt6KBGMBTACw4dmEWRlkz50nl4oyZsUfVYqBGCTinR5FCUV6OKOgVplYAieQ3lCuDYc3nbRHYe3mmZ6KN3nNy4XJZnRZ1mS4HpZ66YaDRBnHlUVeBibrii2+9vhvl2Z0QMAW75DQfvk0gSTMfmn5qWXgk8ypWQVyz5vmc5nxZWfu/mHAYWSUFjFDRZMX8eIOC7aqFxWWllRFdRTEXLFexvR5bh8xVEhbFx2fUUTF9eefYcpV9ibEt56EW3mtZJRV3mdZENk4n2xLiWdG0pbPkNmk6SwTwEXgXJhTgUxL4CCQw5YCLOB9YpRWsD9qcBcWDsFvXh5T9e+Gg8GYG+KEaRB6B2WUkAZ5StrixQVSaMXRF4xbEU4eI2H/kHuoSNGC3osiDCST4uCPghRo7XiNgzkDhaDGY54UJXn0hj6ESDsFKqMCGu0a4AaSNKvFP+KRwL4DnixkSmJwX+WLeFwjeR1OFCS2EVMTAk05iYOujJkACuCV/yAhYJwtAwhQImiF+AOIVYRWXnqbZhrrv5ZKWROdTEAJZOfQgU5j+I4Y285UL0kqlfBfQaokGpSsA7UzurCUmo8JchbahICA8I0waTGblsYAoAhhjgmWDE5LShyhmZW5VMNrSPI9ucqFcONFs7nSCIfhqGpMHub7kyoqABHTQh5eaR7B5NiD5r0q8RV6FKxPockVR5P4q8rYK/1Ez7vFA2Z8UQqpOmF5Fg/BBhQiOIaYlGtGmcMiq6Js0ZVk2chifz5J5jeatHFO3KbABeo/KYqbkAdAFYB8QzzKLy3Wtsd1nOJthpr6C2V5hraCwD7mlQPmjhPaKhAsgBsB+A82MbjQA0AMtCuWtkAEQQWyfjq7kw7VoBh359ol5yS8d0Hdb2pS+SVDgasQGADckosLhbq2I2AXCO6mxJARaYvEKODYW4WA1AsAfnLJG+x+vEfkvwESFLmU2fVtfJFpD0jiJd8pXJJp/athOOy8cY2RJBSA5RjuCFgNiG4wjIIEFLlRK84ZNQWgRuNeW3lr6FxAfpcgkrmNYOhAqEz6zWIhDCwqJNpg22DFqQAvp36derLQhhCs42AbwMH64I49plpIWNFOOxmgcrLxxSa64DJq4VJFfAbColNpzBKYRiIJT5ZhccEDKgDeosR3g/eiZXflMnpzlkVqWvV4QiVNgahzSloN8EMg4lghbaYoGKLjcQzLFbyYA+4ppXaVEBDvpgWXkKZUZuHuWHh3sNiMcxnxkVgZZvmxlg+wSY8VuAaM2QKVuA8gmDIFIV+fqaHmIeFqsh7WqSmQSkk+q1ploJ53ViuV/4X5XZCyALsFbrTu8BhJVJmJANO7/A/kG1BIBA2CSD9V11vAAaRffCSBIBfVv1XN8O0P5DPyw1jkUXFpicbEcs05bOUzWC5YxokAy5R+XgWS1itavl5bJWRdWNEJeBBAO1stU6ZQvpykbVjNFtXzly7kuXNVU7HLblZzbMOWvWjQjk56xtYatW6Z91ZtViE21S9V7VPeeDbNOLxQPm2GG2fNAV2VeIqknJsqQcbKpA6hZD6uP8Ya4bVJrsjbEFHOnXFgklUYy4Hhu8SMWdaB2Sf7RoRcSjmr4l/p4VUZjEaaYIUV5hwDs2GQCqiTIiLHNRppingVafqJUDuUmEkViQ5B5Xti+r8Qb1b2AUOv9I4UBRioV5ms2WAHxCS1+1exKrlS1rlaRWqAEAb0Ax6paCQBtti8IS1N5mbID+RBIumdx1JO0iY2rsefGm2xNTuHWSNIC+p2FAtUVZLSlAOkgAYjTKOouwoWJACZQqUKlCEFjNbHawJBwEsTqZXAPc5OaDATTBBF8HBtCgJ3gt9wOMDAPICyxPlBYASs6VmuoygWADUTlVcUaSb3KvZXIZD2+Ua7E2ZhDjwmaZ+HjjUcs7CdgnpFX+fXX0U+Hk7WfE4nsMWeZRCVEG41YibVjuk3Ftf6aFEWcrXs1HNlzXEQrIB7WzgUtmrXS12oEtZEOoGc3aQBmMhOBURLtSqg3mrIMbX+gptZrZmyn1Vy7CmLKecWGxa1cDWPVoNc9WLle1Xnm95lhv3kf2bxcGHNll0aX4jZywaGAPR6wcNCbBM2T4krVN9UDVXFD1TOUP1oxjtVLlL9dcF7+T0TYgxJNUHElII1pRJj2Iv7r/aQKI4Ikl/wpaAAioNK9DJ6/Q3dXgS8gECGVx6uOHo9LseG1fzqIUCEUaZphEOU86UAIjoFIxEMnmeCUNWpOoiZhBZbCGpVroEGll0ZzDbIUUZRkpK0iHjoHSyEqMcMkh8WOVjG5hthECFnG9lQmZ/m97FqVUOqBX5WKqzRPwQKlZejTj5JDABAwhMGBP5WawL+tGXs6VoReEPJLyAcnsBADkA55MoDuSFHiYCMwTwOw5KR7nxbaoTiU2SVSLWa2qTMI6E4mrpCoVQfBI5BhVEUWaHu6FocTlfywHHLkgpmaXdlVax+KggverMcrDXy/0hD6+Npjj1D+WbFrCA/en+hvXCi/ySFwvgcbh8mYQx4sTCixf/IBhQpGENnWwp0saSRJSiKZ16ygQRlSEOR0Zp7kmlNFDmZb87HDLFI2uYSXWJF6Cdik1lw9iS4V1agchhkpomSsIx5oYYjTIK2nBZyYpg5UynfVC0ayn/VtWbkX+JUDXswtIIKA34L0vjM9qGg6tTpCa1sgOuW+erxVuXu8wtdebRNCqY7wZUzzKeXnlZjhTZxVd0C6jbZVlvPbB8DfuOBVWSJB6CEgQMH7qIl0TTOEvlE9uSDS1rVTiW/lhqZ2qAVwFXeXxVMnhBWsEkaFJSYWcFdwAWg2FaBbXy81f5AWgNfM8Bt8sQDyg+SbfExWfE3aAdKsRtAIhBV4NSiJU2I6urgh0gJoEgCiAjyeQbz4nlaIxWscKFJUyoYCtq1A5eqiEA4VFGhQbnK8uGNgb+J2gDTCVx4GJWEo1+JniQVXALVDVUDoC7DAA87kYKa5E0BKJju2lTdLaVzMFrnEygVW/QuwegGRXysmuSuhWs0Hv1z6tozWWzAmNiA4Dt0M6TJ6ywtrTBWHSLehEUBgTGZUyRaCXGSCEWu1JlrPMyAOtxaVcrDS0ItTivAUvU5AMBnno4KCAoilZQVm3NEmCN3gbBgoPWI4Ez5n/5SEVAMdBcILzai2L2HzZi1Xq5HrK12tP0L4RsYx9edyBtsPJEAVoOLWwD8EVunkSMQkqpt6RAc+vk0NQ3etjHGhloAID2QdQANi0h7lvIDrcvHLS39gSLeRDGgYAOrp/4uDhFSJV35g6S3oqVY3bpVc9plUwE2VVzbvweVWZZGwRVUCmkgOlmEiOFaKbJkVVBPuXX4p3BlAD1VxVGdUCFdQCS0xQ7VZm2iV3uNpge4wZBQb9VulTwhIBejdO7Owu1tpnspt9U83s0lAK82zsSgBi2fg3zfqi/NZCVh0dWOHbUEXV21gx3ZFt1Q1nFOzzWx0Tt+TJx0jt3HSvUfVY5Q82XFP1hyzSdKLdVRvN8nZ80NOkNX2Gq+G5YC3tOQ+Rq5ypyoDOFqZnTj6LuU4NAgUMx2NYw0pAgZovkUtwTjh7rh8SjjUudqWmyWWuVXvDl51dUTDSHhYIXwZU1+YFZVEAjaKSUM1oAVa0G6oxVPUc1s9cwA81X4SAH81EkYLUC2ytXuXwyYtSbXkgqtWbU8dMIHx2KyNZqxyK1RTHNl/YT9MIy6M2tWC3IAR9arb3UoAXbVcmDtT/GBdMIHnV71btTJ4e141Dd7PZa8GR7B2ynoeijqfUeiaXE3hSEBx1v+gnUWFAbiKzAJbIK5hjgCEbXG2dqGg1WxugmL9AF1vgpnUhgiZHnXRRZqkoGVVSRRs3JRhZK3Wp4tdY17B2hCT50t1aRanjt1H3bQmrFhbkF1pWhxTzJfdMnqAET1rNSpCpdcVnPXRIr/qNGldp9eV00AlXQRrtd1MufFVy6DMD3eMcLG7Xn13PpfXtBusTVkSdvQaSqad7HXJ1Tt3HYg2GdkwVqaw18wq4nzB9KYIaZF7sk+5ReTHZA3qd1PbJ3vNXHULARJbAW4lAoHAlwEYNkQFg3ya1UIzioAsoOO3adk7SL3mO5bFgDPAy0DYCyoA3Wla/KE+UjZ0NaWQw0ZR7iikD864Oda4cgv0Y4YdEfDaSDa9uvXj3qIHjXmVQh0JcpJTs3IT7RWy5zDI3bAvvfHQOmVgAiHY5Pem/Tvs30CVAYtaYNpYydqvYMTq9gGIgbhakQBKSKyRHXK2YWCdkky9thglG0zSzNpG3Mw2baBgeN+YSCUtQgTWW7BNaUaE2Xi4TV5CRNoLfuVFMsTQOp8KwVUk03sv0GG5pNv3toCZNSTaI65N/CM+0FNgQEU3fRJTVgD8G57iH46OxjjvHdNZaH00Y+cKTLEwpJggJz3cysvLnYxImBeC5gMmnNCZImUrd2KxaCVWWPdymT+KUKOzZgl7Nfsdj7M+QXiGFDa8edOlYmAdtfUmJ/PboZiudRIJazwjRKj35Mh1X81dZALSz2EC25fl15WE4YqmsOULckwwtl5Y5XHOQSIO1Cc9wqEj1EspQvASYSJe2isFbXaqlIwX0IAgpKeRBFb4tT5Z96qVCFtpx6OlLUBXB8XqJKDwt2AxqnFg9LXkIaEiYMy0q4bLbhagWE0KK0sVbAD1WoWh0qrDC4mIIpVNYBGMGbuUUZEoNKVzYoW36qGEDA4XgFbY2LVtkUcHwOg/wFy2ug5HPKTb6elQEAKoxDKAqSVlXAwUQUn6o1CIVD5BIjuIIQNYrUaUg3QPXot3LgMuxIAw0Q04OSbOBJSWALaBCtlg15Ce69QLABFuYrZM14Dv0EHb76djYqqeQPCD32hVVXZWLAZOjbeDkcbLIN7JDIuPpzDAFQ4dLpx1XOUqiglSgAQNuNEKIEN2Z4DUSb+dYDGj7IYqMxWCxfuFNgDqwBKwIaD16loO+QbgHkC4YbXBaDrcCNdRYeVpFoejZyVAJcyeDZA9EDIOtA3wP9JeIAHkTJwEb1UQQTFjskA0lyKGgr037e8lBg/epOCRQaVdE32ZRIJSAZAZoBkA1ttLHZqmDPtBYOuWtKJTDIMitA5VTsm/is2QAJmNm6PD+5RlVaFWVcGrgdmDPlXQdEHYSKKWCHcyXzet9CIP2AtncsOYgqw4HnywzNl6qIgNQHhVJW+XuypDUFBTOALYBaJQAtd+5ZmpbAHgxxyBo/g26ApIxVDTAKDaLYpVhweoJxxIiUCkwaPi6KXd2odCUeh0tWx1US0J5+Hd+UdV/g2y0SDGrdm1YW/KbUDkj3UNO5pAOSBoNLwtAHR19IYnb4kGBeRU3kKKwAwQOgDqOFCRS1B1e9VHVmHSdVA4+HqJ03VfPXdVQNNoypiQo9o8QOOjGtVANNBe9l9U8+P1YRJ3NFPfkXWjK7P6Mo44AwZ3/N79QYqmdcwD/0qwh5Vyq8x33iH7o1The7xY1HsYw3SeNNv51edXmWlbEwDQ8QyNgIXWTWbpCLgTVnhNNdZV01iMBAiaFPqf6JM11rSzUpdM9XD3pdn4aQB811/h7XL5iA6LXfyxXUvVldSnWvVy1mI2bksEStULYq1y9U6My1WtXi37lutZKGwgBtclLvZmPd10hNIcT/EDY9Q/B2FKBPQfXntuvON1KW5sBSPB1qUBOO+p2BZHVcI2gbHUQuYCsAhJ14UBAiwREyQmVEg+3bniHdPI+0RWl7Fo4LdjaddXo+shadAlbgZIFd1pWQCeWVRlUo1ikJOz/biniJbyuyo4g80CjVpR8TVYDTkZzW7Y4VMo8/xmteHtT7Wmpnqk6KGFWZGM3N//fVmU98Y6ENJjGRE8XQ1kqR/VAtX9Wqotllwvw7c90quA0ADPo+p1+jhA1zTgDDPZEkoNZdin5sN1DRw2MG3SbSHK9NY2+l3jqcH1CNEzXvV4m9FNdeokFEKEjGSqL0RlQAd/vUHSXMQfXnh0in7AQ2fp2ILgiGQpyKo3gpEfUf1tja3qSBWTNRuY34DiYzAhylgSqZji4CQyMgkgNQ3oK7EWQ5lr9gGQ0gCV9ekeAbDOATVmBBNA6j10mOYTQOoRN35lE3t9QZZ31Fj3fYk35D37hk0JunmMXYicc/VU3u0CsAcbL9jTWv3ixjodCkLNDZrkM4+irEh0JF8mVVVP9KRWiAlSyDUsJUphzR/0eoX/YDhiGd8TRFdlciDIZ39tVZrjhjF9Zk5qGtzeT3ejknaSoZA3NBV0asKXtFii9S49APPF4k+mPwDwLTOOtdd5igO6oJ5egPzgl5c71AYBWHC2SqSLWCU/DKGgsSV8HlHTEa6DtF6DQs3jn6T6ogLqdA0I5SEyPwyBLWW0sDofsgwAVHA9VRcDIFSEC0t4FZRCQVuWtsRdtLLWINGtao/0PitV5TeV/K3Fc0iKIardYizksg/Ch0O7SDYByVIdkZ5uNPFRjMas8lf3LjDylZmagY6toYOi+xgwnCutYM4jNWD6Mys76oJ6LYmNiHU4D7KM+LMlYuxCMweC2sgqdxyCgWjWmJCzWiCLMyeiiqrOkWHIA+1izvIBLPVopFqpr82S+J/AGNmhHDPzUUUjqC7ISTQOnBzcmoYOpQLs6COnDLFlCgkAQJEWjYFsU1LN7cs4C7DsVHM3PSc2Vui/KQjdwlSTPDyALHNRV5HLKAutUdRrMWz1UOljXDvIElV/tsVVmGEG0TbCNGWoHQiNxWqI8iP9gMHWiONDpJGVXwcWAwETCjWdRxYUGAszq0wKssxjMAaNENyPyaUiD7q1zdsI6RjgGrIQg/4N3fUbX993es1ETi061ZujCozuOkt7VZ1XdV96Kx0bmV0NO6ys0lewBLw3ACaPkAs831UDVQ1WaOKT/E3GOHad07x2PTUWKcgvTO46vV3Wcow1VCdno3taxjVo0Av3TaPaAsxI4C182vTYY2k4RjJPVk58uV0xaOPN6ncAsPTe3E9MYL+neUXeefecz0STGYzoGI1OY594j5MzoWPrTbsZa441GQHjWVjs0BuFVeGrL3VG99qUeGslkXY0xN4tNZeHxdinol1I2k9ZuPT1nNSOMZd441l2TjOXS7EgtBXQzZzjy7QuMo9WC1V0xSNXWvp1dJiMTCNdiyc137j8MqgCY9VeCfxddW8fbVXjlcoIt7cAyg+Oa27tVotjd3tRN044U3V1HYFlqGApHu6gsKjAI13Bp4LuZrUwHzdNAlt3gTu3aDnQTHI9yMdWfzh6wA653VnU79N6Nd2rNc0w90nzmzdXVYeF3v928JjdYw08LlS6R5/dBCQD2kR6DEIvURGxSMU5R9S3YXiJo9YzXriG42zWw93NdxZr6AGvmAld24yGPOjd1haBG1iuWyzY9ni1MjeLU7PvW+L8laEtRutAbG2IJUEUT1Cm50zrGXT5Uvc2A1yk7oakLqC+QtgLcSF80M9qY3QufTMeGz0fFCwd8V/1Owbz1ELandcsoLkA3cvoLDyw06aT4vdElS9sSaw7xJcvcklkifANeFa9OvYC7UNfUC4rcLrILZOeZk6igRjQjTMxoDe7DTb2cNE81eiy50Ze0QQYcaH7Nj6DsmFMaN+jNIGhgoMx0u2EeK2povgOc7eV5zMAJJD4SPZUHZRu1jrFPAqVAIjOTQVs4EZRVsduLMqt20BbXRI4HgrUwEsBe4PIMFguM01tsq57PyrXU8aVV9duWVNFjFU9QkFu7S14vKoPi4LCbxNs2LZcI75bMu7jP5fHkNTXDnE0WdrsCW3ULQtNckYrvS27F5Dy3Ck0I6g/TCHD9OdRKLApnTctk/+U/X5zFN0jqU2yk6jjU3wwdTUv05LjTZjDCRoaOZGNNfwQ462Jw0w6FIRm/UM3Y+FIxM2+WeAzmWLeXJGcSDALoTa2iVJS+HlodkeU93o8v4u8yJCR0/Gz7Nb/U2Wx58ZT/ULDWLX/2cTXPscuWetnGT3nLiC5OVUS6QB7i9yqiuErvNNANjgQDvzc8vv2ry8OEWQOi0gN/TELaw4vtd5a31k6PwWK1060yuZMZGPPUjORCwLllN6tcgzbZEY2CHE0Au0VGrgTE+ALjOSGjAw2tEtx5exLsD1LePMU+fpAIMJlTLbBWiDd5ThUuwXLTy18tArZAAEVwrZIO0DbM++snEfTfhikUXOHr0UAbhAH4hViqje10VfForhwiC+k4N0ICsLgg34z4BJjBDRIARtRETJExDsIMrHKywELMMX35JLupkBcswm9LPMoVeD+aSqlqCgZEEgiPXjWWgnF0oWWm6qCOpD4uAojkbkgVRtqVWaMyuvrpfn6RgKH8QeiuRp+upqCBlxkfAJtS7nQDnhZFUJtNYtSGVThwII04U0wRCJPhyMHG6bPGFkGXgD+QNQB2xqsEuQmZpJPcuYJtiiRgptBhAZGP3VA+a1DOvJHufHhYcA6cENkGgHR3PAdcI93PIMvc0iNQdA86iMlVNxKPPhQ+I9JtHtk/Se14UR/W2gAVA+upptrlZRHnVlXa66PyjQnYqNtVAdWuuJAFoEQALsNG/kzMwH84dIQiU23/OLrZiXF4TsLYuusM0dKNutYLMC9h0eaIOPAuMdfy8x0iuS22uvRUm61FI7roY0csH280UfbWeKnZcs3T4OCuvLbJ22tui9KYzANpj0NnDXZeoVIjaThVEzglr27C+/2cLC4eb3pAvCwdlVjSiWgB1jlSo2OiL4XeItTO1NVIsdjMi9eGM18i6GCKLIy8OPc1Y4wa2bGH4VONC1P0/uVFdBi8j2CwwYz82hjJi8LJmLytqkz1dViwskDo9SvQMHjpMe5rHjpaIbVnjN2l2J5jPUL117xX/HDs8I94xstu157aN0Z4HUz7Wt07YIHbTd2BYwE/5SS/6KLdvzP4UQu8df1zATG3dkApLrcRBN7dxEdKiZLiqsAhKEmdBjENNyE/dk/cF3QM2YT2E/Yj2yeExill10o52v39X2IGH0AVS2lksmYOyMVGu7gf+BQAQe5wlU1wdhiWw75k6D09L4O/oXDoVmVAD1+nY1zJNFXxtf6J7ku8pDUR2ukCWAiuOzD347c9f+AxZIuX2PrjFI7Q4V7W44uOQLctjXtOBa+T+CK5mO2Lsbh96+iPS7vYJss2r6ACfU07XuzFkUBSXGEtpIALpEvcs0SzTCxLSrCioxZDitBMoaD2reCWo9ccJZF7DYx3teBf4c7sZ1ejBhPkgHu7hNTreidc0XTfE8nlLrj20dtqsK26dvbrTyx9svLX26z3DrVtGOs/FYDfNvGxT28dsVU7+6L3gryDRL3yl0gNCtGwsKxSvy9JDUr11ABeL+sVUgOc17/FNesv4S7D66TVuupvYR4H8roF/bNJjk60nErU0qSvcNcHYPxiohEbwwe8IjX6R+8PveI3+90jZ5PB9vvKH2hQZB3LkMrGjBFNYhwoBYrmTvyvlaqM9kOCg/r5ghVTnt3G7iDxgwDX22Cbwm/2BhuGQBJvv6pyIFv+QwW6Fu9yEW42AFT97sVM19pU3X3lTl49QlVTRYzVPn0y2JFYd97q132erQa8k2Ky/fWkBhr7U/kmj90az3ET9ca/g0Jrs/UmuZmbgov0DTWa7+kLeqPhv0AKd9Fv1uhU0/LHe7ko5inxOCKsdPfU5AIOvhMlExwswHY6ztNyBxyPtM9l6Hf2XP8lzdxN4L9+3dsQNVyz0IpAIvMY3Y43UtxBr0EQPKAQLTq1At7rUNiq5vL305uO6LB5ZOGoDgM2eXAzdsNBs4DAW8rKc5mO75ViW1G9vT8Agjc0RAby6UwNujk6n+WQbnA6sBLHEGN2DugyssFo/67aMSNdH92C9GEk91AcA+uCQL1Ds6S0Kgjnt7Ldsp7YejcgCjUIjtjsOtvZLTOwxpg7vJWbQln3oJmTK/DEOTTBDeAkbXCH97ZtDG7o1OD0kbTPPtnG9lMOsEa122Yn0vHahaOlTGxtKz9osHEzDUSLKDkcCc3bYhAczKIHsUr8NIBcAfeFURqzNc9la3ifpBKKPHHoPq3RgsqCO3x9efQyS4IQp+bDHQh7f/qYHXEGNC7DxZqEjT+AR5hainhofzbcA7J9sQJtoZQJZDIgLsMhwOBoAti6nDJE8GahiEOzrSHz0171LDPvS1PBrisqnxgi87fwROnkM9lv+YoMYqp4gQJFWjg+Nw2thKE0I/DKGDDJ3pGcrypTycHULSKo1Fz0RiQN5bQAl3Ob2OVX3OlbhVeVv0Hw8xiO/CZJ6ZaUzdbYINMEGoyy3HCNgAFDJpUOhITUAuG78Fb41rn0gKECE+VjRgHaILFvHz5mKgynnhOAEL47Ogm0tI4xBHS1DNMP70SngJ/scsjawyKB0gmfcPIiLShayOewd2fie6sqFHbDdgTGDBKeE/p5loGnQ/VSRdt7Wzf2db/a7KM9bsC9tvEtl8wR07O3Z7U72Nx4Dbbeur51FKIAT83KzQn2Ms0RTbABLxqzbXo/tuAD7R50ebH3R7acWAfR3gSKdbey6MQj583AvpY11QgvXTAk3F5QXR50LA9HcF56wDHmC0hfQLN+0OU8TN279UP7E5Qtv8QHR1+cwX29PBfEXVCyhIOJRnbAP0Lqrh8K7ANEzpM6u1nVbX8FiFA535uTneDuxKBqWW6fe86tDuEeiyhWPolP8YFk8gwWf0MI7k6mIstjxUVF3tjMXTnuyL1/qCfvS0PRgDKLaXWotE7jNaTt5dkxxNr6LEttMut7Qx7LV6Mpi/Xq1dQZazvnQ7O0sn7HDi0Lsddzi4p4XjDfe4sbhqlyOn9DQ3QfUjd/iwrsvjvtcxvh1P44Vg1zQEbZs0u2GMgBxLR3vsv0AZhWHUp1EGKftoTbu0Us4TzWHkveEaOBedHzt/eUvdb5CTXXVLLS7UuuB7Hh0ffdceyjs1LDdQFk7p6l/4Mp7A9W7RGuiyiNj9LpJWPUfhQy83sWXqi+MvM7ky9kXU7bcedtzL5sosuJayy4olRXw17QOxXWy5gVq7Ubi7CZXLYiirkXVzZRenLNF5aNP7OF4xf4XsFyxf09svH1JQ1jiR9M/7X01JP/7HPb/Vc9zAaXmm+/wAxcDnBF+9cbpPiamSQMqSEfD/zj+3Rd8QRpaqovtJUAdf7wnIzQ25EfdVEEdHICIcPwMRjdBcLosF9EjPTeiPZOQTVB/pMkrhk6skMHjvY1xDXsVKzNo630RCEsHXvewf/4nB5I3WytsrI0yo8jRJICHajTmEiHe8/alJJ7NzetRITlEaAK7/YFUZdOnorFPYhAGQSf/82x+4BqYja1TfgLy7etws0NbQOfWa+h3MShtOM/hqFTPAHqeAOGoZU0nHIu0EiVTTfdVMt9tU233vBbq6+klQzU+ZW3JYm4D4pXUjsWGO3DJHI4VNijr1OxHeJoNMJH4KUkejTeg1LGLN6R1Wv1XBE7keoe+R92sLTNZYXZU+JKTvZkp5E/9RyEnoqahzQmOwDda8Q2WH2XbrQdrFzrZywK7gXbR8uvGiHqiLfbA32iwB2Im186sjHrTidF9ZuwMes61yA2esAzEG0DMXlixzwMTzUMy+C5QGFcqfX5EMbyBCAggPceSAJ+LTelz/w9QhcNBsxLPdtlRGoeFi12UqeGWlyAGziA/fLDTe6y2bi08R8p+tTaYHk0rDUyaQFRwcANbf2CAPwD7HO5b+5YYPeneQoHMhA+9wIB7DlXJsm63VhLbdhwwoaBCkbzveoRkMADMSg8aWJ8cPSFUI0B4wnRrV5D5WpNsu2wyNVyvqoAA99TE1ryDHDAyXrLBkQ6ghh+UhmIr/X/dlG8Z8+o2DpJDVvZyI2AudIVmfQg8I9kkYBhynsEN+Fak97TW0Adv0KFZJDaRLW1tVmjVHxqOt8K0hy9lNvMQBD87ZqNLpNZ/5B1naQMzQNn5M+Q0+0AAFoa0tMStI+WWpZiDZA0APcCNzgZNa6Zbysk4cNMEZzjH5bGZyZaIjkHUzZlbxVfmcV+dMFIBzngmOI/MAHHM+2BAp5CILAnP9x/ghA7gJlYfwpQ7eDbnyMLnc5HimUlEB7m24J33njq3TvyQV827A4jrLQJWUdQjxOlfz/Vd5AH3U4wmjl+tg1SxIBM2/R1gX+wY9eo3KQH3ekdA90PdR1sgKPdQL/HahfVPu2+J1YXgC/RcTPwOlM/hVMz3M8fVN140cnLvLrdtsp3dw9txeGz/kxbPtUDs/vb708Z1wD4x8Pmo1eJuOGnrHXPan4z86kpdTOIi1pdI7gVDh7+xsIGb0R74z58bI7+3kC8uM3kZQBKSFjHo6q2MqoR5Uc8+eF0MEbY2jsGXMi92Py1vY8Vzp+MLyVz9jSXT7RmXi15MhP+mXbDbj1el8JrM7ze5ADrX+KlgsG272XTovqYADAG2EmPQGCGLE+4JQ8PaJjQJ9pMKMdLasFimFc4Jfe9C+73VqzLs3mtqfxuA4SV8rsX7H41+N9jEdeldcIl1yBHqZ18pYUQRgE73LFXigKnVlXv3BVf/AV+5OnijyHaXV92+dzVUYdLVyHvdLHdfGidX4Ox7aNLhUfHvtXA1+SCEvo18i99LI9TNeDL5e2S+jLc9ctd/SZl4y8zLtT86vbXgu7teaaiiYS/HXNq9suUBs+3wC6varA0e4Lhz9VkLrqz0gvrPJHZs+B90zyPef7dz1xcHrp0Y3fuJEKldHl4sYrdGul5oyM/ELTNOM/Vvlz7W/bPdiKyDaMiWLFAQrHxWg3cB/YIgfGwW6wuiygwJYRGEvUhwouhvrsdpeYg1vTQd29ZvvKg7gpfk5CrvckR9ESFFk4i9/PGUe72QhFaChXFgykZnEQYJN/2BuTQt5qyB9TWP5R7HUtz2aMr7qZFMkDlCGitOYLSPQ4G+wJbk+OQH8GA81twCPB8QPRyl0oUkgoQSc+UmDx4BcIODxXyusD2czb5cuKIoN8P2wOYccBoOYbm2H4V9Ql21YtUTbRNuvIA5YLS+23juHTU83JeHffd2J+HEsxk2JrUdw4bplcR1nHO5jhubE5rLradAFm+cLY7UJFIRVCZGJa703p35a1neVrF+6ilZHh83ndlPrRjeek+/u8dOl3xKenQV3vD/+RI1ELdSkbT0kwAcdl5Gr6r+YfMBc2t3zKaT2d3XQeOWjPIB2zRw0+9D+RLw9gHNi7P+5l/v7rf148/nm5O/Ytz37z+Bs6Q0LQscQz95Yi2+ndZluGK3IGImf3YPiSyfrz6Lsbwle0QPsf4zzAxmCsDMl6cdkz5x6vcuoDh5Wcq4FSH8dSouV1y3I8vLfy3EvxSJl8IAJMDCAiteG8nx6HA0AISRwDALld9fFSD7VKYfX6yM0AwbUxT8AqJw6DXl57AE70tkE+4PrDnO+Sesbis7Vj6DT5qbNEgkVRbeZffJ4bAjN1c1whrfIYFRyKKOlXqvTff8rHGqPpuC/BUkGMy7AAAfpSCpQAAKRS56lXFJEdUaBcfVo5lepvRdNlennw0C6G9wCWGiNjOuWPGFMOFDujHJqY3ciBFX6jUVRRGkgl31vYIpVgjAD0ZIYJ5ZX3NSqCJAl+QlKyff/X8gy/faUED8ShMmy+CkW5fgWhFxbrHaAX37s0T+t0cpwOl6r0d3kCYw+a3N8YzI0IkRtyEovO4A/MyFhtCtIFtsqaPR8IePcarDlpte1Wnc8hq9CnQ6zazhqEUBo+CMAlU74zc04X/tzp+3MwjIT9mOZn4T3oKRPuZ9E8VWBZwAVYjHtJFYsjnekLiNbgQJe1TYyBXwAAVD0DQDNEOSOJWYFHg+jF6I2n6gkNXV501cVPt51tt8J/W7FAuwBP99JgYI334CM0gLAX8TbYaEpi0d/wPcAXfOX5xi+P3WktWYXpz9hf0Xfn3D8BfOIEF/PSuAKF/QL6f1U98Jyz72/ef/b+0et/qq+3/GQnfyF8bb+zyW+zro5Sc99v/y6P8GZE/5YHEQXf+KmNvn22MeHrL3fmNnJyoOC3xfefp884e3z/t6/Pefju+cLUL2zJdXOUOC86X/CHf9BpgMf4Nwv1h49CrphHo/8Op6L+6lMXrF0L/Di9Vxt4YC9oGJc/jUNXQCZdsKNG8q9pvJCdhq98XqjsWbPZdZxuLYx9i2kk3iAs5ljACMaIj01xorUGXky9adrgCU3gFc2ukFc7zCFdr/JK90itK8/eu/8jrtatlkIq8OYAldTIoEtXxo0wXYB+Mw6pD00rnUga5v+MSxBG41ulO4W3KbsduvBF0lpbtbOmxgSclqhRbMAgWRqd0UJvktLurnUcJiU9fdoRM8ji69VMqR43uvgksoq0sxrtdIUgDlAerrx5/XmYCOruVFBoiNcCbjVEH/mG8c9rNc+xvNd4ASosxluzEWdpKpVrnZddyi+oFlmm8bIhm9qEjYVtwv4Ns3sshzZJ1E83rss59obtl9pt03Pnft7ri0clJmc8W/qv8uMFP8ewg28xJvc9uLlF9bPoDcvitbkfigApAGpNlgGtNltgkAcK3k9c8gf58CgRv8QvlAd4ajAdZ3hlRQfJqFkmH8hmAHg01AAQ16Cgr1zdCr19fpL1w0M14oAZzc+oFf8wem4Dt3v89d3tQd3gmWBPggFFmAb8E8bh1MdEBhhGwLt9jSh70H3jJ5+bizk/ep+9uDkrBeDi4x+Dr70tNsdJq+kaRKbCsp7EMKQfmPjl3JlUYEwE1g4tvZl4jMWdZmjjkZQHgARCEHpiGor0cjJugv2NMCSPrp1p2qwJPXDAU6NtEcO4v7NjjlmgO1FwFWuJIYwyjOhc/gz95vq7BMbO3h8AD64YQHNB/vgD8jYCz9WPmuQVuCSCEINLBoMN1BvUPQQ0KKigqFL0AIMEwAWuMu80Dv6ReKBqEZlIzkPciNUsHjOhEQWi0iNpKJ9ZmiZeLKEREfsJQGZirhQQSXkHbs7cBYq7d3Om4t7Dl7dHDj7dnDnVN/bmx9A7igxOPoGACwMP1kAGG5w7pGsBMsEdNElXkwjmMCIjl/YeprHcSmPHcF+sJ8k7vEcDHC+kAUgiRYOsaUxLmCk5cmncy1hYw0jh8RictWs4Hhe1TMEfBMfK6F6fKIBnALoDHXnp9K6v6FqTNs0mJt+Iijr9tzHDZ9VVKCp7PkLY36Bg4cKq59sgQAtK3jzlEdJ4ABGHPBFQD6A/VK9Nx7oOFesrHgJjiEDZ7m88jyluA0BvMdl7il9aWuvcQgO2DtgmEpuwT4V0Wob9XCPwxYxMOkgyjTBsbll8UrgRoyvkcdKvmOASZlBs6vvW1+BjTNWCMEg8tI197AEzNO7FKgmziwxMIJK18ANK0BKjK1O2jH8PKno0msPODuQOX1ccjCB5ZgTNlsJxt0uDYoHACAUk8EtJ2TmtR/weVBroMjMdjryAq2lFUCAA94Q7AfoqSE4tFPnwB3oP60dBJadeEM60fhoL95+OjMajKY08ALlcD+MzRWfiuDQBjYwZsr3hW2tc5qITRQNPAxDbKgtgEIYDldhvyAMfoBDOIbKwGIVvstSHZp+GAuCacCNgDgPvckMIkl0HjgU0gED9vwp3hiIRVZsCNGhYuOPM6FALB+1GuN37vm4pIQBD9GMbNZUNScA5kfdR2tHVZZtGcrjhrBVkuydf7BxCyQaddJoF+DeIAk8EJKBBYEkzglclg9bOhxDjhB0NpYBeAHWrBD3CHtgeGAzBwjv5DDTp3gyuHL42TsAE3kk3Nf2tb9W5migffmmdwCKE8wOsVsIngVU70u797BjcQ4npcoYohWVLzh2sutmn8ULr1tqnln9r5n9oWnr09qOp+DiOoBdnYMBdAuEBdhIYCs9RmaNoOARhVfiRE9wFhEh/qp0DtgO82wSZCGaIuDXwhdoNtn391rGhcrqsNDAmKNCcKhNDlQMjdaLiAc5obYxuQJ2C2MD2DSLtgsuJnP9rtlZ5qLk2CUbodDrEvNDToUuCLtLc8SgU29Ivnv8fugf8XnvKk4vkeVT/iBtZwt0sL/gTUr/mF1b3vBx51K/9CPJ+NBDkQUkJK/9NwfhRcyl/9y3Ei8urqlBUXlDCAAZuIW/NItG3KAD+bHi8rxLADSXkONfAcRBKXuotqXr6k9Lhb4YvnotMAQGB4AjgCHpngDFZAvVPLmvoSAezDUFltdKAR9ly3J11Qrq4tTVqUlAxCjC4gbgB2AdI88AAEsLWjwDaISkAQ6gIDFPFq9hATq8CrllcMiga9jdka9xAWqxTXiAlSrnksXdgUsMjj7Qr9nVo7XrNN21n7s6oYXduPK1dQ9v1dO6l69QXqlAbAU0s7AegRCEijCQ3ljD3ARIlw6vLUFrjG9RxkMtuYaE5SAT39zZMbVCECY4t6tLDWAcDIsAXy824gkCaYFgVkgQW8dYS2Ji3mdNZ1mW8u7kv8Zoe0cjoR2DFoX6pigT9dSgc28p7tpMZJp4kQbs0Cm/ms9WwU9DjoQtCuwUtCxetAdIVvzB4DvO9MGkgchgbg0vQJ6DM2Lo9JgTYg+IcCUakNccixijCN3tGhoYnUkLATphVYWA53eqQVkMm8Fbep0k6Dh79eGoRFV4cwd8yl703uKrRjUMo017n1QFBFmF3JgPdLiMCCM8H+D5ofSt1Ghoxo+hBRZLvNDxRGdCgSvjcH8G/4CWASDJTqg1WBLbcyQXlQP4B0MGcgScUOjuD+hrlYdQd40QHPHdLDkaQqPias7Do7YGvsQ5TQQ0wpjm4cbQh4cUJFx8UmuicIPjBFPXEEccmjGt3QV3hPQTP1vQVEcymvI5KmkJ801q+kcDJmtRPs2pxPiGDMIAegfkIj53vNmUPksp9IUqp8KrhmD4UuwhkpFxxAjHv0pmqI5hDvMkpMCf07iEbcMwbmCFMtVVyns7DiXL2t/xE7CB1rw85gMOtqwUDdx1r/13uBkC7rh3cHriP9l1tyl+hgvR6QNi1Owh0Qt/h9Cd/pPciBLxdXYkDtgYR51NioR5m6qmVp8H2BQ6Ka5WFHwNcEOoACgCpYgsirZLQCjDfqDuD/OqgDnIo24kVlaBu4PZhQArXEvenvtsbt6YfjIaAuAPnxAzOfBWZqbZsbjUMS3EvlWHJbA5WqSAdwYmkvWCrMmEG/R47LBNbwFjAXQnNA2xmSAakSpAOkNUpkAqjARQJUcauEXhQwDuCMEATx9uv7okEXkiGYb8R0doUiRQff4cbFSDGasccy6EEBnnDgEx9rBEaKLCk8LkbAclBkjWkda126MmleOKXtvHOsgmZrg5oqjcQd9jTg72pfdnvqrsdlkG5o3Euk5ureAJIOwg2SilcYfNMQPKC7FWZvvomAIo8sDpYxMAh7RORsMjdwB2RDEfNMSwc8pY9m69PMmHsJLqC9m6tHtrhLCBmloXJmkezcg4eDsYkYFp5AdoUuAFUjjZFMjzLh0gpLgGEo+EgQ8NqSjK5C0iNLi4CjitEi7CtijWUeki1LhkEOUaQZpkRIBlwi7BRkfFISDK0hlwCioY9lHwaUegRTbIHDRUSsDGUcNFcXgMiH/McjQApOoW4lNJkANytUHmHkHkTKjWkUXDieoc8F/jGMWgWM9PEf4NvEf4ZFHsMJn6p9dn7K/U37KMdgke8tv6vYiagWNksAHUCjfD4MhtC5NWHO7dthr8FkUb4jAch40raKw0D4e0kj4Vw1IhojldspI0aYBAgQYnCpHjiSMFbqzMoYto0F8oYgfcu01YQtzovAOH0f4fMlcYsytEEcWjWgJgBy0eTc25AKRbHHhtJUW2NOUTMioTMqBVkRt4MlPLUx0fnw9Du8iE4C+CpWshZ+wLmAL+vqsMbg7djkqqFqJoIiMmueEnvtdAnkgEZNCr7lHBJHc2YgGCvkgNMIfIaFN0RCkzfhncfHhWt0JkmCZptVDk/rVDrzlXUtOIUcLPnLRGyq29Bsu28TNrWD1QHWxuykBYjPnjR5DG4jl/sus9mIEAaEGop1iKX9/DKqsMQQEj64Z9Dd/i299/tr4/oUf8AYQb5J1Gf80suHtiEka5fOmDCkJI7Ud6mxhNLtf91gfjVdLpItdkVi99kVoUCPCUiJolNEoCleJykfEo2MFwBiaoxjkAVeJOkUSAXkaL40YeiCn0CNgoUcWBkMU0BSSDApBLDcQ0MSlcVMRxw0MYcCEqG+ZbUeygPgAWkdBPBhFECV01HsghPbL1A+kWkB+kVEMOEI+wm1Ab0tVuHdRTnQjktt2J3MalpOhmCc24F5j2bPBxqClBYedrmBpgYZiOKpABjMaZiVnOZj2EFLYbMTJj9RuQ1sxlpYXMVLQmOFfdUtJ5iQwFligUSEskgaCiVPH8xZus3YtdtAUyBHrsjYbIIjdvlcrCtG5HBv1w1BDe5E/mHkOtt+jU/qYijAWlETAWqlvmJ90uri51fXjgkdUQNjzAWJiEWAajU9qC9fOiNhQAtvVw4oxivfqE5ggEEB7wHxiPCi2dMMU+gtMe5DC5D6UsCLQhnEU0dbodGNCFuXCILohiBUihj5fGhiFfIV9IDoGiMvIz1aFhF88MU3CyjvSkvliDc7ojGiJsnGiRUI0CAIe3DLsT3dHtkhjiSKhiGYOhjHsY8tnsRUUbgkNp8Znu8OkvmiXwOSsgPAxifQGvDbZoqRsVoQ4YOmbCe2vN4EYpQdAnCjEaAGjFkEKCgtCof0NGrjFdaG3YC6ooUUHsQ4ocaSQ9Mekh0XB4RbMRyBS0rRwriHOA9aKqjksZdk7NCUjYOPugmYoVMWYpwikypzFUyrqFk0dM47mNzd9uPHZXdNlirQgFYT4arjwoQwiJRHpjopiWiT9gDoT0b4RJEdwAP0fhNSnsYj9Pr+iXlCQAijqtNWCCDsSdNbkawUqcIMfuAoMYdMusbBj6jidjS3vOsy4cP8EMY9s7ps1lM8sbV/UdhjOLkEiBwZr5WFqJwSMVOFgNkHEQYf3VCPDwtRTmyVpkvnsDiADkIYeTViEpTVLUn10VCj3VqIvkjCYXF1MdocjcbCcjm4lwNrUUbBjapaAxcocQ3sv4FLXHDROXvb4bMVLY4aOjcLURPMl5sJRyYIDhyHKm18slzBbVgqwtHNQCu8QDll+jKRcqildIAniApEmIFO8XD9u8QW5zbHD9zZG5FXwIsjokOKoyuAvlFGDGBRTivjwDIfjGBCqD+ZLYR2kc5VkctaF2LAqgWkMhgTvAQYh8tv4FkffElkajBFaGFN78QdJN/GyU2AlX1sfk3hSBtadLQGBNx8VuAO8YyE5EviFC8f9kmnnvJirElYv0BQA5oCEpOCvl92kBATDxsxY+dG1iUOnbji7s1c8UjBjr+CxNaEmxNzPnT5ijpSlWCEJd0CHwoTpjgti4TdDXEfdCDoZykUgFHi4fkZlY8f4i64Qnjv9h9jBwcBiW4cNlfsdGjVgo9F6gT4MQGk0Cn1vBQLMftCfPqITxCaqtJCTdo48d0DsvLcEIkf+sy8ogSfslXiqGle9C0lggcEAos69sVwhcjYcWQAIQwMQgBOTKR4SxhuEc8ejcWGvvDUcZDlKGN45Ecn0leHM4TMwlmB74cRgzcmMlX3t5UuTOhRCKKxg90DHRpkpjFpbu2jHsD8ImcWd4pDpghYSAOjdvJaFW8W3F28YrlfsvviAco/iR8XMjiwEASaIo/Er8SxjRDLyDpcmUIFQqWEuvFMtMyjmUdnI1NrQSJ8MmrOB7ks98mQVaFhifjov2Hwprrp6FbcXoCnXiYiXXrHspJsc09oZpNl0oTgS8ggTYNpeDAiGSBBvOzQIcubIzTL2BLifLkx4LmFLiS7cfpNYVuET9JnUTOtBCaXCvPtNCrseYkReF4jfKKYSVXsuB3mnUhPPO9CcMYniZgiEi2ugWNgYdwScruPg2BpEjullvDogopd9stPgUGMuB1AKbl0AEsAv3uwhnXGUg93ErDkrtWhs0fyiviM8AufoloS2tiS/fAmAVUEMUOiQHFLQG3x10OSB3EDiT5TpTp10KyAqrEycQEH7ZGfO1CIILIgXYOsgmQf0Ncrp8jYOIt82SeugWYFSFRolySGSTCxeCBOBlSV4TJ1P2oV3KSB2Se1QT+JSTu0NSSN1IBwv8nSSH2KblqkPiTjUexiCkXF0IEIujTTOcAIWBNU49GBgOABMlpQFZcx6iqCMqLSFTTKqTrSQEhWQMaS1wKaSbtBJjV1DsjCbC79YOnRQqiTcitwAcJZEHoEo/IsoAUa41nvmz9BSVR0bwKnBuAb7VxwKCT4zHPtjhu2gTaJCh6lNPsVjOrsTCuCjysbeBC3ocs7YZ+jdPvbiCwVgkfoY/B3YcmQqfHtswcbkCTYn8TvUQCT81ECShhKIgGeBZlM9rglbMjNiqMZSBYlHntwySQBIyRvgdtvSSQycyTsihmTfMgaTrAJyTtyTySNSctV9yRm0sIrZ1xSZKSZUNKTr5LKTUKKZ4p9oqStSVuSrSaeT10LqSEwOeTFwospvySqSTyYySzyfcjgyTyTQyXnspiseSPyYyTAkBsBfyeb1lyXaSSYYzUMZK6SXMO6T2HI+RvSenDF0ZaTuSYyTQyRXpCAUgS+oDucOflB0xHsHZDsV3lg8fP8CFuW8O4S2DlyfdpaBnTQaSSopJySCSZyTISmeu9iw0X/sm7q2Vqgd8tQceHiK4VGFRyWxTxyZxSiyYaBuKZ55zCagBLCZni9JkN5GbsfCC0dYwwKfVB8SZhAbNDqhJySWSGeMUMdUDWi7ZgTi3aOSTQcoMkEiSMllZMkTibqkSiyjTAwOKvMDwKJhwrEIc20eIQO0XLc4OnJTZHvGlCHmNlIgIJgaQl0ju9jBSHSPiTf7KzMZKSTAOScFFPlGeB/gOshXQFqREHkZTLULIhOCg6VHDH7wnwBlTKAFlTAqRgA4yrN13UgrjdcbujAdhmUBYjM1/Suo5LQneju9Oe1foPO5/DuicdcY1MbcT7s8wZ2TdmiRM6Cff0TPhGZy7mUJaAJXdijjXcaKHXcpaMGVSjudFPcUDdqwoxShyc38TYo1JnoRbDPVCQ4IiV2RwZvHi+KaGik8TKk90QcZj/oDDpwlYThEohTrAbEiuCPEjkyoFZzYCItUkRe0FbjTZskfBwdwbD5p8P6A3zLEAsRAAANRmpCYmTyVI+lEWybODJAaVHuZPkTb5PnYnqVma9I/nEH8R1JII/6lcEQGmWgVZDJMNmClIxTynIu2wXIuKwSIJMmvIftH3UVBGcjAFHM0fLHHor5GEIVp7I8VyGErSABswaFH8ebwAxSJTz1k3wo0BZskFwntw5xGQozib7A9wuZBoTOlGPIhCF5KM/ZtIhF5dedkAuuEgrQQjeYYQGmkK3DBBsBC2E3oSuiCDJCovgTx63lc2YGqFcBJpWTGfKNliyICGkYyWGlso+lEAEb5R1KI4D7UqSjirC2YJDC+FtKCGkxWfIAUcZ1IZBPFFlLAwEGfIlFaBSCRcAPrGLk8oLWA3lERmC0lVI6bGokykAPU5lEooZABQ0jJGXEx2nw07aBkJWPZrkkpTs3J5FdLbPGLhB6mwTFzS7DHOkyohsCW0uzGJ06lEHZVzLdohlER7dOl2FaCZ77QOHGyfOn90lbF407GyE05vGJk65E2oqLGx2bJTCoka4t0+gBFkKWln7Uuly0+aEK0n1id0pcnV0h7S10yGkiYgumyog2lMQOjAa0m2nm0WRCZ0kiIB0uGkp0wDCm0vnGvIuimCEt1EXY8Sk/EjuDp07uEdg3amgEA6nm0hgCiTCElyEgSmKEz5YiUtuFPrfQnuI8xLbUnuHdSH1h7UrZAo4ABkDw+GoqU3SY5ogyaaUjHFREqKZIIx2SyHLlTBQ3W7ofeW4ZI6ybAhSym7+MnHInCnGJoo2BpEmmAIQ7+F5E3ykFEgITAfYSE3gcvw2KCKlAISWkCMaWm/cEl47gokmuuBMDCYFKlbgO2mW7XGk7g2Gl8iIQZB0lRzvECqlQSXomeNC1hFhNmLVUx3L7ZZXFhIzw52PTql8fbqke5XXEslGMH2hEwiKqDUCOGRRE3EfwwhMStp9U7I6rE/MFDUlTKkTLYlbTYqyVkOiYjU4z6sTCanf0DiYaxa6Ht3T4n6xb4ng4z+mI6apzlOJqSouP1Tgk2Qn8UgcHw1csGAYKz7pUAHbpFcJHl2OYih0bQJhzMKHCiKkhu3ecmEOSPyLhbGE8YPVaBxTBmEcDPpZcCwDkEZ4AoucZBsoI3DLQZ4DCoOnQ1aRJnsA0Lql497S/UXpnnwL7i4kS0Bf5QZl2MWF6HZNjD8QWyTggFFa8bZHy03NsYpMlaibBIAQjNSOofHcZoyLGmy9nX1x5Yr1La1Zaj0WOMFQELVov3eP70bCkZEfSpg/ZLUgnEUxjT9GqjaUZuyygcdgA/UOnHzcOmO4nrE4JWOkevPhJR+b2EjY9IotMlBBtMjpldM40D7uAN4ew8kBDMgbBb08oLew2bLzZF8AWk0ZR2Fa7ifMx5nXma5lPo064X+NYZREax79gVKDP0qJmh4r4n3bTan/fRmg1OZJnjIWuEI4mhZv1EBlnUsBnfYiBnujZgKxoqkSaE4HHlQMSmxM4clssxJkhJdrJcs4IBoM7Ly9A0ImbAvNFdJXBnWMUZSsYl/7AE2mBUIAqALEZBgIAODTEsuQaUM7RrUMq4Q3qe96sHdhBwhJBxyNfg6tothl1gqdCYUfymYgOFk1YTCEOKTpkEAVFw9MvpnCoMtHHIYNl7eAmr8M8CIKg8NkbIRYTGgMUAk3ACwLpGfZRuNc5lcCnCxldXFaMndEq4+poHoiKJOgzMpnoj5IXonRmlNa9HknW9EGhbvSyIilnOhV9EVXLT5tklYkDUoJmGAnxlHNPxkVkDzSBMglHIYSvw72cJmnTF1ElwplkxMllmdwlKA2yQ3TL0G2RJyaRokgbqTWxD0lwXP+HuAdHR9gnrJQk8Ra/JWSZeGM8AaNTHaqRLbC9qb6DqIbPTM7fgo4HWpKVyBmBxIWHZ82AZQcsDICpQSkAMAVKACAPUCUgdxQZAIgDFAXEq4dBirpwm/4aNYPS2adRC/2Q0y8zazSKeOlzgQJXDYzbkFbfbRCcDZbCPw35T7dGZBsUWCBxYIBBKWGqDEwC0COzMojrgDqJZrUUQhVWcBB2fPCL+aMl+DTLSbsqDmgBdcSeA/F7KDfmkMhBsnXyPDmMwe9pX5MBQasKXJRg4UZIFCpA7XWhCfpBDk/4Rjll4SUg5JWECysDUbntNCrhQwJYYhQDDjyEAgzeUaKPEZ4ivEOgCsgF2CgYdPQ1AWkAAuBIgWAYbB+kdjR40ZmhUcS3p4IPqBIQ+7C9kbw4hYjjgoaQEpteEipU5MSDWXUuzIOZjnEUf+E43HeJmUAUApzfWr2+KgEb4LIr3ULm6p3Xy5XyA2gMjDtTaneqD4GYokJdPTkhAazlKpFB78EA3ZQRR1LbTbomxaAJz4cf/EeuGnTN2Fs5r6Yqai2WgwL3RL7sElZrUEh15GIztmyjUOSz/AQmMszz5Ts1o7Dk2dl5mTZhIARdnLgWeDjGDaCXaLbCmiH7Q80bdnhfU6l7sqKbT44A6cpcbnzsqbmJyGbnLszsjzc9dlLc+iQrc3ABU0CJy9TLqbKU3rRbGDtQngs46pzKskM1LkzrjZrlloZ0nvSU7l0SBLytYUvTs6RDQUUUimKcwuSERZ94FoH2iPsohCU6O8KMjV0C0M7Ax/KZ7RN4LDkjQ+rkDySGTs6Yb7PoQATgEarrKyShheYlkzAISHlGhfsQfc/xqi2YPQpXePLdE4vyscxUK/2BeracyzQgQXB460TQhEGZKTyoyexW6XqG3gWGJnEKXIcrTwh8wPgHyk49kaMXpI/crCnXs0bQn01ZJmvb+IyDIUnXESaYC86q6rUaMA6tEWYEcztzyAwFmNXYFn+hfrkRMwblVZSdkA1UbmbU3bnUABdkHcrmhzcxQALclUirlS7kY6bf78sjbmDEA9mE6PrQ+VQBhvEO9l3lFvTW5Bhn3UECiCAJ9lw8/05EvGhpI2Vxg0QUDk4wuybVbUEa/YYmBQcrk50wGnFwTLvDJwXzTTDRTBotWXmYgdkn3+M5Q6UzQg4NfECc8ukqcg8MEKATyBPoT9oAgzmKgKZcBsZEarGnfsAm4ayhiQC0DgqJrDvcxTzriTTASsLDR4wBG4Mk0P6MAOJCCgRTQ9Ea+jezeSAFleG6+oi+5FI3lCMZISB/IEKhZrDf5IQMtBVzcUA34CcLYfB0A/8KmhX8ngCH6Z+BzYf/joBATS/UUizdE9Ll4UKDk5JXzTl8pymRMNcB6PCaDmoeTG04gc4dmIGJl+CXRb4ffk74WnlMxVp76VPOiog/Ih1gBLINMvDZpo1FGWoZaCiobqB+AXQjG4eARNYRMj5ZZ5CBIPgB5iB97klGbkQ9WAWezEiq08umZtoLkycogjmAGVzlVpJXAq3N4hYsFchwIFyREC0diTCO8wfNMHw2AB9hVEUlkhgbNKb8XP65XaUkEcipAIPJIhSoMVA3wqKEJId1ya5C1j00Thql4CDgf8lVpf8lMS73egBSPf4HLgujSQYLR7tgLkGF2NijiaDPmlnXgZocjUG4jGVBctMaEOgQE4ShdzSE4ePB4gRPAvHMYZ/8ieH4gOKmcWAsI5wq4ihY7wnG8lP6m8mcwdaBllW84bk28nIF28+egTcx3lLsl3l2JHlnBogcK7sh2LGlK2jm8S3hGwSnLuU1k7+abfwuwNKmloDYAp6CgB5mCwD/sj9lfsn9m0AP9lvswDkYAFgRCULzHR8qvnPs+HnKoN9mdC79m/s/9n9C5+RdDDVkkPO4TOFR4nsgC0z3uH4rHHA0E2s7QoZQZcJO5OJGg81m49E7+iQE0ZnNjSlaWQLG7Y2JvGgBA5m9yQPi4oGCH+AgqAjw08DrtUgCH80Qjmcyzn9cF2AGc4VDWcnw58EDRHEOH5mSiGOB54Nh5SCWSG/rdYwUjcdg3uXNkh3e0HFs7LGBHKNZwdehFtySDJPEF4ic6CKjZFF4lrC0UGjocAx8OQwVhMKnxqONMruQZWnzqD9xn9cwo82EtA0giRDSIkWL/vOZK96Vymo+JrweyHEDBsZYn9UnrmDs4akj2SxH0mPO5tmaZjx+TbKbTC6KrU1IUjlBilh4mVl2840Q5CqbnzgclQ56LejdSZOA4yVoXd6IBnpM9bmlCrBkkrdGx7OO2qpLVYCMGC+JyHe2zk2OoWEoBoXMATsJVWEaBGgeHRCADIDLk+YWibVfgnUMCwogNMBREXNFyCQnAvIwGD3ojsxpU8VZP4XHHVyQB5usgD4aMBvS4aQuw0YJ9yZI38Dd7SqlnGLgAWaDyGiVY/pkoK0pIHObASnSAzYUL0VP4I1w1ASkACADIAMADli0AdKD3gRKDMAHKC7CSCYK1UXFJpP1pyseUlAGLJEmaddl840mZxi9cYMlc/6m4r1h8pJaF+XEQQec8qm03R5z76LJrIAPVAGqPgDygExD3sZvA0oJYW+KZGJOsorxyNEVSU+KJx+Ze9zlMO8S5Mh8xDE9pri6NgapUl2yAPNPnEJD6CYkm8L3s4F71io0CNi5sWti9sWdi7sW9i10A9jGy4vULKYvIz1opY4BDonABRgTLcC5gagoTiNkA9TWcXfLWYmvisvSNgekR0KXUIfilMUovC4UIcMWK1/V9GbfL5xuMnT60EsUXeMx/qDshkxxOGUX3sXh4e40dZKi4QkGEq4ppANmiJo0pzUgSXgoct3lgYd5r6Q0KCe8ndmblUzp+87rSHswPm/QJ7k1fWECh8v+EkbU64k3f2YFPEKiui4F5rYjAB42SYWfs6YU9C2YXFACZzAUeiqiYb8XjM2m5VcqNTMC96I4cuRB4cmlDsCySjEc9gDkGL1hR/O8mcjBKlbdTnKOsf4Bt8RwpywyDk0FOa5sRHYTwbP04fhLjnsc7wmXEDTGTTFkyUCeeZBuViLMIrNJOSSjmxc3vHG1DcAOKNznK7LDlec8qHebXzmF2ONKbo+nlhMLwyQBZVL3YAFAWcSMryPCEadKPOJInZHlkWWk7IpV2KAEJymvRPpKBALFiZ1f5K9UaCoylPSyAiVCBKtISygBQ3L0bbbEfhHJJqcxp57YGzoJCzrFJCtWJvEq7ZDc+DESU8HBCSpGSHDUSWr0QAiSSuC6AEVbne8jJm+8lSUphaj44JfSXxDMbInCu2C1ilxgmSsyXvsiyXdC3oUAcmyWHZeyUgQRyX7Sa6Kxifgy3cp+Foc6rk5cuLSY8hTxNc6nm042h6mtP/nrjVnmT2J3IEPZH7tIWlqbsr9hTS0QCyAf5LC4ylYo+Xy6f8iMyTMLgALSkIB/8tCUrCTjSnEFaXsy/NZAywnZzSmKqE7HmX9WNaVBNDaVTnWTnd4NSGowf2YEdAwoNNbEHHpDKXKQImU6tTpxNYPRoXsz9gH9bGYHSx2E/os3kpOC3njsj4nW8i5a28mdnCSokbeqKkB3SiSW/cs0QXcqtRrcie6ZMm7D+8+7k0kR7mBwUmbjYF7lQ8zHZj8tfSEy8aXQOLUmWgFkyygYnmkvRQByws8Dk8wGUlI/xhI8iMyoy/ggY81FANc7/wjnNoi9cIcxACcyhQcVfTK2RwRU8zpA084Pl08irlhMJmVM8nPQs8rRZBWdnkVM5h4s00UkyeGbRj6UU7zKKGQ0rfmYyoGcjZANviPAU8X9SxWWhgE9n5rM9nu8v7k80BXnebLiAXMIsbORAGVsyEpEdC0GUzCvoWQy1dLQy2WGBrGTxDI/zn5aKKWrjX3riJQQExgOKCMlFCZ5oPEkjSxKDxQexDO0ZABkcvUCLQdgW48ophS7IcCPQDZFdctZom8gu5tGE2Vjs94lnS/iUwMjuBXSkSX2y8SXnQB6UCpHiiWBCojnQZ6WBIn3kWixGXvSn2WWgdSUByhwjLU+MopHEdTN2C8Xw4VtKV6UJAa0briofHt4EAdnROs93n+oGXzoK2hCpygaURmQuUE8jy74ItKIQIY4DE8rQVq8gFCdEhNHGC1uz+Az7lG/dhABOa8HbEJHDEGNgUfg0ymXEAeVkQGTlr6XhUjmLLqhyrRaXoNtBns2FhgsE4iHqTeXmSroU7yiGVYABmQb2UDm6ADeKKrMTSPvEBCaKizhGsFIDqI2taly5CVtoOFRWxXKyjgLvSNbDRqOsVuKDoRwT+/U9oeVOGBI8LhkmKthWkAVkD9qBECOsQJX6zMrhxkqMEGy/QGgKvrk/UCLpeyznp2dT/ptlbabcxERA0RNtS9cilx6Ak6Vt3NIXnSj+n8QOBW2y+iQIK+6VpMk6nuyqEnJ4554zOV55Pi0jFAwzPFu8Upm9Sh4aWgcFkJ7QMSGuISVr+dpENM66CinMbHSDYyUlIpZXK05YH3/c3qLKyiVf5DF4cY4AFkwX5Rls7ChAyzbHkAV0Bsc3mrJ1GCJm7NJbk4sEUooVaRkRYpHrYuyRwS5giKeZLHAoorHdOXjlQJD1A9vfpxKHPV48cwWk0UJTFYAsFwxLJ7yFMDkCpQZVh9MfcRw4/2R8ADyqWCpwqHoITJUEttkii/FGMEkFkEYokCzK1FmevOOnjXISU+wv159XSlWQskpFYsmlUJ0gblmyqBWL/d+lxMtpU2ym6VdKiSW8Ut7HmiwfIbAhm40Ha0WCi9erHitvQkzQQUOiwnlFjIRVOMQlDiK03oHy1mRSLVzA5cONBcIRUDBAcMWYgIwps8nfEN2RXSBUSXGJ8t2IZQHKBpi7kVv0VwRxgCeR21WKWwca/JUM0jz+zQE7xXObCM1TqV8wQFQwJAIzboafp0AD6AvqQkiG8CiwHKCBLTSmmXKgJ6Vi00h4WQODY3i+84rCvUE/SKY6Wo2pr5MvmL7o0O7ZkpmncY4kU+k7NVAw4RGBgtOQF4q4kpZTb6580NVuK6OjpGMW7No7jAIwqiU9NTwivo5xnA3Iux5KtYkO4wsFmIjc4WIo2XToTFLxwAoyt0GoxCLVhw8S3wjFokgqWgH+prUtUXTslsFpARHTriCpx8800W9K/sFvSmA4VC7yoOis8BDLMdHrHfOA746vQ8VAUKaEwUAjC2Pkvsol7FpPICw7ONBgYSW7UMMH48/cfhaI8Qgnq16Lb4lebcUf2C1i3Mo0UC9V88n2iopRKDs2P/6qwIXGygf4B0QykB6FUPhZjO4RV9QsI4SxULh2DawviqHy1U9IrGpT8XYw1/HK0mmzrygrT7Id9pjQL9UJywySBpK5VwahDVr+KrjKacNbBqRiJBqaOo3y+OD4S4jU7SU3rkajjXO5ZYLES3MrtAG1DSyDECP4LMZdxCzYzNMTkUE9BWZI5prcBd7w34LNaiXZuXhUIBWlLIFkFKkFk9rUdWkuAPHX8ECSrEziVKqWZjXqtdXMsq2Wbq7lK4xMwmFC765mivpUWir7HN3PtWCSMKrayIsYWQZSKcvVREDYaWDHk7BAIMxxgFAbQH2ID3BbIGzHApLzGhQsPrINbJnz9XaZwqFGgrBMRxSUcGi5oxGnus4UR7IVHyOM0kicFEZWfeCd7dnHOrXdKKyV+b9bDQegx5lYLwDqzxnETdUT2qdlWQK5pXQKiPEdwRKCadJXbHgbapzWMbTMALeg06fdXCq7zWiq7SZNynylv0T1C6CDGyuLFVQHCWUCXtSiDXtAvRRQwj6giUxW1YGNBFElcVzQFjmOGQ/qvBb1UcKW45t88VSeoHJGZqwphBlOO6W0fDVk6UYlO5Px6GQqXzqC4Oxr2O8QLSTdHv8VyVsZWtV2FM8B78Nxzvw9WSWCaGRXxaxgha+9JRahmgWw9YX3I40DXdRLXYIdG6PoirVg8AIQCi4yBCiqqHts0UUkq4dW38cxEWao6VpCalxVgobTVc1ykAalbVnHSIZsucoQDai6UdwKjjGiGBr8pCZLgoT0VUAXU6zavlmvSnzUkKw9AqqIDUZUf8hsCQZXf2UyKKIEKHdIl7UXgd3J+gj7X9Etlj2ySaorMAzqJWA3VsYCzIkiPcib8SHVuqhr6w6nyaOmBHU62Slk28L3rOFKHI6FaAw12MJRebJb4Ak4L7o0Reru6pGQTeSsyRmWPghAapj5ZP+QSINGWz4oGCA4riqGYOaU3gxdEEgMAj0AXAXI5ecB8zfiS/S7c77YOJBkYP0iZ6riokAZNRoqlEx38WzX3UMInFa9MVCiGeydq06iVhJvSGah2H5K514GfZ2DXyesrU2Q9Bzq34oKi63IYq+fpf8BFDT6EDBYqgCTIqFpU8qviA5QMVxICGdjfSWADKnGSgS6kNHza77ZV3KIjLCaDg0iZSiIA/YTiqXpLIxMrgqqGA668FI6veUgY4I2Ta2YNFrJQgJhr6tKx5GNkT08JJEdmGA6nMKMBggyPqlarFWo+XRXh2fMUdawaldapaY9a02V9aq+o861pW9CRHTWUGoDWUbmgNkDfXFChSWquMoW8SVyXy6m0yI6u0z8HLxx2PUHDd6Yg29o+LSvRHX7es6LHLQaABgAHKCoExFDnEYcDCISsWhgDNCeRVyLQqVALRoen4moc/kEfXTh+sbSzojTHKZuKIwS0skDu64sWBcrsA9gOSY3oWgUjNaQ2tgYPVqy+eocM8PU0GxCjR4fyIWaGxoyIJCDTi7vSXDGiKjqXzQZgpEwJCavVciXOi6DOvX2qgA1aM19hVoNvUdYw2WWa0zWsSqnUOwRhQBOGDgoaVsBAIyVQcKXzWgY2ZzWmRzUjczIWdwzlilOGyjlOB1AhJabUWAdA0w1MoFLGHfUo5N2iOGEDji3NWTM6WEIiqMrgwHAI1HCN/U7UadVJ/TswOGv/VntZHjMxS9GlNB8WYuOoxjmGo1MS3w1qxMOQaAGcAkgNwB8QARilGbYDGJHxgVmDi4HqkoULa/8jXsRwwIUXXXM7MrgF+XABG4NwDggEBhykOxgjG3PCaFQBqPozMDeKSQQXGd/iEUKfXmbDqw+XLRDNgMVD30OFTu6++jXcpIxuKyOyS2WkB1RZGgqaYKh66HgFsBXhGbcpz7TNGVxMEYhWeiaChNq93W+9OuBbKfD49jW9CaUbJU/5QbTniQBzN/Chx/SWmjDOStDw5OWy2c8DEQ0AZRWtGXRWM3JZ0yrY3cgEY0N89aAwQd5D98ezr6agsB+kZsBYAGJE/8cfoKJf+yXEfLjIAEnXQhMA11K42UpC3rWnSgxKqipzWxGzdUi8ZeUe4ENKrsxcxjCTzVTGzA2NqewwxHMfp62bCgQpA9Ja0Rlq3+QnVbSS8Fm0OCjtNK2iGmceVxQL76I6vqZSSAo18qJjVulGJglGpGwwcVCheCC8B/87/VLqqk3KyWvX1G3MKSDTk3aMufpcm4nWBsQUX8m5iWQG/CDCmppWwGrlXqimdl7MM1mdkYXWZ1dI2/XeQmy0Cib/GsSTuOCW57Cb2gJDMo2uSmBS8la00um8VSOYyJWtiMLgV6qo0f6/Hyem51J5hQqY5IStmT2MrU2M4pAt67/gmqCM3dGnpgaiGM3ufXiZwGufX2YieBbICeBcQflIow9M0Nwr6G6mG038HAVSPcMs1uK/fUxMQ/W2Ss1DumoTRn6503s9PzWVGuFxV6to0dmX03044uiN67UFHJYmTtm9mJGYK/qdGjxngG0+Y8GH9BjmsblR4hjDVOV3lCqyXUiq7fXNwgA5toCGQq0ss2N2aRb5Glc18NUOghOQIjNqlJjx0NtUTpE82BkbZo5lIoz5hG80Pm1o19m4UXuMjtmRmrZq06gU0TqmzXnmmZjasBzWz64cmysIL7sKs4ieqdALajec24Y4JFZm/6g5m5wxdm7U21/D6VEAntVM6YIzKqcIyX6jVRJqsKTAUIJj8WnJlUAneyKyeYm3+WC2ayIMpBa4kDwW90yIWkVQoW02T+MWFw+PZs2P9HMr80ZPXbEXNHUYNnXOGpo14W/DW7yNo0MSl83EWgc3dalIVfmzanM0EXh94Wpw0CeU1fXV7GAWrfWKS9ylOsK7UeCXi3US1wyCyb01m5CRoVrA00yRI02L4kDHNwjnxSW7uSWmss3ifaC08qPM2FGuWHQgtebLZYVTe8H5YyqWS1uGGm6GW4dn27HT5Nm1opxvF5CN6q83zJGoygG9w01Qzw306ty0kAZUUefOi2eW+egIPfei/m7ll70BU2BWzfWHqnBXXq2q1eszc0hc4UGhgC/Ws+XXi/0J7hfsW03xGAs0+6hGrFM8yDaWhSRlW+Oi+mKix6IDtUbCcJRbCBKXq0KSTZowMo4xLQ14xAMA1bfEDuAKPhlcZqZZNWcCEcYQXPtGwbQlfgirmXwkUPGWp1m081kW0y0MkH/XCiIoydmqK3dm4NRjUWZy10Qi2MS183kW46WDWzuGOc1jqteEJJKAe7TcjRsDsWyEkWi1U0ZScK0amyK1dquS2T2XU1xW/U04nHioJDVa0fFdK0SGlxwVWo4T027l7WmJS1w+La2qW50zqW10xaW4DjySNIzIW33ioWgy3ImHagVEGigem4LzmWqnKkgS83WW3C1lMey1mtXLWX9A+bOWynWSiquqhyDy242xHSuK4PUa81ehoYsm3YKmY0cqNU3ApGm2yqPi1VW+jaM2mVTM23tXJWla3iWta2SWrm0zpXriK6Xm0e2lWAKWyamC237UyqEW17WxalumSW2e8ShVk4K8Uh5FOgK2uFxLqi8T9E0o2s+Goz624NjPm9rFdWjvXrEwpX4Qc20tg5mh7MSSBTa6zn22qXWO27M3O2nJqu2rU0R22K3e2hMGJWx3R+2s8Ds25sqc2rDUpnBdTh2q+SBXRS0YcIW0qW/K12mqSQumKSgS2wJzHWltV6W+EKZ2rVQYWqG0mmmG1emzxq4WnqacS/s0m2wU2fm+M0bq1oEs0YAa/mydgEYFHCKopu1AWkK1zMMK3qmyBi02u60xWpKVCW9T79QL6QD2sS0XCCS3pahgiPOJIw5W9HYwW+e07WuC2HW5O06Wk62y2/S1b29FWYWve2iWZs1NW1zTI8Vq296Ai3k6olVh0kzXn26I0ZC5sHX25KCTsLmgtVcsj3aCIC+AZ+3BWrA2hWni0usL+0bpH+2n4rww4cYS2AO0S2pW0B22snkF1yq01QOrF4wOuHVFG2DYRIPWRS21O1eAX3pXA5R1gcB4HKSTYSqSW63xIXPxIodCqygU/EPW8PhRQCGD6SM+41GJ1XxGQR364uDg1W+FzYGrB2NWw+1s0h83tWtG2Eqoi3G28dU9G6u1UOkXgjW+eD70MaBwXLcyIAMwIJU5h0zWlu3cWtu25m6R27W6x3Fm4tQvaYLHg0FW20pbu0vBDVmOG8EEBmrizYSqI7Icrs1mnRvRoAEM3+sXk2ZSdG1G24lVn25IV9W4c2ZAoQmX25zVUO+ejJmo5gwgEXUL0NQAL0GRyRO6Y3AWym2AUOO2wO53iFmxxrNwgJzn61akQ2sz51Wmo3ciOo34OvsyLkWXHNGksLK2EA3uO6p2l2r9HdW0h31OxpUjm5o4tOiU1UO40RsYGawoCEsw+6UXgDO5U1ZGth2xOpwxu2xG302zJ3AUZm2bfKtZAOoR2B2sB2iOqGTZWqC3QOvK3xOx3q6yBC1r280DiNVC3qOq4Hy27e0mW9prYW1s0FO4sItW6y2o2rFxEOzx21O7x2DmqA0QKkU0qi457uopilUOtmjc4uH6t4UwKAMgC3TWwZ0hW4Z0K0YCid2q+QNfTLj8O4yiTO8I0RGDcVGCkF2wyXK2xGMZ2SSV3gr2/WTS2uRpy2tB3v60ASf6+6gNW/yV+m682NG9Z12W/oluOh8Ql2mgmY2ki0fmnG0tgtKAPY2l3xoDHDQQ4YL+WoNGKmubVRO4C1SO+3USupsyjiBIZba/c2hGw80fFM01zOmw3nmmvXZOtV0N6lx0620sKn2wl29W451NO6JkUOh6GcpRKBiuScl3S0YyTa1I13SngWKUjzVTWjA0mdVh1v2uY047IJhVmRGz+2kB0AuxriRMXkpZbMF2hOIg3HCkDgltTtiBmOx0YO4nbtIRchF5U9D5rePKEOvV3dcgl1eG8+3Gu6+1UcJzxnAdQDMAO9BzsEcCM0K4gPOvN0qmp21U2nm29gPm1s2gO0c2yS3zOY429cJ10KNF10cCX5LoW+a3sWFDoqu3/U4WjF0yOFq3lans04uyqH9u4BWJCw509Gxp0uI2N2Wy852o3OegL0JeggkcVyiYOmgmgdN0Luh55PO7a3jO7xyuu3xyYOkgAuQ5GKumjK0ZiFe2pqolKt2YonBgP/k/1EHDu6zHXZKNWA12P6RB6nTmh6ikY4UJ8BikGMgyHbTBRgOyijJVnxroiTCtgCIpVmjhlac9cWtu3e3GlLsxa2jV3/2OXFR3fC0EmTq37O8u1Dq+p39W0c1nOyh0/u+eiL0VegAe5LQgQJ6GD3JhU70Rl25u8D1Lm/d35mx3WSyd10n6z13q6hx2pW9K1ce8zUzNK2iw2nJ3/6xvW2WvDX9Ek+2iejslY2ol1Cm6A2kuga2+OuT1/uxT0r0QD0qezejqejehzWB1BgezI06e8F3Ouoo0we2sRiqPc0U4/O0fLWZ0We5MzKu1nyw2xo1duk96EO3Z36uly11O7G3vu07HNOil0bUzuG/uhT3+o1ejKezZjcQeiQheiL2NwsaS6egq36et12TOj11Je710Ro481+u+x21Guka2eho1rOgT0bOx80PuiN1Duo51Se053le7lXDkqr3/ugL0s4VT3AeuC5NezT0ZGlr230aL0Hu2L3HyWD27mmUDGezfz8u8z1Z2ne2WeoV4du6jQ5esk3Hqxy0uero1Fe8UXFg7o3sShqzOekr0h49IVfu2T3GxZb3+e8NBr0JdCr0Br2ge7b0Zmzi2pGSD2HuouzWO7r0wcZL19e0DEnuwb0Xu4UQrO5w23m3zQ/e/L0Dukh2d6023gK/gkcq/rUye+N1XFHKBmxB0rvNPYz3O6H0LmzM3vKVu0rul22f2t5102ru2/2gcX/2323WOoe3STEe1ZuaS3YUSq2T2qO2nCme2x2kV21u6Hniu5nSlmm3ivAwVRQuo63S25R3wuuRrgWeV17KNt3Nwmz3OOjLgPmgn14ujG2FeyN1Rm9WIku2M3eeqn0iEmn1s0OKzoBPo6H+AIDtgJn3Zu+SWLup51tehe1fOJH1Genr1WeoSmyTFX1u0Dc1w+w/VIu9B3ceo30H2oMhYu5bVa2WozTenq02+6N0fui2Xbcmn2W26axslE0Tv+ANETWgK2++7T0JcN+3sOiK3c+7+1RST53YUb52Xgq2FBGDd3lurd3pa4O3i0i00u0F3USOr2hiuiF0jNUglV5Uq0tquG4yWie2hzAb2G+rH04O/DDnGkrVmtDq0W+mp3E+iu2k+9y2O+gSU/WHKDcpEa1fQUJCg2lI2ozQGRiOG10vYiv2ReoxTLukZ2pU+O2JO1ny+u8g7KEbk2hAFj1e9WGTb+d3VCktWVh6l62Ye1HwKfNgAtNIgD6VVNl042/koZLKFI5VrAnu6o17OnLwH2q90bOlP3L+831PuozUgKkn3Du370Ts/715+n6zxyablc0FOTM+ji0ey7I3v2zc2K+iF2GexL0wcSf2amisx82mZ0d+4e2SWmq2IB892ZenO2du2NDxlTAOG2pAOve633JOaM2ee+33Sehb0JmlsEfs4wwo4DBCeqTsJW6Zr2Lm2/3s++/1w+2C0MB072h+6G0GmKNReCfLhSUGkQbqBMBr+Sp0siK72nupV12Gj4pZegQNMYEJh5etf2iBg12uWm31zerIE7+mBVjsO6a1xFi1tERQPKsigPk26J1REGv0d21gO8+r22pU5v1JWp/0+u4wPIek437evT1Agl3XR+h00yu872Ie3IypOxgNwem2q/6+IMa2oN04+vJ1NPXDWcIlnDlOxpjWBjP2vu9z0NOqQMnOs7F/VN+lyB6+0lOMpwVOKpw1OEpD1OWlThBh22OujIPteoP16BiVQmekC2pe2wOY++w3Dek33x6xz1bOqb0vezwNveqN0+Bsr3dBq+2o3DlgJMqaqdkRGR0QJJp1gdQOs+6gNCfGZIeUO/SZsRcRFMbMZWGqeZZUVyBfCJyr/AKAC6ByZ1jrZgPj2td0R2v9KP+leQSa1Ub/sRyJv8YCiRef37AUvviPC9gC7WjH38BvgA8iXQQdyLYNW+mb1vu9oMxu3P0eo42LM0blJW2rcwhe7qBhJbaDXB0BkauVyWbfa3WERGHWH667nUihwNo+gV1ZMrMZn4g1m5amZoYhvZDYuzgpDJDPrlm1nz3Ct6zeCj3jNB3AOSe/APmywgNEhhN13TQAgg6FgDAe/LIymo0CfCmkNUB+YN+agSSzSdDgSIDS1J2lm08mnQk2lY5B9WLcnRajHVWw/6Txa1ehp65wB41a3UHCIhEnKuchaPMJzcOd1gKarNzascyDHIPkNgE4Ur/6CHIje/014O8rUJggZEkFScIo8/iQuSOaCtcjSj0m1/yp9MuhiiLfB2UkPjYhrx24h1oPZ+0r2fuogNM0Kjh3TK20Uh7v5jB5u1DOwDFrTEHYbfFv2MhhFLORBWVNut6yJWQe2buzgPparkNZuHkM0RUMOou3QZ4OoUN1guylTUdEMsowwM+ElBidsKUMR0GUOb+vAP4hnP2Khyl0/uidhbijPCNe8hW1hn31uyh10hW4X3lCsC0C2ja0jiO0wQpEt0CWgnDDDOMO9q1JgaW5wrFuiO12PZyIgcCGkZMN6xW9DVkV6VP0VMXBBNBlaaAYpMhtdVyWR808A8SKnEqNZd7qUXTTnaFnTI0V2kMei8AChpf09mOr37zDo0eBnEOZ+iQO2+8n0wGh32yBw4NA+0cn/2OmhzZfAC6ht6Wu4ysEthmSIjWc0xfh6RadhlkNf6o83o+6A7e0EUN+cAslERLOlh+mxAkE7LUhhpMgHTUCEyFVyx7ipvULoTMVVhAsODuoiMfmksN/ekd0/u40S3O1+a/vUjpcMc4k29BiMU2u/1su70Of28sxdm+8P0bHh2qS8aZY+AB3duusR/O/UN8R7v2kPS0JNh+R2hOQ2QdmfLUc/EP1MBy0M8yD8OCyBAMNmyUaIUXPX3ewQOuBkT0eOy32Fh9SPm8u30dB/YPrUxb2bUnKAu++n36MGTRe+45iM6b4zlYT8BF/YvyzPKx6T0HFBFAQPAnh5l2sO+dS1xTfzIsxcW4GttDrawUD/SWmQbSV33YIC0jz+T33dQF2om6l2y5Rlt2LBw31tRhmwso3jLtIYQP4Rgr0pRloO9WvYNlhpUPO+ieD5Rn/CFRykPFRkG2rsk0QzR8gWfCjKBbMOmSjsLmroBQaMe+7OVpBJAQxKezFysTBXAM+sMhW+dR78MrgtbarnFE5hBBAfmx/8ymytw8JjPGc6T0yfqPOAW6M5Be6Mu1WeTjRxDVYAWFWpUowxUGCdJi3NGP02jcBpe4dShgSmxOBuKMuB3L2JRwn3Puw6WrRrP3rRwkPbhoH15Rt30FR4aObMA6PTWceBICE6NP4M6PxqXqMQxh0rQxvaMPRgYSMcDKAcsGJTRpOSUNRx51Lmz6OqI76OBwJMMSvHt6h84GPymf9zs6eGUfwLqNioM6Tcxq6OQxvmOMxuGNvGBGNhiZGPAUVGMi6dGPx0TGMR27GNTRhP34xtEN486gzbO3F1YB9vWDqrsnFejcOlh6mMVelsG5R7aP0x3aOMx+52HR1mMDCdmPahgZRcxqGN9R3mMMx2GNICIWNpAEWNGsMeDJx1ONix12UvSl+1NRnDxfRpGw/R+kOKx0jw1xeIbymZb6kIyVWk6yeTgx3WPxx4OOJxqdjwx6uS5Rk2MkaFGNe4a2NWxi2NYxjH2G+h2PGWufbJOihrPepKPr+4zWyhvEPpRgkNbhv2PX2gOOQx930wxoqNTsCmgsxjaJsxwpC4AU6MDKSOMUC86N1x3iDXRgaMJx9sAu1WHSysUXwH8cWM5xlh2NqaWNIpTfwtbC45ram0WnSI+PSAE+NQxs+MjR45gtxysBtxmdCmxjW4SUPZl5QrNZxKjtpli2nnBFB0D7iAQbd22sWC8vjQfrC1i7ShoOBCcM2qRjf0Se6eOkRrz0yBg4OtOn910x6LW/xpmNrxp2XLcyCTLx/mMah5OC7x0sy3xrBXvR1h3/kQSRGpHDzLSOzBUCXWTFJRuOL+PWEwR1aTbxphNbSD+M6x4+N6xihOGx4tLGx36jZBI/yl4uDkdRVEAccq8QT8w3miR/kVhm0nWRRxV34+E9knKEeMr+nZ3uB5aNqRimPERqmNzx7KOVeshMM0ChP3O07nrIFQOlOM0QoCGNTNhKRNfxmRONx8+PgWFhNvR3OMPx7hMi6Ycyb+ScXrjX+XKQf+WrAAJzVxgsqgmT+Ou1BuNDRpuPAh+ROtxx/5sBUmRLw00pbQu07YypDjHOISxfJZZHAGpCYRJoAT1qwuNyxl/3ECfQPS8kN1e27/Cmtf5jyAhoMWhyHg4JyeNrhuUPexrSM+e2mOBx8hMBJ/aNUJrCkdSSkNyADxM/aLxPuuOhMhx1JPfx/WOZJ7UDBJrzWnhvONpZXRVRJ6ZNuJuZPE8jHDWJHDzqekEqi2JJNdibWOxxnmM3R2RP/xo2M5Jz4w/q6BPVki4yyK2v7mLLNbmxl9CXHUDXZeALWUSz7hkm6MPlBurn3RtmYXJ3HnKK3nnqo/nl/aRwT76RTRHODJOjqeAUgRpHx6Jvk19JnAMDJ/BNXQy3lxmiiMkJ0ZNLx5xPFR1xOzJzsJlqZxP7xz4UXRuOMPJiZMCxmWpbJpU1++qWPhJ8BOz2UMDUpzZhzJ0xTbHL5PrWXkZ/MP+XzoOpodR+nxSqlJO+JtJMsp9FN/x5uPPJwBO5JjLWu0iDyaOBWDDGNGP9ifVOIJ7YiOQW9Dq2oBBSlB0DRJxWqipjizynE91wbFApnKbeyTU1cN4J1oO2J7SPkpnaPKpyhO9gZBVuJ+XjLJ+6MMJneMcxwGSrJ/xM+pl2qvR7ZONRsJNpZAuP8pw5OUh+Xhs6B0A2pynnT8UsVRoJRO5BZuwqqIHWdR9+OPqiNPpJu6OBJ1VPZJ9VNP/JyhGhoBBap9MqVJmpOdnS5NT8HPQSVEeOvaNLL0tRwTJQ+FjprWKgj6zyWUGlDh2pM8C5piwBCJiRyz+hP0TpoROIc+jZrS81204zt2mtND1uBt2MeG8T2ex4sMepkZOcpRePep8tOTJv1Onc5JVnBBACO6ExRBpr30Mp8NMKptZOPJ96ocp+11xpp53zqfZOyxrtQs6w7BYyyuVloa5Ngxh9ORp49Nsp9GNVphg0ap+Gr5JhtOdxvVO9xlm39ibVIIZupPEETUItbYOD8gl+LACU1MFUm1Ma0MpPTNUQ7uxbpYAKScXnp9mL9xhP2xRo/pZexaMSjZKNWJqeO7p+UOcq0lPfur1NBxn1P3OnpWvpyWMJcDh0p46UC/2edTb4toY5wXwjFprWOlppVOgZuRMPSBRPINGDPTSv5i3oBkXHEzYakkd06q4EqKb2Vuhe2vrGiMeolNPNcmIEQRN0EC0Brkjcm/6w8kWgQ8lKk1rxNYedIZuUe2LOG9BkCsNPKoIn63gOdPdQPBpXGuqj8Kog7dLPA0q0hyHFEzGr0NRTN2O5dNa0FkwLp4WCN6liO0zSnw36MnWbpsu0exrxk38CPzb+TWPO2YDNlpleMqp64lY6nJPAcskB9Ys/KdOSlGx0zb7Ap/XUyZ0+Osp0aNlZ9VPAci4OV0bPAx1PPZkge/GeKPrpiJzzNP45kC+ZMkBrkgbMeLIbNRx3cK9ZibMBdabMHx0xBjZ3zNgcwbMeZmbMrZ/wbWZtbNTZjbMHx7zOzZmLJkgQ8mTZgRaLZs6OHZsbP2ZrUlnZ4TEXZgZRXZubPOAtBwPZrzM5fBsbPZ2gZ3Z52xvZ9PQfZ5VBEbFbPmZ3bPnZ/bOXZgHNjZ0gWMJzzN44RfkA6wTyvZ8HPieZ7M7xH7OOh5HPKofvGqJAMIBZksAp6DKJFZJGygBDux/8jHMw5mbP/Z5oANjfIgg5maSVCxrOFZ2TPFZsDNjR8rOaRggOepzlINSBVnNScWxzs9qSdSJQM9SU7ksc8eCcheeAK0+iSrJw1yvSF9NBWnZPxp7paJpnMqgqQDO1xh9Oy5nKB3SN4xa5nXOwSEIoJYI1CbTGSJ0IVKjk4ipNrEZtM6oMjPTJljlpp3rgZphvaEZguV9QAJzqZpIODTL1VMbKyEaIgEqUITHI4xsUl9QTFNa8wAgtWxjYVk+jP2vMmMHO5jNrR1jOU+9jOA+7nONSUJJqwFqQtAAXNDCIXNc0DBD+pmlPzJ+iSLJjKI+Ju5NXRvXMxpzlOV+saSPxzrxfpoKA/pmRUlJ5bCEZ1hBFpuVOSJ8vPHxvXOzyXvNsBApM3Is2NdxlDOGmmVT4Z/wGna2E5ZrZDP/Jvu3XvTFHmhDDM9AMQBUZm72E/GXnKICxR0ZseOkx7AMvu+PPeBxPMkp4hMcZ1PO85jPP85tJIzJr1DC5zaDHUvjNcphLgBEuYNCZzaYdvI4zFIKTNojZPz2YW5OXRnvPseTmRyNGXNAF7XOsyXsjPYAZxEgEF5UY4Ati3XvPCVGE27fN+QUTQfOOGClVmAvTPmwNtAmp6/lcJt2GmqstihgL/LAIBxRSZVIPhQOZmBiA4BKWKQBS5zxj8eEZRFgTl5cNHPgwsZPjz5ATJiZ+uwwFrXMsyQXH0cfNa3yTfhn+6AueZGUINKRVoRANzMOKDkJH5ICoKgKXNcF3YA8Fy+J8FsAv65m/Gpa+g4b8PmZiF0HaTUNfPpeqGSCeBLPNfLpNcjSd6L+xmoj6l2OPukQOWJ3BM7pliUSi7x1feiZhtGxhQBExNrHCCgumJu8QBajnMKh+zg3AKYAvgy/yjII6KPW1DBoAXYAsqA3KIoEYDnAcYBXAQwDhFtxDYyERC1e1njeqe7JlAAwDhFkgApAEgA5QZmhfshgA1ASospAQcAdi4bWz5KjiSIAQCcsRosCARouGsBgCUgNAAcsBgBWAtIuFFioAu6AYwbVDIBoARKA1ARKBoADIBeW1KDdFtACdCjaqqASRAZAUVw5QEgCAPJzm0AGoAcsSkD9F8ItysWVhOcnKC0AKwHM0WgDwa2gD7+44Pfsj9m0ALy1CSmoAtiqjg1AQB6pQIXBTFvYuDFykBiEtAD3F3otSIOVhrFyYtbF1KAMANIATFlYspARRTti44sCAGoCpQbYu7FyYCDFs4tvstIC0AezE1AZmjbFsOL2Y8vxUcY4uGsZcloAFICJQAYwkAI1i7MZsUI6XjgFFjIsBYLIubQJGRWcb1TBgZEsQATkAMAflJcHfIvXAIwBkgAUsGAAADeVRSGcN8HSeE6S4AAAG0AALpmgUUtZc4ICSlyACylgwAAAXwMAApfus4ReYAXJd8tUjTZL1wCAAA== -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

<details>
<summary>🧪 Generate unit tests</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post copyable unit tests in a comment
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `improve-kubernetes-scheduling-scenarios`

</details>

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=robusta-dev/holmesgpt&utm_content=709):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai generate unit tests` to generate unit tests for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🔭 Outside diff range comments (3)
tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/manifest.yaml (2)

40-45: Consider restoring a sensible CPU limit to avoid noisy-neighbor problems

Dropping the limits.cpu field means the container can burst without bound, which is fine for some synthetic tests but risky for shared clusters and can give misleading scheduling behaviour. A modest limit (e.g. equal to the request or 2-3 × it) keeps the scenario realistic while still exercising low-CPU scheduling.

           limits:
             memory: "64Mi"
+            cpu: "20m"   # keep within a single core while allowing short bursts

21-38: Harden the container with a minimal securityContext

Static-analysis flags (CKV_K8S_20, CKV_K8S_23) indicate the container can run as root and escalate privileges. Even in test fixtures, adding an explicit securityContext improves fidelity and avoids teaching insecure defaults:

       spec:
         containers:
         - name: app-container
           image: busybox:1.35
+          securityContext:
+            runAsNonRoot: true
+            allowPrivilegeEscalation: false
+            readOnlyRootFilesystem: true
tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml (1)

15-17: expected_score should be 1 now that a concrete expected_output is provided

With a non-empty expected_output, leaving expected_score: 0 means the test will mark every answer incorrect irrespective of matching content.

-  expected_score: 0
+  expected_score: 1
♻️ Duplicate comments (1)
tests/llm/fixtures/test_ask_holmes/53_logs_find_term/manifest.yaml (1)

38-42: Repeat note on absent CPU limit

Same recommendation as earlier: confirm the cluster doesn’t mandate cpu limits, or add one.

🧹 Nitpick comments (26)
tests/llm/fixtures/test_ask_holmes/47_truncated_logs_context_window/manifest.yaml (1)

34-40: Extremely low resources may cause OOM / throttling in log generator

long-logs-app is configured to spit out 5 M tokens every 100 ms yet is allotted only 64 Mi memory and 10 m CPU. In practice the container will thrash or be OOM-killed, which may mask the log-truncation behaviour you want to test.

Consider bumping requests/limits to a more realistic floor (e.g. 256 Mi / 100 m) or add a comment explaining the intentional constraint.

tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/manifest.yaml (1)

36-42: Same resource pattern as file 52 – ensure global consistency with any cluster LimitRanges

See earlier comment about missing CPU limits. If a cluster-wide LimitRange enforces both requests and limits, this deployment will be rejected.

If intentional, add a brief comment in the YAML so future readers know the absence of cpu limit is deliberate.

tests/llm/fixtures/test_ask_holmes/64_keda_vs_hpa_confusion/manifest.yaml (1)

29-35: HPA + no CPU limit can distort utilisation calculations

HPA bases % Utilization on requested CPU. With request: 10m and no limit, the pod can burst well above 10 m, quickly reading as 1000 % and scaling up aggressively.
If the purpose of the scenario isn’t to test that edge case, consider setting limit: 50m or raising the request to something closer to expected steady-state to avoid runaway scaling.

tests/llm/fixtures/test_ask_holmes/53_logs_find_term/manifest.yaml (1)

24-26: Spurious blank line introduces visual noise

Line 25 is an empty line between name: and image:. YAML permits it, but removing keeps the manifest tidy and consistent with the other fixtures.

       - name: main-container
-
         image: us-central1-docker.pkg.dev/genuine-flight-317411/devel/multiple-errors-in-logs:v1
tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/manifest.yaml (1)

30-37: Add an explanatory comment for the intentionally-missing Secret

The volume references frontend-api-keys, which is not defined in this manifest (only backend-api-keys is). A short YAML comment will prevent future readers from “fixing” the intentional gap and breaking the test logic.

-      volumes:
-      - name: api-credentials
-        secret:
-          secretName: frontend-api-keys
-          optional: false
+      volumes:
+      - name: api-credentials
+        # NOTE: This secret is *deliberately* absent – the test expects pods to fail.
+        secret:
+          secretName: frontend-api-keys
+          optional: false
holmes/plugins/runbooks/kubernetes/pod_scheduling_failures_instructions.md (2)

69-70: Escape literal dots in grep pattern

Unescaped . acts as “any character”, so grep -E "(cloud.google.com|eks.amazonaws.com|kubernetes.azure.com)" may match unintended labels. Escaping improves precision.

-grep -E "(cloud.google.com|eks.amazonaws.com|kubernetes.azure.com)"
+grep -E "(cloud\.google\.com|eks\.amazonaws\.com|kubernetes\.azure\.com)"

102-117: Specify a language for the fenced block

Markdown linters (MD040) complain about language-less fences. Since this is plain text, set it to text for consistency.

-```
+```text
tests/llm/fixtures/test_ask_holmes/53_logs_find_term/test_case.yaml (1)

5-15: Typo in expected output and changed correctness score may invalidate the test

  • Line 11: “volation” ➜ “violation”.
  • evaluation.correctness was bumped from 0 to 1. Make sure the grading harness now treats the provided expected output as definitive; otherwise previously failing solutions will erroneously pass.
-    2. DB query issues due unicity constraint volation
+    2. DB query issues due to uniqueness-constraint violation
tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/test_case.yaml (1)

6-6: YAML bullet formatting: indent two spaces for consistency.

Minor nit: use two-space indent for list items under expected_output to match surrounding fixtures.

tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/manifest.yaml (2)

41-45: CPU limit removed – evaluate whether unrestricted CPU is intentional.

Requests dropped from 50 m → 10 m and the limit was removed. On burst-heavy clusters this container could monopolise CPU and skew tests. If unbounded CPU is not required, keep a modest limit (e.g., 100m) to stay predictable.


40-44: Static analysis flags root/priv-escalation; consider tightening securityContext.

Although not changed in this diff, Checkov flagged CKV_K8S_20/23 for this container. Adding:

+      securityContext:
+        runAsNonRoot: true
+        allowPrivilegeEscalation: false

would silence the warnings without impacting the scenario.

holmes/plugins/runbooks/catalog.json (1)

14-22: Consider grouping new entries by domain for quicker lookup

Placing the two Kubernetes runbooks next to each other (or under a dedicated "kubernetes" subsection) keeps the catalog logically grouped and improves discoverability as the list grows. No functional change, purely an ordering tweak.

tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/test_case.yaml (1)

3-9: Minor shell-quoting & idempotency improvements in the setup/teardown

  1. Quote ./manifest.yaml to avoid glob expansion accidents.
  2. Prefix the teardown with set -e so failures surface early (e.g., if the secret creation fails).
-before_test: |
-  kubectl create secret generic app-code-63 -n staging-63 --from-file=app.py=./app.py --dry-run=client -o yaml | kubectl apply -f -
-  kubectl apply -f ./manifest.yaml
+before_test: |
+  set -e
+  kubectl create secret generic app-code-63 -n staging-63 \
+    --from-file=app.py=./app.py \
+    --dry-run=client -o yaml | kubectl apply -f -
+  kubectl apply -f "./manifest.yaml"
@@
-after_test: |
-  kubectl delete -f ./manifest.yaml
-  kubectl delete secret app-code-63 -n staging-63 --ignore-not-found
+after_test: |
+  set -e
+  kubectl delete -f "./manifest.yaml"
+  kubectl delete secret app-code-63 -n staging-63 --ignore-not-found
tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/test_case.yaml (1)

12-14: Align evaluation schema with other test cases for consistency

Elsewhere the file nests correctness options (e.g., expected_score, type). Consider adopting the same structure for easier tooling/parsing.

-evaluation:
-  correctness: 1
+evaluation:
+  correctness:
+    expected_score: 1
+    type: "strict"
tests/llm/fixtures/test_ask_holmes/83_secret_not_found/manifest.yaml (1)

33-53: Consider adding security best practices for completeness.

While this is a test fixture, consider adding security best practices to make it more realistic:

     spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 999
       containers:
       - name: database
         image: postgres:alpine
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: false
+          runAsNonRoot: true
tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml (1)

36-45: Consider adding security best practices for completeness.

While this is a test fixture, adding security configurations would make it more realistic:

     spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 101
       containers:
       - name: api-server
         image: nginx:alpine
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/manifest.yaml (2)

22-29: Missing namespaceSelector on anti-affinity may catch unrelated pods

If another team deploys app: cache-server pods outside namespace-82, they will also block scheduling here. Add namespaceSelector: to confine the rule to the current namespace unless cross-namespace blocking is intended.

-          - labelSelector:
+          - namespaceSelector:
+              matchNames:
+                - namespace-82
+            labelSelector:

30-38: No container security context – fails common CIS & Checkov controls

allowPrivilegeEscalation defaults to true. Even in test fixtures it is a good habit to lock this down to avoid noisy scanner findings.

       - name: cache-server
         image: redis:alpine
+        securityContext:
+          allowPrivilegeEscalation: false
         resources:
tests/llm/fixtures/test_ask_holmes/87_resource_like_but_ephemeral_storage/manifest.yaml (2)

26-31: ephemeral-storage request without a matching limit

Kubernetes only enforces quota & eviction on the higher of requests and limits. Omitting limits here means a runaway container can consume more than 15 Gi and mask the scheduling failure the test intends to surface. Add an identical limit to keep the signal clean.

             ephemeral-storage: "15Gi"
+          limits:
+            ephemeral-storage: "15Gi"

50-54: Repeat the storage request pattern for consistency

api-server sets a 10 Gi request but again no limit. Mirror the earlier advice for consistency and to avoid noisy evictions during test runs.

tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml (1)

20-28: Intentional storage-class typo – add an explanatory comment

Future maintainers may “fix” the obvious typo and invalidate the test. Add a clarifying comment to lock the behaviour in place.

-  storageClassName: fast-ssd  # This storage class doesn't exist!
+  # Deliberately incorrect to trigger Pending PVC in the test.
+  storageClassName: fast-ssd
tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml (2)

68-76: Minor mismatch in taint string – may trip brittle parsers

kubectl_describe_pod_database_pending shows the FailedScheduling event with
{node-role.kubernetes.io/control-plane: } (missing NoSchedule) whereas the
actual taint on the nodes (line 95) is node-role.kubernetes.io/control-plane:NoSchedule.

If downstream assertion logic extracts the full taint key + effect from the
event text, this difference can cause false-negatives.
Recommend making the event snippet consistent with the real taint string.


70-72: Topology spread constraint wording deviates from kubectl output

topology.kubernetes.io/zone:ScheduleAnyway when max skew 1 is exceeded …
is not how kubectl describe pod prints the constraint (it emits
topologyKey=topology.kubernetes.io/zone, whenUnsatisfiable=ScheduleAnyway).

Keeping the mock closer to the real CLI output helps future-proof the test.

tests/llm/fixtures/test_ask_holmes/87_resource_like_but_ephemeral_storage/test_case.yaml (2)

60-68: Event reason good, but consider echoing exact scheduler phrasing

The mock uses
0/4 nodes are available: 4 Insufficient ephemeral-storage.

Recent Kubernetes versions output
0/4 nodes had insufficient ephemeral-storage.

Aligning the string avoids fragile substring checks in evaluation code.


78-87: kubectl top nodes output lacks storage columns

Because top omits ephemeral-storage, you already rely on describe nodes
for capacity/allocatable. Consider omitting kubectl top nodes from this
fixture (or add a comment that it’s only for CPU/memory) to avoid implying
that top would reveal the storage shortage.

holmes/plugins/runbooks/kubernetes/replica_mismatch_troubleshooting.md (1)

80-81: Minor: command comment repeats “events”

The comment # Get events for specific pods (more targeted) appears twice in
Step 1 (lines 8 and 11). Removing the duplicate tidies the doc.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47dc196 and e7e41c5.

📒 Files selected for processing (42)
  • CLAUDE.md (0 hunks)
  • holmes/plugins/runbooks/catalog.json (1 hunks)
  • holmes/plugins/runbooks/kubernetes/pod_scheduling_failures_instructions.md (1 hunks)
  • holmes/plugins/runbooks/kubernetes/replica_mismatch_troubleshooting.md (1 hunks)
  • pyproject.toml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/14_pending_resources/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/47_truncated_logs_context_window/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/50_logs_since_specific_date/test_case.yaml (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/test_case.yaml (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/53_logs_find_term/manifest.yaml (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/53_logs_find_term/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/60_count_less_than/manifests.yaml (4 hunks)
  • tests/llm/fixtures/test_ask_holmes/60_count_less_than/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/60_time_based_filtering/kubernetes_countitems_select_.metadata.namespace_test-60_and_.status.containerStatuses_.restartCount_3_.metadata.name_Pod.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/60_time_based_filtering/kubernetes_countitems_select_.metadata.namespace_test-60_and_.status.startTime_fromdateiso8601_now_-_45_.metadata.name_pod.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/app.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/64_keda_vs_hpa_confusion/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/83_secret_not_found/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/83_secret_not_found/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/87_resource_like_but_ephemeral_storage/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/87_resource_like_but_ephemeral_storage/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml (1 hunks)
💤 Files with no reviewable changes (3)
  • CLAUDE.md
  • tests/llm/fixtures/test_ask_holmes/60_time_based_filtering/kubernetes_countitems_select_.metadata.namespace_test-60_and_.status.containerStatuses_.restartCount_3_.metadata.name_Pod.txt
  • tests/llm/fixtures/test_ask_holmes/60_time_based_filtering/kubernetes_countitems_select_.metadata.namespace_test-60_and_.status.startTime_fromdateiso8601_now_-45.metadata.name_pod.txt
🧰 Additional context used
🪛 Checkov (3.2.334)
tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/manifest.yaml

[MEDIUM] 6-44: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-44: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/manifest.yaml

[MEDIUM] 6-38: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-38: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/manifest.yaml

[MEDIUM] 6-44: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-44: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/manifest.yaml

[MEDIUM] 7-38: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 7-38: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/87_resource_like_but_ephemeral_storage/manifest.yaml

[MEDIUM] 6-31: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-31: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 32-55: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 32-55: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 56-78: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 56-78: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/manifest.yaml

[MEDIUM] 19-65: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 19-65: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 66-88: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 66-88: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 89-110: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 89-110: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/manifest.yaml

[MEDIUM] 6-38: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-38: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml

[MEDIUM] 21-45: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 21-45: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/83_secret_not_found/manifest.yaml

[MEDIUM] 18-53: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 18-53: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml

[MEDIUM] 21-55: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 21-55: Minimize the admission of root containers

(CKV_K8S_23)

🪛 Gitleaks (8.27.2)
tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/manifest.yaml

46-46: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


48-48: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


58-58: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


40-46: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)


51-58: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)


61-67: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)

tests/llm/fixtures/test_ask_holmes/83_secret_not_found/manifest.yaml

15-15: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


8-15: Possible Kubernetes Secret detected, posing a risk of leaking credentials/tokens from your deployments

(kubernetes-secret-yaml)

🪛 markdownlint-cli2 (0.17.2)
holmes/plugins/runbooks/kubernetes/pod_scheduling_failures_instructions.md

111-111: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


112-112: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


128-128: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


135-135: Trailing punctuation in heading
Punctuation: ':'

(MD026, no-trailing-punctuation)


164-164: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


182-182: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


200-200: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


219-219: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


238-238: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


256-256: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


275-275: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


295-295: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Pre-commit checks
🔇 Additional comments (21)
tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/manifest.yaml (1)

34-40: Verify or Add a CPU Limit in the Manifest

A missing CPU limits entry may trigger pod rejections on clusters with a LimitRange policy. I didn’t find any LimitRange definitions in the repo, so please confirm your test-cluster defaults or explicitly set a small CPU limit.

• File: tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/manifest.yaml
Lines: 34–40

Suggested diff:

         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
           limits:
             memory: "64Mi"
+            cpu: "50m"
tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/manifest.yaml (1)

40-58: Double-check that sample secrets are non-production placeholders

Static scanners flagged these base64 blobs as “generic API keys”. If they are synthetic (they decode to api-key-value, dbpassword, etc.) leave them; otherwise replace them with obvious dummy strings (e.g. dW5zZWN1cmUtcGxhY2Vob2xkZXI=).

tests/llm/fixtures/test_ask_holmes/60_count_less_than/test_case.yaml (1)

14-14: Tag addition LGTM

Adding the easy tag improves test filtering without changing semantics.

tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml (1)

15-15: Tag addition LGTM

Same rationale as above – no issues spotted.

tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/test_case.yaml (1)

25-26: leaked-information tag & inline YAML comment – confirm parser & marker support

  1. The inline comment (# Hardcoded …) sits inside the sequence. YAML parsers are permissive, but some linters (e.g. yamllint seq-spaces) flag this placement.
  2. Ensure the new leaked-information marker is defined in pytest configuration; otherwise marker-unknown warnings appear.
tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/test_case.yaml (1)

5-6: Confirm consistency between leaked-information tag and remaining evaluation metadata

The new marker signals that the manifest discloses the root cause, yet evaluation.correctness is still 0. Double-check that this is intentional; other tests transitioned to 1 after similar edits.

tests/llm/fixtures/test_ask_holmes/50_logs_since_specific_date/test_case.yaml (1)

5-5: Updated to easy and correctness: 1 — ensure alignment with grading logic

Changing both the difficulty tag and correctness flag alters scoring. Verify upstream reporting dashboards and any difficulty-based weighting use the new values.

Also applies to: 14-14

tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml (2)

12-14: Tag additions look good – confirm harness awareness of the new markers.

The extra tags improve filtering, but ensure the test-runner’s tag registry (pytest markers / metadata parser) recognises easy, kubernetes, and context_window; otherwise they will be silently ignored.


5-6: Incorrect namespace mismatch – no changes needed
The wait_for_replicas.sh calls target the Deployment in ask-holmes-namespace-54a (where it’s defined), while the user_prompt and subsequent kubectl steps run in ask-holmes-namespace-54b for the fast-fail pod. Both namespaces are declared in the manifest, so the fixture is correct as-is.

Likely an incorrect or invalid review comment.

tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/test_case.yaml (2)

5-7: New “leaked-information” marker may need code-base registration.

Adding the marker is fine, but pytest will raise PytestUnknownMarkWarning unless it is declared in pytest.ini/pyproject.toml. Please confirm it was added there alongside the other markers.


17-17: Correctness bumped to 1 – double-check expected_output still matches implementation.

Switching the grading implies the LLM output is now considered right. Make sure CI has a failing test that now passes; otherwise the change masks a real issue.

tests/llm/fixtures/test_ask_holmes/60_count_less_than/manifests.yaml (1)

83-96: No lingering flaky- references found in fixtures
I ran a global search across tests/llm/fixtures and didn’t find any remaining “flaky-” pod names. Test expectations and helper scripts appear to have been updated correctly—no further changes needed.

tests/llm/fixtures/test_ask_holmes/83_secret_not_found/test_case.yaml (1)

1-14: Well-structured test case for Kubernetes secret troubleshooting.

The test case is correctly configured to simulate a database pod failure due to a missing secret reference. The setup, expected output, and teardown are appropriately defined.

tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/app.py (1)

1-42: Well-implemented application simulator for log testing.

The Python script effectively simulates a realistic application that generates various log levels without errors. The code structure is clean with proper timestamping, appropriate use of flush=True for container environments, and realistic timing patterns.

tests/llm/fixtures/test_ask_holmes/83_secret_not_found/manifest.yaml (1)

1-54: Effective test manifest for secret resolution failure scenario.

The manifest correctly simulates a realistic Kubernetes secret resolution failure by creating a secret with one name (db-credentials) while the deployment references a different secret name (database-secret).

tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/test_case.yaml (1)

1-15: Well-designed test case for PVC storage class troubleshooting.

The test case effectively covers both problem identification and solution recommendation for PVC storage class mismatches. The expected output appropriately includes suggesting alternative storage classes, which demonstrates comprehensive troubleshooting capabilities.

tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml (1)

1-46: Effective resource quota violation test scenario.

The manifest correctly demonstrates resource quota enforcement by creating a deployment that exceeds the namespace's CPU quota (90m requested vs 20m allowed). The resource configuration is well-structured with matching requests and limits.

tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/manifest.yaml (1)

12-18: Anti-affinity scenario may pass on large clusters

The test relies on having fewer than 30 schedulable nodes, otherwise all pods will schedule and the scenario won’t reproduce. To make the failure deterministic, consider constraining the Deployment with an additional nodeSelector/topologySpreadConstraints or lowering replicas to node_count + 1 during setup.

tests/llm/fixtures/test_ask_holmes/87_resource_like_but_ephemeral_storage/manifest.yaml (1)

70-78: Front-end pod may schedule when nodes free <8 Gi

The test’s intent is “cache-server pods fail, others succeed”. If a node has only 7 Gi free the front-end pod will also stay Pending, muddying the scenario. Consider lowering its request or adding a comment explaining the expected node capacity.

tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml (1)

148-157: Test expectations are clear and accurate

Replica count, error reason, and remediation guidance line up with the mocked kubectl output. No changes needed.

holmes/plugins/runbooks/kubernetes/replica_mismatch_troubleshooting.md (1)

59-60: grep pattern won’t OR-match due to quoted pipe

Inside double quotes the | is taken literally.
grep -A10 "Liveness\|Readiness" therefore matches the actual string
Liveness|Readiness, not either word.

-grep -A10 "Liveness\|Readiness"
+grep -A10 -E 'Liveness|Readiness'

Apply the same fix for the other grep … "Volumes\|Mounts" pattern.

Likely an incorrect or invalid review comment.

Comment thread pyproject.toml
Comment thread tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

♻️ Duplicate comments (3)
tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/manifest.yaml (3)

11-17: RWO PVC + 4 replicas ⇒ inevitable Multi-Attach failure

ReadWriteOnce volumes can only be mounted by one node at a time. With replicas: 4, the first pod will bind the claim and the remaining three will stay Pending with Multi-Attach errors, masking the taint scenario this fixture is supposed to test.

-  replicas: 4
+  # Option A – simplest: keep a single writer
+  replicas: 1

Other options: switch the claim to an RWX-capable storageClass, or use StatefulSet so each replica gets its own PVC.

Also applies to: 25-25


34-40: Zone-level topology spread conflicts with single-AZ PVC

Because the PVC will bind to a single-AZ EBS volume, topology.kubernetes.io/zone spreading inevitably breaks (pods in other zones cannot mount the volume). Drop the constraint or scope it to the same zone label you know the volume lands in.

-      topologySpreadConstraints:
-      - maxSkew: 1
-        topologyKey: topology.kubernetes.io/zone
-        whenUnsatisfiable: ScheduleAnyway
-        labelSelector:
-          matchLabels:
-            app: database-primary
+# (remove or adapt this block)

60-64: Missing toleration for dedicated=database:NoSchedule taint

The comment already notes it; without the toleration the pods will be rejected before we hit any affinity logic.

       volumes:
       - name: postgres-storage
         persistentVolumeClaim:
           claimName: postgres-pvc-us-east-1a
+      tolerations:
+      - key: "dedicated"
+        operator: "Equal"
+        value: "database"
+        effect: "NoSchedule"
🧹 Nitpick comments (33)
tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/manifest.yaml (1)

6-40: Address security concerns in container configuration.

The deployment configuration is functional but has security implications flagged by static analysis:

  1. Container runs as root by default - Consider adding a non-root user specification
  2. No explicit privilege escalation prevention - Should explicitly disable privilege escalation

For a test fixture, these may be acceptable, but consider adding security context for better practices:

     spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 1000
       containers:
       - name: backend-service
         image: python:3.9-slim
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
         command: ["python"]
tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/manifest.yaml (1)

23-40: Consider adding security context for better container security practices.

While this is a test fixture, adding a security context would demonstrate better practices and address the static analysis security concerns about privilege escalation and root containers.

Consider adding a security context to the container spec:

      containers:
      - name: api-limiter
        image: python:3.9-slim
        command: ["python"]
        args: ["/scripts/generate_logs.py"]
+       securityContext:
+         allowPrivilegeEscalation: false
+         runAsNonRoot: true
+         runAsUser: 1000
+         readOnlyRootFilesystem: true
        volumeMounts:
        - name: script-volume
          mountPath: /scripts
tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/generate_logs.py (2)

31-31: Address unused loop variables as flagged by static analysis.

The loop control variables are not used within the loop bodies. Following the linter suggestion will improve code clarity.

Apply these changes to rename unused loop variables:

-    for i in range(80000):
+    for _ in range(80000):
        print(
            generate_log_entry(
                status=200,
                req_per_sec=random.randint(8, 12),
                message="Request processed successfully",
            )
        )

-    for i in range(60000):
+    for _ in range(60000):
        print(
            generate_log_entry(
                status=200,
                req_per_sec=1000,
                client_ip=spike_client,
                message="Request processed (high rate)",
            )
        )

-    for i in range(5000):
+    for _ in range(5000):
        print(
            generate_log_entry(
                status=429,
                req_per_sec=1000,
                client_ip=spike_client,
                message="Too Many Requests - Rate limit exceeded (100/second)",
            )
        )

-    for i in range(5000):
+    for _ in range(5000):
        print(
            generate_log_entry(
                status=200,
                req_per_sec=random.randint(8, 12),
                message="Request processed successfully",
            )
        )

Also applies to: 59-59, 70-70, 81-81


91-92: Consider adding graceful shutdown handling.

While the infinite sleep keeps the container running as intended, adding signal handling would allow for cleaner container shutdown during testing.

Consider adding signal handling for graceful shutdown:

+import signal
+import sys
+
+def signal_handler(sig, frame):
+    print('Shutting down log generator...')
+    sys.exit(0)

def main():
    # ... existing log generation code ...
    
    # Keep pod running
+   signal.signal(signal.SIGINT, signal_handler)
+   signal.signal(signal.SIGTERM, signal_handler)
    while True:
        time.sleep(3600)
tests/llm/fixtures/test_ask_holmes/75_network_flapping/manifest.yaml (2)

22-40: Consider adding security context for better security posture.

While this is a test fixture, consider adding a security context to follow security best practices:

     spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 1000
+        fsGroup: 1000
       containers:
       - name: frontend
         image: python:3.9-slim
+        securityContext:
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: false
+          capabilities:
+            drop:
+            - ALL

This addresses the static analysis concerns about privilege escalation and root containers.


31-34: Consider adding resource limits for better resource management.

While the resource requests are appropriate for a test scenario, adding limits helps prevent resource exhaustion:

         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
+          limits:
+            memory: "128Mi"
+            cpu: "50m"
tests/llm/fixtures/test_ask_holmes/75_network_flapping/generate_logs.py (1)

154-174: Fix unused loop variable in Phase 4.

The loop variable i is declared but not used within the loop body. Since Phase 4 uses random probability instead of index-based conditions, the variable is unnecessary.

-    for i in range(25000):
+    for _ in range(25000):
         total_requests += 1
         if random.random() < 0.6:  # 60% failure rate
tests/llm/fixtures/test_ask_holmes/67_performance_degradation/manifest.yaml (1)

23-40: Consider adding security context for production-like testing.

The container specification lacks a security context, which means it runs as root by default. While this is acceptable for a test environment, consider adding security hardening to make the test more realistic:

      containers:
      - name: api-gateway
        image: python:3.9-slim
        command: ["python"]
        args: ["/scripts/generate_logs.py"]
+        securityContext:
+          runAsNonRoot: true
+          runAsUser: 1001
+          allowPrivilegeEscalation: false
        volumeMounts:
tests/llm/fixtures/test_ask_holmes/66_http_error_needle/generate_logs.py (1)

26-50: Fix unused loop variables as indicated by static analysis.

The main function logic is correct for the testing scenario, but the loop variables should be renamed to indicate they're intentionally unused.

Apply this diff to address the static analysis hints:

-    for i in range(50000):
+    for _ in range(50000):
         print(generate_log_entry())

     # The critical error in the middle
     print(
         generate_log_entry(
             status=500,
             message="Database connection timeout: Could not acquire connection from pool after 30s",
             path="/api/orders",
         )
     )

     # Another 50,000 successful requests
-    for i in range(50000):
+    for _ in range(50000):
         print(generate_log_entry())
tests/llm/fixtures/test_ask_holmes/66_http_error_needle/manifest.yaml (1)

1-41: Consider adding security hardening for best practices.

The manifest structure is correct and appropriate for a test environment. However, consider adding security best practices even in testing scenarios.

Apply this diff to improve security posture:

       containers:
       - name: web-server
         image: python:3.9-slim
         command: ["python"]
         args: ["/scripts/generate_logs.py"]
+        securityContext:
+          allowPrivilegeEscalation: false
+          runAsNonRoot: true
+          runAsUser: 1000
+          readOnlyRootFilesystem: true
         volumeMounts:
         - name: script-volume
           mountPath: /scripts
         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"

Note: If readOnlyRootFilesystem: true causes issues with the Python runtime, you may need to add a temporary volume mount for /tmp.

tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/manifest.yaml (2)

60-78: Harden the test container with a minimal securityContext

Static analysis flags (CKV_K8S_20, CKV_K8S_23) point out that the pod may run as root and with allowPrivilegeEscalation enabled. Even for fixtures it’s trivial to mitigate:

       containers:
       - name: monitoring-agent
         image: bitnami/kubectl:latest
+        securityContext:
+          allowPrivilegeEscalation: false
+          runAsNonRoot: true
+          readOnlyRootFilesystem: true

This keeps the fixture functionally identical while demonstrating best practices.


64-64: Pin the bitnami/kubectl image to a specific tag/digest

Using :latest makes the test nondeterministic—future upstream image changes could break or slow the CI run. Recommend referencing an immutable tag (e.g., bitnami/kubectl:1.30.1) or digest.

tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/generate_logs.py (2)

39-39: Simplify the time window condition.

The condition can be simplified for better readability.

-        if hour == 3 and minute >= 0 and minute <= 5:
+        if hour == 3 and minute <= 5:

Since minute >= 0 is always true, it can be omitted.


93-94: Consider making the randomization configurable.

While the randomization adds realism, consider making it configurable through environment variables for more predictable testing scenarios.

# Add at the top of main():
randomize_timing = os.getenv('RANDOMIZE_TIMING', 'true').lower() == 'true'

# Then modify the randomization block:
if randomize_timing and random.random() < 0.1:
    current_time += timedelta(seconds=random.randint(1, 5))
tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/manifest.yaml (2)

24-35: Harden the backend container: drop privileges & set CPU limit

The static-analysis hints (CKV_K8S_20 / CKV_K8S_23) flag the lack of basic securityContext settings, and the spec defines a CPU request but no limit. Adding both reduces risk and avoids CPU throttling surprises.

         ports:
         - containerPort: 80
+        securityContext:
+          allowPrivilegeEscalation: false
+          runAsNonRoot: true
+          capabilities:
+            drop: ["ALL"]
         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
           limits:
             memory: "64Mi"
+            cpu: "20m"

66-88: Apply the same hardening to the frontend container

Mirror the securityContext and CPU limit changes here for consistency.

         image: busybox
@@
             sleep 10
           done
         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
           limits:
             memory: "64Mi"
+            cpu: "20m"
+        securityContext:
+          allowPrivilegeEscalation: false
+          runAsNonRoot: true
+          capabilities:
+            drop: ["ALL"]
tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/manifest.yaml (3)

1-5: Namespace lacks identifying labels

Tagging the test namespace with something like app.kubernetes.io/part-of: llm-fixtures (and/or an environment: test label) makes bulk cleanup and dashboard filtering simpler while having zero impact on the fixture’s behaviour.


22-36: Harden the container spec to silence security scanners

Checkov flags CKV_K8S_20 & CKV_K8S_23 because the container runs as root with privilege escalation allowed.
A minimal securityContext keeps the fixture functionally identical while staying compliant:

         image: busybox
         command: ["/bin/sh"]
         args: ["-c", "cat /config/app.properties && sleep 3600"]
+        securityContext:
+          runAsNonRoot: true
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true

38-40: Intentional ConfigMap mismatch — suppress linter noise

The wrong name is the whole point of the scenario. To avoid endless “ConfigMap not found” warnings from static linters, consider an inline suppression, e.g.

# kube-linter.io/disabled-checks: "configmap-volume-mount"
name: app-config  # This ConfigMap doesn't exist!
tests/llm/fixtures/test_ask_holmes/68_cascading_failures/generate_logs.py (1)

83-115: Consider optimizing log volume for test performance

The additional error logs and log burial strategy effectively simulate realistic scenarios where critical errors are hidden in high-volume log streams. However, generating 100,000 total log entries might impact test execution time and resource usage.

Consider reducing the log volumes while maintaining the diagnostic challenge:

-    # More error logs showing the cascade
-    for i in range(100):
+    # More error logs showing the cascade
+    for i in range(50):
        service = random.choice(services)
        # ... existing logic ...

-    # Generate more normal logs to bury the errors
-    for i in range(89900):
+    # Generate more normal logs to bury the errors  
+    for i in range(5000):
        service = random.choice(services)
        # ... existing logic ...

The infinite sleep loop is correctly implemented to keep the container running for log collection.

tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml (1)

37-51: Harden container security context – fail static analysis warnings

Checkov flags CKV_K8S_20 and CKV_K8S_23 because the container runs as root and allows privilege escalation by default.
Tightening the security context keeps the fixture realistic while not interfering with the PVC-mismatch focus.

-      containers:
-      - name: database
-        image: busybox
-        command: ["/bin/sh"]
-        args: ["-c", "echo 'Database started' && sleep 3600"]
+      containers:
+      - name: database
+        image: busybox
+        command: ["/bin/sh"]
+        args: ["-c", "echo 'Database started' && sleep 3600"]
+        securityContext:
+          runAsNonRoot: true
+          runAsUser: 1000
+          allowPrivilegeEscalation: false

This small addition silences the warnings without impacting the test’s purpose.

tests/llm/fixtures/test_ask_holmes/74_config_change_impact/manifest.yaml (1)

40-40: Remove redundant restartPolicy.

The restartPolicy: Always at the pod template level is redundant since Deployments automatically manage pod restarts. This is the default behavior for Deployment-managed pods.

-      restartPolicy: Always
tests/llm/fixtures/test_ask_holmes/74_config_change_impact/generate_logs.py (1)

42-58: Replace unused loop variable with underscore.

The static analysis correctly identifies that the loop control variable i is not used within the loop bodies. This is a common pattern where you only need to iterate a specific number of times.

-    for i in range(50000):
+    for _ in range(50000):

Apply the same change to the second loop:

-    for i in range(50000):
+    for _ in range(50000):

Also applies to: 78-94

tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/generate_logs.py (2)

40-46: Consider making the memory growth pattern more realistic.

The current linear growth based on iteration count may not accurately simulate real memory leaks. Consider using a more realistic exponential or step-wise growth pattern.

-        # Calculate current memory usage (exponential growth)
-        memory_mb = int(base_memory + (i / records_per_gb) * 1000)
+        # Calculate current memory usage (more realistic exponential growth)
+        growth_factor = 1 + (i / 50000)  # Exponential growth over time
+        memory_mb = int(base_memory * growth_factor)

89-91: Document the indefinite sleep purpose.

The indefinite sleep is necessary to keep the Kubernetes pod running for log observation, but this should be documented for clarity.

-    # Keep pod running
+    # Keep pod running indefinitely for Kubernetes pod to remain active
+    # This allows log observation and prevents pod restart
     while True:
         time.sleep(3600)
tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/manifest.yaml (1)

31-34: Consider adding resource limits.

While minimal requests are appropriate for this test scenario, adding limits would demonstrate good resource management practices.

         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
+          limits:
+            memory: "128Mi"
+            cpu: "50m"
tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml (1)

37-42: Harden the container with a minimal securityContext

Static analysis (CKV_K8S_20 / 23) flags the default root user and unchecked privilege escalation. Even for fixtures it’s good to embed sane defaults so examples don’t propagate insecure patterns.

-        image: nginx:alpine
+        image: nginx:alpine
+        securityContext:
+          runAsNonRoot: true
+          allowPrivilegeEscalation: false
         resources:

This keeps the manifest pedagogically sound while remaining harmless to the quota-violation scenario.

tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/manifest.yaml (3)

23-23: Pin the image by digest for deterministic test runs

An unpinned nginx:alpine tag may change over time, breaking historical reproducibility of the fixture.

-        image: nginx:alpine
+#       💡 pin to a specific digest to avoid future tag drift
+        image: nginx@sha256:<replace-with-actual-digest>

39-44: Add a cpu limit to match the memory limit

Only cpu request is set. Omitting a limit means the pod can burst to any CPU, which makes results nondeterministic under load and triggers Checkov advice.

           limits:
             memory: 64Mi
+            cpu: 20m   # keep modest but finite

21-24: Harden the container securityContext (CKV_K8S_20 / 23)

Static analysis points out missing allowPrivilegeEscalation and root safeguards. Even in fixtures it is cheap to model good practice.

       containers:
       - name: web-app
         image: nginx:alpine
+        securityContext:
+          allowPrivilegeEscalation: false
+          runAsNonRoot: true
+          runAsUser: 101   # nginx non-root uid
tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/manifest.yaml (2)

24-34: Harden backend container: add securityContext and set a CPU limit

Checkov correctly warns that the pod can run as root and with privilege escalation. Even for fixtures it’s cheap to model good practice—your generated YAML gets copied into real clusters sooner or later.

       containers:
       - name: backend
         image: nginx:alpine
+        securityContext:
+          runAsNonRoot: true
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          capabilities:
+            drop: ["ALL"]
         ports:
         - containerPort: 80
         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
           limits:
             memory: "64Mi"
+            cpu: "50m"

65-87: Frontend container: pin the image tag and tighten security

BusyBox without an explicit tag is mutable and can break reproducibility; same securityContext gaps as the backend.

       - name: frontend
-        image: busybox
+        image: busybox:1.36      # pin exact version for reproducibility
         command: ["/bin/sh"]
@@
         resources:
           requests:
             memory: "64Mi"
             cpu: "10m"
           limits:
             memory: "64Mi"
+        securityContext:
+          runAsNonRoot: true
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          capabilities:
+            drop: ["ALL"]
tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/manifest.yaml (1)

41-59: Harden containers with an explicit securityContext

None of the deployments set runAsNonRoot or disable privilege escalation, triggering CKV_K8S_20/23 findings. Add a minimal securityContext (replicate to the other two deployments as well):

         env:
           - name: POSTGRES_PASSWORD
             value: supersecret
+        securityContext:
+          runAsNonRoot: true
+          allowPrivilegeEscalation: false
+          capabilities:
+            drop: ["ALL"]
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7e41c5 and 462669a.

📒 Files selected for processing (45)
  • pyproject.toml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/66_http_error_needle/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/66_http_error_needle/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/66_http_error_needle/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/67_performance_degradation/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/67_performance_degradation/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/67_performance_degradation/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/68_cascading_failures/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/68_cascading_failures/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/68_cascading_failures/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/74_config_change_impact/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/74_config_change_impact/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/74_config_change_impact/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/75_network_flapping/generate_logs.py (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/75_network_flapping/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/75_network_flapping/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (15)
  • tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/68_cascading_failures/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/manifest.yaml
  • tests/llm/fixtures/test_ask_holmes/67_performance_degradation/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/66_http_error_needle/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/75_network_flapping/test_case.yaml
🚧 Files skipped from review as they are similar to previous changes (3)
  • pyproject.toml
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml
🧰 Additional context used
🪛 Checkov (3.2.334)
tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/manifest.yaml

[MEDIUM] 6-44: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-44: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/manifest.yaml

[MEDIUM] 46-78: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 46-78: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/manifest.yaml

[MEDIUM] 7-35: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 7-35: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 51-87: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 51-87: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/manifest.yaml

[MEDIUM] 7-34: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 7-34: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 49-87: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 49-87: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/66_http_error_needle/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/67_performance_degradation/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/68_cascading_failures/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/74_config_change_impact/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/75_network_flapping/manifest.yaml

[MEDIUM] 6-40: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-40: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml

[MEDIUM] 19-42: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 19-42: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/manifest.yaml

[MEDIUM] 7-41: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 7-41: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml

[MEDIUM] 21-54: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 21-54: Minimize the admission of root containers

(CKV_K8S_23)

tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/manifest.yaml

[MEDIUM] 19-65: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 19-65: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 66-90: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 66-90: Minimize the admission of root containers

(CKV_K8S_23)


[MEDIUM] 91-114: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 91-114: Minimize the admission of root containers

(CKV_K8S_23)

🪛 Ruff (0.12.2)
tests/llm/fixtures/test_ask_holmes/75_network_flapping/generate_logs.py

154-154: Loop control variable i not used within loop body

(B007)

tests/llm/fixtures/test_ask_holmes/66_http_error_needle/generate_logs.py

28-28: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


41-41: Loop control variable i not used within loop body

Rename unused i to _i

(B007)

tests/llm/fixtures/test_ask_holmes/74_config_change_impact/generate_logs.py

42-42: Loop control variable i not used within loop body

(B007)

tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/generate_logs.py

47-47: Loop control variable i not used within loop body

(B007)


109-109: Loop control variable i not used within loop body

(B007)

tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/generate_logs.py

31-31: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


59-59: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


70-70: Loop control variable i not used within loop body

Rename unused i to _i

(B007)


81-81: Loop control variable i not used within loop body

Rename unused i to _i

(B007)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: llm_evals
  • GitHub Check: Pre-commit checks
  • GitHub Check: Pre-commit checks
🔇 Additional comments (41)
tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/manifest.yaml (1)

1-4: LGTM: Namespace definition is clean and follows conventions.

The namespace definition is straightforward and appropriate for a test fixture environment.

tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/generate_logs.py (5)

8-42: LGTM: Well-structured log entry generation function.

The function effectively generates realistic connection pool monitoring logs with appropriate thresholds and detailed metadata. The conditional logic for different warning levels (80+ and 95+ connections) provides good granularity for testing log analysis scenarios.


45-77: Excellent simulation phases with realistic progression.

The phased approach effectively simulates a realistic connection pool exhaustion scenario:

  • Normal operation baseline (5-20 connections)
  • Gradual increase during peak hours
  • Introduction of wait times as pressure builds

The progression from 20 to 80 connections with corresponding wait time increases creates authentic monitoring data for testing.


79-107: Effective critical phase simulation with realistic error patterns.

The critical exhaustion phase accurately represents real-world database connection pool failures:

  • Pool exhaustion at 100/100 connections
  • Realistic timeout errors (30000ms)
  • Mix of failed requests with occasional successful connections
  • Authentic error messages that would appear in production logs

This provides excellent test data for validating LLM diagnostic capabilities.


47-47: Static analysis false positive: Loop variables are intentionally unused.

The unused loop variables i flagged by Ruff are intentional and appropriate for this log generation context. The loops are used purely for iteration count to generate the desired number of log entries, not for indexing or calculations.

In log generation scenarios, this pattern is common and correct. No changes needed.

Also applies to: 109-109


114-115: Appropriate container lifecycle management.

The infinite sleep loop correctly keeps the container running for the test environment, allowing log collection and analysis. This is the expected pattern for containerized log generation scripts.

tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/manifest.yaml (2)

1-4: LGTM: Namespace definition is clean and follows conventions.

The namespace definition is straightforward and appropriate for test isolation.


31-34: LGTM: Resource requests are appropriate for a log generation test.

The minimal resource allocation (64Mi memory, 10m CPU) is suitable for this synthetic log generation scenario.

tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/generate_logs.py (2)

8-26: LGTM: Log entry generation function is well-structured.

The function correctly generates JSON log entries with appropriate fields for rate limiting simulation. The conditional logic for error levels based on status codes is clean.


29-92: LGTM: Log generation sequence effectively simulates rate limiting scenario.

The script creates a realistic progression from normal traffic → spike detection → rate limiting → recovery. The volume and timing of logs (150,000 total entries) should provide sufficient data for LLM testing scenarios.

tests/llm/fixtures/test_ask_holmes/75_network_flapping/manifest.yaml (1)

1-41: Well-structured manifest for network flapping test scenario.

The manifest correctly defines the namespace and deployment structure. The use of a secret-mounted script with executable permissions is appropriate for this test fixture, and the minimal resource allocation is suitable for log generation.

tests/llm/fixtures/test_ask_holmes/75_network_flapping/generate_logs.py (3)

34-43: Excellent design for progressive network degradation simulation.

The four-phase approach effectively simulates realistic network flapping scenarios with clear progression from 0.1% to 60% timeout rates. This provides comprehensive test data for LLM-based diagnostics.


8-31: Well-structured log entry generation function.

The function correctly generates structured JSON logs with appropriate log levels, timestamps, and contextual information. The conditional logic for error vs. success states is sound.


175-177: Appropriate keep-alive mechanism for test pod.

The infinite sleep loop correctly keeps the pod running for log collection during testing scenarios.

tests/llm/fixtures/test_ask_holmes/67_performance_degradation/manifest.yaml (2)

31-34: Resource allocation is appropriate for the test scenario.

The resource requests (64Mi memory, 10m CPU) are well-suited for a log generation script that simulates performance degradation without consuming excessive cluster resources.


36-39: Secret volume mounting is correctly configured.

The secret volume mount with executable permissions (0755) is properly configured to run the Python script from the mounted location.

tests/llm/fixtures/test_ask_holmes/67_performance_degradation/generate_logs.py (4)

43-49: Effective exponential degradation model.

The mathematical approach using math.exp(i / 20000) creates a realistic performance degradation pattern, starting at 100ms and gradually increasing to timeout levels. The capping at 6000ms ensures the simulation doesn't produce unrealistic values.


11-25: Well-designed log level progression.

The conditional logic effectively maps response times to appropriate log levels:

  • Normal operation (≤3000ms): INFO
  • Performance degradation (3000-5000ms): WARN
  • Timeout conditions (>5000ms): ERROR

This creates a realistic troubleshooting scenario for the LLM evaluation.


31-34: Realistic resource usage simulation.

The CPU and memory calculations provide correlated metrics that would help in diagnosing performance issues:

  • CPU usage increases proportionally with response time
  • Memory usage also scales, simulating resource pressure
  • Values are bounded to prevent unrealistic metrics

54-59: Proper container lifecycle management.

The throttling mechanism (sleep every 1000 entries) simulates real-time log generation, and the infinite sleep loop at the end keeps the container running for the test duration, which is essential for the Kubernetes test scenario.

tests/llm/fixtures/test_ask_holmes/66_http_error_needle/generate_logs.py (1)

8-23: LGTM! Well-designed log generation function.

The function effectively generates realistic HTTP log entries with appropriate randomization and automatic error level detection based on status codes. The use of default parameters and private IP ranges is suitable for testing scenarios.

tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/manifest.yaml (1)

18-24: Intentional omission of list verb looks correct for the negative-test scenario

The Role intentionally grants only get on pods, which will trigger the expected “permission denied – cannot list pods” error exercised by the test. Looks good and aligns with the fixture’s goal.

tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/generate_logs.py (1)

26-102: LGTM! Well-structured log generation script.

The script effectively simulates a realistic time-window anomaly scenario with:

  • Proper 24-hour log generation
  • Targeted error injection in the 03:00-03:05 window
  • Realistic timing variations and job types
  • Appropriate JSON log formatting
  • Correct container lifecycle management

The implementation supports the test case objective of detecting time-window-specific scheduling issues.

tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/manifest.yaml (1)

36-46: Confirm the selector mismatch is truly intentional

The selector deliberately targets version: v2, so no backend Pod will ever be selected and the Service will remain without endpoints.
If that’s the objective of the test fixture, great. Otherwise, switch the selector to version: v1 (or remove the version key) to restore connectivity.

tests/llm/fixtures/test_ask_holmes/68_cascading_failures/manifest.yaml (2)

1-4: LGTM: Clean namespace definition

The namespace definition follows standard Kubernetes conventions and aligns with the test case numbering scheme.


6-40: LGTM: Well-structured test deployment with appropriate security context

The deployment configuration is well-suited for a test fixture generating synthetic logs. The static analysis warnings about privilege escalation and root containers are acceptable in this test context since:

  1. Test fixtures run in isolated environments where security constraints can be relaxed
  2. The container executes a specific Python script for log generation
  3. Minimal resource allocation is appropriate for the workload

The secret volume mounting with executable permissions (0755) is correctly configured for script execution.

tests/llm/fixtures/test_ask_holmes/68_cascading_failures/generate_logs.py (3)

8-19: LGTM: Well-designed log entry generation function

The function creates realistic structured log entries with proper timestamp handling, random instance IDs for authenticity, and conditional trace ID correlation. The ISO timestamp format with UTC is appropriate for log analysis scenarios.


22-49: LGTM: Excellent cascading failure simulation setup

The initial normal log generation and root cause implementation are well-designed:

  1. Realistic baseline: 10,000 normal logs establish expected behavior
  2. Clear root cause: Redis connection failure is a common real-world scenario
  3. Proper timing: Timestamp offsets create chronological log sequence

The Redis connection error message is realistic and provides clear diagnostic information.


50-81: LGTM: Realistic cascading failure propagation

The failure sequence excellently demonstrates microservice dependency chains:

  1. Logical progression: auth-service → user-service → order-service → payment-processor
  2. Realistic timing: 5-second intervals between failures
  3. Trace correlation: Shared trace_id enables failure tracking
  4. Diagnostic messages: Each error clearly indicates the upstream dependency failure

This provides excellent training data for LLM analysis of distributed system failures.

tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/manifest.yaml (1)

15-18: Intentional storage-class mismatch acknowledged

The storageClassName: fast-ssd line is the crux of this negative test; leaving it as an invalid value is correct for the scenario you’re validating.

tests/llm/fixtures/test_ask_holmes/74_config_change_impact/manifest.yaml (1)

31-34: Resource allocation is appropriate for test scenario.

The minimal resource requests (64Mi memory, 10m CPU) are well-suited for a log generation script in a test environment.

tests/llm/fixtures/test_ask_holmes/74_config_change_impact/test_case.yaml (2)

1-21: Well-structured test case with appropriate expectations.

The test case effectively defines a scenario for evaluating cache service performance diagnosis. The expected outputs align well with the log patterns that will be generated by the Python script, and the setup/teardown commands properly manage the test environment.


12-14: Good use of kubectl dry-run for secret creation.

Using --dry-run=client -o yaml | kubectl apply -f - is a best practice that ensures the secret is created or updated safely without conflicts.

tests/llm/fixtures/test_ask_holmes/74_config_change_impact/generate_logs.py (2)

8-34: Well-designed log entry generation function.

The generate_log_entry function effectively creates realistic JSON log entries with appropriate conditional logic for warning levels and metadata inclusion. The hit rate calculation and random TTL metadata inclusion add realism to the simulation.


37-76: Effective simulation of performance degradation scenario.

The two-phase approach (high performance followed by poor performance after configuration reload) creates a clear before/after pattern that aligns perfectly with the test case expectations. The counter reset between phases effectively demonstrates the impact of the configuration change.

tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/generate_logs.py (2)

7-31: LGTM! Well-structured log entry generator.

The generate_log_entry function correctly creates structured JSON logs with appropriate log levels based on memory usage thresholds (WARN >3000MB, ERROR for exceptions). The timestamp formatting and memory percentage calculations are accurate.


70-87: OOM condition logic is sound.

Simulation shows memory_mb reaches 4096 at iteration 99900 (> 99000), so the error block is reliably triggered. No changes required.

tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/manifest.yaml (1)

1-5: LGTM! Proper namespace isolation.

The namespace creation follows Kubernetes best practices for test isolation.

tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/manifest.yaml (1)

1-17: Scenario manifest looks correct and purpose-built to exceed the quota

The namespace, ResourceQuota, and deployment skeleton all line up with the goal of provoking quota violations in the test case. No functional issues spotted in these lines.

tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/manifest.yaml (1)

26-30: Intentional probe mismatch is clear ‑ no action required

The liveness probe deliberately targets port 8080 instead of 80 to exercise the “mis-configured probe” scenario the fixture is meant to test. LGTM as-is.

tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/manifest.yaml (1)

95-107: NetworkPolicy logic looks correct—ingress restricted exactly as the test expects

The policy selects backend pods and only allows ingress from peers with tier=backend, so traffic from the frontend (tier=frontend) is indeed denied. Nice concise reproduction of a common mis-configuration.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml (1)

2-6: Expected-output text is still quite prescriptive and may create brittle grading

Numbered sentences make the judge expect those exact lines, order and wording. From prior feedback (03_what_is_the_command_to_port_forward, see retrieved learnings) we try to keep expectations flexible to avoid false negatives when the LLM answers with equivalent wording or a different ordering. Consider switching to a looser matcher – e.g. a YAML list of key substrings or a regex list – and omit ordering cues.

-expected_output: |
-  user is told:
-  1. holmes lacks permission
-  2. is given an example of configuration to add permissions
-  3. is told to run `helm upgrade ...` to apply that configuration
+expected_output:
+  - lacks permission
+  - example configuration to grant permissions
+  - suggest running `helm upgrade`

This keeps the essence while letting wording/order vary.

tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml (1)

7-9: Replace fixed sleep 30 with a deterministic readiness check

Blind sleeps make the test either slower than needed or flaky on congested clusters.
kubectl wait gives a deterministic, faster setup.

-before_test: |
-  kubectl apply -f manifest.yaml
-  sleep 30
+before_test: |
+  kubectl apply -f manifest.yaml
+  kubectl -n namespace-04 wait --for=condition=available deployment/nginx --timeout=60s
tests/llm/fixtures/test_ask_holmes/05_image_version/test_case.yaml (3)

1-1: Clarify pod selector by including namespace in the entity reference

If another sea-turtle pod ever exists in a different namespace, the LLM could return an unexpected image. Explicitly passing the namespace keeps the prompt unambiguous:

-user_prompt: 'what is the image version of << { "type": "pod", "name": "sea-turtle" } >>'
+user_prompt: 'what is the image version of << { "type": "pod", "name": "sea-turtle", "namespace": "app-05" } >>'

8-26: Remove leading indentation in the heredoc to avoid malformed YAML

The extra two-space prefix on every manifest line is preserved by the heredoc and becomes part of the file, so apiVersion is indented instead of starting at column 0. Most kubectl parsers tolerate it, but a few strict linters and CI jobs flag this as a formatting error. Dropping the indentation keeps the manifest canonical.

-  cat <<EOF | kubectl apply -f -
-  apiVersion: v1
-  kind: Pod
-  metadata:
-    name: sea-turtle
-    namespace: app-05
-    labels:
-      app: analytics-engine
-  spec:
-    containers:
-    - name: web
-      image: nginx:1.23.4
-      env:
-      - name: VERSION
-        value: "1.23.4"
-  EOF
+  cat <<EOF | kubectl apply -f -
+apiVersion: v1
+kind: Pod
+metadata:
+  name: sea-turtle
+  namespace: app-05
+  labels:
+    app: analytics-engine
+spec:
+  containers:
+    - name: web
+      image: nginx:1.23.4
+      env:
+        - name: VERSION
+          value: "1.23.4"
+EOF

29-29: Add --ignore-not-found to make teardown idempotent

If the namespace is already gone (e.g., a flaky previous cleanup), kubectl delete exits non-zero and can fail the test run. Adding the flag makes the teardown resilient:

-  kubectl delete namespace app-05 --force --grace-period=0
+  kubectl delete namespace app-05 --force --grace-period=0 --ignore-not-found
tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (2)

2-6: Expected-output is too prescriptive; relax matching to reduce false negatives

The bullets hard-code the exact pod name (grafana-7f8b9c6d5-x2m4n) and port number. While deterministic for this fixture, it contradicts the previous learning that HolmesGPT tests should reward semantically correct answers rather than string-perfect matches. Any future change to the pod hash or port will break the test even though the LLM’s answer is still correct.

Consider switching to a regex-style matcher or placeholder wording (e.g. “grafana-<pod-hash>”, “<port>”) and validate with a regex in the evaluator instead of literal string comparison.


23-24: busybox:1.35 + nc -l -p 3000 can be flaky across BusyBox builds

Some BusyBox variants omit nc, or require the BSD-style -l -p flags to be combined as -lp. A missing or crashing nc will make the pod stay CrashLoopBackOff, causing the kubectl wait step to time out and the whole test to fail.

A more robust one-liner is to use Alpine + a simple HTTP server, or even keep BusyBox but replace the loop with an inert process:

-      image: busybox:1.35
-      command: ['sh', '-c', 'while true; do nc -l -p 3000; done']
+      image: alpine:3.19
+      command: ['sh', '-c', 'while true; do sleep 3600; done']

The pod comes up instantly and holds port 3000 open regardless of netcat availability, making the fixture less brittle.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 462669a and 0010374.

📒 Files selected for processing (19)
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/kubectl_describe.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/kubectl_find_resource.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/kubectl_logs.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_describe.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_events.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_find_resource.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_lineage_parents.txt (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/manifest.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/05_image_version/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/06_explain_issue/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/48_logs_since_thursday/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/55_kafka_runbook/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/56_kafka_runbook_no_tool/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/test_case.yaml (1 hunks)
💤 Files with no reviewable changes (7)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_find_resource.txt
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_events.txt
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/kubectl_find_resource.txt
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_lineage_parents.txt
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/kubectl_describe.txt
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/kubectl_logs.txt
  • tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/kubectl_describe.txt
✅ Files skipped from review due to trivial changes (5)
  • tests/llm/fixtures/test_ask_holmes/56_kafka_runbook_no_tool/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/48_logs_since_thursday/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/55_kafka_runbook/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/06_explain_issue/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/test_case.yaml
🧰 Additional context used
🧠 Learnings (6)
tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

tests/llm/fixtures/test_ask_holmes/05_image_version/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

🪛 Checkov (3.2.334)
tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/manifest.yaml

[MEDIUM] 6-31: Containers should not run with allowPrivilegeEscalation

(CKV_K8S_20)


[MEDIUM] 6-31: Minimize the admission of root containers

(CKV_K8S_23)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Pre-commit checks
  • GitHub Check: llm_evals
  • GitHub Check: Pre-commit checks
🔇 Additional comments (4)
tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml (1)

13-14: Verify harness support for mock_policy

mock_policy: always_mock is new to this fixture. Ensure the evaluation framework actually consumes this key; if not, the flag will be silently ignored and the test may behave unexpectedly.

tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml (2)

10-10: Confirm that the “skip” mechanism is implemented as a tag

The test harnesses I’ve seen either expect a top-level key (skip: true) or a marker in the filename; using a tag value (- skip) is uncommon. Double-check that the runner actually inspects tags for skip, otherwise the case will still execute.


4-5: Validation complete: referenced manifest exists

The ./app/manifest.yaml file referenced in
tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml
resolves to
tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/app/manifest.yaml.
No further action required.

tests/llm/fixtures/test_ask_holmes/02_what_is_wrong_with_pod/test_case.yaml (1)

1-1: Include namespace in the object reference to avoid ambiguity

Down-stream evaluators often rely on the triple <type/namespace/name>. Omitting the namespace may cause the LLM to mis-resolve the reference when multiple pods with the same name exist in different namespaces.

-user_prompt: 'What is wrong with << { "type": "pod", "name": "giant-narwhal-6958c5bdd8-69gtn" } >> ?'
+user_prompt: 'What is wrong with << { "type": "pod", "namespace": "app-02", "name": "giant-narwhal-6958c5bdd8-69gtn" } >> ?'

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0010374 and 879a110.

📒 Files selected for processing (73)
  • .github/workflows/llm-evaluation.yaml (1 hunks)
  • CLAUDE.md (1 hunks)
  • pyproject.toml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/01_how_many_pods/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/07_high_latency/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/08_sock_shop_frontend/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/09_crashpod/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/10_image_pull_backoff/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/11_init_containers/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/12_job_crashing/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/13_pending_node_selector/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/14_pending_resources/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/15_failed_readiness_probe/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/17_oom_kill/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/18_crash_looping_v2/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/20_long_log_file_search/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/21_job_fail_curl_no_svc_account/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/22_high_latency_dbi_down/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/23_app_error_in_current_logs/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/24_misconfigured_pvc/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/25_misconfigured_ingress_class/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/28_permissions_error_helm_tools_enabled/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/38_rabbitmq_split_head/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_all_tools/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_new_tools/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_new_tools_no_runbook/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_old_tools/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_all_tools/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_old_tools/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/43_slack_deployment_logs/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/45_fetch_deployment_logs_simple/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/46_job_crashing_no_longer_exists/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/test_case.yaml (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/53_logs_find_term/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/55_kafka_runbook/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/56_kafka_runbook_no_tool/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/58_counting_pods_by_status/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/59_label_based_counting/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/60_count_less_than/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/64_keda_vs_hpa_confusion/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/65_health_check_followup/test_case.yaml (0 hunks)
  • tests/llm/fixtures/test_ask_holmes/66_http_error_needle/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/67_performance_degradation/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/68_cascading_failures/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/74_config_change_impact/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/75_network_flapping/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/83_secret_not_found/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml (1 hunks)
💤 Files with no reviewable changes (32)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_old_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/18_crash_looping_v2/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/38_rabbitmq_split_head/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/01_how_many_pods/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_old_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/45_fetch_deployment_logs_simple/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/08_sock_shop_frontend/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/64_keda_vs_hpa_confusion/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/22_high_latency_dbi_down/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/28_permissions_error_helm_tools_enabled/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/23_app_error_in_current_logs/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_new_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/10_image_pull_backoff/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/24_misconfigured_pvc/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/25_misconfigured_ingress_class/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/12_job_crashing/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_all_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/15_failed_readiness_probe/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/46_job_crashing_no_longer_exists/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/17_oom_kill/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/20_long_log_file_search/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_new_tools_no_runbook/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/43_slack_deployment_logs/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/21_job_fail_curl_no_svc_account/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/13_pending_node_selector/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/59_label_based_counting/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/11_init_containers/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_all_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/58_counting_pods_by_status/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/57_wrong_namespace/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/65_health_check_followup/test_case.yaml
✅ Files skipped from review due to trivial changes (9)
  • tests/llm/fixtures/test_ask_holmes/07_high_latency/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/09_crashpod/test_case.yaml
  • CLAUDE.md
  • tests/llm/fixtures/test_ask_holmes/82_pod_anti_affinity_conflict/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/78_resource_quota_exceeded/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/71_connection_pool_starvation/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/85_hpa_not_scaling/test_case.yaml
🚧 Files skipped from review as they are similar to previous changes (31)
  • tests/llm/fixtures/test_ask_holmes/14_pending_resources/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/56_kafka_runbook_no_tool/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/60_count_less_than/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/53_logs_find_term/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/52_logs_login_issues/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/27_permissions_error_no_helm_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/76_service_discovery_issue/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/33_http_latency_graph/test_case.yaml
  • pyproject.toml
  • tests/llm/fixtures/test_ask_holmes/84_network_policy_blocking_traffic/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/63_fetch_error_logs_no_errors/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/80_pvc_storage_class_mismatch/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/55_kafka_runbook/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/73_time_window_anomaly/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/83_secret_not_found/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/68_cascading_failures/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/67_performance_degradation/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/86_configmap_like_but_secret/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/66_http_error_needle/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/70_memory_leak_detection/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/75_network_flapping/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/88_affinity_like_but_taints/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/69_rate_limit_exhaustion/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/74_config_change_impact/test_case.yaml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Pre-commit checks
  • GitHub Check: Pre-commit checks

Comment thread .github/workflows/llm-evaluation.yaml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (2)

3-6: Consider relaxing the exact-match requirement on pod name & port to keep the test resilient

Hard-coding the full pod name (grafana-7f8b9c6d5-x2m4n) and port 3000 in the grading rubric makes the evaluation very brittle:

  • If the manifest or generator ever changes the random suffix, the fixture will fail even when the LLM gives a perfectly valid answer (kubectl port-forward -n app-03 $(kubectl get pod -l app=grafana -n app-03 -o name | cut -d/ -f2) 3000:3000, etc.).
  • Past feedback (see Sheeproid learning) recommends descriptive checks for flexible answers to avoid unnecessary failures.

A more robust approach is to assert that the answer contains:

  1. kubectl port-forward
  2. the correct namespace (app-03)
  3. a variable pod name matching grafana-* (regex)
  4. the port mapping 3000:3000

Example diff (YAML anchors/regex supported by most internal graders):

-  - Must find the actual grafana pod name (grafana-7f8b9c6d5-x2m4n)
-  - Must include the correct port (3000)
-  - "Full kubectl port-forward command like: kubectl port-forward -n app-03 grafana-7f8b9c6d5-x2m4n 3000:3000"
+  - Contains "kubectl port-forward"
+  - Includes namespace "app-03"
+  - References a pod matching /grafana-[a-z0-9-]+/
+  - Maps port 3000:3000

11-29: Pod setup works but can be simplified & made lighter

The busybox command spins an endless nc loop which is unnecessary for a port-forward test and may consume CPU on constrained runners. A lighter (and less error-prone) option is:

command: ["sleep", "infinity"]
ports:
- containerPort: 3000
  name: http

This keeps the container Ready without requiring nc (which is not compiled into all BusyBox builds).
If traffic testing is required later, you can add a readinessProbe instead.

Minor bonus: consider adding --- separators after the namespace creation to avoid accidental YAML concatenation when manifests grow.

tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml (1)

4-6: Replace fixed sleep 20 with a deterministic kubectl wait

Static sleeps slow the test suite and can still be flaky if cluster latency is higher or lower than expected.
Leverage kubectl wait on the Deployment/Pod readiness with a timeout—this is faster when the resource is ready early and safer when it takes longer.

-  sleep 20
+# wait up to 20 s for pod containers to start (non-blocking once ready)
+  kubectl wait --for=condition=ContainersReady pod -l app=app-server -n namespace-79 --timeout=20s || true
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 879a110 and 1d690d0.

📒 Files selected for processing (56)
  • pyproject.toml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/01_how_many_pods/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/05_image_version/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/08_sock_shop_frontend/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/09_crashpod/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/10_image_pull_backoff/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/11_init_containers/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/12_job_crashing/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/13_pending_node_selector/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/14_pending_resources/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/15_failed_readiness_probe/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/16_failed_no_toolset_found/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/17_oom_kill/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/18_crash_looping_v2/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/20_long_log_file_search/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/23_app_error_in_current_logs/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/24_misconfigured_pvc/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/28_permissions_error_helm_tools_enabled/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/29_events_from_alert_manager/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/39_failed_toolset/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/40_disabled_toolset/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/41_setup_argo/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_all_tools/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/43_current_datetime_from_prompt/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/43_slack_deployment_logs/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/conversation_history/01_assistant.md (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-0_ask-holmes-slack-statefulset-logs_-10800.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-0_ask-holmes-slack-statefulset-logs_2025-06-12.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-0_ask-holmes-slack-statefulset-logs_2025-06-12_2025-06-12.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-alertmanager-0_ask-holmes-slack-statefulset-logs_-10800.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_find_resource_statefulset_alertmanager.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_by_kind_in_namespace_pod_ask-holmes-slack-statefulset-logs.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_by_kind_in_namespace_statefulset_ask-holmes-slack-statefulset-logs.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_by_name_statefulset_alertmanager_ask-holmes-slack-statefulset-logs.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_yaml_statefulset_alertmanager_ask-holmes-slack-statefulset-logs.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_lineage_children_statefulset_alertmanager_ask-holmes-slack-statefulset-logs.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/manifest.yaml (11 hunks)
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/45_fetch_deployment_logs_simple/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/48_logs_since_thursday/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/53_logs_find_term/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/kubectl_describepod_fast-fail-pod_ask-holmes-namespace-54b.txt (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/kubectl_get_by_kind_in_namespacepod_ask-holmes-namespace-54b.txt (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/manifest.yaml (2 hunks)
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/59_label_based_counting/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/test_case.yaml (1 hunks)
  • tests/llm/fixtures/test_ask_holmes/83_secret_not_found/test_case.yaml (1 hunks)
✅ Files skipped from review due to trivial changes (26)
  • tests/llm/fixtures/test_ask_holmes/39_failed_toolset/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/41_setup_argo/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/kubectl_get_by_kind_in_namespacepod_ask-holmes-namespace-54b.txt
  • tests/llm/fixtures/test_ask_holmes/16_failed_no_toolset_found/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_find_resource_statefulset_alertmanager.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-0_ask-holmes-slack-statefulset-logs_2025-06-12_2025-06-12.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_by_kind_in_namespace_pod_ask-holmes-slack-statefulset-logs.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/conversation_history/01_assistant.md
  • tests/llm/fixtures/test_ask_holmes/01_how_many_pods/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-0_ask-holmes-slack-statefulset-logs_2025-06-12.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_by_name_statefulset_alertmanager_ask-holmes-slack-statefulset-logs.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_lineage_children_statefulset_alertmanager_ask-holmes-slack-statefulset-logs.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_by_kind_in_namespace_statefulset_ask-holmes-slack-statefulset-logs.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/kubectl_get_yaml_statefulset_alertmanager_ask-holmes-slack-statefulset-logs.txt
  • tests/llm/fixtures/test_ask_holmes/29_events_from_alert_manager/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/48_logs_since_thursday/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/11_init_containers/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/40_disabled_toolset/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/43_current_datetime_from_prompt/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-alertmanager-0_ask-holmes-slack-statefulset-logs_-10800.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/fetch_pod_logs_alertmanager-0_ask-holmes-slack-statefulset-logs_-10800.txt
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_result_all_tools/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/manifest.yaml
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/kubectl_describepod_fast-fail-pod_ask-holmes-namespace-54b.txt
  • tests/llm/fixtures/test_ask_holmes/44_slack_statefulset_logs/manifest.yaml
🚧 Files skipped from review as they are similar to previous changes (28)
  • tests/llm/fixtures/test_ask_holmes/28_permissions_error_helm_tools_enabled/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/08_sock_shop_frontend/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/24_misconfigured_pvc/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/61_exact_match_counting/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/23_app_error_in_current_logs/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/09_crashpod/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/43_slack_deployment_logs/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/20_long_log_file_search/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/53_logs_find_term/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/45_fetch_deployment_logs_simple/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/12_job_crashing/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/13_pending_node_selector/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/18_crash_looping_v2/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/42_dns_issues_steps_new_tools/test_case.yaml
  • pyproject.toml
  • tests/llm/fixtures/test_ask_holmes/54_not_truncated_when_getting_pods/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/17_oom_kill/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/51_logs_summarize_errors/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/10_image_pull_backoff/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/77_liveness_probe_misconfiguration/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/15_failed_readiness_probe/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/62_fetch_error_logs_with_errors/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/05_image_version/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/81_service_account_permission_denied/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/59_label_based_counting/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/83_secret_not_found/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/14_pending_resources/test_case.yaml
  • tests/llm/fixtures/test_ask_holmes/04_related_k8s_events/test_case.yaml
🧰 Additional context used
🧠 Learnings (2)
tests/llm/fixtures/test_ask_holmes/79_configmap_mount_issue/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (1)

Learnt from: Sheeproid
PR: #586
File: tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml:4-4
Timestamp: 2025-07-02T10:27:17.231Z
Learning: In LLM-as-judge test cases for HolmesGPT, expected outputs should be descriptive rather than prescriptive when testing for flexible responses like port numbers. Using specific values in expected outputs can cause unnecessary test failures when the AI generates different but equally valid responses.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.11)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.12)
  • GitHub Check: build (3.10)
  • GitHub Check: build (3.11)
🔇 Additional comments (1)
tests/llm/fixtures/test_ask_holmes/03_what_is_the_command_to_port_forward/test_case.yaml (1)

32-32: Namespace deletion is forceful; verify other tests don’t still rely on it

kubectl delete namespace app-03 --force --grace-period=0 is fine for speed, but will abruptly terminate resources. Ensure no parallel tests expect the namespace to persist or rely on graceful shutdown hooks.

@aantn aantn changed the title Improve kubernetes scheduling scenarios Improve evals Jul 25, 2025
@aantn
aantn enabled auto-merge (squash) July 25, 2025 21:30
@github-actions

Copy link
Copy Markdown
Contributor

Results of HolmesGPT evals

  • ask_holmes: 41/93 test cases were successful, 1 regressions, 5 skipped, 38 mock failures
  • investigate: 14/16 test cases were successful, 0 regressions, 1 mock failures
Test suite Test case Status
ask 01_how_many_pods ✅
ask 02_what_is_wrong_with_pod 🔧
ask 03_what_is_the_command_to_port_forward 🔧
ask 04_related_k8s_events 🔧
ask 05_image_version 🔧
ask 06_explain_issue ✅
ask 07_high_latency ✅
ask 08_sock_shop_frontend ⚠️
ask 09_crashpod ✅
ask 10_image_pull_backoff 🔧
ask 11_init_containers ✅
ask 12_job_crashing ✅
ask 13_pending_node_selector ✅
ask 14_pending_resources ✅
ask 15_failed_readiness_probe ✅
ask 16_failed_no_toolset_found ✅
ask 17_oom_kill ✅
ask 18_crash_looping_v2 ✅
ask 19_detect_missing_app_details 🔧
ask 20_long_log_file_search ✅
ask 21_job_fail_curl_no_svc_account ⚠️
ask 22_high_latency_dbi_down 🔧
ask 23_app_error_in_current_logs ✅
ask 24_misconfigured_pvc ✅
ask 25_misconfigured_ingress_class 🔧
ask 26_multi_container_logs ✅
ask 27_permissions_error_no_helm_tools 🔧
ask 28_permissions_error_helm_tools_enabled 🔧
ask 29_events_from_alert_manager ✅
ask 30_basic_promql_graph_cluster_memory ✅
ask 31_basic_promql_graph_pod_memory 🔧
ask 32_basic_promql_graph_pod_cpu ✅
ask 33_http_latency_graph ↪️
ask 34_memory_graph ✅
ask 35_tempo ✅
ask 36_argocd_find_resource ✅
ask 37_argocd_wrong_namespace ⚠️
ask 38_rabbitmq_split_head ✅
ask 39_failed_toolset ✅
ask 40_disabled_toolset ✅
ask 41_setup_argo0 ✅
ask 41_setup_argo1 ✅
ask 42_dns_issues_result_all_tools ↪️
ask 42_dns_issues_result_new_tools 🔧
ask 42_dns_issues_result_new_tools_no_runbook 🔧
ask 42_dns_issues_result_old_tools 🔧
ask 42_dns_issues_steps_new_all_tools 🔧
ask 42_dns_issues_steps_new_tools 🔧
ask 42_dns_issues_steps_old_tools 🔧
ask 43_current_datetime_from_prompt ✅
ask 43_slack_deployment_logs 🔧
ask 44_slack_statefulset_logs 🔧
ask 45_fetch_deployment_logs_simple ✅
ask 46_job_crashing_no_longer_exists ↪️
ask 47_truncated_logs_context_window ↪️
ask 48_logs_since_thursday 🔧
ask 49_logs_since_last_week 🔧
ask 50_logs_since_specific_date ❌
ask 51_logs_summarize_errors ✅
ask 52_logs_login_issues ⚠️
ask 53_logs_find_term ✅
ask 54_not_truncated_when_getting_pods ✅
ask 55_kafka_runbook ↪️
ask 56_kafka_runbook_no_tool ⚠️
ask 57_wrong_namespace ⚠️
ask 58_counting_pods_by_status ⚠️
ask 59_label_based_counting ✅
ask 60_count_less_than 🔧
ask 61_exact_match_counting ✅
ask 62_fetch_error_logs_with_errors ✅
ask 63_fetch_error_logs_no_errors ✅
ask 64_keda_vs_hpa_confusion ⚠️
ask 65_health_check_followup 🔧
ask 66_http_error_needle 🔧
ask 67_performance_degradation 🔧
ask 68_cascading_failures 🔧
ask 69_rate_limit_exhaustion 🔧
ask 70_memory_leak_detection 🔧
ask 71_connection_pool_starvation 🔧
ask 73_time_window_anomaly 🔧
ask 74_config_change_impact 🔧
ask 75_network_flapping 🔧
ask 76_service_discovery_issue 🔧
ask 77_liveness_probe_misconfiguration 🔧
ask 78_resource_quota_exceeded 🔧
ask 79_configmap_mount_issue 🔧
ask 80_pvc_storage_class_mismatch 🔧
ask 81_service_account_permission_denied 🔧
ask 82_pod_anti_affinity_conflict 🔧
ask 83_secret_not_found 🔧
ask 84_network_policy_blocking_traffic 🔧
ask 85_hpa_not_scaling 🔧
ask 86_configmap_like_but_secret 🔧
investigate 01_oom_kill 🔧
investigate 02_crashloop_backoff 🔧
investigate 03_cpu_throttling ✅
investigate 04_image_pull_backoff 🔧
investigate 05_crashpod ✅
investigate 06_job_failure 🔧
investigate 07_job_syntax_error 🔧
investigate 08_memory_pressure 🔧
investigate 09_high_latency 🔧
investigate 10_KubeDeploymentReplicasMismatch 🔧
investigate 11_KubePodCrashLooping ✅
investigate 12_KubePodNotReady ✅
investigate 13_Watchdog ✅
investigate 14_tempo 🔧
investigate 15_dns_resolution ⚠️
investigate 16_dns_resolution_no_tool 🔧

Legend

  • ✅ the test was successful
  • ↪️ the test was skipped
  • ⚠️ the test failed but is known to be flaky or known to fail
  • 🔧 the test failed due to mock data issues (not a code regression)
  • ❌ the test failed and should be fixed before merging the PR

@arikalon1 arikalon1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants