You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The current MIGRATION REPORT is written from the engine's point of view, not the user's. It exposes internal vocabulary that users can't interpret — "Verification gate: Green/Yellow/Red", "Compiled", "risk=376", "not feasible", "skipped" — and folds the engine's internal pipeline stages into the summary. Users (often evaluating the tool, not engineers working on it) can't answer the basic questions: Did my assets make it? What's left for me to do? Can I trust it? The same problem exists in the report-migration-coverage skill, which surfaces the same terms.
Goal
Rework the report so it reads as an outcome story, framed around assets (dashboards and alerting rules):
what you asked to migrate (assets) → a one-line verdict → what we did → per-asset breakdown → the worklist of what's left for you → a plain-language bottom line.
Use two pipeline words only — Converted and Uploaded — and plain status buckets. Apply the same vocabulary across all three surfaces, for both source vendors.
Scope (three surfaces, one vocabulary)
Terminal report — print_report() in observability_migration/core/reporting/report.py
Markdown artifact — render_markdown() in observability_migration/core/reporting/summary_md.py (migration_summary.md)
Skill — .claude/skills/report-migration-coverage/SKILL.mdand.cursor/skills/report-migration-coverage/SKILL.md (edit both in lockstep)
Ready (ready to use; for alerts: Imported (disabled))
migrated_with_warnings / Datadog warning
Review (converted, worth a glance)
not_feasible + requires_manual / Datadog blocked
Rebuild (you finish it in Kibana)
skipped (rows)
dropped from panel counts (rows aren't panels)
"Compiled" / "migrated" (stage)
Converted
upload step
Uploaded
"Verification gate: G/Y/R"
"Verified against your data: X of Y matched" — only when --validate ran
"risk=376 (G:8 Y:2 R:37)"
"76 panels need your attention"
Report structure
Header — <SOURCE_VENDOR> -> KIBANA MIGRATION, with the source vendor named dynamically (Grafana / Datadog). From: (source URL or folder, by mode, tagged with the vendor) and To: (output folder; Kibana URL only when upload ran).
YOU ASKED TO MIGRATE — requested scope, by asset type; shows dashboards and/or alerting rules only for the asset types the user requested.
SUMMARY — a single one-line verdict, placed directly after the requested scope (the lead): e.g. SUMMARY: 6 of 6 dashboards in Kibana · 125 panels ready · 76 need your attention (see below). This is the short form of the BOTTOM LINE, which repeats the fuller version at the end (lead and close).
RESULTS — per requested asset type. When more than one asset type was requested, each appears under its own plain indented sub-header (no glyph); with a single asset type the figures sit directly under RESULTS:
Dashboards — Converted count, Uploaded count (hidden if upload not run), and panels (widgets for Datadog) converted, with the Ready / Review / Rebuild breakdown.
Alerting rules — Converted count, Uploaded count, and rules migrated, with the Imported (disabled) / Review / Rebuild breakdown.
The status breakdown is shown only when there's a split (Review or Rebuild > 0) — an all-Ready result shows just the X of Y (100%) line. Verified against your data appears only with --validate.
PER-ASSET breakdown — a table per requested asset type. Converted first, sorted alphabetically, Uploaded column hidden when upload not run. The asset-type grouping titles appear only when more than one asset type was requested (heading PER-ASSET, with plain indented Dashboards / Alerting rules sub-headers, no glyph). With a single asset type, use a concrete heading and no sub-header — PER DASHBOARD or PER ALERTING RULE:
Dashboards — per-dashboard table: Converted | Uploaded | Panels | Ready | Review | Rebuild. The total column is named for the unit (Panels, or Widgets for Datadog dashboards) — there is no separate group header above the table.
Alerting rules — per-rule table: Converted | Uploaded | Status. Status is Imported (disabled) / Review / Rebuild. A successfully migrated rule is created disabled and must be reviewed before enabling — so it is shown as Imported (disabled), never "Ready".
REQUIRES YOUR ATTENTION — fully-failed assets first, full list shown (never truncated). Plain indented asset-type sub-headers (Dashboards / Alerting rules) appear only when more than one asset type was requested; with a single asset type they're omitted and the items follow directly:
Dashboards — per dashboard, the affected panels grouped by cause.
Alerting rules — per rule, the reason it needs rebuilding.
Within each asset, items are grouped by cause — two families with distinct calls to action:
Needs redesign in Kibana — a genuine translation gap (Grafana dropdown grouping, unsupported function, unsupported datasource); rebuild it by hand.
Waiting on data — the query is fine, the metric just isn't in Elastic yet; it should work once that metric is ingested (likely cause, not a guarantee).
Leads with the action (each item is already a placeholder note in the dashboard/rule), not raw PromQL.
BOTTOM LINE — the fuller version of the top SUMMARY: a short, readable, plain-language conclusion (two or three sentences) a non-engineer can act on — how many assets are now in Kibana, how many units are ready, and what (if anything) needs the user next. It restates the outcome in words, not a recap of the table. When the report is split by asset type, give one labelled line per asset type (Dashboards: / Alerting rules:).
WHERE TO DIG DEEPER — paths to migration_report.json, migration_summary.md, feature_gap_report.json, and the converted *.yaml.
Detailed requirements / decisions
Presentation-only. Do not rename JSON keys or change exit codes; consumers (CI, rollout, verification packets, the skill) depend on them. The skill maps existing keys to the new vocabulary.
Asset-oriented. Dashboards and alerting rules are peers throughout; alerts are never dropped from the framing.
Vendor-aware. Source vendor (Grafana / Datadog) is named dynamically in the header and labels; works for both, including Datadog "widgets" wording and blocked status. Target is always Kibana/Elastic.
Lead and close. A one-line SUMMARY: verdict sits directly after the requested scope; the fuller BOTTOM LINE repeats it at the end. The headline answer is near the top, not only at the bottom.
Wording for the worklist count. The catch-all phrase is "need your attention" (matching the section title), never "need you" — some of those panels need a rebuild, others are just waiting on data, so "need your attention" is the accurate umbrella.
Alerts are honest about state. A migrated alert rule is created disabled; the report shows it as Imported (disabled) and states it must be reviewed before enabling. It is never labelled "Ready".
Markers:Converted = ✓ / Partial / ✗; Uploaded = ✓ / ✗. Terminal symbols are kept for interactive output; a non-TTY/piped fallback uses ASCII words (yes / partial / no). No emoji on any surface.
Total column folded in: no separate Panels / Widgets banner above the table; the total column header is the unit name itself.
Partial = ≥1 rebuild unit but ≥1 converted. ✗ = 0 converted or compile failed. No separate upload-failure block — detail lives in JSON.
Conditional sections / titles: asset-type blocks appear only for what was requested. Asset-type grouping titles appear only when more than one asset type was requested; a single-asset run uses a concrete heading with no sub-headers. Uploaded line/column hidden entirely when upload was not run (don't narrate the absence).
Sub-header per asset on split: whenever the report splits by asset type (more than one type requested), each asset block — in RESULTS, PER-ASSET, REQUIRES YOUR ATTENTION, and BOTTOM LINE — is introduced with a plain indented Title-case sub-header (Dashboards / Alerting rules), no bullet glyph (most robust across terminals and pipes). Single-asset runs have no split.
No redundant breakdown: the per-status breakdown is shown only when there's a split; when everything converted cleanly the X of Y (100%) line stands alone.
No truncation: the "requires your attention" worklist lists every affected panel/rule in full. The JSON remains the machine-readable copy, but the human report does not abbreviate the list.
Progress always visible: default run shows explanatory plain-language narration, one line per phase stating the outcome (e.g. "Converting dashboards to Kibana format ... 6 of 6 converted; 76 panels need your attention."), not a progress bar. --verbose shows the full [1/7]…[7/7] staged detail. Remove the (run with --validate …) advertisement from the report body.
Single view-model: terminal report, .md artifact, and skill derive from one computed report view so they cannot drift.
Review-bucket vs Verified-line stay distinct:Review = converted with a translation-time warning (semantic loss); Verified against your data = a separate --validate outcome. Never conflated or double-counted.
Formatting conventions
These keep the three surfaces consistent and robust across terminals and pipes:
Indentation, not glyphs, carries hierarchy. Three levels, 2 spaces each:
Level 0 — section header (RESULTS, PER-ASSET, REQUIRES YOUR ATTENTION): ALL-CAPS, blank line above.
Level 1 — asset sub-group (only when split): a plain Title-case label (Dashboards, Alerting rules), indented 2 spaces, no bullet glyph.
Level 2+ — data and tables, indented a further 2 spaces.
Spaces only, never tab characters. Tabs render at different widths per terminal and break column alignment; pad every table cell to a fixed width with spaces.
Status markers: TTY uses ✓ / Partial / ✗ (Converted) and ✓ / ✗ (Uploaded); non-TTY/piped output uses ASCII words (yes / partial / no). No emoji anywhere.
Width target: keep the report body within 80 columns where possible. The per-dashboard table is the pinch point — truncate long dashboard names with an ellipsis (…) to preserve column alignment rather than letting rows wrap.
Acceptance criteria
Understandable without internals knowledge. A first-time user who knows nothing about the tool's internals can read the report and correctly answer "did my assets make it into Kibana?", "what can I use right now?", and "what do I need to do next?" — without consulting the docs, the JSON artifacts, or asking an engineer. Reviewers hold the wording to this bar.
No surface shows "compiled", "verification gate", "risk=N", "not feasible", or "skipped" to the user.
No emoji anywhere; output is readable in both a terminal and a piped/CI log.
The worklist count uses "need your attention", never "need you".
A one-line SUMMARY verdict appears directly after the requested scope, and a fuller BOTTOM LINE at the end.
Uploaded alert rules are shown as Imported (disabled), not "Ready"; the report states they must be reviewed before enabling.
Data-not-ingested gaps are phrased as a likely cause ("should work once that metric is ingested"), not a guarantee.
Dashboards and alerting rules are treated as peer assets; an alerts-only run produces a complete report with no dashboards section, and vice versa.
Asset-type grouping titles appear only when more than one asset type was requested; single-asset runs use a concrete heading with no sub-headers.
Every asset-type split renders each asset block under a plain indented sub-header (no glyph).
Hierarchy is conveyed by indentation and section headers, not bullet glyphs; alignment uses spaces, never tab characters.
The report body stays within 80 columns where possible; long names truncate with an ellipsis rather than wrapping.
An all-Ready conversion shows just the X of Y (100%) line, with no redundant per-status breakdown.
The per-dashboard table has no separate Panels/Widgets banner; the total column header is the unit name.
Convert-only run shows no Uploaded line/column.
Verified against your data appears only with --validate; absent otherwise (no flag advertisement).
The "requires your attention" worklist is never truncated.
Default run shows explanatory per-phase narration; --verbose shows the staged detail.
Terminal report, migration_summary.md, and skill output use identical vocabulary and numbers on the same run.
Header and labels name the source vendor dynamically; works for both Grafana and Datadog.
JSON artifacts and exit codes unchanged; existing consumers pass.
Out of scope
Renaming JSON/artifact keys or changing exit-code semantics.
======================================================================
GRAFANA -> KIBANA MIGRATION
======================================================================
From: https://grafana.acme.com (Grafana)
To: ./migration_output/dashboards/ + https://acme.kb.elastic.cloud
YOU ASKED TO MIGRATE
From Grafana (https://grafana.acme.com):
3 dashboards (48 panels)
SUMMARY: 3 of 3 dashboards in Kibana · all 48 panels ready · nothing needs your attention
RESULTS
Converted to Kibana format: 3 of 3
Uploaded to your cluster: 3 of 3
Panels converted: 48 of 48 across those 3 dashboards (100%)
Verified against your data: 48 of 48 panels returned matching results
PER DASHBOARD
Dashboard Converted Uploaded Panels Ready Review Rebuild
-----------------------------------------------------------------------
API Gateway Overview ✓ ✓ 18 18 0 0
Node Exporter Full ✓ ✓ 22 22 0 0
Postgres Health ✓ ✓ 8 8 0 0
BOTTOM LINE
All 3 dashboards are in Kibana and every panel is ready to use. Nothing needs
your attention.
WHERE TO DIG DEEPER
Full per-panel detail: migration_output/dashboards/migration_report.json
Shareable summary: migration_output/dashboards/migration_summary.md
Converted files: migration_output/dashboards/*.yaml
(All-Ready: no per-status breakdown. Single asset type: PER DASHBOARD, no PER-ASSET/Dashboards wrapper, no Dashboards header in RESULTS, no bullets.)
B. Realistic mixed run — Grafana, dashboards only, uploaded, validation skipped (full worklist, not truncated)
======================================================================
GRAFANA -> KIBANA MIGRATION
======================================================================
From: https://grafana.acme.com (Grafana)
To: ./migration_output/dashboards/ + https://acme.kb.elastic.cloud
YOU ASKED TO MIGRATE
From Grafana (https://grafana.acme.com):
6 dashboards (204 panels)
SUMMARY: 6 of 6 dashboards in Kibana · 125 panels ready · 76 need your attention (see below)
RESULTS
Converted to Kibana format: 6 of 6
Uploaded to your cluster: 6 of 6
Panels converted: 128 of 204 across those 6 dashboards (63%)
125 ready to use
3 converted - worth a quick review
76 need your attention -> see below
PER DASHBOARD
Dashboard Converted Uploaded Panels Ready Review Rebuild
--------------------------------------------------------------------------------------
ArgoCD Partial ✓ 41 35 0 6
DrillPlan backing services key metrics Partial ✓ 40 34 1 5
Elasticsearch Exporter Quickstart and Partial ✓ 55 44 0 11
MongoDB ReplSet Partial ✓ 17 3 0 14
RabbitMQ-Overview Partial ✓ 40 1 2 37
Redis Clusters via redis_exporter 3.0 Partial ✓ 11 8 0 3
REQUIRES YOUR ATTENTION (76 panels)
Each panel below is already a placeholder note in your uploaded Kibana
dashboard - open the dashboard and finish it there. The full list is shown
(not truncated).
ArgoCD (6 panels)
Needs redesign in Kibana - grouping uses a Grafana dropdown ($grouping) we can't resolve
"Sync Activity", "Sync Failures", "Reconciliation Activity"
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Reconciliation Performance", "Git Fetch Performance", "Git Ls-Remote Performance"
DrillPlan backing services key metrics (5 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"mongo uptime", "redis uptime", "rabbitMQ uptime",
"Client-output-buffer-limit", "Command Calls duration"
Elasticsearch Exporter Quickstart and Dashboard (11 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Cluster health", "Tripped for breakers", "Nodes", "Data nodes",
"Pending tasks", "Active primary shards", "Active shards",
"Initializing shards", "Relocating shards", "Unassigned shards",
"Delayed unassigned shards"
MongoDB ReplSet (14 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"mongo uptime", "replset member state", "oplog window", "members",
"primary", "secondaries", "replication lag", "ping time", "asserts",
"page faults", "connections", "cursors open", "network in",
"network out"
RabbitMQ-Overview (37 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Queued messages", "Messages ready", "Messages unacked",
"Publish rate", "Deliver rate", "Ack rate", "Redeliver rate",
"Connections", "Channels", "Consumers", "Queues", "Exchanges",
"Memory used", "Disk free", "File descriptors", "Socket descriptors",
"Erlang processes", "GC rate", "Node up", "Partitions",
... (every affected panel listed in full; abbreviated here only for
the ticket)
Redis Clusters via redis_exporter 3.0 (3 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Memory fragmentation ratio", "Evicted keys", "Keyspace hits/misses"
BOTTOM LINE
All 6 dashboards are in Kibana; 125 panels are ready to use. 76 panels still
need your attention - most just need their metric ingested into Elastic, the
rest need rebuilding in Kibana. Start with "Requires your attention" above.
WHERE TO DIG DEEPER
Full per-panel detail: migration_output/dashboards/migration_report.json
Shareable summary: migration_output/dashboards/migration_summary.md
What needs rebuilding: migration_output/dashboards/feature_gap_report.json
Converted files: migration_output/dashboards/*.yaml
C. Dashboards + alerting rules (both requested -> PER-ASSET with bulleted sub-headers)
YOU ASKED TO MIGRATE
From Grafana (https://grafana.acme.com):
3 dashboards (40 panels)
9 alerting rules
SUMMARY: 3 dashboards + 7 of 9 alert rules in Kibana · 2 panels and 2 rules need your attention
RESULTS
Dashboards
Converted to Kibana format: 3 of 3
Uploaded to your cluster: 3 of 3
Panels converted: 38 of 40 across those 3 dashboards (95%)
38 ready to use
2 need your attention -> see below
Alerting rules
Converted to Kibana rules: 7 of 9
Uploaded to your cluster: 7 of 9
7 imported, disabled - review before enabling
2 need your attention -> see below
PER-ASSET
Dashboards
Dashboard Converted Uploaded Panels Ready Review Rebuild
------------------------------------------------------------------
Latency ✓ ✓ 8 8 0 0
Service Overview Partial ✓ 20 18 0 2
Traffic ✓ ✓ 12 12 0 0
Alerting rules
Rule Converted Uploaded Status
--------------------------------------------------------------------
5xx error rate ✓ ✓ Imported (disabled)
CPU saturation ✓ ✓ Imported (disabled)
Cert expiry ✓ ✓ Imported (disabled)
Disk will fill in 4h ✗ - Rebuild
High request latency ✗ - Rebuild
Memory pressure ✓ ✓ Imported (disabled)
Pod restarts ✓ ✓ Imported (disabled)
Queue backlog ✓ ✓ Imported (disabled)
Replication lag ✓ ✓ Imported (disabled)
REQUIRES YOUR ATTENTION (2 panels, 2 rules)
Dashboards
Service Overview (2 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Error budget burn", "SLO compliance"
Alerting rules
"Disk will fill in 4h"
Needs redesign in Kibana - uses Grafana predict_linear(), no direct
equivalent; approximate with a rate threshold.
"High request latency"
Needs redesign in Kibana - uses a Grafana classic-condition with multiple
reducers; rebuild as a single threshold rule.
BOTTOM LINE
Dashboards: all 3 are in Kibana, 38 panels ready, 2 need your attention.
Alerting rules: 7 of 9 are in Kibana, imported but disabled - review before
enabling; 2 need rebuilding.
Start with "Requires your attention" above.
D. Alerts-only run (single asset type -> no dashboards section, no asset titles, no bullets)
YOU ASKED TO MIGRATE
From Grafana (https://grafana.acme.com):
12 alerting rules
SUMMARY: 10 of 12 alert rules in Kibana (imported, disabled) · 2 need your attention
RESULTS
Converted to Kibana rules: 10 of 12
Uploaded to your cluster: 10 of 12
10 imported, disabled - review before enabling
2 need your attention -> see below
PER ALERTING RULE
Rule Converted Uploaded Status
------------------------------------------------------------------
... (10 imported, disabled rules) ...
Anomaly: login spikes ✗ - Rebuild
Forecast: storage 7d ✗ - Rebuild
REQUIRES YOUR ATTENTION (2 rules)
"Anomaly: login spikes"
Needs redesign in Kibana - uses an anomaly monitor, no Kibana equivalent;
rebuild as an anomaly detection job or threshold rule.
"Forecast: storage 7d"
Needs redesign in Kibana - uses a forecast function; approximate with a
rate-based threshold.
BOTTOM LINE
10 of 12 alerting rules are in Kibana, imported but disabled - review before
enabling. 2 rules need rebuilding in Kibana. See "Requires your attention" above.
E. Convert-only — dashboards only, upload not run (Uploaded line/column hidden)
To: ./migration_output/dashboards/
YOU ASKED TO MIGRATE
From Grafana (https://grafana.acme.com):
2 dashboards (30 panels)
SUMMARY: 2 of 2 dashboards converted (not uploaded yet) · 28 panels ready · 2 need your attention
RESULTS
Converted to Kibana format: 2 of 2
Panels converted: 28 of 30 across those 2 dashboards (93%)
28 ready to use
2 need your attention -> see below
PER DASHBOARD
Dashboard Converted Panels Ready Review Rebuild
--------------------------------------------------------
Kafka Cluster Partial 16 15 0 1
Nginx Ingress Partial 14 13 0 1
REQUIRES YOUR ATTENTION (2 panels)
These are saved as placeholder notes in the converted files in
./migration_output - they carry into Kibana when you upload.
Kafka Cluster (1 panel)
Needs redesign in Kibana - uses topk() without group labels; pick the breakdown in Kibana
"Top consumers by lag"
Nginx Ingress (1 panel)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Upstream response time p99"
BOTTOM LINE
Both dashboards converted to ./migration_output (not uploaded yet); 28 panels
ready, 2 need your attention. Upload when you're ready to push them to Kibana.
F. Upload partially failed — dashboards only (just ✗, no end block)
YOU ASKED TO MIGRATE
From Grafana (https://grafana.acme.com):
4 dashboards (64 panels)
SUMMARY: 3 of 4 dashboards uploaded (1 failed) · 61 panels ready · 3 need your attention
RESULTS
Converted to Kibana format: 4 of 4
Uploaded to your cluster: 3 of 4 - 1 failed
Panels converted: 61 of 64 across those 4 dashboards (95%)
61 ready to use
3 need your attention -> see below
PER DASHBOARD
Dashboard Converted Uploaded Panels Ready Review Rebuild
------------------------------------------------------------------
Billing Service ✓ ✓ 20 20 0 0
Checkout Service Partial ✓ 18 17 0 1
Inventory Service Partial ✗ 14 12 0 2
Shipping Service ✓ ✓ 12 12 0 0
BOTTOM LINE
All 4 dashboards converted; 3 of 4 uploaded - Inventory Service failed, retry
with `obs-migrate upload`. 61 panels ready, 3 need your attention.
G. File-mode source, worst case — dashboards only, a fully-failed dashboard listed first
======================================================================
GRAFANA -> KIBANA MIGRATION
======================================================================
From: ./exported-dashboards/ (Grafana, file mode, 3 files)
To: ./migration_output/dashboards/ + https://acme.kb.elastic.cloud
YOU ASKED TO MIGRATE
From Grafana (./exported-dashboards/):
3 dashboards (29 panels)
SUMMARY: 2 of 3 dashboards in Kibana · 14 panels ready · 15 need your attention (1 dashboard couldn't convert)
RESULTS
Converted to Kibana format: 2 of 3 - 1 could not be converted
Uploaded to your cluster: 2 of 3
Panels converted: 14 of 29 across those 3 dashboards (48%)
14 ready to use
15 need your attention -> see below
PER DASHBOARD
Dashboard Converted Uploaded Panels Ready Review Rebuild
---------------------------------------------------------------------
Cassandra Overview Partial ✓ 11 8 0 3
Legacy Graphite Board ✗ ✗ 12 0 0 12
Spark Streaming ✓ ✓ 6 6 0 0
REQUIRES YOUR ATTENTION (15 panels)
Legacy Graphite Board (could not be converted - 12 panels)
Needs redesign in Kibana - built on a Graphite datasource we don't support;
rebuild from scratch in Kibana using your Elastic data. Nothing was uploaded
for this dashboard.
Cassandra Overview (3 panels)
Waiting on data - metric not in your Elastic data yet; should work once it's ingested
"Compaction pending", "Read repair rate", "Hinted handoff"
BOTTOM LINE
2 of 3 dashboards are in Kibana; Legacy Graphite Board could not be converted at
all and must be rebuilt from scratch. 14 panels ready, 15 need your attention.
H. Datadog source — dashboards only, "widgets" instead of "panels"
======================================================================
DATADOG -> KIBANA MIGRATION
======================================================================
From: https://app.datadoghq.com (Datadog)
To: ./migration_output/dashboards/ + https://acme.kb.elastic.cloud
YOU ASKED TO MIGRATE
From Datadog (https://app.datadoghq.com):
4 dashboards (52 widgets)
SUMMARY: 4 of 4 dashboards in Kibana · 43 widgets ready · 9 need your attention (see below)
RESULTS
Converted to Kibana format: 4 of 4
Uploaded to your cluster: 4 of 4
Widgets converted: 45 of 52 across those 4 dashboards (87%)
43 ready to use
2 converted - worth a quick review
7 need your attention -> see below
PER DASHBOARD
Dashboard Converted Uploaded Widgets Ready Review Rebuild
------------------------------------------------------------------
APM Services Partial ✓ 16 13 2 1
Host Overview ✓ ✓ 14 14 0 0
Kubernetes Partial ✓ 14 10 0 4
Synthetics Partial ✓ 8 6 0 2
BOTTOM LINE
All 4 dashboards are in Kibana; 43 widgets ready to use, 2 worth a quick review,
7 need rebuilding in Kibana. See "Requires your attention" above.
I. Non-TTY / piped output — ASCII fallback (no symbols, no emoji)
Connecting to Grafana at https://grafana.acme.com ... found 6 dashboards (204 panels).
Converting dashboards to Kibana format ... 6 of 6 converted; 76 panels need your attention.
Uploading to your Kibana cluster ... 6 of 6 uploaded.
Generating report ...
--verbose swaps these for the full [1/7]…[7/7] staged log.
Problem
The current
MIGRATION REPORTis written from the engine's point of view, not the user's. It exposes internal vocabulary that users can't interpret — "Verification gate: Green/Yellow/Red", "Compiled", "risk=376", "not feasible", "skipped" — and folds the engine's internal pipeline stages into the summary. Users (often evaluating the tool, not engineers working on it) can't answer the basic questions: Did my assets make it? What's left for me to do? Can I trust it? The same problem exists in thereport-migration-coverageskill, which surfaces the same terms.Goal
Rework the report so it reads as an outcome story, framed around assets (dashboards and alerting rules):
what you asked to migrate (assets) → a one-line verdict → what we did → per-asset breakdown → the worklist of what's left for you → a plain-language bottom line.
Use two pipeline words only — Converted and Uploaded — and plain status buckets. Apply the same vocabulary across all three surfaces, for both source vendors.
Scope (three surfaces, one vocabulary)
print_report()inobservability_migration/core/reporting/report.pyrender_markdown()inobservability_migration/core/reporting/summary_md.py(migration_summary.md).claude/skills/report-migration-coverage/SKILL.mdand.cursor/skills/report-migration-coverage/SKILL.md(edit both in lockstep)Vocabulary mapping (human-facing only; JSON keys unchanged)
migrated/ Datadogokmigrated_with_warnings/ Datadogwarningnot_feasible+requires_manual/ Datadogblockedskipped(rows)--validateranReport structure
Header —
<SOURCE_VENDOR> -> KIBANA MIGRATION, with the source vendor named dynamically (Grafana / Datadog).From:(source URL or folder, by mode, tagged with the vendor) andTo:(output folder; Kibana URL only when upload ran).YOU ASKED TO MIGRATE — requested scope, by asset type; shows dashboards and/or alerting rules only for the asset types the user requested.
SUMMARY — a single one-line verdict, placed directly after the requested scope (the lead): e.g.
SUMMARY: 6 of 6 dashboards in Kibana · 125 panels ready · 76 need your attention (see below). This is the short form of the BOTTOM LINE, which repeats the fuller version at the end (lead and close).RESULTS — per requested asset type. When more than one asset type was requested, each appears under its own plain indented sub-header (no glyph); with a single asset type the figures sit directly under
RESULTS:The status breakdown is shown only when there's a split (Review or Rebuild > 0) — an all-Ready result shows just the
X of Y (100%)line.Verified against your dataappears only with--validate.PER-ASSET breakdown — a table per requested asset type.
Convertedfirst, sorted alphabetically,Uploadedcolumn hidden when upload not run. The asset-type grouping titles appear only when more than one asset type was requested (headingPER-ASSET, with plain indentedDashboards/Alerting rulessub-headers, no glyph). With a single asset type, use a concrete heading and no sub-header —PER DASHBOARDorPER ALERTING RULE:Converted | Uploaded | Panels | Ready | Review | Rebuild. The total column is named for the unit (Panels, orWidgetsfor Datadog dashboards) — there is no separate group header above the table.Converted | Uploaded | Status. Status isImported (disabled)/Review/Rebuild. A successfully migrated rule is created disabled and must be reviewed before enabling — so it is shown asImported (disabled), never "Ready".REQUIRES YOUR ATTENTION — fully-failed assets first, full list shown (never truncated). Plain indented asset-type sub-headers (
Dashboards/Alerting rules) appear only when more than one asset type was requested; with a single asset type they're omitted and the items follow directly:Within each asset, items are grouped by cause — two families with distinct calls to action:
Leads with the action (each item is already a placeholder note in the dashboard/rule), not raw PromQL.
BOTTOM LINE — the fuller version of the top SUMMARY: a short, readable, plain-language conclusion (two or three sentences) a non-engineer can act on — how many assets are now in Kibana, how many units are ready, and what (if anything) needs the user next. It restates the outcome in words, not a recap of the table. When the report is split by asset type, give one labelled line per asset type (
Dashboards:/Alerting rules:).WHERE TO DIG DEEPER — paths to
migration_report.json,migration_summary.md,feature_gap_report.json, and the converted*.yaml.Detailed requirements / decisions
blockedstatus. Target is always Kibana/Elastic.SUMMARY:verdict sits directly after the requested scope; the fuller BOTTOM LINE repeats it at the end. The headline answer is near the top, not only at the bottom.Imported (disabled)and states it must be reviewed before enabling. It is never labelled "Ready".Converted=✓/Partial/✗;Uploaded=✓/✗. Terminal symbols are kept for interactive output; a non-TTY/piped fallback uses ASCII words (yes/partial/no). No emoji on any surface.Panels/Widgetsbanner above the table; the total column header is the unit name itself.Uploadedline/column hidden entirely when upload was not run (don't narrate the absence).Dashboards/Alerting rules), no bullet glyph (most robust across terminals and pipes). Single-asset runs have no split.X of Y (100%)line stands alone.--verboseshows the full[1/7]…[7/7]staged detail. Remove the(run with --validate …)advertisement from the report body..mdartifact, and skill derive from one computed report view so they cannot drift.--validateoutcome. Never conflated or double-counted.Formatting conventions
These keep the three surfaces consistent and robust across terminals and pipes:
RESULTS,PER-ASSET,REQUIRES YOUR ATTENTION): ALL-CAPS, blank line above.Dashboards,Alerting rules), indented 2 spaces, no bullet glyph.✓/Partial/✗(Converted) and✓/✗(Uploaded); non-TTY/piped output uses ASCII words (yes/partial/no). No emoji anywhere.…) to preserve column alignment rather than letting rows wrap.Acceptance criteria
Imported (disabled), not "Ready"; the report states they must be reviewed before enabling.X of Y (100%)line, with no redundant per-status breakdown.Panels/Widgetsbanner; the total column header is the unit name.Verified against your dataappears only with--validate; absent otherwise (no flag advertisement).--verboseshows the staged detail.migration_summary.md, and skill output use identical vocabulary and numbers on the same run.Out of scope
--validateinternals) — we only restate its result.Examples
Each example leads with the
obs-migrate migratecommand (and flags) that produced it.A. Best case — Grafana, dashboards only, all clean, uploaded, validated
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --es-url https://acme.es.elastic.cloud --upload --validate(All-Ready: no per-status breakdown. Single asset type:
PER DASHBOARD, noPER-ASSET/Dashboardswrapper, noDashboardsheader in RESULTS, no bullets.)B. Realistic mixed run — Grafana, dashboards only, uploaded, validation skipped (full worklist, not truncated)
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --uploadC. Dashboards + alerting rules (both requested -> PER-ASSET with bulleted sub-headers)
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --assets all --uploadD. Alerts-only run (single asset type -> no dashboards section, no asset titles, no bullets)
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --assets alerts --uploadE. Convert-only — dashboards only, upload not run (Uploaded line/column hidden)
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --output-dir ./migration_output(no--upload-> convert only)F. Upload partially failed — dashboards only (just ✗, no end block)
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --uploadG. File-mode source, worst case — dashboards only, a fully-failed dashboard listed first
obs-migrate migrate --source grafana --input-mode files --input-dir ./exported-dashboards --kibana-url https://acme.kb.elastic.cloud --uploadH. Datadog source — dashboards only, "widgets" instead of "panels"
obs-migrate migrate --source datadog --kibana-url https://acme.kb.elastic.cloud --uploadI. Non-TTY / piped output — ASCII fallback (no symbols, no emoji)
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --upload | tee migration.log(piped -> ASCII fallback)J. Default (non-verbose) progress — explanatory narration, not a bar
obs-migrate migrate --source grafana --grafana-url https://grafana.acme.com --kibana-url https://acme.kb.elastic.cloud --upload(default; no--verbose)--verboseswaps these for the full[1/7]…[7/7]staged log.References
observability_migration/core/reporting/report.py·summary_md.pyobservability_migration/adapters/source/grafana/{cli.py,verification.py,rollout.py}.claude/skills/report-migration-coverage/SKILL.md·.cursor/skills/report-migration-coverage/SKILL.md