# Sync Report — `grafana-metric-gateway` > Phase: **sync** · Change: `grafana-metric-gateway` · Repo: `/home/user/manage` > Mode: file-backed (`artifactStore: openspec`). No source-code edits; only OpenSpec artifacts were > written. Not committed (parent owns the commit). The change folder was **not** moved (that is > `sdd-archive`'s job). **Status: SYNCED.** The first non-additive canonical sync in the project. The `prometheus-charting` canonical domain's transport requirements were **MODIFIED** in place (direct Prom → Grafana gateway), durable behavior requirements were **PRESERVED** verbatim, and three genuinely new requirements were **ADDED** (SC-128 … SC-130). --- ## 1. Executive summary The `prometheus-charting` canonical domain (`openspec/specs/prometheus-charting/spec.md`) was created by `prometheus-direct-charting` with 27 requirements (SC-101 … SC-127) describing the **direct** Prometheus transport. The `grafana-metric-gateway` change switches the `prometheus` service to a **Grafana gateway** transport (`POST {grafana_url}/api/ds/query`), renames the chart/gauge/mean widgets to neutral `Metric*` components, and validates the gateway path — while preserving the durable *behavior* contracts (multi-series chart, gauge threshold bands, windowed mean, scalar-only rules). This sync authored a change-side **delta** spec with both `## MODIFIED Requirements` and `## ADDED Requirements` (the project's first delta to use MODIFIED), then applied it to the canonical spec by exact-ID block replacement. No requirements were REMOVED or RENAMED. Verify returned PASS (verdict in `verify-report.md`: 15 PASS + GM-115 PARTIAL-on-coverage-only, treated as effectively 16/16 per the dispatch brief; `apply-progress.md` exists and reconciles the 33 task checkboxes the verify report flagged). All five quality gates were green at the verified commits (`df80c68`, `7e91e7f`). ## 2. Structured status & actionContext findings The native `gentle-pi.sdd-status` passed by the parent reports `changeName: null` / `blockedReasons: ["Change selection is ambiguous: grafana-metric-gateway, per-instance-hook-scoping, service-credential-tester."]` because the engine auto-detected three active changes. This sync task was **explicitly assigned** `grafana-metric-gateway`; the ambiguity is a dispatch artifact and does not block this phase (`isNonAuthoritative: false`). - `artifactStore: openspec`; change root `openspec/changes/grafana-metric-gateway/`. - Artifacts present: `proposal.md`, `spec.md` (flat, with §"Canonical delta intent" mapping), `design.md`, `tasks.md`, `verify-report.md`, `apply-progress.md`. - `verify: PASS` (verify-report verdict; gates green). The verify report's single CRITICAL was an **archive** blocker (33 unchecked task checkboxes + missing `apply-progress.md`); `apply-progress.md` now exists, and that condition does **not** block `sdd-sync` of the green code. - `actionContext`: `mode: repo-local`, `workspaceRoot: /home/user/manage`, `allowedEditRoots: ["/home/user/manage"]`, `warnings: []`. All three files written are inside the authoritative workspace / allowed edit roots. ✓ - `relationships.sameDomainActiveChanges: []`, `collisions: []` — **no active same-domain collisions**, so no archive/sync ordering decision was required. - The `web-ui` and `service-storage` canonical domains were **not touched** (verified untouched by `git status`). ## 3. Domains synced & canonical files updated | Domain | Change-side delta (source) | Canonical (sync target) | Action | |---|---|---|---| | `prometheus-charting` | `openspec/changes/grafana-metric-gateway/specs/prometheus-charting/spec.md` | `openspec/specs/prometheus-charting/spec.md` | **MODIFIED + ADDED** — 16 requirement blocks replaced in place by exact ID; 3 new requirements appended | - **Canonical file updated:** `openspec/specs/prometheus-charting/spec.md` — now 30 requirements (SC-101 … SC-130). - **Change-side delta created:** `openspec/changes/grafana-metric-gateway/specs/prometheus-charting/spec.md` — `## MODIFIED Requirements` (16) + `## ADDED Requirements` (3). ## 4. Requirement delta (MODIFIED / ADDED / PRESERVED) ### MODIFIED (16) — transport & naming requirements replaced in place by exact SC- ID | Canonical ID | Header (matched exactly) | What changed (GM source) | |---|---|---| | SC-101 | Prometheus range query returns the existing series shape | direct Prom `/api/v1/query_range` → Grafana `POST /api/ds/query`; config `base_url` → `grafana_url`+`datasource_uid`; secret `api_key` → `grafana_api_key` (GM-101, GM-102) | | SC-102 | Series label normalization is shared and Prometheus-native | shared module now holds both `normalize_prometheus_matrix` + `normalize_grafana_frames`; shared dedup (GM-104) | | SC-103 | Range query errors degrade gracefully | gateway-specific failures (401/403, datasource-not-found) added (GM-103) | | SC-104 | Step is derived from the window preset | step math reused, mapped to `intervalMs`/`maxDataPoints`/`from`-`to` (GM-105) | | SC-105 | Chart widget moves from grafana to prometheus | kinds stay on `prometheus`; transport is gateway; `Metric*` component binding (GM-109, GM-112) | | SC-107 | Chart supports multiple series | multi-series via gateway (GM-106) | | SC-109 | Gauge renders an instant scalar | instant query via gateway (GM-107) | | SC-111 | Gauge is scalar-only | scalar-only preserved through gateway (GM-107) | | SC-112 | Mean computes client-side over a window | range query via gateway (GM-108) | | SC-114 | Mean is scalar-only | scalar-only preserved through gateway (GM-108) | | SC-115 | No grafana references in backend source | becomes "no `grafana` **service type**"; transport refs allowed | | SC-116 | No grafana references in frontend source | grafana transport allowed; no service type / link widget | | SC-121 | config.yaml matches implementation | config.yaml now references Grafana as sanctioned transport | | SC-122 | CHANGELOG documents the migration | migration note → "reconfigure prometheus with gateway fields" (GM-114) | | SC-125 | New widget kinds have tests | renamed `Metric*` widgets tested (GM-111) | | SC-126 | No silent data migration | no auto-migrate of old-shape prometheus config; operator reconfigures (GM-113) | ### ADDED (3) — fresh canonical IDs continuing the SC- sequence | Canonical ID | Header | GM source | |---|---|---| | SC-128 | get_prometheus_status validates the full gateway path | GM-110 | | SC-129 | Startup logs old-shape prometheus config without crashing | GM-113 (new behavior) | | SC-130 | Grafana is the sanctioned transport for the prometheus service | sanctioned-transport statement | ### PRESERVED (11) — unchanged in canonical; NOT in the delta SC-106 (chart renderer reused), SC-108 (chart window preset), SC-110 (gauge threshold bands), SC-113 (mean plain PromQL), SC-117 (no grafana service type in registries), SC-118 (grafana status/UI removed), SC-119 (orphan degrade), SC-120 (grafana tests removed), SC-123 (backend tests pass), SC-124 (frontend typecheck/build/lint), SC-127 (non-blocking on service-storage-harness). > **Count reconciliation:** 16 MODIFIED + 11 PRESERVED = 27 pre-existing requirements (SC-101 … > SC-127, all retained); + 3 ADDED (SC-128 … SC-130) = **30 total** in the canonical spec post-sync. ### REMOVED (0) · RENAMED (0) No requirements were removed or renamed. RENAMED is intentionally unsupported by the native delta helper; the GM→SC mapping was resolved as ADDED/MODIFIED, never RENAMED. ## 5. Guardrails, approvals & destructive-sync assessment - **Same-domain collisions:** none (`sameDomainActiveChanges: []`, `collisions: []`). No ordering decision was needed. - **Destructive sync:** This is a **non-additive** (MODIFIED-heavy) sync — 16 MODIFIED blocks, 0 REMOVED. Per the sync contract, a destructive/large-MODIFIED sync requires explicit approval recorded in the parent prompt. **Approval is recorded:** the dispatch brief explicitly identifies this as "the project's first non-additive sync," enumerates the exact SC-xxx to modify (including the partial ones), and directs the MODIFIED/ADDED/PRESERVED split. No REMOVED requirements are used. Proceeding under that recorded approval. - **MODIFIED-block matching:** every MODIFIED block header matched the canonical spec **exactly** (verified by `grep` of all 16 headers pre-edit; all 16 replacements applied with zero header mismatches). No `## RENAMED Requirements` section was authored. - **Legacy flat spec:** the flat `spec.md` is intentionally **left in place** as the planning artifact the work was built against (archive convention keeps flat specs); the new domain delta sits alongside it under `specs/prometheus-charting/`. - **Other-domain isolation:** `openspec/specs/web-ui/spec.md` and `openspec/specs/service-storage/spec.md` were **not modified** (`git status --porcelain` empty for both). ## 6. Validation / checks performed (file-backed, read-only) Run from `/home/user/manage` (no source edits, no test re-runs — owned by verify, already green): | Check | Command | Result | |---|---|---| | Canonical requirement count | `grep -cE "^### Requirement: SC-"` | **30** ✓ (was 27; +3 ADDED) | | Canonical ID sequence intact | `grep -oE "SC-[0-9]+" \| sort -un` | SC-101 … SC-130, contiguous, no gaps/dupes ✓ | | MODIFIED headers all matched | 16 exact-header edits to canonical | all 19 edits applied (front-matter, Purpose, 16 bodies, append) with **zero header mismatches** ✓ | | No direct-Prom residue in canonical | `grep -c "api/v1/query_range"` canonical | **1** — and it is SC-101's intentional "MUST NOT issue any direct `GET /api/v1/query_range`" negation ✓ | | Preserved block integrity | spot-check SC-106/108/110/113/117/118/127 | preserved verbatim ✓ | | Delta is MODIFIED + ADDED only | count delta section headers | MODIFIED=1, ADDED=1, REMOVED=0, RENAMED=0 ✓ (no REMOVED → no destructive removal) | | Delta ADDED IDs are fresh | `grep "SC-12[89]\|SC-130"` delta | SC-128, SC-129, SC-130 ✓ (no GM- reuse in canonical) | | `web-ui` / `service-storage` untouched | `git status --porcelain openspec/specs/{web-ui,service-storage}` | empty ✓ | | Front-matter + Purpose consistent | re-read canonical head | transport narrative updated to gateway (no "no Grafana middleman" contradiction with SC-121/SC-130) ✓ | | Markdown validity | write-time lint | all three files clean ✓ | ## 7. Matching issues **None.** All 16 MODIFIED block headers matched the canonical spec exactly; no fallback or fuzzy matching was required. The single `api/v1/query_range` occurrence in the canonical post-sync is the intentional negation inside SC-101, not a stale direct-Prom requirement. ## 8. Carry-over / residual notes for the archive summary 1. **[COSMETIC, non-blocking]** SC-106 (PRESERVED) still reads "preserved in the rebranded `PrometheusChartWidget`". This change renames that component to `MetricChartWidget` (GM-111), but SC-106 was explicitly listed as **PRESERVED** (not MODIFIED) in the change's "Canonical delta intent", so it was left verbatim. The *intent* (recharts renderer reused unchanged) remains true; only the component name in that one block is a cosmetic carry-over. A future change may refresh the name if desired. 2. **[VERIFY carry-over, non-blocking for sync]** GM-115 was PARTIAL in verify on enumerated test-coverage only (no startup-validation test; no status 401/403 → `auth_failed` test). Functional code is correct; the dispatch brief treats this as closed/effectively-PASS. The new SC-128 (status gateway path) and SC-129 (startup validation) requirements now canonicalize this behavior; `sdd-archive` may note the test-coverage gap. 3. **[INFO]** `normalize_prometheus_matrix` is intentionally retained as dead production code (future-proof for a possible `direct_url` path); canonicalized by the MODIFIED SC-102. 4. **[INFO]** Stale generated `.pi-map.md` artifacts still reference old `Prometheus*Widget` names; not deliverable source — regenerate at the orchestrator's convenience. 5. **[INFO]** Two uncommitted whitespace-only edits exist in the working tree (`PrometheusMetricWidget.tsx`, `ServicesPage.tsx`) — unrelated to this sync; no files were staged by this phase (`git diff --cached` empty). ## 9. Next recommended phase → **`sdd-archive`** (clean). Re-scan the native status engine to confirm `specs: done` / `sync: ready` / `archive: ready`, then move the change to `openspec/changes/archive/YYYY-MM-DD-grafana-metric-gateway`, carrying over the items in §8. Do **not** commit or push — the parent owns the commit with explicit paths. --- ### Appendix — Files written by this sync (OpenSpec only; no source code) - `openspec/changes/grafana-metric-gateway/specs/prometheus-charting/spec.md` — **change-side domain delta** (`## MODIFIED Requirements` × 16 + `## ADDED Requirements` × 3). - `openspec/specs/prometheus-charting/spec.md` — **canonical spec** (MODIFIED × 16 in place, ADDED × 3 appended; now 30 requirements SC-101 … SC-130; front-matter + Purpose updated for transport consistency). - `openspec/changes/grafana-metric-gateway/sync-report.md` — this report.