From d41ef75040dc84ff343496a2e705855767ae4f2f Mon Sep 17 00:00:00 2001 From: Oliver Kieselbach Date: Thu, 19 Mar 2026 16:44:12 +0100 Subject: [PATCH] update skills --- .claude/commands/backend-logs.md | 176 -------------------- .claude/commands/event-query.md | 231 --------------------------- .claude/commands/session-debug.md | 116 -------------- .claude/scripts/query-appinsights.sh | 76 --------- .claude/scripts/query-table.sh | 56 ------- 5 files changed, 655 deletions(-) delete mode 100644 .claude/commands/backend-logs.md delete mode 100644 .claude/commands/event-query.md delete mode 100644 .claude/commands/session-debug.md delete mode 100644 .claude/scripts/query-appinsights.sh delete mode 100644 .claude/scripts/query-table.sh diff --git a/.claude/commands/backend-logs.md b/.claude/commands/backend-logs.md deleted file mode 100644 index 39023317..00000000 --- a/.claude/commands/backend-logs.md +++ /dev/null @@ -1,176 +0,0 @@ -# Backend Logs Skill - -Du analysierst Azure Functions Backend-Logs (Application Insights) um Probleme zu debuggen. Du baust KQL-Queries, fuehrst sie aus und korrelierst die Ergebnisse mit dem Source Code. - -## Input - -`$ARGUMENTS` enthaelt entweder: -- Eine Beschreibung des Problems (z.B. "warum schlagen die ingest calls fehl") -- Einen Funktionsnamen (z.B. "IngestEventsFunction" oder "IngestEvents") -- Einen Zeitraum und/oder spezifischen Fehler (z.B. "errors letzte Stunde") -- Leer = Health-Overview der letzten Stunde - -## Hilfsskript - -Nutze das Helper-Script `.claude/scripts/query-appinsights.sh` fuer alle Abfragen: - -```bash -.claude/scripts/query-appinsights.sh "" [timespan] -``` - -- Timespan-Format: ISO 8601 Duration (z.B. PT1H, PT24H, P7D) -- Default-Timespan: PT1H (letzte Stunde) -- Das Script nutzt `$AUTOPILOT_MONITOR_APPINSIGHTS_ID` fuer die App Insights App-ID -- Ergebnis ist ein JSON-Array mit Objekten (Spaltenname → Wert) - -## Wichtig: Datenmodell - -Die `requests`-Tabelle ist bei diesem Projekt leer (.NET isolated worker Eigenheit). Alle Function-Ausfuehrungsdaten liegen in der `traces`-Tabelle: -- **FunctionStarted**: `message` beginnt mit `Executing 'Functions.'` -- **FunctionCompleted**: `message` beginnt mit `Executed 'Functions.'` und enthaelt `(Succeeded, ...)` oder `(Failed, ...)` -- `customDimensions` enthaelt JSON mit `prop__functionName`, `prop__status`, `prop__executionDuration`, `prop__invocationId` -- `operation_Name` = Function-Name (ohne "Functions." Prefix) - -## Schritt 1: Problem verstehen und Strategie waehlen - -Analysiere `$ARGUMENTS` und waehle die passende Strategie: - -### Strategie A: Fehler-Analyse (User beschreibt ein Problem) -→ Weiter mit Schritt 2a - -### Strategie B: Funktions-Analyse (User nennt eine Function) -→ Weiter mit Schritt 2b - -### Strategie C: Allgemeine Uebersicht (Input leer oder User will Status sehen) -→ Weiter mit Schritt 2c - -## Schritt 2a: Fehler-Uebersicht - -### Exceptions pruefen: - -```bash -.claude/scripts/query-appinsights.sh "exceptions | where timestamp > ago(1h) | summarize count() by outerMessage, problemId | order by count_ desc | take 20" -``` - -Falls leer, Zeitraum auf 24h erweitern: - -```bash -.claude/scripts/query-appinsights.sh "exceptions | where timestamp > ago(24h) | summarize count() by outerMessage, problemId | order by count_ desc | take 20" PT24H -``` - -### Details zum relevantesten Fehler: - -```bash -.claude/scripts/query-appinsights.sh "exceptions | where timestamp > ago(1h) | where outerMessage contains '' | project timestamp, outerMessage, innermostMessage, details[0].rawStack, operation_Id | order by timestamp desc | take 5" -``` - -### Fehlgeschlagene Functions: - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where message contains 'Failed' | project timestamp, message, operation_Name, operation_Id, customDimensions | order by timestamp desc | take 20" -``` - -### Warnings und Errors in Traces: - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where severityLevel >= 2 | summarize count() by message, severityLevel | order by count_ desc | take 20" -``` - -## Schritt 2b: Function-Requests analysieren - -Uebersicht einer Function (Erfolge vs Fehler): - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where operation_Name == '' | where message contains 'Succeeded' or message contains 'Failed' | extend status = iif(message contains 'Succeeded', 'Succeeded', 'Failed') | extend durationMs = toint(extract('Duration=([0-9]+)ms', 1, message)) | summarize total=count(), succeeded=countif(status == 'Succeeded'), failed=countif(status == 'Failed'), avgMs=avg(durationMs), p95Ms=percentile(durationMs, 95) by operation_Name" -``` - -Letzte fehlgeschlagene Ausfuehrungen: - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where operation_Name == '' | where message contains 'Failed' | project timestamp, message, operation_Id | order by timestamp desc | take 10" -``` - -Alle Traces einer Function (letzte Ausfuehrungen): - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where operation_Name == '' | project timestamp, message, severityLevel, operation_Id | order by timestamp desc | take 50" -``` - -## Schritt 2c: Health-Overview - -Alle Functions mit Erfolgsrate: - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message startswith 'Executed' | where message contains 'Succeeded' or message contains 'Failed' | extend status = iif(message contains 'Succeeded', 'Succeeded', 'Failed') | extend durationMs = toint(extract('Duration=([0-9]+)ms', 1, message)) | summarize total=count(), failed=countif(status == 'Failed'), avgMs=avg(durationMs) by operation_Name | extend failRate=round(100.0 * failed / total, 1) | order by failed desc, total desc | take 30" -``` - -## Schritt 3: Tiefere Analyse mit Operation-ID - -Wenn du eine operation_Id hast, hole ALLE Telemetrie dazu: - -```bash -.claude/scripts/query-appinsights.sh "union traces, exceptions, dependencies | where operation_Id == '' | order by timestamp asc | project timestamp, itemType, message, outerMessage, severityLevel, operation_Name, customDimensions" PT24H -``` - -## Schritt 4: Trace-Logs durchsuchen - -Nach Suchbegriff: - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(1h) | where message contains '' | project timestamp, message, severityLevel, operation_Name, operation_Id | order by timestamp desc | take 30" -``` - -Severity-Level: 0=Verbose, 1=Information, 2=Warning, 3=Error, 4=Critical - -## Schritt 5: Dependency-Analyse (externe Aufrufe) - -```bash -.claude/scripts/query-appinsights.sh "dependencies | where timestamp > ago(1h) | where success == false | summarize count() by name, type, target, resultCode | order by count_ desc | take 20" -``` - -## Schritt 6: Zeitreihen-Analyse (Pattern erkennen) - -```bash -.claude/scripts/query-appinsights.sh "traces | where timestamp > ago(24h) | where message startswith 'Executed' | where operation_Name == '' | where message contains 'Succeeded' or message contains 'Failed' | extend status = iif(message contains 'Succeeded', 'Succeeded', 'Failed') | summarize total=count(), errors=countif(status == 'Failed') by bin(timestamp, 15m) | order by timestamp asc" PT24H -``` - -## Schritt 7: Code-Korrelation - -Nachdem du die Fehler identifiziert hast, lies den relevanten Code: - -### Backend Functions (nach Function-Name suchen) -- Alle Functions: `src/Backend/AutopilotMonitor.Functions/Functions/` -- Unterordner: Admin, Bootstrap, Config, Diagnostics, Feedback, Galactic, Infrastructure, Ingest, Metrics, Progress, Reports, Rules, Sessions -- Security: `src/Backend/AutopilotMonitor.Functions/Security/` -- Services: `src/Backend/AutopilotMonitor.Functions/Services/` -- Middleware: `src/Backend/AutopilotMonitor.Functions/Middleware/` - -### Haeufige Fehlerquellen -- **401/403**: `SecurityValidationExtensions.cs`, `AuthenticationMiddleware.cs`, `PolicyEnforcementMiddleware.cs` -- **429 Rate Limiting**: `RateLimitService.cs` -- **Table Storage**: `TableStorageService*.cs` -- **Deserialization**: JSON-Parsing im Function Body -- **Tenant-Config**: `TenantConfigurationService.cs` - -## Ausgabe-Format - -1. **Problem-Zusammenfassung**: Was wurde gefunden (1-3 Saetze) -2. **Fehler-Details**: Exceptions, fehlgeschlagene Functions mit Kontext -3. **Timeline**: Wann das Problem begann/endet, ob es andauert -4. **Root-Cause Analyse**: Korrelation mit Code, was den Fehler verursacht -5. **Empfehlung**: Was getan werden sollte - -## Hinweise - -- Die Env-Variable `AUTOPILOT_MONITOR_APPINSIGHTS_ID` enthaelt die Application Insights App-ID -- `az monitor app-insights query` braucht aktiven Azure CLI Login (`az login`) -- Die `requests`-Tabelle ist leer — nutze immer `traces` fuer Function-Ausfuehrungen -- Bei grossen Ergebnismengen immer `| take N` verwenden -- `operation_Id` verbindet Traces → Exceptions → Dependencies einer einzelnen Ausfuehrung -- `operation_Name` = Function-Name (z.B. "IngestEvents", "GetAgentConfig") -- `cloud_RoleName` = `autopilotmonitor-api` -- Sampling ist aktiv — bei Traces koennen einzelne Eintraege fehlen -- Zeitraum-Mapping: PT1H=1h, PT6H=6h, PT24H=24h, P7D=7 Tage -- Wenn der User einen Zeitraum nennt (z.B. "letzte 6 Stunden"), passe sowohl den KQL-Filter (`ago(6h)`) als auch den Timespan-Parameter (`PT6H`) an -- **KQL-Escaping**: Vermeide `extract()` mit Regex-Backslashes — das Bash→az→KQL Escaping ist fragil. Nutze stattdessen `iif(message contains ..., ...)` oder `has`/`contains` Operatoren -- Nutze einfache Anfuehrungszeichen fuer die KQL-Query im Script-Aufruf, damit Bash keine Variablensubstitution macht diff --git a/.claude/commands/event-query.md b/.claude/commands/event-query.md deleted file mode 100644 index a2eb98d0..00000000 --- a/.claude/commands/event-query.md +++ /dev/null @@ -1,231 +0,0 @@ -# Event Query Skill - -Du suchst und analysierst Events aus Azure Table Storage fuer Debugging und Weiterentwicklung. Du kannst Events nach Typ suchen, Kontext um ein bestimmtes Event herum anzeigen, oder einzelne Events abrufen. - -## Input - -`$ARGUMENTS` enthaelt eine natuerlichsprachige Beschreibung was gesucht wird. Beispiele: - -- `"finde app_tracking_summary events mit vielen app namen"` — Suche nach EventType mit Kriterien -- `"zeige mir die 5 events vor und nach event abc123... in session def456..."` — Kontext-Fenster -- `"event abc123... in session def456..."` — Einzelnes Event abrufen -- `"alle error_detected events in session def456..."` — Events nach Typ in einer Session -- `"finde phase_transition events"` — Globale Suche ueber alle Events - -## Hilfsskript - -Nutze das Helper-Script `.claude/scripts/query-table.sh` fuer alle Table-Abfragen: - -```bash -.claude/scripts/query-table.sh "" [select_fields] [top_count] -``` - -Das Script: -- Parst den Connection String aus `$AUTOPILOT_MONITOR_TABLE_CS` -- Baut die REST API URL zusammen -- Gibt JSON zurueck -- `top_count` begrenzt die Ergebnismenge serverseitig (optional) - -## Schritt 1: Intent erkennen - -Analysiere `$ARGUMENTS` und waehle die passende Strategie: - -### Strategie A: Events nach Typ suchen (mit optionalen Kriterien) -User sucht Events eines bestimmten EventType, ggf. mit Inhaltskriterien. -→ Weiter mit Schritt 2a - -### Strategie B: Kontext-Fenster (Events um ein bestimmtes Event herum) -User will Events VOR und NACH einem bestimmten Event sehen. -→ Weiter mit Schritt 2b - -### Strategie C: Einzelnes Event abrufen -User will ein spezifisches Event mit allen Details sehen. -→ Weiter mit Schritt 2c - -## TenantId ermitteln (nur wenn SessionId angegeben) - -Wenn eine SessionId angegeben ist, zuerst TenantId aus der Sessions-Table holen: - -```bash -.claude/scripts/query-table.sh Sessions "RowKey eq '${SESSION_ID}'" "PartitionKey,RowKey,Status" -``` - -- **PartitionKey** = TenantId -- Events-PartitionKey wird dann: `{TenantId}_{SessionId}` - -Bei globaler Suche (kein SessionId) ist kein TenantId noetig. - -## Schritt 2a: Events nach Typ suchen - -### Standard: Globale Suche (ueber alle Events) - -```bash -.claude/scripts/query-table.sh Events "EventType eq '${EVENT_TYPE}'" "" 20 -``` - -Das ist ein Table Scan — in der Preview-Phase kein Problem wegen geringer Datenmenge. `$top=20` als Default-Limit, anpassen wenn User mehr oder weniger will. - -### Optional: Eingeschraenkt auf eine Session - -Wenn User eine SessionId angibt: - -```bash -.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and EventType eq '${EVENT_TYPE}'" -``` - -### Optional: Eingeschraenkt auf einen Tenant - -Alle Events des Typs holen und per `jq` nach TenantId filtern (Table Storage unterstuetzt kein `startswith`): - -```bash -.claude/scripts/query-table.sh Events "EventType eq '${EVENT_TYPE}'" "" 50 | jq '[.value[] | select(.PartitionKey | startswith("'${TENANT_ID}'_"))]' -``` - -### Mit Inhaltskriterien (DataJson durchsuchen) - -Wenn User nach Inhalten sucht (z.B. "mit vielen app namen"), erst Events holen, dann DataJson mit `jq` filtern: - -```bash -# Beispiel: app_tracking_summary mit vielen Apps -.claude/scripts/query-table.sh Events "EventType eq 'app_tracking_summary'" "" 50 | jq '[.value[] | select(.DataJson | fromjson | .apps | length > 5)]' -``` - -Die `jq`-Filter muessen je nach Suchkriterium angepasst werden. Schau dir zuerst ein paar Beispiel-Events an um die DataJson-Struktur zu verstehen, bevor du filterst. - -### Mit Severity-Filter - -```bash -# Nur Warnings und hoeher -.claude/scripts/query-table.sh Events "EventType eq '${EVENT_TYPE}' and Severity ge 2" "" 20 -``` - -## Schritt 2b: Kontext-Fenster - -User will N Events vor und nach einem bestimmten Event sehen (Default: 5). - -### 1. Ziel-Event finden - -Per EventId: - -```bash -.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and EventId eq '${EVENT_ID}'" -``` - -Oder per anderer Identifikation (z.B. EventType + ungefaehrer Zeitpunkt). Aus dem Ergebnis den **RowKey** merken. - -### 2. Events davor holen - -```bash -.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and RowKey lt '${TARGET_ROW_KEY}'" "" 200 -``` - -Da Table Storage kein `$orderby` unterstuetzt und RowKeys aufsteigend zurueckkommen, kommen die Events chronologisch. Nutze `jq` um die letzten N zu extrahieren: - -```bash -... | jq '[.value | sort_by(.RowKey) | .[-5:]]' -``` - -### 3. Events danach holen - -```bash -.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and RowKey gt '${TARGET_ROW_KEY}'" "" 5 -``` - -Hier funktioniert `$top=N` direkt, da RowKeys aufsteigend sind. - -### 4. Kombinierte Timeline ausgeben - -Zeige die Events in chronologischer Reihenfolge mit dem Ziel-Event deutlich markiert: - -``` -... (events davor) ->>> TARGET EVENT >>> EventType | Severity | Message -... (events danach) -``` - -## Schritt 2c: Einzelnes Event abrufen - -```bash -.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}' and EventId eq '${EVENT_ID}'" -``` - -Wenn kein SessionId bekannt: globale Suche per EventId (Table Scan): - -```bash -.claude/scripts/query-table.sh Events "EventId eq '${EVENT_ID}'" "" 1 -``` - -Zeige ALLE Felder des Events, inklusive DataJson geparsed und formatiert. - -## Ausgabe-Format - -### Einzelnes Event -Alle Properties strukturiert anzeigen. DataJson IMMER parsen und als formatierten JSON-Block darstellen: - -``` -EventId: abc123... -EventType: app_tracking_summary -Severity: Info (1) -Phase: DeviceSetup (2) -Source: Agent -Timestamp: 2024-03-15T10:23:45.123Z -Message: App tracking summary collected -Session: def456... -Tenant: ghi789... - -DataJson: -{ - "apps": [...], - "totalApps": 12, - ... -} -``` - -### Event-Liste (Suche/Kontext) -Kompakte Tabelle mit den wichtigsten Feldern: - -``` -RowKey (kurz) | EventType | Sev | Phase | Message (gekuerzt) -------------------|------------------------|------|--------------|-------------------- -...0001 | phase_transition | Info | DeviceSetup | Phase changed to... -...0002 | app_install_started | Info | DeviceSetup | Installing App X ->>> ...0003 | error_detected | Err | DeviceSetup | Failed to install <<< -...0004 | app_install_failed | Err | DeviceSetup | App X failed -...0005 | phase_transition | Info | AccountSetup | Phase changed to... -``` - -Bei Interesse an einem bestimmten Event aus der Liste: DataJson auf Nachfrage anzeigen oder direkt wenn es fuer die Frage relevant ist. - -## Bekannte EventTypes - -Referenz der gaengigsten EventTypes: - -**Phase & Lifecycle:** `phase_transition`, `esp_phase_changed`, `enrollment_complete`, `enrollment_failed`, `whiteglove_complete`, `whiteglove_resumed`, `completion_check` - -**App Installation:** `app_install_started`, `app_install_completed`, `app_install_failed`, `app_install_skipped`, `app_download_started`, `download_progress` - -**App Tracking:** `app_tracking_summary`, `app_tracking_started`, `app_tracking_completed` - -**Network:** `network_state_change`, `network_connectivity_check` - -**System:** `error_detected`, `log_entry`, `performance_snapshot`, `gather_result`, `gather_rules_collection_completed`, `agent_metrics_snapshot` - -**ESP:** `esp_state_change`, `esp_ui_state`, `esp_failure`, `esp_provisioning_status` - -**Andere:** `cert_validation`, `desktop_arrived`, `script_completed`, `script_failed` - -## Enum-Mappings - -**Severity:** Trace=-1, Debug=0, Info=1, Warning=2, Error=3, Critical=4 - -**Phase:** Unknown=0, DevicePreparation=1, DeviceSetup=2, AccountSetup=3, DeviceESP=4, UserESP=5, Complete=6, PreProvisioning=7 - -## Hinweise - -- DataJson ist ein JSON-String als Table-Property — IMMER parsen und anzeigen -- `performance_snapshot` und `agent_metrics_snapshot` Events standardmaessig ausblenden (Hochfrequenz-Rauschen), ausser explizit angefragt -- RowKey-Format `yyyyMMddHHmmssfff_NNNNNNNNNN` — String-Vergleich funktioniert fuer chronologische Sortierung -- EventId ist eine Property, KEIN Key — Suche per EventId ohne PartitionKey ist ein Table Scan -- Bei grossen Ergebnismengen `$top` nutzen und User informieren wenn Ergebnisse abgeschnitten wurden -- Fuer cross-session Suchen ist Table Scan OK in der Preview — bei Wachstum spaeter einschraenken -- Die Env-Variable `AUTOPILOT_MONITOR_TABLE_CS` enthaelt den Connection String mit SAS Token diff --git a/.claude/commands/session-debug.md b/.claude/commands/session-debug.md deleted file mode 100644 index 269deec0..00000000 --- a/.claude/commands/session-debug.md +++ /dev/null @@ -1,116 +0,0 @@ -# Session Debug Skill - -Du analysierst eine Autopilot-Monitor Session anhand ihrer SessionId. Du holst dir selbststaendig alle noetigen Daten aus Azure Table Storage via REST API und korrelierst sie mit dem Code. - -## Input - -Der User gibt eine SessionId (GUID) an, optional gefolgt von einer Frage. -Beispiel: `/session-debug abc12345-1234-1234-1234-123456789abc warum ist die Session failed?` - -Die gesamte Eingabe wird als `$ARGUMENTS` uebergeben. Die SessionId ist das erste Wort (GUID-Format), alles danach ist die Frage des Users. - -## Hilfsskript - -Nutze das Helper-Script `.claude/scripts/query-table.sh` fuer alle Table-Abfragen: - -```bash -.claude/scripts/query-table.sh "" [select_fields] -``` - -Das Script: -- Parst den Connection String aus `$AUTOPILOT_MONITOR_TABLE_CS` -- Baut die REST API URL zusammen -- Gibt JSON zurueck - -## Schritt 1: Session-Metadaten holen - -Zuerst die Session aus der **Sessions** Table holen um die TenantId zu bekommen: - -```bash -.claude/scripts/query-table.sh Sessions "RowKey eq '${SESSION_ID}'" -``` - -Aus dem Ergebnis: -- **PartitionKey** = TenantId -- **RowKey** = SessionId -- Wichtige Felder: `Status`, `CurrentPhase`, `EnrollmentType`, `StartedAt`, `CompletedAt`, `FailureReason`, `EventCount`, `DurationSeconds`, `AgentVersion`, `IsPreProvisioned`, `IsHybridJoin`, `IsUserDriven`, `OsBuild`, `SerialNumber`, `DeviceName`, `Manufacturer`, `Model` - -Fasse die Session-Metadaten kurz zusammen bevor du weiter machst. - -## Schritt 2: Events holen - -Mit TenantId und SessionId die Events aus der **Events** Table holen: - -```bash -.claude/scripts/query-table.sh Events "PartitionKey eq '${TENANT_ID}_${SESSION_ID}'" -``` - -Events-Struktur: -- **PartitionKey**: `{TenantId}_{SessionId}` -- **RowKey**: `{Timestamp:yyyyMMddHHmmssfff}_{Sequence:D10}` (chronologisch sortiert) -- **EventType**: z.B. `phase_transition`, `app_install_started`, `app_install_completed`, `error_detected`, `esp_page_change`, `hello_screen_detected`, `desktop_arrival`, `enrollment_complete`, `enrollment_failed`, `performance_snapshot`, `agent_metrics_snapshot`, `collector_started`, `collector_stopped`, `completion_check`, etc. -- **Severity**: Trace=-1, Debug=0, Info=1, Warning=2, Error=3, Critical=4 -- **Source**: Agent, IME, Registry, WMI, Network -- **Phase**: 0=Unknown, 1=DevicePreparation, 2=DeviceSetup, 3=AccountSetup, 4=DeviceESP, 5=UserESP, 6=Complete, 7=PreProvisioning -- **Message**: Menschenlesbarer Text -- **DataJson**: Strukturierte Metadaten als JSON String (oft die wichtigsten Details!) -- **Sequence**: Reihenfolge bei gleichem Timestamp - -## Schritt 3: Bei Bedarf weitere Tables abfragen - -Nur abfragen wenn fuer die Frage relevant: - -### RuleResults (automatische Analyse-Ergebnisse) -```bash -.claude/scripts/query-table.sh RuleResults "PartitionKey eq '${TENANT_ID}_${SESSION_ID}'" -``` - -### AppInstallSummaries (App-Installationen) -```bash -.claude/scripts/query-table.sh AppInstallSummaries "PartitionKey eq '${TENANT_ID}'" "PartitionKey,RowKey,AppName,Status,DurationSeconds,StartedAt,CompletedAt" -``` -Dann filtern: nur Eintraege deren RowKey mit der SessionId beginnt. - -### TenantConfiguration -```bash -.claude/scripts/query-table.sh TenantConfiguration "PartitionKey eq '${TENANT_ID}' and RowKey eq 'config'" -``` - -## Schritt 4: Analyse - -Nachdem du die Daten hast: - -1. **Chronologie aufbauen**: Events nach Timestamp sortiert durchgehen -2. **Phasen-Uebergaenge**: phase_transition Events zeigen den Ablauf -3. **Fehler identifizieren**: Events mit Severity >= 2 (Warning) hervorheben -4. **DataJson parsen**: Oft stecken die Details in DataJson - immer reinschauen bei relevanten Events -5. **Code-Korrelation**: Wenn der User eine Frage zum Verhalten hat, lies den relevanten Code im Repo und erklaere anhand der Events was passiert ist -6. **Completion-Logik**: Bei Fragen zur Session-Completion, schau in: - - `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker*.cs` - - Die 3 Completion-Pfade: IME pattern, ESP+Hello composite, Desktop+Hello (no-ESP) - -## Wichtige Code-Pfade fuer Event-Analyse - -- **Event-Ingestion**: `src/Backend/AutopilotMonitor.Functions/Functions/IngestEventsFunction.cs` -- **Session-Updates**: `src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Sessions.cs` -- **Enrollment Tracker**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Tracking/EnrollmentTracker*.cs` -- **ESP/Hello Tracker**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/EspAndHelloTracker*.cs` -- **Desktop Arrival**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/DesktopArrivalDetector.cs` -- **Collectors**: `src/Agent/AutopilotMonitor.Agent.Core/Monitoring/Collectors/` -- **Web Session View**: `src/Web/autopilot-monitor-web/app/sessions/[sessionId]/` -- **Shared Models/Enums**: `src/Shared/AutopilotMonitor.Shared/` - -## Ausgabe-Format - -1. **Session-Uebersicht**: Status, Typ, Dauer, Geraet (kompakt, tabellarisch) -2. **Event-Timeline**: Wichtigste Events chronologisch mit Timestamp, EventType, Message (nicht alle - nur relevante, keine performance_snapshot/agent_metrics_snapshot) -3. **Auffaelligkeiten**: Errors, Warnings, ungewoehnliche Muster -4. **Antwort auf User-Frage**: Falls der User eine spezifische Frage hat, diese gezielt beantworten mit Verweis auf Events UND Code - -## Hinweise - -- Die Env-Variable `AUTOPILOT_MONITOR_TABLE_CS` enthaelt den Connection String mit SAS Token -- Bei vielen Events (>100) kann die Ausgabe gross werden - fokussiere auf relevante Events, filtere performance_snapshot und agent_metrics_snapshot raus wenn sie nicht relevant sind -- DataJson Felder enthalten oft die wichtigsten Details - IMMER reinschauen bei relevanten Events -- Wenn ein Event unklar ist, schau im Code nach wo dieser EventType erzeugt wird (grep nach dem EventType-String) -- Phase-Enum: Unknown=0, DevicePreparation=1, DeviceSetup=2, AccountSetup=3, DeviceESP=4, UserESP=5, Complete=6, PreProvisioning=7 diff --git a/.claude/scripts/query-appinsights.sh b/.claude/scripts/query-appinsights.sh deleted file mode 100644 index 9d921684..00000000 --- a/.claude/scripts/query-appinsights.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash -# Helper script to query Application Insights via Azure CLI -# Usage: query-appinsights.sh "" [timespan] -# -# Requires AUTOPILOT_MONITOR_APPINSIGHTS_ID environment variable (App Insights App-ID GUID) -# Timespan format: ISO 8601 Duration (e.g., PT1H, PT24H, P7D). Default: PT1H - -QUERY="$1" -TIMESPAN="${2:-PT1H}" - -if [ -z "$QUERY" ]; then - echo "Usage: query-appinsights.sh \"\" [timespan]" - exit 1 -fi - -if [ -z "$AUTOPILOT_MONITOR_APPINSIGHTS_ID" ]; then - echo "ERROR: AUTOPILOT_MONITOR_APPINSIGHTS_ID environment variable not set" - echo "Set it to the Application Insights App ID (Azure Portal > API Access > Application ID)" - exit 1 -fi - -# Ensure az CLI is in PATH (Windows/Git Bash may not have it) -if ! command -v az &>/dev/null; then - for AZ_DIR in \ - "/c/Program Files/Microsoft SDKs/Azure/CLI2/wbin" \ - "/c/Program Files (x86)/Microsoft SDKs/Azure/CLI2/wbin"; do - if [ -d "$AZ_DIR" ]; then - export PATH="$AZ_DIR:$PATH" - break - fi - done - if ! command -v az &>/dev/null; then - echo "ERROR: Azure CLI (az) not found. Install it or add it to PATH." - exit 1 - fi -fi - -# Execute query -RESULT=$(az monitor app-insights query \ - --app "$AUTOPILOT_MONITOR_APPINSIGHTS_ID" \ - --analytics-query "$QUERY" \ - --offset "$TIMESPAN" \ - 2>&1) - -EXIT_CODE=$? -if [ $EXIT_CODE -ne 0 ]; then - echo "ERROR: az query failed (exit code $EXIT_CODE)" - echo "$RESULT" - exit $EXIT_CODE -fi - -# Transform nested table format (columns+rows) into readable JSON array of objects -python3 -c " -import json, sys - -data = json.loads(sys.stdin.read()) -tables = data.get('tables', data.get('Tables', [])) -if not tables: - print('[]') - sys.exit(0) - -table = tables[0] -cols = [c['name'] for c in table['columns']] -rows = table['rows'] - -result = [] -for row in rows: - obj = {} - for i, col in enumerate(cols): - val = row[i] if i < len(row) else None - if val is not None: - obj[col] = val - result.append(obj) - -print(json.dumps(result, indent=2, default=str)) -" <<< "$RESULT" diff --git a/.claude/scripts/query-table.sh b/.claude/scripts/query-table.sh deleted file mode 100644 index 31a3161e..00000000 --- a/.claude/scripts/query-table.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -# Helper script to query Azure Table Storage via REST API -# Usage: query-table.sh [select_fields] [top_count] -# -# Requires AUTOPILOT_MONITOR_TABLE_CS environment variable with format: -# TableEndpoint=https://....table.core.windows.net/;SharedAccessSignature=sv=... - -TABLE_NAME="$1" -FILTER="$2" -SELECT="$3" - -if [ -z "$TABLE_NAME" ] || [ -z "$FILTER" ]; then - echo "Usage: query-table.sh [select_fields]" - exit 1 -fi - -if [ -z "$AUTOPILOT_MONITOR_TABLE_CS" ]; then - echo "ERROR: AUTOPILOT_MONITOR_TABLE_CS environment variable not set" - exit 1 -fi - -# Parse connection string -ENDPOINT=$(echo "$AUTOPILOT_MONITOR_TABLE_CS" | sed -n 's/.*TableEndpoint=\([^;]*\).*/\1/p' | sed 's:/*$::') -SAS=$(echo "$AUTOPILOT_MONITOR_TABLE_CS" | sed -n 's/.*SharedAccessSignature=\(.*\)/\1/p') - -if [ -z "$ENDPOINT" ] || [ -z "$SAS" ]; then - echo "ERROR: Could not parse connection string (need TableEndpoint and SharedAccessSignature)" - exit 1 -fi - -# URL-encode the OData filter for use in query string -ENCODED_FILTER=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$FILTER', safe=''))" 2>/dev/null) -if [ -z "$ENCODED_FILTER" ]; then - # Fallback: manual encoding of common OData chars - ENCODED_FILTER=$(echo "$FILTER" | sed "s/ /%20/g; s/'/%27/g") -fi - -# Build full URL with query params -QUERY_URL="${ENDPOINT}/${TABLE_NAME}()?${SAS}&\$filter=${ENCODED_FILTER}" -if [ -n "$SELECT" ]; then - ENCODED_SELECT=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$SELECT', safe=''))" 2>/dev/null) - if [ -z "$ENCODED_SELECT" ]; then - ENCODED_SELECT="$SELECT" - fi - QUERY_URL="${QUERY_URL}&\$select=${ENCODED_SELECT}" -fi -TOP="$4" -if [ -n "$TOP" ]; then - QUERY_URL="${QUERY_URL}&\$top=${TOP}" -fi - -# Execute query -curl -s -H "Accept: application/json;odata=nometadata" \ - -H "x-ms-version: 2024-11-04" \ - --globoff \ - "$QUERY_URL"